diff --git a/src/components/Analysis/PieChart.spec.tsx b/src/components/Analysis/PieChart.spec.tsx index 035cd072f3108718a1db2886751e56e6be44e69b..11b0a66908c3bf5338bcb071e7adcfc800ca0d47 100644 --- a/src/components/Analysis/PieChart.spec.tsx +++ b/src/components/Analysis/PieChart.spec.tsx @@ -5,6 +5,8 @@ import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' import PieChart from './PieChart' +import { DataloadValueDetail } from 'models' +import { DataloadState } from 'enum/dataload.enum' jest.mock('cozy-ui/transpiled/react/I18n', () => { return { @@ -18,6 +20,11 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => { const mockStore = configureStore([]) describe('PieChart component', () => { + const mockDataloadValueDetailArray: DataloadValueDetail[] = [ + { value: 10, state: DataloadState.VALID }, + { value: 20, state: DataloadState.VALID }, + { value: 30, state: DataloadState.VALID }, + ] it('should be rendered correctly', () => { const store = mockStore({ ecolyo: { @@ -35,7 +42,7 @@ describe('PieChart component', () => { zone: 'utc', })} totalValue={60} - dataArray={[10, 20, 30]} + dataloadValueDetailArray={mockDataloadValueDetailArray} /> </Provider> ).getElement() @@ -58,7 +65,7 @@ describe('PieChart component', () => { zone: 'utc', })} totalValue={60} - dataArray={[10, 20, 30]} + dataloadValueDetailArray={mockDataloadValueDetailArray} /> </Provider> ) diff --git a/src/components/Analysis/PieChart.tsx b/src/components/Analysis/PieChart.tsx index 7091bd02c1416411a3b15d753f60758292f966d0..d831ee1e8f82095f8c593f399c2b178e2fb358e2 100644 --- a/src/components/Analysis/PieChart.tsx +++ b/src/components/Analysis/PieChart.tsx @@ -6,11 +6,12 @@ import { formatNumberValues } from 'utils/utils' import { DateTime } from 'luxon' import { convertDateToMonthString } from 'utils/date' import EstimatedConsumptionModal from 'components/ConsumptionVisualizer/EstimatedConsumptionModal' +import { DataloadValueDetail } from 'models' interface PieProps { innerRadius: number outerRadius: number - dataArray: number[] + dataloadValueDetailArray: DataloadValueDetail[] width: number height: number totalValue: number @@ -20,7 +21,7 @@ interface PieProps { const PieChart: React.FC<PieProps> = ({ innerRadius, outerRadius, - dataArray, + dataloadValueDetailArray, width, height, totalValue, @@ -36,7 +37,10 @@ const PieChart: React.FC<PieProps> = ({ }, []) useEffect(() => { - const data = createPie(dataArray) + const dataloadArray: number[] = dataloadValueDetailArray.map( + dataload => dataload.value + ) + const data = createPie(dataloadArray) const group = d3.select(ref.current) const groupWithData = group.selectAll('g.arc').data(data) const colors = ['#D87B39', '#3A98EC', '#45D1B8'] @@ -62,7 +66,7 @@ const PieChart: React.FC<PieProps> = ({ .attr('class', 'arc') .attr('d', createArc) .attr('fill', (d, i) => colors[i]) - }, [createPie, dataArray, innerRadius, outerRadius]) + }, [createPie, dataloadValueDetailArray, innerRadius, outerRadius]) return ( <div diff --git a/src/components/Analysis/TotalAnalysisChart.spec.tsx b/src/components/Analysis/TotalAnalysisChart.spec.tsx index 26f22d13f87761cc73e5c6ccb91d029b5d90fcf0..2ec549add6f76f5e1ece5bf527f729fb31829249 100644 --- a/src/components/Analysis/TotalAnalysisChart.spec.tsx +++ b/src/components/Analysis/TotalAnalysisChart.spec.tsx @@ -2,13 +2,17 @@ import React from 'react' import { mount } from 'enzyme' import { DateTime } from 'luxon' -import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' import { Provider } from 'react-redux' -import configureStore from 'redux-mock-store' import TotalAnalysisChart from './TotalAnalysisChart' import { FluidType } from 'enum/fluid.enum' import { graphMonthData } from '../../../tests/__mocks__/datachartData.mock' import { act } from 'react-dom/test-utils' +import { + createMockStore, + mockInitialEcolyoState, +} from '../../../tests/__mocks__/store' +import { Datachart } from 'models' +import { DataloadState } from 'enum/dataload.enum' jest.mock('cozy-ui/transpiled/react/I18n', () => { return { @@ -27,16 +31,16 @@ jest.mock('services/consumption.service', () => { } }) }) -jest.mock('components/Analysis/PieChart', () => 'PieChart') -const mockStore = configureStore([]) +jest.mock('components/Analysis/PieChart', () => 'mock-piechart') describe('TotalAnalysisChart component', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let store: any + beforeEach(() => { + store = createMockStore(mockInitialEcolyoState) + }) + it('should be rendered correctly', () => { - const store = mockStore({ - ecolyo: { - global: globalStateData, - }, - }) const wrapper = mount( <Provider store={store}> <TotalAnalysisChart @@ -50,11 +54,6 @@ describe('TotalAnalysisChart component', () => { expect(wrapper).toMatchSnapshot() }) it('should render several fluids and display month data', async () => { - const store = mockStore({ - ecolyo: { - global: globalStateData, - }, - }) mockgetGraphData.mockResolvedValueOnce(graphMonthData) const wrapper = mount( <Provider store={store}> @@ -73,21 +72,18 @@ describe('TotalAnalysisChart component', () => { expect(wrapper.find('.fluidconso').exists()).toBeTruthy() }) it('should render empty price', async () => { - const store = mockStore({ - ecolyo: { - global: globalStateData, - }, - }) - const emptyData = { + const emptyData: Datachart = { actualData: [ { date: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [-1], + state: DataloadState.VALID, + valueDetail: [{ value: -1, state: DataloadState.EMPTY }], }, ], + comparisonData: null, } mockgetGraphData.mockResolvedValueOnce(emptyData) const wrapper = mount( @@ -107,21 +103,18 @@ describe('TotalAnalysisChart component', () => { expect(wrapper.find('.fluidconso').text()).toBe('--- €') }) it('should render empty price for one fluid', async () => { - const store = mockStore({ - ecolyo: { - global: globalStateData, - }, - }) - const emptyData = { + const emptyData: Datachart = { actualData: [ { date: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [-1], + state: DataloadState.VALID, + valueDetail: [{ value: -1, state: DataloadState.EMPTY }], }, ], + comparisonData: null, } mockgetGraphData.mockResolvedValueOnce(emptyData) const wrapper = mount( diff --git a/src/components/Analysis/TotalAnalysisChart.tsx b/src/components/Analysis/TotalAnalysisChart.tsx index 817670e0ef98f87eae654f8cb3575eee12fd9f38..e7997182a4d1dcfa2779e5c51a2346e89b1d9b06 100644 --- a/src/components/Analysis/TotalAnalysisChart.tsx +++ b/src/components/Analysis/TotalAnalysisChart.tsx @@ -5,7 +5,7 @@ import { useClient } from 'cozy-client' import ConsumptionDataManager from 'services/consumption.service' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' -import { TimePeriod } from 'models' +import { DataloadValueDetail, TimePeriod } from 'models' import PieChart from './PieChart' import './totalAnalysisChart.scss' import { getNavPicto } from 'utils/picto' @@ -23,7 +23,9 @@ const TotalAnalysisChart: React.FC<TotalAnalysisChartProps> = ({ analysisDate, fluidTypes, }: TotalAnalysisChartProps) => { - const [dataLoadArray, setDataLoadArray] = useState<number[] | null>(null) + const [dataLoadValueDetailArray, setDataLoadValueDetailArray] = useState< + DataloadValueDetail[] | null + >(null) const [totalLoadValue, setTotalLoadValue] = useState<number>(0) const client = useClient() const { t } = useI18n() @@ -45,10 +47,11 @@ const TotalAnalysisChart: React.FC<TotalAnalysisChartProps> = ({ TimeStep.MONTH, fluidTypes, undefined, + undefined, true ) if (monthTotalData && monthTotalData.actualData) { - setDataLoadArray(monthTotalData.actualData[0].valueDetail) + setDataLoadValueDetailArray(monthTotalData.actualData[0].valueDetail) setTotalLoadValue(monthTotalData.actualData[0].value) } } @@ -67,9 +70,9 @@ const TotalAnalysisChart: React.FC<TotalAnalysisChartProps> = ({ }} > <div className="text-24-normal title">{t('analysis_pie.total')}</div> - {dataLoadArray && ( + {dataLoadValueDetailArray && ( <PieChart - dataArray={dataLoadArray} + dataloadValueDetailArray={dataLoadValueDetailArray} totalValue={totalLoadValue} width={radius} height={radius} @@ -80,13 +83,15 @@ const TotalAnalysisChart: React.FC<TotalAnalysisChartProps> = ({ .startOf('month')} /> )} - {dataLoadArray && fluidTypes.length > 1 && ( + {dataLoadValueDetailArray && fluidTypes.length > 1 && ( <div className="total-card-container"> - {dataLoadArray.map((load, index) => { + {dataLoadValueDetailArray.map((dataload, index) => { return ( <div key={index} className="total-card"> <div className="text-18-bold fluidconso"> - {load !== -1 ? `${formatNumberValues(load)} €` : '--- €'} + {dataload.value !== -1 + ? `${formatNumberValues(dataload.value)} €` + : '--- €'} </div> <Icon className="euro-fluid-icon" diff --git a/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap b/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap index 4b4a2fe8569330df5c18b54ca6a02c15980ed2c0..8e10e4a997cc5b3069dd4726d95840d0a235aac6 100644 --- a/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/ElecHalfHourChart.spec.tsx.snap @@ -18,21 +18,25 @@ exports[`ElecHalfHourChart component should be rendered correctly 1`] = ` Array [ Object { "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", "value": 12, "valueDetail": null, }, Object { "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", "value": 12, "valueDetail": null, }, Object { "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", "value": 12, "valueDetail": null, }, Object { "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", "value": 12, "valueDetail": null, }, diff --git a/src/components/Analysis/__snapshots__/PieChart.spec.tsx.snap b/src/components/Analysis/__snapshots__/PieChart.spec.tsx.snap index 9e545f371d73d8e87cb173cc6a0e8cfde8465071..b630956172b5d0b582dae7a95fc56e3f765a3a24 100644 --- a/src/components/Analysis/__snapshots__/PieChart.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/PieChart.spec.tsx.snap @@ -15,11 +15,20 @@ exports[`PieChart component should be rendered correctly 1`] = ` > <PieChart currentAnalysisDate={"2021-07-01T00:00:00.000Z"} - dataArray={ + dataloadValueDetailArray={ Array [ - 10, - 20, - 30, + Object { + "state": "VALID", + "value": 10, + }, + Object { + "state": "VALID", + "value": 20, + }, + Object { + "state": "VALID", + "value": 30, + }, ] } height={300} diff --git a/src/components/Charts/BarChart.tsx b/src/components/Charts/BarChart.tsx index 2068381d287e8bd692ec5822aee0f7c1da576aa6..e47408ad23520e872360a122ddf180e1e251e1d2 100644 --- a/src/components/Charts/BarChart.tsx +++ b/src/components/Charts/BarChart.tsx @@ -9,6 +9,7 @@ import { Datachart } from 'models' import Bar from 'components/Charts/Bar' import AxisBottom from 'components/Charts/AxisBottom' import AxisRight from 'components/Charts/AxisRight' +import { DataloadState } from 'enum/dataload.enum' export interface BarChartProps { chartData: Datachart @@ -106,10 +107,10 @@ const BarChart: React.FC<BarChartProps> = ({ height={getContentHeight()} isSwitching={isSwitching} isMultiMissingFluid={ - d.valueDetail - ? d.valueDetail.includes(-1) - ? true - : false + d.state === DataloadState.AGGREGATED_WITH_EMPTY || + d.state === DataloadState.AGGREGATED_WITH_COMING || + d.state === DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING + ? true : false } /> diff --git a/src/components/Charts/__snapshots__/AxisBottom.spec.tsx.snap b/src/components/Charts/__snapshots__/AxisBottom.spec.tsx.snap index 6586916e72db0fed35347ca763269925f8370f33..08178300f2b50684d832be691757d305c1ecb88d 100644 --- a/src/components/Charts/__snapshots__/AxisBottom.spec.tsx.snap +++ b/src/components/Charts/__snapshots__/AxisBottom.spec.tsx.snap @@ -19,24 +19,45 @@ exports[`AxisBottom component test should correctly render DAY format of AxisBot Array [ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], }, Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], }, Object { "date": "2020-10-03T00:00:00.000Z", + "state": "EMPTY", "value": -1, "valueDetail": null, }, @@ -72,24 +93,45 @@ exports[`AxisBottom component test should correctly render DAY format of AxisBot Array [ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], }, Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], }, Object { "date": "2020-10-03T00:00:00.000Z", + "state": "EMPTY", "value": -1, "valueDetail": null, }, @@ -124,24 +166,45 @@ exports[`AxisBottom component test should correctly render HALF_AN_HOUR format o Array [ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], }, Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], }, Object { "date": "2020-10-03T00:00:00.000Z", + "state": "EMPTY", "value": -1, "valueDetail": null, }, @@ -176,24 +239,45 @@ exports[`AxisBottom component test should correctly render MONTH format of AxisB Array [ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], }, Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], }, Object { "date": "2020-10-03T00:00:00.000Z", + "state": "EMPTY", "value": -1, "valueDetail": null, }, @@ -228,24 +312,45 @@ exports[`AxisBottom component test should correctly render WEEK format of AxisBo Array [ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], }, Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], }, Object { "date": "2020-10-03T00:00:00.000Z", + "state": "EMPTY", "value": -1, "valueDetail": null, }, @@ -280,24 +385,45 @@ exports[`AxisBottom component test should correctly render YEAR format of AxisBo Array [ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], }, Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], }, Object { "date": "2020-10-03T00:00:00.000Z", + "state": "EMPTY", "value": -1, "valueDetail": null, }, diff --git a/src/components/Charts/__snapshots__/Bar.spec.tsx.snap b/src/components/Charts/__snapshots__/Bar.spec.tsx.snap index c184d66626f97a124cc65ec17bc3979375885f2b..128c11396c52501b25a9457a280dbc78442b8f2d 100644 --- a/src/components/Charts/__snapshots__/Bar.spec.tsx.snap +++ b/src/components/Charts/__snapshots__/Bar.spec.tsx.snap @@ -18,22 +18,42 @@ exports[`Bar component test should correctly render Bar with isDuel 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } @@ -69,22 +89,42 @@ exports[`Bar component test should correctly render Bar with isSwitching 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } @@ -120,22 +160,42 @@ exports[`Bar component test should correctly render Bar with showCompare 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } @@ -171,22 +231,42 @@ exports[`Bar component test should correctly render Electricity Bar 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } @@ -222,22 +302,42 @@ exports[`Bar component test should correctly render Gas Bar 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } @@ -273,22 +373,42 @@ exports[`Bar component test should correctly render Multifluid Bar 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } @@ -324,22 +444,42 @@ exports[`Bar component test should correctly render Water Bar 1`] = ` compareDataload={ Object { "date": "2020-10-02T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 61.65554999999999, "valueDetail": Array [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + Object { + "state": "VALID", + "value": 40.21918999999999, + }, + Object { + "state": "VALID", + "value": 0.8064649999999999, + }, + Object { + "state": "VALID", + "value": 20.629894999999998, + }, ], } } dataload={ Object { "date": "2020-10-01T00:00:00.000Z", + "state": "AGGREGATED_VALID", "value": 69.18029999999999, "valueDetail": Array [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + Object { + "state": "VALID", + "value": 45.127739999999996, + }, + Object { + "state": "VALID", + "value": 0.9048899999999999, + }, + Object { + "state": "VALID", + "value": 23.147669999999998, + }, ], } } diff --git a/src/components/Connection/__snapshots__/Connection.spec.tsx.snap b/src/components/Connection/__snapshots__/Connection.spec.tsx.snap index c252168a373d53b784409c59b5359353c938b039..990ba1c0eea23080bf48922657c3ce3bba03515a 100644 --- a/src/components/Connection/__snapshots__/Connection.spec.tsx.snap +++ b/src/components/Connection/__snapshots__/Connection.spec.tsx.snap @@ -19,6 +19,7 @@ exports[`Connection component test should call ConnectionLogin 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": "2019-09-01T00:00:00.000Z", "fluidType": 0, "lastDataDate": "2020-09-01T00:00:00.000Z", "status": 200, @@ -46,6 +47,7 @@ exports[`Connection component test should call ConnectionLogin 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": "2019-09-01T00:00:00.000Z", "fluidType": 0, "lastDataDate": "2020-09-01T00:00:00.000Z", "status": 200, @@ -80,6 +82,7 @@ exports[`Connection component test should call ConnectionOAuth 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": "2019-09-01T00:00:00.000Z", "fluidType": 2, "lastDataDate": "2020-09-01T00:00:00.000Z", "status": 200, @@ -107,6 +110,7 @@ exports[`Connection component test should call ConnectionOAuth 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": "2019-09-01T00:00:00.000Z", "fluidType": 2, "lastDataDate": "2020-09-01T00:00:00.000Z", "status": 200, diff --git a/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx index 4db81bc7574a56a31f8098bdaaa0ddf6498386ef..11d88685726a9b85c7e6aa98dad3b06a19564283 100644 --- a/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx @@ -4,13 +4,10 @@ import { useSelector } from 'react-redux' import { AppStore } from 'store' import { FluidType } from 'enum/fluid.enum' import { DateTime } from 'luxon' - import { Dataload } from 'models' -import DateChartService from 'services/dateChart.service' import DataloadConsumptionVisualizer from 'components/ConsumptionVisualizer/DataloadConsumptionVisualizer' -import LastDataConsumptionVisualizer from 'components/ConsumptionVisualizer/LastDataConsumptionVisualizer' -import ErrorDataConsumptionVisualizer from 'components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer' +import InfoDataConsumptionVisualizer from './InfoDataConsumptionVisualizer' interface ConsumptionVisualizerProps { fluidType: FluidType @@ -32,7 +29,6 @@ const ConsumptionVisualizer: React.FC<ConsumptionVisualizerProps> = ({ const compareDataload: Dataload | null = currentDatachart.comparisonData ? currentDatachart.comparisonData[currentDatachartIndex] : null - const dateChartService = new DateChartService() const getLastDataDate = (): DateTime | null => { let lastDay: DateTime | null = null @@ -55,6 +51,7 @@ const ConsumptionVisualizer: React.FC<ConsumptionVisualizerProps> = ({ return lastDay } const lastDataDate: DateTime | null = getLastDataDate() + return ( <div className="consumptionvisualizer-root"> <DataloadConsumptionVisualizer @@ -62,30 +59,14 @@ const ConsumptionVisualizer: React.FC<ConsumptionVisualizerProps> = ({ dataload={dataload} compareDataload={compareDataload} showCompare={showCompare} - lastDataDate={lastDataDate} setActive={setActive} /> <div className="consumptionvisualizer-info"> - {dataload && - dataload.valueDetail && - ((dataload.valueDetail[0] === -1 && - !dateChartService.isDataToCome(dataload, FluidType.ELECTRICITY)) || - (dataload.valueDetail[1] === -1 && - !dateChartService.isDataToCome(dataload, FluidType.WATER)) || - (dataload.valueDetail[2] === -1 && - !dateChartService.isDataToCome(dataload, FluidType.GAS))) && ( - <ErrorDataConsumptionVisualizer fluidType={fluidType} /> - )} - {!dataload || - (dataload && - dataload.value === -1 && - lastDataDate && - dataload.date > lastDataDate && ( - <LastDataConsumptionVisualizer - lastDataDate={lastDataDate} - fluidType={fluidType} - /> - ))} + <InfoDataConsumptionVisualizer + dataload={dataload} + fluidType={fluidType} + lastDataDate={lastDataDate} + /> </div> </div> ) diff --git a/src/components/ConsumptionVisualizer/DataloadComparisonLeft.spec.tsx b/src/components/ConsumptionVisualizer/DataloadComparisonLeft.spec.tsx deleted file mode 100644 index f8823fc779403c72bcfce5b07504cfc1bbbb38c9..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/DataloadComparisonLeft.spec.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import { mount } from 'enzyme' -import { Provider } from 'react-redux' - -import configureStore from 'redux-mock-store' -import { mockInitialEcolyoState } from '../../../tests/__mocks__/store' -import { FluidType } from 'enum/fluid.enum' - -import { baseDataLoad } from '../../../tests/__mocks__/datachartData.mock' -import DataloadComparisonLeft from './DataloadComparisonLeft' - -jest.mock('cozy-ui/transpiled/react/I18n', () => { - return { - useI18n: jest.fn(() => { - return { - t: (str: string) => str, - } - }), - } -}) - -const mockStore = configureStore([]) -describe('DataloadComparisonLeft component', () => { - it('should render correctly', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const wrapper = mount( - <Provider store={store}> - <DataloadComparisonLeft - compareDataload={baseDataLoad} - fluidType={FluidType.ELECTRICITY} - /> - </Provider> - ) - expect(wrapper).toMatchSnapshot() - }) - it('should render empty comparison', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const updatedDataload = { ...baseDataLoad, value: -1 } - const wrapper = mount( - <Provider store={store}> - <DataloadComparisonLeft - compareDataload={updatedDataload} - fluidType={FluidType.ELECTRICITY} - /> - </Provider> - ) - expect(wrapper.find('.dataloadvisualizer-novalue')).toBeTruthy() - }) -}) diff --git a/src/components/ConsumptionVisualizer/DataloadComparisonLeft.tsx b/src/components/ConsumptionVisualizer/DataloadComparisonLeft.tsx deleted file mode 100644 index e0edf457ccbb20cb3bd629a694c7b67de9ddfc96..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/DataloadComparisonLeft.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react' -import './consumptionVisualizer.scss' -import { FluidType } from 'enum/fluid.enum' -import { Dataload } from 'models' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { formatNumberValues } from 'utils/utils' -import ConverterService from 'services/converter.service' - -interface DataloadComparisonLeftProps { - compareDataload: Dataload - fluidType: FluidType -} -const DataloadComparisonLeft: React.FC<DataloadComparisonLeftProps> = ({ - compareDataload, - fluidType, -}: DataloadComparisonLeftProps) => { - const { t } = useI18n() - const converterService = new ConverterService() - - return ( - <> - {compareDataload.value === -1 ? ( - <div className="dataloadvisualizer-section dataloadvisualizer-section-left-novalue"> - <div - className={`dataloadvisualizer-novalue ${FluidType[ - fluidType - ].toLowerCase()}-compare text-20-normal`} - > - {t('consumption_visualizer.missing_data')} - </div> - </div> - ) : ( - <div className="dataloadvisualizer-section dataloadvisualizer-section-left"> - <div - className={`dataloadvisualizer-value ${FluidType[ - fluidType - ].toLowerCase()}-compare text-36-bold`} - > - {formatNumberValues(compareDataload.value)} - <span className="text-18-normal">{`${t( - 'FLUID.' + FluidType[fluidType] + '.UNIT' - )}`}</span> - </div> - <> - {fluidType === FluidType.MULTIFLUID ? ( - <></> - ) : ( - <div - className={`dataloadvisualizer-euro ${FluidType[ - fluidType - ].toLowerCase()}-compare text-16-normal`} - > - {`${formatNumberValues( - converterService.LoadToEuro( - compareDataload.value, - fluidType, - compareDataload.price ? compareDataload.price : null - ) - )} €`} - </div> - )} - </> - </div> - )} - </> - ) -} - -export default DataloadComparisonLeft diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx index ddb94ea25ca318253fb7a30a206dfc9f7a6e5469..d8ff32d39c3499893d0e514f1a7188d12306ed1c 100644 --- a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx @@ -49,9 +49,6 @@ describe('Dataload consumption visualizer component', () => { dataload={baseDataLoad} showCompare={false} compareDataload={baseDataLoad} - lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { - zone: 'utc', - })} setActive={jest.fn()} /> </Provider> @@ -71,9 +68,6 @@ describe('Dataload consumption visualizer component', () => { dataload={baseDataLoad} showCompare={false} compareDataload={baseDataLoad} - lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { - zone: 'utc', - })} setActive={jest.fn()} /> </Provider> @@ -93,9 +87,6 @@ describe('Dataload consumption visualizer component', () => { dataload={baseDataLoad} showCompare={true} compareDataload={emptyDataLoad} - lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { - zone: 'utc', - })} setActive={jest.fn()} /> </Provider> @@ -115,9 +106,6 @@ describe('Dataload consumption visualizer component', () => { dataload={baseDataLoad} showCompare={true} compareDataload={baseDataLoad} - lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { - zone: 'utc', - })} setActive={jest.fn()} /> </Provider> @@ -138,9 +126,6 @@ describe('Dataload consumption visualizer component', () => { dataload={dataLoadWithValueDetailEmpty} showCompare={false} compareDataload={emptyDataLoad} - lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { - zone: 'utc', - })} setActive={jest.fn()} /> </Provider> @@ -175,9 +160,6 @@ describe('Dataload consumption visualizer component', () => { dataload={dataLoadWithValueDetail} showCompare={false} compareDataload={emptyDataLoad} - lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { - zone: 'utc', - })} setActive={jest.fn()} /> </Router> diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx index 15dfb718fd53632d67ae33426b5f8e30efeff5c3..ec1fa6b954c4a7890d24e5c5ba00c5bcd03df3e1 100644 --- a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx @@ -1,32 +1,19 @@ import React, { useCallback, useState } from 'react' import './dataloadConsumptionVisualizer.scss' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { useSelector } from 'react-redux' import { AppStore } from 'store' -import { NavLink } from 'react-router-dom' -import { DateTime } from 'luxon' -import { formatNumberValues } from 'utils/utils' - import { Dataload } from 'models' import { FluidType } from 'enum/fluid.enum' -import ConverterService from 'services/converter.service' -import DateChartService from 'services/dateChart.service' -import { getNavPicto } from 'utils/picto' - -import Icon from 'cozy-ui/transpiled/react/Icon' -import { useClient } from 'cozy-client' -import { UsageEventType } from 'enum/usageEvent.enum' -import UsageEventService from 'services/usageEvent.service' import EstimatedConsumptionModal from './EstimatedConsumptionModal' -import DataloadComparisonLeft from './DataloadComparisonLeft' import DataloadNoValue from './DataloadNoValue' +import { DataloadSectionType, DataloadState } from 'enum/dataload.enum' +import DataloadSection from './DataloadSection' interface DataloadConsumptionVisualizerProps { fluidType: FluidType dataload: Dataload compareDataload: Dataload | null showCompare: boolean - lastDataDate: DateTime | null setActive: React.Dispatch<React.SetStateAction<boolean>> } const DataloadConsumptionVisualizer = ({ @@ -34,209 +21,64 @@ const DataloadConsumptionVisualizer = ({ dataload, compareDataload, showCompare, - lastDataDate, setActive, }: DataloadConsumptionVisualizerProps) => { - const { t } = useI18n() - const { loading, currentDatachart } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) - const client = useClient() + const { loading } = useSelector((state: AppStore) => state.ecolyo.chart) const [openEstimationModal, setOpenEstimationModal] = useState<boolean>(false) - const converterService = new ConverterService() - const dateChartService = new DateChartService() - const emitNavEvent = useCallback( - async (targetPage: string) => { - await UsageEventService.addEvent(client, { - type: UsageEventType.NAVIGATION_EVENT, - target: targetPage, - }) - }, - [client] - ) const toggleEstimationModal = useCallback(() => { setOpenEstimationModal(prev => !prev) }, []) - return ( - <div className="dataloadvisualizer-root"> - {!loading && dataload && dataload.value > -1 ? ( - <div className="dataloadvisualizer-content"> - {showCompare && compareDataload && ( - <DataloadComparisonLeft - fluidType={fluidType} - compareDataload={compareDataload} - /> - )} - <div - className={ - showCompare - ? 'dataloadvisualizer-section dataloadvisualizer-section-right' - : 'dataloadvisualizer-section' - } - > - <div - className={`dataloadvisualizer-value ${FluidType[ - fluidType - ].toLowerCase()} text-36-bold ${ - fluidType === FluidType.MULTIFLUID && showCompare - ? 'multifluid-compare-color' - : '' - }`} - > - {formatNumberValues(dataload.value, FluidType[fluidType], true) >= - 1000 && fluidType !== FluidType.MULTIFLUID ? ( - <> - {formatNumberValues(dataload.value, FluidType[fluidType])} - <span className="text-18-normal"> - {`${t('FLUID.' + FluidType[fluidType] + '.MEGAUNIT')}`} - </span> - </> - ) : ( - <> - {formatNumberValues(dataload.value)} - <span - className={`text-18-normal ${ - fluidType === FluidType.MULTIFLUID && !showCompare - ? 'euroUnit' - : '' - }`} - > - {`${t('FLUID.' + FluidType[fluidType] + '.UNIT')}`} - </span> - {fluidType === FluidType.MULTIFLUID && !showCompare && ( - <span - className="text-14-normal estimated" - onClick={toggleEstimationModal} - > - {t('consumption_visualizer.estimated')} - </span> - )} - </> - )} - </div> - {fluidType !== FluidType.MULTIFLUID ? ( - <div - className={`dataloadvisualizer-euro ${FluidType[ - fluidType - ].toLowerCase()} text-16-normal`} - > - {`${formatNumberValues( - converterService.LoadToEuro( - dataload.value, - fluidType, - dataload.price ? dataload.price : null - ) - )} €`} - </div> - ) : ( - <> - {showCompare ? ( - <></> - ) : ( - <div className="dataloadvisualizer-euro text-16-normal"> - {dataload.valueDetail ? ( - dataload.valueDetail.map((load, index) => { - return ( - <NavLink - key={index} - to={`/consumption/${FluidType[ - index - ].toLowerCase()}`} - className="dataloadvisualizer-euro-link" - > - <div - className={ - load !== -1 - ? `dataloadvisualizer-euro-fluid ${FluidType[ - index - ].toLowerCase()}` - : !dateChartService.isDataToCome( - dataload, - index - ) - ? dateChartService.isDataHole( - currentDatachart, - index - ) - ? `dataloadvisualizer-euro-fluid ${FluidType[ - index - ].toLowerCase()}` - : 'dataloadvisualizer-euro-fluid error' - : `dataloadvisualizer-euro-fluid ${FluidType[ - index - ].toLowerCase()}` - } - onClick={() => - emitNavEvent(FluidType[index].toLowerCase()) - } - > - <Icon - className="dataloadvisualizer-euro-fluid-icon" - icon={getNavPicto(index, true, true)} - size={22} - /> - <div> - {load !== -1 - ? `${formatNumberValues(load)} €` - : !dateChartService.isDataToCome( - dataload, - index - ) - ? dateChartService.isDataHole( - currentDatachart, - index - ) - ? 'Vide' - : t('consumption_visualizer.aie') - : t('consumption_visualizer.data_to_come')} - </div> - </div> - </NavLink> - ) - }) - ) : ( - <NavLink - to={`/consumption/${FluidType[ - fluidType - ].toLowerCase()}`} - className="dataloadvisualizer-euro-link" - > - <div - className={`dataloadvisualizer-euro-fluid ${FluidType[ - fluidType - ].toLowerCase()}`} - > - <Icon - className="dataloadvisualizer-euro-fluid-icon" - icon={getNavPicto(fluidType, true, true)} - size={22} - /> - <div>{`${formatNumberValues( - converterService.LoadToEuro( - dataload.value, - fluidType, - dataload.price ? dataload.price : null - ) - )} €`}</div> - </div> - </NavLink> - )} - </div> - )} - </> - )} - </div> - </div> - ) : ( + if (loading || !dataload) { + return <div className="dataloadvisualizer-root"></div> + } + + if ( + dataload.state !== DataloadState.VALID && + dataload.state !== DataloadState.AGGREGATED_VALID && + dataload.state !== DataloadState.AGGREGATED_WITH_EMPTY && + dataload.state !== DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING && + dataload.state !== DataloadState.AGGREGATED_WITH_COMING + ) { + return ( + <div className="dataloadvisualizer-root"> <DataloadNoValue - lastDataDate={lastDataDate} dataload={dataload} setActive={setActive} fluidType={fluidType} /> - )} + </div> + ) + } + + return ( + <div className="dataloadvisualizer-root"> + <div className="dataloadvisualizer-content"> + {showCompare && compareDataload ? ( + <> + <DataloadSection + dataload={compareDataload} + fluidType={fluidType} + dataloadSectionType={DataloadSectionType.LEFT} + toggleEstimationModal={toggleEstimationModal} + /> + <DataloadSection + dataload={dataload} + fluidType={fluidType} + dataloadSectionType={DataloadSectionType.RIGHT} + toggleEstimationModal={toggleEstimationModal} + /> + </> + ) : ( + <DataloadSection + dataload={dataload} + fluidType={fluidType} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={toggleEstimationModal} + /> + )} + </div> <EstimatedConsumptionModal open={openEstimationModal} handleCloseClick={toggleEstimationModal} diff --git a/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx b/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx index 3a2cf00dafef1534959dc6f6959c8046e59a5d29..f1e680ae2c54ba432b88a476a368a561f1a877f1 100644 --- a/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx +++ b/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx @@ -1,17 +1,11 @@ import React from 'react' import { mount } from 'enzyme' -import { Provider } from 'react-redux' -import * as reactRedux from 'react-redux' -import configureStore from 'redux-mock-store' -import { mockInitialEcolyoState } from '../../../tests/__mocks__/store' +import toJson from 'enzyme-to-json' import { FluidType } from 'enum/fluid.enum' import DataloadNoValue from './DataloadNoValue' -import { - baseDataLoad, - graphData, -} from '../../../tests/__mocks__/datachartData.mock' -import { DateTime } from 'luxon' -import { Datachart } from 'models' +import { baseDataLoad } from '../../../tests/__mocks__/datachartData.mock' +import { DataloadState } from 'enum/dataload.enum' +import { Dataload } from 'models' jest.mock('cozy-ui/transpiled/react/I18n', () => { return { @@ -22,84 +16,109 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => { }), } }) +const mockSetActive = jest.fn() -const mockStore = configureStore([]) describe('DataloadNoValue component', () => { + const mockDataload: Dataload = baseDataLoad + it('should render correctly', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const mockSetActive = jest.fn() const wrapper = mount( - <Provider store={store}> + <DataloadNoValue + dataload={mockDataload} + fluidType={FluidType.ELECTRICITY} + setActive={mockSetActive} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + + describe('should render correctly no data', () => { + it('case state EMPTY', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.EMPTY } + const wrapper = mount( <DataloadNoValue - dataload={baseDataLoad} + dataload={_mockdataLoad} fluidType={FluidType.ELECTRICITY} - lastDataDate={null} setActive={mockSetActive} /> - </Provider> - ) - expect(wrapper).toMatchSnapshot() - }) - it('should render with missing data state and click on the error button', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, + ) + expect(wrapper.find('.dataloadvisualizer-value').text()).toBe( + 'consumption_visualizer.no_data' + ) }) - const mockSetActive = jest.fn() - const wrapper = mount( - <Provider store={store}> + it('case state HOLE', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.HOLE } + const wrapper = mount( <DataloadNoValue - dataload={baseDataLoad} + dataload={_mockdataLoad} fluidType={FluidType.ELECTRICITY} - lastDataDate={null} setActive={mockSetActive} /> - </Provider> - ) - expect(wrapper.find('.error').simulate('click')) - }) - - it('should render with data-to-come state', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, + ) + expect(wrapper.find('.dataloadvisualizer-value').text()).toBe( + 'consumption_visualizer.no_data' + ) }) - const updatedDataLoad = { ...baseDataLoad, date: DateTime.local() } - const mockSetActive = jest.fn() - const wrapper = mount( - <Provider store={store}> + it('case state AGGREGATED_EMPTY', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.AGGREGATED_EMPTY, + } + const wrapper = mount( <DataloadNoValue - dataload={updatedDataLoad} - fluidType={FluidType.ELECTRICITY} - lastDataDate={null} + dataload={_mockdataLoad} + fluidType={FluidType.MULTIFLUID} setActive={mockSetActive} /> - </Provider> - ) - expect(wrapper.find('.to-come')).toBeTruthy() - }) - it('should render with data-hole state', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, + ) + expect(wrapper.find('.dataloadvisualizer-value').text()).toBe( + 'consumption_visualizer.no_data' + ) }) - const mockSetActive = jest.fn() - const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') - const updatedData: Datachart = { ...graphData } - //Data hole insertion - updatedData.actualData[0].value = -1 - updatedData.actualData[1].value = 90 + }) - mockUseSelector.mockReturnValue({ currentDatachart: graphData }) - const wrapper = mount( - <Provider store={store}> + describe('should render correctly missing data', () => { + it('case state MISSING', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.MISSING } + const wrapper = mount( <DataloadNoValue - dataload={baseDataLoad} + dataload={_mockdataLoad} fluidType={FluidType.ELECTRICITY} - lastDataDate={DateTime.local()} setActive={mockSetActive} /> - </Provider> + ) + expect(wrapper.find('.dataloadvisualizer-content').text()).toBe( + 'consumption_visualizer.missing_data' + ) + }) + it('case state AGGREGATED_HOLE_OR_MISSING', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.AGGREGATED_HOLE_OR_MISSING, + } + const wrapper = mount( + <DataloadNoValue + dataload={_mockdataLoad} + fluidType={FluidType.MULTIFLUID} + setActive={mockSetActive} + /> + ) + expect(wrapper.find('.dataloadvisualizer-content').text()).toBe( + 'consumption_visualizer.missing_data' + ) + }) + }) + + it('should call setActive when missing message is clicked', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.MISSING } + const wrapper = mount( + <DataloadNoValue + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + setActive={mockSetActive} + /> ) - expect(wrapper.find('.no-data-text').simulate('click')) + wrapper.find('.dataloadvisualizer-content').simulate('click') + expect(mockSetActive).toBeCalledWith(true) }) }) diff --git a/src/components/ConsumptionVisualizer/DataloadNoValue.tsx b/src/components/ConsumptionVisualizer/DataloadNoValue.tsx index 667bc7de690abaf63823028e5569926f88af89c5..e00a7d364f4fad6492e0791110b50331c5b285aa 100644 --- a/src/components/ConsumptionVisualizer/DataloadNoValue.tsx +++ b/src/components/ConsumptionVisualizer/DataloadNoValue.tsx @@ -1,44 +1,24 @@ -import React, { useCallback, useState } from 'react' +import React, { useCallback } from 'react' import './consumptionVisualizer.scss' import { FluidType } from 'enum/fluid.enum' import { Dataload } from 'models' import { useI18n } from 'cozy-ui/transpiled/react/I18n' - -import { DateTime } from 'luxon' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' -import ConfigService from 'services/fluidConfig.service' -import NoDataModal from './NoDataModal' -import DateChartService from 'services/dateChart.service' +import { DataloadState } from 'enum/dataload.enum' +import classNames from 'classnames' interface DataloadNoValueProps { dataload: Dataload fluidType: FluidType - lastDataDate: DateTime | null setActive: React.Dispatch<React.SetStateAction<boolean>> } -enum NoDataState { - DATA_TO_COME = 0, - DATA_HOLE = 1, - MISSING_DATA = 2, -} const DataloadNoValue: React.FC<DataloadNoValueProps> = ({ dataload, fluidType, setActive, - lastDataDate, }: DataloadNoValueProps) => { const { t } = useI18n() - const { currentDatachart } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) - const configService = new ConfigService() - const fluidConfig = configService.getFluidConfig() - const [openNodataModal, setopenNodataModal] = useState<boolean>(false) - const toggleNoDataModal = useCallback(() => { - setopenNodataModal(prev => !prev) - }, []) + const handleToggleKonnectionCard = useCallback(() => { setActive(true) const app = document.querySelector('.app-content') @@ -60,77 +40,54 @@ const DataloadNoValue: React.FC<DataloadNoValueProps> = ({ } }, [setActive]) - const getDataState = useCallback(() => { - if (fluidType !== FluidType.MULTIFLUID) { - //J+3 for elec and J+5 for the other - const delay = fluidConfig[fluidType].dataDelayOffset + 1 - const today = DateTime.local().setZone('utc', { - keepLocalTime: true, - }) - const offsetDate = today.minus({ days: delay }) - if (dataload && offsetDate < dataload.date) { - return NoDataState.DATA_TO_COME - } else if (dataload && dataload.date < offsetDate) { - const dateChartService = new DateChartService() - if ( - dateChartService.isDataHole(currentDatachart) && - lastDataDate && - lastDataDate > dataload.date - ) { - return NoDataState.DATA_HOLE - } else return NoDataState.MISSING_DATA - } else return NoDataState.MISSING_DATA - } - return NoDataState.DATA_TO_COME - }, [currentDatachart, dataload, fluidConfig, fluidType, lastDataDate]) - - return ( - <> - {getDataState() === NoDataState.DATA_TO_COME && ( - <div className={`dataloadvisualizer-content text-22-normal`}> - <div className="dataloadvisualizer-section"> - <div - className={`dataloadvisualizer-value ${FluidType[ - fluidType - ].toLowerCase()} upper to-come`} - > - {t('consumption_visualizer.data_to_come')} - </div> + if ( + dataload.state === DataloadState.EMPTY || + dataload.state === DataloadState.HOLE || + dataload.state === DataloadState.AGGREGATED_EMPTY + ) { + return ( + <div className={`dataloadvisualizer-content text-22-normal`}> + <div className="dataloadvisualizer-section"> + <div + className={`dataloadvisualizer-value ${FluidType[ + fluidType + ].toLowerCase()} upper`} + > + {t('consumption_visualizer.no_data')} </div> </div> - )} - {getDataState() === NoDataState.MISSING_DATA && ( + </div> + ) + } + + if ( + dataload.state === DataloadState.MISSING || + dataload.state === DataloadState.AGGREGATED_HOLE_OR_MISSING + ) { + return ( + <div + onClick={handleToggleKonnectionCard} + className={classNames('dataloadvisualizer-content text-22-normal', { + ['error']: fluidType !== FluidType.MULTIFLUID, + })} + > + {t('consumption_visualizer.missing_data')} + </div> + ) + } + + return ( + <div className={`dataloadvisualizer-content text-22-normal`}> + <div className="dataloadvisualizer-section"> <div - onClick={handleToggleKonnectionCard} - className={`dataloadvisualizer-content error text-22-normal`} + className={`dataloadvisualizer-value ${FluidType[ + fluidType + ].toLowerCase()} upper to-come`} > - {t('consumption_visualizer.missing_data')} - </div> - )} - {getDataState() === NoDataState.DATA_HOLE && ( - <div className={`dataloadvisualizer-content text-22-normal`}> - <div className="dataloadvisualizer-section"> - <div - className={`dataloadvisualizer-value ${FluidType[ - fluidType - ].toLowerCase()} upper`} - > - {t('consumption_visualizer.no_data')} - </div> - <div - className="text-15-normal no-data-text" - onClick={toggleNoDataModal} - > - {t('consumption_visualizer.why_no_data')} - </div> - </div> + {t('consumption_visualizer.data_to_come')} </div> - )} - <NoDataModal - open={openNodataModal} - handleCloseClick={toggleNoDataModal} - /> - </> + </div> + </div> ) } diff --git a/src/components/ConsumptionVisualizer/DataloadSection.spec.tsx b/src/components/ConsumptionVisualizer/DataloadSection.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..348a7d9d6077effa0a4190e39b31f06bf02799cf --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadSection.spec.tsx @@ -0,0 +1,60 @@ +import React from 'react' +import { mount } from 'enzyme' +import toJson from 'enzyme-to-json' +import { FluidType } from 'enum/fluid.enum' +import { baseDataLoad } from '../../../tests/__mocks__/datachartData.mock' +import { DataloadSectionType } from 'enum/dataload.enum' +import { Dataload } from 'models' +import DataloadSection from './DataloadSection' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +jest.mock( + 'components/ConsumptionVisualizer/DataloadSectionValue', + () => 'mock-dataloadsectionvalue' +) +jest.mock( + 'components/ConsumptionVisualizer/DataloadSectionDetail', + () => 'mock-dataloadsectiondetail' +) + +const mockToggleEstimationModal = jest.fn() + +describe('DataloadSection component', () => { + const mockDataload: Dataload = baseDataLoad + + it('should render correctly', () => { + const wrapper = mount( + <DataloadSection + dataload={mockDataload} + fluidType={FluidType.ELECTRICITY} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + + it('should render no_data when dataload value is -1 and section is left', () => { + const _mockDatalaod = { ...mockDataload, value: -1 } + const wrapper = mount( + <DataloadSection + dataload={_mockDatalaod} + fluidType={FluidType.ELECTRICITY} + dataloadSectionType={DataloadSectionType.LEFT} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect(wrapper.find('.dataloadvisualizer-novalue').text()).toBe( + 'consumption_visualizer.no_data' + ) + }) +}) diff --git a/src/components/ConsumptionVisualizer/DataloadSection.tsx b/src/components/ConsumptionVisualizer/DataloadSection.tsx new file mode 100644 index 0000000000000000000000000000000000000000..14482e3c5bc62bc4738652ca9e5f385ea25f60dd --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadSection.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import './consumptionVisualizer.scss' +import { FluidType } from 'enum/fluid.enum' +import { Dataload } from 'models' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { DataloadSectionType } from 'enum/dataload.enum' +import classNames from 'classnames' + +import DataloadSectionValue from './DataloadSectionValue' +import DataloadSectionDetail from './DataloadSectionDetail' + +interface DataloadSectionProps { + dataload: Dataload + fluidType: FluidType + dataloadSectionType: DataloadSectionType + toggleEstimationModal: () => void +} +const DataloadSection: React.FC<DataloadSectionProps> = ({ + dataload, + fluidType, + dataloadSectionType, + toggleEstimationModal, +}: DataloadSectionProps) => { + const { t } = useI18n() + + if ( + dataload.value === -1 && + dataloadSectionType === DataloadSectionType.LEFT + ) { + return ( + <div className="dataloadvisualizer-section dataloadvisualizer-section-left-novalue"> + <div + className={`dataloadvisualizer-novalue ${FluidType[ + fluidType + ].toLowerCase()}-compare text-20-normal`} + > + {t('consumption_visualizer.no_data')} + </div> + </div> + ) + } + + return ( + <div + className={classNames('dataloadvisualizer-section', { + ['dataloadvisualizer-section-left']: + dataloadSectionType === DataloadSectionType.LEFT, + ['dataloadvisualizer-section-right']: + dataloadSectionType === DataloadSectionType.RIGHT, + })} + > + <div + className={classNames('dataloadvisualizer-value', 'text-36-bold', { + [`${FluidType[fluidType].toLowerCase()}-compare`]: + dataloadSectionType === DataloadSectionType.LEFT, + [`${FluidType[fluidType].toLowerCase()}`]: + dataloadSectionType === DataloadSectionType.NO_COMPARE || + dataloadSectionType === DataloadSectionType.RIGHT, + ['multifluid-compare-color']: + dataloadSectionType === DataloadSectionType.RIGHT && + fluidType === FluidType.MULTIFLUID, + })} + > + <DataloadSectionValue + dataload={dataload} + fluidType={fluidType} + dataloadSectionType={dataloadSectionType} + toggleEstimationModal={toggleEstimationModal} + /> + </div> + <DataloadSectionDetail + dataload={dataload} + fluidType={fluidType} + dataloadSectionType={dataloadSectionType} + /> + </div> + ) +} + +export default DataloadSection diff --git a/src/components/ConsumptionVisualizer/DataloadSectionDetail.spec.tsx b/src/components/ConsumptionVisualizer/DataloadSectionDetail.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6a515cd71baeab0f7dc8f0182a21e42013d8010f --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadSectionDetail.spec.tsx @@ -0,0 +1,137 @@ +import React from 'react' +import { mount } from 'enzyme' +import toJson from 'enzyme-to-json' +import { FluidType } from 'enum/fluid.enum' +import { baseDataLoad } from '../../../tests/__mocks__/datachartData.mock' +import { DataloadSectionType, DataloadState } from 'enum/dataload.enum' +import { Dataload } from 'models' +import DataloadSectionDetail from './DataloadSectionDetail' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +describe('DataloadSectionDetail component', () => { + const mockDataload: Dataload = baseDataLoad + + it('should render correctly', () => { + const wrapper = mount( + <DataloadSectionDetail + dataload={mockDataload} + fluidType={FluidType.ELECTRICITY} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + it('should not display if multifluid and comparison', () => { + const wrapper = mount( + <DataloadSectionDetail + dataload={mockDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + it('should not display value details if multifluid, no valueDetail and comparison', () => { + const mockMultiDataload: Dataload = { + ...mockDataload, + state: DataloadState.AGGREGATED_EMPTY, + valueDetail: null, + } + const wrapper = mount( + <DataloadSectionDetail + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + + describe('should display value details if multifluid and no comparison', () => { + it('case all valid data', () => { + const mockMultiDataload: Dataload = { + ...mockDataload, + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 1, state: DataloadState.VALID }, + { value: 2, state: DataloadState.VALID }, + { value: 3, state: DataloadState.VALID }, + ], + } + const wrapper = mount( + <DataloadSectionDetail + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + it('case valid and coming data', () => { + const mockMultiDataload: Dataload = { + ...mockDataload, + state: DataloadState.AGGREGATED_WITH_COMING, + valueDetail: [ + { value: 1, state: DataloadState.VALID }, + { value: -1, state: DataloadState.COMING }, + { value: 3, state: DataloadState.VALID }, + ], + } + const wrapper = mount( + <DataloadSectionDetail + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + it('case valid and missing data', () => { + const mockMultiDataload: Dataload = { + ...mockDataload, + state: DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING, + valueDetail: [ + { value: 1, state: DataloadState.VALID }, + { value: -1, state: DataloadState.MISSING }, + { value: 3, state: DataloadState.VALID }, + ], + } + const wrapper = mount( + <DataloadSectionDetail + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + it('case valid and hole data', () => { + const mockMultiDataload: Dataload = { + ...mockDataload, + state: DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING, + valueDetail: [ + { value: 1, state: DataloadState.VALID }, + { value: -1, state: DataloadState.HOLE }, + { value: 3, state: DataloadState.VALID }, + ], + } + const wrapper = mount( + <DataloadSectionDetail + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + }) +}) diff --git a/src/components/ConsumptionVisualizer/DataloadSectionDetail.tsx b/src/components/ConsumptionVisualizer/DataloadSectionDetail.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f3b4494d432dc54abb4fd801342ef2e06272b811 --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadSectionDetail.tsx @@ -0,0 +1,145 @@ +import React, { useCallback } from 'react' +import { NavLink } from 'react-router-dom' +import { useClient } from 'cozy-client' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import Icon from 'cozy-ui/transpiled/react/Icon' +import { getNavPicto } from 'utils/picto' +import { Dataload } from 'models' +import { FluidType } from 'enum/fluid.enum' +import { DataloadSectionType, DataloadState } from 'enum/dataload.enum' +import { UsageEventType } from 'enum/usageEvent.enum' +import ConverterService from 'services/converter.service' +import { formatNumberValues } from 'utils/utils' +import UsageEventService from 'services/usageEvent.service' +import classNames from 'classnames' + +interface DataloadSectionDetailProps { + dataload: Dataload + fluidType: FluidType + dataloadSectionType: DataloadSectionType +} + +const DataloadSectionDetail = ({ + dataload, + fluidType, + dataloadSectionType, +}: DataloadSectionDetailProps) => { + const client = useClient() + const { t } = useI18n() + const converterService = new ConverterService() + + const emitNavEvent = useCallback( + async (targetPage: string) => { + await UsageEventService.addEvent(client, { + type: UsageEventType.NAVIGATION_EVENT, + target: targetPage, + }) + }, + [client] + ) + + if (fluidType !== FluidType.MULTIFLUID) { + return ( + <div + className={`dataloadvisualizer-euro ${FluidType[ + fluidType + ].toLowerCase()}-compare text-16-normal`} + > + {`${formatNumberValues( + converterService.LoadToEuro( + dataload.value, + fluidType, + dataload.price ? dataload.price : null + ) + )} €`} + </div> + ) + } + + if ( + fluidType === FluidType.MULTIFLUID && + dataloadSectionType !== DataloadSectionType.NO_COMPARE + ) { + return <div className="dataloadvisualizer-euro text-16-normal"></div> + } + + if ( + fluidType === FluidType.MULTIFLUID && + dataloadSectionType === DataloadSectionType.NO_COMPARE && + dataload.valueDetail + ) { + return ( + <div className="dataloadvisualizer-euro text-16-normal"> + {dataload.valueDetail.map((valueDetail, index) => { + return ( + <NavLink + key={index} + to={`/consumption/${FluidType[index].toLowerCase()}`} + className="dataloadvisualizer-euro-link" + > + <div + className={classNames('dataloadvisualizer-euro-fluid', { + [` ${FluidType[index].toLowerCase()}`]: + valueDetail.state === DataloadState.VALID || + valueDetail.state === DataloadState.UPCOMING || + valueDetail.state === DataloadState.COMING || + valueDetail.state === DataloadState.EMPTY || + valueDetail.state === DataloadState.HOLE, + [` error`]: valueDetail.state === DataloadState.MISSING, + })} + onClick={() => emitNavEvent(FluidType[index].toLowerCase())} + > + <Icon + className="dataloadvisualizer-euro-fluid-icon" + icon={getNavPicto(index, true, true)} + size={22} + /> + <div> + {valueDetail.state === DataloadState.VALID && + `${formatNumberValues(valueDetail.value)} €`} + {(valueDetail.state === DataloadState.UPCOMING || + valueDetail.state === DataloadState.COMING) && + t('consumption_visualizer.data_to_come')} + {(valueDetail.state === DataloadState.EMPTY || + valueDetail.state === DataloadState.HOLE) && + t('consumption_visualizer.data_empty')} + {valueDetail.state === DataloadState.MISSING && + t('consumption_visualizer.aie')} + </div> + </div> + </NavLink> + ) + })} + </div> + ) + } + return ( + <div className="dataloadvisualizer-euro text-16-normal"> + <NavLink + to={`/consumption/${FluidType[fluidType].toLowerCase()}`} + className="dataloadvisualizer-euro-link" + > + <div + className={`dataloadvisualizer-euro-fluid ${FluidType[ + fluidType + ].toLowerCase()}`} + > + <Icon + className="dataloadvisualizer-euro-fluid-icon" + icon={getNavPicto(fluidType, true, true)} + size={22} + /> + <div>{`${formatNumberValues( + converterService.LoadToEuro( + dataload.value, + fluidType, + dataload.price ? dataload.price : null + ) + )} €`}</div> + </div> + </NavLink> + </div> + ) +} + +export default DataloadSectionDetail diff --git a/src/components/ConsumptionVisualizer/DataloadSectionValue.spec.tsx b/src/components/ConsumptionVisualizer/DataloadSectionValue.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..24afd34dce7ab6f32fff60aa615b8ccbfd83333e --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadSectionValue.spec.tsx @@ -0,0 +1,134 @@ +import React from 'react' +import { mount } from 'enzyme' +import toJson from 'enzyme-to-json' +import { FluidType } from 'enum/fluid.enum' +import { + baseDataLoad, + baseMultiFluidDataLoad, +} from '../../../tests/__mocks__/datachartData.mock' +import { DataloadSectionType } from 'enum/dataload.enum' +import { Dataload } from 'models' +import DataloadSectionValue from './DataloadSectionValue' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +const mockToggleEstimationModal = jest.fn() + +describe('DataloadSectionValue component', () => { + const mockDataload: Dataload = baseDataLoad + const mockMultiDataload: Dataload = baseMultiFluidDataLoad + it('should render correctly', () => { + const wrapper = mount( + <DataloadSectionValue + dataload={mockDataload} + fluidType={FluidType.ELECTRICITY} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + + describe('case Singlefluid', () => { + it('should render with unit when value < 1000', () => { + const wrapper = mount( + <DataloadSectionValue + dataload={mockDataload} + fluidType={FluidType.ELECTRICITY} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect( + wrapper + .find(DataloadSectionValue) + .first() + .contains('12,00') + ).toBeTruthy() + expect(wrapper.find('.text-18-normal').text()).toBe( + 'FLUID.ELECTRICITY.UNIT' + ) + }) + it('should render with mega unit when value >= 1000', () => { + const _mockDatalaod: Dataload = { ...mockDataload, value: 1000 } + const wrapper = mount( + <DataloadSectionValue + dataload={_mockDatalaod} + fluidType={FluidType.ELECTRICITY} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect( + wrapper + .find(DataloadSectionValue) + .first() + .contains('1,00') + ).toBeTruthy() + expect(wrapper.find('.text-18-normal').text()).toBe( + 'FLUID.ELECTRICITY.MEGAUNIT' + ) + }) + }) + + describe('case Multifluid', () => { + it('should render correctly when comparison', () => { + const wrapper = mount( + <DataloadSectionValue + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.RIGHT} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect( + wrapper + .find(DataloadSectionValue) + .first() + .contains('12,00') + ).toBeTruthy() + expect(wrapper.find('.euroUnit').exists()).toBeTruthy() + }) + it('should render correctly with a estimated link when no comparison', () => { + const wrapper = mount( + <DataloadSectionValue + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + expect( + wrapper + .find(DataloadSectionValue) + .first() + .contains('12,00') + ).toBeTruthy() + expect(wrapper.find('.euroUnit').exists()).toBeFalsy() + expect(wrapper.find('.estimated').exists()).toBeTruthy() + expect(wrapper.find('.estimated').text()).toBe( + 'consumption_visualizer.estimated' + ) + }) + it('should call toggleEstimationModal when click on the estimated link', () => { + const wrapper = mount( + <DataloadSectionValue + dataload={mockMultiDataload} + fluidType={FluidType.MULTIFLUID} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={mockToggleEstimationModal} + /> + ) + wrapper.find('.estimated').simulate('click') + expect(mockToggleEstimationModal).toBeCalled() + }) + }) +}) diff --git a/src/components/ConsumptionVisualizer/DataloadSectionValue.tsx b/src/components/ConsumptionVisualizer/DataloadSectionValue.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f1ffda1ed6da377c9acd564ff13ddee56d8179a9 --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadSectionValue.tsx @@ -0,0 +1,70 @@ +import React from 'react' +import classNames from 'classnames' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { Dataload } from 'models' +import { FluidType } from 'enum/fluid.enum' +import { DataloadSectionType } from 'enum/dataload.enum' +import { formatNumberValues } from 'utils/utils' + +interface DataloadSectionValueProps { + dataload: Dataload + fluidType: FluidType + dataloadSectionType: DataloadSectionType + toggleEstimationModal: () => void +} + +const DataloadSectionValue = ({ + dataload, + fluidType, + dataloadSectionType, + toggleEstimationModal, +}: DataloadSectionValueProps) => { + const { t } = useI18n() + + if (fluidType === FluidType.MULTIFLUID) { + return ( + <> + {formatNumberValues(dataload.value)} + <span + className={classNames('text-18-normal', { + ['euroUnit']: + dataloadSectionType !== DataloadSectionType.NO_COMPARE, + })} + > + {`${t('FLUID.' + FluidType[fluidType] + '.UNIT')}`} + </span> + {dataloadSectionType === DataloadSectionType.NO_COMPARE && ( + <span + className="text-14-normal estimated" + onClick={toggleEstimationModal} + > + {t('consumption_visualizer.estimated')} + </span> + )} + </> + ) + } + + return ( + <> + {formatNumberValues(dataload.value, FluidType[fluidType], true) >= + 1000 ? ( + <> + {formatNumberValues(dataload.value, FluidType[fluidType])} + <span className="text-18-normal"> + {`${t('FLUID.' + FluidType[fluidType] + '.MEGAUNIT')}`} + </span> + </> + ) : ( + <> + {formatNumberValues(dataload.value)} + <span className={'text-18-normal'}> + {`${t('FLUID.' + FluidType[fluidType] + '.UNIT')}`} + </span> + </> + )} + </> + ) +} + +export default DataloadSectionValue diff --git a/src/components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer.spec.tsx deleted file mode 100644 index 2f56ebdb10d2eaf4308a240f3baa12bd840f57c6..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer.spec.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react' -import { mount } from 'enzyme' -import { Provider } from 'react-redux' -import * as reactRedux from 'react-redux' - -import configureStore from 'redux-mock-store' -import { mockInitialEcolyoState } from '../../../tests/__mocks__/store' -import ErrorDataConsumptionVisualizer from './ErrorDataConsumptionVisualizer' -import { FluidType } from 'enum/fluid.enum' -import { FluidStatus } from 'models' -import { fluidStatusData } from '../../../tests/__mocks__/fluidStatusData.mock' - -jest.mock('cozy-ui/transpiled/react/I18n', () => { - return { - useI18n: jest.fn(() => { - return { - t: (str: string) => str, - } - }), - } -}) - -const mockStore = configureStore([]) -describe('ErrorDataConsumptionVisualizer component', () => { - it('should render correctly', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const wrapper = mount( - <Provider store={store}> - <ErrorDataConsumptionVisualizer fluidType={FluidType.ELECTRICITY} /> - </Provider> - ) - expect(wrapper).toMatchSnapshot() - }) - it('should click and move to lastDataDate', async () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') - - const wrapper = mount( - <Provider store={store}> - <ErrorDataConsumptionVisualizer fluidType={FluidType.MULTIFLUID} /> - </Provider> - ) - wrapper.find('.error-line').simulate('click') - expect(mockUseDispatch).toHaveBeenCalled() - }) - it('should render with Electricity and no LastDataDate', async () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') - const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') - const updatedFluidStatus: FluidStatus[] = { ...fluidStatusData } - updatedFluidStatus[0].lastDataDate = null - mockUseSelector.mockReturnValueOnce({ - fluidStatus: updatedFluidStatus, - fluidTypes: [FluidType.ELECTRICITY], - }) - const wrapper = mount( - <Provider store={store}> - <ErrorDataConsumptionVisualizer fluidType={FluidType.ELECTRICITY} /> - </Provider> - ) - wrapper.find('.error-line').simulate('click') - expect(mockUseDispatch).toHaveBeenCalled() - expect(wrapper.find('.underlined-error').text()).toBe( - 'consumption_visualizer.last_valid_data : -' - ) - }) -}) diff --git a/src/components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer.tsx deleted file mode 100644 index 2748eb165ffe2204deef54b0fcaefa9b9c590859..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/ErrorDataConsumptionVisualizer.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react' -import './errorDataConsumptionVisualizer.scss' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { useDispatch, useSelector } from 'react-redux' -import { AppStore } from 'store' -import { setCurrentIndex, setSelectedDate } from 'store/chart/chart.actions' -import { DateTime } from 'luxon' -import DateChartService from 'services/dateChart.service' -import { FluidType } from 'enum/fluid.enum' - -interface ErrorDataConsumptionVisualizerProps { - fluidType: FluidType -} -const ErrorDataConsumptionVisualizer: React.FC<ErrorDataConsumptionVisualizerProps> = ({ - fluidType, -}: ErrorDataConsumptionVisualizerProps) => { - const { t } = useI18n() - const dispatch = useDispatch() - const { selectedDate } = useSelector((state: AppStore) => state.ecolyo.chart) - const { currentTimeStep } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) - const { fluidStatus, fluidTypes } = useSelector( - (state: AppStore) => state.ecolyo.global - ) - - const getLastDateWithAllData = (): DateTime | null => { - let lastDay: DateTime | null = null - const lastDays: DateTime[] = [] - if (fluidType === FluidType.MULTIFLUID) { - for (const _fluidType of fluidTypes) { - const date: DateTime | null = fluidStatus[_fluidType].lastDataDate - if (date) { - lastDays.push(date) - } - } - if (lastDays.length > 0) { - lastDay = lastDays.reduce(function(a, b) { - return a < b ? a : b - }) - } - } else { - lastDay = fluidStatus[fluidType].lastDataDate - } - return lastDay - } - const lastDateWithAllData: DateTime | null = getLastDateWithAllData() - - const setDateAndMoveToindex = () => { - if (lastDateWithAllData) { - const dateChartService = new DateChartService() - const updatedIndex: number = dateChartService.defineDateIndex( - currentTimeStep, - lastDateWithAllData - ) - dispatch(setSelectedDate(lastDateWithAllData)) - dispatch(setCurrentIndex(updatedIndex)) - } - } - - return ( - <> - {lastDateWithAllData && selectedDate < lastDateWithAllData ? ( - <></> - ) : ( - <div onClick={() => setDateAndMoveToindex()} className="error-line"> - <span className={`text-16-normal underlined-error`}> - {(fluidType && fluidType === FluidType.MULTIFLUID - ? `${t('consumption_visualizer.last_valid_data_multi')}` - : `${t('consumption_visualizer.last_valid_data')}`) + - ` : ${ - lastDateWithAllData - ? lastDateWithAllData.toFormat("dd'/'MM'/'yy") - : '-' - }`} - </span> - </div> - )} - </> - ) -} - -export default ErrorDataConsumptionVisualizer diff --git a/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..679d8ca0a069558a5214e2413d7e717f02f42ca0 --- /dev/null +++ b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx @@ -0,0 +1,240 @@ +import React from 'react' +import { mount } from 'enzyme' +import toJson from 'enzyme-to-json' +import { Provider } from 'react-redux' +import * as reactRedux from 'react-redux' +import { + createMockStore, + mockInitialEcolyoState, +} from '../../../tests/__mocks__/store' +import { FluidType } from 'enum/fluid.enum' +import { Dataload } from 'models' +import { DateTime } from 'luxon' +import InfoDataConsumptionVisualizer from './InfoDataConsumptionVisualizer' +import { DataloadState } from 'enum/dataload.enum' +import NoDataModal from './NoDataModal' +import { baseDataLoad } from '../../../tests/__mocks__/datachartData.mock' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') + +describe('InfoDataConsumptionVisualizer component', () => { + const mockLastDataDate = DateTime.fromISO('2020-10-01T00:00:00.000Z', { + zone: 'utc', + }) + const mockDataload: Dataload = baseDataLoad + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let store: any + beforeEach(() => { + store = createMockStore(mockInitialEcolyoState) + }) + + it('should render correctly when data valid', () => { + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={mockDataload} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(toJson(wrapper)).toMatchSnapshot() + }) + + describe('should render correctly consumption_visualizer.last_valid_data', () => { + it('case state MISSING', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.MISSING } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.last_valid_data : 01/10/20' + ) + }) + it('case state UPCOMING', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.UPCOMING } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.last_valid_data : 01/10/20' + ) + }) + it('case state COMING', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.COMING, + } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.last_valid_data : 01/10/20' + ) + }) + it('case state AGGREGATED_HOLE_OR_MISSING', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.AGGREGATED_HOLE_OR_MISSING, + } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.MULTIFLUID} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.last_valid_data_multi : 01/10/20' + ) + }) + it('case state AGGREGATED_WITH_HOLE_OR_MISSING', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING, + } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.MULTIFLUID} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.last_valid_data_multi : 01/10/20' + ) + }) + it('case state AGGREGATED_COMING', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.AGGREGATED_COMING, + } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.MULTIFLUID} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.last_valid_data_multi : 01/10/20' + ) + }) + }) + + describe('should render correctly with why_no_data', () => { + it('case state EMPTY', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.EMPTY } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.why_no_data' + ) + }) + it('case state HOLE', () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.HOLE } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.why_no_data' + ) + }) + it('case state AGGREGATED_EMPTY', () => { + const _mockdataLoad = { + ...mockDataload, + state: DataloadState.AGGREGATED_EMPTY, + } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.MULTIFLUID} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + expect(wrapper.find('span').text()).toBe( + 'consumption_visualizer.why_no_data' + ) + }) + }) + + it('should click on last valid data date and move to it', async () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.EMPTY } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + wrapper.find('.error-line').simulate('click') + expect(wrapper.find(NoDataModal).prop('open')).toBeTruthy() + }) + + it('should click to display NoDataModal', async () => { + const _mockdataLoad = { ...mockDataload, state: DataloadState.EMPTY } + const wrapper = mount( + <Provider store={store}> + <InfoDataConsumptionVisualizer + dataload={_mockdataLoad} + fluidType={FluidType.ELECTRICITY} + lastDataDate={mockLastDataDate} + /> + </Provider> + ) + wrapper.find('.error-line').simulate('click') + expect(mockUseDispatch).toHaveBeenCalled() + }) +}) diff --git a/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..030ed6ae736ae4fc95a9830cb25904989d127383 --- /dev/null +++ b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx @@ -0,0 +1,95 @@ +import React, { useCallback, useState } from 'react' +import './infoDataConsumptionVisualizer.scss' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { useDispatch, useSelector } from 'react-redux' +import { DateTime } from 'luxon' +import { Dataload } from 'models' +import { FluidType } from 'enum/fluid.enum' +import { AppStore } from 'store' +import { setCurrentIndex, setSelectedDate } from 'store/chart/chart.actions' +import DateChartService from 'services/dateChart.service' +import { DataloadState } from 'enum/dataload.enum' +import NoDataModal from './NoDataModal' + +interface InfoDataConsumptionVisualizerProps { + dataload: Dataload + fluidType: FluidType + lastDataDate: DateTime | null +} + +const InfoDataConsumptionVisualizer = ({ + dataload, + fluidType, + lastDataDate, +}: InfoDataConsumptionVisualizerProps) => { + const { t } = useI18n() + const dispatch = useDispatch() + const { currentTimeStep } = useSelector( + (state: AppStore) => state.ecolyo.chart + ) + const [openNodataModal, setopenNodataModal] = useState<boolean>(false) + + const toggleNoDataModal = useCallback(() => { + setopenNodataModal(prev => !prev) + }, []) + + const moveToDate = () => { + if (lastDataDate) { + const dateChartService = new DateChartService() + const updatedIndex: number = dateChartService.defineDateIndex( + currentTimeStep, + lastDataDate + ) + dispatch(setSelectedDate(lastDataDate)) + dispatch(setCurrentIndex(updatedIndex)) + } + } + + if (!dataload) { + return <></> + } + + if ( + dataload.state === DataloadState.MISSING || + dataload.state === DataloadState.UPCOMING || + dataload.state === DataloadState.COMING || + dataload.state === DataloadState.AGGREGATED_HOLE_OR_MISSING || + dataload.state === DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING || + dataload.state === DataloadState.AGGREGATED_COMING + ) { + return ( + <div onClick={() => moveToDate()} className="error-line"> + <span className={`text-16-normal underlined-error`}> + {(fluidType === FluidType.MULTIFLUID + ? `${t('consumption_visualizer.last_valid_data_multi')}` + : `${t('consumption_visualizer.last_valid_data')}`) + + ` : ${lastDataDate ? lastDataDate.toFormat("dd'/'MM'/'yy") : '-'}`} + </span> + </div> + ) + } + + if ( + dataload.state === DataloadState.EMPTY || + dataload.state === DataloadState.HOLE || + dataload.state === DataloadState.AGGREGATED_EMPTY + ) { + return ( + <> + <div className="error-line" onClick={toggleNoDataModal}> + <span className={`text-16-normal underlined-error`}> + {t('consumption_visualizer.why_no_data')} + </span> + </div> + <NoDataModal + open={openNodataModal} + handleCloseClick={toggleNoDataModal} + /> + </> + ) + } + + return <></> +} + +export default InfoDataConsumptionVisualizer diff --git a/src/components/ConsumptionVisualizer/LastDataConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/LastDataConsumptionVisualizer.spec.tsx deleted file mode 100644 index b299ed7ac8c16f6e5166ea0356b2a95cac759350..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/LastDataConsumptionVisualizer.spec.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react' -import { mount } from 'enzyme' -import { Provider } from 'react-redux' -import * as reactRedux from 'react-redux' - -import configureStore from 'redux-mock-store' -import { mockInitialEcolyoState } from '../../../tests/__mocks__/store' -import LastDataConsumptionVisualizer from './LastDataConsumptionVisualizer' -import { DateTime } from 'luxon' - -jest.mock('cozy-ui/transpiled/react/I18n', () => { - return { - useI18n: jest.fn(() => { - return { - t: (str: string) => str, - } - }), - } -}) - -const mockStore = configureStore([]) -describe('LastDataConsumptionVisualizer component', () => { - it('should render correctly', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const wrapper = mount( - <Provider store={store}> - <LastDataConsumptionVisualizer lastDataDate={null} /> - </Provider> - ) - expect(wrapper).toMatchSnapshot() - }) - it('should click and move to last data date', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') - - const wrapper = mount( - <Provider store={store}> - <LastDataConsumptionVisualizer lastDataDate={DateTime.local()} /> - </Provider> - ) - wrapper.find('.lastdatavisualizer-button').simulate('click') - expect(mockUseDispatch).toHaveBeenCalled() - }) - it('should render empty last data date', () => { - const store = mockStore({ - ecolyo: mockInitialEcolyoState, - }) - - const wrapper = mount( - <Provider store={store}> - <LastDataConsumptionVisualizer lastDataDate={null} /> - </Provider> - ) - wrapper.find('.lastdatavisualizer-button').simulate('click') - - expect(wrapper.find('.lastdatavisualizer-button').text()).toBe( - 'consumption_visualizer.last_valid_data : -' - ) - }) -}) diff --git a/src/components/ConsumptionVisualizer/LastDataConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/LastDataConsumptionVisualizer.tsx deleted file mode 100644 index a9698fd9c02bfb201f6d83571ff831d3c554da8f..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/LastDataConsumptionVisualizer.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react' -import './lastDataConsumptionVisualizer.scss' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { useDispatch, useSelector } from 'react-redux' -import { AppStore } from 'store' -import { setCurrentIndex, setSelectedDate } from 'store/chart/chart.actions' -import { DateTime } from 'luxon' -import DateChartService from 'services/dateChart.service' -import { FluidType } from 'enum/fluid.enum' - -interface LastDataConsumptionVisualizerProps { - lastDataDate: DateTime | null - fluidType: FluidType -} - -const LastDataConsumptionVisualizer: React.FC<LastDataConsumptionVisualizerProps> = ({ - lastDataDate, - fluidType, -}: LastDataConsumptionVisualizerProps) => { - const { t } = useI18n() - const dispatch = useDispatch() - const { currentTimeStep } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) - - const moveToDate = () => { - if (lastDataDate) { - const dateChartService = new DateChartService() - const updatedIndex: number = dateChartService.defineDateIndex( - currentTimeStep, - lastDataDate - ) - dispatch(setSelectedDate(lastDataDate)) - dispatch(setCurrentIndex(updatedIndex)) - } - } - - return ( - <div onClick={() => moveToDate()} className="lastdatavisualizer-button"> - <span className={`text-16-normal underlined-error`}> - {(fluidType && fluidType === FluidType.MULTIFLUID - ? `${t('consumption_visualizer.last_valid_data_multi')}` - : `${t('consumption_visualizer.last_valid_data')}`) + - ` : ${lastDataDate ? lastDataDate.toFormat("dd'/'MM'/'yy") : '-'}`} - </span> - </div> - ) -} - -export default LastDataConsumptionVisualizer diff --git a/src/components/ConsumptionVisualizer/__snapshots__/DataloadComparisonLeft.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/DataloadComparisonLeft.spec.tsx.snap deleted file mode 100644 index 9f5722378e8098a9a2321df5c7889b97c9915583..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/__snapshots__/DataloadComparisonLeft.spec.tsx.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DataloadComparisonLeft component should render correctly 1`] = `ReactWrapper {}`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/DataloadNoValue.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/DataloadNoValue.spec.tsx.snap index 372c0275b86ca025a7d34130d5e958f91305de12..e70438172e75594f67fc1bfd8b2a592df5b78e1f 100644 --- a/src/components/ConsumptionVisualizer/__snapshots__/DataloadNoValue.spec.tsx.snap +++ b/src/components/ConsumptionVisualizer/__snapshots__/DataloadNoValue.spec.tsx.snap @@ -1,3 +1,30 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DataloadNoValue component should render correctly 1`] = `ReactWrapper {}`; +exports[`DataloadNoValue component should render correctly 1`] = ` +<DataloadNoValue + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + fluidType={0} + setActive={[MockFunction]} +> + <div + className="dataloadvisualizer-content text-22-normal" + > + <div + className="dataloadvisualizer-section" + > + <div + className="dataloadvisualizer-value electricity upper to-come" + > + consumption_visualizer.data_to_come + </div> + </div> + </div> +</DataloadNoValue> +`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/DataloadSection.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/DataloadSection.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..325b7cc8616a4a6658bc4675860cc86b99c37c9f --- /dev/null +++ b/src/components/ConsumptionVisualizer/__snapshots__/DataloadSection.spec.tsx.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataloadSection component should render correctly 1`] = ` +<DataloadSection + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="NO_COMPARE" + fluidType={0} + toggleEstimationModal={[MockFunction]} +> + <div + className="dataloadvisualizer-section" + > + <div + className="dataloadvisualizer-value text-36-bold electricity" + > + <mock-dataloadsectionvalue + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="NO_COMPARE" + fluidType={0} + toggleEstimationModal={[MockFunction]} + /> + </div> + <mock-dataloadsectiondetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="NO_COMPARE" + fluidType={0} + /> + </div> +</DataloadSection> +`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/DataloadSectionDetail.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/DataloadSectionDetail.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..f0b4a992be4fb8c7739d9ea366b33ff34e4a3342 --- /dev/null +++ b/src/components/ConsumptionVisualizer/__snapshots__/DataloadSectionDetail.spec.tsx.snap @@ -0,0 +1,188 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataloadSectionDetail component should display value details if multifluid and no comparison case all valid data 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "AGGREGATED_VALID", + "value": 12, + "valueDetail": Array [ + Object { + "state": "VALID", + "value": 1, + }, + Object { + "state": "VALID", + "value": 2, + }, + Object { + "state": "VALID", + "value": 3, + }, + ], + } + } + dataloadSectionType="RIGHT" + fluidType={3} +> + <div + className="dataloadvisualizer-euro text-16-normal" + /> +</DataloadSectionDetail> +`; + +exports[`DataloadSectionDetail component should display value details if multifluid and no comparison case valid and coming data 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "AGGREGATED_WITH_UPCOMING", + "value": 12, + "valueDetail": Array [ + Object { + "state": "VALID", + "value": 1, + }, + Object { + "state": "COMING", + "value": -1, + }, + Object { + "state": "VALID", + "value": 3, + }, + ], + } + } + dataloadSectionType="RIGHT" + fluidType={3} +> + <div + className="dataloadvisualizer-euro text-16-normal" + /> +</DataloadSectionDetail> +`; + +exports[`DataloadSectionDetail component should display value details if multifluid and no comparison case valid and hole data 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "AGGREGATED_WITH_HOLE_OR_MISSING", + "value": 12, + "valueDetail": Array [ + Object { + "state": "VALID", + "value": 1, + }, + Object { + "state": "HOLE", + "value": -1, + }, + Object { + "state": "VALID", + "value": 3, + }, + ], + } + } + dataloadSectionType="RIGHT" + fluidType={3} +> + <div + className="dataloadvisualizer-euro text-16-normal" + /> +</DataloadSectionDetail> +`; + +exports[`DataloadSectionDetail component should display value details if multifluid and no comparison case valid and missing data 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "AGGREGATED_WITH_HOLE_OR_MISSING", + "value": 12, + "valueDetail": Array [ + Object { + "state": "VALID", + "value": 1, + }, + Object { + "state": "MISSING", + "value": -1, + }, + Object { + "state": "VALID", + "value": 3, + }, + ], + } + } + dataloadSectionType="RIGHT" + fluidType={3} +> + <div + className="dataloadvisualizer-euro text-16-normal" + /> +</DataloadSectionDetail> +`; + +exports[`DataloadSectionDetail component should not display if multifluid and comparison 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="RIGHT" + fluidType={3} +> + <div + className="dataloadvisualizer-euro text-16-normal" + /> +</DataloadSectionDetail> +`; + +exports[`DataloadSectionDetail component should not display value details if multifluid, no valueDetail and comparison 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "AGGREGATED_EMPTY", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="RIGHT" + fluidType={3} +> + <div + className="dataloadvisualizer-euro text-16-normal" + /> +</DataloadSectionDetail> +`; + +exports[`DataloadSectionDetail component should render correctly 1`] = ` +<DataloadSectionDetail + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="NO_COMPARE" + fluidType={0} +> + <div + className="dataloadvisualizer-euro electricity-compare text-16-normal" + > + 2,09 € + </div> +</DataloadSectionDetail> +`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/DataloadSectionValue.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/DataloadSectionValue.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..ba54ac2c063fd7a1ee4845f20d13e328aa999872 --- /dev/null +++ b/src/components/ConsumptionVisualizer/__snapshots__/DataloadSectionValue.spec.tsx.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DataloadSectionValue component should render correctly 1`] = ` +<DataloadSectionValue + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + dataloadSectionType="NO_COMPARE" + fluidType={0} + toggleEstimationModal={[MockFunction]} +> + 12,00 + <span + className="text-18-normal" + > + FLUID.ELECTRICITY.UNIT + </span> +</DataloadSectionValue> +`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/ErrorDataConsumptionVisualizer.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/ErrorDataConsumptionVisualizer.spec.tsx.snap deleted file mode 100644 index a95a17a508f07a2b3deb2ec4954baee60bae12e4..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/__snapshots__/ErrorDataConsumptionVisualizer.spec.tsx.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ErrorDataConsumptionVisualizer component should render correctly 1`] = `ReactWrapper {}`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/InfoDataConsumptionVisualizer.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/InfoDataConsumptionVisualizer.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..151a98bdd258e3755248640676bf77b747bab782 --- /dev/null +++ b/src/components/ConsumptionVisualizer/__snapshots__/InfoDataConsumptionVisualizer.spec.tsx.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InfoDataConsumptionVisualizer component should render correctly when data valid 1`] = ` +<Provider + store={ + Object { + "clearActions": [Function], + "dispatch": [Function], + "getActions": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], + } + } +> + <InfoDataConsumptionVisualizer + dataload={ + Object { + "date": "2021-09-23T00:00:00.000Z", + "state": "VALID", + "value": 12, + "valueDetail": null, + } + } + fluidType={0} + lastDataDate={"2020-10-01T00:00:00.000Z"} + /> +</Provider> +`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/LastDataConsumptionVisualizer.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/LastDataConsumptionVisualizer.spec.tsx.snap deleted file mode 100644 index c10fc2c6145b8fb653eec9d764fc072a23347229..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/__snapshots__/LastDataConsumptionVisualizer.spec.tsx.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LastDataConsumptionVisualizer component should render correctly 1`] = `ReactWrapper {}`; diff --git a/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss b/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss index 5efbd3ae93f2f5aca6f39bdc4f702df8a7e819b7..d7c31f56252b7913e0ba71ec1514c320bab647d1 100644 --- a/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss +++ b/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss @@ -2,7 +2,7 @@ @import 'src/styles/base/breakpoint'; .dataloadvisualizer-root { - min-height: 5rem; + min-height: 5.719rem; display: flex; align-items: center; } diff --git a/src/components/ConsumptionVisualizer/errorDataConsumptionVisualizer.scss b/src/components/ConsumptionVisualizer/infoDataConsumptionVisualizer.scss similarity index 100% rename from src/components/ConsumptionVisualizer/errorDataConsumptionVisualizer.scss rename to src/components/ConsumptionVisualizer/infoDataConsumptionVisualizer.scss diff --git a/src/components/ConsumptionVisualizer/lastDataConsumptionVisualizer.scss b/src/components/ConsumptionVisualizer/lastDataConsumptionVisualizer.scss deleted file mode 100644 index 8554a2ca1c28f6d2923e4febc9dd3dbeb9f37d4c..0000000000000000000000000000000000000000 --- a/src/components/ConsumptionVisualizer/lastDataConsumptionVisualizer.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import 'src/styles/base/color'; - -.lastdatavisualizer-button { - color: $grey-bright; - cursor: pointer; - display: flex; - align-items: center; -} diff --git a/src/components/EcogestureForm/EcogestureFormView.spec.tsx b/src/components/EcogestureForm/EcogestureFormView.spec.tsx index 8d6c77f09e895fc6da10344b15b3ef2d571e81c7..e3e0cbce3cc84773501f7aa58d87eb5de7631303 100644 --- a/src/components/EcogestureForm/EcogestureFormView.spec.tsx +++ b/src/components/EcogestureForm/EcogestureFormView.spec.tsx @@ -99,7 +99,6 @@ describe('EcogestureFormView component', () => { .first() .simulate('change') await waitForComponentToPaint(wrapper) - console.log(wrapper.debug()) wrapper .find(Button) .at(1) diff --git a/src/components/FluidChart/FluidChartSlide.tsx b/src/components/FluidChart/FluidChartSlide.tsx index c459e9c770eac57c3004a309f1742baa663181c2..59847d745905852a7684ba00f5ccd80e2152bc7c 100644 --- a/src/components/FluidChart/FluidChartSlide.tsx +++ b/src/components/FluidChart/FluidChartSlide.tsx @@ -40,7 +40,9 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ const { currentTimeStep, currentIndex } = useSelector( (state: AppStore) => state.ecolyo.chart ) - const { fluidTypes } = useSelector((state: AppStore) => state.ecolyo.global) + const { fluidTypes, fluidStatus } = useSelector( + (state: AppStore) => state.ecolyo.global + ) const [chartData, setChartData] = useState<Datachart>({ actualData: [], comparisonData: null, @@ -83,6 +85,7 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ timePeriod, currentTimeStep, fluidTypeArray, + fluidStatus, compareTimePeriod, fluidType === FluidType.MULTIFLUID ) @@ -107,6 +110,7 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ index, isDataLoaded, timeStep, + fluidStatus, ]) useEffect(() => { diff --git a/src/components/Home/ConsumptionView.spec.tsx b/src/components/Home/ConsumptionView.spec.tsx index 4466cc773f1e8cde73ab0f51d5f884e68edc06ad..bfb1c23bda96af882ada6e14ebfaa088b7614c02 100644 --- a/src/components/Home/ConsumptionView.spec.tsx +++ b/src/components/Home/ConsumptionView.spec.tsx @@ -14,23 +14,6 @@ import StyledSpinner from 'components/CommonKit/Spinner/StyledSpinner' import ConsumptionView from './ConsumptionView' import { FluidStatus } from 'models' -jest.mock('components/Header/CozyBar', () => () => <div id="cozybar"></div>) -jest.mock('components/Header/Header', () => () => <div id="header"></div>) -jest.mock('components/DateNavigator/DateNavigator', () => () => ( - <div id="datenavigator"></div> -)) -jest.mock('components/FluidChart/FluidChart', () => () => ( - <div id="fluidchart"></div> -)) -jest.mock('components/Home/ConsumptionDetails', () => () => ( - <div id="consumptiondetails"></div> -)) -jest.mock('components/Connection/Connection', () => () => ( - <div id="connection"></div> -)) -jest.mock('components/Konnector/KonnectorViewerCard', () => () => ( - <div id="konnectorViewerCard"></div> -)) jest.mock('cozy-ui/transpiled/react/I18n', () => { return { useI18n: jest.fn(() => { @@ -40,6 +23,24 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => { }), } }) + +jest.mock('components/Header/CozyBar', () => 'mock-cozybar') +jest.mock('components/Header/Header', () => 'mock-header') +jest.mock('components/DateNavigator/DateNavigator', () => 'mock-datenavigator') +jest.mock('components/Content/Content', () => 'mock-content') +jest.mock('components/Home/FluidButtons', () => 'mock-fluidbuttons') +jest.mock('components/FluidChart/FluidChart', () => 'mock-fluidchart') +jest.mock('components/Home/ConsumptionDetails', () => 'mock-consumptiondetails') +jest.mock('components/Connection/Connection', () => 'mock-connection') +jest.mock( + 'components/Konnector/KonnectorViewerCard', + () => 'mock-konnectorviewercard' +) +jest.mock( + 'components/PartnersIssue/PartnersIssueModal', + () => 'mock-partnersissuemodal' +) + const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') const setCurrentTimeStepSpy = jest.spyOn(chartActions, 'setCurrentTimeStep') @@ -54,10 +55,13 @@ describe('ConsumptionView component', () => { }) it('should be rendered correctly', async () => { + const mockFluidStatus: FluidStatus[] = + mockInitialEcolyoState.global.fluidStatus + mockFluidStatus[FluidType.ELECTRICITY].status = FluidState.DONE useSelectorSpy.mockReturnValue({ currentTimeStep: TimeStep.WEEK, loading: false, - fluidStatus: mockInitialEcolyoState.global.fluidStatus, + fluidStatus: mockFluidStatus, releaseNotes: mockInitialEcolyoState.global.releaseNotes, }) const wrapper = mount( @@ -65,18 +69,22 @@ describe('ConsumptionView component', () => { <ConsumptionView fluidType={FluidType.ELECTRICITY} /> </Provider> ) - expect(wrapper.find('#cozybar')).toBeTruthy() - expect(wrapper.find('#header')).toBeTruthy() - expect(wrapper.find('#datenavigator')).toBeTruthy() - expect(wrapper.find('#fluidchart')).toBeTruthy() - expect(wrapper.find('#consumptiondetails')).toBeTruthy() + expect(wrapper.find('mock-cozybar').exists()).toBeTruthy() + expect(wrapper.find('mock-header').exists()).toBeTruthy() + expect(wrapper.find('mock-datenavigator').exists()).toBeTruthy() + expect(wrapper.find('mock-fluidbuttons').exists()).toBeTruthy() + expect(wrapper.find('mock-fluidchart').exists()).toBeTruthy() + expect(wrapper.find('mock-consumptiondetails').exists()).toBeTruthy() }) - it('should display a spinner', () => { + it('should display a spinner when fluid connected and is loading', () => { + const mockFluidStatus: FluidStatus[] = + mockInitialEcolyoState.global.fluidStatus + mockFluidStatus[FluidType.ELECTRICITY].status = FluidState.DONE useSelectorSpy.mockReturnValue({ currentTimeStep: TimeStep.WEEK, loading: true, - fluidStatus: mockInitialEcolyoState.global.fluidStatus, + fluidStatus: mockFluidStatus, releaseNotes: mockInitialEcolyoState.global.releaseNotes, }) const wrapper = mount( @@ -84,10 +92,10 @@ describe('ConsumptionView component', () => { <ConsumptionView fluidType={FluidType.ELECTRICITY} /> </Provider> ) - expect(wrapper.find('#cozybar')).toBeTruthy() - expect(wrapper.find('#header')).toBeTruthy() - expect(wrapper.find('#datenavigator')).toBeTruthy() - expect(wrapper.find(StyledSpinner)).toBeTruthy() + expect(wrapper.find('mock-cozybar').exists()).toBeTruthy() + expect(wrapper.find('mock-header').exists()).toBeTruthy() + expect(wrapper.find('mock-datenavigator').exists()).toBeTruthy() + expect(wrapper.find(StyledSpinner).exists()).toBeTruthy() }) it('should set CurrentTimeStep to WEEK when fluid != ELECTRICITY and timeStep = HALF_AN_HOUR', () => { @@ -117,7 +125,7 @@ describe('ConsumptionView component', () => { <ConsumptionView fluidType={FluidType.MULTIFLUID} /> </Provider> ) - expect(wrapper.find('.konnectorViewerCard')).toBeTruthy() + expect(wrapper.find('mock-consumptiondetails').exists()).toBeTruthy() }) it('should render mutlifluid consumption if at least one fluid is connected', () => { const updatedStatus: FluidStatus[] = @@ -135,7 +143,7 @@ describe('ConsumptionView component', () => { <ConsumptionView fluidType={FluidType.MULTIFLUID} /> </Provider> ) - expect(wrapper.find('.consumptionview-content')).toBeTruthy() + expect(wrapper.find('.consumptionview-content').exists()).toBeTruthy() }) it('should render Electricity when elec is connected', () => { const updatedStatus: FluidStatus[] = @@ -152,7 +160,7 @@ describe('ConsumptionView component', () => { <ConsumptionView fluidType={FluidType.ELECTRICITY} /> </Provider> ) - expect(wrapper.find('.consumptionview-content')).toBeTruthy() - expect(wrapper.find('.konnectorViewerCard')).toBeTruthy() + expect(wrapper.find('.consumptionview-content').exists()).toBeTruthy() + expect(wrapper.find('mock-consumptiondetails').exists()).toBeTruthy() }) }) diff --git a/src/components/PartnersIssue/__snapshots__/PartnersIssueModal.spec.tsx.snap b/src/components/PartnersIssue/__snapshots__/PartnersIssueModal.spec.tsx.snap index 83470054297327e1b1148148befc710d973a1e5d..8c6fbfe31dcd94a9709905cd1b5e8235f8dd750b 100644 --- a/src/components/PartnersIssue/__snapshots__/PartnersIssueModal.spec.tsx.snap +++ b/src/components/PartnersIssue/__snapshots__/PartnersIssueModal.spec.tsx.snap @@ -32,6 +32,7 @@ exports[`PartnersIssueModal component should render correctly 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": null, "fluidType": 0, "lastDataDate": null, "status": 0, @@ -52,6 +53,7 @@ exports[`PartnersIssueModal component should render correctly 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": null, "fluidType": 1, "lastDataDate": null, "status": 0, @@ -72,6 +74,7 @@ exports[`PartnersIssueModal component should render correctly 1`] = ` "trigger": null, "triggerState": null, }, + "firstDataDate": null, "fluidType": 2, "lastDataDate": null, "status": 0, diff --git a/src/enum/dataload.enum.ts b/src/enum/dataload.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d98c473019ea842e4773e9e0b4f6bc840aab6aa --- /dev/null +++ b/src/enum/dataload.enum.ts @@ -0,0 +1,21 @@ +export enum DataloadState { + VALID = 'VALID', + EMPTY = 'EMPTY', + MISSING = 'MISSING', + HOLE = 'HOLE', + UPCOMING = 'UPCOMING', + COMING = 'COMING', + AGGREGATED_VALID = 'AGGREGATED_VALID', + AGGREGATED_EMPTY = 'AGGREGATED_EMPTY', + AGGREGATED_WITH_EMPTY = 'AGGREGATED_WITH_EMPTY', + AGGREGATED_HOLE_OR_MISSING = 'AGGREGATED_HOLE_OR_MISSING', + AGGREGATED_WITH_HOLE_OR_MISSING = 'AGGREGATED_WITH_HOLE_OR_MISSING', + AGGREGATED_WITH_COMING = 'AGGREGATED_WITH_UPCOMING', + AGGREGATED_COMING = 'AGGREGATED_COMING', +} + +export enum DataloadSectionType { + NO_COMPARE = 'NO_COMPARE', + LEFT = 'LEFT', + RIGHT = 'RIGHT', +} diff --git a/src/locales/fr.json b/src/locales/fr.json index c5fd454cb92a5e925d0efb516151bf2433cdb143..0146e7455dfd55e77c7ed6d08a401423f48d1f24 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -287,6 +287,7 @@ "last_valid_data_multi": "Dernières données complètes", "data_to_come": "à venir", "aie": "Aïe !", + "data_empty": "Vide", "estimated": "estimés", "dataModal": { "list_title": "3 raisons possibles :", diff --git a/src/models/dataload.model.ts b/src/models/dataload.model.ts index 9a2b157c3655b3c3a7a3583e092e5928563a8fc4..3f7f73b015d7674971456e33d8026d27ed8c1c4d 100644 --- a/src/models/dataload.model.ts +++ b/src/models/dataload.model.ts @@ -1,10 +1,17 @@ +import { DataloadState } from 'enum/dataload.enum' import { DateTime } from 'luxon' +export interface DataloadValueDetail { + value: number + state: DataloadState +} + export interface Dataload { date: DateTime value: number price?: number - valueDetail: number[] | null + state: DataloadState + valueDetail: DataloadValueDetail[] | null } export interface DataloadEntity { diff --git a/src/models/fluid.model.ts b/src/models/fluid.model.ts index 00077fcd16dd5a3131070c7a1895836d93f0a92c..812d6e59e61e2852b3f74c5d564fc32bcbbe87e5 100644 --- a/src/models/fluid.model.ts +++ b/src/models/fluid.model.ts @@ -15,6 +15,7 @@ export interface FluidConnection { export interface FluidStatus { fluidType: FluidType status: FluidState + firstDataDate: DateTime | null lastDataDate: DateTime | null connection: FluidConnection } diff --git a/src/services/challenge.service.spec.ts b/src/services/challenge.service.spec.ts index b547120362df55f2293e15dc89c466a367c50783..4af58ec04f0dbd6bd5ffb96797821ca051e22401 100644 --- a/src/services/challenge.service.spec.ts +++ b/src/services/challenge.service.spec.ts @@ -49,6 +49,7 @@ import { import { fluidStatusData } from '../../tests/__mocks__/fluidStatusData.mock' import { cloneDeep } from 'lodash' import { UserActionState } from 'enum/userAction.enum' +import { DataloadState } from 'enum/dataload.enum' const mockGetExplorationEntityById = jest.fn() const mockParseExplorationEntityToUserExploration = jest.fn() @@ -449,10 +450,11 @@ describe('Challenge service', () => { }) .minus({ days: 5 }), value: 69.18029999999999, + state: DataloadState.AGGREGATED_VALID, valueDetail: [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, ], }, { @@ -462,10 +464,11 @@ describe('Challenge service', () => { }) .minus({ days: 4 }), value: 61.65554999999999, + state: DataloadState.AGGREGATED_VALID, valueDetail: [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + { value: 40.21918999999999, state: DataloadState.VALID }, + { value: 0.8064649999999999, state: DataloadState.VALID }, + { value: 20.629894999999998, state: DataloadState.VALID }, ], }, { @@ -475,7 +478,12 @@ describe('Challenge service', () => { }) .minus({ days: 3 }), value: 50.0, - valueDetail: [25.0, 5.0, 20.0], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 25.0, state: DataloadState.VALID }, + { value: 5.0, state: DataloadState.VALID }, + { value: 20.0, state: DataloadState.VALID }, + ], }, ] it('should return isDone = true, isWin = true and isEmpty=false when userConsumption < threshold', async () => { @@ -527,10 +535,11 @@ describe('Challenge service', () => { }) .minus({ days: 3 }), value: 69.18029999999999, + state: DataloadState.AGGREGATED_VALID, valueDetail: [ - 45.127739999999996, - 0.9048899999999999, - 23.147669999999998, + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, ], }, { @@ -540,10 +549,11 @@ describe('Challenge service', () => { }) .minus({ days: 2 }), value: 61.65554999999999, + state: DataloadState.AGGREGATED_VALID, valueDetail: [ - 40.21918999999999, - 0.8064649999999999, - 20.629894999999998, + { value: 40.21918999999999, state: DataloadState.VALID }, + { value: 0.8064649999999999, state: DataloadState.VALID }, + { value: 20.629894999999998, state: DataloadState.VALID }, ], }, { @@ -553,7 +563,12 @@ describe('Challenge service', () => { }) .minus({ days: 1 }), value: 50, - valueDetail: [25.0, 5.0, 20.0], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 25.0, state: DataloadState.VALID }, + { value: 5.0, state: DataloadState.VALID }, + { value: 20.0, state: DataloadState.VALID }, + ], }, ] it('should return isDone = true, isWin = true when all data are available and userConsumption < threshold', async () => { @@ -591,7 +606,11 @@ describe('Challenge service', () => { }) it('should return isDone = false and isWin = false when data in the middle is not available', async () => { const updatedDataloads = cloneDeep(dataloads) - updatedDataloads[1].valueDetail = [20.0, -1, 10.0] + updatedDataloads[1].valueDetail = [ + { value: 20.0, state: DataloadState.VALID }, + { value: -1, state: DataloadState.MISSING }, + { value: 10.0, state: DataloadState.VALID }, + ] const result = await challengeService.isChallengeDone( userChallenge, updatedDataloads diff --git a/src/services/challenge.service.ts b/src/services/challenge.service.ts index f5dd5e940c62503211582857d8dc34932597bf9f..255e008f2a719ceccb7370a7f217bbba5fed2963 100644 --- a/src/services/challenge.service.ts +++ b/src/services/challenge.service.ts @@ -800,6 +800,7 @@ export default class ChallengeService { TimeStep.DAY, userChallenge.duel.fluidTypes, undefined, + undefined, true ) if (dataChart) { @@ -864,7 +865,8 @@ export default class ChallengeService { dataloads.forEach((d: Dataload) => { if ( d.value === -1 || - (d.valueDetail && d.valueDetail.includes(-1)) + (d.valueDetail && + d.valueDetail.filter(data => data.value === -1).length > 0) ) { isDone = false } diff --git a/src/services/consumption.service.spec.ts b/src/services/consumption.service.spec.ts index 51ac9fccd2e58c20460287472f48fae02e6d3834..c5eb07f5317e169e5d690b18c6fc814c5d303ab0 100644 --- a/src/services/consumption.service.spec.ts +++ b/src/services/consumption.service.spec.ts @@ -3,14 +3,24 @@ import mockClient from '../../tests/__mocks__/client' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { FluidType } from 'enum/fluid.enum' -import { Dataload, DataloadEntity, FluidPrice, TimePeriod } from 'models' +import { + Datachart, + Dataload, + DataloadEntity, + FluidPrice, + FluidStatus, + TimePeriod, +} from 'models' import { ENEDIS_MINUTE_DOCTYPE } from 'doctypes' import { fluidPrices } from '../../tests/__mocks__/fluidPrice.mock' import { QueryResult } from 'cozy-client' import { loadDayData } from '../../tests/__mocks__/loadDayData.mock' +import { DataloadState } from 'enum/dataload.enum' +import { fluidStatusConnectedData } from '../../tests/__mocks__/fluidStatusData.mock' const mockFetchFluidData = jest.fn() const mockFetchFluidMaxData = jest.fn() +const mockGetFirstDateData = jest.fn() const mockGetLastDateData = jest.fn() const mockGetEntries = jest.fn() jest.mock('./queryRunner.service', () => { @@ -18,6 +28,7 @@ jest.mock('./queryRunner.service', () => { return { fetchFluidData: mockFetchFluidData, fetchFluidMaxData: mockFetchFluidMaxData, + getFirstDateData: mockGetFirstDateData, getLastDateData: mockGetLastDateData, getEntries: mockGetEntries, } @@ -26,85 +37,101 @@ jest.mock('./queryRunner.service', () => { describe('Consumption service', () => { const consumptionDataManager = new ConsumptionDataManager(mockClient) - let fluidTypes: FluidType[] = [0] + const fluidStatus: FluidStatus[] = fluidStatusConnectedData const mockTimePeriod: TimePeriod = { - startDate: DateTime.fromISO('2020-10-01T00:00:00.000Z'), - endDate: DateTime.fromISO('2020-10-03T23:59:59.999Z'), + startDate: DateTime.fromISO('2020-08-01T00:00:00.000Z'), + endDate: DateTime.fromISO('2020-08-03T23:59:59.999Z'), } const mockTimePeriodComparison: TimePeriod = { - startDate: DateTime.fromISO('2020-09-01T00:00:00.000Z'), - endDate: DateTime.fromISO('2020-09-03T23:59:59.999Z'), + startDate: DateTime.fromISO('2020-07-01T00:00:00.000Z'), + endDate: DateTime.fromISO('2020-07-03T23:59:59.999Z'), } const mockFetchDataActual: Dataload[] = [ { - date: DateTime.fromISO('2020-10-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-01T00:00:00.000Z'), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, { - date: DateTime.fromISO('2020-10-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-02T00:00:00.000Z'), value: 260.15, + state: DataloadState.VALID, valueDetail: null, }, ] const mockFetchDataComparison: Dataload[] = [ { - date: DateTime.fromISO('2020-09-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-01T00:00:00.000Z'), value: 228.23, + state: DataloadState.VALID, valueDetail: null, }, { - date: DateTime.fromISO('2020-09-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-02T00:00:00.000Z'), value: 238.71, + state: DataloadState.VALID, valueDetail: null, }, ] + describe('getGraphData method', () => { + beforeEach(() => { + mockFetchFluidData.mockClear() + }) it('should return null', async () => { const result = await consumptionDataManager.getGraphData( mockTimePeriod, TimeStep.DAY, [], + [], mockTimePeriodComparison, false ) expect(result).toBeNull() }) it('should return a mapped data for one fluid', async () => { + const fluidTypes: FluidType[] = [FluidType.ELECTRICITY] mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual) mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison) - const mockResult = { + const mockResult: Datachart = { actualData: [ { - date: DateTime.fromISO('2020-10-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-01T00:00:00.000Z'), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, { - date: DateTime.fromISO('2020-10-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-02T00:00:00.000Z'), value: 260.15, + state: DataloadState.VALID, valueDetail: null, }, { - date: DateTime.fromISO('2020-10-03T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-03T00:00:00.000Z'), value: -1, + state: DataloadState.HOLE, valueDetail: null, }, ], comparisonData: [ { - date: DateTime.fromISO('2020-09-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-01T00:00:00.000Z'), value: 228.23, + state: DataloadState.VALID, valueDetail: null, }, { - date: DateTime.fromISO('2020-09-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-02T00:00:00.000Z'), value: 238.71, + state: DataloadState.VALID, valueDetail: null, }, { - date: DateTime.fromISO('2020-09-03T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-03T00:00:00.000Z'), value: -1, + state: DataloadState.HOLE, valueDetail: null, }, ], @@ -113,6 +140,7 @@ describe('Consumption service', () => { mockTimePeriod, TimeStep.DAY, fluidTypes, + fluidStatus, mockTimePeriodComparison, false ) @@ -120,44 +148,70 @@ describe('Consumption service', () => { }) it('should return a mapped data for multiple fluid', async () => { - fluidTypes = [0, 1, 2] - for (let i = 0; i < fluidTypes.length; i++) { + const fluidTypes: FluidType[] = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, + ] + for (const fluidType of fluidTypes) { mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual) mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison) } - const mockResult = { + const mockResult: Datachart = { actualData: [ { - date: DateTime.fromISO('2020-10-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-01T00:00:00.000Z'), value: 84.44375099999999, - valueDetail: [50.79059999999999, 0.931161, 32.72199], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 50.79059999999999, state: DataloadState.VALID }, + { value: 0.931161, state: DataloadState.VALID }, + { value: 32.72199, state: DataloadState.VALID }, + ], }, { - date: DateTime.fromISO('2020-10-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-02T00:00:00.000Z'), value: 75.2587935, - valueDetail: [45.266099999999994, 0.8298785, 29.162815], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.266099999999994, state: DataloadState.VALID }, + { value: 0.8298785, state: DataloadState.VALID }, + { value: 29.162815, state: DataloadState.VALID }, + ], }, { - date: DateTime.fromISO('2020-10-03T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-03T00:00:00.000Z'), value: -1, + state: DataloadState.AGGREGATED_HOLE_OR_MISSING, valueDetail: null, }, ], comparisonData: [ { - date: DateTime.fromISO('2020-09-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-01T00:00:00.000Z'), value: 66.0246567, - valueDetail: [39.712019999999995, 0.7280537, 25.584583], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 39.712019999999995, state: DataloadState.VALID }, + { value: 0.7280537, state: DataloadState.VALID }, + { value: 25.584583, state: DataloadState.VALID }, + ], }, { - date: DateTime.fromISO('2020-09-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-02T00:00:00.000Z'), value: 69.05641589999999, - valueDetail: [41.53554, 0.7614849, 26.759391], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 41.53554, state: DataloadState.VALID }, + { value: 0.7614849, state: DataloadState.VALID }, + { value: 26.759391, state: DataloadState.VALID }, + ], }, { - date: DateTime.fromISO('2020-09-03T00:00:00.000Z'), + date: DateTime.fromISO('2020-07-03T00:00:00.000Z'), value: -1, + state: DataloadState.AGGREGATED_HOLE_OR_MISSING, valueDetail: null, }, ], @@ -166,71 +220,96 @@ describe('Consumption service', () => { mockTimePeriod, TimeStep.DAY, fluidTypes, + fluidStatus, mockTimePeriodComparison, true ) expect(result).toEqual(mockResult) }) - it('should return a mapped data for one fluid without comparison date', async () => { - const mockResult = { + it('should return a mapped data for multi fluid without comparison date', async () => { + const fluidTypes: FluidType[] = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, + ] + const mockResult: Datachart = { actualData: [ { - date: DateTime.fromISO('2020-10-01T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-01T00:00:00.000Z'), value: 84.44375099999999, - valueDetail: [50.79059999999999, 0.931161, 32.72199], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 50.79059999999999, state: DataloadState.VALID }, + { value: 0.931161, state: DataloadState.VALID }, + { value: 32.72199, state: DataloadState.VALID }, + ], }, { - date: DateTime.fromISO('2020-10-02T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-02T00:00:00.000Z'), value: 75.2587935, - valueDetail: [45.266099999999994, 0.8298785, 29.162815], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.266099999999994, state: DataloadState.VALID }, + { value: 0.8298785, state: DataloadState.VALID }, + { value: 29.162815, state: DataloadState.VALID }, + ], }, { - date: DateTime.fromISO('2020-10-03T00:00:00.000Z'), + date: DateTime.fromISO('2020-08-03T00:00:00.000Z'), value: -1, + state: DataloadState.AGGREGATED_HOLE_OR_MISSING, valueDetail: null, }, ], comparisonData: [], } - for (let i = 0; i < fluidTypes.length; i++) { - mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual) - } + mockFetchFluidData.mockResolvedValue(mockFetchDataActual) const result = await consumptionDataManager.getGraphData( mockTimePeriod, TimeStep.DAY, - fluidTypes + fluidTypes, + fluidStatus ) expect(result).toEqual(mockResult) }) it('should return null because of wrong parameters', async () => { - const mockFluidTypes = [1] + const fluidTypes = [FluidType.WATER] const result = await consumptionDataManager.getGraphData( mockTimePeriod, - TimeStep.DAY, - mockFluidTypes, + TimeStep.HALF_AN_HOUR, + fluidTypes, + fluidStatus, mockTimePeriodComparison, true ) expect(result).toBeNull() }) it('should return null because of timePeriod and comparaison', async () => { + const fluidTypes: FluidType[] = [FluidType.ELECTRICITY] const wrongTimePeriod = { - startDate: DateTime.fromISO('2020-10-03T23:59:59.999Z'), - endDate: DateTime.fromISO('2020-10-01T00:00:00.000Z'), + startDate: DateTime.fromISO('2020-08-03T23:59:59.999Z'), + endDate: DateTime.fromISO('2020-08-01T00:00:00.000Z'), } const result = await consumptionDataManager.getGraphData( wrongTimePeriod, TimeStep.DAY, fluidTypes, + fluidStatus, mockTimePeriodComparison, true ) expect(result).toBeNull() }) }) + describe('getMaxLoad method', () => { it('should return the maxed value for a time period for the home', async () => { - for (let i = 0; i < fluidTypes.length; i++) { + const fluidTypes: FluidType[] = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, + ] + for (const fluidtype of fluidTypes) { mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual) mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison) } @@ -245,21 +324,27 @@ describe('Consumption service', () => { expect(result).toEqual(expectedResult) }) it('should return the maxed value for a time period', async () => { - const mockFluidTypes = [1] + const fluidTypes: FluidType[] = [FluidType.ELECTRICITY] const expectedResult = 63.1254 mockFetchFluidMaxData.mockResolvedValueOnce(expectedResult) const result = await consumptionDataManager.getMaxLoad( mockTimePeriod, TimeStep.DAY, - mockFluidTypes, + fluidTypes, mockTimePeriodComparison, false ) expect(result).toEqual(expectedResult) }) }) + describe('getPerformanceIndicators method', () => { it('should return the performance indicator', async () => { + const fluidTypes: FluidType[] = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, + ] mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual) mockFetchFluidData.mockResolvedValueOnce(mockFetchDataComparison) mockFetchFluidData.mockResolvedValueOnce(mockFetchDataActual) @@ -267,24 +352,24 @@ describe('Consumption service', () => { //Incomplete Data to test all possibilities mockFetchFluidData.mockResolvedValueOnce([ { - date: DateTime.fromISO('2020-10-01T00:23:20.000Z'), + date: DateTime.fromISO('2020-08-01T00:23:20.000Z'), value: 298.283, }, ]) mockFetchFluidData.mockResolvedValueOnce([ { - date: DateTime.fromISO('2020-10-01T03:10:00.000Z'), + date: DateTime.fromISO('2020-08-01T03:10:00.000Z'), value: 398.283, }, ]) const mockTimePeriodComplete = { - startDate: DateTime.fromISO('2020-10-01T00:00:00.000Z'), - endDate: DateTime.fromISO('2020-10-02T23:59:59.999Z'), + startDate: DateTime.fromISO('2020-08-01T00:00:00.000Z'), + endDate: DateTime.fromISO('2020-08-02T23:59:59.999Z'), } const mockTimePeriodComparisonComplete = { - startDate: DateTime.fromISO('2020-09-01T00:00:00.000Z'), - endDate: DateTime.fromISO('2020-09-02T23:59:59.999Z'), + startDate: DateTime.fromISO('2020-07-01T00:00:00.000Z'), + endDate: DateTime.fromISO('2020-07-02T23:59:59.999Z'), } const expectedResult = [ { @@ -315,63 +400,64 @@ describe('Consumption service', () => { expect(result).toEqual(expectedResult) }) }) - describe('fetchLastDateData method', () => { + + describe('fetchAllFirstDateData method', () => { it('should return the latest date data of one fluid', async () => { - const mockFluidTypes = [0] - const expectedResult = DateTime.fromISO('2020-09-03T23:59:59.999Z') - mockGetLastDateData.mockResolvedValueOnce( - DateTime.fromISO('2020-09-03T23:59:59.999Z') - ) - const result = await consumptionDataManager.fetchLastDateData( - mockFluidTypes - ) - expect(result).toEqual(expectedResult) - }) - it('should return the latest date data of multiple fluid', async () => { - const mockFluidTypes = [0, 2] - mockGetLastDateData.mockResolvedValueOnce( + const fluidTypes: FluidType[] = [FluidType.ELECTRICITY] + const expectedResult = [DateTime.fromISO('2020-09-03T23:59:59.999Z')] + mockGetFirstDateData.mockResolvedValueOnce( DateTime.fromISO('2020-09-03T23:59:59.999Z') ) - mockGetLastDateData.mockResolvedValueOnce( - DateTime.fromISO('2020-09-02T23:59:59.999Z') - ) - const expectedResult = DateTime.fromISO('2020-09-03T23:59:59.999Z') - const result = await consumptionDataManager.fetchLastDateData( - mockFluidTypes + const result = await consumptionDataManager.fetchAllFirstDateData( + fluidTypes ) expect(result).toEqual(expectedResult) }) - it('should return the latest date data of all fluids', async () => { - mockGetLastDateData.mockResolvedValueOnce( + it('should return the latest date data of All fluid', async () => { + const fluidTypes: FluidType[] = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, + ] + mockGetFirstDateData.mockResolvedValueOnce( DateTime.fromISO('2020-09-02T23:59:59.999Z') ) - mockGetLastDateData.mockResolvedValueOnce( + mockGetFirstDateData.mockResolvedValueOnce( DateTime.fromISO('2020-09-03T23:59:59.999Z') ) - mockGetLastDateData.mockResolvedValueOnce( + mockGetFirstDateData.mockResolvedValueOnce( DateTime.fromISO('2020-09-01T23:59:59.999Z') ) - const expectedResult = DateTime.fromISO('2020-09-01T23:59:59.999Z') - const result = await consumptionDataManager.fetchLastDateData( - fluidTypes, - true + const expectedResult = [ + DateTime.fromISO('2020-09-02T23:59:59.999Z'), + DateTime.fromISO('2020-09-03T23:59:59.999Z'), + DateTime.fromISO('2020-09-01T23:59:59.999Z'), + ] + const result = await consumptionDataManager.fetchAllFirstDateData( + fluidTypes ) expect(result).toEqual(expectedResult) }) }) + describe('fetchAllLastDateData method', () => { it('should return the latest date data of one fluid', async () => { - const mockFluidTypes = [0] + const fluidTypes: FluidType[] = [FluidType.ELECTRICITY] const expectedResult = [DateTime.fromISO('2020-09-03T23:59:59.999Z')] mockGetLastDateData.mockResolvedValueOnce( DateTime.fromISO('2020-09-03T23:59:59.999Z') ) const result = await consumptionDataManager.fetchAllLastDateData( - mockFluidTypes + fluidTypes ) expect(result).toEqual(expectedResult) }) it('should return the latest date data of All fluid', async () => { + const fluidTypes: FluidType[] = [ + FluidType.ELECTRICITY, + FluidType.WATER, + FluidType.GAS, + ] mockGetLastDateData.mockResolvedValueOnce( DateTime.fromISO('2020-09-02T23:59:59.999Z') ) @@ -392,9 +478,10 @@ describe('Consumption service', () => { expect(result).toEqual(expectedResult) }) }) + describe('checkDoctypeEntries method', () => { it('should return a boolean if doctype are correct', async () => { - let fluidType = 2 + let fluidType: FluidType = FluidType.GAS mockGetEntries.mockResolvedValueOnce({ data: [1] }) let result = await consumptionDataManager.checkDoctypeEntries( fluidType, @@ -410,6 +497,7 @@ describe('Consumption service', () => { expect(result).toBeFalsy() }) }) + describe('getFirstDataDateFromDoctypeWithPrice', () => { it('should Get the first entry of a given data doctype', async () => { const data: QueryResult<FluidPrice[]> = { @@ -426,6 +514,7 @@ describe('Consumption service', () => { expect(result).toEqual(data.data[0]) }) }) + describe('getLastHourData', () => { it('should get last hour data', async () => { const mockQueryResult: QueryResult<DataloadEntity[]> = { @@ -444,6 +533,7 @@ describe('Consumption service', () => { expect(result.length).toEqual(1) }) }) + describe('saveDoc & saveDocs', () => { it('should saveDoc', async () => { const mockQueryResult: QueryResult<DataloadEntity> = { diff --git a/src/services/consumption.service.ts b/src/services/consumption.service.ts index f4648871f87b478f026b2b71a3ae636835d473a3..107b36b21b472a9de08e267d02466a27b8d771eb 100644 --- a/src/services/consumption.service.ts +++ b/src/services/consumption.service.ts @@ -6,6 +6,8 @@ import { Datachart, Dataload, DataloadEntity, + DataloadValueDetail, + FluidStatus, PerformanceIndicator, TimePeriod, } from 'models' @@ -16,6 +18,7 @@ import ConverterService from 'services/converter.service' import { ENEDIS_MINUTE_DOCTYPE } from 'doctypes' import { Doctype } from 'cozy-client/types/types' import { EnedisMonthlyAnalysisData } from 'models/enedisMonthlyAnalysis' +import { DataloadState } from 'enum/dataload.enum' // eslint-disable-next-line @typescript-eslint/interface-name-prefix export interface ISingleFluidChartData { @@ -41,6 +44,7 @@ export default class ConsumptionDataManager { * @param timePeriod TimePeriod * @param timeStep TimeStep * @param fluidTypes FluidType[] + * @param fluidStatus FluidStatus[] * @param compareTimePeriod - Optional TimePeriod * @param isHome - Optional boolean * @returns DataChart | null @@ -49,6 +53,7 @@ export default class ConsumptionDataManager { timePeriod: TimePeriod, timeStep: TimeStep, fluidTypes: FluidType[], + fluidStatus?: FluidStatus[], compareTimePeriod?: TimePeriod, isHome?: boolean ): Promise<Datachart | null> { @@ -60,23 +65,23 @@ export default class ConsumptionDataManager { ) if (!InputisValid) return null if (fluidTypes.length === 1 && !isHome) { - //TODO validating input data - //TODO applying buisness logic to the query arguments - + const fluidType: FluidType = fluidTypes[0] // running the query - const fetchedData = await this.fetchSingleFluidGraphData( + const fetchedData: Datachart | null = await this.fetchSingleFluidGraphData( timePeriod, timeStep, - fluidTypes[0], + fluidType, compareTimePeriod ) // formatting data - const formattedData = this.formatGraphDataManage( + const formattedData: Datachart | null = this.formatGraphDataManager( fetchedData, timeStep, timePeriod, - compareTimePeriod || null + compareTimePeriod || null, + fluidType, + fluidStatus ? fluidStatus[fluidType] : undefined ) return formattedData } else if (fluidTypes.length > 1 || isHome) { @@ -89,11 +94,13 @@ export default class ConsumptionDataManager { compareTimePeriod ) // formatting data - const formattedData = this.formatGraphDataManage( + const formattedData = this.formatGraphDataManager( fetchedData, timeStep, timePeriod, - compareTimePeriod || null + compareTimePeriod || null, + fluidType, + fluidStatus ? fluidStatus[fluidType] : undefined ) // validating output data toBeAgreggatedData.push({ @@ -101,8 +108,9 @@ export default class ConsumptionDataManager { chartFluid: fluidType, }) } - const aggregatedData = this.aggregateGraphData(toBeAgreggatedData) - + const aggregatedData: Datachart | null = this.aggregateGraphData( + toBeAgreggatedData + ) return aggregatedData } else return null } @@ -121,15 +129,13 @@ export default class ConsumptionDataManager { maxTimePeriod, timeStep, fluidTypes, + undefined, compareMaxTimePeriod, isHome ) - const max = - allData && allData.actualData - ? Math.max(...allData.actualData.map(d => d.value)) - : 0 - - return max + return allData && allData.actualData + ? Math.max(...allData.actualData.map(d => d.value)) + : 0 } else { const max = await this._queryRunnerService.fetchFluidMaxData( maxTimePeriod, @@ -166,14 +172,13 @@ export default class ConsumptionDataManager { fluidTypes: FluidType[], compareTimePeriod?: TimePeriod ): Promise<PerformanceIndicator[]> { - //const result = {}; const performanceIndicators: PerformanceIndicator[] = [] - - for (const fluideType of fluidTypes) { + for (const fluidType of fluidTypes) { const graphData: Datachart | null = await this.getGraphData( timePeriod, timeStep, - [fluideType], + [fluidType], + undefined, compareTimePeriod ) @@ -214,7 +219,7 @@ export default class ConsumptionDataManager { ) } - performanceIndicators[fluideType] = performanceIndicator + performanceIndicators[fluidType] = performanceIndicator } } @@ -235,7 +240,7 @@ export default class ConsumptionDataManager { } public calculatePerformanceIndicatorPrice(data: Dataload[]): number { - return data.reduce((a, b) => (b.price !== null ? a + b.price : a), 0) + return data.reduce((a, b) => (b.price ? a + b.price : a), 0) } private calculatePerformanceIndicatorVariationPercentage( @@ -287,18 +292,22 @@ export default class ConsumptionDataManager { return singleFluidGraphData } - private formatGraphDataManage( + private formatGraphDataManager( data: Datachart | null, timeStep: TimeStep, timePeriod: TimePeriod, - compareTimePeriod: TimePeriod | null + compareTimePeriod: TimePeriod | null, + fluidType: FluidType, + fluidStatus?: FluidStatus ): Datachart | null { if (!data) return null const formattedActualData: Dataload[] = this._consumptionFormatterService.formatGraphData( data.actualData, timePeriod, - timeStep + timeStep, + fluidType, + fluidStatus ) let formattedComparisonData: Dataload[] | null = null @@ -306,7 +315,9 @@ export default class ConsumptionDataManager { formattedComparisonData = this._consumptionFormatterService.formatGraphData( data.comparisonData ? data.comparisonData : [], compareTimePeriod, - timeStep + timeStep, + fluidType, + fluidStatus ) const result: Datachart = { @@ -317,40 +328,23 @@ export default class ConsumptionDataManager { return result } - public async fetchLastDateData( - fluidTypes: FluidType[], - allFluids?: boolean - ): Promise<DateTime | null> { - let lastDay = null + public async fetchAllFirstDateData( + fluidTypes: FluidType[] + ): Promise<(DateTime | null)[]> { + let firstDay = null + const firstDays = [] if (fluidTypes.length === 1) { - lastDay = - (await this._queryRunnerService.getLastDateData(fluidTypes[0])) || null + firstDay = + (await this._queryRunnerService.getFirstDateData(fluidTypes[0])) || null + firstDays.push(firstDay) } else if (fluidTypes.length > 1) { - const lastDays = [] for (const fluidType of fluidTypes) { - lastDay = - (await this._queryRunnerService.getLastDateData(fluidType)) || null - - if (lastDay) { - lastDays.push(lastDay) - } - } - if (lastDays.length < 1) { - return null - } - if (allFluids) { - lastDay = lastDays.reduce(function(a, b) { - return a < b ? a : b - }) - } else { - lastDay = lastDays.reduce(function(a, b) { - return a > b ? a : b - }) + firstDay = + (await this._queryRunnerService.getFirstDateData(fluidType)) || null + firstDays.push(firstDay) } } - //validate input - // validate output - return lastDay + return firstDays } public async fetchAllLastDateData( @@ -428,16 +422,22 @@ export default class ConsumptionDataManager { let agreggatedConvertedValue = 0 let comparisonAgreggatedConvertedValue = 0 + const tempAggregatedState: DataloadState[] = [] + const tempComparisonAggregatedState: DataloadState[] = [] + let noDataCount = 0 let comparisonNoDataCount = 0 - const convertedValueDetail = [] - const comparisonConvertedValueDetail = [] + const convertedValueDetail: DataloadValueDetail[] = [] + const comparisonConvertedValueDetail: DataloadValueDetail[] = [] for (const singleFluidChart of singleFluidCharts) { if (!singleFluidChart.chartData) break if (!singleFluidChart.chartData.actualData[i]) break const value = singleFluidChart.chartData.actualData[i].value + tempAggregatedState.push( + singleFluidChart.chartData.actualData[i].state + ) let convertedValue = -1 if (value === -1) noDataCount++ @@ -450,7 +450,10 @@ export default class ConsumptionDataManager { agreggatedConvertedValue += convertedValue } - convertedValueDetail[singleFluidChart.chartFluid] = convertedValue + convertedValueDetail[singleFluidChart.chartFluid] = { + value: convertedValue, + state: singleFluidChart.chartData.actualData[i].state, + } if ( singleFluidChart.chartData.comparisonData && @@ -458,6 +461,9 @@ export default class ConsumptionDataManager { ) { const comparisonValue = singleFluidChart.chartData.comparisonData[i].value + tempComparisonAggregatedState.push( + singleFluidChart.chartData.comparisonData[i].state + ) let convertedComparisonValue = -1 if (comparisonValue === -1) comparisonNoDataCount++ @@ -470,9 +476,10 @@ export default class ConsumptionDataManager { comparisonAgreggatedConvertedValue += convertedComparisonValue } - comparisonConvertedValueDetail[ - singleFluidChart.chartFluid - ] = convertedComparisonValue + comparisonConvertedValueDetail[singleFluidChart.chartFluid] = { + value: convertedComparisonValue, + state: singleFluidChart.chartData.comparisonData[i].state, + } } } @@ -482,9 +489,14 @@ export default class ConsumptionDataManager { comparisonAgreggatedConvertedValue = -1 if (singleFluidCharts[0].chartData.actualData[i]) { + // Define the aggregated state + const aggregatedDataloadState: DataloadState = this._consumptionFormatterService.defineAggregatedDataloadState( + tempAggregatedState + ) const acutaldataLoad: Dataload = { date: singleFluidCharts[0].chartData.actualData[i].date, value: agreggatedConvertedValue, + state: aggregatedDataloadState, valueDetail: agreggatedConvertedValue === -1 ? null : convertedValueDetail, } @@ -496,9 +508,14 @@ export default class ConsumptionDataManager { resultChartData.comparisonData && singleFluidCharts[0].chartData.comparisonData[i] ) { + // Define the aggregated state + const aggregatedComparisonDataloadState: DataloadState = this._consumptionFormatterService.defineAggregatedDataloadState( + tempComparisonAggregatedState + ) const comparisondataLoad: Dataload = { date: singleFluidCharts[0].chartData.comparisonData[i].date, value: comparisonAgreggatedConvertedValue, + state: aggregatedComparisonDataloadState, valueDetail: comparisonAgreggatedConvertedValue === -1 ? null diff --git a/src/services/consumptionFormatter.service.spec.ts b/src/services/consumptionFormatter.service.spec.ts index 420129fa314891de5e828bc567d9ecf52bdcfb89..2c5a80f5def0cf78327e1cb2144ead7b86fb9d44 100644 --- a/src/services/consumptionFormatter.service.spec.ts +++ b/src/services/consumptionFormatter.service.spec.ts @@ -1,7 +1,12 @@ import ConsumptionFormatterService from './consumptionFormatter.service' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' -import { Dataload, TimePeriod } from 'models' +import { Dataload, FluidStatus, TimePeriod } from 'models' +import { DataloadState } from 'enum/dataload.enum' +import { FluidType } from 'enum/fluid.enum' +import { fluidStatusConnectedData } from '../../tests/__mocks__/fluidStatusData.mock' + +const localSpy = jest.spyOn(DateTime, 'local') const mockDataLoad: Dataload[] = [ { @@ -9,6 +14,7 @@ const mockDataLoad: Dataload[] = [ zone: 'utc', }), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, { @@ -16,6 +22,7 @@ const mockDataLoad: Dataload[] = [ zone: 'utc', }), value: 235.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -23,6 +30,7 @@ const mockDataLoad: Dataload[] = [ zone: 'utc', }), value: 260.15, + state: DataloadState.VALID, valueDetail: null, }, { @@ -30,6 +38,7 @@ const mockDataLoad: Dataload[] = [ zone: 'utc', }), value: 293.33, + state: DataloadState.VALID, valueDetail: null, }, { @@ -37,6 +46,7 @@ const mockDataLoad: Dataload[] = [ zone: 'utc', }), value: 268.55, + state: DataloadState.VALID, valueDetail: null, }, { @@ -44,6 +54,7 @@ const mockDataLoad: Dataload[] = [ zone: 'utc', }), value: 272.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -56,16 +67,30 @@ let mockTimePeriod: TimePeriod = { }), } const unknowTimeStep = 999 + describe('ConsumptionFormatter service', () => { const consumptionFormatterService = new ConsumptionFormatterService() + const fluidStatus: FluidStatus[] = fluidStatusConnectedData + + beforeEach(() => { + localSpy.mockClear() + fluidStatus[FluidType.ELECTRICITY].firstDataDate = null + fluidStatus[FluidType.ELECTRICITY].lastDataDate = null + fluidStatus[FluidType.WATER].firstDataDate = null + fluidStatus[FluidType.WATER].lastDataDate = null + fluidStatus[FluidType.GAS].firstDataDate = null + fluidStatus[FluidType.GAS].firstDataDate = null + }) + describe('formatGraphData method', () => { it('should return a formattedData for DAY', () => { - const mockResult = [ + const mockResult: Dataload[] = [ { date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { zone: 'utc', }), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, { @@ -73,6 +98,7 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: -1, + state: DataloadState.EMPTY, valueDetail: null, }, { @@ -80,13 +106,16 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: 260.15, + state: DataloadState.VALID, valueDetail: null, }, ] const result = consumptionFormatterService.formatGraphData( mockDataLoad, mockTimePeriod, - TimeStep.DAY + TimeStep.DAY, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] ) expect(result).toEqual(mockResult) }) @@ -99,13 +128,13 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), } - const mockResult = [ { date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { zone: 'utc', }), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, { @@ -113,6 +142,7 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: 235.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -120,13 +150,16 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: -1, + state: DataloadState.EMPTY, valueDetail: null, }, ] const result = consumptionFormatterService.formatGraphData( mockDataLoad, mockTimePeriod, - TimeStep.HALF_AN_HOUR + TimeStep.HALF_AN_HOUR, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] ) expect(result).toEqual(mockResult) }) @@ -139,13 +172,13 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), } - const mockResult = [ { date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { zone: 'utc', }), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, { @@ -153,6 +186,7 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: 268.55, + state: DataloadState.VALID, valueDetail: null, }, { @@ -160,13 +194,16 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: -1, + state: DataloadState.EMPTY, valueDetail: null, }, ] const result = consumptionFormatterService.formatGraphData( mockDataLoad, mockTimePeriod, - TimeStep.MONTH + TimeStep.MONTH, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] ) expect(result).toEqual(mockResult) }) @@ -179,13 +216,13 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), } - const mockResult = [ { date: DateTime.fromISO('2018-11-03T20:59:59.999Z', { zone: 'utc', }), value: 272.33, + state: DataloadState.VALID, valueDetail: null, }, { @@ -193,6 +230,7 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: -1, + state: DataloadState.EMPTY, valueDetail: null, }, { @@ -200,13 +238,16 @@ describe('ConsumptionFormatter service', () => { zone: 'utc', }), value: 291.9, + state: DataloadState.VALID, valueDetail: null, }, ] const result = consumptionFormatterService.formatGraphData( mockDataLoad, mockTimePeriod, - TimeStep.YEAR + TimeStep.YEAR, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] ) expect(result).toEqual(mockResult) }) @@ -215,11 +256,408 @@ describe('ConsumptionFormatter service', () => { consumptionFormatterService.formatGraphData( mockDataLoad, mockTimePeriod, - unknowTimeStep + unknowTimeStep, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] ) } catch (error) { expect(error).toEqual(new Error('TimeStep unknown')) } }) }) + + describe('defineDataloadState method', () => { + const mockData: Dataload = { + date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { + zone: 'utc', + }), + value: 291.9, + state: DataloadState.VALID, + valueDetail: null, + } + const mockEmptyData: Dataload = { + date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { + zone: 'utc', + }), + value: -1, + state: DataloadState.EMPTY, + valueDetail: null, + } + it('sould return not change state because data date < today and no firstFluidDataDate & no lastFluidDataDate', () => { + const expectedResult: Dataload = mockData + const result: Dataload = consumptionFormatterService.defineDataloadState( + mockData, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + it('sould return COMING state because data date >= today', () => { + localSpy.mockReturnValueOnce( + DateTime.fromISO('2020-10-01T00:00:00.000Z', { zone: 'utc' }) + ) + const expectedResult: Dataload = { + ...mockData, + state: DataloadState.COMING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + mockData, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + it('sould return EMPTY state because data.date < firstFluidDataDate', () => { + fluidStatus[FluidType.ELECTRICITY].firstDataDate = DateTime.fromISO( + '2020-10-20T00:00:00.000Z', + { + zone: 'utc', + } + ) + const expectedResult: Dataload = { + ...mockData, + state: DataloadState.EMPTY, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + mockData, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + it('sould return HOLE state because data.date between firstFluidDataDate and lastFluidDataDate and value is -1', () => { + fluidStatus[FluidType.ELECTRICITY].firstDataDate = DateTime.fromISO( + '2020-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.ELECTRICITY].lastDataDate = DateTime.fromISO( + '2020-12-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + const expectedResult: Dataload = { + ...mockEmptyData, + state: DataloadState.HOLE, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + mockEmptyData, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + it('sould return VALID state because data.date between firstFluidDataDate and lastFluidDataDate and value is -1', () => { + fluidStatus[FluidType.ELECTRICITY].firstDataDate = DateTime.fromISO( + '2020-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.ELECTRICITY].lastDataDate = DateTime.fromISO( + '2020-12-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + const expectedResult: Dataload = { + ...mockData, + state: DataloadState.VALID, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + mockData, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + + describe('sould return COMING state because data.date > lastFluidDataDate and is in dataDelayOffset period', () => { + beforeEach(() => { + fluidStatus[FluidType.ELECTRICITY].firstDataDate = DateTime.fromISO( + '2020-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.ELECTRICITY].lastDataDate = DateTime.fromISO( + '2020-09-30T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.WATER].firstDataDate = DateTime.fromISO( + '2020-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.WATER].lastDataDate = DateTime.fromISO( + '2020-09-30T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.GAS].firstDataDate = DateTime.fromISO( + '2020-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.GAS].lastDataDate = DateTime.fromISO( + '2020-09-30T00:00:00.000Z', + { + zone: 'utc', + } + ) + }) + const today = DateTime.local().setZone('utc', { + keepLocalTime: true, + }) + it('case ELECTRICITY with date >= today-3', () => { + const data: Dataload = { + ...mockEmptyData, + date: today.minus({ day: 3 }), + } + const expectedResult: Dataload = { + ...data, + state: DataloadState.COMING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + data, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + it('case GAS with date >= today-5', () => { + const data: Dataload = { + ...mockEmptyData, + date: today.minus({ day: 5 }), + } + const expectedResult: Dataload = { + ...data, + state: DataloadState.COMING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + data, + FluidType.GAS, + fluidStatus[FluidType.GAS] + ) + expect(result).toEqual(expectedResult) + }) + it('case WATER with date >= today-5', () => { + const data: Dataload = { + ...mockEmptyData, + date: today.minus({ day: 5 }), + } + const expectedResult: Dataload = { + ...data, + state: DataloadState.COMING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + data, + FluidType.WATER, + fluidStatus[FluidType.WATER] + ) + expect(result).toEqual(expectedResult) + }) + }) + + describe('sould return MISSING state because data.date > lastFluidDataDate and is not in dataDelayOffset period', () => { + beforeEach(() => { + fluidStatus[FluidType.ELECTRICITY].firstDataDate = DateTime.fromISO( + '2019-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.ELECTRICITY].lastDataDate = DateTime.fromISO( + '2020-09-30T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.WATER].firstDataDate = DateTime.fromISO( + '2019-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.WATER].lastDataDate = DateTime.fromISO( + '2020-09-30T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.GAS].firstDataDate = DateTime.fromISO( + '2019-08-01T00:00:00.000Z', + { + zone: 'utc', + } + ) + fluidStatus[FluidType.GAS].lastDataDate = DateTime.fromISO( + '2020-09-30T00:00:00.000Z', + { + zone: 'utc', + } + ) + }) + const today = DateTime.local().setZone('utc', { + keepLocalTime: true, + }) + it('case ELECTRICITY with date <= today-3', () => { + const data: Dataload = { + ...mockEmptyData, + date: today.minus({ day: 4 }), + } + const expectedResult: Dataload = { + ...data, + state: DataloadState.MISSING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + data, + FluidType.ELECTRICITY, + fluidStatus[FluidType.ELECTRICITY] + ) + expect(result).toEqual(expectedResult) + }) + it('case GAS with date <= today-5', () => { + const data: Dataload = { + ...mockEmptyData, + date: today.minus({ day: 6 }), + } + const expectedResult: Dataload = { + ...data, + state: DataloadState.MISSING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + data, + FluidType.GAS, + fluidStatus[FluidType.GAS] + ) + expect(result).toEqual(expectedResult) + }) + it('case WATER with date <= today-5', () => { + const data: Dataload = { + ...mockEmptyData, + date: today.minus({ day: 6 }), + } + const expectedResult: Dataload = { + ...data, + state: DataloadState.MISSING, + } + const result: Dataload = consumptionFormatterService.defineDataloadState( + data, + FluidType.WATER, + fluidStatus[FluidType.WATER] + ) + expect(result).toEqual(expectedResult) + }) + }) + }) + + describe('defineAggregatedDataloadState method', () => { + it('should return AGGREGATED_WITH_HOLE_OR_MISSING because of HOLE data', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.VALID, + DataloadState.HOLE, + DataloadState.VALID, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING) + }) + it('should return AGGREGATED_WITH_HOLE_OR_MISSING because of MISSING data', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.VALID, + DataloadState.MISSING, + DataloadState.VALID, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING) + }) + it('should return AGGREGATED_WITH_COMING because of UPCOMING data', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.VALID, + DataloadState.UPCOMING, + DataloadState.VALID, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_WITH_COMING) + }) + it('should return AGGREGATED_WITH_COMING because of COMING data', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.VALID, + DataloadState.COMING, + DataloadState.VALID, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_WITH_COMING) + }) + it('should return AGGREGATED_WITH_EMPTY because of EMPTY data', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.VALID, + DataloadState.EMPTY, + DataloadState.VALID, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_WITH_EMPTY) + }) + it('should return AGGREGATED_VALID', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.VALID, + DataloadState.VALID, + DataloadState.VALID, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_VALID) + }) + it('should return AGGREGATED_HOLE_OR_MISSING', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.HOLE, + DataloadState.MISSING, + DataloadState.MISSING, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_HOLE_OR_MISSING) + }) + it('should return AGGREGATED_COMING', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.UPCOMING, + DataloadState.COMING, + DataloadState.COMING, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_COMING) + }) + it('should return AGGREGATED_EMPTY', () => { + const dataloadStateArray: DataloadState[] = [ + DataloadState.EMPTY, + DataloadState.EMPTY, + DataloadState.EMPTY, + ] + const result: DataloadState = consumptionFormatterService.defineAggregatedDataloadState( + dataloadStateArray + ) + expect(result).toEqual(DataloadState.AGGREGATED_EMPTY) + }) + }) }) diff --git a/src/services/consumptionFormatter.service.ts b/src/services/consumptionFormatter.service.ts index 9a7504c54b5c1282a8289e51dcb3c62a95cbe5ac..ce3f9e43800e843cca0eb197de8c45a219a5a3d0 100644 --- a/src/services/consumptionFormatter.service.ts +++ b/src/services/consumptionFormatter.service.ts @@ -1,51 +1,169 @@ /* eslint-disable @typescript-eslint/interface-name-prefix */ +import { DataloadState } from 'enum/dataload.enum' +import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' -import { Dataload, TimePeriod } from 'models' +import { DateTime, Interval } from 'luxon' +import { Dataload, FluidStatus, TimePeriod } from 'models' import DateChartService from 'services/dateChart.service' import { compareDates } from 'utils/date' +import ConfigService from './fluidConfig.service' export default class ConsumptionFormatterService { public formatGraphData( data: Dataload[], timePeriod: TimePeriod, - timeStep: TimeStep + timeStep: TimeStep, + fluidType: FluidType, + fluidStatus?: FluidStatus ): Dataload[] { + // Sort data data.sort((dataA, dataB) => compareDates(dataA.date, dataB.date)) - const formattedData = this.fillMissingData(data, timePeriod, timeStep) - - // complete missing data within data - - return formattedData - } - - private fillMissingData( - data: Dataload[], - timePeriod: TimePeriod, - timeStep: TimeStep - ): Dataload[] { + // Set status of data and complete missing/empty data const filledData = [] let parsingDate = timePeriod.startDate const dateChartService = new DateChartService() while (parsingDate <= timePeriod.endDate) { - //const filtereddata = data.filter(dt => dt.date.equals(parsingDate)) const filtereddata = data.filter(dt => dateChartService.compareStepDate(timeStep, dt.date, parsingDate) ) - const newElement = filtereddata[0] - ? filtereddata[0] - : { - date: parsingDate, - value: -1, - } - filledData.push({ ...newElement, valueDetail: null }) + const newElement: Dataload = this.defineDataloadState( + filtereddata[0] + ? filtereddata[0] + : { + date: parsingDate, + value: -1, + state: DataloadState.EMPTY, + valueDetail: null, + }, + fluidType, + fluidStatus + ) + filledData.push({ ...newElement }) parsingDate = parsingDate.plus(this.getTimeFromStepTime(timeStep)) } return filledData } + public defineDataloadState( + data: Dataload, + fluidType: FluidType, + fluidStatus?: FluidStatus + ): Dataload { + const today = DateTime.local().setZone('utc', { + keepLocalTime: true, + }) + // Return coming state if data data is >= today + if (data.date >= today) { + return { ...data, state: DataloadState.COMING } + } + if (!fluidStatus) { + return data + } + // Define state in function of first and last fluid data date + if (fluidStatus.firstDataDate && data.date < fluidStatus.firstDataDate) { + return { ...data, state: DataloadState.EMPTY } + } + if (fluidStatus.lastDataDate && data.date > fluidStatus.lastDataDate) { + const isDataToCome: boolean = this.isDataToCome(data, fluidType) + return { + ...data, + state: isDataToCome ? DataloadState.COMING : DataloadState.MISSING, + } + } + if ( + fluidStatus.firstDataDate && + fluidStatus.lastDataDate && + data.date >= fluidStatus.firstDataDate && + data.date <= fluidStatus.lastDataDate + ) { + return { + ...data, + state: data.value === -1 ? DataloadState.HOLE : DataloadState.VALID, + } + } + return data + } + + private isDataToCome(dataload: Dataload, fluidType: FluidType): boolean { + const configService = new ConfigService() + const fluidConfig = configService.getFluidConfig() + + const inter = Interval.fromDateTimes( + dataload.date.startOf('day'), + DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .startOf('day') + ).count('days') + if ( + fluidType === FluidType.ELECTRICITY && + inter <= fluidConfig[0].dataDelayOffset + 1 + ) { + return true + } + if ( + fluidType === FluidType.WATER && + inter <= fluidConfig[1].dataDelayOffset + 1 + ) { + return true + } + if ( + fluidType === FluidType.GAS && + inter <= fluidConfig[2].dataDelayOffset + 1 + ) { + return true + } else { + return false + } + } + + public defineAggregatedDataloadState( + dataloadStateArray: DataloadState[] + ): DataloadState { + if (dataloadStateArray.includes(DataloadState.VALID)) { + if ( + dataloadStateArray.includes(DataloadState.HOLE) || + dataloadStateArray.includes(DataloadState.MISSING) + ) { + return DataloadState.AGGREGATED_WITH_HOLE_OR_MISSING + } + if ( + dataloadStateArray.includes(DataloadState.UPCOMING) || + dataloadStateArray.includes(DataloadState.COMING) + ) { + return DataloadState.AGGREGATED_WITH_COMING + } + if (dataloadStateArray.includes(DataloadState.EMPTY)) { + return DataloadState.AGGREGATED_WITH_EMPTY + } + return DataloadState.AGGREGATED_VALID + } + // No valid data but at least one hole or missing data + if ( + dataloadStateArray.includes(DataloadState.HOLE) || + dataloadStateArray.includes(DataloadState.MISSING) + ) { + return DataloadState.AGGREGATED_HOLE_OR_MISSING + } + // No valid data but at least one upcoming or coming data + if ( + dataloadStateArray.includes(DataloadState.UPCOMING) || + dataloadStateArray.includes(DataloadState.COMING) + ) { + return DataloadState.AGGREGATED_COMING + } + // No valid data but at least one empty data + if (dataloadStateArray.includes(DataloadState.EMPTY)) { + return DataloadState.AGGREGATED_EMPTY + } + // Default + return DataloadState.AGGREGATED_VALID + } + private getTimeFromStepTime(timeStep: TimeStep) { switch (timeStep) { case TimeStep.HALF_AN_HOUR: diff --git a/src/services/dateChart.service.spec.ts b/src/services/dateChart.service.spec.ts index 606c816f635dcb58bc4e5caeb1c0e834397e49d9..e78e403999fb843c453cfec1ac720fd9fd19825e 100644 --- a/src/services/dateChart.service.spec.ts +++ b/src/services/dateChart.service.spec.ts @@ -1,8 +1,7 @@ import DateChartService from './dateChart.service' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' -import { Dataload, TimePeriod } from 'models' -import { FluidType } from 'enum/fluid.enum' +import { TimePeriod } from 'models' const localSpy = jest.spyOn(DateTime, 'local') @@ -1292,77 +1291,4 @@ describe('dateChart service', () => { expect(result).toEqual(0) }) }) - - describe('isDataToCome method', () => { - const dateChartService = new DateChartService() - const today = DateTime.local().setZone('utc', { - keepLocalTime: true, - }) - - it('should return true for electricity fluidType and dataLoad with date < today-1', () => { - const dataLoad: Dataload = { - date: today, - value: 0, - valueDetail: null, - } - const result = dateChartService.isDataToCome( - dataLoad, - FluidType.ELECTRICITY - ) - expect(result).toBe(true) - }) - - it('should return false for electricity fluidType and dataLoad with date > J+3', () => { - const dataLoad: Dataload = { - date: today.plus({ day: -4 }), - value: 0, - valueDetail: null, - } - const result = dateChartService.isDataToCome( - dataLoad, - FluidType.ELECTRICITY - ) - expect(result).toBe(false) - }) - - it('should return true for gaz fluidType and dataLoad with date < today-2', () => { - const dataLoad: Dataload = { - date: today.plus({ day: -1 }), - value: 0, - valueDetail: null, - } - const result = dateChartService.isDataToCome(dataLoad, FluidType.GAS) - expect(result).toBe(true) - }) - - it('should return false for gaz fluidType and dataLoad with date > J+5', () => { - const dataLoad: Dataload = { - date: today.plus({ day: -6 }), - value: 0, - valueDetail: null, - } - const result = dateChartService.isDataToCome(dataLoad, FluidType.GAS) - expect(result).toBe(false) - }) - - it('should return true for water fluidType and dataLoad with date < today-3', () => { - const dataLoad: Dataload = { - date: today.plus({ day: -2 }), - value: 0, - valueDetail: null, - } - const result = dateChartService.isDataToCome(dataLoad, FluidType.WATER) - expect(result).toBe(true) - }) - - it('should return false for water fluidType and dataLoad with date > J+5', () => { - const dataLoad: Dataload = { - date: today.plus({ day: -6 }), - value: 0, - valueDetail: null, - } - const result = dateChartService.isDataToCome(dataLoad, FluidType.WATER) - expect(result).toBe(false) - }) - }) }) diff --git a/src/services/dateChart.service.ts b/src/services/dateChart.service.ts index 2d85d3fd24866df224d8c2f82ff4d85ea2586826..6bb1f0f67e78db16e12934d17f01a5bead39b154 100644 --- a/src/services/dateChart.service.ts +++ b/src/services/dateChart.service.ts @@ -296,76 +296,6 @@ export default class DateChartService { } } - public isDataToCome(dataload: Dataload, fluidType: FluidType) { - const configService = new ConfigService() - const fluidConfig = configService.getFluidConfig() - - const inter = - dataload && - Interval.fromDateTimes( - dataload.date.startOf('day'), - DateTime.local() - .setZone('utc', { - keepLocalTime: true, - }) - .startOf('day') - ).count('days') - if ( - fluidType === FluidType.ELECTRICITY && - inter <= fluidConfig[0].dataDelayOffset + 1 - ) { - return true - } - if ( - fluidType === FluidType.WATER && - inter <= fluidConfig[1].dataDelayOffset + 1 - ) { - return true - } - if ( - fluidType === FluidType.GAS && - inter <= fluidConfig[2].dataDelayOffset + 1 - ) { - return true - } else { - return false - } - } - - /** - * Checks if there is data after a lack of data so we know if it is a hole or not - * @param {Datachart} currentDatachart - * @returns - */ - public isDataHole( - currentDatachart: Datachart, - fluidType?: FluidType - ): boolean { - let isDataHole = false - let isEmpty = false - if (fluidType || fluidType === 0) { - currentDatachart.actualData.forEach((data: Dataload) => { - if (data.valueDetail && data.valueDetail[fluidType] === -1) { - isEmpty = true - } - if (data.valueDetail && data.valueDetail[fluidType] > -1 && isEmpty) - isDataHole = true - }) - } else { - currentDatachart.actualData.forEach((data: Dataload) => { - if (data.value === -1) { - isEmpty = true - } - if (data.value > -1 && isEmpty) isDataHole = true - }) - } - if (isDataHole) { - return true - } else { - return false - } - } - /** * Checks if the last data date is outdated and returns the number of missing days * @param {DateTime | null} lastDataDate diff --git a/src/services/duel.service.ts b/src/services/duel.service.ts index 7360017e6d981f4963cb880e0515a338f943c640..82bf4af528a04073afd4d2511898bd716a9cc0eb 100644 --- a/src/services/duel.service.ts +++ b/src/services/duel.service.ts @@ -91,11 +91,16 @@ export default class DuelService { TimeStep.DAY, fluidType, undefined, + undefined, true ) if (dataLoad && dataLoad.actualData) { dataLoad.actualData.forEach((d: Dataload) => { - if (d.value === -1 || (d.valueDetail && d.valueDetail.includes(-1))) + if ( + d.value === -1 || + (d.valueDetail && + d.valueDetail.filter(data => data.value === -1).length > 0) + ) isComplete = false }) } diff --git a/src/services/enedisMonthlyAnalysisData.service.ts b/src/services/enedisMonthlyAnalysisData.service.ts index dbb828bdb493ef565bb0e5390145686ca48a3dc9..ce18173c4f017cd31cdb401e2f50674c0bbc3071 100644 --- a/src/services/enedisMonthlyAnalysisData.service.ts +++ b/src/services/enedisMonthlyAnalysisData.service.ts @@ -4,6 +4,7 @@ import { ENEDIS_MAXPOWER_DOCTYPE, ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE, } from 'doctypes' +import { DataloadState } from 'enum/dataload.enum' import { DateTime } from 'luxon' import { Dataload } from 'models' import { @@ -64,6 +65,7 @@ export default class EnedisMonthlyAnalysisDataService { data.weekDaysHalfHourAverageValues.forEach((value, index) => { dataLoadWeekDays.push({ value: value, + state: DataloadState.VALID, valueDetail: null, date: DateTime.fromObject({ year: data.year, @@ -79,6 +81,7 @@ export default class EnedisMonthlyAnalysisDataService { data.weekEndDaysHalfHourAverageValues.forEach((value, index) => { dataLoadWeekEndDays.push({ value: value, + state: DataloadState.VALID, valueDetail: null, date: DateTime.fromObject({ year: data.year, diff --git a/src/services/fluid.service.spec.ts b/src/services/fluid.service.spec.ts index fe9caea0aeae04d9a52a8f495d8563c7b412a88f..af2a1acff31a454c01a22d8dce29ffa10d1db562 100644 --- a/src/services/fluid.service.spec.ts +++ b/src/services/fluid.service.spec.ts @@ -41,15 +41,29 @@ jest.mock('./triggers.service', () => { }) }) +const mockFetchAllFirstDateData = jest.fn() const mockFetchAllLastDateData = jest.fn() jest.mock('./consumption.service', () => { return jest.fn(() => { return { + fetchAllFirstDateData: mockFetchAllFirstDateData, fetchAllLastDateData: mockFetchAllLastDateData, } }) }) +const mockDataDates: (DateTime | null)[] = [ + DateTime.local().setZone('utc', { + keepLocalTime: true, + }), + DateTime.local().setZone('utc', { + keepLocalTime: true, + }), + DateTime.local().setZone('utc', { + keepLocalTime: true, + }), +] + describe('FLuid service', () => { const fluidService = new FluidService(mockClient) @@ -57,27 +71,19 @@ describe('FLuid service', () => { mockGetAccountByType.mockReset() mockGetTrigger.mockReset() mockFetchTriggerState.mockReset() + mockFetchTriggerState.mockReset() + mockFetchAllFirstDateData.mockReset() mockFetchAllLastDateData.mockReset() }) describe('getFluidStatus method', () => { it('should return fluid status for all fluids', async () => { - const mockLastDataDates: (DateTime | null)[] = [ - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - ] const mockResult: FluidStatus[] = [ { fluidType: FluidType.ELECTRICITY, status: FluidState.ERROR, - lastDataDate: mockLastDataDates[FluidType.ELECTRICITY], + firstDataDate: mockDataDates[FluidType.ELECTRICITY], + lastDataDate: mockDataDates[FluidType.ELECTRICITY], connection: { konnector: konnectorsData[0], account: accountsData[0], @@ -97,7 +103,8 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.ERROR, - lastDataDate: mockLastDataDates[FluidType.WATER], + firstDataDate: mockDataDates[FluidType.WATER], + lastDataDate: mockDataDates[FluidType.WATER], connection: { konnector: konnectorsData[1], account: accountsData[1], @@ -118,7 +125,8 @@ describe('FLuid service', () => { { fluidType: FluidType.GAS, status: FluidState.ERROR, - lastDataDate: mockLastDataDates[FluidType.GAS], + firstDataDate: mockDataDates[FluidType.GAS], + lastDataDate: mockDataDates[FluidType.GAS], connection: { konnector: konnectorsData[2], account: accountsData[2], @@ -149,28 +157,19 @@ describe('FLuid service', () => { .mockResolvedValueOnce(triggersData[1]) .mockResolvedValueOnce(triggersData[2]) mockFetchTriggerState.mockResolvedValue(triggerStateData) - mockFetchAllLastDateData.mockResolvedValue(mockLastDataDates) + mockFetchAllFirstDateData.mockResolvedValue(mockDataDates) + mockFetchAllLastDateData.mockResolvedValue(mockDataDates) const result: FluidStatus[] = await fluidService.getFluidStatus() expect(result).toEqual(mockResult) }) it('should return fluid status with NOT_CONNECTED status when no accounts', async () => { - const mockLastDataDates: (DateTime | null)[] = [ - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - ] const mockResult: FluidStatus[] = [ { fluidType: FluidType.ELECTRICITY, status: FluidState.NOT_CONNECTED, - lastDataDate: mockLastDataDates[FluidType.ELECTRICITY], + firstDataDate: mockDataDates[FluidType.ELECTRICITY], + lastDataDate: mockDataDates[FluidType.ELECTRICITY], connection: { konnector: konnectorsData[0], account: null, @@ -190,7 +189,8 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.NOT_CONNECTED, - lastDataDate: mockLastDataDates[FluidType.WATER], + firstDataDate: mockDataDates[FluidType.WATER], + lastDataDate: mockDataDates[FluidType.WATER], connection: { konnector: konnectorsData[1], account: null, @@ -211,7 +211,8 @@ describe('FLuid service', () => { { fluidType: FluidType.GAS, status: FluidState.NOT_CONNECTED, - lastDataDate: mockLastDataDates[FluidType.GAS], + firstDataDate: mockDataDates[FluidType.GAS], + lastDataDate: mockDataDates[FluidType.GAS], connection: { konnector: konnectorsData[2], account: null, @@ -242,28 +243,19 @@ describe('FLuid service', () => { .mockResolvedValueOnce(triggersData[1]) .mockResolvedValueOnce(triggersData[2]) mockFetchTriggerState.mockResolvedValue(triggerStateData) - mockFetchAllLastDateData.mockResolvedValue(mockLastDataDates) + mockFetchAllFirstDateData.mockResolvedValue(mockDataDates) + mockFetchAllLastDateData.mockResolvedValue(mockDataDates) const result: FluidStatus[] = await fluidService.getFluidStatus() expect(result).toEqual(mockResult) }) it('should return fluid status with KONNECTOR_NOT_FOUND status when no konnector', async () => { - const mockLastDataDates: (DateTime | null)[] = [ - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - ] const mockResult: FluidStatus[] = [ { fluidType: FluidType.ELECTRICITY, status: FluidState.KONNECTOR_NOT_FOUND, - lastDataDate: mockLastDataDates[FluidType.ELECTRICITY], + firstDataDate: mockDataDates[FluidType.ELECTRICITY], + lastDataDate: mockDataDates[FluidType.ELECTRICITY], connection: { konnector: null, account: accountsData[0], @@ -283,7 +275,8 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.KONNECTOR_NOT_FOUND, - lastDataDate: mockLastDataDates[FluidType.WATER], + firstDataDate: mockDataDates[FluidType.WATER], + lastDataDate: mockDataDates[FluidType.WATER], connection: { konnector: null, account: accountsData[1], @@ -304,7 +297,8 @@ describe('FLuid service', () => { { fluidType: FluidType.GAS, status: FluidState.KONNECTOR_NOT_FOUND, - lastDataDate: mockLastDataDates[FluidType.GAS], + firstDataDate: mockDataDates[FluidType.GAS], + lastDataDate: mockDataDates[FluidType.GAS], connection: { konnector: null, account: accountsData[2], @@ -335,28 +329,19 @@ describe('FLuid service', () => { .mockResolvedValueOnce(triggersData[1]) .mockResolvedValueOnce(triggersData[2]) mockFetchTriggerState.mockResolvedValue(triggerStateData) - mockFetchAllLastDateData.mockResolvedValue(mockLastDataDates) + mockFetchAllFirstDateData.mockResolvedValue(mockDataDates) + mockFetchAllLastDateData.mockResolvedValue(mockDataDates) const result: FluidStatus[] = await fluidService.getFluidStatus() expect(result).toEqual(mockResult) }) it('should return fluid status with NOT_CONNECTED status when no triggers', async () => { - const mockLastDataDates: (DateTime | null)[] = [ - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - ] const mockResult: FluidStatus[] = [ { fluidType: FluidType.ELECTRICITY, status: FluidState.NOT_CONNECTED, - lastDataDate: mockLastDataDates[FluidType.ELECTRICITY], + firstDataDate: mockDataDates[FluidType.ELECTRICITY], + lastDataDate: mockDataDates[FluidType.ELECTRICITY], connection: { konnector: konnectorsData[0], account: accountsData[0], @@ -376,7 +361,8 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.NOT_CONNECTED, - lastDataDate: mockLastDataDates[FluidType.WATER], + firstDataDate: mockDataDates[FluidType.WATER], + lastDataDate: mockDataDates[FluidType.WATER], connection: { konnector: konnectorsData[1], account: accountsData[1], @@ -397,7 +383,8 @@ describe('FLuid service', () => { { fluidType: FluidType.GAS, status: FluidState.NOT_CONNECTED, - lastDataDate: mockLastDataDates[FluidType.GAS], + firstDataDate: mockDataDates[FluidType.GAS], + lastDataDate: mockDataDates[FluidType.GAS], connection: { konnector: konnectorsData[2], account: accountsData[2], @@ -428,22 +415,20 @@ describe('FLuid service', () => { .mockResolvedValueOnce(null) .mockResolvedValueOnce(null) mockFetchTriggerState.mockResolvedValue(triggerStateData) - mockFetchAllLastDateData.mockResolvedValue(mockLastDataDates) + mockFetchAllFirstDateData.mockResolvedValue(mockDataDates) + mockFetchAllLastDateData.mockResolvedValue(mockDataDates) const result: FluidStatus[] = await fluidService.getFluidStatus() expect(result).toEqual(mockResult) }) - it('should return fluid status with null laste date for water and gaz', async () => { - const mockLastDataDates: (DateTime | null)[] = [ - DateTime.local(), - null, - null, - ] + it('should return fluid status with a null first/last date for water and gaz', async () => { + const _mockDataDates: (DateTime | null)[] = [DateTime.local(), null, null] const mockResult: FluidStatus[] = [ { fluidType: FluidType.ELECTRICITY, status: FluidState.ERROR, - lastDataDate: mockLastDataDates[FluidType.ELECTRICITY], + firstDataDate: _mockDataDates[FluidType.ELECTRICITY], + lastDataDate: _mockDataDates[FluidType.ELECTRICITY], connection: { konnector: konnectorsData[0], account: accountsData[0], @@ -463,6 +448,7 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.NOT_CONNECTED, + firstDataDate: null, lastDataDate: null, connection: { konnector: konnectorsData[1], @@ -484,6 +470,7 @@ describe('FLuid service', () => { { fluidType: FluidType.GAS, status: FluidState.NOT_CONNECTED, + firstDataDate: null, lastDataDate: null, connection: { konnector: konnectorsData[2], @@ -518,7 +505,8 @@ describe('FLuid service', () => { .mockResolvedValueOnce(triggerStateData) .mockResolvedValueOnce(null) .mockResolvedValueOnce(null) - mockFetchAllLastDateData.mockResolvedValue(mockLastDataDates) + mockFetchAllFirstDateData.mockResolvedValue(_mockDataDates) + mockFetchAllLastDateData.mockResolvedValue(_mockDataDates) const result: FluidStatus[] = await fluidService.getFluidStatus() expect(result).toEqual(mockResult) }) @@ -530,6 +518,9 @@ describe('FLuid service', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.DONE, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -561,6 +552,11 @@ describe('FLuid service', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.DONE, + firstDataDate: DateTime.local() + .minus({ day: 31 }) + .setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.local() .minus({ day: 1 }) .setZone('utc', { @@ -594,6 +590,9 @@ describe('FLuid service', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.NOT_CONNECTED, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -625,6 +624,9 @@ describe('FLuid service', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -660,6 +662,9 @@ describe('FLuid service', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.PARTNER_ISSUE, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -682,6 +687,9 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.ERROR_LOGIN_FAILED, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -712,6 +720,9 @@ describe('FLuid service', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.PARTNER_ISSUE, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -734,6 +745,9 @@ describe('FLuid service', () => { { fluidType: FluidType.WATER, status: FluidState.ERROR_LOGIN_FAILED, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), @@ -756,6 +770,9 @@ describe('FLuid service', () => { { fluidType: FluidType.GAS, status: FluidState.ERROR_LOGIN_FAILED, + firstDataDate: DateTime.fromISO('2019-01-01').setZone('utc', { + keepLocalTime: true, + }), lastDataDate: DateTime.fromISO('2020-01-01').setZone('utc', { keepLocalTime: true, }), diff --git a/src/services/fluid.service.ts b/src/services/fluid.service.ts index b23c492a36c4d4ad21d3d450bde449b357d86411..5569acc917698f01b0492261d7c48613f0a81f3c 100644 --- a/src/services/fluid.service.ts +++ b/src/services/fluid.service.ts @@ -148,6 +148,9 @@ export default class FluidService { waterTrigger ? triggerService.fetchTriggerState(waterTrigger) : null, gasTrigger ? triggerService.fetchTriggerState(gasTrigger) : null, ]) + const firstDataDates: (DateTime | null)[] = await consumptionService.fetchAllFirstDateData( + [FluidType.ELECTRICITY, FluidType.WATER, FluidType.GAS] + ) const lastDataDates: (DateTime | null)[] = await consumptionService.fetchAllLastDateData( [FluidType.ELECTRICITY, FluidType.WATER, FluidType.GAS] ) @@ -164,6 +167,7 @@ export default class FluidService { ) ? FluidState.PARTNER_ISSUE : this.parseFluidStatus(elecKonnector, elecStatus), + firstDataDate: firstDataDates[FluidType.ELECTRICITY], lastDataDate: lastDataDates[FluidType.ELECTRICITY], connection: { shouldLaunchKonnector: false, @@ -187,6 +191,7 @@ export default class FluidService { ) ? FluidState.PARTNER_ISSUE : this.parseFluidStatus(waterKonnector, waterStatus), + firstDataDate: firstDataDates[FluidType.WATER], lastDataDate: lastDataDates[FluidType.WATER], connection: { shouldLaunchKonnector: false, @@ -210,6 +215,7 @@ export default class FluidService { ) ? FluidState.PARTNER_ISSUE : this.parseFluidStatus(gasKonnector, gasStatus), + firstDataDate: firstDataDates[FluidType.GAS], lastDataDate: lastDataDates[FluidType.GAS], connection: { shouldLaunchKonnector: false, diff --git a/src/services/queryRunner.service.spec.ts b/src/services/queryRunner.service.spec.ts index 9c6d35c3d5dfc5145e22bb326abce8c07c0174ed..6a33687dd396695377165746c8cb4a495205ffad 100644 --- a/src/services/queryRunner.service.spec.ts +++ b/src/services/queryRunner.service.spec.ts @@ -9,6 +9,7 @@ import { loadYearData } from '../../tests/__mocks__/loadYearData.mock' import { loadMonthData } from '../../tests/__mocks__/loadMonthData.mock' import { loadDayData } from '../../tests/__mocks__/loadDayData.mock' import { loadMinuteData } from '../../tests/__mocks__/loadMinuteData.mock' +import { DataloadState } from 'enum/dataload.enum' describe('queryRunner service', () => { const queryRunner = new QueryRunner(mockClient) @@ -29,6 +30,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -36,6 +38,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -43,6 +46,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -76,6 +80,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -83,6 +88,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -90,6 +96,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -124,6 +131,7 @@ describe('queryRunner service', () => { }), value: 25.25, price: 0.12, + state: DataloadState.VALID, valueDetail: null, }, { @@ -132,6 +140,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -140,6 +149,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -148,6 +158,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -182,6 +193,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -190,6 +202,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -198,6 +211,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -231,6 +245,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 4.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -238,6 +253,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 1.33, + state: DataloadState.VALID, valueDetail: null, }, { @@ -245,6 +261,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 3.22, + state: DataloadState.VALID, valueDetail: null, }, { @@ -252,6 +269,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 7.82, + state: DataloadState.VALID, valueDetail: null, }, { @@ -259,6 +277,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 1.23, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -295,6 +314,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -302,6 +322,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -309,6 +330,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -342,6 +364,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -349,6 +372,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -356,6 +380,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -390,6 +415,7 @@ describe('queryRunner service', () => { }), value: 25.25, price: 0.12, + state: DataloadState.VALID, valueDetail: null, }, { @@ -398,6 +424,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -406,6 +433,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -414,6 +442,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -448,6 +477,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -456,6 +486,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -464,6 +495,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -498,6 +530,7 @@ describe('queryRunner service', () => { }), value: 25.25, price: 0.12, + state: DataloadState.VALID, valueDetail: null, }, { @@ -506,6 +539,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -514,6 +548,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -522,6 +557,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -555,6 +591,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -562,6 +599,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -569,6 +607,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -602,6 +641,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -609,6 +649,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -616,6 +657,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -650,6 +692,7 @@ describe('queryRunner service', () => { }), price: 0.12, value: 25.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -658,6 +701,7 @@ describe('queryRunner service', () => { }), price: 0.14, value: 20.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -666,6 +710,7 @@ describe('queryRunner service', () => { }), price: 0.18, value: 30.33, + state: DataloadState.VALID, valueDetail: null, }, { @@ -674,6 +719,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -708,6 +754,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -716,6 +763,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -724,6 +772,7 @@ describe('queryRunner service', () => { }), price: 0.16, value: 1.22, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -758,6 +807,7 @@ describe('queryRunner service', () => { }), price: 0.12, value: 25.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -766,6 +816,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -774,6 +825,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -782,6 +834,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -815,6 +868,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -822,6 +876,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -855,6 +910,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 125.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -862,6 +918,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 220.5, + state: DataloadState.VALID, valueDetail: null, }, { @@ -869,6 +926,7 @@ describe('queryRunner service', () => { zone: 'utc', }), value: 130.33, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -903,6 +961,7 @@ describe('queryRunner service', () => { }), price: 0.12, value: 25.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -911,6 +970,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -919,6 +979,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -927,6 +988,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -961,6 +1023,7 @@ describe('queryRunner service', () => { }), price: 0.12, value: 25.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -969,6 +1032,7 @@ describe('queryRunner service', () => { }), value: 20.5, price: 0.14, + state: DataloadState.VALID, valueDetail: null, }, { @@ -977,6 +1041,7 @@ describe('queryRunner service', () => { }), value: 30.33, price: 0.18, + state: DataloadState.VALID, valueDetail: null, }, { @@ -985,6 +1050,7 @@ describe('queryRunner service', () => { }), value: 1.22, price: 0.16, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -1073,7 +1139,10 @@ describe('queryRunner service', () => { skip: 0, } mockClient.query.mockResolvedValue(mockQueryResult) - const result: number | null = await queryRunner.fetchFluidMaxData( + const result: + | number + | Dataload + | null = await queryRunner.fetchFluidMaxData( mockTimePeriod, TimeStep.DAY, FluidType.ELECTRICITY @@ -1105,7 +1174,10 @@ describe('queryRunner service', () => { mockClient.query .mockResolvedValueOnce(mockQueryResult) .mockResolvedValueOnce(mockQueryResult2) - const result: number | null = await queryRunner.fetchFluidMaxData( + const result: + | number + | Dataload + | null = await queryRunner.fetchFluidMaxData( mockTimePeriod, TimeStep.HALF_AN_HOUR, FluidType.ELECTRICITY @@ -1137,7 +1209,10 @@ describe('queryRunner service', () => { mockClient.query .mockResolvedValueOnce(mockQueryResult) .mockResolvedValueOnce(mockQueryResult2) - const result: number | null = await queryRunner.fetchFluidMaxData( + const result: + | number + | Dataload + | null = await queryRunner.fetchFluidMaxData( mockTimePeriod, TimeStep.HALF_AN_HOUR, FluidType.ELECTRICITY @@ -1155,7 +1230,10 @@ describe('queryRunner service', () => { }), } mockClient.query.mockRejectedValue(new Error()) - const result: number | null = await queryRunner.fetchFluidMaxData( + const result: + | number + | Dataload + | null = await queryRunner.fetchFluidMaxData( mockTimePeriod, TimeStep.DAY, FluidType.ELECTRICITY @@ -1172,7 +1250,10 @@ describe('queryRunner service', () => { zone: 'utc', }), } - const result: number | null = await queryRunner.fetchFluidMaxData( + const result: + | number + | Dataload + | null = await queryRunner.fetchFluidMaxData( mockTimePeriod, TimeStep.DAY, 99 @@ -1189,7 +1270,10 @@ describe('queryRunner service', () => { zone: 'utc', }), } - const result: number | null = await queryRunner.fetchFluidMaxData( + const result: + | number + | Dataload + | null = await queryRunner.fetchFluidMaxData( mockTimePeriod, 99, FluidType.ELECTRICITY @@ -1198,6 +1282,37 @@ describe('queryRunner service', () => { }) }) + describe('getFirstDateData method', () => { + it('should return the first load for elec fluid', async () => { + const firstMockData = [...loadDayData] + firstMockData.sort( + (a, b) => + (a.year - b.year) * 100 + (a.month - b.month) * 10 + (a.day - b.day) + ) + const mockQueryResult: QueryResult<DataloadEntity[]> = { + data: [firstMockData[0]], + bookmark: '', + next: false, + skip: 0, + } + mockClient.query.mockResolvedValue(mockQueryResult) + const result = await queryRunner.getFirstDateData(FluidType.ELECTRICITY) + expect(result).toEqual( + DateTime.local( + loadDayData[0].year, + loadDayData[0].month, + loadDayData[0].day + ).setZone('utc', { keepLocalTime: true }) + ) + }) + + it('should return null when fetch data failed', async () => { + mockClient.query.mockRejectedValue(new Error()) + const result = await queryRunner.getFirstDateData(FluidType.ELECTRICITY) + expect(result).toBeNull() + }) + }) + describe('getLastDateData method', () => { it('should return the last load for elec fluid', async () => { const lastMockData = [...loadDayData] diff --git a/src/services/queryRunner.service.ts b/src/services/queryRunner.service.ts index f9733ef3d9057b040d7c3347483a23ffc9f866a2..dad1cf33881518487332439aef9b5eba76da18ce 100644 --- a/src/services/queryRunner.service.ts +++ b/src/services/queryRunner.service.ts @@ -19,6 +19,7 @@ import { TimeStep } from 'enum/timeStep.enum' import { Dataload, TimePeriod } from 'models' import { QueryResult } from 'cozy-client/types/types' import log from 'utils/logger' +import { DataloadState } from 'enum/dataload.enum' export default class QueryRunner { // TODO to be clean up @@ -87,15 +88,26 @@ export default class QueryRunner { if (timeStep === TimeStep.HALF_AN_HOUR) { return Q(doctype) .where(this.getPredicate(maxTimePeriod, TimeStep.HALF_AN_HOUR)) + .indexFields(['load']) .limitBy(1) .sortBy([{ load: 'desc' }]) } return Q(doctype) .where(this.getPredicate(maxTimePeriod, timeStep)) + .indexFields(['load']) .limitBy(limit) .sortBy([{ load: 'desc' }]) } + private buildFirstDateQuery(fluidType: FluidType, limit: number) { + const doctype = this.getRelevantDoctype(fluidType, TimeStep.DAY) + return Q(doctype) + .where({}) + .indexFields(['year', 'month', 'day']) + .sortBy([{ year: 'asc' }, { month: 'asc' }, { day: 'asc' }]) + .limitBy(limit) + } + private buildLastDateQuery(fluidType: FluidType, limit: number) { const doctype = this.getRelevantDoctype(fluidType, TimeStep.DAY) return Q(doctype) @@ -146,10 +158,10 @@ export default class QueryRunner { keepLocalTime: true, }), value: entry.load, + state: DataloadState.VALID, price: entry.price, valueDetail: null, })) - return mappedResult } @@ -401,6 +413,30 @@ export default class QueryRunner { return null } + public async getFirstDateData( + fluidType: FluidType + ): Promise<DateTime | null> { + const query: QueryDefinition = this.buildFirstDateQuery(fluidType, 1) + const result = await this.fetchData(query) + if ( + result && + result.data && + result.data[0] && + result.data[0].year && + result.data[0].month && + result.data[0].day + ) { + return DateTime.local( + result.data[0].year, + result.data[0].month, + result.data[0].day + ).setZone('utc', { + keepLocalTime: true, + }) + } + return null + } + public async getLastDateData(fluidType: FluidType): Promise<DateTime | null> { const query: QueryDefinition = this.buildLastDateQuery(fluidType, 1) const result = await this.fetchData(query) diff --git a/src/services/quiz.service.spec.ts b/src/services/quiz.service.spec.ts index a17871364287e3c0be7bd3cb7b9c81d031b3906d..bbaa8a1a3fa6372c647b2921cf6c13313cebfc03 100644 --- a/src/services/quiz.service.spec.ts +++ b/src/services/quiz.service.spec.ts @@ -423,6 +423,7 @@ describe('Quiz service', () => { TimeStep.MONTH, [FluidType.ELECTRICITY], undefined, + undefined, true ) expect(result).toEqual(expected) diff --git a/src/services/quiz.service.ts b/src/services/quiz.service.ts index 1749a5edcd23c9aaf72c063d6404d0be3ee4e6aa..831ab75352afb725269863e231573faf4341bfce 100644 --- a/src/services/quiz.service.ts +++ b/src/services/quiz.service.ts @@ -521,6 +521,7 @@ export default class QuizService { timeStep, fluidType, undefined, + undefined, !singleFluid ) let average = 0 diff --git a/src/store/global/global.reducer.spec.ts b/src/store/global/global.reducer.spec.ts index 600ac0f12af64ba245ac6adbec3765d88d63427e..23cb3d1d1343eef67f175990ec2a368c0bfa059c 100644 --- a/src/store/global/global.reducer.spec.ts +++ b/src/store/global/global.reducer.spec.ts @@ -16,7 +16,7 @@ import { accountsData } from '../../../tests/__mocks__/accountsData.mock' import { triggersData } from '../../../tests/__mocks__/triggersData.mock' import { mockInitialGlobalState } from '../../../tests/__mocks__/store' -const mockLastDataDates: (DateTime | null)[] = [ +const mockDataDates: (DateTime | null)[] = [ DateTime.local().setZone('utc', { keepLocalTime: true, }), @@ -130,7 +130,8 @@ describe('global reducer', () => { { fluidType: FluidType.ELECTRICITY, status: FluidState.ERROR, - lastDataDate: mockLastDataDates[FluidType.ELECTRICITY], + firstDataDate: mockDataDates[FluidType.ELECTRICITY], + lastDataDate: mockDataDates[FluidType.ELECTRICITY], connection: { konnector: konnectorsData[0], account: accountsData[0], @@ -150,7 +151,8 @@ describe('global reducer', () => { { fluidType: FluidType.WATER, status: FluidState.ERROR, - lastDataDate: mockLastDataDates[FluidType.WATER], + firstDataDate: mockDataDates[FluidType.WATER], + lastDataDate: mockDataDates[FluidType.WATER], connection: { konnector: konnectorsData[1], account: accountsData[1], @@ -171,7 +173,8 @@ describe('global reducer', () => { { fluidType: FluidType.GAS, status: FluidState.KONNECTOR_NOT_FOUND, - lastDataDate: mockLastDataDates[FluidType.GAS], + firstDataDate: mockDataDates[FluidType.GAS], + lastDataDate: mockDataDates[FluidType.GAS], connection: { konnector: null, account: accountsData[2], diff --git a/src/store/global/global.reducer.ts b/src/store/global/global.reducer.ts index 13dad1217de374bd62115448056ffb4681f2b4f5..123d0b524957a7d1db4fe633c9feb21c439a372b 100644 --- a/src/store/global/global.reducer.ts +++ b/src/store/global/global.reducer.ts @@ -39,6 +39,7 @@ const initialState: GlobalState = { { fluidType: FluidType.ELECTRICITY, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, @@ -59,6 +60,7 @@ const initialState: GlobalState = { { fluidType: FluidType.WATER, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, @@ -79,6 +81,7 @@ const initialState: GlobalState = { { fluidType: FluidType.GAS, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, diff --git a/src/targets/services/enedisHalfHourMonthlyAnalysis.ts b/src/targets/services/enedisHalfHourMonthlyAnalysis.ts index 3c8e0e615567adc4001fddac2211f2817184cac4..e7f269b51af3d40f51cf2d904da580f68289da5d 100644 --- a/src/targets/services/enedisHalfHourMonthlyAnalysis.ts +++ b/src/targets/services/enedisHalfHourMonthlyAnalysis.ts @@ -156,8 +156,8 @@ const getEnedisMonthAnalysisData = async ( year: year, }).daysInMonth monthlyAveragesLoads.minimumLoad = getMinMonthlyLoad( - weekValuesArray, weekEndValuesArray, + weekValuesArray, numberofDaysInMonth ) const arrAvg = (arr: number[]) => diff --git a/tests/__mocks__/datachartData.mock.ts b/tests/__mocks__/datachartData.mock.ts index de82121d074b8bc6fd5730a408eee99e68142ffc..4fd3ddd29cfa1692333444f391143f65949d0aaf 100644 --- a/tests/__mocks__/datachartData.mock.ts +++ b/tests/__mocks__/datachartData.mock.ts @@ -1,5 +1,6 @@ import { Datachart, Dataload } from 'models' import { DateTime } from 'luxon' +import { DataloadState } from 'enum/dataload.enum' export const graphData: Datachart = { actualData: [ @@ -8,20 +9,31 @@ export const graphData: Datachart = { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-02T00:00:00.000Z', { zone: 'utc', }), value: 61.65554999999999, - valueDetail: [40.21918999999999, 0.8064649999999999, 20.629894999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 40.21918999999999, state: DataloadState.VALID }, + { value: 0.8064649999999999, state: DataloadState.VALID }, + { value: 20.629894999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-03T00:00:00.000Z', { zone: 'utc', }), value: -1, + state: DataloadState.EMPTY, valueDetail: null, }, ], @@ -31,20 +43,31 @@ export const graphData: Datachart = { zone: 'utc', }), value: 54.090509999999995, - valueDetail: [35.284358, 0.707513, 18.098639], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 35.284358, state: DataloadState.VALID }, + { value: 0.707513, state: DataloadState.VALID }, + { value: 18.098639, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-09-02T00:00:00.000Z', { zone: 'utc', }), value: 56.57427, - valueDetail: [36.904565999999996, 0.740001, 18.929703], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 36.904565999999996, state: DataloadState.VALID }, + { value: 0.740001, state: DataloadState.VALID }, + { value: 18.929703, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-09-03T00:00:00.000Z', { zone: 'utc', }), value: -1, + state: DataloadState.EMPTY, valueDetail: null, }, ], @@ -54,14 +77,28 @@ export const baseDataLoad: Dataload = { zone: 'utc', }), value: 12, + state: DataloadState.VALID, valueDetail: null, } +export const baseMultiFluidDataLoad: Dataload = { + date: DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + }), + value: 12, + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 6, state: DataloadState.VALID }, + { value: 4, state: DataloadState.VALID }, + { value: 2, state: DataloadState.VALID }, + ], +} export const dataLoadArray: Dataload[] = [ { date: DateTime.fromISO('2021-09-23T00:00:00.000Z', { zone: 'utc', }), value: 12, + state: DataloadState.VALID, valueDetail: null, }, { @@ -69,6 +106,7 @@ export const dataLoadArray: Dataload[] = [ zone: 'utc', }), value: 12, + state: DataloadState.VALID, valueDetail: null, }, { @@ -76,6 +114,7 @@ export const dataLoadArray: Dataload[] = [ zone: 'utc', }), value: 12, + state: DataloadState.VALID, valueDetail: null, }, { @@ -83,6 +122,7 @@ export const dataLoadArray: Dataload[] = [ zone: 'utc', }), value: 12, + state: DataloadState.VALID, valueDetail: null, }, ] @@ -94,20 +134,31 @@ export const graphMonthData: Datachart = { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { zone: 'utc', }), value: 61.65554999999999, - valueDetail: [40.21918999999999, 0.8064649999999999, 20.629894999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 40.21918999999999, state: DataloadState.VALID }, + { value: 0.8064649999999999, state: DataloadState.VALID }, + { value: 20.629894999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-11-01T00:00:00.000Z', { zone: 'utc', }), value: -1, + state: DataloadState.AGGREGATED_EMPTY, valueDetail: null, }, ], @@ -117,20 +168,31 @@ export const graphMonthData: Datachart = { zone: 'utc', }), value: 54.090509999999995, - valueDetail: [35.284358, 0.707513, 18.098639], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 35.284358, state: DataloadState.VALID }, + { value: 0.707513, state: DataloadState.VALID }, + { value: 18.098639, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-09-02T00:00:00.000Z', { zone: 'utc', }), value: 56.57427, - valueDetail: [36.904565999999996, 0.740001, 18.929703], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 36.904565999999996, state: DataloadState.VALID }, + { value: 0.740001, state: DataloadState.VALID }, + { value: 18.929703, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-09-03T00:00:00.000Z', { zone: 'utc', }), value: -1, + state: DataloadState.AGGREGATED_EMPTY, valueDetail: null, }, ], @@ -143,238 +205,408 @@ export const fullMonthGraphData: Datachart = { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-02T00:00:00.000Z', { zone: 'utc', }), value: 61.65554999999999, - valueDetail: [40.21918999999999, 0.8064649999999999, 20.629894999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 40.21918999999999, state: DataloadState.VALID }, + { value: 0.8064649999999999, state: DataloadState.VALID }, + { value: 20.629894999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-03T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-04T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-05T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-06T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-04T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-05T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-06T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-07T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-08T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-09T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-10T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-11T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-12T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-13T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-14T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-15T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-16T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-17T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-18T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-19T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-20T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-21T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-22T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-23T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-24T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-25T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-26T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-27T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-28T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-29T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-30T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-31T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, ], comparisonData: null, @@ -387,42 +619,72 @@ export const fullGraphData: Datachart = { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-02T00:00:00.000Z', { zone: 'utc', }), value: 61.65554999999999, - valueDetail: [40.21918999999999, 0.8064649999999999, 20.629894999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 40.21918999999999, state: DataloadState.VALID }, + { value: 0.8064649999999999, state: DataloadState.VALID }, + { value: 20.629894999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-03T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-04T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-05T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-10-06T00:00:00.000Z', { zone: 'utc', }), value: 69.18029999999999, - valueDetail: [45.127739999999996, 0.9048899999999999, 23.147669999999998], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 45.127739999999996, state: DataloadState.VALID }, + { value: 0.9048899999999999, state: DataloadState.VALID }, + { value: 23.147669999999998, state: DataloadState.VALID }, + ], }, ], comparisonData: [ @@ -431,20 +693,31 @@ export const fullGraphData: Datachart = { zone: 'utc', }), value: 54.090509999999995, - valueDetail: [35.284358, 0.707513, 18.098639], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 35.284358, state: DataloadState.VALID }, + { value: 0.707513, state: DataloadState.VALID }, + { value: 18.098639, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-09-02T00:00:00.000Z', { zone: 'utc', }), value: 56.57427, - valueDetail: [36.904565999999996, 0.740001, 18.929703], + state: DataloadState.AGGREGATED_VALID, + valueDetail: [ + { value: 36.904565999999996, state: DataloadState.VALID }, + { value: 0.740001, state: DataloadState.VALID }, + { value: 18.929703, state: DataloadState.VALID }, + ], }, { date: DateTime.fromISO('2020-09-03T00:00:00.000Z', { zone: 'utc', }), value: -1, + state: DataloadState.AGGREGATED_EMPTY, valueDetail: null, }, ], diff --git a/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts b/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts index e58cf19889ade7ac6ebc28ed9ad94a1a3e8e5564..76c474dfb3341bf1f0dda0a31dfcba588dbe0822 100644 --- a/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts +++ b/tests/__mocks__/enedisMonthlyAnalysisData.mock.ts @@ -1,3 +1,4 @@ +import { DataloadState } from 'enum/dataload.enum' import { DateTime } from 'luxon' import { AggregatedEnedisMonthlyDataloads, @@ -56,6 +57,7 @@ export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = { zone: 'utc', }), value: 0.35, + state: DataloadState.VALID, valueDetail: null, }, { @@ -63,6 +65,7 @@ export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = { zone: 'utc', }), value: 0.34, + state: DataloadState.VALID, valueDetail: null, }, { @@ -70,6 +73,7 @@ export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = { zone: 'utc', }), value: 0.33, + state: DataloadState.VALID, valueDetail: null, }, ], @@ -79,6 +83,7 @@ export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = { zone: 'utc', }), value: 0.25, + state: DataloadState.VALID, valueDetail: null, }, { @@ -86,6 +91,7 @@ export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = { zone: 'utc', }), value: 0.24, + state: DataloadState.VALID, valueDetail: null, }, { @@ -93,6 +99,7 @@ export const mockDataLoadEnedisAnalysis: AggregatedEnedisMonthlyDataloads = { zone: 'utc', }), value: 0.23, + state: DataloadState.VALID, valueDetail: null, }, ], diff --git a/tests/__mocks__/fluidStatusData.mock.ts b/tests/__mocks__/fluidStatusData.mock.ts index 82efc473ab71183eafc7339fa546520e08a522f5..e9495d91ad3d8e71ce43308f0098bb7512ec301b 100644 --- a/tests/__mocks__/fluidStatusData.mock.ts +++ b/tests/__mocks__/fluidStatusData.mock.ts @@ -8,6 +8,9 @@ export const fluidStatusData: FluidStatus[] = [ { fluidType: 0, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: DateTime.fromISO('2019-09-01T00:00:00.000Z', { + zone: 'utc', + }), lastDataDate: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), @@ -30,7 +33,9 @@ export const fluidStatusData: FluidStatus[] = [ { fluidType: 1, status: FluidState.KONNECTOR_NOT_FOUND, - + firstDataDate: DateTime.fromISO('2019-09-01T00:00:00.000Z', { + zone: 'utc', + }), lastDataDate: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), @@ -53,6 +58,9 @@ export const fluidStatusData: FluidStatus[] = [ { fluidType: 2, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: DateTime.fromISO('2019-09-01T00:00:00.000Z', { + zone: 'utc', + }), lastDataDate: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), @@ -90,6 +98,9 @@ export const fluidStatusConnectedData: FluidStatus[] = [ { fluidType: 0, status: FluidState.DONE, + firstDataDate: DateTime.fromISO('2019-09-01T00:00:00.000Z', { + zone: 'utc', + }), lastDataDate: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), @@ -112,6 +123,9 @@ export const fluidStatusConnectedData: FluidStatus[] = [ { fluidType: 1, status: FluidState.DONE, + firstDataDate: DateTime.fromISO('2019-09-01T00:00:00.000Z', { + zone: 'utc', + }), lastDataDate: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), @@ -134,6 +148,9 @@ export const fluidStatusConnectedData: FluidStatus[] = [ { fluidType: 2, status: FluidState.DONE, + firstDataDate: DateTime.fromISO('2019-09-01T00:00:00.000Z', { + zone: 'utc', + }), lastDataDate: DateTime.fromISO('2020-09-01T00:00:00.000Z', { zone: 'utc', }), diff --git a/tests/__mocks__/globalStateData.mock.ts b/tests/__mocks__/globalStateData.mock.ts index 7315927e701cbb3470a7c838e460510025297aeb..056616428ab5ed28c432c353b5db60b90430ee76 100644 --- a/tests/__mocks__/globalStateData.mock.ts +++ b/tests/__mocks__/globalStateData.mock.ts @@ -21,6 +21,7 @@ export const globalStateData: GlobalState = { { fluidType: FluidType.ELECTRICITY, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, @@ -41,6 +42,7 @@ export const globalStateData: GlobalState = { { fluidType: FluidType.WATER, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, @@ -61,6 +63,7 @@ export const globalStateData: GlobalState = { { fluidType: FluidType.GAS, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, diff --git a/tests/__mocks__/store.ts b/tests/__mocks__/store.ts index 38527720544dd537c7c879b6229fc93dff018f70..aa11783d02126975057714e26e12acd0eda6dead 100644 --- a/tests/__mocks__/store.ts +++ b/tests/__mocks__/store.ts @@ -42,6 +42,7 @@ export const mockInitialGlobalState: GlobalState = { { fluidType: FluidType.ELECTRICITY, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, @@ -62,6 +63,7 @@ export const mockInitialGlobalState: GlobalState = { { fluidType: FluidType.WATER, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false, @@ -82,6 +84,7 @@ export const mockInitialGlobalState: GlobalState = { { fluidType: FluidType.GAS, status: FluidState.KONNECTOR_NOT_FOUND, + firstDataDate: null, lastDataDate: null, connection: { shouldLaunchKonnector: false,