@jmruthers/pace-core 0.5.73 → 0.5.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/dist/{DataTable-INW5YIFV.js → DataTable-HWZQGASI.js} +8 -8
  2. package/dist/{PublicLoadingSpinner-DLpF5bbs.d.ts → PublicLoadingSpinner-BKNBT6b6.d.ts} +2 -2
  3. package/dist/RBACService-C4udt_Zp.d.ts +528 -0
  4. package/dist/{UnifiedAuthProvider-6SYT5WFN.js → UnifiedAuthProvider-3NKDOSOK.js} +6 -4
  5. package/dist/UnifiedAuthProvider-Bj6YCf7c.d.ts +113 -0
  6. package/dist/{chunk-2PRPDH66.js → chunk-2CHATWBF.js} +5 -7
  7. package/dist/chunk-2CHATWBF.js.map +1 -0
  8. package/dist/{chunk-43C63KLH.js → chunk-2DFZ432F.js} +496 -30
  9. package/dist/chunk-2DFZ432F.js.map +1 -0
  10. package/dist/{chunk-M4UMXYNK.js → chunk-33PHABLB.js} +36 -3
  11. package/dist/chunk-33PHABLB.js.map +1 -0
  12. package/dist/chunk-5F3NDPJV.js +232 -0
  13. package/dist/chunk-5F3NDPJV.js.map +1 -0
  14. package/dist/chunk-A4FUBC7B.js +17 -0
  15. package/dist/chunk-A4FUBC7B.js.map +1 -0
  16. package/dist/{chunk-SMJZMKYN.js → chunk-A6HBIY5P.js} +2 -11
  17. package/dist/{chunk-SMJZMKYN.js.map → chunk-A6HBIY5P.js.map} +1 -1
  18. package/dist/{chunk-GBC5PC3N.js → chunk-CY3AHGO4.js} +6256 -1937
  19. package/dist/chunk-CY3AHGO4.js.map +1 -0
  20. package/dist/{chunk-BYG6OSTC.js → chunk-DAXLNIDY.js} +48 -50
  21. package/dist/chunk-DAXLNIDY.js.map +1 -0
  22. package/dist/{chunk-VKOCWWVY.js → chunk-L3RV2ALE.js} +1 -6
  23. package/dist/{chunk-VKOCWWVY.js.map → chunk-L3RV2ALE.js.map} +1 -1
  24. package/dist/chunk-LW7MMEAQ.js +59 -0
  25. package/dist/chunk-LW7MMEAQ.js.map +1 -0
  26. package/dist/{chunk-LANO5IFV.js → chunk-NTNILOBC.js} +7 -9
  27. package/dist/chunk-NTNILOBC.js.map +1 -0
  28. package/dist/chunk-PYUXFQJ3.js +11 -0
  29. package/dist/chunk-PYUXFQJ3.js.map +1 -0
  30. package/dist/chunk-URUTVZ7N.js +27 -0
  31. package/dist/chunk-URUTVZ7N.js.map +1 -0
  32. package/dist/chunk-WN6XJWOS.js +2468 -0
  33. package/dist/chunk-WN6XJWOS.js.map +1 -0
  34. package/dist/{chunk-3SP4P7NS.js → chunk-XLZ7U46Z.js} +59 -1
  35. package/dist/chunk-XLZ7U46Z.js.map +1 -0
  36. package/dist/{chunk-UC2BWIK7.js → chunk-ZTT2AXMX.js} +9 -14
  37. package/dist/chunk-ZTT2AXMX.js.map +1 -0
  38. package/dist/components.d.ts +4 -5
  39. package/dist/components.js +32 -39
  40. package/dist/components.js.map +1 -1
  41. package/dist/hooks.d.ts +3 -3
  42. package/dist/hooks.js +9 -8
  43. package/dist/hooks.js.map +1 -1
  44. package/dist/index.d.ts +156 -10
  45. package/dist/index.js +188 -93
  46. package/dist/index.js.map +1 -1
  47. package/dist/{organisation-t-vvQC3g.d.ts → organisation-BtshODVF.d.ts} +4 -3
  48. package/dist/providers.d.ts +27 -38
  49. package/dist/providers.js +33 -23
  50. package/dist/rbac/index.d.ts +61 -5
  51. package/dist/rbac/index.js +13 -14
  52. package/dist/styles/index.js +2 -2
  53. package/dist/theming/runtime.js +1 -3
  54. package/dist/types.d.ts +3 -3
  55. package/dist/types.js +1 -1
  56. package/dist/types.js.map +1 -1
  57. package/dist/{unified-CMPjE_fv.d.ts → unified-CM7T0aTK.d.ts} +1 -1
  58. package/dist/useInactivityTracker-MRUU55XI.js +10 -0
  59. package/dist/useInactivityTracker-MRUU55XI.js.map +1 -0
  60. package/dist/{usePublicRouteParams-Ua1Vz-HG.d.ts → usePublicRouteParams-B-CumWRc.d.ts} +3 -3
  61. package/dist/utils.js +7 -9
  62. package/dist/utils.js.map +1 -1
  63. package/dist/validation.d.ts +1 -1
  64. package/docs/api/classes/ColumnFactory.md +1 -1
  65. package/docs/api/classes/ErrorBoundary.md +1 -1
  66. package/docs/api/classes/InvalidScopeError.md +1 -1
  67. package/docs/api/classes/MissingUserContextError.md +1 -1
  68. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  69. package/docs/api/classes/PermissionDeniedError.md +1 -1
  70. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  71. package/docs/api/classes/RBACAuditManager.md +1 -1
  72. package/docs/api/classes/RBACCache.md +1 -1
  73. package/docs/api/classes/RBACEngine.md +1 -1
  74. package/docs/api/classes/RBACError.md +1 -1
  75. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  76. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  77. package/docs/api/classes/StorageUtils.md +1 -1
  78. package/docs/api/enums/FileCategory.md +1 -1
  79. package/docs/api/interfaces/AggregateConfig.md +1 -1
  80. package/docs/api/interfaces/ButtonProps.md +3 -3
  81. package/docs/api/interfaces/CardProps.md +2 -2
  82. package/docs/api/interfaces/ColorPalette.md +1 -1
  83. package/docs/api/interfaces/ColorShade.md +1 -1
  84. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  85. package/docs/api/interfaces/DataTableAction.md +1 -1
  86. package/docs/api/interfaces/DataTableColumn.md +1 -1
  87. package/docs/api/interfaces/DataTableProps.md +1 -1
  88. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  89. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  90. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  91. package/docs/api/interfaces/EventLogoProps.md +2 -2
  92. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  93. package/docs/api/interfaces/FileMetadata.md +1 -1
  94. package/docs/api/interfaces/FileReference.md +1 -1
  95. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  96. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  97. package/docs/api/interfaces/FileUploadProps.md +1 -1
  98. package/docs/api/interfaces/FooterProps.md +1 -1
  99. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  100. package/docs/api/interfaces/InputProps.md +2 -2
  101. package/docs/api/interfaces/LabelProps.md +1 -1
  102. package/docs/api/interfaces/LoginFormProps.md +1 -1
  103. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  104. package/docs/api/interfaces/NavigationContextType.md +1 -1
  105. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  106. package/docs/api/interfaces/NavigationItem.md +1 -1
  107. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  108. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  109. package/docs/api/interfaces/Organisation.md +1 -1
  110. package/docs/api/interfaces/OrganisationContextType.md +28 -17
  111. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  112. package/docs/api/interfaces/OrganisationProviderProps.md +2 -2
  113. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  114. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  115. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  116. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  117. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  118. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  119. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  120. package/docs/api/interfaces/PaletteData.md +1 -1
  121. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  122. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  123. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  124. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +2 -2
  125. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  126. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  127. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  128. package/docs/api/interfaces/RBACConfig.md +1 -1
  129. package/docs/api/interfaces/RBACContextType.md +5 -11
  130. package/docs/api/interfaces/RBACLogger.md +1 -1
  131. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  132. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  133. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  134. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  135. package/docs/api/interfaces/RouteConfig.md +1 -1
  136. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  137. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  138. package/docs/api/interfaces/StorageConfig.md +1 -1
  139. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  140. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  141. package/docs/api/interfaces/StorageListOptions.md +1 -1
  142. package/docs/api/interfaces/StorageListResult.md +1 -1
  143. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  144. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  145. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  146. package/docs/api/interfaces/StyleImport.md +1 -1
  147. package/docs/api/interfaces/SwitchProps.md +1 -1
  148. package/docs/api/interfaces/ToastActionElement.md +1 -1
  149. package/docs/api/interfaces/ToastProps.md +1 -1
  150. package/docs/api/interfaces/UnifiedAuthContextType.md +524 -440
  151. package/docs/api/interfaces/UnifiedAuthProviderProps.md +14 -14
  152. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  153. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  154. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  155. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  159. package/docs/api/interfaces/UserEventAccess.md +11 -11
  160. package/docs/api/interfaces/UserMenuProps.md +1 -1
  161. package/docs/api/interfaces/UserProfile.md +1 -1
  162. package/docs/api/modules.md +179 -52
  163. package/docs/architecture/services.md +30 -32
  164. package/docs/breaking-changes.md +2 -5
  165. package/docs/implementation-guides/data-tables.md +82 -1
  166. package/docs/migration/service-architecture.md +121 -260
  167. package/docs/rbac/README-rbac-rls-integration.md +48 -38
  168. package/{src/rbac/examples → examples/RBAC}/CompleteRBACExample.tsx +3 -2
  169. package/{src/rbac/examples → examples/RBAC}/EventBasedApp.tsx +5 -4
  170. package/{src/components/examples → examples/RBAC}/PermissionExample.tsx +7 -6
  171. package/examples/RBAC/__tests__/PermissionExample.test.tsx +150 -0
  172. package/examples/RBAC/index.ts +13 -0
  173. package/examples/README.md +37 -0
  174. package/examples/index.ts +22 -0
  175. package/{src/examples → examples/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
  176. package/{src/examples → examples/public-pages}/PublicEventPage.tsx +1 -1
  177. package/{src/examples → examples/public-pages}/PublicPageApp.tsx +1 -1
  178. package/{src/examples → examples/public-pages}/PublicPageUsageExample.tsx +1 -1
  179. package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +159 -0
  180. package/examples/public-pages/index.ts +14 -0
  181. package/package.json +22 -18
  182. package/src/__tests__/TEST_GUIDE_CURSOR.md +650 -9
  183. package/src/__tests__/helpers/README.md +255 -0
  184. package/src/__tests__/helpers/index.ts +62 -0
  185. package/src/__tests__/helpers/supabaseMock.ts +27 -3
  186. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -8
  187. package/src/components/DataTable/components/DataTableCore.tsx +37 -3
  188. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +55 -0
  189. package/src/components/DataTable/core/ColumnManager.ts +10 -0
  190. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +254 -0
  191. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +193 -0
  192. package/src/components/DataTable/examples/__tests__/HierarchicalExample.test.tsx +45 -0
  193. package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +117 -0
  194. package/src/components/Dialog/Dialog.tsx +2 -2
  195. package/src/components/Dialog/examples/__tests__/HtmlDialogExample.test.tsx +71 -0
  196. package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +122 -0
  197. package/src/components/EventSelector/EventSelector.tsx +1 -1
  198. package/src/components/Header/Header.test.tsx +35 -1
  199. package/src/components/Header/Header.tsx +3 -1
  200. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -3
  201. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +24 -4
  202. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +3 -2
  203. package/src/components/Toast/Toast.test.tsx +1 -1
  204. package/src/components/Toast/Toast.tsx +1 -1
  205. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +220 -0
  206. package/src/hooks/__tests__/useIsMobile.unit.test.ts +117 -0
  207. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +295 -0
  208. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +29 -19
  209. package/src/hooks/__tests__/useRBAC.unit.test.ts +7 -3
  210. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +115 -19
  211. package/src/hooks/useEventTheme.test.ts +350 -0
  212. package/src/hooks/useEventTheme.ts +1 -1
  213. package/src/hooks/useEvents.ts +61 -0
  214. package/src/hooks/useOrganisationSecurity.test.ts +4 -4
  215. package/src/hooks/useOrganisationSecurity.ts +2 -2
  216. package/src/hooks/useOrganisations.ts +64 -0
  217. package/src/hooks/useSecureDataAccess.test.ts +9 -5
  218. package/src/hooks/useSecureDataAccess.ts +2 -2
  219. package/src/index.ts +18 -3
  220. package/src/providers/AuthProvider.tsx +8 -292
  221. package/src/providers/EventProvider.tsx +15 -425
  222. package/src/providers/InactivityProvider.tsx +8 -231
  223. package/src/providers/OrganisationProvider.test.simple.tsx +3 -2
  224. package/src/providers/OrganisationProvider.tsx +11 -890
  225. package/src/providers/UnifiedAuthProvider.tsx +8 -320
  226. package/src/providers/__tests__/AuthProvider.test.tsx +18 -17
  227. package/src/providers/__tests__/EventProvider.test.tsx +253 -2
  228. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +65 -0
  229. package/src/providers/__tests__/InactivityProvider.test.tsx +46 -114
  230. package/src/providers/__tests__/OrganisationProvider.test.tsx +313 -3
  231. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +383 -2
  232. package/src/providers/index.ts +8 -7
  233. package/src/providers/services/EventServiceProvider.tsx +3 -0
  234. package/src/providers/services/UnifiedAuthProvider.tsx +3 -0
  235. package/src/rbac/hooks/usePermissions.test.ts +296 -0
  236. package/src/rbac/hooks/useRBAC.test.ts +9 -5
  237. package/src/rbac/hooks/useRBAC.ts +3 -3
  238. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +688 -0
  239. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +507 -0
  240. package/src/services/AuthService.ts +19 -4
  241. package/src/services/__tests__/AuthService.test.ts +288 -0
  242. package/src/styles/core.css +2 -0
  243. package/src/types/__tests__/guards.test.ts +246 -0
  244. package/src/types/guards.ts +1 -0
  245. package/src/types/organisation.ts +3 -2
  246. package/src/validation/__tests__/sanitization.unit.test.ts +250 -0
  247. package/src/validation/__tests__/schemaUtils.unit.test.ts +451 -0
  248. package/src/validation/__tests__/user.unit.test.ts +440 -0
  249. package/dist/RBACProvider-BO4ilsQB.d.ts +0 -63
  250. package/dist/UnifiedAuthProvider-D02AMXgO.d.ts +0 -103
  251. package/dist/chunk-2PRPDH66.js.map +0 -1
  252. package/dist/chunk-3SP4P7NS.js.map +0 -1
  253. package/dist/chunk-43C63KLH.js.map +0 -1
  254. package/dist/chunk-5A4RL4BC.js +0 -5670
  255. package/dist/chunk-5A4RL4BC.js.map +0 -1
  256. package/dist/chunk-BYG6OSTC.js.map +0 -1
  257. package/dist/chunk-CDDYJCYU.js +0 -79
  258. package/dist/chunk-CDDYJCYU.js.map +0 -1
  259. package/dist/chunk-F24P24TZ.js +0 -17
  260. package/dist/chunk-F24P24TZ.js.map +0 -1
  261. package/dist/chunk-GBC5PC3N.js.map +0 -1
  262. package/dist/chunk-LANO5IFV.js.map +0 -1
  263. package/dist/chunk-M4UMXYNK.js.map +0 -1
  264. package/dist/chunk-RJNE764D.js +0 -953
  265. package/dist/chunk-RJNE764D.js.map +0 -1
  266. package/dist/chunk-UC2BWIK7.js.map +0 -1
  267. package/dist/rbac/cli/policy-manager.js +0 -278
  268. package/dist/rbac/cli/policy-manager.js.map +0 -1
  269. package/docs/api/interfaces/EventContextType.md +0 -96
  270. package/docs/api/interfaces/EventProviderProps.md +0 -19
  271. package/src/providers/OrganisationProvider.test.tsx +0 -164
  272. package/src/providers/UnifiedAuthProvider.test.tsx +0 -124
  273. package/src/providers/__tests__/AuthProvider.test.tsx.backup +0 -771
  274. package/src/providers/__tests__/EventProvider.test.tsx.backup +0 -824
  275. package/src/providers/__tests__/OrganisationProvider.test.tsx.backup +0 -820
  276. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup +0 -911
  277. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup2 +0 -166
  278. package/src/rbac/cli/__tests__/policy-manager.test.ts +0 -339
  279. package/src/rbac/cli/policy-manager.ts +0 -443
  280. package/dist/{DataTable-INW5YIFV.js.map → DataTable-HWZQGASI.js.map} +0 -0
  281. package/dist/{UnifiedAuthProvider-6SYT5WFN.js.map → UnifiedAuthProvider-3NKDOSOK.js.map} +0 -0
  282. package/dist/{validation-PM_iOaTI.d.ts → validation-D8VcbTzC.d.ts} +2 -2
  283. /package/src/utils/{appNameResolver.test.ts.backup → appNameResolver.test 2.ts} +0 -0
@@ -1,911 +0,0 @@
1
- /**
2
- * @file UnifiedAuthProvider Tests
3
- * @description Comprehensive tests for UnifiedAuthProvider component
4
- * @package @jmruthers/pace-core
5
- * @module Providers/__tests__
6
- * @since 0.1.0
7
- *
8
- * Comprehensive test suite for UnifiedAuthProvider component covering all critical functionality.
9
- * Follows testing guidelines with proper structure, naming, and best practices.
10
- */
11
-
12
- import React from 'react';
13
- import { render, screen, waitFor, act } from '@testing-library/react';
14
- import userEvent from '@testing-library/user-event';
15
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
16
- import { BrowserRouter } from 'react-router-dom';
17
- import { UnifiedAuthProvider, useUnifiedAuth } from '../UnifiedAuthProvider';
18
- import { createMockSupabaseClient, testDataGenerators } from '../../__tests__/helpers/test-utils';
19
-
20
- // Mock the debug logger
21
- vi.mock('../../utils/debugLogger', () => ({
22
- DebugLogger: {
23
- log: vi.fn(),
24
- },
25
- }));
26
-
27
- // Mock the AuthProvider
28
- const mockAuthState = {
29
- user: null as any,
30
- session: null as any,
31
- isAuthenticated: false,
32
- authLoading: true,
33
- authError: null as any,
34
- signIn: vi.fn(),
35
- signOut: vi.fn(),
36
- signUp: vi.fn(),
37
- resetPassword: vi.fn(),
38
- updatePassword: vi.fn(),
39
- refreshSession: vi.fn(),
40
- supabase: null as any,
41
- };
42
-
43
- vi.mock('../AuthProvider', () => ({
44
- AuthProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="auth-provider">{children}</div>,
45
- useAuth: () => mockAuthState,
46
- }));
47
-
48
- // Mock the RBAC provider
49
- const mockRBACState = {
50
- rbacLoading: false,
51
- rbacError: null,
52
- hasPermission: vi.fn(() => true),
53
- hasRole: vi.fn(() => true),
54
- hasAccessLevel: vi.fn(() => 'ADMIN'),
55
- validatePermission: vi.fn(() => Promise.resolve(true)),
56
- canAccess: vi.fn(() => true),
57
- permissions: [],
58
- roles: [],
59
- accessLevel: 'ADMIN',
60
- userEventAccess: null,
61
- setSelectedEventId: vi.fn(),
62
- };
63
-
64
- vi.mock('../../rbac/providers/RBACProvider', () => ({
65
- RBACProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="rbac-provider">{children}</div>,
66
- useRBAC: () => mockRBACState,
67
- }));
68
-
69
- // Mock the inactivity provider
70
- const mockInactivityState = {
71
- showInactivityWarning: false,
72
- inactivityTimeRemaining: 0,
73
- isIdle: false,
74
- timeRemaining: 0,
75
- showWarning: false,
76
- isTracking: false,
77
- resetActivity: vi.fn(),
78
- startTracking: vi.fn(),
79
- stopTracking: vi.fn(),
80
- handleIdleLogout: vi.fn(),
81
- handleStaySignedIn: vi.fn(),
82
- handleSignOutNow: vi.fn(),
83
- };
84
-
85
- vi.mock('../InactivityProvider', () => ({
86
- InactivityProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="inactivity-provider">{children}</div>,
87
- useInactivity: () => mockInactivityState,
88
- }));
89
-
90
- // Mock react-router-dom
91
- vi.mock('react-router-dom', () => ({
92
- BrowserRouter: ({ children }: { children: React.ReactNode }) => <div data-testid="browser-router">{children}</div>,
93
- useNavigate: () => vi.fn(),
94
- }));
95
-
96
- // Test component that uses the unified auth context
97
- const TestComponent = () => {
98
- const auth = useUnifiedAuth();
99
-
100
- return (
101
- <div data-testid="test-component">
102
- <div data-testid="user">{auth.user?.email || 'No user'}</div>
103
- <div data-testid="isAuthenticated">{auth.isAuthenticated ? 'true' : 'false'}</div>
104
- <div data-testid="isLoading">{auth.isLoading ? 'true' : 'false'}</div>
105
- <div data-testid="hasErrors">{auth.hasErrors ? 'true' : 'false'}</div>
106
- <div data-testid="appName">{auth.appName}</div>
107
- <div data-testid="hasPermission">{auth.hasPermission('test:permission') ? 'true' : 'false'}</div>
108
- <div data-testid="hasRole">{auth.hasRole('admin') ? 'true' : 'false'}</div>
109
- <div data-testid="accessLevel">{auth.accessLevel}</div>
110
- <div data-testid="showInactivityWarning">{auth.showInactivityWarning ? 'true' : 'false'}</div>
111
- <div data-testid="isIdle">{auth.isIdle ? 'true' : 'false'}</div>
112
- <button onClick={() => auth.signIn('test@example.com', 'password')}>
113
- Sign In
114
- </button>
115
- <button onClick={() => auth.signOut()}>
116
- Sign Out
117
- </button>
118
- <button onClick={() => auth.signUp('test@example.com', 'password')}>
119
- Sign Up
120
- </button>
121
- <button onClick={() => auth.resetPassword('test@example.com')}>
122
- Reset Password
123
- </button>
124
- <button onClick={() => auth.updatePassword('newpassword')}>
125
- Update Password
126
- </button>
127
- <button onClick={() => auth.refreshSession()}>
128
- Refresh Session
129
- </button>
130
- <button onClick={() => auth.setSelectedEventId('event-123')}>
131
- Set Event
132
- </button>
133
- <button onClick={() => auth.resetActivity()}>
134
- Reset Activity
135
- </button>
136
- </div>
137
- );
138
- };
139
-
140
- // Wrapper component
141
- const TestWrapper = ({
142
- children,
143
- supabaseClient,
144
- appName = 'test-app',
145
- enableRBAC = false,
146
- persistState = true,
147
- enablePersistence = true,
148
- requireOrganisationContext = true,
149
- idleTimeoutMs = 30000,
150
- warnBeforeMs = 60000,
151
- onIdleLogout = vi.fn(),
152
- renderInactivityWarning,
153
- dangerouslyDisableInactivity = false,
154
- ...props
155
- }: {
156
- children: React.ReactNode;
157
- supabaseClient?: any;
158
- appName?: string;
159
- enableRBAC?: boolean;
160
- persistState?: boolean;
161
- enablePersistence?: boolean;
162
- requireOrganisationContext?: boolean;
163
- idleTimeoutMs?: number;
164
- warnBeforeMs?: number;
165
- onIdleLogout?: (reason: 'inactivity') => void;
166
- renderInactivityWarning?: (args: any) => React.ReactNode;
167
- dangerouslyDisableInactivity?: boolean;
168
- [key: string]: any;
169
- }) => (
170
- <BrowserRouter>
171
- <UnifiedAuthProvider
172
- supabaseClient={supabaseClient}
173
- appName={appName}
174
- enableRBAC={enableRBAC}
175
- persistState={persistState}
176
- enablePersistence={enablePersistence}
177
- requireOrganisationContext={requireOrganisationContext}
178
- idleTimeoutMs={idleTimeoutMs}
179
- warnBeforeMs={warnBeforeMs}
180
- onIdleLogout={onIdleLogout}
181
- renderInactivityWarning={renderInactivityWarning}
182
- dangerouslyDisableInactivity={dangerouslyDisableInactivity}
183
- {...props}
184
- >
185
- {children}
186
- </UnifiedAuthProvider>
187
- </BrowserRouter>
188
- );
189
-
190
- describe('[component] UnifiedAuthProvider', () => {
191
- let mockSupabaseClient: any;
192
-
193
- beforeEach(() => {
194
- vi.clearAllMocks();
195
-
196
- // Create mock Supabase client
197
- mockSupabaseClient = createMockSupabaseClient();
198
-
199
- // Reset mock states
200
- mockAuthState.user = null;
201
- mockAuthState.session = null;
202
- mockAuthState.isAuthenticated = false;
203
- mockAuthState.authLoading = true;
204
- mockAuthState.authError = null;
205
- mockAuthState.supabase = mockSupabaseClient;
206
-
207
- // Mock auth state
208
- mockSupabaseClient.auth.getUser = vi.fn().mockResolvedValue({
209
- data: { user: testDataGenerators.user({ id: 'user-1' }) },
210
- error: null
211
- });
212
- mockSupabaseClient.auth.getSession = vi.fn().mockResolvedValue({
213
- data: { session: testDataGenerators.session() },
214
- error: null
215
- });
216
- mockSupabaseClient.auth.onAuthStateChange = vi.fn(() => ({
217
- data: { subscription: { unsubscribe: vi.fn() } }
218
- }));
219
- });
220
-
221
- afterEach(() => {
222
- vi.restoreAllMocks();
223
- });
224
-
225
- describe('Rendering', () => {
226
- it('renders children without crashing', () => {
227
- render(
228
- <TestWrapper supabaseClient={mockSupabaseClient}>
229
- <div>Test content</div>
230
- </TestWrapper>
231
- );
232
-
233
- expect(screen.getByText('Test content')).toBeInTheDocument();
234
- });
235
-
236
- it('renders without supabase client', () => {
237
- render(
238
- <TestWrapper>
239
- <div>Test content</div>
240
- </TestWrapper>
241
- );
242
-
243
- expect(screen.getByText('Test content')).toBeInTheDocument();
244
- });
245
-
246
- it('renders with custom app name', () => {
247
- render(
248
- <TestWrapper supabaseClient={mockSupabaseClient} appName="custom-app">
249
- <TestComponent />
250
- </TestWrapper>
251
- );
252
-
253
- expect(screen.getByTestId('appName')).toHaveTextContent('Custom App');
254
- });
255
-
256
- it('renders with all provider components', () => {
257
- render(
258
- <TestWrapper supabaseClient={mockSupabaseClient}>
259
- <div>Test content</div>
260
- </TestWrapper>
261
- );
262
-
263
- // Should have all providers in the hierarchy
264
- expect(screen.getByTestId('auth-provider')).toBeInTheDocument();
265
- expect(screen.getByTestId('rbac-provider')).toBeInTheDocument();
266
- expect(screen.getByTestId('inactivity-provider')).toBeInTheDocument();
267
- });
268
- });
269
-
270
- describe('Context Composition', () => {
271
- it('provides combined auth context', () => {
272
- render(
273
- <TestWrapper supabaseClient={mockSupabaseClient}>
274
- <TestComponent />
275
- </TestWrapper>
276
- );
277
-
278
- expect(screen.getByTestId('user')).toBeInTheDocument();
279
- expect(screen.getByTestId('isAuthenticated')).toBeInTheDocument();
280
- expect(screen.getByTestId('isLoading')).toBeInTheDocument();
281
- expect(screen.getByTestId('hasErrors')).toBeInTheDocument();
282
- expect(screen.getByTestId('appName')).toBeInTheDocument();
283
- });
284
-
285
- it('includes RBAC provider when enabled', () => {
286
- render(
287
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
288
- <TestComponent />
289
- </TestWrapper>
290
- );
291
-
292
- expect(screen.getByTestId('rbac-provider')).toBeInTheDocument();
293
- });
294
-
295
- it('includes inactivity provider', () => {
296
- render(
297
- <TestWrapper supabaseClient={mockSupabaseClient}>
298
- <TestComponent />
299
- </TestWrapper>
300
- );
301
-
302
- expect(screen.getByTestId('inactivity-provider')).toBeInTheDocument();
303
- });
304
- });
305
-
306
- describe('Authentication Integration', () => {
307
- it('handles successful authentication', async () => {
308
- const mockUser = testDataGenerators.user({ email: 'test@example.com' });
309
- const mockSession = testDataGenerators.session({ user: mockUser });
310
-
311
- // Update mock state
312
- mockAuthState.user = mockUser;
313
- mockAuthState.session = mockSession;
314
- mockAuthState.isAuthenticated = true;
315
- mockAuthState.authLoading = false;
316
- mockAuthState.authError = null;
317
-
318
- render(
319
- <TestWrapper supabaseClient={mockSupabaseClient}>
320
- <TestComponent />
321
- </TestWrapper>
322
- );
323
-
324
- await waitFor(() => {
325
- expect(screen.getByTestId('user')).toHaveTextContent('test@example.com');
326
- expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('true');
327
- expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
328
- expect(screen.getByTestId('hasErrors')).toHaveTextContent('false');
329
- });
330
- });
331
-
332
- it('handles authentication errors', async () => {
333
- const mockError = new Error('Auth error');
334
-
335
- // Update mock state
336
- mockAuthState.user = null;
337
- mockAuthState.session = null;
338
- mockAuthState.isAuthenticated = false;
339
- mockAuthState.authLoading = false;
340
- mockAuthState.authError = mockError;
341
-
342
- render(
343
- <TestWrapper supabaseClient={mockSupabaseClient}>
344
- <TestComponent />
345
- </TestWrapper>
346
- );
347
-
348
- await waitFor(() => {
349
- expect(screen.getByTestId('user')).toHaveTextContent('No user');
350
- expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
351
- expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
352
- expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
353
- });
354
- });
355
-
356
- it('handles loading states', async () => {
357
- // Update mock state
358
- mockAuthState.user = null;
359
- mockAuthState.session = null;
360
- mockAuthState.isAuthenticated = false;
361
- mockAuthState.authLoading = true;
362
- mockAuthState.authError = null;
363
-
364
- render(
365
- <TestWrapper supabaseClient={mockSupabaseClient}>
366
- <TestComponent />
367
- </TestWrapper>
368
- );
369
-
370
- expect(screen.getByTestId('isLoading')).toHaveTextContent('true');
371
- });
372
- });
373
-
374
- describe('RBAC Integration', () => {
375
- it('provides RBAC context when enabled', () => {
376
- render(
377
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
378
- <TestComponent />
379
- </TestWrapper>
380
- );
381
-
382
- expect(screen.getByTestId('hasPermission')).toHaveTextContent('true');
383
- expect(screen.getByTestId('hasRole')).toHaveTextContent('true');
384
- expect(screen.getByTestId('accessLevel')).toHaveTextContent('ADMIN');
385
- });
386
-
387
- it('does not provide RBAC context when disabled', () => {
388
- render(
389
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={false}>
390
- <TestComponent />
391
- </TestWrapper>
392
- );
393
-
394
- // RBAC should still be available but may return different values
395
- expect(screen.getByTestId('hasPermission')).toBeInTheDocument();
396
- expect(screen.getByTestId('hasRole')).toBeInTheDocument();
397
- expect(screen.getByTestId('accessLevel')).toBeInTheDocument();
398
- });
399
-
400
- it('handles RBAC errors', async () => {
401
- // Update RBAC mock state
402
- mockRBACState.rbacError = new Error('RBAC error');
403
- mockRBACState.rbacLoading = false;
404
-
405
- render(
406
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
407
- <TestComponent />
408
- </TestWrapper>
409
- );
410
-
411
- await waitFor(() => {
412
- expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
413
- });
414
- });
415
- });
416
-
417
- describe('Inactivity Integration', () => {
418
- it('provides inactivity context', () => {
419
- render(
420
- <TestWrapper supabaseClient={mockSupabaseClient}>
421
- <TestComponent />
422
- </TestWrapper>
423
- );
424
-
425
- expect(screen.getByTestId('showInactivityWarning')).toHaveTextContent('false');
426
- expect(screen.getByTestId('isIdle')).toHaveTextContent('false');
427
- });
428
-
429
- it('handles inactivity warning state', async () => {
430
- // Update inactivity mock state
431
- mockInactivityState.showInactivityWarning = true;
432
- mockInactivityState.inactivityTimeRemaining = 30000;
433
-
434
- render(
435
- <TestWrapper supabaseClient={mockSupabaseClient}>
436
- <TestComponent />
437
- </TestWrapper>
438
- );
439
-
440
- await waitFor(() => {
441
- expect(screen.getByTestId('showInactivityWarning')).toHaveTextContent('true');
442
- });
443
- });
444
-
445
- it('handles idle state', async () => {
446
- // Update inactivity mock state
447
- mockInactivityState.isIdle = true;
448
-
449
- render(
450
- <TestWrapper supabaseClient={mockSupabaseClient}>
451
- <TestComponent />
452
- </TestWrapper>
453
- );
454
-
455
- await waitFor(() => {
456
- expect(screen.getByTestId('isIdle')).toHaveTextContent('true');
457
- });
458
- });
459
- });
460
-
461
- describe('Loading States', () => {
462
- it('combines loading states from all providers', async () => {
463
- // Update mock states
464
- mockAuthState.authLoading = true;
465
- mockRBACState.rbacLoading = false;
466
-
467
- render(
468
- <TestWrapper supabaseClient={mockSupabaseClient}>
469
- <TestComponent />
470
- </TestWrapper>
471
- );
472
-
473
- expect(screen.getByTestId('isLoading')).toHaveTextContent('true');
474
- });
475
-
476
- it('handles mixed loading states', async () => {
477
- // Update mock states
478
- mockAuthState.authLoading = false;
479
- mockRBACState.rbacLoading = true;
480
-
481
- render(
482
- <TestWrapper supabaseClient={mockSupabaseClient}>
483
- <TestComponent />
484
- </TestWrapper>
485
- );
486
-
487
- expect(screen.getByTestId('isLoading')).toHaveTextContent('true');
488
- });
489
-
490
- it('shows no loading when all providers are ready', async () => {
491
- // Update mock states
492
- mockAuthState.authLoading = false;
493
- mockRBACState.rbacLoading = false;
494
-
495
- render(
496
- <TestWrapper supabaseClient={mockSupabaseClient}>
497
- <TestComponent />
498
- </TestWrapper>
499
- );
500
-
501
- expect(screen.getByTestId('isLoading')).toHaveTextContent('false');
502
- });
503
- });
504
-
505
- describe('Error States', () => {
506
- it('combines error states from all providers', async () => {
507
- // Update mock states
508
- mockAuthState.authError = new Error('Auth error');
509
- mockRBACState.rbacError = null;
510
-
511
- render(
512
- <TestWrapper supabaseClient={mockSupabaseClient}>
513
- <TestComponent />
514
- </TestWrapper>
515
- );
516
-
517
- await waitFor(() => {
518
- expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
519
- });
520
- });
521
-
522
- it('handles no errors state', async () => {
523
- // Update mock states
524
- mockAuthState.authError = null;
525
- mockRBACState.rbacError = null;
526
-
527
- render(
528
- <TestWrapper supabaseClient={mockSupabaseClient}>
529
- <TestComponent />
530
- </TestWrapper>
531
- );
532
-
533
- await waitFor(() => {
534
- expect(screen.getByTestId('hasErrors')).toHaveTextContent('false');
535
- });
536
- });
537
-
538
- it('handles multiple errors', async () => {
539
- // Update mock states
540
- mockAuthState.authError = new Error('Auth error');
541
- mockRBACState.rbacError = new Error('RBAC error');
542
-
543
- render(
544
- <TestWrapper supabaseClient={mockSupabaseClient}>
545
- <TestComponent />
546
- </TestWrapper>
547
- );
548
-
549
- await waitFor(() => {
550
- expect(screen.getByTestId('hasErrors')).toHaveTextContent('true');
551
- });
552
- });
553
- });
554
-
555
- describe('Authentication Methods', () => {
556
- it('handles sign in', async () => {
557
- render(
558
- <TestWrapper supabaseClient={mockSupabaseClient}>
559
- <TestComponent />
560
- </TestWrapper>
561
- );
562
-
563
- const signInButton = screen.getByText('Sign In');
564
- await userEvent.click(signInButton);
565
-
566
- expect(mockAuthState.signIn).toHaveBeenCalledWith('test@example.com', 'password');
567
- });
568
-
569
- it('handles sign out', async () => {
570
- render(
571
- <TestWrapper supabaseClient={mockSupabaseClient}>
572
- <TestComponent />
573
- </TestWrapper>
574
- );
575
-
576
- const signOutButton = screen.getByText('Sign Out');
577
- await userEvent.click(signOutButton);
578
-
579
- expect(mockAuthState.signOut).toHaveBeenCalled();
580
- });
581
-
582
- it('handles sign up', async () => {
583
- render(
584
- <TestWrapper supabaseClient={mockSupabaseClient}>
585
- <TestComponent />
586
- </TestWrapper>
587
- );
588
-
589
- const signUpButton = screen.getByText('Sign Up');
590
- await userEvent.click(signUpButton);
591
-
592
- expect(mockAuthState.signUp).toHaveBeenCalledWith('test@example.com', 'password');
593
- });
594
-
595
- it('handles password reset', async () => {
596
- render(
597
- <TestWrapper supabaseClient={mockSupabaseClient}>
598
- <TestComponent />
599
- </TestWrapper>
600
- );
601
-
602
- const resetButton = screen.getByText('Reset Password');
603
- await userEvent.click(resetButton);
604
-
605
- expect(mockAuthState.resetPassword).toHaveBeenCalledWith('test@example.com');
606
- });
607
-
608
- it('handles password update', async () => {
609
- render(
610
- <TestWrapper supabaseClient={mockSupabaseClient}>
611
- <TestComponent />
612
- </TestWrapper>
613
- );
614
-
615
- const updateButton = screen.getByText('Update Password');
616
- await userEvent.click(updateButton);
617
-
618
- expect(mockAuthState.updatePassword).toHaveBeenCalledWith('newpassword');
619
- });
620
-
621
- it('handles session refresh', async () => {
622
- render(
623
- <TestWrapper supabaseClient={mockSupabaseClient}>
624
- <TestComponent />
625
- </TestWrapper>
626
- );
627
-
628
- const refreshButton = screen.getByText('Refresh Session');
629
- await userEvent.click(refreshButton);
630
-
631
- expect(mockAuthState.refreshSession).toHaveBeenCalled();
632
- });
633
- });
634
-
635
- describe('RBAC Methods', () => {
636
- it('handles permission checking', () => {
637
- render(
638
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
639
- <TestComponent />
640
- </TestWrapper>
641
- );
642
-
643
- expect(screen.getByTestId('hasPermission')).toHaveTextContent('true');
644
- expect(mockRBACState.hasPermission).toHaveBeenCalledWith('test:permission');
645
- });
646
-
647
- it('handles role checking', () => {
648
- render(
649
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
650
- <TestComponent />
651
- </TestWrapper>
652
- );
653
-
654
- expect(screen.getByTestId('hasRole')).toHaveTextContent('true');
655
- expect(mockRBACState.hasRole).toHaveBeenCalledWith('admin');
656
- });
657
-
658
- it('handles event ID setting', async () => {
659
- render(
660
- <TestWrapper supabaseClient={mockSupabaseClient} enableRBAC={true}>
661
- <TestComponent />
662
- </TestWrapper>
663
- );
664
-
665
- const setEventButton = screen.getByText('Set Event');
666
- await userEvent.click(setEventButton);
667
-
668
- expect(mockRBACState.setSelectedEventId).toHaveBeenCalledWith('event-123');
669
- });
670
- });
671
-
672
- describe('Inactivity Methods', () => {
673
- it('handles activity reset', async () => {
674
- render(
675
- <TestWrapper supabaseClient={mockSupabaseClient}>
676
- <TestComponent />
677
- </TestWrapper>
678
- );
679
-
680
- const resetButton = screen.getByText('Reset Activity');
681
- await userEvent.click(resetButton);
682
-
683
- expect(mockInactivityState.resetActivity).toHaveBeenCalled();
684
- });
685
- });
686
-
687
- describe('Configuration Options', () => {
688
- it('handles custom configuration', () => {
689
- render(
690
- <TestWrapper
691
- supabaseClient={mockSupabaseClient}
692
- persistState={false}
693
- enablePersistence={false}
694
- requireOrganisationContext={false}
695
- enableRBAC={true}
696
- idleTimeoutMs={120000}
697
- warnBeforeMs={60000}
698
- >
699
- <div>Test content</div>
700
- </TestWrapper>
701
- );
702
-
703
- expect(screen.getByText('Test content')).toBeInTheDocument();
704
- });
705
-
706
- it('uses default configuration', () => {
707
- render(
708
- <TestWrapper supabaseClient={mockSupabaseClient}>
709
- <div>Test content</div>
710
- </TestWrapper>
711
- );
712
-
713
- expect(screen.getByText('Test content')).toBeInTheDocument();
714
- });
715
-
716
- it('handles inactivity configuration', () => {
717
- const customOnIdleLogout = vi.fn();
718
- const customRenderWarning = vi.fn(() => <div>Custom Warning</div>);
719
-
720
- render(
721
- <TestWrapper
722
- supabaseClient={mockSupabaseClient}
723
- idleTimeoutMs={60000}
724
- warnBeforeMs={30000}
725
- onIdleLogout={customOnIdleLogout}
726
- renderInactivityWarning={customRenderWarning}
727
- >
728
- <div>Test content</div>
729
- </TestWrapper>
730
- );
731
-
732
- expect(screen.getByText('Test content')).toBeInTheDocument();
733
- });
734
- });
735
-
736
- describe('useUnifiedAuth Hook', () => {
737
- it('throws error when used outside provider', () => {
738
- // Suppress console.error for this test
739
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
740
-
741
- expect(() => {
742
- render(<TestComponent />);
743
- }).toThrow('useUnifiedAuth must be used within a UnifiedAuthProvider');
744
-
745
- consoleSpy.mockRestore();
746
- });
747
-
748
- it('provides all required context values', () => {
749
- const TestContextComponent = () => {
750
- const auth = useUnifiedAuth();
751
-
752
- return (
753
- <div>
754
- <div data-testid="hasUser">{auth.user !== undefined ? 'true' : 'false'}</div>
755
- <div data-testid="hasSession">{auth.session !== undefined ? 'true' : 'false'}</div>
756
- <div data-testid="hasSupabase">{auth.supabase !== undefined ? 'true' : 'false'}</div>
757
- <div data-testid="hasAppName">{typeof auth.appName === 'string' ? 'true' : 'false'}</div>
758
- <div data-testid="hasIsLoading">{typeof auth.isLoading === 'boolean' ? 'true' : 'false'}</div>
759
- <div data-testid="hasHasErrors">{typeof auth.hasErrors === 'boolean' ? 'true' : 'false'}</div>
760
- <div data-testid="hasSignIn">{typeof auth.signIn === 'function' ? 'true' : 'false'}</div>
761
- <div data-testid="hasSignOut">{typeof auth.signOut === 'function' ? 'true' : 'false'}</div>
762
- <div data-testid="hasHasPermission">{typeof auth.hasPermission === 'function' ? 'true' : 'false'}</div>
763
- <div data-testid="hasHasRole">{typeof auth.hasRole === 'function' ? 'true' : 'false'}</div>
764
- <div data-testid="hasHasAccessLevel">{typeof auth.hasAccessLevel === 'function' ? 'true' : 'false'}</div>
765
- <div data-testid="hasValidatePermission">{typeof auth.validatePermission === 'function' ? 'true' : 'false'}</div>
766
- <div data-testid="hasCanAccess">{typeof auth.canAccess === 'function' ? 'true' : 'false'}</div>
767
- <div data-testid="hasSetSelectedEventId">{typeof auth.setSelectedEventId === 'function' ? 'true' : 'false'}</div>
768
- <div data-testid="hasResetActivity">{typeof auth.resetActivity === 'function' ? 'true' : 'false'}</div>
769
- <div data-testid="hasStartTracking">{typeof auth.startTracking === 'function' ? 'true' : 'false'}</div>
770
- <div data-testid="hasStopTracking">{typeof auth.stopTracking === 'function' ? 'true' : 'false'}</div>
771
- </div>
772
- );
773
- };
774
-
775
- render(
776
- <TestWrapper supabaseClient={mockSupabaseClient}>
777
- <TestContextComponent />
778
- </TestWrapper>
779
- );
780
-
781
- expect(screen.getByTestId('hasUser')).toHaveTextContent('true');
782
- expect(screen.getByTestId('hasSession')).toHaveTextContent('true');
783
- expect(screen.getByTestId('hasSupabase')).toHaveTextContent('true');
784
- expect(screen.getByTestId('hasAppName')).toHaveTextContent('true');
785
- expect(screen.getByTestId('hasIsLoading')).toHaveTextContent('true');
786
- expect(screen.getByTestId('hasHasErrors')).toHaveTextContent('true');
787
- expect(screen.getByTestId('hasSignIn')).toHaveTextContent('true');
788
- expect(screen.getByTestId('hasSignOut')).toHaveTextContent('true');
789
- expect(screen.getByTestId('hasHasPermission')).toHaveTextContent('true');
790
- expect(screen.getByTestId('hasHasRole')).toHaveTextContent('true');
791
- expect(screen.getByTestId('hasHasAccessLevel')).toHaveTextContent('true');
792
- expect(screen.getByTestId('hasValidatePermission')).toHaveTextContent('true');
793
- expect(screen.getByTestId('hasCanAccess')).toHaveTextContent('true');
794
- expect(screen.getByTestId('hasSetSelectedEventId')).toHaveTextContent('true');
795
- expect(screen.getByTestId('hasResetActivity')).toHaveTextContent('true');
796
- expect(screen.getByTestId('hasStartTracking')).toHaveTextContent('true');
797
- expect(screen.getByTestId('hasStopTracking')).toHaveTextContent('true');
798
- });
799
- });
800
-
801
- describe('Provider Composition', () => {
802
- it('maintains proper provider hierarchy', () => {
803
- render(
804
- <TestWrapper supabaseClient={mockSupabaseClient}>
805
- <div>Test content</div>
806
- </TestWrapper>
807
- );
808
-
809
- // Should have all providers in the hierarchy
810
- expect(screen.getByText('Test content')).toBeInTheDocument();
811
- });
812
-
813
- it('passes props correctly to child providers', () => {
814
- const TestPropsComponent = () => {
815
- const auth = useUnifiedAuth();
816
- return (
817
- <div>
818
- <div data-testid="appName">{auth.appName}</div>
819
- </div>
820
- );
821
- };
822
-
823
- render(
824
- <TestWrapper supabaseClient={mockSupabaseClient} appName="custom-app">
825
- <TestPropsComponent />
826
- </TestWrapper>
827
- );
828
-
829
- expect(screen.getByTestId('appName')).toHaveTextContent('Custom App');
830
- });
831
- });
832
-
833
- describe('Context Value Stability', () => {
834
- it('maintains stable context value references', () => {
835
- const TestStabilityComponent = () => {
836
- const auth = useUnifiedAuth();
837
- const [renderCount, setRenderCount] = React.useState(0);
838
-
839
- React.useEffect(() => {
840
- // Limit render count to prevent infinite loops
841
- if (renderCount < 5) {
842
- setRenderCount(prev => prev + 1);
843
- }
844
- });
845
-
846
- return (
847
- <div>
848
- <div data-testid="renderCount">{renderCount}</div>
849
- <div data-testid="hasStableSignIn">{typeof auth.signIn === 'function' ? 'true' : 'false'}</div>
850
- <div data-testid="hasStableHasPermission">{typeof auth.hasPermission === 'function' ? 'true' : 'false'}</div>
851
- </div>
852
- );
853
- };
854
-
855
- render(
856
- <TestWrapper supabaseClient={mockSupabaseClient}>
857
- <TestStabilityComponent />
858
- </TestWrapper>
859
- );
860
-
861
- // Should render multiple times but maintain stable function references
862
- expect(screen.getByTestId('hasStableSignIn')).toHaveTextContent('true');
863
- expect(screen.getByTestId('hasStableHasPermission')).toHaveTextContent('true');
864
- }, 5000);
865
- });
866
-
867
- describe('Cleanup', () => {
868
- it('handles component unmount gracefully', () => {
869
- const { unmount } = render(
870
- <TestWrapper supabaseClient={mockSupabaseClient}>
871
- <div>Test content</div>
872
- </TestWrapper>
873
- );
874
-
875
- // Should not throw errors on unmount
876
- expect(() => unmount()).not.toThrow();
877
- });
878
- });
879
-
880
- describe('Edge Cases', () => {
881
- it('handles missing required props', () => {
882
- expect(() => {
883
- render(
884
- <TestWrapper>
885
- <div>Test content</div>
886
- </TestWrapper>
887
- );
888
- }).not.toThrow();
889
- });
890
-
891
- it('handles null supabase client', () => {
892
- expect(() => {
893
- render(
894
- <TestWrapper supabaseClient={null}>
895
- <div>Test content</div>
896
- </TestWrapper>
897
- );
898
- }).not.toThrow();
899
- });
900
-
901
- it('handles undefined app name', () => {
902
- expect(() => {
903
- render(
904
- <TestWrapper supabaseClient={mockSupabaseClient} appName={undefined as any}>
905
- <div>Test content</div>
906
- </TestWrapper>
907
- );
908
- }).not.toThrow();
909
- });
910
- });
911
- });