Skip to content

Commit 2ade2fc

Browse files
fix tests
1 parent 6e35b3b commit 2ade2fc

File tree

17 files changed

+886
-701
lines changed

17 files changed

+886
-701
lines changed

src/pages/PlanPage/PlanPage.test.jsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { MemoryRouter, Route } from 'react-router-dom'
1111

1212
import config from 'config'
1313

14+
import { ThemeContextProvider } from 'shared/ThemeContext'
15+
1416
import PlanPage from './PlanPage'
1517

1618
vi.mock('config')
@@ -44,18 +46,20 @@ const wrapper =
4446
({ children }) => (
4547
<QueryClientProviderV5 client={queryClientV5}>
4648
<QueryClientProvider client={queryClient}>
47-
<Suspense fallback={null}>
48-
<MemoryRouter initialEntries={[initialEntries]}>
49-
<Route path="/plan/:provider/:owner">{children}</Route>
50-
<Route
51-
path="*"
52-
render={({ location }) => {
53-
testLocation = location
54-
return null
55-
}}
56-
/>
57-
</MemoryRouter>
58-
</Suspense>
49+
<ThemeContextProvider>
50+
<Suspense fallback={null}>
51+
<MemoryRouter initialEntries={[initialEntries]}>
52+
<Route path="/plan/:provider/:owner">{children}</Route>
53+
<Route
54+
path="*"
55+
render={({ location }) => {
56+
testLocation = location
57+
return null
58+
}}
59+
/>
60+
</MemoryRouter>
61+
</Suspense>
62+
</ThemeContextProvider>
5963
</QueryClientProvider>
6064
</QueryClientProviderV5>
6165
)

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/BillingDetails.test.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ import { Plans } from 'shared/utils/billing'
88

99
import BillingDetails from './BillingDetails'
1010

11-
vi.mock('./PaymentCard/PaymentCard', () => ({ default: () => 'Payment Card' }))
11+
vi.mock('./ViewPaymentMethod/PaymentMethod/PaymentMethod', () => ({
12+
default: () => 'Payment Method',
13+
}))
1214
vi.mock('./EmailAddress/EmailAddress', () => ({
1315
default: () => 'Email Address',
1416
}))
15-
vi.mock('./Address/AddressCard', () => ({ default: () => 'Address Card' }))
17+
vi.mock('./ViewPaymentMethod/Address/Address', () => ({
18+
default: () => 'Address',
19+
}))
1620

