/* Écran d'administration des référentiels. Avant correction, les boutons Modifier / Désactiver n'appelaient rien (console.log) et « Enregistrer » se contentait de fermer la modale. Ces tests vérifient que chaque action atteint réellement l'API et que les erreurs métier remontent à l'utilisateur. */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import Admin from '../pages/Admin/Admin'; import * as api from '../services/api'; const SERVICES = [ { id_service: 1, nom_service: 'Contrat' }, { id_service: 2, nom_service: 'Fournisseur' }, ]; const CATEGORIES = [{ id_categorie: 1, intitule_categorie: 'Tiers-payeurs' }]; const MONITORINGS = [{ id_monito: 1, monito_intitule: 'Erreurs tiers payeurs', id_service: 1, id_categorie: 1, table_source: 'MONITO_TIERS_PAYEURS', bdd_source: 'Sage', }]; function stubLectures() { vi.spyOn(api, 'getServices').mockResolvedValue(SERVICES); vi.spyOn(api, 'getCategories').mockResolvedValue(CATEGORIES); vi.spyOn(api, 'getContacts').mockResolvedValue([]); vi.spyOn(api, 'getMonitorings').mockResolvedValue(MONITORINGS); } async function ouvrirOnglet(utilisateur, nom) { await utilisateur.click(screen.getByRole('button', { name: nom })); } describe('Admin — CRUD des référentiels', () => { beforeEach(() => { vi.restoreAllMocks(); stubLectures(); vi.spyOn(window, 'confirm').mockReturnValue(true); }); it('crée un service via l’API et recharge la liste', async () => { const creation = vi.spyOn(api, 'createService').mockResolvedValue({ id_service: 3 }); const utilisateur = userEvent.setup(); render(); await screen.findByText('Administration'); await ouvrirOnglet(utilisateur, 'Services'); await utilisateur.click(screen.getByRole('button', { name: /Ajouter/ })); await utilisateur.type(screen.getByLabelText('Nom du service'), 'Comptabilité'); await utilisateur.click(screen.getByRole('button', { name: 'Enregistrer' })); await waitFor(() => { expect(creation).toHaveBeenCalledWith({ nom_service: 'Comptabilité' }); }); /* Rechargement après écriture : la liste doit être redemandée. */ expect(api.getServices).toHaveBeenCalledTimes(2); }); it('pré-remplit le formulaire de modification avec la ligne choisie', async () => { vi.spyOn(api, 'updateService').mockResolvedValue({}); const utilisateur = userEvent.setup(); render(); await screen.findByText('Administration'); await ouvrirOnglet(utilisateur, 'Services'); const ligne = screen.getByText('Fournisseur').closest('tr'); await utilisateur.click(within(ligne).getByRole('button', { name: /Modifier/ })); expect(screen.getByLabelText('Nom du service')).toHaveValue('Fournisseur'); }); it('envoie l’identifiant de la ligne lors d’une modification', async () => { const modification = vi.spyOn(api, 'updateService').mockResolvedValue({}); const utilisateur = userEvent.setup(); render(); await screen.findByText('Administration'); await ouvrirOnglet(utilisateur, 'Services'); const ligne = screen.getByText('Fournisseur').closest('tr'); await utilisateur.click(within(ligne).getByRole('button', { name: /Modifier/ })); await utilisateur.click(screen.getByRole('button', { name: 'Enregistrer' })); await waitFor(() => { expect(modification).toHaveBeenCalledWith(2, { nom_service: 'Fournisseur' }); }); }); it('demande confirmation avant de supprimer, et n’appelle rien si on refuse', async () => { window.confirm.mockReturnValue(false); const suppression = vi.spyOn(api, 'deleteService').mockResolvedValue({}); const utilisateur = userEvent.setup(); render(); await screen.findByText('Administration'); await ouvrirOnglet(utilisateur, 'Services'); const ligne = screen.getByText('Contrat').closest('tr'); await utilisateur.click(within(ligne).getByRole('button', { name: /Supprimer/ })); expect(window.confirm).toHaveBeenCalled(); expect(suppression).not.toHaveBeenCalled(); }); it('affiche le refus de l’API quand des enregistrements sont rattachés', async () => { /* Cas réel : l'API renvoie 409 avec le décompte des rattachements. */ vi.spyOn(api, 'deleteService').mockRejectedValue( new Error('Erreur 409: Suppression impossible : 5 monitoring(s) rattachés.'), ); const utilisateur = userEvent.setup(); render(); await screen.findByText('Administration'); await ouvrirOnglet(utilisateur, 'Services'); const ligne = screen.getByText('Contrat').closest('tr'); await utilisateur.click(within(ligne).getByRole('button', { name: /Supprimer/ })); expect(await screen.findByText(/5 monitoring\(s\) rattachés/)).toBeInTheDocument(); }); it('parle de désactivation, pas de suppression, pour un monitoring', async () => { /* TABLE_FINAL référence le monitoring : il n'est jamais supprimé. */ const desactivation = vi.spyOn(api, 'desactiverMonitoring').mockResolvedValue({}); const utilisateur = userEvent.setup(); render(); await screen.findByText('Administration'); const ligne = screen.getByText('Erreurs tiers payeurs').closest('tr'); expect(within(ligne).getByRole('button', { name: /Désactiver/ })).toBeInTheDocument(); await utilisateur.click(within(ligne).getByRole('button', { name: /Désactiver/ })); await waitFor(() => expect(desactivation).toHaveBeenCalledWith(1)); }); it('affiche le libellé du service plutôt que son identifiant brut', async () => { render(); await screen.findByText('Administration'); const ligne = screen.getByText('Erreurs tiers payeurs').closest('tr'); /* id_service = 1 doit s'afficher « Contrat ». */ expect(within(ligne).getByText('Contrat')).toBeInTheDocument(); }); it('convertit en entier les identifiants issus des listes déroulantes', async () => { /* Un