@jmruthers/pace-core 0.6.9 → 0.6.11

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 (1182) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/audit-tool/00-dependencies.cjs +46 -13
  3. package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
  4. package/audit-tool/audits/02-project-structure.cjs +74 -2
  5. package/audit-tool/audits/03-architecture.cjs +220 -20
  6. package/audit-tool/audits/04-code-quality.cjs +95 -3
  7. package/audit-tool/audits/05-styling.cjs +19 -7
  8. package/audit-tool/audits/06-security-rbac.cjs +214 -25
  9. package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
  10. package/audit-tool/audits/08-testing-documentation.cjs +11 -3
  11. package/audit-tool/audits/09-operations.cjs +19 -7
  12. package/audit-tool/index.cjs +22 -11
  13. package/audit-tool/utils/report-utils.cjs +4 -0
  14. package/cursor-rules/01-pace-core-compliance.mdc +1 -0
  15. package/cursor-rules/02-project-structure.mdc +3 -26
  16. package/cursor-rules/03-architecture.mdc +3 -1
  17. package/cursor-rules/04-code-quality.mdc +1 -0
  18. package/cursor-rules/05-styling.mdc +120 -8
  19. package/cursor-rules/06-security-rbac.mdc +126 -2
  20. package/cursor-rules/07-api-tech-stack.mdc +1 -0
  21. package/cursor-rules/08-testing-documentation.mdc +1 -0
  22. package/cursor-rules/09-operations.mdc +1 -0
  23. package/dist/DataTable-EFYP2QLE.js +16 -0
  24. package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
  25. package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
  26. package/dist/api-BZR2CYXL.js +5 -0
  27. package/dist/api-result-USV1Czr-.d.ts +51 -0
  28. package/dist/assets/app-icons/admin_favicon.svg +462 -0
  29. package/dist/assets/app-icons/base_favicon.svg +85 -0
  30. package/dist/assets/app-icons/cake_favicon.svg +68 -0
  31. package/dist/assets/app-icons/core_favicon.svg +256 -0
  32. package/dist/assets/app-icons/gear_favicon.svg +91 -0
  33. package/dist/assets/app-icons/medi_favicon.svg +92 -0
  34. package/dist/assets/app-icons/mint_favicon.svg +83 -0
  35. package/dist/assets/app-icons/pace_favicon.svg +49 -0
  36. package/dist/assets/app-icons/pump_favicon.svg +68 -0
  37. package/dist/assets/app-icons/seed_favicon.svg +91 -0
  38. package/dist/assets/app-icons/team_favicon.svg +67 -0
  39. package/dist/assets/app-icons/trac_favicon.svg +112 -0
  40. package/dist/assets/app-icons/trip_favicon.svg +102 -0
  41. package/dist/audit-HI2DHUVU.js +4 -0
  42. package/dist/auth-JvdRVaud.d.ts +49 -0
  43. package/dist/chunk-2DL2WSOE.js +327 -0
  44. package/dist/chunk-2OEVOGGR.js +9598 -0
  45. package/dist/chunk-44CNXN4P.js +15 -0
  46. package/dist/chunk-4R3T5ENU.js +2943 -0
  47. package/dist/chunk-7A6IMHH2.js +2321 -0
  48. package/dist/chunk-BTHN5MKC.js +121 -0
  49. package/dist/chunk-CU2BU2MQ.js +2 -0
  50. package/dist/chunk-D6BMFMQZ.js +200 -0
  51. package/dist/chunk-DDMPHZ3D.js +58 -0
  52. package/dist/chunk-ENLXB7GP.js +721 -0
  53. package/dist/chunk-J2KQK6DG.js +2159 -0
  54. package/dist/chunk-KJXRL3XE.js +6434 -0
  55. package/dist/chunk-L5LFKKLJ.js +61 -0
  56. package/dist/chunk-PCSHBLPB.js +811 -0
  57. package/dist/chunk-QRYSEPHB.js +429 -0
  58. package/dist/chunk-RMLY6KB5.js +187 -0
  59. package/dist/chunk-SACF5YSM.js +31 -0
  60. package/dist/chunk-UZNAFKGW.js +125 -0
  61. package/dist/chunk-V7FTM2LU.js +1080 -0
  62. package/dist/chunk-WY6Y7KC3.js +264 -0
  63. package/dist/chunk-XOJME5T7.js +407 -0
  64. package/dist/chunk-XPFVT3GN.js +492 -0
  65. package/dist/chunk-YFTFFJIV.js +529 -0
  66. package/dist/chunk-YYTWKVHO.js +1334 -0
  67. package/dist/components.d.ts +12 -89
  68. package/dist/components.js +23 -55
  69. package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
  70. package/dist/eslint-rules/index.cjs +3 -0
  71. package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
  72. package/dist/eslint-rules/rules/05-styling.cjs +507 -0
  73. package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
  74. package/dist/event-BfCox3N2.d.ts +265 -0
  75. package/dist/file-reference-DU1hcawx.d.ts +164 -0
  76. package/dist/functions-DH45k8ec.d.ts +208 -0
  77. package/dist/hooks.d.ts +28 -14
  78. package/dist/hooks.js +90 -56
  79. package/dist/icons/index.d.ts +1 -0
  80. package/dist/icons/index.js +1 -0
  81. package/dist/index.d.ts +392 -155
  82. package/dist/index.js +337 -347
  83. package/dist/pagination-BW1mqywp.d.ts +201 -0
  84. package/dist/papaparseLoader-WG2UXQ22.js +7 -0
  85. package/dist/providers.d.ts +29 -14
  86. package/dist/providers.js +7 -5
  87. package/dist/rbac/eslint-rules.js +2 -2
  88. package/dist/rbac/index.d.ts +180 -351
  89. package/dist/rbac/index.js +13 -11
  90. package/dist/theming/runtime.d.ts +28 -5
  91. package/dist/theming/runtime.js +2 -2
  92. package/dist/timezone-BTWWXKVY.d.ts +696 -0
  93. package/dist/types-BE2sEHKd.d.ts +55 -0
  94. package/dist/types-CvOPXWWZ.d.ts +111 -0
  95. package/dist/types-Dr8sNhER.d.ts +50 -0
  96. package/dist/types.d.ts +20 -13
  97. package/dist/types.js +1 -0
  98. package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
  99. package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
  100. package/dist/utils.d.ts +338 -156
  101. package/dist/utils.js +78 -60
  102. package/dist/validation-g5n0hDkh.d.ts +177 -0
  103. package/docs/api/modules.md +1226 -1094
  104. package/docs/api-reference/components.md +5 -5
  105. package/docs/api-reference/rpc-functions.md +12 -3
  106. package/docs/core-concepts/rbac-system.md +8 -0
  107. package/docs/getting-started/cursor-rules.md +17 -20
  108. package/docs/getting-started/dependencies.md +1 -1
  109. package/docs/getting-started/setup.md +235 -0
  110. package/docs/implementation-guides/authentication.md +27 -0
  111. package/docs/implementation-guides/data-tables.md +365 -10
  112. package/docs/migration/ApiResult-migration.md +25 -0
  113. package/docs/rbac/RBAC_CONTRACT.md +0 -12
  114. package/docs/rbac/api-reference.md +33 -31
  115. package/docs/standards/0-standards-overview.md +50 -15
  116. package/docs/standards/1-pace-core-compliance-standards.md +62 -57
  117. package/docs/standards/2-project-structure-standards.md +45 -90
  118. package/docs/standards/3-architecture-standards.md +41 -1
  119. package/docs/standards/4-code-quality-standards.md +26 -6
  120. package/docs/standards/5-styling-standards.md +35 -1
  121. package/docs/standards/6-security-rbac-standards.md +288 -7
  122. package/docs/standards/7-api-tech-stack-standards.md +116 -17
  123. package/docs/standards/8-testing-documentation-standards.md +31 -0
  124. package/docs/standards/9-operations-standards.md +19 -0
  125. package/docs/standards/README.md +20 -201
  126. package/docs/testing/README.md +10 -0
  127. package/docs/testing/test-setup-for-consumers.md +916 -0
  128. package/docs/troubleshooting/common-issues.md +17 -1
  129. package/docs/troubleshooting/organisation-context-setup.md +8 -0
  130. package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
  131. package/eslint-config-pace-core.cjs +24 -0
  132. package/package.json +14 -20
  133. package/scripts/build-docs.js +180 -0
  134. package/scripts/setup.cjs +536 -0
  135. package/scripts/validate.cjs +480 -0
  136. package/src/__mocks__/lucide-react.ts +0 -2
  137. package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
  138. package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
  139. package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
  140. package/src/__tests__/helpers/test-providers.test.tsx +99 -0
  141. package/src/__tests__/helpers/test-providers.tsx +37 -39
  142. package/src/__tests__/helpers/test-utils.test.tsx +447 -0
  143. package/src/__tests__/helpers/timer-utils.test.ts +371 -0
  144. package/src/assets/app-icons/admin_favicon.svg +462 -0
  145. package/src/assets/app-icons/base_favicon.svg +85 -0
  146. package/src/assets/app-icons/cake_favicon.svg +68 -0
  147. package/src/assets/app-icons/core_favicon.svg +256 -0
  148. package/src/assets/app-icons/gear_favicon.svg +91 -0
  149. package/src/assets/app-icons/index.test.ts +304 -0
  150. package/src/assets/app-icons/index.ts +83 -0
  151. package/src/assets/app-icons/medi_favicon.svg +92 -0
  152. package/src/assets/app-icons/mint_favicon.svg +83 -0
  153. package/src/assets/app-icons/pace_favicon.svg +49 -0
  154. package/src/assets/app-icons/pump_favicon.svg +68 -0
  155. package/src/assets/app-icons/seed_favicon.svg +91 -0
  156. package/src/assets/app-icons/team_favicon.svg +67 -0
  157. package/src/assets/app-icons/trac_favicon.svg +112 -0
  158. package/src/assets/app-icons/trip_favicon.svg +102 -0
  159. package/src/components/AddressField/AddressField.test.tsx +379 -4
  160. package/src/components/AddressField/AddressField.tsx +239 -213
  161. package/src/components/AddressField/types.ts +2 -2
  162. package/src/components/Alert/Alert.test.tsx +35 -25
  163. package/src/components/Alert/Alert.tsx +8 -8
  164. package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
  165. package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
  166. package/src/components/Avatar/Avatar.test.tsx +11 -1
  167. package/src/components/Avatar/Avatar.tsx +3 -2
  168. package/src/components/Badge/Badge.test.tsx +11 -1
  169. package/src/components/Button/Button.test.tsx +13 -3
  170. package/src/components/Button/Button.tsx +1 -1
  171. package/src/components/Calendar/Calendar.test.tsx +523 -131
  172. package/src/components/Calendar/Calendar.tsx +107 -488
  173. package/src/components/Card/Card.test.tsx +384 -258
  174. package/src/components/Card/Card.tsx +19 -10
  175. package/src/components/Checkbox/Checkbox.test.tsx +58 -174
  176. package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
  177. package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
  178. package/src/components/ContextSelector/ContextSelector.tsx +66 -280
  179. package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
  180. package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
  181. package/src/components/DataTable/AUDIT_REPORT.md +59 -44
  182. package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
  183. package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
  184. package/src/components/DataTable/DataTable.export.test.tsx +705 -0
  185. package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
  186. package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
  187. package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
  188. package/src/components/DataTable/DataTable.test.tsx +787 -416
  189. package/src/components/DataTable/DataTable.tsx +14 -14
  190. package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
  191. package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
  192. package/src/components/DataTable/DataTableCore.test.tsx +970 -0
  193. package/src/components/DataTable/README.md +155 -0
  194. package/src/components/DataTable/TESTING.md +101 -0
  195. package/src/components/DataTable/a11y.basic.test.tsx +788 -0
  196. package/src/components/DataTable/components/DataTableCore.tsx +126 -894
  197. package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
  198. package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
  199. package/src/components/DataTable/components/ImportModal.tsx +82 -408
  200. package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
  201. package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
  202. package/src/components/DataTable/context/DataTableContext.tsx +13 -13
  203. package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
  204. package/src/components/DataTable/core/ColumnFactory.ts +3 -3
  205. package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
  206. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
  207. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
  208. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
  209. package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
  210. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
  211. package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
  212. package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
  213. package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
  214. package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
  215. package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
  216. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
  217. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
  218. package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
  219. package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
  220. package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
  221. package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
  222. package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
  223. package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
  224. package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
  225. package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
  226. package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
  227. package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
  228. package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
  229. package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
  230. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
  231. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
  232. package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
  233. package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
  234. package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
  235. package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
  236. package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
  237. package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
  238. package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
  239. package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
  240. package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
  241. package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
  242. package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
  243. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
  244. package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
  245. package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
  246. package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
  247. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
  248. package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
  249. package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
  250. package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
  251. package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
  252. package/src/components/DataTable/index.ts +28 -5
  253. package/src/components/DataTable/keyboard.test.tsx +734 -0
  254. package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
  255. package/src/components/DataTable/pagination.modes.test.tsx +728 -0
  256. package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
  257. package/src/components/DataTable/styles.test.ts +379 -0
  258. package/src/components/DataTable/styles.ts +0 -1
  259. package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
  260. package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
  261. package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
  262. package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
  263. package/src/components/DataTable/test-utils.ts +94 -0
  264. package/src/components/DataTable/types/actions.ts +71 -0
  265. package/src/components/DataTable/types/base.ts +39 -0
  266. package/src/components/DataTable/types/columns.ts +125 -0
  267. package/src/components/DataTable/types/export.ts +32 -0
  268. package/src/components/DataTable/types/features.ts +81 -0
  269. package/src/components/DataTable/types/hierarchical.ts +44 -0
  270. package/src/components/DataTable/types/index.ts +43 -0
  271. package/src/components/DataTable/types/pagination.ts +85 -0
  272. package/src/components/DataTable/types/performance.ts +47 -0
  273. package/src/components/DataTable/types/props.ts +62 -0
  274. package/src/components/DataTable/types/rbac.ts +45 -0
  275. package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
  276. package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
  277. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
  278. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
  279. package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
  280. package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
  281. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
  282. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
  283. package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
  284. package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
  285. package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
  286. package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
  287. package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
  288. package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
  289. package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
  290. package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
  291. package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
  292. package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
  293. package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
  294. package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
  295. package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
  296. package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
  297. package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
  298. package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
  299. package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
  300. package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
  301. package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
  302. package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
  303. package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
  304. package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
  305. package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
  306. package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
  307. package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
  308. package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
  309. package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
  310. package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
  311. package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
  312. package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
  313. package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
  314. package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
  315. package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
  316. package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
  317. package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
  318. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
  319. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
  320. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
  321. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
  322. package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
  323. package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
  324. package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
  325. package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
  326. package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
  327. package/src/components/DataTable/utils/a11yUtils.ts +1 -1
  328. package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
  329. package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
  330. package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
  331. package/src/components/DataTable/utils/csvParse.test.ts +74 -0
  332. package/src/components/DataTable/utils/csvParse.ts +65 -0
  333. package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
  334. package/src/components/DataTable/utils/errorHandling.ts +3 -1
  335. package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
  336. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  337. package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
  338. package/src/components/DataTable/utils/flexibleImport.ts +3 -186
  339. package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
  340. package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
  341. package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
  342. package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
  343. package/src/components/DataTable/utils/importDateParser.ts +114 -0
  344. package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
  345. package/src/components/DataTable/utils/importValueParser.ts +91 -0
  346. package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
  347. package/src/components/DataTable/utils/paginationUtils.ts +7 -4
  348. package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
  349. package/src/components/DataTable/utils/performanceUtils.ts +1 -1
  350. package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
  351. package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
  352. package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
  353. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
  354. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
  355. package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
  356. package/src/components/DateTimeField/DateTimeField.tsx +1 -1
  357. package/src/components/Dialog/Dialog.test-utils.ts +49 -0
  358. package/src/components/Dialog/Dialog.test.tsx +2865 -458
  359. package/src/components/Dialog/Dialog.tsx +183 -986
  360. package/src/components/Dialog/dialogLock.test.ts +238 -0
  361. package/src/components/Dialog/dialogLock.ts +98 -0
  362. package/src/components/Dialog/index.ts +2 -0
  363. package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
  364. package/src/components/Dialog/useDialogDimensions.ts +140 -0
  365. package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
  366. package/src/components/Dialog/useDialogLifecycle.ts +135 -0
  367. package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
  368. package/src/components/Dialog/useDialogPersistence.ts +357 -0
  369. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
  370. package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
  371. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
  372. package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
  373. package/src/components/ErrorBoundary/index.ts +3 -4
  374. package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
  375. package/src/components/FileDisplay/FileDisplay.test.tsx +479 -247
  376. package/src/components/FileDisplay/FileDisplay.tsx +29 -659
  377. package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
  378. package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
  379. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
  380. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
  381. package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
  382. package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
  383. package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
  384. package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
  385. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
  386. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
  387. package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
  388. package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
  389. package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
  390. package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
  391. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
  392. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
  393. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
  394. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
  395. package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
  396. package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
  397. package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
  398. package/src/components/FileDisplay/fallbackUtils.ts +44 -0
  399. package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
  400. package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
  401. package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
  402. package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
  403. package/src/components/FileDisplay/index.tsx +1 -1
  404. package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
  405. package/src/components/FileDisplay/useFileDisplay.ts +515 -0
  406. package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
  407. package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
  408. package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
  409. package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
  410. package/src/components/FileUpload/FileUpload.test.tsx +69 -27
  411. package/src/components/FileUpload/FileUpload.tsx +112 -527
  412. package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
  413. package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
  414. package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
  415. package/src/components/FileUpload/index.tsx +1 -1
  416. package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
  417. package/src/components/FileUpload/useFileUploadManager.ts +454 -0
  418. package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
  419. package/src/components/FileUpload/useResolvedAppId.ts +77 -0
  420. package/src/components/Footer/Footer.test.tsx +15 -382
  421. package/src/components/Footer/Footer.tsx +8 -125
  422. package/src/components/Form/Form.test.tsx +425 -88
  423. package/src/components/Form/Form.tsx +91 -299
  424. package/src/components/Form/useFormPersistence.ts +257 -0
  425. package/src/components/Header/Header.test.tsx +653 -163
  426. package/src/components/Header/Header.tsx +62 -44
  427. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
  428. package/src/components/Input/Input.test.tsx +34 -120
  429. package/src/components/Input/Input.tsx +1 -1
  430. package/src/components/Label/Label.test.tsx +46 -45
  431. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
  432. package/src/components/LoginForm/LoginForm.test.tsx +0 -1
  433. package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
  434. package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
  435. package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
  436. package/src/components/NavigationMenu/index.ts +6 -1
  437. package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
  438. package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
  439. package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
  440. package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
  441. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
  442. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
  443. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
  444. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
  445. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
  446. package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
  447. package/src/components/PaceAppLayout/README.md +0 -9
  448. package/src/components/PaceAppLayout/test-setup.tsx +15 -9
  449. package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
  450. package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
  451. package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
  452. package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
  453. package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
  454. package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
  455. package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
  456. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
  457. package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
  458. package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
  459. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
  460. package/src/components/Progress/Progress.test.tsx +127 -1
  461. package/src/components/Progress/Progress.tsx +1 -2
  462. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
  463. package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
  464. package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
  465. package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
  466. package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
  467. package/src/components/PublicLayout/PublicPageContext.ts +28 -0
  468. package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
  469. package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
  470. package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
  471. package/src/components/Select/Select.test.tsx +45 -8
  472. package/src/components/Select/Select.tsx +57 -40
  473. package/src/components/Select/context.test.tsx +56 -0
  474. package/src/components/Select/text.test.tsx +104 -0
  475. package/src/components/Select/text.ts +26 -0
  476. package/src/components/Select/types.ts +3 -0
  477. package/src/components/Select/useSelectEvents.test.ts +279 -0
  478. package/src/components/Select/useSelectEvents.ts +87 -0
  479. package/src/components/Select/useSelectSearch.test.tsx +295 -0
  480. package/src/components/Select/useSelectSearch.ts +91 -0
  481. package/src/components/Select/useSelectState.test.ts +268 -0
  482. package/src/components/Select/useSelectState.ts +104 -0
  483. package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
  484. package/src/components/Switch/Switch.test.tsx +57 -153
  485. package/src/components/Table/Table.test.tsx +395 -317
  486. package/src/components/Tabs/Tabs.test.tsx +270 -0
  487. package/src/components/Tabs/Tabs.tsx +4 -4
  488. package/src/components/Textarea/Textarea.test.tsx +11 -38
  489. package/src/components/Toast/Toast.test.tsx +425 -496
  490. package/src/components/Tooltip/Tooltip.test.tsx +4 -21
  491. package/src/components/UserMenu/UserMenu.test.tsx +1 -21
  492. package/src/components/UserMenu/UserMenu.tsx +0 -1
  493. package/src/components/index.test.ts +346 -0
  494. package/src/components/index.ts +12 -1
  495. package/src/constants/performance.test.ts +91 -0
  496. package/src/hooks/ServiceHooks.test.tsx +725 -0
  497. package/src/hooks/hooks.integration.test.tsx +608 -0
  498. package/src/hooks/index.ts +18 -3
  499. package/src/hooks/index.unit.test.ts +220 -0
  500. package/src/hooks/public/usePublicEvent.test.ts +304 -0
  501. package/src/hooks/public/usePublicEvent.ts +11 -11
  502. package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
  503. package/src/hooks/public/usePublicEventLogo.ts +2 -2
  504. package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
  505. package/src/hooks/public/usePublicRouteParams.ts +2 -2
  506. package/src/hooks/services/useAuth.ts +9 -7
  507. package/src/hooks/services/useAuthService.ts +1 -1
  508. package/src/hooks/services/useEventService.ts +1 -1
  509. package/src/hooks/useAccessibleApps.test.ts +400 -0
  510. package/src/hooks/useAccessibleApps.ts +264 -0
  511. package/src/hooks/useAddressAutocomplete.test.ts +170 -47
  512. package/src/hooks/useAddressAutocomplete.ts +109 -81
  513. package/src/hooks/useApiFetch.unit.test.ts +111 -0
  514. package/src/hooks/useAppConfig.ts +13 -3
  515. package/src/hooks/useAppConfig.unit.test.ts +712 -0
  516. package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
  517. package/src/hooks/useDataTablePerformance.ts +111 -130
  518. package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
  519. package/src/hooks/useDataTableState.test.ts +170 -0
  520. package/src/hooks/useDataTableState.ts +5 -5
  521. package/src/hooks/useDebounce.unit.test.ts +157 -0
  522. package/src/hooks/useEventTheme.test.ts +70 -18
  523. package/src/hooks/useEventTheme.ts +50 -22
  524. package/src/hooks/useEvents.ts +49 -2
  525. package/src/hooks/useEvents.unit.test.ts +227 -0
  526. package/src/hooks/useFileReference.test.ts +388 -107
  527. package/src/hooks/useFileReference.ts +184 -179
  528. package/src/hooks/useFileUrl.ts +1 -1
  529. package/src/hooks/useFileUrl.unit.test.ts +686 -0
  530. package/src/hooks/useFileUrlCache.test.ts +319 -0
  531. package/src/hooks/useFileUrlCache.ts +5 -2
  532. package/src/hooks/useFocusManagement.unit.test.ts +604 -0
  533. package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
  534. package/src/hooks/useFormDialog.test.ts +307 -0
  535. package/src/hooks/useFormDialog.ts +2 -2
  536. package/src/hooks/useInactivityTracker.ts +141 -134
  537. package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
  538. package/src/hooks/useIsMobile.unit.test.ts +317 -0
  539. package/src/hooks/useIsPrint.ts +62 -0
  540. package/src/hooks/useIsPrint.unit.test.ts +545 -0
  541. package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
  542. package/src/hooks/useOrganisationPermissions.test.ts +1 -2
  543. package/src/hooks/useOrganisationPermissions.ts +1 -4
  544. package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
  545. package/src/hooks/useOrganisationSecurity.test.ts +4 -33
  546. package/src/hooks/useOrganisationSecurity.ts +192 -203
  547. package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
  548. package/src/hooks/useOrganisations.ts +1 -1
  549. package/src/hooks/useOrganisations.unit.test.ts +369 -0
  550. package/src/hooks/usePerformanceMonitor.ts +1 -1
  551. package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
  552. package/src/hooks/usePermissionCache.test.ts +298 -329
  553. package/src/hooks/usePermissionCache.ts +277 -276
  554. package/src/hooks/usePreventTabReload.test.ts +307 -0
  555. package/src/hooks/usePublicEvent.simple.test.ts +794 -0
  556. package/src/hooks/usePublicEvent.test.ts +670 -0
  557. package/src/hooks/usePublicEvent.unit.test.ts +638 -0
  558. package/src/hooks/usePublicFileDisplay.test.ts +948 -0
  559. package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
  560. package/src/hooks/useQueryCache.test.ts +391 -0
  561. package/src/hooks/useQueryCache.ts +7 -9
  562. package/src/hooks/useRBAC.unit.test.ts +253 -0
  563. package/src/hooks/useSessionDraft.test.ts +556 -0
  564. package/src/hooks/useSessionDraft.ts +14 -11
  565. package/src/hooks/useSessionRestoration.ts +1 -1
  566. package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
  567. package/src/hooks/useStorage.ts +94 -54
  568. package/src/hooks/useStorage.unit.test.ts +684 -0
  569. package/src/hooks/useToast.test.ts +413 -0
  570. package/src/hooks/useToast.ts +2 -2
  571. package/src/hooks/useToast.unit.test.tsx +481 -0
  572. package/src/hooks/useZodForm.ts +3 -3
  573. package/src/hooks/useZodForm.unit.test.tsx +191 -0
  574. package/src/icons/index.test.ts +133 -0
  575. package/src/icons/index.ts +3 -1
  576. package/src/index.test.ts +528 -0
  577. package/src/index.ts +56 -9
  578. package/src/providers/AuthProvider.test.tsx +218 -0
  579. package/src/providers/EventProvider.test.tsx +487 -0
  580. package/src/providers/InactivityProvider.test-helper.tsx +40 -0
  581. package/src/providers/InactivityProvider.test.tsx +421 -0
  582. package/src/providers/ProviderLifecycle.test.tsx +308 -0
  583. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
  584. package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
  585. package/src/providers/index.test.ts +138 -0
  586. package/src/providers/services/AuthServiceContext.ts +27 -0
  587. package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
  588. package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
  589. package/src/providers/services/AuthServiceProvider.tsx +81 -20
  590. package/src/providers/services/EventServiceContext.ts +25 -0
  591. package/src/providers/services/EventServiceProvider.test.tsx +839 -0
  592. package/src/providers/services/EventServiceProvider.tsx +11 -20
  593. package/src/providers/services/InactivityServiceContext.ts +25 -0
  594. package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
  595. package/src/providers/services/InactivityServiceProvider.tsx +7 -17
  596. package/src/providers/services/OrganisationServiceContext.ts +25 -0
  597. package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
  598. package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
  599. package/src/providers/services/UnifiedAuthContext.ts +102 -0
  600. package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
  601. package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
  602. package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
  603. package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
  604. package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
  605. package/src/providers/services/contexts.test.tsx +281 -0
  606. package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
  607. package/src/providers/services/useUnifiedAuth.ts +29 -0
  608. package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
  609. package/src/providers/useInactivity.test-helper.ts +27 -0
  610. package/src/rbac/README.md +5 -5
  611. package/src/rbac/adapters.comprehensive.test.tsx +429 -0
  612. package/src/rbac/adapters.test.tsx +654 -0
  613. package/src/rbac/adapters.tsx +53 -38
  614. package/src/rbac/api.test.ts +986 -259
  615. package/src/rbac/api.ts +260 -216
  616. package/src/rbac/audit-batched.test.ts +550 -0
  617. package/src/rbac/audit-batched.ts +5 -4
  618. package/src/rbac/audit.test.ts +225 -28
  619. package/src/rbac/audit.ts +26 -18
  620. package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
  621. package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
  622. package/src/rbac/cache-invalidation.test.ts +715 -0
  623. package/src/rbac/cache-invalidation.ts +18 -15
  624. package/src/rbac/cache.test.ts +123 -63
  625. package/src/rbac/cache.ts +3 -4
  626. package/src/rbac/components/AccessDenied.test.tsx +324 -0
  627. package/src/rbac/components/AccessDenied.tsx +20 -18
  628. package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
  629. package/src/rbac/components/NavigationGuard.tsx +10 -8
  630. package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
  631. package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
  632. package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
  633. package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
  634. package/src/rbac/components/PagePermissionGuard.tsx +188 -381
  635. package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
  636. package/src/rbac/config.test.ts +131 -48
  637. package/src/rbac/config.ts +69 -26
  638. package/src/rbac/docs/event-based-apps.md +26 -13
  639. package/src/rbac/engine.comprehensive.test.ts +808 -0
  640. package/src/rbac/engine.test.ts +974 -130
  641. package/src/rbac/engine.ts +53 -13
  642. package/src/rbac/errors.test.ts +99 -87
  643. package/src/rbac/errors.ts +89 -55
  644. package/src/rbac/eslint-rules.js +2 -2
  645. package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
  646. package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
  647. package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
  648. package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
  649. package/src/rbac/hooks/permissions/useCan.ts +173 -253
  650. package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
  651. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
  652. package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
  653. package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
  654. package/src/rbac/hooks/useCan.test.ts +348 -32
  655. package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
  656. package/src/rbac/hooks/usePageGuardScope.ts +117 -0
  657. package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
  658. package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
  659. package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
  660. package/src/rbac/hooks/usePermissions.test.ts +459 -33
  661. package/src/rbac/hooks/usePermissions.ts +5 -7
  662. package/src/rbac/hooks/useRBAC.test.ts +1784 -21
  663. package/src/rbac/hooks/useRBAC.ts +148 -88
  664. package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
  665. package/src/rbac/hooks/useResolvedScope.ts +4 -1
  666. package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
  667. package/src/rbac/hooks/useResourcePermissions.ts +76 -140
  668. package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
  669. package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
  670. package/src/rbac/hooks/useRoleManagement.ts +158 -586
  671. package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
  672. package/src/rbac/hooks/useSecureSupabase.ts +21 -14
  673. package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
  674. package/src/rbac/index.test.ts +107 -0
  675. package/src/rbac/index.ts +32 -32
  676. package/src/rbac/performance.test.ts +451 -0
  677. package/src/rbac/permissions.test.ts +149 -68
  678. package/src/rbac/permissions.ts +0 -3
  679. package/src/rbac/rbac-core.test.tsx +276 -0
  680. package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
  681. package/src/rbac/rbac-engine-simplified.test.ts +252 -0
  682. package/src/rbac/rbac-functions.test.ts +703 -0
  683. package/src/rbac/rbac-integration.test.ts +523 -0
  684. package/src/rbac/rbac-role-isolation.test.ts +456 -0
  685. package/src/rbac/request-deduplication.test.ts +352 -0
  686. package/src/rbac/request-deduplication.ts +5 -4
  687. package/src/rbac/scenarios.user-role.test.tsx +271 -0
  688. package/src/rbac/secureClient.test.ts +499 -115
  689. package/src/rbac/secureClient.ts +54 -28
  690. package/src/rbac/security.test.ts +448 -44
  691. package/src/rbac/security.ts +7 -6
  692. package/src/rbac/types/roleManagement.ts +66 -0
  693. package/src/rbac/types.test.ts +236 -0
  694. package/src/rbac/types.ts +7 -5
  695. package/src/rbac/utils/clientSecurity.test.ts +192 -0
  696. package/src/rbac/utils/clientSecurity.ts +6 -4
  697. package/src/rbac/utils/contextValidator.test.ts +126 -0
  698. package/src/rbac/utils/contextValidator.ts +6 -3
  699. package/src/rbac/utils/deep-equal.test.ts +76 -0
  700. package/src/rbac/utils/eventContext.test.ts +401 -0
  701. package/src/rbac/utils/eventContext.ts +38 -34
  702. package/src/rbac/utils/fetchPermissionMap.ts +13 -0
  703. package/src/rbac/utils/permissionMapHelpers.ts +34 -0
  704. package/src/rbac/utils/roleManagementRpc.ts +303 -0
  705. package/src/services/AuthService.edge-cases.test.ts +746 -0
  706. package/src/services/AuthService.restoreSession.test.ts +59 -0
  707. package/src/services/AuthService.test.ts +1362 -0
  708. package/src/services/AuthService.ts +197 -216
  709. package/src/services/BaseService.edge-cases.test.ts +506 -0
  710. package/src/services/BaseService.test.ts +363 -0
  711. package/src/services/EventService.edge-cases.test.ts +636 -0
  712. package/src/services/EventService.eventColours.test.ts +64 -0
  713. package/src/services/EventService.test.ts +1250 -0
  714. package/src/services/EventService.ts +244 -315
  715. package/src/services/InactivityService.edge-cases.test.ts +492 -0
  716. package/src/services/InactivityService.lifecycle.test.ts +406 -0
  717. package/src/services/InactivityService.test.ts +829 -0
  718. package/src/services/InactivityService.ts +172 -213
  719. package/src/services/OrganisationService.edge-cases.test.ts +633 -0
  720. package/src/services/OrganisationService.pagination.test.ts +409 -0
  721. package/src/services/OrganisationService.test.ts +1579 -0
  722. package/src/services/OrganisationService.ts +186 -257
  723. package/src/services/base/BaseService.test.ts +214 -0
  724. package/src/services/interfaces/IAuthService.test.ts +184 -0
  725. package/src/services/interfaces/IAuthService.ts +10 -9
  726. package/src/services/interfaces/IEventService.test.ts +176 -0
  727. package/src/services/interfaces/IInactivityService.test.ts +183 -0
  728. package/src/services/interfaces/IOrganisationService.test.ts +207 -0
  729. package/src/services/interfaces/IOrganisationService.ts +0 -1
  730. package/src/styles/core.css +244 -12
  731. package/src/theming/parseEventColours.test.ts +321 -0
  732. package/src/theming/parseEventColours.ts +18 -9
  733. package/src/theming/runtime.test.ts +495 -0
  734. package/src/theming/runtime.ts +72 -7
  735. package/src/types/api-result.ts +53 -0
  736. package/src/types/auth.ts +0 -1
  737. package/src/types/core.test.ts +397 -0
  738. package/src/types/database-generated.test.ts +78 -0
  739. package/src/types/database.generated.ts +45 -10
  740. package/src/types/event.ts +39 -19
  741. package/src/types/file-reference.test.ts +351 -0
  742. package/src/types/file-reference.ts +37 -12
  743. package/src/types/guards.test.ts +246 -0
  744. package/src/types/index.test.ts +265 -0
  745. package/src/types/index.ts +3 -0
  746. package/src/types/organisation.roles.test.ts +55 -0
  747. package/src/types/organisation.test.ts +1105 -0
  748. package/src/types/organisation.ts +15 -15
  749. package/src/types/rpc-responses.ts +33 -0
  750. package/src/types/supabase.ts +14 -6
  751. package/src/types/theme.test.ts +830 -0
  752. package/src/types/type-validation.test.ts +526 -0
  753. package/src/types/validation.test.ts +729 -0
  754. package/src/types/vitest-globals.d.ts +1 -1
  755. package/src/utils/app/appConfig.test.ts +235 -0
  756. package/src/utils/app/appIdResolver.test.ts +252 -57
  757. package/src/utils/app/appIdResolver.ts +31 -20
  758. package/src/utils/app/appNameResolver.test.ts +18 -10
  759. package/src/utils/app/appNameResolver.ts +11 -9
  760. package/src/utils/app/appPortMap.test.ts +125 -0
  761. package/src/utils/app/appPortMap.ts +51 -0
  762. package/src/utils/app/buildAppUrl.test.ts +273 -0
  763. package/src/utils/app/buildAppUrl.ts +114 -0
  764. package/src/utils/appConfig.unit.test.ts +55 -0
  765. package/src/utils/audit/audit.test.ts +354 -39
  766. package/src/utils/audit.unit.test.ts +69 -0
  767. package/src/utils/auth-utils.unit.test.ts +69 -0
  768. package/src/utils/bundleAnalysis.unit.test.ts +326 -0
  769. package/src/utils/cn.unit.test.ts +34 -0
  770. package/src/utils/context/organisationContext.test.ts +115 -95
  771. package/src/utils/context/organisationContext.ts +32 -43
  772. package/src/utils/context/sessionTracking.test.ts +354 -0
  773. package/src/utils/core/cn.test.ts +66 -0
  774. package/src/utils/core/debugLogger.test.ts +113 -0
  775. package/src/utils/core/debugLogger.ts +15 -8
  776. package/src/utils/core/logger.test.ts +217 -0
  777. package/src/utils/core/logger.ts +20 -16
  778. package/src/utils/core/mergeRefs.ts +24 -0
  779. package/src/utils/debugLogger.test.ts +417 -0
  780. package/src/utils/device/deviceFingerprint.test.ts +8 -5
  781. package/src/utils/device/deviceFingerprint.ts +3 -3
  782. package/src/utils/deviceFingerprint.unit.test.ts +818 -0
  783. package/src/utils/dynamic/createLazyComponent.tsx +46 -0
  784. package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
  785. package/src/utils/dynamic/dynamicUtils.ts +6 -6
  786. package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
  787. package/src/utils/dynamic/lazyLoad.tsx +8 -36
  788. package/src/utils/dynamic/papaparseLoader.ts +7 -0
  789. package/src/utils/dynamicUtils.unit.test.ts +331 -0
  790. package/src/utils/file-reference/file-reference.test.ts +1238 -0
  791. package/src/utils/file-reference/index.ts +330 -348
  792. package/src/utils/formatDate.unit.test.ts +109 -0
  793. package/src/utils/formatting/formatDate.test.ts +22 -148
  794. package/src/utils/formatting/formatDateTime.test.ts +41 -119
  795. package/src/utils/formatting/formatDateTimeTimezone.test.ts +41 -85
  796. package/src/utils/formatting/formatNumber.test.ts +259 -0
  797. package/src/utils/formatting/formatTime.test.ts +36 -128
  798. package/src/utils/formatting/formatting.ts +1 -1
  799. package/src/utils/formatting.unit.test.ts +99 -0
  800. package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
  801. package/src/utils/google-places/googlePlacesUtils.ts +67 -86
  802. package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
  803. package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
  804. package/src/utils/index.ts +52 -11
  805. package/src/utils/index.unit.test.ts +251 -0
  806. package/src/utils/lazyLoad.unit.test.tsx +319 -0
  807. package/src/utils/location/location.test.ts +19 -116
  808. package/src/utils/logger.unit.test.ts +398 -0
  809. package/src/utils/organisationContext.unit.test.ts +180 -0
  810. package/src/utils/performance/bundleAnalysis.test.ts +148 -0
  811. package/src/utils/performance/bundleAnalysis.ts +16 -22
  812. package/src/utils/performance/performanceBenchmark.test.ts +251 -0
  813. package/src/utils/performance/performanceBenchmark.ts +12 -4
  814. package/src/utils/performance/performanceBudgets.test.ts +241 -0
  815. package/src/utils/performance/performanceBudgets.ts +9 -6
  816. package/src/utils/performanceBenchmark.test.ts +174 -0
  817. package/src/utils/performanceBudgets.unit.test.ts +288 -0
  818. package/src/utils/permissionTypes.unit.test.ts +250 -0
  819. package/src/utils/permissionUtils.unit.test.ts +362 -0
  820. package/src/utils/permissions/permissionTypes.test.ts +149 -0
  821. package/src/utils/permissions/permissionUtils.test.ts +20 -42
  822. package/src/utils/persistence/keyDerivation.test.ts +306 -0
  823. package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
  824. package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
  825. package/src/utils/request-deduplication.test.ts +349 -0
  826. package/src/utils/request-deduplication.ts +6 -4
  827. package/src/utils/sanitization.unit.test.ts +346 -0
  828. package/src/utils/schemaUtils.unit.test.ts +441 -0
  829. package/src/utils/secureDataAccess.unit.test.ts +334 -0
  830. package/src/utils/secureErrors.unit.test.ts +390 -0
  831. package/src/utils/secureStorage.unit.test.ts +289 -0
  832. package/src/utils/security/auth-utils.ts +38 -27
  833. package/src/utils/security/secureDataAccess.test.ts +22 -191
  834. package/src/utils/security/secureDataAccess.ts +241 -281
  835. package/src/utils/security/secureErrors.test.ts +163 -0
  836. package/src/utils/security/secureStorage.test.ts +156 -0
  837. package/src/utils/security/secureStorage.ts +1 -1
  838. package/src/utils/security/security.test.ts +212 -0
  839. package/src/utils/security/security.ts +15 -18
  840. package/src/utils/security/securityMonitor.test.ts +90 -0
  841. package/src/utils/security/securityMonitor.ts +1 -1
  842. package/src/utils/security.unit.test.ts +155 -0
  843. package/src/utils/securityMonitor.unit.test.ts +276 -0
  844. package/src/utils/sessionTracking.unit.test.ts +218 -0
  845. package/src/utils/storage/config.unit.test.ts +239 -0
  846. package/src/utils/storage/helpers.test.ts +769 -456
  847. package/src/utils/storage/helpers.ts +174 -253
  848. package/src/utils/storage/index.unit.test.ts +68 -0
  849. package/src/utils/storage/storageUtils.ts +32 -0
  850. package/src/utils/storage/types.ts +9 -2
  851. package/src/utils/supabase/createBaseClient.test.ts +201 -0
  852. package/src/utils/supabase/createBaseClient.ts +2 -1
  853. package/src/utils/timezone/timezone.test.ts +26 -44
  854. package/src/utils/timezone.test.ts +345 -0
  855. package/src/utils/validation/common.test.ts +115 -0
  856. package/src/utils/validation/csrf.test.ts +198 -0
  857. package/src/utils/validation/csrf.ts +42 -41
  858. package/src/utils/validation/htmlSanitization.ts +27 -31
  859. package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
  860. package/src/utils/validation/passwordSchema.test.ts +164 -0
  861. package/src/utils/validation/schema.test.ts +127 -0
  862. package/src/utils/validation/schema.ts +6 -3
  863. package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
  864. package/src/utils/validation/sqlInjectionProtection.ts +2 -2
  865. package/src/utils/validation/user.test.ts +173 -0
  866. package/src/utils/validation/validation.test.ts +197 -0
  867. package/src/utils/validation/validationUtils.test.ts +294 -0
  868. package/src/utils/validation.unit.test.ts +307 -0
  869. package/src/utils/validationUtils.unit.test.ts +558 -0
  870. package/src/vite-env.d.ts +6 -0
  871. package/dist/AuthService-DmfO5rGS.d.ts +0 -524
  872. package/dist/DataTable-DRUIgtUH.d.ts +0 -166
  873. package/dist/DataTable-SOAFXIWY.js +0 -15
  874. package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
  875. package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
  876. package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
  877. package/dist/api-7P7DI652.js +0 -4
  878. package/dist/audit-MYQXYZFU.js +0 -3
  879. package/dist/auth-BZOJqrdd.d.ts +0 -49
  880. package/dist/chunk-4DDCYDQ3.js +0 -544
  881. package/dist/chunk-5HNSDQWH.js +0 -5046
  882. package/dist/chunk-5W2A3DRC.js +0 -164
  883. package/dist/chunk-6GLLNA6U.js +0 -31
  884. package/dist/chunk-7ILTDCL2.js +0 -80
  885. package/dist/chunk-A3W6LW53.js +0 -70
  886. package/dist/chunk-AHU7G2R5.js +0 -423
  887. package/dist/chunk-C7ZQ5O4C.js +0 -481
  888. package/dist/chunk-EF2UGZWY.js +0 -611
  889. package/dist/chunk-FEJLJNWA.js +0 -181
  890. package/dist/chunk-FYHN4DD5.js +0 -415
  891. package/dist/chunk-GS5672WG.js +0 -2003
  892. package/dist/chunk-HF6O3O37.js +0 -187
  893. package/dist/chunk-J2U36LHD.js +0 -8517
  894. package/dist/chunk-LX6U42O3.js +0 -2177
  895. package/dist/chunk-MPBLMWVR.js +0 -2161
  896. package/dist/chunk-OJ4SKRSV.js +0 -105
  897. package/dist/chunk-S6ZQKDY6.js +0 -62
  898. package/dist/chunk-S7DKJPLT.js +0 -699
  899. package/dist/chunk-T5CVK4R3.js +0 -2816
  900. package/dist/chunk-TTRFSOKR.js +0 -121
  901. package/dist/chunk-Z2FNRKF3.js +0 -994
  902. package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
  903. package/dist/event-CW5YB_2p.d.ts +0 -239
  904. package/dist/file-reference-BavO2eQj.d.ts +0 -148
  905. package/dist/functions-lBy5L2ry.d.ts +0 -208
  906. package/dist/timezone-0AyangqX.d.ts +0 -697
  907. package/dist/types-BeoeWV5I.d.ts +0 -110
  908. package/dist/types-DXstZpNI.d.ts +0 -614
  909. package/dist/types-t9H8qKRw.d.ts +0 -55
  910. package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
  911. package/dist/useToast-AyaT-x7p.d.ts +0 -68
  912. package/dist/validation-643vUDZW.d.ts +0 -177
  913. package/scripts/build-docs-incremental.js +0 -179
  914. package/scripts/eslint-audit.cjs +0 -123
  915. package/scripts/generate-docs.js +0 -157
  916. package/scripts/install-cursor-rules.cjs +0 -255
  917. package/scripts/install-eslint-config.cjs +0 -349
  918. package/scripts/setup-build-cache.js +0 -73
  919. package/scripts/validate-pre-publish.js +0 -145
  920. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
  921. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
  922. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
  923. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
  924. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
  925. package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
  926. package/src/__tests__/hooks/usePermissions.test.ts +0 -268
  927. package/src/__tests__/integration/UserProfile.test.tsx +0 -124
  928. package/src/__tests__/public-recipe-view.test.ts +0 -228
  929. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
  930. package/src/__tests__/rls-policies.test.ts +0 -471
  931. package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
  932. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
  933. package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
  934. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
  935. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
  936. package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
  937. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
  938. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
  939. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
  940. package/src/components/DataTable/__tests__/README.md +0 -145
  941. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
  942. package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
  943. package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
  944. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
  945. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
  946. package/src/components/DataTable/__tests__/styles.test.ts +0 -382
  947. package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
  948. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
  949. package/src/components/DataTable/__tests__/test-utils.ts +0 -94
  950. package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
  951. package/src/components/DataTable/components/ActionButtons.tsx +0 -190
  952. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
  953. package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
  954. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
  955. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
  956. package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
  957. package/src/components/DataTable/components/DataTableModals.tsx +0 -245
  958. package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
  959. package/src/components/DataTable/components/EditFields.tsx +0 -327
  960. package/src/components/DataTable/components/EditableRow.tsx +0 -462
  961. package/src/components/DataTable/components/EmptyState.tsx +0 -79
  962. package/src/components/DataTable/components/FilterRow.tsx +0 -141
  963. package/src/components/DataTable/components/LoadingState.tsx +0 -17
  964. package/src/components/DataTable/components/PaginationControls.tsx +0 -289
  965. package/src/components/DataTable/components/RowComponent.tsx +0 -403
  966. package/src/components/DataTable/components/SortIndicator.tsx +0 -50
  967. package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
  968. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
  969. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
  970. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
  971. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
  972. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
  973. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
  974. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
  975. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
  976. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
  977. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
  978. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
  979. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
  980. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
  981. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
  982. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
  983. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
  984. package/src/components/DataTable/components/cellValueUtils.ts +0 -40
  985. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
  986. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
  987. package/src/components/DataTable/components/index.ts +0 -16
  988. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
  989. package/src/components/DataTable/core/ActionManager.ts +0 -235
  990. package/src/components/DataTable/core/ColumnManager.ts +0 -205
  991. package/src/components/DataTable/core/DataManager.ts +0 -188
  992. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
  993. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  994. package/src/components/DataTable/core/StateManager.ts +0 -312
  995. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
  996. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
  997. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
  998. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
  999. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
  1000. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
  1001. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
  1002. package/src/components/DataTable/core/index.ts +0 -1
  1003. package/src/components/DataTable/core/interfaces.ts +0 -338
  1004. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
  1005. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
  1006. package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
  1007. package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
  1008. package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
  1009. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
  1010. package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
  1011. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
  1012. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
  1013. package/src/components/DataTable/hooks/index.ts +0 -13
  1014. package/src/components/DataTable/types.ts +0 -761
  1015. package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
  1016. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
  1017. package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
  1018. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
  1019. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
  1020. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
  1021. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
  1022. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
  1023. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
  1024. package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
  1025. package/src/components/DataTable/utils/index.ts +0 -10
  1026. package/src/components/PublicLayout/index.ts +0 -32
  1027. package/src/components/Select/hooks/useSelectEvents.ts +0 -87
  1028. package/src/components/Select/hooks/useSelectSearch.ts +0 -91
  1029. package/src/components/Select/hooks/useSelectState.ts +0 -104
  1030. package/src/components/Select/utils/text.ts +0 -26
  1031. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
  1032. package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
  1033. package/src/hooks/__tests__/index.unit.test.ts +0 -220
  1034. package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
  1035. package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
  1036. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
  1037. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
  1038. package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
  1039. package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
  1040. package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
  1041. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
  1042. package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
  1043. package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
  1044. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
  1045. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
  1046. package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
  1047. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
  1048. package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
  1049. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
  1050. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
  1051. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
  1052. package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
  1053. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
  1054. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  1055. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  1056. package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
  1057. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
  1058. package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
  1059. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
  1060. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
  1061. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
  1062. package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
  1063. package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
  1064. package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
  1065. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
  1066. package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
  1067. package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
  1068. package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
  1069. package/src/hooks/public/index.ts +0 -36
  1070. package/src/hooks/public/usePublicFileDisplay.ts +0 -504
  1071. package/src/hooks/useFileDisplay.ts +0 -715
  1072. package/src/providers/OrganisationProvider.tsx +0 -92
  1073. package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
  1074. package/src/providers/__tests__/EventProvider.test.tsx +0 -551
  1075. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
  1076. package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
  1077. package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
  1078. package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
  1079. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
  1080. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
  1081. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
  1082. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
  1083. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
  1084. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
  1085. package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
  1086. package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
  1087. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
  1088. package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
  1089. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
  1090. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
  1091. package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
  1092. package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
  1093. package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
  1094. package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
  1095. package/src/rbac/audit-enhanced.ts +0 -384
  1096. package/src/rbac/compliance/database-validator.ts +0 -165
  1097. package/src/rbac/compliance/index.ts +0 -48
  1098. package/src/rbac/compliance/pattern-detector.ts +0 -553
  1099. package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
  1100. package/src/rbac/compliance/runtime-compliance.ts +0 -99
  1101. package/src/rbac/compliance/setup-validator.ts +0 -131
  1102. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +0 -975
  1103. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
  1104. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
  1105. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
  1106. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
  1107. package/src/rbac/components/index.ts +0 -26
  1108. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
  1109. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
  1110. package/src/rbac/hooks/index.ts +0 -34
  1111. package/src/rbac/hooks/permissions/index.ts +0 -4
  1112. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  1113. package/src/rbac/utils/__tests__/contextValidator.test.ts +0 -128
  1114. package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
  1115. package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
  1116. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
  1117. package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
  1118. package/src/services/__tests__/AuthService.test.ts +0 -1332
  1119. package/src/services/__tests__/BaseService.test.ts +0 -314
  1120. package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
  1121. package/src/services/__tests__/EventService.test.ts +0 -1025
  1122. package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
  1123. package/src/services/__tests__/InactivityService.test.ts +0 -654
  1124. package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
  1125. package/src/services/__tests__/OrganisationService.test.ts +0 -1176
  1126. package/src/theming/__tests__/parseEventColours.test.ts +0 -321
  1127. package/src/theming/__tests__/runtime.test.ts +0 -569
  1128. package/src/types/__tests__/file-reference.test.ts +0 -447
  1129. package/src/types/__tests__/guards.test.ts +0 -246
  1130. package/src/types/__tests__/organisation.roles.test.ts +0 -55
  1131. package/src/types/__tests__/organisation.test.ts +0 -1133
  1132. package/src/types/__tests__/theme.test.ts +0 -830
  1133. package/src/types/__tests__/type-validation.test.ts +0 -526
  1134. package/src/types/__tests__/validation.test.ts +0 -731
  1135. package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
  1136. package/src/utils/__tests__/audit.unit.test.ts +0 -69
  1137. package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
  1138. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
  1139. package/src/utils/__tests__/cn.unit.test.ts +0 -34
  1140. package/src/utils/__tests__/debugLogger.test.ts +0 -417
  1141. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
  1142. package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
  1143. package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
  1144. package/src/utils/__tests__/formatting.unit.test.ts +0 -99
  1145. package/src/utils/__tests__/index.unit.test.ts +0 -251
  1146. package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
  1147. package/src/utils/__tests__/logger.unit.test.ts +0 -398
  1148. package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
  1149. package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
  1150. package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
  1151. package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
  1152. package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
  1153. package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
  1154. package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
  1155. package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
  1156. package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
  1157. package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
  1158. package/src/utils/__tests__/security.unit.test.ts +0 -149
  1159. package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
  1160. package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
  1161. package/src/utils/__tests__/timezone.test.ts +0 -345
  1162. package/src/utils/__tests__/validation.unit.test.ts +0 -308
  1163. package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
  1164. package/src/utils/app/appNameResolver.simple.test.ts +0 -212
  1165. package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
  1166. package/src/utils/google-places/index.ts +0 -26
  1167. package/src/utils/location/index.ts +0 -16
  1168. package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
  1169. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
  1170. package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
  1171. package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
  1172. package/src/utils/storage/index.ts +0 -67
  1173. package/src/utils/timezone/index.ts +0 -17
  1174. package/src/utils/validation/__tests__/csrf.test.ts +0 -105
  1175. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
  1176. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
  1177. package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
  1178. package/src/utils/validation/index.ts +0 -73
  1179. /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
  1180. /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
  1181. /package/src/providers/{__tests__/README.md → README.md} +0 -0
  1182. /package/src/types/{__tests__/README.md → README.md} +0 -0
