Adds the sign-in screen, sign-out, and a server-side guard on every application route. The guard lives in the layout rather than the proxy because the proxy cannot query the database to check whether a session was revoked — and revocation is the reason sessions are stored there. Sign-in returns one message for an unknown account and for a wrong password, and verifies a dummy hash when the account does not exist, so neither the wording nor the timing enumerates staff addresses. An end-to-end test compares the two messages rather than trusting the code to keep them aligned. The shell now shows the signed-in person and their role from the database instead of hardcoded initials. Playwright signs in once in a setup project and shares the cookie; argon2 is deliberately slow, and logging in per test would also drive the shared failed-attempt counter toward a lockout. The seed resets that counter so repeated local runs cannot lock the demo account. Two test locators had to be scoped to the form: Next's route announcer carries role="alert" and an empty string, which silently satisfied the assertion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
20 lines
801 B
TypeScript
20 lines
801 B
TypeScript
import { test as setup, expect } from '@playwright/test';
|
|
|
|
import { STORAGE_STATE } from './storage';
|
|
|
|
/**
|
|
* Ouvre une session une fois et enregistre le cookie pour les autres tests.
|
|
*
|
|
* Chaque test se connecterait sinon, ce qui coûterait un argon2 par test —
|
|
* volontairement lent — et ferait grimper le compteur d'échecs partagé.
|
|
*/
|
|
setup('authentifie la direction', async ({ page }) => {
|
|
await page.goto('/connexion');
|
|
await page.getByLabel('Adresse électronique').fill('direction@example.test');
|
|
await page.getByLabel('Mot de passe').fill('planflow-demo-2026');
|
|
await page.getByRole('button', { name: 'Se connecter' }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: 'Aperçu RH' })).toBeVisible();
|
|
await page.context().storageState({ path: STORAGE_STATE });
|
|
});
|