Billing address
{isAddressSameAsPrimary ? (
@@ -83,28 +78,8 @@ function BillingInner({
>
)}
- )
- }
-
- return (
-
-
- No address has been set. Please contact support if you think it’s an
- error or set it yourself.
-
-
-
-
)
}
-export default AddressCard
+export default Address
diff --git a/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/Address/AddressCard.test.tsx b/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/Address/AddressCard.test.tsx
deleted file mode 100644
index 49ca52c423..0000000000
--- a/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/Address/AddressCard.test.tsx
+++ /dev/null
@@ -1,409 +0,0 @@
-import { render, screen } from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
-import { z } from 'zod'
-
-import { SubscriptionDetailSchema } from 'services/account'
-import { ThemeContextProvider } from 'shared/ThemeContext'
-
-import AddressCard from './AddressCard'
-
-const mocks = vi.hoisted(() => ({
- useUpdateBillingAddress: vi.fn(),
-}))
-
-vi.mock('services/account/useUpdateBillingAddress', async () => {
- const actual = await import('services/account/useUpdateBillingAddress')
- return {
- ...actual,
- useUpdateBillingAddress: mocks.useUpdateBillingAddress,
- }
-})
-
-const subscriptionDetail = {
- defaultPaymentMethod: {
- card: {
- brand: 'visa',
- expMonth: 12,
- expYear: 2021,
- last4: '1234',
- },
- billingDetails: {
- name: 'Bob Smith',
- address: {
- line1: '123 Sesame St.',
- line2: 'Apt A',
- city: 'San Francisco',
- country: 'US',
- state: 'CA',
- postalCode: '12345',
- },
- },
- },
- currentPeriodEnd: 1606851492,
- cancelAtPeriodEnd: false,
-} as z.infer
-
-const wrapper: React.FC = ({ children }) => (
- {children}
-)
-
-// mocking stripe components
-vi.mock('@stripe/react-stripe-js', () => {
- function makeFakeComponent(name: string) {
- // mocking onReady to be called after a bit of time
- return function Component({ _onReady }: { _onReady?: any }) {
- return name
- }
- }
-
- return {
- useElements: () => ({
- getElement: vi.fn().mockReturnValue({
- getValue: vi.fn().mockResolvedValue({
- complete: true,
- value: {
- address: {},
- },
- }),
- }),
- update: vi.fn(),
- }),
- useStripe: () => ({}),
- AddressElement: makeFakeComponent('AddressElement'),
- }
-})
-
-describe('AddressCard', () => {
- function setup() {
- const user = userEvent.setup()
-
- return { user }
- }
-
- describe(`when the user doesn't have any subscriptionDetail`, () => {
- // NOTE: This test is misleading because we hide this component from a higher level in
- // BillingDetails.tsx if there is no subscriptionDetail
- it('renders the set card message', () => {
- render(
- {}}
- />,
- { wrapper }
- )
-
- expect(
- screen.getByText(
- /No address has been set. Please contact support if you think it's an error or set it yourself./
- )
- ).toBeInTheDocument()
- })
- })
-
- describe(`when the user doesn't have billing details`, () => {
- it('renders an error message', () => {
- render(
- {}}
- />,
- { wrapper }
- )
-
- expect(
- screen.getByText(
- /No address has been set. Please contact support if you think it's an error or set it yourself./
- )
- ).toBeInTheDocument()
- })
-
- describe('when the user clicks on "Set Address"', () => {
- it(`doesn't render address info stuff anymore`, async () => {
- const { user } = setup()
- render(
- {}}
- />,
- { wrapper }
- )
-
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: () => null,
- isLoading: false,
- })
- await user.click(screen.getByTestId('open-modal'))
-
- expect(
- screen.queryByText(/123 Sesame St. Apt A/)
- ).not.toBeInTheDocument()
- })
-
- it('renders the address form component', async () => {
- const { user } = setup()
- render(
- {}}
- />,
- { wrapper }
- )
-
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: () => null,
- isLoading: false,
- })
- await user.click(screen.getByTestId('open-modal'))
-
- expect(
- screen.getByRole('button', { name: /update/i })
- ).toBeInTheDocument()
- })
- })
- })
-
- describe('when the user has an address', () => {
- it('renders the address information', () => {
- render(
- {}}
- />,
- { wrapper }
- )
-
- expect(screen.getByText(/Billing address/)).toBeInTheDocument()
- expect(screen.getByText(/123 Sesame St. Apt A/)).toBeInTheDocument()
- expect(screen.getByText(/San Francisco, CA 12345/)).toBeInTheDocument()
- })
-
- it('can render partial information too', () => {
- render(
- {}}
- />,
- { wrapper }
- )
-
- expect(screen.getByText('Cardholder name')).toBeInTheDocument()
- expect(screen.getByText('N/A')).toBeInTheDocument()
- expect(screen.getByText('Billing address')).toBeInTheDocument()
- expect(screen.queryByText(/null/)).not.toBeInTheDocument()
- expect(screen.getByText('12345')).toBeInTheDocument()
- })
-
- it('renders the card holder information', () => {
- render(
- {}}
- />,
- { wrapper }
- )
-
- expect(screen.getByText(/Cardholder name/)).toBeInTheDocument()
- expect(screen.getByText(/Bob Smith/)).toBeInTheDocument()
- })
- })
-
- describe('when the user clicks on Edit', () => {
- it(`doesn't render the card anymore`, async () => {
- const { user } = setup()
- const updateAddress = vi.fn()
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: updateAddress,
- isLoading: false,
- })
-
- render(
- {}}
- />,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-address'))
-
- expect(screen.queryByText(/Cardholder name/)).not.toBeInTheDocument()
- expect(screen.queryByText(/Bob Smith/)).not.toBeInTheDocument()
- expect(screen.queryByText(/Billing address/)).not.toBeInTheDocument()
- expect(screen.queryByText(/123 Sesame St. Apt A/)).not.toBeInTheDocument()
- expect(
- screen.queryByText(/San Francisco, CA 12345/)
- ).not.toBeInTheDocument()
- })
-
- it('renders the form', async () => {
- const { user } = setup()
- const updateAddress = vi.fn()
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: updateAddress,
- isLoading: false,
- })
- render(
- {}}
- />,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-address'))
-
- expect(
- screen.getByRole('button', { name: /update/i })
- ).toBeInTheDocument()
- })
-
- describe('when submitting', () => {
- it('calls the service to update the address', async () => {
- const { user } = setup()
- const updateAddress = vi.fn()
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: updateAddress,
- isLoading: false,
- })
- render(
- {}}
- />,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-address'))
- await user.click(screen.queryByRole('button', { name: /update/i })!)
-
- expect(updateAddress).toHaveBeenCalled()
- })
- })
-
- describe('when the user clicks on cancel', () => {
- it(`doesn't render the form anymore`, async () => {
- const { user } = setup()
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: vi.fn(),
- isLoading: false,
- })
- render(
- {}}
- />,
- { wrapper }
- )
-
- await user.click(screen.getByTestId('edit-address'))
- await user.click(screen.getByRole('button', { name: /Cancel/ }))
-
- expect(
- screen.queryByRole('button', { name: /save/i })
- ).not.toBeInTheDocument()
- })
- })
- })
-
- describe('when there is an error in the form', () => {
- it('renders the error', async () => {
- const { user } = setup()
- const randomError = 'not a valid address'
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: vi.fn(),
- error: randomError,
- })
- render(
- {}}
- />,
- { wrapper }
- )
-
- await user.click(screen.getByTestId('edit-address'))
-
- expect(screen.getByText(randomError)).toBeInTheDocument()
- })
- })
-
- describe('when the form is loading', () => {
- it('has the error and save button disabled', async () => {
- const { user } = setup()
- mocks.useUpdateBillingAddress.mockReturnValue({
- mutate: vi.fn(),
- isLoading: true,
- })
- render(
- {}}
- />,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-address'))
-
- expect(screen.queryByRole('button', { name: /update/i })).toBeDisabled()
- expect(screen.queryByRole('button', { name: /cancel/i })).toBeDisabled()
- })
- })
-})
diff --git a/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.test.tsx b/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.test.tsx
index b3f7fdd2d6..bf8cb4a92e 100644
--- a/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.test.tsx
+++ b/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.test.tsx
@@ -2,24 +2,36 @@ import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { ThemeContextProvider } from 'shared/ThemeContext'
-import { Plans } from 'shared/utils/billing'
import PaymentMethod from './PaymentMethod'
const mocks = vi.hoisted(() => ({
- useUpdateCard: vi.fn(),
+ useUpdatePaymentMethod: vi.fn(),
}))
vi.mock('services/account', async () => {
const actual = await vi.importActual('services/account')
return {
...actual,
- useUpdateCard: mocks.useUpdateCard,
+ useUpdatePaymentMethod: mocks.useUpdatePaymentMethod,
}
})
const subscriptionDetail = {
defaultPaymentMethod: {
+ billingDetails: {
+ name: 'John Doe',
+ email: 'john.doe@example.com',
+ address: {
+ city: 'New York',
+ country: 'US',
+ line1: '123 Main St',
+ line2: 'Apt 4B',
+ postalCode: '10001',
+ state: 'NY',
+ },
+ phone: '1234567890',
+ },
card: {
brand: 'visa',
expMonth: 12,
@@ -27,20 +39,24 @@ const subscriptionDetail = {
last4: '1234',
},
},
- plan: {
- value: Plans.USERS_PR_INAPPY,
- },
currentPeriodEnd: 1606851492,
cancelAtPeriodEnd: false,
+ customer: {
+ id: 'cust_123',
+ email: 'test@test.com',
+ },
+ latestInvoice: null,
+ taxIds: [],
+ trialEnd: null,
}
-const wrapper = ({ children }) => (
+const wrapper: React.FC = ({ children }) => (
{children}
)
// mocking all the stripe components; and trusting the library :)
vi.mock('@stripe/react-stripe-js', () => {
- function makeFakeComponent(name) {
+ function makeFakeComponent(name: string) {
return function Component() {
return name
}
@@ -54,7 +70,7 @@ vi.mock('@stripe/react-stripe-js', () => {
}
})
-describe('PaymentCard', () => {
+describe('PaymentMethodCard', () => {
function setup() {
const user = userEvent.setup()
@@ -70,85 +86,75 @@ describe('PaymentCard', () => {
subscriptionDetail={null}
provider="gh"
owner="codecov"
+ setEditMode={() => {}}
/>
)
expect(
screen.getByText(
- /No credit card set. Please contact support if you think it’s an error or set it yourself./
+ /No payment method set. Please contact support if you think it’s an error or set it yourself./
)
).toBeInTheDocument()
})
})
- describe(`when the user doesn't have any card`, () => {
+ describe(`when the user doesn't have any payment method`, () => {
it('renders an error message', () => {
+ const subscriptionDetailMissingPaymentMethod = {
+ ...subscriptionDetail,
+ defaultPaymentMethod: {
+ ...subscriptionDetail.defaultPaymentMethod,
+ card: null,
+ usBankAccount: null,
+ },
+ }
render(
{}}
/>,
{ wrapper }
)
expect(
screen.getByText(
- /No credit card set. Please contact support if you think it’s an error or set it yourself./
+ /No payment method set. Please contact support if you think it’s an error or set it yourself./
)
).toBeInTheDocument()
})
describe('when the user clicks on Set card', () => {
- it(`doesn't render the card anymore`, async () => {
+ it(`doesn't render the card anymore and opens the form`, async () => {
const { user } = setup()
+ const subscriptionDetailMissingPaymentMethod = {
+ ...subscriptionDetail,
+ defaultPaymentMethod: {
+ ...subscriptionDetail.defaultPaymentMethod,
+ card: null,
+ usBankAccount: null,
+ },
+ }
+ const setEditMode = vi.fn()
render(
,
{ wrapper }
)
- mocks.useUpdateCard.mockReturnValue({
+ mocks.useUpdatePaymentMethod.mockReturnValue({
mutate: () => null,
isLoading: false,
})
- await user.click(screen.getByTestId('open-modal'))
+ await user.click(screen.getByTestId('open-edit-mode'))
expect(screen.queryByText(/Visa/)).not.toBeInTheDocument()
- })
-
- it('renders the form', async () => {
- const { user } = setup()
- render(
- ,
- { wrapper }
- )
-
- mocks.useUpdateCard.mockReturnValue({
- mutate: () => null,
- isLoading: false,
- })
- await user.click(screen.getByTestId('open-modal'))
-
- expect(
- screen.getByRole('button', { name: /update/i })
- ).toBeInTheDocument()
+ expect(setEditMode).toHaveBeenCalledWith(true)
})
})
})
@@ -160,6 +166,7 @@ describe('PaymentCard', () => {
subscriptionDetail={subscriptionDetail}
provider="gh"
owner="codecov"
+ setEditMode={() => {}}
/>,
{ wrapper }
)
@@ -174,6 +181,7 @@ describe('PaymentCard', () => {
subscriptionDetail={subscriptionDetail}
provider="gh"
owner="codecov"
+ setEditMode={() => {}}
/>,
{ wrapper }
)
@@ -192,6 +200,7 @@ describe('PaymentCard', () => {
}}
provider="gh"
owner="codecov"
+ setEditMode={() => {}}
/>,
{ wrapper }
)
@@ -199,142 +208,4 @@ describe('PaymentCard', () => {
expect(screen.queryByText(/1st December, 2020/)).not.toBeInTheDocument()
})
})
-
- describe('when the user clicks on Edit card', () => {
- it(`doesn't render the card anymore`, async () => {
- const { user } = setup()
- const updateCard = vi.fn()
- mocks.useUpdateCard.mockReturnValue({
- mutate: updateCard,
- isLoading: false,
- })
-
- render(
- ,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-card'))
-
- expect(screen.queryByText(/Visa/)).not.toBeInTheDocument()
- })
-
- it('renders the form', async () => {
- const { user } = setup()
- const updateCard = vi.fn()
- mocks.useUpdateCard.mockReturnValue({
- mutate: updateCard,
- isLoading: false,
- })
- render(
- ,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-card'))
-
- expect(
- screen.getByRole('button', { name: /update/i })
- ).toBeInTheDocument()
- })
-
- describe('when submitting', () => {
- it('calls the service to update the card', async () => {
- const { user } = setup()
- const updateCard = vi.fn()
- mocks.useUpdateCard.mockReturnValue({
- mutate: updateCard,
- isLoading: false,
- })
- render(
- ,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-card'))
- await user.click(screen.queryByRole('button', { name: /update/i }))
-
- expect(updateCard).toHaveBeenCalled()
- })
- })
-
- describe('when the user clicks on cancel', () => {
- it(`doesn't render the form anymore`, async () => {
- const { user } = setup()
- mocks.useUpdateCard.mockReturnValue({
- mutate: vi.fn(),
- isLoading: false,
- })
- render(
- ,
- { wrapper }
- )
-
- await user.click(screen.getByTestId('edit-card'))
- await user.click(screen.getByRole('button', { name: /Cancel/ }))
-
- expect(
- screen.queryByRole('button', { name: /save/i })
- ).not.toBeInTheDocument()
- })
- })
- })
-
- describe('when there is an error in the form', () => {
- it('renders the error', async () => {
- const { user } = setup()
- const randomError = 'not rich enough'
- mocks.useUpdateCard.mockReturnValue({
- mutate: vi.fn(),
- error: { message: randomError },
- })
- render(
- ,
- { wrapper }
- )
-
- await user.click(screen.getByTestId('edit-card'))
-
- expect(screen.getByText(randomError)).toBeInTheDocument()
- })
- })
-
- describe('when the form is loading', () => {
- it('has the error and save button disabled', async () => {
- const { user } = setup()
- mocks.useUpdateCard.mockReturnValue({
- mutate: vi.fn(),
- isLoading: true,
- })
- render(
- ,
- { wrapper }
- )
- await user.click(screen.getByTestId('edit-card'))
-
- expect(screen.queryByRole('button', { name: /update/i })).toBeDisabled()
- expect(screen.queryByRole('button', { name: /cancel/i })).toBeDisabled()
- })
- })
})
diff --git a/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.tsx b/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.tsx
index c0fe83e00a..812c144055 100644
--- a/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.tsx
+++ b/src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/ViewPaymentMethod/PaymentMethod/PaymentMethod.tsx
@@ -39,6 +39,7 @@ function PaymentMethod({
{/* Address */}
- = ({
children,
}) => {
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
+ const prefersDark =
+ typeof window !== 'undefined' &&
+ (window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false)
let systemTheme = Theme.LIGHT
if (prefersDark) {