feat: add Playwright E2E and API regression test suite
Production-ready test infrastructure with Page Object Model pattern, reusable fixtures for auth/DB/test-data, and example tests covering login flow, dashboard, accounts CRUD API, and visual regression. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
87
tests/fixtures/base.fixture.ts
vendored
Normal file
87
tests/fixtures/base.fixture.ts
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Base Playwright fixture that combines auth + DB helpers.
|
||||
*
|
||||
* Extends the default `test` object so every test file can
|
||||
* import from here and get typed access to fixtures.
|
||||
*
|
||||
* Usage:
|
||||
* import { test, expect } from '../fixtures/base.fixture';
|
||||
* test('my test', async ({ authedPage, apiContext }) => { ... });
|
||||
*/
|
||||
|
||||
import { test as base, type Page, type APIRequestContext } from '@playwright/test';
|
||||
import { apiLogin, apiSwitchOrg, authHeaders, type AuthTokens } from './auth.fixture';
|
||||
import { testDb } from './db.fixture';
|
||||
import { TEST_USERS } from './test-data';
|
||||
|
||||
const API_BASE = process.env.API_BASE_URL || 'http://localhost/api';
|
||||
|
||||
type TestFixtures = {
|
||||
/** Pre-authenticated API request context with Bearer token */
|
||||
authedRequest: APIRequestContext & { _tokens: AuthTokens };
|
||||
/** Access token string for manual header construction */
|
||||
accessToken: string;
|
||||
};
|
||||
|
||||
type WorkerFixtures = {
|
||||
/** Shared database connection (one per worker) */
|
||||
db: typeof testDb;
|
||||
};
|
||||
|
||||
export const test = base.extend<TestFixtures, WorkerFixtures>({
|
||||
/**
|
||||
* Worker-scoped database connection.
|
||||
* Connects once per worker, disconnects when the worker exits.
|
||||
*/
|
||||
db: [
|
||||
async ({}, use) => {
|
||||
await testDb.connect();
|
||||
await use(testDb);
|
||||
await testDb.disconnect();
|
||||
},
|
||||
{ scope: 'worker' },
|
||||
],
|
||||
|
||||
/**
|
||||
* Per-test authenticated API request context.
|
||||
* Logs in as the default test user and attaches the Bearer token.
|
||||
*/
|
||||
authedRequest: async ({ playwright }, use) => {
|
||||
const requestContext = await playwright.request.newContext({
|
||||
baseURL: API_BASE,
|
||||
});
|
||||
|
||||
const tokens = await apiLogin(requestContext, TEST_USERS.treasurer);
|
||||
|
||||
// If user belongs to orgs, switch to the first one
|
||||
let finalTokens = tokens;
|
||||
if (tokens.organizations?.length > 0) {
|
||||
const orgId = (tokens.organizations[0] as any).id;
|
||||
try {
|
||||
finalTokens = await apiSwitchOrg(requestContext, tokens.accessToken, orgId);
|
||||
} catch {
|
||||
// switch-org may not be needed if token already scoped
|
||||
finalTokens = tokens;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new context with the auth header baked in
|
||||
const authedContext = await playwright.request.newContext({
|
||||
baseURL: API_BASE,
|
||||
extraHTTPHeaders: authHeaders(finalTokens.accessToken),
|
||||
});
|
||||
|
||||
// Attach tokens for tests that need them
|
||||
(authedContext as any)._tokens = finalTokens;
|
||||
|
||||
await use(authedContext as any);
|
||||
await authedContext.dispose();
|
||||
await requestContext.dispose();
|
||||
},
|
||||
|
||||
accessToken: async ({ authedRequest }, use) => {
|
||||
await use((authedRequest as any)._tokens.accessToken);
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from '@playwright/test';
|
||||
Reference in New Issue
Block a user