@jmruthers/pace-core 0.5.181 → 0.5.182
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.
- package/CHANGELOG.md +1 -1
- package/README.md +16 -2
- package/dist/{AuthService-DYuQPJj6.d.ts → AuthService-B-cd2MA4.d.ts} +9 -11
- package/dist/{DataTable-CWAZZcXC.d.ts → DataTable-Bz8ffqyA.d.ts} +1 -1
- package/dist/{DataTable-UA6CL4JI.js → DataTable-QAB34V6K.js} +14 -15
- package/dist/UnifiedAuthProvider-7F6T4B6K.js +13 -0
- package/dist/{UnifiedAuthProvider-DJxGTftH.d.ts → UnifiedAuthProvider-F86d7dSi.d.ts} +5 -6
- package/dist/{api-45XYYO2A.js → api-ROMBCNKU.js} +5 -5
- package/dist/{audit-64X3VJXB.js → audit-WRS3KJKI.js} +4 -4
- package/dist/auth-BZOJqrdd.d.ts +49 -0
- package/dist/{chunk-CX5M4ZAG.js → chunk-5DRSZLL2.js} +1 -1
- package/dist/chunk-5DRSZLL2.js.map +1 -0
- package/dist/{chunk-BESYRHQM.js → chunk-6C4YBBJM.js} +10 -7
- package/dist/chunk-6C4YBBJM.js.map +1 -0
- package/dist/{chunk-PLDDJCW6.js → chunk-7D4SUZUM.js} +2 -13
- package/dist/{chunk-HRO5HWN2.js → chunk-CSOFYHAG.js} +55 -162
- package/dist/chunk-CSOFYHAG.js.map +1 -0
- package/dist/{chunk-ANBQRTPX.js → chunk-E66EQZE6.js} +3 -5
- package/dist/{chunk-ANBQRTPX.js.map → chunk-E66EQZE6.js.map} +1 -1
- package/dist/{chunk-Q5QRDWKI.js → chunk-F2IMUDXZ.js} +4 -6
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-SBVILCCA.js → chunk-FSFQFJCU.js} +28 -6
- package/dist/chunk-FSFQFJCU.js.map +1 -0
- package/dist/chunk-FUEYYMX5.js +2296 -0
- package/dist/chunk-FUEYYMX5.js.map +1 -0
- package/dist/{chunk-FFKNH6U5.js → chunk-HKIT6O7W.js} +3 -5
- package/dist/{chunk-FFKNH6U5.js.map → chunk-HKIT6O7W.js.map} +1 -1
- package/dist/chunk-KQCRWDSA.js +1 -0
- package/dist/{chunk-S5OFRT4M.js → chunk-KUEN3HFB.js} +6 -6
- package/dist/chunk-KUEN3HFB.js.map +1 -0
- package/dist/chunk-LMC26NLJ.js +84 -0
- package/dist/chunk-LMC26NLJ.js.map +1 -0
- package/dist/{chunk-BVYWGZVV.js → chunk-M7W4CP3M.js} +52 -19
- package/dist/chunk-M7W4CP3M.js.map +1 -0
- package/dist/{chunk-HZLDFOE4.js → chunk-MI7HBHN3.js} +164 -243
- package/dist/chunk-MI7HBHN3.js.map +1 -0
- package/dist/{chunk-PPMP5J6T.js → chunk-PWAHJW4G.js} +180 -29
- package/dist/chunk-PWAHJW4G.js.map +1 -0
- package/dist/chunk-PWLANIRT.js +127 -0
- package/dist/{chunk-XDNLUEXI.js.map → chunk-PWLANIRT.js.map} +1 -1
- package/dist/chunk-QCDXODCA.js +75 -0
- package/dist/chunk-QCDXODCA.js.map +1 -0
- package/dist/{chunk-D7LCGMVS.js → chunk-QETLRQI6.js} +526 -887
- package/dist/chunk-QETLRQI6.js.map +1 -0
- package/dist/{chunk-5MT24GKJ.js → chunk-QUVSNGIP.js} +264 -262
- package/dist/chunk-QUVSNGIP.js.map +1 -0
- package/dist/chunk-QXHPKYJV.js +113 -0
- package/dist/chunk-QXHPKYJV.js.map +1 -0
- package/dist/{chunk-OWAG3GSU.js → chunk-R77UEZ4E.js} +11 -1
- package/dist/chunk-R77UEZ4E.js.map +1 -0
- package/dist/{chunk-ZYTYSTO5.js → chunk-RA3JUFMW.js} +314 -161
- package/dist/chunk-RA3JUFMW.js.map +1 -0
- package/dist/{chunk-ERISIBYU.js → chunk-SQGMNID3.js} +3 -8
- package/dist/chunk-SQGMNID3.js.map +1 -0
- package/dist/{chunk-XJ2HZOBU.js → chunk-UHNYIBXL.js} +1 -1
- package/dist/chunk-UHNYIBXL.js.map +1 -0
- package/{src/utils/secureStorage.ts → dist/chunk-VBXEHIUJ.js} +113 -88
- package/dist/{chunk-7QCC6MCP.js.map → chunk-VBXEHIUJ.js.map} +1 -1
- package/dist/{chunk-VZ4VDGTB.js → chunk-W22JP75J.js} +5 -13
- package/dist/{chunk-VZ4VDGTB.js.map → chunk-W22JP75J.js.map} +1 -1
- package/dist/components.d.ts +12 -93
- package/dist/components.js +23 -106
- package/dist/components.js.map +1 -1
- package/dist/core-CUElvH_C.d.ts +164 -0
- package/dist/database.generated-CBmg2950.d.ts +8284 -0
- package/dist/event-CW5YB_2p.d.ts +239 -0
- package/dist/{file-reference-C6Gkn77H.d.ts → file-reference-D06mEEWW.d.ts} +7 -5
- package/dist/functions-D_kgHktt.d.ts +208 -0
- package/dist/hooks.d.ts +54 -7
- package/dist/hooks.js +204 -17
- package/dist/hooks.js.map +1 -1
- package/dist/{EventLogo-B3V3otev.d.ts → index-Bl--n7-T.d.ts} +387 -397
- package/dist/index.d.ts +94 -261
- package/dist/index.js +314 -126
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +7 -8
- package/dist/providers.js +6 -13
- package/dist/rbac/index.d.ts +171 -101
- package/dist/rbac/index.js +23 -17
- package/dist/styles/index.d.ts +1 -3
- package/dist/styles/index.js +2 -17
- package/dist/theming/runtime.js +3 -3
- package/dist/types-UU913iLA.d.ts +102 -0
- package/dist/{types-Dfz9dmVH.d.ts → types-_x1f4QBF.d.ts} +6 -6
- package/dist/types.d.ts +88 -227
- package/dist/types.js +64 -112
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B7PabvuH.d.ts → usePublicRouteParams-JJczomYq.d.ts} +203 -6
- package/dist/utils.d.ts +299 -13
- package/dist/utils.js +481 -55
- package/dist/utils.js.map +1 -1
- package/dist/validation-643vUDZW.d.ts +177 -0
- package/docs/DOCUMENTATION_REVIEW_TRACKER.md +511 -0
- package/docs/README.md +9 -8
- package/docs/api/README.md +16 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +14 -14
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +29 -9
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +17 -17
- package/docs/api/enums/RBACErrorCode.md +228 -0
- package/docs/api/enums/RPCFunction.md +118 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +2 -2
- package/docs/api/interfaces/CalendarProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +29 -3
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +2 -2
- package/docs/api/interfaces/DataTableColumn.md +6 -6
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +2 -2
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +5 -5
- package/docs/api/interfaces/ExportOptions.md +4 -4
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +13 -13
- package/docs/api/interfaces/FileReference.md +12 -12
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +10 -10
- package/docs/api/interfaces/FileUploadProps.md +19 -19
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +166 -0
- package/docs/api/interfaces/FormProps.md +113 -0
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +8 -8
- package/docs/api/interfaces/InputProps.md +2 -2
- package/docs/api/interfaces/LabelProps.md +8 -8
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +17 -73
- package/docs/api/interfaces/NavigationMenuProps.md +38 -53
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +13 -13
- package/docs/api/interfaces/OrganisationContextType.md +21 -21
- package/docs/api/interfaces/OrganisationMembership.md +15 -15
- package/docs/api/interfaces/OrganisationProviderProps.md +59 -2
- package/docs/api/interfaces/OrganisationSecurityError.md +5 -5
- package/docs/api/interfaces/PaceAppLayoutProps.md +26 -39
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +50 -0
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +15 -15
- package/docs/api/interfaces/RBACAccessValidateParams.md +52 -0
- package/docs/api/interfaces/RBACAccessValidateResult.md +41 -0
- package/docs/api/interfaces/RBACAuditLogParams.md +85 -0
- package/docs/api/interfaces/RBACAuditLogResult.md +52 -0
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +52 -0
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +74 -0
- package/docs/api/interfaces/RBACPermissionCheckParams.md +74 -0
- package/docs/api/interfaces/RBACPermissionCheckResult.md +52 -0
- package/docs/api/interfaces/RBACPermissionsGetParams.md +63 -0
- package/docs/api/interfaces/RBACPermissionsGetResult.md +63 -0
- package/docs/api/interfaces/RBACResult.md +58 -0
- package/docs/api/interfaces/RBACRoleGrantParams.md +63 -0
- package/docs/api/interfaces/RBACRoleGrantResult.md +52 -0
- package/docs/api/interfaces/RBACRoleRevokeParams.md +63 -0
- package/docs/api/interfaces/RBACRoleRevokeResult.md +52 -0
- package/docs/api/interfaces/RBACRoleValidateParams.md +52 -0
- package/docs/api/interfaces/RBACRoleValidateResult.md +63 -0
- package/docs/api/interfaces/RBACRolesListParams.md +52 -0
- package/docs/api/interfaces/RBACRolesListResult.md +74 -0
- package/docs/api/interfaces/RBACSessionTrackParams.md +74 -0
- package/docs/api/interfaces/RBACSessionTrackResult.md +52 -0
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +15 -2
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +43 -2
- package/docs/api/interfaces/TextareaProps.md +2 -2
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +61 -61
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +87 -0
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +81 -0
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +4 -4
- package/docs/api/interfaces/UserProfile.md +7 -7
- package/docs/api/modules.md +484 -462
- package/docs/api-reference/components.md +186 -15
- package/docs/api-reference/deprecated.md +376 -0
- package/docs/api-reference/hooks.md +149 -19
- package/docs/api-reference/providers.md +61 -6
- package/docs/api-reference/rpc-functions.md +397 -0
- package/docs/api-reference/types.md +135 -78
- package/docs/api-reference/utilities.md +51 -380
- package/docs/architecture/README.md +49 -3
- package/docs/architecture/database-schema-requirements.md +40 -3
- package/docs/architecture/rbac-security-architecture.md +41 -4
- package/docs/architecture/services.md +127 -42
- package/docs/best-practices/README.md +51 -5
- package/docs/best-practices/accessibility.md +32 -3
- package/docs/best-practices/common-patterns.md +50 -3
- package/docs/best-practices/deployment.md +50 -4
- package/docs/best-practices/performance.md +50 -3
- package/docs/best-practices/security.md +94 -41
- package/docs/best-practices/testing.md +33 -4
- package/docs/core-concepts/authentication.md +5 -5
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +3 -3
- package/docs/core-concepts/permissions.md +3 -3
- package/docs/core-concepts/rbac-system.md +5 -5
- package/docs/documentation-index.md +30 -8
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +7 -5
- package/docs/getting-started/examples/basic-auth-app.md +3 -0
- package/docs/getting-started/examples/full-featured-app.md +5 -3
- package/docs/getting-started/faq.md +6 -6
- package/docs/getting-started/installation-guide.md +192 -13
- package/docs/getting-started/local-development.md +303 -0
- package/docs/getting-started/quick-reference.md +3 -3
- package/docs/getting-started/quick-start.md +517 -0
- package/docs/implementation-guides/app-layout.md +45 -3
- package/docs/implementation-guides/authentication.md +66 -7
- package/docs/implementation-guides/component-styling.md +53 -3
- package/docs/implementation-guides/data-tables.md +76 -7
- package/docs/implementation-guides/datatable-filtering.md +1 -2
- package/docs/implementation-guides/datatable-rbac-usage.md +0 -1
- package/docs/implementation-guides/dynamic-colors.md +155 -4
- package/docs/implementation-guides/file-reference-system.md +72 -3
- package/docs/implementation-guides/file-upload-storage.md +72 -3
- package/docs/implementation-guides/forms.md +53 -3
- package/docs/implementation-guides/inactivity-tracking.md +53 -3
- package/docs/implementation-guides/large-datasets.md +1 -1
- package/docs/implementation-guides/navigation.md +55 -5
- package/docs/implementation-guides/organisation-security.md +72 -3
- package/docs/implementation-guides/performance.md +57 -1
- package/docs/implementation-guides/permission-enforcement.md +81 -8
- package/docs/implementation-guides/public-pages.md +560 -14
- package/docs/migration/MIGRATION_GUIDE.md +409 -50
- package/docs/migration/README.md +37 -3
- package/docs/migration/organisation-context-timing-fix.md +39 -4
- package/docs/migration/quick-migration-guide.md +41 -5
- package/docs/migration/rbac-migration.md +59 -3
- package/docs/migration/service-architecture.md +77 -14
- package/docs/rbac/README.md +79 -3
- package/docs/rbac/advanced-patterns.md +47 -3
- package/docs/rbac/api-reference.md +77 -8
- package/docs/rbac/event-based-apps.md +50 -5
- package/docs/rbac/examples/rbac-rls-integration-example.md +3 -3
- package/docs/rbac/examples.md +39 -3
- package/docs/rbac/getting-started.md +63 -4
- package/docs/rbac/quick-start.md +57 -5
- package/docs/rbac/rbac-rls-integration.md +68 -6
- package/docs/rbac/super-admin-guide.md +47 -3
- package/docs/rbac/troubleshooting.md +3 -3
- package/docs/security/README.md +68 -3
- package/docs/security/checklist.md +50 -3
- package/docs/standards/01-architecture-standard.md +39 -0
- package/docs/standards/02-api-and-rpc-standard.md +39 -0
- package/docs/standards/03-component-standard.md +32 -0
- package/docs/standards/04-code-style-standard.md +32 -0
- package/docs/standards/05-security-standard.md +30 -0
- package/docs/standards/06-testing-and-docs-standard.md +29 -0
- package/docs/standards/README.md +35 -0
- package/docs/styles/README.md +89 -8
- package/docs/testing/README.md +175 -24
- package/docs/troubleshooting/README.md +50 -3
- package/docs/troubleshooting/common-issues.md +271 -5
- package/docs/troubleshooting/debugging.md +54 -1
- package/docs/troubleshooting/migration.md +54 -1
- package/docs/troubleshooting/organisation-context-setup.md +29 -3
- package/docs/troubleshooting/styling-issues.md +246 -4
- package/{src/components/DataTable/examples → examples/DataTable}/GroupingAggregationExample.tsx +1 -1
- package/examples/{components 2/DataTable/HierarchicalActionsExample.tsx → DataTable/HierarchicalActionsExample.tsx} +7 -6
- package/{src/components/DataTable/examples → examples/DataTable}/HierarchicalExample.tsx +8 -6
- package/examples/{components 2/DataTable/PerformanceExample.tsx → DataTable/PerformanceExample.tsx} +2 -2
- package/examples/{components 2/DataTable/index.ts → DataTable/index.ts} +1 -0
- package/{src/components/Dialog/examples → examples/Dialog}/HtmlDialogExample.tsx +3 -3
- package/examples/{components 2/Dialog/ScrollableDialogExample.tsx → Dialog/ScrollableDialogExample.tsx} +1 -1
- package/{src/components/Dialog/examples → examples/Dialog}/SmartDialogExample.tsx +1 -1
- package/examples/{components 2/Dialog/index.ts → Dialog/index.ts} +0 -3
- package/examples/{features/public-pages → PublicPages}/CorrectPublicPageImplementation.tsx +52 -17
- package/examples/{features/public-pages → PublicPages}/PublicEventPage.tsx +65 -35
- package/examples/{features/public-pages → PublicPages}/PublicPageApp.tsx +52 -18
- package/examples/{features/public-pages → PublicPages}/PublicPageUsageExample.tsx +28 -15
- package/examples/README.md +81 -33
- package/examples/index.ts +14 -12
- package/examples/{RBAC → rbac}/CompleteRBACExample.tsx +1 -1
- package/examples/{features/rbac → rbac}/EventBasedApp.tsx +4 -4
- package/examples/{features/rbac → rbac}/PermissionExample.tsx +5 -3
- package/package.json +21 -27
- package/src/__tests__/helpers/test-utils.tsx +29 -3
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +7 -5
- package/src/components/Alert/Alert.test.tsx +2 -2
- package/src/components/Alert/Alert.tsx +4 -4
- package/src/components/Avatar/Avatar.test.tsx +17 -6
- package/src/components/Badge/Badge.test.tsx +1 -1
- package/src/components/Badge/Badge.tsx +2 -2
- package/src/components/Button/Button.test.tsx +2 -2
- package/src/components/Button/Button.tsx +11 -7
- package/src/components/Calendar/Calendar.test.tsx +41 -8
- package/src/components/Calendar/Calendar.tsx +39 -36
- package/src/components/Card/Card.tsx +51 -13
- package/src/components/Checkbox/Checkbox.test.tsx +36 -12
- package/src/components/DataTable/DataTable.test.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +13 -7
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +14 -42
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +13 -10
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +14 -11
- package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +4 -2
- package/src/components/DataTable/__tests__/DataTable.test.tsx +13 -7
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +13 -10
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +15 -11
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +12 -6
- package/src/components/DataTable/__tests__/keyboard.test.tsx +12 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +10 -6
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +1 -1
- package/src/components/DataTable/components/DataTableBody.tsx +10 -25
- package/src/components/DataTable/components/DataTableCore.tsx +1 -1
- package/src/components/DataTable/components/FilterRow.tsx +3 -1
- package/src/components/DataTable/components/ImportModal.tsx +1 -1
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +9 -9
- package/src/components/DataTable/core/ColumnFactory.ts +6 -6
- package/src/components/DataTable/core/DataTableContext.tsx +14 -10
- package/src/components/DataTable/core/LocalDataAdapter.ts +2 -1
- package/src/components/DataTable/core/PluginRegistry.ts +3 -3
- package/src/components/DataTable/core/StateManager.ts +12 -11
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +104 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +101 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +84 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +102 -0
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +104 -0
- package/src/components/DataTable/core/interfaces.ts +17 -17
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +124 -0
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +117 -0
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +102 -0
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +53 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +0 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +9 -8
- package/src/components/DataTable/types.ts +5 -5
- package/src/components/DataTable/utils/aggregationUtils.ts +4 -4
- package/src/components/DataTable/utils/columnUtils.ts +3 -2
- package/src/components/DataTable/utils/debugTools.ts +1 -1
- package/src/components/DataTable/utils/exportUtils.ts +6 -6
- package/src/components/DataTable/utils/hierarchicalSorting.ts +6 -6
- package/src/components/DataTable/utils/hierarchicalUtils.ts +0 -8
- package/src/components/DataTable/utils/index.ts +0 -1
- package/src/components/DataTable/utils/performanceUtils.ts +9 -4
- package/src/components/Dialog/Dialog.test.tsx +49 -27
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +13 -8
- package/src/components/EventSelector/EventSelector.test.tsx +60 -12
- package/src/components/EventSelector/EventSelector.tsx +38 -15
- package/src/components/EventSelector/index.ts +2 -2
- package/src/components/FileDisplay/FileDisplay.test.tsx +143 -85
- package/src/components/FileDisplay/FileDisplay.tsx +1 -0
- package/src/components/FileUpload/FileUpload.test.tsx +532 -152
- package/src/components/FileUpload/FileUpload.tsx +43 -8
- package/src/components/Footer/Footer.test.tsx +19 -14
- package/src/components/Form/Form.test.tsx +96 -14
- package/src/components/Form/Form.tsx +210 -1
- package/src/components/Form/index.ts +3 -7
- package/src/components/Header/Header.test.tsx +24 -17
- package/src/components/Header/Header.tsx +3 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -4
- package/src/components/Input/Input.test.tsx +61 -36
- package/src/components/Label/{__tests__/Label.test.tsx → Label.test.tsx} +2 -2
- package/src/components/Label/Label.tsx +2 -3
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +6 -5
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +6 -2
- package/src/components/LoginForm/LoginForm.test.tsx +14 -13
- package/src/components/LoginForm/LoginForm.tsx +1 -1
- package/src/components/LoginForm/index.ts +7 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +233 -20
- package/src/components/NavigationMenu/NavigationMenu.tsx +191 -55
- package/src/components/NavigationMenu/index.ts +1 -1
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +20 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/{__tests__/PaceAppLayout.integration.test.tsx → PaceAppLayout.integration.test.tsx} +272 -79
- package/src/components/PaceAppLayout/{__tests__/PaceAppLayout.performance.test.tsx → PaceAppLayout.performance.test.tsx} +155 -32
- package/src/components/PaceAppLayout/{__tests__/PaceAppLayout.security.test.tsx → PaceAppLayout.security.test.tsx} +211 -65
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +498 -210
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +63 -64
- package/src/components/PaceAppLayout/test-setup.tsx +192 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +193 -39
- package/src/components/{PasswordReset → PasswordChange}/PasswordChangeForm.test.tsx +2 -2
- package/src/components/{PasswordReset → PasswordChange}/PasswordChangeForm.tsx +10 -4
- package/src/components/PasswordChange/index.ts +2 -0
- package/src/components/Progress/Progress.test.tsx +11 -0
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/Progress/index.ts +10 -0
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +2 -1
- package/src/components/PublicLayout/PublicLayout.test.tsx +1210 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +190 -36
- package/src/components/PublicLayout/PublicPageProvider.tsx +8 -7
- package/src/components/PublicLayout/index.ts +10 -28
- package/src/components/Select/Select.test.tsx +7 -7
- package/src/components/Select/Select.tsx +277 -11
- package/src/components/Select/index.ts +1 -2
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +232 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +40 -19
- package/src/components/Table/{__tests__/Table.test.tsx → Table.test.tsx} +94 -41
- package/src/components/Tabs/Tabs.test.tsx +10 -9
- package/src/components/Tabs/Tabs.tsx +61 -33
- package/src/components/Textarea/Textarea.test.tsx +31 -18
- package/src/components/Toast/Toast.tsx +2 -2
- package/src/components/Tooltip/Tooltip.test.tsx +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +7 -6
- package/src/components/UserMenu/UserMenu.tsx +2 -2
- package/src/components/index.ts +5 -4
- package/src/constants/performance.ts +19 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +21 -22
- package/src/hooks/__tests__/useEvents.unit.test.ts +5 -4
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -0
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +16 -11
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -3
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +1 -3
- package/src/hooks/__tests__/useRBAC.unit.test.ts +24 -2
- package/src/hooks/index.ts +4 -0
- package/src/hooks/public/index.ts +2 -0
- package/src/hooks/public/usePublicEvent.ts +4 -6
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuth.ts +2 -4
- package/src/hooks/services/useCurrentEvent.ts +1 -1
- package/src/hooks/useAppConfig.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +2 -2
- package/src/hooks/useEventTheme.ts +1 -1
- package/src/hooks/useEvents.ts +51 -10
- package/src/hooks/useOrganisationPermissions.test.ts +3 -3
- package/src/hooks/useOrganisationPermissions.ts +1 -1
- package/src/hooks/useOrganisationSecurity.ts +2 -2
- package/src/hooks/usePermissionCache.test.ts +9 -9
- package/src/hooks/usePermissionCache.ts +2 -2
- package/src/index.ts +19 -12
- package/src/providers/OrganisationProvider.tsx +73 -9
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +113 -13
- package/src/providers/__tests__/AuthProvider.test.tsx +2 -1
- package/src/providers/__tests__/EventProvider.test.tsx +24 -15
- package/src/providers/__tests__/OrganisationProvider.test.tsx +87 -36
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +80 -24
- package/src/providers/index.ts +0 -3
- package/src/providers/services/AuthServiceProvider.tsx +2 -17
- package/src/providers/services/EventServiceProvider.tsx +11 -16
- package/src/providers/services/InactivityServiceProvider.tsx +9 -12
- package/src/providers/services/OrganisationServiceProvider.tsx +9 -12
- package/src/providers/services/UnifiedAuthProvider.tsx +85 -18
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +11 -4
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +105 -21
- package/src/rbac/adapters.tsx +1 -1
- package/src/rbac/api.ts +20 -4
- package/src/rbac/audit-enhanced.ts +47 -2
- package/src/rbac/audit.ts +47 -2
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.test.tsx +7 -6
- package/src/rbac/components/NavigationProvider.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.test.tsx +7 -6
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +7 -6
- package/src/rbac/components/SecureDataProvider.tsx +1 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +6 -6
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +11 -10
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +10 -11
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +19 -15
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +13 -12
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +19 -15
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +18 -18
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +11 -10
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +8 -7
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +10 -11
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +48 -19
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +476 -0
- package/src/rbac/hooks/index.ts +3 -0
- package/src/rbac/hooks/usePermissions.ts +31 -85
- package/src/rbac/hooks/useRBAC.test.ts +13 -1
- package/src/rbac/hooks/useRBAC.ts +13 -67
- package/src/rbac/hooks/useResolvedScope.ts +11 -0
- package/src/rbac/hooks/useSecureSupabase.ts +308 -0
- package/src/rbac/index.ts +3 -0
- package/src/rbac/secureClient.ts +53 -6
- package/src/rbac/security.ts +37 -1
- package/src/{types/rbac-functions.ts → rbac/types/functions.ts} +30 -30
- package/src/rbac/types.ts +3 -2
- package/src/services/AuthService.ts +33 -25
- package/src/services/EventService.ts +56 -44
- package/src/services/InactivityService.ts +33 -53
- package/src/services/OrganisationService.ts +36 -40
- package/src/services/__tests__/AuthService.restoreSession.test.ts +6 -2
- package/src/services/__tests__/EventService.test.ts +67 -33
- package/src/services/interfaces/IEventService.ts +1 -1
- package/src/styles/core.css +2 -2
- package/src/styles/index.ts +1 -5
- package/src/types/__tests__/guards.test.ts +1 -1
- package/src/types/__tests__/type-validation.test.ts +0 -1
- package/src/types/auth.ts +42 -2
- package/src/types/core.ts +251 -0
- package/src/types/database.ts +11 -496
- package/src/types/event.ts +102 -0
- package/src/types/file-reference.ts +6 -4
- package/src/types/guards.ts +2 -1
- package/src/types/index.ts +48 -14
- package/src/types/lodash.debounce.d.ts +15 -0
- package/src/types/organisation.ts +14 -10
- package/src/types/supabase.ts +15 -17
- package/src/utils/__tests__/secureErrors.unit.test.ts +1 -1
- package/src/utils/__tests__/validationUtils.unit.test.ts +0 -29
- package/src/utils/app/appNameResolver.ts +1 -1
- package/src/utils/dynamic/dynamicUtils.ts +3 -2
- package/src/utils/file-reference/index.ts +25 -6
- package/src/utils/security/secureErrors.ts +1 -1
- package/src/utils/validation/index.ts +6 -12
- package/src/utils/validation/validationUtils.ts +0 -13
- package/dist/UnifiedAuthProvider-B37ATQHE.js +0 -16
- package/dist/auth-DReDSLq9.d.ts +0 -16
- package/dist/chunk-3JI76CYK.js +0 -2444
- package/dist/chunk-3JI76CYK.js.map +0 -1
- package/dist/chunk-56XJ3TU6.js +0 -11
- package/dist/chunk-56XJ3TU6.js.map +0 -1
- package/dist/chunk-5MT24GKJ.js.map +0 -1
- package/dist/chunk-7QCC6MCP.js +0 -288
- package/dist/chunk-BESYRHQM.js.map +0 -1
- package/dist/chunk-BJPBT3CU.js +0 -21
- package/dist/chunk-BJPBT3CU.js.map +0 -1
- package/dist/chunk-BVYWGZVV.js.map +0 -1
- package/dist/chunk-CX5M4ZAG.js.map +0 -1
- package/dist/chunk-D7LCGMVS.js.map +0 -1
- package/dist/chunk-EGI6MUL6.js +0 -27
- package/dist/chunk-EGI6MUL6.js.map +0 -1
- package/dist/chunk-ERISIBYU.js.map +0 -1
- package/dist/chunk-HRO5HWN2.js.map +0 -1
- package/dist/chunk-HZLDFOE4.js.map +0 -1
- package/dist/chunk-JISYG63F.js +0 -70
- package/dist/chunk-JISYG63F.js.map +0 -1
- package/dist/chunk-LIMSTKYD.js +0 -61
- package/dist/chunk-LIMSTKYD.js.map +0 -1
- package/dist/chunk-OWAG3GSU.js.map +0 -1
- package/dist/chunk-PPMP5J6T.js.map +0 -1
- package/dist/chunk-Q5QRDWKI.js.map +0 -1
- package/dist/chunk-S5OFRT4M.js.map +0 -1
- package/dist/chunk-SBVILCCA.js.map +0 -1
- package/dist/chunk-TUMEWN34.js +0 -15
- package/dist/chunk-TUMEWN34.js.map +0 -1
- package/dist/chunk-XDNLUEXI.js +0 -138
- package/dist/chunk-XJ2HZOBU.js.map +0 -1
- package/dist/chunk-ZYTYSTO5.js.map +0 -1
- package/dist/chunk-ZZ2SS7NI.js +0 -237
- package/dist/chunk-ZZ2SS7NI.js.map +0 -1
- package/dist/database-C6jy7EOu.d.ts +0 -500
- package/dist/organisation-D6qRDtbF.d.ts +0 -93
- package/dist/schema-DTDZQe2u.d.ts +0 -28
- package/dist/unified-DQ4VcT7H.d.ts +0 -198
- package/dist/useInactivityTracker-TO6ZOF35.js +0 -11
- package/dist/validation.d.ts +0 -47
- package/dist/validation.js +0 -24
- package/dist/validation.js.map +0 -1
- package/docs/DOCUMENTATION_AUDIT.md +0 -172
- package/docs/DOCUMENTATION_STANDARD.md +0 -137
- package/docs/api/classes/PublicErrorBoundary.md +0 -132
- package/docs/api/interfaces/EventLogoProps.md +0 -152
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +0 -94
- package/docs/api/interfaces/PublicErrorBoundaryState.md +0 -68
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +0 -86
- package/docs/architecture/rpc-function-standards.md +0 -1106
- package/docs/getting-started/consuming-app-vite-config.md +0 -239
- package/docs/implementation-guides/event-theming-summary.md +0 -226
- package/docs/implementation-guides/public-pages-advanced.md +0 -1038
- package/docs/migration/v0.4.15-tailwind-scanning.md +0 -278
- package/docs/migration/v0.4.16-css-first-approach.md +0 -312
- package/docs/migration/v0.4.17-source-path-fix.md +0 -235
- package/docs/rbac/RBAC_EVENT_CONTEXT_LOADING.md +0 -222
- package/docs/rbac/RBAC_LOGIN_SAFETY_FIX.md +0 -95
- package/docs/rbac/RBAC_V0.5.147_FIX.md +0 -117
- package/docs/rbac/README-rbac-rls-integration.md +0 -374
- package/docs/styles/usage.md +0 -227
- package/docs/testing/visual-testing.md +0 -120
- package/docs/troubleshooting/DEBUG_NETWORK_ERROR.md +0 -152
- package/docs/troubleshooting/FIX_SUPABASE_CORS.md +0 -184
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +0 -193
- package/docs/troubleshooting/database-view-compatibility.md +0 -125
- package/docs/troubleshooting/react-hooks-issue-analysis.md +0 -172
- package/docs/troubleshooting/tailwind-content-scanning.md +0 -219
- package/examples/RBAC/EventBasedApp.tsx +0 -239
- package/examples/RBAC/PermissionExample.tsx +0 -151
- package/examples/STRUCTURE.md +0 -125
- package/examples/components 2/DataTable/HierarchicalExample.tsx +0 -475
- package/examples/components 2/Dialog/BasicHtmlTest.tsx +0 -55
- package/examples/components 2/Dialog/DebugHtmlExample.tsx +0 -68
- package/examples/components 2/Dialog/HtmlDialogExample.tsx +0 -202
- package/examples/components 2/Dialog/SimpleHtmlTest.tsx +0 -61
- package/examples/components 2/Dialog/SmartDialogExample.tsx +0 -322
- package/examples/components 2/index.ts +0 -11
- package/examples/features/index.ts +0 -12
- package/examples/features/rbac/CompleteRBACExample.tsx +0 -324
- package/examples/features/rbac/index.ts +0 -13
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +0 -301
- package/examples/public-pages/PublicEventPage.tsx +0 -274
- package/examples/public-pages/PublicPageApp.tsx +0 -308
- package/examples/public-pages/PublicPageUsageExample.tsx +0 -216
- package/examples/public-pages/index.ts +0 -14
- package/src/__tests__/TEST_STANDARD.md +0 -1008
- package/src/components/Checkbox/__mocks__/Checkbox.tsx +0 -2
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +0 -421
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +0 -177
- package/src/components/DataTable/examples/PerformanceExample.tsx +0 -506
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +0 -316
- package/src/components/DataTable/examples/__tests__/HierarchicalExample.test.tsx +0 -45
- package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +0 -211
- package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +0 -126
- package/src/components/Dialog/README.md +0 -804
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +0 -55
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +0 -68
- package/src/components/Dialog/examples/ScrollableDialogExample.tsx +0 -290
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +0 -61
- package/src/components/Dialog/examples/__tests__/HtmlDialogExample.test.tsx +0 -71
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +0 -122
- package/src/components/Dialog/examples/__tests__/SmartDialogExample.unit.test.tsx +0 -147
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +0 -611
- package/src/components/Dialog/utils/safeHtml.ts +0 -185
- package/src/components/EventSelector/types.ts +0 -79
- package/src/components/Form/FormErrorSummary.tsx +0 -113
- package/src/components/Form/FormField.tsx +0 -249
- package/src/components/Form/FormFieldset.tsx +0 -127
- package/src/components/Form/FormLiveRegion.tsx +0 -198
- package/src/components/Input/__mocks__/Input.tsx +0 -2
- package/src/components/NavigationMenu/types.ts +0 -85
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +0 -326
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -1078
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +0 -597
- package/src/components/PasswordReset/PasswordResetForm.tsx +0 -201
- package/src/components/PasswordReset/index.ts +0 -2
- package/src/components/ProtectedRoute/README.md +0 -164
- package/src/components/PublicLayout/EventLogo.tsx +0 -175
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +0 -282
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +0 -216
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +0 -131
- package/src/components/PublicLayout/PublicPageDebugger.tsx +0 -104
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +0 -162
- package/src/components/PublicLayout/PublicPageFooter.tsx +0 -124
- package/src/components/PublicLayout/PublicPageHeader.tsx +0 -209
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +0 -449
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +0 -393
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +0 -192
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +0 -351
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +0 -402
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +0 -460
- package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +0 -313
- package/src/components/Select/hooks.ts +0 -289
- package/src/hooks/useCounter.test.ts +0 -131
- package/src/hooks/useDebounce.test.ts +0 -375
- package/src/providers/AuthProvider.tsx +0 -15
- package/src/providers/EventProvider.tsx +0 -16
- package/src/providers/InactivityProvider.tsx +0 -15
- package/src/providers/OrganisationProvider.context.test.tsx +0 -169
- package/src/providers/UnifiedAuthProvider.tsx +0 -15
- package/src/types/theme.ts +0 -6
- package/src/types/unified.ts +0 -265
- package/src/utils/appConfig.ts +0 -47
- package/src/utils/appIdResolver.test.ts +0 -499
- package/src/utils/appIdResolver.ts +0 -130
- package/src/utils/appNameResolver.simple.test.ts +0 -212
- package/src/utils/appNameResolver.test.ts +0 -121
- package/src/utils/appNameResolver.ts +0 -191
- package/src/utils/audit.ts +0 -127
- package/src/utils/auth-utils.ts +0 -96
- package/src/utils/bundleAnalysis.ts +0 -129
- package/src/utils/debugLogger.ts +0 -67
- package/src/utils/deviceFingerprint.ts +0 -215
- package/src/utils/dynamicUtils.ts +0 -105
- package/src/utils/file-reference.test.ts +0 -788
- package/src/utils/file-reference.ts +0 -519
- package/src/utils/formatDate.test.ts +0 -237
- package/src/utils/formatting.ts +0 -170
- package/src/utils/lazyLoad.tsx +0 -44
- package/src/utils/logger.ts +0 -179
- package/src/utils/organisationContext.test.ts +0 -322
- package/src/utils/organisationContext.ts +0 -153
- package/src/utils/performanceBenchmark.ts +0 -64
- package/src/utils/performanceBudgets.ts +0 -110
- package/src/utils/permissionTypes.ts +0 -37
- package/src/utils/permissionUtils.test.ts +0 -393
- package/src/utils/permissionUtils.ts +0 -34
- package/src/utils/sanitization.ts +0 -264
- package/src/utils/schemaUtils.ts +0 -37
- package/src/utils/secureDataAccess.test.ts +0 -711
- package/src/utils/secureDataAccess.ts +0 -377
- package/src/utils/secureErrors.ts +0 -79
- package/src/utils/security.ts +0 -156
- package/src/utils/securityMonitor.ts +0 -45
- package/src/utils/sessionTracking.ts +0 -126
- package/src/utils/validation.ts +0 -111
- package/src/utils/validationUtils.ts +0 -120
- package/src/validation/index.ts +0 -12
- /package/dist/{DataTable-UA6CL4JI.js.map → DataTable-QAB34V6K.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-B37ATQHE.js.map → UnifiedAuthProvider-7F6T4B6K.js.map} +0 -0
- /package/dist/{api-45XYYO2A.js.map → api-ROMBCNKU.js.map} +0 -0
- /package/dist/{audit-64X3VJXB.js.map → audit-WRS3KJKI.js.map} +0 -0
- /package/dist/{chunk-PLDDJCW6.js.map → chunk-7D4SUZUM.js.map} +0 -0
- /package/dist/{useInactivityTracker-TO6ZOF35.js.map → chunk-KQCRWDSA.js.map} +0 -0
- /package/examples/{components 2/DataTable → DataTable}/InitialPageSizeExample.tsx +0 -0
- /package/examples/{features/public-pages → PublicPages}/index.ts +0 -0
- /package/examples/{RBAC → rbac}/index.ts +0 -0
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* @file PaceAppLayout Component Tests
|
|
3
3
|
* @description Comprehensive test suite for PaceAppLayout component
|
|
4
4
|
* @package @jmruthers/pace-core
|
|
5
|
+
* @module Components/PaceAppLayout
|
|
6
|
+
* @since 0.1.0
|
|
7
|
+
*
|
|
8
|
+
* Comprehensive test suite for PaceAppLayout component following testing guidelines.
|
|
9
|
+
* Tests cover all major functionality, edge cases, and user interactions.
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
12
|
import React from 'react';
|
|
@@ -11,11 +16,27 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
11
16
|
import { MemoryRouter } from 'react-router-dom';
|
|
12
17
|
import { PaceAppLayout } from './PaceAppLayout';
|
|
13
18
|
import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
14
|
-
import { useUnifiedAuth } from '../../providers/UnifiedAuthProvider';
|
|
19
|
+
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
20
|
+
import {
|
|
21
|
+
mockNavigate,
|
|
22
|
+
mockLocation,
|
|
23
|
+
mockUser,
|
|
24
|
+
mockSignOut,
|
|
25
|
+
mockUpdatePassword,
|
|
26
|
+
mockOrganisation,
|
|
27
|
+
mockOrganisationContext,
|
|
28
|
+
mockIsPermitted,
|
|
29
|
+
mockHasPermissionRBAC,
|
|
30
|
+
mockUseRBAC,
|
|
31
|
+
mockUseCan,
|
|
32
|
+
resetPaceAppLayoutMocks,
|
|
33
|
+
} from './test-setup.tsx';
|
|
15
34
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
35
|
+
// Import the hoisted mock for isSuperAdmin
|
|
36
|
+
let mockIsSuperAdmin: ReturnType<typeof vi.fn>;
|
|
37
|
+
|
|
38
|
+
// Override mockLocation pathname for this test file
|
|
39
|
+
mockLocation.pathname = '/dashboard';
|
|
19
40
|
|
|
20
41
|
vi.mock('react-router-dom', async () => {
|
|
21
42
|
const actual = await vi.importActual('react-router-dom');
|
|
@@ -28,62 +49,27 @@ vi.mock('react-router-dom', async () => {
|
|
|
28
49
|
});
|
|
29
50
|
|
|
30
51
|
// Mock UnifiedAuth hook
|
|
31
|
-
const mockUser = {
|
|
32
|
-
id: 'user-123',
|
|
33
|
-
email: 'test@example.com',
|
|
34
|
-
user_metadata: {
|
|
35
|
-
organisationId: 'org-123',
|
|
36
|
-
eventId: 'event-123',
|
|
37
|
-
appId: 'app-123',
|
|
38
|
-
is_admin: false,
|
|
39
|
-
},
|
|
40
|
-
app_metadata: {
|
|
41
|
-
organisationId: 'org-123',
|
|
42
|
-
eventId: 'event-123',
|
|
43
|
-
appId: 'app-123',
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
52
|
const mockUnifiedAuth = {
|
|
48
|
-
user: mockUser,
|
|
49
|
-
signOut:
|
|
50
|
-
updatePassword:
|
|
53
|
+
user: { ...mockUser, id: 'user-123', user_metadata: { ...mockUser.user_metadata, is_admin: false }, app_metadata: { organisationId: 'org-123', eventId: 'event-123', appId: 'app-123' } },
|
|
54
|
+
signOut: mockSignOut,
|
|
55
|
+
updatePassword: mockUpdatePassword,
|
|
51
56
|
};
|
|
52
57
|
|
|
53
|
-
vi.
|
|
54
|
-
|
|
58
|
+
const mockUseUnifiedAuthFn = vi.fn(() => mockUnifiedAuth);
|
|
59
|
+
vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
|
|
60
|
+
useUnifiedAuth: () => mockUseUnifiedAuthFn(),
|
|
61
|
+
UnifiedAuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
55
62
|
}));
|
|
56
63
|
|
|
57
64
|
// Mock useOrganisations hook
|
|
58
|
-
const mockSelectedOrganisation = {
|
|
59
|
-
id: 'org-123',
|
|
60
|
-
name: 'Test Organisation',
|
|
61
|
-
display_name: 'Test Organisation',
|
|
62
|
-
slug: 'test-org',
|
|
63
|
-
description: 'Test organisation',
|
|
64
|
-
subscription_tier: 'basic',
|
|
65
|
-
settings: {},
|
|
66
|
-
is_active: true,
|
|
67
|
-
created_at: '2023-01-01T00:00:00Z',
|
|
68
|
-
updated_at: '2023-01-01T00:00:00Z',
|
|
69
|
-
};
|
|
65
|
+
const mockSelectedOrganisation = { ...mockOrganisation, id: 'org-123', slug: 'test-org' };
|
|
70
66
|
|
|
71
67
|
vi.mock('../../hooks/useOrganisations', () => ({
|
|
72
68
|
useOrganisations: vi.fn(() => ({
|
|
69
|
+
...mockOrganisationContext,
|
|
73
70
|
selectedOrganisation: mockSelectedOrganisation,
|
|
74
71
|
organisations: [mockSelectedOrganisation],
|
|
75
72
|
userMemberships: [],
|
|
76
|
-
isLoading: false,
|
|
77
|
-
error: null,
|
|
78
|
-
hasValidOrganisationContext: true,
|
|
79
|
-
setSelectedOrganisation: vi.fn(),
|
|
80
|
-
switchOrganisation: vi.fn().mockResolvedValue(undefined),
|
|
81
|
-
getUserRole: vi.fn().mockReturnValue('member'),
|
|
82
|
-
validateOrganisationAccess: vi.fn().mockReturnValue(true),
|
|
83
|
-
ensureOrganisationContext: vi.fn().mockReturnValue(mockSelectedOrganisation),
|
|
84
|
-
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
85
|
-
getPrimaryOrganisation: vi.fn().mockReturnValue(mockSelectedOrganisation),
|
|
86
|
-
isOrganisationSecure: vi.fn().mockReturnValue(true),
|
|
87
73
|
})),
|
|
88
74
|
}));
|
|
89
75
|
|
|
@@ -102,48 +88,108 @@ vi.mock('../../hooks/useEventTheme', () => ({
|
|
|
102
88
|
useEventTheme: vi.fn(),
|
|
103
89
|
}));
|
|
104
90
|
|
|
91
|
+
// Mock logger
|
|
92
|
+
// Note: Must define mock inside factory to avoid hoisting issues
|
|
93
|
+
vi.mock('../../utils/core/logger', () => {
|
|
94
|
+
const mockLogger = {
|
|
95
|
+
debug: vi.fn(),
|
|
96
|
+
info: vi.fn(),
|
|
97
|
+
warn: vi.fn(),
|
|
98
|
+
error: vi.fn(),
|
|
99
|
+
createScopedLogger: vi.fn(() => mockLogger),
|
|
100
|
+
configure: vi.fn(),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
logger: mockLogger,
|
|
105
|
+
createLogger: () => mockLogger,
|
|
106
|
+
Logger: {
|
|
107
|
+
debug: vi.fn(),
|
|
108
|
+
info: vi.fn(),
|
|
109
|
+
warn: vi.fn(),
|
|
110
|
+
error: vi.fn(),
|
|
111
|
+
configure: vi.fn(),
|
|
112
|
+
createScopedLogger: vi.fn(() => mockLogger),
|
|
113
|
+
},
|
|
114
|
+
LogLevel: {
|
|
115
|
+
DEBUG: 0,
|
|
116
|
+
INFO: 1,
|
|
117
|
+
WARN: 2,
|
|
118
|
+
ERROR: 3,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Mock EventServiceProvider to avoid context requirement
|
|
124
|
+
vi.mock('../../providers/services/EventServiceProvider', () => ({
|
|
125
|
+
EventServiceProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
126
|
+
useEventService: vi.fn(() => ({
|
|
127
|
+
getEvent: vi.fn(),
|
|
128
|
+
getEvents: vi.fn(),
|
|
129
|
+
createEvent: vi.fn(),
|
|
130
|
+
updateEvent: vi.fn(),
|
|
131
|
+
deleteEvent: vi.fn(),
|
|
132
|
+
})),
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
// Mock Supabase client for RBAC engine
|
|
136
|
+
// Note: Must define mock inside factory to avoid hoisting issues
|
|
137
|
+
vi.mock('@supabase/supabase-js', () => {
|
|
138
|
+
const createMockQueryBuilder = () => ({
|
|
139
|
+
select: vi.fn().mockReturnThis(),
|
|
140
|
+
eq: vi.fn().mockReturnThis(),
|
|
141
|
+
single: vi.fn().mockResolvedValue({ data: null, error: null }),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const mockSupabaseClient = {
|
|
145
|
+
from: vi.fn(() => createMockQueryBuilder()),
|
|
146
|
+
rpc: vi.fn().mockResolvedValue({ data: null, error: null }),
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
createClient: vi.fn(() => mockSupabaseClient),
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
|
|
105
154
|
// Mock RBAC functions
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
const mockIsSuperAdmin = vi.fn().mockResolvedValue(false);
|
|
155
|
+
// Use hoisted mock for isSuperAdmin so it can be accessed in tests
|
|
156
|
+
const mockIsSuperAdminAPI = vi.hoisted(() => vi.fn().mockResolvedValue(false));
|
|
109
157
|
|
|
110
158
|
vi.mock('../../rbac/api', () => ({
|
|
111
|
-
isPermitted: vi.fn(),
|
|
112
|
-
isPermittedCached: vi.fn(),
|
|
113
|
-
isSuperAdmin:
|
|
159
|
+
isPermitted: vi.fn().mockResolvedValue(true),
|
|
160
|
+
isPermittedCached: vi.fn().mockResolvedValue(true),
|
|
161
|
+
isSuperAdmin: mockIsSuperAdminAPI,
|
|
114
162
|
setupRBAC: vi.fn(),
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// Mock useCan hook - this is what PaceAppLayout actually uses
|
|
118
|
-
const mockUseCan = vi.fn(() => ({
|
|
119
|
-
can: true,
|
|
120
|
-
isLoading: false,
|
|
121
|
-
error: null,
|
|
122
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
163
|
+
getPermissionMap: vi.fn().mockResolvedValue({}),
|
|
164
|
+
getAccessLevel: vi.fn().mockResolvedValue('viewer'),
|
|
123
165
|
}));
|
|
124
166
|
|
|
125
167
|
// Mock RBAC hooks
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
168
|
+
// Note: Must use function syntax in factory to avoid hoisting issues
|
|
169
|
+
// Import mockUseCan from test-setup so tests can control it
|
|
170
|
+
vi.mock('../../rbac/hooks', async () => {
|
|
171
|
+
const testSetup = await import('./test-setup.tsx');
|
|
172
|
+
return {
|
|
173
|
+
useRBAC: vi.fn(() => ({
|
|
174
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
175
|
+
isLoading: false,
|
|
176
|
+
error: null,
|
|
177
|
+
hasGlobalPermission: vi.fn().mockResolvedValue(true),
|
|
178
|
+
hasOrganisationPermission: vi.fn().mockResolvedValue(true),
|
|
179
|
+
hasEventPermission: vi.fn().mockResolvedValue(true),
|
|
180
|
+
globalRole: null,
|
|
181
|
+
organisationRoles: [],
|
|
182
|
+
eventRoles: [],
|
|
183
|
+
permissionMap: {},
|
|
184
|
+
})),
|
|
185
|
+
useCan: (...args: any[]) => testSetup.mockUseCan(...args),
|
|
186
|
+
useResolvedScope: vi.fn(() => ({
|
|
187
|
+
resolvedScope: { organisationId: 'org-123', eventId: 'event-123', appId: 'app-123' },
|
|
188
|
+
isLoading: false,
|
|
189
|
+
error: null,
|
|
190
|
+
})),
|
|
191
|
+
};
|
|
192
|
+
});
|
|
147
193
|
|
|
148
194
|
// Mock Header component
|
|
149
195
|
vi.mock('../Header', () => ({
|
|
@@ -232,27 +278,18 @@ describe('PaceAppLayout Component', () => {
|
|
|
232
278
|
};
|
|
233
279
|
|
|
234
280
|
beforeEach(async () => {
|
|
235
|
-
|
|
236
|
-
// Reset location mock
|
|
281
|
+
resetPaceAppLayoutMocks();
|
|
282
|
+
// Reset location mock for this test file
|
|
237
283
|
mockLocation.pathname = '/dashboard';
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
mockUseCan.mockReturnValue({
|
|
242
|
-
can: true,
|
|
243
|
-
isLoading: false,
|
|
244
|
-
error: null,
|
|
245
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
246
|
-
});
|
|
284
|
+
// Get the mocked isSuperAdmin function
|
|
285
|
+
const rbacApi = await import('../../rbac/api');
|
|
286
|
+
mockIsSuperAdmin = vi.mocked(rbacApi.isSuperAdmin);
|
|
247
287
|
// Reset RBAC API mocks
|
|
248
288
|
const { isPermitted, isPermittedCached } = await import('../../rbac/api');
|
|
249
289
|
vi.mocked(isPermitted).mockReset();
|
|
250
290
|
vi.mocked(isPermitted).mockResolvedValue(true);
|
|
251
291
|
vi.mocked(isPermittedCached).mockReset();
|
|
252
292
|
vi.mocked(isPermittedCached).mockResolvedValue(true);
|
|
253
|
-
// Reset super admin mock (use module-level mockIsSuperAdmin)
|
|
254
|
-
mockIsSuperAdmin.mockReset();
|
|
255
|
-
mockIsSuperAdmin.mockResolvedValue(false);
|
|
256
293
|
});
|
|
257
294
|
|
|
258
295
|
describe('Basic Rendering', () => {
|
|
@@ -260,7 +297,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
260
297
|
renderWithProviders(
|
|
261
298
|
<TestWrapper>
|
|
262
299
|
<PaceAppLayout {...defaultProps} />
|
|
263
|
-
</TestWrapper
|
|
300
|
+
</TestWrapper>,
|
|
301
|
+
{ withRouter: false }
|
|
264
302
|
);
|
|
265
303
|
|
|
266
304
|
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
@@ -272,7 +310,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
272
310
|
renderWithProviders(
|
|
273
311
|
<TestWrapper>
|
|
274
312
|
<PaceAppLayout {...defaultProps} appName="Custom App" />
|
|
275
|
-
</TestWrapper
|
|
313
|
+
</TestWrapper>,
|
|
314
|
+
{ withRouter: false }
|
|
276
315
|
);
|
|
277
316
|
|
|
278
317
|
expect(screen.getByTestId('header-logo')).toHaveAttribute('data-logo-alt', 'Custom App Logo');
|
|
@@ -287,7 +326,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
287
326
|
renderWithProviders(
|
|
288
327
|
<TestWrapper>
|
|
289
328
|
<PaceAppLayout {...defaultProps} navItems={customNavItems} />
|
|
290
|
-
</TestWrapper
|
|
329
|
+
</TestWrapper>,
|
|
330
|
+
{ withRouter: false }
|
|
291
331
|
);
|
|
292
332
|
|
|
293
333
|
expect(screen.getByTestId('nav-item-home')).toBeInTheDocument();
|
|
@@ -298,7 +338,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
298
338
|
renderWithProviders(
|
|
299
339
|
<TestWrapper>
|
|
300
340
|
<PaceAppLayout {...defaultProps} />
|
|
301
|
-
</TestWrapper
|
|
341
|
+
</TestWrapper>,
|
|
342
|
+
{ withRouter: false }
|
|
302
343
|
);
|
|
303
344
|
|
|
304
345
|
expect(screen.getByTestId('nav-item-home')).toBeInTheDocument();
|
|
@@ -312,7 +353,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
312
353
|
renderWithProviders(
|
|
313
354
|
<TestWrapper>
|
|
314
355
|
<PaceAppLayout {...defaultProps} headerActions={headerActions} />
|
|
315
|
-
</TestWrapper
|
|
356
|
+
</TestWrapper>,
|
|
357
|
+
{ withRouter: false }
|
|
316
358
|
);
|
|
317
359
|
|
|
318
360
|
expect(screen.getByTestId('custom-actions')).toBeInTheDocument();
|
|
@@ -324,7 +366,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
324
366
|
renderWithProviders(
|
|
325
367
|
<TestWrapper>
|
|
326
368
|
<PaceAppLayout {...defaultProps} customLogo={customLogo} />
|
|
327
|
-
</TestWrapper
|
|
369
|
+
</TestWrapper>,
|
|
370
|
+
{ withRouter: false }
|
|
328
371
|
);
|
|
329
372
|
|
|
330
373
|
expect(screen.getByTestId('custom-logo')).toBeInTheDocument();
|
|
@@ -336,7 +379,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
336
379
|
renderWithProviders(
|
|
337
380
|
<TestWrapper>
|
|
338
381
|
<PaceAppLayout {...defaultProps} customUserMenu={customUserMenu} />
|
|
339
|
-
</TestWrapper
|
|
382
|
+
</TestWrapper>,
|
|
383
|
+
{ withRouter: false }
|
|
340
384
|
);
|
|
341
385
|
|
|
342
386
|
expect(screen.getByTestId('custom-user-menu')).toBeInTheDocument();
|
|
@@ -346,10 +390,25 @@ describe('PaceAppLayout Component', () => {
|
|
|
346
390
|
renderWithProviders(
|
|
347
391
|
<TestWrapper>
|
|
348
392
|
<PaceAppLayout {...defaultProps} headerClassName="custom-header" />
|
|
349
|
-
</TestWrapper
|
|
393
|
+
</TestWrapper>,
|
|
394
|
+
{ withRouter: false }
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const header = screen.getByTestId('header');
|
|
398
|
+
expect(header).toBeInTheDocument();
|
|
399
|
+
expect(header).toBeVisible();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('uses default header className when not provided', () => {
|
|
403
|
+
renderWithProviders(
|
|
404
|
+
<TestWrapper>
|
|
405
|
+
<PaceAppLayout {...defaultProps} />
|
|
406
|
+
</TestWrapper>,
|
|
407
|
+
{ withRouter: false }
|
|
350
408
|
);
|
|
351
409
|
|
|
352
|
-
|
|
410
|
+
const header = screen.getByTestId('header');
|
|
411
|
+
expect(header).toHaveClass('sticky', 'top-0', 'z-[40]', 'w-full');
|
|
353
412
|
});
|
|
354
413
|
});
|
|
355
414
|
|
|
@@ -358,7 +417,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
358
417
|
renderWithProviders(
|
|
359
418
|
<TestWrapper>
|
|
360
419
|
<PaceAppLayout {...defaultProps} />
|
|
361
|
-
</TestWrapper
|
|
420
|
+
</TestWrapper>,
|
|
421
|
+
{ withRouter: false }
|
|
362
422
|
);
|
|
363
423
|
|
|
364
424
|
expect(screen.getByTestId('header')).toHaveAttribute('data-show-event-selector', 'true');
|
|
@@ -368,11 +428,23 @@ describe('PaceAppLayout Component', () => {
|
|
|
368
428
|
renderWithProviders(
|
|
369
429
|
<TestWrapper>
|
|
370
430
|
<PaceAppLayout {...defaultProps} showEventSelector={false} />
|
|
371
|
-
</TestWrapper
|
|
431
|
+
</TestWrapper>,
|
|
432
|
+
{ withRouter: false }
|
|
372
433
|
);
|
|
373
434
|
|
|
374
435
|
expect(screen.getByTestId('header')).toHaveAttribute('data-show-event-selector', 'false');
|
|
375
436
|
});
|
|
437
|
+
|
|
438
|
+
it('shows event selector when showEventSelector is explicitly true', () => {
|
|
439
|
+
renderWithProviders(
|
|
440
|
+
<TestWrapper>
|
|
441
|
+
<PaceAppLayout {...defaultProps} showEventSelector={true} />
|
|
442
|
+
</TestWrapper>,
|
|
443
|
+
{ withRouter: false }
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
expect(screen.getByTestId('header')).toHaveAttribute('data-show-event-selector', 'true');
|
|
447
|
+
});
|
|
376
448
|
});
|
|
377
449
|
|
|
378
450
|
describe('User Menu Control', () => {
|
|
@@ -380,7 +452,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
380
452
|
renderWithProviders(
|
|
381
453
|
<TestWrapper>
|
|
382
454
|
<PaceAppLayout {...defaultProps} />
|
|
383
|
-
</TestWrapper
|
|
455
|
+
</TestWrapper>,
|
|
456
|
+
{ withRouter: false }
|
|
384
457
|
);
|
|
385
458
|
|
|
386
459
|
expect(screen.getByTestId('header')).toHaveAttribute('data-show-user-menu', 'true');
|
|
@@ -390,7 +463,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
390
463
|
renderWithProviders(
|
|
391
464
|
<TestWrapper>
|
|
392
465
|
<PaceAppLayout {...defaultProps} showUserMenu={false} />
|
|
393
|
-
</TestWrapper
|
|
466
|
+
</TestWrapper>,
|
|
467
|
+
{ withRouter: false }
|
|
394
468
|
);
|
|
395
469
|
|
|
396
470
|
expect(screen.getByTestId('header')).toHaveAttribute('data-show-user-menu', 'false');
|
|
@@ -403,7 +477,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
403
477
|
renderWithProviders(
|
|
404
478
|
<TestWrapper>
|
|
405
479
|
<PaceAppLayout {...defaultProps} />
|
|
406
|
-
</TestWrapper
|
|
480
|
+
</TestWrapper>,
|
|
481
|
+
{ withRouter: false }
|
|
407
482
|
);
|
|
408
483
|
|
|
409
484
|
await user.click(screen.getByTestId('sign-out-btn'));
|
|
@@ -415,7 +490,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
415
490
|
renderWithProviders(
|
|
416
491
|
<TestWrapper>
|
|
417
492
|
<PaceAppLayout {...defaultProps} />
|
|
418
|
-
</TestWrapper
|
|
493
|
+
</TestWrapper>,
|
|
494
|
+
{ withRouter: false }
|
|
419
495
|
);
|
|
420
496
|
|
|
421
497
|
await user.click(screen.getByTestId('change-password-btn'));
|
|
@@ -432,7 +508,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
432
508
|
renderWithProviders(
|
|
433
509
|
<TestWrapper>
|
|
434
510
|
<PaceAppLayout {...defaultProps} navItems={customNavItems} />
|
|
435
|
-
</TestWrapper
|
|
511
|
+
</TestWrapper>,
|
|
512
|
+
{ withRouter: false }
|
|
436
513
|
);
|
|
437
514
|
|
|
438
515
|
await user.click(screen.getByTestId('nav-item-home'));
|
|
@@ -441,6 +518,18 @@ describe('PaceAppLayout Component', () => {
|
|
|
441
518
|
await user.click(screen.getByTestId('nav-item-about'));
|
|
442
519
|
expect(mockNavigate).toHaveBeenCalledWith('/about');
|
|
443
520
|
});
|
|
521
|
+
|
|
522
|
+
it('passes current path to header', () => {
|
|
523
|
+
renderWithProviders(
|
|
524
|
+
<TestWrapper>
|
|
525
|
+
<PaceAppLayout {...defaultProps} />
|
|
526
|
+
</TestWrapper>,
|
|
527
|
+
{ withRouter: false }
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
// The header mock doesn't show current path in this test setup, but we can verify it's rendered
|
|
531
|
+
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
532
|
+
});
|
|
444
533
|
});
|
|
445
534
|
|
|
446
535
|
describe('Permission Enforcement', () => {
|
|
@@ -448,7 +537,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
448
537
|
renderWithProviders(
|
|
449
538
|
<TestWrapper>
|
|
450
539
|
<PaceAppLayout {...defaultProps} enforcePermissions={false} />
|
|
451
|
-
</TestWrapper
|
|
540
|
+
</TestWrapper>,
|
|
541
|
+
{ withRouter: false }
|
|
452
542
|
);
|
|
453
543
|
|
|
454
544
|
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
@@ -467,7 +557,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
467
557
|
renderWithProviders(
|
|
468
558
|
<TestWrapper>
|
|
469
559
|
<PaceAppLayout {...defaultProps} enforcePermissions={true} />
|
|
470
|
-
</TestWrapper
|
|
560
|
+
</TestWrapper>,
|
|
561
|
+
{ withRouter: false }
|
|
471
562
|
);
|
|
472
563
|
|
|
473
564
|
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
@@ -475,8 +566,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
475
566
|
});
|
|
476
567
|
|
|
477
568
|
it('shows permission error when check fails', async () => {
|
|
478
|
-
// Ensure super admin check completes first
|
|
479
|
-
mockIsSuperAdmin.
|
|
569
|
+
// Ensure super admin check completes first - resolve immediately
|
|
570
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
480
571
|
|
|
481
572
|
// Mock useCan to return an error state
|
|
482
573
|
mockUseCan.mockReturnValue({
|
|
@@ -489,26 +580,27 @@ describe('PaceAppLayout Component', () => {
|
|
|
489
580
|
renderWithProviders(
|
|
490
581
|
<TestWrapper>
|
|
491
582
|
<PaceAppLayout {...defaultProps} enforcePermissions={true} />
|
|
492
|
-
</TestWrapper
|
|
583
|
+
</TestWrapper>,
|
|
584
|
+
{ withRouter: false }
|
|
493
585
|
);
|
|
494
586
|
|
|
495
587
|
// Wait for super admin check to complete and component to re-render
|
|
588
|
+
// The component calls isSuperAdmin in a useEffect, so we need to wait for it
|
|
496
589
|
await waitFor(() => {
|
|
497
590
|
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
498
|
-
}, { timeout:
|
|
499
|
-
|
|
500
|
-
// Wait a bit for the component to process the super admin check result
|
|
501
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
591
|
+
}, { timeout: 2000 });
|
|
502
592
|
|
|
593
|
+
// Wait for the component to process the super admin check result and render error
|
|
594
|
+
// Need to wait for both checks to complete (super admin + permission)
|
|
503
595
|
await waitFor(() => {
|
|
504
596
|
expect(screen.getByText('Permission Error')).toBeInTheDocument();
|
|
505
597
|
expect(screen.getByText('Permission check failed')).toBeInTheDocument();
|
|
506
|
-
});
|
|
598
|
+
}, { timeout: 3000 });
|
|
507
599
|
});
|
|
508
600
|
|
|
509
601
|
it('shows access denied when user lacks permission', async () => {
|
|
510
|
-
// Ensure super admin check completes first
|
|
511
|
-
mockIsSuperAdmin.
|
|
602
|
+
// Ensure super admin check completes first - resolve immediately
|
|
603
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
512
604
|
|
|
513
605
|
// Mock useCan to return false (no permission)
|
|
514
606
|
mockUseCan.mockReturnValue({
|
|
@@ -521,26 +613,98 @@ describe('PaceAppLayout Component', () => {
|
|
|
521
613
|
renderWithProviders(
|
|
522
614
|
<TestWrapper>
|
|
523
615
|
<PaceAppLayout {...defaultProps} enforcePermissions={true} />
|
|
524
|
-
</TestWrapper
|
|
616
|
+
</TestWrapper>,
|
|
617
|
+
{ withRouter: false }
|
|
525
618
|
);
|
|
526
619
|
|
|
527
620
|
// Wait for super admin check to complete and component to re-render
|
|
528
621
|
await waitFor(() => {
|
|
529
622
|
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
530
|
-
}, { timeout:
|
|
531
|
-
|
|
532
|
-
// Wait a bit for the component to process the super admin check result
|
|
533
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
623
|
+
}, { timeout: 2000 });
|
|
534
624
|
|
|
625
|
+
// Wait for the component to process the super admin check result and render access denied
|
|
626
|
+
// Need to wait for both checks to complete (super admin + permission)
|
|
535
627
|
await waitFor(() => {
|
|
536
628
|
expect(screen.getByText('Access Denied')).toBeInTheDocument();
|
|
537
629
|
expect(screen.getByText("You don't have permission to access this page.")).toBeInTheDocument();
|
|
630
|
+
}, { timeout: 3000 });
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it('provides go home button in access denied state', async () => {
|
|
634
|
+
// Ensure super admin check completes first - resolve immediately
|
|
635
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
636
|
+
|
|
637
|
+
// Mock useCan to return false (no permission)
|
|
638
|
+
mockUseCan.mockReturnValue({
|
|
639
|
+
can: false,
|
|
640
|
+
isLoading: false,
|
|
641
|
+
error: null,
|
|
642
|
+
refetch: vi.fn().mockResolvedValue(undefined),
|
|
538
643
|
});
|
|
644
|
+
|
|
645
|
+
renderWithProviders(
|
|
646
|
+
<TestWrapper>
|
|
647
|
+
<PaceAppLayout {...defaultProps} enforcePermissions={true} />
|
|
648
|
+
</TestWrapper>,
|
|
649
|
+
{ withRouter: false }
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
// Wait for super admin check to complete and component to re-render
|
|
653
|
+
await waitFor(() => {
|
|
654
|
+
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
655
|
+
}, { timeout: 2000 });
|
|
656
|
+
|
|
657
|
+
// Wait for the component to process the super admin check result and render access denied
|
|
658
|
+
await waitFor(() => {
|
|
659
|
+
const goHomeButton = screen.getByText('Go Home');
|
|
660
|
+
expect(goHomeButton).toBeInTheDocument();
|
|
661
|
+
}, { timeout: 3000 });
|
|
662
|
+
|
|
663
|
+
const user = userEvent.setup();
|
|
664
|
+
await user.click(screen.getByText('Go Home'));
|
|
665
|
+
expect(mockNavigate).toHaveBeenCalledWith('/');
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('provides go home button in permission error state', async () => {
|
|
669
|
+
const mockError = new Error('Permission check failed');
|
|
670
|
+
|
|
671
|
+
// Ensure super admin check completes first - resolve immediately
|
|
672
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
673
|
+
|
|
674
|
+
// Mock useCan to return an error state
|
|
675
|
+
mockUseCan.mockReturnValue({
|
|
676
|
+
can: false,
|
|
677
|
+
isLoading: false,
|
|
678
|
+
error: mockError,
|
|
679
|
+
refetch: vi.fn().mockResolvedValue(undefined),
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
renderWithProviders(
|
|
683
|
+
<TestWrapper>
|
|
684
|
+
<PaceAppLayout {...defaultProps} enforcePermissions={true} />
|
|
685
|
+
</TestWrapper>,
|
|
686
|
+
{ withRouter: false }
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
// Wait for super admin check to complete and component to re-render
|
|
690
|
+
await waitFor(() => {
|
|
691
|
+
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
692
|
+
}, { timeout: 2000 });
|
|
693
|
+
|
|
694
|
+
// Wait for the component to process the super admin check result and render error
|
|
695
|
+
await waitFor(() => {
|
|
696
|
+
const goHomeButton = screen.getByText('Go Home');
|
|
697
|
+
expect(goHomeButton).toBeInTheDocument();
|
|
698
|
+
}, { timeout: 3000 });
|
|
699
|
+
|
|
700
|
+
const user = userEvent.setup();
|
|
701
|
+
await user.click(screen.getByText('Go Home'));
|
|
702
|
+
expect(mockNavigate).toHaveBeenCalledWith('/');
|
|
539
703
|
});
|
|
540
704
|
|
|
541
705
|
it('shows custom permission fallback when provided', async () => {
|
|
542
|
-
// Ensure super admin check completes first
|
|
543
|
-
mockIsSuperAdmin.
|
|
706
|
+
// Ensure super admin check completes first - resolve immediately
|
|
707
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
544
708
|
|
|
545
709
|
// Arrange
|
|
546
710
|
mockUseCan.mockReturnValue({
|
|
@@ -559,26 +723,24 @@ describe('PaceAppLayout Component', () => {
|
|
|
559
723
|
enforcePermissions={true}
|
|
560
724
|
permissionFallback={customFallback}
|
|
561
725
|
/>
|
|
562
|
-
</TestWrapper
|
|
726
|
+
</TestWrapper>,
|
|
727
|
+
{ withRouter: false }
|
|
563
728
|
);
|
|
564
729
|
|
|
565
730
|
// Wait for super admin check to complete and component to re-render
|
|
566
731
|
await waitFor(() => {
|
|
567
732
|
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
568
|
-
}, { timeout:
|
|
569
|
-
|
|
570
|
-
// Wait a bit for the component to process the super admin check result
|
|
571
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
733
|
+
}, { timeout: 2000 });
|
|
572
734
|
|
|
573
|
-
//
|
|
735
|
+
// Wait for the component to process the super admin check result and render fallback
|
|
574
736
|
await waitFor(() => {
|
|
575
737
|
expect(screen.getByTestId('custom-fallback')).toBeInTheDocument();
|
|
576
|
-
});
|
|
738
|
+
}, { timeout: 3000 });
|
|
577
739
|
});
|
|
578
740
|
|
|
579
741
|
it('shows page permission fallback when enforcePagePermissions is true', async () => {
|
|
580
|
-
// Ensure super admin check completes first
|
|
581
|
-
mockIsSuperAdmin.
|
|
742
|
+
// Ensure super admin check completes first - resolve immediately
|
|
743
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
582
744
|
|
|
583
745
|
// Arrange
|
|
584
746
|
mockUseCan.mockReturnValue({
|
|
@@ -598,21 +760,19 @@ describe('PaceAppLayout Component', () => {
|
|
|
598
760
|
enforcePagePermissions={true}
|
|
599
761
|
pagePermissionFallback={pageFallback}
|
|
600
762
|
/>
|
|
601
|
-
</TestWrapper
|
|
763
|
+
</TestWrapper>,
|
|
764
|
+
{ withRouter: false }
|
|
602
765
|
);
|
|
603
766
|
|
|
604
767
|
// Wait for super admin check to complete and component to re-render
|
|
605
768
|
await waitFor(() => {
|
|
606
769
|
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
607
|
-
}, { timeout:
|
|
608
|
-
|
|
609
|
-
// Wait a bit for the component to process the super admin check result
|
|
610
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
770
|
+
}, { timeout: 2000 });
|
|
611
771
|
|
|
612
|
-
//
|
|
772
|
+
// Wait for the component to process the super admin check result and render fallback
|
|
613
773
|
await waitFor(() => {
|
|
614
774
|
expect(screen.getByTestId('page-fallback')).toBeInTheDocument();
|
|
615
|
-
});
|
|
775
|
+
}, { timeout: 3000 });
|
|
616
776
|
});
|
|
617
777
|
});
|
|
618
778
|
|
|
@@ -634,14 +794,14 @@ describe('PaceAppLayout Component', () => {
|
|
|
634
794
|
{...defaultProps}
|
|
635
795
|
navItems={customNavItems}
|
|
636
796
|
enforcePermissions={true}
|
|
637
|
-
filterNavigationByPermissions={true}
|
|
638
797
|
routePermissions={{
|
|
639
798
|
'/': 'read',
|
|
640
799
|
'/dashboard': 'read',
|
|
641
800
|
'/settings': 'read',
|
|
642
801
|
}}
|
|
643
802
|
/>
|
|
644
|
-
</TestWrapper
|
|
803
|
+
</TestWrapper>,
|
|
804
|
+
{ withRouter: false }
|
|
645
805
|
);
|
|
646
806
|
|
|
647
807
|
// Wait for the component to render initially
|
|
@@ -657,25 +817,33 @@ describe('PaceAppLayout Component', () => {
|
|
|
657
817
|
}, { timeout: 2000 });
|
|
658
818
|
});
|
|
659
819
|
|
|
660
|
-
it('
|
|
820
|
+
it('always filters navigation items by permissions (filtering cannot be disabled)', async () => {
|
|
661
821
|
const customNavItems = [
|
|
662
822
|
{ id: 'home', label: 'Home', href: '/', icon: 'Home' },
|
|
663
823
|
{ id: 'dashboard', label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
|
|
664
824
|
];
|
|
665
825
|
|
|
826
|
+
// Permission filtering is always enabled - this test verifies that items are filtered based on permissions
|
|
666
827
|
renderWithProviders(
|
|
667
828
|
<TestWrapper>
|
|
668
829
|
<PaceAppLayout
|
|
669
830
|
{...defaultProps}
|
|
670
831
|
navItems={customNavItems}
|
|
671
832
|
enforcePermissions={false}
|
|
672
|
-
|
|
833
|
+
routePermissions={{
|
|
834
|
+
'/': 'read',
|
|
835
|
+
'/dashboard': 'read',
|
|
836
|
+
}}
|
|
673
837
|
/>
|
|
674
|
-
</TestWrapper
|
|
838
|
+
</TestWrapper>,
|
|
839
|
+
{ withRouter: false }
|
|
675
840
|
);
|
|
676
841
|
|
|
677
|
-
|
|
678
|
-
|
|
842
|
+
// Wait for permission checks to complete
|
|
843
|
+
await waitFor(() => {
|
|
844
|
+
expect(screen.getByTestId('nav-item-home')).toBeInTheDocument();
|
|
845
|
+
expect(screen.getByTestId('nav-item-dashboard')).toBeInTheDocument();
|
|
846
|
+
}, { timeout: 2000 });
|
|
679
847
|
});
|
|
680
848
|
});
|
|
681
849
|
|
|
@@ -693,7 +861,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
693
861
|
'/dashboard': 'dashboard-page',
|
|
694
862
|
}}
|
|
695
863
|
/>
|
|
696
|
-
</TestWrapper
|
|
864
|
+
</TestWrapper>,
|
|
865
|
+
{ withRouter: false }
|
|
697
866
|
);
|
|
698
867
|
|
|
699
868
|
await waitFor(() => {
|
|
@@ -719,7 +888,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
719
888
|
'/other': 'read',
|
|
720
889
|
}}
|
|
721
890
|
/>
|
|
722
|
-
</TestWrapper
|
|
891
|
+
</TestWrapper>,
|
|
892
|
+
{ withRouter: false }
|
|
723
893
|
);
|
|
724
894
|
|
|
725
895
|
await waitFor(() => {
|
|
@@ -747,7 +917,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
747
917
|
{...defaultProps}
|
|
748
918
|
enforcePermissions={true}
|
|
749
919
|
/>
|
|
750
|
-
</TestWrapper
|
|
920
|
+
</TestWrapper>,
|
|
921
|
+
{ withRouter: false }
|
|
751
922
|
);
|
|
752
923
|
|
|
753
924
|
await waitFor(() => {
|
|
@@ -776,7 +947,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
776
947
|
enforcePermissions={true}
|
|
777
948
|
onPageAccessDenied={onPageAccessDenied}
|
|
778
949
|
/>
|
|
779
|
-
</TestWrapper
|
|
950
|
+
</TestWrapper>,
|
|
951
|
+
{ withRouter: false }
|
|
780
952
|
);
|
|
781
953
|
|
|
782
954
|
// Assert - Callback should be called immediately when can is false
|
|
@@ -804,7 +976,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
804
976
|
strictMode={true}
|
|
805
977
|
onStrictModeViolation={onStrictModeViolation}
|
|
806
978
|
/>
|
|
807
|
-
</TestWrapper
|
|
979
|
+
</TestWrapper>,
|
|
980
|
+
{ withRouter: false }
|
|
808
981
|
);
|
|
809
982
|
|
|
810
983
|
// Assert - Callback should be called immediately when can is false
|
|
@@ -835,7 +1008,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
835
1008
|
routeConfig={routeConfig}
|
|
836
1009
|
onRouteAccessDenied={onRouteAccessDenied}
|
|
837
1010
|
/>
|
|
838
|
-
</TestWrapper
|
|
1011
|
+
</TestWrapper>,
|
|
1012
|
+
{ withRouter: false }
|
|
839
1013
|
);
|
|
840
1014
|
|
|
841
1015
|
// Should render normally since route is in config
|
|
@@ -864,13 +1038,61 @@ describe('PaceAppLayout Component', () => {
|
|
|
864
1038
|
strictMode={true}
|
|
865
1039
|
onRouteStrictModeViolation={onRouteStrictModeViolation}
|
|
866
1040
|
/>
|
|
867
|
-
</TestWrapper
|
|
1041
|
+
</TestWrapper>,
|
|
1042
|
+
{ withRouter: false }
|
|
868
1043
|
);
|
|
869
1044
|
|
|
870
1045
|
await waitFor(() => {
|
|
871
1046
|
expect(onRouteStrictModeViolation).toHaveBeenCalledWith('/unknown', 'Route not found in configuration');
|
|
872
1047
|
});
|
|
873
1048
|
});
|
|
1049
|
+
|
|
1050
|
+
it('navigates to fallback route when route access is denied', async () => {
|
|
1051
|
+
const onRouteAccessDenied = vi.fn();
|
|
1052
|
+
const onRouteStrictModeViolation = vi.fn();
|
|
1053
|
+
const routeConfig = [
|
|
1054
|
+
{
|
|
1055
|
+
path: '/secure',
|
|
1056
|
+
component: () => <div>Secure</div>,
|
|
1057
|
+
permissions: ['read:page.secure'],
|
|
1058
|
+
pageId: 'secure',
|
|
1059
|
+
},
|
|
1060
|
+
];
|
|
1061
|
+
|
|
1062
|
+
mockLocation.pathname = '/secure';
|
|
1063
|
+
mockNavigate.mockClear();
|
|
1064
|
+
|
|
1065
|
+
const { isPermittedCached } = await import('../../rbac/api');
|
|
1066
|
+
vi.mocked(isPermittedCached).mockResolvedValueOnce(false);
|
|
1067
|
+
|
|
1068
|
+
const FallbackWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
1069
|
+
<MemoryRouter initialEntries={['/secure']}>
|
|
1070
|
+
{children}
|
|
1071
|
+
</MemoryRouter>
|
|
1072
|
+
);
|
|
1073
|
+
|
|
1074
|
+
renderWithProviders(
|
|
1075
|
+
<FallbackWrapper>
|
|
1076
|
+
<PaceAppLayout
|
|
1077
|
+
{...defaultProps}
|
|
1078
|
+
roleBasedRouting={true}
|
|
1079
|
+
strictMode={true}
|
|
1080
|
+
fallbackRoute="/no-access"
|
|
1081
|
+
routeConfig={routeConfig}
|
|
1082
|
+
onRouteAccessDenied={onRouteAccessDenied}
|
|
1083
|
+
onRouteStrictModeViolation={onRouteStrictModeViolation}
|
|
1084
|
+
/>
|
|
1085
|
+
</FallbackWrapper>,
|
|
1086
|
+
{ withRouter: false }
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
await waitFor(() => {
|
|
1090
|
+
expect(mockNavigate).toHaveBeenCalledWith('/no-access', { replace: true });
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
expect(onRouteAccessDenied).toHaveBeenCalledWith('/secure', 'Insufficient permissions');
|
|
1094
|
+
expect(onRouteStrictModeViolation).toHaveBeenCalledWith('/secure', 'Insufficient permissions');
|
|
1095
|
+
});
|
|
874
1096
|
});
|
|
875
1097
|
|
|
876
1098
|
describe('Error Handling', () => {
|
|
@@ -882,11 +1104,11 @@ describe('PaceAppLayout Component', () => {
|
|
|
882
1104
|
};
|
|
883
1105
|
|
|
884
1106
|
// Mock the useUnifiedAuth hook to return null user
|
|
885
|
-
|
|
1107
|
+
mockUseUnifiedAuthFn.mockReturnValue(mockAuthWithoutUser);
|
|
886
1108
|
|
|
887
|
-
// When there's no user,
|
|
888
|
-
// and will return false (no permission)
|
|
889
|
-
mockUseCan.
|
|
1109
|
+
// When there's no user, isSuperAdmin won't be called (it checks user?.id first)
|
|
1110
|
+
// and useCan is called with empty string userId, which will return false (no permission)
|
|
1111
|
+
mockUseCan.mockReturnValue({
|
|
890
1112
|
can: false,
|
|
891
1113
|
isLoading: false,
|
|
892
1114
|
error: null,
|
|
@@ -899,17 +1121,20 @@ describe('PaceAppLayout Component', () => {
|
|
|
899
1121
|
{...defaultProps}
|
|
900
1122
|
enforcePermissions={true}
|
|
901
1123
|
/>
|
|
902
|
-
</TestWrapper
|
|
1124
|
+
</TestWrapper>,
|
|
1125
|
+
{ withRouter: false }
|
|
903
1126
|
);
|
|
904
1127
|
|
|
1128
|
+
// When user is null, isSuperAdmin won't be called (early return in useEffect)
|
|
1129
|
+
// So we should see "Access Denied" immediately after useCan returns false
|
|
905
1130
|
await waitFor(() => {
|
|
906
1131
|
expect(screen.getByText('Access Denied')).toBeInTheDocument();
|
|
907
1132
|
}, { timeout: 3000 });
|
|
908
1133
|
});
|
|
909
1134
|
|
|
910
1135
|
it('handles missing organisation context', async () => {
|
|
911
|
-
// Ensure super admin check completes first
|
|
912
|
-
mockIsSuperAdmin.
|
|
1136
|
+
// Ensure super admin check completes first - resolve immediately
|
|
1137
|
+
mockIsSuperAdmin.mockResolvedValue(false);
|
|
913
1138
|
|
|
914
1139
|
// Arrange
|
|
915
1140
|
const mockUserWithoutOrg = {
|
|
@@ -923,7 +1148,7 @@ describe('PaceAppLayout Component', () => {
|
|
|
923
1148
|
user: mockUserWithoutOrg,
|
|
924
1149
|
};
|
|
925
1150
|
|
|
926
|
-
|
|
1151
|
+
mockUseUnifiedAuthFn.mockReturnValue(mockAuthWithoutOrg);
|
|
927
1152
|
mockUseCan.mockReturnValue({
|
|
928
1153
|
can: false,
|
|
929
1154
|
isLoading: false,
|
|
@@ -938,57 +1163,117 @@ describe('PaceAppLayout Component', () => {
|
|
|
938
1163
|
{...defaultProps}
|
|
939
1164
|
enforcePermissions={true}
|
|
940
1165
|
/>
|
|
941
|
-
</TestWrapper
|
|
1166
|
+
</TestWrapper>,
|
|
1167
|
+
{ withRouter: false }
|
|
942
1168
|
);
|
|
943
1169
|
|
|
944
1170
|
// Wait for super admin check to complete and component to re-render
|
|
945
1171
|
await waitFor(() => {
|
|
946
1172
|
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
947
|
-
}, { timeout:
|
|
948
|
-
|
|
949
|
-
// Wait a bit for the component to process the super admin check result
|
|
950
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1173
|
+
}, { timeout: 2000 });
|
|
951
1174
|
|
|
952
|
-
//
|
|
1175
|
+
// Wait for the component to process the super admin check result and render access denied
|
|
953
1176
|
await waitFor(() => {
|
|
954
1177
|
expect(screen.getByRole('heading', { name: 'Access Denied' })).toBeInTheDocument();
|
|
955
|
-
});
|
|
1178
|
+
}, { timeout: 3000 });
|
|
956
1179
|
});
|
|
957
1180
|
});
|
|
958
1181
|
|
|
959
1182
|
describe('Accessibility', () => {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
<
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1183
|
+
describe('Semantic HTML Structure', () => {
|
|
1184
|
+
it('renders with proper semantic HTML elements', () => {
|
|
1185
|
+
renderWithProviders(
|
|
1186
|
+
<TestWrapper>
|
|
1187
|
+
<PaceAppLayout {...defaultProps} />
|
|
1188
|
+
</TestWrapper>,
|
|
1189
|
+
{ withRouter: false }
|
|
1190
|
+
);
|
|
1191
|
+
|
|
1192
|
+
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
1193
|
+
expect(screen.getByRole('main')).toBeInTheDocument();
|
|
1194
|
+
expect(screen.getByTestId('footer')).toBeInTheDocument();
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
it('provides proper main content area', () => {
|
|
1198
|
+
renderWithProviders(
|
|
1199
|
+
<TestWrapper>
|
|
1200
|
+
<PaceAppLayout {...defaultProps} />
|
|
1201
|
+
</TestWrapper>,
|
|
1202
|
+
{ withRouter: false }
|
|
1203
|
+
);
|
|
1204
|
+
|
|
1205
|
+
const main = screen.getByRole('main');
|
|
1206
|
+
expect(main).toBeInTheDocument();
|
|
1207
|
+
expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
it('renders main content area with proper styling', () => {
|
|
1211
|
+
renderWithProviders(
|
|
1212
|
+
<TestWrapper>
|
|
1213
|
+
<PaceAppLayout {...defaultProps} />
|
|
1214
|
+
</TestWrapper>,
|
|
1215
|
+
{ withRouter: false }
|
|
1216
|
+
);
|
|
1217
|
+
|
|
1218
|
+
const main = screen.getByTestId('outlet').parentElement;
|
|
1219
|
+
expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
|
|
1220
|
+
});
|
|
970
1221
|
});
|
|
971
1222
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
<
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1223
|
+
describe('ARIA Attributes and Labels', () => {
|
|
1224
|
+
it('has accessible navigation', () => {
|
|
1225
|
+
renderWithProviders(
|
|
1226
|
+
<TestWrapper>
|
|
1227
|
+
<PaceAppLayout {...defaultProps} />
|
|
1228
|
+
</TestWrapper>,
|
|
1229
|
+
{ withRouter: false }
|
|
1230
|
+
);
|
|
1231
|
+
|
|
1232
|
+
expect(screen.getByTestId('header-nav')).toBeInTheDocument();
|
|
1233
|
+
expect(screen.getByTestId('nav-item-home')).toBeInTheDocument();
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
it('has accessible user menu', () => {
|
|
1237
|
+
renderWithProviders(
|
|
1238
|
+
<TestWrapper>
|
|
1239
|
+
<PaceAppLayout {...defaultProps} />
|
|
1240
|
+
</TestWrapper>,
|
|
1241
|
+
{ withRouter: false }
|
|
1242
|
+
);
|
|
1243
|
+
|
|
1244
|
+
expect(screen.getByTestId('header-user-menu')).toBeInTheDocument();
|
|
1245
|
+
expect(screen.getByTestId('user-email')).toHaveTextContent('test@example.com');
|
|
1246
|
+
});
|
|
981
1247
|
});
|
|
982
1248
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
<
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1249
|
+
describe('Screen Reader Support', () => {
|
|
1250
|
+
it('provides meaningful text for screen readers', () => {
|
|
1251
|
+
renderWithProviders(
|
|
1252
|
+
<TestWrapper>
|
|
1253
|
+
<PaceAppLayout {...defaultProps} />
|
|
1254
|
+
</TestWrapper>,
|
|
1255
|
+
{ withRouter: false }
|
|
1256
|
+
);
|
|
1257
|
+
|
|
1258
|
+
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
1259
|
+
expect(screen.getByTestId('user-email')).toHaveTextContent('test@example.com');
|
|
1260
|
+
});
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
describe('Keyboard Navigation', () => {
|
|
1264
|
+
it('provides proper focus management', async () => {
|
|
1265
|
+
const user = userEvent.setup();
|
|
1266
|
+
renderWithProviders(
|
|
1267
|
+
<TestWrapper>
|
|
1268
|
+
<PaceAppLayout {...defaultProps} />
|
|
1269
|
+
</TestWrapper>,
|
|
1270
|
+
{ withRouter: false }
|
|
1271
|
+
);
|
|
1272
|
+
|
|
1273
|
+
const signOutButton = screen.getByTestId('sign-out-btn');
|
|
1274
|
+
await user.tab();
|
|
1275
|
+
expect(signOutButton).toBeInTheDocument();
|
|
1276
|
+
});
|
|
992
1277
|
});
|
|
993
1278
|
});
|
|
994
1279
|
|
|
@@ -997,7 +1282,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
997
1282
|
renderWithProviders(
|
|
998
1283
|
<TestWrapper>
|
|
999
1284
|
<PaceAppLayout {...defaultProps} navItems={[]} />
|
|
1000
|
-
</TestWrapper
|
|
1285
|
+
</TestWrapper>,
|
|
1286
|
+
{ withRouter: false }
|
|
1001
1287
|
);
|
|
1002
1288
|
|
|
1003
1289
|
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
@@ -1015,10 +1301,10 @@ describe('PaceAppLayout Component', () => {
|
|
|
1015
1301
|
<PaceAppLayout
|
|
1016
1302
|
{...defaultProps}
|
|
1017
1303
|
navItems={navItemsWithoutHref}
|
|
1018
|
-
filterNavigationByPermissions={false}
|
|
1019
1304
|
enforcePermissions={false}
|
|
1020
1305
|
/>
|
|
1021
|
-
</TestWrapper
|
|
1306
|
+
</TestWrapper>,
|
|
1307
|
+
{ withRouter: false }
|
|
1022
1308
|
);
|
|
1023
1309
|
|
|
1024
1310
|
expect(screen.getByTestId('nav-item-home')).toBeInTheDocument();
|
|
@@ -1031,7 +1317,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
1031
1317
|
const { rerender } = renderWithProviders(
|
|
1032
1318
|
<TestWrapper>
|
|
1033
1319
|
<PaceAppLayout {...defaultProps} enforcePermissions={false} />
|
|
1034
|
-
</TestWrapper
|
|
1320
|
+
</TestWrapper>,
|
|
1321
|
+
{ withRouter: false }
|
|
1035
1322
|
);
|
|
1036
1323
|
|
|
1037
1324
|
// Wait for initial render
|
|
@@ -1065,7 +1352,8 @@ describe('PaceAppLayout Component', () => {
|
|
|
1065
1352
|
const { rerender } = renderWithProviders(
|
|
1066
1353
|
<TestWrapper>
|
|
1067
1354
|
<PaceAppLayout {...stableProps} />
|
|
1068
|
-
</TestWrapper
|
|
1355
|
+
</TestWrapper>,
|
|
1356
|
+
{ withRouter: false }
|
|
1069
1357
|
);
|
|
1070
1358
|
|
|
1071
1359
|
const initialHeader = screen.getByTestId('header');
|