@jmruthers/pace-core 0.5.74 → 0.5.76
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/dist/DataTable-4GAVPIEG.js +120 -0
- package/dist/{PublicLoadingSpinner-DLpF5bbs.d.ts → PublicLoadingSpinner-BiNER8F5.d.ts} +30 -19
- package/dist/RBACService-C4udt_Zp.d.ts +528 -0
- package/dist/{UnifiedAuthProvider-K4NRGXL4.js → UnifiedAuthProvider-3NKDOSOK.js} +6 -4
- package/dist/UnifiedAuthProvider-Bj6YCf7c.d.ts +113 -0
- package/dist/chunk-5F3NDPJV.js +232 -0
- package/dist/chunk-5F3NDPJV.js.map +1 -0
- package/dist/chunk-A4FUBC7B.js +17 -0
- package/dist/chunk-A4FUBC7B.js.map +1 -0
- package/dist/{chunk-SMJZMKYN.js → chunk-A6HBIY5P.js} +2 -11
- package/dist/{chunk-SMJZMKYN.js.map → chunk-A6HBIY5P.js.map} +1 -1
- package/dist/{chunk-LVQ26TCN.js → chunk-AFGTSUAD.js} +43 -127
- package/dist/chunk-AFGTSUAD.js.map +1 -0
- package/dist/{chunk-BKVGJVUR.js → chunk-K34IM5CT.js} +497 -33
- package/dist/chunk-K34IM5CT.js.map +1 -0
- package/dist/{chunk-UJMCGBLS.js → chunk-KHJS6VIA.js} +203 -41
- package/dist/chunk-KHJS6VIA.js.map +1 -0
- package/dist/{chunk-ORSMVXO2.js → chunk-KK73ZB4E.js} +9 -14
- package/dist/chunk-KK73ZB4E.js.map +1 -0
- package/dist/{chunk-VKOCWWVY.js → chunk-L3RV2ALE.js} +1 -6
- package/dist/{chunk-VKOCWWVY.js.map → chunk-L3RV2ALE.js.map} +1 -1
- package/dist/chunk-LW7MMEAQ.js +59 -0
- package/dist/chunk-LW7MMEAQ.js.map +1 -0
- package/dist/{chunk-IHMMNKNA.js → chunk-M5IWZRBT.js} +5118 -1864
- package/dist/chunk-M5IWZRBT.js.map +1 -0
- package/dist/{chunk-DG5Z55HH.js → chunk-NTNILOBC.js} +7 -9
- package/dist/chunk-NTNILOBC.js.map +1 -0
- package/dist/chunk-PYUXFQJ3.js +11 -0
- package/dist/chunk-PYUXFQJ3.js.map +1 -0
- package/dist/chunk-URUTVZ7N.js +27 -0
- package/dist/chunk-URUTVZ7N.js.map +1 -0
- package/dist/chunk-WN6XJWOS.js +2468 -0
- package/dist/chunk-WN6XJWOS.js.map +1 -0
- package/dist/{chunk-3SP4P7NS.js → chunk-XLZ7U46Z.js} +59 -1
- package/dist/chunk-XLZ7U46Z.js.map +1 -0
- package/dist/{chunk-H2TNUICK.js → chunk-Y6TXWPJO.js} +50 -50
- package/dist/chunk-Y6TXWPJO.js.map +1 -0
- package/dist/{chunk-YNUBMSMV.js → chunk-YCKPEMJA.js} +186 -263
- package/dist/chunk-YCKPEMJA.js.map +1 -0
- package/dist/components.d.ts +4 -5
- package/dist/components.js +35 -41
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +20 -43
- package/dist/hooks.js +13 -12
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +156 -10
- package/dist/index.js +193 -96
- package/dist/index.js.map +1 -1
- package/dist/{organisation-t-vvQC3g.d.ts → organisation-BtshODVF.d.ts} +4 -3
- package/dist/providers.d.ts +27 -38
- package/dist/providers.js +33 -23
- package/dist/rbac/index.d.ts +114 -5
- package/dist/rbac/index.js +15 -15
- package/dist/styles/index.js +2 -2
- package/dist/theming/runtime.js +1 -3
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/{unified-CMPjE_fv.d.ts → unified-CM7T0aTK.d.ts} +1 -1
- package/dist/useInactivityTracker-MRUU55XI.js +10 -0
- package/dist/{usePublicRouteParams-Ua1Vz-HG.d.ts → usePublicRouteParams-B-CumWRc.d.ts} +3 -3
- package/dist/utils.js +7 -9
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/docs/TERMINOLOGY.md +231 -0
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +3 -3
- package/docs/api/interfaces/CardProps.md +2 -2
- 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/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +2 -2
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +2 -2
- package/docs/api/interfaces/LabelProps.md +1 -1
- 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 +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +28 -17
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +2 -2
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- 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/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +2 -2
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +5 -11
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.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/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/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +524 -440
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +14 -14
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +47 -0
- package/docs/api/interfaces/UseResolvedScopeReturn.md +47 -0
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +234 -61
- package/docs/api-reference/providers.md +26 -7
- package/docs/architecture/services.md +30 -32
- package/docs/best-practices/README.md +20 -0
- package/docs/best-practices/accessibility.md +566 -0
- package/docs/best-practices/performance-expansion.md +473 -0
- package/docs/breaking-changes.md +2 -5
- package/docs/core-concepts/authentication.md +15 -7
- package/docs/documentation-index.md +1 -1
- package/docs/documentation-templates.md +539 -0
- package/docs/getting-started/quick-start.md +16 -66
- package/docs/implementation-guides/component-styling.md +410 -0
- package/docs/implementation-guides/data-tables.md +1 -1
- package/docs/migration/service-architecture.md +121 -260
- package/docs/rbac/README-rbac-rls-integration.md +48 -38
- package/docs/style-guide.md +39 -0
- package/{src/rbac/examples → examples/RBAC}/CompleteRBACExample.tsx +3 -2
- package/{src/rbac/examples → examples/RBAC}/EventBasedApp.tsx +5 -4
- package/{src/components/examples → examples/RBAC}/PermissionExample.tsx +7 -6
- package/examples/RBAC/__tests__/PermissionExample.test.tsx +150 -0
- package/examples/RBAC/index.ts +13 -0
- package/examples/README.md +37 -0
- package/examples/index.ts +22 -0
- package/{src/examples → examples/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
- package/{src/examples → examples/public-pages}/PublicEventPage.tsx +1 -1
- package/{src/examples → examples/public-pages}/PublicPageApp.tsx +1 -1
- package/{src/examples → examples/public-pages}/PublicPageUsageExample.tsx +1 -1
- package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +159 -0
- package/examples/public-pages/index.ts +14 -0
- package/package.json +22 -18
- package/src/__tests__/TEST_GUIDE_CURSOR.md +940 -9
- package/src/__tests__/helpers/README.md +255 -0
- package/src/__tests__/helpers/index.ts +62 -0
- package/src/__tests__/helpers/supabaseMock.ts +75 -5
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -8
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +17 -6
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +73 -9
- package/src/components/DataTable/components/DataTableCore.tsx +280 -475
- package/src/components/DataTable/components/UnifiedTableBody.tsx +120 -153
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +55 -0
- package/src/components/DataTable/components/index.ts +1 -2
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +208 -275
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +254 -0
- package/src/components/DataTable/core/index.ts +1 -8
- package/src/components/DataTable/examples/__tests__/HierarchicalExample.test.tsx +45 -0
- package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +117 -0
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +525 -0
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +570 -0
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +214 -0
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +224 -0
- package/src/components/DataTable/hooks/index.ts +6 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +1 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +149 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +12 -6
- package/src/components/DataTable/hooks/useHierarchicalState.ts +26 -8
- package/src/components/DataTable/hooks/useTableColumns.ts +153 -0
- package/src/components/DataTable/index.ts +1 -9
- package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +89 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +3 -6
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +462 -0
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +247 -0
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +8 -6
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +466 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +265 -0
- package/src/components/DataTable/utils/errorHandling.ts +52 -460
- package/src/components/DataTable/utils/exportUtils.ts +46 -15
- package/src/components/DataTable/utils/hierarchicalSorting.ts +50 -3
- package/src/components/DataTable/utils/hierarchicalUtils.ts +167 -34
- package/src/components/DataTable/utils/index.ts +5 -0
- package/src/components/DataTable/utils/rowUtils.ts +68 -0
- package/src/components/Dialog/examples/__tests__/HtmlDialogExample.test.tsx +71 -0
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +122 -0
- package/src/components/EventSelector/EventSelector.test.tsx +672 -0
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/Header/Header.test.tsx +35 -1
- package/src/components/Header/Header.tsx +3 -1
- package/src/components/Label/__tests__/Label.test.tsx +434 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -3
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +24 -4
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +3 -2
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +190 -0
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +185 -0
- package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +313 -0
- package/src/components/Select/Select.test.tsx +143 -120
- package/src/components/Select/Select.tsx +47 -212
- package/src/components/Select/hooks.ts +36 -1
- package/src/components/Select/index.ts +2 -1
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +220 -0
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +117 -0
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +295 -0
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +29 -19
- package/src/hooks/__tests__/useRBAC.unit.test.ts +7 -3
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +115 -19
- package/src/hooks/services/__tests__/useServiceHooks.test.tsx +137 -0
- package/src/hooks/useEventTheme.test.ts +350 -0
- package/src/hooks/useEventTheme.ts +1 -1
- package/src/hooks/useEvents.ts +61 -0
- package/src/hooks/useOrganisationSecurity.test.ts +4 -4
- package/src/hooks/useOrganisationSecurity.ts +2 -2
- package/src/hooks/useOrganisations.ts +64 -0
- package/src/hooks/useSecureDataAccess.test.ts +37 -30
- package/src/hooks/useSecureDataAccess.ts +2 -2
- package/src/index.ts +18 -3
- package/src/providers/AuthProvider.tsx +8 -292
- package/src/providers/EventProvider.tsx +15 -425
- package/src/providers/InactivityProvider.tsx +8 -231
- package/src/providers/OrganisationProvider.test.simple.tsx +3 -2
- package/src/providers/OrganisationProvider.tsx +11 -890
- package/src/providers/UnifiedAuthProvider.tsx +8 -320
- package/src/providers/__tests__/AuthProvider.test.tsx +18 -17
- package/src/providers/__tests__/EventProvider.test.tsx +253 -2
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +65 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +46 -114
- package/src/providers/__tests__/OrganisationProvider.test.tsx +313 -3
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +341 -0
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +383 -2
- package/src/providers/index.ts +8 -7
- package/src/providers/services/EventServiceProvider.tsx +3 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +3 -0
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +437 -0
- package/src/rbac/hooks/index.ts +2 -0
- package/src/rbac/hooks/usePermissions.test.ts +296 -0
- package/src/rbac/hooks/useRBAC.test.ts +9 -5
- package/src/rbac/hooks/useRBAC.ts +3 -3
- package/src/rbac/hooks/useResolvedScope.ts +232 -0
- package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +688 -0
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +507 -0
- package/src/services/AuthService.ts +19 -4
- package/src/services/__tests__/AuthService.test.ts +288 -0
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +411 -0
- package/src/services/__tests__/OrganisationService.pagination.test.ts +375 -0
- package/src/styles/core.css +2 -0
- package/src/types/__tests__/README.md +114 -0
- package/src/types/__tests__/guards.test.ts +246 -0
- package/src/types/__tests__/validation.test.ts +731 -0
- package/src/types/guards.ts +1 -0
- package/src/types/organisation.ts +3 -2
- package/src/utils/__tests__/file-reference.test.ts +383 -0
- package/src/utils/__tests__/performanceBenchmark.test.ts +175 -0
- package/src/utils/appNameResolver.test.ts +54 -0
- package/src/validation/__tests__/csrf.unit.test.ts +63 -0
- package/src/validation/__tests__/passwordSchema.unit.test.ts +105 -0
- package/src/validation/__tests__/sanitization.unit.test.ts +250 -0
- package/src/validation/__tests__/schemaUtils.unit.test.ts +451 -0
- package/src/validation/__tests__/user.unit.test.ts +440 -0
- package/dist/DataTable-2QR5TER5.js +0 -102
- package/dist/RBACProvider-BO4ilsQB.d.ts +0 -63
- package/dist/UnifiedAuthProvider-D02AMXgO.d.ts +0 -103
- package/dist/chunk-3SP4P7NS.js.map +0 -1
- package/dist/chunk-B5LK25HV.js +0 -953
- package/dist/chunk-B5LK25HV.js.map +0 -1
- package/dist/chunk-BKVGJVUR.js.map +0 -1
- package/dist/chunk-C5Q5LRU5.js +0 -5691
- package/dist/chunk-C5Q5LRU5.js.map +0 -1
- package/dist/chunk-CDDYJCYU.js +0 -79
- package/dist/chunk-CDDYJCYU.js.map +0 -1
- package/dist/chunk-DG5Z55HH.js.map +0 -1
- package/dist/chunk-H2TNUICK.js.map +0 -1
- package/dist/chunk-IHMMNKNA.js.map +0 -1
- package/dist/chunk-LVQ26TCN.js.map +0 -1
- package/dist/chunk-ORSMVXO2.js.map +0 -1
- package/dist/chunk-TYHR5X4W.js +0 -33
- package/dist/chunk-TYHR5X4W.js.map +0 -1
- package/dist/chunk-UJMCGBLS.js.map +0 -1
- package/dist/chunk-V6BHACCH.js +0 -17
- package/dist/chunk-V6BHACCH.js.map +0 -1
- package/dist/chunk-YNUBMSMV.js.map +0 -1
- package/dist/eventContext-BBA42P6G.js +0 -14
- package/dist/rbac/cli/policy-manager.js +0 -278
- package/dist/rbac/cli/policy-manager.js.map +0 -1
- package/docs/api/interfaces/EventContextType.md +0 -96
- package/docs/api/interfaces/EventProviderProps.md +0 -19
- package/docs/documentation-style-checklist.md +0 -294
- package/src/components/DataTable/components/DataTableBody.tsx +0 -488
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -144
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -515
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -205
- package/src/components/DataTable/core/DataManager.ts +0 -188
- package/src/components/DataTable/core/DataTableContext.tsx +0 -181
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -264
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -311
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -634
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -519
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -714
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/utils/debugTools.ts +0 -583
- package/src/components/Select/Select.bug-test.tsx +0 -69
- package/src/components/Select/Select.refactored.tsx +0 -497
- package/src/providers/OrganisationProvider.test.tsx +0 -164
- package/src/providers/UnifiedAuthProvider.test.tsx +0 -124
- package/src/providers/__tests__/AuthProvider.test.tsx.backup +0 -771
- package/src/providers/__tests__/EventProvider.test.tsx.backup +0 -824
- package/src/providers/__tests__/OrganisationProvider.test.tsx.backup +0 -820
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup +0 -911
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup2 +0 -166
- package/src/rbac/cli/__tests__/policy-manager.test.ts +0 -339
- package/src/rbac/cli/policy-manager.ts +0 -443
- package/dist/{DataTable-2QR5TER5.js.map → DataTable-4GAVPIEG.js.map} +0 -0
- package/dist/{UnifiedAuthProvider-K4NRGXL4.js.map → UnifiedAuthProvider-3NKDOSOK.js.map} +0 -0
- package/dist/{eventContext-BBA42P6G.js.map → useInactivityTracker-MRUU55XI.js.map} +0 -0
- package/dist/{validation-PM_iOaTI.d.ts → validation-D8VcbTzC.d.ts} +2 -2
- /package/src/utils/{appNameResolver.test.ts.backup → appNameResolver.test 2.ts} +0 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Validation Schemas Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Types/Validation/__tests__
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive tests for validation schemas and utility functions.
|
|
8
|
+
* Tests cover runtime validation logic, edge cases, and error messages.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import {
|
|
14
|
+
emailSchema,
|
|
15
|
+
nameSchema,
|
|
16
|
+
phoneSchema,
|
|
17
|
+
urlSchema,
|
|
18
|
+
dateSchema,
|
|
19
|
+
passwordSchema,
|
|
20
|
+
securePasswordSchema,
|
|
21
|
+
loginSchema,
|
|
22
|
+
registrationSchema,
|
|
23
|
+
secureLoginSchema,
|
|
24
|
+
passwordResetSchema,
|
|
25
|
+
changePasswordSchema,
|
|
26
|
+
userProfileSchema,
|
|
27
|
+
contactFormSchema,
|
|
28
|
+
pickSchema,
|
|
29
|
+
combineSchemas,
|
|
30
|
+
ValidationError,
|
|
31
|
+
ValidationResult,
|
|
32
|
+
} from '../validation';
|
|
33
|
+
|
|
34
|
+
describe('[types] Validation Schemas', () => {
|
|
35
|
+
describe('emailSchema', () => {
|
|
36
|
+
it('accepts valid email addresses', () => {
|
|
37
|
+
expect(() => emailSchema.parse('test@example.com')).not.toThrow();
|
|
38
|
+
expect(() => emailSchema.parse('user.name@domain.co.uk')).not.toThrow();
|
|
39
|
+
expect(() => emailSchema.parse('user+tag@example.com')).not.toThrow();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('rejects invalid email addresses', () => {
|
|
43
|
+
expect(() => emailSchema.parse('invalid')).toThrow();
|
|
44
|
+
expect(() => emailSchema.parse('@example.com')).toThrow();
|
|
45
|
+
expect(() => emailSchema.parse('test@')).toThrow();
|
|
46
|
+
expect(() => emailSchema.parse('test.example.com')).toThrow();
|
|
47
|
+
expect(() => emailSchema.parse('')).toThrow();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('provides clear error messages', () => {
|
|
51
|
+
try {
|
|
52
|
+
emailSchema.parse('invalid');
|
|
53
|
+
} catch (error) {
|
|
54
|
+
expect(error.issues[0].message).toBe('Please enter a valid email address');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('nameSchema', () => {
|
|
60
|
+
it('accepts valid names', () => {
|
|
61
|
+
expect(() => nameSchema.parse('John Doe')).not.toThrow();
|
|
62
|
+
expect(() => nameSchema.parse('A')).not.toThrow(); // min length
|
|
63
|
+
expect(() => nameSchema.parse('A'.repeat(100))).not.toThrow(); // max length
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('rejects empty names', () => {
|
|
67
|
+
expect(() => nameSchema.parse('')).toThrow();
|
|
68
|
+
// Note: Zod doesn't trim whitespace by default, so ' ' passes min(1)
|
|
69
|
+
// This is expected behavior - empty string fails, whitespace passes
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('rejects names exceeding max length', () => {
|
|
73
|
+
const longName = 'A'.repeat(101);
|
|
74
|
+
expect(() => nameSchema.parse(longName)).toThrow();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('provides clear error messages', () => {
|
|
78
|
+
try {
|
|
79
|
+
nameSchema.parse('');
|
|
80
|
+
} catch (error) {
|
|
81
|
+
expect(error.issues[0].message).toBe('Name is required');
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('phoneSchema', () => {
|
|
87
|
+
it('accepts valid phone numbers', () => {
|
|
88
|
+
expect(() => phoneSchema.parse('+1234567890')).not.toThrow();
|
|
89
|
+
expect(() => phoneSchema.parse('123-456-7890')).not.toThrow();
|
|
90
|
+
expect(() => phoneSchema.parse('(123) 456-7890')).not.toThrow();
|
|
91
|
+
expect(() => phoneSchema.parse('123 456 7890')).not.toThrow();
|
|
92
|
+
expect(() => phoneSchema.parse('1234567890')).not.toThrow();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('rejects invalid phone numbers', () => {
|
|
96
|
+
expect(() => phoneSchema.parse('abc')).toThrow(); // No digits
|
|
97
|
+
expect(() => phoneSchema.parse('abc@def')).toThrow(); // Invalid characters
|
|
98
|
+
// Note: 'abc123' and '123@456' actually pass regex as they contain digits
|
|
99
|
+
expect(() => phoneSchema.parse('12345')).not.toThrow(); // Valid with digits
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('provides clear error messages', () => {
|
|
103
|
+
try {
|
|
104
|
+
phoneSchema.parse('invalid');
|
|
105
|
+
} catch (error) {
|
|
106
|
+
expect(error.issues[0].message).toBe('Please enter a valid phone number');
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('urlSchema', () => {
|
|
112
|
+
it('accepts valid URLs', () => {
|
|
113
|
+
expect(() => urlSchema.parse('https://example.com')).not.toThrow();
|
|
114
|
+
expect(() => urlSchema.parse('http://example.com')).not.toThrow();
|
|
115
|
+
expect(() => urlSchema.parse('https://www.example.com/path?query=value')).not.toThrow();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('rejects invalid URLs', () => {
|
|
119
|
+
expect(() => urlSchema.parse('not-a-url')).toThrow();
|
|
120
|
+
expect(() => urlSchema.parse('example')).toThrow();
|
|
121
|
+
// Note: ftp:// URLs are technically valid URLs in some contexts
|
|
122
|
+
// Testing with clearly invalid formats
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('provides clear error messages', () => {
|
|
126
|
+
try {
|
|
127
|
+
urlSchema.parse('invalid');
|
|
128
|
+
} catch (error) {
|
|
129
|
+
expect(error.issues[0].message).toBe('Please enter a valid URL');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('dateSchema', () => {
|
|
135
|
+
it('accepts valid dates', () => {
|
|
136
|
+
expect(() => dateSchema.parse('2024-01-01')).not.toThrow();
|
|
137
|
+
expect(() => dateSchema.parse('2024-12-31')).not.toThrow();
|
|
138
|
+
expect(() => dateSchema.parse('1990-01-15')).not.toThrow();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('rejects invalid dates', () => {
|
|
142
|
+
expect(() => dateSchema.parse('invalid')).toThrow();
|
|
143
|
+
expect(() => dateSchema.parse('not-a-date')).toThrow();
|
|
144
|
+
expect(() => dateSchema.parse('')).toThrow(); // Empty string fails
|
|
145
|
+
// Note: Date.parse is lenient and auto-adjusts invalid dates
|
|
146
|
+
// '2024-13-01' becomes valid date, '2024-02-30' auto-adjusts
|
|
147
|
+
// This is expected behavior of JavaScript Date.parse
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('passwordSchema', () => {
|
|
152
|
+
it('accepts valid passwords', () => {
|
|
153
|
+
expect(() => passwordSchema.parse('Password123')).not.toThrow();
|
|
154
|
+
expect(() => passwordSchema.parse('SecurePass1')).not.toThrow();
|
|
155
|
+
expect(() => passwordSchema.parse('MyStr0ngP@ss')).not.toThrow();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('rejects passwords shorter than 8 characters', () => {
|
|
159
|
+
expect(() => passwordSchema.parse('Pass1')).toThrow();
|
|
160
|
+
expect(() => passwordSchema.parse('Abc12')).toThrow();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('rejects passwords without uppercase letters', () => {
|
|
164
|
+
expect(() => passwordSchema.parse('password123')).toThrow();
|
|
165
|
+
expect(() => passwordSchema.parse('mysecurepass123')).toThrow();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('rejects passwords without lowercase letters', () => {
|
|
169
|
+
expect(() => passwordSchema.parse('PASSWORD123')).toThrow();
|
|
170
|
+
expect(() => passwordSchema.parse('SECUREPASS123')).toThrow();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('rejects passwords without numbers', () => {
|
|
174
|
+
expect(() => passwordSchema.parse('Password')).toThrow();
|
|
175
|
+
expect(() => passwordSchema.parse('SecurePass')).toThrow();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('provides clear error messages for each requirement', () => {
|
|
179
|
+
// Test min length
|
|
180
|
+
try {
|
|
181
|
+
passwordSchema.parse('Short1');
|
|
182
|
+
} catch (error) {
|
|
183
|
+
expect(error.issues[0].message).toBe('Password must be at least 8 characters');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Test uppercase requirement
|
|
187
|
+
try {
|
|
188
|
+
passwordSchema.parse('password123');
|
|
189
|
+
} catch (error) {
|
|
190
|
+
expect(error.issues[0].message).toBe('Password must contain at least one uppercase letter');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Test lowercase requirement
|
|
194
|
+
try {
|
|
195
|
+
passwordSchema.parse('PASSWORD123');
|
|
196
|
+
} catch (error) {
|
|
197
|
+
expect(error.issues[0].message).toBe('Password must contain at least one lowercase letter');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Test number requirement
|
|
201
|
+
try {
|
|
202
|
+
passwordSchema.parse('Password');
|
|
203
|
+
} catch (error) {
|
|
204
|
+
expect(error.issues[0].message).toBe('Password must contain at least one number');
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('securePasswordSchema', () => {
|
|
210
|
+
it('accepts valid secure passwords', () => {
|
|
211
|
+
expect(() => securePasswordSchema.parse('Password123!')).not.toThrow();
|
|
212
|
+
expect(() => securePasswordSchema.parse('Secure@Pass1')).not.toThrow();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('rejects passwords without special characters', () => {
|
|
216
|
+
expect(() => securePasswordSchema.parse('Password123')).toThrow();
|
|
217
|
+
expect(() => securePasswordSchema.parse('SecurePass1')).toThrow();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('provides clear error message for special character requirement', () => {
|
|
221
|
+
try {
|
|
222
|
+
securePasswordSchema.parse('Password123');
|
|
223
|
+
} catch (error) {
|
|
224
|
+
const errorMessages = error.issues.map(i => i.message);
|
|
225
|
+
expect(errorMessages).toContain('Password must contain at least one special character');
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('includes all password requirements', () => {
|
|
230
|
+
try {
|
|
231
|
+
securePasswordSchema.parse('weak');
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const errorMessages = error.issues.map(i => i.message);
|
|
234
|
+
// Check that all requirements are present in the error messages
|
|
235
|
+
expect(errorMessages).toContain('Password must be at least 8 characters');
|
|
236
|
+
expect(errorMessages).toContain('Password must contain at least one uppercase letter');
|
|
237
|
+
expect(errorMessages).toContain('Password must contain at least one number');
|
|
238
|
+
expect(errorMessages).toContain('Password must contain at least one special character');
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('loginSchema', () => {
|
|
244
|
+
it('accepts valid login credentials', () => {
|
|
245
|
+
const validLogin = {
|
|
246
|
+
email: 'test@example.com',
|
|
247
|
+
password: 'anypassword',
|
|
248
|
+
};
|
|
249
|
+
expect(() => loginSchema.parse(validLogin)).not.toThrow();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('rejects invalid email', () => {
|
|
253
|
+
const invalidLogin = {
|
|
254
|
+
email: 'invalid',
|
|
255
|
+
password: 'password',
|
|
256
|
+
};
|
|
257
|
+
expect(() => loginSchema.parse(invalidLogin)).toThrow();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('rejects empty password', () => {
|
|
261
|
+
const invalidLogin = {
|
|
262
|
+
email: 'test@example.com',
|
|
263
|
+
password: '',
|
|
264
|
+
};
|
|
265
|
+
expect(() => loginSchema.parse(invalidLogin)).toThrow();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('validates complete login data', () => {
|
|
269
|
+
const result = loginSchema.safeParse({
|
|
270
|
+
email: 'user@example.com',
|
|
271
|
+
password: 'secretpassword',
|
|
272
|
+
});
|
|
273
|
+
expect(result.success).toBe(true);
|
|
274
|
+
if (result.success) {
|
|
275
|
+
expect(result.data.email).toBe('user@example.com');
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe('registrationSchema', () => {
|
|
281
|
+
it('accepts valid registration data with matching passwords', () => {
|
|
282
|
+
const validRegistration = {
|
|
283
|
+
email: 'newuser@example.com',
|
|
284
|
+
password: 'SecurePass123',
|
|
285
|
+
confirmPassword: 'SecurePass123',
|
|
286
|
+
};
|
|
287
|
+
expect(() => registrationSchema.parse(validRegistration)).not.toThrow();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('rejects mismatched passwords', () => {
|
|
291
|
+
const invalidRegistration = {
|
|
292
|
+
email: 'newuser@example.com',
|
|
293
|
+
password: 'SecurePass123',
|
|
294
|
+
confirmPassword: 'DifferentPass123',
|
|
295
|
+
};
|
|
296
|
+
expect(() => registrationSchema.parse(invalidRegistration)).toThrow();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('rejects weak passwords even when matching', () => {
|
|
300
|
+
const weakPasswords = {
|
|
301
|
+
email: 'user@example.com',
|
|
302
|
+
password: 'weak',
|
|
303
|
+
confirmPassword: 'weak',
|
|
304
|
+
};
|
|
305
|
+
expect(() => registrationSchema.parse(weakPasswords)).toThrow();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('provides clear error message for password mismatch', () => {
|
|
309
|
+
try {
|
|
310
|
+
registrationSchema.parse({
|
|
311
|
+
email: 'user@example.com',
|
|
312
|
+
password: 'SecurePass123',
|
|
313
|
+
confirmPassword: 'Different123',
|
|
314
|
+
});
|
|
315
|
+
} catch (error) {
|
|
316
|
+
const errorMessages = error.issues.map(i => i.message);
|
|
317
|
+
expect(errorMessages).toContain("Passwords don't match");
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
describe('secureLoginSchema', () => {
|
|
323
|
+
it('accepts valid secure login credentials', () => {
|
|
324
|
+
const validLogin = {
|
|
325
|
+
email: 'test@example.com',
|
|
326
|
+
password: 'SecurePass123!',
|
|
327
|
+
};
|
|
328
|
+
expect(() => secureLoginSchema.parse(validLogin)).not.toThrow();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('rejects passwords without special characters', () => {
|
|
332
|
+
const invalidLogin = {
|
|
333
|
+
email: 'test@example.com',
|
|
334
|
+
password: 'Password123', // Missing special character
|
|
335
|
+
};
|
|
336
|
+
expect(() => secureLoginSchema.parse(invalidLogin)).toThrow();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('rejects other weak passwords', () => {
|
|
340
|
+
expect(() => secureLoginSchema.parse({
|
|
341
|
+
email: 'test@example.com',
|
|
342
|
+
password: 'weak',
|
|
343
|
+
})).toThrow();
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('passwordResetSchema', () => {
|
|
348
|
+
it('accepts valid email for password reset', () => {
|
|
349
|
+
expect(() => passwordResetSchema.parse({
|
|
350
|
+
email: 'user@example.com',
|
|
351
|
+
})).not.toThrow();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('rejects invalid email addresses', () => {
|
|
355
|
+
expect(() => passwordResetSchema.parse({
|
|
356
|
+
email: 'invalid-email',
|
|
357
|
+
})).toThrow();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('validates complete password reset data', () => {
|
|
361
|
+
const result = passwordResetSchema.safeParse({
|
|
362
|
+
email: 'user@example.com',
|
|
363
|
+
});
|
|
364
|
+
expect(result.success).toBe(true);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
describe('changePasswordSchema', () => {
|
|
369
|
+
it('accepts valid password change data with matching passwords', () => {
|
|
370
|
+
const validChange = {
|
|
371
|
+
currentPassword: 'OldPass123',
|
|
372
|
+
newPassword: 'NewSecurePass123!',
|
|
373
|
+
confirmPassword: 'NewSecurePass123!',
|
|
374
|
+
};
|
|
375
|
+
expect(() => changePasswordSchema.parse(validChange)).not.toThrow();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('rejects mismatched new passwords', () => {
|
|
379
|
+
const invalidChange = {
|
|
380
|
+
currentPassword: 'OldPass',
|
|
381
|
+
newPassword: 'NewSecurePass123!',
|
|
382
|
+
confirmPassword: 'Different123!',
|
|
383
|
+
};
|
|
384
|
+
expect(() => changePasswordSchema.parse(invalidChange)).toThrow();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('rejects weak new passwords', () => {
|
|
388
|
+
const weakNewPassword = {
|
|
389
|
+
currentPassword: 'OldPass',
|
|
390
|
+
newPassword: 'weak',
|
|
391
|
+
confirmPassword: 'weak',
|
|
392
|
+
};
|
|
393
|
+
expect(() => changePasswordSchema.parse(weakNewPassword)).toThrow();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('requires current password', () => {
|
|
397
|
+
const noCurrentPassword = {
|
|
398
|
+
currentPassword: '',
|
|
399
|
+
newPassword: 'NewSecurePass123!',
|
|
400
|
+
confirmPassword: 'NewSecurePass123!',
|
|
401
|
+
};
|
|
402
|
+
expect(() => changePasswordSchema.parse(noCurrentPassword)).toThrow();
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('provides clear error messages', () => {
|
|
406
|
+
try {
|
|
407
|
+
changePasswordSchema.parse({
|
|
408
|
+
currentPassword: '',
|
|
409
|
+
newPassword: 'Weak',
|
|
410
|
+
confirmPassword: 'Different',
|
|
411
|
+
});
|
|
412
|
+
} catch (error) {
|
|
413
|
+
const errorMessages = error.issues.map(i => i.message);
|
|
414
|
+
expect(errorMessages).toContain('Current password is required');
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe('userProfileSchema', () => {
|
|
420
|
+
it('accepts valid user profile data', () => {
|
|
421
|
+
const validProfile = {
|
|
422
|
+
name: 'John Doe',
|
|
423
|
+
email: 'john@example.com',
|
|
424
|
+
phone: '+1234567890',
|
|
425
|
+
website: 'https://johndoe.com',
|
|
426
|
+
bio: 'Software developer',
|
|
427
|
+
};
|
|
428
|
+
expect(() => userProfileSchema.parse(validProfile)).not.toThrow();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('accepts profile with only required fields', () => {
|
|
432
|
+
const minimalProfile = {
|
|
433
|
+
name: 'Jane Doe',
|
|
434
|
+
email: 'jane@example.com',
|
|
435
|
+
};
|
|
436
|
+
expect(() => userProfileSchema.parse(minimalProfile)).not.toThrow();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('accepts optional fields', () => {
|
|
440
|
+
const profileWithOptional = {
|
|
441
|
+
name: 'Test User',
|
|
442
|
+
email: 'test@example.com',
|
|
443
|
+
phone: '+1234567890',
|
|
444
|
+
};
|
|
445
|
+
expect(() => userProfileSchema.parse(profileWithOptional)).not.toThrow();
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('rejects invalid email', () => {
|
|
449
|
+
const invalidProfile = {
|
|
450
|
+
name: 'John Doe',
|
|
451
|
+
email: 'invalid-email',
|
|
452
|
+
};
|
|
453
|
+
expect(() => userProfileSchema.parse(invalidProfile)).toThrow();
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('rejects invalid phone format', () => {
|
|
457
|
+
const invalidProfile = {
|
|
458
|
+
name: 'John Doe',
|
|
459
|
+
email: 'john@example.com',
|
|
460
|
+
phone: 'abc123',
|
|
461
|
+
};
|
|
462
|
+
expect(() => userProfileSchema.parse(invalidProfile)).toThrow();
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('rejects invalid URL format', () => {
|
|
466
|
+
const invalidProfile = {
|
|
467
|
+
name: 'John Doe',
|
|
468
|
+
email: 'john@example.com',
|
|
469
|
+
website: 'not-a-url',
|
|
470
|
+
};
|
|
471
|
+
expect(() => userProfileSchema.parse(invalidProfile)).toThrow();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('rejects bio exceeding max length', () => {
|
|
475
|
+
const longBio = 'A'.repeat(501);
|
|
476
|
+
const invalidProfile = {
|
|
477
|
+
name: 'John Doe',
|
|
478
|
+
email: 'john@example.com',
|
|
479
|
+
bio: longBio,
|
|
480
|
+
};
|
|
481
|
+
expect(() => userProfileSchema.parse(invalidProfile)).toThrow();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('validates complete profile data', () => {
|
|
485
|
+
const result = userProfileSchema.safeParse({
|
|
486
|
+
name: 'John Doe',
|
|
487
|
+
email: 'john@example.com',
|
|
488
|
+
phone: '+1234567890',
|
|
489
|
+
website: 'https://johndoe.com',
|
|
490
|
+
bio: 'Software developer',
|
|
491
|
+
});
|
|
492
|
+
expect(result.success).toBe(true);
|
|
493
|
+
if (result.success) {
|
|
494
|
+
expect(result.data.name).toBe('John Doe');
|
|
495
|
+
expect(result.data.email).toBe('john@example.com');
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
describe('contactFormSchema', () => {
|
|
501
|
+
it('accepts valid contact form data', () => {
|
|
502
|
+
const validContact = {
|
|
503
|
+
name: 'John Doe',
|
|
504
|
+
email: 'john@example.com',
|
|
505
|
+
message: 'Hello, I would like to contact you.',
|
|
506
|
+
};
|
|
507
|
+
expect(() => contactFormSchema.parse(validContact)).not.toThrow();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('rejects empty name', () => {
|
|
511
|
+
const invalidContact = {
|
|
512
|
+
name: '',
|
|
513
|
+
email: 'john@example.com',
|
|
514
|
+
message: 'Hello',
|
|
515
|
+
};
|
|
516
|
+
expect(() => contactFormSchema.parse(invalidContact)).toThrow();
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('rejects invalid email', () => {
|
|
520
|
+
const invalidContact = {
|
|
521
|
+
name: 'John Doe',
|
|
522
|
+
email: 'invalid',
|
|
523
|
+
message: 'Hello',
|
|
524
|
+
};
|
|
525
|
+
expect(() => contactFormSchema.parse(invalidContact)).toThrow();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('rejects empty message', () => {
|
|
529
|
+
const invalidContact = {
|
|
530
|
+
name: 'John Doe',
|
|
531
|
+
email: 'john@example.com',
|
|
532
|
+
message: '',
|
|
533
|
+
};
|
|
534
|
+
expect(() => contactFormSchema.parse(invalidContact)).toThrow();
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('rejects message exceeding max length', () => {
|
|
538
|
+
const longMessage = 'A'.repeat(1001);
|
|
539
|
+
const invalidContact = {
|
|
540
|
+
name: 'John Doe',
|
|
541
|
+
email: 'john@example.com',
|
|
542
|
+
message: longMessage,
|
|
543
|
+
};
|
|
544
|
+
expect(() => contactFormSchema.parse(invalidContact)).toThrow();
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('provides clear error messages', () => {
|
|
548
|
+
try {
|
|
549
|
+
contactFormSchema.parse({
|
|
550
|
+
name: '',
|
|
551
|
+
email: 'invalid',
|
|
552
|
+
message: '',
|
|
553
|
+
});
|
|
554
|
+
} catch (error) {
|
|
555
|
+
const errorMessages = error.issues.map(i => i.message);
|
|
556
|
+
expect(errorMessages).toContain('Name is required');
|
|
557
|
+
expect(errorMessages).toContain('Message is required');
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
describe('Utility Functions', () => {
|
|
563
|
+
describe('pickSchema', () => {
|
|
564
|
+
it('creates schema with selected fields', () => {
|
|
565
|
+
const fullSchema = z.object({
|
|
566
|
+
name: z.string(),
|
|
567
|
+
email: z.string(),
|
|
568
|
+
phone: z.string(),
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
const pickedSchema = pickSchema(fullSchema, ['name', 'email']);
|
|
572
|
+
|
|
573
|
+
const validData = { name: 'John', email: 'john@example.com' };
|
|
574
|
+
expect(() => pickedSchema.parse(validData)).not.toThrow();
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('rejects data with fields not in picked schema', () => {
|
|
578
|
+
const fullSchema = z.object({
|
|
579
|
+
name: z.string(),
|
|
580
|
+
email: z.string(),
|
|
581
|
+
phone: z.string(),
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
const pickedSchema = pickSchema(fullSchema, ['name']);
|
|
585
|
+
|
|
586
|
+
const invalidData = { name: 'John', extraField: 'value' };
|
|
587
|
+
expect(() => pickedSchema.parse(invalidData)).not.toThrow(); // Extra fields are allowed by default
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('rejects data missing required fields', () => {
|
|
591
|
+
const fullSchema = z.object({
|
|
592
|
+
name: z.string(),
|
|
593
|
+
email: z.string(),
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
const pickedSchema = pickSchema(fullSchema, ['name', 'email']);
|
|
597
|
+
|
|
598
|
+
const incompleteData = { name: 'John' };
|
|
599
|
+
expect(() => pickedSchema.parse(incompleteData)).toThrow();
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('maintains field validation rules', () => {
|
|
603
|
+
const fullSchema = z.object({
|
|
604
|
+
name: z.string().min(3),
|
|
605
|
+
email: z.string().email(),
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const pickedSchema = pickSchema(fullSchema, ['name']);
|
|
609
|
+
|
|
610
|
+
const shortName = { name: 'Jo' };
|
|
611
|
+
expect(() => pickedSchema.parse(shortName)).toThrow();
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('can pick all fields', () => {
|
|
615
|
+
const fullSchema = z.object({
|
|
616
|
+
name: z.string(),
|
|
617
|
+
email: z.string(),
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const pickedSchema = pickSchema(fullSchema, ['name', 'email']);
|
|
621
|
+
|
|
622
|
+
const validData = { name: 'John', email: 'john@example.com' };
|
|
623
|
+
expect(() => pickedSchema.parse(validData)).not.toThrow();
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
describe('combineSchemas', () => {
|
|
628
|
+
it('merges multiple schemas correctly', () => {
|
|
629
|
+
const schema1 = z.object({ name: z.string() });
|
|
630
|
+
const schema2 = z.object({ email: z.string() });
|
|
631
|
+
|
|
632
|
+
const combined = combineSchemas([schema1, schema2]);
|
|
633
|
+
|
|
634
|
+
const validData = {
|
|
635
|
+
name: 'John Doe',
|
|
636
|
+
email: 'john@example.com',
|
|
637
|
+
};
|
|
638
|
+
expect(() => combined.parse(validData)).not.toThrow();
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('handles overlapping fields by merging validation', () => {
|
|
642
|
+
const schema1 = z.object({ value: z.string().min(3) });
|
|
643
|
+
const schema2 = z.object({ value: z.string().email() });
|
|
644
|
+
|
|
645
|
+
const combined = combineSchemas([schema1, schema2]);
|
|
646
|
+
|
|
647
|
+
const validData = { value: 'email@test.com' }; // >3 chars and email format
|
|
648
|
+
expect(() => combined.parse(validData)).not.toThrow();
|
|
649
|
+
|
|
650
|
+
const invalidEmail = { value: 'ab' };
|
|
651
|
+
expect(() => combined.parse(invalidEmail)).toThrow();
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it('combines three or more schemas', () => {
|
|
655
|
+
const schema1 = z.object({ name: z.string() });
|
|
656
|
+
const schema2 = z.object({ email: z.string() });
|
|
657
|
+
const schema3 = z.object({ phone: z.string() });
|
|
658
|
+
|
|
659
|
+
const combined = combineSchemas([schema1, schema2, schema3]);
|
|
660
|
+
|
|
661
|
+
const validData = {
|
|
662
|
+
name: 'John',
|
|
663
|
+
email: 'john@example.com',
|
|
664
|
+
phone: '1234567890',
|
|
665
|
+
};
|
|
666
|
+
expect(() => combined.parse(validData)).not.toThrow();
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it('works with empty array (returns empty object schema)', () => {
|
|
670
|
+
const combined = combineSchemas([]);
|
|
671
|
+
expect(() => combined.parse({})).not.toThrow();
|
|
672
|
+
expect(() => combined.parse({ extra: 'field' })).not.toThrow();
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
it('handles single schema array', () => {
|
|
676
|
+
const schema = z.object({ name: z.string() });
|
|
677
|
+
const combined = combineSchemas([schema]);
|
|
678
|
+
|
|
679
|
+
const validData = { name: 'John' };
|
|
680
|
+
expect(() => combined.parse(validData)).not.toThrow();
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
describe('Edge Cases and Error Handling', () => {
|
|
686
|
+
it('handles null values appropriately', () => {
|
|
687
|
+
expect(() => emailSchema.parse(null as any)).toThrow();
|
|
688
|
+
expect(() => nameSchema.parse(null as any)).toThrow();
|
|
689
|
+
expect(() => passwordSchema.parse(null as any)).toThrow();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('handles undefined values appropriately', () => {
|
|
693
|
+
expect(() => emailSchema.parse(undefined as any)).toThrow();
|
|
694
|
+
expect(() => nameSchema.parse(undefined as any)).toThrow();
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('handles non-string values appropriately', () => {
|
|
698
|
+
expect(() => emailSchema.parse(123 as any)).toThrow();
|
|
699
|
+
expect(() => emailSchema.parse({} as any)).toThrow();
|
|
700
|
+
expect(() => emailSchema.parse([] as any)).toThrow();
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('provides detailed validation errors', () => {
|
|
704
|
+
try {
|
|
705
|
+
registrationSchema.parse({
|
|
706
|
+
email: 'invalid',
|
|
707
|
+
password: 'weak',
|
|
708
|
+
confirmPassword: 'different',
|
|
709
|
+
});
|
|
710
|
+
} catch (error) {
|
|
711
|
+
expect(error.issues).toBeInstanceOf(Array);
|
|
712
|
+
expect(error.issues.length).toBeGreaterThan(0);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it('validates nested object structures correctly', () => {
|
|
717
|
+
const result = userProfileSchema.safeParse({
|
|
718
|
+
name: 'John Doe',
|
|
719
|
+
email: 'john@example.com',
|
|
720
|
+
phone: '+1234567890',
|
|
721
|
+
});
|
|
722
|
+
expect(result.success).toBe(true);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('handles whitespace in strings correctly', () => {
|
|
726
|
+
expect(() => nameSchema.parse(' John Doe ')).not.toThrow();
|
|
727
|
+
expect(() => emailSchema.parse(' test@example.com ')).toThrow(); // Email schema rejects whitespace
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
|