Files
Classeo/frontend/e2e/user-creation.spec.ts
Mathias STRASSER 76e16db0d8 feat: Pagination et recherche des sections admin
Les listes admin (utilisateurs, classes, matières, affectations) chargeaient
toutes les données d'un coup, ce qui dégradait l'expérience avec un volume
croissant. La pagination côté serveur existait dans la config API Platform
mais aucun Provider ne l'exploitait.

Cette implémentation ajoute la pagination serveur (30 items/page, max 100)
avec recherche textuelle sur toutes les sections, des composants frontend
réutilisables (Pagination + SearchInput avec debounce), et la synchronisation
URL pour le partage de liens filtrés.

Les Query valident leurs paramètres (clamp page/limit, trim search) pour
éviter les abus. Les affectations utilisent des lookup maps pour résoudre
les noms sans N+1 queries. Les pages admin gèrent les race conditions
via AbortController.
2026-02-15 13:54:51 +01:00

90 lines
3.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const baseUrl = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:4173';
const urlMatch = baseUrl.match(/:(\d+)$/);
const PORT = urlMatch ? urlMatch[1] : '4173';
const ALPHA_URL = `http://ecole-alpha.classeo.local:${PORT}`;
const ADMIN_EMAIL = 'e2e-creation-admin@example.com';
const ADMIN_PASSWORD = 'CreationTest123';
const UNIQUE_SUFFIX = Date.now();
const INVITED_EMAIL = `e2e-invited-prof-${UNIQUE_SUFFIX}@example.com`;
test.describe('User Creation', () => {
test.describe.configure({ mode: 'serial' });
test.beforeAll(async () => {
const projectRoot = join(__dirname, '../..');
const composeFile = join(projectRoot, 'compose.yaml');
execSync(
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=${ADMIN_EMAIL} --password=${ADMIN_PASSWORD} --role=ROLE_ADMIN 2>&1`,
{ encoding: 'utf-8' }
);
});
async function loginAsAdmin(page: import('@playwright/test').Page) {
await page.goto(`${ALPHA_URL}/login`);
await page.locator('#email').fill(ADMIN_EMAIL);
await page.locator('#password').fill(ADMIN_PASSWORD);
await Promise.all([
page.waitForURL(/\/dashboard/, { timeout: 30000 }),
page.getByRole('button', { name: /se connecter/i }).click()
]);
}
test('admin can invite a user with roles array', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
// Wait for users table or empty state to load
await expect(
page.locator('.users-table, .empty-state')
).toBeVisible({ timeout: 10000 });
// Click "Inviter un utilisateur"
await page.getByRole('button', { name: /inviter un utilisateur/i }).first().click();
// Modal should appear
await expect(page.locator('#modal-title')).toBeVisible();
await expect(page.locator('#modal-title')).toHaveText('Inviter un utilisateur');
// Fill in the form
await page.locator('#user-firstname').fill('Marie');
await page.locator('#user-lastname').fill('Curie');
await page.locator('#user-email').fill(INVITED_EMAIL);
// Select "Enseignant" role via checkbox (this sends roles[] without role singular)
await page.locator('.role-checkbox-label', { hasText: 'Enseignant' }).click();
// Submit the form (target the modal's submit button specifically)
const modal = page.locator('.modal');
await modal.getByRole('button', { name: "Envoyer l'invitation" }).click();
// Verify success message
await expect(page.locator('.alert-success')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.alert-success')).toContainText(INVITED_EMAIL);
// Search for the newly created user (pagination may hide them beyond page 1)
const searchInput = page.locator('input[type="search"]');
await searchInput.fill(INVITED_EMAIL);
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
// Verify the user appears in the table
await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 });
const newUserRow = page.locator('tr', { has: page.locator(`text=${INVITED_EMAIL}`) });
await expect(newUserRow).toBeVisible();
await expect(newUserRow).toContainText('Marie');
await expect(newUserRow).toContainText('Curie');
await expect(newUserRow).toContainText('Enseignant');
await expect(newUserRow).toContainText('En attente');
});
});