2026-10-13 10:33:31 +02:00
'use strict' ;
if ( ! process . versions . electron ) {
const fs = require ( 'node:fs' ), path = require ( 'node:path' ), os = require ( 'node:os' );
const { spawn } = require ( 'node:child_process' );
const owned = fs . mkdtempSync ( path . join ( os . tmpdir (), 'safetynet-ui-showcase-' ));
const env = { ... process . env , SAFETYNET_SHOWCASE_PROFILE : owned }; delete env . ELECTRON_RUN_AS_NODE ;
const child = spawn ( require ( 'electron' ), [ __filename ], { stdio : 'inherit' , windowsHide : true , env });
const deadline = setTimeout (() => child . kill (), 90_000 );
const finish = code => {
clearTimeout ( deadline );
const resolved = path . resolve ( owned );
if ( ! resolved . startsWith ( path . resolve ( os . tmpdir ()) + path . sep ) || ! path . basename ( resolved ). startsWith ( 'safetynet-ui-showcase-' )) throw Error ( 'Unsafe showcase cleanup' );
fs . rmSync ( resolved , { recursive : true , force : true , maxRetries : 10 , retryDelay : 100 });
process . exit ( code ?? 1 );
};
child . once ( 'error' , () => finish ( 1 )); child . once ( 'exit' , finish );
} else {
// Finite test harness for the real trusted shell. No production browser startup,
// site view, real credentials, network browsing or external application state.
const { app , BrowserWindow , ipcMain , nativeTheme } = require ( 'electron' );
const fs = require ( 'node:fs' );
const path = require ( 'node:path' );
const os = require ( 'node:os' );
const assert = require ( 'node:assert/strict' );
const { capturePNG } = require ( '../apps/browser/visual-capture.cjs' );
app . disableHardwareAcceleration (); // Windows fixture uses the CPU compositor; production keeps its sandboxed defaults.
app . commandLine . appendSwitch ( 'force-device-scale-factor' , '1' );
const root = path . resolve ( __dirname , '..' );
const profile = process . env . SAFETYNET_SHOWCASE_PROFILE || fs . mkdtempSync ( path . join ( os . tmpdir (), 'safetynet-ui-showcase-' ));
if ( ! profile . startsWith ( path . resolve ( os . tmpdir ()) + path . sep )) throw Error ( 'Unsafe showcase profile' );
app . setPath ( 'userData' , profile );
const output = path . join ( root , 'artifacts' );
fs . mkdirSync ( output , { recursive : true });
2026-10-13 16:58:15 +02:00
const methods = [ 'organizeLibrary' , 'applyLibraryOrganization' , 'createTab' , 'activateTab' , 'closeTab' , 'groupTab' , 'mutateBookmarks' , 'updateSettings' , 'authorizeSetting' , 'state' , 'windowControls' , 'setChromeLayout' , 'shareURL' , 'enroll' , 'sso' , 'navigate' , 'back' , 'forward' , 'reload' , 'ai' , 'inspectFile' , 'agentConnection' , 'toggleAI' , 'toggleVault' , 'vaultStatus' , 'vaultCreate' , 'vaultUnlock' , 'vaultLock' , 'vaultList' , 'vaultSave' , 'vaultDelete' , 'vaultFill' , 'vaultGenerate' ];
2026-10-13 10:33:31 +02:00
const preload = path . join ( profile , 'showcase-preload.cjs' );
fs . writeFileSync ( preload , `const {contextBridge,ipcRenderer}=require('electron'); const methods= ${ JSON . stringify ( methods ) } ; const api={};for(const method of methods)api[method]=(...args)=>ipcRenderer.invoke('showcase:call',method,args);api.onState=callback=>{const listener=(_event,state)=>callback(state);ipcRenderer.on('showcase:state',listener);return()=>ipcRenderer.removeListener('showcase:state',listener);};contextBridge.exposeInMainWorld('safetynet',api);` );
const fixtures = [
{ title : 'Apple Documentation' , url : 'https://developer.apple.com/' },
{ title : 'GitHub' , url : 'https://github.com/' },
{ title : 'MDN' , url : 'https://developer.mozilla.org/' },
{ title : 'Wikipedia' , url : 'https://wikipedia.org/' },
{ title : 'Company Intranet' , url : 'https://intranet.example.test/' },
{ title : 'Design Library' , url : 'https://design.example.test/' }
];
let state = { ready : true , enrolled : true , brand : { name : 'SafetyNet' }, url : 'https://example.test' , pageTitle : 'Reference Workspace' , pageColor : '#8aacb4' , canNavigate : true , policyVersion : 12 , message : 'Public fixture · centrally managed workspace' , aiEnabled : true , agentsEnabled : true , documentScanEnabled : true , passwordManagerEnabled : true , vaultLocked : false };
let nextTab = 1 ; state . tabsRevision = 0 ; state . tabs = [{ id : 'showcase-native-1' , url : state . url , title : state . pageTitle , color : state . pageColor , group : '' , startPage : false , canGoBack : false , canGoForward : false }]; state . activeTabId = state . tabs [ 0 ]. id ;
function snapshot (){ const tab = state . tabs . find ( tab => tab . id === state . activeTabId ); state = {... state , url : tab . url , pageTitle : tab . title , pageColor : tab . color , tabsRevision : state . tabsRevision + 1 }; return JSON . parse ( JSON . stringify ( state ));}
function publish ( sender ){ const result = snapshot (); sender . send ( 'showcase:state' , result ); return result ;}
state . settings = { available : true , visible : true , hiddenSections : [], lockedSections : [], appearance : { mode : 'system' , accent : 'blue' , tabMode : 'separate' }};
state . bookmarks = { managed : [{... fixtures [ 4 ], id : 'showcase-company' , folder : 'Company' }], personal : { revision : 0 , items : fixtures . filter (( _ , i )=> i !== 4 ). map (( item , i )=>({... item , id : `showcase- ${ i } ` , folder : 'Work' }))}, allowUserAdd : true , allowUserEdit : true , allowUserDelete : true , adminReadUserBookmarks : true };
let vaultLocked = false ;
const records = [], screenshots = [], consoleErrors = [];
let window , finished = false ;
function cleanup () {
if ( path . resolve ( profile ). startsWith ( path . resolve ( os . tmpdir ()) + path . sep ) && path . basename ( profile ). startsWith ( 'safetynet-ui-showcase-' )) {
try { fs . rmSync ( profile , { recursive : true , force : true }); } catch {}
}
}
if ( ! process . env . SAFETYNET_SHOWCASE_PROFILE ) app . on ( 'quit' , cleanup );
const watchdog = setTimeout (() => { console . error ( 'UI showcase exceeded 80 seconds' ); app . exit ( 1 ); }, 80_000 );
watchdog . unref ();
ipcMain . handle ( 'showcase:call' , ( event , method , args ) => {
if ( event . sender !== window ? . webContents || ! methods . includes ( method )) return { ok : false , error : 'Showcase operation refused' };
let data = null ;
const navigation = typeof args [ 0 ] === 'object' ? args [ 0 ] : { url : args [ 0 ], tabId : state . activeTabId };
if ( method === 'navigate' && navigation . url === 'https://example.test/slow' ){ const id = navigation . tabId ; return new Promise ( resolve => setTimeout (()=>{ const tab = state . tabs . find ( tab => tab . id === id ); if ( tab ){ tab . url = navigation . url ; tab . title = 'Delayed public fixture' ; tab . startPage = false ;} resolve ({ ok : true , data : publish ( event . sender )});}, 250 ));}
if ( method === 'state' || [ 'enroll' , 'sso' , 'back' , 'forward' , 'reload' ]. includes ( method )) data = snapshot ();
else if ( method === 'createTab' ){ const tab = { id : `showcase-native- ${ ++ nextTab } ` , url : '' , title : 'New Tab' , group : args [ 0 ] ? . group || '' , startPage : true , color : '#b8c7dd' , canGoBack : false , canGoForward : false }; state . tabs . push ( tab ); state . activeTabId = tab . id ; data = publish ( event . sender );}
else if ( method === 'activateTab' ){ assert . ok ( state . tabs . some ( tab => tab . id === args [ 0 ])); state . activeTabId = args [ 0 ]; data = publish ( event . sender );}
else if ( method === 'closeTab' ){ const index = state . tabs . findIndex ( tab => tab . id === args [ 0 ]); assert . ok ( index >= 0 ); state . tabs . splice ( index , 1 ); if ( ! state . tabs . length ){ state . tabs . push ({ id : `showcase-native- ${ ++ nextTab } ` , url : '' , title : 'New Tab' , group : '' , startPage : true , color : '#b8c7dd' , canGoBack : false , canGoForward : false });} if ( state . activeTabId === args [ 0 ]) state . activeTabId = state . tabs [ Math . min ( index , state . tabs . length - 1 )]. id ; data = publish ( event . sender );}
2026-10-13 16:58:15 +02:00
else if ( method === 'organizeLibrary' ){ assert . ok ( state . libraryAI ? . suggestionsEnabled ); data = { proposalId : 'fixture-review-id' , plan : { bookmarks : [{ id : 'showcase-1' , folder : 'Engineering' }], tabs : []}};}
else if ( method === 'applyLibraryOrganization' ){ assert . equal ( args [ 0 ]. proposalId , 'fixture-review-id' ); assert . equal ( args [ 0 ]. confirm , true ); state . bookmarks . personal . items . find ( item => item . id === 'showcase-1' ). folder = 'Engineering' ; state . bookmarks . personal . revision ++ ; data = { completed : true };}
2026-10-13 10:33:31 +02:00
else if ( method === 'groupTab' ){ assert . ok ( state . settings . visible &&! state . settings . hiddenSections . includes ( 'library' ) &&! state . settings . lockedSections . includes ( 'library' )); const tab = state . tabs . find ( tab => tab . id === args [ 0 ]. id ); assert . ok ( tab ); tab . group = args [ 0 ]. group ; data = publish ( event . sender );}
else if ( method === 'mutateBookmarks' ){ const input = args [ 0 ]; if ( ! state . settings . visible || state . settings . hiddenSections . includes ( 'library' ) || state . settings . lockedSections . includes ( 'library' ) || input . expectedRevision !== state . bookmarks . personal . revision ) return { ok : false , error : 'Managed bookmark refused' }; assert . equal ( input . action , 'add' ); state . bookmarks . personal = { revision : state . bookmarks . personal . revision + 1 , items : [... state . bookmarks . personal . items , input . bookmark ]}; data = state ; event . sender . send ( 'showcase:state' , state );}
else if ( method === 'updateSettings' ){ if ( ! state . settings . available ||! state . settings . visible || state . settings . hiddenSections . includes ( 'appearance' ) || state . settings . lockedSections . includes ( 'appearance' )) return { ok : false , error : 'Managed setting refused' }; state . settings = {... state . settings , appearance : {... args [ 0 ]}}; data = state . settings ;}
else if ( method === 'authorizeSetting' ){ if ( ! state . settings . available ||! state . settings . visible || state . settings . hiddenSections . includes ( args [ 0 ]) || state . settings . lockedSections . includes ( args [ 0 ])) return { ok : false , error : 'Managed setting refused' }; data = { allowed : true };}
else if ( method === 'navigate' ) { const url = new URL ( navigation . url ); assert . equal ( url . protocol , 'https:' ); const tab = state . tabs . find ( tab => tab . id === navigation . tabId ); assert . ok ( tab ); tab . url = url . href ; tab . title = fixtures . find ( item => item . url === url . href ) ? . title || 'Reference Workspace' ; tab . startPage = false ; data = publish ( event . sender ); }
else if ( method === 'setChromeLayout' ) {
const layout = args [ 0 ]; assert . ok ([ 64 , 104 ]. includes ( layout . height ));
for ( const key of [ 'sidebarOpen' , 'startPageOpen' , 'overviewOpen' , 'toolsOpen' ]) assert . equal ( typeof layout [ key ], 'boolean' );
records . push ({ method , ... layout , nativeHidden : layout . startPageOpen || layout . overviewOpen || layout . toolsOpen }); data = { applied : true };
} else if ( method === 'windowControls' ) { assert . ok ([ 'minimize' , 'maximize' , 'close' ]. includes ( args [ 0 ])); records . push ({ method , action : args [ 0 ], nativeFlag : false }); data = { recorded : true }; }
else if ( method === 'shareURL' ) data = { shared : true };
else if ( method === 'toggleAI' || method === 'toggleVault' ) { records . push ({ method , open : args [ 0 ] }); data = { applied : true }; }
else if ( method === 'vaultStatus' ) data = { enabled : true , exists : true , locked : vaultLocked , allowSaving : true , allowAutofill : true , idleLockSeconds : 300 };
else if ( method === 'vaultList' ) data = [{ id : 'public-fixture' , origin : 'https://example.test' , username : 'example-user' , label : 'Public demonstration account' }];
else if ( method === 'vaultLock' ) { vaultLocked = true ; data = { locked : true }; }
else if ( method === 'vaultCreate' || method === 'vaultUnlock' ) { vaultLocked = false ; data = { locked : false }; }
else if ( method === 'vaultGenerate' ) data = 'PUBLIC-DEMONSTRATION-ONLY' ;
else if ( method === 'vaultSave' || method === 'vaultDelete' || method === 'vaultFill' ) data = { completed : true };
else if ( method === 'ai' ) data = { answer : 'Public local fixture response.' };
else if ( method === 'agentConnection' ) data = { address : 'http://127.0.0.1:0' , credentialPath : 'Public showcase · no pairing credential' };
return { ok : true , data };
});
const delay = milliseconds => new Promise ( resolve => setTimeout ( resolve , milliseconds ));
async function execute ( source ) { return window . webContents . executeJavaScript ( source , true ); }
async function reloadShell () {
const loaded = new Promise (( resolve , reject ) => {
const timer = setTimeout (() => reject ( Error ( 'Showcase shell reload timed out' )), 5000 );
window . webContents . once ( 'did-finish-load' , () => { clearTimeout ( timer ); resolve (); });
});
window . webContents . reload (); await loaded ; await delay ( 100 );
}
async function waitFor ( source , expected ) {
for ( let attempt = 0 ; attempt < 40 ; attempt ++ ) { if ( await execute ( source ) === expected ) return ; await delay ( 70 ); }
assert . equal ( await execute ( source ), expected );
}
async function click ( id ) { await execute ( `document.getElementById( ${ JSON . stringify ( id ) } ).click()` ); await delay ( 100 ); }
async function appearance ( theme , mode ) {
await execute ( `for(const [id,value] of ${ JSON . stringify ([[ 'theme-mode' , theme ], [ 'tab-mode' , mode ]]) } ){const element=document.getElementById(id);element.value=value;element.dispatchEvent(new Event('change',{bubbles:true}));}` ); await delay ( 100 );
}
async function accent ( value ) {
await execute ( `(()=>{const selector=document.getElementById('accent-mode');selector.value= ${ JSON . stringify ( value ) } ;selector.dispatchEvent(new Event('change',{bubbles:true}));})()` ); await delay ( 90 );
}
async function shot ( name ) {
console . log ( 'Showcase capture: ' + name );
await execute ( 'document.fonts.ready' ); await delay ( 90 );
2026-10-13 16:58:15 +02:00
const measurements = await execute ( `(()=>{const rect=id=>{const element=document.getElementById(id),r=element.getBoundingClientRect();return{hidden:element.hidden,x:r.x,y:r.y,width:r.width,height:r.height}};const brokenImages=[...document.images].filter(image=>image.hasAttribute('src')&&(!image.complete||!image.naturalWidth)).map(image=>image.getAttribute('src'));return{viewport:{width:innerWidth,height:innerHeight,zoom:devicePixelRatio},theme:document.documentElement.dataset.theme,effectiveTheme:document.documentElement.dataset.effectiveTheme,accent:document.documentElement.dataset.accent,tabMode:document.documentElement.dataset.tabMode,chrome:rect('chrome'),toolbar:document.querySelector('.toolbar').getBoundingClientRect().height,library:rect('library'),ai:rect('ai'),vault:rect('vault'),startPage:rect('start-page'),overview:rect('overview'),tools:rect('tools-menu'),scrollWidth:document.documentElement.scrollWidth,brokenImages,error:document.getElementById('error').textContent,passwordFieldsEmpty:[...document.querySelectorAll('input[type=password]')].every(input=>!input.value)}})()` );
2026-10-13 10:33:31 +02:00
assert . equal ( measurements . toolbar , 64 ); assert . ok ([ 64 , 104 ]. includes ( measurements . chrome . height ));
assert . equal ( measurements . scrollWidth , measurements . viewport . width ); assert . deepEqual ( measurements . brokenImages , []); assert . equal ( measurements . passwordFieldsEmpty , true ); assert . equal ( measurements . error , '' );
if ( ! measurements . library . hidden ) assert . equal ( measurements . library . width , 240 );
for ( const panel of [ 'ai' , 'vault' ]) if ( ! measurements [ panel ]. hidden ) assert . equal ( measurements [ panel ]. width , 360 );
// This Windows host's native capturePage can return an earlier compositor frame.
// Exercise the existing trusted screenshot-only CDP fallback for finite fixtures.
const capturePage = window . webContents . capturePage ;
let png ;
try { window . webContents . capturePage = async () => { throw Error ( 'Use finite showcase compositor fallback' ); }; png = await capturePNG ( window . webContents ); }
finally { window . webContents . capturePage = capturePage ; }
fs . writeFileSync ( path . join ( output , name + '.png' ), png ); screenshots . push ({ name , ... measurements });
}
app . whenReady (). then ( async () => {
window = new BrowserWindow ({ width : 1320 , height : 900 , useContentSize : true , frame : false , show : true , backgroundColor : '#f7f8fa' , webPreferences : { preload , contextIsolation : true , sandbox : true , nodeIntegration : false , zoomFactor : 1 } });
window . webContents . setZoomFactor ( 1 );
window . webContents . on ( 'console-message' , event => { if ( event . level === 'error' || event . level >= 3 ) consoleErrors . push ( event . message ); });
window . webContents . on ( 'will-navigate' , event => event . preventDefault ());
window . webContents . setWindowOpenHandler (() => ({ action : 'deny' }));
await window . loadFile ( path . join ( root , 'apps/browser/index.html' ));
await execute ( `localStorage.clear();localStorage.setItem('sbrowser.bookmarks', ${ JSON . stringify ( JSON . stringify ( fixtures )) } );localStorage.setItem('sbrowser.theme','"light"');localStorage.setItem('sbrowser.tab-mode','"separate"');localStorage.setItem('sbrowser.accent','{"toString":0}');` );
await reloadShell ();
assert . equal ( await execute ( `document.documentElement.dataset.accent` ), 'blue' );
await appearance ( 'auto' , 'separate' ); nativeTheme . themeSource = 'dark' ;
await waitFor ( `document.documentElement.dataset.effectiveTheme` , 'dark' );
nativeTheme . themeSource = 'light' ;
await waitFor ( `document.documentElement.dataset.effectiveTheme` , 'light' );
await appearance ( 'dark' , 'separate' ); nativeTheme . themeSource = 'light' ; await delay ( 90 );
assert . equal ( await execute ( `document.body.dataset.theme` ), 'dark' );
await accent ( 'teal' ); await reloadShell ();
assert . equal ( await execute ( `document.documentElement.dataset.accent` ), 'teal' );
assert . equal ( await execute ( `document.documentElement.dataset.theme` ), 'dark' );
assert . equal ( await execute ( `document.documentElement.style.getPropertyValue('--accent')` ), '#007d76' );
await accent ( 'blue' ); await appearance ( 'light' , 'separate' );
// Real DOM interactions verify tabs, bookmarks, themes and overlay signaling.
await click ( 'tools-toggle' ); await click ( 'bookmark' );
await execute ( `document.getElementById('bookmark-form').requestSubmit()` );
await waitFor ( `document.getElementById('bookmark-editor').open` , false );
assert . equal ( state . bookmarks . managed . length + state . bookmarks . personal . items . length , 7 );
assert . equal ( await execute ( `JSON.parse(localStorage.getItem('sbrowser.bookmarks')).length` ), 6 , 'User bookmarks must not be mutated through renderer storage' );
2026-10-13 16:58:15 +02:00
await click ( 'new-tab' ); assert . equal ( await execute ( `document.getElementById('library').hidden` ), true , 'Sidebar starts collapsed' );
2026-10-13 10:33:31 +02:00
assert . equal ( await execute ( `document.querySelectorAll('#favorites-grid .favorite-card').length` ), 7 );
2026-10-13 16:58:15 +02:00
await click ( 'sidebar-toggle' ); await execute ( `document.getElementById('group-name').value='Work'` ); await click ( 'group-add' ); assert . equal ( state . tabs . find ( t => t . id === state . activeTabId ). group , 'Work' ); await execute ( `document.getElementById('group-name').value='Research';document.querySelector('[aria-label="Rename group Work"]').click()` ); await waitFor ( `document.querySelector('[aria-label="Ungroup tabs in Research"]')!==null` , true ); assert . equal ( state . tabs . find ( t => t . id === state . activeTabId ). group , 'Research' ); await execute ( `document.querySelector('[aria-label="Ungroup tabs in Research"]').click()` ); await waitFor ( `document.querySelector('[aria-label="Ungroup tabs in Research"]')===null` , true ); assert . equal ( state . tabs . find ( t => t . id === state . activeTabId ). group , '' ); await click ( 'sidebar-toggle' );
const aligned = await execute ( `(()=>{const cards=[...document.querySelectorAll('#favorites-grid .favorite-card')];const first=cards[0].getBoundingClientRect().top;return cards.filter(c=>c.getBoundingClientRect().top===first).map(c=>c.querySelector('.favorite-icon').getBoundingClientRect().top)})()` ); assert . ok ( aligned . length > 1 ); assert . ok ( aligned . every ( top => Math . abs ( top - aligned [ 0 ]) < 1 ), 'Managed and personal favorites share icon baseline' );
await click ( 'favorites-manage' ); assert . equal ( await execute ( `document.getElementById('bookmark-manager').open` ), true ); assert . equal ( await execute ( `document.querySelectorAll('#manager-list .manager-row').length` ), 7 ); assert . equal ( await execute ( `document.querySelector('#manager-list [data-bookmark-id="showcase-company"] .manager-row-actions')===null` ), true ); assert . equal ( await execute ( `document.getElementById('manager-ai').disabled` ), true ); await execute ( `document.getElementById('bookmark-search').value='github';document.getElementById('bookmark-search').dispatchEvent(new Event('input'))` ); assert . equal ( await execute ( `document.querySelectorAll('#manager-list .manager-row').length` ), 1 ); await shot ( 'sbrowser-bookmark-manager' ); state . libraryAI = { suggestionsEnabled : true , autoApplyEnabled : false , busy : false }; window . webContents . send ( 'showcase:state' , snapshot ()); await delay ( 100 ); await click ( 'manager-ai' ); assert . equal ( await execute ( `document.getElementById('manager-proposal').hidden` ), false ); assert . match ( await execute ( `document.getElementById('manager-diff').textContent` ), /GitHub.*Engineering/ ); assert . equal ( state . bookmarks . personal . items . find ( item => item . id === 'showcase-1' ). folder , 'Work' , 'Preview has no side effects' ); await click ( 'manager-apply' ); assert . equal ( state . bookmarks . personal . items . find ( item => item . id === 'showcase-1' ). folder , 'Engineering' ); await click ( 'bookmark-manager-close' );
2026-10-13 10:33:31 +02:00
await shot ( 'sbrowser-light' );
2026-10-13 16:58:15 +02:00
state . brand = { name : 'Northstar Company' , color : '#166b68' , logoDataUrl : 'data:image/png;base64,' + fs . readFileSync ( path . join ( root , 'docs/brand/safetynet-icon.png' )). toString ( 'base64' )};
state . startPage = { title : 'Welcome to Northstar.' , subtitle : 'Your company workspace, news and favorite tools.' , newsEnabled : true , news : [{ id : 'news-1' , title : 'Company update: our new intranet is here' , url : 'https://intranet.example.test/news' , publishedAt : '2026-10-13T12:00:00Z' },{ id : 'news-2' , title : 'Security week: join the local AI workshop' , url : 'https://intranet.example.test/events' }]};
window . webContents . send ( 'showcase:state' , snapshot ()); await delay ( 150 );
assert . equal ( await execute ( `document.getElementById('start-brand-name').textContent` ), 'Northstar Company' );
assert . equal ( await execute ( `document.querySelectorAll('#company-news-items .company-news-card').length` ), 2 );
assert . equal ( await execute ( `document.getElementById('start-company-logo').hidden` ), false );
await shot ( 'sbrowser-company-branding' );
state . startPage = { newsEnabled : false }; state . brand = { name : 'SafetyNet' }; window . webContents . send ( 'showcase:state' , snapshot ()); await delay ( 100 );
assert . equal ( await execute ( `document.getElementById('company-news').hidden` ), true ); assert . equal ( await execute ( `document.getElementById('start-company-logo').hasAttribute('src')` ), false );
2026-10-13 10:33:31 +02:00
await appearance ( 'dark' , 'compact' ); await shot ( 'sbrowser-dark' );
await click ( 'tab-overview' ); assert . equal ( await execute ( `document.querySelectorAll('#overview-grid .overview-card').length` ), 2 ); await shot ( 'sbrowser-overview' );
await click ( 'overview-close' ); await appearance ( 'light' , 'separate' ); await click ( 'tools-toggle' ); await shot ( 'sbrowser-settings' );
await appearance ( 'dark' , 'separate' ); await accent ( 'violet' ); await shot ( 'sbrowser-theme-violet' );
await appearance ( 'light' , 'separate' ); await accent ( 'blue' );
await click ( 'passwords' ); await shot ( 'sbrowser-vault' ); await click ( 'vault-close' );
await appearance ( 'light' , 'compact' ); window . setContentSize ( 800 , 650 ); await delay ( 150 ); await shot ( 'sbrowser-compact' );
2026-10-13 16:58:15 +02:00
await click ( 'tools-toggle' ); await click ( 'ai-toggle' ); await shot ( 'sbrowser-compact-panels' ); await click ( 'ai-close' ); window . setContentSize ( 480 , 740 ); await delay ( 120 ); await shot ( 'sbrowser-narrow' ); window . setContentSize ( 800 , 650 );
2026-10-13 10:33:31 +02:00
await execute ( `document.getElementById('address').value='https://example.test/slow';document.getElementById('address-form').requestSubmit();` );
await click ( 'new-tab' ); await delay ( 300 );
assert . equal ( await execute ( `document.querySelector('.browser-tab.active .tab-label').textContent` ), 'New Tab' );
assert . equal ( await execute ( `document.getElementById('address').value` ), '' );
assert . equal ( await execute ( `document.getElementById('start-page').hidden` ), false );
const latestLayout = records . findLast ( record => record . method === 'setChromeLayout' ); assert . ok ( latestLayout . startPageOpen && latestLayout . nativeHidden );
for ( const overlay of [ 'startPageOpen' , 'overviewOpen' , 'toolsOpen' ]) assert . ok ( records . some ( record => record . method === 'setChromeLayout' && record [ overlay ] && record . nativeHidden ));
// Policy updates override stale or hostile renderer preferences immediately.
state = {... state , settings : {... state . settings , lockedSections : [ 'appearance' , 'ai' , 'passwords' , 'documentScan' , 'agents' ], appearance : { mode : 'dark' , accent : 'violet' , tabMode : 'compact' }}};
window . webContents . send ( 'showcase:state' , state ); await delay ( 120 );
await execute ( `localStorage.setItem('sbrowser.theme','"light"');localStorage.setItem('sbrowser.accent','"blue"');window.dispatchEvent(new StorageEvent('storage',{key:'sbrowser.theme'}));` );
assert . equal ( await execute ( `document.body.dataset.theme` ), 'dark' );
assert . equal ( await execute ( `document.documentElement.dataset.accent` ), 'violet' );
assert . equal ( await execute ( `['theme-mode','accent-mode','tab-mode','ai-toggle','passwords','inspect','agents'].every(id=>document.getElementById(id).disabled)` ), true );
assert . equal (( await execute ( `window.safetynet.updateSettings({mode:'light',accent:'blue',tabMode:'separate'})` )). ok , false );
state = {... state , settings : {... state . settings , lockedSections : [], hiddenSections : [ 'appearance' , 'ai' , 'passwords' , 'documentScan' , 'agents' ], visible : true }};
window . webContents . send ( 'showcase:state' , state ); await delay ( 100 );
assert . equal ( await execute ( `['appearance-section','ai-toggle','sidebar','passwords','inspect','agents'].every(id=>document.getElementById(id).hidden)` ), true );
state = {... state , settings : {... state . settings , visible : false }}; window . webContents . send ( 'showcase:state' , state ); await delay ( 100 );
assert . equal ( await execute ( `document.getElementById('tools-toggle').hidden&&document.getElementById('tools-menu').hidden` ), true );
state = {... state , settings : {... state . settings , hiddenSections : [ 'appearance' ]}}; window . webContents . send ( 'showcase:state' , state ); await delay ( 100 );
assert . equal ( await execute ( `!document.getElementById('quick-ai').hidden&&!document.getElementById('quick-ai').disabled&&document.getElementById('appearance-section').hidden` ), true );
await click ( 'quick-ai' ); assert . equal ( await execute ( `document.getElementById('ai').hidden` ), false ); await click ( 'ai-close' );
await reloadShell (); assert . equal ( await execute ( `document.getElementById('tools-toggle').hidden&&document.body.dataset.theme==='dark'` ), true );
await execute ( `document.getElementById('error').textContent=''` );
for ( const control of [ 'window-minimize' , 'window-maximize' , 'window-close' ]) await click ( control );
assert . equal ( records . filter ( record => record . method === 'windowControls' ). length , 3 );
fs . writeFileSync ( path . join ( output , 'sbrowser-ui-layout.json' ), JSON . stringify ({ fixtureOnly : true , managedSettingsProof : { liveLocks : true , hiddenSections : true , globalHidden : true , localStorageCannotOverride : true , reloadPreservesPolicy : true , directUpdateRejected : true , globalHiddenPreservesApprovedFeatures : true }, screenshots , records , consoleErrors }, null , 2 ));
assert . deepEqual ( consoleErrors , []);
2026-10-13 16:58:15 +02:00
finished = true ; clearTimeout ( watchdog ); console . log ( 'UI showcase passed: ten screenshots, live system themes, persisted accents, real DOM interactions and delayed-navigation regression.' );
2026-10-13 10:33:31 +02:00
if ( window . webContents . debugger . isAttached ()) window . webContents . debugger . detach ();
window . destroy (); app . exit ( 0 );
}). catch ( error => { clearTimeout ( watchdog ); console . error ( error . message ); if ( ! finished ) app . exit ( 1 ); });
}