Files
paseo/packages/app/e2e/support/helpers/with-workspace.ts
T
Mohamed BoudraandGitHub 5a30affd7f Skip unrelated required checks without losing their names (#2542)
* fix(ci): skip unrelated required checks cleanly

Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable.

* fix(ci): gate changes to install patching script

* fix(ci): route cross-package test contracts narrowly

* fix(ci): keep routed contracts merge-blocking

* fix(server): exclude nested test utilities from builds

* fix(ci): cover app-owned native and browser domains

* refactor(ci): align tests with runtime domains

Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check.

* fix(ci): keep routing and desktop suites isolated

The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs.

* fix(ci): isolate desktop e2e runtime

* perf(test): isolate Playwright workers

* perf(test): parallelize Playwright within specs

* fix(test): keep Playwright file scheduling

* fix(test): share dynamic Metro fixture

* fix(test): preserve own-host restart fixtures

* fix(ci): preserve routed contracts after rebase

* Stabilize Playwright wheel-scroll assertions
2026-08-02 00:56:05 +08:00

118 lines
3.5 KiB
TypeScript

import { execSync } from "node:child_process";
import path from "node:path";
import type { Page } from "@playwright/test";
import { waitForTabBar } from "./launcher";
import { selectWorkspaceInSidebar } from "./sidebar";
import { createTempGitRepo, resolveTempRoot } from "./workspace";
import {
connectWorkspaceSetupClient,
openHomeWithProject,
type WorkspaceSetupDaemonClient,
} from "./workspace-setup";
export interface CreatedWorkspace {
workspaceId: string;
repoPath: string;
navigateTo(): Promise<void>;
}
export interface WithWorkspaceOptions {
worktree?: boolean;
prefix?: string;
}
export type WithWorkspace = (options?: WithWorkspaceOptions) => Promise<CreatedWorkspace>;
interface WorktreeRecord {
repoPath: string;
worktreePath: string;
}
export interface WithWorkspaceHandle {
withWorkspace: WithWorkspace;
cleanup: () => Promise<void>;
}
export function createWithWorkspace(page: Page): WithWorkspaceHandle {
let client: WorkspaceSetupDaemonClient | null = null;
const repos: Array<{ cleanup: () => Promise<void> }> = [];
const worktrees: WorktreeRecord[] = [];
const projectIds = new Set<string>();
const withWorkspace: WithWorkspace = async (options) => {
if (!client) {
client = await connectWorkspaceSetupClient();
}
const prefix = options?.prefix ?? (options?.worktree ? "wt-" : "ws-");
const repo = await createTempGitRepo(prefix);
repos.push(repo);
let workspacePath = repo.path;
if (options?.worktree) {
const tempRoot = await resolveTempRoot();
workspacePath = path.join(
tempRoot,
`paseo-wt-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
const branchName = `paseo-wt-${Date.now()}`;
execSync(
`git worktree add ${JSON.stringify(workspacePath)} -b ${JSON.stringify(branchName)} main`,
{ cwd: repo.path, stdio: "ignore" },
);
worktrees.push({ repoPath: repo.path, worktreePath: workspacePath });
// Register the parent project so the sidebar lists it before we navigate.
const added = await client.addProject(repo.path);
if (!added.project) {
throw new Error(added.error ?? `Failed to add project ${repo.path}`);
}
projectIds.add(added.project.projectId);
}
const created = await client.createWorkspace({
source: { kind: "directory", path: workspacePath },
});
if (!created.workspace) {
throw new Error(created.error ?? `Failed to create workspace ${workspacePath}`);
}
const workspaceId = created.workspace.id;
projectIds.add(created.workspace.projectId);
return {
workspaceId,
repoPath: workspacePath,
navigateTo: async () => {
await openHomeWithProject(page, repo.path);
await selectWorkspaceInSidebar(page, workspaceId);
await waitForTabBar(page);
},
};
};
return {
withWorkspace,
cleanup: async () => {
if (client) {
for (const projectId of projectIds) {
await client.removeProject(projectId).catch(() => undefined);
}
}
for (const { repoPath, worktreePath } of worktrees) {
try {
execSync(`git worktree remove ${JSON.stringify(worktreePath)} --force`, {
cwd: repoPath,
stdio: "ignore",
});
} catch {
// Best-effort cleanup so the original test failure is preserved.
}
}
for (const repo of repos) {
await repo.cleanup();
}
if (client) {
await client.close().catch(() => undefined);
}
},
};
}