Merge pull request #37 from R0m1k3/claude/mfa-lever-barriere-enrolement
Claude/mfa lever barriere enrolement
This commit is contained in:
29 files changed
+7179
-270
No files matched your search
@@ -117,10 +117,14 @@ test('une pièce sans politique n’est jamais purgée', async ({ page }) => {
|
||||
await create.getByLabel('Prénom').fill('Garde');
|
||||
await create.getByLabel('Nom', { exact: true }).fill(`Garde${suffix}`);
|
||||
await create.getByLabel('Matricule').fill(`RET${suffix}`);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await create.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await create.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: new RegExp(`Garde${suffix}`) }).click();
|
||||
await page.goto(`${page.url()}/documents`);
|
||||
const upload = page.locator('form').filter({ hasText: 'Déposer' });
|
||||
await upload.getByLabel('Catégorie').selectOption('OTHER');
|
||||
await upload
|
||||
@@ -167,10 +171,14 @@ test('une pièce échue est effectivement effacée', async ({ page }) => {
|
||||
await create.getByLabel('Prénom').fill('Purge');
|
||||
await create.getByLabel('Nom', { exact: true }).fill(`Purge${suffix}`);
|
||||
await create.getByLabel('Matricule').fill(`PUR${suffix}`);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await create.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await create.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: new RegExp(`Purge${suffix}`) }).click();
|
||||
await page.goto(`${page.url()}/documents`);
|
||||
const dossier = page.url();
|
||||
const upload = page.locator('form').filter({ hasText: 'Déposer' });
|
||||
await upload.getByLabel('Catégorie').selectOption('REGISTER');
|
||||
|
||||
@@ -31,11 +31,18 @@ async function createEmployee(page: Page, tag: string) {
|
||||
await form.getByLabel('Prénom').fill('Inès');
|
||||
await form.getByLabel('Nom', { exact: true }).fill(lastName);
|
||||
await form.getByLabel('Matricule').fill(`DOC${suffix}`);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await form.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await form.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: new RegExp(lastName) }).click();
|
||||
await expect(page.getByRole('heading', { name: new RegExp(lastName) })).toBeVisible();
|
||||
|
||||
// La fiche s'ouvre sur les informations personnelles : les pièces sont un
|
||||
// onglet, et une route à part entière.
|
||||
await page.goto(`${page.url()}/documents`);
|
||||
return { lastName, url: page.url() };
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Fiche salarié — onglets et saisie du dossier.
|
||||
*
|
||||
* Le dossier personnel est la première chose qu'on remplit après une embauche,
|
||||
* et la dernière qu'on relit avant une déclaration. Ce parcours éprouve donc ce
|
||||
* que voit un gestionnaire : les onglets tiennent leur route, et ce qui est
|
||||
* saisi est encore là au rechargement.
|
||||
*/
|
||||
|
||||
async function createEmployee(page: Page) {
|
||||
const suffix = `${Date.now()}-fiche`;
|
||||
const lastName = `Fiche${suffix}`;
|
||||
|
||||
await page.goto('/equipe');
|
||||
const form = page.locator('form').filter({ hasText: 'Ajouter' });
|
||||
await form.getByLabel('Prénom').fill('Awa');
|
||||
await form.getByLabel('Nom', { exact: true }).fill(lastName);
|
||||
await form.getByLabel('Matricule').fill(`FIC${suffix}`);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await form.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await form.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: new RegExp(lastName) }).click();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: new RegExp(lastName) }),
|
||||
).toBeVisible();
|
||||
|
||||
return { lastName, url: page.url() };
|
||||
}
|
||||
|
||||
test('les onglets de la fiche sont des routes', async ({ page }) => {
|
||||
const employee = await createEmployee(page);
|
||||
|
||||
// Le bandeau ne bouge pas d'un onglet à l'autre : c'est ce qui dit de qui on
|
||||
// parle pendant qu'on navigue.
|
||||
for (const [label, segment] of [
|
||||
['Contrats', '/contrats'],
|
||||
['Planification et accès', '/planification'],
|
||||
['Congés et Absences', '/absences'],
|
||||
['Documents', '/documents'],
|
||||
] as const) {
|
||||
await page.getByRole('link', { name: label, exact: true }).click();
|
||||
await expect(page).toHaveURL(new RegExp(`${segment}$`));
|
||||
await expect(
|
||||
page.getByRole('heading', { name: new RegExp(employee.lastName) }),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
// Rouvrir l'onglet directement par son adresse doit donner le même écran :
|
||||
// une fiche s'envoie par lien.
|
||||
await page.goto(`${employee.url}/contrats`);
|
||||
await expect(page.getByText('Tous les contrats et avenants')).toBeVisible();
|
||||
});
|
||||
|
||||
test('le dossier personnel se saisit et se conserve', async ({ page }) => {
|
||||
const employee = await createEmployee(page);
|
||||
|
||||
await expect(page.getByText('Non renseigné').first()).toBeVisible();
|
||||
await page
|
||||
.getByRole('button', { name: 'Modifier les informations personnelles' })
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Date de naissance').fill('1988-03-12');
|
||||
await page.getByLabel('Lieu de naissance').fill('Nancy');
|
||||
await page.getByLabel('Nationalité').fill('France');
|
||||
await page.getByLabel('Téléphone mobile').fill('+33 6 12 34 56 78');
|
||||
await page.getByLabel('Ville').fill('Frouard');
|
||||
await page.getByRole('button', { name: 'Enregistrer les modifications' }).click();
|
||||
|
||||
await expect(page.getByText('Nancy')).toBeVisible();
|
||||
|
||||
// Le rechargement est la seule preuve qui vaille : un écran qui affiche ce
|
||||
// qu'on vient de taper ne dit rien de ce qui a été écrit.
|
||||
await page.goto(employee.url);
|
||||
await expect(page.getByText('Nancy')).toBeVisible();
|
||||
await expect(page.getByText('Frouard')).toBeVisible();
|
||||
});
|
||||
|
||||
test('une date de naissance à venir est refusée', async ({ page }) => {
|
||||
await createEmployee(page);
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'Modifier les informations personnelles' })
|
||||
.click();
|
||||
await page.getByLabel('Date de naissance').fill('2999-01-01');
|
||||
await page.getByRole('button', { name: 'Enregistrer les modifications' }).click();
|
||||
|
||||
await expect(page.getByText('Date de naissance invalide.')).toBeVisible();
|
||||
});
|
||||
|
||||
test('une embauche pose le contrat en même temps que le dossier', async ({
|
||||
page,
|
||||
}) => {
|
||||
const suffix = `${Date.now()}-embauche`;
|
||||
const lastName = `Embauche${suffix}`;
|
||||
|
||||
await page.goto('/equipe');
|
||||
const form = page.locator('form').filter({ hasText: 'Ajouter' });
|
||||
await form.getByLabel('Prénom').fill('Sofia');
|
||||
await form.getByLabel('Nom', { exact: true }).fill(lastName);
|
||||
await form.getByLabel('Matricule').fill(`EMB${suffix}`);
|
||||
|
||||
// Le contrat est proposé coché : un dossier créé sans lui n'apparaît sur
|
||||
// aucune grille et ne se déclare pas.
|
||||
await expect(
|
||||
form.getByLabel('Ouvrir un contrat maintenant'),
|
||||
).toBeChecked();
|
||||
await form.getByLabel('Début du contrat').fill('2026-01-05');
|
||||
await form.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
// Le contrat est visible depuis l'annuaire, sans passer par la fiche.
|
||||
await expect(page.getByRole('row', { name: new RegExp(lastName) })).toContainText(
|
||||
'CDI',
|
||||
);
|
||||
|
||||
await page.getByRole('link', { name: new RegExp(lastName) }).click();
|
||||
// Le bandeau porte le contrat : c'est ce qui distingue un dossier embauché
|
||||
// d'un dossier ouvert.
|
||||
await expect(page.getByText('Type de contrat')).toBeVisible();
|
||||
await page.getByRole('link', { name: 'Contrats', exact: true }).click();
|
||||
await expect(page.getByText('35 heures hebdomadaires')).toBeVisible();
|
||||
});
|
||||
@@ -26,6 +26,9 @@ async function createEmployee(page: Page, tag: string) {
|
||||
await form.getByLabel('Nom', { exact: true }).fill(lastName);
|
||||
await form.getByLabel('Matricule').fill(`E2E${suffix}`);
|
||||
await form.getByLabel('Adresse électronique').fill(email);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await form.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await form.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
@@ -37,6 +40,10 @@ async function createEmployee(page: Page, tag: string) {
|
||||
page.getByRole('heading', { name: `${firstName} ${lastName}` }),
|
||||
).toBeVisible();
|
||||
|
||||
// L'accès applicatif est porté par l'onglet « Documents », avec les pièces du
|
||||
// dossier : la fiche s'ouvre ailleurs.
|
||||
await page.goto(`${page.url()}/documents`);
|
||||
|
||||
return { firstName, lastName, email, url: page.url() };
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,13 @@ test('un salarié sans compte applicatif est créable', async ({ page }) => {
|
||||
await page.goto('/equipe');
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Équipe' })).toBeVisible();
|
||||
// L'effectif vient de la base, pas du module de démonstration.
|
||||
await expect(page.getByText('E0001')).toBeVisible();
|
||||
// L'effectif vient de la base, pas du module de démonstration : un annuaire
|
||||
// fabriqué à l'écran ne se filtrerait pas par matricule.
|
||||
await page
|
||||
.getByPlaceholder('Rechercher par prénom, nom ou matricule.')
|
||||
.fill('E0001');
|
||||
await expect(page.getByText(/^1 sur \d+ salariés$/)).toBeVisible();
|
||||
await page.goto('/equipe');
|
||||
|
||||
// Nom unique par exécution : la base de test n'est pas remise à zéro entre
|
||||
// deux passages, et un nom fixe finirait par désigner plusieurs salariés.
|
||||
@@ -54,6 +59,9 @@ test('un salarié sans compte applicatif est créable', async ({ page }) => {
|
||||
await form.locator('input[name="firstName"]').fill('Sans');
|
||||
await form.locator('input[name="lastName"]').fill(nom);
|
||||
await form.locator('input[name="employeeNumber"]').fill(matricule);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await form.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await page.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
@@ -64,9 +72,9 @@ test('un salarié sans compte applicatif est créable', async ({ page }) => {
|
||||
|
||||
// Un salarié sans adresse doit exister : la plupart des équipes de vente ne
|
||||
// se connectent jamais à l'outil.
|
||||
await expect(
|
||||
page.getByRole('cell', { name: matricule, exact: true }),
|
||||
).toBeVisible();
|
||||
// Sans compte applicatif, l'annuaire affiche le matricule sous le nom : c'est
|
||||
// la seule adresse à laquelle on désigne ce salarié.
|
||||
await expect(page.getByText(`Matricule ${matricule}`)).toBeVisible();
|
||||
// Le nom vit sur le dossier, pas sur le compte : un salarié sans accès
|
||||
// applicatif doit tout de même figurer nommément au registre du personnel.
|
||||
await expect(
|
||||
|
||||
@@ -163,6 +163,9 @@ async function inviteWithRole(page: Page, roleLabel: string) {
|
||||
await create.getByLabel('Nom', { exact: true }).fill(lastName);
|
||||
await create.getByLabel('Matricule').fill(`ROL${suffix}`);
|
||||
await create.getByLabel('Adresse électronique').fill(email);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await create.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await create.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
@@ -177,6 +180,8 @@ async function inviteWithRole(page: Page, roleLabel: string) {
|
||||
const { assignRole } = await import('./support/db');
|
||||
await assignRole(membershipId, roleLabel);
|
||||
|
||||
// L'invitation est portée par l'onglet « Documents » de la fiche.
|
||||
await page.goto(`/equipe/${membershipId}/documents`);
|
||||
await page
|
||||
.locator('form')
|
||||
.filter({ hasText: 'Adresse d’invitation' })
|
||||
|
||||
@@ -156,10 +156,15 @@ async function createAccessibleEmployee(page: Page) {
|
||||
await form.getByLabel('Nom', { exact: true }).fill(lastName);
|
||||
await form.getByLabel('Matricule').fill(`MFA${suffix}`);
|
||||
await form.getByLabel('Adresse électronique').fill(email);
|
||||
// Le formulaire propose d’ouvrir un contrat d’emblée : ce parcours n’en veut
|
||||
// pas, et un salarié sans contrat doit rester créable.
|
||||
await form.getByLabel('Ouvrir un contrat maintenant').uncheck();
|
||||
await form.getByRole('button', { name: 'Ajouter' }).click();
|
||||
await expect(page.getByText('Salarié ajouté.')).toBeVisible();
|
||||
|
||||
await page.getByRole('link', { name: new RegExp(lastName) }).click();
|
||||
// L'invitation est portée par l'onglet « Documents » de la fiche.
|
||||
await page.goto(`${page.url()}/documents`);
|
||||
await page
|
||||
.locator('form')
|
||||
.filter({ hasText: 'Adresse d’invitation' })
|
||||
|
||||
Reference in new issue
Block a user