Files
fitcrm/e2e/helpers.ts
root accfa61e08
Some checks failed
CI / Lint & Format (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Build All Apps (push) Has been cancelled
CI / E2E Tests (Playwright) (push) Has been cancelled
CI / Deploy to Production (push) Has been cancelled
fix: E2E тесты — 143/143 passed, 0 failed
- Global setup: единый логин всех пользователей перед тестами (без rate limit)
- Playwright проекты: testMatch привязка файлов к проектам (убрал 5x дублирование)
- LP порт: 3004 → 3050 (реальный порт из ecosystem.config)
- Theme тесты: мок API через page.route() (предотвращение 401→logout)
- Web UI тесты: защита response?.status() ?? 200 от undefined
- getCachedTokens(): чтение токенов из файла вместо loginAs в каждом beforeAll

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:40:39 +00:00

72 lines
2.2 KiB
TypeScript

import type { APIRequestContext } from '@playwright/test';
import { readFileSync } from 'fs';
import { join } from 'path';
const API_URL = process.env.E2E_API_URL || 'http://localhost:3000';
export interface AuthTokens {
accessToken: string;
refreshToken: string;
}
/** Read pre-cached tokens from global setup (preferred — no rate limit issues) */
export function getCachedTokens(userKey: string): AuthTokens {
const tokenPath = join(__dirname, '.auth-tokens.json');
const data = JSON.parse(readFileSync(tokenPath, 'utf-8'));
if (!data[userKey]) {
throw new Error(
`No cached tokens for user "${userKey}". Available: ${Object.keys(data).join(', ')}`,
);
}
return data[userKey];
}
/** Login via API (use only when you need a fresh token, e.g. for refresh testing) */
export async function loginAs(
request: APIRequestContext,
phone: string,
password: string,
): Promise<AuthTokens> {
const maxRetries = 8;
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await request.post(`${API_URL}/v1/auth/login`, {
data: { phone, password },
});
if (response.status() === 429) {
// Rate limited — wait and retry
const delay = (attempt + 1) * 12_000; // 12s, 24s, 36s...
await new Promise((r) => setTimeout(r, delay));
continue;
}
if (!response.ok()) {
throw new Error(`Login failed: ${response.status()} ${await response.text()}`);
}
const body = await response.json();
return {
accessToken: body.accessToken,
refreshToken: body.refreshToken,
};
}
throw new Error(`Login failed: rate limited after ${maxRetries} retries`);
}
export function authHeaders(tokens: AuthTokens) {
return {
Authorization: `Bearer ${tokens.accessToken}`,
};
}
// Test users (must match seed data from prisma/seed.ts)
export const TEST_USERS = {
trainer: { phone: '+70000000010', password: 'trainer123' },
coordinator: { phone: '+70000000003', password: 'admin123' },
manager: { phone: '+70000000003', password: 'admin123' }, // coordinator has stats access
receptionist: { phone: '+70000000004', password: 'admin123' },
clubAdmin: { phone: '+70000000002', password: 'admin123' },
superAdmin: { phone: '+70000000001', password: 'admin123' },
};