@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
@@ -1,26 +1,32 @@
1
1
  /**
2
- * @file PublicLayout Component Tests
2
+ * @file Integration Tests for PublicLayout Components
3
3
  * @package @jmruthers/pace-core
4
4
  * @module Components/PublicLayout
5
5
  * @since 1.0.0
6
- *
7
- * Comprehensive test suite for all PublicLayout components following testing standards.
8
- * Tests cover all major functionality, edge cases, user interactions, and accessibility.
6
+ *
7
+ * Integration tests for PublicLayout components focusing on observable behavior,
8
+ * user interactions, accessibility, and edge cases.
9
9
  */
10
10
 
11
11
  import React from 'react';
12
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
12
+ import { render, screen, waitFor } from '@testing-library/react';
13
13
  import userEvent from '@testing-library/user-event';
14
14
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
15
- import {
16
- PublicPageLayout,
17
- PublicPageHeader,
18
- PublicPageFooter
15
+ import {
16
+ PublicPageLayout,
17
+ PublicPageHeader,
18
+ PublicPageFooter
19
19
  } from './PublicPageLayout';
20
- import { PublicPageProvider, usePublicPageContext, useIsPublicPage } from './PublicPageProvider';
20
+ import { PublicPageProvider } from './PublicPageProvider';
21
+ import { usePublicPageContext, useIsPublicPage } from './usePublicPageContext';
22
+ import type { PublicPageContextType } from './PublicPageContext';
21
23
  import { useEventTheme } from '../../hooks/useEventTheme';
22
24
  import type { Event } from '../../types/event';
23
25
  import { assertOrganisationId } from '../../types/core';
26
+ import { logger } from '../../utils/core/logger';
27
+
28
+ // Following testing standards: use timeout parameter to prevent hanging
29
+ const TEST_TIMEOUT = 10000; // 10 seconds per test
24
30
 
25
31
  // === SHARED MOCKS ===
26
32
 
@@ -65,18 +71,22 @@ vi.mock('../../hooks/useAppConfig', () => ({
65
71
  }));
66
72
 
67
73
  vi.mock('../FileDisplay/FileDisplay', () => ({
68
- FileDisplay: vi.fn(({ table_name, record_id, organisation_id, category, className, generateFallbackText }) => (
69
- <div
70
- data-testid="event-logo"
71
- data-table-name={table_name}
72
- data-record-id={record_id}
73
- data-organisation-id={organisation_id}
74
- data-category={category}
75
- className={className}
76
- >
77
- Event Logo
78
- </div>
79
- ))
74
+ FileDisplay: vi.fn(({ table_name, record_id, organisation_id, category, className, generateFallbackText }) => {
75
+ const fallbackText = typeof generateFallbackText === 'function' ? generateFallbackText('') : '';
76
+ return (
77
+ <div
78
+ data-testid="event-logo"
79
+ data-table-name={table_name}
80
+ data-record-id={record_id}
81
+ data-organisation-id={organisation_id}
82
+ data-category={category}
83
+ data-fallback={fallbackText}
84
+ className={className}
85
+ >
86
+ {fallbackText || 'Event Logo'}
87
+ </div>
88
+ );
89
+ })
80
90
  }));
81
91
 
82
92
  vi.mock('../../utils/core/cn', () => ({
@@ -247,13 +257,13 @@ describe('[component] PublicPageLayout', () => {
247
257
  </PublicPageLayout>
248
258
  );
249
259
 
250
- // The loading state renders a <p> tag with grid and place-items-center classes
251
- // There may be multiple "Loading..." texts (from LoadingSpinner mock and actual text)
252
- // So we query by the container element using getAllByText and find the one in the <p> tag
253
- const loadingTexts = screen.getAllByText('Loading...');
254
- const loadingContainer = loadingTexts.find(text => text.closest('p'))?.closest('p');
260
+ // Loading state uses <main> with role="status" and layout classes
261
+ const loadingContainer = screen.getByRole('status');
255
262
  expect(loadingContainer).toBeInTheDocument();
256
- expect(loadingContainer).toHaveClass('grid', 'place-items-center', 'text-center', 'size-full');
263
+ expect(loadingContainer).toHaveClass('grid', 'place-items-center', 'text-center', 'size-full', 'gap-2');
264
+ expect(loadingContainer).toHaveAttribute('role', 'status');
265
+ expect(loadingContainer).toHaveAttribute('aria-busy', 'true');
266
+ expect(loadingContainer.querySelector('.sr-only')).toHaveTextContent('Loading');
257
267
  });
258
268
  });
259
269
 
@@ -272,6 +282,20 @@ describe('[component] PublicPageLayout', () => {
272
282
  expect(screen.getByText('Try Again')).toBeInTheDocument();
273
283
  });
274
284
 
285
+ it('error state has one h1 and Try Again is a button (accessibility)', () => {
286
+ const error = new Error('Event not found');
287
+ render(
288
+ <PublicPageLayout eventCode="EVENT123" error={error}>
289
+ <div>Test Content</div>
290
+ </PublicPageLayout>
291
+ );
292
+
293
+ const headings = screen.getAllByRole('heading', { level: 1 });
294
+ expect(headings).toHaveLength(1);
295
+ expect(headings[0]).toHaveTextContent('Event Not Found');
296
+ expect(screen.getByRole('button', { name: /try again/i })).toBeInTheDocument();
297
+ });
298
+
275
299
  it('calls refetch when Try Again button is clicked', async () => {
276
300
  const user = userEvent.setup();
277
301
  const mockRefetch = vi.fn().mockResolvedValue(undefined);
@@ -361,6 +385,19 @@ describe('[component] PublicPageLayout', () => {
361
385
  expect(screen.getByText('Try Again')).toBeInTheDocument();
362
386
  });
363
387
 
388
+ it('missing event state has one h1 and Try Again is a button (accessibility)', () => {
389
+ render(
390
+ <PublicPageLayout eventCode="EVENT123">
391
+ <div>Test Content</div>
392
+ </PublicPageLayout>
393
+ );
394
+
395
+ const headings = screen.getAllByRole('heading', { level: 1 });
396
+ expect(headings).toHaveLength(1);
397
+ expect(headings[0]).toHaveTextContent('Event Not Available');
398
+ expect(screen.getByRole('button', { name: /try again/i })).toBeInTheDocument();
399
+ });
400
+
364
401
  it('calls refetch when Try Again is clicked in missing event state', async () => {
365
402
  const user = userEvent.setup();
366
403
  const mockRefetch = vi.fn().mockResolvedValue(undefined);
@@ -476,6 +513,7 @@ describe('[component] PublicPageLayout', () => {
476
513
  );
477
514
 
478
515
  expect(screen.getByTestId('custom-footer')).toBeInTheDocument();
516
+ expect(screen.queryByRole('contentinfo')).not.toBeInTheDocument();
479
517
  });
480
518
 
481
519
  it('renders main content with correct classes', () => {
@@ -487,7 +525,7 @@ describe('[component] PublicPageLayout', () => {
487
525
 
488
526
  const main = container.querySelector('main');
489
527
  expect(main).toBeInTheDocument();
490
- expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
528
+ expect(main).toHaveClass('px-4', 'w-full', 'max-w-app-content-max', 'mx-auto', 'py-8');
491
529
  });
492
530
 
493
531
  it('wraps content in error boundary', () => {
@@ -501,6 +539,82 @@ describe('[component] PublicPageLayout', () => {
501
539
  });
502
540
  });
503
541
 
542
+ describe('Print orientation', () => {
543
+ it('applies print-page-landscape class in error state when printPageOrientation is landscape', () => {
544
+ const error = new Error('Event not found');
545
+ const { container } = render(
546
+ <PublicPageLayout eventCode="EVENT123" error={error} printPageOrientation="landscape">
547
+ <div>Test Content</div>
548
+ </PublicPageLayout>
549
+ );
550
+
551
+ const main = container.querySelector('main');
552
+ expect(main).toBeInTheDocument();
553
+ expect(main).toHaveClass('print-page-landscape');
554
+ });
555
+
556
+ it('applies print-page-portrait class in error state when printPageOrientation is portrait', () => {
557
+ const error = new Error('Event not found');
558
+ const { container } = render(
559
+ <PublicPageLayout eventCode="EVENT123" error={error} printPageOrientation="portrait">
560
+ <div>Test Content</div>
561
+ </PublicPageLayout>
562
+ );
563
+
564
+ const main = container.querySelector('main');
565
+ expect(main).toBeInTheDocument();
566
+ expect(main).toHaveClass('print-page-portrait');
567
+ });
568
+
569
+ it('applies print-page-landscape class in missing-event state when printPageOrientation is landscape', () => {
570
+ const { container } = render(
571
+ <PublicPageLayout eventCode="EVENT123" printPageOrientation="landscape">
572
+ <div>Test Content</div>
573
+ </PublicPageLayout>
574
+ );
575
+
576
+ const main = container.querySelector('main');
577
+ expect(main).toBeInTheDocument();
578
+ expect(main).toHaveClass('print-page-landscape');
579
+ });
580
+
581
+ it('applies print-page-portrait class in missing-event state when printPageOrientation is portrait', () => {
582
+ const { container } = render(
583
+ <PublicPageLayout eventCode="EVENT123" printPageOrientation="portrait">
584
+ <div>Test Content</div>
585
+ </PublicPageLayout>
586
+ );
587
+
588
+ const main = container.querySelector('main');
589
+ expect(main).toBeInTheDocument();
590
+ expect(main).toHaveClass('print-page-portrait');
591
+ });
592
+
593
+ it('applies print-page-landscape class to main content when printPageOrientation is landscape', () => {
594
+ const { container } = render(
595
+ <PublicPageLayout eventCode="EVENT123" event={mockEvent} printPageOrientation="landscape">
596
+ <div data-testid="content">Test Content</div>
597
+ </PublicPageLayout>
598
+ );
599
+
600
+ const mains = container.querySelectorAll('main');
601
+ expect(mains.length).toBe(1);
602
+ expect(mains[0]).toHaveClass('print-page-landscape');
603
+ });
604
+
605
+ it('applies print-page-portrait class to main content when printPageOrientation is portrait', () => {
606
+ const { container } = render(
607
+ <PublicPageLayout eventCode="EVENT123" event={mockEvent} printPageOrientation="portrait">
608
+ <div data-testid="content">Test Content</div>
609
+ </PublicPageLayout>
610
+ );
611
+
612
+ const mains = container.querySelectorAll('main');
613
+ expect(mains.length).toBe(1);
614
+ expect(mains[0]).toHaveClass('print-page-portrait');
615
+ });
616
+ });
617
+
504
618
  describe('Edge Cases', () => {
505
619
  it('handles empty eventCode', () => {
506
620
  render(
@@ -523,10 +637,10 @@ describe('[component] PublicPageLayout', () => {
523
637
  expect(screen.queryByText('Test Venue')).not.toBeInTheDocument();
524
638
  });
525
639
 
526
- it('handles deprecated className prop gracefully', () => {
640
+ it('handles unknown props gracefully', () => {
527
641
  const { container } = render(
528
- <PublicPageLayout
529
- eventCode="EVENT123"
642
+ <PublicPageLayout
643
+ eventCode="EVENT123"
530
644
  event={mockEvent}
531
645
  className="deprecated-class"
532
646
  >
@@ -534,9 +648,107 @@ describe('[component] PublicPageLayout', () => {
534
648
  </PublicPageLayout>
535
649
  );
536
650
 
537
- // Component should still render even with deprecated prop
651
+ // Component ignores unknown props and still renders
538
652
  expect(container).toBeInTheDocument();
539
- });
653
+ }, TEST_TIMEOUT);
654
+
655
+ it('handles theme application with invalid event data', () => {
656
+ const invalidEvent = {
657
+ ...mockEvent,
658
+ event_colours: null
659
+ } as Event;
660
+
661
+ render(
662
+ <PublicPageLayout eventCode="EVENT123" event={invalidEvent} showValidationErrors={false}>
663
+ <div data-testid="content">Test Content</div>
664
+ </PublicPageLayout>
665
+ );
666
+
667
+ expect(screen.getByTestId('content')).toBeInTheDocument();
668
+ // Should not crash even with invalid event data
669
+ }, TEST_TIMEOUT);
670
+
671
+ it('handles loading state transitions', async () => {
672
+ const { rerender } = render(
673
+ <PublicPageLayout eventCode="EVENT123" isLoading={true}>
674
+ <div>Content</div>
675
+ </PublicPageLayout>
676
+ );
677
+
678
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
679
+
680
+ rerender(
681
+ <PublicPageLayout eventCode="EVENT123" isLoading={false} event={mockEvent}>
682
+ <div>Content</div>
683
+ </PublicPageLayout>
684
+ );
685
+
686
+ expect(screen.queryByTestId('loading-spinner')).not.toBeInTheDocument();
687
+ expect(screen.getByText('Content')).toBeInTheDocument();
688
+ }, TEST_TIMEOUT);
689
+
690
+ it('handles error state recovery scenarios', async () => {
691
+ const error = new Error('Event not found');
692
+
693
+ const { rerender } = render(
694
+ <PublicPageLayout eventCode="EVENT123" error={error}>
695
+ <div>Content</div>
696
+ </PublicPageLayout>
697
+ );
698
+
699
+ expect(screen.getByText('Event Not Found')).toBeInTheDocument();
700
+
701
+ // Simulate recovery by removing error
702
+ rerender(
703
+ <PublicPageLayout eventCode="EVENT123" event={mockEvent}>
704
+ <div>Content</div>
705
+ </PublicPageLayout>
706
+ );
707
+
708
+ expect(screen.queryByText('Event Not Found')).not.toBeInTheDocument();
709
+ expect(screen.getByText('Content')).toBeInTheDocument();
710
+ }, TEST_TIMEOUT);
711
+
712
+ it('handles missing event with various refetch scenarios', async () => {
713
+ const user = userEvent.setup();
714
+ const mockRefetchPromise = vi.fn().mockResolvedValue(undefined);
715
+ const mockRefetchVoid = vi.fn();
716
+ const mockRefetchError = vi.fn().mockRejectedValue(new Error('Refetch failed'));
717
+
718
+ // Test with promise-returning refetch
719
+ const { rerender } = render(
720
+ <PublicPageLayout eventCode="EVENT123" refetch={mockRefetchPromise}>
721
+ <div>Content</div>
722
+ </PublicPageLayout>
723
+ );
724
+
725
+ const tryAgain1 = screen.getByText('Try Again');
726
+ await user.click(tryAgain1);
727
+ expect(mockRefetchPromise).toHaveBeenCalledTimes(1);
728
+
729
+ // Test with void-returning refetch
730
+ rerender(
731
+ <PublicPageLayout eventCode="EVENT123" refetch={mockRefetchVoid}>
732
+ <div>Content</div>
733
+ </PublicPageLayout>
734
+ );
735
+
736
+ const tryAgain2 = screen.getByText('Try Again');
737
+ await user.click(tryAgain2);
738
+ expect(mockRefetchVoid).toHaveBeenCalledTimes(1);
739
+
740
+ // Test with error-throwing refetch
741
+ rerender(
742
+ <PublicPageLayout eventCode="EVENT123" refetch={mockRefetchError}>
743
+ <div>Content</div>
744
+ </PublicPageLayout>
745
+ );
746
+
747
+ const tryAgain3 = screen.getByText('Try Again');
748
+ await user.click(tryAgain3);
749
+ expect(mockRefetchError).toHaveBeenCalledTimes(1);
750
+ // Should handle error gracefully
751
+ }, TEST_TIMEOUT);
540
752
  });
541
753
  });
542
754
 
@@ -649,10 +861,31 @@ describe('[component] PublicPageHeader', () => {
649
861
  );
650
862
 
651
863
  expect(screen.getByTestId('event-logo')).toBeInTheDocument();
652
- expect(screen.getByTestId('event-logo')).toHaveAttribute('data-table-name', 'event');
864
+ expect(screen.getByTestId('event-logo')).toHaveAttribute('data-table-name', 'core_events');
653
865
  expect(screen.getByTestId('event-logo')).toHaveAttribute('data-record-id', 'event-123');
654
866
  });
655
867
 
868
+ it('renders event logo fallback text from event name abbreviation (generateFallbackText)', () => {
869
+ render(
870
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123" />
871
+ );
872
+
873
+ const eventLogo = screen.getByTestId('event-logo');
874
+ expect(eventLogo).toHaveAttribute('data-fallback', 'TE');
875
+ expect(eventLogo).toHaveTextContent('TE');
876
+ });
877
+
878
+ it('renders event logo fallback EV when event has no event_name', () => {
879
+ const eventWithNoName = { ...mockEvent, event_name: '' };
880
+ render(
881
+ <PublicPageHeader event={eventWithNoName} eventCode="EVENT123" />
882
+ );
883
+
884
+ const eventLogo = screen.getByTestId('event-logo');
885
+ expect(eventLogo).toHaveAttribute('data-fallback', 'EV');
886
+ expect(eventLogo).toHaveTextContent('EV');
887
+ });
888
+
656
889
  it('hides event logo when showEventLogo is false', () => {
657
890
  render(
658
891
  <PublicPageHeader
@@ -707,6 +940,20 @@ describe('[component] PublicPageHeader', () => {
707
940
  expect(screen.getByText('Test Venue')).toBeInTheDocument();
708
941
  });
709
942
 
943
+ it('uses correct heading order (h1 then h4 when venue present) for accessibility', () => {
944
+ render(
945
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123" />
946
+ );
947
+
948
+ const header = screen.getByRole('banner');
949
+ expect(header.tagName).toBe('HEADER');
950
+ const h1 = header.querySelector('h1');
951
+ const h4 = header.querySelector('h4');
952
+ expect(h1).toBeInTheDocument();
953
+ expect(h4).toBeInTheDocument();
954
+ expect((h1!.compareDocumentPosition(h4!) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true);
955
+ });
956
+
710
957
  it('does not display venue when event has no venue', () => {
711
958
  render(
712
959
  <PublicPageHeader event={mockEventWithoutVenue} eventCode="EVENT123" />
@@ -1060,6 +1307,41 @@ describe('[component] PublicPageProvider', () => {
1060
1307
  expect(screen.getByTestId('url')).toHaveTextContent('https://test.supabase.co');
1061
1308
  expect(screen.getByTestId('key')).toHaveTextContent('test-key');
1062
1309
  });
1310
+
1311
+ it('uses process.env when import.meta.env is undefined (getEnvVar process.env branch)', () => {
1312
+ mockCreateClient.mockClear();
1313
+ Object.defineProperty(import.meta, 'env', {
1314
+ value: undefined,
1315
+ writable: true,
1316
+ configurable: true
1317
+ });
1318
+ const originalProcessEnv = process.env;
1319
+ process.env = {
1320
+ ...originalProcessEnv,
1321
+ VITE_SUPABASE_URL: 'https://process-env.supabase.co',
1322
+ VITE_SUPABASE_PUBLISHABLE_KEY: 'process-env-key'
1323
+ };
1324
+
1325
+ const TestComponent = () => {
1326
+ const context = usePublicPageContext();
1327
+ return (
1328
+ <div>
1329
+ <span data-testid="url">{context.environment.supabaseUrl || 'no-url'}</span>
1330
+ <span data-testid="key">{context.environment.supabaseKey || 'no-key'}</span>
1331
+ </div>
1332
+ );
1333
+ };
1334
+
1335
+ render(
1336
+ <PublicPageProvider>
1337
+ <TestComponent />
1338
+ </PublicPageProvider>
1339
+ );
1340
+
1341
+ expect(screen.getByTestId('url')).toHaveTextContent('https://process-env.supabase.co');
1342
+ expect(screen.getByTestId('key')).toHaveTextContent('process-env-key');
1343
+ expect(mockCreateClient).toHaveBeenCalledWith('https://process-env.supabase.co', 'process-env-key');
1344
+ });
1063
1345
  });
1064
1346
 
1065
1347
  describe('Supabase Client Creation', () => {
@@ -1119,6 +1401,10 @@ describe('[component] PublicPageProvider', () => {
1119
1401
  );
1120
1402
 
1121
1403
  expect(screen.getByText('no-client')).toBeInTheDocument();
1404
+ expect(logger.warn).toHaveBeenCalledWith(
1405
+ 'PublicPageProvider',
1406
+ expect.stringContaining('Missing Supabase')
1407
+ );
1122
1408
  });
1123
1409
 
1124
1410
  it('supports NEXT_PUBLIC_ prefixed environment variables', () => {
@@ -1178,7 +1464,9 @@ describe('[component] PublicPageProvider', () => {
1178
1464
 
1179
1465
  render(<TestComponent />);
1180
1466
 
1181
- expect(screen.getByText('usePublicPageContext must be used within a PublicPageProvider')).toBeInTheDocument();
1467
+ const message = screen.getByText('usePublicPageContext must be used within a PublicPageProvider');
1468
+ expect(message).toBeInTheDocument();
1469
+ expect(message.textContent).toContain('PublicPageProvider');
1182
1470
 
1183
1471
  console.error = consoleError;
1184
1472
  });
@@ -1193,7 +1481,7 @@ describe('[component] PublicPageProvider', () => {
1193
1481
 
1194
1482
  render(<TestComponent />);
1195
1483
  expect(screen.getByText('false')).toBeInTheDocument();
1196
- });
1484
+ }, TEST_TIMEOUT);
1197
1485
 
1198
1486
  it('returns true when used inside provider', () => {
1199
1487
  const TestComponent = () => {
@@ -1208,7 +1496,1321 @@ describe('[component] PublicPageProvider', () => {
1208
1496
  );
1209
1497
 
1210
1498
  expect(screen.getByText('true')).toBeInTheDocument();
1211
- });
1499
+ }, TEST_TIMEOUT);
1500
+ });
1501
+
1502
+ describe('Edge Cases', () => {
1503
+ it('handles empty string appName', () => {
1504
+ const TestComponent = () => {
1505
+ const context = usePublicPageContext();
1506
+ return <div>{context.appName === null ? 'null' : context.appName || 'empty'}</div>;
1507
+ };
1508
+
1509
+ render(
1510
+ <PublicPageProvider appName="">
1511
+ <TestComponent />
1512
+ </PublicPageProvider>
1513
+ );
1514
+
1515
+ // Empty string should be treated as falsy and become null
1516
+ expect(screen.getByText('null')).toBeInTheDocument();
1517
+ }, TEST_TIMEOUT);
1518
+
1519
+ it('handles very long appName', () => {
1520
+ const longAppName = 'A'.repeat(1000);
1521
+ const TestComponent = () => {
1522
+ const context = usePublicPageContext();
1523
+ return <div>{context.appName}</div>;
1524
+ };
1525
+
1526
+ render(
1527
+ <PublicPageProvider appName={longAppName}>
1528
+ <TestComponent />
1529
+ </PublicPageProvider>
1530
+ );
1531
+
1532
+ expect(screen.getByText(longAppName)).toBeInTheDocument();
1533
+ }, TEST_TIMEOUT);
1534
+
1535
+ it('handles special characters in appName', () => {
1536
+ const specialAppName = 'Test-App_123 & Co.';
1537
+ const TestComponent = () => {
1538
+ const context = usePublicPageContext();
1539
+ return <div>{context.appName}</div>;
1540
+ };
1541
+
1542
+ render(
1543
+ <PublicPageProvider appName={specialAppName}>
1544
+ <TestComponent />
1545
+ </PublicPageProvider>
1546
+ );
1547
+
1548
+ expect(screen.getByText(specialAppName)).toBeInTheDocument();
1549
+ }, TEST_TIMEOUT);
1550
+
1551
+ it('handles nested providers (uses innermost)', () => {
1552
+ const TestComponent = () => {
1553
+ const context = usePublicPageContext();
1554
+ return <div>{context.appName || 'no-app'}</div>;
1555
+ };
1556
+
1557
+ render(
1558
+ <PublicPageProvider appName="Outer">
1559
+ <PublicPageProvider appName="Inner">
1560
+ <TestComponent />
1561
+ </PublicPageProvider>
1562
+ </PublicPageProvider>
1563
+ );
1564
+
1565
+ // Should use the innermost provider's appName
1566
+ expect(screen.getByText('Inner')).toBeInTheDocument();
1567
+ }, TEST_TIMEOUT);
1568
+
1569
+ it('handles context value stability on re-render', () => {
1570
+ const TestComponent = () => {
1571
+ const context = usePublicPageContext();
1572
+ return <div data-testid="context">{context.appName || 'no-app'}</div>;
1573
+ };
1574
+
1575
+ const { rerender } = render(
1576
+ <PublicPageProvider appName="TestApp">
1577
+ <TestComponent />
1578
+ </PublicPageProvider>
1579
+ );
1580
+
1581
+ const firstRender = screen.getByTestId('context');
1582
+ expect(firstRender).toHaveTextContent('TestApp');
1583
+
1584
+ rerender(
1585
+ <PublicPageProvider appName="TestApp">
1586
+ <TestComponent />
1587
+ </PublicPageProvider>
1588
+ );
1589
+
1590
+ const secondRender = screen.getByTestId('context');
1591
+ expect(secondRender).toHaveTextContent('TestApp');
1592
+ }, TEST_TIMEOUT);
1593
+
1594
+ it('handles appName prop change', () => {
1595
+ const TestComponent = () => {
1596
+ const context = usePublicPageContext();
1597
+ return <div>{context.appName || 'no-app'}</div>;
1598
+ };
1599
+
1600
+ const { rerender } = render(
1601
+ <PublicPageProvider appName="App1">
1602
+ <TestComponent />
1603
+ </PublicPageProvider>
1604
+ );
1605
+
1606
+ expect(screen.getByText('App1')).toBeInTheDocument();
1607
+
1608
+ rerender(
1609
+ <PublicPageProvider appName="App2">
1610
+ <TestComponent />
1611
+ </PublicPageProvider>
1612
+ );
1613
+
1614
+ expect(screen.getByText('App2')).toBeInTheDocument();
1615
+ }, TEST_TIMEOUT);
1616
+ });
1617
+ });
1618
+
1619
+ // === ADDITIONAL EDGE CASES AND IMPROVED COVERAGE ===
1620
+
1621
+ describe('[component] PublicPageLayout - Additional Edge Cases', () => {
1622
+ const mockUseEventTheme = vi.mocked(useEventTheme);
1623
+
1624
+ beforeEach(() => {
1625
+ vi.clearAllMocks();
1626
+ });
1627
+
1628
+ describe('Loading State Edge Cases', () => {
1629
+ it('handles loading state with empty loadingMessage', () => {
1630
+ render(
1631
+ <PublicPageLayout eventCode="EVENT123" isLoading={true} loadingMessage="">
1632
+ <div>Content</div>
1633
+ </PublicPageLayout>
1634
+ );
1635
+
1636
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
1637
+ // When loadingMessage is empty, it defaults to 'Loading...'
1638
+ const loadingTexts = screen.getAllByText('Loading...');
1639
+ expect(loadingTexts.length).toBeGreaterThan(0);
1640
+ });
1641
+
1642
+ it('handles loading state with whitespace-only loadingMessage', () => {
1643
+ const { container } = render(
1644
+ <PublicPageLayout eventCode="EVENT123" isLoading={true} loadingMessage=" ">
1645
+ <div>Content</div>
1646
+ </PublicPageLayout>
1647
+ );
1648
+
1649
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
1650
+ // Check that the container exists and contains the whitespace message
1651
+ const loadingContainer = container.querySelector('.grid.place-items-center');
1652
+ expect(loadingContainer).toBeInTheDocument();
1653
+ expect(loadingContainer?.textContent).toContain(' ');
1654
+ });
1655
+
1656
+ it('handles loading state transition from true to false with event', async () => {
1657
+ const { rerender } = render(
1658
+ <PublicPageLayout eventCode="EVENT123" isLoading={true}>
1659
+ <div>Content</div>
1660
+ </PublicPageLayout>
1661
+ );
1662
+
1663
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
1664
+
1665
+ rerender(
1666
+ <PublicPageLayout eventCode="EVENT123" isLoading={false} event={mockEvent}>
1667
+ <div>Content</div>
1668
+ </PublicPageLayout>
1669
+ );
1670
+
1671
+ expect(screen.queryByTestId('loading-spinner')).not.toBeInTheDocument();
1672
+ expect(screen.getByText('Content')).toBeInTheDocument();
1673
+ });
1674
+ });
1675
+
1676
+ describe('Error State Edge Cases', () => {
1677
+ it('handles error with empty message', () => {
1678
+ const error = new Error('');
1679
+ render(
1680
+ <PublicPageLayout eventCode="EVENT123" error={error}>
1681
+ <div>Content</div>
1682
+ </PublicPageLayout>
1683
+ );
1684
+
1685
+ expect(screen.getByText('Event Not Found')).toBeInTheDocument();
1686
+ });
1687
+
1688
+ it('handles error with very long message', () => {
1689
+ const longMessage = 'A'.repeat(1000);
1690
+ const error = new Error(longMessage);
1691
+ render(
1692
+ <PublicPageLayout eventCode="EVENT123" error={error}>
1693
+ <div>Content</div>
1694
+ </PublicPageLayout>
1695
+ );
1696
+
1697
+ expect(screen.getByText('Event Not Found')).toBeInTheDocument();
1698
+ });
1699
+
1700
+ it('handles error state with custom error fallback that receives correct props', () => {
1701
+ const mockRetry = vi.fn();
1702
+ const ErrorFallback = ({ error, retry }: { error: Error; retry: () => void }) => {
1703
+ // Verify props are passed correctly
1704
+ expect(error).toBeInstanceOf(Error);
1705
+ expect(error.message).toBe('Test error');
1706
+ expect(typeof retry).toBe('function');
1707
+ return <div data-testid="custom-error-fallback">Custom Error: {error.message}</div>;
1708
+ };
1709
+ const error = new Error('Test error');
1710
+
1711
+ render(
1712
+ <PublicPageLayout
1713
+ eventCode="EVENT123"
1714
+ error={error}
1715
+ errorFallback={ErrorFallback}
1716
+ refetch={mockRetry}
1717
+ >
1718
+ <div>Content</div>
1719
+ </PublicPageLayout>
1720
+ );
1721
+
1722
+ // Custom error fallback should be rendered with correct props
1723
+ expect(screen.getByTestId('custom-error-fallback')).toBeInTheDocument();
1724
+ expect(screen.getByText('Custom Error: Test error')).toBeInTheDocument();
1725
+ });
1726
+
1727
+ it('handles error state with refetch that throws error', async () => {
1728
+ const user = userEvent.setup();
1729
+ const error = new Error('Test error');
1730
+ const mockRefetch = vi.fn().mockRejectedValue(new Error('Refetch failed'));
1731
+
1732
+ render(
1733
+ <PublicPageLayout eventCode="EVENT123" error={error} refetch={mockRefetch}>
1734
+ <div>Content</div>
1735
+ </PublicPageLayout>
1736
+ );
1737
+
1738
+ const tryAgainButton = screen.getByText('Try Again');
1739
+ await user.click(tryAgainButton);
1740
+
1741
+ expect(mockRefetch).toHaveBeenCalledTimes(1);
1742
+ // Should handle error gracefully
1743
+ });
1744
+ });
1745
+
1746
+ describe('Missing Event Edge Cases', () => {
1747
+ it('handles missing event with showValidationErrors false and showFooter true', () => {
1748
+ render(
1749
+ <PublicPageLayout
1750
+ eventCode="EVENT123"
1751
+ showValidationErrors={false}
1752
+ showFooter={true}
1753
+ >
1754
+ <div data-testid="content">Content</div>
1755
+ </PublicPageLayout>
1756
+ );
1757
+
1758
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1759
+ // Footer should not render when event is missing
1760
+ expect(screen.queryByRole('contentinfo')).not.toBeInTheDocument();
1761
+ });
1762
+
1763
+ it('handles missing event with custom header and footer', () => {
1764
+ const customHeader = <div data-testid="custom-header">Custom Header</div>;
1765
+ const customFooter = <div data-testid="custom-footer">Custom Footer</div>;
1766
+
1767
+ render(
1768
+ <PublicPageLayout
1769
+ eventCode="EVENT123"
1770
+ showValidationErrors={false}
1771
+ customHeader={customHeader}
1772
+ customFooter={customFooter}
1773
+ showFooter={true}
1774
+ >
1775
+ <div data-testid="content">Content</div>
1776
+ </PublicPageLayout>
1777
+ );
1778
+
1779
+ expect(screen.getByTestId('custom-header')).toBeInTheDocument();
1780
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1781
+ // Custom footer should not render when event is missing
1782
+ expect(screen.queryByTestId('custom-footer')).not.toBeInTheDocument();
1783
+ });
1784
+ });
1785
+
1786
+ describe('Custom Components Edge Cases', () => {
1787
+ it('handles custom header that returns null', () => {
1788
+ const CustomHeader = () => null;
1789
+
1790
+ render(
1791
+ <PublicPageLayout
1792
+ eventCode="EVENT123"
1793
+ event={mockEvent}
1794
+ customHeader={<CustomHeader />}
1795
+ >
1796
+ <div data-testid="content">Content</div>
1797
+ </PublicPageLayout>
1798
+ );
1799
+
1800
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1801
+ expect(screen.queryByRole('banner')).not.toBeInTheDocument();
1802
+ });
1803
+
1804
+ it('handles custom footer that returns null', () => {
1805
+ const CustomFooter = () => null;
1806
+
1807
+ render(
1808
+ <PublicPageLayout
1809
+ eventCode="EVENT123"
1810
+ event={mockEvent}
1811
+ customFooter={<CustomFooter />}
1812
+ showFooter={true}
1813
+ >
1814
+ <div data-testid="content">Content</div>
1815
+ </PublicPageLayout>
1816
+ );
1817
+
1818
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1819
+ expect(screen.queryByRole('contentinfo')).not.toBeInTheDocument();
1820
+ });
1821
+
1822
+ it('handles custom loading fallback that returns null', () => {
1823
+ const CustomLoading = () => null;
1824
+
1825
+ render(
1826
+ <PublicPageLayout
1827
+ eventCode="EVENT123"
1828
+ isLoading={true}
1829
+ loadingFallback={CustomLoading}
1830
+ >
1831
+ <div>Content</div>
1832
+ </PublicPageLayout>
1833
+ );
1834
+
1835
+ // Should render nothing (null)
1836
+ expect(screen.queryByTestId('loading-spinner')).not.toBeInTheDocument();
1837
+ });
1838
+ });
1839
+
1840
+ describe('Event Code Edge Cases', () => {
1841
+ it('handles very long event code', () => {
1842
+ const longEventCode = 'A'.repeat(1000);
1843
+ render(
1844
+ <PublicPageLayout eventCode={longEventCode} event={mockEvent}>
1845
+ <div data-testid="content">Content</div>
1846
+ </PublicPageLayout>
1847
+ );
1848
+
1849
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1850
+ });
1851
+
1852
+ it('handles event code with special characters', () => {
1853
+ const specialEventCode = 'EVENT-123_TEST@456';
1854
+ render(
1855
+ <PublicPageLayout eventCode={specialEventCode} event={mockEvent}>
1856
+ <div data-testid="content">Content</div>
1857
+ </PublicPageLayout>
1858
+ );
1859
+
1860
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1861
+ });
1862
+
1863
+ it('handles event code with unicode characters', () => {
1864
+ const unicodeEventCode = 'EVENT-测试-123';
1865
+ render(
1866
+ <PublicPageLayout eventCode={unicodeEventCode} event={mockEvent}>
1867
+ <div data-testid="content">Content</div>
1868
+ </PublicPageLayout>
1869
+ );
1870
+
1871
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1872
+ });
1873
+ });
1874
+
1875
+ describe('Theme Application Edge Cases', () => {
1876
+ it('applies theme with event that has empty event_colours object', () => {
1877
+ const eventWithEmptyColours = {
1878
+ ...mockEvent,
1879
+ event_colours: {}
1880
+ } as Event;
1881
+
1882
+ render(
1883
+ <PublicPageLayout eventCode="EVENT123" event={eventWithEmptyColours}>
1884
+ <div data-testid="content">Content</div>
1885
+ </PublicPageLayout>
1886
+ );
1887
+
1888
+ expect(mockUseEventTheme).toHaveBeenCalledWith(eventWithEmptyColours);
1889
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1890
+ });
1891
+
1892
+ it('applies theme with event that has partial event_colours', () => {
1893
+ const eventWithPartialColours = {
1894
+ ...mockEvent,
1895
+ event_colours: {
1896
+ main: { '500': { L: 0.5, C: 0.2, H: 0 } }
1897
+ }
1898
+ } as Event;
1899
+
1900
+ render(
1901
+ <PublicPageLayout eventCode="EVENT123" event={eventWithPartialColours}>
1902
+ <div data-testid="content">Content</div>
1903
+ </PublicPageLayout>
1904
+ );
1905
+
1906
+ expect(mockUseEventTheme).toHaveBeenCalledWith(eventWithPartialColours);
1907
+ expect(screen.getByTestId('content')).toBeInTheDocument();
1908
+ });
1909
+ });
1910
+
1911
+ describe('Refetch Function Edge Cases', () => {
1912
+ it('handles refetch that returns undefined', async () => {
1913
+ const user = userEvent.setup();
1914
+ const mockRefetch = vi.fn().mockReturnValue(undefined);
1915
+ const error = new Error('Test error');
1916
+
1917
+ render(
1918
+ <PublicPageLayout eventCode="EVENT123" error={error} refetch={mockRefetch}>
1919
+ <div>Content</div>
1920
+ </PublicPageLayout>
1921
+ );
1922
+
1923
+ const tryAgainButton = screen.getByText('Try Again');
1924
+ await user.click(tryAgainButton);
1925
+
1926
+ expect(mockRefetch).toHaveBeenCalledTimes(1);
1927
+ });
1928
+
1929
+ it('handles refetch that returns a resolved promise', async () => {
1930
+ const user = userEvent.setup();
1931
+ const mockRefetch = vi.fn().mockResolvedValue(undefined);
1932
+ const error = new Error('Test error');
1933
+
1934
+ render(
1935
+ <PublicPageLayout eventCode="EVENT123" error={error} refetch={mockRefetch}>
1936
+ <div>Content</div>
1937
+ </PublicPageLayout>
1938
+ );
1939
+
1940
+ const tryAgainButton = screen.getByText('Try Again');
1941
+ await user.click(tryAgainButton);
1942
+
1943
+ await waitFor(() => {
1944
+ expect(mockRefetch).toHaveBeenCalledTimes(1);
1945
+ });
1946
+ });
1947
+ });
1948
+ });
1949
+
1950
+ describe('[component] PublicPageHeader - Additional Edge Cases', () => {
1951
+ beforeEach(() => {
1952
+ vi.clearAllMocks();
1953
+ mockUseAppConfig.mockReturnValue({
1954
+ appName: 'Test App'
1955
+ });
1956
+ });
1957
+
1958
+ describe('Title and Description Edge Cases', () => {
1959
+ it('handles title with empty string', () => {
1960
+ render(
1961
+ <PublicPageHeader
1962
+ event={mockEvent}
1963
+ eventCode="EVENT123"
1964
+ title=""
1965
+ />
1966
+ );
1967
+
1968
+ // Empty title should still render as an h1 element
1969
+ // When title is provided (even if empty), it overrides event name
1970
+ const headings = screen.getAllByRole('heading', { level: 1 });
1971
+ // Should have at least one heading (the empty title)
1972
+ expect(headings.length).toBeGreaterThan(0);
1973
+ });
1974
+
1975
+ it('handles title with only whitespace', () => {
1976
+ render(
1977
+ <PublicPageHeader
1978
+ event={mockEvent}
1979
+ eventCode="EVENT123"
1980
+ title=" "
1981
+ />
1982
+ );
1983
+
1984
+ const headings = screen.getAllByRole('heading', { level: 1 });
1985
+ expect(headings.some(h => h.textContent === ' ')).toBe(true);
1986
+ });
1987
+
1988
+ it('handles description with empty string when title is provided', () => {
1989
+ render(
1990
+ <PublicPageHeader
1991
+ event={mockEvent}
1992
+ eventCode="EVENT123"
1993
+ title="Page Title"
1994
+ description=""
1995
+ />
1996
+ );
1997
+
1998
+ expect(screen.getByText('Page Title')).toBeInTheDocument();
1999
+ // Empty description should not render a paragraph
2000
+ // Check that there's no paragraph with empty text content
2001
+ const paragraphs = screen.queryAllByRole('paragraph');
2002
+ const emptyParagraphs = paragraphs.filter(p => p.textContent === '');
2003
+ expect(emptyParagraphs.length).toBe(0);
2004
+ });
2005
+
2006
+ it('handles very long title', () => {
2007
+ const longTitle = 'A'.repeat(1000);
2008
+ render(
2009
+ <PublicPageHeader
2010
+ event={mockEvent}
2011
+ eventCode="EVENT123"
2012
+ title={longTitle}
2013
+ />
2014
+ );
2015
+
2016
+ expect(screen.getByText(longTitle)).toBeInTheDocument();
2017
+ });
2018
+
2019
+ it('handles very long description', () => {
2020
+ const longDescription = 'A'.repeat(1000);
2021
+ render(
2022
+ <PublicPageHeader
2023
+ event={mockEvent}
2024
+ eventCode="EVENT123"
2025
+ title="Page Title"
2026
+ description={longDescription}
2027
+ />
2028
+ );
2029
+
2030
+ expect(screen.getByText(longDescription)).toBeInTheDocument();
2031
+ });
2032
+
2033
+ it('handles title with special characters', () => {
2034
+ const specialTitle = 'Title & Co. <Test> "Quote"';
2035
+ render(
2036
+ <PublicPageHeader
2037
+ event={mockEvent}
2038
+ eventCode="EVENT123"
2039
+ title={specialTitle}
2040
+ />
2041
+ );
2042
+
2043
+ expect(screen.getByText(specialTitle)).toBeInTheDocument();
2044
+ });
2045
+
2046
+ it('handles description with HTML-like content', () => {
2047
+ const htmlLikeDescription = '<script>alert("xss")</script>Safe content';
2048
+ render(
2049
+ <PublicPageHeader
2050
+ event={mockEvent}
2051
+ eventCode="EVENT123"
2052
+ title="Page Title"
2053
+ description={htmlLikeDescription}
2054
+ />
2055
+ );
2056
+
2057
+ // Should render as text, not execute HTML
2058
+ expect(screen.getByText(htmlLikeDescription)).toBeInTheDocument();
2059
+ });
2060
+ });
2061
+
2062
+ describe('Event Information Edge Cases', () => {
2063
+ it('handles event with empty event_name', () => {
2064
+ const eventWithEmptyName = {
2065
+ ...mockEvent,
2066
+ event_name: ''
2067
+ };
2068
+
2069
+ render(
2070
+ <PublicPageHeader event={eventWithEmptyName} eventCode="EVENT123" />
2071
+ );
2072
+
2073
+ const headings = screen.getAllByRole('heading', { level: 1 });
2074
+ expect(headings.some(h => h.textContent === '')).toBe(true);
2075
+ });
2076
+
2077
+ it('handles event with very long event_name', () => {
2078
+ const longEventName = 'A'.repeat(1000);
2079
+ const eventWithLongName = {
2080
+ ...mockEvent,
2081
+ event_name: longEventName
2082
+ };
2083
+
2084
+ render(
2085
+ <PublicPageHeader event={eventWithLongName} eventCode="EVENT123" />
2086
+ );
2087
+
2088
+ expect(screen.getByText(longEventName)).toBeInTheDocument();
2089
+ });
2090
+
2091
+ it('handles event with very long event_venue', () => {
2092
+ const longVenue = 'A'.repeat(1000);
2093
+ const eventWithLongVenue = {
2094
+ ...mockEvent,
2095
+ event_venue: longVenue
2096
+ };
2097
+
2098
+ render(
2099
+ <PublicPageHeader event={eventWithLongVenue} eventCode="EVENT123" />
2100
+ );
2101
+
2102
+ expect(screen.getByText(longVenue)).toBeInTheDocument();
2103
+ });
2104
+
2105
+ it('handles event with venue containing special characters', () => {
2106
+ const specialVenue = 'Venue & Co. <Test> "Quote"';
2107
+ const eventWithSpecialVenue = {
2108
+ ...mockEvent,
2109
+ event_venue: specialVenue
2110
+ };
2111
+
2112
+ render(
2113
+ <PublicPageHeader event={eventWithSpecialVenue} eventCode="EVENT123" />
2114
+ );
2115
+
2116
+ expect(screen.getByText(specialVenue)).toBeInTheDocument();
2117
+ });
2118
+ });
2119
+
2120
+ describe('Logo Display Edge Cases', () => {
2121
+ it('handles appName with special characters in logo path', () => {
2122
+ mockUseAppConfig.mockReturnValueOnce({
2123
+ appName: 'Test-App_123'
2124
+ });
2125
+
2126
+ render(
2127
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123" />
2128
+ );
2129
+
2130
+ const appLogo = screen.getByAltText('Test-App_123');
2131
+ expect(appLogo).toHaveAttribute('src', '/test-app_123_logo_wide.svg');
2132
+ });
2133
+
2134
+ it('handles appName with unicode characters', () => {
2135
+ mockUseAppConfig.mockReturnValueOnce({
2136
+ appName: '测试App'
2137
+ });
2138
+
2139
+ render(
2140
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123" />
2141
+ );
2142
+
2143
+ const appLogo = screen.getByAltText('测试App');
2144
+ expect(appLogo).toHaveAttribute('src', '/测试app_logo_wide.svg');
2145
+ });
2146
+
2147
+ it('handles customEventLogo that returns null', () => {
2148
+ const CustomLogo = () => null;
2149
+
2150
+ render(
2151
+ <PublicPageHeader
2152
+ event={mockEvent}
2153
+ eventCode="EVENT123"
2154
+ showEventLogo={true}
2155
+ customEventLogo={<CustomLogo />}
2156
+ />
2157
+ );
2158
+
2159
+ // When customEventLogo is provided but returns null,
2160
+ // the component should not render the default event logo
2161
+ // (customEventLogo takes precedence even if it's null)
2162
+ expect(screen.queryByTestId('event-logo')).not.toBeInTheDocument();
2163
+ });
2164
+ });
2165
+
2166
+ describe('Children Edge Cases', () => {
2167
+ it('handles multiple children', () => {
2168
+ render(
2169
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123">
2170
+ <div data-testid="child1">Child 1</div>
2171
+ <div data-testid="child2">Child 2</div>
2172
+ <div data-testid="child3">Child 3</div>
2173
+ </PublicPageHeader>
2174
+ );
2175
+
2176
+ expect(screen.getByTestId('child1')).toBeInTheDocument();
2177
+ expect(screen.getByTestId('child2')).toBeInTheDocument();
2178
+ expect(screen.getByTestId('child3')).toBeInTheDocument();
2179
+ });
2180
+
2181
+ it('handles children that return null', () => {
2182
+ const NullChild = () => null;
2183
+
2184
+ render(
2185
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123">
2186
+ <NullChild />
2187
+ <div data-testid="valid-child">Valid Child</div>
2188
+ </PublicPageHeader>
2189
+ );
2190
+
2191
+ expect(screen.getByTestId('valid-child')).toBeInTheDocument();
2192
+ });
2193
+
2194
+ it('handles empty children array', () => {
2195
+ render(
2196
+ <PublicPageHeader event={mockEvent} eventCode="EVENT123">
2197
+ {[]}
2198
+ </PublicPageHeader>
2199
+ );
2200
+
2201
+ expect(screen.getByRole('banner')).toBeInTheDocument();
2202
+ });
2203
+ });
2204
+ });
2205
+
2206
+ describe('[component] PublicPageFooter - Additional Edge Cases', () => {
2207
+ beforeEach(() => {
2208
+ vi.clearAllMocks();
2209
+ });
2210
+
2211
+ describe('Copyright Text Edge Cases', () => {
2212
+ it('handles year in the past', () => {
2213
+ render(
2214
+ <PublicPageFooter
2215
+ event={mockEvent}
2216
+ year={2020}
2217
+ />
2218
+ );
2219
+
2220
+ expect(screen.getByText(/© Copyright 2022–2020/)).toBeInTheDocument();
2221
+ });
2222
+
2223
+ it('handles year in the future', () => {
2224
+ const futureYear = new Date().getFullYear() + 10;
2225
+ render(
2226
+ <PublicPageFooter
2227
+ event={mockEvent}
2228
+ year={futureYear}
2229
+ />
2230
+ );
2231
+
2232
+ expect(screen.getByText(new RegExp(`© Copyright 2022–${futureYear}`))).toBeInTheDocument();
2233
+ });
2234
+
2235
+ it('handles year of 0', () => {
2236
+ render(
2237
+ <PublicPageFooter
2238
+ event={mockEvent}
2239
+ year={0}
2240
+ />
2241
+ );
2242
+
2243
+ expect(screen.getByText(/© Copyright 2022–0/)).toBeInTheDocument();
2244
+ });
2245
+
2246
+ it('handles copyright with special characters', () => {
2247
+ const specialCopyright = '© Copyright & Co. <Test> "Quote"';
2248
+ render(
2249
+ <PublicPageFooter
2250
+ event={mockEvent}
2251
+ copyright={specialCopyright}
2252
+ />
2253
+ );
2254
+
2255
+ expect(screen.getByText(specialCopyright)).toBeInTheDocument();
2256
+ });
2257
+
2258
+ it('handles very long copyright text', () => {
2259
+ const longCopyright = 'A'.repeat(1000);
2260
+ render(
2261
+ <PublicPageFooter
2262
+ event={mockEvent}
2263
+ copyright={longCopyright}
2264
+ />
2265
+ );
2266
+
2267
+ expect(screen.getByText(longCopyright)).toBeInTheDocument();
2268
+ });
2269
+ });
2270
+
2271
+ describe('Company Name Edge Cases', () => {
2272
+ it('handles company name with special characters', () => {
2273
+ const specialCompany = 'Company & Co. <Test> "Quote"';
2274
+ render(
2275
+ <PublicPageFooter
2276
+ event={mockEvent}
2277
+ companyName={specialCompany}
2278
+ />
2279
+ );
2280
+
2281
+ expect(screen.getByText(new RegExp(specialCompany))).toBeInTheDocument();
2282
+ });
2283
+
2284
+ it('handles very long company name', () => {
2285
+ const longCompany = 'A'.repeat(1000);
2286
+ render(
2287
+ <PublicPageFooter
2288
+ event={mockEvent}
2289
+ companyName={longCompany}
2290
+ />
2291
+ );
2292
+
2293
+ expect(screen.getByText(new RegExp(longCompany))).toBeInTheDocument();
2294
+ });
2295
+
2296
+ it('handles empty company name', () => {
2297
+ render(
2298
+ <PublicPageFooter
2299
+ event={mockEvent}
2300
+ companyName=""
2301
+ />
2302
+ );
2303
+
2304
+ expect(screen.getByText(/© Copyright 2022–/)).toBeInTheDocument();
2305
+ });
2306
+ });
2307
+
2308
+ describe('Logo Edge Cases', () => {
2309
+ it('handles logo with relative path', () => {
2310
+ render(
2311
+ <PublicPageFooter
2312
+ event={mockEvent}
2313
+ logo="./logo.png"
2314
+ />
2315
+ );
2316
+
2317
+ const logo = screen.getByAltText('Logo');
2318
+ expect(logo).toHaveAttribute('src', './logo.png');
2319
+ });
2320
+
2321
+ it('handles logo with absolute URL', () => {
2322
+ render(
2323
+ <PublicPageFooter
2324
+ event={mockEvent}
2325
+ logo="https://example.com/logo.png"
2326
+ />
2327
+ );
2328
+
2329
+ const logo = screen.getByAltText('Logo');
2330
+ expect(logo).toHaveAttribute('src', 'https://example.com/logo.png');
2331
+ });
2332
+
2333
+ it('handles logo with data URL', () => {
2334
+ const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
2335
+ render(
2336
+ <PublicPageFooter
2337
+ event={mockEvent}
2338
+ logo={dataUrl}
2339
+ />
2340
+ );
2341
+
2342
+ const logo = screen.getByAltText('Logo');
2343
+ expect(logo).toHaveAttribute('src', dataUrl);
2344
+ });
2345
+ });
2346
+
2347
+ describe('Links Edge Cases', () => {
2348
+ it('handles links with empty label', () => {
2349
+ const links = [
2350
+ { label: '', href: '/' },
2351
+ { label: 'About', href: '/about' }
2352
+ ];
2353
+
2354
+ render(
2355
+ <PublicPageFooter
2356
+ event={mockEvent}
2357
+ links={links}
2358
+ />
2359
+ );
2360
+
2361
+ const emptyLink = screen.getByRole('link', { name: '' });
2362
+ expect(emptyLink).toHaveAttribute('href', '/');
2363
+ expect(screen.getByText('About')).toBeInTheDocument();
2364
+ });
2365
+
2366
+ it('handles links with empty href', () => {
2367
+ const links = [
2368
+ { label: 'Home', href: '' },
2369
+ { label: 'About', href: '/about' }
2370
+ ];
2371
+
2372
+ render(
2373
+ <PublicPageFooter
2374
+ event={mockEvent}
2375
+ links={links}
2376
+ />
2377
+ );
2378
+
2379
+ const homeLink = screen.getByText('Home');
2380
+ expect(homeLink.closest('a')).toHaveAttribute('href', '');
2381
+ });
2382
+
2383
+ it('handles links with external URLs', () => {
2384
+ const links = [
2385
+ { label: 'External', href: 'https://example.com' },
2386
+ { label: 'Another', href: 'http://test.com' }
2387
+ ];
2388
+
2389
+ render(
2390
+ <PublicPageFooter
2391
+ event={mockEvent}
2392
+ links={links}
2393
+ />
2394
+ );
2395
+
2396
+ expect(screen.getByText('External').closest('a')).toHaveAttribute('href', 'https://example.com');
2397
+ expect(screen.getByText('Another').closest('a')).toHaveAttribute('href', 'http://test.com');
2398
+ });
2399
+
2400
+ it('handles links with special characters in label', () => {
2401
+ const links = [
2402
+ { label: 'Link & Co. <Test>', href: '/' }
2403
+ ];
2404
+
2405
+ render(
2406
+ <PublicPageFooter
2407
+ event={mockEvent}
2408
+ links={links}
2409
+ />
2410
+ );
2411
+
2412
+ expect(screen.getByText('Link & Co. <Test>')).toBeInTheDocument();
2413
+ });
2414
+
2415
+ it('handles links with special characters in href', () => {
2416
+ const links = [
2417
+ { label: 'Test', href: '/path?param=value&other=test' }
2418
+ ];
2419
+
2420
+ render(
2421
+ <PublicPageFooter
2422
+ event={mockEvent}
2423
+ links={links}
2424
+ />
2425
+ );
2426
+
2427
+ const link = screen.getByText('Test').closest('a');
2428
+ expect(link).toHaveAttribute('href', '/path?param=value&other=test');
2429
+ });
2430
+
2431
+ it('handles very large number of links', () => {
2432
+ const links = Array.from({ length: 100 }, (_, i) => ({
2433
+ label: `Link ${i}`,
2434
+ href: `/link-${i}`
2435
+ }));
2436
+
2437
+ render(
2438
+ <PublicPageFooter
2439
+ event={mockEvent}
2440
+ links={links}
2441
+ />
2442
+ );
2443
+
2444
+ expect(screen.getByText('Link 0')).toBeInTheDocument();
2445
+ expect(screen.getByText('Link 99')).toBeInTheDocument();
2446
+ });
2447
+ });
2448
+
2449
+ describe('Children Edge Cases', () => {
2450
+ it('handles multiple children', () => {
2451
+ render(
2452
+ <PublicPageFooter event={mockEvent}>
2453
+ <div data-testid="child1">Child 1</div>
2454
+ <div data-testid="child2">Child 2</div>
2455
+ </PublicPageFooter>
2456
+ );
2457
+
2458
+ expect(screen.getByTestId('child1')).toBeInTheDocument();
2459
+ expect(screen.getByTestId('child2')).toBeInTheDocument();
2460
+ });
2461
+
2462
+ it('handles children that return null', () => {
2463
+ const NullChild = () => null;
2464
+
2465
+ render(
2466
+ <PublicPageFooter event={mockEvent}>
2467
+ <NullChild />
2468
+ <div data-testid="valid-child">Valid Child</div>
2469
+ </PublicPageFooter>
2470
+ );
2471
+
2472
+ expect(screen.getByTestId('valid-child')).toBeInTheDocument();
2473
+ });
2474
+ });
2475
+ });
2476
+
2477
+ describe('[component] PublicPageProvider - Additional Edge Cases', () => {
2478
+ const originalEnv = import.meta.env;
2479
+ const originalProcess = process.env;
2480
+
2481
+ beforeEach(() => {
2482
+ vi.clearAllMocks();
2483
+ Object.defineProperty(import.meta, 'env', {
2484
+ value: {},
2485
+ writable: true,
2486
+ configurable: true
2487
+ });
2488
+ process.env = {};
2489
+ });
2490
+
2491
+ afterEach(() => {
2492
+ Object.defineProperty(import.meta, 'env', {
2493
+ value: originalEnv,
2494
+ writable: true,
2495
+ configurable: true
2496
+ });
2497
+ process.env = originalProcess;
2498
+ });
2499
+
2500
+ describe('Environment Variable Edge Cases', () => {
2501
+ it('handles VITE_SUPABASE_ANON_KEY as fallback', () => {
2502
+ const originalEnv = import.meta.env;
2503
+ const originalProcessEnv = process.env;
2504
+
2505
+ const mockEnv = {
2506
+ VITE_SUPABASE_URL: 'https://test.supabase.co',
2507
+ VITE_SUPABASE_ANON_KEY: 'anon-key'
2508
+ };
2509
+
2510
+ Object.defineProperty(import.meta, 'env', {
2511
+ value: mockEnv,
2512
+ writable: true,
2513
+ configurable: true
2514
+ });
2515
+
2516
+ process.env = {
2517
+ ...originalProcessEnv,
2518
+ VITE_SUPABASE_URL: 'https://test.supabase.co',
2519
+ VITE_SUPABASE_ANON_KEY: 'anon-key'
2520
+ };
2521
+
2522
+ render(
2523
+ <PublicPageProvider>
2524
+ <div>Test</div>
2525
+ </PublicPageProvider>
2526
+ );
2527
+
2528
+ expect(mockCreateClient).toHaveBeenCalledWith('https://test.supabase.co', 'anon-key');
2529
+
2530
+ Object.defineProperty(import.meta, 'env', {
2531
+ value: originalEnv,
2532
+ writable: true,
2533
+ configurable: true
2534
+ });
2535
+ process.env = originalProcessEnv;
2536
+ });
2537
+
2538
+ it('handles NEXT_PUBLIC_SUPABASE_ANON_KEY as fallback', () => {
2539
+ const originalEnv = import.meta.env;
2540
+ const originalProcessEnv = process.env;
2541
+
2542
+ const mockEnv = {
2543
+ NEXT_PUBLIC_SUPABASE_URL: 'https://next.supabase.co',
2544
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: 'next-anon-key'
2545
+ };
2546
+
2547
+ Object.defineProperty(import.meta, 'env', {
2548
+ value: mockEnv,
2549
+ writable: true,
2550
+ configurable: true
2551
+ });
2552
+
2553
+ process.env = {
2554
+ ...originalProcessEnv,
2555
+ NEXT_PUBLIC_SUPABASE_URL: 'https://next.supabase.co',
2556
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: 'next-anon-key'
2557
+ };
2558
+
2559
+ render(
2560
+ <PublicPageProvider>
2561
+ <div>Test</div>
2562
+ </PublicPageProvider>
2563
+ );
2564
+
2565
+ expect(mockCreateClient).toHaveBeenCalledWith('https://next.supabase.co', 'next-anon-key');
2566
+
2567
+ Object.defineProperty(import.meta, 'env', {
2568
+ value: originalEnv,
2569
+ writable: true,
2570
+ configurable: true
2571
+ });
2572
+ process.env = originalProcessEnv;
2573
+ });
2574
+
2575
+ it('handles missing URL but present key', () => {
2576
+ const originalEnv = import.meta.env;
2577
+ const originalProcessEnv = process.env;
2578
+
2579
+ const mockEnv = {
2580
+ VITE_SUPABASE_PUBLISHABLE_KEY: 'test-key'
2581
+ };
2582
+
2583
+ Object.defineProperty(import.meta, 'env', {
2584
+ value: mockEnv,
2585
+ writable: true,
2586
+ configurable: true
2587
+ });
2588
+
2589
+ process.env = {
2590
+ ...originalProcessEnv,
2591
+ VITE_SUPABASE_PUBLISHABLE_KEY: 'test-key'
2592
+ };
2593
+
2594
+ const TestComponent = () => {
2595
+ const context = usePublicPageContext();
2596
+ return <div>{context.supabase === null ? 'no-client' : 'has-client'}</div>;
2597
+ };
2598
+
2599
+ render(
2600
+ <PublicPageProvider>
2601
+ <TestComponent />
2602
+ </PublicPageProvider>
2603
+ );
2604
+
2605
+ expect(screen.getByText('no-client')).toBeInTheDocument();
2606
+ expect(mockCreateClient).not.toHaveBeenCalled();
2607
+
2608
+ Object.defineProperty(import.meta, 'env', {
2609
+ value: originalEnv,
2610
+ writable: true,
2611
+ configurable: true
2612
+ });
2613
+ process.env = originalProcessEnv;
2614
+ });
2615
+
2616
+ it('handles missing key but present URL', () => {
2617
+ const originalEnv = import.meta.env;
2618
+ const originalProcessEnv = process.env;
2619
+
2620
+ const mockEnv = {
2621
+ VITE_SUPABASE_URL: 'https://test.supabase.co'
2622
+ };
2623
+
2624
+ Object.defineProperty(import.meta, 'env', {
2625
+ value: mockEnv,
2626
+ writable: true,
2627
+ configurable: true
2628
+ });
2629
+
2630
+ process.env = {
2631
+ ...originalProcessEnv,
2632
+ VITE_SUPABASE_URL: 'https://test.supabase.co'
2633
+ };
2634
+
2635
+ const TestComponent = () => {
2636
+ const context = usePublicPageContext();
2637
+ return <div>{context.supabase === null ? 'no-client' : 'has-client'}</div>;
2638
+ };
2639
+
2640
+ render(
2641
+ <PublicPageProvider>
2642
+ <TestComponent />
2643
+ </PublicPageProvider>
2644
+ );
2645
+
2646
+ expect(screen.getByText('no-client')).toBeInTheDocument();
2647
+ expect(mockCreateClient).not.toHaveBeenCalled();
2648
+
2649
+ Object.defineProperty(import.meta, 'env', {
2650
+ value: originalEnv,
2651
+ writable: true,
2652
+ configurable: true
2653
+ });
2654
+ process.env = originalProcessEnv;
2655
+ });
2656
+
2657
+ it('handles empty string environment variables', () => {
2658
+ const originalEnv = import.meta.env;
2659
+ const originalProcessEnv = process.env;
2660
+
2661
+ const mockEnv = {
2662
+ VITE_SUPABASE_URL: '',
2663
+ VITE_SUPABASE_PUBLISHABLE_KEY: ''
2664
+ };
2665
+
2666
+ Object.defineProperty(import.meta, 'env', {
2667
+ value: mockEnv,
2668
+ writable: true,
2669
+ configurable: true
2670
+ });
2671
+
2672
+ process.env = {
2673
+ ...originalProcessEnv,
2674
+ VITE_SUPABASE_URL: '',
2675
+ VITE_SUPABASE_PUBLISHABLE_KEY: ''
2676
+ };
2677
+
2678
+ const TestComponent = () => {
2679
+ const context = usePublicPageContext();
2680
+ return <div>{context.supabase === null ? 'no-client' : 'has-client'}</div>;
2681
+ };
2682
+
2683
+ render(
2684
+ <PublicPageProvider>
2685
+ <TestComponent />
2686
+ </PublicPageProvider>
2687
+ );
2688
+
2689
+ expect(screen.getByText('no-client')).toBeInTheDocument();
2690
+
2691
+ Object.defineProperty(import.meta, 'env', {
2692
+ value: originalEnv,
2693
+ writable: true,
2694
+ configurable: true
2695
+ });
2696
+ process.env = originalProcessEnv;
2697
+ });
2698
+ });
2699
+
2700
+ describe('Supabase Client Creation Edge Cases', () => {
2701
+ it('creates client only once when environment variables are stable', () => {
2702
+ const originalEnv = import.meta.env;
2703
+ const originalProcessEnv = process.env;
2704
+
2705
+ const mockEnv = {
2706
+ VITE_SUPABASE_URL: 'https://test.supabase.co',
2707
+ VITE_SUPABASE_PUBLISHABLE_KEY: 'test-key'
2708
+ };
2709
+
2710
+ Object.defineProperty(import.meta, 'env', {
2711
+ value: mockEnv,
2712
+ writable: true,
2713
+ configurable: true
2714
+ });
2715
+
2716
+ process.env = {
2717
+ ...originalProcessEnv,
2718
+ VITE_SUPABASE_URL: 'https://test.supabase.co',
2719
+ VITE_SUPABASE_PUBLISHABLE_KEY: 'test-key'
2720
+ };
2721
+
2722
+ mockCreateClient.mockClear();
2723
+
2724
+ const { rerender } = render(
2725
+ <PublicPageProvider>
2726
+ <div>Test</div>
2727
+ </PublicPageProvider>
2728
+ );
2729
+
2730
+ expect(mockCreateClient).toHaveBeenCalledTimes(1);
2731
+
2732
+ rerender(
2733
+ <PublicPageProvider>
2734
+ <div>Test</div>
2735
+ </PublicPageProvider>
2736
+ );
2737
+
2738
+ // Should still be called only once (memoized)
2739
+ expect(mockCreateClient).toHaveBeenCalledTimes(1);
2740
+
2741
+ Object.defineProperty(import.meta, 'env', {
2742
+ value: originalEnv,
2743
+ writable: true,
2744
+ configurable: true
2745
+ });
2746
+ process.env = originalProcessEnv;
2747
+ });
2748
+ });
2749
+ });
2750
+
2751
+ describe('[hook] usePublicPageContext - Additional Edge Cases', () => {
2752
+ it('provides stable context reference on re-render', () => {
2753
+ const contextRefs: Array<PublicPageContextType> = [];
2754
+
2755
+ const TestComponent = () => {
2756
+ const context = usePublicPageContext();
2757
+ contextRefs.push(context);
2758
+ return <div>{context.isPublicPage ? 'true' : 'false'}</div>;
2759
+ };
2760
+
2761
+ const { rerender } = render(
2762
+ <PublicPageProvider appName="TestApp">
2763
+ <TestComponent />
2764
+ </PublicPageProvider>
2765
+ );
2766
+
2767
+ const firstContext = contextRefs[0];
2768
+
2769
+ rerender(
2770
+ <PublicPageProvider appName="TestApp">
2771
+ <TestComponent />
2772
+ </PublicPageProvider>
2773
+ );
2774
+
2775
+ const secondContext = contextRefs[1];
2776
+ // Context should be stable (same reference or same values)
2777
+ expect(secondContext.isPublicPage).toBe(firstContext.isPublicPage);
2778
+ expect(secondContext.appName).toBe(firstContext.appName);
2779
+ });
2780
+ });
2781
+
2782
+ describe('[hook] useIsPublicPage - Additional Edge Cases', () => {
2783
+ it('returns false when context is undefined (not just null)', () => {
2784
+ const TestComponent = () => {
2785
+ const isPublic = useIsPublicPage();
2786
+ return <div>{isPublic ? 'true' : 'false'}</div>;
2787
+ };
2788
+
2789
+ render(<TestComponent />);
2790
+ expect(screen.getByText('false')).toBeInTheDocument();
2791
+ });
2792
+
2793
+ it('returns true consistently when inside provider', () => {
2794
+ const TestComponent = () => {
2795
+ const isPublic = useIsPublicPage();
2796
+ return <div data-testid="result">{isPublic ? 'true' : 'false'}</div>;
2797
+ };
2798
+
2799
+ const { rerender } = render(
2800
+ <PublicPageProvider>
2801
+ <TestComponent />
2802
+ </PublicPageProvider>
2803
+ );
2804
+
2805
+ expect(screen.getByTestId('result')).toHaveTextContent('true');
2806
+
2807
+ rerender(
2808
+ <PublicPageProvider>
2809
+ <TestComponent />
2810
+ </PublicPageProvider>
2811
+ );
2812
+
2813
+ expect(screen.getByTestId('result')).toHaveTextContent('true');
1212
2814
  });
1213
2815
  });
1214
2816