const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs'),vm=require('node:vm'); function client(fetch){ const context={fetch,AbortController,setTimeout,clearTimeout,crypto:require('node:crypto').webcrypto,Uint8Array}; vm.createContext(context);vm.runInContext(fs.readFileSync('static/js/remote-api.js','utf8').replaceAll('export ',''),context); return context; } const response=(data,type='application/json',extra={})=>({ok:true,headers:{get:key=>key==='Content-Type'?type:null},json:async()=>data,...extra}); test('HTML or redirected login responses cannot replace the remote snapshot',async()=>{ for(const result of [response({},'text/html'),response({},'application/json',{redirected:true})]){ const c=client(async()=>result);await assert.rejects(c.api('/state'),/forventede svar/); } }); test('network failure is recoverable and later state can be read',async()=>{ let offline=true; const c=client(async(url,options)=>{assert.ok(options.signal);if(offline)throw new Error('offline');return response({timer:{active:{active:false}}});}); await assert.rejects(c.api('/state'),/Prøver igen/);offline=false;assert.equal((await c.api('/state')).timer.active.active,false); }); test('unchanged list data preserves its DOM, changed data triggers a render',()=>{ const source=fs.readFileSync('static/js/remote-mobile.js','utf8'); const start=source.indexOf('const renderedLists =');const end=source.indexOf('try { pendingCall',start); const c={Map};vm.createContext(c);vm.runInContext(source.slice(start,end),c); assert.equal(c.listChanged('messages',[{id:1,text:'Hej'}]),true); assert.equal(c.listChanged('messages',[{id:1,text:'Hej'}]),false); assert.equal(c.listChanged('messages',[{id:1,text:'Ændret'}]),true); });