BillingCustomerData.test.tsx137 lines · main
1import { screen } from '@testing-library/react'
2import type { ReactNode } from 'react'
3import { beforeEach, describe, expect, it, vi } from 'vitest'
4
5import { BillingCustomerData } from './BillingCustomerData'
6import { MANAGED_BY } from '@/lib/constants/infrastructure'
7import { createMockOrganization, render } from '@/tests/helpers'
8
9const mockSelectedOrganization = vi.hoisted(() => vi.fn())
10
11vi.mock('common', async (importOriginal) => {
12 const original = (await importOriginal()) as typeof import('common')
13 return {
14 ...original,
15 useParams: () => ({ slug: 'stripe-org' }),
16 }
17})
18
19vi.mock('next-themes', () => ({
20 useTheme: () => ({ resolvedTheme: 'dark' }),
21}))
22
23vi.mock('@stripe/react-stripe-js', () => ({
24 Elements: ({ children }: { children: ReactNode }) => <div>{children}</div>,
25}))
26
27vi.mock('@stripe/stripe-js', () => ({
28 loadStripe: vi.fn(() => Promise.resolve(null)),
29}))
30
31vi.mock('ui', async (importOriginal) => {
32 const original = (await importOriginal()) as typeof import('ui')
33 return {
34 ...original,
35 Form: ({ children }: { children: ReactNode }) => <div>{children}</div>,
36 }
37})
38
39vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
40 useSelectedOrganizationQuery: () => ({ data: mockSelectedOrganization() }),
41}))
42
43vi.mock('@/hooks/misc/useCheckPermissions', () => ({
44 useAsyncCheckPermissions: () => ({ can: true, isSuccess: true }),
45}))
46
47vi.mock('@/data/organizations/organization-customer-profile-query', () => ({
48 useOrganizationCustomerProfileQuery: () => ({
49 data: {
50 address: {
51 line1: '',
52 line2: '',
53 city: '',
54 state: '',
55 postal_code: '',
56 country: 'US',
57 },
58 billing_name: 'Toolshed',
59 },
60 error: null,
61 isPending: false,
62 isSuccess: true,
63 }),
64}))
65
66vi.mock('@/data/organizations/organization-tax-id-query', () => ({
67 useOrganizationTaxIdQuery: () => ({
68 data: null,
69 error: null,
70 isPending: false,
71 isSuccess: true,
72 }),
73}))
74
75vi.mock('@/data/organizations/organization-customer-profile-update-mutation', () => ({
76 useOrganizationCustomerProfileUpdateMutation: () => ({
77 mutateAsync: vi.fn(),
78 }),
79}))
80
81vi.mock('./useBillingCustomerDataForm', () => ({
82 useBillingCustomerDataForm: () => ({
83 form: {},
84 handleSubmit: vi.fn(),
85 handleReset: vi.fn(),
86 isDirty: false,
87 resetKey: 0,
88 onAddressChange: vi.fn(),
89 applyAddressElementValue: vi.fn(),
90 markCurrentValuesAsSaved: vi.fn(),
91 addressCountry: 'US',
92 addressOptions: {},
93 }),
94}))
95
96vi.mock('./BillingCustomerDataForm', () => ({
97 BillingCustomerDataForm: () => <div data-testid="billing-customer-data-form" />,
98}))
99
100vi.mock('@/components/ui/PartnerManagedResource', () => ({
101 default: () => <div data-testid="partner-managed-resource" />,
102}))
103
104describe('BillingCustomerData', () => {
105 beforeEach(() => {
106 vi.clearAllMocks()
107 mockSelectedOrganization.mockReturnValue(
108 createMockOrganization({
109 slug: 'stripe-org',
110 billing_partner: null,
111 integration_source: 'stripe_projects',
112 managed_by: MANAGED_BY.STRIPE_PROJECTS,
113 })
114 )
115 })
116
117 it('keeps Stripe-connected orgs editable in Studio', () => {
118 render(<BillingCustomerData />)
119
120 expect(screen.queryByTestId('partner-managed-resource')).not.toBeInTheDocument()
121 expect(screen.getByTestId('billing-customer-data-form')).toBeInTheDocument()
122 })
123
124 it('still blocks billing-partner orgs behind the partner-managed resource', () => {
125 mockSelectedOrganization.mockReturnValue(
126 createMockOrganization({
127 slug: 'vercel-org',
128 billing_partner: 'vercel_marketplace',
129 managed_by: MANAGED_BY.VERCEL_MARKETPLACE,
130 })
131 )
132
133 render(<BillingCustomerData />)
134
135 expect(screen.getByTestId('partner-managed-resource')).toBeInTheDocument()
136 })
137})