RedeemCredits.test.tsx 4.5 KB

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