fix(ci): Corriger les tests E2E en CI

Plusieurs problèmes empêchaient les tests E2E de passer en CI :

1. Healthcheck : L'endpoint /api nécessite une authentification et
   retournait 401, causant l'échec du healthcheck. Remplacé par
   /api/docs qui est public.

2. Mailer : L'activation de compte déclenche l'envoi d'un email via
   mailpit, qui n'est pas disponible en CI. Ajout d'une variable
   d'environnement MAILER_DSN=null://null pour désactiver l'envoi.

3. Token partagé : Chaque navigateur (chromium, firefox, webkit)
   consommait le même token, causant des échecs pour les suivants.
   Maintenant chaque navigateur crée son propre token dans beforeAll
   avec un email unique (e2e-{browser}@example.com).

4. Nettoyage : Suppression de test-utils.ts et global-setup simplifié
   car la création de token est maintenant dans le fichier de test.
This commit is contained in:
2026-01-31 21:14:06 +01:00
parent c5e6c1d810
commit 6889c67a44
5 changed files with 76 additions and 81 deletions

View File

@@ -1,5 +1,48 @@
import { test, expect } from '@playwright/test';
import { getTestToken } from './test-utils';
import { execSync } from 'child_process';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Each browser project gets its own token to avoid conflicts
let testToken: string | null = null;
// eslint-disable-next-line no-empty-pattern
test.beforeAll(async ({ }, testInfo) => {
const browserName = testInfo.project.name;
// Create a unique token for this browser project
try {
const projectRoot = join(__dirname, '../..');
const composeFile = join(projectRoot, 'compose.yaml');
const email = `e2e-${browserName}@example.com`;
const result = execSync(
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-activation-token --email=${email} 2>&1`,
{ encoding: 'utf-8' }
);
const tokenMatch = result.match(/Token\s+([a-f0-9-]{36})/i);
if (tokenMatch) {
testToken = tokenMatch[1];
// eslint-disable-next-line no-console
console.warn(`[${browserName}] Test token created: ${testToken}`);
} else {
console.error(`[${browserName}] Could not extract token from output:`, result);
}
} catch (error) {
console.error(`[${browserName}] Failed to create test token:`, error);
}
});
function getToken(): string {
if (!testToken) {
throw new Error('No test token available. Make sure Docker is running.');
}
return testToken;
}
test.describe('Account Activation Flow', () => {
test.describe('Token Validation', () => {
@@ -23,7 +66,7 @@ test.describe('Account Activation Flow', () => {
test.describe('Password Form', () => {
test('validates password requirements in real-time', async ({ page }) => {
const token = getTestToken();
const token = getToken();
await page.goto(`/activate/${token}`);
// Wait for form to be visible (token must be valid)
@@ -54,7 +97,7 @@ test.describe('Account Activation Flow', () => {
});
test('requires password confirmation to match', async ({ page }) => {
const token = getTestToken();
const token = getToken();
await page.goto(`/activate/${token}`);
const form = page.locator('form');
@@ -74,7 +117,7 @@ test.describe('Account Activation Flow', () => {
});
test('submit button is disabled until form is valid', async ({ page }) => {
const token = getTestToken();
const token = getToken();
await page.goto(`/activate/${token}`);
const form = page.locator('form');
@@ -96,7 +139,7 @@ test.describe('Account Activation Flow', () => {
test.describe('Establishment Info Display', () => {
test('shows establishment name and role when token is valid', async ({ page }) => {
const token = getTestToken();
const token = getToken();
await page.goto(`/activate/${token}`);
const form = page.locator('form');
@@ -111,7 +154,7 @@ test.describe('Account Activation Flow', () => {
test.describe('Password Visibility Toggle', () => {
test('toggles password visibility', async ({ page }) => {
const token = getTestToken();
const token = getToken();
await page.goto(`/activate/${token}`);
const form = page.locator('form');
@@ -135,21 +178,31 @@ test.describe('Account Activation Flow', () => {
test.describe('Full Activation Flow', () => {
test('activates account and redirects to login', async ({ page }) => {
const token = getTestToken();
const token = getToken();
await page.goto(`/activate/${token}`);
const form = page.locator('form');
await expect(form).toBeVisible({ timeout: 5000 });
const submitButton = page.getByRole('button', { name: /activer mon compte/i });
// Button should be disabled initially (no password yet)
await expect(submitButton).toBeDisabled();
// Fill valid password
await page.locator('#password').fill('SecurePass123');
await page.locator('#passwordConfirmation').fill('SecurePass123');
// Submit
await page.getByRole('button', { name: /activer mon compte/i }).click();
// Wait for validation to complete - button should now be enabled
await expect(submitButton).toBeEnabled({ timeout: 2000 });
// Should redirect to login with success message
await expect(page).toHaveURL(/\/login\?activated=true/);
// Submit and wait for navigation
await Promise.all([
page.waitForURL(/\/login\?activated=true/, { timeout: 10000 }),
submitButton.click()
]);
// Verify success message
await expect(page.getByText(/compte a été activé avec succès/i)).toBeVisible();
});
});