@jmruthers/pace-core 0.6.8 → 0.6.10

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 (669) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/audit-tool/audits/02-project-structure.cjs +97 -32
  3. package/audit-tool/audits/03-architecture.cjs +145 -19
  4. package/audit-tool/audits/04-code-quality.cjs +86 -1
  5. package/audit-tool/audits/06-security-rbac.cjs +109 -11
  6. package/cursor-rules/02-project-structure.mdc +2 -26
  7. package/cursor-rules/05-styling.mdc +84 -6
  8. package/cursor-rules/06-security-rbac.mdc +124 -1
  9. package/dist/{DataTable-6RMSCQJ6.js → DataTable-SAXFG4XI.js} +11 -13
  10. package/dist/{AuthService-DmfO5rGS.d.ts → InactivityServiceProvider-DHryoh6K.d.ts} +24 -249
  11. package/dist/UnifiedAuthProvider-BBD2PS3Q.js +7 -0
  12. package/dist/{UnifiedAuthProvider-CKvHP1MK.d.ts → UnifiedAuthProvider-CiBAl9-s.d.ts} +34 -22
  13. package/dist/{api-7P7DI652.js → api-F47QJ7FX.js} +3 -3
  14. package/dist/assets/app-icons/admin_favicon.svg +462 -0
  15. package/dist/assets/app-icons/base_favicon.svg +85 -0
  16. package/dist/assets/app-icons/cake_favicon.svg +68 -0
  17. package/dist/assets/app-icons/core_favicon.svg +256 -0
  18. package/dist/assets/app-icons/gear_favicon.svg +91 -0
  19. package/dist/assets/app-icons/medi_favicon.svg +92 -0
  20. package/dist/assets/app-icons/mint_favicon.svg +83 -0
  21. package/dist/assets/app-icons/pace_favicon.svg +49 -0
  22. package/dist/assets/app-icons/pump_favicon.svg +68 -0
  23. package/dist/assets/app-icons/seed_favicon.svg +91 -0
  24. package/dist/assets/app-icons/team_favicon.svg +67 -0
  25. package/dist/assets/app-icons/trac_favicon.svg +112 -0
  26. package/dist/assets/app-icons/trip_favicon.svg +102 -0
  27. package/dist/audit-Z6ZZBWLU.js +3 -0
  28. package/dist/chunk-3GWSPISD.js +61 -0
  29. package/dist/{chunk-4DDCYDQ3.js → chunk-66R6RLUZ.js} +12 -27
  30. package/dist/{chunk-FYHN4DD5.js → chunk-7YDC7LMU.js} +80 -8
  31. package/dist/{chunk-S7DKJPLT.js → chunk-BCTXBU6U.js} +22 -17
  32. package/dist/{chunk-TTRFSOKR.js → chunk-BTHN5MKC.js} +4 -4
  33. package/dist/{chunk-A3W6LW53.js → chunk-DDMPHZ3D.js} +6 -18
  34. package/dist/{chunk-MPBLMWVR.js → chunk-FBZ7U3ID.js} +140 -92
  35. package/dist/chunk-FN52B75D.js +246 -0
  36. package/dist/{chunk-5W2A3DRC.js → chunk-JJEYZ3DX.js} +5 -4
  37. package/dist/chunk-KPYQWGFQ.js +183 -0
  38. package/dist/{chunk-IUBRCBSY.js → chunk-KSNLMI7N.js} +14 -8
  39. package/dist/chunk-KYURMOQM.js +977 -0
  40. package/dist/{chunk-LX6U42O3.js → chunk-LNHFAF4X.js} +160 -58
  41. package/dist/{chunk-NKHKXPI4.js → chunk-MPY44PWB.js} +683 -627
  42. package/dist/{chunk-AHU7G2R5.js → chunk-NIU6DPQV.js} +10 -6
  43. package/dist/{chunk-HF6O3O37.js → chunk-RMLY6KB5.js} +1 -1
  44. package/dist/{chunk-6GLLNA6U.js → chunk-SACF5YSM.js} +1 -1
  45. package/dist/{chunk-EURB7QFZ.js → chunk-TFIPNIPE.js} +867 -534
  46. package/dist/{chunk-OJ4SKRSV.js → chunk-UZNAFKGW.js} +25 -5
  47. package/dist/chunk-W46INAVW.js +1216 -0
  48. package/dist/chunk-X5EAU5G7.js +793 -0
  49. package/dist/{chunk-T5CVK4R3.js → chunk-Y4PF6HIM.js} +110 -64
  50. package/dist/components.d.ts +8 -86
  51. package/dist/components.js +21 -55
  52. package/dist/{database.generated-CcnC_DRc.d.ts → database.generated-DT8JTZiP.d.ts} +12 -12
  53. package/dist/eslint-rules/rules/05-styling.cjs +507 -0
  54. package/dist/eslint-rules/rules/06-security-rbac.cjs +10 -0
  55. package/dist/{event-CW5YB_2p.d.ts → event-WTAQuGcq.d.ts} +1 -1
  56. package/dist/{functions-lBy5L2ry.d.ts → functions-DH45k8ec.d.ts} +1 -1
  57. package/dist/hooks.d.ts +12 -11
  58. package/dist/hooks.js +69 -44
  59. package/dist/index.d.ts +380 -32
  60. package/dist/index.js +46 -32
  61. package/dist/papaparseLoader-WG2UXQ22.js +7 -0
  62. package/dist/providers.d.ts +28 -14
  63. package/dist/providers.js +5 -5
  64. package/dist/rbac/eslint-rules.js +2 -2
  65. package/dist/rbac/index.d.ts +58 -214
  66. package/dist/rbac/index.js +11 -11
  67. package/dist/theming/runtime.d.ts +9 -3
  68. package/dist/theming/runtime.js +2 -2
  69. package/dist/{timezone-BZe_eUxx.d.ts → timezone-K-ptz3HO.d.ts} +22 -23
  70. package/dist/{types-t9H8qKRw.d.ts → types-BE2sEHKd.d.ts} +1 -1
  71. package/dist/{types-BeoeWV5I.d.ts → types-CvOPXWWZ.d.ts} +6 -5
  72. package/dist/{types-DXstZpNI.d.ts → types-D05dCGma.d.ts} +56 -149
  73. package/dist/types-Dr8sNhER.d.ts +50 -0
  74. package/dist/types.d.ts +5 -5
  75. package/dist/{PublicPageProvider-CIGSujI2.d.ts → usePublicPageContext-vxBlEHO9.d.ts} +294 -151
  76. package/dist/{usePublicRouteParams-MamNgwqe.d.ts → usePublicRouteParams-G3Ks53mk.d.ts} +8 -7
  77. package/dist/utils.d.ts +301 -137
  78. package/dist/utils.js +42 -41
  79. package/dist/{validation-643vUDZW.d.ts → validation-g5n0hDkh.d.ts} +2 -2
  80. package/docs/api/modules.md +542 -549
  81. package/docs/api-reference/components.md +5 -5
  82. package/docs/api-reference/rpc-functions.md +3 -3
  83. package/docs/implementation-guides/data-tables.md +256 -8
  84. package/docs/rbac/RBAC_CONTRACT.md +0 -12
  85. package/docs/standards/2-project-structure-standards.md +12 -74
  86. package/docs/standards/6-security-rbac-standards.md +222 -7
  87. package/docs/standards/7-api-tech-stack-standards.md +91 -3
  88. package/docs/testing/README.md +10 -0
  89. package/docs/testing/test-setup-for-consumers.md +914 -0
  90. package/eslint-config-pace-core.cjs +4 -0
  91. package/package.json +1 -1
  92. package/scripts/eslint-audit.cjs +110 -11
  93. package/src/__mocks__/lucide-react.ts +0 -2
  94. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -2
  95. package/src/__tests__/index.test.ts +532 -0
  96. package/src/__tests__/integration/UserProfile.test.tsx +1 -1
  97. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -8
  98. package/src/__tests__/rls-policies.test.ts +3 -2
  99. package/src/assets/app-icons/admin_favicon.svg +462 -0
  100. package/src/assets/app-icons/base_favicon.svg +85 -0
  101. package/src/assets/app-icons/cake_favicon.svg +68 -0
  102. package/src/assets/app-icons/core_favicon.svg +256 -0
  103. package/src/assets/app-icons/gear_favicon.svg +91 -0
  104. package/src/assets/app-icons/index.ts +83 -0
  105. package/src/assets/app-icons/medi_favicon.svg +92 -0
  106. package/src/assets/app-icons/mint_favicon.svg +83 -0
  107. package/src/assets/app-icons/pace_favicon.svg +49 -0
  108. package/src/assets/app-icons/pump_favicon.svg +68 -0
  109. package/src/assets/app-icons/seed_favicon.svg +91 -0
  110. package/src/assets/app-icons/team_favicon.svg +67 -0
  111. package/src/assets/app-icons/trac_favicon.svg +112 -0
  112. package/src/assets/app-icons/trip_favicon.svg +102 -0
  113. package/src/components/AddressField/AddressField.test.tsx +378 -3
  114. package/src/components/AddressField/AddressField.tsx +2 -2
  115. package/src/components/AddressField/types.ts +2 -2
  116. package/src/components/Alert/Alert.test.tsx +35 -25
  117. package/src/components/Alert/Alert.tsx +8 -8
  118. package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
  119. package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
  120. package/src/components/Avatar/Avatar.test.tsx +11 -1
  121. package/src/components/Avatar/Avatar.tsx +3 -2
  122. package/src/components/Badge/Badge.test.tsx +11 -1
  123. package/src/components/Button/Button.test.tsx +13 -3
  124. package/src/components/Calendar/Calendar.test.tsx +523 -131
  125. package/src/components/Calendar/Calendar.tsx +107 -488
  126. package/src/components/Card/Card.test.tsx +220 -249
  127. package/src/components/Checkbox/Checkbox.test.tsx +58 -174
  128. package/src/components/ContextSelector/ContextSelector.tsx +3 -3
  129. package/src/components/ContextSelector/__tests__/ContextSelector.test.tsx +360 -0
  130. package/src/components/DataTable/DataTable.tsx +2 -2
  131. package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +1 -1
  132. package/src/components/DataTable/__tests__/DataTable.export.test.tsx +2 -2
  133. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +1 -1
  134. package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +485 -0
  135. package/src/components/DataTable/__tests__/DataTable.test.tsx +2 -2
  136. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +1 -1
  137. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +76 -580
  138. package/src/components/DataTable/__tests__/README.md +1 -1
  139. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +1 -1
  140. package/src/components/DataTable/__tests__/keyboard.test.tsx +1 -1
  141. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +1 -3
  142. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -6
  143. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +14 -6
  144. package/src/components/DataTable/components/ActionButtons.tsx +9 -4
  145. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +3 -3
  146. package/src/components/DataTable/components/ColumnFilter.tsx +2 -7
  147. package/src/components/DataTable/components/DataTableCore.tsx +44 -52
  148. package/src/components/DataTable/components/DataTableLayout.tsx +37 -26
  149. package/src/components/DataTable/components/DataTableModals.tsx +118 -30
  150. package/src/components/DataTable/components/DataTableToolbar.tsx +2 -2
  151. package/src/components/DataTable/components/EditFields.tsx +6 -47
  152. package/src/components/DataTable/components/EditableRow.tsx +8 -8
  153. package/src/components/DataTable/components/EmptyState.tsx +6 -3
  154. package/src/components/DataTable/components/FilterRow.tsx +18 -11
  155. package/src/components/DataTable/components/GroupingDropdown.tsx +0 -1
  156. package/src/components/DataTable/components/ImportModal.tsx +305 -133
  157. package/src/components/DataTable/components/LoadingState.tsx +2 -2
  158. package/src/components/DataTable/components/PaginationControls.tsx +0 -4
  159. package/src/components/DataTable/components/RowComponent.tsx +42 -22
  160. package/src/components/DataTable/components/UnifiedTableBody.tsx +52 -12
  161. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +51 -463
  162. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +122 -116
  163. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +40 -68
  164. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +9 -137
  165. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +57 -17
  166. package/src/components/DataTable/components/__tests__/DataTableCore.test.tsx +792 -0
  167. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +24 -65
  168. package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +467 -0
  169. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +8 -125
  170. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +528 -56
  171. package/src/components/DataTable/components/__tests__/EditFields.test.tsx +526 -0
  172. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +1 -68
  173. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +8 -25
  174. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +3 -62
  175. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +9 -14
  176. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +50 -186
  177. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +39 -97
  178. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +13 -103
  179. package/src/components/DataTable/components/__tests__/RowComponent.test.tsx +629 -0
  180. package/src/components/DataTable/components/__tests__/SortIndicator.test.tsx +135 -0
  181. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +31 -171
  182. package/src/components/DataTable/components/__tests__/cellValueUtils.test.ts +453 -0
  183. package/src/components/DataTable/components/hooks/useImportModalFocus.test.ts +184 -0
  184. package/src/components/DataTable/components/hooks/usePermissionTracking.test.ts +381 -0
  185. package/src/components/DataTable/context/DataTableContext.tsx +9 -10
  186. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +12 -26
  187. package/src/components/DataTable/core/ColumnFactory.ts +3 -3
  188. package/src/components/DataTable/core/ColumnManager.ts +0 -1
  189. package/src/components/DataTable/core/DataManager.ts +4 -2
  190. package/src/components/DataTable/core/LocalDataAdapter.ts +1 -1
  191. package/src/components/DataTable/core/PluginRegistry.ts +2 -2
  192. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +114 -2
  193. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +103 -5
  194. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +57 -0
  195. package/src/components/DataTable/core/__tests__/DataManager.test.ts +63 -0
  196. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +42 -9
  197. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +29 -7
  198. package/src/components/DataTable/core/__tests__/StateManager.test.ts +58 -4
  199. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +16 -21
  200. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +93 -4
  201. package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +227 -54
  202. package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +215 -62
  203. package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +217 -39
  204. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +101 -6
  205. package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +157 -27
  206. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +80 -0
  207. package/src/components/DataTable/hooks/__tests__/useKeyboardNavigation.test.ts +787 -0
  208. package/src/components/DataTable/hooks/__tests__/useServerSideDataEffect.test.ts +258 -0
  209. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +298 -23
  210. package/src/components/DataTable/hooks/__tests__/useTableHandlers.test.ts +440 -0
  211. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
  212. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
  213. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +1 -1
  214. package/src/components/DataTable/hooks/useDataTablePermissions.ts +11 -22
  215. package/src/components/DataTable/hooks/useDataTableState.ts +20 -24
  216. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +5 -5
  217. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +13 -1
  218. package/src/components/DataTable/hooks/useTableColumns.ts +36 -38
  219. package/src/components/DataTable/hooks/useTableHandlers.ts +8 -20
  220. package/src/components/DataTable/index.ts +24 -2
  221. package/src/components/DataTable/types.ts +6 -3
  222. package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +3 -67
  223. package/src/components/DataTable/utils/__tests__/aggregationUtils.test.ts +288 -0
  224. package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +3 -60
  225. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +1 -1
  226. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +9 -21
  227. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +102 -86
  228. package/src/components/DataTable/utils/__tests__/paginationUtils.test.ts +593 -0
  229. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +33 -49
  230. package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +208 -0
  231. package/src/components/DataTable/utils/a11yUtils.ts +1 -1
  232. package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
  233. package/src/components/DataTable/utils/errorHandling.ts +3 -1
  234. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  235. package/src/components/DataTable/utils/flexibleImport.ts +2 -2
  236. package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
  237. package/src/components/DataTable/utils/paginationUtils.ts +1 -1
  238. package/src/components/DataTable/utils/performanceUtils.ts +1 -1
  239. package/src/components/DataTable/utils/selectFieldUtils.ts +127 -0
  240. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +17 -24
  241. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
  242. package/src/components/DateTimeField/DateTimeField.test.tsx +2 -15
  243. package/src/components/DateTimeField/DateTimeField.tsx +1 -1
  244. package/src/components/Dialog/Dialog.test.tsx +2007 -407
  245. package/src/components/Dialog/Dialog.tsx +97 -192
  246. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
  247. package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
  248. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
  249. package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
  250. package/src/components/ErrorBoundary/index.ts +3 -4
  251. package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
  252. package/src/components/FileDisplay/FileDisplay.test.tsx +454 -222
  253. package/src/components/FileDisplay/FileDisplay.tsx +14 -12
  254. package/src/components/FileDisplay/index.tsx +1 -1
  255. package/src/components/FileUpload/FileUpload.test.tsx +54 -18
  256. package/src/components/FileUpload/FileUpload.tsx +10 -7
  257. package/src/components/FileUpload/index.tsx +1 -1
  258. package/src/components/Footer/Footer.test.tsx +33 -114
  259. package/src/components/Form/Form.test.tsx +388 -68
  260. package/src/components/Form/Form.tsx +57 -42
  261. package/src/components/Header/Header.test.tsx +645 -154
  262. package/src/components/Header/Header.tsx +52 -43
  263. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
  264. package/src/components/Input/Input.test.tsx +34 -120
  265. package/src/components/Label/Label.test.tsx +47 -46
  266. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +9 -12
  267. package/src/components/LoginForm/LoginForm.test.tsx +0 -1
  268. package/src/components/NavigationMenu/NavigationMenu.test.tsx +1399 -82
  269. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  270. package/src/components/NavigationMenu/__tests__/useNavigationFiltering.test.ts +1934 -0
  271. package/src/components/NavigationMenu/useNavigationFiltering.ts +5 -15
  272. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1307 -0
  273. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +47 -46
  274. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
  275. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +87 -66
  276. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +245 -39
  277. package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -20
  278. package/src/components/PaceAppLayout/README.md +0 -9
  279. package/src/components/PaceAppLayout/test-setup.tsx +15 -9
  280. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +759 -3
  281. package/src/components/PaceLoginPage/PaceLoginPage.tsx +2 -3
  282. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
  283. package/src/components/Progress/Progress.test.tsx +127 -1
  284. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
  285. package/src/components/ProtectedRoute/ProtectedRoute.tsx +24 -6
  286. package/src/components/PublicLayout/PublicLayout.test.tsx +1435 -14
  287. package/src/components/PublicLayout/PublicPageContext.ts +28 -0
  288. package/src/components/PublicLayout/PublicPageLayout.tsx +6 -6
  289. package/src/components/PublicLayout/PublicPageProvider.tsx +2 -41
  290. package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
  291. package/src/components/Select/Select.test.tsx +46 -9
  292. package/src/components/Select/Select.tsx +31 -24
  293. package/src/components/Select/__tests__/context.test.tsx +56 -0
  294. package/src/components/Select/hooks/__tests__/useSelectEvents.test.ts +279 -0
  295. package/src/components/Select/hooks/__tests__/useSelectSearch.test.tsx +295 -0
  296. package/src/components/Select/hooks/__tests__/useSelectState.test.ts +254 -0
  297. package/src/components/Select/hooks/useSelectState.ts +16 -16
  298. package/src/components/Select/types.ts +3 -0
  299. package/src/components/Select/utils/__tests__/text.test.tsx +104 -0
  300. package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
  301. package/src/components/Switch/Switch.test.tsx +57 -153
  302. package/src/components/Table/Table.test.tsx +47 -317
  303. package/src/components/Tabs/Tabs.tsx +3 -3
  304. package/src/components/Textarea/Textarea.test.tsx +11 -38
  305. package/src/components/Toast/Toast.test.tsx +78 -569
  306. package/src/components/Tooltip/Tooltip.test.tsx +4 -21
  307. package/src/components/UserMenu/UserMenu.test.tsx +1 -21
  308. package/src/components/UserMenu/UserMenu.tsx +3 -6
  309. package/src/components/__tests__/index.test.ts +346 -0
  310. package/src/components/index.ts +12 -1
  311. package/src/constants/__tests__/performance.test.ts +91 -0
  312. package/src/hooks/__tests__/ServiceHooks.test.tsx +239 -129
  313. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -3
  314. package/src/hooks/__tests__/useApiFetch.unit.test.ts +1 -1
  315. package/src/hooks/__tests__/useAppConfig.unit.test.ts +88 -29
  316. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +282 -98
  317. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +53 -109
  318. package/src/hooks/__tests__/useDataTableState.test.ts +143 -49
  319. package/src/hooks/__tests__/useDebounce.unit.test.ts +94 -19
  320. package/src/hooks/__tests__/useEvents.unit.test.ts +100 -125
  321. package/src/hooks/__tests__/useFileDisplay.test.ts +540 -0
  322. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +1 -4
  323. package/src/hooks/__tests__/useFileUrl.unit.test.ts +27 -247
  324. package/src/hooks/__tests__/useFileUrlCache.test.ts +246 -56
  325. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +442 -68
  326. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +345 -560
  327. package/src/hooks/__tests__/useFormDialog.test.ts +51 -222
  328. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +1 -1
  329. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +1 -4
  330. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -1
  331. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +1 -1
  332. package/src/hooks/__tests__/usePermissionCache.test.ts +506 -0
  333. package/src/hooks/__tests__/usePreventTabReload.test.ts +255 -36
  334. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +17 -8
  335. package/src/hooks/__tests__/usePublicEvent.test.ts +16 -24
  336. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +12 -4
  337. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +3 -6
  338. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +1 -2
  339. package/src/hooks/__tests__/useQueryCache.test.ts +313 -66
  340. package/src/hooks/__tests__/useSessionDraft.test.ts +496 -103
  341. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +2 -2
  342. package/src/hooks/__tests__/useStorage.unit.test.ts +72 -102
  343. package/src/hooks/__tests__/useToast.test.ts +413 -0
  344. package/src/hooks/__tests__/useToast.unit.test.tsx +1 -1
  345. package/src/hooks/__tests__/useZodForm.unit.test.tsx +175 -21
  346. package/src/hooks/index.ts +13 -1
  347. package/src/hooks/public/usePublicEvent.test.ts +304 -0
  348. package/src/hooks/public/usePublicEvent.ts +11 -11
  349. package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
  350. package/src/hooks/public/usePublicEventLogo.ts +2 -2
  351. package/src/hooks/public/usePublicFileDisplay.test.ts +723 -0
  352. package/src/hooks/public/usePublicFileDisplay.ts +79 -49
  353. package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
  354. package/src/hooks/public/usePublicRouteParams.ts +2 -2
  355. package/src/hooks/services/useAuthService.ts +1 -1
  356. package/src/hooks/services/useEventService.ts +1 -1
  357. package/src/hooks/useAccessibleApps.test.ts +400 -0
  358. package/src/hooks/useAccessibleApps.ts +264 -0
  359. package/src/hooks/useAddressAutocomplete.test.ts +165 -42
  360. package/src/hooks/useAddressAutocomplete.ts +41 -28
  361. package/src/hooks/useAppConfig.ts +13 -3
  362. package/src/hooks/useDataTablePerformance.ts +13 -12
  363. package/src/hooks/useDataTableState.ts +5 -5
  364. package/src/hooks/useEventTheme.test.ts +66 -17
  365. package/src/hooks/useEventTheme.ts +1 -1
  366. package/src/hooks/useEvents.ts +8 -1
  367. package/src/hooks/useFileDisplay.ts +66 -33
  368. package/src/hooks/useFileReference.test.ts +365 -87
  369. package/src/hooks/useFileReference.ts +2 -6
  370. package/src/hooks/useFileUrlCache.ts +4 -1
  371. package/src/hooks/useFormDialog.ts +2 -2
  372. package/src/hooks/useInactivityTracker.ts +3 -3
  373. package/src/hooks/useOrganisationPermissions.test.ts +1 -2
  374. package/src/hooks/useOrganisationPermissions.ts +1 -4
  375. package/src/hooks/useOrganisationSecurity.test.ts +1 -30
  376. package/src/hooks/useOrganisationSecurity.ts +3 -3
  377. package/src/hooks/useOrganisations.ts +1 -1
  378. package/src/hooks/usePerformanceMonitor.ts +1 -1
  379. package/src/hooks/usePermissionCache.ts +2 -6
  380. package/src/hooks/useQueryCache.ts +7 -7
  381. package/src/hooks/useSessionDraft.ts +14 -11
  382. package/src/hooks/useSessionRestoration.ts +1 -1
  383. package/src/hooks/useStorage.ts +75 -40
  384. package/src/hooks/useToast.ts +2 -2
  385. package/src/hooks/useZodForm.ts +3 -3
  386. package/src/icons/__tests__/index.test.ts +133 -0
  387. package/src/icons/index.ts +1 -1
  388. package/src/index.ts +43 -4
  389. package/src/providers/OrganisationProvider.test.tsx +40 -0
  390. package/src/providers/OrganisationProvider.tsx +5 -5
  391. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
  392. package/src/providers/__tests__/AuthProvider.test.tsx +22 -91
  393. package/src/providers/__tests__/EventProvider.test.tsx +16 -80
  394. package/src/providers/__tests__/InactivityProvider.test.tsx +29 -173
  395. package/src/providers/__tests__/OrganisationProvider.test.tsx +4 -5
  396. package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +591 -0
  397. package/src/providers/__tests__/ProviderLifecycle.test.tsx +80 -196
  398. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +40 -133
  399. package/src/providers/__tests__/index.test.ts +138 -0
  400. package/src/providers/services/AuthServiceContext.ts +27 -0
  401. package/src/providers/services/AuthServiceProvider.tsx +81 -20
  402. package/src/providers/services/EventServiceContext.ts +25 -0
  403. package/src/providers/services/EventServiceProvider.tsx +11 -20
  404. package/src/providers/services/InactivityServiceContext.ts +25 -0
  405. package/src/providers/services/InactivityServiceProvider.tsx +7 -17
  406. package/src/providers/services/OrganisationServiceContext.ts +25 -0
  407. package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
  408. package/src/providers/services/UnifiedAuthContext.ts +99 -0
  409. package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
  410. package/src/providers/services/UnifiedAuthProvider.tsx +38 -143
  411. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +61 -95
  412. package/src/providers/services/__tests__/AuthServiceProvider.test.tsx +638 -0
  413. package/src/providers/services/__tests__/EventServiceProvider.test.tsx +839 -0
  414. package/src/providers/services/__tests__/InactivityServiceProvider.test.tsx +662 -0
  415. package/src/providers/services/__tests__/OrganisationServiceProvider.test.tsx +440 -0
  416. package/src/providers/services/__tests__/UnifiedAuthProvider.advanced.test.tsx +435 -0
  417. package/src/providers/services/__tests__/UnifiedAuthProvider.appId.test.tsx +408 -0
  418. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +55 -48
  419. package/src/providers/services/__tests__/contexts.test.tsx +281 -0
  420. package/src/providers/services/__tests__/useUnifiedAuth.test.tsx +251 -0
  421. package/src/providers/services/useUnifiedAuth.ts +29 -0
  422. package/src/rbac/README.md +5 -5
  423. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +9 -14
  424. package/src/rbac/__tests__/audit-batched.test.ts +550 -0
  425. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +1 -14
  426. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +43 -12
  427. package/src/rbac/__tests__/cache-invalidation.test.ts +8 -14
  428. package/src/rbac/__tests__/engine.comprehensive.test.ts +2 -7
  429. package/src/rbac/__tests__/index.test.ts +107 -0
  430. package/src/rbac/__tests__/performance.test.ts +451 -0
  431. package/src/rbac/__tests__/rbac-core.test.tsx +2 -2
  432. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -5
  433. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +1 -7
  434. package/src/rbac/__tests__/rbac-functions.test.ts +0 -1
  435. package/src/rbac/__tests__/rbac-integration.test.ts +0 -1
  436. package/src/rbac/__tests__/scenarios.user-role.test.tsx +21 -32
  437. package/src/rbac/adapters.test.tsx +654 -0
  438. package/src/rbac/adapters.tsx +24 -9
  439. package/src/rbac/api.test.ts +13 -217
  440. package/src/rbac/api.ts +85 -16
  441. package/src/rbac/audit-batched.ts +5 -4
  442. package/src/rbac/audit.test.ts +225 -28
  443. package/src/rbac/audit.ts +22 -17
  444. package/src/rbac/cache-invalidation.ts +18 -15
  445. package/src/rbac/cache.test.ts +123 -63
  446. package/src/rbac/cache.ts +3 -4
  447. package/src/rbac/components/AccessDenied.tsx +20 -18
  448. package/src/rbac/components/NavigationGuard.tsx +10 -8
  449. package/src/rbac/components/PagePermissionGuard.tsx +27 -25
  450. package/src/rbac/components/__tests__/AccessDenied.test.tsx +324 -0
  451. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +242 -71
  452. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +20 -37
  453. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +18 -17
  454. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +452 -129
  455. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -13
  456. package/src/rbac/config.test.ts +131 -48
  457. package/src/rbac/config.ts +11 -8
  458. package/src/rbac/docs/event-based-apps.md +26 -13
  459. package/src/rbac/engine.test.ts +496 -146
  460. package/src/rbac/engine.ts +53 -13
  461. package/src/rbac/errors.test.ts +99 -87
  462. package/src/rbac/eslint-rules.js +2 -2
  463. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -5
  464. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +601 -1
  465. package/src/rbac/hooks/permissions/__tests__/useAccessLevel.test.ts +622 -0
  466. package/src/rbac/hooks/permissions/__tests__/useCan.test.ts +798 -0
  467. package/src/rbac/hooks/permissions/__tests__/useMultiplePermissions.test.ts +843 -0
  468. package/src/rbac/hooks/permissions/__tests__/usePermissions.test.ts +545 -0
  469. package/src/rbac/hooks/permissions/useAccessLevel.ts +7 -8
  470. package/src/rbac/hooks/permissions/useCan.ts +12 -10
  471. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +57 -8
  472. package/src/rbac/hooks/permissions/usePermissions.ts +15 -14
  473. package/src/rbac/hooks/useCan.test.ts +319 -3
  474. package/src/rbac/hooks/usePermissions.test.ts +426 -0
  475. package/src/rbac/hooks/usePermissions.ts +5 -7
  476. package/src/rbac/hooks/useRBAC.test.ts +1669 -2
  477. package/src/rbac/hooks/useRBAC.ts +7 -11
  478. package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
  479. package/src/rbac/hooks/useResolvedScope.ts +4 -1
  480. package/src/rbac/hooks/useResourcePermissions.test.ts +538 -1
  481. package/src/rbac/hooks/useResourcePermissions.ts +9 -7
  482. package/src/rbac/hooks/useRoleManagement.test.ts +659 -1
  483. package/src/rbac/hooks/useRoleManagement.ts +16 -12
  484. package/src/rbac/hooks/useSecureSupabase.ts +11 -12
  485. package/src/rbac/index.ts +32 -32
  486. package/src/rbac/permissions.test.ts +149 -68
  487. package/src/rbac/permissions.ts +0 -3
  488. package/src/rbac/request-deduplication.test.ts +347 -0
  489. package/src/rbac/secureClient.test.ts +112 -159
  490. package/src/rbac/secureClient.ts +46 -26
  491. package/src/rbac/security.test.ts +125 -44
  492. package/src/rbac/security.ts +7 -6
  493. package/src/rbac/types.test.ts +236 -0
  494. package/src/rbac/types.ts +7 -5
  495. package/src/rbac/utils/__tests__/clientSecurity.test.ts +192 -0
  496. package/src/rbac/utils/__tests__/contextValidator.test.ts +1 -3
  497. package/src/rbac/utils/__tests__/deep-equal.test.ts +23 -0
  498. package/src/rbac/utils/__tests__/eventContext.test.ts +10 -57
  499. package/src/rbac/utils/clientSecurity.ts +6 -4
  500. package/src/rbac/utils/contextValidator.ts +1 -2
  501. package/src/rbac/utils/eventContext.ts +2 -2
  502. package/src/services/AuthService.ts +13 -11
  503. package/src/services/EventService.ts +4 -5
  504. package/src/services/OrganisationService.ts +13 -30
  505. package/src/services/__tests__/AuthService.edge-cases.test.ts +746 -0
  506. package/src/services/__tests__/AuthService.restoreSession.test.ts +23 -3
  507. package/src/services/__tests__/AuthService.test.ts +4 -8
  508. package/src/services/__tests__/BaseService.edge-cases.test.ts +506 -0
  509. package/src/services/__tests__/BaseService.test.ts +49 -0
  510. package/src/services/__tests__/EventService.edge-cases.test.ts +633 -0
  511. package/src/services/__tests__/EventService.eventColours.test.ts +0 -12
  512. package/src/services/__tests__/EventService.test.ts +0 -7
  513. package/src/services/__tests__/InactivityService.edge-cases.test.ts +492 -0
  514. package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -5
  515. package/src/services/__tests__/OrganisationService.edge-cases.test.ts +633 -0
  516. package/src/services/base/BaseService.test.ts +214 -0
  517. package/src/services/interfaces/IOrganisationService.ts +0 -1
  518. package/src/services/interfaces/__tests__/IAuthService.test.ts +190 -0
  519. package/src/services/interfaces/__tests__/IEventService.test.ts +176 -0
  520. package/src/services/interfaces/__tests__/IInactivityService.test.ts +183 -0
  521. package/src/services/interfaces/__tests__/IOrganisationService.test.ts +207 -0
  522. package/src/styles/core.css +1 -0
  523. package/src/theming/__tests__/runtime.test.ts +29 -94
  524. package/src/theming/parseEventColours.ts +18 -9
  525. package/src/theming/runtime.ts +1 -5
  526. package/src/types/__tests__/core.test.ts +397 -0
  527. package/src/types/__tests__/database-generated.test.ts +78 -0
  528. package/src/types/__tests__/file-reference.test.ts +270 -366
  529. package/src/types/__tests__/guards.test.ts +26 -26
  530. package/src/types/__tests__/index.test.ts +265 -0
  531. package/src/types/__tests__/type-validation.test.ts +3 -3
  532. package/src/types/__tests__/validation.test.ts +0 -2
  533. package/src/types/auth.ts +0 -1
  534. package/src/types/database.generated.ts +9 -9
  535. package/src/types/event.ts +1 -1
  536. package/src/types/rpc-responses.ts +33 -0
  537. package/src/types/supabase.ts +1 -2
  538. package/src/types/vitest-globals.d.ts +1 -1
  539. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +64 -77
  540. package/src/utils/__tests__/dynamicUtils.unit.test.ts +13 -0
  541. package/src/utils/__tests__/formatDate.unit.test.ts +1 -1
  542. package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -1
  543. package/src/utils/__tests__/logger.unit.test.ts +1 -1
  544. package/src/utils/__tests__/performanceBenchmark.test.ts +1 -2
  545. package/src/utils/__tests__/performanceBudgets.unit.test.ts +48 -13
  546. package/src/utils/__tests__/request-deduplication.test.ts +349 -0
  547. package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -1
  548. package/src/utils/__tests__/timezone.test.ts +1 -1
  549. package/src/utils/__tests__/validation.unit.test.ts +1 -2
  550. package/src/utils/__tests__/validationUtils.unit.test.ts +1 -1
  551. package/src/utils/app/appConfig.test.ts +235 -0
  552. package/src/utils/app/appIdResolver.test.ts +188 -20
  553. package/src/utils/app/appNameResolver.test.ts +18 -10
  554. package/src/utils/app/appNameResolver.ts +11 -9
  555. package/src/utils/app/appPortMap.test.ts +125 -0
  556. package/src/utils/app/appPortMap.ts +51 -0
  557. package/src/utils/app/buildAppUrl.test.ts +273 -0
  558. package/src/utils/app/buildAppUrl.ts +114 -0
  559. package/src/utils/audit/audit.test.ts +354 -39
  560. package/src/utils/context/organisationContext.test.ts +10 -4
  561. package/src/utils/context/organisationContext.ts +5 -5
  562. package/src/utils/context/sessionTracking.test.ts +354 -0
  563. package/src/utils/core/__tests__/cn.test.ts +66 -0
  564. package/src/utils/core/__tests__/debugLogger.test.ts +113 -0
  565. package/src/utils/core/__tests__/logger.test.ts +217 -0
  566. package/src/utils/core/debugLogger.ts +15 -8
  567. package/src/utils/core/logger.ts +20 -16
  568. package/src/utils/device/deviceFingerprint.test.ts +8 -5
  569. package/src/utils/device/deviceFingerprint.ts +3 -3
  570. package/src/utils/dynamic/__tests__/dynamicUtils.test.ts +185 -0
  571. package/src/utils/dynamic/__tests__/lazyLoad.test.tsx +156 -0
  572. package/src/utils/dynamic/createLazyComponent.tsx +38 -0
  573. package/src/utils/dynamic/dynamicUtils.ts +6 -6
  574. package/src/utils/dynamic/lazyLoad.tsx +8 -36
  575. package/src/utils/dynamic/papaparseLoader.ts +7 -0
  576. package/src/utils/file-reference/__tests__/file-reference.test.ts +583 -145
  577. package/src/utils/file-reference/index.ts +0 -1
  578. package/src/utils/formatting/formatDate.test.ts +22 -148
  579. package/src/utils/formatting/formatDateTime.test.ts +41 -119
  580. package/src/utils/formatting/formatDateTimeTimezone.test.ts +40 -84
  581. package/src/utils/formatting/formatNumber.test.ts +259 -0
  582. package/src/utils/formatting/formatTime.test.ts +36 -128
  583. package/src/utils/formatting/formatting.ts +1 -1
  584. package/src/utils/google-places/googlePlacesUtils.test.ts +72 -3
  585. package/src/utils/google-places/googlePlacesUtils.ts +15 -2
  586. package/src/utils/google-places/loadGoogleMapsScript.test.ts +58 -1
  587. package/src/utils/google-places/loadGoogleMapsScript.ts +2 -1
  588. package/src/utils/index.ts +52 -11
  589. package/src/utils/location/location.test.ts +18 -115
  590. package/src/utils/performance/__tests__/bundleAnalysis.test.ts +148 -0
  591. package/src/utils/performance/__tests__/performanceBenchmark.test.ts +251 -0
  592. package/src/utils/performance/__tests__/performanceBudgets.test.ts +241 -0
  593. package/src/utils/performance/bundleAnalysis.ts +16 -22
  594. package/src/utils/performance/performanceBenchmark.ts +12 -4
  595. package/src/utils/performance/performanceBudgets.ts +9 -6
  596. package/src/utils/permissions/__tests__/permissionTypes.test.ts +149 -0
  597. package/src/utils/permissions/permissionUtils.test.ts +20 -42
  598. package/src/utils/persistence/__tests__/keyDerivation.test.ts +180 -9
  599. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +164 -16
  600. package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
  601. package/src/utils/request-deduplication.ts +6 -4
  602. package/src/utils/security/auth-utils.ts +7 -7
  603. package/src/utils/security/secureDataAccess.test.ts +22 -191
  604. package/src/utils/security/secureErrors.test.ts +163 -0
  605. package/src/utils/security/secureStorage.test.ts +156 -0
  606. package/src/utils/security/secureStorage.ts +1 -1
  607. package/src/utils/security/security.test.ts +204 -0
  608. package/src/utils/security/securityMonitor.test.ts +90 -0
  609. package/src/utils/security/securityMonitor.ts +1 -1
  610. package/src/utils/storage/__tests__/config.unit.test.ts +239 -0
  611. package/src/utils/storage/__tests__/index.unit.test.ts +64 -12
  612. package/src/utils/storage/helpers.test.ts +757 -430
  613. package/src/utils/storage/helpers.ts +1 -2
  614. package/src/utils/storage/{index.ts → storageUtils.ts} +1 -36
  615. package/src/utils/storage/types.ts +2 -2
  616. package/src/utils/supabase/createBaseClient.test.ts +201 -0
  617. package/src/utils/supabase/createBaseClient.ts +27 -8
  618. package/src/utils/timezone/timezone.test.ts +25 -43
  619. package/src/utils/validation/__tests__/common.test.ts +115 -0
  620. package/src/utils/validation/__tests__/csrf.test.ts +65 -0
  621. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +27 -7
  622. package/src/utils/validation/__tests__/passwordSchema.test.ts +164 -0
  623. package/src/utils/validation/__tests__/schema.test.ts +127 -0
  624. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +76 -3
  625. package/src/utils/validation/__tests__/user.test.ts +173 -0
  626. package/src/utils/validation/__tests__/validation.test.ts +197 -0
  627. package/src/utils/validation/__tests__/validationUtils.test.ts +265 -43
  628. package/src/utils/validation/htmlSanitization.ts +27 -31
  629. package/src/utils/validation/schema.ts +6 -3
  630. package/src/utils/validation/sqlInjectionProtection.ts +2 -2
  631. package/src/vite-env.d.ts +6 -0
  632. package/dist/DataTable-DRUIgtUH.d.ts +0 -166
  633. package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
  634. package/dist/audit-MYQXYZFU.js +0 -3
  635. package/dist/chunk-7ILTDCL2.js +0 -80
  636. package/dist/chunk-EF2UGZWY.js +0 -611
  637. package/dist/chunk-FEJLJNWA.js +0 -181
  638. package/dist/chunk-GS5672WG.js +0 -2003
  639. package/dist/chunk-S6ZQKDY6.js +0 -62
  640. package/dist/chunk-Z2FNRKF3.js +0 -994
  641. package/dist/useToast-AyaT-x7p.d.ts +0 -68
  642. package/src/components/DataTable/components/index.ts +0 -16
  643. package/src/components/DataTable/core/index.ts +0 -1
  644. package/src/components/DataTable/hooks/index.ts +0 -13
  645. package/src/components/DataTable/utils/index.ts +0 -9
  646. package/src/components/PublicLayout/index.ts +0 -32
  647. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  648. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  649. package/src/hooks/public/index.ts +0 -36
  650. package/src/hooks/usePermissionCache.test.ts +0 -536
  651. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
  652. package/src/rbac/audit-enhanced.ts +0 -384
  653. package/src/rbac/compliance/database-validator.ts +0 -165
  654. package/src/rbac/compliance/index.ts +0 -48
  655. package/src/rbac/compliance/pattern-detector.ts +0 -553
  656. package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
  657. package/src/rbac/compliance/runtime-compliance.ts +0 -99
  658. package/src/rbac/compliance/setup-validator.ts +0 -131
  659. package/src/rbac/components/index.ts +0 -26
  660. package/src/rbac/hooks/index.ts +0 -34
  661. package/src/rbac/hooks/permissions/index.ts +0 -4
  662. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  663. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
  664. package/src/utils/app/appNameResolver.simple.test.ts +0 -212
  665. package/src/utils/google-places/index.ts +0 -26
  666. package/src/utils/location/index.ts +0 -16
  667. package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
  668. package/src/utils/timezone/index.ts +0 -17
  669. package/src/utils/validation/index.ts +0 -73
