test(app): align restore coverage and await fixture readiness

Workspace restore leaves agents archived until explicit Unarchive. Keep that distinction covered for multiple agents. Compile the recovery fixture before its interaction test starts, and wait for sidebar agent hydration before asserting tab presence.
This commit is contained in:
Mohamed Boudra
2026-09-11 23:45:33 +02:00
parent 35af7eda92
commit df452a09e7
3 changed files with 17 additions and 4 deletions
@@ -4,9 +4,15 @@ import { expect, test, type Page, type TestInfo } from "@playwright/test";
let server: Server;
let appUrl: string;
test.beforeAll(async () => {
// Compile this separate fixture entrypoint in setup, like the main app warmup.
// Cold Metro compilation must not consume the recovery interaction's timeout.
test.setTimeout(120_000);
const metroPort = process.env.E2E_METRO_PORT;
if (!metroPort) throw new Error("E2E_METRO_PORT is required");
const bundle = `http://localhost:${metroPort}/packages/app/e2e/support/recovery-app.bundle?platform=web&dev=true&minify=false&hot=false&lazy=true&transform.engine=hermes&transform.routerRoot=src%2Fapp`;
const compiled = await fetch(bundle, { signal: AbortSignal.timeout(120_000) });
if (!compiled.ok) throw new Error(`Recovery fixture compilation failed: HTTP ${compiled.status}`);
await compiled.arrayBuffer();
server = createServer((_request, response) => {
response.setHeader("Content-Type", "text/html");
response.end(
@@ -7,7 +7,6 @@ import { getServerId } from "../support/helpers/server-id";
import { projectEquivalenceViewKey } from "../support/helpers/project-view-key";
import { selectSidebarStatusGrouping } from "../support/helpers/sidebar";
import { waitForSidebarHydration } from "../support/helpers/workspace-ui";
import { getVisibleWorkspaceAgentTabIds } from "../support/helpers/workspace-tabs";
// Model B sidebar shape: every project — git or non-git, single- or
// multi-workspace — renders as the same expandable parent, the deepest sidebar
@@ -96,8 +95,9 @@ test.describe("Model B sidebar shape", () => {
try {
// Open the workspace and materialize both an agent tab and a terminal tab.
await gotoWorkspace(page, mock.workspaceId);
const agentTabs = await getVisibleWorkspaceAgentTabIds(page);
expect(agentTabs).toContain(`workspace-tab-agent_${mock.agentId}`);
await expect(
page.getByTestId(`workspace-tab-agent_${mock.agentId}`).filter({ visible: true }),
).toBeVisible();
await clickNewTerminal(page);
await expect(
@@ -265,7 +265,7 @@ test.describe("Worktree restore", () => {
).toHaveText(switchedBranch, { timeout: 30_000 });
});
test("recovers the selected agent with its workspace and later rescues another archived agent", async ({
test("restores the workspace before explicitly unarchiving each selected agent", async ({
page,
}) => {
const { agents, worktree } = await createArchivedMissingWorktree("restore-agents", {
@@ -292,9 +292,16 @@ test.describe("Worktree restore", () => {
await expect(
page.getByTestId(`workspace-tab-agent_${firstAgent.id}`).filter({ visible: true }).first(),
).toBeVisible({ timeout: 30_000 });
await expect(
page.getByTestId(`workspace-tab-agent_${firstAgent.id}`).filter({ visible: true }).first(),
).toHaveAttribute("aria-selected", "true");
expect(await fetchAgentArchivedAt(client, firstAgent.id)).not.toBeNull();
expect(await fetchAgentArchivedAt(client, secondAgent.id)).not.toBeNull();
await page.getByRole("button", { name: "Unarchive", exact: true }).click();
await expect
.poll(() => fetchAgentArchivedAt(client, firstAgent.id), { timeout: 30_000 })
.toBeNull();
expect(await fetchAgentArchivedAt(client, secondAgent.id)).not.toBeNull();
await expect(page.getByRole("button", { name: "Unarchive" })).toHaveCount(0, {
timeout: 30_000,
});