-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add activation banner for trial eligible owners 1/4 (#2817)
* feat: Add activation banner for trial eligible owners * pull out interface + spec stuff * Refactor CircleCI repo onboarding into one file (#2806) * Refactor Other CI repo onboarding into one file (#2807) * Update repo onboarding title position and page alignment (#2818) * sec: 390 - Add validation for potential XSS vuln (#2797) * add tests, and validation for provider * add back supportServiceless param * ref: 1548 Part 1: Convert all Header files to TS (#2821) * ref all header files to TS * remove prop types and rebase * fix: Remove repository from GUT settings page header (#2823) Small tweak removing `repository` from the GUT settings page. * Install radix-ui react radio group (#2825) * Update repo onboarding steps with new Card component (#2819) GH codecov/engineering-team#1665 * Update tests * Update to correct import orders * Update tests --------- Co-authored-by: Spencer Murray <[email protected]> Co-authored-by: ajay-sentry <[email protected]> Co-authored-by: nicholas-codecov <[email protected]>
- Loading branch information
1 parent
b5abb62
commit bfae2b1
Showing
8 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
src/pages/RepoPage/CoverageOnboarding/ActivationBanner/ActivationBanner.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' | ||
import { render, screen, waitFor } from '@testing-library/react' | ||
import { graphql } from 'msw' | ||
import { setupServer } from 'msw/node' | ||
import { MemoryRouter, Route } from 'react-router-dom' | ||
|
||
import ActivationBanner from './ActivationBanner' | ||
|
||
jest.mock('./TrialEligibleBanner', () => () => 'TrialEligibleBanner') | ||
|
||
const queryClient = new QueryClient() | ||
|
||
const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => ( | ||
<QueryClientProvider client={queryClient}> | ||
<MemoryRouter initialEntries={['/gh/codecov/gazebo/new']}> | ||
<Route path="/:provider/:owner/:repo/new">{children}</Route> | ||
</MemoryRouter> | ||
</QueryClientProvider> | ||
) | ||
|
||
const server = setupServer() | ||
|
||
beforeAll(() => { | ||
server.listen() | ||
}) | ||
afterEach(() => { | ||
queryClient.clear() | ||
server.resetHandlers() | ||
}) | ||
afterAll(() => { | ||
server.close() | ||
}) | ||
|
||
const mockTrialData = { | ||
baseUnitPrice: 10, | ||
benefits: [], | ||
billingRate: 'monthly', | ||
marketingName: 'Users Basic', | ||
monthlyUploadLimit: 250, | ||
value: 'users-basic', | ||
trialStatus: 'ONGOING', | ||
trialStartDate: '2023-01-01T08:55:25', | ||
trialEndDate: '2023-01-10T08:55:25', | ||
trialTotalDays: 0, | ||
pretrialUsersCount: 0, | ||
planUserCount: 1, | ||
} | ||
|
||
describe('ActivationBanner', () => { | ||
function setup( | ||
privateRepos = true, | ||
trialStatus = 'NOT_STARTED', | ||
value = 'users-basic' | ||
) { | ||
server.use( | ||
graphql.query('GetPlanData', (req, res, ctx) => { | ||
return res( | ||
ctx.status(200), | ||
ctx.data({ | ||
owner: { | ||
hasPrivateRepos: privateRepos, | ||
plan: { | ||
...mockTrialData, | ||
trialStatus, | ||
value, | ||
}, | ||
pretrialPlan: { | ||
baseUnitPrice: 10, | ||
benefits: [], | ||
billingRate: 'monthly', | ||
marketingName: 'Users Basic', | ||
monthlyUploadLimit: 250, | ||
value: 'users-basic', | ||
}, | ||
}, | ||
}) | ||
) | ||
}) | ||
) | ||
} | ||
|
||
it('renders trial eligible banner if user is eligible to trial', async () => { | ||
setup() | ||
render(<ActivationBanner />, { wrapper }) | ||
|
||
const trialEligibleBanner = await screen.findByText(/TrialEligibleBanner/) | ||
expect(trialEligibleBanner).toBeInTheDocument() | ||
}) | ||
|
||
it('does not render trial eligible banner if user is not eligible to trial', async () => { | ||
setup(false) | ||
const { container } = render(<ActivationBanner />, { wrapper }) | ||
|
||
await waitFor(() => queryClient.isFetching) | ||
await waitFor(() => !queryClient.isFetching) | ||
|
||
expect(container).toBeEmptyDOMElement() | ||
}) | ||
}) |
36 changes: 36 additions & 0 deletions
36
src/pages/RepoPage/CoverageOnboarding/ActivationBanner/ActivationBanner.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { useParams } from 'react-router-dom' | ||
|
||
import { TrialStatuses, usePlanData } from 'services/account' | ||
import { isBasicPlan } from 'shared/utils/billing' | ||
|
||
import TrialEligibleBanner from './TrialEligibleBanner' | ||
|
||
interface URLParams { | ||
provider: string | ||
owner: string | ||
} | ||
|
||
function ActivationBanner() { | ||
const { owner, provider } = useParams<URLParams>() | ||
const { data: planData } = usePlanData({ | ||
owner, | ||
provider, | ||
}) | ||
const isNewTrial = planData?.plan?.trialStatus === TrialStatuses.NOT_STARTED | ||
const isTrialEligible = | ||
isBasicPlan(planData?.plan?.value) && | ||
planData?.hasPrivateRepos && | ||
isNewTrial | ||
|
||
if (!isTrialEligible) { | ||
return null | ||
} | ||
|
||
return ( | ||
<div className="mt-4"> | ||
<TrialEligibleBanner /> | ||
</div> | ||
) | ||
} | ||
|
||
export default ActivationBanner |
91 changes: 91 additions & 0 deletions
91
...Page/CoverageOnboarding/ActivationBanner/TrialEligibleBanner/TrialEligibleBanner.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +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 TrialEligibleBanner from './TrialEligibleBanner' | ||
|
||
const queryClient = new QueryClient() | ||
|
||
const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => ( | ||
<QueryClientProvider client={queryClient}> | ||
<MemoryRouter initialEntries={['/gh/codecov/gazebo/new']}> | ||
<Route path="/:provider/:owner/:repo/new">{children}</Route> | ||
</MemoryRouter> | ||
</QueryClientProvider> | ||
) | ||
|
||
const server = setupServer() | ||
|
||
beforeAll(() => { | ||
server.listen() | ||
}) | ||
afterEach(() => { | ||
queryClient.clear() | ||
server.resetHandlers() | ||
}) | ||
afterAll(() => { | ||
server.close() | ||
}) | ||
|
||
describe('TrialEligibleBanner', () => { | ||
function setup() { | ||
const mockTrialMutationVariables = jest.fn() | ||
const user = userEvent.setup() | ||
server.use( | ||
graphql.mutation('startTrial', (req, res, ctx) => { | ||
mockTrialMutationVariables(req?.variables) | ||
|
||
return res(ctx.status(200)) | ||
}) | ||
) | ||
|
||
return { mockTrialMutationVariables, user } | ||
} | ||
|
||
it('renders the banner with correct content', () => { | ||
setup() | ||
render(<TrialEligibleBanner />, { wrapper }) | ||
|
||
const bannerHeading = screen.getByRole('heading', { | ||
name: /start a free 14-day trial on pro team plan/i, | ||
}) | ||
expect(bannerHeading).toBeInTheDocument() | ||
|
||
const list = screen.getByText(/Unlimited members/i) | ||
expect(list).toBeInTheDocument() | ||
}) | ||
|
||
it('renders correct links', () => { | ||
setup() | ||
render(<TrialEligibleBanner />, { wrapper }) | ||
|
||
const upgradeLink = screen.getByRole('link', { name: /upgrade/ }) | ||
expect(upgradeLink).toBeInTheDocument() | ||
expect(upgradeLink).toHaveAttribute('href', '/plan/gh/codecov/upgrade') | ||
|
||
const manageMembersLink = screen.getByRole('link', { | ||
name: /manage members/, | ||
}) | ||
expect(manageMembersLink).toBeInTheDocument() | ||
expect(manageMembersLink).toHaveAttribute('href', '/members/gh/codecov') | ||
}) | ||
|
||
it('calls the start trial function when the "Start Trial" button is clicked', async () => { | ||
const { mockTrialMutationVariables, user } = setup() | ||
render(<TrialEligibleBanner />, { wrapper }) | ||
|
||
const startTrialButton = screen.getByRole('button', { | ||
name: /Start Trial/, | ||
}) | ||
await user.click(startTrialButton) | ||
|
||
await waitFor(() => | ||
expect(mockTrialMutationVariables).toHaveBeenCalledWith({ | ||
input: { orgUsername: 'codecov' }, | ||
}) | ||
) | ||
}) | ||
}) |
74 changes: 74 additions & 0 deletions
74
.../RepoPage/CoverageOnboarding/ActivationBanner/TrialEligibleBanner/TrialEligibleBanner.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { useParams } from 'react-router-dom' | ||
|
||
import { useStartTrial } from 'services/trial' | ||
import A from 'ui/A' | ||
import Banner from 'ui/Banner' | ||
import BannerContent from 'ui/Banner/BannerContent' | ||
import BannerHeading from 'ui/Banner/BannerHeading' | ||
import Button from 'ui/Button' | ||
|
||
interface URLParams { | ||
owner: string | ||
} | ||
|
||
function TrialEligibleBanner() { | ||
const { owner } = useParams<URLParams>() | ||
const { mutate: fireTrial, isLoading } = useStartTrial() | ||
|
||
return ( | ||
<Banner variant="plain"> | ||
<BannerContent> | ||
<BannerHeading> | ||
<h2 className="font-semibold"> | ||
Start a free 14-day trial on Pro Team plan 🚀 | ||
</h2> | ||
</BannerHeading> | ||
<div className="flex justify-between"> | ||
<ul className="mb-2 list-inside list-disc"> | ||
<li>Unlimited members</li> | ||
<li>Unlimited repos</li> | ||
<li>Unlimited uploads</li> | ||
<li>Access to all features</li> | ||
<li>No credit card required</li> | ||
</ul> | ||
<div className="flex items-start justify-end"> | ||
<Button | ||
onClick={() => fireTrial({ owner })} | ||
hook="trial-eligible-banner-start-trial" | ||
to={undefined} | ||
disabled={isLoading} | ||
variant="primary" | ||
> | ||
Start Trial | ||
</Button> | ||
</div> | ||
</div> | ||
<span className="text-ds-gray-quinary"> | ||
Plan limits reached, you can{' '} | ||
<A | ||
to={{ | ||
pageName: 'upgradeOrgPlan', | ||
}} | ||
hook="trial-eligible-banner-to-upgrade-page" | ||
isExternal={false} | ||
> | ||
upgrade | ||
</A>{' '} | ||
or{' '} | ||
<A | ||
to={{ | ||
pageName: 'membersTab', | ||
}} | ||
hook="trial-eligible-banner-to-manage-members-page" | ||
isExternal={false} | ||
> | ||
manage members | ||
</A> | ||
. | ||
</span> | ||
</BannerContent> | ||
</Banner> | ||
) | ||
} | ||
|
||
export default TrialEligibleBanner |
1 change: 1 addition & 0 deletions
1
src/pages/RepoPage/CoverageOnboarding/ActivationBanner/TrialEligibleBanner/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './TrialEligibleBanner' |
1 change: 1 addition & 0 deletions
1
src/pages/RepoPage/CoverageOnboarding/ActivationBanner/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './ActivationBanner' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters