Files
SGF/scripts/sgf-human-resources-prepared-ui.mjs
2026-11-11 15:04:01 +02:00

13 lines
7.5 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',checks=[],ok=(v,n)=>{assert(v,n);checks.push(n);};let slots=[],cursor=0,effects=[],tree,calls=[],rootComponent,props;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'),Suspense:Symbol('suspense'),lazy:()=>Symbol('lazy'),useRef(initial){const i=cursor++;slots[i]??={value:{current:initial}};return slots[i].value;},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.__hrHooks=R;const stub='const r=globalThis.__hrHooks;export default r;export const useState=r.useState,useMemo=r.useMemo,useEffect=r.useEffect,useRef=r.useRef;',reactUrl='data:text/javascript;base64,'+Buffer.from(stub).toString('base64'),compile=async(file,imports)=>{let s=fs.readFileSync('resources/js/'+file,'utf8').replace(/from\s*['"]react['"]/, 'from '+JSON.stringify(reactUrl));for(const f of imports)s=s.replace(new RegExp('from\\s*[\'"]\\./'+f.replaceAll('.','\\.')+'[\'"]'),'from '+JSON.stringify(pathToFileURL(process.cwd()+'/resources/js/'+f).href));const c=await transformWithOxc(s,file,{jsx:{runtime:'classic'}});return import('data:text/javascript;base64,'+Buffer.from(c.code).toString('base64'));};
const{default:Workspace,checkedRemunerations}=await compile('RemunerationsWorkspace.jsx',['workshop-stock-operation.js','statement-download.js']);const{navigationGroups}=await compile('WorkspaceNavigation.jsx',['billing-menu.js','workshop-menu.js','stock-menu.js','accounting-menu.js']);const{moduleGroups}=await compile('ModuleNavigation.jsx',[]);
const row={id:99,kind:'worker',record_key:'00000000-0000-4000-8000-000000000001',version:1,state:'prepared',actor_id:999,prepared_by:999,created_at:'QA',reason:'Fixture de UI sem autoridade real',data:{input:{code:'QA-TEST-ONLY',status:'active',operation:'establish'},calculation:null},effective:null,impact:{before:{version:0,state:null},after:{version:1,state:'prepared'}}};let allowPrepare=true,failConfirm=true,commandCalls=[];
const page=q=>({policy:'SGF-REMUNERATIONS-QA-v1',synthetic_only:true,legal_payroll_validated:false,kind:q.kind,records:{data:q.kind==='worker'?[row]:[],current_page:q.page,last_page:2,total:26},capabilities:{prepare:allowPrepare,review:false},page_totals:{approved_active_on_page:0,gross_cents:'0',net_cents:'0',employer_cost_cents:'0'}});
const api=async(path,method,body,unused,key)=>{calls.push({path,method,body,key});if(method==='POST'&&path.endsWith('/preview'))return{preview:'opaque-fixture',impact:{before:{version:0,state:null},after:{version:1,state:'prepared',proposal:{input:body.data,status:'active',calculation:null}}}};if(method==='POST'){commandCalls.push({path,body:structuredClone(body),key});if(failConfirm){const e=Error('Resposta perdida');e.status=503;throw e;}return{id:100,version:1,state:'prepared',replayed:true};}const q=Object.fromEntries(new URL('http://qa'+path).searchParams);q.page=Number(q.page);q.per_page=Number(q.per_page);return page(q);};
const render=()=>{cursor=0;tree=rootComponent(props);const jobs=effects;effects=[];jobs.forEach(f=>f());return tree;},tick=async()=>{await new Promise(r=>setTimeout(r,0));render();},descend=n=>Array.isArray(n)?n.flatMap(descend):n&&typeof n==='object'?[n,...descend(n.children)]:[],nodes=(n=tree)=>descend(n),text=n=>Array.isArray(n)?n.map(text).join(''):n&&typeof n==='object'?text(n.children):String(n??''),find=(tag,label)=>nodes().find(n=>n.type===tag&&text(n).includes(label));
rootComponent=Workspace;props={api,context:{token:'hr-context',module_id:'human_resources',unit:{id:2}}};render();await tick();ok(text(tree).includes('RECURSOS HUMANOS')&&text(tree).includes('A conformidade legal continua pendente'),'RH autonomous identity and honest limits');ok(!find('h2','Registo escolhido'),'no implicit first record selection');find('button','Escolher este registo').props.onClick();render();ok(Boolean(find('h2','Registo escolhido')),'individual chosen record opens');find('button','Novo:').props.onClick();render();ok(nodes().find(n=>n.type==='input'&&n.props.pattern==='QA-[A-Z0-9-]{2,60}').props.value==='QA-','new worker does not inherit selected code');ok(find('button','Processamento interno').props.disabled===true,'form blocks tab change');find('label','Código pseudónimo').children.find(n=>n?.type==='input').props.onChange({target:{value:'QA-NEW-ONLY'}});find('label','Fundamento individual').children.find(n=>n?.type==='textarea').props.onChange({target:{value:'Fundamento individual de ensaio sem operação real'}});render();const form=nodes().find(n=>n.type==='form'&&text(n).includes('Conferir impacto'));await form.props.onSubmit({preventDefault(){}});render();ok(calls.at(-1).body.data.code==='QA-NEW-ONLY'&&calls.at(-1).body.kind==='worker','preview exact selected form body');ok(Boolean(find('h2','Impacto antes da confirmação')),'impact shown before confirm');const confirm=find('button','Confirmar esta decisão individual');await confirm.props.onClick();render();ok(Boolean(find('button','Recuperar a mesma confirmação'))&&find('button','Processamento interno').props.disabled,'503 preserves uncertain decision and blocks navigation');failConfirm=false;await find('button','Recuperar a mesma confirmação').props.onClick();render();await tick();ok(commandCalls.length===2&&JSON.stringify(commandCalls[0])===JSON.stringify(commandCalls[1]),'recovery sends identical body path and key');ok(!find('button','Recuperar a mesma confirmação'),'successful receipt ends uncertainty');
find('button','Processamento interno').props.onClick();render();await tick();find('button','Novo:').props.onClick();render();const gross=find('label','Bruto MZN').children.find(n=>n?.type==='input');ok(new RegExp('^'+gross.props.pattern+'$').test('10000.00'),'gross browser pattern accepts documentary amount');const pickers=nodes().filter(n=>typeof n.type==='function'&&n.props.kind);ok(pickers.length===2&&pickers.every(n=>n.props.value===''),'worker and rules sources explicitly unselected');
const hrCan=(right)=>right==='human_resources.remunerations.view',groups=navigationGroups(hrCan,2,{remunerations:true}),owned=moduleGroups(groups,'human_resources');ok(owned.length===1&&owned[0].id==='human_resources'&&owned[0].items[0].id==='remunerations','own dropdown contains only HR functions');ok(moduleGroups(groups,'finance').length===0,'HR grant grants no financial menu');ok(navigationGroups(()=>false,2,{remunerations:true}).every(g=>!g.items.some(i=>i.id==='remunerations')),'no rights no HR menu');const valid=page({kind:'worker',page:1,per_page:25});assert.equal(checkedRemunerations(valid,{kind:'worker',page:1,per_page:25}),valid);assert.throws(()=>checkedRemunerations({...valid,kind:'payroll'},{kind:'worker',page:1,per_page:25}));ok(true,'late wrong kind response refused');
const file=root+'/hp-remunerations-ui-20261111.json';fs.writeFileSync(file,JSON.stringify({passed:true,checks,api_mocked:true,profiles_not_granted:true,browser_visual_review:false},null,2));console.log(JSON.stringify({passed:true,checks:checks.length,proof:file}));