Initial commit

This commit is contained in:
dahoud
2025-10-01 01:39:07 +00:00
commit b430bf3b96
826 changed files with 255287 additions and 0 deletions

View File

@@ -0,0 +1,364 @@
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<typeof clientService>
const mockChantierService = chantierService as jest.Mocked<typeof chantierService>
const mockDevisService = devisService as jest.Mocked<typeof devisService>
const mockFactureService = factureService as jest.Mocked<typeof factureService>
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)
})
})