@@ -10,17 +10,20 @@
10
10
  */
11
11
 
12
12
  import React from 'react';
13
- import { screen, waitFor } from '@testing-library/react';
13
+ import { screen } from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
16
16
  import { User } from '@supabase/supabase-js';
17
- import { Header, HeaderProps } from './Header';
17
+ import { Header } from './Header';
18
18
  import { renderWithProviders } from '../../__tests__/helpers/test-utils';
19
19
  import type { NavigationItem } from '../NavigationMenu';
20
+ import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
21
+ import { useOrganisations } from '../../hooks/useOrganisations';
22
+ import { useEvents } from '../../hooks/useEvents';
20
23
 
21
24
  // Mock the child components
22
25
  vi.mock('../NavigationMenu', () => ({
23
- NavigationMenu: ({ items, onNavigate, currentPath, buttonText, className }: any) => (
26
+ NavigationMenu: ({ items, _onNavigate, _currentPath, buttonText, className }: any) => (
24
27
  <div data-testid="navigation-menu" className={className}>
25
28
  <button data-testid="navigation-menu-trigger">{buttonText}</button>
26
29
  {items?.map((item: any) => (
@@ -52,12 +55,23 @@ vi.mock('../UserMenu', () => ({
52
55
  ),
53
56
  }));
54
57
 
55
- vi.mock('../ContextSelector', () => ({
56
- ContextSelector: ({ placeholder, className, 'data-testid': testId }: any) => (
57
- <div data-testid={testId || 'context-selector'} className={className}>
58
+ const mockContextSelector = vi.hoisted(() =>
59
+ vi.fn(({ placeholder, className, showOrganisations, showEvents, onOrganisationSelect, onEventSelect, 'data-testid': testId }: any) => (
60
+ <div
61
+ data-testid={testId || 'context-selector'}
62
+ className={className}
63
+ data-show-orgs={showOrganisations}
64
+ data-show-events={showEvents}
65
+ data-on-org-select={onOrganisationSelect ? 'present' : 'absent'}
66
+ data-on-event-select={onEventSelect ? 'present' : 'absent'}
67
+ >
58
68
  <button>{placeholder || 'Select organisation or event'}</button>
59
69
  </div>
60
- ),
70
+ ))
71
+ );
72
+
73
+ vi.mock('../ContextSelector', () => ({
74
+ ContextSelector: mockContextSelector,
61
75
  }));
62
76
 
63
77
  // Mock useEventService to prevent provider requirement
@@ -72,6 +86,36 @@ vi.mock('../../hooks/services/useEventService', () => ({
72
86
  }))
73
87
  }));
74
88
 
89
+ // Mock useUnifiedAuth to prevent infinite loops and async calls
90
+ vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
91
+ useUnifiedAuth: vi.fn(() => ({
92
+ user: null,
93
+ isAuthenticated: false,
94
+ appName: undefined,
95
+ session: null,
96
+ authLoading: false,
97
+ authError: null,
98
+ supabase: null,
99
+ })),
100
+ }));
101
+
102
+ // Mock AppSwitcher to prevent async hook calls that could hang
103
+ // AppSwitcher uses useAccessibleApps which makes async database queries
104
+ vi.mock('../AppSwitcher/AppSwitcher', () => ({
105
+ AppSwitcher: ({ trigger, currentAppName: _currentAppName, showSwitcher, className }: any) => {
106
+ // If showSwitcher is false, return null or trigger (matches actual behavior)
107
+ if (showSwitcher === false) {
108
+ return trigger || null;
109
+ }
110
+ // If trigger is provided, return it wrapped in a div (simulating Select component)
111
+ if (trigger) {
112
+ return <div data-testid="app-switcher" className={className}>{trigger}</div>;
113
+ }
114
+ // Otherwise return a div with testid (when no trigger, AppSwitcher renders its own trigger)
115
+ return <div data-testid="app-switcher" className={className}>App Switcher</div>;
116
+ },
117
+ }));
118
+
75
119
  // Mock useOrganisations hook
76
120
  vi.mock('../../hooks/useOrganisations', () => ({
77
121
  useOrganisations: vi.fn(() => ({
@@ -100,6 +144,19 @@ vi.mock('../../hooks/useOrganisations', () => ({
100
144
  }))
101
145
  }));
102
146
 
147
+ // Mock useEvents hook
148
+ vi.mock('../../hooks/useEvents', () => ({
149
+ useEvents: vi.fn(() => ({
150
+ events: [],
151
+ selectedEvent: null,
152
+ selectedEventId: null,
153
+ eventLoading: false,
154
+ eventError: null,
155
+ setSelectedEvent: vi.fn(),
156
+ refreshEvents: vi.fn(),
157
+ })),
158
+ }));
159
+
103
160
  // Test data
104
161
  const mockUser: User = {
105
162
  id: '123',
@@ -118,16 +175,7 @@ const mockNavItems: NavigationItem[] = [
118
175
  { id: 'settings', label: 'Settings', href: '/settings' },
119
176
  ];
120
177
 
121
- const mockProps: HeaderProps = {
122
- logoUrl: '/test-logo.svg',
123
- logoAlt: 'Test Logo',
124
- navItems: mockNavItems,
125
- user: mockUser,
126
- onSignOut: vi.fn(),
127
- onChangePassword: vi.fn(),
128
- currentPath: '/dashboard',
129
- onNavigate: vi.fn(),
130
- };
178
+ // mockProps removed - not used in tests
131
179
 
132
180
  describe('Header Component', () => {
133
181
  beforeEach(() => {
@@ -175,44 +223,12 @@ describe('Header Component', () => {
175
223
  expect(logo).toHaveAttribute('alt', 'Test Logo');
176
224
  });
177
225
 
178
- it('renders custom logo component when provided', () => {
179
- const CustomLogo = () => <div data-testid="custom-logo">Custom Logo</div>;
180
-
181
- renderWithProviders(<Header logo={<CustomLogo />} />);
182
-
183
- expect(screen.getByTestId('custom-logo')).toBeInTheDocument();
184
- expect(screen.queryByRole('img')).not.toBeInTheDocument();
185
- });
186
-
187
- it('renders default logo when no logo props provided', () => {
188
- renderWithProviders(<Header />);
189
-
190
- const logo = screen.getByRole('img', { name: 'Logo' });
191
- expect(logo).toBeInTheDocument();
192
- expect(logo).toBeVisible();
193
- expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
194
- });
195
-
196
- it('uses logoAlt as fallback when logoUrl provided without logoAlt', () => {
197
- renderWithProviders(<Header logoUrl="/test-logo.svg" />);
198
-
199
- const logo = screen.getByRole('img', { name: 'Logo' });
200
- expect(logo).toHaveAttribute('alt', 'Logo');
201
- });
202
-
203
- it('prioritizes custom logo over logoUrl', () => {
204
- const CustomLogo = () => <div data-testid="custom-logo">Custom Logo</div>;
205
-
206
- renderWithProviders(
207
- <Header
208
- logo={<CustomLogo />}
209
- logoUrl="/test-logo.svg"
210
- logoAlt="Test Logo"
211
- />
212
- );
226
+ it('renders clickable logo when logoHref is provided with logoUrl', () => {
227
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard" />);
213
228
 
214
- expect(screen.getByTestId('custom-logo')).toBeInTheDocument();
215
- expect(screen.queryByRole('img')).not.toBeInTheDocument();
229
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
230
+ expect(logoLink).toBeInTheDocument();
231
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
216
232
  });
217
233
  });
218
234
 
@@ -223,16 +239,17 @@ describe('Header Component', () => {
223
239
 
224
240
  expect(screen.getByTestId('navigation-menu')).toBeInTheDocument();
225
241
  expect(screen.getByTestId('navigation-menu-trigger')).toHaveTextContent('Menu');
226
- });
227
-
228
- it('does not render navigation menu when navItems empty', () => {
229
- renderWithProviders(<Header navItems={[]} />);
230
242
 
231
- expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
243
+ mockNavItems.forEach(item => {
244
+ expect(screen.getByTestId(`nav-item-${item.id}`)).toHaveTextContent(item.label);
245
+ });
232
246
  });
233
247
 
234
- it('does not render navigation menu when navItems undefined', () => {
235
- renderWithProviders(<Header />);
248
+ it.each([
249
+ { navItems: [], description: 'empty array' },
250
+ { navItems: undefined, description: 'undefined' },
251
+ ])('does not render navigation menu when navItems is $description', ({ navItems }) => {
252
+ renderWithProviders(<Header navItems={navItems} />);
236
253
 
237
254
  expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
238
255
  });
@@ -249,39 +266,26 @@ describe('Header Component', () => {
249
266
  );
250
267
 
251
268
  expect(screen.getByTestId('navigation-menu')).toBeInTheDocument();
252
- // NavigationMenu component should receive the props
253
- });
254
-
255
- it('renders all navigation items', () => {
256
- renderWithProviders(<Header navItems={mockNavItems} />);
257
-
258
- mockNavItems.forEach(item => {
259
- expect(screen.getByTestId(`nav-item-${item.id}`)).toHaveTextContent(item.label);
260
- });
261
269
  });
262
270
  });
263
271
 
264
272
  // User menu tests
265
273
  describe('User Menu', () => {
266
- it('renders user menu when showUserMenu is true', () => {
267
- renderWithProviders(<Header user={mockUser} showUserMenu={true} />);
268
-
269
- expect(screen.getByTestId('user-menu')).toBeInTheDocument();
270
- });
271
-
272
- it('does not render user menu when showUserMenu is false', () => {
273
- renderWithProviders(<Header user={mockUser} showUserMenu={false} />);
274
-
275
- expect(screen.queryByTestId('user-menu')).not.toBeInTheDocument();
276
- });
277
-
278
- it('renders user menu by default when user provided', () => {
279
- renderWithProviders(<Header user={mockUser} />);
280
-
281
- expect(screen.getByTestId('user-menu')).toBeInTheDocument();
274
+ it.each([
275
+ { showUserMenu: true, shouldRender: true },
276
+ { showUserMenu: false, shouldRender: false },
277
+ { showUserMenu: undefined, shouldRender: true }, // default is true
278
+ ])('renders user menu based on showUserMenu prop ($showUserMenu)', ({ showUserMenu, shouldRender }) => {
279
+ renderWithProviders(<Header user={mockUser} showUserMenu={showUserMenu} />);
280
+
281
+ if (shouldRender) {
282
+ expect(screen.getByTestId('user-menu')).toBeInTheDocument();
283
+ } else {
284
+ expect(screen.queryByTestId('user-menu')).not.toBeInTheDocument();
285
+ }
282
286
  });
283
287
 
284
- it('displays user email in user menu trigger', () => {
288
+ it('displays user email when user is provided', () => {
285
289
  renderWithProviders(<Header user={mockUser} />);
286
290
 
287
291
  expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('test@example.com');
@@ -293,7 +297,7 @@ describe('Header Component', () => {
293
297
  expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('Sign In');
294
298
  });
295
299
 
296
- it('renders custom user menu when provided', () => {
300
+ it('renders custom user menu when provided (overrides default)', () => {
297
301
  const CustomUserMenu = () => <div data-testid="custom-user-menu">Custom Menu</div>;
298
302
 
299
303
  renderWithProviders(<Header user={mockUser} userMenu={<CustomUserMenu />} />);
@@ -320,36 +324,19 @@ describe('Header Component', () => {
320
324
 
321
325
  // Context selector tests
322
326
  describe('Context Selector', () => {
323
- it('renders context selector by default', () => {
324
- renderWithProviders(<Header />);
325
-
326
- expect(screen.getByTestId('context-selector')).toBeInTheDocument();
327
- });
328
-
329
- it('does not render context selector when showContextSelector is false', () => {
330
- renderWithProviders(<Header showContextSelector={false} />);
331
-
332
- expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
333
- });
334
-
335
- it('renders context selector when showContextSelector is explicitly true', () => {
336
- renderWithProviders(<Header showContextSelector={true} />);
337
-
338
- expect(screen.getByTestId('context-selector')).toBeInTheDocument();
339
- });
340
-
341
- it('applies correct className to context selector', () => {
342
- renderWithProviders(<Header showContextSelector={true} />);
343
-
344
- const contextSelector = screen.getByTestId('context-selector');
345
- expect(contextSelector).toBeInTheDocument();
346
- expect(contextSelector).toBeVisible();
347
- });
348
-
349
- it('shows correct placeholder text', () => {
350
- renderWithProviders(<Header showContextSelector={true} />);
351
-
352
- expect(screen.getByRole('button', { name: 'Select organisation or event' })).toBeInTheDocument();
327
+ it.each([
328
+ { showContextSelector: true, shouldRender: true },
329
+ { showContextSelector: false, shouldRender: false },
330
+ { showContextSelector: undefined, shouldRender: true }, // default is true
331
+ ])('renders context selector based on showContextSelector prop ($showContextSelector)', ({ showContextSelector, shouldRender }) => {
332
+ renderWithProviders(<Header showContextSelector={showContextSelector} />);
333
+
334
+ if (shouldRender) {
335
+ expect(screen.getByTestId('context-selector')).toBeInTheDocument();
336
+ expect(screen.getByRole('button', { name: 'Select organisation or event' })).toBeInTheDocument();
337
+ } else {
338
+ expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
339
+ }
353
340
  });
354
341
 
355
342
  it('preserves layout when context selector is hidden', () => {
@@ -365,10 +352,7 @@ describe('Header Component', () => {
365
352
  expect(nav).toBeInTheDocument();
366
353
  expect(nav).toBeVisible();
367
354
 
368
- // Context selector should not be rendered
369
355
  expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
370
-
371
- // User menu should still be present and positioned correctly
372
356
  expect(screen.getByTestId('user-menu')).toBeInTheDocument();
373
357
  });
374
358
  });
@@ -460,28 +444,16 @@ describe('Header Component', () => {
460
444
  });
461
445
 
462
446
  describe('Layout and Responsive Design', () => {
463
- it('applies correct grid layout classes', () => {
464
- renderWithProviders(<Header />);
465
-
466
- const nav = screen.getByRole('navigation');
467
- expect(nav).toBeInTheDocument();
468
- expect(nav).toBeVisible();
469
- });
470
-
471
- it('applies correct container classes', () => {
447
+ it('applies correct layout structure', () => {
472
448
  renderWithProviders(<Header />);
473
449
 
450
+ const header = screen.getByRole('banner');
474
451
  const nav = screen.getByRole('navigation');
475
- expect(nav).toBeInTheDocument();
476
- expect(nav).toBeVisible();
477
- });
478
-
479
- it('maintains proper header height', () => {
480
- renderWithProviders(<Header />);
481
452
 
482
- const header = screen.getByRole('banner');
483
453
  expect(header).toBeInTheDocument();
484
454
  expect(header).toBeVisible();
455
+ expect(nav).toBeInTheDocument();
456
+ expect(nav).toBeVisible();
485
457
  });
486
458
  });
487
459
 
@@ -518,42 +490,51 @@ describe('Header Component', () => {
518
490
 
519
491
  // Edge cases and error handling
520
492
  describe('Edge Cases and Error Handling', () => {
521
- it('handles null user gracefully', () => {
522
- renderWithProviders(<Header user={null} />);
493
+ it.each([
494
+ { user: null, description: 'null user' },
495
+ { user: undefined, description: 'undefined user' },
496
+ ])('handles $description gracefully', ({ user }) => {
497
+ renderWithProviders(<Header user={user} />);
523
498
 
524
499
  expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('Sign In');
525
500
  });
526
501
 
527
- it('handles undefined user gracefully', () => {
528
- renderWithProviders(<Header user={undefined} />);
502
+ it.each([
503
+ { navItems: [], description: 'empty array' },
504
+ { navItems: undefined, description: 'undefined' },
505
+ ])('does not render navigation menu when navItems is $description', ({ navItems }) => {
506
+ renderWithProviders(<Header navItems={navItems} />);
529
507
 
530
- expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('Sign In');
508
+ expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
531
509
  });
532
510
 
533
- it('handles empty navItems array', () => {
534
- renderWithProviders(<Header navItems={[]} />);
511
+ it('handles missing logoUrl gracefully (uses default)', () => {
512
+ renderWithProviders(<Header logoAlt="Test Logo" />);
535
513
 
536
- expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
514
+ const logo = screen.getByRole('img', { name: 'Test Logo' });
515
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
537
516
  });
538
517
 
539
- it('handles undefined navItems', () => {
540
- renderWithProviders(<Header navItems={undefined} />);
518
+ it('handles missing logoAlt gracefully (uses default)', () => {
519
+ renderWithProviders(<Header logoUrl="/test-logo.svg" />);
541
520
 
542
- expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
521
+ const logo = screen.getByRole('img', { name: 'Logo' });
522
+ expect(logo).toHaveAttribute('alt', 'Logo');
543
523
  });
544
524
 
545
- it('handles missing logoUrl gracefully', () => {
546
- renderWithProviders(<Header logoAlt="Test Logo" />);
525
+ it('handles logoHref for clickable logo', () => {
526
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard" />);
547
527
 
548
- const logo = screen.getByRole('img', { name: 'Test Logo' });
549
- expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
528
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
529
+ expect(logoLink).toBeInTheDocument();
530
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
550
531
  });
551
532
 
552
- it('handles missing logoAlt gracefully', () => {
533
+ it('renders logo without link when logoHref is not provided', () => {
553
534
  renderWithProviders(<Header logoUrl="/test-logo.svg" />);
554
535
 
555
536
  const logo = screen.getByRole('img', { name: 'Logo' });
556
- expect(logo).toHaveAttribute('alt', 'Logo');
537
+ expect(logo.closest('a')).not.toBeInTheDocument();
557
538
  });
558
539
  });
559
540
 
@@ -639,4 +620,514 @@ describe('Header Component', () => {
639
620
  expect(screen.getByTestId('user-menu')).toBeInTheDocument();
640
621
  });
641
622
  });
623
+
624
+ // AppSwitcher tests
625
+ describe('AppSwitcher Integration', () => {
626
+ beforeEach(() => {
627
+ // Reset to default mock state
628
+ vi.mocked(useUnifiedAuth).mockReturnValue({
629
+ user: null,
630
+ isAuthenticated: false,
631
+ appName: undefined,
632
+ session: null,
633
+ authLoading: false,
634
+ authError: null,
635
+ supabase: null,
636
+ });
637
+ });
638
+
639
+ it('renders AppSwitcher when authenticated and appName is provided', () => {
640
+ vi.mocked(useUnifiedAuth).mockReturnValue({
641
+ user: mockUser,
642
+ isAuthenticated: true,
643
+ appName: 'TEST',
644
+ session: null,
645
+ authLoading: false,
646
+ authError: null,
647
+ supabase: null,
648
+ });
649
+
650
+ renderWithProviders(<Header />);
651
+
652
+ expect(screen.getByTestId('app-switcher')).toBeInTheDocument();
653
+ });
654
+
655
+ it('does not render AppSwitcher when not authenticated', () => {
656
+ vi.mocked(useUnifiedAuth).mockReturnValue({
657
+ user: null,
658
+ isAuthenticated: false,
659
+ appName: undefined,
660
+ session: null,
661
+ authLoading: false,
662
+ authError: null,
663
+ supabase: null,
664
+ });
665
+
666
+ renderWithProviders(<Header />);
667
+
668
+ expect(screen.queryByTestId('app-switcher')).not.toBeInTheDocument();
669
+ });
670
+
671
+ it('does not render AppSwitcher when appName is not provided', () => {
672
+ vi.mocked(useUnifiedAuth).mockReturnValue({
673
+ user: mockUser,
674
+ isAuthenticated: true,
675
+ appName: undefined,
676
+ session: null,
677
+ authLoading: false,
678
+ authError: null,
679
+ supabase: null,
680
+ });
681
+
682
+ renderWithProviders(<Header />);
683
+
684
+ expect(screen.queryByTestId('app-switcher')).not.toBeInTheDocument();
685
+ });
686
+
687
+ it('renders AppSwitcher with correct className', () => {
688
+ vi.mocked(useUnifiedAuth).mockReturnValue({
689
+ user: mockUser,
690
+ isAuthenticated: true,
691
+ appName: 'TEST',
692
+ session: null,
693
+ authLoading: false,
694
+ authError: null,
695
+ supabase: null,
696
+ });
697
+
698
+ renderWithProviders(<Header />);
699
+
700
+ const appSwitcher = screen.getByTestId('app-switcher');
701
+ expect(appSwitcher).toBeInTheDocument();
702
+ expect(appSwitcher).toHaveClass('w-9');
703
+ });
704
+ });
705
+
706
+ // Default logo tests
707
+ describe('Default Logo Rendering', () => {
708
+ it('renders default logo when logoUrl is not provided and logoHref is provided', () => {
709
+ renderWithProviders(<Header logoHref="/dashboard" />);
710
+
711
+ const logo = screen.getByRole('img', { name: 'Logo' });
712
+ expect(logo).toBeInTheDocument();
713
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
714
+
715
+ const logoLink = logo.closest('a');
716
+ expect(logoLink).toBeInTheDocument();
717
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
718
+ });
719
+
720
+ it('renders default logo when logoUrl is not provided and logoHref is not provided', () => {
721
+ renderWithProviders(<Header />);
722
+
723
+ const logo = screen.getByRole('img', { name: 'Logo' });
724
+ expect(logo).toBeInTheDocument();
725
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
726
+
727
+ const logoLink = logo.closest('a');
728
+ expect(logoLink).not.toBeInTheDocument();
729
+ });
730
+
731
+ it('renders default logo with custom alt text', () => {
732
+ renderWithProviders(<Header logoAlt="Custom Alt" />);
733
+
734
+ const logo = screen.getByRole('img', { name: 'Custom Alt' });
735
+ expect(logo).toHaveAttribute('alt', 'Custom Alt');
736
+ });
737
+ });
738
+
739
+ // ContextSelector integration tests
740
+ describe('ContextSelector Integration', () => {
741
+ beforeEach(() => {
742
+ mockContextSelector.mockClear();
743
+ });
744
+
745
+ it('passes showOrganisations prop to ContextSelector', () => {
746
+ renderWithProviders(<Header showContextSelector={true} showOrganisations={false} />);
747
+
748
+ const contextSelector = screen.getByTestId('context-selector');
749
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'false');
750
+
751
+ // Check that mockContextSelector was called with showOrganisations
752
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
753
+ expect(lastCall[0].showOrganisations).toBe(false);
754
+ });
755
+
756
+ it('passes showEvents prop to ContextSelector', () => {
757
+ renderWithProviders(<Header showContextSelector={true} showEvents={false} />);
758
+
759
+ const contextSelector = screen.getByTestId('context-selector');
760
+ expect(contextSelector).toHaveAttribute('data-show-events', 'false');
761
+
762
+ // Check that mockContextSelector was called with showEvents
763
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
764
+ expect(lastCall[0].showEvents).toBe(false);
765
+ });
766
+
767
+ it('uses default values for showOrganisations and showEvents', () => {
768
+ renderWithProviders(<Header showContextSelector={true} />);
769
+
770
+ const contextSelector = screen.getByTestId('context-selector');
771
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'true');
772
+ expect(contextSelector).toHaveAttribute('data-show-events', 'true');
773
+
774
+ // Check that mockContextSelector was called with default values
775
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
776
+ expect(lastCall[0].showOrganisations).toBe(true);
777
+ expect(lastCall[0].showEvents).toBe(true);
778
+ });
779
+
780
+ it('passes onOrganisationSelect callback to ContextSelector', () => {
781
+ const mockSwitchOrganisation = vi.fn().mockResolvedValue(undefined);
782
+ vi.mocked(useOrganisations).mockReturnValue({
783
+ organisations: [],
784
+ selectedOrganisation: null,
785
+ isContextReady: true,
786
+ isLoading: false,
787
+ error: null,
788
+ switchOrganisation: mockSwitchOrganisation,
789
+ refreshOrganisations: vi.fn(),
790
+ userMemberships: []
791
+ });
792
+
793
+ renderWithProviders(<Header showContextSelector={true} />);
794
+
795
+ // Check that mockContextSelector was called with onOrganisationSelect
796
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
797
+ expect(lastCall[0]).toHaveProperty('onOrganisationSelect');
798
+ expect(typeof lastCall[0].onOrganisationSelect).toBe('function');
799
+ });
800
+
801
+ it('passes onEventSelect callback to ContextSelector', () => {
802
+ vi.mocked(useEvents).mockReturnValue({
803
+ events: [],
804
+ selectedEvent: null,
805
+ selectedEventId: null,
806
+ eventLoading: false,
807
+ eventError: null,
808
+ setSelectedEvent: vi.fn(),
809
+ refreshEvents: vi.fn(),
810
+ });
811
+
812
+ renderWithProviders(<Header showContextSelector={true} />);
813
+
814
+ // Check that mockContextSelector was called with onEventSelect
815
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
816
+ expect(lastCall[0]).toHaveProperty('onEventSelect');
817
+ expect(typeof lastCall[0].onEventSelect).toBe('function');
818
+ });
819
+
820
+ it('calls setSelectedEvent(null) before switchOrganisation when organisation is selected', async () => {
821
+ const mockSwitchOrganisation = vi.fn().mockResolvedValue(undefined);
822
+ const mockSetSelectedEvent = vi.fn();
823
+
824
+ vi.mocked(useOrganisations).mockReturnValue({
825
+ organisations: [],
826
+ selectedOrganisation: null,
827
+ isContextReady: true,
828
+ isLoading: false,
829
+ error: null,
830
+ switchOrganisation: mockSwitchOrganisation,
831
+ refreshOrganisations: vi.fn(),
832
+ userMemberships: []
833
+ });
834
+
835
+ vi.mocked(useEvents).mockReturnValue({
836
+ events: [],
837
+ selectedEvent: null,
838
+ selectedEventId: null,
839
+ eventLoading: false,
840
+ eventError: null,
841
+ setSelectedEvent: mockSetSelectedEvent,
842
+ refreshEvents: vi.fn(),
843
+ });
844
+
845
+ renderWithProviders(<Header showContextSelector={true} />);
846
+
847
+ // Get the onOrganisationSelect callback from the mock call
848
+ const contextSelectorCall = mockContextSelector.mock.calls.find(
849
+ call => call[0].onOrganisationSelect
850
+ );
851
+
852
+ if (contextSelectorCall && contextSelectorCall[0].onOrganisationSelect) {
853
+ const onOrganisationSelect = contextSelectorCall[0].onOrganisationSelect;
854
+
855
+ // Call the callback
856
+ await onOrganisationSelect({ id: 'test-org-id', name: 'Test Organisation' });
857
+
858
+ // Verify setSelectedEvent is called first
859
+ expect(mockSetSelectedEvent).toHaveBeenCalledWith(null);
860
+ expect(mockSwitchOrganisation).toHaveBeenCalledWith('test-org-id');
861
+ }
862
+ });
863
+ });
864
+
865
+ // Grid layout tests
866
+ describe('Grid Layout', () => {
867
+ it('applies correct grid layout when authenticated with appName', () => {
868
+ vi.mocked(useUnifiedAuth).mockReturnValue({
869
+ user: mockUser,
870
+ isAuthenticated: true,
871
+ appName: 'TEST',
872
+ session: null,
873
+ authLoading: false,
874
+ authError: null,
875
+ supabase: null,
876
+ });
877
+
878
+ renderWithProviders(<Header />);
879
+
880
+ const nav = screen.getByRole('navigation');
881
+ // Check that the nav element has the grid class (may be in className string)
882
+ const className = nav.className;
883
+ expect(className).toContain('grid-cols-[auto_auto_1fr_auto_auto]');
884
+ });
885
+
886
+ it('applies correct grid layout when not authenticated', () => {
887
+ vi.mocked(useUnifiedAuth).mockReturnValue({
888
+ user: null,
889
+ isAuthenticated: false,
890
+ appName: undefined,
891
+ session: null,
892
+ authLoading: false,
893
+ authError: null,
894
+ supabase: null,
895
+ });
896
+
897
+ renderWithProviders(<Header />);
898
+
899
+ const nav = screen.getByRole('navigation');
900
+ const className = nav.className;
901
+ expect(className).toContain('grid-cols-[auto_1fr_auto_auto]');
902
+ });
903
+
904
+ it('applies correct grid layout when authenticated without appName', () => {
905
+ vi.mocked(useUnifiedAuth).mockReturnValue({
906
+ user: mockUser,
907
+ isAuthenticated: true,
908
+ appName: undefined,
909
+ session: null,
910
+ authLoading: false,
911
+ authError: null,
912
+ supabase: null,
913
+ });
914
+
915
+ renderWithProviders(<Header />);
916
+
917
+ const nav = screen.getByRole('navigation');
918
+ const className = nav.className;
919
+ expect(className).toContain('grid-cols-[auto_1fr_auto_auto]');
920
+ });
921
+ });
922
+
923
+ // Logo navigation tests
924
+ describe('Logo Navigation', () => {
925
+ it('renders logo link with correct href when logoHref is provided', () => {
926
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard" />);
927
+
928
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
929
+ expect(logoLink).toBeInTheDocument();
930
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
931
+ });
932
+
933
+ it('does not navigate when logoHref is not provided', () => {
934
+ renderWithProviders(<Header logoUrl="/test-logo.svg" />);
935
+
936
+ const logo = screen.getByRole('img', { name: 'Logo' });
937
+ const logoLink = logo.closest('a');
938
+ expect(logoLink).not.toBeInTheDocument();
939
+ });
940
+
941
+ it('handles logoHref with default logo', () => {
942
+ renderWithProviders(<Header logoHref="/home" />);
943
+
944
+ const logo = screen.getByRole('img', { name: 'Logo' });
945
+ const logoLink = logo.closest('a');
946
+ expect(logoLink).toBeInTheDocument();
947
+ expect(logoLink).toHaveAttribute('href', '/home');
948
+ });
949
+ });
950
+
951
+ // Additional edge cases
952
+ describe('Additional Edge Cases', () => {
953
+ it('handles className merging correctly', () => {
954
+ renderWithProviders(<Header className="custom-class" />);
955
+
956
+ const header = screen.getByRole('banner');
957
+ expect(header).toHaveClass('custom-class');
958
+ expect(header).toHaveClass('w-full'); // Default classes should still be present
959
+ });
960
+
961
+ it('handles empty navItems array', () => {
962
+ renderWithProviders(<Header navItems={[]} />);
963
+
964
+ expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
965
+ });
966
+
967
+ it('handles logoUrl with empty string', () => {
968
+ renderWithProviders(<Header logoUrl="" />);
969
+
970
+ // Empty string should be treated as falsy, so default logo should render
971
+ const logo = screen.getByRole('img', { name: 'Logo' });
972
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
973
+ });
974
+
975
+ it('handles logoAlt with empty string', () => {
976
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoAlt="" />);
977
+
978
+ const logo = screen.getByRole('img', { name: 'Logo' }); // Falls back to default
979
+ expect(logo).toBeInTheDocument();
980
+ });
981
+
982
+ it('handles all optional props as undefined', () => {
983
+ renderWithProviders(
984
+ <Header
985
+ logoUrl={undefined}
986
+ logoAlt={undefined}
987
+ navItems={undefined}
988
+ user={undefined}
989
+ onSignOut={undefined}
990
+ onChangePassword={undefined}
991
+ actions={undefined}
992
+ userMenu={undefined}
993
+ className={undefined}
994
+ showContextSelector={undefined}
995
+ showOrganisations={undefined}
996
+ showEvents={undefined}
997
+ showUserMenu={undefined}
998
+ currentPath={undefined}
999
+ onNavigate={undefined}
1000
+ logoHref={undefined}
1001
+ />
1002
+ );
1003
+
1004
+ const header = screen.getByRole('banner');
1005
+ expect(header).toBeInTheDocument();
1006
+ });
1007
+
1008
+ it('handles logoHref with special characters', () => {
1009
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard?tab=settings" />);
1010
+
1011
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
1012
+ expect(logoLink).toHaveAttribute('href', '/dashboard?tab=settings');
1013
+ });
1014
+
1015
+ it('handles logoHref with hash', () => {
1016
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard#section" />);
1017
+
1018
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
1019
+ expect(logoLink).toHaveAttribute('href', '/dashboard#section');
1020
+ });
1021
+ });
1022
+
1023
+ // Comprehensive integration scenarios
1024
+ describe('Comprehensive Integration Scenarios', () => {
1025
+ it('renders full header with all features when authenticated', () => {
1026
+ vi.mocked(useUnifiedAuth).mockReturnValue({
1027
+ user: mockUser,
1028
+ isAuthenticated: true,
1029
+ appName: 'TEST',
1030
+ session: null,
1031
+ authLoading: false,
1032
+ authError: null,
1033
+ supabase: null,
1034
+ });
1035
+
1036
+ const customActions = <div data-testid="custom-actions">Actions</div>;
1037
+
1038
+ renderWithProviders(
1039
+ <Header
1040
+ logoUrl="/test-logo.svg"
1041
+ logoAlt="Test Logo"
1042
+ logoHref="/dashboard"
1043
+ navItems={mockNavItems}
1044
+ user={mockUser}
1045
+ actions={customActions}
1046
+ showContextSelector={true}
1047
+ showOrganisations={true}
1048
+ showEvents={true}
1049
+ showUserMenu={true}
1050
+ currentPath="/dashboard"
1051
+ />
1052
+ );
1053
+
1054
+ // AppSwitcher
1055
+ expect(screen.getByTestId('app-switcher')).toBeInTheDocument();
1056
+
1057
+ // Logo
1058
+ expect(screen.getByRole('img', { name: 'Test Logo' })).toBeInTheDocument();
1059
+
1060
+ // Navigation
1061
+ expect(screen.getByTestId('navigation-menu')).toBeInTheDocument();
1062
+
1063
+ // Context Selector
1064
+ expect(screen.getByTestId('context-selector')).toBeInTheDocument();
1065
+
1066
+ // Custom Actions
1067
+ expect(screen.getByTestId('custom-actions')).toBeInTheDocument();
1068
+
1069
+ // User Menu
1070
+ expect(screen.getByTestId('user-menu')).toBeInTheDocument();
1071
+ });
1072
+
1073
+ it('renders minimal header when not authenticated', () => {
1074
+ vi.mocked(useUnifiedAuth).mockReturnValue({
1075
+ user: null,
1076
+ isAuthenticated: false,
1077
+ appName: undefined,
1078
+ session: null,
1079
+ authLoading: false,
1080
+ authError: null,
1081
+ supabase: null,
1082
+ });
1083
+
1084
+ renderWithProviders(
1085
+ <Header
1086
+ logoUrl="/test-logo.svg"
1087
+ showContextSelector={false}
1088
+ showUserMenu={false}
1089
+ />
1090
+ );
1091
+
1092
+ // Should not have AppSwitcher
1093
+ expect(screen.queryByTestId('app-switcher')).not.toBeInTheDocument();
1094
+
1095
+ // Should have logo
1096
+ expect(screen.getByRole('img', { name: 'Logo' })).toBeInTheDocument();
1097
+
1098
+ // Should not have context selector
1099
+ expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
1100
+
1101
+ // Should not have user menu
1102
+ expect(screen.queryByTestId('user-menu')).not.toBeInTheDocument();
1103
+ });
1104
+
1105
+ it('handles context selector with only organisations', () => {
1106
+ renderWithProviders(
1107
+ <Header
1108
+ showContextSelector={true}
1109
+ showOrganisations={true}
1110
+ showEvents={false}
1111
+ />
1112
+ );
1113
+
1114
+ const contextSelector = screen.getByTestId('context-selector');
1115
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'true');
1116
+ expect(contextSelector).toHaveAttribute('data-show-events', 'false');
1117
+ });
1118
+
1119
+ it('handles context selector with only events', () => {
1120
+ renderWithProviders(
1121
+ <Header
1122
+ showContextSelector={true}
1123
+ showOrganisations={false}
1124
+ showEvents={true}
1125
+ />
1126
+ );
1127
+
1128
+ const contextSelector = screen.getByTestId('context-selector');
1129
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'false');
1130
+ expect(contextSelector).toHaveAttribute('data-show-events', 'true');
1131
+ });
1132
+ });
642
1133
  });