const test = require('node:test'); const assert = require('node:assert/strict'); const api = require('../../static/js/case-commands.js'); const now = new Date(2026,9,6,12); test('commands preserve prose and deterministic child scope', () => { const r = api.parse('Kommentar\n/tid 30m research "Undersøgt AI"\n/tildel ct "Bestil varer"\n/undersag "Router opsætning"\n//tildel mkk "Opsæt router"\n//forventet 30\n/undersag "Test"\n//todo "Test VPN" @ct fredag\nSlut',{now}); assert.deepEqual(r.errors, []); assert.equal(r.comment,'Kommentar\nSlut'); assert.equal(r.commands[0].minutes,30); assert.equal(r.commands[0].description,'Undersøgt AI'); assert.equal(r.commands[1].scope,'main'); assert.equal(r.commands[2].scope,0); assert.equal(r.commands[3].scope,0); assert.equal(r.commands[5].scope,1); assert.equal(r.commands[6].scope,1); assert.equal(r.commands[6].date,'2026-10-09'); }); test('hours, decimal comma and escaped quoted strings', () => { const r = api.parse('/tid 1,5h udvikling "Test \\"router\\""'); assert.equal(r.errors.length,0); assert.equal(r.commands[0].minutes,90); assert.equal(r.commands[0].description,'Test "router"'); assert.equal(api.duration('2h'),120); assert.equal(api.duration('30min'),30); for(const bad of ['-1','0','1.2m','10x','Infinity']) assert.throws(()=>api.duration(bad)); }); test('relative dates and impossible dates', () => { assert.equal(api.resolveDate('i morgen',now),'2026-10-07'); assert.equal(api.resolveDate('tirsdag',now),'2026-10-06'); assert.equal(api.resolveDate('mandag',now),'2026-10-12'); assert.equal(api.resolveDate('2028-02-29',now),'2028-02-29'); assert.throws(()=>api.resolveDate('2026-02-29',now)); assert.equal(api.parse('/udsat i morgen',{now}).commands[0].date,'2026-10-07'); }); test('malformed commands are rejected and are never saved as comment', () => { for (const line of ['//forventet 30','/tildek ct "Test"','/tid 30m research Test','/note "unfinished','/note ""','/mig ekstra','/relation #0','/ukendt','/timer stop','/prioritet ukendt "Test"','/afventer leverandør "Test"']) { const r=api.parse(line); assert.equal(r.errors.length,1,line); assert.equal(r.comment,'',line); assert.equal(r.commands.length,0,line); } assert.match(api.parse('/tildek ct "Test"').errors[0].message,/tildel/); }); test('all remaining commands expose canonical fields', () => { const r=api.parse('/mig\n/todo "Ring kunden"\n/afventer kunde "Adgang"\n/løsning "Genstart"\n/deadline fredag\n/fil\n/hjælp\n/timer start "Router"\n/timer pause\n/relation #1248\n/prioritet høj "Ingen internet"\n/gruppe support "Fejlsøgning"\n/mail "Vi har bestilt router"\n/note "Intern tekst"',{now}); assert.deepEqual(r.errors,[]); assert.deepEqual(r.commands.map(c=>c.type),['self','todo','waiting','solution','deadline','file','help','timer','timer','relation','priority','group','mail','note']); assert.equal(r.commands[9].caseId,1248); assert.equal(r.commands[10].priority,'høj'); }); test('invalid undersag does not create a child scope',()=>{ const r=api.parse('/undersag ""\n//forventet 30'); assert.equal(r.errors.length,2); }); test('quoted full names and groups are valid identifiers',()=>{ const r=api.parse('/tildel "Christian Thomas" "Bestil"\n/gruppe "Teknisk support" "Begrundelse"'); assert.deepEqual(r.errors,[]); assert.equal(r.commands[0].assignee,'Christian Thomas'); assert.equal(r.commands[1].group,'Teknisk support'); }); test('todo rejects leftover fields, duplicate assignees and quoted dates',()=>{ for (const line of ['/todo','/todo "Ring" ekstra','/todo "Ring" @ct @mkk','/todo "Ring" fredag ekstra','/todo "Ring" "fredag"','/todo "Ring" @ct "i morgen"']) { const r=api.parse(line,{now}); assert.equal(r.errors.length,1,line); assert.equal(r.commands.length,0,line); } assert.equal(api.parse('/todo "Ring" @ct i morgen',{now}).commands[0].date,'2026-10-07'); });