OrganizationLayout.test.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import { fireEvent, screen } from '@testing-library/react'
  2. import { LOCAL_STORAGE_KEYS } from 'common'
  3. import { beforeEach, describe, expect, it, vi } from 'vitest'
  4. import OrganizationLayout from './OrganizationLayout'
  5. import { MANAGED_BY } from '@/lib/constants/infrastructure'
  6. import { createMockOrganization, render } from '@/tests/helpers'
  7. const {
  8. mockUseAwsRedirectQuery,
  9. mockUseCustomContent,
  10. mockUseLocalStorageQuery,
  11. mockSetIsBannerDismissed,
  12. mockUseRegisterOrgMenu,
  13. mockUseSelectedOrganizationQuery,
  14. mockUseVercelRedirectQuery,
  15. } = vi.hoisted(() => ({
  16. mockUseAwsRedirectQuery: vi.fn(),
  17. mockUseCustomContent: vi.fn(),
  18. mockUseLocalStorageQuery: vi.fn(),
  19. mockSetIsBannerDismissed: vi.fn(),
  20. mockUseRegisterOrgMenu: vi.fn(),
  21. mockUseSelectedOrganizationQuery: vi.fn(),
  22. mockUseVercelRedirectQuery: vi.fn(),
  23. }))
  24. vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
  25. useSelectedOrganizationQuery: mockUseSelectedOrganizationQuery,
  26. }))
  27. vi.mock('@/data/integrations/vercel-redirect-query', () => ({
  28. useVercelRedirectQuery: mockUseVercelRedirectQuery,
  29. }))
  30. vi.mock('@/data/integrations/aws-redirect-query', () => ({
  31. useAwsRedirectQuery: mockUseAwsRedirectQuery,
  32. }))
  33. vi.mock('@/hooks/misc/useLocalStorage', () => ({
  34. useLocalStorageQuery: mockUseLocalStorageQuery,
  35. }))
  36. vi.mock('@/hooks/custom-content/useCustomContent', () => ({
  37. useCustomContent: mockUseCustomContent,
  38. }))
  39. vi.mock('@/hooks/misc/withAuth', () => ({
  40. withAuth: (Component: any) => Component,
  41. }))
  42. vi.mock('./OrganizationLayout/useRegisterOrgMenu', () => ({
  43. useRegisterOrgMenu: mockUseRegisterOrgMenu,
  44. }))
  45. vi.mock('@/components/ui/PartnerIcon', () => ({
  46. default: () => <div data-testid="partner-icon" />,
  47. }))
  48. const renderLayout = () =>
  49. render(
  50. <OrganizationLayout title="General">
  51. <div>Organization content</div>
  52. </OrganizationLayout>
  53. )
  54. describe('OrganizationLayout', () => {
  55. beforeEach(() => {
  56. vi.clearAllMocks()
  57. mockUseLocalStorageQuery.mockReturnValue([
  58. false,
  59. mockSetIsBannerDismissed,
  60. { isSuccess: true, isLoading: false, isError: false, error: null },
  61. ])
  62. mockUseCustomContent.mockReturnValue({ appTitle: 'Briven' })
  63. mockUseVercelRedirectQuery.mockReturnValue({ data: undefined, isSuccess: false })
  64. mockUseAwsRedirectQuery.mockReturnValue({ data: undefined, isSuccess: false })
  65. })
  66. it('renders the exact Vercel banner copy and manage URL', () => {
  67. mockUseSelectedOrganizationQuery.mockReturnValue({
  68. data: createMockOrganization({
  69. managed_by: MANAGED_BY.VERCEL_MARKETPLACE,
  70. partner_id: 'vercel-installation-id',
  71. }),
  72. })
  73. mockUseVercelRedirectQuery.mockReturnValue({
  74. data: { url: 'https://vercel.com/manage-org' },
  75. isSuccess: true,
  76. })
  77. renderLayout()
  78. expect(screen.getByText('This organization is managed via Vercel Marketplace')).toBeTruthy()
  79. expect(
  80. screen.getByText('Billing and some organization access settings are managed in Vercel.')
  81. ).toBeTruthy()
  82. expect(screen.getByRole('link', { name: 'Manage' }).getAttribute('href')).toBe(
  83. 'https://vercel.com/manage-org'
  84. )
  85. expect(mockUseVercelRedirectQuery).toHaveBeenCalledWith(
  86. { installationId: 'vercel-installation-id' },
  87. expect.objectContaining({ enabled: true })
  88. )
  89. expect(mockUseLocalStorageQuery).toHaveBeenCalledWith(
  90. LOCAL_STORAGE_KEYS.ORGANIZATION_MARKETPLACE_BANNER_DISMISSED(
  91. 'abcdefghijklmnopqrst',
  92. MANAGED_BY.VERCEL_MARKETPLACE
  93. ),
  94. false
  95. )
  96. expect(mockUseAwsRedirectQuery).toHaveBeenCalledWith(
  97. { organizationSlug: 'abcdefghijklmnopqrst' },
  98. expect.objectContaining({ enabled: false })
  99. )
  100. })
  101. it('renders the exact AWS banner copy and manage URL', () => {
  102. mockUseSelectedOrganizationQuery.mockReturnValue({
  103. data: createMockOrganization({
  104. managed_by: MANAGED_BY.AWS_MARKETPLACE,
  105. slug: 'aws-org',
  106. }),
  107. })
  108. mockUseAwsRedirectQuery.mockReturnValue({
  109. data: { url: 'https://console.aws.amazon.com/billing/home' },
  110. isSuccess: true,
  111. })
  112. renderLayout()
  113. expect(screen.getByText('This organization is billed via AWS Marketplace')).toBeTruthy()
  114. expect(
  115. screen.getByText('Changes to billing and payment details must be made in AWS.')
  116. ).toBeTruthy()
  117. expect(screen.getByRole('link', { name: 'Manage' }).getAttribute('href')).toBe(
  118. 'https://console.aws.amazon.com/billing/home'
  119. )
  120. expect(mockUseAwsRedirectQuery).toHaveBeenCalledWith(
  121. { organizationSlug: 'aws-org' },
  122. expect.objectContaining({ enabled: true })
  123. )
  124. expect(mockUseLocalStorageQuery).toHaveBeenCalledWith(
  125. LOCAL_STORAGE_KEYS.ORGANIZATION_MARKETPLACE_BANNER_DISMISSED(
  126. 'aws-org',
  127. MANAGED_BY.AWS_MARKETPLACE
  128. ),
  129. false
  130. )
  131. expect(mockUseVercelRedirectQuery).toHaveBeenCalledWith(
  132. { installationId: undefined },
  133. expect.objectContaining({ enabled: false })
  134. )
  135. })
  136. it('does not render Manage for Vercel when redirect URL is unavailable', () => {
  137. mockUseSelectedOrganizationQuery.mockReturnValue({
  138. data: createMockOrganization({
  139. managed_by: MANAGED_BY.VERCEL_MARKETPLACE,
  140. partner_id: 'vercel-installation-id',
  141. }),
  142. })
  143. mockUseVercelRedirectQuery.mockReturnValue({
  144. data: undefined,
  145. isSuccess: false,
  146. })
  147. renderLayout()
  148. expect(screen.getByText('This organization is managed via Vercel Marketplace')).toBeTruthy()
  149. expect(screen.queryByRole('link', { name: 'Manage' })).toBeNull()
  150. })
  151. it('does not render Manage for AWS when redirect URL is unavailable', () => {
  152. mockUseSelectedOrganizationQuery.mockReturnValue({
  153. data: createMockOrganization({
  154. managed_by: MANAGED_BY.AWS_MARKETPLACE,
  155. slug: 'aws-org',
  156. }),
  157. })
  158. mockUseAwsRedirectQuery.mockReturnValue({
  159. data: undefined,
  160. isSuccess: false,
  161. })
  162. renderLayout()
  163. expect(screen.getByText('This organization is billed via AWS Marketplace')).toBeTruthy()
  164. expect(screen.queryByRole('link', { name: 'Manage' })).toBeNull()
  165. })
  166. it('does not render a banner for Briven-managed organizations', () => {
  167. mockUseSelectedOrganizationQuery.mockReturnValue({
  168. data: createMockOrganization({
  169. managed_by: MANAGED_BY.BRIVEN,
  170. }),
  171. })
  172. renderLayout()
  173. expect(screen.queryByRole('link', { name: 'Manage' })).toBeNull()
  174. expect(screen.queryByText('This organization is managed via Vercel Marketplace')).toBeNull()
  175. expect(screen.queryByText('This organization is billed via AWS Marketplace')).toBeNull()
  176. })
  177. it('renders Stripe connected copy with no Manage button', () => {
  178. mockUseSelectedOrganizationQuery.mockReturnValue({
  179. data: createMockOrganization({
  180. managed_by: MANAGED_BY.STRIPE_PROJECTS,
  181. }),
  182. })
  183. renderLayout()
  184. expect(screen.getByText('This organization is connected to Stripe')).toBeTruthy()
  185. expect(
  186. screen.getByText('Changes here will be reflected in your connected Stripe account.')
  187. ).toBeTruthy()
  188. expect(screen.queryByRole('link', { name: 'Manage' })).toBeNull()
  189. expect(mockUseVercelRedirectQuery).toHaveBeenCalledWith(
  190. { installationId: undefined },
  191. expect.objectContaining({ enabled: false })
  192. )
  193. expect(mockUseLocalStorageQuery).toHaveBeenCalledWith(
  194. LOCAL_STORAGE_KEYS.ORGANIZATION_MARKETPLACE_BANNER_DISMISSED(
  195. 'abcdefghijklmnopqrst',
  196. MANAGED_BY.STRIPE_PROJECTS
  197. ),
  198. false
  199. )
  200. expect(mockUseAwsRedirectQuery).toHaveBeenCalledWith(
  201. { organizationSlug: 'abcdefghijklmnopqrst' },
  202. expect.objectContaining({ enabled: false })
  203. )
  204. })
  205. it('hides banner when dismissal state is persisted', () => {
  206. mockUseLocalStorageQuery.mockReturnValue([
  207. true,
  208. mockSetIsBannerDismissed,
  209. { isSuccess: true, isLoading: false, isError: false, error: null },
  210. ])
  211. mockUseSelectedOrganizationQuery.mockReturnValue({
  212. data: createMockOrganization({
  213. managed_by: MANAGED_BY.VERCEL_MARKETPLACE,
  214. partner_id: 'vercel-installation-id',
  215. }),
  216. })
  217. renderLayout()
  218. expect(screen.queryByRole('link', { name: 'Manage' })).toBeNull()
  219. expect(screen.queryByText('This organization is managed via Vercel Marketplace')).toBeNull()
  220. })
  221. it('dismisses the banner and persists the state', () => {
  222. mockUseSelectedOrganizationQuery.mockReturnValue({
  223. data: createMockOrganization({
  224. managed_by: MANAGED_BY.AWS_MARKETPLACE,
  225. }),
  226. })
  227. mockUseAwsRedirectQuery.mockReturnValue({
  228. data: { url: 'https://console.aws.amazon.com/billing/home' },
  229. isSuccess: true,
  230. })
  231. renderLayout()
  232. fireEvent.click(screen.getByRole('button', { name: 'Dismiss banner' }))
  233. expect(mockSetIsBannerDismissed).toHaveBeenCalledWith(true)
  234. })
  235. })