fix(frontend): maintient le rafraichissement du tableau de bord en cas d'erreur

Sans catchError, la premiere reponse en erreur terminait le flux du timer :
le rafraichissement ne repartait jamais et l'ecran restait fige sur des
chiffres perimes, sans rien signaler.

Le catchError porte sur l'observable interne du switchMap. Place sur le flux
externe il terminerait le timer tout autant. Un signal error alimente un
bandeau, efface des qu'une reponse valide revient.
This commit is contained in:
Johan LEROY
2026-09-16 09:20:17 +02:00
parent 881f503f1a
commit 7b9406965e
4 changed files with 94 additions and 8 deletions
@@ -1,6 +1,6 @@
import { TestBed } from '@angular/core/testing';
import { vi } from 'vitest';
import { of } from 'rxjs';
import { of, throwError } from 'rxjs';
import { Dashboard } from './dashboard';
import { StatsService } from '../../core/services/stats.service';
import { AlertsService } from '../../core/services/alerts.service';
@@ -8,6 +8,7 @@ import { AlertsService } from '../../core/services/alerts.service';
vi.mock('chart.js', () => {
class ChartMock {
update = vi.fn();
destroy = vi.fn();
data = { datasets: [{}] };
static register = vi.fn();
}
@@ -15,6 +16,8 @@ vi.mock('chart.js', () => {
});
describe('Dashboard', () => {
afterEach(() => vi.useRealTimers());
it('charge les stats et les alertes 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' }])) };
@@ -37,5 +40,56 @@ describe('Dashboard', () => {
expect(statsMock.getSummary).toHaveBeenCalled();
expect(alertsMock.getAlerts).toHaveBeenCalled();
expect(fixture.componentInstance.alerts().length).toBe(1);
expect(fixture.componentInstance.error()).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 },
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
vi.advanceTimersByTime(1);
expect(statsMock.getSummary).toHaveBeenCalledTimes(1);
expect(fixture.componentInstance.error()).not.toBeNull();
expect(fixture.componentInstance.stats()).toBeNull();
vi.advanceTimersByTime(10000);
expect(statsMock.getSummary).toHaveBeenCalledTimes(2);
expect(fixture.componentInstance.stats()).not.toBeNull();
expect(fixture.componentInstance.error()).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 },
],
});
const fixture = TestBed.createComponent(Dashboard);
fixture.detectChanges();
expect(fixture.componentInstance.alerts().length).toBe(0);
});
});