Skip to content

Commit 2133c8b

Browse files
Test: the window opens at the same fitted size after a relaunch
The window test now starts from the window-state file a grown 1.0.0 window left (1536x1170, placed low), checks the first window has its fitted size (and on X11 the saved place, moved inside the screen), then after the usual quit opens the app again: the window is back at the first launch's size although it was left at 1360x860, and no size was saved. Under Xvfb and the private Weston it passes with 1.0.1 (1296x920 and 1380x873 content) and fails at the first check with a build that restores SIZE again (1536x1170). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6364826 commit 2133c8b

1 file changed

Lines changed: 75 additions & 1 deletion

File tree

‎scripts/test-native-desktop.mjs‎

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
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 }));
159170
const launches = () => readFile(path.join(runtimeDir, "launches.jsonl"), "utf8").then((t) => t.trim().split("\n").filter(Boolean).map(JSON.parse), () => []);
160171
const 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. */
312358
const 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

Comments
 (0)