fix: stabilize workspace e2e routing and cli helper export
This commit is contained in:
@@ -83,14 +83,14 @@ export async function connectTerminalClient(): Promise<TerminalPerfDaemonClient>
|
||||
return client;
|
||||
}
|
||||
|
||||
export function buildTerminalWorkspaceUrl(cwd: string, terminalId: string): string {
|
||||
export function buildTerminalWorkspaceUrl(workspaceId: string, terminalId: string): string {
|
||||
const serverId = getServerId();
|
||||
const route = buildHostWorkspaceRoute(serverId, cwd);
|
||||
const route = buildHostWorkspaceRoute(serverId, workspaceId);
|
||||
return `${route}?open=${encodeURIComponent(`terminal:${terminalId}`)}`;
|
||||
}
|
||||
|
||||
function buildWorkspaceUrl(cwd: string): string {
|
||||
return buildHostWorkspaceRoute(getServerId(), cwd);
|
||||
function buildWorkspaceUrl(workspaceId: string): string {
|
||||
return buildHostWorkspaceRoute(getServerId(), workspaceId);
|
||||
}
|
||||
|
||||
export async function getTerminalBufferText(page: Page): Promise<string> {
|
||||
@@ -129,19 +129,19 @@ export async function waitForTerminalContent(
|
||||
|
||||
export async function navigateToTerminal(
|
||||
page: Page,
|
||||
input: { cwd: string; terminalId: string },
|
||||
input: { workspaceId: string; terminalId: string },
|
||||
): Promise<void> {
|
||||
// Boot the app at the workspace route directly.
|
||||
// The fixtures.ts beforeEach addInitScript seeds localStorage on every navigation,
|
||||
// so the daemon registry is already configured when the app starts.
|
||||
const workspaceRoute = buildTerminalWorkspaceUrl(input.cwd, input.terminalId);
|
||||
const workspaceRoute = buildTerminalWorkspaceUrl(input.workspaceId, input.terminalId);
|
||||
await page.goto(workspaceRoute);
|
||||
|
||||
// The workspace layout consumes `?open=...`, returns null during the effect,
|
||||
// then replaces the URL with the clean workspace route after preparing the tab.
|
||||
// On CI, Expo Router's rootNavigationState may take time to initialize,
|
||||
// so we allow a generous timeout here.
|
||||
const cleanWorkspaceRoute = buildWorkspaceUrl(input.cwd);
|
||||
const cleanWorkspaceRoute = buildWorkspaceUrl(input.workspaceId);
|
||||
await page.waitForURL(
|
||||
(url) => url.pathname === cleanWorkspaceRoute && !url.searchParams.has("open"),
|
||||
{ timeout: 30_000 },
|
||||
|
||||
@@ -20,6 +20,7 @@ const KEYSTROKE_P95_BUDGET_MS = 150;
|
||||
test.describe("Terminal wire performance", () => {
|
||||
let client: TerminalPerfDaemonClient;
|
||||
let tempRepo: { path: string; cleanup: () => Promise<void> };
|
||||
let workspaceId: string;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
tempRepo = await createTempGitRepo("perf-");
|
||||
@@ -27,6 +28,7 @@ test.describe("Terminal wire performance", () => {
|
||||
// Seed the workspace in the daemon so the app can resolve the path
|
||||
const seedResult = await client.openProject(tempRepo.path);
|
||||
if (!seedResult.workspace) throw new Error(seedResult.error ?? "Failed to seed workspace");
|
||||
workspaceId = seedResult.workspace.id;
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
@@ -48,7 +50,7 @@ test.describe("Terminal wire performance", () => {
|
||||
const terminalId = result.terminal.id;
|
||||
|
||||
try {
|
||||
await navigateToTerminal(page, { cwd: tempRepo.path, terminalId });
|
||||
await navigateToTerminal(page, { workspaceId, terminalId });
|
||||
await setupDeterministicPrompt(page);
|
||||
|
||||
const sentinel = `PERF_DONE_${Date.now()}`;
|
||||
@@ -107,7 +109,7 @@ test.describe("Terminal wire performance", () => {
|
||||
const terminalId = result.terminal.id;
|
||||
|
||||
try {
|
||||
await navigateToTerminal(page, { cwd: tempRepo.path, terminalId });
|
||||
await navigateToTerminal(page, { workspaceId, terminalId });
|
||||
await setupDeterministicPrompt(page);
|
||||
|
||||
// Ensure clean prompt state
|
||||
|
||||
@@ -95,15 +95,12 @@ test.describe("Workspace cwd correctness", () => {
|
||||
if (!workspaceResult.workspace) {
|
||||
throw new Error(workspaceResult.error ?? `Failed to open project ${worktreePath}`);
|
||||
}
|
||||
const workspaceName = workspaceResult.workspace.name;
|
||||
const workspaceId = String(workspaceResult.workspace.id);
|
||||
|
||||
// Use sidebar navigation to avoid Expo Router hydration issues
|
||||
// with direct URL navigation to the 2nd+ workspace.
|
||||
await openHomeWithProject(page, repo.path);
|
||||
const sidebarWorkspace = page.getByRole("button", { name: workspaceName });
|
||||
await expect(sidebarWorkspace).toBeVisible({ timeout: 30_000 });
|
||||
await sidebarWorkspace.click();
|
||||
await waitForTabBar(page);
|
||||
await navigateToWorkspaceViaSidebar(page, workspaceId);
|
||||
|
||||
await clickTerminal(page);
|
||||
|
||||
|
||||
@@ -9,6 +9,24 @@ import {
|
||||
openHomeWithProject,
|
||||
} from "./helpers/workspace-setup";
|
||||
|
||||
function getServerId(): string {
|
||||
const serverId = process.env.E2E_SERVER_ID;
|
||||
if (!serverId) {
|
||||
throw new Error("E2E_SERVER_ID is not set.");
|
||||
}
|
||||
return serverId;
|
||||
}
|
||||
|
||||
async function navigateToWorkspaceViaSidebar(
|
||||
page: import("@playwright/test").Page,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const row = page.getByTestId(`sidebar-workspace-row-${getServerId()}:${workspaceId}`);
|
||||
await expect(row).toBeVisible({ timeout: 30_000 });
|
||||
await row.click();
|
||||
await waitForTabBar(page);
|
||||
}
|
||||
|
||||
test.describe("Workspace setup runtime authority", () => {
|
||||
test.describe.configure({ retries: 1 });
|
||||
|
||||
@@ -24,6 +42,7 @@ test.describe("Workspace setup runtime authority", () => {
|
||||
cwd: repo.path,
|
||||
worktreeSlug: `setup-chat-${Date.now()}`,
|
||||
});
|
||||
const workspaceId = String(workspace.id);
|
||||
|
||||
const wsInfo = await findWorktreeWorkspaceForProject(client, repo.path);
|
||||
expect(wsInfo.workspaceDirectory).not.toBe(repo.path);
|
||||
@@ -31,9 +50,7 @@ test.describe("Workspace setup runtime authority", () => {
|
||||
|
||||
// Navigate to the workspace via sidebar
|
||||
await openHomeWithProject(page, repo.path);
|
||||
const wsButton = page.getByRole("button", { name: workspace.name });
|
||||
await expect(wsButton).toBeVisible({ timeout: 30_000 });
|
||||
await wsButton.click();
|
||||
await navigateToWorkspaceViaSidebar(page, workspaceId);
|
||||
await expect(page).toHaveURL(/\/workspace\//, { timeout: 30_000 });
|
||||
} finally {
|
||||
await client.close();
|
||||
@@ -61,16 +78,13 @@ test.describe("Workspace setup runtime authority", () => {
|
||||
throw new Error(result.error ?? "Failed to create workspace");
|
||||
}
|
||||
const workspaceDir = result.workspace.workspaceDirectory;
|
||||
const workspaceName = result.workspace.name;
|
||||
const workspaceId = String(result.workspace.id);
|
||||
|
||||
// Navigate to the worktree workspace via sidebar click (direct URL
|
||||
// navigation for freshly created worktree workspaces can race with
|
||||
// Expo Router hydration, so we use the sidebar which is authoritative).
|
||||
await openHomeWithProject(page, repo.path);
|
||||
const sidebarWorkspace = page.getByRole("button", { name: workspaceName });
|
||||
await expect(sidebarWorkspace).toBeVisible({ timeout: 30_000 });
|
||||
await sidebarWorkspace.click();
|
||||
await waitForTabBar(page);
|
||||
await navigateToWorkspaceViaSidebar(page, workspaceId);
|
||||
|
||||
await clickTerminal(page);
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ import { parseDuration } from "../../utils/duration.js";
|
||||
import { collectMultiple } from "../../utils/command-options.js";
|
||||
import { resolveProviderAndModel } from "../../utils/provider-model.js";
|
||||
|
||||
export { resolveProviderAndModel } from "../../utils/provider-model.js";
|
||||
|
||||
export function addRunOptions(cmd: Command): Command {
|
||||
return cmd
|
||||
.description("Create and start an agent with a task")
|
||||
|
||||
Reference in New Issue
Block a user