bmc_hub/tests/test_absence_report_ui.js

44 lines
3.7 KiB
JavaScript

/* Exercise report rendering and CSV export using the production script. */
const assert=require('node:assert/strict'),fs=require('node:fs'),vm=require('node:vm');
const elements=new Map(),requests=[];
function element(id){if(!elements.has(id))elements.set(id,{innerHTML:'',textContent:'',hidden:false,disabled:false,dataset:{},value:'',insertAdjacentHTML(_,html){this.innerHTML+=html;}});return elements.get(id);}
const form=element('absenceFilters');form.elements={start_date:element('absenceFrom'),end_date:element('absenceTo'),user_id:element('absenceEmployee')};
const item={id:1,kind:'vacation',name:'=Private spreadsheet formula',kind_label:'Ferie',starts_at:'2026-10-15T06:00:00Z',ends_at:'2026-10-16T14:00:00Z',norm_configured:true,planned_minutes:900,planned_days:2,actual_minutes:450,registered:true};
const result={employees:[{id:1,name:'One'}],summary:[{user_id:1,name:'One',norm_configured:true,planned_minutes:900,planned_days:2,actual_minutes:450}],items:[item],total:1,totals:{planned_minutes:900,planned_days:2,actual_minutes:450}};
result.type_summary=[{kind:'vacation',kind_label:'Ferie',periods:1,norm_configured:true,planned_minutes:900,planned_days:2,actual_minutes:450,employees:result.summary}];
let download=null;
const context={window:{},Intl,Date,Number,String,Promise,URLSearchParams,Blob,
FormData:class{constructor(form){this.values=Object.entries(form.elements).map(([key,input])=>[key,input.value]);}[Symbol.iterator](){return this.values[Symbol.iterator]();}},
document:{getElementById:element,createElement:()=>({click(){}})},
fetch:async path=>{requests.push(path);return{ok:true,json:async()=>result};},
URL:{createObjectURL:blob=>{download=blob;return 'blob:report';},revokeObjectURL(){}},setTimeout:()=>{},
};
vm.createContext(context);vm.runInContext(fs.readFileSync('static/js/absence-report.js','utf8'),context);
(async()=>{
await new Promise(resolve=>setImmediate(resolve));
assert.match(element('absenceRows').innerHTML,/Tid registreret/);
assert.match(element('absenceRows').innerHTML,/scope="rowgroup" colspan="8">Ferie/);
assert.match(element('absenceSummary').innerHTML,/Ferie/);
assert.match(element('absenceSummary').innerHTML,/One/);
assert.equal(new URLSearchParams(requests[0].split('?')[1]).get('group_by'),'type');
assert.match(element('absenceTotals').innerHTML,/15 t/);
assert.match(element('absenceTotals').innerHTML,/7,5 t/);
assert.match(element('absenceRows').innerHTML,/<td>2<\/td>/);
assert.match(element('absencePage').textContent,/2 arbejdsdage i alt/);
assert.equal(element('absenceExport').disabled,false);
await element('absenceExport').onclick();
const csv=await download.text();
assert.match(csv,/"Ferie";"'=Private spreadsheet formula"/);
assert.equal(new URLSearchParams(requests[1].split('?')[1]).get('group_by'),'type');
assert.match(csv,/"Arbejdsdage"/);
assert.match(csv,/"2";"15";"7,5";"Tid registreret"/);
assert.equal(requests.length,2);
assert.equal(new URLSearchParams(requests[1].split('?')[1]).get('limit'),'500');
assert.equal(new URLSearchParams(requests[0].split('?')[1]).get('start_date'),new URLSearchParams(requests[1].split('?')[1]).get('start_date'));
context.fetch=async()=>({ok:false,json:async()=>({detail:'Ingen adgang'})});
await form.onsubmit({preventDefault(){}});await new Promise(resolve=>setImmediate(resolve));
assert.equal(element('absenceExport').disabled,true);
assert.equal(element('absenceRows').innerHTML,'');assert.equal(element('absenceError').textContent,'Ingen adgang');
console.log('Absence report UI: rendering, separate actual time, CSV quoting, filters and access failures OK');
})().catch(err=>{console.error(err);process.exitCode=1;});