208 lines
7.8 KiB
JavaScript
208 lines
7.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { harness, props, text, leadId } from './helpers/crm-task-creation.mjs';
|
|
const form = 'new-crm-task';
|
|
function fill(h, title = 'SYNTHETIC first') {
|
|
h.change(form, 'Tarefa', title);
|
|
h.change(form, 'Prazo (hora local)', '2027-11-04T10:30');
|
|
}
|
|
function saved(h, request, patch = {}) {
|
|
return {
|
|
...h.props,
|
|
lead: {
|
|
...h.props.lead,
|
|
tasks: [
|
|
{
|
|
id: '20000000-0000-4000-8000-000000000001',
|
|
title: request.data.title,
|
|
due_at: request.data.due_at,
|
|
assignee_user_id: Number(request.data.assignee_user_id),
|
|
version_no: 1,
|
|
status: 'open',
|
|
},
|
|
],
|
|
},
|
|
taskCreation: {
|
|
lead_id: leadId,
|
|
actor_id: 1,
|
|
commands: [
|
|
{
|
|
key: request.data.client_key,
|
|
task_id: '20000000-0000-4000-8000-000000000001',
|
|
version: 1,
|
|
},
|
|
],
|
|
},
|
|
...patch,
|
|
};
|
|
}
|
|
test('confirmation preserves title and deadline edited after the request was sent', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
h.change(form, 'Tarefa', 'SYNTHETIC later');
|
|
h.change(form, 'Prazo (hora local)', '2027-11-05T11:00');
|
|
h.success(saved(h, request), request);
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC later');
|
|
assert.equal(
|
|
h.field(form, 'Prazo (hora local)').props.value,
|
|
'2027-11-05T11:00',
|
|
);
|
|
});
|
|
test('a lost response retries exactly the frozen request, preserving later edits', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
h.change(form, 'Tarefa', 'SYNTHETIC next task');
|
|
h.change(form, 'Responsável pela tarefa', '2');
|
|
h.fail(request);
|
|
const retry = h.submit(form);
|
|
assert.deepEqual(retry.data, request.data);
|
|
h.success(saved(h, retry), retry);
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC next task');
|
|
assert.equal(h.field(form, 'Responsável pela tarefa').props.value, '2');
|
|
const next = h.submit(form);
|
|
assert.notEqual(next.data.client_key, request.data.client_key);
|
|
assert.equal(next.data.title, 'SYNTHETIC next task');
|
|
});
|
|
test('a fresh read acknowledges the committed creation without posting again', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
h.fail(request, 500);
|
|
h.click('Actualizar tarefas');
|
|
assert.deepEqual(h.reloads.at(-1).only, [
|
|
'lead',
|
|
'owners',
|
|
'taskCreation',
|
|
'nextAction',
|
|
]);
|
|
h.refreshed(saved(h, request));
|
|
assert.equal(h.sent.length, 1);
|
|
assert.equal(h.field(form, 'Tarefa').props.value, '');
|
|
assert.match(text(h.tree), /Tarefa registada/);
|
|
});
|
|
test('unconfirmed reads keep the original command and later edits', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
h.fail(request);
|
|
h.change(form, 'Tarefa', 'SYNTHETIC later');
|
|
h.click('Actualizar tarefas');
|
|
h.refreshed(h.props);
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC later');
|
|
assert.deepEqual(h.submit(form).data, request.data);
|
|
});
|
|
test('another account or lead cannot acknowledge or clear a pending command', () => {
|
|
for (const context of [
|
|
{ lead_id: leadId, actor_id: 2 },
|
|
{ lead_id: 'other', actor_id: 1 },
|
|
]) {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
const next = saved(h, request);
|
|
next.taskCreation = { ...next.taskCreation, ...context };
|
|
request.options.onSuccess({ props: next });
|
|
request.form.processing = false;
|
|
h.render();
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC first');
|
|
assert.doesNotMatch(text(h.tree), /Tarefa registada/);
|
|
}
|
|
});
|
|
test('a same-version task mismatch does not count as confirmation', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
const next = saved(h, request);
|
|
next.lead.tasks[0].title = 'SYNTHETIC different';
|
|
h.success(next, request);
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC first');
|
|
assert.match(text(h.tree), /não permitiu confirmar/);
|
|
});
|
|
test('later task changes and a removed task are acknowledged without restoration', () => {
|
|
for (const removed of [false, true]) {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
const next = saved(h, request);
|
|
if (removed) next.lead.tasks = [];
|
|
else
|
|
Object.assign(next.lead.tasks[0], {
|
|
version_no: 2,
|
|
status: 'done',
|
|
title: 'SYNTHETIC updated',
|
|
});
|
|
request.options.onSuccess({ props: next });
|
|
request.form.processing = false;
|
|
h.render();
|
|
assert.equal(h.field(form, 'Tarefa').props.value, '');
|
|
assert.equal(h.sent.length, 1);
|
|
if (removed) assert.match(text(h.tree), /já não está disponível/);
|
|
}
|
|
});
|
|
test('validation retains the draft and allows a corrected fresh command', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
h.validation({ due_at: 'SYNTHETIC validation' }, request);
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC first');
|
|
h.change(form, 'Prazo (hora local)', '2027-11-06T12:00');
|
|
const next = h.submit(form);
|
|
assert.notEqual(next.data.client_key, request.data.client_key);
|
|
assert.equal(next.data.due_at, new Date('2027-11-06T12:00').toISOString());
|
|
});
|
|
test('an untouched assignee follows a refreshed owner while an explicit choice is retained', () => {
|
|
const h = harness();
|
|
h.render({ ...h.props, lead: { ...h.props.lead, owner_user_id: 2 } });
|
|
assert.equal(h.field(form, 'Responsável pela tarefa').props.value, '2');
|
|
h.change(form, 'Responsável pela tarefa', '1');
|
|
h.render({
|
|
...h.props,
|
|
lead: { ...h.props.lead, owner_user_id: null },
|
|
owners: [],
|
|
});
|
|
assert.equal(h.field(form, 'Responsável pela tarefa').props.value, '1');
|
|
assert.match(text(h.form(form)), /Responsável anterior indisponível/);
|
|
});
|
|
test('draft and unknown results have one navigation guard; confirmed unchanged values clear it', () => {
|
|
const h = harness();
|
|
assert.equal(h.leave(), false);
|
|
fill(h);
|
|
assert.equal(h.leave(), true);
|
|
assert.equal(h.confirmCalls, 1);
|
|
assert.equal(h.unload(), true);
|
|
const request = h.submit(form);
|
|
h.fail(request);
|
|
assert.equal(h.leave(true), false);
|
|
h.success(saved(h, request), request);
|
|
assert.equal(h.leave(), false);
|
|
assert.equal(h.unload(), false);
|
|
});
|
|
test('an obsolete callback cannot erase the draft of a different lead or actor', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
const other = props({
|
|
lead: { ...h.props.lead, id: 'other' },
|
|
taskCreation: { lead_id: 'other', actor_id: 2, commands: [] },
|
|
});
|
|
h.render(other);
|
|
h.change(form, 'Tarefa', 'SYNTHETIC other draft');
|
|
request.options.onSuccess({ props: saved(h, request) });
|
|
request.form.processing = false;
|
|
h.render();
|
|
assert.equal(h.field(form, 'Tarefa').props.value, 'SYNTHETIC other draft');
|
|
});
|
|
test('access and conflict responses keep the command blocked until a scoped refresh', () => {
|
|
const h = harness();
|
|
fill(h);
|
|
const request = h.submit(form);
|
|
h.fail(request, 403);
|
|
assert.equal(h.button('Recuperar a mesma criação').props.disabled, true);
|
|
h.click('Actualizar tarefas');
|
|
h.refreshed(h.props);
|
|
assert.equal(h.button('Recuperar a mesma criação').props.disabled, false);
|
|
assert.deepEqual(h.submit(form).data, request.data);
|
|
});
|