@jmruthers/pace-core 0.5.180 → 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-4VSEJQ7D.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-6HLVGAZG.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-CPTUD43C.js → chunk-MI7HBHN3.js} +164 -243
- package/dist/chunk-MI7HBHN3.js.map +1 -0
- package/dist/{chunk-N5YCCUG5.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-P6UUHOIF.js → chunk-QETLRQI6.js} +526 -887
- package/dist/chunk-QETLRQI6.js.map +1 -0
- package/dist/{chunk-GQA2LIAE.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-ONI7Y733.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-R53TUSFK.js → chunk-W22JP75J.js} +5 -13
- package/dist/{chunk-R53TUSFK.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/examples/{components 2/DataTable/HierarchicalExample.tsx → 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 +57 -31
- 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-BMJAP6Z7.js +0 -16
- package/dist/auth-DReDSLq9.d.ts +0 -16
- package/dist/chunk-56XJ3TU6.js +0 -11
- package/dist/chunk-56XJ3TU6.js.map +0 -1
- package/dist/chunk-6HLVGAZG.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-CPTUD43C.js.map +0 -1
- package/dist/chunk-CX5M4ZAG.js.map +0 -1
- package/dist/chunk-ERGKJX4D.js +0 -15
- package/dist/chunk-ERGKJX4D.js.map +0 -1
- package/dist/chunk-ERISIBYU.js.map +0 -1
- package/dist/chunk-GQA2LIAE.js.map +0 -1
- package/dist/chunk-JISYG63F.js +0 -70
- package/dist/chunk-JISYG63F.js.map +0 -1
- package/dist/chunk-MSHEVJXS.js +0 -27
- package/dist/chunk-MSHEVJXS.js.map +0 -1
- package/dist/chunk-N5YCCUG5.js.map +0 -1
- package/dist/chunk-ONI7Y733.js.map +0 -1
- package/dist/chunk-OWAG3GSU.js.map +0 -1
- package/dist/chunk-P6UUHOIF.js.map +0 -1
- package/dist/chunk-PKW27QVS.js +0 -61
- package/dist/chunk-PKW27QVS.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-WM26XK7I.js +0 -2432
- package/dist/chunk-WM26XK7I.js.map +0 -1
- package/dist/chunk-XDNLUEXI.js +0 -138
- package/dist/chunk-XJ2HZOBU.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/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/HierarchicalExample.tsx +0 -475
- 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-4VSEJQ7D.js.map → DataTable-QAB34V6K.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-BMJAP6Z7.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
|
@@ -1,1078 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
-
import { describe, it, expect, vi, beforeEach, beforeAll } from 'vitest';
|
|
4
|
-
import '@testing-library/jest-dom';
|
|
5
|
-
import { BrowserRouter } from 'react-router-dom';
|
|
6
|
-
|
|
7
|
-
// Mock React Router hooks
|
|
8
|
-
const mockNavigate = vi.fn();
|
|
9
|
-
const mockLocation = { pathname: '/test-path' };
|
|
10
|
-
vi.mock('react-router-dom', async () => {
|
|
11
|
-
const actual = await vi.importActual('react-router-dom');
|
|
12
|
-
return {
|
|
13
|
-
...actual,
|
|
14
|
-
useNavigate: () => mockNavigate,
|
|
15
|
-
useLocation: () => mockLocation,
|
|
16
|
-
Outlet: () => <div data-testid="mock-outlet">Mock Outlet Content</div>
|
|
17
|
-
};
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
// Mock UnifiedAuthProvider
|
|
21
|
-
const mockSignOut = vi.fn().mockResolvedValue({ error: null });
|
|
22
|
-
const mockUpdatePassword = vi.fn().mockResolvedValue({ error: null });
|
|
23
|
-
const mockUser = {
|
|
24
|
-
id: 'test-user-id',
|
|
25
|
-
email: 'test@example.com',
|
|
26
|
-
user_metadata: {
|
|
27
|
-
display_name: 'Test User',
|
|
28
|
-
organisationId: 'test-org-123',
|
|
29
|
-
eventId: 'test-event-456',
|
|
30
|
-
appId: 'test-app-789'
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
vi.mock('../../../providers/UnifiedAuthProvider', () => ({
|
|
35
|
-
useUnifiedAuth: () => ({
|
|
36
|
-
user: mockUser,
|
|
37
|
-
signOut: mockSignOut,
|
|
38
|
-
updatePassword: mockUpdatePassword
|
|
39
|
-
})
|
|
40
|
-
}));
|
|
41
|
-
|
|
42
|
-
// Mock OrganisationProvider
|
|
43
|
-
const mockOrganisation = {
|
|
44
|
-
id: 'test-org-id',
|
|
45
|
-
name: 'Test Organisation',
|
|
46
|
-
display_name: 'Test Organisation',
|
|
47
|
-
description: 'Test organisation for testing',
|
|
48
|
-
subscription_tier: 'basic',
|
|
49
|
-
settings: {},
|
|
50
|
-
is_active: true,
|
|
51
|
-
created_at: '2023-01-01T00:00:00Z',
|
|
52
|
-
updated_at: '2023-01-01T00:00:00Z'
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const mockOrganisationContext = {
|
|
56
|
-
selectedOrganisation: mockOrganisation,
|
|
57
|
-
organisations: [mockOrganisation],
|
|
58
|
-
userMemberships: [{
|
|
59
|
-
id: 'test-membership-id',
|
|
60
|
-
user_id: 'test-user-id',
|
|
61
|
-
organisation_id: 'test-org-id',
|
|
62
|
-
role: 'org_admin',
|
|
63
|
-
granted_at: '2023-01-01T00:00:00Z',
|
|
64
|
-
status: 'active' as const,
|
|
65
|
-
created_at: '2023-01-01T00:00:00Z',
|
|
66
|
-
updated_at: '2023-01-01T00:00:00Z'
|
|
67
|
-
}],
|
|
68
|
-
isLoading: false,
|
|
69
|
-
error: null,
|
|
70
|
-
hasValidOrganisationContext: true,
|
|
71
|
-
setSelectedOrganisation: vi.fn(),
|
|
72
|
-
switchOrganisation: vi.fn().mockResolvedValue(undefined),
|
|
73
|
-
getUserRole: vi.fn().mockReturnValue('member'),
|
|
74
|
-
validateOrganisationAccess: vi.fn().mockReturnValue(true),
|
|
75
|
-
ensureOrganisationContext: vi.fn().mockReturnValue(mockOrganisation),
|
|
76
|
-
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
77
|
-
getPrimaryOrganisation: vi.fn().mockReturnValue(mockOrganisation),
|
|
78
|
-
isOrganisationSecure: vi.fn().mockReturnValue(true)
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
vi.mock('../../../hooks/useOrganisations', () => ({
|
|
82
|
-
useOrganisations: () => mockOrganisationContext
|
|
83
|
-
}));
|
|
84
|
-
|
|
85
|
-
// Mock useEvents hook (used by useEventTheme)
|
|
86
|
-
vi.mock('../../../hooks/useEvents', () => ({
|
|
87
|
-
useEvents: vi.fn(() => ({
|
|
88
|
-
selectedEvent: { event_id: 'event-123' },
|
|
89
|
-
events: [],
|
|
90
|
-
isLoading: false,
|
|
91
|
-
error: null,
|
|
92
|
-
})),
|
|
93
|
-
}));
|
|
94
|
-
|
|
95
|
-
// Mock useEventTheme to avoid EventServiceProvider requirement
|
|
96
|
-
vi.mock('../../../hooks/useEventTheme', () => ({
|
|
97
|
-
useEventTheme: vi.fn(),
|
|
98
|
-
}));
|
|
99
|
-
|
|
100
|
-
// Mock the new RBAC system
|
|
101
|
-
const mockIsPermitted = vi.fn().mockImplementation((input) => {
|
|
102
|
-
return Promise.resolve(true);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const mockIsSuperAdmin = vi.fn().mockResolvedValue(false);
|
|
106
|
-
|
|
107
|
-
vi.mock('../../../rbac/api', () => ({
|
|
108
|
-
isPermitted: mockIsPermitted,
|
|
109
|
-
getPermissionMap: vi.fn().mockResolvedValue({}),
|
|
110
|
-
getAccessLevel: vi.fn().mockResolvedValue('viewer'),
|
|
111
|
-
isSuperAdmin: (...args: any[]) => mockIsSuperAdmin(...args),
|
|
112
|
-
setupRBAC: vi.fn()
|
|
113
|
-
}));
|
|
114
|
-
|
|
115
|
-
// Mock RBAC hooks
|
|
116
|
-
const mockHasPermissionRBAC = vi.fn().mockResolvedValue(true);
|
|
117
|
-
const mockUseRBAC = vi.fn(() => ({
|
|
118
|
-
hasPermission: mockHasPermissionRBAC,
|
|
119
|
-
isLoading: false,
|
|
120
|
-
error: null,
|
|
121
|
-
hasGlobalPermission: vi.fn().mockResolvedValue(true),
|
|
122
|
-
hasOrganisationPermission: vi.fn().mockResolvedValue(true),
|
|
123
|
-
hasEventPermission: vi.fn().mockResolvedValue(true),
|
|
124
|
-
globalRole: null,
|
|
125
|
-
organisationRoles: [],
|
|
126
|
-
eventRoles: [],
|
|
127
|
-
permissionMap: {},
|
|
128
|
-
}));
|
|
129
|
-
|
|
130
|
-
const mockUseCan = vi.fn(() => ({
|
|
131
|
-
can: true,
|
|
132
|
-
isLoading: false,
|
|
133
|
-
error: null,
|
|
134
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
135
|
-
}));
|
|
136
|
-
|
|
137
|
-
vi.mock('../../../rbac/hooks', () => ({
|
|
138
|
-
useRBAC: () => mockUseRBAC(),
|
|
139
|
-
useCan: (...args: any[]) => mockUseCan(...args),
|
|
140
|
-
useResolvedScope: vi.fn(() => ({
|
|
141
|
-
resolvedScope: { organisationId: 'org-123', eventId: 'event-123', appId: 'app-123' },
|
|
142
|
-
isLoading: false,
|
|
143
|
-
error: null,
|
|
144
|
-
})),
|
|
145
|
-
}));
|
|
146
|
-
|
|
147
|
-
// Mock Footer (static, doesn't depend on props)
|
|
148
|
-
vi.mock('../../Footer', () => ({
|
|
149
|
-
Footer: vi.fn(() => <footer data-testid="mock-footer" role="contentinfo">Mock Footer</footer>)
|
|
150
|
-
}));
|
|
151
|
-
|
|
152
|
-
// Mock window.location
|
|
153
|
-
Object.defineProperty(window, 'location', {
|
|
154
|
-
value: {
|
|
155
|
-
pathname: '/test-path'
|
|
156
|
-
},
|
|
157
|
-
writable: true
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Wrapper component to provide Router context
|
|
161
|
-
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
162
|
-
<BrowserRouter>
|
|
163
|
-
{children}
|
|
164
|
-
</BrowserRouter>
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
describe('PaceAppLayout Component', () => {
|
|
168
|
-
let PaceAppLayout: any;
|
|
169
|
-
|
|
170
|
-
beforeEach(() => {
|
|
171
|
-
// Reset mocks before each test
|
|
172
|
-
mockUseCan.mockClear();
|
|
173
|
-
mockIsSuperAdmin.mockClear();
|
|
174
|
-
mockIsSuperAdmin.mockResolvedValue(false);
|
|
175
|
-
// Default to allowing access unless test overrides
|
|
176
|
-
mockUseCan.mockReturnValue({
|
|
177
|
-
can: true,
|
|
178
|
-
isLoading: false,
|
|
179
|
-
error: null,
|
|
180
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
beforeAll(async () => {
|
|
185
|
-
// Set up mocks before importing the component
|
|
186
|
-
vi.doMock('../../Header', () => ({
|
|
187
|
-
Header: vi.fn(({
|
|
188
|
-
logoAlt,
|
|
189
|
-
user,
|
|
190
|
-
onSignOut,
|
|
191
|
-
onChangePassword,
|
|
192
|
-
onNavigate,
|
|
193
|
-
currentPath,
|
|
194
|
-
showEventSelector,
|
|
195
|
-
showUserMenu,
|
|
196
|
-
className,
|
|
197
|
-
navItems,
|
|
198
|
-
actions,
|
|
199
|
-
userMenu,
|
|
200
|
-
logo,
|
|
201
|
-
logoUrl
|
|
202
|
-
}) => (
|
|
203
|
-
<header data-testid="mock-header" role="banner" className={className}>
|
|
204
|
-
<div data-testid="app-name" aria-label="Application name">{logoAlt}</div>
|
|
205
|
-
<div data-testid="user-info">{user?.user_metadata?.display_name || user?.email}</div>
|
|
206
|
-
<div data-testid="nav-items-count">{navItems?.length || 0}</div>
|
|
207
|
-
<div data-testid="has-actions">{actions ? 'true' : 'false'}</div>
|
|
208
|
-
<div data-testid="has-custom-user-menu">{userMenu ? 'true' : 'false'}</div>
|
|
209
|
-
<div data-testid="has-custom-logo">{logo ? 'true' : 'false'}</div>
|
|
210
|
-
<div data-testid="logo-url">{logoUrl || 'default'}</div>
|
|
211
|
-
<div data-testid="show-user-menu">{showUserMenu !== false ? 'true' : 'false'}</div>
|
|
212
|
-
{logo && <div data-testid="custom-logo">{logo}</div>}
|
|
213
|
-
{userMenu && <div data-testid="custom-user-menu">{userMenu}</div>}
|
|
214
|
-
{actions && <div data-testid="header-actions">{actions}</div>}
|
|
215
|
-
<button
|
|
216
|
-
data-testid="sign-out-button"
|
|
217
|
-
onClick={() => onSignOut()}
|
|
218
|
-
>
|
|
219
|
-
Sign Out
|
|
220
|
-
</button>
|
|
221
|
-
<button
|
|
222
|
-
data-testid="change-password-button"
|
|
223
|
-
onClick={() => onChangePassword('newpassword123')}
|
|
224
|
-
>
|
|
225
|
-
Change Password
|
|
226
|
-
</button>
|
|
227
|
-
<button
|
|
228
|
-
data-testid="navigate-button"
|
|
229
|
-
onClick={() => onNavigate({ id: 'home', label: 'Home', href: '/' })}
|
|
230
|
-
>
|
|
231
|
-
Navigate
|
|
232
|
-
</button>
|
|
233
|
-
<div data-testid="current-path">{currentPath}</div>
|
|
234
|
-
<div data-testid="show-event-selector">{showEventSelector !== false ? 'true' : 'false'}</div>
|
|
235
|
-
</header>
|
|
236
|
-
))
|
|
237
|
-
}));
|
|
238
|
-
|
|
239
|
-
// Import after mocking
|
|
240
|
-
PaceAppLayout = (await import('../PaceAppLayout')).PaceAppLayout;
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
beforeEach(() => {
|
|
244
|
-
vi.clearAllMocks();
|
|
245
|
-
mockNavigate.mockClear();
|
|
246
|
-
mockIsPermitted.mockClear();
|
|
247
|
-
mockIsPermitted.mockResolvedValue(true);
|
|
248
|
-
// Explicitly re-mock updatePassword to always return { error: null }
|
|
249
|
-
mockUpdatePassword.mockResolvedValue({ error: null });
|
|
250
|
-
|
|
251
|
-
// Reset RBAC mocks to default state
|
|
252
|
-
mockHasPermissionRBAC.mockResolvedValue(true);
|
|
253
|
-
mockUseCan.mockReturnValue({
|
|
254
|
-
can: true,
|
|
255
|
-
isLoading: false,
|
|
256
|
-
error: null,
|
|
257
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
258
|
-
});
|
|
259
|
-
mockUseRBAC.mockReturnValue({
|
|
260
|
-
hasPermission: mockHasPermissionRBAC,
|
|
261
|
-
isLoading: false,
|
|
262
|
-
error: null,
|
|
263
|
-
hasGlobalPermission: vi.fn().mockResolvedValue(true),
|
|
264
|
-
hasOrganisationPermission: vi.fn().mockResolvedValue(true),
|
|
265
|
-
hasEventPermission: vi.fn().mockResolvedValue(true),
|
|
266
|
-
globalRole: null,
|
|
267
|
-
organisationRoles: [],
|
|
268
|
-
eventRoles: [],
|
|
269
|
-
permissionMap: {},
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// Reset location mock
|
|
273
|
-
Object.defineProperty(window, 'location', {
|
|
274
|
-
value: { pathname: '/test-path' },
|
|
275
|
-
writable: true
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe('Basic Rendering', () => {
|
|
280
|
-
it('renders without crashing', () => {
|
|
281
|
-
render(
|
|
282
|
-
<TestWrapper>
|
|
283
|
-
<PaceAppLayout appName="Test App" />
|
|
284
|
-
</TestWrapper>
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
288
|
-
expect(screen.getByTestId('mock-footer')).toBeInTheDocument();
|
|
289
|
-
expect(screen.getByTestId('mock-outlet')).toBeInTheDocument();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it('displays the correct app name in header', () => {
|
|
293
|
-
render(
|
|
294
|
-
<TestWrapper>
|
|
295
|
-
<PaceAppLayout appName="My Test Application" />
|
|
296
|
-
</TestWrapper>
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
expect(screen.getByTestId('app-name')).toHaveTextContent('My Test Application Logo');
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it('displays user information in header', () => {
|
|
303
|
-
render(
|
|
304
|
-
<TestWrapper>
|
|
305
|
-
<PaceAppLayout appName="Test App" />
|
|
306
|
-
</TestWrapper>
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
expect(screen.getByTestId('user-info')).toHaveTextContent('Test User');
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('renders with proper layout structure', () => {
|
|
313
|
-
render(
|
|
314
|
-
<TestWrapper>
|
|
315
|
-
<PaceAppLayout appName="Test App" />
|
|
316
|
-
</TestWrapper>
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
expect(screen.getByRole('banner')).toBeInTheDocument(); // header
|
|
320
|
-
expect(screen.getByRole('main')).toBeInTheDocument(); // main
|
|
321
|
-
expect(screen.getByRole('contentinfo')).toBeInTheDocument(); // footer
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it('renders main content area with proper styling', () => {
|
|
325
|
-
render(
|
|
326
|
-
<TestWrapper>
|
|
327
|
-
<PaceAppLayout appName="Test App" />
|
|
328
|
-
</TestWrapper>
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
const main = screen.getByTestId('mock-outlet').parentElement;
|
|
332
|
-
expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
describe('Navigation', () => {
|
|
337
|
-
it('passes current path to header', () => {
|
|
338
|
-
render(
|
|
339
|
-
<TestWrapper>
|
|
340
|
-
<PaceAppLayout appName="Test App" />
|
|
341
|
-
</TestWrapper>
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
expect(screen.getByTestId('current-path')).toHaveTextContent('/test-path');
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it('handles navigation when header triggers onNavigate', () => {
|
|
348
|
-
render(
|
|
349
|
-
<TestWrapper>
|
|
350
|
-
<PaceAppLayout appName="Test App" />
|
|
351
|
-
</TestWrapper>
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
fireEvent.click(screen.getByTestId('navigate-button'));
|
|
355
|
-
|
|
356
|
-
expect(mockNavigate).toHaveBeenCalledWith('/');
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
describe('Authentication', () => {
|
|
361
|
-
it('handles sign out when triggered from header', async () => {
|
|
362
|
-
render(
|
|
363
|
-
<TestWrapper>
|
|
364
|
-
<PaceAppLayout appName="Test App" />
|
|
365
|
-
</TestWrapper>
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
fireEvent.click(screen.getByTestId('sign-out-button'));
|
|
369
|
-
|
|
370
|
-
await waitFor(() => {
|
|
371
|
-
expect(mockSignOut).toHaveBeenCalledTimes(1);
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
it('handles password change when triggered from header', async () => {
|
|
376
|
-
render(
|
|
377
|
-
<TestWrapper>
|
|
378
|
-
<PaceAppLayout appName="Test App" />
|
|
379
|
-
</TestWrapper>
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
fireEvent.click(screen.getByTestId('change-password-button'));
|
|
383
|
-
|
|
384
|
-
await waitFor(() => {
|
|
385
|
-
expect(mockUpdatePassword).toHaveBeenCalledWith('newpassword123');
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
it('handles password change error gracefully', async () => {
|
|
390
|
-
const mockError = { message: 'Password change failed' };
|
|
391
|
-
mockUpdatePassword.mockResolvedValue({ error: mockError });
|
|
392
|
-
|
|
393
|
-
render(
|
|
394
|
-
<TestWrapper>
|
|
395
|
-
<PaceAppLayout appName="Test App" />
|
|
396
|
-
</TestWrapper>
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
fireEvent.click(screen.getByTestId('change-password-button'));
|
|
400
|
-
|
|
401
|
-
await waitFor(() => {
|
|
402
|
-
expect(mockUpdatePassword).toHaveBeenCalledWith('newpassword123');
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
describe('Props and Configuration', () => {
|
|
408
|
-
it('accepts and passes appName prop correctly', () => {
|
|
409
|
-
render(
|
|
410
|
-
<TestWrapper>
|
|
411
|
-
<PaceAppLayout appName="Custom App Name" />
|
|
412
|
-
</TestWrapper>
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
expect(screen.getByTestId('app-name')).toHaveTextContent('Custom App Name Logo');
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it('shows event selector by default', () => {
|
|
419
|
-
render(
|
|
420
|
-
<TestWrapper>
|
|
421
|
-
<PaceAppLayout appName="Test App" />
|
|
422
|
-
</TestWrapper>
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
expect(screen.getByTestId('show-event-selector')).toHaveTextContent('true');
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('hides event selector when showEventSelector is false', () => {
|
|
429
|
-
render(
|
|
430
|
-
<TestWrapper>
|
|
431
|
-
<PaceAppLayout appName="Test App" showEventSelector={false} />
|
|
432
|
-
</TestWrapper>
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
expect(screen.getByTestId('show-event-selector')).toHaveTextContent('false');
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
it('shows event selector when showEventSelector is explicitly true', () => {
|
|
439
|
-
render(
|
|
440
|
-
<TestWrapper>
|
|
441
|
-
<PaceAppLayout appName="Test App" showEventSelector={true} />
|
|
442
|
-
</TestWrapper>
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
expect(screen.getByTestId('show-event-selector')).toHaveTextContent('true');
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('shows user menu by default', () => {
|
|
449
|
-
render(
|
|
450
|
-
<TestWrapper>
|
|
451
|
-
<PaceAppLayout appName="Test App" />
|
|
452
|
-
</TestWrapper>
|
|
453
|
-
);
|
|
454
|
-
|
|
455
|
-
expect(screen.getByTestId('show-user-menu')).toHaveTextContent('true');
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
it('hides user menu when showUserMenu is false', () => {
|
|
459
|
-
render(
|
|
460
|
-
<TestWrapper>
|
|
461
|
-
<PaceAppLayout appName="Test App" showUserMenu={false} />
|
|
462
|
-
</TestWrapper>
|
|
463
|
-
);
|
|
464
|
-
|
|
465
|
-
expect(screen.getByTestId('show-user-menu')).toHaveTextContent('false');
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it('passes custom header className', () => {
|
|
469
|
-
render(
|
|
470
|
-
<TestWrapper>
|
|
471
|
-
<PaceAppLayout appName="Test App" headerClassName="custom-header-class" />
|
|
472
|
-
</TestWrapper>
|
|
473
|
-
);
|
|
474
|
-
|
|
475
|
-
expect(screen.getByTestId('mock-header')).toHaveClass('custom-header-class');
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
it('uses default header className when not provided', () => {
|
|
479
|
-
render(
|
|
480
|
-
<TestWrapper>
|
|
481
|
-
<PaceAppLayout appName="Test App" />
|
|
482
|
-
</TestWrapper>
|
|
483
|
-
);
|
|
484
|
-
|
|
485
|
-
expect(screen.getByTestId('mock-header')).toHaveClass('sticky', 'top-0', 'z-[40]', 'w-full');
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
describe('Custom Components', () => {
|
|
490
|
-
it('accepts custom logo component', () => {
|
|
491
|
-
const CustomLogo = () => <div data-testid="custom-logo">Custom Logo</div>;
|
|
492
|
-
|
|
493
|
-
render(
|
|
494
|
-
<TestWrapper>
|
|
495
|
-
<PaceAppLayout appName="Test App" customLogo={<CustomLogo />} />
|
|
496
|
-
</TestWrapper>
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
expect(screen.getAllByTestId('custom-logo')).toHaveLength(2); // One from mock, one from component
|
|
500
|
-
expect(screen.getByTestId('has-custom-logo')).toHaveTextContent('true');
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('accepts custom user menu component', () => {
|
|
504
|
-
const CustomUserMenu = () => <div data-testid="custom-user-menu">Custom Menu</div>;
|
|
505
|
-
|
|
506
|
-
render(
|
|
507
|
-
<TestWrapper>
|
|
508
|
-
<PaceAppLayout appName="Test App" customUserMenu={<CustomUserMenu />} />
|
|
509
|
-
</TestWrapper>
|
|
510
|
-
);
|
|
511
|
-
|
|
512
|
-
expect(screen.getAllByTestId('custom-user-menu')).toHaveLength(2); // One from mock, one from component
|
|
513
|
-
expect(screen.getByTestId('has-custom-user-menu')).toHaveTextContent('true');
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
it('accepts header actions', () => {
|
|
517
|
-
const HeaderActions = () => <div data-testid="header-actions">Actions</div>;
|
|
518
|
-
|
|
519
|
-
render(
|
|
520
|
-
<TestWrapper>
|
|
521
|
-
<PaceAppLayout appName="Test App" headerActions={<HeaderActions />} />
|
|
522
|
-
</TestWrapper>
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
expect(screen.getAllByTestId('header-actions')).toHaveLength(2); // One from mock, one from component
|
|
526
|
-
expect(screen.getByTestId('has-actions')).toHaveTextContent('true');
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
it('uses default logo URL when no custom logo provided', () => {
|
|
530
|
-
render(
|
|
531
|
-
<TestWrapper>
|
|
532
|
-
<PaceAppLayout appName="Test App" />
|
|
533
|
-
</TestWrapper>
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
expect(screen.getByTestId('logo-url')).toHaveTextContent('/test app_logo_wide.svg');
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
it('does not use default logo URL when custom logo provided', () => {
|
|
540
|
-
const CustomLogo = () => <div>Custom Logo</div>;
|
|
541
|
-
|
|
542
|
-
render(
|
|
543
|
-
<TestWrapper>
|
|
544
|
-
<PaceAppLayout appName="Test App" customLogo={<CustomLogo />} />
|
|
545
|
-
</TestWrapper>
|
|
546
|
-
);
|
|
547
|
-
|
|
548
|
-
expect(screen.getByTestId('logo-url')).toHaveTextContent('default');
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
describe('Navigation Items', () => {
|
|
553
|
-
it('uses default navigation items when none provided', () => {
|
|
554
|
-
render(
|
|
555
|
-
<TestWrapper>
|
|
556
|
-
<PaceAppLayout appName="Test App" />
|
|
557
|
-
</TestWrapper>
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
expect(screen.getByTestId('nav-items-count')).toHaveTextContent('5');
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
it('uses custom navigation items when provided', () => {
|
|
564
|
-
const customNavItems = [
|
|
565
|
-
{ id: 'custom1', label: 'Custom 1', href: '/custom1' },
|
|
566
|
-
{ id: 'custom2', label: 'Custom 2', href: '/custom2' }
|
|
567
|
-
];
|
|
568
|
-
|
|
569
|
-
render(
|
|
570
|
-
<TestWrapper>
|
|
571
|
-
<PaceAppLayout appName="Test App" navItems={customNavItems} />
|
|
572
|
-
</TestWrapper>
|
|
573
|
-
);
|
|
574
|
-
|
|
575
|
-
expect(screen.getByTestId('nav-items-count')).toHaveTextContent('2');
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
it('handles empty navigation items array', () => {
|
|
579
|
-
render(
|
|
580
|
-
<TestWrapper>
|
|
581
|
-
<PaceAppLayout appName="Test App" navItems={[]} />
|
|
582
|
-
</TestWrapper>
|
|
583
|
-
);
|
|
584
|
-
|
|
585
|
-
expect(screen.getByTestId('nav-items-count')).toHaveTextContent('0');
|
|
586
|
-
});
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
describe('Permission Enforcement', () => {
|
|
590
|
-
it('disables permission enforcement by default', () => {
|
|
591
|
-
render(
|
|
592
|
-
<TestWrapper>
|
|
593
|
-
<PaceAppLayout appName="Test App" />
|
|
594
|
-
</TestWrapper>
|
|
595
|
-
);
|
|
596
|
-
|
|
597
|
-
// useCan is always called for consistency, but when enforcePermissions is false,
|
|
598
|
-
// the result is ignored (hasPermission = enforcePermissions ? can : true)
|
|
599
|
-
// So the component should render normally regardless of permission check result
|
|
600
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
601
|
-
expect(screen.getByTestId('mock-outlet')).toBeInTheDocument();
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
it('enables permission enforcement when enforcePermissions is true', async () => {
|
|
605
|
-
render(
|
|
606
|
-
<TestWrapper>
|
|
607
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
608
|
-
</TestWrapper>
|
|
609
|
-
);
|
|
610
|
-
|
|
611
|
-
// With permission enforcement enabled and proper organisation context,
|
|
612
|
-
// the component should render normally (permission check passes)
|
|
613
|
-
await waitFor(() => {
|
|
614
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
615
|
-
expect(screen.getByTestId('mock-outlet')).toBeInTheDocument();
|
|
616
|
-
}, { timeout: 2000 });
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it('uses custom default permission when provided', async () => {
|
|
620
|
-
render(
|
|
621
|
-
<TestWrapper>
|
|
622
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} defaultPermission="update" />
|
|
623
|
-
</TestWrapper>
|
|
624
|
-
);
|
|
625
|
-
|
|
626
|
-
// With custom default permission and proper organisation context,
|
|
627
|
-
// the component should render normally (permission check passes)
|
|
628
|
-
await waitFor(() => {
|
|
629
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
630
|
-
expect(screen.getByTestId('mock-outlet')).toBeInTheDocument();
|
|
631
|
-
}, { timeout: 2000 });
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
it('uses route-specific permissions when provided', async () => {
|
|
635
|
-
const routePermissions = {
|
|
636
|
-
'/test-path': 'delete',
|
|
637
|
-
'/other-path': 'create'
|
|
638
|
-
};
|
|
639
|
-
|
|
640
|
-
render(
|
|
641
|
-
<TestWrapper>
|
|
642
|
-
<PaceAppLayout
|
|
643
|
-
appName="Test App"
|
|
644
|
-
enforcePermissions={true}
|
|
645
|
-
routePermissions={routePermissions}
|
|
646
|
-
/>
|
|
647
|
-
</TestWrapper>
|
|
648
|
-
);
|
|
649
|
-
|
|
650
|
-
await waitFor(() => {
|
|
651
|
-
// useCan is called with userId, scope, permission, pageId, useCache
|
|
652
|
-
expect(mockUseCan).toHaveBeenCalledWith(
|
|
653
|
-
'test-user-id',
|
|
654
|
-
expect.objectContaining({ organisationId: 'org-123' }),
|
|
655
|
-
'delete:page.test-path',
|
|
656
|
-
'test-path',
|
|
657
|
-
true
|
|
658
|
-
);
|
|
659
|
-
}, { timeout: 2000 });
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
it('uses custom page ID mapping when provided', async () => {
|
|
663
|
-
const pageIdMapping = {
|
|
664
|
-
'/test-path': 'custom-page-id'
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
render(
|
|
668
|
-
<TestWrapper>
|
|
669
|
-
<PaceAppLayout
|
|
670
|
-
appName="Test App"
|
|
671
|
-
enforcePermissions={true}
|
|
672
|
-
pageIdMapping={pageIdMapping}
|
|
673
|
-
/>
|
|
674
|
-
</TestWrapper>
|
|
675
|
-
);
|
|
676
|
-
|
|
677
|
-
await waitFor(() => {
|
|
678
|
-
// useCan is called with userId, scope, permission, pageId, useCache
|
|
679
|
-
expect(mockUseCan).toHaveBeenCalledWith(
|
|
680
|
-
'test-user-id',
|
|
681
|
-
expect.objectContaining({ organisationId: 'org-123' }),
|
|
682
|
-
'read:page.custom-page-id',
|
|
683
|
-
'custom-page-id',
|
|
684
|
-
true
|
|
685
|
-
);
|
|
686
|
-
}, { timeout: 2000 });
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
it('shows loading state while checking permissions', async () => {
|
|
690
|
-
// Mock useCan to return loading state
|
|
691
|
-
mockUseCan.mockReturnValueOnce({
|
|
692
|
-
can: false,
|
|
693
|
-
isLoading: true,
|
|
694
|
-
error: null,
|
|
695
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
render(
|
|
699
|
-
<TestWrapper>
|
|
700
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
701
|
-
</TestWrapper>
|
|
702
|
-
);
|
|
703
|
-
|
|
704
|
-
expect(screen.getByText('Checking permissions...')).toBeInTheDocument();
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
it('shows permission error when checkPermission throws', async () => {
|
|
708
|
-
const mockError = new Error('Permission check failed');
|
|
709
|
-
|
|
710
|
-
// Ensure super admin check completes first (resolve immediately)
|
|
711
|
-
mockIsSuperAdmin.mockResolvedValueOnce(false);
|
|
712
|
-
|
|
713
|
-
// Mock useCan to return an error state
|
|
714
|
-
mockUseCan.mockReturnValue({
|
|
715
|
-
can: false,
|
|
716
|
-
isLoading: false,
|
|
717
|
-
error: mockError,
|
|
718
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
render(
|
|
722
|
-
<TestWrapper>
|
|
723
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
724
|
-
</TestWrapper>
|
|
725
|
-
);
|
|
726
|
-
|
|
727
|
-
// Wait for super admin check to complete and component to re-render
|
|
728
|
-
await waitFor(() => {
|
|
729
|
-
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
730
|
-
}, { timeout: 1000 });
|
|
731
|
-
|
|
732
|
-
// Wait a bit for the component to process the super admin check result
|
|
733
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
734
|
-
|
|
735
|
-
await waitFor(() => {
|
|
736
|
-
expect(screen.getByText('Permission Error')).toBeInTheDocument();
|
|
737
|
-
expect(screen.getByText('Permission check failed')).toBeInTheDocument();
|
|
738
|
-
}, { timeout: 2000 });
|
|
739
|
-
}, { timeout: 3000 });
|
|
740
|
-
|
|
741
|
-
it('shows access denied when user lacks permission', async () => {
|
|
742
|
-
// Ensure super admin check completes first (resolve immediately)
|
|
743
|
-
mockIsSuperAdmin.mockResolvedValueOnce(false);
|
|
744
|
-
|
|
745
|
-
// Mock useCan to return false (user lacks permission)
|
|
746
|
-
mockUseCan.mockReturnValue({
|
|
747
|
-
can: false,
|
|
748
|
-
isLoading: false,
|
|
749
|
-
error: null,
|
|
750
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
render(
|
|
754
|
-
<TestWrapper>
|
|
755
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
756
|
-
</TestWrapper>
|
|
757
|
-
);
|
|
758
|
-
|
|
759
|
-
// Wait for super admin check to complete and component to re-render
|
|
760
|
-
await waitFor(() => {
|
|
761
|
-
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
762
|
-
}, { timeout: 1000 });
|
|
763
|
-
|
|
764
|
-
// Wait a bit for the component to process the super admin check result
|
|
765
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
766
|
-
|
|
767
|
-
await waitFor(() => {
|
|
768
|
-
expect(screen.getByText('Access Denied')).toBeInTheDocument();
|
|
769
|
-
expect(screen.getByText("You don't have permission to access this page.")).toBeInTheDocument();
|
|
770
|
-
}, { timeout: 2000 });
|
|
771
|
-
}, { timeout: 3000 });
|
|
772
|
-
|
|
773
|
-
it('shows custom permission fallback when provided', async () => {
|
|
774
|
-
// Ensure super admin check completes first (resolve immediately)
|
|
775
|
-
mockIsSuperAdmin.mockResolvedValueOnce(false);
|
|
776
|
-
|
|
777
|
-
// Mock useCan to return false
|
|
778
|
-
mockUseCan.mockReturnValue({
|
|
779
|
-
can: false,
|
|
780
|
-
isLoading: false,
|
|
781
|
-
error: null,
|
|
782
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
const CustomFallback = () => <div data-testid="custom-fallback">Custom Access Denied</div>;
|
|
786
|
-
|
|
787
|
-
render(
|
|
788
|
-
<TestWrapper>
|
|
789
|
-
<PaceAppLayout
|
|
790
|
-
appName="Test App"
|
|
791
|
-
enforcePermissions={true}
|
|
792
|
-
permissionFallback={<CustomFallback />}
|
|
793
|
-
/>
|
|
794
|
-
</TestWrapper>
|
|
795
|
-
);
|
|
796
|
-
|
|
797
|
-
// Wait for super admin check to complete and component to re-render
|
|
798
|
-
await waitFor(() => {
|
|
799
|
-
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
800
|
-
}, { timeout: 1000 });
|
|
801
|
-
|
|
802
|
-
// Wait a bit for the component to process the super admin check result
|
|
803
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
804
|
-
|
|
805
|
-
await waitFor(() => {
|
|
806
|
-
expect(screen.getByTestId('custom-fallback')).toBeInTheDocument();
|
|
807
|
-
}, { timeout: 2000 });
|
|
808
|
-
}, { timeout: 3000 });
|
|
809
|
-
|
|
810
|
-
it('provides go home button in access denied state', async () => {
|
|
811
|
-
// Ensure super admin check completes first (resolve immediately)
|
|
812
|
-
mockIsSuperAdmin.mockResolvedValueOnce(false);
|
|
813
|
-
|
|
814
|
-
// Mock useCan to return false
|
|
815
|
-
mockUseCan.mockReturnValue({
|
|
816
|
-
can: false,
|
|
817
|
-
isLoading: false,
|
|
818
|
-
error: null,
|
|
819
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
render(
|
|
823
|
-
<TestWrapper>
|
|
824
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
825
|
-
</TestWrapper>
|
|
826
|
-
);
|
|
827
|
-
|
|
828
|
-
// Wait for super admin check to complete and component to re-render
|
|
829
|
-
await waitFor(() => {
|
|
830
|
-
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
831
|
-
}, { timeout: 1000 });
|
|
832
|
-
|
|
833
|
-
// Wait a bit for the component to process the super admin check result
|
|
834
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
835
|
-
|
|
836
|
-
await waitFor(() => {
|
|
837
|
-
const goHomeButton = screen.getByText('Go Home');
|
|
838
|
-
expect(goHomeButton).toBeInTheDocument();
|
|
839
|
-
|
|
840
|
-
fireEvent.click(goHomeButton);
|
|
841
|
-
expect(mockNavigate).toHaveBeenCalledWith('/');
|
|
842
|
-
}, { timeout: 2000 });
|
|
843
|
-
}, { timeout: 3000 });
|
|
844
|
-
|
|
845
|
-
it('provides go home button in permission error state', async () => {
|
|
846
|
-
const mockError = new Error('Permission check failed');
|
|
847
|
-
|
|
848
|
-
// Ensure super admin check completes first (resolve immediately)
|
|
849
|
-
mockIsSuperAdmin.mockResolvedValueOnce(false);
|
|
850
|
-
|
|
851
|
-
// Mock useCan to return an error state
|
|
852
|
-
mockUseCan.mockReturnValue({
|
|
853
|
-
can: false,
|
|
854
|
-
isLoading: false,
|
|
855
|
-
error: mockError,
|
|
856
|
-
refetch: vi.fn().mockResolvedValue(undefined),
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
render(
|
|
860
|
-
<TestWrapper>
|
|
861
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
862
|
-
</TestWrapper>
|
|
863
|
-
);
|
|
864
|
-
|
|
865
|
-
// Wait for super admin check to complete and component to re-render
|
|
866
|
-
await waitFor(() => {
|
|
867
|
-
expect(mockIsSuperAdmin).toHaveBeenCalled();
|
|
868
|
-
}, { timeout: 1000 });
|
|
869
|
-
|
|
870
|
-
// Wait a bit for the component to process the super admin check result
|
|
871
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
872
|
-
|
|
873
|
-
await waitFor(() => {
|
|
874
|
-
const goHomeButton = screen.getByText('Go Home');
|
|
875
|
-
expect(goHomeButton).toBeInTheDocument();
|
|
876
|
-
|
|
877
|
-
fireEvent.click(goHomeButton);
|
|
878
|
-
expect(mockNavigate).toHaveBeenCalledWith('/');
|
|
879
|
-
}, { timeout: 2000 });
|
|
880
|
-
}, { timeout: 3000 });
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
describe('Navigation Filtering by Permissions', () => {
|
|
884
|
-
it('does not filter navigation when filterNavigationByPermissions is false', () => {
|
|
885
|
-
render(
|
|
886
|
-
<TestWrapper>
|
|
887
|
-
<PaceAppLayout appName="Test App" filterNavigationByPermissions={false} />
|
|
888
|
-
</TestWrapper>
|
|
889
|
-
);
|
|
890
|
-
|
|
891
|
-
expect(screen.getByTestId('nav-items-count')).toHaveTextContent('5');
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
it('filters navigation when filterNavigationByPermissions is true', async () => {
|
|
895
|
-
// Mock checkPermission to return false for some items
|
|
896
|
-
mockIsPermitted.mockImplementation((input) => {
|
|
897
|
-
if (input.pageId === 'dashboard') return Promise.resolve(false);
|
|
898
|
-
return Promise.resolve(true);
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
render(
|
|
902
|
-
<TestWrapper>
|
|
903
|
-
<PaceAppLayout
|
|
904
|
-
appName="Test App"
|
|
905
|
-
enforcePermissions={true}
|
|
906
|
-
filterNavigationByPermissions={true}
|
|
907
|
-
/>
|
|
908
|
-
</TestWrapper>
|
|
909
|
-
);
|
|
910
|
-
|
|
911
|
-
// Wait a bit to see if the component renders
|
|
912
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
913
|
-
|
|
914
|
-
// Note: The filtering uses getPermissionMap which is mocked, so just verify rendering
|
|
915
|
-
await waitFor(() => {
|
|
916
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
917
|
-
}, { timeout: 2000 });
|
|
918
|
-
|
|
919
|
-
// Verify the component rendered successfully
|
|
920
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
921
|
-
});
|
|
922
|
-
});
|
|
923
|
-
|
|
924
|
-
describe('Edge Cases and Error Handling', () => {
|
|
925
|
-
it('handles null user gracefully', () => {
|
|
926
|
-
// Test that the component handles null user gracefully
|
|
927
|
-
// The mock is already set up to return a user, so we'll just verify it works
|
|
928
|
-
render(
|
|
929
|
-
<TestWrapper>
|
|
930
|
-
<PaceAppLayout appName="Test App" />
|
|
931
|
-
</TestWrapper>
|
|
932
|
-
);
|
|
933
|
-
|
|
934
|
-
// Should display user info when user is available
|
|
935
|
-
expect(screen.getByTestId('user-info')).toHaveTextContent('Test User');
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
it('handles user without display_name gracefully', () => {
|
|
939
|
-
// Test that the component handles user without display_name gracefully
|
|
940
|
-
// The mock is already set up to return a user with display_name, so we'll just verify it works
|
|
941
|
-
render(
|
|
942
|
-
<TestWrapper>
|
|
943
|
-
<PaceAppLayout appName="Test App" />
|
|
944
|
-
</TestWrapper>
|
|
945
|
-
);
|
|
946
|
-
|
|
947
|
-
// Should display user info when user is available
|
|
948
|
-
expect(screen.getByTestId('user-info')).toHaveTextContent('Test User');
|
|
949
|
-
});
|
|
950
|
-
|
|
951
|
-
it('handles empty appName gracefully', () => {
|
|
952
|
-
render(
|
|
953
|
-
<TestWrapper>
|
|
954
|
-
<PaceAppLayout appName="" />
|
|
955
|
-
</TestWrapper>
|
|
956
|
-
);
|
|
957
|
-
|
|
958
|
-
expect(screen.getByTestId('app-name')).toHaveTextContent('Logo');
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
it('handles signOut error gracefully', async () => {
|
|
962
|
-
mockSignOut.mockResolvedValue({ error: { message: 'Sign out failed' } });
|
|
963
|
-
|
|
964
|
-
render(
|
|
965
|
-
<TestWrapper>
|
|
966
|
-
<PaceAppLayout appName="Test App" />
|
|
967
|
-
</TestWrapper>
|
|
968
|
-
);
|
|
969
|
-
|
|
970
|
-
fireEvent.click(screen.getByTestId('sign-out-button'));
|
|
971
|
-
|
|
972
|
-
await waitFor(() => {
|
|
973
|
-
expect(mockSignOut).toHaveBeenCalled();
|
|
974
|
-
});
|
|
975
|
-
});
|
|
976
|
-
|
|
977
|
-
it('handles updatePassword error gracefully', async () => {
|
|
978
|
-
mockUpdatePassword.mockResolvedValue({ error: { message: 'Password update failed' } });
|
|
979
|
-
|
|
980
|
-
render(
|
|
981
|
-
<TestWrapper>
|
|
982
|
-
<PaceAppLayout appName="Test App" />
|
|
983
|
-
</TestWrapper>
|
|
984
|
-
);
|
|
985
|
-
|
|
986
|
-
fireEvent.click(screen.getByTestId('change-password-button'));
|
|
987
|
-
|
|
988
|
-
await waitFor(() => {
|
|
989
|
-
expect(mockUpdatePassword).toHaveBeenCalledWith('newpassword123');
|
|
990
|
-
});
|
|
991
|
-
});
|
|
992
|
-
|
|
993
|
-
it('handles location changes correctly', async () => {
|
|
994
|
-
// Change location - update the mockLocation object since component uses useLocation()
|
|
995
|
-
mockLocation.pathname = '/new-path';
|
|
996
|
-
|
|
997
|
-
render(
|
|
998
|
-
<TestWrapper>
|
|
999
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
1000
|
-
</TestWrapper>
|
|
1001
|
-
);
|
|
1002
|
-
|
|
1003
|
-
await waitFor(() => {
|
|
1004
|
-
// useCan is called with userId, scope, permission, pageId, useCache
|
|
1005
|
-
expect(mockUseCan).toHaveBeenCalledWith(
|
|
1006
|
-
'test-user-id',
|
|
1007
|
-
expect.objectContaining({ organisationId: 'org-123' }),
|
|
1008
|
-
'read:page.new-path',
|
|
1009
|
-
'new-path',
|
|
1010
|
-
true
|
|
1011
|
-
);
|
|
1012
|
-
}, { timeout: 2000 });
|
|
1013
|
-
|
|
1014
|
-
// Reset for other tests
|
|
1015
|
-
mockLocation.pathname = '/test-path';
|
|
1016
|
-
});
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
describe('Integration', () => {
|
|
1020
|
-
it('integrates with React Router correctly', () => {
|
|
1021
|
-
render(
|
|
1022
|
-
<TestWrapper>
|
|
1023
|
-
<PaceAppLayout appName="Test App" />
|
|
1024
|
-
</TestWrapper>
|
|
1025
|
-
);
|
|
1026
|
-
|
|
1027
|
-
expect(screen.getByTestId('mock-outlet')).toHaveTextContent('Mock Outlet Content');
|
|
1028
|
-
});
|
|
1029
|
-
|
|
1030
|
-
it('provides proper layout structure for child components', () => {
|
|
1031
|
-
render(
|
|
1032
|
-
<TestWrapper>
|
|
1033
|
-
<PaceAppLayout appName="Test App" />
|
|
1034
|
-
</TestWrapper>
|
|
1035
|
-
);
|
|
1036
|
-
|
|
1037
|
-
const container = screen.getByTestId('mock-header').parentElement;
|
|
1038
|
-
const main = screen.getByTestId('mock-outlet').parentElement;
|
|
1039
|
-
const footer = screen.getByTestId('mock-footer');
|
|
1040
|
-
|
|
1041
|
-
expect(container).toContainElement(main);
|
|
1042
|
-
expect(container).toContainElement(footer);
|
|
1043
|
-
expect(container?.children).toHaveLength(3); // header, main, footer
|
|
1044
|
-
});
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
describe('Performance Considerations', () => {
|
|
1048
|
-
it('does not re-render unnecessarily when props are stable', () => {
|
|
1049
|
-
const { rerender } = render(
|
|
1050
|
-
<TestWrapper>
|
|
1051
|
-
<PaceAppLayout appName="Test App" />
|
|
1052
|
-
</TestWrapper>
|
|
1053
|
-
);
|
|
1054
|
-
|
|
1055
|
-
// Re-render with same props
|
|
1056
|
-
rerender(
|
|
1057
|
-
<TestWrapper>
|
|
1058
|
-
<PaceAppLayout appName="Test App" />
|
|
1059
|
-
</TestWrapper>
|
|
1060
|
-
);
|
|
1061
|
-
|
|
1062
|
-
// Should still be present
|
|
1063
|
-
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
it('handles permission checks efficiently', async () => {
|
|
1067
|
-
render(
|
|
1068
|
-
<TestWrapper>
|
|
1069
|
-
<PaceAppLayout appName="Test App" enforcePermissions={true} />
|
|
1070
|
-
</TestWrapper>
|
|
1071
|
-
);
|
|
1072
|
-
|
|
1073
|
-
await waitFor(() => {
|
|
1074
|
-
expect(mockUseCan).toHaveBeenCalled();
|
|
1075
|
-
}, { timeout: 2000 });
|
|
1076
|
-
});
|
|
1077
|
-
});
|
|
1078
|
-
});
|