diff --git a/packages/app/e2e/global-setup.ts b/packages/app/e2e/global-setup.ts index 2962d1336..cd32e39c7 100644 --- a/packages/app/e2e/global-setup.ts +++ b/packages/app/e2e/global-setup.ts @@ -220,6 +220,11 @@ if (args[0] === "pr" && args[1] === "list") { process.exit(0); } +if (args[0] === "pr" && args[1] === "view" && args[2] === "--json" && args[3]) { + console.error("no pull requests found for branch"); + process.exit(1); +} + if (args[0] === "issue" && args[1] === "list") { console.log("[]"); process.exit(0); diff --git a/packages/app/e2e/terminal-alternate-screen.spec.ts b/packages/app/e2e/terminal-alternate-screen.spec.ts index 0e32e6eb1..bffa69c8e 100644 --- a/packages/app/e2e/terminal-alternate-screen.spec.ts +++ b/packages/app/e2e/terminal-alternate-screen.spec.ts @@ -1,3 +1,4 @@ +import type { Page } from "@playwright/test"; import { test, expect } from "./fixtures"; import { TerminalE2EHarness, withTerminalInApp } from "./helpers/terminal-dsl"; import { @@ -10,6 +11,47 @@ import { } from "./helpers/terminal-probes"; import { waitForTerminalContent } from "./helpers/terminal-perf"; +async function waitForAlternateScreenExit(page: Page, afterAlt: string, timeout: number) { + let lastText = ""; + let lastProbe = await readTerminalRenderProbe(page); + + try { + await expect + .poll( + async () => { + lastText = await terminalVisibleText(page); + lastProbe = await readTerminalRenderProbe(page); + return ( + lastProbe.altEnterWrites > 0 && + lastProbe.altExitWrites > 0 && + lastText.includes(afterAlt) + ); + }, + { + intervals: [50], + message: `wait for alternate-screen exit and ${afterAlt} output`, + timeout, + }, + ) + .toBe(true); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error( + `Timed out waiting for alternate-screen exit: ${message}\n${JSON.stringify( + { + afterAlt, + probe: summarizeTerminalRenderProbe(lastProbe), + visibleTextTail: lastText.slice(-500), + }, + null, + 2, + )}`, + ); + } + + return lastProbe; +} + test.describe("Terminal alternate-screen transitions", () => { let harness: TerminalE2EHarness; @@ -53,9 +95,7 @@ test.describe("Terminal alternate-screen transitions", () => { `printf '\\033[?1049h\\033[2J\\033[HALT_SCREEN_TOP\\n'; sleep 0.25; printf '\\033[?1049l'; echo ${afterAlt}\n`, { delay: 0 }, ); - await waitForTerminalContent(page, (text) => text.includes(afterAlt), 10_000); - await page.waitForTimeout(250); - const probe = await readTerminalRenderProbe(page); + const probe = await waitForAlternateScreenExit(page, afterAlt, 10_000); const probeSummary = summarizeTerminalRenderProbe(probe); await testInfo.attach("alternate-screen-probe", { diff --git a/packages/app/e2e/workspace-setup-streaming.spec.ts b/packages/app/e2e/workspace-setup-streaming.spec.ts index d6b40a8ac..329edbffa 100644 --- a/packages/app/e2e/workspace-setup-streaming.spec.ts +++ b/packages/app/e2e/workspace-setup-streaming.spec.ts @@ -19,6 +19,18 @@ function getServerId(): string { return serverId; } +type WorkspaceScriptStarter = { + startWorkspaceScript( + workspaceId: string, + scriptName: string, + ): Promise<{ + workspaceId: string; + scriptName: string; + terminalId: string | null; + error: string | null; + }>; +}; + /** Click the sidebar row for a workspace (by ID) and wait for navigation. */ async function navigateToWorkspaceViaSidebar( page: import("@playwright/test").Page, @@ -226,7 +238,7 @@ test.describe("Workspace setup streaming", () => { } }); - test("launches script terminals after setup completes", async ({ page }) => { + test("launches script terminals from the workspace scripts menu", async ({ page }) => { test.setTimeout(90_000); const client = await connectWorkspaceSetupClient(); const repo = await createTempGitRepo("setup-svc-ui-", { @@ -263,12 +275,15 @@ test.describe("Workspace setup streaming", () => { await waitForWorkspaceTabsVisible(page); - // Wait for the script terminal tab to appear in the tabs bar. - // The tab title shows the command, not the script name. + await expect(page.locator('[data-testid^="workspace-tab-terminal_"]')).toHaveCount(0); + await page.getByTestId("workspace-scripts-button").click(); + await expect(page.getByTestId("workspace-scripts-menu")).toBeVisible({ timeout: 10_000 }); + await page.getByTestId("workspace-scripts-start-web").click(); + await page.keyboard.press("Escape"); + const terminalTab = page.locator('[data-testid^="workspace-tab-terminal_"]').first(); await expect(terminalTab).toBeVisible({ timeout: 30_000 }); - // Click the script terminal tab await terminalTab.click(); // Verify the terminal surface rendered @@ -294,7 +309,7 @@ test.describe("Workspace setup streaming", () => { } }); - test("launches workspace scripts after setup completes", async () => { + test("launches workspace scripts through an explicit daemon request", async () => { const client = await connectWorkspaceSetupClient(); const repo = await createTempGitRepo("setup-scripts-", { paseoConfig: { @@ -303,7 +318,7 @@ test.describe("Workspace setup streaming", () => { }, scripts: { editor: { - command: "npm run dev", + command: "node -e \"console.log('editor ready'); setInterval(() => {}, 1000)\"", }, }, }, @@ -325,9 +340,19 @@ test.describe("Workspace setup streaming", () => { throw new Error(result.error ?? "Failed to create workspace"); } const workspaceDir = result.workspace.workspaceDirectory; + const workspaceId = String(result.workspace.id); await completed; + const scriptClient = client as typeof client & WorkspaceScriptStarter; + const startResult = await scriptClient.startWorkspaceScript(workspaceId, "editor"); + expect(startResult).toMatchObject({ + workspaceId, + scriptName: "editor", + terminalId: expect.any(String), + error: null, + }); + await expect .poll(async () => { const terminals = await client.listTerminals(workspaceDir);