diff --git a/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.test.tsx b/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.test.tsx index 7b335fccdc..773ddd36ed 100644 --- a/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.test.tsx +++ b/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.test.tsx @@ -1,4 +1,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + QueryClientProvider as QueryClientProviderV5, + QueryClient as QueryClientV5, +} from '@tanstack/react-queryV5' import { render, screen } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' @@ -77,15 +81,20 @@ const server = setupServer() const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, suspense: true } }, }) +const queryClientV5 = new QueryClientV5({ + defaultOptions: { queries: { retry: false } }, +}) const wrapper: React.FC = ({ children }) => ( - - - - {children} - - - + + + + + {children} + + + + ) beforeAll(() => { @@ -94,6 +103,7 @@ beforeAll(() => { afterEach(() => { queryClient.clear() + queryClientV5.clear() server.resetHandlers() }) diff --git a/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.tsx b/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.tsx index 8fc07267b9..a9a0e85f93 100644 --- a/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.tsx +++ b/src/pages/RepoPage/CoverageTab/OverviewTab/Summary/CoverageTrend/CoverageTrend.tsx @@ -1,3 +1,4 @@ +import { keepPreviousData } from '@tanstack/react-queryV5' import { useParams } from 'react-router-dom' import { useBranches } from 'services/branches' @@ -30,13 +31,9 @@ function CoverageTrend() { defaultBranch: overview?.defaultBranch ?? '', }) - const { data, isLoading } = useRepoCoverageTimeseries({ + const { data, isPending, isSuccess } = useRepoCoverageTimeseries({ branch: selection?.name, - options: { - enabled: !!selection?.name, - suspense: false, - keepPreviousData: true, - }, + options: { enabled: !!selection?.name, placeholderData: keepPreviousData }, }) return ( @@ -44,9 +41,9 @@ function CoverageTrend() {
{/* ^ CSS doesn't want to render like the others without a p tag in the dom. */} - {isLoading ? ( + {isPending ? ( - ) : data?.measurements?.length > 0 ? ( + ) : isSuccess && data.measurements.length > 0 ? ( ) : (

diff --git a/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.test.tsx b/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.test.tsx index 5a8181b865..bc2be730d1 100644 --- a/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.test.tsx +++ b/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.test.tsx @@ -1,4 +1,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + QueryClientProvider as QueryClientProviderV5, + QueryClient as QueryClientV5, +} from '@tanstack/react-queryV5' import { renderHook, waitFor } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' @@ -58,14 +62,20 @@ const server = setupServer() const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }) +const queryClientV5 = new QueryClientV5({ + defaultOptions: { queries: { retry: false } }, +}) + const wrapper = (searchParams = ''): React.FC => ({ children }) => ( - - - {children} - - + + + + {children} + + + ) beforeAll(() => { @@ -74,6 +84,7 @@ beforeAll(() => { afterEach(() => { queryClient.clear() + queryClientV5.clear() server.resetHandlers() }) @@ -89,7 +100,9 @@ describe('useRepoCoverageTimeseries', () => { const config = vi.fn() function setup( - { noCoverageData = false }: SetupArgs = { noCoverageData: false } + { noCoverageData = false }: SetupArgs = { + noCoverageData: false, + } ) { server.use( graphql.query('GetRepoOverview', () => { @@ -147,18 +160,8 @@ describe('useRepoCoverageTimeseries', () => { }) }) - describe('with no coverage data', () => { - it('returns an empty array', async () => { - setup({ noCoverageData: true }) - const { result } = renderHook( - () => useRepoCoverageTimeseries({ branch: 'c3' }), - { wrapper: wrapper('') } - ) - - await waitFor(() => expect(result.current.data?.measurements).toEqual([])) - }) - - it('returns 0 for coverage change', async () => { + describe('with null coverage data', () => { + it('returns measurements with 0 for coverage', async () => { setup({ noCoverageData: true }) const { result } = renderHook( () => useRepoCoverageTimeseries({ branch: 'c3' }), @@ -166,7 +169,10 @@ describe('useRepoCoverageTimeseries', () => { ) await waitFor(() => - expect(result.current.data?.coverageChange).toEqual(0) + expect(result.current.data?.measurements).toEqual([ + { coverage: 0, date: new Date('2023-01-01T00:00:00.000Z') }, + { coverage: 0, date: new Date('2023-01-02T00:00:00.000Z') }, + ]) ) }) }) diff --git a/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.ts b/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.ts index e8064f7ce9..28e27581d0 100644 --- a/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.ts +++ b/src/pages/RepoPage/CoverageTab/OverviewTab/hooks/useRepoCoverageTimeseries.ts @@ -1,7 +1,11 @@ +import { + keepPreviousData, + useQuery as useQueryV5, +} from '@tanstack/react-queryV5' import { useMemo } from 'react' import { useParams } from 'react-router-dom' -import { useBranchCoverageMeasurements } from 'services/charts/useBranchCoverageMeasurements' +import { BranchCoverageMeasurementsQueryOpts } from 'services/charts/BranchCoverageMeasurementsQueryOpts' import { useLocationParams } from 'services/navigation' import { useRepoOverview } from 'services/repo' import { @@ -21,8 +25,7 @@ interface UseRepoCoverageTimeseriesArgs { branch: string options?: { enabled?: boolean - suspense?: boolean - keepPreviousData?: boolean + placeholderData?: typeof keepPreviousData } } @@ -49,59 +52,42 @@ export function useRepoCoverageTimeseries({ return createTimeSeriesQueryVars({ trend, oldestCommit, today }) }, [overview?.oldestCommitAt, params?.trend, today]) - const { data, ...rest } = useBranchCoverageMeasurements({ - provider, - owner, - repo, - branch, - after: queryVars?.after, - before: today, - interval: queryVars.interval, - opts: { - enabled: !!overview?.oldestCommitAt, - staleTime: 30000, - keepPreviousData: false, - suspense: false, - ...options, - }, - }) - - return useMemo(() => { - let coverage = [] - - if (!data?.measurements) { - return { - ...rest, - data: { measurements: [] }, + return useQueryV5({ + ...BranchCoverageMeasurementsQueryOpts({ + provider, + owner, + repo, + branch, + after: queryVars?.after, + before: today, + interval: queryVars.interval, + }), + select: (data) => { + let coverage = [] + if (data.measurements?.[0]?.max === null) { + data.measurements[0].max = 0 } - } - if (data?.measurements?.[0]?.max === null) { - data.measurements[0].max = 0 - } + // set initial coverage percentage + let prevPercent = data?.measurements?.[0]?.max ?? 0 + coverage = data?.measurements?.map((measurement) => { + const coverage = measurement?.max ?? prevPercent - // set set initial t - let prevPercent = data?.measurements?.[0]?.max ?? 0 - coverage = data?.measurements?.map((measurement) => { - const coverage = measurement?.max ?? prevPercent + // can save on a few reassignments + if (prevPercent !== coverage) { + prevPercent = coverage + } - // can save on a few reassignments - if (prevPercent !== coverage) { - prevPercent = coverage - } - - return { - date: new Date(measurement?.timestamp), - coverage, - } - }) + return { date: new Date(measurement?.timestamp), coverage } + }) - const coverageChange = - (coverage.at(-1)?.coverage ?? 0) - (coverage.at(0)?.coverage ?? 0) + const coverageChange = + (coverage.at(-1)?.coverage ?? 0) - (coverage.at(0)?.coverage ?? 0) - return { - ...rest, - data: { measurements: coverage, coverageChange }, - } - }, [data, rest]) + return { measurements: coverage, coverageChange } + }, + enabled: !!overview?.oldestCommitAt, + staleTime: 30000, + ...options, + }) } diff --git a/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.test.tsx b/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.test.tsx index ef6ef1eae2..680c7a9067 100644 --- a/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.test.tsx +++ b/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.test.tsx @@ -1,4 +1,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + QueryClientProvider as QueryClientProviderV5, + QueryClient as QueryClientV5, +} from '@tanstack/react-queryV5' import { render, screen, within } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' @@ -26,21 +30,27 @@ vi.mock('recharts', async () => { } }) +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}) +const queryClientV5 = new QueryClientV5({ + defaultOptions: { queries: { retry: false } }, +}) + const wrapper = ( initialEntries = ['/gh/codecov/bells-hells/tree/main'] ): React.FC => ({ children }) => ( - - - {children} - - + + + + {children} + + + ) -const queryClient = new QueryClient({ - defaultOptions: { queries: { retry: false } }, -}) const server = setupServer() beforeAll(() => { @@ -73,6 +83,7 @@ beforeEach(() => { afterEach(() => { queryClient.clear() + queryClientV5.clear() server.resetHandlers() }) diff --git a/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.tsx b/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.tsx index c5c9cbf3fe..6adcb6173a 100644 --- a/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.tsx +++ b/src/pages/RepoPage/CoverageTab/OverviewTab/subroute/CoverageChart/CoverageChart.tsx @@ -1,3 +1,4 @@ +import { keepPreviousData } from '@tanstack/react-queryV5' import { format } from 'date-fns' import { useParams } from 'react-router-dom' import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts' @@ -43,17 +44,16 @@ function CoverageChart() { defaultBranch: overview?.defaultBranch ?? '', }) - const { data, isPreviousData, isLoading, isError } = + const { data, isPlaceholderData, isPending, isError } = useRepoCoverageTimeseries({ branch: selection?.name, options: { enabled: !!selection?.name, - suspense: false, - keepPreviousData: true, + placeholderData: keepPreviousData, }, }) - if (!isPreviousData && isLoading) { + if (!isPlaceholderData && isPending) { return } @@ -65,7 +65,8 @@ function CoverageChart() { ) } - if (data?.measurements.length < 1) { + const dataCount = data?.measurements?.length ?? 0 + if (dataCount < 1) { return (

diff --git a/src/services/charts/useBranchCoverageMeasurements.test.tsx b/src/services/charts/BranchCoverageMeasurementsQueryOpts.test.tsx similarity index 64% rename from src/services/charts/useBranchCoverageMeasurements.test.tsx rename to src/services/charts/BranchCoverageMeasurementsQueryOpts.test.tsx index d77150eef8..f025abcfc2 100644 --- a/src/services/charts/useBranchCoverageMeasurements.test.tsx +++ b/src/services/charts/BranchCoverageMeasurementsQueryOpts.test.tsx @@ -1,10 +1,14 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + QueryClientProvider as QueryClientProviderV5, + QueryClient as QueryClientV5, + useQuery as useQueryV5, +} from '@tanstack/react-queryV5' import { renderHook, waitFor } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' import { type MockInstance } from 'vitest' -import { useBranchCoverageMeasurements } from './useBranchCoverageMeasurements' +import { BranchCoverageMeasurementsQueryOpts } from './BranchCoverageMeasurementsQueryOpts' const mockBranchMeasurements = { owner: { @@ -12,22 +16,10 @@ const mockBranchMeasurements = { __typename: 'Repository', coverageAnalytics: { measurements: [ - { - timestamp: '2023-01-01T00:00:00+00:00', - max: 85, - }, - { - timestamp: '2023-01-02T00:00:00+00:00', - max: 80, - }, - { - timestamp: '2023-01-03T00:00:00+00:00', - max: 90, - }, - { - timestamp: '2023-01-04T00:00:00+00:00', - max: 100, - }, + { timestamp: '2023-01-01T00:00:00+00:00', max: 85 }, + { timestamp: '2023-01-02T00:00:00+00:00', max: 80 }, + { timestamp: '2023-01-03T00:00:00+00:00', max: 90 }, + { timestamp: '2023-01-04T00:00:00+00:00', max: 100 }, ], }, }, @@ -60,13 +52,15 @@ const mockNullOwner = { const mockUnsuccessfulParseError = {} -const queryClient = new QueryClient({ +const queryClientV5 = new QueryClientV5({ defaultOptions: { queries: { retry: false } }, }) const server = setupServer() const wrapper: React.FC = ({ children }) => ( - {children} + + {children} + ) beforeAll(() => { @@ -74,7 +68,7 @@ beforeAll(() => { }) afterEach(() => { - queryClient.clear() + queryClientV5.clear() server.resetHandlers() }) @@ -120,36 +114,26 @@ describe('useBranchCoverageMeasurements', () => { setup({}) const { result } = renderHook( () => - useBranchCoverageMeasurements({ - provider: 'gh', - owner: 'codecov', - repo: 'cool-repo', - interval: 'INTERVAL_7_DAY', - before: new Date('2023/03/02'), - after: new Date('2022/03/02'), - branch: 'main', - }), + useQueryV5( + BranchCoverageMeasurementsQueryOpts({ + provider: 'gh', + owner: 'codecov', + repo: 'cool-repo', + interval: 'INTERVAL_7_DAY', + before: new Date('2023/03/02'), + after: new Date('2022/03/02'), + branch: 'main', + }) + ), { wrapper } ) const expectedData = { measurements: [ - { - timestamp: '2023-01-01T00:00:00+00:00', - max: 85, - }, - { - timestamp: '2023-01-02T00:00:00+00:00', - max: 80, - }, - { - timestamp: '2023-01-03T00:00:00+00:00', - max: 90, - }, - { - timestamp: '2023-01-04T00:00:00+00:00', - max: 100, - }, + { timestamp: '2023-01-01T00:00:00+00:00', max: 85 }, + { timestamp: '2023-01-02T00:00:00+00:00', max: 80 }, + { timestamp: '2023-01-03T00:00:00+00:00', max: 90 }, + { timestamp: '2023-01-04T00:00:00+00:00', max: 100 }, ], } @@ -164,15 +148,17 @@ describe('useBranchCoverageMeasurements', () => { setup({ isNullOwner: true }) const { result } = renderHook( () => - useBranchCoverageMeasurements({ - provider: 'gh', - owner: 'codecov', - repo: 'cool-repo', - interval: 'INTERVAL_7_DAY', - before: new Date('2023/03/02'), - after: new Date('2022/03/02'), - branch: 'main', - }), + useQueryV5( + BranchCoverageMeasurementsQueryOpts({ + provider: 'gh', + owner: 'codecov', + repo: 'cool-repo', + interval: 'INTERVAL_7_DAY', + before: new Date('2023/03/02'), + after: new Date('2022/03/02'), + branch: 'main', + }) + ), { wrapper } ) @@ -199,15 +185,17 @@ describe('useBranchCoverageMeasurements', () => { setup({ isNotFoundError: true }) const { result } = renderHook( () => - useBranchCoverageMeasurements({ - provider: 'gh', - owner: 'codecov', - repo: 'cool-repo', - interval: 'INTERVAL_7_DAY', - before: new Date('2023/03/02'), - after: new Date('2022/03/02'), - branch: 'main', - }), + useQueryV5( + BranchCoverageMeasurementsQueryOpts({ + provider: 'gh', + owner: 'codecov', + repo: 'cool-repo', + interval: 'INTERVAL_7_DAY', + before: new Date('2023/03/02'), + after: new Date('2022/03/02'), + branch: 'main', + }) + ), { wrapper } ) @@ -237,15 +225,17 @@ describe('useBranchCoverageMeasurements', () => { setup({ isOwnerNotActivatedError: true }) const { result } = renderHook( () => - useBranchCoverageMeasurements({ - provider: 'gh', - owner: 'codecov', - repo: 'cool-repo', - interval: 'INTERVAL_7_DAY', - before: new Date('2023/03/02'), - after: new Date('2022/03/02'), - branch: 'main', - }), + useQueryV5( + BranchCoverageMeasurementsQueryOpts({ + provider: 'gh', + owner: 'codecov', + repo: 'cool-repo', + interval: 'INTERVAL_7_DAY', + before: new Date('2023/03/02'), + after: new Date('2022/03/02'), + branch: 'main', + }) + ), { wrapper } ) @@ -275,15 +265,17 @@ describe('useBranchCoverageMeasurements', () => { setup({ isUnsuccessfulParseError: true }) const { result } = renderHook( () => - useBranchCoverageMeasurements({ - provider: 'gh', - owner: 'codecov', - repo: 'cool-repo', - interval: 'INTERVAL_7_DAY', - before: new Date('2023/03/02'), - after: new Date('2022/03/02'), - branch: 'main', - }), + useQueryV5( + BranchCoverageMeasurementsQueryOpts({ + provider: 'gh', + owner: 'codecov', + repo: 'cool-repo', + interval: 'INTERVAL_7_DAY', + before: new Date('2023/03/02'), + after: new Date('2022/03/02'), + branch: 'main', + }) + ), { wrapper } ) diff --git a/src/services/charts/useBranchCoverageMeasurements.tsx b/src/services/charts/BranchCoverageMeasurementsQueryOpts.tsx similarity index 79% rename from src/services/charts/useBranchCoverageMeasurements.tsx rename to src/services/charts/BranchCoverageMeasurementsQueryOpts.tsx index 35d4265439..b75b3678be 100644 --- a/src/services/charts/useBranchCoverageMeasurements.tsx +++ b/src/services/charts/BranchCoverageMeasurementsQueryOpts.tsx @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query' +import { queryOptions as queryOptionsV5 } from '@tanstack/react-queryV5' import { z } from 'zod' import { @@ -6,7 +6,7 @@ import { RepoOwnerNotActivatedErrorSchema, } from 'services/repo' import Api from 'shared/api' -import { NetworkErrorObject } from 'shared/api/helpers' +import { rejectNetworkError } from 'shared/api/helpers' import A from 'ui/A' type MeasurementIntervals = @@ -75,7 +75,7 @@ query GetBranchCoverageMeasurements( } }` -interface UseBranchCoverageMeasurementsArgs { +interface BranchCoverageMeasurementsQueryArgs { provider: string owner: string repo: string @@ -83,15 +83,9 @@ interface UseBranchCoverageMeasurementsArgs { before: Date | null after: Date | null branch: string - opts?: { - enabled?: boolean - suspense?: boolean - keepPreviousData?: boolean - staleTime?: number - } } -export const useBranchCoverageMeasurements = ({ +export const BranchCoverageMeasurementsQueryOpts = ({ provider, owner, repo, @@ -99,9 +93,8 @@ export const useBranchCoverageMeasurements = ({ before, after, branch, - opts = {}, -}: UseBranchCoverageMeasurementsArgs) => - useQuery({ +}: BranchCoverageMeasurementsQueryArgs) => + queryOptionsV5({ queryKey: [ 'GetBranchCoverageMeasurements', provider, @@ -133,25 +126,26 @@ export const useBranchCoverageMeasurements = ({ ) if (!parsedData.success) { - return Promise.reject({ + return rejectNetworkError({ status: 404, data: {}, - dev: 'useBranchCoverageMeasurements - 404 Failed to parse data', - } satisfies NetworkErrorObject) + dev: 'BranchCoverageMeasurementsQueryOpts - 404 Failed to parse data', + error: parsedData.error, + }) } const data = parsedData.data if (data?.owner?.repository?.__typename === 'NotFoundError') { - return Promise.reject({ + return rejectNetworkError({ status: 404, data: {}, - dev: 'useBranchCoverageMeasurements - 404 Not found error', - } satisfies NetworkErrorObject) + dev: 'BranchCoverageMeasurementsQueryOpts - 404 Not found error', + }) } if (data?.owner?.repository?.__typename === 'OwnerNotActivatedError') { - return Promise.reject({ + return rejectNetworkError({ status: 403, data: { detail: ( @@ -163,8 +157,8 @@ export const useBranchCoverageMeasurements = ({

), }, - dev: 'useBranchCoverageMeasurements - 403 Owner not activated', - } satisfies NetworkErrorObject) + dev: 'BranchCoverageMeasurementsQueryOpts - 403 Owner not activated', + }) } return { @@ -172,5 +166,4 @@ export const useBranchCoverageMeasurements = ({ data?.owner?.repository?.coverageAnalytics?.measurements ?? [], } }), - ...opts, })