diff --git a/packages/app/e2e/00-sessions-empty.spec.ts b/packages/app/e2e/00-sessions-empty.spec.ts new file mode 100644 index 000000000..d08e6d55b --- /dev/null +++ b/packages/app/e2e/00-sessions-empty.spec.ts @@ -0,0 +1,37 @@ +// "00-" prefix is intentional: this file must sort before every other spec. +// Sessions history is daemon-global — any agent created by a prior spec hides the empty state. +// If the beforeAll probe below fails, a spec sorted before this file is creating agents. +import { test } from "./fixtures"; +import { + connectArchiveTabDaemonClient, + expectSessionsEmptyState, + openSessions, +} from "./helpers/archive-tab"; + +test.describe("Sessions screen empty state", () => { + test.beforeAll(async () => { + const client = await connectArchiveTabDaemonClient(); + try { + const history = await client.fetchAgentHistory({ page: { limit: 1 } }); + if (history.entries.length > 0) { + throw new Error( + `Sessions empty-state precondition failed: daemon already has ${history.entries.length} agent(s). ` + + `Either a spec that sorts before 00-sessions-empty.spec.ts created agents, ` + + `or the daemon has stale history from a previous run.`, + ); + } + } finally { + await client.close().catch(() => undefined); + } + }); + + test("shows empty placeholder when there is no session history", async ({ + page, + withWorkspace, + }) => { + const workspace = await withWorkspace({ prefix: "sessions-empty-" }); + await workspace.navigateTo(); + await openSessions(page); + await expectSessionsEmptyState(page); + }); +}); diff --git a/packages/app/e2e/helpers/archive-tab.ts b/packages/app/e2e/helpers/archive-tab.ts index b3f4de8bc..3c901b2d1 100644 --- a/packages/app/e2e/helpers/archive-tab.ts +++ b/packages/app/e2e/helpers/archive-tab.ts @@ -36,6 +36,9 @@ interface ArchiveTabDaemonClient { predicate: (snapshot: { status: string }) => boolean, timeout?: number, ): Promise<{ status: string }>; + fetchAgentHistory(options?: { + page?: { limit: number }; + }): Promise<{ entries: Array<{ id: string }> }>; } function getDaemonPort(): string { @@ -265,8 +268,10 @@ export async function openSessions(page: Page): Promise { }); } +const AGENT_ROW_SELECTOR = '[data-testid^="agent-row-"]'; + function getSessionRowByTitle(page: Page, title: string) { - return page.locator('[data-testid^="agent-row-"]').filter({ hasText: title }).first(); + return page.locator(AGENT_ROW_SELECTOR).filter({ hasText: title }).first(); } export async function expectSessionRowVisible(page: Page, title: string): Promise { @@ -283,6 +288,12 @@ export async function clickSessionRow(page: Page, title: string): Promise await row.click(); } +export async function expectSessionsEmptyState(page: Page): Promise { + // Guard: if session rows appear, a prior spec polluted the shared daemon — see 00-sessions-empty.spec.ts. + await expect(page.locator(AGENT_ROW_SELECTOR)).toHaveCount(0, { timeout: 5_000 }); + await expect(page.getByText("No sessions yet")).toBeVisible({ timeout: 30_000 }); +} + export async function archiveAgentFromSessions( page: Page, input: { agentId: string; title: string },