RedeemCredits.test.tsx145 lines · main
1import { screen, waitFor } from '@testing-library/react'
2import userEvent from '@testing-library/user-event'
3import { FeatureFlagContext } from 'common'
4import { HttpResponse } from 'msw'
5import { beforeEach, describe, expect, test, vi } from 'vitest'
6
7import { RedeemCreditsScreen } from './RedeemCredits'
8import type { ProfileContextType } from '@/lib/profile'
9import { createMockOrganization } from '@/tests/helpers'
10import { customRender } from '@/tests/lib/custom-render'
11import { addAPIMock } from '@/tests/lib/msw'
12import { routerMock } from '@/tests/lib/route-mock'
13
14const { creditRedemptionProps } = vi.hoisted(() => ({
15 creditRedemptionProps: vi.fn(),
16}))
17const { creditRedemptionQueryCode } = vi.hoisted(() => ({
18 creditRedemptionQueryCode: { current: undefined as string | undefined },
19}))
20
21vi.mock('@/components/interfaces/Organization/BillingSettings/CreditCodeRedemption', () => {
22 return {
23 CreditCodeRedemption: (props: { slug?: string }) => {
24 creditRedemptionProps({ ...props, queryCode: creditRedemptionQueryCode.current })
25 return (
26 <div data-testid="credit-redemption">
27 Credit redemption for {props.slug} with code {creditRedemptionQueryCode.current}
28 </div>
29 )
30 },
31 }
32})
33
34const DEFAULT_PROFILE_CONTEXT: ProfileContextType = {
35 profile: {
36 id: 1,
37 auth0_id: 'auth0|test',
38 gotrue_id: 'gotrue-test',
39 username: 'testuser',
40 primary_email: 'test@example.com',
41 first_name: null,
42 last_name: null,
43 mobile: null,
44 is_alpha_user: false,
45 is_sso_user: false,
46 disabled_features: [],
47 free_project_limit: null,
48 },
49 error: null,
50 isLoading: false,
51 isError: false,
52 isSuccess: true,
53}
54
55const ORGANIZATION = createMockOrganization({
56 id: 1,
57 name: 'Acme Production',
58 slug: 'acme-production',
59 plan: { id: 'pro', name: 'Pro' },
60})
61
62function renderScreen() {
63 return customRender(
64 <FeatureFlagContext.Provider value={{ configcat: {}, posthog: {}, hasLoaded: true }}>
65 <RedeemCreditsScreen />
66 </FeatureFlagContext.Provider>,
67 { profileContext: DEFAULT_PROFILE_CONTEXT }
68 )
69}
70
71describe('RedeemCreditsScreen', () => {
72 beforeEach(() => {
73 vi.clearAllMocks()
74 creditRedemptionQueryCode.current = undefined
75 routerMock.setCurrentUrl('/redeem')
76 })
77
78 test('renders ready state from organizations query and opens redemption for selected organization', async () => {
79 const user = userEvent.setup()
80 routerMock.setCurrentUrl('/redeem?code=SUPA-CREDIT-123')
81 creditRedemptionQueryCode.current = 'SUPA-CREDIT-123'
82 addAPIMock({
83 method: 'get',
84 path: '/platform/organizations',
85 response: () => HttpResponse.json([ORGANIZATION]),
86 })
87
88 renderScreen()
89
90 await user.click(await screen.findByRole('button', { name: /Acme Production/ }))
91 await user.click(screen.getByRole('button', { name: 'Redeem credits' }))
92
93 expect(await screen.findByTestId('credit-redemption')).toHaveTextContent(
94 'Credit redemption for acme-production with code SUPA-CREDIT-123'
95 )
96 expect(creditRedemptionProps).toHaveBeenCalledWith(
97 expect.objectContaining({
98 slug: 'acme-production',
99 queryCode: 'SUPA-CREDIT-123',
100 })
101 )
102 })
103
104 test('routes new organization creation back to the current redeem URL', async () => {
105 routerMock.setCurrentUrl('/redeem?code=SUPA-CREDIT-123')
106 addAPIMock({
107 method: 'get',
108 path: '/platform/organizations',
109 response: () => HttpResponse.json([ORGANIZATION]),
110 })
111
112 renderScreen()
113
114 const createOrganizationLink = await screen.findByRole('link', {
115 name: /Create new organization/,
116 })
117
118 expect(createOrganizationLink).toHaveAttribute(
119 'href',
120 '/new?returnTo=%2Fredeem%3Fcode%3DSUPA-CREDIT-123&returnToOrgParam=selected_org'
121 )
122 })
123
124 test('preselects an organization returned from new organization creation', async () => {
125 const user = userEvent.setup()
126 routerMock.setCurrentUrl('/redeem?code=SUPA-CREDIT-123&selected_org=acme-production')
127 creditRedemptionQueryCode.current = 'SUPA-CREDIT-123'
128 addAPIMock({
129 method: 'get',
130 path: '/platform/organizations',
131 response: () => HttpResponse.json([ORGANIZATION]),
132 })
133
134 renderScreen()
135
136 const redeemButton = await screen.findByRole('button', { name: 'Redeem credits' })
137
138 await waitFor(() => expect(redeemButton).toBeEnabled())
139 await user.click(redeemButton)
140
141 expect(await screen.findByTestId('credit-redemption')).toHaveTextContent(
142 'Credit redemption for acme-production with code SUPA-CREDIT-123'
143 )
144 })
145})