diff --git a/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.spec.ts b/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.spec.ts index e793c39..be25fb1 100644 --- a/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.spec.ts +++ b/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.spec.ts @@ -1,16 +1,28 @@ import { TestBed } from '@angular/core/testing'; import { vi } from 'vitest'; +import { Chart } from 'chart.js'; import { ConsumptionGauge } from './consumption-gauge'; vi.mock('chart.js', () => { class ChartMock { - update = vi.fn(); - data = { datasets: [{}] }; + static instances: ChartMock[] = []; static register = vi.fn(); + update = vi.fn(); + destroy = vi.fn(); + data = { datasets: [{}] }; + constructor() { + ChartMock.instances.push(this); + } } return { Chart: ChartMock, registerables: [] }; }); +type ChartDouble = { destroy: ReturnType }; + +function lastChart(): ChartDouble | undefined { + return (Chart as unknown as { instances: ChartDouble[] }).instances.at(-1); +} + describe('ConsumptionGauge', () => { it('se crée sans erreur avec des entrées valides', () => { TestBed.configureTestingModule({ imports: [ConsumptionGauge] }); @@ -31,4 +43,17 @@ describe('ConsumptionGauge', () => { expect(() => fixture.detectChanges()).not.toThrow(); }); + + it('détruit le graphique quand le composant est détruit', () => { + TestBed.configureTestingModule({ imports: [ConsumptionGauge] }); + const fixture = TestBed.createComponent(ConsumptionGauge); + fixture.componentRef.setInput('consumption', 300); + fixture.componentRef.setInput('capacity', 1000); + fixture.detectChanges(); + + const chart = lastChart(); + fixture.destroy(); + + expect(chart?.destroy).toHaveBeenCalledTimes(1); + }); }); diff --git a/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.ts b/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.ts index 73544ea..bda661a 100644 --- a/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.ts +++ b/apps/frontend/src/app/shared/components/consumption-gauge/consumption-gauge.ts @@ -1,4 +1,12 @@ -import { Component, ElementRef, ViewChild, input, effect, AfterViewInit } from '@angular/core'; +import { + Component, + ElementRef, + ViewChild, + input, + effect, + AfterViewInit, + OnDestroy, +} from '@angular/core'; import { Chart, registerables } from 'chart.js'; Chart.register(...registerables); @@ -9,7 +17,7 @@ Chart.register(...registerables); templateUrl: './consumption-gauge.html', styleUrl: './consumption-gauge.scss', }) -export class ConsumptionGauge implements AfterViewInit { +export class ConsumptionGauge implements AfterViewInit, OnDestroy { consumption = input.required(); capacity = input.required(); @@ -52,4 +60,8 @@ export class ConsumptionGauge implements AfterViewInit { }, }); } + + ngOnDestroy(): void { + this.chart?.destroy(); + } } diff --git a/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.spec.ts b/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.spec.ts index 1e1c46b..0d5944d 100644 --- a/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.spec.ts +++ b/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.spec.ts @@ -1,16 +1,28 @@ import { TestBed } from '@angular/core/testing'; import { vi } from 'vitest'; +import { Chart } from 'chart.js'; import { SiteLoadChart } from './site-load-chart'; vi.mock('chart.js', () => { class ChartMock { - update = vi.fn(); - data = { datasets: [{}] }; + static instances: ChartMock[] = []; static register = vi.fn(); + update = vi.fn(); + destroy = vi.fn(); + data = { datasets: [{}] }; + constructor() { + ChartMock.instances.push(this); + } } return { Chart: ChartMock, registerables: [] }; }); +type ChartDouble = { destroy: ReturnType }; + +function lastChart(): ChartDouble | undefined { + return (Chart as unknown as { instances: ChartDouble[] }).instances.at(-1); +} + describe('SiteLoadChart', () => { it('se crée sans erreur avec une liste de sites valide', () => { TestBed.configureTestingModule({ imports: [SiteLoadChart] }); @@ -35,4 +47,25 @@ describe('SiteLoadChart', () => { expect(() => fixture.detectChanges()).not.toThrow(); }); + + it('détruit le graphique quand le composant est détruit', () => { + TestBed.configureTestingModule({ imports: [SiteLoadChart] }); + const fixture = TestBed.createComponent(SiteLoadChart); + fixture.componentRef.setInput('sites', [ + { + site_id: 'S1', + site_name: 'A', + current_consumption_kw: 50, + capacity_kw: 100, + load_percent: 50, + data_quality: 'good', + }, + ]); + fixture.detectChanges(); + + const chart = lastChart(); + fixture.destroy(); + + expect(chart?.destroy).toHaveBeenCalledTimes(1); + }); }); diff --git a/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.ts b/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.ts index f8ee5cb..017ce1b 100644 --- a/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.ts +++ b/apps/frontend/src/app/shared/components/site-load-chart/site-load-chart.ts @@ -1,4 +1,12 @@ -import { Component, ElementRef, ViewChild, input, effect, AfterViewInit } from '@angular/core'; +import { + Component, + ElementRef, + ViewChild, + input, + effect, + AfterViewInit, + OnDestroy, +} from '@angular/core'; import { Chart, registerables } from 'chart.js'; import { SiteSummary } from '../../models/stats.model'; @@ -17,7 +25,7 @@ const QUALITY_COLORS: Record = { templateUrl: './site-load-chart.html', styleUrl: './site-load-chart.scss', }) -export class SiteLoadChart implements AfterViewInit { +export class SiteLoadChart implements AfterViewInit, OnDestroy { sites = input.required(); @ViewChild('canvas') private canvasRef!: ElementRef; @@ -59,4 +67,8 @@ export class SiteLoadChart implements AfterViewInit { }, }); } + + ngOnDestroy(): void { + this.chart?.destroy(); + } }