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>
141 lines
4.3 KiB
TypeScript
141 lines
4.3 KiB
TypeScript
/**
|
|
* API regression tests for the Accounts endpoint.
|
|
*
|
|
* Tests CRUD operations on /api/accounts.
|
|
* Requires authentication — logs in first to get a Bearer token.
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { apiLogin, apiSwitchOrg, authHeaders } from '../fixtures/auth.fixture';
|
|
import { TEST_USERS, TEST_PREFIX } from '../fixtures/test-data';
|
|
|
|
const API_BASE = process.env.API_BASE_URL || 'http://localhost/api';
|
|
|
|
let accessToken: string;
|
|
|
|
test.beforeAll(async ({ request }) => {
|
|
// Login and switch to test org
|
|
const tokens = await apiLogin(request, TEST_USERS.treasurer);
|
|
if (tokens.organizations?.length > 0) {
|
|
const orgId = (tokens.organizations[0] as any).id;
|
|
try {
|
|
const switched = await apiSwitchOrg(request, tokens.accessToken, orgId);
|
|
accessToken = switched.accessToken;
|
|
} catch {
|
|
accessToken = tokens.accessToken;
|
|
}
|
|
} else {
|
|
accessToken = tokens.accessToken;
|
|
}
|
|
});
|
|
|
|
test.describe('GET /api/accounts', () => {
|
|
test('should return accounts list', async ({ request }) => {
|
|
const response = await request.get(`${API_BASE}/accounts`, {
|
|
headers: authHeaders(accessToken),
|
|
});
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const accounts = await response.json();
|
|
expect(Array.isArray(accounts)).toBe(true);
|
|
});
|
|
|
|
test('should return 401 without auth', async ({ request }) => {
|
|
const response = await request.get(`${API_BASE}/accounts`);
|
|
expect(response.status()).toBe(401);
|
|
});
|
|
|
|
test('should filter by fund type', async ({ request }) => {
|
|
const response = await request.get(`${API_BASE}/accounts?fundType=operating`, {
|
|
headers: authHeaders(accessToken),
|
|
});
|
|
|
|
expect(response.status()).toBe(200);
|
|
const accounts = await response.json();
|
|
expect(Array.isArray(accounts)).toBe(true);
|
|
});
|
|
});
|
|
|
|
test.describe('Accounts CRUD', () => {
|
|
let createdAccountId: string;
|
|
|
|
test('should create a new account', async ({ request }) => {
|
|
const response = await request.post(`${API_BASE}/accounts`, {
|
|
headers: authHeaders(accessToken),
|
|
data: {
|
|
name: `${TEST_PREFIX}Operating Checking`,
|
|
number: '1099',
|
|
accountType: 'asset',
|
|
fundType: 'operating',
|
|
description: 'E2E test account — safe to delete',
|
|
},
|
|
});
|
|
|
|
expect(response.status()).toBe(201);
|
|
|
|
const account = await response.json();
|
|
expect(account).toHaveProperty('id');
|
|
expect(account.name).toBe(`${TEST_PREFIX}Operating Checking`);
|
|
createdAccountId = account.id;
|
|
});
|
|
|
|
test('should get the created account', async ({ request }) => {
|
|
test.skip(!createdAccountId, 'No account was created');
|
|
|
|
const response = await request.get(`${API_BASE}/accounts/${createdAccountId}`, {
|
|
headers: authHeaders(accessToken),
|
|
});
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const account = await response.json();
|
|
expect(account.id).toBe(createdAccountId);
|
|
expect(account.name).toBe(`${TEST_PREFIX}Operating Checking`);
|
|
});
|
|
|
|
test('should update the account', async ({ request }) => {
|
|
test.skip(!createdAccountId, 'No account was created');
|
|
|
|
const response = await request.put(`${API_BASE}/accounts/${createdAccountId}`, {
|
|
headers: authHeaders(accessToken),
|
|
data: {
|
|
name: `${TEST_PREFIX}Updated Checking`,
|
|
description: 'Updated by E2E test',
|
|
},
|
|
});
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const account = await response.json();
|
|
expect(account.name).toBe(`${TEST_PREFIX}Updated Checking`);
|
|
});
|
|
|
|
test('should appear in accounts list', async ({ request }) => {
|
|
test.skip(!createdAccountId, 'No account was created');
|
|
|
|
const response = await request.get(`${API_BASE}/accounts`, {
|
|
headers: authHeaders(accessToken),
|
|
});
|
|
|
|
const accounts = await response.json();
|
|
const found = accounts.find((a: any) => a.id === createdAccountId);
|
|
expect(found).toBeTruthy();
|
|
expect(found.name).toBe(`${TEST_PREFIX}Updated Checking`);
|
|
});
|
|
});
|
|
|
|
test.describe('GET /api/accounts/trial-balance', () => {
|
|
test('should return trial balance data', async ({ request }) => {
|
|
const response = await request.get(`${API_BASE}/accounts/trial-balance`, {
|
|
headers: authHeaders(accessToken),
|
|
});
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
// Trial balance returns an object or array with debit/credit totals
|
|
expect(data).toBeDefined();
|
|
});
|
|
});
|