index.test.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import { act, screen, waitFor } from '@testing-library/react'
  2. import { ResizablePanel, ResizablePanelGroup } from 'ui'
  3. import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
  4. import { LayoutSidebar } from './index'
  5. import { LayoutSidebarProvider, SIDEBAR_KEYS } from './LayoutSidebarProvider'
  6. import { MobileSheetProvider } from '@/components/layouts/Navigation/NavigationBar/MobileSheetContext'
  7. import { sidebarManagerState } from '@/state/sidebar-manager-state'
  8. import { render } from '@/tests/helpers'
  9. import { routerMock } from '@/tests/lib/route-mock'
  10. vi.mock('@/components/ui/AIAssistantPanel/AIAssistant', () => ({
  11. AIAssistant: () => <div data-testid="ai-assistant-sidebar">AI Assistant</div>,
  12. }))
  13. vi.mock('@/components/ui/EditorPanel/EditorPanel', () => ({
  14. EditorPanel: () => <div data-testid="editor-panel-sidebar">Editor Panel</div>,
  15. }))
  16. vi.mock('@/components/ui/AdvisorPanel/AdvisorPanel', () => ({
  17. AdvisorPanel: () => <div data-testid="advisor-panel-sidebar">Advisor Panel</div>,
  18. }))
  19. vi.mock('nuqs', async () => {
  20. let queryValue = 'ai-assistant'
  21. return {
  22. useQueryState: () => [queryValue, (v: string) => (queryValue = v)],
  23. parseAsString: () => {},
  24. }
  25. })
  26. const mockProject = {
  27. id: 1,
  28. ref: 'default',
  29. name: 'Project 1',
  30. status: 'ACTIVE_HEALTHY' as const,
  31. organization_id: 1,
  32. cloud_provider: 'AWS',
  33. region: 'us-east-1',
  34. inserted_at: new Date().toISOString(),
  35. subscription_id: 'subscription-1',
  36. db_host: 'db.supabase.co',
  37. is_branch_enabled: false,
  38. is_physical_backups_enabled: false,
  39. restUrl: 'https://project-1.supabase.co',
  40. }
  41. let mockProjectData: typeof mockProject | undefined = mockProject
  42. vi.mock('@/hooks/misc/useSelectedProject', () => ({
  43. useSelectedProjectQuery: () => {
  44. // Access the variable at runtime when the function is called
  45. return {
  46. data: mockProjectData,
  47. }
  48. },
  49. }))
  50. vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
  51. useSelectedOrganizationQuery: () => ({
  52. data: {
  53. id: 1,
  54. name: 'Organization 1',
  55. slug: 'test-org',
  56. plan: { id: 'free', name: 'Free' },
  57. managed_by: 'briven',
  58. is_owner: true,
  59. billing_email: 'billing@example.com',
  60. billing_partner: null,
  61. usage_billing_enabled: false,
  62. stripe_customer_id: 'stripe-1',
  63. subscription_id: 'subscription-1',
  64. organization_requires_mfa: false,
  65. opt_in_tags: [],
  66. restriction_status: null,
  67. restriction_data: null,
  68. organization_missing_address: false,
  69. },
  70. }),
  71. }))
  72. vi.mock('@/data/telemetry/send-event-mutation', () => ({
  73. useSendEventMutation: () => ({
  74. mutate: vi.fn(),
  75. }),
  76. }))
  77. const resetSidebarManagerState = () => {
  78. Object.keys(sidebarManagerState.sidebars).forEach((id) => {
  79. sidebarManagerState.unregisterSidebar(id)
  80. })
  81. sidebarManagerState.closeActive()
  82. }
  83. describe('LayoutSidebar', () => {
  84. beforeEach(() => {
  85. routerMock.setCurrentUrl('/projects/default')
  86. })
  87. afterEach(() => {
  88. resetSidebarManagerState()
  89. localStorage.clear()
  90. vi.clearAllMocks()
  91. })
  92. const renderSidebar = () =>
  93. render(
  94. <ResizablePanelGroup orientation="horizontal">
  95. <ResizablePanel>
  96. <div />
  97. </ResizablePanel>
  98. <LayoutSidebarProvider>
  99. <MobileSheetProvider>
  100. <LayoutSidebar />
  101. </MobileSheetProvider>
  102. </LayoutSidebarProvider>
  103. </ResizablePanelGroup>
  104. )
  105. it('does not render when there is no active sidebar', () => {
  106. renderSidebar()
  107. expect(screen.queryByTestId('ai-assistant-sidebar')).toBeNull()
  108. })
  109. it('renders the active sidebar content when toggled on', async () => {
  110. renderSidebar()
  111. await waitFor(() => {
  112. expect(sidebarManagerState.sidebars[SIDEBAR_KEYS.AI_ASSISTANT]).toBeDefined()
  113. })
  114. act(() => {
  115. sidebarManagerState.toggleSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
  116. })
  117. const sidebar = await screen.findByTestId('ai-assistant-sidebar')
  118. expect(sidebar).toBeTruthy()
  119. })
  120. describe('at organization level', () => {
  121. beforeEach(() => {
  122. routerMock.setCurrentUrl('/org/default')
  123. // Set project to undefined to simulate org-level (no project)
  124. mockProjectData = undefined
  125. })
  126. afterEach(() => {
  127. // Reset to project data for other tests
  128. mockProjectData = mockProject
  129. })
  130. it('does not register project-related sidebars when no project is available', async () => {
  131. renderSidebar()
  132. // Wait a bit to ensure sidebars have been registered
  133. await waitFor(() => {
  134. // Project-related sidebars should not be registered
  135. expect(sidebarManagerState.sidebars[SIDEBAR_KEYS.AI_ASSISTANT]).toBeUndefined()
  136. expect(sidebarManagerState.sidebars[SIDEBAR_KEYS.EDITOR_PANEL]).toBeUndefined()
  137. // Advisor panel should still be available (doesn't require project)
  138. expect(sidebarManagerState.sidebars[SIDEBAR_KEYS.ADVISOR_PANEL]).toBeDefined()
  139. })
  140. })
  141. it('does not render project-related sidebars even when toggled', async () => {
  142. renderSidebar()
  143. await waitFor(() => {
  144. expect(sidebarManagerState.sidebars[SIDEBAR_KEYS.ADVISOR_PANEL]).toBeDefined()
  145. })
  146. // Try to toggle AI_ASSISTANT - should not work since it's not registered
  147. act(() => {
  148. sidebarManagerState.toggleSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
  149. })
  150. // Should not render since it's not registered
  151. expect(screen.queryByTestId('ai-assistant-sidebar')).toBeNull()
  152. expect(screen.queryByTestId('editor-panel-sidebar')).toBeNull()
  153. // Advisor panel should work
  154. act(() => {
  155. sidebarManagerState.toggleSidebar(SIDEBAR_KEYS.ADVISOR_PANEL)
  156. })
  157. expect(await screen.findByTestId('advisor-panel-sidebar')).toBeTruthy()
  158. })
  159. })
  160. // [Joshen] JFYI temporarily commented this one out - I'm struggling to figure out the mocking to get this to work
  161. // it('auto-opens when sidebar query param matches a registered sidebar', async () => {
  162. // routerMock.setCurrentUrl(`/?sidebar=${SIDEBAR_KEYS.AI_ASSISTANT}`)
  163. // renderSidebar()
  164. // await screen.findByTestId('ai-assistant-sidebar')
  165. // })
  166. })