54 lines
6.4 KiB
JavaScript
54 lines
6.4 KiB
JavaScript
import fs from 'node:fs';
|
|
import assert from 'node:assert/strict';
|
|
import {Element,walk,parsePage} from './purchasing-workspace-dom.mjs';
|
|
|
|
// Executes the actual browser module in a small DOM. This is not visual acceptance.
|
|
const document=parsePage(fs.readFileSync('resources/views/stock-operations-workspace.blade.php','utf8'));
|
|
document.createElement=tag=>new Element(tag);
|
|
Element.prototype.contains=function(el){return walk(this).includes(el);};
|
|
Element.prototype.cloneNode=function(){const e=new Element(this.tag,this._text);Object.assign(e,{_value:this._value,selected:this.selected});return e;};
|
|
Object.defineProperty(Element.prototype,'options',{get(){return this.children;}});
|
|
Object.defineProperty(Element.prototype,'selectedOptions',{get(){return this.children.filter(e=>e.value===this.value);}});
|
|
const form=document.querySelector('#operation');
|
|
form.elements=new Proxy({},{get:(_,key)=>walk(form).find(e=>e.name===key)});
|
|
form.reset=()=>{for(const e of walk(form)){if(e.name)e.value=e.tag==='select'?(e.options[0]?.value??''):'';}};
|
|
const listeners={};globalThis.window={addEventListener:(name,fn)=>{listeners[name]=fn;}};globalThis.document=document;
|
|
globalThis.FormData=class{constructor(form){this.rows=walk(form).filter(e=>e.name&&!e.disabled&&!(()=>{for(let p=e.parent;p;p=p.parent)if(p.disabled)return true;return false;})()).map(e=>[e.name,e.value]);}[Symbol.iterator](){return this.rows[Symbol.iterator]();}};
|
|
const calls=[];let confirms=0;
|
|
globalThis.fetch=async(url,options)=>{const u=new URL(url,'http://sgf.test'),body=options.body?JSON.parse(options.body):null;calls.push({path:u.pathname,body});let value;
|
|
if(u.pathname.endsWith('/basis'))value={basis:{item:{id:1,name:'Material QA',unit:'UN'},warehouses:[{id:1,name:'Origem'},{id:2,name:'Destino'}],responsible:{id:6,name:'Armazém QA'},balances:[{warehouse_id:1,quantity:1000,version:55}]},revision:'a'.repeat(64)};
|
|
else if(u.pathname.endsWith('/preview'))value={preview:'sealed-preview',impact:{reference:body.data.reference,reason:body.data.reason,quantity_milli:body.data.kind==='adjustment'?1500:Number(body.data.quantity)*1000,theoretical_milli:1000,change_milli:body.data.kind==='count'?0:body.data.kind==='adjustment'?500:-Number(body.data.quantity)*1000,quantity_after_milli:body.data.kind==='count'?1000:body.data.kind==='adjustment'?1500:1000-Number(body.data.quantity)*1000}};
|
|
else if(u.pathname.endsWith('/confirm')){if(++confirms===1)throw Error('Response lost');value={reference:'SAIDA-QA',operation_id:44,replayed:true};}
|
|
else {const view=u.searchParams.get('view');value={current_page:1,next_page_url:null,data:view==='history'?[]:view==='warehouses'?[{id:1,name:'Origem'},{id:2,name:'Destino'}]:view==='items'?[{id:1,name:'Material QA',unit:'UN'}]:[{id:6,name:'Armazém QA'}]};}
|
|
return {ok:true,status:200,json:async()=>value};
|
|
};
|
|
const checks=[];const ok=(v,name)=>{assert(v,name);checks.push(name);};
|
|
await import('../public/stock-operations-workspace.mjs');await new Promise(resolve=>setImmediate(resolve));
|
|
ok(calls.every(c=>!c.body),'startup makes reads only');
|
|
ok(form.elements.stock_item_id.value===''&&form.elements.responsible_id.value==='','catalogues never auto-select item or responsible');
|
|
for(const [name,value] of Object.entries({kind:'issue',warehouse_id:'1',stock_item_id:'1',responsible_id:'6',quantity:'0.125',reference:'SAIDA-QA',reason:'Fundamento explícito para ensaio',destination:'Oficina QA'}))form.elements[name].value=value;
|
|
form.oninput();let unload={preventDefault(){this.prevented=true;}};listeners.beforeunload(unload);ok(unload.prevented,'unsaved draft guards page exit');
|
|
await form.onsubmit({preventDefault(){}});
|
|
const sent=calls.find(c=>c.path.endsWith('/preview')).body;
|
|
ok(sent.data.stock_item_id===1&&sent.data.quantity==='0.125'&&sent.data.reason.includes('explícito'),'form captured before disabling fields; exact decimal and reason retained');
|
|
ok(sent.data.destination_warehouse_id===undefined,'issue does not send a hidden transfer destination');
|
|
ok(document.querySelector('#impact').textContent.includes('Material QA')&&document.querySelector('#impact').textContent.includes('0.125 UN'),'impact shown in readable form before confirmation');
|
|
ok(!calls.some(c=>c.path.endsWith('/confirm')),'preview never auto-confirms');
|
|
await document.querySelector('#confirm').onclick();
|
|
ok(document.querySelector('#fields').disabled&&!document.querySelector('#confirm').disabled,'uncertain result protects fields and permits only recovery');
|
|
await document.querySelector('#confirm').onclick();
|
|
const sends=calls.filter(c=>c.path.endsWith('/confirm'));
|
|
ok(sends.length===2&&sends[0].body.preview===sends[1].body.preview,'recovery uses identical sealed conference without a new key');
|
|
ok(document.querySelector('#impact-section').hidden&&document.querySelector('#status').textContent.includes('recuperado'),'recovered receipt closes confirmation');
|
|
unload={preventDefault(){this.prevented=true;}};listeners.beforeunload(unload);ok(!unload.prevented,'successful recovery releases exit guard');
|
|
for(const [name,value] of Object.entries({kind:'count',warehouse_id:'1',stock_item_id:'1',responsible_id:'6',quantity:'0',reference:'COUNT-QA',reason:'Contagem física declarada no ensaio'}))form.elements[name].value=value;
|
|
form.onchange({target:form.elements.kind});await form.onsubmit({preventDefault(){}});
|
|
const countBody=calls.filter(c=>c.path.endsWith('/preview')).at(-1).body.data;
|
|
ok(countBody.ledger_version===55&&countBody.quantity==='0','count uses freshly observed revision and permits explicit zero quantity');
|
|
ok(countBody.destination===undefined&&countBody.equipment_id===undefined,'count excludes issue-only fields');
|
|
form.elements.kind.value='adjustment';form.onchange({target:form.elements.kind});await new Promise(resolve=>setImmediate(resolve));form.elements.count_id.value='6';await form.onsubmit({preventDefault(){}});
|
|
const adjustBody=calls.filter(c=>c.path.endsWith('/preview')).at(-1).body.data;
|
|
ok(adjustBody.count_id===6&&adjustBody.quantity===undefined,'adjustment uses the explicitly selected count rather than a manually entered difference');
|
|
ok(document.querySelector('#impact').textContent.includes('Variação na origem0.5 UN')&&document.querySelector('#impact').textContent.includes('Saldo físico posterior1.5 UN'),'adjustment impact distinguishes difference from the counted total');
|
|
const out=process.argv[2];const result={passed:true,checks,dom_simulation:true,visual_review:false};if(out)fs.writeFileSync(out,JSON.stringify(result,null,2));console.log(JSON.stringify({passed:true,checks:checks.length}));
|