test(app): restore e2e playwright suite to green

- Fake gh stub now handles `gh pr view --json <fields>` by exiting 1
  with "no pull requests found for branch", matching the daemon's
  expected no-current-PR path; resolveCurrentPullRequestView no longer
  cascades failures across the suite.
- Rewrite the two post-setup script tests in workspace-setup-streaming
  to exercise explicit launch paths (UI scripts menu + daemon RPC) —
  39db3ea5 intentionally removed automatic post-setup script spawning.
- Harden terminal-alternate-screen synchronization: wait for the probe
  to observe alt-enter and alt-exit plus the final normal-screen marker
  before asserting, so the echoed command no longer races the alt-screen
  transition.
This commit is contained in:
Mohamed Boudra
2026-04-21 23:04:57 +07:00
parent 3fe63d5a2c
commit 244c40a62f
3 changed files with 79 additions and 9 deletions
+5
View File
@@ -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);
@@ -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", {
@@ -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);