Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Team plan repos list #2357

Merged
merged 28 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8e88ac0
first pass, wheeeeeo
RulaKhaled Oct 24, 2023
2ae6960
Sorting functionality
RulaKhaled Oct 25, 2023
94ac859
Update with tests
RulaKhaled Oct 25, 2023
7b01abc
wrap up repos list:
RulaKhaled Oct 30, 2023
7306378
Spelling correction (#2336)
drazisil-codecov Oct 23, 2023
b8aa1d3
616 add patch setction pr page team tier (#2337)
adrian-codecov Oct 23, 2023
d44a683
fix: Filter out certain browser from sending events to Sentry (#2338)
nicholas-codecov Oct 23, 2023
8f7f2dc
feat: Hide Flag MultiSelect when on Team Plan on Commit Detail Page (…
nicholas-codecov Oct 23, 2023
e6349ec
feat, ref: Disable Flag MultiSelect on Coverage Tab when on a Team Pl…
nicholas-codecov Oct 24, 2023
31045c3
feat: Grab flags in IndirectChangesTable and pass along with request …
nicholas-codecov Oct 24, 2023
bd0277c
feat: Update CommitDetailPage FilesChangedTable to pass along flags (…
nicholas-codecov Oct 24, 2023
f79c32e
ref: Update TOS to work for service less users. (#2321)
RulaKhaled Oct 24, 2023
95c78e6
restructure folders anticipating second header component for team tie…
adrian-codecov Oct 24, 2023
94d9d20
feat: add hook for commit detail page team tier (#2341)
adrian-codecov Oct 24, 2023
96c2555
build: Update PostCSS (#2346)
nicholas-codecov Oct 25, 2023
8677af5
Migrate TextInput to TypeScript (#2342)
rohitvinnakota-codecov Oct 25, 2023
f0c60c7
Connect flag selector to flags filter on PR details page (#2343)
terry-codecov Oct 26, 2023
35eb0be
fix: Attempting to fix CommitDetailPage and RepoPage Tests (#2350)
nicholas-codecov Oct 26, 2023
f75ba44
Add patch section commit detail page team tier (#2344)
adrian-codecov Oct 26, 2023
be108e9
Convert Sparkline to typescript (#2347)
rohitvinnakota-codecov Oct 27, 2023
68d7028
use enum
RulaKhaled Oct 31, 2023
56cb7b0
Merge branch 'main' into team-repos-list
RulaKhaled Oct 31, 2023
7eb59f7
quick fixes
RulaKhaled Oct 31, 2023
e203b78
Update use memo
RulaKhaled Nov 2, 2023
5790a82
Update tests with getSortingOption
RulaKhaled Nov 3, 2023
f5731e9
pull out the function of the test block
RulaKhaled Nov 3, 2023
61d685a
Merge branch 'main' into team-repos-list
RulaKhaled Nov 3, 2023
eefd552
Merge branch 'main' into team-repos-list
RulaKhaled Nov 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/pages/OwnerPage/OwnerPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ function OwnerPage() {
<Tabs owner={ownerData} provider={provider} />
)}
<ActiveContext.Provider value={params?.repoDisplay}>
<ListRepo
canRefetch={ownerData?.isCurrentUserPartOfOrg}
owner={ownerData?.username}
/>
<ListRepo canRefetch={ownerData?.isCurrentUserPartOfOrg} />
</ActiveContext.Provider>
</div>
</div>
Expand Down
10 changes: 10 additions & 0 deletions src/services/repos/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ export const nonActiveOrderingOptions = [
direction: 'DESC',
},
]

export const OrderingDirection = Object.freeze({
DESC: 'DESC',
ASC: 'ASC',
})

export const TeamOrdering = Object.freeze({
COMMIT_DATE: 'COMMIT_DATE',
NAME: 'NAME',
})
33 changes: 31 additions & 2 deletions src/services/repos/useReposTeam.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ describe('useReposTeam', () => {
graphql.query('GetReposTeam', (req, res, ctx) => {
const data = {
owner: {
isCurrentUserPartOfOrg: true,
repositories: {
edges: req.variables.after
? [
Expand Down Expand Up @@ -131,7 +132,17 @@ describe('useReposTeam', () => {

await waitFor(() =>
expect(result.current.data).toEqual({
repos: [repo1, repo2],
pages: [
{
repos: [repo1, repo2],
isCurrentUserPartOfOrg: true,
pageInfo: {
hasNextPage: true,
endCursor: 'MjAyMC0wOC0xMSAxNzozMDowMiswMDowMHwxMDA=',
},
},
],
pageParams: [undefined],
})
)
})
Expand Down Expand Up @@ -165,7 +176,25 @@ describe('useReposTeam', () => {

await waitFor(() =>
expect(result.current.data).toEqual({
repos: [repo1, repo2, repo3, repo4],
pages: [
{
repos: [repo1, repo2],
isCurrentUserPartOfOrg: true,
pageInfo: {
hasNextPage: true,
endCursor: 'MjAyMC0wOC0xMSAxNzozMDowMiswMDowMHwxMDA=',
},
},
{
repos: [repo3, repo4],
isCurrentUserPartOfOrg: true,
pageInfo: {
hasNextPage: false,
endCursor: 'aa',
},
},
],
pageParams: [undefined, 'MjAyMC0wOC0xMSAxNzozMDowMiswMDowMHwxMDA='],
})
)
})
Expand Down
41 changes: 22 additions & 19 deletions src/services/repos/useReposTeam.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ import { mapEdges } from 'shared/utils/graphql'

import { orderingOptions } from './config'

const RepositorySchema = z.object({
name: z.string(),
active: z.boolean(),
activated: z.boolean(),
private: z.boolean(),
latestCommitAt: z.string().nullable(),
lines: z.number().nullable(),
author: z.object({
username: z.string().nullable(),
}),
})
const RepositorySchema = z
.object({
name: z.string(),
active: z.boolean(),
activated: z.boolean().nullable(),
private: z.boolean(),
latestCommitAt: z.string().nullable(),
lines: z.number().nullable(),
author: z.object({
username: z.string().nullable(),
}),
})
.nullable()

export type Repository = z.infer<typeof RepositorySchema>

const repositoryFragment = `
fragment RepoForList on Repository {
Expand All @@ -38,7 +42,7 @@ interface FetchReposTeamArgs {
owner: string
variables: {
filters: {
activated: boolean
activated?: boolean
term?: string
repoNames?: string[]
}
Expand All @@ -53,6 +57,7 @@ interface FetchReposTeamArgs {
const RequestSchema = z.object({
owner: z
.object({
isCurrentUserPartOfOrg: z.boolean(),
repositories: z
.object({
edges: z.array(
Expand Down Expand Up @@ -80,6 +85,7 @@ function fetchReposForOwner({
const query = `
query GetReposTeam($filters: RepositorySetFilters!, $owner: String!, $ordering: RepositoryOrdering!, $direction: OrderingDirection!, $after: String, $first: Int) {
owner(username: $owner) {
isCurrentUserPartOfOrg
repositories(filters: $filters, ordering: $ordering, orderingDirection: $direction, first: $first, after: $after) {
edges {
node {
Expand Down Expand Up @@ -120,16 +126,17 @@ function fetchReposForOwner({
return {
repos: mapEdges(owner?.repositories),
pageInfo: owner?.repositories?.pageInfo,
isCurrentUserPartOfOrg: !!owner?.isCurrentUserPartOfOrg,
}
})
}

interface UseReposTeamArgs {
activated: boolean
activated?: boolean
term?: string
owner: string
sortItem?: {
ordering: string
ordering?: string
direction: string
}
first?: number
Expand All @@ -153,7 +160,7 @@ export function useReposTeam({
first,
}

const { data, ...rest } = useInfiniteQuery(
return useInfiniteQuery(
['GetReposTeam', provider, variables, owner],
({ pageParam, signal }) => {
return fetchReposForOwner({
Expand All @@ -170,8 +177,4 @@ export function useReposTeam({
...options,
}
)
return {
data: { repos: data?.pages.map((page) => page.repos).flat() },
...rest,
}
}
1 change: 1 addition & 0 deletions src/shared/ListRepo/InactiveRepo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './InactiveRepo'
31 changes: 23 additions & 8 deletions src/shared/ListRepo/ListRepo.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
/* */
import PropTypes from 'prop-types'
import { Suspense, useContext } from 'react'
import { useParams } from 'react-router-dom'

import { useLocationParams } from 'services/navigation'
import { nonActiveOrderingOptions, orderingOptions } from 'services/repos'
import { TierNames, useTier } from 'services/tier'
import { ActiveContext } from 'shared/context'
import { useFlags } from 'shared/featureFlags'
import Spinner from 'ui/Spinner'

import OrgControlTable from './OrgControlTable'
import ReposTable from './ReposTable'
import ReposTableTeam from './ReposTableTeam'

const defaultQueryParams = {
search: '',
Expand All @@ -20,11 +24,18 @@ const defaultQueryParams = {
export const repoDisplayOptions = Object.freeze({
ACTIVE: { text: 'Active', status: true },
INACTIVE: { text: 'Inactive', status: false },
ALL: { text: 'All', status: null },
ALL: { text: 'All', status: undefined },
})

function ListRepo({ owner, canRefetch }) {
function ListRepo({ canRefetch }) {
const { provider, owner } = useParams()
const { params, updateParams } = useLocationParams(defaultQueryParams)
const { data: tierData } = useTier({ provider, owner })
const { multipleTiers } = useFlags({
multipleTiers: false,
})

const showTeamRepos = tierData === TierNames.TEAM && multipleTiers

const repoDisplay = useContext(ActiveContext)

Expand Down Expand Up @@ -67,20 +78,24 @@ function ListRepo({ owner, canRefetch }) {
updateParams({ search })
}}
canRefetch={canRefetch}
showTeamRepos={showTeamRepos}
/>
<Suspense fallback={loadingState}>
<ReposTable
sortItem={sortItem}
owner={owner}
searchValue={params.search}
/>
{showTeamRepos ? (
<ReposTableTeam searchValue={params.search} />
) : (
<ReposTable
sortItem={sortItem}
owner={owner}
searchValue={params.search}
/>
)}
</Suspense>
</>
)
}

ListRepo.propTypes = {
owner: PropTypes.string,
canRefetch: PropTypes.bool.isRequired,
}

Expand Down
93 changes: 80 additions & 13 deletions src/shared/ListRepo/ListRepo.spec.jsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,91 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { graphql } from 'msw'
import { setupServer } from 'msw/node'
import { MemoryRouter, Route } from 'react-router-dom'

import { TierNames } from 'services/tier'
import { ActiveContext } from 'shared/context'
import { useFlags } from 'shared/featureFlags'

import ListRepo, { repoDisplayOptions } from './ListRepo'

jest.mock('shared/featureFlags')

jest.mock('./OrgControlTable/RepoOrgNotFound', () => () => 'RepoOrgNotFound')
jest.mock('./ReposTable', () => () => 'ReposTable')
jest.mock('./ReposTableTeam', () => () => 'ReposTableTeam.tsx')

const server = setupServer()

beforeAll(() => {
server.listen({ onUnhandledRequest: 'warn' })
console.error = () => {}
})
beforeEach(() => {
queryClient.clear()
server.resetHandlers()
})
afterAll(() => {
server.close()
})

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})

let testLocation

const wrapper =
({ url = '', path = '', repoDisplay = '' } = {}) =>
({ children }) =>
(
<MemoryRouter initialEntries={[url]}>
<ActiveContext.Provider value={repoDisplay}>
{children}
<Route
path={path}
render={({ location }) => {
testLocation = location
return null
}}
/>
</ActiveContext.Provider>
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[url]}>
<ActiveContext.Provider value={repoDisplay}>
{children}
<Route
path={path}
render={({ location }) => {
testLocation = location
return null
}}
/>
</ActiveContext.Provider>
</MemoryRouter>
</QueryClientProvider>
)

describe('ListRepo', () => {
function setup() {
function setup({ tierValue = TierNames.PRO } = { tierValue: TierNames.PRO }) {
const user = userEvent.setup()

useFlags.mockReturnValue({
multipleTiers: true,
})

server.use(
graphql.query('OwnerTier', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.data({ owner: { plan: { tierName: tierValue } } })
)
})
)

return { user }
}

describe('renders', () => {
beforeEach(() => {
setup()
})

it('renders the children', () => {
render(<ListRepo canRefetch />, {
wrapper: wrapper(),
Expand All @@ -55,6 +104,10 @@ describe('ListRepo', () => {
})

describe('reads URL parameters', () => {
beforeEach(() => {
setup()
})

it('reads search parameter from URL', () => {
render(<ListRepo canRefetch />, {
wrapper: wrapper({ url: '?search=thisisaquery' }),
Expand Down Expand Up @@ -256,4 +309,18 @@ describe('ListRepo', () => {
expect(options.length).toBe(2)
})
})

describe('when rendered for team tier', () => {
beforeEach(() => {
setup({ tierValue: TierNames.TEAM })
})

it('renders the team table', async () => {
render(<ListRepo canRefetch />, {
wrapper: wrapper(),
})
const table = await screen.findByText(/ReposTableTeam/)
expect(table).toBeInTheDocument()
})
})
})
1 change: 1 addition & 0 deletions src/shared/ListRepo/NoReposBlock/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './NoReposBlock'
Loading
Loading