Files
Investidor-V2/tests/frontend/crm-task-preparation.test.mjs
T

327 lines
12 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { harness, props, text } from './helpers/crm-task-preparation.mjs';
const form = 'new-crm-task';
const taskId = '20000000-0000-4000-8000-000000000001';
function fill(h, title = 'SYNTHETIC prepared task') {
h.change(form, 'Tarefa', title);
h.change(form, 'Prazo (hora local)', '2027-11-04T10:30');
}
function savedContext(h, call = h.draftRequests.at(-1)) {
return {
...h.props.taskCreation,
preparation: {
installed: true,
version: call.data.version + 1,
operation_key: call.data.operation_key,
snapshot: call.data.snapshot,
unavailable: false,
updated_at: '2026-11-03T12:00:00Z',
},
};
}
function pageWith(context, patch = {}) {
return props({ taskCreation: context, ...patch });
}
test('automatic preparation survives a new page instance and adoption is explicit', async () => {
const first = harness();
fill(first);
await first.tick();
assert.equal(first.sent.length, 0);
assert.equal(first.draftRequests.length, 1);
const context = savedContext(first);
await first.respond(context);
const next = harness(pageWith(context));
assert.equal(next.field(form, 'Tarefa').props.value, '');
assert.ok(next.button('Comparar preparação guardada'));
next.click('Comparar preparação guardada');
next.click('Aplicar escolhas e retomar');
assert.equal(
next.field(form, 'Tarefa').props.value,
'SYNTHETIC prepared task',
);
assert.equal(
new Date(
next.field(form, 'Prazo (hora local)').props.value,
).toISOString(),
context.preparation.snapshot.values.due_at,
);
assert.equal(next.sent.length, 0);
});
test('creation waits until its frozen command has been checkpointed', async () => {
const h = harness();
fill(h);
h.submit(form);
assert.equal(h.sent.length, 0);
assert.equal(h.draftRequests.length, 1);
const context = savedContext(h);
await h.respond(context);
assert.equal(h.sent.length, 1);
assert.deepEqual(
h.sent[0].data,
context.preparation.snapshot.pending.values,
);
});
test('an uncertain preparation write retries exactly its key and preserves later edits', async () => {
const h = harness();
fill(h);
h.submit(form);
const first = h.draftRequests[0];
h.change(form, 'Tarefa', 'SYNTHETIC later');
await h.rejectDraft(first);
h.click('Voltar a guardar preparação');
const retry = h.draftRequests.at(-1);
assert.deepEqual(retry.data, first.data);
await h.respond(savedContext(h, retry), retry);
assert.equal(h.sent.length, 0);
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC later');
h.submit(form);
await h.flush();
assert.equal(h.sent.length, 1);
assert.equal(h.sent[0].data.title, 'SYNTHETIC prepared task');
});
test('a new page recovers an uncertain business command with its original key', async () => {
const h = harness();
fill(h);
h.submit(form);
const context = savedContext(h);
await h.respond(context);
const request = h.sent[0];
h.fail(request, 500);
const next = harness(pageWith(context));
next.click('Comparar preparação guardada');
next.click('Aplicar escolhas e retomar');
assert.equal(next.sent.length, 0);
next.submit(form);
assert.deepEqual(next.sent[0].data, request.data);
});
test('a committed creation is acknowledged after reopening without another business request', async () => {
const h = harness();
fill(h);
h.submit(form);
const context = savedContext(h);
await h.respond(context);
const request = h.sent[0];
const stored = structuredClone(context);
stored.preparation.snapshot.values.title = 'SYNTHETIC later';
stored.preparation.snapshot.generation.title++;
stored.commands = [
{ key: request.data.client_key, task_id: taskId, version: 1 },
];
const next = harness(
pageWith(stored, {
lead: {
...h.props.lead,
tasks: [
{
id: taskId,
title: request.data.title,
due_at: request.data.due_at,
assignee_user_id: 1,
version_no: 1,
status: 'open',
},
],
},
}),
);
next.click('Comparar preparação guardada');
next.click('Aplicar escolhas e retomar');
assert.equal(next.sent.length, 0);
assert.equal(next.field(form, 'Tarefa').props.value, 'SYNTHETIC later');
assert.equal(next.field(form, 'Prazo (hora local)').props.value, '');
assert.match(text(next.tree), /Tarefa registada/);
});
test('changes made during the business request are checkpointed with its immutable command', async () => {
const h = harness();
fill(h);
h.submit(form);
await h.respond(savedContext(h));
const request = h.sent[0];
h.change(form, 'Tarefa', 'SYNTHETIC later');
await h.tick();
const latest = h.draftRequests.at(-1).data;
assert.equal(latest.snapshot.values.title, 'SYNTHETIC later');
assert.deepEqual(latest.snapshot.pending.values, request.data);
});
test('comparison preserves local edits and requires a new choice after further changes', async () => {
const h = harness();
fill(h);
await h.tick();
const context = savedContext(h);
const next = harness(pageWith(context));
next.change(form, 'Tarefa', 'SYNTHETIC local');
next.click('Comparar preparação guardada');
next.change(form, 'Valor a retomar: Tarefa', 'saved');
next.change(form, 'Tarefa', 'SYNTHETIC after choice');
assert.equal(
next.button('Aplicar escolhas e retomar').props.disabled,
true,
);
next.change(form, 'Valor a retomar: Tarefa', 'page');
next.click('Aplicar escolhas e retomar');
assert.equal(
next.field(form, 'Tarefa').props.value,
'SYNTHETIC after choice',
);
});
test('a response for a different account cannot authorise a business send or clear the draft', async () => {
const h = harness();
fill(h);
h.submit(form);
await h.respond({ ...savedContext(h), actor_id: 2 });
assert.equal(h.sent.length, 0);
assert.equal(
h.field(form, 'Tarefa').props.value,
'SYNTHETIC prepared task',
);
assert.match(text(h.tree), /Não foi possível confirmar a preparação/);
});
test('a callback from the previous account cannot send a task after navigation', async () => {
const h = harness();
fill(h);
h.submit(form);
const call = h.draftRequests[0],
context = savedContext(h, call);
h.render(props({ taskCreation: { ...props().taskCreation, actor_id: 2 } }));
h.change(form, 'Tarefa', 'SYNTHETIC next account');
await h.respond(context, call);
assert.equal(h.sent.length, 0);
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC next account');
});
test('an unavailable installation retains the existing manual creation path', () => {
const initial = props();
initial.taskCreation.preparation.installed = false;
const h = harness(initial);
fill(h);
const request = h.submit(form);
assert.equal(h.draftRequests.length, 0);
assert.equal(request.data.title, 'SYNTHETIC prepared task');
assert.match(text(h.tree), /criação manual continua disponível/);
});
test('another saved preparation cannot replace a locally sent unknown command', async () => {
const h = harness();
fill(h);
h.submit(form);
await h.respond(savedContext(h));
const request = h.sent[0];
h.fail(request);
const newer = savedContext(h);
newer.preparation.version = 3;
newer.preparation.operation_key = '00000000-0000-4000-8000-999999999999';
newer.preparation.snapshot = structuredClone(newer.preparation.snapshot);
newer.preparation.snapshot.pending.values.client_key =
'00000000-0000-4000-8000-888888888888';
h.render({ ...h.props, taskCreation: newer });
h.click('Comparar preparação guardada');
h.click('Aplicar escolhas e retomar');
assert.match(text(h.tree), /outro envio sem confirmação/);
const retry = h.submit(form);
assert.deepEqual(retry.data, request.data);
});
test('a confirmed durable preparation does not repeat the leave warning but new edits do', async () => {
const h = harness();
fill(h);
assert.equal(h.leave(), true);
await h.tick();
await h.respond(savedContext(h));
assert.equal(h.leave(), false);
assert.equal(h.unload(), false);
h.change(form, 'Tarefa', 'SYNTHETIC unsaved change');
assert.equal(h.leave(), true);
assert.equal(h.unload(), true);
});
test('validation allows a corrected preparation with a fresh save key without discarding its fields', async () => {
const h = harness();
fill(h, 'S'.repeat(201));
await h.tick();
const first = h.draftRequests[0];
await h.validationDraft(
{ 'snapshot.values.title': 'SYNTHETIC validation' },
first,
);
h.change(form, 'Tarefa', 'SYNTHETIC corrected');
h.click('Voltar a guardar preparação');
const corrected = h.draftRequests.at(-1);
assert.notEqual(corrected.data.operation_key, first.data.operation_key);
assert.equal(corrected.data.snapshot.values.title, 'SYNTHETIC corrected');
await h.respond(savedContext(h, corrected), corrected);
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC corrected');
});
test('unreadable saved data is explained and the explicit manual path preserves the server preparation', () => {
const initial = props();
initial.taskCreation.preparation = {
...initial.taskCreation.preparation,
version: 1,
unavailable: true,
};
const h = harness(initial);
assert.match(text(h.tree), /não pôde ser lida/);
h.click('Continuar só nesta página');
fill(h);
h.submit(form);
assert.equal(h.sent.length, 1);
assert.equal(h.draftRequests.length, 0);
assert.match(text(h.tree), /guardada no servidor foi preservada/);
});
test('cross-time-zone recovery keeps the saved instant including repeated local time and seconds', async () => {
const previous = process.env.TZ;
process.env.TZ = 'America/New_York';
try {
const initial = props(),
instant = '2027-11-07T06:30:12.345Z';
initial.taskCreation.preparation = {
installed: true,
version: 1,
operation_key: '00000000-0000-4000-8000-111111111111',
unavailable: false,
updated_at: '2026-11-03T12:00:00Z',
snapshot: {
values: {
title: 'SYNTHETIC saved instant',
due_at: instant,
assignee_user_id: '1',
},
generation: { title: 1, due_at: 1, assignee_user_id: 0 },
pending: null,
},
};
const h = harness(initial);
h.click('Comparar preparação guardada');
h.click('Aplicar escolhas e retomar');
h.submit(form);
assert.equal(
h.draftRequests[0].data.snapshot.pending.values.due_at,
instant,
);
await h.respond(savedContext(h));
assert.equal(h.sent[0].data.due_at, instant);
} finally {
if (previous === undefined) delete process.env.TZ;
else process.env.TZ = previous;
}
});
test('an empty completed preparation matching the page does not ask the user to compare it again', () => {
const initial = props();
initial.taskCreation.preparation = {
installed: true,
version: 2,
operation_key: '00000000-0000-4000-8000-111111111111',
unavailable: false,
updated_at: '2026-11-03T12:00:00Z',
snapshot: {
values: { title: '', due_at: '', assignee_user_id: '1' },
generation: { title: 2, due_at: 1, assignee_user_id: 0 },
pending: null,
},
};
const h = harness(initial);
assert.equal(h.button('Comparar preparação guardada'), undefined);
assert.equal(h.button('Criar tarefa').props.disabled, false);
assert.equal(h.leave(), false);
assert.equal(h.draftRequests.length, 0);
});