import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { createRequire } from 'node:module'; import { compileFunction } from 'node:vm'; import ts from 'typescript'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; const require = createRequire(import.meta.url); const Link = ({ children, href }) => React.createElement('a', { href }, children); const Empty = () => null; const mocks = { '@inertiajs/react': { Link, useForm: (data) => ({ data, processing: false, errors: {} }), }, '@/lib/crm-record-edit': { useCrmRecordEdit: ({ values }) => ({ form: { processing: false }, values, blocked: false, error: () => undefined, }), }, '@/components/crm-edit-comparison': { default: Empty }, '../management/shared': { Management: ({ children }) => React.createElement('main', null, children), Field: ({ label, children }) => React.createElement('label', null, label, children), button: '', control: '', }, './types': { leadStates: { new: 'Novo', qualified: 'Qualificado', disqualified: 'Desqualificado', converted: 'Convertido', }, taskStates: { open: 'Aberta', done: 'Concluída' }, }, }; function load(path) { const code = ts.transpileModule( fs.readFileSync(new URL('../../' + path, import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX, target: ts.ScriptTarget.ES2022, }, }, ).outputText; const module = { exports: {} }; compileFunction(code, ['require', 'module', 'exports'])( (name) => mocks[name] ?? require(name), module, module.exports, ); return module.exports; } mocks['@/pages/management/shared'] = mocks['../management/shared']; mocks['./operation-key'] = { createOperationKey: () => '00000000-0000-4000-8000-000000000001', }; mocks['./profile-json'] = load('resources/js/lib/profile-json.ts'); mocks['./crm-task-preparation'] = load( 'resources/js/lib/crm-task-preparation.ts', ); mocks['@/lib/crm-task-creation'] = load( 'resources/js/lib/crm-task-creation.ts', ); mocks['@/components/crm-task-creation'] = load( 'resources/js/components/crm-task-creation.tsx', ); const Detail = load('resources/js/pages/crm/lead.tsx').default; function render(patch = {}) { return renderToStaticMarkup( React.createElement(Detail, { lead: { id: 'one', version_no: 1, title: 'SYNTHETIC lead', status: 'new', owner_user_id: 1, tasks: [], }, taskCreation: { lead_id: 'one', actor_id: 1, commands: [] }, contacts: [], owners: [{ id: 1, name: 'SYNTHETIC manager' }], nextAction: { future_tasks: 0 }, ...patch, }), ); } test('an active lead without an available future task explains the existing requirement and links to its creation form', () => { const html = render(); assert.match(html, /0 tarefas disponíveis com prazo futuro/); assert.match(html, /role="status"/); assert.match(html, /dossier encerrado ficam no histórico/); assert.match(html, /href="#new-crm-task"/); assert.match(html, /id="new-crm-task"/); assert.doesNotMatch(html, /disabled=""/); // Disqualification and creation remain available; the server checks the chosen operation. }); test('a future action count does not claim that qualification or completion has already occurred', () => { const singular = render({ nextAction: { future_tasks: 1 } }); assert.match(singular, /1 tarefa disponível com prazo futuro/); assert.doesNotMatch(singular, /1 tarefas/); const html = render({ nextAction: { future_tasks: 2 } }); assert.match(html, /2 tarefas disponíveis com prazo futuro/); assert.doesNotMatch( html, /role="status"|acompanhamento qualificado|licença aprovada/, ); }); test('a suspended task retains its title, deadline and history without a response button', () => { const html = render({ lead: { id: 'one', version_no: 1, title: 'SYNTHETIC lead', status: 'qualified', owner_user_id: 1, tasks: [ { id: 'paused', version_no: 1, title: 'SYNTHETIC preserved task', status: 'open', due_at: '2026-11-05T10:00:00Z', assignee_user_id: 1, can_update: false, }, ], }, }); assert.match(html, /SYNTHETIC preserved task/); assert.match(html, /Dossier associado encerrado/); assert.match(html, /Maputo/); assert.match(html, /id="crm-task-paused"/); assert.doesNotMatch(html, /Actualizar tarefa<\/button>/); }); test('an older response without availability metadata does not invent a zero', () => { const html = render({ nextAction: undefined }); assert.doesNotMatch( html, /tarefas disponíveis com prazo futuro|role="status"/, ); assert.match(html, /Registar próxima acção/); }); test('disqualified leads retain their form without the active-lead requirement notice', () => { const html = render({ lead: { id: 'one', version_no: 1, title: 'SYNTHETIC disqualified lead', status: 'disqualified', owner_user_id: 1, tasks: [], }, }); assert.match(html, /0 tarefas disponíveis com prazo futuro/); assert.doesNotMatch(html, /role="status"|disabled=""/); });