diff --git a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.test.jsx b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.test.jsx index 8d44b3f734..267778eb05 100644 --- a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.test.jsx +++ b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.test.jsx @@ -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, waitFor } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' @@ -10,31 +14,23 @@ vi.mock('./ComponentsNotConfigured', () => ({ default: () => 'ComponentsNotConfigured', })) -const queryClient = new QueryClient() -const server = setupServer() - -const wrapper = - (initialEntries = '/gh/codecov/gazebo/pull/123/components') => - ({ children }) => ( - - - - {children} - - - - ) - -beforeAll(() => { - server.listen() -}) -afterEach(() => { - queryClient.clear() - server.resetHandlers() -}) -afterAll(() => { - server.close() -}) +const mockPullComponentsResponse = { + owner: { + repository: { + __typename: 'Repository', + pull: { + compareWithBase: { + __typename: 'Comparison', + componentComparisons: [ + { name: 'component-1' }, + { name: 'component-2' }, + { name: 'component-3' }, + ], + }, + }, + }, + }, +} const mockPull = { owner: { @@ -46,26 +42,52 @@ const mockPull = { componentComparisons: [ { name: 'secondTest', - headTotals: { - percentCovered: 82.71, - }, - baseTotals: { - percentCovered: 80.0, - }, - patchTotals: { - percentCovered: 59.0, - }, + headTotals: { percentCovered: 82.71 }, + baseTotals: { percentCovered: 80.0 }, + patchTotals: { percentCovered: 59.0 }, }, ], }, - head: { - branchName: 'abc', - }, + head: { branchName: 'abc' }, }, }, }, } +const server = setupServer() +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}) +const queryClientV5 = new QueryClientV5({ + defaultOptions: { queries: { retry: false } }, +}) + +const wrapper = + (initialEntries = '/gh/codecov/gazebo/pull/123/components') => + ({ children }) => ( + + + + + {children} + + + + + ) + +beforeAll(() => { + server.listen() +}) +afterEach(() => { + queryClient.clear() + queryClientV5.clear() + server.resetHandlers() +}) +afterAll(() => { + server.close() +}) + describe('ComponentsTable', () => { function setup(overrideData) { const componentsMock = vi.fn() @@ -79,8 +101,10 @@ describe('ComponentsTable', () => { if (overrideData) { return HttpResponse.json({ data: overrideData }) } - return HttpResponse.json({ data: mockPull }) + }), + graphql.query('PullComponentsSelector', () => { + return HttpResponse.json({ data: mockPullComponentsResponse }) }) ) @@ -88,13 +112,8 @@ describe('ComponentsTable', () => { } describe('when there are no components in the new tab', () => { - beforeEach(() => { - setup({ - owner: null, - }) - }) - it('will render card with no dismiss button', async () => { + setup({ owner: null }) render(, { wrapper: wrapper() }) const componentNotConfigured = await screen.findByText( @@ -105,11 +124,8 @@ describe('ComponentsTable', () => { }) describe('when rendered with populated data in the new tab', () => { - beforeEach(() => { - setup() - }) - it('shows title and body', async () => { + setup() render(, { wrapper: wrapper() }) const nameTableField = await screen.findByText(`Name`) @@ -158,6 +174,7 @@ describe('ComponentsTable', () => { describe('when loading', () => { it('renders spinner', () => { + setup() render(, { wrapper: wrapper() }) const spinner = screen.getByTestId('spinner') diff --git a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.tsx b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.tsx index ac8c8ddac4..3a945f1919 100644 --- a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.tsx +++ b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/ComponentsTable.tsx @@ -1,3 +1,4 @@ +import { useQuery as useQueryV5 } from '@tanstack/react-queryV5' import { createColumnHelper, flexRender, @@ -8,14 +9,17 @@ import cs from 'classnames' import isArray from 'lodash/isArray' import qs, { type ParsedQs } from 'qs' import { useMemo } from 'react' -import { useLocation } from 'react-router-dom' +import { useLocation, useParams } from 'react-router-dom' import A from 'ui/A' import Spinner from 'ui/Spinner' import TotalsNumber from 'ui/TotalsNumber' import ComponentsNotConfigured from './ComponentsNotConfigured' -import { ComponentsComparison, useComponentComparison } from './hooks' +import { + ComponentComparisonQueryOpts, + ComponentsComparison, +} from './queries/ComponentComparisonQueryOpts' import ComponentsSelector from '../ComponentsSelector' @@ -91,7 +95,15 @@ function getFilters({ components }: { components?: ParsedQs[] | string[] }) { } } +interface URLParams { + provider: string + owner: string + repo: string + pullId: string +} + export default function ComponentsTable() { + const { provider, owner, repo, pullId } = useParams() const location = useLocation() const queryParams = qs.parse(location.search, { ignoreQueryPrefix: true, @@ -104,9 +116,15 @@ export default function ComponentsTable() { } const filters = getFilters({ components }) - const { data, isLoading } = useComponentComparison({ - filters, - }) + const { data, isLoading } = useQueryV5( + ComponentComparisonQueryOpts({ + provider, + owner, + repo, + pullId, + filters, + }) + ) const tableData = useMemo(() => { if ( diff --git a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/index.ts b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/index.ts deleted file mode 100644 index b19ad78b25..0000000000 --- a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './useComponentComparison' diff --git a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/query.ts b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/query.ts deleted file mode 100644 index bc87cc1930..0000000000 --- a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/query.ts +++ /dev/null @@ -1,57 +0,0 @@ -export const query = ` - query PullComponentComparison($owner: String!, $repo: String!, $pullId: Int!, $filters: ComponentsFilters) { - owner(username: $owner) { - repository(name: $repo) { - __typename - ... on Repository { - pull(id: $pullId) { - compareWithBase { - __typename - ... on Comparison { - componentComparisons(filters: $filters) { - name - patchTotals { - percentCovered - } - headTotals { - percentCovered - } - baseTotals { - percentCovered - } - } - } - ... on FirstPullRequest { - message - } - ... on MissingBaseCommit { - message - } - ... on MissingHeadCommit { - message - } - ... on MissingComparison { - message - } - ... on MissingBaseReport { - message - } - ... on MissingHeadReport { - message - } - } - head { - branchName - } - } - } - ... on NotFoundError { - message - } - ... on OwnerNotActivatedError { - message - } - } - } - } -` diff --git a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/useComponentComparison.test.tsx b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/queries/ComponentComparisonQueryOpts.test.tsx similarity index 65% rename from src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/useComponentComparison.test.tsx rename to src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/queries/ComponentComparisonQueryOpts.test.tsx index e5d2193db8..b59bbf6b35 100644 --- a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/useComponentComparison.test.tsx +++ b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/queries/ComponentComparisonQueryOpts.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 { MemoryRouter, Route } from 'react-router-dom' -import { useComponentComparison } from './useComponentComparison' +import { ComponentComparisonQueryOpts } from './ComponentComparisonQueryOpts' const mockResponse = { owner: { @@ -15,22 +19,14 @@ const mockResponse = { __typename: 'Comparison', componentComparisons: [ { - name: 'kevdak', - patchTotals: { - percentCovered: 31.46, - }, - headTotals: { - percentCovered: 71.46, - }, - baseTotals: { - percentCovered: 51.46, - }, + name: 'test-component', + patchTotals: { percentCovered: 31.46 }, + headTotals: { percentCovered: 71.46 }, + baseTotals: { percentCovered: 51.46 }, }, ], }, - head: { - branchName: 'abc', - }, + head: { branchName: 'abc' }, }, }, }, @@ -56,17 +52,19 @@ const mockOwnerNotActivatedError = { const mockUnsuccessfulParseError = {} -const queryClient = new QueryClient({ +const queryClientV5 = new QueryClientV5({ defaultOptions: { queries: { retry: false } }, }) const wrapper: React.FC = ({ children }) => ( - - - {children} - - + + + + {children} + + + ) const server = setupServer() @@ -74,7 +72,7 @@ const server = setupServer() beforeAll(() => server.listen()) beforeEach(() => { server.resetHandlers() - queryClient.clear() + queryClientV5.clear() }) afterAll(() => server.close()) @@ -118,9 +116,18 @@ describe('useComponentComparison', () => { it('returns data for the owner page', async () => { setup({}) - const { result } = renderHook(() => useComponentComparison(), { - wrapper, - }) + const { result } = renderHook( + () => + useQueryV5( + ComponentComparisonQueryOpts({ + provider: 'gh', + owner: 'test-org', + repo: 'test-repo', + pullId: '123', + }) + ), + { wrapper } + ) await waitFor(() => expect(result.current.data).toStrictEqual({ @@ -129,22 +136,14 @@ describe('useComponentComparison', () => { __typename: 'Comparison', componentComparisons: [ { - name: 'kevdak', - patchTotals: { - percentCovered: 31.46, - }, - headTotals: { - percentCovered: 71.46, - }, - baseTotals: { - percentCovered: 51.46, - }, + name: 'test-component', + patchTotals: { percentCovered: 31.46 }, + headTotals: { percentCovered: 71.46 }, + baseTotals: { percentCovered: 51.46 }, }, ], }, - head: { - branchName: 'abc', - }, + head: { branchName: 'abc' }, }, }) ) @@ -164,9 +163,18 @@ describe('useComponentComparison', () => { it('throws a 404', async () => { setup({ isNotFoundError: true }) - const { result } = renderHook(() => useComponentComparison(), { - wrapper, - }) + const { result } = renderHook( + () => + useQueryV5( + ComponentComparisonQueryOpts({ + provider: 'gh', + owner: 'test-org', + repo: 'test-repo', + pullId: '123', + }) + ), + { wrapper } + ) await waitFor(() => expect(result.current.isError).toBeTruthy()) await waitFor(() => @@ -192,9 +200,18 @@ describe('useComponentComparison', () => { it('throws a 403', async () => { setup({ isOwnerNotActivatedError: true }) - const { result } = renderHook(() => useComponentComparison(), { - wrapper, - }) + const { result } = renderHook( + () => + useQueryV5( + ComponentComparisonQueryOpts({ + provider: 'gh', + owner: 'test-org', + repo: 'test-repo', + pullId: '123', + }) + ), + { wrapper } + ) await waitFor(() => expect(result.current.isError).toBeTruthy()) await waitFor(() => @@ -220,9 +237,18 @@ describe('useComponentComparison', () => { it('throws a 404', async () => { setup({ isUnsuccessfulParseError: true }) - const { result } = renderHook(() => useComponentComparison(), { - wrapper, - }) + const { result } = renderHook( + () => + useQueryV5( + ComponentComparisonQueryOpts({ + provider: 'gh', + owner: 'test-org', + repo: 'test-repo', + pullId: '123', + }) + ), + { wrapper } + ) await waitFor(() => expect(result.current.isError).toBeTruthy()) await waitFor(() => expect(result.current.isError).toBeTruthy()) @@ -244,14 +270,18 @@ describe('useComponentComparison', () => { const { result } = renderHook( () => - useComponentComparison({ - filters: { - components: componentsFilter, - }, - }), - { - wrapper, - } + useQueryV5( + ComponentComparisonQueryOpts({ + provider: 'gh', + owner: 'test-org', + repo: 'test-repo', + pullId: '123', + filters: { + components: componentsFilter, + }, + }) + ), + { wrapper } ) await waitFor(() => result.current.isLoading) diff --git a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/useComponentComparison.tsx b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/queries/ComponentComparisonQueryOpts.tsx similarity index 58% rename from src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/useComponentComparison.tsx rename to src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/queries/ComponentComparisonQueryOpts.tsx index 89788acac5..f58cf09b08 100644 --- a/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/hooks/useComponentComparison.tsx +++ b/src/pages/PullRequestPage/PullCoverage/routes/ComponentsTab/queries/ComponentComparisonQueryOpts.tsx @@ -1,6 +1,5 @@ -import { useQuery } from '@tanstack/react-query' +import { queryOptions as queryOptionsV5 } from '@tanstack/react-queryV5' import { type ParsedQs } from 'qs' -import { useParams } from 'react-router-dom' import { z } from 'zod' import { @@ -16,10 +15,9 @@ import { RepoOwnerNotActivatedErrorSchema, } from 'services/repo' import Api from 'shared/api' +import { rejectNetworkError } from 'shared/api/helpers' import A from 'ui/A' -import { query } from './query' - const ComponentsComparisonSchema = z .object({ name: z.string(), @@ -74,24 +72,86 @@ const ComponentComparisonSchema = z.object({ .nullable(), }) -interface URLParams { +const query = ` +query PullComponentComparison( + $owner: String! + $repo: String! + $pullId: Int! + $filters: ComponentsFilters +) { + owner(username: $owner) { + repository(name: $repo) { + __typename + ... on Repository { + pull(id: $pullId) { + compareWithBase { + __typename + ... on Comparison { + componentComparisons(filters: $filters) { + name + patchTotals { + percentCovered + } + headTotals { + percentCovered + } + baseTotals { + percentCovered + } + } + } + ... on FirstPullRequest { + message + } + ... on MissingBaseCommit { + message + } + ... on MissingHeadCommit { + message + } + ... on MissingComparison { + message + } + ... on MissingBaseReport { + message + } + ... on MissingHeadReport { + message + } + } + head { + branchName + } + } + } + ... on NotFoundError { + message + } + ... on OwnerNotActivatedError { + message + } + } + } +}` + +interface ComponentComparisonQueryArgs { provider: string owner: string repo: string pullId: string -} - -interface ComponentComparisonParams { filters?: { components?: string[] | ParsedQs[] } } -export function useComponentComparison({ +export function ComponentComparisonQueryOpts({ + provider, + owner, + repo, + pullId, filters, -}: ComponentComparisonParams = {}) { - const { provider, owner, repo, pullId } = useParams() - return useQuery({ +}: ComponentComparisonQueryArgs) { + return queryOptionsV5({ queryKey: [ 'PullComponentComparison', provider, @@ -114,26 +174,29 @@ export function useComponentComparison({ pullId: parseInt(pullId, 10), }, }).then((res) => { - const parsedData = ComponentComparisonSchema.safeParse(res?.data) + const parsedRes = ComponentComparisonSchema.safeParse(res?.data) - if (!parsedData.success) { - return Promise.reject({ + if (!parsedRes.success) { + return rejectNetworkError({ status: 404, data: {}, + dev: `ComponentComparisonQueryOpts - 404 Failed to parse`, + error: parsedRes.error, }) } - const data = parsedData.data + const data = parsedRes.data if (data?.owner?.repository?.__typename === 'NotFoundError') { - return Promise.reject({ + return rejectNetworkError({ status: 404, data: {}, + dev: `ComponentComparisonQueryOpts - 404 Not Found`, }) } if (data?.owner?.repository?.__typename === 'OwnerNotActivatedError') { - return Promise.reject({ + return rejectNetworkError({ status: 403, data: { detail: ( @@ -145,12 +208,11 @@ export function useComponentComparison({

), }, + dev: `ComponentComparisonQueryOpts - 403 Owner Not Activated`, }) } - return { - pull: data?.owner?.repository?.pull, - } + return { pull: data?.owner?.repository?.pull } }), }) }