Skip to content

Commit

Permalink
feat: Team plan repos list (#2357)
Browse files Browse the repository at this point in the history
* first pass, wheeeeeo

* Sorting functionality

* Update with tests

* wrap up repos list:

* Spelling correction (#2336)

* 616 add patch setction pr page team tier (#2337)

* feat: add header component for team tier customers

* feat: converted Header.jsx to Header.tsx + tests

* fix: add comparison schema types

* fix: Filter out certain browser from sending events to Sentry (#2338)

Filter out events for a given browser because they don't have all JS functions fully implemented causing issues that we cannot address.

* feat: Hide Flag MultiSelect when on Team Plan on Commit Detail Page (#2327)

We will need to hide the flag multi select on the commit detail page when the user is on the team plan as they are not an available feature to those users.

GH codecov/engineering-team#687

* feat, ref: Disable Flag MultiSelect on Coverage Tab when on a Team Plan (#2329)

Disable the flag multi-select on the coverage tab when users/orgs are on a team plan.

GH codecov/engineering-team#685

* feat: Grab flags in IndirectChangesTable and pass along with request (#2335)

Update indirect changes tab on the commit detail page to grab flags from the url params and pass along as query args.

GH codecov/engineering-team#348

* feat: Update CommitDetailPage FilesChangedTable to pass along flags (#2334)

Update the files changed tab on the commit detail page to grab flags url param and pass along as query args.

GH codecov/engineering-team#347

* ref: Update TOS to work for service less users. (#2321)

* Update with service less requests

* Make sure hook supports service less

* feat: Add Flag MultiSelect to CommitPageTabs (#2303)

Add a feature flagged multi select to the CommitPageTabs component.

GH codecov/engineering-team#344

* Add patch column to pulls table (#2308)

* feat: add patch column to the pulls list page

* uncomment development settings

* feat: Create Team Plan Table for the Files Changed Table on the Commit Detail Page (#2309)

Create new commit fetching hooks for the team plan, as well as creating a new table for files changed tab on the
commit detail page for the new team plan.

GH codecov/engineering-team#633

* Setup pull request page to pass around selected flags (#2282)

* feat: setup pull request page to pass around selected flags in links

* Feedback, fix passing links to files+folders, additional testing

* fix file explorer test failing on href match due to new query param pass through

* airplane commit, cant check local dev server: Resolve merge issues / tests + unify codebases missed of commitSHA and commitSha to just commitSha

* Prevent multislect from collapsing + wire up PR details page to pass through flags links

* Fix accidental removal of ref on usePrefetchPullFileEntry

* Add patch column to pulls table (#2308)

* feat: add patch column to the pulls list page

* uncomment development settings

* feat: Create Team Plan Table for the Files Changed Table on the Commit Detail Page (#2309)

Create new commit fetching hooks for the team plan, as well as creating a new table for files changed tab on the
commit detail page for the new team plan.

GH codecov/engineering-team#633

---------

Co-authored-by: Adrian <adrian@codecov.io>
Co-authored-by: nicholas-codecov <nicholas.deschenes@sentry.io>

* chore: Remove segment from frontend (#2314)

* Update with tests

* test with support service less

* adjust logic to handle original route

* it's fine it works with no providers

---------

Co-authored-by: nicholas-codecov <nicholas.deschenes@sentry.io>
Co-authored-by: Adrian <adrian@codecov.io>
Co-authored-by: Terry <87824812+terry-codecov@users.noreply.github.com>

* restructure folders anticipating second header component for team tier (#2340)

* feat: add hook for commit detail page team tier (#2341)

* build: Update PostCSS (#2346)

Update PostCSS dependency.

* Migrate TextInput to TypeScript (#2342)

* Migrate textinput to TS

* Add story

* formatting

* organize imports

* Connect flag selector to flags filter on PR details page (#2343)

* feat: Update impacted files resolver for use pull, connect the flag selector to the api.

* update missing logic as requested + unknown flags message to be aligned with repo overview design

* Noticed the changing the pull query broke impacted files while smoke testing, dupicated the same compatibility work for the new resolver.

* Refactor to use a impacted files enum as requested.

* fix: Attempting to fix CommitDetailPage and RepoPage Tests (#2350)

Addressing flaky tests in CommitDetailPage and RepoPage.

* Add patch section commit detail page team tier (#2344)

* restructure folders anticipating second header component for team tier

* feat: add patch coverage section to commit detail page for team tier customer

* fix: rename HeaderDefault to HeaderTeam for team file

* Convert Sparkline to typescript (#2347)

* Convert sparkline to typescript

* Consistent type defs

* better variable names

* use enum

* quick fixes

* Update use memo

* Update tests with getSortingOption

* pull out the function of the test block

---------

Co-authored-by: Joe Becher <joe@codecov.io>
Co-authored-by: Adrian <adrian@codecov.io>
Co-authored-by: nicholas-codecov <nicholas.deschenes@sentry.io>
Co-authored-by: Terry <87824812+terry-codecov@users.noreply.github.com>
Co-authored-by: Rohit Vinnakota <148245014+rohitvinnakota-codecov@users.noreply.github.com>
  • Loading branch information
6 people authored Nov 3, 2023
1 parent f507526 commit fb643b0
Show file tree
Hide file tree
Showing 21 changed files with 1,319 additions and 67 deletions.
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

0 comments on commit fb643b0

Please sign in to comment.