Files
Classeo/frontend/e2e/admin-responsive-nav.spec.ts
Mathias STRASSER f2f57bb999 fix: Corriger les tests E2E de blocage utilisateur qui échouent de manière intermittente
Sur Firefox, l'hydration Svelte peut prendre plus de temps que sur
Chromium. L'assertion tabs.nth(1).toHaveAttribute manquait de timeout
dans periods.spec.ts, et le formulaire de login n'était pas
complètement hydraté avant interaction dans admin-responsive-nav.spec.ts.
2026-02-25 20:42:52 +01:00

224 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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-responsive-nav@example.com';
const ADMIN_PASSWORD = 'ResponsiveNav123';
const projectRoot = join(__dirname, '../..');
const composeFile = join(projectRoot, 'compose.yaml');
test.describe('Admin Responsive Navigation', () => {
test.beforeAll(async () => {
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.waitForLoadState('networkidle');
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()
]);
}
// =========================================================================
// MOBILE (375×667)
// =========================================================================
test.describe('Mobile (375×667)', () => {
test.use({ viewport: { width: 375, height: 667 } });
test('shows hamburger button and hides desktop nav', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const hamburger = page.getByRole('button', { name: /ouvrir le menu/i });
await expect(hamburger).toBeVisible();
const desktopNav = page.locator('.desktop-nav');
await expect(desktopNav).not.toBeVisible();
});
test('displays current section label', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const label = page.locator('.mobile-section-label');
await expect(label).toBeVisible();
await expect(label).toHaveText('Utilisateurs');
});
test('opens and closes menu via hamburger button', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const hamburger = page.getByRole('button', { name: /ouvrir le menu/i });
// Open
await hamburger.click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// Close via × button
const closeButton = page.getByRole('button', { name: /fermer le menu/i });
await closeButton.click();
await expect(drawer).not.toBeVisible();
});
test('closes menu on overlay click', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// Click overlay (outside drawer)
const overlay = page.locator('.mobile-overlay');
await overlay.click({ position: { x: 350, y: 300 } });
await expect(drawer).not.toBeVisible();
});
test('closes menu on Escape key', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
await page.keyboard.press('Escape');
await expect(drawer).not.toBeVisible();
});
test('shows active state for current section in mobile menu', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
const activeLink = drawer.locator('.mobile-nav-link.active');
await expect(activeLink).toHaveText('Utilisateurs');
});
test('navigates via mobile menu and closes it', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
// Open menu and click Classes
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
await drawer.getByRole('link', { name: 'Classes' }).click();
// Menu should close and page should navigate
await expect(drawer).not.toBeVisible();
await expect(page).toHaveURL(/\/admin\/classes/);
// Section label should update
const label = page.locator('.mobile-section-label');
await expect(label).toHaveText('Classes');
});
});
// =========================================================================
// TABLET (768×1024)
// =========================================================================
test.describe('Tablet (768×1024)', () => {
test.use({ viewport: { width: 768, height: 1024 } });
test('shows hamburger button (below 1200px)', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const hamburger = page.getByRole('button', { name: /ouvrir le menu/i });
await expect(hamburger).toBeVisible();
const desktopNav = page.locator('.desktop-nav');
await expect(desktopNav).not.toBeVisible();
});
test('drawer opens and works', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// All nav links should be visible in drawer
await expect(drawer.getByRole('link', { name: 'Utilisateurs' })).toBeVisible();
await expect(drawer.getByRole('link', { name: 'Classes' })).toBeVisible();
await expect(drawer.getByRole('link', { name: 'Matières' })).toBeVisible();
});
});
// =========================================================================
// DESKTOP (1280×800)
// =========================================================================
test.describe('Desktop (1280×800)', () => {
test.use({ viewport: { width: 1280, height: 800 } });
test('hides hamburger and shows desktop nav', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const hamburger = page.getByRole('button', { name: /ouvrir le menu/i });
await expect(hamburger).not.toBeVisible();
const desktopNav = page.locator('.desktop-nav');
await expect(desktopNav).toBeVisible();
});
test('desktop nav shows all navigation links', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const nav = page.locator('.desktop-nav');
await expect(nav.getByRole('link', { name: 'Utilisateurs' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Classes' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Matières' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Affectations' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Périodes' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Pédagogie' })).toBeVisible();
});
test('hides mobile section label', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const label = page.locator('.mobile-section-label');
await expect(label).not.toBeVisible();
});
});
});