diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/FailedTestsTable/FailedTestsTable.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/FailedTestsTable/FailedTestsTable.tsx index 32190faf2d..14d63772f6 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/FailedTestsTable/FailedTestsTable.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/FailedTestsTable/FailedTestsTable.tsx @@ -235,13 +235,13 @@ const FailedTestsTable = () => { }, }) - const [isDefaultBranch, setIsDefaultBranch] = useState(false) + const [showResetButton, setShowResetButton] = useState(false) const [isTeamOrFreePlan, setIsTeamOrFreePlan] = useState(false) const [isPrivate, setIsPrivate] = useState(false) useEffect(() => { if (testData?.defaultBranch) { - setIsDefaultBranch(testData.defaultBranch === branch) + setShowResetButton(testData.defaultBranch === branch || !branch) } }, [testData?.defaultBranch, branch]) @@ -258,7 +258,7 @@ const FailedTestsTable = () => { }, [testData?.private]) const hideFlakeRate = - (isTeamOrFreePlan && isPrivate) || (!!branch && !isDefaultBranch) + (isTeamOrFreePlan && isPrivate) || (!!branch && !showResetButton) const tableData = useMemo(() => { if (!testData?.testResults) return [] @@ -325,30 +325,35 @@ const FailedTestsTable = () => { } }, [fetchNextPage, inView, hasNextPage]) - if (testData?.isFirstPullRequest && testData.totalCount === 0) { + if ( + testData?.isFirstPullRequest && + testData.totalCount === 0 && + branch === testData?.defaultBranch + ) { return (

No data yet

- To see data for the main branch, merge your PR into the main branch. + To see data for the {testData?.defaultBranch} branch, merge your PR + into the {testData?.defaultBranch} branch.

) } - if (testData.totalCount === 0 && !isLoading && !!branch) { + if (testData.totalCount === 0 && !isLoading) { return (

No test results found

@@ -363,7 +368,7 @@ const FailedTestsTable = () => { <>
diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.test.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.test.tsx index af9535f38a..c4ff9a56a0 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.test.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.test.tsx @@ -148,11 +148,14 @@ describe('MetricsSection', () => { }) }) - describe('when on default branch', () => { + describe.each([ + ['default branch', 'main'], + ['all branches', ''], + ])('when on %s', (_, encodedBranch) => { it('renders subheaders', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const runEfficiency = await screen.findByText('Improve CI Run Efficiency') @@ -164,7 +167,7 @@ describe('MetricsSection', () => { it('renders total test runtime card', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const title = await screen.findByText('Total test run time') @@ -182,7 +185,7 @@ describe('MetricsSection', () => { it('renders slowest tests card', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const title = await screen.findByText('Slowest tests') @@ -199,7 +202,7 @@ describe('MetricsSection', () => { it('can update the location params on button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText('12') expect(select).toBeInTheDocument() @@ -217,7 +220,7 @@ describe('MetricsSection', () => { it('removes the location param on second button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText('12') expect(select).toBeInTheDocument() @@ -239,7 +242,7 @@ describe('MetricsSection', () => { it('renders total flaky tests card', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const title = await screen.findByText('Flaky tests') @@ -256,7 +259,7 @@ describe('MetricsSection', () => { it('can update the location params on button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText(88) expect(select).toBeInTheDocument() @@ -274,7 +277,7 @@ describe('MetricsSection', () => { it('removes the location param on second button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText(88) expect(select).toBeInTheDocument() @@ -313,7 +316,7 @@ describe('MetricsSection', () => { it('renders total failures card', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const title = await screen.findByText('Cumulative Failures') @@ -330,7 +333,7 @@ describe('MetricsSection', () => { it('can update the location params on button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText(1) expect(select).toBeInTheDocument() @@ -370,7 +373,7 @@ describe('MetricsSection', () => { it('renders total skips card', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const title = await screen.findByText('Skipped tests') @@ -387,7 +390,7 @@ describe('MetricsSection', () => { it('can update the location params on button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText(20) expect(select).toBeInTheDocument() @@ -405,7 +408,7 @@ describe('MetricsSection', () => { it('removes the location param on second button click', async () => { const { user } = setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const select = await screen.findByText(20) expect(select).toBeInTheDocument() diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.tsx index b38d7c0899..429e3e93f1 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/MetricsSection/MetricsSection.tsx @@ -377,9 +377,12 @@ function MetricsSection() { }) const decodedBranch = getDecodedBranch(branch) - const selectedBranch = decodedBranch ?? testResults?.defaultBranch ?? '' + const selectedBranch = decodedBranch - if (selectedBranch !== testResults?.defaultBranch) { + if ( + selectedBranch !== undefined && + selectedBranch !== testResults?.defaultBranch + ) { return null } diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.test.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.test.tsx index 1a04fff6af..f370c9f065 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.test.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.test.tsx @@ -134,6 +134,7 @@ interface SetupArgs { hasNextPage?: boolean nullOverview?: boolean nullHead?: boolean + nullBranch?: boolean } describe('BranchSelector', () => { @@ -142,10 +143,12 @@ describe('BranchSelector', () => { hasNextPage = false, nullOverview = false, nullHead = false, + nullBranch = false, }: SetupArgs = { hasNextPage: false, nullOverview: false, nullHead: false, + nullBranch: false, } ) { const user = userEvent.setup() @@ -182,6 +185,16 @@ describe('BranchSelector', () => { branch = info.variables?.branch } + if (nullBranch) { + return HttpResponse.json({ + data: { + owner: { + repository: { __typename: 'Repository', branch: null }, + }, + }, + }) + } + let mockedBranch = mockBranch(branch) if (nullHead) { mockedBranch = mockBranch(branch, null) @@ -241,7 +254,7 @@ describe('BranchSelector', () => { wrapper: wrapper(queryClient), }) - const dropDownBtn = await screen.findByText('main') + const dropDownBtn = await screen.findByText('All branches') expect(dropDownBtn).toBeInTheDocument() }) }) @@ -255,7 +268,7 @@ describe('BranchSelector', () => { }) await waitFor(() => - expect(testLocation.pathname).toBe('/gh/codecov/test-repo/tests/main') + expect(testLocation.pathname).toBe('/gh/codecov/test-repo/tests') ) }) @@ -286,7 +299,7 @@ describe('BranchSelector', () => { await user.click(select) const options = screen.getAllByRole('option') - expect(options[0]).toHaveTextContent('main') + expect(options[0]).toHaveTextContent('All branches') }) it('navigates to the selected branch', async () => { @@ -367,7 +380,7 @@ describe('BranchSelector', () => { wrapper: wrapper(queryClient), }) - const select = await screen.findByText('main') + const select = await screen.findByText('All branches') await user.click(select) const input = await screen.findByRole('combobox') @@ -383,9 +396,13 @@ describe('BranchSelector', () => { it('displays select a branch in the button', async () => { const { queryClient } = setup({ nullOverview: true, + nullBranch: true, }) render(, { - wrapper: wrapper(queryClient), + wrapper: wrapper( + queryClient, + '/gh/codecov/test-repo/tests/nonexistent-branch' + ), }) const select = await screen.findByRole('button', { diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.tsx index cce19a690d..dd9bb3eea4 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/BranchSelector/BranchSelector.tsx @@ -15,6 +15,14 @@ interface URLParams { branch?: string } +type Head = { commitid: string } | null + +interface BranchSelection { + name: string + value: string + head: Head +} + const getDecodedBranch = (branch?: string) => branch ? decodeURIComponent(branch) : undefined @@ -46,50 +54,53 @@ const BranchSelector = () => { }) const decodedBranch = getDecodedBranch(branch) - const selectedBranch = decodedBranch ?? overview?.defaultBranch ?? '' const { data: searchBranchValue } = useBranch({ provider, owner, repo, - branch: selectedBranch, + branch: decodedBranch, opts: { - queryKey: ['GetSelectedBranch', provider, owner, repo, selectedBranch], - enabled: !!selectedBranch, + queryKey: ['GetSelectedBranch', provider, owner, repo, decodedBranch], + enabled: !!decodedBranch, }, }) - - let selection = searchBranchValue?.branch - if (!selection) { - selection = { - name: 'Select branch', - head: null, - } - } - - if ( - selectedBranch === overview?.defaultBranch && - !branch && - selection.head !== null - ) { - history.push( - failedTestsLink.path({ branch: encodeURIComponent(selection?.name) }) - ) - } + const selection: BranchSelection = searchBranchValue?.branch + ? { + name: searchBranchValue.branch.name, + value: searchBranchValue.branch.name, + head: searchBranchValue.branch.head, + } + : { + name: branch ? 'Select branch' : 'All branches', + value: branch || '', + head: null, + } const sortedBranchList = useMemo(() => { - if (!branchList?.branches) return [] - - if (overview?.defaultBranch) { - return [ - // Pins the default branch to the top of the list always, filters it from results otherwise - { name: overview.defaultBranch, head: null }, - ...branchList.branches.filter( - (branch) => branch.name !== overview.defaultBranch - ), - ] + if (!branchList?.branches?.length) return [] + + const allBranches = { name: 'All branches', value: '', head: null as Head } + const defaultBranch = overview?.defaultBranch + ? { + name: overview.defaultBranch, + value: overview.defaultBranch, + head: null as Head, + } + : undefined + const branches = branchList.branches + .filter((branch) => branch.name !== defaultBranch?.name) + .map((branch) => ({ + name: branch.name, + value: branch.name, + head: branch.head, + })) + + if (defaultBranch) { + return [allBranches, defaultBranch, ...branches] } - return branchList.branches + + return [allBranches, ...branches] }, [overview?.defaultBranch, branchList.branches]) return ( @@ -107,9 +118,11 @@ const BranchSelector = () => { ariaName="test results branch selector" items={sortedBranchList} value={selection} - onChange={(item: Branch) => { + onChange={(item: BranchSelection) => { history.push( - failedTestsLink.path({ branch: encodeURIComponent(item?.name) }) + failedTestsLink.path({ + branch: encodeURIComponent(item.value), + }) ) }} variant="gray" diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.test.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.test.tsx index c57bb09e7e..90f7a665e7 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.test.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.test.tsx @@ -178,11 +178,14 @@ describe('SelectorSection', () => { }) }) - describe('when on default branch', () => { + describe.each([ + ['default branch', 'main'], + ['all branches', ''], + ])('when on %s', (_, encodedBranch) => { it('has all four selectors', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const branchSelector = await screen.findByText('Branch Context') @@ -198,7 +201,7 @@ describe('SelectorSection', () => { it('has 60 day retention link', async () => { setup() render(, { - wrapper: wrapper('/gh/owner/repo/tests/main'), + wrapper: wrapper(`/gh/owner/repo/tests/${encodedBranch}`), }) const link = await screen.findByRole('link') diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.tsx index 68a8ff0a65..979db5a25d 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/SelectorSection/SelectorSection.tsx @@ -77,7 +77,7 @@ function SelectorSection() { }) const decodedBranch = getDecodedBranch(branch) - const selectedBranch = decodedBranch ?? overview?.defaultBranch ?? '' + const selectedBranch = decodedBranch ?? '' const timeValue = MeasurementTimeOptions.find( // @ts-expect-error need to type out useLocationParams @@ -91,7 +91,7 @@ function SelectorSection() { return (
- {selectedBranch === overview?.defaultBranch ? ( + {selectedBranch === overview?.defaultBranch || selectedBranch === '' ? ( <>

diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.test.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.test.tsx index be947442df..ffb89be2d6 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.test.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.test.tsx @@ -42,7 +42,7 @@ describe('TableHeader', () => { }) it('renders the TableHeader component', () => { - render(, { + render(, { wrapper: wrapper(), }) const testsText = screen.getByText('Tests (50.0K)') @@ -55,7 +55,7 @@ describe('TableHeader', () => { }) it('updates search term on input change', async () => { - render(, { + render(, { wrapper: wrapper(), }) const searchInput = screen.getByPlaceholderText('Search by name') @@ -67,7 +67,7 @@ describe('TableHeader', () => { }) it('resets to default parameters on button click', async () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?term=test'), }) const resetButton = screen.getByText('Reset to default') @@ -78,7 +78,7 @@ describe('TableHeader', () => { }) it('disables reset button when parameters are default', () => { - render(, { + render(, { wrapper: wrapper(), }) const resetButton = screen.getByText('Reset to default') @@ -86,7 +86,7 @@ describe('TableHeader', () => { }) it('enables reset button when parameters are not default', () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?term=test'), }) const resetButton = screen.getByText('Reset to default') @@ -94,7 +94,7 @@ describe('TableHeader', () => { }) it('hides reset button when not on default branch', () => { - render(, { + render(, { wrapper: wrapper(), }) const resetButton = screen.queryByText('Reset to default') @@ -103,7 +103,7 @@ describe('TableHeader', () => { describe('header title', () => { it('renders the default header title', () => { - render(, { + render(, { wrapper: wrapper(), }) const headerTitle = screen.getByText('Tests (50.0K)') @@ -111,7 +111,7 @@ describe('TableHeader', () => { }) it('renders the flaky tests header title', () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?parameter=FLAKY_TESTS'), }) const headerTitle = screen.getByText('Flaky tests (50.0K)') @@ -119,7 +119,7 @@ describe('TableHeader', () => { }) it('renders the failed tests header title', () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?parameter=FAILED_TESTS'), }) const headerTitle = screen.getByText('Failed tests (50.0K)') @@ -127,7 +127,7 @@ describe('TableHeader', () => { }) it('renders the tests header title', () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?parameter=TESTS'), }) const headerTitle = screen.getByText('Tests (50.0K)') @@ -135,7 +135,7 @@ describe('TableHeader', () => { }) it('renders the skipped tests header title', () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?parameter=SKIPPED_TESTS'), }) const headerTitle = screen.getByText('Skipped tests (50.0K)') @@ -143,7 +143,7 @@ describe('TableHeader', () => { }) it('renders the slowest tests header title', () => { - render(, { + render(, { wrapper: wrapper('/gh/codecov/cool-repo/tests?parameter=SLOWEST_TESTS'), }) const headerTitle = screen.getByText('Slowest tests (50.0K)') diff --git a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.tsx b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.tsx index a8cda9514b..dc3da927a3 100644 --- a/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.tsx +++ b/src/pages/RepoPage/FailedTestsTab/FailedTestsPage/TableHeader/TableHeader.tsx @@ -10,7 +10,7 @@ import { defaultQueryParams } from '../SelectorSection' interface TableHeaderProps { totalCount: number - isDefaultBranch: boolean + showResetButton: boolean } const getHeaderTitle = (parameter: keyof typeof TestResultsFilterParameter) => { @@ -21,7 +21,7 @@ const getHeaderTitle = (parameter: keyof typeof TestResultsFilterParameter) => { const TableHeader: React.FC = ({ totalCount, - isDefaultBranch, + showResetButton, }) => { const { params, updateParams } = useLocationParams(defaultQueryParams) // @ts-expect-error, useLocationParams needs to be updated to have full types @@ -54,7 +54,7 @@ const TableHeader: React.FC = ({ setSearchValue={handleSearchChange} data-testid="search-input-failed-tests" /> - {isDefaultBranch ? ( + {showResetButton && ( - ) : null} + )}

diff --git a/src/services/branches/useBranch.tsx b/src/services/branches/useBranch.tsx index 5beb35918b..878369c452 100644 --- a/src/services/branches/useBranch.tsx +++ b/src/services/branches/useBranch.tsx @@ -41,7 +41,7 @@ export interface UseBranchArgs { provider: string owner: string repo: string - branch: string + branch?: string opts?: UseQueryOptions<{ branch: Branch }> } @@ -76,9 +76,15 @@ export const useBranch = ({ opts, }: UseBranchArgs) => useQuery({ - queryKey: ['GetBranch', provider, owner, repo, branch, query], - queryFn: ({ signal }) => - Api.graphql({ + queryKey: ['GetBranch', { provider, owner, repo, branch, query }], + queryFn: ({ signal }) => { + if (!branch) { + return { + branch: null, + } + } + + return Api.graphql({ provider, query, signal, @@ -127,6 +133,7 @@ export const useBranch = ({ return { branch: data?.owner?.repository?.branch ?? null, } - }), - ...(!!opts && opts), + }) + }, + ...(opts || {}), })