fix(app): make e2e setup an auto fixture so first-of-spec tests get setup (#702)

* fix(app): make e2e setup an auto fixture so it runs for the first test of every spec

The fixtures.ts beforeEach was declared at the top level of a non-test
fixture file. Playwright sometimes skipped it for the first test of a
subsequent spec when multiple specs ran in the same worker — that test
hit page.evaluate without the seed nonce or daemon registry in
localStorage and failed (then passed on retry, masking the bug behind
retries: 1). Reproduced locally by running sidebar-workspace then
startup-loading: the first startup-loading test got no fixture setup
and threw "Expected e2e seed nonce".

Move the setup into an `auto: true` fixture, the canonical Playwright
pattern for running setup for every test in a workspace. The afterEach
console-attachment is folded into the same fixture's teardown.

* refactor(server): simplify nullish handling in workspace-git-metadata

Drop the explicit `null` arg in `deriveProjectSlug(input.cwd, null)` —
the second parameter already defaults to `null`. Inline the
`repoName && repoName.length > 0` check in `parseGitHubRepoNameFromRemote`
since `pop()` on a non-empty array returns a non-empty string here, and
`|| null` covers the empty-string case directly.
This commit is contained in:
Mohamed Boudra
2026-05-05 19:56:52 +07:00
parent f974812dfc
commit ab9f07dd8a
2 changed files with 92 additions and 97 deletions
+14 -18
View File
@@ -1,8 +1,12 @@
import { test as base, expect, type Page } from "@playwright/test"; import { test as base, expect, type Page } from "@playwright/test";
import { buildCreateAgentPreferences, buildSeededHost } from "./helpers/daemon-registry"; import { buildCreateAgentPreferences, buildSeededHost } from "./helpers/daemon-registry";
// Extend base test to provide dynamic baseURL from global-setup // Test setup is wired through an `auto: true` fixture rather than `test.beforeEach`.
const test = base.extend({ // `test.beforeEach` declared at the top level of a non-test fixture file is unreliable
// across spec-file boundaries — Playwright sometimes skips it for the first test of a
// subsequent spec when multiple specs run in the same worker. Auto fixtures run
// reliably for every test that uses this `test` object.
const test = base.extend<{ paseoE2ESetup: void }>({
baseURL: async ({}, provide) => { baseURL: async ({}, provide) => {
const metroPort = process.env.E2E_METRO_PORT; const metroPort = process.env.E2E_METRO_PORT;
if (!metroPort) { if (!metroPort) {
@@ -10,11 +14,8 @@ const test = base.extend({
} }
await provide(`http://localhost:${metroPort}`); await provide(`http://localhost:${metroPort}`);
}, },
}); paseoE2ESetup: [
async ({ page }, provide, testInfo) => {
const consoleEntries = new WeakMap<Page, string[]>();
test.beforeEach(async ({ page }) => {
const daemonPort = process.env.E2E_DAEMON_PORT; const daemonPort = process.env.E2E_DAEMON_PORT;
const metroPort = process.env.E2E_METRO_PORT; const metroPort = process.env.E2E_METRO_PORT;
if (!daemonPort) { if (!daemonPort) {
@@ -43,7 +44,6 @@ test.beforeEach(async ({ page }) => {
}); });
const entries: string[] = []; const entries: string[] = [];
consoleEntries.set(page, entries);
page.on("console", (message) => { page.on("console", (message) => {
entries.push(`[console:${message.type()}] ${message.text()}`); entries.push(`[console:${message.type()}] ${message.text()}`);
@@ -90,22 +90,18 @@ test.beforeEach(async ({ page }) => {
}, },
{ daemon: testDaemon, preferences: createAgentPreferences, seedNonce }, { daemon: testDaemon, preferences: createAgentPreferences, seedNonce },
); );
});
test.afterEach(async ({ page }, testInfo) => { await provide();
const entries = consoleEntries.get(page);
if (!entries || entries.length === 0) {
return;
}
if (testInfo.status === testInfo.expectedStatus) {
return;
}
if (entries.length > 0 && testInfo.status !== testInfo.expectedStatus) {
await testInfo.attach("browser-console", { await testInfo.attach("browser-console", {
body: entries.join("\n"), body: entries.join("\n"),
contentType: "text/plain", contentType: "text/plain",
}); });
}
},
{ auto: true },
],
}); });
export { test, expect, type Page }; export { test, expect, type Page };
@@ -25,8 +25,7 @@ export function parseGitHubRepoNameFromRemote(remoteUrl: string): string | null
return null; return null;
} }
const repoName = githubRepo.split("/").pop(); return githubRepo.split("/").pop() || null;
return repoName && repoName.length > 0 ? repoName : null;
} }
export function deriveProjectSlug(cwd: string, remoteUrl: string | null = null): string { export function deriveProjectSlug(cwd: string, remoteUrl: string | null = null): string {
@@ -51,7 +50,7 @@ export function buildWorkspaceGitMetadataFromSnapshot(input: {
workspaceDisplayName: input.directoryName, workspaceDisplayName: input.directoryName,
gitRemote: null, gitRemote: null,
isWorktree: false, isWorktree: false,
projectSlug: deriveProjectSlug(input.cwd, null), projectSlug: deriveProjectSlug(input.cwd),
repoRoot: null, repoRoot: null,
currentBranch: null, currentBranch: null,
remoteUrl: null, remoteUrl: null,