16 lines
3.6 KiB
JavaScript
16 lines
3.6 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import registry from '../apps/browser/tab-registry.cjs';
|
|
function fixture(options={}) {
|
|
let id=0;const attached=[],detached=[],disposed=[];
|
|
const tabs=registry.createTabRegistry({createView:()=>{const wc={id:++id,url:'https://example.test/',destroyed:false,stops:0,history:[],getURL(){return this.url;},isDestroyed(){return this.destroyed;},stop(){this.stops++;},close(){this.destroyed=true;}};return {webContents:wc,visible:false,setVisible(v){this.visible=v;}};},attach:v=>attached.push(v),detach:v=>detached.push(v),dispose:t=>disposed.push(t),...options});
|
|
return {tabs,attached,detached,disposed};
|
|
}
|
|
test('native views retain separate DOM/history ownership when switching',()=>{const {tabs}=fixture();const a=tabs.create(),b=tabs.create();a.view.webContents.history.push('private history A');b.view.webContents.history.push('history B');tabs.activate(b.id);tabs.activate(a.id);assert.notEqual(a.view.webContents,b.view.webContents);assert.deepEqual(a.view.webContents.history,['private history A']);assert.deepEqual(b.view.webContents.history,['history B']);assert.equal(tabs.fromContents(b.view.webContents.id),b);});
|
|
test('foreground approval fails after switch-away-and-back even on identical URL',()=>{const {tabs}=fixture();const a=tabs.create(),b=tabs.create();tabs.resume(1);const token=tabs.context(a.id);assert.equal(tabs.current(token),true);tabs.activate(b.id);tabs.activate(a.id);assert.equal(tabs.current(token),false);});
|
|
test('background network contexts survive switches but not navigation or policy change',()=>{const {tabs}=fixture();const a=tabs.create(),b=tabs.create();tabs.resume(1);const token=tabs.context(a.id,{foreground:false});tabs.activate(b.id);assert.equal(tabs.current(token),true);tabs.navigation(a.id);assert.equal(tabs.current(token),false);const renewed=tabs.context(a.id,{foreground:false});tabs.resume(2);assert.equal(tabs.current(renewed),false);assert.throws(()=>tabs.resume(1));});
|
|
test('quarantine stops hidden and visible tabs, invalidates approvals and denies all old contexts',()=>{const {tabs}=fixture();const a=tabs.create(),b=tabs.create();tabs.resume(1);const tokens=[tabs.context(a.id,{foreground:false}),tabs.context(b.id,{foreground:false})];a.security.visualApproval={approved:true};b.security.visualApproval={approved:true};tabs.quarantine();for(const tab of [a,b]){assert.equal(tab.view.webContents.stops,1);assert.equal(tab.view.visible,false);assert.equal(tab.security.visualApproval,null);}tabs.resume(1);assert.equal(tokens.every(t=>!tabs.current(t)),true);});
|
|
test('close destroys native contents and cannot revive closed tab capabilities',()=>{const {tabs,disposed}=fixture();const a=tabs.create(),b=tabs.create();tabs.resume(1);const token=tabs.context();tabs.remove(a.id);assert.equal(a.view.webContents.destroyed,true);assert.equal(tabs.current(token),false);assert.equal(tabs.active(),b);assert.equal(tabs.fromContents(a.view.webContents.id),null);tabs.close();assert.equal(b.view.webContents.destroyed,true);assert.equal(disposed.length,2);assert.throws(()=>tabs.create());});
|
|
test('URL mutation and renderer-chosen identifiers cannot reuse native approval',()=>{const {tabs}=fixture();const a=tabs.create();tabs.resume(1);const token=tabs.context();a.view.webContents.url='https://other.test/';assert.equal(tabs.current(token),false);assert.throws(()=>tabs.activate('renderer-forged-id'));assert.equal(Object.isFrozen(token),true);assert.notEqual(a.id,'managed');});
|
|
test('capacity is bounded before another renderer process is created',()=>{const {tabs}=fixture({maxTabs:1});tabs.create();assert.throws(()=>tabs.create(),/capacity/);assert.equal(tabs.all().length,1);});
|