Files
HOA_Financial_Platform/tests/fixtures/auth.fixture.ts
JoeBot dfd1bccb89 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>
2026-04-05 12:40:05 -04:00

109 lines
3.0 KiB
TypeScript

/**
* Authentication fixture for E2E tests.
*
* Provides helpers to authenticate via the API and manage JWT tokens.
* The auth.setup.ts project uses this to create persistent auth state
* that other tests reuse (avoiding login in every test).
*/
import { type APIRequestContext, type BrowserContext } from '@playwright/test';
import { TEST_USERS } from './test-data';
const API_BASE = process.env.API_BASE_URL || 'http://localhost/api';
export interface AuthTokens {
accessToken: string;
user: Record<string, unknown>;
organizations: Array<Record<string, unknown>>;
}
/**
* Login via the API and return tokens.
* Uses Playwright's request context (no browser needed).
*/
export async function apiLogin(
request: APIRequestContext,
credentials: { email: string; password: string } = TEST_USERS.treasurer,
): Promise<AuthTokens> {
const response = await request.post(`${API_BASE}/auth/login`, {
data: {
email: credentials.email,
password: credentials.password,
},
});
if (!response.ok()) {
const body = await response.text();
throw new Error(`Login failed (${response.status()}): ${body}`);
}
return response.json();
}
/**
* Switch to a specific organization after login.
*/
export async function apiSwitchOrg(
request: APIRequestContext,
accessToken: string,
organizationId: string,
): Promise<AuthTokens> {
const response = await request.post(`${API_BASE}/auth/switch-org`, {
data: { organizationId },
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!response.ok()) {
const body = await response.text();
throw new Error(`Switch org failed (${response.status()}): ${body}`);
}
return response.json();
}
/**
* Create an authenticated API request context with a Bearer token.
* Useful for API-level tests that don't need a browser.
*/
export function authHeaders(accessToken: string) {
return {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
};
}
/**
* Inject auth state into a browser context's localStorage.
* Matches the frontend's Zustand authStore shape.
*/
export async function injectAuthState(
context: BrowserContext,
tokens: AuthTokens,
orgId?: string,
): Promise<void> {
const baseURL = process.env.BASE_URL || 'http://localhost';
// The frontend uses Zustand with persist middleware in localStorage
// under key 'auth-storage'. We inject it so the frontend thinks
// the user is already logged in.
const selectedOrg = orgId
? tokens.organizations.find((o: any) => o.id === orgId)
: tokens.organizations[0];
const authState = {
state: {
token: tokens.accessToken,
user: tokens.user,
organizations: tokens.organizations,
currentOrg: selectedOrg
? { id: (selectedOrg as any).id, name: (selectedOrg as any).name, role: (selectedOrg as any).role }
: null,
},
version: 0,
};
await context.addInitScript((authData) => {
window.localStorage.setItem('auth-storage', JSON.stringify(authData));
}, authState);
}