@@ -0,0 +1,1250 @@
1
+ /**
2
+ * @file AppSwitcher Component Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/AppSwitcher
5
+ */
6
+
7
+ import React from 'react';
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
9
+ import { screen, waitFor } from '@testing-library/react';
10
+ import userEvent from '@testing-library/user-event';
11
+ import { AppSwitcher } from './AppSwitcher';
12
+ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
13
+
14
+ // Mock useAccessibleApps - use vi.hoisted() to make mock available
15
+ const mockApps = [
16
+ { id: 'app-1', name: 'CAKE', displayName: 'Cake App' },
17
+ { id: 'app-2', name: 'TRAC', displayName: 'Trac App' },
18
+ ];
19
+
20
+ const mockUseAccessibleApps = vi.hoisted(() => vi.fn(() => ({
21
+ apps: mockApps,
22
+ loading: false,
23
+ error: null,
24
+ refetch: vi.fn(),
25
+ })));
26
+
27
+ vi.mock('../../hooks/useAccessibleApps', () => ({
28
+ useAccessibleApps: mockUseAccessibleApps,
29
+ }));
30
+
31
+ // Mock buildAppUrl - use vi.hoisted() to make mock available in both factory and tests
32
+ const mockBuildAppUrl = vi.hoisted(() => vi.fn(({ appName }: { appName: string }) => {
33
+ // Use actual port map values
34
+ const portMap: Record<string, number> = {
35
+ 'CAKE': 8083,
36
+ 'TRAC': 8088,
37
+ 'CORE': 8080,
38
+ };
39
+ const port = portMap[appName] || 3000;
40
+ return `http://localhost:${port}/`;
41
+ }));
42
+
43
+ vi.mock('../../utils/app/buildAppUrl', () => ({
44
+ buildAppUrl: mockBuildAppUrl,
45
+ }));
46
+
47
+ // Mock useUnifiedAuth - use vi.hoisted() to make mock available
48
+ const mockUseUnifiedAuth = vi.hoisted(() => vi.fn(() => ({
49
+ user: { id: 'user-1', email: 'test@example.com' },
50
+ session: {
51
+ access_token: 'test-token',
52
+ refresh_token: 'test-refresh-token',
53
+ expires_at: Date.now() + 3600000,
54
+ expires_in: 3600,
55
+ token_type: 'bearer',
56
+ user: { id: 'user-1', email: 'test@example.com' },
57
+ },
58
+ isAuthenticated: true,
59
+ loading: false,
60
+ error: null,
61
+ appName: 'CORE',
62
+ })));
63
+
64
+ vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
65
+ useUnifiedAuth: mockUseUnifiedAuth,
66
+ }));
67
+
68
+ // Mock window.open
69
+ const mockWindowOpen = vi.hoisted(() => vi.fn());
70
+
71
+ // Mock getAppIcon
72
+ const mockGetAppIcon = vi.hoisted(() => vi.fn((appName: string) => {
73
+ const iconMap: Record<string, string> = {
74
+ 'CAKE': '/icons/cake.svg',
75
+ 'TRAC': '/icons/trac.svg',
76
+ 'CORE': '/icons/core.svg',
77
+ 'PACE': '/icons/pace.svg',
78
+ 'ADMIN': '/icons/admin.svg',
79
+ 'TEAM': '/icons/team.svg',
80
+ };
81
+ return iconMap[appName.toUpperCase()] || undefined;
82
+ }));
83
+
84
+ vi.mock('../../assets/app-icons', () => ({
85
+ getAppIcon: mockGetAppIcon,
86
+ }));
87
+
88
+ // Store onValueChange callback for testing - use vi.hoisted to make it available
89
+ const storedOnValueChangeRef = vi.hoisted(() => ({ current: null as ((value: string) => void) | null }));
90
+
91
+ // Mock Select component
92
+ vi.mock('../Select', () => ({
93
+ Select: ({ children, onValueChange, disabled, className }: any) => {
94
+ // Store onValueChange for testing
95
+ storedOnValueChangeRef.current = onValueChange;
96
+ return (
97
+ <div data-testid="select" data-disabled={disabled} className={className}>
98
+ {children}
99
+ </div>
100
+ );
101
+ },
102
+ SelectTrigger: ({ children, asChild, disabled, variant, showChevron, className }: any) => {
103
+ if (asChild && children) {
104
+ return <div data-testid="select-trigger" data-disabled={disabled} data-variant={variant} data-show-chevron={showChevron}>{children}</div>;
105
+ }
106
+ return (
107
+ <div
108
+ data-testid="select-trigger"
109
+ role="combobox"
110
+ data-disabled={disabled}
111
+ data-variant={variant}
112
+ data-show-chevron={showChevron}
113
+ className={className}
114
+ >
115
+ {children}
116
+ </div>
117
+ );
118
+ },
119
+ SelectContent: ({ children, className }: any) => (
120
+ <div data-testid="select-content" className={className}>
121
+ {children}
122
+ </div>
123
+ ),
124
+ SelectItem: ({ children, value, className, onClick }: any) => (
125
+ <div
126
+ data-testid={`select-item-${value}`}
127
+ data-value={value}
128
+ className={className}
129
+ onClick={() => {
130
+ if (storedOnValueChangeRef.current) {
131
+ storedOnValueChangeRef.current(value);
132
+ }
133
+ if (onClick) {
134
+ onClick();
135
+ }
136
+ }}
137
+ >
138
+ {children}
139
+ </div>
140
+ ),
141
+ SelectValue: ({ placeholder, className }: any) => (
142
+ <div data-testid="select-value" data-placeholder={placeholder} className={className}>
143
+ {placeholder}
144
+ </div>
145
+ ),
146
+ }));
147
+
148
+ // Following testing standards: use timeout parameter to prevent hanging
149
+ const TEST_TIMEOUT = 10000; // 10 seconds per test
150
+
151
+ describe('AppSwitcher', () => {
152
+ beforeEach(() => {
153
+ vi.clearAllMocks();
154
+ storedOnValueChangeRef.current = null;
155
+ // Clear sessionStorage
156
+ if (typeof window !== 'undefined' && window.sessionStorage) {
157
+ window.sessionStorage.clear();
158
+ }
159
+ // Reset window.open mock
160
+ window.open = mockWindowOpen;
161
+ // Restore JSON.stringify if it was mocked
162
+ vi.restoreAllMocks();
163
+ // Reset mock to default state
164
+ mockUseAccessibleApps.mockReturnValue({
165
+ apps: mockApps,
166
+ loading: false,
167
+ error: null,
168
+ refetch: vi.fn(),
169
+ });
170
+ mockUseUnifiedAuth.mockReturnValue({
171
+ user: { id: 'user-1', email: 'test@example.com' },
172
+ session: {
173
+ access_token: 'test-token',
174
+ refresh_token: 'test-refresh-token',
175
+ expires_at: Date.now() + 3600000,
176
+ expires_in: 3600,
177
+ token_type: 'bearer',
178
+ user: { id: 'user-1', email: 'test@example.com' },
179
+ },
180
+ isAuthenticated: true,
181
+ loading: false,
182
+ error: null,
183
+ appName: 'CORE',
184
+ });
185
+ mockGetAppIcon.mockImplementation((appName: string) => {
186
+ const iconMap: Record<string, string> = {
187
+ 'CAKE': '/icons/cake.svg',
188
+ 'TRAC': '/icons/trac.svg',
189
+ 'CORE': '/icons/core.svg',
190
+ 'PACE': '/icons/pace.svg',
191
+ 'ADMIN': '/icons/admin.svg',
192
+ 'TEAM': '/icons/team.svg',
193
+ };
194
+ return iconMap[appName.toUpperCase()] || undefined;
195
+ });
196
+ });
197
+
198
+ it('should render trigger when provided', () => {
199
+ const trigger = <div data-testid="custom-trigger">Logo</div>;
200
+
201
+ renderWithProviders(
202
+ <AppSwitcher currentAppName="CORE" trigger={trigger} />
203
+ );
204
+
205
+ expect(screen.getByTestId('custom-trigger')).toBeInTheDocument();
206
+ });
207
+
208
+ it('should render default trigger when not provided', () => {
209
+ renderWithProviders(
210
+ <AppSwitcher currentAppName="CORE" />
211
+ );
212
+
213
+ // SelectTrigger should be rendered
214
+ expect(screen.getByRole('combobox')).toBeInTheDocument();
215
+ });
216
+
217
+ it('should not render when showSwitcher is false', () => {
218
+ const { container } = renderWithProviders(
219
+ <AppSwitcher currentAppName="CORE" showSwitcher={false} />
220
+ );
221
+
222
+ expect(container.firstChild).toBeNull();
223
+ });
224
+
225
+ it('should not render when no apps available', () => {
226
+ mockUseAccessibleApps.mockReturnValueOnce({
227
+ apps: [],
228
+ loading: false,
229
+ error: null,
230
+ refetch: vi.fn(),
231
+ });
232
+
233
+ const { container } = renderWithProviders(
234
+ <AppSwitcher currentAppName="CORE" />
235
+ );
236
+
237
+ // When no apps available and not loading, AppSwitcher returns null (no trigger)
238
+ // This matches the actual behavior: if (!showSwitcher || (!loading && apps.length === 0))
239
+ expect(container.firstChild).toBeNull();
240
+ });
241
+
242
+ it('should handle app selection and navigate', async () => {
243
+ const user = userEvent.setup();
244
+
245
+ renderWithProviders(
246
+ <AppSwitcher currentAppName="CORE" />
247
+ );
248
+
249
+ const trigger = screen.getByRole('combobox');
250
+ await user.click(trigger);
251
+
252
+ await waitFor(() => {
253
+ expect(screen.getByText('Cake App')).toBeInTheDocument();
254
+ });
255
+
256
+ const cakeApp = screen.getByText('Cake App');
257
+ await user.click(cakeApp);
258
+
259
+ expect(mockBuildAppUrl).toHaveBeenCalledWith({ appName: 'CAKE', path: '/' });
260
+ // Verify window.open was called with the correct URL
261
+ // The URL should start with the base URL and may include session parameter
262
+ expect(mockWindowOpen).toHaveBeenCalledTimes(1);
263
+ const [url, target] = mockWindowOpen.mock.calls[0];
264
+ expect(url).toContain('http://localhost:8083/');
265
+ expect(target).toBe('_blank');
266
+ // Session should be included when user has a session
267
+ expect(url).toContain('session=');
268
+ });
269
+
270
+ it('should be disabled when loading', () => {
271
+ mockUseAccessibleApps.mockReturnValueOnce({
272
+ apps: [],
273
+ loading: true,
274
+ error: null,
275
+ refetch: vi.fn(),
276
+ });
277
+
278
+ renderWithProviders(
279
+ <AppSwitcher currentAppName="CORE" />
280
+ );
281
+
282
+ // When loading, Select should be disabled
283
+ const select = screen.getByTestId('select');
284
+ expect(select).toHaveAttribute('data-disabled', 'true');
285
+ });
286
+
287
+ it('should handle errors gracefully', () => {
288
+ mockUseAccessibleApps.mockReturnValueOnce({
289
+ apps: [],
290
+ loading: false,
291
+ error: new Error('Failed to fetch'),
292
+ refetch: vi.fn(),
293
+ });
294
+
295
+ const trigger = <div data-testid="trigger">Logo</div>;
296
+ const { container } = renderWithProviders(
297
+ <AppSwitcher currentAppName="CORE" trigger={trigger} />
298
+ );
299
+
300
+ // Should still render trigger
301
+ expect(screen.getByTestId('trigger')).toBeInTheDocument();
302
+ });
303
+
304
+ // Cache functionality tests
305
+ describe('Cache Functionality', () => {
306
+ it('loads apps from cache on mount when user is available', async () => {
307
+ const cachedApps = [
308
+ { id: 'app-1', name: 'CAKE', displayName: 'Cake App', logoUrl: '/icons/cake.svg', sortId: 10 },
309
+ { id: 'app-2', name: 'TRAC', displayName: 'Trac App', logoUrl: '/icons/trac.svg', sortId: 8 },
310
+ ];
311
+
312
+ // Set up cache
313
+ if (typeof window !== 'undefined' && window.sessionStorage) {
314
+ window.sessionStorage.setItem('pace-core:appswitcher:apps:user-1', JSON.stringify(cachedApps));
315
+ }
316
+
317
+ // Mock useAccessibleApps to return empty initially (cache will be used)
318
+ mockUseAccessibleApps.mockReturnValueOnce({
319
+ apps: [],
320
+ loading: false,
321
+ error: null,
322
+ refetch: vi.fn(),
323
+ });
324
+
325
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
326
+
327
+ // Cache should be loaded and apps should be available
328
+ // The component will use cached apps if available
329
+ await waitFor(() => {
330
+ const trigger = screen.queryByRole('combobox');
331
+ expect(trigger).toBeInTheDocument();
332
+ }, { timeout: TEST_TIMEOUT });
333
+ });
334
+
335
+ it('saves apps to cache when new data arrives', async () => {
336
+ mockUseAccessibleApps.mockReturnValue({
337
+ apps: mockApps,
338
+ loading: false,
339
+ error: null,
340
+ refetch: vi.fn(),
341
+ });
342
+
343
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
344
+
345
+ await waitFor(() => {
346
+ // Verify cache was saved
347
+ if (typeof window !== 'undefined' && window.sessionStorage) {
348
+ const cached = window.sessionStorage.getItem('pace-core:appswitcher:apps:user-1');
349
+ expect(cached).toBeTruthy();
350
+ if (cached) {
351
+ const parsed = JSON.parse(cached);
352
+ expect(Array.isArray(parsed)).toBe(true);
353
+ expect(parsed.length).toBeGreaterThan(0);
354
+ }
355
+ }
356
+ }, { timeout: TEST_TIMEOUT });
357
+ });
358
+
359
+ it('clears cache when user changes', async () => {
360
+ // Set up cache for user-1
361
+ if (typeof window !== 'undefined' && window.sessionStorage) {
362
+ window.sessionStorage.setItem('pace-core:appswitcher:apps:user-1', JSON.stringify([{ id: 'app-1', name: 'CAKE' }]));
363
+ }
364
+
365
+ // Change user
366
+ mockUseUnifiedAuth.mockReturnValue({
367
+ user: { id: 'user-2', email: 'test2@example.com' },
368
+ session: null,
369
+ isAuthenticated: true,
370
+ loading: false,
371
+ error: null,
372
+ appName: 'CORE',
373
+ });
374
+
375
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
376
+
377
+ // Cache for user-1 should still exist, but component should not use it
378
+ // New user should trigger cache load for user-2 (which will be empty)
379
+ await waitFor(() => {
380
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
381
+ }, { timeout: TEST_TIMEOUT });
382
+ });
383
+
384
+ it('handles cache load errors gracefully', () => {
385
+ // Mock sessionStorage.getItem to throw
386
+ const originalGetItem = window.sessionStorage.getItem;
387
+ window.sessionStorage.getItem = vi.fn(() => {
388
+ throw new Error('Storage error');
389
+ });
390
+
391
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
392
+
393
+ // Should still render without crashing
394
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
395
+
396
+ // Restore
397
+ window.sessionStorage.getItem = originalGetItem;
398
+ });
399
+
400
+ it('handles cache save errors gracefully', () => {
401
+ // Mock sessionStorage.setItem to throw
402
+ const originalSetItem = window.sessionStorage.setItem;
403
+ window.sessionStorage.setItem = vi.fn(() => {
404
+ throw new Error('Quota exceeded');
405
+ });
406
+
407
+ mockUseAccessibleApps.mockReturnValue({
408
+ apps: mockApps,
409
+ loading: false,
410
+ error: null,
411
+ refetch: vi.fn(),
412
+ });
413
+
414
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
415
+
416
+ // Should still render without crashing
417
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
418
+
419
+ // Restore
420
+ window.sessionStorage.setItem = originalSetItem;
421
+ });
422
+
423
+ it('handles invalid cache data gracefully', () => {
424
+ // Set invalid cache data
425
+ if (typeof window !== 'undefined' && window.sessionStorage) {
426
+ window.sessionStorage.setItem('pace-core:appswitcher:apps:user-1', 'invalid json');
427
+ }
428
+
429
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
430
+
431
+ // Should still render without crashing
432
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
433
+ });
434
+ });
435
+
436
+ // App sorting and filtering tests
437
+ describe('App Sorting and Filtering', () => {
438
+ it('filters out current app from the list', async () => {
439
+ const allApps = [
440
+ { id: 'app-1', name: 'CAKE', displayName: 'Cake App' },
441
+ { id: 'app-2', name: 'TRAC', displayName: 'Trac App' },
442
+ { id: 'app-3', name: 'CORE', displayName: 'Core App' },
443
+ ];
444
+
445
+ mockUseAccessibleApps.mockReturnValue({
446
+ apps: allApps,
447
+ loading: false,
448
+ error: null,
449
+ refetch: vi.fn(),
450
+ });
451
+
452
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
453
+
454
+ await waitFor(() => {
455
+ const trigger = screen.getByRole('combobox');
456
+ expect(trigger).toBeInTheDocument();
457
+ }, { timeout: TEST_TIMEOUT });
458
+
459
+ // CORE should be filtered out
460
+ expect(screen.queryByText('Core App')).not.toBeInTheDocument();
461
+ });
462
+
463
+ it('filters out current app case-insensitively', async () => {
464
+ const allApps = [
465
+ { id: 'app-1', name: 'CAKE', displayName: 'Cake App' },
466
+ { id: 'app-2', name: 'core', displayName: 'Core App' }, // lowercase
467
+ ];
468
+
469
+ mockUseAccessibleApps.mockReturnValue({
470
+ apps: allApps,
471
+ loading: false,
472
+ error: null,
473
+ refetch: vi.fn(),
474
+ });
475
+
476
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
477
+
478
+ await waitFor(() => {
479
+ const trigger = screen.getByRole('combobox');
480
+ expect(trigger).toBeInTheDocument();
481
+ }, { timeout: TEST_TIMEOUT });
482
+
483
+ // core (lowercase) should be filtered out when currentAppName is CORE (uppercase)
484
+ expect(screen.queryByText('Core App')).not.toBeInTheDocument();
485
+ });
486
+
487
+ it('sorts apps by display order when apps are in APP_DISPLAY_ORDER', async () => {
488
+ const allApps = [
489
+ { id: 'app-1', name: 'TRAC', displayName: 'Trac App' },
490
+ { id: 'app-2', name: 'CAKE', displayName: 'Cake App' },
491
+ { id: 'app-3', name: 'ADMIN', displayName: 'Admin App' },
492
+ ];
493
+
494
+ mockUseAccessibleApps.mockReturnValue({
495
+ apps: allApps,
496
+ loading: false,
497
+ error: null,
498
+ refetch: vi.fn(),
499
+ });
500
+
501
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
502
+
503
+ await waitFor(() => {
504
+ const content = screen.queryByTestId('select-content');
505
+ expect(content).toBeInTheDocument();
506
+ }, { timeout: TEST_TIMEOUT });
507
+
508
+ // ADMIN should come before CAKE, which should come before TRAC (based on APP_DISPLAY_ORDER)
509
+ const items = screen.queryAllByTestId(/select-item-/);
510
+ expect(items.length).toBeGreaterThan(0);
511
+ });
512
+
513
+ it('sorts apps alphabetically when not in APP_DISPLAY_ORDER', async () => {
514
+ const allApps = [
515
+ { id: 'app-1', name: 'ZAPP', displayName: 'Z App' },
516
+ { id: 'app-2', name: 'AAPP', displayName: 'A App' },
517
+ { id: 'app-3', name: 'MAPP', displayName: 'M App' },
518
+ ];
519
+
520
+ mockUseAccessibleApps.mockReturnValue({
521
+ apps: allApps,
522
+ loading: false,
523
+ error: null,
524
+ refetch: vi.fn(),
525
+ });
526
+
527
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
528
+
529
+ await waitFor(() => {
530
+ const content = screen.queryByTestId('select-content');
531
+ expect(content).toBeInTheDocument();
532
+ }, { timeout: TEST_TIMEOUT });
533
+
534
+ // Apps not in display order should be sorted alphabetically
535
+ const items = screen.queryAllByTestId(/select-item-/);
536
+ expect(items.length).toBe(3);
537
+ });
538
+
539
+ it('prioritizes apps in display order over alphabetical sorting', async () => {
540
+ const allApps = [
541
+ { id: 'app-1', name: 'ZAPP', displayName: 'Z App' }, // Not in order
542
+ { id: 'app-2', name: 'ADMIN', displayName: 'Admin App' }, // In order (index 0)
543
+ { id: 'app-3', name: 'AAPP', displayName: 'A App' }, // Not in order
544
+ ];
545
+
546
+ mockUseAccessibleApps.mockReturnValue({
547
+ apps: allApps,
548
+ loading: false,
549
+ error: null,
550
+ refetch: vi.fn(),
551
+ });
552
+
553
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
554
+
555
+ await waitFor(() => {
556
+ const content = screen.queryByTestId('select-content');
557
+ expect(content).toBeInTheDocument();
558
+ }, { timeout: TEST_TIMEOUT });
559
+
560
+ // ADMIN (in order) should come before apps not in order
561
+ const items = screen.queryAllByTestId(/select-item-/);
562
+ expect(items.length).toBe(3);
563
+ });
564
+ });
565
+
566
+ // Session encoding tests
567
+ describe('Session Encoding', () => {
568
+ it('includes session data in URL when session is available', async () => {
569
+ const user = userEvent.setup();
570
+
571
+ mockUseUnifiedAuth.mockReturnValue({
572
+ user: { id: 'user-1', email: 'test@example.com' },
573
+ session: {
574
+ access_token: 'test-access-token',
575
+ refresh_token: 'test-refresh-token',
576
+ expires_at: Date.now() + 3600000,
577
+ expires_in: 3600,
578
+ token_type: 'bearer',
579
+ user: { id: 'user-1', email: 'test@example.com' },
580
+ },
581
+ isAuthenticated: true,
582
+ loading: false,
583
+ error: null,
584
+ appName: 'CORE',
585
+ });
586
+
587
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
588
+
589
+ await waitFor(() => {
590
+ const select = screen.getByTestId('select');
591
+ expect(select).toBeInTheDocument();
592
+ }, { timeout: TEST_TIMEOUT });
593
+
594
+ // Wait for apps to be processed and callback to be set
595
+ await waitFor(() => {
596
+ expect(storedOnValueChangeRef.current).toBeTruthy();
597
+ }, { timeout: TEST_TIMEOUT });
598
+
599
+ // Simulate app selection using stored callback
600
+ if (storedOnValueChangeRef.current) {
601
+ storedOnValueChangeRef.current('CAKE');
602
+ }
603
+
604
+ expect(mockWindowOpen).toHaveBeenCalled();
605
+ const [url] = mockWindowOpen.mock.calls[0];
606
+ expect(url).toContain('session=');
607
+ });
608
+
609
+ it('does not include session data when session is not available', async () => {
610
+ mockUseUnifiedAuth.mockReturnValue({
611
+ user: { id: 'user-1', email: 'test@example.com' },
612
+ session: null,
613
+ isAuthenticated: true,
614
+ loading: false,
615
+ error: null,
616
+ appName: 'CORE',
617
+ });
618
+
619
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
620
+
621
+ await waitFor(() => {
622
+ const select = screen.getByTestId('select');
623
+ expect(select).toBeInTheDocument();
624
+ }, { timeout: TEST_TIMEOUT });
625
+
626
+ // Wait for apps to be processed and callback to be set
627
+ await waitFor(() => {
628
+ expect(storedOnValueChangeRef.current).toBeTruthy();
629
+ }, { timeout: TEST_TIMEOUT });
630
+
631
+ // Simulate app selection using stored callback
632
+ if (storedOnValueChangeRef.current) {
633
+ storedOnValueChangeRef.current('CAKE');
634
+ }
635
+
636
+ expect(mockWindowOpen).toHaveBeenCalled();
637
+ const [url] = mockWindowOpen.mock.calls[0];
638
+ expect(url).not.toContain('session=');
639
+ });
640
+
641
+ it('handles session encoding errors gracefully', async () => {
642
+ mockUseUnifiedAuth.mockReturnValue({
643
+ user: { id: 'user-1', email: 'test@example.com' },
644
+ session: {
645
+ access_token: 'test-token',
646
+ refresh_token: 'test-refresh-token',
647
+ expires_at: Date.now() + 3600000,
648
+ expires_in: 3600,
649
+ token_type: 'bearer',
650
+ user: { id: 'user-1', email: 'test@example.com' },
651
+ },
652
+ isAuthenticated: true,
653
+ loading: false,
654
+ error: null,
655
+ appName: 'CORE',
656
+ });
657
+
658
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
659
+
660
+ await waitFor(() => {
661
+ const select = screen.getByTestId('select');
662
+ expect(select).toBeInTheDocument();
663
+ }, { timeout: TEST_TIMEOUT });
664
+
665
+ // Wait for apps to be processed and callback to be set
666
+ await waitFor(() => {
667
+ expect(storedOnValueChangeRef.current).toBeTruthy();
668
+ }, { timeout: TEST_TIMEOUT });
669
+
670
+ // Create a spy on JSON.stringify that throws only when called
671
+ const stringifySpy = vi.spyOn(JSON, 'stringify').mockImplementationOnce(() => {
672
+ throw new Error('Stringify error');
673
+ });
674
+
675
+ // Simulate app selection using stored callback
676
+ if (storedOnValueChangeRef.current) {
677
+ storedOnValueChangeRef.current('CAKE');
678
+ }
679
+
680
+ // Should fall back to session_token parameter
681
+ expect(mockWindowOpen).toHaveBeenCalled();
682
+ const [url] = mockWindowOpen.mock.calls[0];
683
+ expect(url).toContain('session_token=');
684
+
685
+ // Restore
686
+ stringifySpy.mockRestore();
687
+ });
688
+
689
+ it('includes minimal user data in session encoding', async () => {
690
+ mockUseUnifiedAuth.mockReturnValue({
691
+ user: { id: 'user-1', email: 'test@example.com' },
692
+ session: {
693
+ access_token: 'test-access-token',
694
+ refresh_token: 'test-refresh-token',
695
+ expires_at: Date.now() + 3600000,
696
+ expires_in: 3600,
697
+ token_type: 'bearer',
698
+ user: { id: 'user-1', email: 'test@example.com' },
699
+ },
700
+ isAuthenticated: true,
701
+ loading: false,
702
+ error: null,
703
+ appName: 'CORE',
704
+ });
705
+
706
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
707
+
708
+ await waitFor(() => {
709
+ const select = screen.getByTestId('select');
710
+ expect(select).toBeInTheDocument();
711
+ }, { timeout: TEST_TIMEOUT });
712
+
713
+ // Wait for apps to be processed and callback to be set
714
+ await waitFor(() => {
715
+ expect(storedOnValueChangeRef.current).toBeTruthy();
716
+ }, { timeout: TEST_TIMEOUT });
717
+
718
+ // Simulate app selection using stored callback
719
+ if (storedOnValueChangeRef.current) {
720
+ storedOnValueChangeRef.current('CAKE');
721
+ }
722
+
723
+ expect(mockWindowOpen).toHaveBeenCalled();
724
+ const [url] = mockWindowOpen.mock.calls[0];
725
+
726
+ // Decode and verify session data
727
+ const urlObj = new URL(url);
728
+ const sessionParam = urlObj.searchParams.get('session');
729
+ if (sessionParam) {
730
+ const decoded = JSON.parse(atob(sessionParam));
731
+ expect(decoded.user).toHaveProperty('id');
732
+ expect(decoded.user).toHaveProperty('email');
733
+ expect(decoded.user.id).toBe('user-1');
734
+ expect(decoded.user.email).toBe('test@example.com');
735
+ }
736
+ });
737
+ });
738
+
739
+ // Error handling tests
740
+ describe('Error Handling', () => {
741
+ it('renders trigger when error occurs and trigger is provided', () => {
742
+ mockUseAccessibleApps.mockReturnValueOnce({
743
+ apps: [],
744
+ loading: false,
745
+ error: new Error('Network error'),
746
+ refetch: vi.fn(),
747
+ });
748
+
749
+ const trigger = <div data-testid="error-trigger">Logo</div>;
750
+ renderWithProviders(<AppSwitcher currentAppName="CORE" trigger={trigger} />);
751
+
752
+ expect(screen.getByTestId('error-trigger')).toBeInTheDocument();
753
+ });
754
+
755
+ it('returns null when error occurs and no trigger is provided', () => {
756
+ mockUseAccessibleApps.mockReturnValueOnce({
757
+ apps: [],
758
+ loading: false,
759
+ error: new Error('Network error'),
760
+ refetch: vi.fn(),
761
+ });
762
+
763
+ const { container } = renderWithProviders(
764
+ <AppSwitcher currentAppName="CORE" />
765
+ );
766
+
767
+ expect(container.firstChild).toBeNull();
768
+ });
769
+
770
+ it('handles different error types gracefully', () => {
771
+ const errorTypes = [
772
+ new Error('Network error'),
773
+ new Error('Permission denied'),
774
+ ];
775
+
776
+ errorTypes.forEach((error, index) => {
777
+ mockUseAccessibleApps.mockReturnValueOnce({
778
+ apps: [],
779
+ loading: false,
780
+ error: error as Error,
781
+ refetch: vi.fn(),
782
+ });
783
+
784
+ const trigger = <div data-testid={`trigger-${index}`}>Logo</div>;
785
+ const { unmount } = renderWithProviders(
786
+ <AppSwitcher currentAppName="CORE" trigger={trigger} />
787
+ );
788
+
789
+ expect(screen.getByTestId(`trigger-${index}`)).toBeInTheDocument();
790
+ unmount();
791
+ });
792
+ });
793
+ });
794
+
795
+ // Loading state tests
796
+ describe('Loading States', () => {
797
+ it('disables select when loading', () => {
798
+ mockUseAccessibleApps.mockReturnValue({
799
+ apps: mockApps, // Provide apps so component renders
800
+ loading: true,
801
+ error: null,
802
+ refetch: vi.fn(),
803
+ });
804
+
805
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
806
+
807
+ const select = screen.getByTestId('select');
808
+ expect(select).toHaveAttribute('data-disabled', 'true');
809
+ });
810
+
811
+ it('disables select when no apps available but still loading', () => {
812
+ mockUseAccessibleApps.mockReturnValue({
813
+ apps: [],
814
+ loading: true,
815
+ error: null,
816
+ refetch: vi.fn(),
817
+ });
818
+
819
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
820
+
821
+ // When loading, component should render but be disabled
822
+ const select = screen.getByTestId('select');
823
+ expect(select).toHaveAttribute('data-disabled', 'true');
824
+ });
825
+
826
+ it('enables select when apps are available and not loading', async () => {
827
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
828
+
829
+ await waitFor(() => {
830
+ const select = screen.getByTestId('select');
831
+ // When apps are available and not loading, disabled should be false
832
+ expect(select).toHaveAttribute('data-disabled', 'false');
833
+ }, { timeout: TEST_TIMEOUT });
834
+ });
835
+ });
836
+
837
+ // User state tests
838
+ describe('User State Variations', () => {
839
+ it('handles no user gracefully', () => {
840
+ mockUseUnifiedAuth.mockReturnValue({
841
+ user: null,
842
+ session: null,
843
+ isAuthenticated: false,
844
+ loading: false,
845
+ error: null,
846
+ appName: undefined,
847
+ });
848
+
849
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
850
+
851
+ // Without user, cache won't load, but component should still render if apps are available
852
+ // Component may return null if no apps and not loading
853
+ const { container } = renderWithProviders(<AppSwitcher currentAppName="CORE" />);
854
+ // Component behavior depends on apps availability
855
+ expect(container).toBeDefined();
856
+ });
857
+
858
+ it('handles user without id gracefully', () => {
859
+ mockUseUnifiedAuth.mockReturnValue({
860
+ user: { email: 'test@example.com' } as any,
861
+ session: null,
862
+ isAuthenticated: true,
863
+ loading: false,
864
+ error: null,
865
+ appName: 'CORE',
866
+ });
867
+
868
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
869
+
870
+ // Should not crash - component should handle missing user.id
871
+ const { container } = renderWithProviders(<AppSwitcher currentAppName="CORE" />);
872
+ expect(container).toBeDefined();
873
+ });
874
+
875
+ it('handles user change correctly', async () => {
876
+ const { rerender } = renderWithProviders(
877
+ <AppSwitcher currentAppName="CORE" />
878
+ );
879
+
880
+ await waitFor(() => {
881
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
882
+ }, { timeout: TEST_TIMEOUT });
883
+
884
+ // Change user
885
+ mockUseUnifiedAuth.mockReturnValue({
886
+ user: { id: 'user-2', email: 'test2@example.com' },
887
+ session: null,
888
+ isAuthenticated: true,
889
+ loading: false,
890
+ error: null,
891
+ appName: 'CORE',
892
+ });
893
+
894
+ rerender(<AppSwitcher currentAppName="CORE" />);
895
+
896
+ // Should handle user change without crashing
897
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
898
+ });
899
+ });
900
+
901
+ // Trigger rendering tests
902
+ describe('Trigger Rendering', () => {
903
+ it('renders custom trigger with asChild', () => {
904
+ const trigger = <img src="/logo.svg" alt="Logo" data-testid="custom-logo" />;
905
+
906
+ renderWithProviders(
907
+ <AppSwitcher currentAppName="CORE" trigger={trigger} />
908
+ );
909
+
910
+ expect(screen.getByTestId('custom-logo')).toBeInTheDocument();
911
+ const triggerElement = screen.getByTestId('select-trigger');
912
+ expect(triggerElement).toBeInTheDocument();
913
+ expect(triggerElement).toHaveAttribute('data-variant', 'ghost');
914
+ expect(triggerElement).toHaveAttribute('data-show-chevron', 'false');
915
+ });
916
+
917
+ it('renders default trigger with PACE icon when no trigger provided', () => {
918
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
919
+
920
+ const trigger = screen.getByTestId('select-trigger');
921
+ expect(trigger).toBeInTheDocument();
922
+ expect(trigger).toHaveAttribute('data-variant', 'ghost');
923
+ expect(trigger).toHaveAttribute('data-show-chevron', 'false');
924
+
925
+ // Should have PACE icon
926
+ const img = trigger.querySelector('img');
927
+ expect(img).toBeInTheDocument();
928
+ expect(img).toHaveAttribute('alt', 'PACE');
929
+ });
930
+
931
+ it('applies className to Select component', () => {
932
+ renderWithProviders(
933
+ <AppSwitcher currentAppName="CORE" className="custom-class" />
934
+ );
935
+
936
+ const select = screen.getByTestId('select');
937
+ expect(select).toHaveClass('custom-class');
938
+ });
939
+
940
+ it('applies className to default trigger', () => {
941
+ renderWithProviders(
942
+ <AppSwitcher currentAppName="CORE" className="custom-class" />
943
+ );
944
+
945
+ const trigger = screen.getByTestId('select-trigger');
946
+ expect(trigger).toBeInTheDocument();
947
+ });
948
+ });
949
+
950
+ // App selection and navigation tests
951
+ describe('App Selection and Navigation', () => {
952
+ it('opens app URL in new tab when app is selected', async () => {
953
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
954
+
955
+ await waitFor(() => {
956
+ const select = screen.getByTestId('select');
957
+ expect(select).toBeInTheDocument();
958
+ }, { timeout: TEST_TIMEOUT });
959
+
960
+ // Wait for apps to be processed and callback to be set
961
+ await waitFor(() => {
962
+ expect(storedOnValueChangeRef.current).toBeTruthy();
963
+ }, { timeout: TEST_TIMEOUT });
964
+
965
+ // Simulate app selection using stored callback
966
+ if (storedOnValueChangeRef.current) {
967
+ storedOnValueChangeRef.current('CAKE');
968
+ }
969
+
970
+ expect(mockBuildAppUrl).toHaveBeenCalledWith({ appName: 'CAKE', path: '/' });
971
+ expect(mockWindowOpen).toHaveBeenCalledWith(
972
+ expect.stringContaining('http://localhost:8083/'),
973
+ '_blank'
974
+ );
975
+ });
976
+
977
+ it('calls buildAppUrl with correct parameters', async () => {
978
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
979
+
980
+ await waitFor(() => {
981
+ const select = screen.getByTestId('select');
982
+ expect(select).toBeInTheDocument();
983
+ }, { timeout: TEST_TIMEOUT });
984
+
985
+ // Wait for apps to be processed and callback to be set
986
+ await waitFor(() => {
987
+ expect(storedOnValueChangeRef.current).toBeTruthy();
988
+ }, { timeout: TEST_TIMEOUT });
989
+
990
+ // Simulate app selection using stored callback
991
+ if (storedOnValueChangeRef.current) {
992
+ storedOnValueChangeRef.current('TRAC');
993
+ }
994
+
995
+ expect(mockBuildAppUrl).toHaveBeenCalledWith({ appName: 'TRAC', path: '/' });
996
+ });
997
+
998
+ it('handles window.open errors gracefully', async () => {
999
+ // Mock window.open to throw
1000
+ const originalOpen = window.open;
1001
+ const mockOpenWithError = vi.fn(() => {
1002
+ throw new Error('Popup blocked');
1003
+ });
1004
+ window.open = mockOpenWithError;
1005
+
1006
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1007
+
1008
+ await waitFor(() => {
1009
+ const select = screen.getByTestId('select');
1010
+ expect(select).toBeInTheDocument();
1011
+ }, { timeout: TEST_TIMEOUT });
1012
+
1013
+ // Wait for apps to be processed and callback to be set
1014
+ await waitFor(() => {
1015
+ expect(storedOnValueChangeRef.current).toBeTruthy();
1016
+ }, { timeout: TEST_TIMEOUT });
1017
+
1018
+ // Simulate app selection using stored callback
1019
+ // The component calls window.open directly, so the error will be thrown
1020
+ // but the component should handle it gracefully (not crash the app)
1021
+ if (storedOnValueChangeRef.current) {
1022
+ // The error will be thrown, but we're testing that the component doesn't crash
1023
+ try {
1024
+ storedOnValueChangeRef.current('CAKE');
1025
+ } catch (error) {
1026
+ // Error is expected - component should handle it
1027
+ expect(error).toBeInstanceOf(Error);
1028
+ }
1029
+ }
1030
+
1031
+ // Verify window.open was called (even though it threw)
1032
+ expect(mockOpenWithError).toHaveBeenCalled();
1033
+
1034
+ // Restore
1035
+ window.open = originalOpen;
1036
+ });
1037
+ });
1038
+
1039
+ // ShowSwitcher prop tests
1040
+ describe('ShowSwitcher Prop', () => {
1041
+ it('returns trigger when showSwitcher is false and trigger is provided', () => {
1042
+ const trigger = <div data-testid="trigger">Logo</div>;
1043
+
1044
+ const { container } = renderWithProviders(
1045
+ <AppSwitcher currentAppName="CORE" showSwitcher={false} trigger={trigger} />
1046
+ );
1047
+
1048
+ expect(screen.getByTestId('trigger')).toBeInTheDocument();
1049
+ });
1050
+
1051
+ it('returns null when showSwitcher is false and no trigger is provided', () => {
1052
+ const { container } = renderWithProviders(
1053
+ <AppSwitcher currentAppName="CORE" showSwitcher={false} />
1054
+ );
1055
+
1056
+ expect(container.firstChild).toBeNull();
1057
+ });
1058
+
1059
+ it('uses default showSwitcher value (true)', () => {
1060
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1061
+
1062
+ expect(screen.getByRole('combobox')).toBeInTheDocument();
1063
+ });
1064
+ });
1065
+
1066
+ // Edge cases
1067
+ describe('Edge Cases', () => {
1068
+ it('handles empty apps array', () => {
1069
+ mockUseAccessibleApps.mockReturnValue({
1070
+ apps: [],
1071
+ loading: false,
1072
+ error: null,
1073
+ refetch: vi.fn(),
1074
+ });
1075
+
1076
+ const { container } = renderWithProviders(
1077
+ <AppSwitcher currentAppName="CORE" />
1078
+ );
1079
+
1080
+ expect(container.firstChild).toBeNull();
1081
+ });
1082
+
1083
+ it('handles apps with missing displayName', async () => {
1084
+ const appsWithMissingData = [
1085
+ { id: 'app-1', name: 'CAKE', displayName: '' },
1086
+ { id: 'app-2', name: 'TRAC' } as any,
1087
+ ];
1088
+
1089
+ mockUseAccessibleApps.mockReturnValue({
1090
+ apps: appsWithMissingData,
1091
+ loading: false,
1092
+ error: null,
1093
+ refetch: vi.fn(),
1094
+ });
1095
+
1096
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1097
+
1098
+ await waitFor(() => {
1099
+ const select = screen.getByTestId('select');
1100
+ expect(select).toBeInTheDocument();
1101
+ }, { timeout: TEST_TIMEOUT });
1102
+ });
1103
+
1104
+ it('handles apps with missing logoUrl', async () => {
1105
+ mockGetAppIcon.mockReturnValueOnce(undefined);
1106
+
1107
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1108
+
1109
+ await waitFor(() => {
1110
+ const select = screen.getByTestId('select');
1111
+ expect(select).toBeInTheDocument();
1112
+ }, { timeout: TEST_TIMEOUT });
1113
+ });
1114
+
1115
+ it('handles very long app names', async () => {
1116
+ const longNameApp = {
1117
+ id: 'app-1',
1118
+ name: 'A'.repeat(100),
1119
+ displayName: 'Very Long App Name',
1120
+ };
1121
+
1122
+ mockUseAccessibleApps.mockReturnValue({
1123
+ apps: [longNameApp],
1124
+ loading: false,
1125
+ error: null,
1126
+ refetch: vi.fn(),
1127
+ });
1128
+
1129
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1130
+
1131
+ await waitFor(() => {
1132
+ const select = screen.getByTestId('select');
1133
+ expect(select).toBeInTheDocument();
1134
+ }, { timeout: TEST_TIMEOUT });
1135
+ });
1136
+
1137
+ it('handles special characters in app names', async () => {
1138
+ const specialCharApp = {
1139
+ id: 'app-1',
1140
+ name: 'APP-NAME',
1141
+ displayName: 'App with Special Chars',
1142
+ };
1143
+
1144
+ mockUseAccessibleApps.mockReturnValue({
1145
+ apps: [specialCharApp],
1146
+ loading: false,
1147
+ error: null,
1148
+ refetch: vi.fn(),
1149
+ });
1150
+
1151
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1152
+
1153
+ await waitFor(() => {
1154
+ const select = screen.getByTestId('select');
1155
+ expect(select).toBeInTheDocument();
1156
+ }, { timeout: TEST_TIMEOUT });
1157
+ });
1158
+
1159
+ it('handles currentAppName with special characters', () => {
1160
+ renderWithProviders(<AppSwitcher currentAppName="APP-NAME" />);
1161
+
1162
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
1163
+ });
1164
+
1165
+ it('handles empty currentAppName', () => {
1166
+ renderWithProviders(<AppSwitcher currentAppName="" />);
1167
+
1168
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
1169
+ });
1170
+
1171
+ it('handles undefined className', () => {
1172
+ renderWithProviders(<AppSwitcher currentAppName="CORE" className={undefined} />);
1173
+
1174
+ const select = screen.getByTestId('select');
1175
+ expect(select).toBeInTheDocument();
1176
+ });
1177
+ });
1178
+
1179
+ // Integration scenarios
1180
+ describe('Integration Scenarios', () => {
1181
+ it('handles full flow: load cache, fetch apps, save cache, render, select app', async () => {
1182
+ // Set up initial cache
1183
+ const cachedApps = [
1184
+ { id: 'app-1', name: 'CAKE', displayName: 'Cake App', logoUrl: '/icons/cake.svg', sortId: 10 },
1185
+ ];
1186
+
1187
+ if (typeof window !== 'undefined' && window.sessionStorage) {
1188
+ window.sessionStorage.setItem('pace-core:appswitcher:apps:user-1', JSON.stringify(cachedApps));
1189
+ }
1190
+
1191
+ // Mock apps to be fetched
1192
+ mockUseAccessibleApps.mockReturnValue({
1193
+ apps: mockApps,
1194
+ loading: false,
1195
+ error: null,
1196
+ refetch: vi.fn(),
1197
+ });
1198
+
1199
+ renderWithProviders(<AppSwitcher currentAppName="CORE" />);
1200
+
1201
+ await waitFor(() => {
1202
+ const select = screen.getByTestId('select');
1203
+ expect(select).toBeInTheDocument();
1204
+ }, { timeout: TEST_TIMEOUT });
1205
+
1206
+ // Verify cache was updated
1207
+ await waitFor(() => {
1208
+ if (typeof window !== 'undefined' && window.sessionStorage) {
1209
+ const cached = window.sessionStorage.getItem('pace-core:appswitcher:apps:user-1');
1210
+ expect(cached).toBeTruthy();
1211
+ }
1212
+ }, { timeout: TEST_TIMEOUT });
1213
+
1214
+ // Wait for apps to be processed and callback to be set
1215
+ await waitFor(() => {
1216
+ expect(storedOnValueChangeRef.current).toBeTruthy();
1217
+ }, { timeout: TEST_TIMEOUT });
1218
+
1219
+ // Simulate app selection using stored callback
1220
+ if (storedOnValueChangeRef.current) {
1221
+ storedOnValueChangeRef.current('CAKE');
1222
+ }
1223
+
1224
+ expect(mockWindowOpen).toHaveBeenCalled();
1225
+ });
1226
+
1227
+ it('handles rapid user changes', async () => {
1228
+ const { rerender } = renderWithProviders(
1229
+ <AppSwitcher currentAppName="CORE" />
1230
+ );
1231
+
1232
+ // Rapidly change users
1233
+ for (let i = 1; i <= 5; i++) {
1234
+ mockUseUnifiedAuth.mockReturnValue({
1235
+ user: { id: `user-${i}`, email: `test${i}@example.com` },
1236
+ session: null,
1237
+ isAuthenticated: true,
1238
+ loading: false,
1239
+ error: null,
1240
+ appName: 'CORE',
1241
+ });
1242
+
1243
+ rerender(<AppSwitcher currentAppName="CORE" />);
1244
+ }
1245
+
1246
+ // Should handle rapid changes without crashing
1247
+ expect(screen.queryByRole('combobox')).toBeInTheDocument();
1248
+ });
1249
+ });
1250
+ });