Files
HOA_Financial_Platform/tests/api/auth.api.spec.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

125 lines
3.9 KiB
TypeScript

/**
* API regression tests for authentication endpoints.
*
* Tests the NestJS auth controller directly via HTTP.
* No browser needed — uses Playwright's request context.
*/
import { test, expect } from '@playwright/test';
import { TEST_USERS } from '../fixtures/test-data';
const API_BASE = process.env.API_BASE_URL || 'http://localhost/api';
test.describe('POST /api/auth/login', () => {
test('should return access token for valid credentials', async ({ request }) => {
const response = await request.post(`${API_BASE}/auth/login`, {
data: {
email: TEST_USERS.treasurer.email,
password: TEST_USERS.treasurer.password,
},
});
expect(response.status()).toBe(201);
const body = await response.json();
expect(body).toHaveProperty('accessToken');
expect(body).toHaveProperty('user');
expect(body.user).toHaveProperty('email', TEST_USERS.treasurer.email);
expect(body).toHaveProperty('organizations');
expect(Array.isArray(body.organizations)).toBe(true);
});
test('should return 401 for invalid password', async ({ request }) => {
const response = await request.post(`${API_BASE}/auth/login`, {
data: {
email: TEST_USERS.treasurer.email,
password: 'WrongPassword123!',
},
});
expect(response.status()).toBe(401);
});
test('should return 401 for non-existent user', async ({ request }) => {
const response = await request.post(`${API_BASE}/auth/login`, {
data: {
email: 'nonexistent@example.com',
password: 'AnyPassword123!',
},
});
expect(response.status()).toBe(401);
});
test('should set httpOnly refresh cookie on success', async ({ request }) => {
const response = await request.post(`${API_BASE}/auth/login`, {
data: {
email: TEST_USERS.treasurer.email,
password: TEST_USERS.treasurer.password,
},
});
expect(response.status()).toBe(201);
// Check for Set-Cookie header with the refresh token
const setCookie = response.headers()['set-cookie'] || '';
expect(setCookie).toContain('ledgeriq_rt');
expect(setCookie).toContain('HttpOnly');
});
});
test.describe('GET /api/auth/profile', () => {
test('should return 401 without auth header', async ({ request }) => {
const response = await request.get(`${API_BASE}/auth/profile`);
expect(response.status()).toBe(401);
});
test('should return user profile with valid token', async ({ request }) => {
// Login first
const loginResponse = await request.post(`${API_BASE}/auth/login`, {
data: {
email: TEST_USERS.treasurer.email,
password: TEST_USERS.treasurer.password,
},
});
const { accessToken } = await loginResponse.json();
// Fetch profile
const response = await request.get(`${API_BASE}/auth/profile`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(response.status()).toBe(200);
const profile = await response.json();
expect(profile).toHaveProperty('email', TEST_USERS.treasurer.email);
});
});
test.describe('POST /api/auth/logout', () => {
test('should return success and clear cookie', async ({ request }) => {
// Login first to get the cookie
await request.post(`${API_BASE}/auth/login`, {
data: {
email: TEST_USERS.treasurer.email,
password: TEST_USERS.treasurer.password,
},
});
// Logout
const response = await request.post(`${API_BASE}/auth/logout`);
expect(response.status()).toBe(200);
const body = await response.json();
expect(body).toEqual({ success: true });
});
});
test.describe('POST /api/auth/refresh', () => {
test('should return 400 without refresh cookie', async ({ request }) => {
// Create a fresh context without cookies
const response = await request.post(`${API_BASE}/auth/refresh`);
// Should fail — no refresh token cookie
expect([400, 401]).toContain(response.status());
});
});