Skip to content

Commit 2adb8e6

Browse files
committed
test(ui): add A4 test for closing the last dock restoring welcome
Spec MCP-TEST-PLAN v2 §2.A A4: from the A3 state, close fixture A's dock and gate on dockCount reaching 0 within 5s, the welcome page becoming visible again with the "Welcome back" greeting (fixture A stays installed, only unloaded), and backend.currentVisibleApp returning to "". The dock is closed via the inspector's evaluate path (closeDock("test_qml_only") on the WorkspaceArea object) rather than callMethod, which does not marshal the QString argument correctly. The test establishes its own precondition — if no dock is open it re-opens fixture A first, and skips (per spec §0.A) when the fixture is absent outside --ci mode.
1 parent 9796b57 commit 2adb8e6

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

tests/ui-tests.mjs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,133 @@ test("workspace: opening an app replaces the welcome page with a dock", async (a
256256
// (workspace.closeDock), and no later test asserts welcome-page state.
257257
});
258258

259+
// --- Workspace (A4) — closing the last dock brings the welcome page back ---
260+
//
261+
// Spec §2.A A4: from A3 state, close fixture A's dock. Closing the last dock
262+
// also unloads the module by design (WorkspaceArea::pluginClosed →
263+
// unloadUiModule), so the gates double as a regression guard for the
264+
// currentVisibleApp clear on unload of the visible app.
265+
//
266+
// closeDock is invoked through the inspector's evaluate, NOT callMethod:
267+
// callMethod does not marshal the QString argument correctly (logos-qt-mcp
268+
// limitation), while the evaluate path's JS engine converts it fine.
269+
270+
test("workspace: closing the last dock brings the welcome page back", async (app) => {
271+
// Same stable evaluate anchor as A3 — has `backend` in context and
272+
// survives the dock teardown.
273+
let welcome = null;
274+
await app.waitFor(async () => {
275+
welcome = await findWelcomePage(app);
276+
if (!welcome) throw new Error("no WelcomePage instance in the QML tree");
277+
}, { timeout: 10000, interval: 500, description: "WelcomePage instance to exist" });
278+
279+
const workspace = await findByObjectName(app.inspector, "workspace");
280+
if (!workspace) {
281+
throw new Error('WorkspaceArea (objectName "workspace") not found');
282+
}
283+
284+
// Establish the A3 end state without assuming A3 left it: fixture A's
285+
// dock must be open before we can close it.
286+
const preCount = await app.inspector.send("evaluate", {
287+
objectId: workspace.id, expression: "dockCount",
288+
});
289+
if (preCount.error) throw new Error(`evaluate(dockCount) failed: ${preCount.error}`);
290+
if (preCount.result !== 1) {
291+
let tile = null;
292+
try {
293+
await app.waitFor(async () => {
294+
tile = await findByObjectName(app.inspector, `sidebar.app.${FIXTURE_A.name}`);
295+
if (!tile) throw new Error(`sidebar.app.${FIXTURE_A.name} not in the tree`);
296+
}, { timeout: 10000, interval: 500, description: "fixture A sidebar tile to appear" });
297+
} catch (e) {
298+
if (!CI_MODE) {
299+
console.log(
300+
` SKIP: fixture A (${FIXTURE_A.name}) is not installed in this ` +
301+
`app instance (spec §0.A: skip, not fail, outside --ci)`);
302+
return;
303+
}
304+
throw new Error(
305+
`no dock open and fixture A sidebar tile never appeared — ` +
306+
`integration-test pre-seeds ${FIXTURE_A.name} at boot, so this is ` +
307+
`a real failure: ${e.message}`);
308+
}
309+
const clicked = await app.inspector.send("callMethod", {
310+
objectId: tile.id, method: "clicked",
311+
});
312+
if (clicked.error) {
313+
throw new Error(`clicking sidebar.app.${FIXTURE_A.name} failed: ${clicked.error}`);
314+
}
315+
}
316+
await app.waitFor(async () => {
317+
const count = await app.inspector.send("evaluate", {
318+
objectId: workspace.id, expression: "dockCount",
319+
});
320+
if (count.error) throw new Error(`evaluate(dockCount) failed: ${count.error}`);
321+
if (count.result !== 1) {
322+
throw new Error(`WorkspaceArea.dockCount=${count.result} (expected 1)`);
323+
}
324+
const visibleApp = await app.inspector.send("evaluate", {
325+
objectId: welcome.id, expression: "backend.currentVisibleApp",
326+
});
327+
if (visibleApp.error) {
328+
throw new Error(`evaluate(backend.currentVisibleApp) failed: ${visibleApp.error}`);
329+
}
330+
if (visibleApp.result !== FIXTURE_A.name) {
331+
throw new Error(
332+
`backend.currentVisibleApp=${JSON.stringify(visibleApp.result)} ` +
333+
`(expected "${FIXTURE_A.name}")`);
334+
}
335+
}, { timeout: 10000, interval: 500,
336+
description: `fixture A dock to be open and front-most` });
337+
338+
// Close the dock.
339+
const closed = await app.inspector.send("evaluate", {
340+
objectId: workspace.id,
341+
expression: `closeDock(${JSON.stringify(FIXTURE_A.name)})`,
342+
});
343+
if (closed.error) throw new Error(`evaluate(closeDock) failed: ${closed.error}`);
344+
345+
// Gate: dock count reaches 0 within 5 s.
346+
await app.waitFor(async () => {
347+
const res = await app.inspector.send("evaluate", {
348+
objectId: workspace.id, expression: "dockCount",
349+
});
350+
if (res.error) throw new Error(`evaluate(dockCount) failed: ${res.error}`);
351+
if (res.result !== 0) {
352+
throw new Error(`WorkspaceArea.dockCount=${res.result} (expected 0)`);
353+
}
354+
}, { timeout: 5000, interval: 250, description: "workspace dockCount to reach 0" });
355+
356+
// Gate: the welcome page is visible again…
357+
await app.waitFor(async () => {
358+
if ((await welcomePageHidden(app, welcome.id)) !== false) {
359+
throw new Error("welcome page is still hidden after closing the last dock");
360+
}
361+
}, { timeout: 5000, interval: 250, description: "welcome page to reappear" });
362+
363+
// …with the installed-apps greeting — closing unloads fixture A but does
364+
// not uninstall it, so launcherApps stays non-empty and the greeting is
365+
// "Welcome back", not the first-launch text.
366+
await app.waitFor(
367+
async () => { await app.expectTexts(["Welcome back"]); },
368+
{ timeout: 5000, interval: 250, description: '"Welcome back" greeting to render' }
369+
);
370+
371+
// Gate: the backend no longer reports a front-most app.
372+
await app.waitFor(async () => {
373+
const res = await app.inspector.send("evaluate", {
374+
objectId: welcome.id, expression: "backend.currentVisibleApp",
375+
});
376+
if (res.error) {
377+
throw new Error(`evaluate(backend.currentVisibleApp) failed: ${res.error}`);
378+
}
379+
if (res.result !== "") {
380+
throw new Error(
381+
`backend.currentVisibleApp=${JSON.stringify(res.result)} (expected "")`);
382+
}
383+
}, { timeout: 5000, interval: 250, description: "currentVisibleApp to clear" });
384+
});
385+
259386
// --- Package Manager ---
260387
//
261388
// PMUI is no longer launched from the sidebar app launcher (filtered out

0 commit comments

Comments
 (0)