Skip to content

Commit

Permalink
feat: Add org list for multi org setups on plan page (#3407)
Browse files Browse the repository at this point in the history
  • Loading branch information
spalmurray-codecov authored Oct 21, 2024
1 parent 34c9ae8 commit ddf8ef4
Show file tree
Hide file tree
Showing 4 changed files with 484 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { delay, graphql, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { mockAllIsIntersecting } from 'react-intersection-observer/test-utils'
import { MemoryRouter, Route, useLocation } from 'react-router'

import AccountOrgs from './AccountOrgs'

Expand All @@ -14,14 +19,146 @@ const mockAccount: Account = {
},
}

const org1 = {
username: 'org1',
activatedUserCount: 7,
isCurrentUserPartOfOrg: true,
}

const org2 = {
username: 'org2',
activatedUserCount: 4,
isCurrentUserPartOfOrg: false,
}

const org3 = {
username: 'org3',
activatedUserCount: 2,
isCurrentUserPartOfOrg: true,
}

const mockPageOne = {
owner: {
account: {
organizations: {
edges: [
{
node: org1,
},
{
node: org2,
},
],
pageInfo: {
hasNextPage: true,
endCursor: 'asdf',
},
},
},
},
}

const mockPageTwo = {
owner: {
account: {
organizations: {
edges: [
{
node: org3,
},
{
node: null,
},
],
pageInfo: {
hasNextPage: false,
endCursor: null,
},
},
},
},
}

const mockReversedOrgs = {
owner: {
account: {
organizations: {
edges: [
{
node: org3,
},
{
node: org2,
},
],
pageInfo: {
hasNextPage: true,
endCursor: 'zzzz',
},
},
},
},
}

let testLocation: ReturnType<typeof useLocation>

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
suspense: true,
},
},
})
const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
<MemoryRouter initialEntries={['/plan/gh/codecov']}>
<Route path="/plan/:provider/:owner">{children}</Route>
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={['/plan/gh/codecov']}>
<Route path="/plan/:provider/:owner">{children}</Route>
<Route
path="*"
render={({ location }) => {
testLocation = location
return null
}}
/>
</MemoryRouter>
</QueryClientProvider>
)

const server = setupServer()

beforeAll(() => server.listen())
afterEach(() => {
queryClient.clear()
server.resetHandlers()
})
afterAll(() => server.close())

describe('AccountOrgs', () => {
function setup() {
mockAllIsIntersecting(false)
server.use(
graphql.query('InfiniteAccountOrganizations', async (info) => {
if (info.variables.direction === 'DESC') {
return HttpResponse.json({
data: mockReversedOrgs,
})
}
if (info.variables.after) {
await delay(100) // for testing the spinner
return HttpResponse.json({
data: mockPageTwo,
})
}
return HttpResponse.json({
data: mockPageOne,
})
})
)
}

it('renders Header', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const header = await screen.findByText('Account details')
Expand All @@ -33,6 +170,7 @@ describe('AccountOrgs', () => {
})

it('renders total orgs', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const label = await screen.findByText('Total organizations')
Expand All @@ -43,6 +181,7 @@ describe('AccountOrgs', () => {
})

it('renders total seats', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const label = await screen.findByText('Total seats')
Expand All @@ -53,6 +192,7 @@ describe('AccountOrgs', () => {
})

it('renders seats remaining', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const label = await screen.findByText('Seats remaining')
Expand All @@ -63,4 +203,118 @@ describe('AccountOrgs', () => {
)
expect(number).toBeInTheDocument()
})

describe('organization list', () => {
it('renders column headers', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const nameHeader = await screen.findByText('Organization name')
expect(nameHeader).toBeInTheDocument()
const membersHeader = await screen.findByText('Activated members')
expect(membersHeader).toBeInTheDocument()
})

it('renders column data', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const org1 = await screen.findByText('org1')
expect(org1).toBeInTheDocument()
const org1Members = await screen.findByRole('cell', { name: '7' })
expect(org1Members).toBeInTheDocument()
const org2 = await screen.findByText('org2')
expect(org2).toBeInTheDocument()
const org2Members = await screen.findByRole('cell', { name: '4' })
expect(org2Members).toBeInTheDocument()
})

it('renders not in org message', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const notInOrg = await screen.findByText('Not a member')
expect(notInOrg).toBeInTheDocument()
})

describe('when another page of data is available', () => {
describe('and bottom of table is visible', () => {
it('fetches next page of data', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const org1 = await screen.findByText('org1')
expect(org1).toBeInTheDocument()
let org3 = screen.queryByText('org3')
expect(org3).not.toBeInTheDocument()
let org3Members = screen.queryByRole('cell', { name: '2' })
expect(org3Members).not.toBeInTheDocument()

mockAllIsIntersecting(true)

org3 = await screen.findByText('org3')
expect(org3).toBeInTheDocument()
org3Members = await screen.findByRole('cell', { name: '2' })
expect(org3Members).toBeInTheDocument()
})
})
})

describe('when user clicks on the Org name header', () => {
it('changes the ordering direction', async () => {
setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })
const user = userEvent.setup()

const header = await screen.findByText('Organization name')
expect(header).toBeInTheDocument()

let org1: HTMLElement | null = await screen.findByText('org1')
expect(org1).toBeInTheDocument()

await user.click(header)

const org3 = await screen.findByText('org3')
expect(org3).toBeInTheDocument()
org1 = screen.queryByText('org1')
expect(org1).not.toBeInTheDocument()
})
})

describe('when user clicks on activated user count', () => {
describe('and they are a member of that org', () => {
it('redirects them to the member page for that org', async () => {
setup()
const user = userEvent.setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const org1Members = await screen.findByRole('link', { name: '7' })
expect(org1Members).toBeInTheDocument()

await user.click(org1Members)

await waitFor(() =>
expect(testLocation.pathname).toBe('/members/gh/org1')
)
})
})

describe('and they are not a member of that org', () => {
it('does nothing', async () => {
setup()
const user = userEvent.setup()
render(<AccountOrgs account={mockAccount} />, { wrapper })

const org2Members = await screen.findByRole('cell', { name: '4' })
expect(org2Members).toBeInTheDocument()

await user.click(org2Members)

await waitFor(() =>
expect(testLocation.pathname).toBe('/plan/gh/codecov')
)
})
})
})
})
})
Loading

0 comments on commit ddf8ef4

Please sign in to comment.