fix(e2e): rebuild auth helpers + tune playwright config

Login helpers now hit dev endpoints via APIRequestContext instead of
page.goto, eliminating the loginAsAdmin networkidle race that was
masking real test failures. Adjusted parallelism + retries to reduce
cross-file contention on shared dev DB state.
This commit is contained in:
Jennie Robinson Faber 2026-04-30 22:25:28 +01:00
parent 33ba082b82
commit b9fa9f603c
2 changed files with 26 additions and 30 deletions

View file

@ -1,36 +1,32 @@
/** /**
* Login helpers using dev endpoints. * Login helpers using dev endpoints.
* These set real httpOnly JWT cookies so all middleware works naturally. *
*/ * Implementation note: hits the dev endpoints via the APIRequestContext
* (no page navigation). The Set-Cookie response writes auth-token to the
/** * BrowserContext's cookie jar, so any subsequent page.goto() is authed.
* Login as admin via the dev test-login endpoint. * Avoids the Nuxt-dev networkidle race that made page.goto-based login flaky.
* Creates a test admin user if none exists and sets the auth cookie.
* Waits for networkidle so the client-side auth check (admin middleware +
* auth-init plugin) completes before the test navigates anywhere.
*/ */
export async function loginAsAdmin(page) { export async function loginAsAdmin(page) {
await page.goto('/api/dev/test-login', { waitUntil: 'domcontentloaded' }) const res = await page.context().request.get('/api/dev/test-login', { maxRedirects: 0 })
if (res.status() !== 302) {
// The endpoint sets the cookie and redirects to /admin. throw new Error(`/api/dev/test-login returned ${res.status()}; expected 302`)
// waitForURL fires as soon as the URL changes — not when JS finishes. }
// waitForLoadState('networkidle') ensures the auth-init plugin and admin const cookies = await page.context().cookies()
// middleware have both completed their checkMemberStatus() calls before if (!cookies.find((c) => c.name === 'auth-token')) {
// the test proceeds. throw new Error('/api/dev/test-login did not set auth-token cookie')
try {
await page.waitForURL(/\/admin/, { timeout: 15000 })
await page.waitForLoadState('networkidle')
} catch {
// Cookie should be set even if redirect failed — navigate manually
await page.goto('/admin', { waitUntil: 'networkidle' })
await page.waitForURL(/\/admin/)
} }
} }
/**
* Login as a specific member by email via the dev member-login endpoint.
*/
export async function loginAsMember(page, email) { export async function loginAsMember(page, email) {
await page.goto(`/api/dev/member-login?email=${encodeURIComponent(email)}`, { waitUntil: 'domcontentloaded' }) const res = await page.context().request.get(
await page.waitForURL(/\/member\//) `/api/dev/member-login?email=${encodeURIComponent(email)}`,
{ maxRedirects: 0 }
)
if (res.status() !== 302) {
throw new Error(`/api/dev/member-login returned ${res.status()}; expected 302`)
}
const cookies = await page.context().cookies()
if (!cookies.find((c) => c.name === 'auth-token')) {
throw new Error('/api/dev/member-login did not set auth-token cookie')
}
} }

View file

@ -7,10 +7,10 @@ export default defineConfig({
testDir: "./e2e", testDir: "./e2e",
outputDir: "e2e/test-results", outputDir: "e2e/test-results",
snapshotDir: "e2e/__screenshots__", snapshotDir: "e2e/__screenshots__",
fullyParallel: true, fullyParallel: false,
forbidOnly: !!process.env.CI, forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0, retries: process.env.CI ? 1 : 1,
workers: process.env.CI ? 1 : undefined, workers: process.env.CI ? 1 : 4,
reporter: "html", reporter: "html",
timeout: 60000, timeout: 60000,
use: { use: {