Files
Investidor-V2/tests/frontend/partnership-publication.test.mjs
T

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);
});