From ae077558c0369a4f2b7326f12a9ea9382ad39358 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:15:16 +0100 Subject: [PATCH] =?UTF-8?q?feat:=C2=A0insights=20project=20filtering=20hoo?= =?UTF-8?q?ks=20with=20tests=20(#6403)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hooks to filter and aggregate insights data on frontend --- .../hooks/useFilteredFlagsSummary.test.ts | 122 ++++++++++++++++++ .../hooks/useFilteredFlagsSummary.ts | 46 +++++++ .../hooks/useFilteredTrends.test.ts | 107 +++++++++++++++ .../hooks/useFilteredTrends.ts | 22 ++++ 4 files changed, 297 insertions(+) create mode 100644 frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.test.ts create mode 100644 frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.ts create mode 100644 frontend/src/component/executiveDashboard/hooks/useFilteredTrends.test.ts create mode 100644 frontend/src/component/executiveDashboard/hooks/useFilteredTrends.ts diff --git a/frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.test.ts b/frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.test.ts new file mode 100644 index 0000000000..9b9ba85ead --- /dev/null +++ b/frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.test.ts @@ -0,0 +1,122 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useFilteredFlagsSummary } from './useFilteredFlagsSummary'; + +describe('useFilteredFlagTrends', () => { + it('should summarize only last week of project flag trends', () => { + const { result } = renderHook(() => + useFilteredFlagsSummary([ + { + week: '2024-01', + project: 'project1', + total: 1, + active: 1, + stale: 0, + potentiallyStale: 0, + users: 1, + date: '', + }, + { + week: '2024-01', + project: 'project2', + total: 2, + active: 2, + stale: 0, + potentiallyStale: 0, + users: 2, + date: '', + }, + { + week: '2024-02', + project: 'project1', + total: 4, + active: 3, + stale: 0, + potentiallyStale: 1, + users: 1, + date: '', + }, + { + week: '2024-02', + project: 'project3', + total: 10, + active: 8, + stale: 2, + potentiallyStale: 0, + users: 3, + date: '', + }, + ]), + ); + + expect(result.current).toEqual({ + total: 14, + active: 11, + stale: 2, + potentiallyStale: 1, + averageUsers: '2.00', + averageHealth: '79', + }); + }); + + it('should work with project with zero users', () => { + const { result, rerender } = renderHook(() => + useFilteredFlagsSummary([ + { + week: '2024-01', + project: 'project1', + total: 5, + active: 5, + stale: 0, + potentiallyStale: 0, + users: 0, + date: '', + }, + ]), + ); + + expect(result.current).toEqual({ + total: 5, + active: 5, + stale: 0, + potentiallyStale: 0, + averageUsers: '0.00', + averageHealth: '100', + }); + }); + + it('should work with projects where some have with zero users', () => { + const { result } = renderHook(() => + useFilteredFlagsSummary([ + { + week: '2024-01', + project: 'project1', + total: 5, + active: 5, + stale: 0, + potentiallyStale: 0, + users: 0, + date: '', + }, + { + week: '2024-01', + project: 'project2', + total: 5, + active: 5, + stale: 0, + potentiallyStale: 0, + users: 2, + date: '', + }, + ]), + ); + + expect(result.current).toEqual({ + total: 10, + active: 10, + stale: 0, + potentiallyStale: 0, + averageUsers: '1.00', + averageHealth: '100', + }); + }); +}); diff --git a/frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.ts b/frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.ts new file mode 100644 index 0000000000..10676ac9d4 --- /dev/null +++ b/frontend/src/component/executiveDashboard/hooks/useFilteredFlagsSummary.ts @@ -0,0 +1,46 @@ +import { useMemo } from 'react'; +import { ExecutiveSummarySchemaProjectFlagTrendsItem } from 'openapi'; + +export const useFilteredFlagsSummary = ( + filteredProjectFlagTrends: ExecutiveSummarySchemaProjectFlagTrendsItem[], +) => + useMemo(() => { + const lastWeekId = filteredProjectFlagTrends.reduce((prev, current) => { + if (current.week > prev) return current.week; + return prev; + }, ''); + + const lastWeekSummary = filteredProjectFlagTrends.filter( + (summary) => summary.week === lastWeekId, + ); + + const averageUsers = ( + lastWeekSummary.reduce( + (acc, current) => acc + (current.users || 0), + 0, + ) / lastWeekSummary.length + ).toFixed(2); + + const sum = lastWeekSummary.reduce( + (acc, current) => ({ + total: acc.total + current.total, + active: acc.active + current.active, + stale: acc.stale + current.stale, + potentiallyStale: + acc.potentiallyStale + current.potentiallyStale, + averageUsers, + }), + { + total: 0, + active: 0, + stale: 0, + potentiallyStale: 0, + }, + ); + + return { + ...sum, + averageUsers, + averageHealth: ((sum.active / (sum.total || 1)) * 100).toFixed(0), + }; + }, [filteredProjectFlagTrends]); diff --git a/frontend/src/component/executiveDashboard/hooks/useFilteredTrends.test.ts b/frontend/src/component/executiveDashboard/hooks/useFilteredTrends.test.ts new file mode 100644 index 0000000000..197fa78ff6 --- /dev/null +++ b/frontend/src/component/executiveDashboard/hooks/useFilteredTrends.test.ts @@ -0,0 +1,107 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useFilteredTrends } from './useFilteredTrends'; + +const mockProjectFlagTrends = [ + { + week: '2024-01', + project: 'project1', + }, + { + week: '2024-01', + project: 'project2', + }, + { + week: '2024-02', + project: 'project1', + }, + { + week: '2024-02', + project: 'project3', + }, +]; + +describe('useFilteredFlagTrends', () => { + it('should return all project flag trends when all projects option is selected', () => { + const projects = ['*']; + const { result } = renderHook(() => + useFilteredTrends(mockProjectFlagTrends, projects), + ); + expect(result.current).toEqual(mockProjectFlagTrends); + }); + + it('should return all project flag trends project selection is empty', () => { + const projects: string[] = []; + const { result } = renderHook(() => + useFilteredTrends(mockProjectFlagTrends, projects), + ); + expect(result.current).toEqual(mockProjectFlagTrends); + }); + + it('should return filtered project when specific project is selected', () => { + const projects = ['project1']; + const { result } = renderHook(() => + useFilteredTrends(mockProjectFlagTrends, projects), + ); + expect(result.current).toEqual([ + { + week: '2024-01', + project: 'project1', + }, + { + week: '2024-02', + project: 'project1', + }, + ]); + }); + + it('should return filtered project flag trends when specific projects are selected', () => { + const projects = ['project1', 'project2']; + const { result } = renderHook(() => + useFilteredTrends(mockProjectFlagTrends, projects), + ); + expect(result.current).toEqual([ + { + week: '2024-01', + project: 'project1', + }, + { + week: '2024-01', + project: 'project2', + }, + { + week: '2024-02', + project: 'project1', + }, + ]); + }); + + it('should re-render if input has changed', () => { + const projects = ['project1']; + const { result, rerender } = renderHook( + ({ input, projects }) => useFilteredTrends(input, projects), + { + initialProps: { + input: mockProjectFlagTrends, + projects, + }, + }, + ); + + rerender({ + input: [ + { + week: '2024-01', + project: 'project1', + }, + ], + projects, + }); + + expect(result.current).toEqual([ + { + week: '2024-01', + project: 'project1', + }, + ]); + }); +}); diff --git a/frontend/src/component/executiveDashboard/hooks/useFilteredTrends.ts b/frontend/src/component/executiveDashboard/hooks/useFilteredTrends.ts new file mode 100644 index 0000000000..cd334b3db8 --- /dev/null +++ b/frontend/src/component/executiveDashboard/hooks/useFilteredTrends.ts @@ -0,0 +1,22 @@ +import { useMemo } from 'react'; +import { allOption } from '../ProjectSelect/ProjectSelect'; + +export const useFilteredTrends = < + T extends { + project: string; + }, +>( + input: T[], + projects: string[], +) => + useMemo(() => { + if (projects.length < 1 || projects[0] === allOption.id) { + return input; + } + + const output = input.filter((trend) => + projects.includes(trend.project), + ) as T[]; + + return output; + }, [input, projects]);