Skip to content

Commit dd1fc8b

Browse files
mchestrclaude
andcommitted
perf: parallelize E2E tests for faster execution
- Enable fullyParallel and increase workers (4 local, 2 CI) - Split tests into 3 projects: readonly (parallel), mutations (serial), setup-wizard (serial) - Add serial mode to describe blocks with shared state - Fix tests expecting wrong page after admin default changed to observability 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent fa22019 commit dd1fc8b

File tree

6 files changed

+44
-7
lines changed

6 files changed

+44
-7
lines changed

e2e/accessibility.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { waitForAdminContent, WAIT_TIMEOUTS } from './helpers/test-utils';
55
test.describe('Accessibility Tests', () => {
66
test.describe('Admin Pages', () => {
77
test('admin users page should have no critical accessibility violations', async ({ adminPage }) => {
8+
await adminPage.locator('aside').getByTestId('admin-nav-users').first().click();
89
await waitForAdminContent(adminPage, [
910
{ type: 'heading', value: 'Users' }
1011
]);
@@ -178,6 +179,7 @@ test.describe('Accessibility Tests', () => {
178179

179180
test.describe('Interactive Elements', () => {
180181
test('icon buttons should have accessible names', async ({ adminPage }) => {
182+
await adminPage.locator('aside').getByTestId('admin-nav-users').first().click();
181183
await waitForAdminContent(adminPage, [
182184
{ type: 'heading', value: 'Users' }
183185
]);

e2e/admin-functionality.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { verifyPageAccessible, waitForAdminContent, waitForAdminPageReady } from
33

44
test.describe('Admin Functionality', () => {
55
test('should access admin dashboard', async ({ adminPage }) => {
6-
// After authentication, adminPage starts on /admin/users (dashboard)
6+
// Admin lands on observability page, navigate to users
7+
await adminPage.locator('aside').getByTestId('admin-nav-users').first().click();
78
await waitForAdminContent(adminPage, [
89
{ type: 'heading', value: /Users/i }
910
], { timeout: 30000 });
@@ -20,7 +21,8 @@ test.describe('Admin Functionality', () => {
2021
});
2122

2223
test('should access admin users list', async ({ adminPage }) => {
23-
// Already on /admin/users; verify heading first
24+
// Navigate to users page
25+
await adminPage.locator('aside').getByTestId('admin-nav-users').first().click();
2426
await waitForAdminContent(adminPage, [
2527
{ type: 'heading', value: 'Users' }
2628
], { timeout: 30000 });

e2e/admin-maintenance.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ test.describe('Admin Maintenance Feature', () => {
220220
});
221221

222222
test.describe('Candidate Review Workflow', () => {
223+
// These tests share seeded data and must run sequentially
224+
test.describe.configure({ mode: 'serial' });
225+
223226
test.beforeAll(async () => {
224227
await seedMaintenanceData(prisma);
225228
});
@@ -407,6 +410,9 @@ test.describe('Admin Maintenance Feature', () => {
407410
});
408411

409412
test.describe('Maintenance History', () => {
413+
// These tests share seeded data and must run sequentially
414+
test.describe.configure({ mode: 'serial' });
415+
410416
test.beforeAll(async () => {
411417
await seedMaintenanceData(prisma);
412418
});

e2e/onboarding-flow.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { createE2EPrismaClient } from './helpers/prisma';
33
import { waitForLoadingGone } from './helpers/test-utils';
44

55
test.describe('Onboarding Flow', () => {
6+
// This test modifies user onboarding state and must run in isolation
7+
test.describe.configure({ mode: 'serial' });
8+
69
test('new user completes onboarding and is redirected to homepage', async ({ browser }) => {
710
const prisma = createE2EPrismaClient();
811

e2e/user-scenarios.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import {
66
} from './helpers/test-utils';
77

88
test.describe('User Scenarios', () => {
9+
// These tests modify user data (wrapped content) and must run sequentially
10+
test.describe.configure({ mode: 'serial' });
11+
912
test.describe('Authentication & Authorization', () => {
1013
test('both user types can authenticate successfully', async ({ adminPage, regularUserPage }) => {
1114
// Admin page should be on home after auth

playwright.config.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ console.log(`Running tests on ${baseURL}`);
99
*/
1010
export default defineConfig({
1111
testDir: './e2e',
12-
fullyParallel: false,
12+
fullyParallel: true, // Enable parallel test execution within files
1313
/* Fail the build on CI if you accidentally left test.only in the source code. */
1414
forbidOnly: !!process.env.CI,
1515
/* Retry on CI only */
1616
retries: process.env.CI ? 2 : 0,
17-
workers: 1, // Run tests sequentially to avoid database race conditions
17+
workers: process.env.CI ? 2 : 4, // Parallel workers (tests with shared state use serial mode)
1818
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
1919
reporter: [
2020
['list'], // Console output
@@ -40,16 +40,37 @@ export default defineConfig({
4040
/* Set up database before running tests */
4141
globalSetup: require.resolve('./e2e/global-setup.ts'),
4242

43-
/* Configure projects for major browsers */
43+
/* Configure projects for different test types */
4444
projects: [
45+
// Read-only tests can run in parallel (no shared state mutations)
4546
{
46-
name: 'chromium',
47+
name: 'readonly',
48+
testMatch: [
49+
/accessibility\.spec\.ts/,
50+
/admin-functionality\.spec\.ts/,
51+
/admin-observability\.spec\.ts/,
52+
/admin-protection\.spec\.ts/,
53+
],
4754
use: { ...devices['Desktop Chrome'] },
48-
testIgnore: /setup-wizard\.spec\.ts/,
4955
},
56+
// Tests that mutate shared state must run sequentially
57+
{
58+
name: 'mutations',
59+
testMatch: [
60+
/admin-maintenance\.spec\.ts/,
61+
/onboarding-flow\.spec\.ts/,
62+
/public-flows\.spec\.ts/,
63+
/user-scenarios\.spec\.ts/,
64+
],
65+
fullyParallel: false,
66+
workers: 1,
67+
use: { ...devices['Desktop Chrome'] },
68+
},
69+
// Setup wizard runs in complete isolation
5070
{
5171
name: 'setup-wizard',
5272
testMatch: /setup-wizard\.spec\.ts/,
73+
fullyParallel: false,
5374
workers: 1,
5475
use: { ...devices['Desktop Chrome'] },
5576
},

0 commit comments

Comments
 (0)