AccountLayout.selfhosted.test.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { render, screen, waitFor } from '@testing-library/react'
  2. import type { PropsWithChildren, ReactNode } from 'react'
  3. import { beforeEach, describe, expect, it, vi } from 'vitest'
  4. import AccountLayout from './AccountLayout'
  5. const { mockRouter, mockRegisterOpenMenu, mockSetMobileSheetContent } = vi.hoisted(() => ({
  6. mockRouter: {
  7. pathname: '/account/me',
  8. push: vi.fn(),
  9. },
  10. mockRegisterOpenMenu: vi.fn(),
  11. mockSetMobileSheetContent: vi.fn(),
  12. }))
  13. vi.mock('@/lib/constants', async () => {
  14. const actual = await vi.importActual<Record<string, unknown>>('@/lib/constants')
  15. return {
  16. ...actual,
  17. IS_PLATFORM: false,
  18. }
  19. })
  20. vi.mock('next/router', () => ({
  21. useRouter: () => mockRouter,
  22. }))
  23. vi.mock('next/head', async () => {
  24. const React = await import('react')
  25. const Head = ({ children }: { children?: ReactNode }) => {
  26. React.useEffect(() => {
  27. const titleElement = React.Children.toArray(children).find(
  28. (child) => React.isValidElement(child) && child.type === 'title'
  29. )
  30. if (!React.isValidElement<{ children: ReactNode }>(titleElement)) return
  31. const titleText = React.Children.toArray(titleElement.props.children).join('')
  32. document.title = titleText
  33. }, [children])
  34. return null
  35. }
  36. return { default: Head }
  37. })
  38. vi.mock('@/hooks/custom-content/useCustomContent', () => ({
  39. useCustomContent: () => ({ appTitle: 'Briven' }),
  40. }))
  41. vi.mock('@/hooks/misc/useIsFeatureEnabled', () => ({
  42. useIsFeatureEnabled: () => false,
  43. }))
  44. vi.mock('@/hooks/misc/useLocalStorage', () => ({
  45. useLocalStorageQuery: () => [''],
  46. }))
  47. vi.mock('@/hooks/misc/withAuth', () => ({
  48. withAuth: <T,>(Component: T) => Component,
  49. }))
  50. vi.mock('@/state/app-state', () => ({
  51. useAppStateSnapshot: () => ({
  52. lastRouteBeforeVisitingAccountPage: '',
  53. }),
  54. }))
  55. vi.mock('../Navigation/NavigationBar/MobileSheetContext', () => ({
  56. useMobileSheet: () => ({
  57. setContent: mockSetMobileSheetContent,
  58. registerOpenMenu: (callback: () => void) => {
  59. mockRegisterOpenMenu(callback)
  60. return () => {}
  61. },
  62. }),
  63. }))
  64. vi.mock('./WithSidebar', () => ({
  65. WithSidebar: ({
  66. sections,
  67. children,
  68. }: PropsWithChildren<{
  69. sections: Array<{
  70. key: string
  71. heading?: string
  72. links: Array<{ key: string; label: string }>
  73. }>
  74. }>) => (
  75. <div>
  76. <nav>
  77. {sections.map((section) => (
  78. <div key={section.key}>
  79. {section.heading ? <span>{section.heading}</span> : null}
  80. {section.links.map((link) => (
  81. <span key={link.key}>{link.label}</span>
  82. ))}
  83. </div>
  84. ))}
  85. </nav>
  86. {children}
  87. </div>
  88. ),
  89. }))
  90. vi.mock('ui', () => ({
  91. cn: (...classes: Array<string | false | null | undefined>) => classes.filter(Boolean).join(' '),
  92. }))
  93. describe('AccountLayout (self-hosted)', () => {
  94. beforeEach(() => {
  95. mockRouter.pathname = '/account/me'
  96. mockRouter.push.mockReset()
  97. mockRegisterOpenMenu.mockReset()
  98. mockSetMobileSheetContent.mockReset()
  99. document.title = ''
  100. })
  101. it('keeps /account/me available and shows only the Preferences link', async () => {
  102. render(
  103. <AccountLayout title="Preferences">
  104. <div>Preferences page</div>
  105. </AccountLayout>
  106. )
  107. await waitFor(() => {
  108. expect(document.title).toBe('Preferences | Briven')
  109. })
  110. expect(screen.getByText('Preferences')).toBeInTheDocument()
  111. expect(screen.queryByText('Access Tokens')).not.toBeInTheDocument()
  112. expect(screen.queryByText('Account Settings')).not.toBeInTheDocument()
  113. expect(mockRouter.push).not.toHaveBeenCalled()
  114. })
  115. it('redirects unsupported account routes back to the project dashboard', async () => {
  116. mockRouter.pathname = '/account/tokens'
  117. render(
  118. <AccountLayout title="Preferences">
  119. <div>Unsupported</div>
  120. </AccountLayout>
  121. )
  122. await waitFor(() => {
  123. expect(mockRouter.push).toHaveBeenCalledWith('/project/default')
  124. })
  125. })
  126. })