Skip to content

Commit

Permalink
Merge branch 'main' into user_onboard_back_cancel_btns
Browse files Browse the repository at this point in the history
  • Loading branch information
calvin-codecov authored Jul 12, 2024
2 parents 1083f42 + fe42ccf commit adbde38
Show file tree
Hide file tree
Showing 72 changed files with 1,923 additions and 625 deletions.
Binary file removed public/logo.png
Binary file not shown.
1 change: 1 addition & 0 deletions public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 21 additions & 18 deletions src/layouts/BaseLayout/BaseLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import OldHeader from 'layouts/OldHeader'
import ErrorBoundary from 'layouts/shared/ErrorBoundary'
import NetworkErrorBoundary from 'layouts/shared/NetworkErrorBoundary'
import ToastNotifications from 'layouts/ToastNotifications'
import { RepoBreadcrumbProvider } from 'pages/RepoPage/context'
import { useImpersonate } from 'services/impersonate'
import { useTracking } from 'services/tracking'
import { useFlags } from 'shared/featureFlags'
Expand Down Expand Up @@ -73,25 +74,27 @@ function BaseLayout({ children }) {

return (
<>
{isFullExperience ? (
<>
{newHeader ? <Header /> : <OldHeader />}
<GlobalTopBanners />
</>
) : (
<Suspense fallback={null}>
{showDefaultOrgSelector && <InstallationHelpBanner />}
</Suspense>
)}
<Suspense fallback={<FullPageLoader />}>
<ErrorBoundary sentryScopes={[['layout', 'base']]}>
<NetworkErrorBoundary>
<main className="container mb-8 mt-2 flex grow flex-col gap-2 md:p-0">
<GlobalBanners />
<OnboardingOrChildren>{children}</OnboardingOrChildren>
</main>
</NetworkErrorBoundary>
</ErrorBoundary>
<RepoBreadcrumbProvider>
{isFullExperience ? (
<>
<GlobalTopBanners />
{newHeader ? <Header /> : <OldHeader />}
</>
) : (
<Suspense fallback={null}>
{showDefaultOrgSelector && <InstallationHelpBanner />}
</Suspense>
)}
<ErrorBoundary sentryScopes={[['layout', 'base']]}>
<NetworkErrorBoundary>
<main className="container mb-8 mt-2 flex grow flex-col gap-2 md:p-0">
<GlobalBanners />
<OnboardingOrChildren>{children}</OnboardingOrChildren>
</main>
</NetworkErrorBoundary>
</ErrorBoundary>
</RepoBreadcrumbProvider>
</Suspense>
{isFullExperience && (
<>
Expand Down
3 changes: 2 additions & 1 deletion src/layouts/BaseLayout/BaseLayout.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jest.mock('shared/GlobalTopBanners', () => () => 'GlobalTopBanners')
jest.mock('./InstallationHelpBanner', () => () => 'InstallationHelpBanner')
jest.mock('pages/TermsOfService', () => () => 'TermsOfService')
jest.mock('pages/DefaultOrgSelector', () => () => 'DefaultOrgSelector')
jest.mock('layouts/Header', () => () => 'New header')

jest.mock('shared/featureFlags')

Expand Down Expand Up @@ -402,7 +403,7 @@ describe('BaseLayout', () => {
wrapper: wrapper(),
})

const newHeader = await screen.findByText('Navigation')
const newHeader = await screen.findByText(/New header/)
expect(newHeader).toBeInTheDocument()
})
})
Expand Down
8 changes: 4 additions & 4 deletions src/layouts/EnterpriseLoginLayout/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function Header() {
to={{
pageName: 'root',
}}
variant="header"
variant="headerDeprecated"
data-testid="homepage-link"
hook="homepage-link"
isExternal
Expand All @@ -33,15 +33,15 @@ function Header() {
<A
hook="header-enterprise"
to={{ pageName: 'docs' }}
variant="header"
variant="headerDeprecated"
showExternalIcon={false}
isExternal
>
Docs
</A>
<A
to={{ pageName: 'support' }}
variant="header"
variant="headerDeprecated"
showExternalIcon={false}
hook="support-link"
isExternal
Expand All @@ -50,7 +50,7 @@ function Header() {
</A>
<A
to={{ pageName: 'blog' }}
variant="header"
variant="headerDeprecated"
showExternalIcon={false}
hook="blog-link"
isExternal
Expand Down
34 changes: 33 additions & 1 deletion src/layouts/Header/Header.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@ import { graphql } from 'msw'
import { setupServer } from 'msw/node'
import { MemoryRouter, Route } from 'react-router-dom'

import config from 'config'

import { User } from 'services/user'

import Header from './Header'

jest.mock('src/layouts/Header/components/Navigator', () => () => 'Navigator')
jest.mock(
'src/layouts/Header/components/UserDropdown',
() => () => 'User Dropdown'
)
jest.mock(
'src/layouts/Header/components/HelpDropdown',
() => () => 'Help Dropdown'
)
jest.mock('src/layouts/Header/components/AdminLink', () => () => 'Admin Link')
jest.mock(
'src/layouts/Header/components/SeatDetails',
() => () => 'Seat Details'
)

const mockUser = {
me: {
Expand Down Expand Up @@ -97,7 +109,7 @@ describe('Header', () => {
setup({})
render(<Header />, { wrapper })

const text = await screen.findByText('Navigation')
const text = await screen.findByText('Navigator')
expect(text).toBeInTheDocument()
})
})
Expand All @@ -111,4 +123,24 @@ describe('Header', () => {
expect(link).toBeInTheDocument()
})
})

describe('when on self-hosted', () => {
it('shows seat details', async () => {
config.IS_SELF_HOSTED = true
setup({})
render(<Header />, { wrapper })

const text = await screen.findByText(/Seat Details/)
expect(text).toBeInTheDocument()
})

it('shows Admin link', async () => {
config.IS_SELF_HOSTED = true
setup({})
render(<Header />, { wrapper })

const text = await screen.findByText(/Admin Link/)
expect(text).toBeInTheDocument()
})
})
})
18 changes: 16 additions & 2 deletions src/layouts/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Suspense } from 'react'

