Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
suejung-sentry committed Jan 30, 2025
1 parent 3210ed6 commit 9a5f94d
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import config from 'config'

import { ThemeContextProvider } from 'shared/ThemeContext'

import { Location } from 'history'
import { UnverifiedPaymentMethodSchema } from 'services/account'
import { z } from 'zod'
import PlanPage from './PlanPage'

vi.mock('config')
Expand Down Expand Up @@ -40,10 +43,10 @@ const queryClientV5 = new QueryClientV5({
defaultOptions: { queries: { retry: false } },
})

let testLocation
let testLocation: Location<unknown>
const wrapper =
(initialEntries = '') =>
({ children }) => (
({ children }: { children: React.ReactNode }) => (
<QueryClientProviderV5 client={queryClientV5}>
<QueryClientProvider client={queryClient}>
<ThemeContextProvider>
Expand Down Expand Up @@ -79,7 +82,13 @@ afterAll(() => {

describe('PlanPage', () => {
function setup(
{ owner, isSelfHosted = false } = {
{
owner,
isSelfHosted = false,
unverifiedPaymentMethods = [] as z.infer<
typeof UnverifiedPaymentMethodSchema
>[],
} = {
owner: {
username: 'codecov',
isCurrentUserPartOfOrg: true,
Expand All @@ -92,6 +101,17 @@ describe('PlanPage', () => {
server.use(
graphql.query('PlanPageData', () => {
return HttpResponse.json({ data: { owner } })
}),
graphql.query('UnverifiedPaymentMethods', () => {
return HttpResponse.json({
data: {
owner: {
billing: {
unverifiedPaymentMethods,
},
},
},
})
})
)
}
Expand All @@ -102,7 +122,7 @@ describe('PlanPage', () => {
owner: {
username: 'codecov',
isCurrentUserPartOfOrg: false,
numberOfUploads: null,
numberOfUploads: 0,
},
})
})
Expand All @@ -120,7 +140,7 @@ describe('PlanPage', () => {
owner: {
username: 'codecov',
isCurrentUserPartOfOrg: false,
numberOfUploads: null,
numberOfUploads: 0,
},
})
})
Expand Down Expand Up @@ -149,6 +169,34 @@ describe('PlanPage', () => {
const tabs = await screen.findByText(/Tabs/)
expect(tabs).toBeInTheDocument()
})

describe('when there are unverified payment methods', () => {
beforeEach(() => {
setup({
owner: {
username: 'codecov',
isCurrentUserPartOfOrg: true,
numberOfUploads: 30,
},
unverifiedPaymentMethods: [
{
paymentMethodId: 'pm_123',
hostedVerificationUrl: 'https://verify.stripe.com',
},
],
})
})

it('renders unverified payment method alert', async () => {
render(<PlanPage />, { wrapper: wrapper('/plan/gh/codecov') })

const alert = await screen.findByText(/Verify Your New Payment Method/)
expect(alert).toBeInTheDocument()

const link = screen.getByText('Click here')
expect(link).toHaveAttribute('href', 'https://verify.stripe.com')
})
})
})

describe('testing routes', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import {
QueryClientProvider as QueryClientProviderV5,
QueryClient as QueryClientV5,
} from '@tanstack/react-queryV5'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { graphql, http, HttpResponse } from 'msw'
Expand Down Expand Up @@ -233,6 +237,15 @@ const queryClient = new QueryClient({
},
},
})

const queryClientV5 = new QueryClientV5({
defaultOptions: {
queries: {
retry: false,
},
},
})

const server = setupServer()

beforeAll(() => {
Expand All @@ -241,6 +254,7 @@ beforeAll(() => {

afterEach(() => {
queryClient.clear()
queryClientV5.clear()
server.resetHandlers()
vi.clearAllMocks()
})
Expand All @@ -256,20 +270,22 @@ const wrapper: (
) => React.FC<React.PropsWithChildren> =
(initialEntries = ['/gh/codecov']) =>
({ children }) => (
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={initialEntries}>
<Route path="/:provider/:owner">
<Suspense fallback={null}>{children}</Suspense>
</Route>
<Route
path="*"
render={({ location }) => {
testLocation = location
return null
}}
/>
</MemoryRouter>
</QueryClientProvider>
<QueryClientProviderV5 client={queryClientV5}>
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={initialEntries}>
<Route path="/:provider/:owner">
<Suspense fallback={null}>{children}</Suspense>
</Route>
<Route
path="*"
render={({ location }) => {
testLocation = location
return null
}}
/>
</MemoryRouter>
</QueryClientProvider>
</QueryClientProviderV5>
)

type SetupArgs = {
Expand All @@ -281,6 +297,7 @@ type SetupArgs = {
hasSentryPlans?: boolean
monthlyPlan?: boolean
planUserCount?: number
hasUnverifiedPaymentMethod?: boolean
}

describe('UpgradeForm', () => {
Expand All @@ -293,6 +310,7 @@ describe('UpgradeForm', () => {
hasSentryPlans = false,
monthlyPlan = true,
planUserCount = 1,
hasUnverifiedPaymentMethod = false,
}: SetupArgs) {
const addNotification = vi.fn()
const user = userEvent.setup()
Expand Down Expand Up @@ -384,13 +402,84 @@ describe('UpgradeForm', () => {
},
},
})
}),
graphql.query('UnverifiedPaymentMethods', () => {
return HttpResponse.json({
data: {
owner: {
billing: {
unverifiedPaymentMethods: hasUnverifiedPaymentMethod
? [
{
paymentMethodId: 'asdf',
hostedVerficationUrl: 'https://stripe.com',
},
]
: null,
},
},
},
})
})
)

return { addNotification, user, patchRequest }
}

describe('when rendered', () => {
describe('when user has unverified payment methods', () => {
const props = {
setSelectedPlan: vi.fn(),
selectedPlan: proPlanYear,
}

it('shows modal when form is submitted', async () => {
const { user } = setup({
planValue: Plans.USERS_BASIC,
hasUnverifiedPaymentMethod: true,
})
render(<UpgradeForm {...props} />, { wrapper: wrapper() })

const proceedToCheckoutButton = await screen.findByRole('button', {
name: /Proceed to checkout/,
})
await user.click(proceedToCheckoutButton)

const modal = await screen.findByText(
/Are you sure you want to abandon this upgrade and start a new one/,
{
exact: false,
}
)
expect(modal).toBeInTheDocument()
})

it('does not show modal when no unverified payment methods', async () => {
const { user } = setup({
planValue: Plans.USERS_BASIC,
hasUnverifiedPaymentMethod: false,
})
render(<UpgradeForm {...props} />, { wrapper: wrapper() })

const input = await screen.findByRole('spinbutton')
await user.type(input, '{backspace}{backspace}{backspace}')
await user.type(input, '20')

const proceedToCheckoutButton = await screen.findByRole('button', {
name: /Proceed to checkout/,
})
await user.click(proceedToCheckoutButton)

const modal = screen.queryByText(
/Are you sure you want to abandon this upgrade and start a new one/,
{
exact: false,
}
)
expect(modal).not.toBeInTheDocument()
})
})

describe('when the user has a basic plan', () => {
const props = {
setSelectedPlan: vi.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useAccountDetails,
useAvailablePlans,
usePlanData,
useUnverifiedPaymentMethods,
} from 'services/account'
import { Provider } from 'shared/api/helpers'
import { canApplySentryUpgrade, getNextBillingDate } from 'shared/utils/billing'
Expand Down Expand Up @@ -45,6 +46,10 @@ function UpgradeForm({ selectedPlan, setSelectedPlan }: UpgradeFormProps) {
const { data: accountDetails } = useAccountDetails({ provider, owner })
const { data: plans } = useAvailablePlans({ provider, owner })
const { data: planData } = usePlanData({ owner, provider })
const { data: unverifiedPaymentMethods } = useUnverifiedPaymentMethods({
provider,
owner,
})
const { upgradePlan } = useUpgradeControls()
const [showModal, setShowModal] = useState(false)
const [formData, setFormData] = useState<UpgradeFormFields>()
Expand Down Expand Up @@ -95,7 +100,7 @@ function UpgradeForm({ selectedPlan, setSelectedPlan }: UpgradeFormProps) {
}, [newPlan, trigger])

const onSubmit = handleSubmit((data) => {
if (accountDetails?.unverifiedPaymentMethods?.length) {
if (unverifiedPaymentMethods?.length) {
setFormData(data)
setShowModal(true)
} else {
Expand Down Expand Up @@ -141,10 +146,7 @@ function UpgradeForm({ selectedPlan, setSelectedPlan }: UpgradeFormProps) {
setIsUpgrading(true)
upgradePlan(formData)
}}
url={
accountDetails?.unverifiedPaymentMethods?.[0]
?.hostedVerificationLink || ''
}
url={unverifiedPaymentMethods?.[0]?.hostedVerificationUrl || ''}
isUpgrading={isUpgrading}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import UpgradeFormModal from './UpgradeFormModal'

describe('UpgradeFormModal', () => {
const mockOnClose = vi.fn()
const mockOnConfirm = vi.fn()
const mockUrl = 'https://verify.stripe.com'

const setup = (isUpgrading = false) => {
return render(
<UpgradeFormModal
isOpen={true}
onClose={mockOnClose}
onConfirm={mockOnConfirm}
url={mockUrl}
isUpgrading={isUpgrading}
/>
)
}

beforeEach(() => {
vi.resetAllMocks()
})

it('renders modal with correct content', () => {
setup()

expect(screen.getByText('Incomplete Plan Upgrade')).toBeInTheDocument()
expect(
screen.getByText(
/You have a pending plan upgrade awaiting payment verification/
)
).toBeInTheDocument()
expect(screen.getByText('here')).toHaveAttribute('href', mockUrl)
expect(
screen.getByText(
/Are you sure you want to abandon this upgrade and start a new one/
)
).toBeInTheDocument()
})

it('calls onClose when cancel button is clicked', async () => {
setup()
const utils = userEvent.setup()

const cancelButton = screen.getByRole('button', { name: 'Cancel' })
await utils.click(cancelButton)

expect(mockOnClose).toHaveBeenCalled()
})

it('calls onConfirm when confirm button is clicked', async () => {
setup()
const utils = userEvent.setup()

const confirmButton = screen.getByRole('button', {
name: 'Yes, Start New Upgrade',
})
await utils.click(confirmButton)

expect(mockOnConfirm).toHaveBeenCalled()
})

describe('when isUpgrading is true', () => {
it('disables buttons and shows processing text', () => {
setup(true)

const cancelButton = screen.getByRole('button', { name: 'Cancel' })
const confirmButton = screen.getByRole('button', {
name: 'Processing...',
})

expect(cancelButton).toBeDisabled()
expect(confirmButton).toBeDisabled()
})
})
})

0 comments on commit 9a5f94d

Please sign in to comment.