* fix(ci): skip unrelated required checks cleanly Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable. * fix(ci): gate changes to install patching script * fix(ci): route cross-package test contracts narrowly * fix(ci): keep routed contracts merge-blocking * fix(server): exclude nested test utilities from builds * fix(ci): cover app-owned native and browser domains * refactor(ci): align tests with runtime domains Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check. * fix(ci): keep routing and desktop suites isolated The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs. * fix(ci): isolate desktop e2e runtime * perf(test): isolate Playwright workers * perf(test): parallelize Playwright within specs * fix(test): keep Playwright file scheduling * fix(test): share dynamic Metro fixture * fix(test): preserve own-host restart fixtures * fix(ci): preserve routed contracts after rebase * Stabilize Playwright wheel-scroll assertions
154 lines
5.4 KiB
TypeScript
154 lines
5.4 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { expect } from "@playwright/test";
|
|
import { test } from "../support/fixtures";
|
|
import { getServerId } from "../support/helpers/server-id";
|
|
import { connectSeedClient } from "../support/helpers/seed-client";
|
|
import { createTempGitRepo } from "../support/helpers/workspace";
|
|
import { buildHostWorkspaceRoute } from "@/utils/host-routes";
|
|
import {
|
|
archiveAgentFromDaemon,
|
|
archiveAgentFromSessions,
|
|
clickSessionRow,
|
|
createIdleAgent,
|
|
expectSessionRowArchived,
|
|
expectSessionRowVisible,
|
|
expectWorkspaceArchiveOutcome,
|
|
expectWorkspaceTabHidden,
|
|
fetchAgentArchivedAt,
|
|
openSessions,
|
|
openWorkspaceWithAgents,
|
|
primeAdditionalPage,
|
|
resetSeededPageState,
|
|
reloadWorkspace,
|
|
} from "../support/helpers/archive-tab";
|
|
|
|
test.describe("Archive tab reconciliation", () => {
|
|
let client: Awaited<ReturnType<typeof connectSeedClient>>;
|
|
let tempRepo: { path: string; cleanup: () => Promise<void> };
|
|
let projectId: string;
|
|
let workspaceId: string;
|
|
|
|
test.describe.configure({ timeout: 300_000 });
|
|
|
|
test.beforeAll(async () => {
|
|
tempRepo = await createTempGitRepo("archive-tab-");
|
|
client = await connectSeedClient();
|
|
const created = await client.createWorkspace({
|
|
source: { kind: "directory", path: tempRepo.path },
|
|
});
|
|
if (!created.workspace) {
|
|
throw new Error(created.error ?? `Failed to create workspace ${tempRepo.path}`);
|
|
}
|
|
projectId = created.workspace.projectId;
|
|
workspaceId = created.workspace.id;
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
await client?.removeProject(projectId).catch(() => undefined);
|
|
await client?.close().catch(() => undefined);
|
|
await tempRepo?.cleanup();
|
|
});
|
|
|
|
test("non-UI archive prunes the archived tab across open pages and reload", async ({ page }) => {
|
|
const archived = await createIdleAgent(client, {
|
|
cwd: tempRepo.path,
|
|
workspaceId,
|
|
title: `cli-archive-${randomUUID().slice(0, 8)}`,
|
|
});
|
|
const surviving = await createIdleAgent(client, {
|
|
cwd: tempRepo.path,
|
|
workspaceId,
|
|
title: `cli-control-${randomUUID().slice(0, 8)}`,
|
|
});
|
|
const passivePage = await page.context().newPage();
|
|
|
|
try {
|
|
await primeAdditionalPage(passivePage);
|
|
await resetSeededPageState(page);
|
|
await resetSeededPageState(passivePage);
|
|
await openSessions(page);
|
|
await expectSessionRowVisible(page, archived.title);
|
|
await expectSessionRowVisible(page, surviving.title);
|
|
await openSessions(passivePage);
|
|
await expectSessionRowVisible(passivePage, archived.title);
|
|
await expectSessionRowVisible(passivePage, surviving.title);
|
|
await openWorkspaceWithAgents(page, [archived, surviving]);
|
|
await openWorkspaceWithAgents(passivePage, [archived, surviving]);
|
|
await archiveAgentFromDaemon(client, archived.id);
|
|
await expectWorkspaceArchiveOutcome(page, {
|
|
archivedAgentId: archived.id,
|
|
survivingAgentId: surviving.id,
|
|
});
|
|
await expectWorkspaceArchiveOutcome(passivePage, {
|
|
archivedAgentId: archived.id,
|
|
survivingAgentId: surviving.id,
|
|
});
|
|
await reloadWorkspace(passivePage, surviving.workspaceId);
|
|
await expectWorkspaceTabHidden(passivePage, archived.id);
|
|
} finally {
|
|
await passivePage.close();
|
|
}
|
|
});
|
|
|
|
test("Sessions archive prunes the archived tab across open pages", async ({ page }) => {
|
|
const archived = await createIdleAgent(client, {
|
|
cwd: tempRepo.path,
|
|
workspaceId,
|
|
title: `ui-archive-${randomUUID().slice(0, 8)}`,
|
|
});
|
|
const surviving = await createIdleAgent(client, {
|
|
cwd: tempRepo.path,
|
|
workspaceId,
|
|
title: `ui-control-${randomUUID().slice(0, 8)}`,
|
|
});
|
|
const passivePage = await page.context().newPage();
|
|
|
|
try {
|
|
await primeAdditionalPage(passivePage);
|
|
await resetSeededPageState(page);
|
|
await resetSeededPageState(passivePage);
|
|
await openWorkspaceWithAgents(page, [archived, surviving]);
|
|
await openWorkspaceWithAgents(passivePage, [archived, surviving]);
|
|
await openSessions(page);
|
|
await archiveAgentFromSessions(page, { agentId: archived.id, title: archived.title });
|
|
await reloadWorkspace(page, surviving.workspaceId);
|
|
await expectWorkspaceTabHidden(page, archived.id);
|
|
await expectWorkspaceArchiveOutcome(passivePage, {
|
|
archivedAgentId: archived.id,
|
|
survivingAgentId: surviving.id,
|
|
});
|
|
} finally {
|
|
await passivePage.close();
|
|
}
|
|
});
|
|
|
|
test("clicking an archived session navigates without unarchiving it", async ({ page }) => {
|
|
const archived = await createIdleAgent(client, {
|
|
cwd: tempRepo.path,
|
|
workspaceId,
|
|
title: `unarchive-archived-${randomUUID().slice(0, 8)}`,
|
|
});
|
|
const surviving = await createIdleAgent(client, {
|
|
cwd: tempRepo.path,
|
|
workspaceId,
|
|
title: `unarchive-control-${randomUUID().slice(0, 8)}`,
|
|
});
|
|
|
|
await resetSeededPageState(page);
|
|
await openWorkspaceWithAgents(page, [archived, surviving]);
|
|
await archiveAgentFromDaemon(client, archived.id);
|
|
const archivedAt = await fetchAgentArchivedAt(client, archived.id);
|
|
expect(archivedAt).not.toBeNull();
|
|
await openSessions(page);
|
|
await expectSessionRowArchived(page, archived.title);
|
|
|
|
await clickSessionRow(page, archived.title);
|
|
|
|
expect(await fetchAgentArchivedAt(client, archived.id)).toBe(archivedAt);
|
|
|
|
await expect(page).toHaveURL(buildHostWorkspaceRoute(getServerId(), archived.workspaceId), {
|
|
timeout: 30_000,
|
|
});
|
|
});
|
|
});
|