import { expect, type Page } from "@playwright/test"; import { getServerId } from "./server-id"; export async function selectWorkspaceInSidebar(page: Page, workspaceId: string): Promise { const row = page.getByTestId(`sidebar-workspace-row-${getServerId()}:${workspaceId}`); await expect(row).toBeVisible({ timeout: 30_000 }); await row.click(); } async function openWorkspaceSidebarKebab(page: Page, workspaceId: string) { const serverId = getServerId(); const row = page.getByTestId(`sidebar-workspace-row-${serverId}:${workspaceId}`); await expect(row).toBeVisible({ timeout: 30_000 }); await row.hover(); const kebab = page.getByTestId(`sidebar-workspace-kebab-${serverId}:${workspaceId}`); await expect(kebab).toBeVisible({ timeout: 10_000 }); await kebab.click(); return serverId; } export async function expectWorkspaceListed(page: Page, name: string): Promise { await expect( page.locator('[data-testid^="sidebar-workspace-row-"]').filter({ hasText: name }).first(), ).toBeVisible({ timeout: 30_000 }); } // The workspace row kebab and its menu items carry no web ARIA role, so the sidebar // suite addresses them by the stable test ids the app assigns per workspace — the same // convention the rename flow uses. The kebab only reveals on hover. export async function clickArchiveWorkspaceMenuItem( page: Page, workspaceId: string, ): Promise { const serverId = await openWorkspaceSidebarKebab(page, workspaceId); const archiveItem = page.getByTestId(`sidebar-workspace-menu-archive-${serverId}:${workspaceId}`); await expect(archiveItem).toBeVisible({ timeout: 10_000 }); await archiveItem.click(); } export async function archiveWorktreeFromSidebar(page: Page, workspaceId: string): Promise { // A clean worktree archives with no prompt; if the host reports unsynced work the app // raises a browser confirm. Accept it so the user-confirmed archive stays deterministic // either way. page.once("dialog", (dialog) => void dialog.accept()); await clickArchiveWorkspaceMenuItem(page, workspaceId); } export async function expectWorkspaceAbsentFromSidebar( page: Page, workspaceId: string, ): Promise { await expect( page.getByTestId(`sidebar-workspace-row-${getServerId()}:${workspaceId}`), ).toHaveCount(0, { timeout: 30_000 }); } export async function openMobileAgentSidebar(page: Page): Promise { await page.getByRole("button", { name: "Open menu" }).click(); } export async function closeMobileAgentSidebar(page: Page): Promise { const closeButton = page.getByTestId("sidebar-close"); await expect(closeButton).toBeInViewport({ timeout: 5_000 }); await closeButton.click({ force: true }); } // The mobile sidebar panel animates via translateX; toBeInViewport reflects the rendered position. export async function expectMobileAgentSidebarVisible(page: Page): Promise { await expect(page.getByTestId("sidebar-sessions")).toBeInViewport({ timeout: 5_000 }); } export async function expectMobileAgentSidebarHidden(page: Page): Promise { await expect(page.getByTestId("sidebar-sessions")).not.toBeInViewport({ timeout: 5_000 }); }