15 lines
7.1 KiB
JavaScript
15 lines
7.1 KiB
JavaScript
import fs from 'node:fs';import assert from 'node:assert/strict';import {pathToFileURL} from 'node:url';import {transformWithOxc} from 'vite';
|
|
const root=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-current.json')).root+'/private/receipt-composition-cc50a276c10c',sample=JSON.parse(fs.readFileSync(root+'/payable-due-sample-private.json')),checks=[],ok=(v,n)=>{assert(v,n);checks.push(n);};
|
|
// Execute actual component handlers with a small hook host. This is not a browser/visual proof.
|
|
let slots=[],cursor=0,effects=[],tree,props,calls=[],next=structuredClone(sample),uncertain=true,closed=false;
|
|
const same=(a,b)=>a&&b&&a.length===b.length&&a.every((v,i)=>v===b[i]);
|
|
const R={createElement:(type,props,...children)=>({type,props:props??{},children}),Fragment:Symbol('fragment'),useState(initial){const i=cursor++;slots[i]??={value:initial};return[slots[i].value,v=>{slots[i].value=typeof v==='function'?v(slots[i].value):v;}];},useMemo(fn,deps){const i=cursor++;if(!same(slots[i]?.deps,deps))slots[i]={value:fn(),deps};return slots[i].value;},useEffect(fn,deps){const i=cursor++;if(!same(slots[i]?.deps,deps)){slots[i]?.cleanup?.();slots[i]={deps};effects.push(()=>slots[i].cleanup=fn());}}};globalThis.__sgfDueHooks=R;
|
|
const stub='const r=globalThis.__sgfDueHooks;export default r;export const useState=r.useState,useMemo=r.useMemo,useEffect=r.useEffect;',stubUrl='data:text/javascript;base64,'+Buffer.from(stub).toString('base64');let source=fs.readFileSync('resources/js/PayableDueWorkspace.jsx','utf8').replace("from 'react'",'from '+JSON.stringify(stubUrl));for(const f of ['workshop-stock-operation.js','ledger-report-view.js'])source=source.replace("from './"+f+"'",'from '+JSON.stringify(pathToFileURL(process.cwd()+'/resources/js/'+f).href));const code=await transformWithOxc(source,'PayableDueWorkspace.jsx',{jsx:{runtime:'classic'}}),{default:Workspace,DueFacts}=await import('data:text/javascript;base64,'+Buffer.from(code.code).toString('base64'));
|
|
const api=async(path,method='GET',body,context,key)=>{calls.push({path,method,body:structuredClone(body),key});if(path.endsWith('/preview'))return{preview:'encrypted-cut',impact:{before:{version:2,reviewed_terms:sample.reviewed_terms},after:{version:3,state:'prepared',proposal:body.data,reviewed_terms:sample.reviewed_terms},balance_minor:'10000',currency:'MZN'}};if(path.endsWith('/confirm')){if(uncertain){uncertain=false;const e=Error('QA response lost');e.status=503;throw e;}return{id:123,version:3,state:'prepared',replayed:true};}return structuredClone(next);};
|
|
props={row:{obligation_id:2,reference:sample.basis.document.number},api,context:{token:'qa-generation'},onClose(){closed=true;}};const render=()=>{cursor=0;tree=Workspace(props);const jobs=effects;effects=[];jobs.forEach(f=>f());return tree;},tick=async()=>{await new Promise(r=>setTimeout(r,0));render();};const nodes=(n=tree)=>Array.isArray(n)?n.flatMap(nodes):n&&typeof n==='object'?[n,...nodes(n.children)]:[],text=n=>Array.isArray(n)?n.map(text).join(''):n&&typeof n==='object'?text(typeof n.type==='function'?n.type(n.props):n.children):String(n??''),find=(tag,label)=>nodes().find(n=>n.type===tag&&text(n).includes(label)),click=label=>{const b=find('button',label);assert(b,label);assert(!b.props.disabled,label+' disabled');b.props.onClick();render();};
|
|
render();ok(calls.length===0,'no automatic reference date/query');ok(nodes().some(n=>n.type==='input'&&n.props.type==='date'&&n.props.value===''),'reference initially empty');next.capabilities={prepare:true,review:false};nodes().find(n=>n.type==='input').props.onChange({target:{value:'2026-11-30'}});render();find('form','Consultar condições').props.onSubmit({preventDefault(){}});render();await tick();ok(calls[0].path.includes('reference_on=2026-11-30'),'explicit reference sent');ok(text(DueFacts({data:sample})).includes('0 dia(s) de atraso'),'same day known zero rendered');const unknown=structuredClone(sample);unknown.days_overdue=null;unknown.reviewed_terms=null;ok(text(DueFacts({data:unknown})).includes('Atraso indeterminado'),'unknown not rendered as zero');
|
|
click('Preparar condição individual');ok(find('button','Voltar às posições').props.disabled,'cannot leave dirty form silently');ok(find('fieldset','Consultar condições').props.disabled,'cannot change query while editing');ok(nodes().some(n=>n.type==='textarea'&&n.props.value===''),'reason not copied from prior decision');const checkbox=nodes().find(n=>n.type==='input'&&n.props.type==='checkbox');checkbox.props.onChange({target:{checked:true}});render();ok(nodes().some(n=>n.type==='input'&&n.props.type==='date'&&n.props.disabled),'unknown requires explicit checkbox and disables date');const evidence=nodes().find(n=>n.type==='input'&&n.props.minLength===10);evidence.props.onChange({target:{value:'QA artificial explicit clear evidence.'}});render();nodes().find(n=>n.type==='textarea').props.onChange({target:{value:'QA reason individual and deliberate.'}});render();await find('form','Conferir impacto').props.onSubmit({preventDefault(){}});render();ok(calls.find(c=>c.path.endsWith('/preview')).body.data.due_on===null,'explicit unknown sent as null');ok(text(tree).includes('Impacto a confirmar')&&text(tree).includes('zero pagamentos e zero lançamentos'),'impact visible before confirmation');ok(nodes().some(n=>n.type==='fieldset'&&n.props.disabled),'preview protects source fields');
|
|
click('Confirmar esta decisão individual');await tick();ok(text(tree).includes('Resultado incerto'),'uncertain result shown');ok(find('button','Descartar formulário').props.disabled&&find('button','Voltar às posições').props.disabled,'uncertain operation cannot be discarded/switched');click('Recuperar a mesma confirmação');await tick();const confirmations=calls.filter(c=>c.path.endsWith('/confirm'));ok(confirmations.length===2&&JSON.stringify(confirmations[0])===JSON.stringify(confirmations[1]),'recovery preserves body context and operation key');ok(text(tree).includes('recibo recuperado'),'successful recovery acknowledged');ok(!find('button','Voltar às posições').props.disabled,'return enabled after recovery');click('Voltar às posições');ok(closed,'explicit return works after completion');
|
|
const version=structuredClone(sample);version.capabilities={prepare:false,review:true};version.basis.last.state='prepared';version.basis.last.data=JSON.stringify({due_on:'2026-11-30',evidence:'QA evidence to review.'});next=version;slots=[];effects=[];calls=[];render();nodes().find(n=>n.type==='input').props.onChange({target:{value:'2026-11-30'}});render();find('form','Consultar condições').props.onSubmit({preventDefault(){}});render();await tick();ok(!find('button','Preparar condição individual'),'reviewer no prepare action');click('Rever preparação actual');ok(nodes().some(n=>n.type==='select'&&n.props.required&&n.props.value===''),'review decision has no automatic choice');ok(text(tree).includes('QA evidence to review.'),'review shows actual preparation evidence');
|
|
const file=root+'/payable-due-ui-20261110.json';fs.writeFileSync(file,JSON.stringify({passed:true,checks,actual_component_handlers:true,api_mocked:true,browser_visual_review:false,business_commands:0},null,2));console.log(JSON.stringify({passed:true,checks:checks.length,proof:file}));
|