import { renderHook, waitFor } from '@testing-library/react' import { useDashboard } from '../useDashboard' import { clientService, chantierService, devisService, factureService } from '../../services/api' // Mock des services jest.mock('../../services/api') const mockClientService = clientService as jest.Mocked const mockChantierService = chantierService as jest.Mocked const mockDevisService = devisService as jest.Mocked const mockFactureService = factureService as jest.Mocked const mockClients = [ { id: '1', nom: 'Client', prenom: 'Un', email: 'client1@test.com' }, { id: '2', nom: 'Client', prenom: 'Deux', email: 'client2@test.com' }, ] const mockChantiers = [ { id: '1', nom: 'Chantier 1', client: mockClients[0], statut: 'EN_COURS', dateDebut: '2024-01-01', montantPrevu: 10000, }, { id: '2', nom: 'Chantier 2', client: mockClients[1], statut: 'PLANIFIE', dateDebut: '2024-01-15', montantPrevu: 15000, }, { id: '3', nom: 'Chantier 3', client: mockClients[0], statut: 'TERMINE', dateDebut: '2024-01-10', montantPrevu: 8000, }, ] const mockDevis = [ { id: '1', numero: 'D2024-001', client: mockClients[0], statut: 'ENVOYE', dateEmission: '2024-01-01', dateValidite: '2024-12-31', montantTTC: 5000, }, { id: '2', numero: 'D2024-002', client: mockClients[1], statut: 'ACCEPTE', dateEmission: '2024-01-05', dateValidite: '2024-12-31', montantTTC: 7000, }, ] const mockFactures = [ { id: '1', numero: 'F2024-001', client: mockClients[0], statut: 'PAYEE', dateEmission: '2024-01-01', dateEcheance: '2024-01-31', montantTTC: 10000, }, { id: '2', numero: 'F2024-002', client: mockClients[1], statut: 'ENVOYEE', dateEmission: '2024-01-10', dateEcheance: '2024-01-01', // En retard montantTTC: 5000, }, ] describe('Hook useDashboard', () => { beforeEach(() => { jest.clearAllMocks() jest.spyOn(console, 'log').mockImplementation(() => {}) jest.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { jest.restoreAllMocks() }) it('devrait initialiser avec les valeurs par défaut', () => { mockClientService.getAll.mockImplementation(() => new Promise(() => {})) mockChantierService.getAll.mockImplementation(() => new Promise(() => {})) mockDevisService.getAll.mockImplementation(() => new Promise(() => {})) mockFactureService.getAll.mockImplementation(() => new Promise(() => {})) const { result } = renderHook(() => useDashboard()) expect(result.current.stats).toBeNull() expect(result.current.chantiersRecents).toEqual([]) expect(result.current.facturesEnRetard).toEqual([]) expect(result.current.devisEnAttente).toEqual([]) expect(result.current.loading).toBe(true) expect(result.current.error).toBeNull() }) it('devrait charger les données avec succès', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(mockChantiers) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.stats).toEqual({ totalClients: 2, totalChantiers: 3, chantiersEnCours: 1, chantiersPlanifies: 1, chantiersTermines: 1, totalDevis: 2, devisAcceptes: 1, devisEnAttente: 1, totalFactures: 2, facturesPayees: 1, facturesEnRetard: 1, chiffreAffaires: 10000, chiffreAffairesMois: expect.any(Number), chiffreAffairesAnnee: expect.any(Number), }) }) it('devrait calculer les chantiers récents', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(mockChantiers) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.chantiersRecents).toHaveLength(3) expect(result.current.chantiersRecents[0]).toEqual({ id: '2', nom: 'Chantier 2', client: 'Client Deux', statut: 'PLANIFIE', dateDebut: '2024-01-15', montantPrevu: 15000, }) }) it('devrait calculer les factures en retard', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(mockChantiers) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.facturesEnRetard).toHaveLength(1) expect(result.current.facturesEnRetard[0]).toEqual({ id: '2', numero: 'F2024-002', client: 'Client Deux', montantTTC: 5000, dateEcheance: '2024-01-01', joursRetard: expect.any(Number), }) }) it('devrait calculer les devis en attente', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(mockChantiers) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.devisEnAttente).toHaveLength(1) expect(result.current.devisEnAttente[0]).toEqual({ id: '1', numero: 'D2024-001', client: 'Client Un', montantTTC: 5000, dateEmission: '2024-01-01', dateValidite: '2024-12-31', joursRestants: expect.any(Number), }) }) it('devrait gérer les erreurs partielles', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockRejectedValue(new Error('Erreur chantiers')) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.error).toBeNull() expect(result.current.stats?.totalClients).toBe(2) expect(result.current.stats?.totalChantiers).toBe(0) expect(result.current.chantiersRecents).toEqual([]) }) it('devrait gérer les erreurs complètes', async () => { mockClientService.getAll.mockRejectedValue(new Error('Erreur clients')) mockChantierService.getAll.mockRejectedValue(new Error('Erreur chantiers')) mockDevisService.getAll.mockRejectedValue(new Error('Erreur devis')) mockFactureService.getAll.mockRejectedValue(new Error('Erreur factures')) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.error).toBeNull() expect(result.current.stats?.totalClients).toBe(0) expect(result.current.stats?.totalChantiers).toBe(0) expect(result.current.chantiersRecents).toEqual([]) }) it('devrait permettre de rafraîchir les données', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(mockChantiers) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(mockClientService.getAll).toHaveBeenCalledTimes(1) // Rafraîchir result.current.refresh() await waitFor(() => { expect(mockClientService.getAll).toHaveBeenCalledTimes(2) }) }) it('devrait gérer les données non-array', async () => { mockClientService.getAll.mockResolvedValue(null as any) mockChantierService.getAll.mockResolvedValue(undefined as any) mockDevisService.getAll.mockResolvedValue({} as any) mockFactureService.getAll.mockResolvedValue('invalid' as any) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.stats?.totalClients).toBe(0) expect(result.current.stats?.totalChantiers).toBe(0) expect(result.current.stats?.totalDevis).toBe(0) expect(result.current.stats?.totalFactures).toBe(0) }) it('devrait calculer correctement le chiffre d\'affaires mensuel', async () => { const today = new Date() const thisMonth = today.getMonth() const thisYear = today.getFullYear() const facturesThisMonth = [ { id: '1', numero: 'F2024-001', client: mockClients[0], statut: 'PAYEE', dateEmission: new Date(thisYear, thisMonth, 1).toISOString(), dateEcheance: new Date(thisYear, thisMonth, 31).toISOString(), montantTTC: 10000, }, { id: '2', numero: 'F2024-002', client: mockClients[1], statut: 'PAYEE', dateEmission: new Date(thisYear, thisMonth - 1, 1).toISOString(), // Mois précédent dateEcheance: new Date(thisYear, thisMonth - 1, 31).toISOString(), montantTTC: 5000, }, ] mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue([]) mockDevisService.getAll.mockResolvedValue([]) mockFactureService.getAll.mockResolvedValue(facturesThisMonth) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.stats?.chiffreAffairesMois).toBe(10000) }) it('devrait limiter les chantiers récents à 5', async () => { const manyChantiers = Array.from({ length: 10 }, (_, i) => ({ id: `${i + 1}`, nom: `Chantier ${i + 1}`, client: mockClients[0], statut: 'EN_COURS', dateDebut: new Date(2024, 0, i + 1).toISOString(), montantPrevu: 10000, })) mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(manyChantiers) mockDevisService.getAll.mockResolvedValue([]) mockFactureService.getAll.mockResolvedValue([]) const { result } = renderHook(() => useDashboard()) await waitFor(() => { expect(result.current.loading).toBe(false) }) expect(result.current.chantiersRecents).toHaveLength(5) }) it('devrait fournir un alias isLoading', async () => { mockClientService.getAll.mockResolvedValue(mockClients) mockChantierService.getAll.mockResolvedValue(mockChantiers) mockDevisService.getAll.mockResolvedValue(mockDevis) mockFactureService.getAll.mockResolvedValue(mockFactures) const { result } = renderHook(() => useDashboard()) expect(result.current.isLoading).toBe(result.current.loading) }) })