perf(e2e): log in once and share the admin session via storage state

Every test in the `app` project submitted the login form through the
authenticatedPage fixture: a page load, an Argon2 verification on the
server, a redirect and a networkidle wait. Over 137 tests that was
roughly half of the median test's 1.8s.

A new `login-state` setup project (tests/e2e/auth.setup.ts) authenticates
once after auth.spec has created the account and saves the session cookie
to data/test-storage-state.json. The `app` project loads it into every
context, so setupAndLogin takes its "session still valid" path and the
fixture is reduced to a page load.

pwa.spec and mcp.spec describe anonymous visitors and opt out with an
empty storageState so their baseline is unchanged.

Local run, 4 workers: summed test time 276s -> 256s, median 1.8s -> 1.6s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-09-14 15:29:46 +02:00
committed by Fred Cabassut
co-authored by Claude Fable 5.1
parent af7186675e
commit 7bf06edd97
6 changed files with 42 additions and 2 deletions
+18
View File
@@ -0,0 +1,18 @@
import { test as setup } from '@playwright/test';
import { setupAndLogin } from './helpers/fixtures';
import { STORAGE_STATE_FILE } from './global-setup';
/**
* Log in as the admin once and persist the session cookie. The `app` project
* loads this storage state into every browser context, so tests start already
* authenticated instead of each one submitting the login form (~0.8s per test,
* roughly half of a typical test's duration).
*
* The admin's own session is never revoked by any test: admin-users only
* resets/revokes victim accounts, and auth.spec's logout scenario runs in the
* auth-setup project with its own context before this setup executes.
*/
setup('authenticate as admin and save storage state', async ({ page }) => {
await setupAndLogin(page);
await page.context().storageState({ path: STORAGE_STATE_FILE });
});
+2
View File
@@ -2,6 +2,8 @@ import { writeFileSync } from 'fs';
import { randomUUID } from 'crypto';
export const TEST_CREDENTIALS_FILE = './data/test-credentials.json';
/** Admin session cookie captured by auth.setup.ts and shared by the `app` project. */
export const STORAGE_STATE_FILE = './data/test-storage-state.json';
export default function globalSetup() {
// The test database is wiped before the webServer starts (see the
+4
View File
@@ -9,6 +9,10 @@ import { setupAndLogin } from './helpers/fixtures.js';
* 3. Test tool invocations via JSON-RPC over Streamable HTTP
*/
// Start unauthenticated: the "rejects requests without API key" scenario must
// not carry the shared admin session cookie, and the apiKey fixture logs in itself.
base.use({ storageState: { cookies: [], origins: [] } });
const test = base.extend<{ apiKey: string }>({
apiKey: async ({ page }, use) => {
await setupAndLogin(page);
+4
View File
@@ -1,5 +1,9 @@
import { test, expect } from '@playwright/test';
// These scenarios describe what an anonymous visitor receives, so drop the
// shared admin session the `app` project injects.
test.use({ storageState: { cookies: [], origins: [] } });
test.describe('PWA', () => {
test('Scenario: Web manifest provides correct app metadata', async ({ page }) => {
// When the browser requests the web manifest