import config from 'config'

import { useUser } from 'services/user'

import AdminLink from './components/AdminLink'
import HelpDropdown from './components/HelpDropdown'
import Navigator from './components/Navigator'
import SeatDetails from './components/SeatDetails'
import UserDropdown from './components/UserDropdown'

function Header() {
Expand All @@ -12,9 +19,16 @@ function Header() {

return (
<div className="container flex h-14 w-full items-center">
<div className="flex-1">Navigation</div>
<div className="flex-1">
<Navigator />
</div>
<div className="flex items-center gap-4">
<div>Self hosted stuff</div>
{config.IS_SELF_HOSTED ? (
<Suspense fallback={null}>
<SeatDetails />
<AdminLink />
</Suspense>
) : null}
<HelpDropdown />
<UserDropdown />
</div>
Expand Down
86 changes: 86 additions & 0 deletions src/layouts/Header/components/AdminLink/AdminLink.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen } from '@testing-library/react'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { MemoryRouter, Route } from 'react-router-dom'

import AdminLink from './AdminLink'

const wrapper: ({
initialEntries,
}: {
initialEntries?: string
}) => React.FC<React.PropsWithChildren> =
({ initialEntries = '/gh' }) =>
({ children }) =>
(
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[initialEntries]}>
<Route path="/:provider" exact>
{children}
</Route>
</MemoryRouter>
</QueryClientProvider>
)

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

const server = setupServer()
beforeAll(() => server.listen())
beforeEach(() => {
server.resetHandlers()
queryClient.clear()
})
afterAll(() => server.close())

describe('AdminLink', () => {
function setup(data = {}) {
server.use(
rest.get('/internal/users/current', (req, res, ctx) =>
res(ctx.status(200), ctx.json(data))
)
)
}

describe('user is an admin', () => {
beforeEach(() => {
setup({
activated: false,
email: '[email protected]',
isAdmin: true,
name: 'Codecov',
ownerid: 2,
username: 'codecov',
})
})

it('renders link to access page', async () => {
render(<AdminLink />, { wrapper: wrapper({}) })
const link = await screen.findByText(/Admin/)

expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', '/admin/gh/access')
})
})

describe('user is not an admin', () => {
beforeEach(() => {
setup({
activated: false,
email: '[email protected]',
isAdmin: false,
name: 'Codecov',
ownerid: 2,
username: 'codecov',
})
})

const { container } = render(<AdminLink />, { wrapper: wrapper({}) })

it('renders nothing', () => {
expect(container).toBeEmptyDOMElement()
})
})
})
24 changes: 24 additions & 0 deletions src/layouts/Header/components/AdminLink/AdminLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useSelfHostedCurrentUser } from 'services/selfHosted'
import A from 'ui/A'
import Icon from 'ui/Icon'

function AdminLink() {
const { data: user } = useSelfHostedCurrentUser()

if (!user?.isAdmin) {
return null
}

return (
<A
variant="header"
to={{ pageName: 'access' }}
isExternal={false}
hook="header-admin-link"
>
<Icon size="md" name="cog" variant="solid" /> Admin
</A>
)
}

export default AdminLink
1 change: 1 addition & 0 deletions src/layouts/Header/components/AdminLink/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './AdminLink'
44 changes: 44 additions & 0 deletions src/layouts/Header/components/Navigator/Navigator.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router-dom'

import { RepoBreadcrumbProvider } from 'pages/RepoPage/context'

import Navigator from './Navigator'

jest.mock('ui/Breadcrumb', () => () => 'Breadcrumb')

const wrapper: (initialEntries?: string) => React.FC<React.PropsWithChildren> =
(initialEntries = '/gh/codecov') =>
({ children }) =>
(
<MemoryRouter initialEntries={[initialEntries]}>
<RepoBreadcrumbProvider>
<Route path="/:provider/:owner" exact>
{children}
</Route>
<Route path="/:provider/:owner/:repo" exact>
{children}
</Route>
</RepoBreadcrumbProvider>
</MemoryRouter>
)

describe('Header Navigator', () => {
describe('when on repo page', () => {
it('should render repo breadcrumb', async () => {
render(<Navigator />, { wrapper: wrapper('/gh/codecov/test-repo') })

const breadcrumb = await screen.findByText('Breadcrumb')
expect(breadcrumb).toBeInTheDocument()
})
})

describe('temp: when not on repo page', () => {
it('should render MyContextSwitcher', async () => {
render(<Navigator />, { wrapper: wrapper() })

const switcher = await screen.findByText('MyContextSwitcher')
expect(switcher).toBeInTheDocument()
})
})
})
17 changes: 17 additions & 0 deletions src/layouts/Header/components/Navigator/Navigator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useRouteMatch } from 'react-router-dom'

import { useCrumbs } from 'pages/RepoPage/context'
import Breadcrumb from 'ui/Breadcrumb'

function Navigator() {
const { path } = useRouteMatch()
const { breadcrumbs } = useCrumbs()

if (path.startsWith('/:provider/:owner/:repo')) {
return <Breadcrumb paths={breadcrumbs} />
}

return <p>MyContextSwitcher</p>
}

export default Navigator
1 change: 1 addition & 0 deletions src/layouts/Header/components/Navigator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Navigator'
Loading

0 comments on commit adbde38

Please sign in to comment.