import test from 'node:test'; import assert from 'node:assert/strict'; import vm from 'node:vm'; import {readFileSync} from 'node:fs'; const source=readFileSync(new URL('../apps/browser/main.cjs',import.meta.url),'utf8'); function fixture(){ const contents={isDestroyed:()=>false,getURL:()=> 'https://example.test/page'},tab={id:'tab-1',contents,startPage:false,security:{interactionDocumentGeneration:2}},live={navigationId:8,activation:4,paused:false},policy={version:3,expiresAt:new Date(Date.now()+60000).toISOString(),evidence:{enabled:true,captureInteractions:true}}; const c=vm.createContext({ready:true,policy,activeTab:()=>tab,tabs:{context:()=>({...live})},tabContext:()=>({...live}),currentTabContext:()=>true,overviewPanel:false,toolsOpen:false}); vm.runInContext(source.slice(source.indexOf('function interactionObserverContext(tab)'),source.indexOf('function installSiteHooks(tab)')),c); return {c,tab,live,contents,context:c.interactionObserverContext(tab)}; } test('human observation survives visual DOM revisions but rejects real document or activation changes',()=>{ const f=fixture();assert.equal(f.c.currentInteractionObserverContext(f.context),true);f.live.navigationId++;assert.equal(f.c.currentInteractionObserverContext(f.context),true,'Visual reinspection does not imply another document');f.tab.security.interactionDocumentGeneration++;assert.equal(f.c.currentInteractionObserverContext(f.context),false);f.tab.security.interactionDocumentGeneration--;f.live.activation++;assert.equal(f.c.currentInteractionObserverContext(f.context),false); }); test('human observation rejects revocation, disabled capture, expiry, overlays and changed native contents',()=>{ for(const mutate of [f=>f.c.ready=false,f=>f.c.policy.evidence.captureInteractions=false,f=>f.c.policy.version++,f=>f.c.policy.expiresAt=new Date(0).toISOString(),f=>f.c.toolsOpen=true,f=>f.c.overviewPanel=true,f=>f.tab.startPage=true,f=>f.tab.contents={...f.contents},f=>f.contents.getURL=()=> 'https://example.test/other',f=>f.live.paused=true]){const f=fixture();mutate(f);assert.equal(f.c.currentInteractionObserverContext(f.context),false);} });