1721
const queryClient = new QueryClient({
1822
defaultOptions: { queries: { retry: false } },
@@ -84,12 +88,12 @@ describe('BillingDetails', () => {
8488
}
8589

8690
describe('when there is a subscription', () => {
87-
it('renders the payment card', async () => {
91+
it('renders the payment method card', async () => {
8892
setup({ hasSubscription: true })
8993
render(<BillingDetails />, { wrapper })
9094

91-
const paymentCard = await screen.findByText(/Payment Card/)
92-
expect(paymentCard).toBeInTheDocument()
95+
const paymentCards = await screen.findAllByText(/Payment Method/)
96+
expect(paymentCards.length).toBeGreaterThan(0)
9397
})
9498

9599
it('renders the email address component', async () => {
@@ -104,7 +108,7 @@ describe('BillingDetails', () => {
104108
setup({ hasSubscription: true })
105109
render(<BillingDetails />, { wrapper })
106110

107-
const addressCard = await screen.findByText(/Address Card/)
111+
const addressCard = await screen.findByText(/Address/)
108112
expect(addressCard).toBeInTheDocument()
109113
})
110114

@@ -132,7 +136,7 @@ describe('BillingDetails', () => {
132136
it('renders the payment card', async () => {
133137
render(<BillingDetails />, { wrapper })
134138

135-
const paymentCard = screen.queryByText(/Payment Card/)
139+
const paymentCard = screen.queryByText(/Payment Method/)
136140
expect(paymentCard).not.toBeInTheDocument()
137141
})
138142
})

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/BillingDetails.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function BillingDetails() {
7171
setEditMode={setEditMode}
7272
provider={provider}
7373
owner={owner}
74-
existingSubscriptionDetail={subscriptionDetail}
74+
subscriptionDetail={subscriptionDetail}
7575
/>
7676
) : (
7777
<>
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import { Elements } from '@stripe/react-stripe-js'
2+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
3+
import { render, screen } from '@testing-library/react'
4+
import userEvent from '@testing-library/user-event'
5+
import { MemoryRouter, Route } from 'react-router-dom'
6+
import { vi } from 'vitest'
7+
import { z } from 'zod'
8+
9+
import { SubscriptionDetailSchema } from 'services/account/useAccountDetails'
10+
11+
import AddressForm from './AddressForm'
12+
13+
const queryClient = new QueryClient()
14+
15+
const mockGetElement = vi.fn()
16+
const mockGetValue = vi.fn()
17+
18+
vi.mock('@stripe/react-stripe-js', async () => {
19+
const actual = await vi.importActual('@stripe/react-stripe-js')
20+
return {
21+
...actual,
22+
useElements: () => ({
23+
getElement: mockGetElement.mockReturnValue({
24+
getValue: mockGetValue.mockResolvedValue({
25+
complete: true,
26+
value: {
27+
name: 'John Doe',
28+
address: {
29+
line1: '123 Main St',
30+
line2: null,
31+
city: 'San Francisco',
32+
state: 'CA',
33+
postal_code: '94105',
34+
country: 'US',
35+
},
36+
},
37+
}),
38+
}),
39+
}),
40+
}
41+
})
42+
43+
const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
44+
<QueryClientProvider client={queryClient}>
45+
<Elements stripe={null}>
46+
<MemoryRouter initialEntries={['/plan/gh/codecov']}>
47+
<Route path="/plan/:provider/:owner">{children}</Route>
48+
</MemoryRouter>
49+
</Elements>
50+
</QueryClientProvider>
51+
)
52+
53+
const mockSubscriptionDetail: z.infer<typeof SubscriptionDetailSchema> = {
54+
defaultPaymentMethod: {
55+
billingDetails: {
56+
address: {
57+
line1: '123 Main St',
58+
city: 'San Francisco',
59+
state: 'CA',
60+
postalCode: '94105',
61+
country: 'US',
62+
line2: null,
63+
},
64+
phone: '1234567890',
65+
name: 'John Doe',
66+
67+
},
68+
card: {
69+
brand: 'visa',
70+
expMonth: 12,
71+
expYear: 2025,
72+
last4: '4242',
73+
},
74+
},
75+
currentPeriodEnd: 1706851492,
76+
cancelAtPeriodEnd: false,
77+
customer: {
78+
id: 'cust_123',
79+
80+
},
81+
latestInvoice: null,
82+
taxIds: [],
83+
trialEnd: null,
84+
}
85+
86+
const mocks = {
87+
useUpdateBillingAddress: vi.fn(),
88+
}
89+
90+
vi.mock('services/account/useUpdateBillingAddress', () => ({
91+
useUpdateBillingAddress: () => mocks.useUpdateBillingAddress(),
92+
}))
93+
94+
afterEach(() => {
95+
vi.clearAllMocks()
96+
})
97+
98+
describe('AddressForm', () => {
99+
const setup = () => {
100+
return { user: userEvent.setup() }
101+
}
102+
103+
it('renders the form', () => {
104+
mocks.useUpdateBillingAddress.mockReturnValue({
105+
mutate: vi.fn(),
106+
isLoading: false,
107+
})
108+
109+
render(
110+
<AddressForm
111+
address={
112+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.address
113+
}
114+
name={
115+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.name ||
116+
undefined
117+
}
118+
provider="gh"
119+
owner="codecov"
120+
closeForm={() => {}}
121+
/>,
122+
{ wrapper }
123+
)
124+
125+
expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument()
126+
expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument()
127+
})
128+
129+
describe('when submitting', () => {
130+
it('calls the service to update the address', async () => {
131+
const user = userEvent.setup()
132+
const updateAddress = vi.fn()
133+
mocks.useUpdateBillingAddress.mockReturnValue({
134+
mutate: updateAddress,
135+
isLoading: false,
136+
})
137+
138+
render(
139+
<AddressForm
140+
address={
141+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.address
142+
}
143+
name={
144+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.name ||
145+
undefined
146+
}
147+
provider="gh"
148+
owner="codecov"
149+
closeForm={() => {}}
150+
/>,
151+
{ wrapper }
152+
)
153+
154+
await user.click(screen.getByTestId('submit-address-update'))
155+
expect(updateAddress).toHaveBeenCalledWith(
156+
{
157+
name: 'John Doe',
158+
address: {
159+
line1: '123 Main St',
160+
line2: null,
161+
city: 'San Francisco',
162+
state: 'CA',
163+
postal_code: '94105',
164+
country: 'US',
165+
},
166+
},
167+
expect.any(Object)
168+
)
169+
})
170+
})
171+
172+
describe('when the user clicks on cancel', () => {
173+
it('calls the closeForm prop', async () => {
174+
const { user } = setup()
175+
const closeForm = vi.fn()
176+
mocks.useUpdateBillingAddress.mockReturnValue({
177+
mutate: vi.fn(),
178+
isLoading: false,
179+
})
180+
181+
render(
182+
<AddressForm
183+
address={
184+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.address
185+
}
186+
name={
187+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.name ||
188+
undefined
189+
}
190+
provider="gh"
191+
owner="codecov"
192+
closeForm={closeForm}
193+
/>,
194+
{ wrapper }
195+
)
196+
197+
await user.click(screen.getByRole('button', { name: /cancel/i }))
198+
199+
expect(closeForm).toHaveBeenCalled()
200+
})
201+
})
202+
203+
describe('when the form is loading', () => {
204+
it('has the save and cancel buttons disabled', () => {
205+
mocks.useUpdateBillingAddress.mockReturnValue({
206+
mutate: vi.fn(),
207+
isLoading: true,
208+
error: null,
209+
reset: vi.fn(),
210+
})
211+
212+
render(
213+
<AddressForm
214+
address={
215+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.address
216+
}
217+
name={
218+
mockSubscriptionDetail.defaultPaymentMethod?.billingDetails?.name ||
219+
undefined
220+
}
221+
provider="gh"
222+
owner="codecov"
223+
closeForm={() => {}}
224+
/>,
225+
{ wrapper }
226+
)
227+
228+
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled()
229+
expect(screen.getByRole('button', { name: /cancel/i })).toBeDisabled()
230+
})
231+
})
232+
})

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethods/Address/AddressForm.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Button from 'ui/Button'
88

99
interface AddressFormProps {
1010
address?: z.infer<typeof AddressSchema>
11-
name?: string | null | undefined
11+
name?: string
1212
closeForm: () => void
1313
provider: string
1414
owner: string
@@ -23,12 +23,7 @@ function AddressForm({
2323
}: AddressFormProps) {
2424
const elements = useElements()
2525

26-
const {
27-
mutate: updateAddress,
28-
isLoading,
29-
error,
30-
reset,
31-
} = useUpdateBillingAddress({
26+
const { mutate: updateAddress, isLoading } = useUpdateBillingAddress({
3227
provider,
3328
owner,
3429
})
@@ -47,8 +42,6 @@ function AddressForm({
4742
}
4843
}
4944

50-
const showError = error && !reset
51-
5245
return (
5346
<form onSubmit={submit} aria-label="form">
5447
<div className={cs('flex flex-col gap-3')}>
@@ -72,15 +65,13 @@ function AddressForm({
7265
},
7366
}}
7467
/>
75-
<p className="mt-1 text-ds-primary-red">{showError && error}</p>
7668
</div>
7769
<div className="flex gap-1">
7870
<Button
7971
hook="submit-address-update"
8072
type="submit"
8173
variant="primary"
8274
disabled={isLoading}
83-
to={undefined}
8475
>
8576
Save
8677
</Button>
@@ -90,7 +81,6 @@ function AddressForm({
9081
variant="plain"
9182
disabled={isLoading}
9283
onClick={closeForm}
93-
to={undefined}
9484
>
9585
Cancel
9686
</Button>

0 commit comments

Comments
 (0)