mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 20:51:53 +00:00
- liste OT (filtres, strie rouge, « immédiat ») ; fiche OT : transitions pilotées par allowedTransitions, clôture grisée avec la garde expliquée, bilan codé (6 selects sur référentiels), checklist Fait→N-A→à faire, activité + commentaires, assignation, annulation motivée - nouvel OT : interrupteur « personne bloquée » qui force la priorité - demandes : table + panneau d'approbation (priorité, assignation, approuver → fiche OT), rejet en modale à motif obligatoire, signalement interne ; statut « Résolue » dérivé de l'OT lié - préventif : tuiles réelles, générer + résumé (« regénérer ne double rien »), gabarits administrables ; compteurs : saisie + historique - tableau de bord réel : bandeau urgence cliquable, KPIs, OT par statut, interventions récentes ; accueil dédié aux rôles sans exploitation - urgence traversante : chip topbar pulsante (60 s), badges de nav - GET /assets/options (auth seule) : le Demandeur peut désigner l'appareil qu'il signale — trou débusqué par l'e2e (49 opérations au contrat) - génération préventive durcie : collision de référence RETENTÉE (plus de saut silencieux), P2003 toléré ; 3 runs Jest complets consécutifs verts - 9 tests Playwright (recette R2 officielle rejouée intégralement), 50 tests API (94,6 % / 78,9 %) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
118 lines
6.1 KiB
TypeScript
118 lines
6.1 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
/**
|
|
* Recette R2 (plan de releases) : demande gardien → approbation → OT →
|
|
* intervention → bilan codé → clôture → suivi demandeur ; génération du
|
|
* préventif du mois (idempotente) ; compteur croissant.
|
|
*/
|
|
|
|
const suffix = Date.now().toString(36);
|
|
|
|
test('recette R2 : demande → OT → bilan codé → clôture → suivi demandeur', async ({
|
|
page,
|
|
}) => {
|
|
// 1 · Karim (demandeur) signale depuis l'app
|
|
await page.goto('/connexion');
|
|
await page.getByRole('button', { name: /Karim Doukkali/ }).click();
|
|
await expect(page.getByRole('heading', { name: 'Mes signalements' })).toBeVisible();
|
|
await page.getByRole('link', { name: /Demandes/ }).click();
|
|
await page.getByRole('button', { name: '+ Signaler' }).click();
|
|
await page.getByLabel('Équipement *').selectOption({ index: 1 });
|
|
await page.getByLabel('Décrivez le problème *').fill(`E2E ${suffix} — porte qui claque très fort`);
|
|
await page.getByRole('button', { name: 'Envoyer le signalement' }).click();
|
|
await expect(page.getByRole('cell', { name: new RegExp(suffix) })).toBeVisible();
|
|
|
|
// 2 · Salma (dispatcher) approuve → OT créé, assigné à Ahmed
|
|
await page.locator('.compte').click();
|
|
await page.getByRole('menuitem', { name: /Salma Idrissi/ }).click();
|
|
await page.getByRole('link', { name: /Demandes/ }).click();
|
|
await page.getByRole('cell', { name: new RegExp(suffix) }).click();
|
|
await page.getByLabel('Assigner à').selectOption({ index: 1 }); // Ahmed Benali
|
|
await page.getByRole('button', { name: "Approuver → créer l'OT" }).click();
|
|
|
|
// → fiche OT
|
|
await expect(page.getByRole('heading', { name: new RegExp(suffix) })).toBeVisible();
|
|
await expect(page.locator('.entete-page .st')).toHaveText('Ouvert');
|
|
|
|
// 3 · Démarrer, tenter de clôturer (bloqué : bilan), remplir le bilan, clôturer
|
|
await page.getByRole('button', { name: 'Démarrer' }).click();
|
|
await expect(page.locator('.entete-page .st')).toHaveText('En cours');
|
|
const boutonCloture = page.getByRole('button', { name: "Clôturer l'intervention" });
|
|
await expect(boutonCloture).toBeDisabled(); // garde visible dans l'UI
|
|
await expect(page.locator('.avertissement')).toContainText('bilan incomplet');
|
|
|
|
await page.getByLabel(/État des portes/).selectOption({ label: 'Fonctionnement normal' });
|
|
await page.getByLabel(/Action réalisée/).selectOption({ label: 'Réglage' });
|
|
await page.getByLabel(/Élément concerné/).selectOption({ label: 'Portes' });
|
|
await expect(boutonCloture).toBeEnabled();
|
|
await boutonCloture.click();
|
|
await expect(page.locator('.entete-page .st')).toHaveText('Terminé');
|
|
|
|
// 4 · Karim suit : sa demande est Résolue
|
|
await page.locator('.compte').click();
|
|
await page.getByRole('menuitem', { name: /Karim Doukkali/ }).click();
|
|
await page.getByRole('link', { name: /Demandes/ }).click();
|
|
const ligne = page.locator('tr', { hasText: suffix });
|
|
await expect(ligne.locator('.st')).toHaveText('Résolue');
|
|
});
|
|
|
|
test('préventif : générer le mois — puis regénérer ne double rien', async ({ page }) => {
|
|
await page.goto('/connexion');
|
|
await page.getByRole('button', { name: /Yasmine Alaoui/ }).click();
|
|
await page.getByRole('link', { name: 'Préventif' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Préventif' })).toBeVisible();
|
|
await expect(page.getByText('Essai du parachute')).toBeVisible();
|
|
|
|
await page.getByRole('button', { name: /Générer la grille de/ }).click();
|
|
await expect(page.locator('.gen-info')).toContainText('regénérer ne double rien');
|
|
// Second appel : 0 créé
|
|
await page.getByRole('button', { name: /Générer la grille de/ }).click();
|
|
await expect(page.locator('.gen-info')).toContainText('0 OT créés');
|
|
});
|
|
|
|
test('urgence « personne bloquée » : interrupteur → chip topbar + bandeau + tête de liste', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/connexion');
|
|
await page.getByRole('button', { name: /Salma Idrissi/ }).click();
|
|
await page.getByRole('link', { name: /Ordres de travail/ }).click();
|
|
await page.getByRole('button', { name: '+ Nouvel OT' }).click();
|
|
await page.getByLabel('Objet *').fill(`E2E ${suffix} — personne bloquée cage B`);
|
|
await page.getByLabel('Équipement *').selectOption({ index: 1 });
|
|
await page.getByRole('switch', { name: 'Personne bloquée' }).click();
|
|
await page.getByRole('button', { name: "Créer l'OT (statut Ouvert)" }).click();
|
|
await expect(page.getByRole('heading', { name: new RegExp(suffix) })).toBeVisible();
|
|
|
|
// chip topbar (pulse) présent et bandeau sur le tableau de bord
|
|
await expect(page.locator('.topbar .chip-urgence')).toContainText('personne');
|
|
await page.getByRole('link', { name: 'Tableau de bord' }).click();
|
|
await expect(page.locator('.bandeau-urgence').first()).toBeVisible();
|
|
// tête de liste OT
|
|
await page.getByRole('link', { name: /Ordres de travail/ }).click();
|
|
await expect(page.locator('tbody tr').first()).toHaveClass(/ligne-bloque/);
|
|
|
|
// nettoyage : annuler l'OT d'urgence créé (motif via prompt)
|
|
await page.locator('tbody tr').first().click();
|
|
page.once('dialog', (d) => d.accept('Test e2e — nettoyage'));
|
|
await page.getByRole('button', { name: 'Annuler', exact: true }).click();
|
|
await expect(page.locator('.entete-page .st')).toHaveText('Annulé');
|
|
});
|
|
|
|
test('compteurs : relevé croissant accepté, régression refusée', async ({ page }) => {
|
|
await page.goto('/connexion');
|
|
await page.getByRole('button', { name: /Ahmed Benali/ }).click();
|
|
await page.getByRole('link', { name: 'Ascenseurs' }).click();
|
|
await page.getByRole('cell', { name: 'A1', exact: true }).click();
|
|
await page.getByRole('button', { name: 'Compteurs' }).click();
|
|
await expect(page.getByRole('heading', { name: /Compteurs — Ascenseur A1/ })).toBeVisible();
|
|
|
|
const saisie = page.getByLabel('Nouveau relevé — Heures de marche');
|
|
await saisie.fill('99999999');
|
|
await page.getByRole('button', { name: 'Enregistrer le relevé' }).first().click();
|
|
await expect(page.locator('.chrono').first()).toContainText('99 999 999');
|
|
|
|
await saisie.fill('12');
|
|
await page.getByRole('button', { name: 'Enregistrer le relevé' }).first().click();
|
|
await expect(page.locator('.erreur-form')).toContainText('ne redescend pas');
|
|
});
|