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:
@@ -220,6 +220,11 @@ if (args[0] === "pr" && args[1] === "list") {
|
|||||||
process.exit(0);
|
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") {
|
if (args[0] === "issue" && args[1] === "list") {
|
||||||
console.log("[]");
|
console.log("[]");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { Page } from "@playwright/test";
|
||||||
import { test, expect } from "./fixtures";
|
import { test, expect } from "./fixtures";
|
||||||
import { TerminalE2EHarness, withTerminalInApp } from "./helpers/terminal-dsl";
|
import { TerminalE2EHarness, withTerminalInApp } from "./helpers/terminal-dsl";
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +11,47 @@ import {
|
|||||||
} from "./helpers/terminal-probes";
|
} from "./helpers/terminal-probes";
|
||||||
import { waitForTerminalContent } from "./helpers/terminal-perf";
|
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", () => {
|
test.describe("Terminal alternate-screen transitions", () => {
|
||||||
let harness: TerminalE2EHarness;
|
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`,
|
`printf '\\033[?1049h\\033[2J\\033[HALT_SCREEN_TOP\\n'; sleep 0.25; printf '\\033[?1049l'; echo ${afterAlt}\n`,
|
||||||
{ delay: 0 },
|
{ delay: 0 },
|
||||||
);
|
);
|
||||||
await waitForTerminalContent(page, (text) => text.includes(afterAlt), 10_000);
|
const probe = await waitForAlternateScreenExit(page, afterAlt, 10_000);
|
||||||
await page.waitForTimeout(250);
|
|
||||||
const probe = await readTerminalRenderProbe(page);
|
|
||||||
const probeSummary = summarizeTerminalRenderProbe(probe);
|
const probeSummary = summarizeTerminalRenderProbe(probe);
|
||||||
|
|
||||||
await testInfo.attach("alternate-screen-probe", {
|
await testInfo.attach("alternate-screen-probe", {
|
||||||
|
|||||||
@@ -19,6 +19,18 @@ function getServerId(): string {
|
|||||||
return serverId;
|
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. */
|
/** Click the sidebar row for a workspace (by ID) and wait for navigation. */
|
||||||
async function navigateToWorkspaceViaSidebar(
|
async function navigateToWorkspaceViaSidebar(
|
||||||
page: import("@playwright/test").Page,
|
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);
|
test.setTimeout(90_000);
|
||||||
const client = await connectWorkspaceSetupClient();
|
const client = await connectWorkspaceSetupClient();
|
||||||
const repo = await createTempGitRepo("setup-svc-ui-", {
|
const repo = await createTempGitRepo("setup-svc-ui-", {
|
||||||
@@ -263,12 +275,15 @@ test.describe("Workspace setup streaming", () => {
|
|||||||
|
|
||||||
await waitForWorkspaceTabsVisible(page);
|
await waitForWorkspaceTabsVisible(page);
|
||||||
|
|
||||||
// Wait for the script terminal tab to appear in the tabs bar.
|
await expect(page.locator('[data-testid^="workspace-tab-terminal_"]')).toHaveCount(0);
|
||||||
// The tab title shows the command, not the script name.
|
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();
|
const terminalTab = page.locator('[data-testid^="workspace-tab-terminal_"]').first();
|
||||||
await expect(terminalTab).toBeVisible({ timeout: 30_000 });
|
await expect(terminalTab).toBeVisible({ timeout: 30_000 });
|
||||||
|
|
||||||
// Click the script terminal tab
|
|
||||||
await terminalTab.click();
|
await terminalTab.click();
|
||||||
|
|
||||||
// Verify the terminal surface rendered
|
// 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 client = await connectWorkspaceSetupClient();
|
||||||
const repo = await createTempGitRepo("setup-scripts-", {
|
const repo = await createTempGitRepo("setup-scripts-", {
|
||||||
paseoConfig: {
|
paseoConfig: {
|
||||||
@@ -303,7 +318,7 @@ test.describe("Workspace setup streaming", () => {
|
|||||||
},
|
},
|
||||||
scripts: {
|
scripts: {
|
||||||
editor: {
|
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");
|
throw new Error(result.error ?? "Failed to create workspace");
|
||||||
}
|
}
|
||||||
const workspaceDir = result.workspace.workspaceDirectory;
|
const workspaceDir = result.workspace.workspaceDirectory;
|
||||||
|
const workspaceId = String(result.workspace.id);
|
||||||
|
|
||||||
await completed;
|
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
|
await expect
|
||||||
.poll(async () => {
|
.poll(async () => {
|
||||||
const terminals = await client.listTerminals(workspaceDir);
|
const terminals = await client.listTerminals(workspaceDir);
|
||||||
|
|||||||
Reference in New Issue
Block a user