Files
ENI-projet-piscine/apps/frontend/src/app/features/dashboard/dashboard.spec.ts
T

281 lines
11 KiB
TypeScript

import { TestBed } from '@angular/core/testing';
import { vi } from 'vitest';
import { of, throwError } from 'rxjs';
import { Dashboard } from './dashboard';
import { StatsService } from '../../core/services/stats.service';
import { AlertsService } from '../../core/services/alerts.service';
import { PredictionsService } from '../../core/services/predictions.service';
import {AuthService} from '../../core/services/auth.service';
import {Router, provideRouter} from '@angular/router';
vi.mock('chart.js', () => {
class ChartMock {
update = vi.fn();
destroy = vi.fn();
data = { datasets: [{}] };
static register = vi.fn();
}
return { Chart: ChartMock, registerables: [] };
});
function predictionsMock(sites: unknown[] = []) {
return { getPredictions: vi.fn().mockReturnValue(of({ timestamp: '2026-09-18T09:00:00Z', sites })) };
}
describe('Dashboard', () => {
afterEach(() => vi.useRealTimers());
it('charge les stats, les alertes et les prévisions au démarrage', async () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([{ alert_id: 'A1' }])) };
const predictions = predictionsMock([{ site_id: 'SITE001', site_name: 'Test', prediction: null }]);
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictions },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
// laisse le timer(0, ...) se déclencher avant de vérifier
await new Promise((resolve) => setTimeout(resolve, 0));
fixture.detectChanges();
expect(statsMock.getSummary).toHaveBeenCalled();
expect(alertsMock.getAlerts).toHaveBeenCalled();
expect(predictions.getPredictions).toHaveBeenCalled();
expect(fixture.componentInstance.alerts().length).toBe(1);
expect(fixture.componentInstance.predictions().length).toBe(1);
expect(fixture.componentInstance.statsError()).toBeNull();
expect(fixture.componentInstance.alertsError()).toBeNull();
expect(fixture.componentInstance.predictionsError()).toBeNull();
});
it("signale l'indisponibilité puis repart au rafraîchissement suivant", () => {
vi.useFakeTimers();
const statsMock = {
getSummary: vi
.fn()
.mockReturnValueOnce(throwError(() => new Error('API injoignable')))
.mockReturnValue(of({ total_sites: 7, sites: [] })),
};
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictionsMock() },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
vi.advanceTimersByTime(1);
expect(statsMock.getSummary).toHaveBeenCalledTimes(1);
expect(fixture.componentInstance.statsError()).not.toBeNull();
expect(fixture.componentInstance.stats()).toBeNull();
vi.advanceTimersByTime(10000);
expect(statsMock.getSummary).toHaveBeenCalledTimes(2);
expect(fixture.componentInstance.stats()).not.toBeNull();
expect(fixture.componentInstance.statsError()).toBeNull();
});
it("n'interrompt pas la page quand le chargement des alertes échoue", () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(throwError(() => new Error('nope'))) };
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictionsMock() },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
expect(fixture.componentInstance.alerts().length).toBe(0);
expect(fixture.componentInstance.alertsError()).not.toBeNull();
});
it("n'interrompt pas la page quand le chargement des prévisions échoue", () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
const predictions = {
getPredictions: vi.fn().mockReturnValue(throwError(() => new Error('nope'))),
};
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictions },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
expect(fixture.componentInstance.predictions().length).toBe(0);
expect(fixture.componentInstance.predictionsError()).not.toBeNull();
});
it("un rafraîchissement de stats n'efface pas une erreur de prévisions en attente", () => {
vi.useFakeTimers();
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
const predictions = {
getPredictions: vi.fn().mockReturnValue(throwError(() => new Error('nope'))),
};
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictions },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
expect(fixture.componentInstance.predictionsError()).not.toBeNull();
// Plusieurs cycles de `timer(0, 10_000)` (stats) plus tard, l'erreur des prévisions doit
// toujours être visible : rien ne vient la rafraîchir tant que la section n'est pas rechargée.
vi.advanceTimersByTime(30000);
expect(fixture.componentInstance.predictionsError()).not.toBeNull();
expect(fixture.componentInstance.statsError()).toBeNull();
});
it('appelle logout et redirige vers /login au clic sur le bouton de déconnexion', () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
const authMock = {
logout: vi.fn().mockReturnValue(of(undefined)),
clearSession: vi.fn(),
principal: vi.fn().mockReturnValue({ role: 'admin' }),
};
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictionsMock() },
{ provide: AuthService, useValue: authMock },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
const router = TestBed.inject(Router);
const navigateSpy = vi.spyOn(router, 'navigate').mockResolvedValue(true);
const button = fixture.nativeElement.querySelector('.logout-button');
button.click();
expect(authMock.logout).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith(['/login']);
});
it('déconnecte localement et redirige vers /login même si logout échoue côté réseau', () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
const authMock = {
logout: vi.fn().mockReturnValue(throwError(() => new Error('réseau indisponible'))),
clearSession: vi.fn(),
principal: vi.fn().mockReturnValue({ role: 'admin' }),
};
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictionsMock() },
{ provide: AuthService, useValue: authMock },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
const router = TestBed.inject(Router);
const navigateSpy = vi.spyOn(router, 'navigate').mockResolvedValue(true);
const button = fixture.nativeElement.querySelector('.logout-button');
button.click();
expect(authMock.clearSession).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith(['/login']);
});
it('distingue le ton des sévérités high et critical', () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictionsMock() },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
const dashboard = fixture.componentInstance;
expect(dashboard.badgeToneForSeverity('low')).toBe('success');
expect(dashboard.badgeToneForSeverity('medium')).toBe('warning');
expect(dashboard.badgeToneForSeverity('high')).toBe('danger');
expect(dashboard.badgeToneForSeverity('critical')).toBe('critical');
expect(dashboard.badgeToneForSeverity('high')).not.toBe(
dashboard.badgeToneForSeverity('critical'),
);
});
it('distingue le ton des statuts de prévision', () => {
const statsMock = { getSummary: vi.fn().mockReturnValue(of({ total_sites: 7, sites: [] })) };
const alertsMock = { getAlerts: vi.fn().mockReturnValue(of([])) };
TestBed.configureTestingModule({
imports: [Dashboard],
providers: [
{ provide: StatsService, useValue: statsMock },
{ provide: AlertsService, useValue: alertsMock },
{ provide: PredictionsService, useValue: predictionsMock() },
provideRouter([]),
],
});
const fixture = TestBed.createComponent(Dashboard);
const dashboard = fixture.componentInstance;
expect(dashboard.badgeToneForPredictionStatus('available')).toBe('success');
expect(dashboard.badgeToneForPredictionStatus('insufficient_data')).toBe('warning');
expect(dashboard.badgeToneForPredictionStatus('error')).toBe('danger');
});
});