1010// reconnecting through dropped/duplicated/stale events and a mid-stream reload
1111// with a second conversation queued, every Settings page, the cloud consent dialog
1212// (always cancelled: no vendor turn ever runs), Stop, light/dark/compact
13- // layouts with axe checks, and process cleanup after quit.
13+ // layouts with axe checks, process cleanup after quit, and a relaunch: the
14+ // window opens at the same fitted size every time, whatever size it was
15+ // left at or a grown 1.0.0 window state says.
1416//
1517// Requirements: a display (xvfb-run), DBus, tauri-driver, WebKitWebDriver.
1618// node scripts/run-native-x11.mjs node scripts/test-native-desktop.mjs
@@ -156,6 +158,15 @@ if (displayBackend === "x11") {
156158 delete nativeEnv . DISPLAY ; // No XWayland fallback can satisfy this run.
157159 displayEvidence . dmabufOverride = nativeEnv . WEBKIT_DISABLE_DMABUF_RENDERER || null ;
158160}
161+ // The window-state plugin's file as a 1.0.0 window that grew on Wayland left
162+ // it (1536x1170 after a few launches), placed low on the screen. The app
163+ // launches without --profile, so the plugin is active: it must not restore
164+ // that size, and on X11 the window comes back at that place, moved up and
165+ // left until it fits the screen.
166+ const windowStateFile = path . join ( profile , "config/com.shadowfetch.shadowcode/.window-state.json" ) ;
167+ const GROWN = { width : 1536 , height : 1170 , x : 500 , y : 500 , prev_x : 500 , prev_y : 500 , maximized : false , visible : true , decorated : true , fullscreen : false } ;
168+ await mkdir ( path . dirname ( windowStateFile ) , { recursive : true } ) ;
169+ await writeFile ( windowStateFile , JSON . stringify ( { main : GROWN } ) ) ;
159170const launches = ( ) => readFile ( path . join ( runtimeDir , "launches.jsonl" ) , "utf8" ) . then ( ( t ) => t . trim ( ) . split ( "\n" ) . filter ( Boolean ) . map ( JSON . parse ) , ( ) => [ ] ) ;
160171const modelRequests = ( ) => readFile ( path . join ( runtimeDir , "requests.jsonl" ) , "utf8" ) . then ( ( t ) => t . trim ( ) . split ( "\n" ) . filter ( Boolean ) . map ( JSON . parse ) , ( ) => [ ] ) ;
161172
@@ -308,6 +319,41 @@ async function setWindow({ width, height }) {
308319 await delay ( 300 ) ;
309320 return true ;
310321}
322+ /** The window as the page sees it: its inner size, its place and its screen. */
323+ const windowGeometry = ( ) => execute ( "return {inner:[innerWidth,innerHeight],position:[screenX,screenY],screen:{width:screen.width,height:screen.height,availWidth:screen.availWidth,availHeight:screen.availHeight}}" ) ;
324+ // The private runners have one screen: Xvfb, or the private Weston.
325+ const ONE_SCREEN = displayBackend === "x11" || process . env . XDG_CURRENT_DESKTOP === "ShadowCodePrivateWeston" ;
326+ /** The first size window_size::fit gives on one screen: 1380x920 at most,
327+ * 90% of the screen and its work area, less a 48 px title bar on X11 (on
328+ * Wayland the header bar is inside the size), never below 520x640. */
329+ function fittedSize ( screen ) {
330+ const room = ( full , work ) => Math . min ( full * 0.9 , work > 0 ? work : Infinity ) ;
331+ const titleBar = displayBackend === "x11" ? 48 : 0 ;
332+ return {
333+ width : Math . max ( 520 , Math . min ( 1380 , Math . floor ( room ( screen . width , screen . availWidth ) ) ) ) ,
334+ height : Math . max ( 640 , Math . min ( 920 , Math . floor ( room ( screen . height , screen . availHeight ) - titleBar ) ) ) ,
335+ } ;
336+ }
337+ /** The window opened at its fitted size, not a remembered one. */
338+ async function expectFittedWindow ( label ) {
339+ const opened = await until ( `${ label } : window shown` , async ( ) => { const g = await windowGeometry ( ) ; return g . inner [ 0 ] > 0 && g . inner [ 1 ] > 0 && g ; } ) ;
340+ const fitted = fittedSize ( opened . screen ) ;
341+ const [ width , height ] = opened . inner ;
342+ assert . ok ( width < GROWN . width && height < GROWN . height , `${ label } : the saved ${ GROWN . width } x${ GROWN . height } size is not restored (${ width } x${ height } )` ) ;
343+ assert . ok ( width <= fitted . width && height <= fitted . height , `${ label } : ${ width } x${ height } fits the screen (${ fitted . width } x${ fitted . height } )` ) ;
344+ if ( ONE_SCREEN ) {
345+ assert . equal ( width , fitted . width , `${ label } : fitted width` ) ;
346+ // On Wayland the GTK header bar is part of the fitted height.
347+ if ( displayBackend === "x11" ) assert . equal ( height , fitted . height , `${ label } : fitted height` ) ;
348+ else assert . ok ( fitted . height - height > 0 && fitted . height - height < 80 , `${ label } : fitted height ${ fitted . height } with the header bar (content ${ height } )` ) ;
349+ }
350+ if ( displayBackend === "x11" ) {
351+ // Back at the saved place, moved inside the screen (Xvfb has no frame).
352+ const inside = [ Math . max ( 0 , Math . min ( GROWN . x , opened . screen . availWidth - width ) ) , Math . max ( 0 , Math . min ( GROWN . y , opened . screen . availHeight - height - 48 ) ) ] ;
353+ assert . deepEqual ( opened . position , inside , `${ label } : the saved place, inside the screen` ) ;
354+ }
355+ return opened ;
356+ }
311357/** Brightness of the page background (0 black … 1 white), as rendered. */
312358const pageLuminance = ( ) => execute ( "const [r,g,b]=getComputedStyle(document.body).backgroundColor.match(/[\\d.]+/g).map(Number);return (0.2126*r+0.7152*g+0.0722*b)/255" ) ;
313359/** The page really shows `theme` (its computed background), not just the
@@ -417,6 +463,9 @@ try {
417463 const created = await wd ( "POST" , "/session" , { capabilities : { alwaysMatch : { "tauri:options" : { application : binary , args : binaryArgs } } } } ) ;
418464 session = created . sessionId ;
419465 await wd ( "POST" , `/session/${ session } /timeouts` , { script : 60000 , implicit : 0 , pageLoad : 30000 } ) ;
466+ const firstWindow = await expectFittedWindow ( "First launch" ) ;
467+ displayEvidence . firstWindow = firstWindow ;
468+ note ( `window opens at its fitted size ${ firstWindow . inner . join ( "x" ) } on a ${ firstWindow . screen . width } x${ firstWindow . screen . height } screen, not the saved ${ GROWN . width } x${ GROWN . height } ` ) ;
420469 if ( displayBackend === "wayland" ) {
421470 const screen = await execute ( "return {width:screen.availWidth,height:screen.availHeight,scale:devicePixelRatio}" ) ;
422471 assert . ok ( screen . width > 0 && screen . height > 0 , "Physical display dimensions available" ) ;
@@ -868,6 +917,7 @@ try {
868917 }
869918
870919 // ------------------------------------------------------------ quit
920+ const leftAt = ( await windowGeometry ( ) ) . inner ;
871921 const children = await descendants ( appPid ) ;
872922 const serverPids = ( await launches ( ) ) . map ( ( l ) => l . pid ) ;
873923 const described = await Promise . all ( children . map ( async ( pid ) => `${ pid } ${ await commandLine ( pid ) } ` ) ) ;
@@ -880,6 +930,30 @@ try {
880930 note ( `quit: app and ${ children . length } child processes (${ serverPids . length } llama-server launches) exited` ) ;
881931 await wd ( "DELETE" , `/session/${ session } ` ) . catch ( ( ) => { } ) ; session = undefined ;
882932
933+ // ------------------------------------------------------------ relaunch
934+ // The window was left at another size. The window-state plugin saved where
935+ // it was and whether it was maximized, not its size (it keeps the one it
936+ // read), and the second launch opens exactly as the first did.
937+ const saved = JSON . parse ( await readFile ( windowStateFile , "utf8" ) ) . main ;
938+ assert . deepEqual ( [ saved . width , saved . height ] , [ GROWN . width , GROWN . height ] , "No window size saved" ) ;
939+ assert . equal ( saved . maximized , false ) ;
940+ if ( displayBackend === "x11" ) assert . deepEqual ( [ saved . x , saved . y ] , firstWindow . position , "The window's place saved" ) ;
941+ const relaunched = await wd ( "POST" , "/session" , { capabilities : { alwaysMatch : { "tauri:options" : { application : binary , args : binaryArgs } } } } ) ;
942+ session = relaunched . sessionId ;
943+ await wd ( "POST" , `/session/${ session } /timeouts` , { script : 60000 , implicit : 0 , pageLoad : 30000 } ) ;
944+ const secondWindow = await expectFittedWindow ( "Relaunch" ) ;
945+ assert . deepEqual ( secondWindow . inner , firstWindow . inner , `The relaunched window has the first launch's size (it was left at ${ leftAt . join ( "x" ) } )` ) ;
946+ if ( displayBackend === "x11" ) assert . deepEqual ( secondWindow . position , firstWindow . position , "The relaunched window is back at its place" ) ;
947+ appPid = ( await api ( "GET" , "/api/version" ) ) . pid ;
948+ await until ( "Relaunched workspace ready" , ( ) => execute ( "const t=document.querySelector(arguments[0]);return !!t && !t.disabled && !document.querySelector('.wizard')" , [ composer ] ) , 30000 ) ;
949+ const relaunchChildren = await descendants ( appPid ) ;
950+ await execute ( "setTimeout(()=>window.__TAURI_INTERNALS__.invoke('desktop_quit'),30);return true;" ) ;
951+ await until ( "Relaunched app exited" , ( ) => dead ( appPid ) , 30000 ) ;
952+ for ( const pid of relaunchChildren ) await until ( `Child ${ pid } exited (${ await commandLine ( pid ) } )` , ( ) => dead ( pid ) , 15000 ) ;
953+ assert . equal ( ( await run ( "pgrep" , [ "-f" , runtimeDir ] ) . catch ( ( ) => ( { stdout : "" } ) ) ) . stdout . trim ( ) , "" , "No llama-server test double remains" ) ;
954+ await wd ( "DELETE" , `/session/${ session } ` ) . catch ( ( ) => { } ) ; session = undefined ;
955+ note ( `relaunch: the window opens at ${ secondWindow . inner . join ( "x" ) } again after it was left at ${ leftAt . join ( "x" ) } ; no size saved${ displayBackend === "x11" ? ", same place" : "" } ` ) ;
956+
883957 finalReport = { passed : displayEvidence . geometryFailures . length === 0 , version : version . version , vendors : vendorSummary , checks, axe : axeFindings , screenshots : shots } ;
884958} catch ( error ) {
885959 finalReport = { passed : false , checks, failure : error . stack || String ( error ) } ;
0 commit comments