Files
Classeo/frontend/e2e/super-admin.spec.ts
Mathias STRASSER 0951322d71 feat: Permettre au super admin de se connecter et accéder à son dashboard
Le super admin (table super_admins, master DB) ne pouvait pas se connecter
via /api/login car ce firewall n'utilisait que le provider tenant. De même,
le JWT n'était pas enrichi pour les super admins, l'endpoint /api/me/roles
les rejetait, et le frontend redirigeait systématiquement vers /dashboard.

Un chain provider (super_admin + tenant) résout l'authentification,
le JwtPayloadEnricher et MyRolesProvider gèrent désormais les deux types
d'utilisateurs, et le frontend redirige selon le rôle après login.
2026-02-18 10:15:47 +01:00

209 lines
7.0 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 projectRoot = join(__dirname, '../..');
const composeFile = join(projectRoot, 'compose.yaml');
// Extract port from PLAYWRIGHT_BASE_URL or use default (4173 matches playwright.config.ts)
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 SA_PASSWORD = 'SuperAdmin123';
const REGULAR_PASSWORD = 'TestPassword123';
function getSuperAdminEmail(browserName: string): string {
return `e2e-sadmin-${browserName}@test.com`;
}
function getRegularUserEmail(browserName: string): string {
return `e2e-sadmin-regular-${browserName}@example.com`;
}
// eslint-disable-next-line no-empty-pattern
test.beforeAll(async ({}, testInfo) => {
const browserName = testInfo.project.name;
const saEmail = getSuperAdminEmail(browserName);
const regularEmail = getRegularUserEmail(browserName);
try {
// Create a test super admin
const saResult = execSync(
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-super-admin --email=${saEmail} --password=${SA_PASSWORD} 2>&1`,
{ encoding: 'utf-8' }
);
console.warn(
`[${browserName}] Super admin created or exists:`,
saResult.includes('already exists') ? 'exists' : 'created'
);
// Create a regular user (for access control test)
const userResult = execSync(
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --email=${regularEmail} --password=${REGULAR_PASSWORD} 2>&1`,
{ encoding: 'utf-8' }
);
console.warn(
`[${browserName}] Regular user created or exists:`,
userResult.includes('already exists') ? 'exists' : 'created'
);
} catch (error) {
console.error(`[${browserName}] Failed to create test users:`, error);
}
});
async function loginAsSuperAdmin(
page: import('@playwright/test').Page,
email: string
) {
await page.goto(`${ALPHA_URL}/login`);
await expect(page.getByRole('heading', { name: /connexion/i })).toBeVisible();
await page.locator('#email').fill(email);
await page.locator('#password').fill(SA_PASSWORD);
const submitButton = page.getByRole('button', { name: /se connecter/i });
await Promise.all([
page.waitForURL('**/super-admin/dashboard', { timeout: 30000 }),
submitButton.click()
]);
}
test.describe('Super Admin', () => {
test.describe('Login & Redirect', () => {
test('super admin login redirects to /super-admin/dashboard', async ({ page }, testInfo) => {
const email = getSuperAdminEmail(testInfo.project.name);
await loginAsSuperAdmin(page, email);
await expect(page).toHaveURL(/\/super-admin\/dashboard/);
});
});
test.describe('Dashboard', () => {
test('dashboard displays stats cards', async ({ page }, testInfo) => {
const email = getSuperAdminEmail(testInfo.project.name);
await loginAsSuperAdmin(page, email);
// The dashboard should show stat cards
await expect(page.locator('.stat-card').first()).toBeVisible({ timeout: 10000 });
// Verify dashboard heading
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
});
test.describe('Navigation', () => {
test('navigates to establishments page', async ({ page }, testInfo) => {
const email = getSuperAdminEmail(testInfo.project.name);
await loginAsSuperAdmin(page, email);
const etablissementsLink = page.getByRole('link', { name: /établissements/i });
await Promise.all([
page.waitForURL('**/super-admin/establishments', { timeout: 10000 }),
etablissementsLink.click()
]);
await expect(page).toHaveURL(/\/super-admin\/establishments/);
await expect(
page.getByRole('heading', { name: /établissements/i })
).toBeVisible();
});
test('establishments page has create button', async ({ page }, testInfo) => {
const email = getSuperAdminEmail(testInfo.project.name);
await loginAsSuperAdmin(page, email);
// Navigate via SPA link (page.goto would reload and lose in-memory token)
const etablissementsLink = page.getByRole('link', { name: /établissements/i });
await Promise.all([
page.waitForURL('**/super-admin/establishments', { timeout: 10000 }),
etablissementsLink.click()
]);
await expect(page.getByRole('heading', { name: /établissements/i })).toBeVisible({
timeout: 10000
});
// Check "Nouvel établissement" button/link
await expect(
page.getByRole('link', { name: /nouvel établissement/i })
).toBeVisible();
});
});
test.describe('Create Establishment Form', () => {
test('new establishment form has required fields', async ({ page }, testInfo) => {
const email = getSuperAdminEmail(testInfo.project.name);
await loginAsSuperAdmin(page, email);
// Navigate via SPA links (page.goto would reload and lose in-memory token)
const etablissementsLink = page.getByRole('link', { name: /établissements/i });
await Promise.all([
page.waitForURL('**/super-admin/establishments', { timeout: 10000 }),
etablissementsLink.click()
]);
const newLink = page.getByRole('link', { name: /nouvel établissement/i });
await expect(newLink).toBeVisible({ timeout: 10000 });
await Promise.all([
page.waitForURL('**/super-admin/establishments/new', { timeout: 10000 }),
newLink.click()
]);
// Verify form fields
await expect(page.locator('#name')).toBeVisible({ timeout: 10000 });
await expect(page.locator('#subdomain')).toBeVisible();
await expect(page.locator('#adminEmail')).toBeVisible();
// Submit button should be disabled when empty
const submitButton = page.getByRole('button', {
name: /créer l'établissement/i
});
await expect(submitButton).toBeDisabled();
// Fill in the form
await page.locator('#name').fill('École Test E2E');
await page.locator('#adminEmail').fill('admin-e2e@test.com');
// Subdomain should be auto-generated
await expect(page.locator('#subdomain')).not.toHaveValue('');
// Submit button should be enabled
await expect(submitButton).toBeEnabled();
});
});
test.describe('Access Control', () => {
test('regular user is redirected away from /super-admin', async ({ page }, testInfo) => {
const regularEmail = getRegularUserEmail(testInfo.project.name);
// Login as regular user on alpha tenant
await page.goto(`${ALPHA_URL}/login`);
await page.locator('#email').fill(regularEmail);
await page.locator('#password').fill(REGULAR_PASSWORD);
const submitButton = page.getByRole('button', { name: /se connecter/i });
await Promise.all([
page.waitForURL('**/dashboard', { timeout: 30000 }),
submitButton.click()
]);
// Try to navigate to super-admin area
await page.goto(`${ALPHA_URL}/super-admin/dashboard`);
// Should be redirected away (to /dashboard since not super admin)
await expect(page).not.toHaveURL(/\/super-admin/, { timeout: 10000 });
});
});
});