import test from 'node:test'; import assert from 'node:assert/strict'; import {readFileSync} from 'node:fs'; import ts from 'typescript'; const code=ts.transpileModule(readFileSync(new URL('../../../packages/sensor-ui/src/boardLayout.ts',import.meta.url),'utf8'),{compilerOptions:{module:ts.ModuleKind.ESNext,target:ts.ScriptTarget.ES2022}}).outputText; const {createBoardLayoutStore,defaultBoardLayout}=await import('data:text/javascript;base64,'+Buffer.from(code).toString('base64')); const tick=()=>new Promise(resolve=>setImmediate(resolve)); function server(){ let value=structuredClone(defaultBoardLayout);const calls=[]; return {calls,read:async()=>structuredClone(value),patch:async(section,open)=>{ calls.push({section,open});await tick(); value={...value,revision:value.revision+1,open_sections:defaultBoardLayout.open_sections.filter(id=>id===section?open:value.open_sections.includes(id))}; return structuredClone(value); }}; } test('rapid toggles persist after navigation and preserve all-closed state',async()=>{ const api=server();const store=createBoardLayoutStore(api);await store.load(); const unsubscribe=store.subscribe(()=>{}); store.change(['settings','devices']);store.change(['devices']);store.change([]); unsubscribe(); while(store.getSnapshot().saving)await tick(); const reopened=createBoardLayoutStore(api);await reopened.load(); assert.deepEqual(reopened.getSnapshot().value.open_sections,[]); assert.equal(api.calls.length,3); }); test('a later toggle wins while an older patch is still in flight',async()=>{ const api=server();const store=createBoardLayoutStore(api);await store.load(); store.change(['settings','devices']);store.change(['computer','settings','devices']); while(store.getSnapshot().saving)await tick(); assert.deepEqual((await api.read()).open_sections,defaultBoardLayout.open_sections); }); test('different vehicles and simultaneous section changes do not clobber one another',async()=>{ const api=server(),other=server();const a=createBoardLayoutStore(api),b=createBoardLayoutStore(api),c=createBoardLayoutStore(other); await Promise.all([a.load(),b.load(),c.load()]); a.change(['settings','devices']);b.change(['computer','devices']); while(a.getSnapshot().saving||b.getSnapshot().saving)await tick(); assert.deepEqual((await api.read()).open_sections,['devices']); assert.deepEqual(c.getSnapshot().value.open_sections,defaultBoardLayout.open_sections); }); test('failed save reports failure, rolls back, and explicit reload recovers',async()=>{ const api=server();let fail=true; const store=createBoardLayoutStore({...api,patch:(...args)=>fail?Promise.reject(new Error('offline')):api.patch(...args)}); await store.load();store.change([]); while(store.getSnapshot().saving)await tick(); assert.ok(store.getSnapshot().error); assert.deepEqual(store.getSnapshot().value.open_sections,defaultBoardLayout.open_sections); fail=false;await store.load();store.change(['devices']); while(store.getSnapshot().saving)await tick(); assert.equal(store.getSnapshot().error,null); assert.deepEqual((await api.read()).open_sections,['devices']); }); test('invalid layout cannot overwrite stored preferences',async()=>{ let writes=0; const store=createBoardLayoutStore({read:async()=>({...defaultBoardLayout,open_sections:['motor-start']}),patch:async()=>{writes++;return defaultBoardLayout;}}); await store.load();store.change([]); assert.equal(store.getSnapshot().ready,false);assert.ok(store.getSnapshot().error);assert.equal(writes,0); });