diff --git a/src/components/Analysis/Comparison/Comparison.spec.tsx b/src/components/Analysis/Comparison/Comparison.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..26c73803a96649d2ff8afd85bf3fcf730d52a17b --- /dev/null +++ b/src/components/Analysis/Comparison/Comparison.spec.tsx @@ -0,0 +1,116 @@ +import { act, render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { FluidType } from 'enums' +import { PerformanceIndicator } from 'models' +import React from 'react' +import { Provider } from 'react-redux' +import { setPeriod } from 'store/analysis/analysis.slice' +import { createMockEcolyoStore, mockAnalysisState } from 'tests/__mocks__/store' +import Comparison from './Comparison' + +jest.mock('services/consumption.service', () => { + return jest.fn().mockImplementation(() => ({ + fetchAvgTemperature: jest.fn().mockResolvedValue(25.3), + getPerformanceIndicators: jest.fn().mockResolvedValue([]), + })) +}) + +const mockFluidsWithData = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, +] + +const mockPerformanceIndicators: PerformanceIndicator[] = [ + { + compareValue: 203.49, + percentageVariation: 0.12261044768784712, + value: 178.54, + }, + { + compareValue: 7926.82, + percentageVariation: 0.020542916327102145, + value: 7763.98, + }, + { + compareValue: 1316.46, + percentageVariation: -0.0009191316105312541, + value: 1317.67, + }, +] + +describe('Comparison component', () => { + it('renders loading state', async () => { + render( + <Provider store={createMockEcolyoStore()}> + <Comparison fluidsWithData={[]} monthPerformanceIndicators={[]} /> + </Provider> + ) + + await waitFor(() => { + expect(screen.getByRole('progressbar')).toBeInTheDocument() + }) + }) + + it('switches between monthly and yearly periods', async () => { + const store = createMockEcolyoStore({ + analysis: { ...mockAnalysisState, period: 'month' }, + global: { + fluidTypes: mockFluidsWithData, + }, + }) + + const mockDispatch = jest.fn() + jest.spyOn(store, 'dispatch').mockImplementation(mockDispatch) + + render( + <Provider store={store}> + <Comparison + fluidsWithData={mockFluidsWithData} + monthPerformanceIndicators={mockPerformanceIndicators} + /> + </Provider> + ) + + const yearlyButton = await screen.findByRole('button', { + name: `analysis.compare.year_tab`, + }) + const monthlyButton = await screen.findByRole('button', { + name: `analysis.compare.month_tab`, + }) + + await act(async () => { + await userEvent.click(yearlyButton) + expect(mockDispatch).toHaveBeenCalledWith(setPeriod('year')) + }) + + await act(async () => { + await userEvent.click(monthlyButton) + expect(mockDispatch).toHaveBeenCalledWith(setPeriod('month')) + }) + }) + + it('renders performance indicators and weather correctly', async () => { + const store = createMockEcolyoStore({ + analysis: { ...mockAnalysisState }, + global: { + fluidTypes: mockFluidsWithData, + }, + }) + + render( + <Provider store={store}> + <Comparison + fluidsWithData={mockFluidsWithData} + monthPerformanceIndicators={mockPerformanceIndicators} + /> + </Provider> + ) + + const indicators = await screen.findAllByRole('button', { name: /fluid/i }) + expect(indicators.length).toBe(mockFluidsWithData.length) + expect( + await screen.findByText('analysis.temperature_comparison.unit') + ).toBeInTheDocument() + }) +}) diff --git a/src/components/Analysis/Comparison/Comparison.tsx b/src/components/Analysis/Comparison/Comparison.tsx index 927e1094cc8bbcc529ba7d64f38a38e36b8bae5c..a81af240b39546a0fab32e02dce90b7a3f4a4a00 100644 --- a/src/components/Analysis/Comparison/Comparison.tsx +++ b/src/components/Analysis/Comparison/Comparison.tsx @@ -57,7 +57,6 @@ const Comparison = ({ fluidsWithData.length * 84 + (fluidsWithData.length - 1) * 10 + NAVIGATION_HEIGHT - useEffect(() => { let subscribed = true async function populateData() { @@ -82,6 +81,7 @@ const Comparison = ({ setIsLoading(false) } } + populateData() return () => { subscribed = false diff --git a/src/components/Analysis/Comparison/FluidPerformanceIndicator.spec.tsx b/src/components/Analysis/Comparison/FluidPerformanceIndicator.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..13b53d72ddeac40710793db4b06b7952594fe3b4 --- /dev/null +++ b/src/components/Analysis/Comparison/FluidPerformanceIndicator.spec.tsx @@ -0,0 +1,136 @@ +import { AnyAction } from '@reduxjs/toolkit' +import { act, render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { FluidType } from 'enums' +import { DateTime } from 'luxon' +import { PerformanceIndicator } from 'models' +import React from 'react' +import { Provider } from 'react-redux' +import { createMockEcolyoStore } from 'tests/__mocks__/store' +import FluidPerformanceIndicator from './FluidPerformanceIndicator' + +const monthlyStore = createMockEcolyoStore() +const yearlyStore = createMockEcolyoStore({ analysis: { period: 'year' } }) +const mockedNavigate = jest.fn() +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => mockedNavigate, +})) + +const elecPerformanceIndicator: PerformanceIndicator = { + value: 100, + compareValue: 90, + percentageVariation: 0.1, +} + +const waterPerformanceIndicator: PerformanceIndicator = { + value: 140, + compareValue: 150, + percentageVariation: -0.1, +} + +const comparisonDate = DateTime.local(2024, 4, 15) + +describe('FluidPerformanceIndicator component', () => { + it('should render elec indicators correctly and navigate on click', async () => { + const dispatchSpy = jest.spyOn(monthlyStore, 'dispatch') + const setShowCompareMock = jest.fn() + dispatchSpy.mockImplementation((action: AnyAction) => { + if (action.type === 'chart/setShowCompare') { + setShowCompareMock(action.payload) + } + return action + }) + + const { container } = render( + <Provider store={monthlyStore}> + <FluidPerformanceIndicator + performanceIndicator={elecPerformanceIndicator} + fluidType={FluidType.ELECTRICITY} + comparisonDate={comparisonDate} + /> + </Provider> + ) + + expect(screen.getByTestId('fluid-type-ELECTRICITY')).toHaveTextContent( + 'ELECTRICITY' + ) + expect(screen.getByTestId('fluid-value-ELECTRICITY')).toHaveTextContent( + '100' + ) + expect( + screen.getByTestId('fluid-comparison-ELECTRICITY') + ).toHaveTextContent('+10,00 % / avril 2024') + + await act(async () => { + await userEvent.click(screen.getByRole('button')) + }) + + expect(mockedNavigate).toHaveBeenCalledWith('/consumption/electricity') + expect(dispatchSpy).toHaveBeenCalledWith({ + type: 'chart/setShowCompare', + payload: false, + }) + + expect(container).toMatchSnapshot() + }) + + it('should render water indicators correctly and dispatch setShowCompare year on click', async () => { + const dispatchSpy = jest.spyOn(yearlyStore, 'dispatch') + const setShowCompareMock = jest.fn() + dispatchSpy.mockImplementation((action: AnyAction) => { + if (action.type === 'chart/setShowCompare') { + setShowCompareMock(action.payload) + } + return action + }) + + render( + <Provider store={yearlyStore}> + <FluidPerformanceIndicator + performanceIndicator={waterPerformanceIndicator} + fluidType={FluidType.WATER} + comparisonDate={comparisonDate} + /> + </Provider> + ) + + expect(screen.getByTestId('fluid-type-WATER')).toHaveTextContent('WATER') + expect(screen.getByTestId('fluid-value-WATER')).toHaveTextContent('140') + expect(screen.getByTestId('fluid-comparison-WATER')).toHaveTextContent( + '-10,00 % / avril 2024' + ) + + await act(async () => { + await userEvent.click(screen.getByRole('button')) + }) + + expect(mockedNavigate).toHaveBeenCalledWith('/consumption/water') + expect(dispatchSpy).toHaveBeenCalledWith({ + type: 'chart/setShowCompare', + payload: true, + }) + }) + + it('should render "no data" message when value is not available', () => { + const performanceIndicator: PerformanceIndicator = { + value: null, + compareValue: null, + percentageVariation: null, + } + const { container } = render( + <Provider store={monthlyStore}> + <FluidPerformanceIndicator + performanceIndicator={performanceIndicator} + fluidType={FluidType.WATER} + comparisonDate={comparisonDate} + /> + </Provider> + ) + + expect(container).toMatchSnapshot() + expect( + screen.getByText('performance_indicator.fpi.no_data') + ).toBeInTheDocument() + }) +}) diff --git a/src/components/Analysis/Comparison/FluidPerformanceIndicator.tsx b/src/components/Analysis/Comparison/FluidPerformanceIndicator.tsx index 1c193147dcb6a8ffa8fd59981205e30b4e9357e6..b516ca00c0ba40d21370ab88accea485c39aa5a8 100644 --- a/src/components/Analysis/Comparison/FluidPerformanceIndicator.tsx +++ b/src/components/Analysis/Comparison/FluidPerformanceIndicator.tsx @@ -1,9 +1,9 @@ +import React from 'react' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidType, TimeStep } from 'enums' import { DateTime } from 'luxon' import { PerformanceIndicator } from 'models' -import React from 'react' import { useNavigate } from 'react-router-dom' import { setCurrentTimeStep, setShowCompare } from 'store/chart/chart.slice' import { useAppDispatch, useAppSelector } from 'store/hooks' @@ -57,7 +57,10 @@ const FluidPerformanceIndicator = ({ return ( <button className="fpi" onClick={() => handleFluidClick(fluidType)}> <StyledIcon icon={iconType} size={50} /> - <div className="fpi-content"> + <div + data-testid={`fluid-type-${FluidType[fluidType]}`} + className="fpi-content" + > {!displayedValue && ( <div className="fpi-content-no-data"> <span>{t('performance_indicator.fpi.no_data')}</span> @@ -65,7 +68,10 @@ const FluidPerformanceIndicator = ({ )} {displayedValue && ( <> - <div className="fpi-value"> + <div + data-testid={`fluid-value-${FluidType[fluidType]}`} + className="fpi-value" + > <span className="fpi-load">{displayedValue}</span> <span className="fpi-unit"> {t(`FLUID.${FluidType[fluidType]}.UNIT`)} @@ -77,7 +83,10 @@ const FluidPerformanceIndicator = ({ </span> )} {performanceIndicator?.percentageVariation !== null && ( - <div className="fpi-comparison"> + <div + data-testid={`fluid-comparison-${FluidType[fluidType]}`} + className="fpi-comparison" + > <span className={`percent ${positive ? 'positive' : 'negative'}`} > diff --git a/src/components/Analysis/Comparison/__snapshots__/FluidPerformanceIndicator.spec.tsx.snap b/src/components/Analysis/Comparison/__snapshots__/FluidPerformanceIndicator.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..e7da0ab898af602000d623d71b059b138b503916 --- /dev/null +++ b/src/components/Analysis/Comparison/__snapshots__/FluidPerformanceIndicator.spec.tsx.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FluidPerformanceIndicator component should render "no data" message when value is not available 1`] = ` +<div> + <button + class="fpi" + > + <svg + aria-hidden="true" + class="styles__icon___23x3R" + height="50" + width="50" + > + <use + xlink:href="#test-file-stub" + /> + </svg> + <div + class="fpi-content" + data-testid="fluid-type-WATER" + > + <div + class="fpi-content-no-data" + > + <span> + performance_indicator.fpi.no_data + </span> + </div> + </div> + </button> +</div> +`; + +exports[`FluidPerformanceIndicator component should render elec indicators correctly and navigate on click 1`] = ` +<div> + <button + class="fpi" + > + <svg + aria-hidden="true" + class="styles__icon___23x3R" + height="50" + width="50" + > + <use + xlink:href="#test-file-stub" + /> + </svg> + <div + class="fpi-content" + data-testid="fluid-type-ELECTRICITY" + > + <div + class="fpi-value" + data-testid="fluid-value-ELECTRICITY" + > + <span + class="fpi-load" + > + 100,00 + </span> + <span + class="fpi-unit" + > + FLUID.ELECTRICITY.UNIT + </span> + </div> + <div + class="fpi-comparison" + data-testid="fluid-comparison-ELECTRICITY" + > + <span + class="percent positive" + > + +10,00 % + </span> + <span + class="fpi-comparison-date" + > + / avril 2024 + </span> + </div> + </div> + </button> +</div> +`;