mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 20:51:53 +00:00
- ESLint 10 flat config racine (typescript-eslint, react-hooks) ; règle ADR-001 codée : no-restricted-imports interdit « minio » partout sauf minio-storage.service.ts (violation vérifiée) ; jobs lint par paquet - Playwright apps/web/e2e : redirection sans jeton, connexion démo 1 clic, coquille (rail actif, chip DÉMO), bascule de rôle chronométrée < 3 s (ADR-002), /design, déconnexion avec purge ; webServer api dist + vite, seed idempotent en globalSetup, PW_CHANNEL=chrome en local - CI : jobs lint + e2e (services PostgreSQL/Redis, artefact playwright-report sur échec) ; vitest restreint à src/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
||
|
||
/**
|
||
* LE parcours de recette R0 : connexion démo 1 clic, coquille fidèle,
|
||
* bascule de rôle < 3 s (critère d'acceptation ADR-002), /design accessible.
|
||
*/
|
||
|
||
test('sans jeton, l’application renvoie sur la connexion (fermée par défaut)', async ({
|
||
page,
|
||
}) => {
|
||
await page.goto('/');
|
||
await expect(page).toHaveURL(/\/connexion$/);
|
||
});
|
||
|
||
test('connexion démo → coquille → bascule de rôle < 3 s → /design', async ({
|
||
page,
|
||
}) => {
|
||
await page.goto('/connexion');
|
||
|
||
// Mode démonstration : 7 comptes seedés, chip d'avertissement
|
||
await expect(page.locator('.sep')).toHaveText('Mode démonstration');
|
||
await expect(page.locator('.demo-liste button')).toHaveCount(7);
|
||
await expect(page.locator('.chip-demo')).toContainText('DEMO_MODE=true');
|
||
|
||
// Connexion 1 clic (Dispatcher)
|
||
await page.getByRole('button', { name: /Salma Idrissi/ }).click();
|
||
await expect(
|
||
page.getByRole('heading', { name: 'Tableau de bord' }),
|
||
).toBeVisible();
|
||
await expect(page.locator('.topbar')).toContainText('Salma Idrissi');
|
||
await expect(page.locator('.topbar .chip-demo')).toHaveText('DÉMO');
|
||
// Rail safran : l'entrée active de la sidebar
|
||
await expect(page.locator('.nav a.actif')).toHaveText(/Tableau de bord/);
|
||
|
||
// Bascule de rôle via la topbar — critère ADR-002 : < 3 s
|
||
const debut = Date.now();
|
||
await page.locator('.compte').click();
|
||
await page.getByRole('menuitem', { name: /Ahmed Benali/ }).click();
|
||
await expect(page.locator('.topbar')).toContainText('Ahmed Benali');
|
||
await expect(page.locator('.topbar')).toContainText('Technicien');
|
||
expect(Date.now() - debut).toBeLessThan(3000);
|
||
|
||
// Référence vivante du design
|
||
await page.goto('/design');
|
||
await expect(
|
||
page.getByRole('heading', { name: /Design — référence vivante/ }),
|
||
).toBeVisible();
|
||
await expect(page.locator('.puce-c').first()).toBeVisible();
|
||
});
|
||
|
||
test('la déconnexion ramène à la connexion et ferme la session', async ({
|
||
page,
|
||
}) => {
|
||
await page.goto('/connexion');
|
||
await page.getByRole('button', { name: /Yasmine Alaoui/ }).click();
|
||
await expect(page.locator('.topbar')).toContainText('Yasmine Alaoui');
|
||
|
||
await page.locator('.compte').click();
|
||
await page.getByRole('menuitem', { name: 'Se déconnecter' }).click();
|
||
await expect(page).toHaveURL(/\/connexion$/);
|
||
|
||
// Retour direct sur / : toujours redirigé (le jeton est bien purgé)
|
||
await page.goto('/');
|
||
await expect(page).toHaveURL(/\/connexion$/);
|
||
});
|