268 lines
9.8 KiB
JavaScript
268 lines
9.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
harness,
|
|
props,
|
|
nodes,
|
|
text,
|
|
} from './helpers/partnership-candidacy.mjs';
|
|
const reason = (h) =>
|
|
nodes(h.tree).find(
|
|
(node) =>
|
|
node.type === 'label' && node.props.label === 'Motivo da ação',
|
|
)?.props.children;
|
|
const field = (h) => nodes(reason(h)).find((node) => node.type === 'textarea');
|
|
test('publication reason survives a proposal version refresh', () => {
|
|
const p = props({
|
|
owner: true,
|
|
canEdit: true,
|
|
record: { ...props().record, status: 'draft' },
|
|
publicationActions: ['submit', 'archive'],
|
|
publicationCommands: [],
|
|
});
|
|
const h = harness(p);
|
|
field(h).props.onChange({ target: { value: 'SYNTHETIC prepared reason' } });
|
|
h.render();
|
|
h.render({ ...p, record: { ...p.record, version: p.record.version + 1 } });
|
|
assert.equal(field(h).props.value, 'SYNTHETIC prepared reason');
|
|
});
|
|
const prepared = (patch = {}) =>
|
|
harness(
|
|
props({
|
|
owner: true,
|
|
canEdit: true,
|
|
record: { ...props().record, status: 'draft' },
|
|
publicationActions: ['submit', 'archive'],
|
|
publicationCommands: [],
|
|
...patch,
|
|
}),
|
|
);
|
|
function change(h, value) {
|
|
field(h).props.onChange({ target: { value } });
|
|
h.render();
|
|
}
|
|
const adopt = 'Revi esta versão; conservar o motivo e preparar nova acção';
|
|
const compare = 'Comparar com a proposta actual';
|
|
function saved(h, request = h.sent.at(-1), patch = {}) {
|
|
const status = {
|
|
submit: 'submitted',
|
|
publish: 'published',
|
|
return: 'returned',
|
|
reject: 'rejected',
|
|
suspend: 'suspended',
|
|
archive: 'archived',
|
|
renew: 'draft',
|
|
}[request.data.action];
|
|
return {
|
|
...h.props,
|
|
record: {
|
|
...h.props.record,
|
|
version: request.data.version + 1,
|
|
status,
|
|
},
|
|
publicationActions: [],
|
|
publicationCommands: [
|
|
{
|
|
key: request.data.client_key,
|
|
action: request.data.action,
|
|
version: request.data.version + 1,
|
|
},
|
|
],
|
|
...patch,
|
|
};
|
|
}
|
|
test('publication sends the reviewed version with a private operation key and one frozen reason', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC reason');
|
|
const request = h.click('Submeter para publicação');
|
|
assert.equal(request.url, '/parcerias/1/estado');
|
|
assert.equal(request.data.version, 4);
|
|
assert.equal(request.data.action, 'submit');
|
|
assert.equal(request.data.reason, 'SYNTHETIC reason');
|
|
assert.match(request.data.client_key, /^[0-9a-f-]{36}$/);
|
|
});
|
|
test('a content refresh requires explicit review and retains the prepared reason', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC deliberate reason');
|
|
h.render({
|
|
...h.props,
|
|
record: {
|
|
...h.props.record,
|
|
version: 5,
|
|
title: 'SYNTHETIC revised title',
|
|
},
|
|
});
|
|
assert.equal(h.button('Submeter para publicação').props.disabled, true);
|
|
assert.ok(text(h.tree).includes('SYNTHETIC revised title'));
|
|
h.click(adopt);
|
|
const request = h.click('Submeter para publicação');
|
|
assert.equal(request.data.version, 5);
|
|
assert.equal(request.data.reason, 'SYNTHETIC deliberate reason');
|
|
});
|
|
test('an untouched publication adopts current data without a stale empty form', () => {
|
|
const h = prepared();
|
|
h.render({ ...h.props, record: { ...h.props.record, version: 5 } });
|
|
change(h, 'SYNTHETIC new preparation');
|
|
assert.equal(h.button('Submeter para publicação').props.disabled, false);
|
|
assert.equal(h.click('Submeter para publicação').data.version, 5);
|
|
});
|
|
test('network recovery repeats the original command and preserves text typed afterwards', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC original reason');
|
|
const first = h.click('Submeter para publicação');
|
|
change(h, 'SYNTHETIC later reason');
|
|
h.fail(first);
|
|
const retry = h.click(
|
|
'Recuperar a mesma tentativa: Submeter para publicação',
|
|
);
|
|
assert.deepEqual(retry.data, first.data);
|
|
h.success(saved(h, retry), retry);
|
|
assert.equal(field(h).props.value, 'SYNTHETIC later reason');
|
|
});
|
|
test('successful acknowledgement clears only the unchanged reason', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC sent reason');
|
|
const request = h.click('Submeter para publicação');
|
|
h.success(saved(h, request), request);
|
|
assert.equal(
|
|
nodes(h.tree).find((node) => node.props?.['data-publication-form']) !==
|
|
undefined,
|
|
true,
|
|
);
|
|
assert.equal(h.leave(), false);
|
|
assert.equal(h.sent.length, 1);
|
|
});
|
|
test('deliberate changes back to an earlier string are preserved by their generation', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC sent');
|
|
const request = h.click('Submeter para publicação');
|
|
change(h, 'SYNTHETIC different');
|
|
change(h, 'SYNTHETIC sent');
|
|
h.success(saved(h, request), request);
|
|
assert.equal(field(h).props.value, 'SYNTHETIC sent');
|
|
});
|
|
test('a conflict requires an actual refresh before adopting a new attempt', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC retained');
|
|
const request = h.click('Submeter para publicação');
|
|
h.fail(request, 409);
|
|
assert.equal(h.button(adopt).props.disabled, true);
|
|
h.click(compare);
|
|
h.refreshed({ ...h.props, record: { ...h.props.record, version: 6 } });
|
|
assert.equal(h.button(adopt).props.disabled, false);
|
|
h.click(adopt);
|
|
const retry = h.click('Submeter para publicação');
|
|
assert.equal(retry.data.version, 6);
|
|
assert.notEqual(retry.data.client_key, request.data.client_key);
|
|
assert.equal(retry.data.reason, 'SYNTHETIC retained');
|
|
});
|
|
test('a refresh can recognize a lost response from the actors own command ledger', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC sent');
|
|
const request = h.click('Submeter para publicação');
|
|
h.fail(request);
|
|
h.click(compare);
|
|
h.refreshed(saved(h, request));
|
|
assert.equal(
|
|
h.button('Recuperar a mesma tentativa: Submeter para publicação'),
|
|
undefined,
|
|
);
|
|
assert.equal(h.leave(), false);
|
|
});
|
|
test('a recognised operation does not silently adopt later proposal edits', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC sent');
|
|
const request = h.click('Submeter para publicação');
|
|
change(h, 'SYNTHETIC next reason');
|
|
h.success(
|
|
saved(h, request, {
|
|
record: {
|
|
...h.props.record,
|
|
version: 7,
|
|
status: 'draft',
|
|
title: 'SYNTHETIC changed later',
|
|
},
|
|
publicationActions: ['submit', 'archive'],
|
|
}),
|
|
request,
|
|
);
|
|
assert.equal(field(h).props.value, 'SYNTHETIC next reason');
|
|
assert.equal(h.button('Submeter para publicação').props.disabled, true);
|
|
h.click(adopt);
|
|
assert.equal(h.click('Submeter para publicação').data.version, 7);
|
|
});
|
|
test('an incompatible response does not confirm the attempt or clear the reason', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC conserved');
|
|
const request = h.click('Submeter para publicação');
|
|
h.success(saved(h, request, { publicationCommands: [] }), request);
|
|
assert.equal(field(h).props.value, 'SYNTHETIC conserved');
|
|
assert.ok(
|
|
h.button('Recuperar a mesma tentativa: Submeter para publicação'),
|
|
);
|
|
assert.equal(h.button(adopt).props.disabled, true);
|
|
});
|
|
test('loss of authority keeps local preparation and disables recovery', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC preserved');
|
|
const request = h.click('Submeter para publicação');
|
|
h.fail(request);
|
|
h.render({
|
|
...h.props,
|
|
owner: false,
|
|
canEdit: false,
|
|
moderator: false,
|
|
publicationActions: [],
|
|
publicationCommands: [],
|
|
});
|
|
assert.equal(field(h).props.value, 'SYNTHETIC preserved');
|
|
assert.equal(
|
|
h.button('Recuperar a mesma tentativa: Submeter para publicação').props
|
|
.disabled,
|
|
true,
|
|
);
|
|
assert.equal(h.button('Submeter para publicação'), undefined);
|
|
});
|
|
test('a different actor starts with a separate preparation and ignores an old callback', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC former actor reason');
|
|
const request = h.click('Submeter para publicação');
|
|
h.render({ ...h.props, actorId: 99 });
|
|
assert.equal(field(h).props.value, '');
|
|
request.options.onSuccess({ props: saved(h, request) });
|
|
request.form.processing = false;
|
|
h.render();
|
|
assert.equal(field(h).props.value, '');
|
|
});
|
|
test('publication and candidacy drafts share one leave warning', () => {
|
|
const h = prepared();
|
|
change(h, 'SYNTHETIC publication preparation');
|
|
h.change(
|
|
'decision:10',
|
|
'Motivo da decisão ou pedido de esclarecimento',
|
|
'SYNTHETIC candidacy preparation',
|
|
);
|
|
assert.equal(h.leave(), true);
|
|
assert.equal(h.confirmCalls, 1);
|
|
assert.equal(h.unload(), true);
|
|
});
|
|
test('suspension is displayed when authorized by the server and uses its existing command', () => {
|
|
const h = prepared({
|
|
owner: false,
|
|
canEdit: false,
|
|
moderator: true,
|
|
record: { ...props().record, status: 'published' },
|
|
publicationActions: ['suspend'],
|
|
});
|
|
change(h, 'SYNTHETIC suspension');
|
|
assert.equal(h.click('Suspender publicação').data.action, 'suspend');
|
|
assert.equal(h.button('Publicar'), undefined);
|
|
});
|
|
test('an empty API payload dictionary remains a valid draft for archival', () => {
|
|
const h = prepared({
|
|
record: { ...props().record, payload: [], status: 'draft' },
|
|
});
|
|
change(h, 'SYNTHETIC incomplete idea');
|
|
assert.equal(h.button('Arquivar publicação').props.disabled, false);
|
|
});
|