@jmruthers/pace-core 0.6.9 → 0.6.10
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/audit-tool/audits/02-project-structure.cjs +62 -0
- package/audit-tool/audits/03-architecture.cjs +145 -19
- package/audit-tool/audits/04-code-quality.cjs +86 -1
- package/audit-tool/audits/06-security-rbac.cjs +109 -11
- package/cursor-rules/02-project-structure.mdc +2 -26
- package/cursor-rules/05-styling.mdc +84 -6
- package/cursor-rules/06-security-rbac.mdc +124 -1
- package/dist/{DataTable-SOAFXIWY.js → DataTable-SAXFG4XI.js} +11 -13
- package/dist/{AuthService-DmfO5rGS.d.ts → InactivityServiceProvider-DHryoh6K.d.ts} +24 -249
- package/dist/UnifiedAuthProvider-BBD2PS3Q.js +7 -0
- package/dist/{UnifiedAuthProvider-CKvHP1MK.d.ts → UnifiedAuthProvider-CiBAl9-s.d.ts} +34 -22
- package/dist/{api-7P7DI652.js → api-F47QJ7FX.js} +3 -3
- package/dist/assets/app-icons/admin_favicon.svg +462 -0
- package/dist/assets/app-icons/base_favicon.svg +85 -0
- package/dist/assets/app-icons/cake_favicon.svg +68 -0
- package/dist/assets/app-icons/core_favicon.svg +256 -0
- package/dist/assets/app-icons/gear_favicon.svg +91 -0
- package/dist/assets/app-icons/medi_favicon.svg +92 -0
- package/dist/assets/app-icons/mint_favicon.svg +83 -0
- package/dist/assets/app-icons/pace_favicon.svg +49 -0
- package/dist/assets/app-icons/pump_favicon.svg +68 -0
- package/dist/assets/app-icons/seed_favicon.svg +91 -0
- package/dist/assets/app-icons/team_favicon.svg +67 -0
- package/dist/assets/app-icons/trac_favicon.svg +112 -0
- package/dist/assets/app-icons/trip_favicon.svg +102 -0
- package/dist/audit-Z6ZZBWLU.js +3 -0
- package/dist/chunk-3GWSPISD.js +61 -0
- package/dist/{chunk-4DDCYDQ3.js → chunk-66R6RLUZ.js} +12 -27
- package/dist/{chunk-FYHN4DD5.js → chunk-7YDC7LMU.js} +80 -8
- package/dist/{chunk-S7DKJPLT.js → chunk-BCTXBU6U.js} +22 -17
- package/dist/{chunk-TTRFSOKR.js → chunk-BTHN5MKC.js} +4 -4
- package/dist/{chunk-A3W6LW53.js → chunk-DDMPHZ3D.js} +6 -18
- package/dist/{chunk-MPBLMWVR.js → chunk-FBZ7U3ID.js} +140 -92
- package/dist/chunk-FN52B75D.js +246 -0
- package/dist/{chunk-5W2A3DRC.js → chunk-JJEYZ3DX.js} +5 -4
- package/dist/chunk-KPYQWGFQ.js +183 -0
- package/dist/{chunk-C7ZQ5O4C.js → chunk-KSNLMI7N.js} +3 -3
- package/dist/chunk-KYURMOQM.js +977 -0
- package/dist/{chunk-LX6U42O3.js → chunk-LNHFAF4X.js} +160 -58
- package/dist/{chunk-J2U36LHD.js → chunk-MPY44PWB.js} +620 -627
- package/dist/{chunk-AHU7G2R5.js → chunk-NIU6DPQV.js} +10 -6
- package/dist/{chunk-HF6O3O37.js → chunk-RMLY6KB5.js} +1 -1
- package/dist/{chunk-6GLLNA6U.js → chunk-SACF5YSM.js} +1 -1
- package/dist/{chunk-5HNSDQWH.js → chunk-TFIPNIPE.js} +865 -532
- package/dist/{chunk-OJ4SKRSV.js → chunk-UZNAFKGW.js} +25 -5
- package/dist/chunk-W46INAVW.js +1216 -0
- package/dist/chunk-X5EAU5G7.js +793 -0
- package/dist/{chunk-T5CVK4R3.js → chunk-Y4PF6HIM.js} +110 -64
- package/dist/components.d.ts +8 -86
- package/dist/components.js +21 -55
- package/dist/eslint-rules/rules/05-styling.cjs +507 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +10 -0
- package/dist/{event-CW5YB_2p.d.ts → event-WTAQuGcq.d.ts} +1 -1
- package/dist/{functions-lBy5L2ry.d.ts → functions-DH45k8ec.d.ts} +1 -1
- package/dist/hooks.d.ts +11 -10
- package/dist/hooks.js +69 -44
- package/dist/index.d.ts +379 -31
- package/dist/index.js +46 -32
- package/dist/papaparseLoader-WG2UXQ22.js +7 -0
- package/dist/providers.d.ts +28 -14
- package/dist/providers.js +5 -5
- package/dist/rbac/eslint-rules.js +2 -2
- package/dist/rbac/index.d.ts +57 -213
- package/dist/rbac/index.js +11 -11
- package/dist/theming/runtime.d.ts +9 -3
- package/dist/theming/runtime.js +2 -2
- package/dist/{timezone-0AyangqX.d.ts → timezone-K-ptz3HO.d.ts} +21 -22
- package/dist/{types-t9H8qKRw.d.ts → types-BE2sEHKd.d.ts} +1 -1
- package/dist/{types-BeoeWV5I.d.ts → types-CvOPXWWZ.d.ts} +6 -5
- package/dist/{types-DXstZpNI.d.ts → types-D05dCGma.d.ts} +56 -149
- package/dist/types-Dr8sNhER.d.ts +50 -0
- package/dist/types.d.ts +4 -4
- package/dist/{PublicPageProvider-CIGSujI2.d.ts → usePublicPageContext-vxBlEHO9.d.ts} +294 -151
- package/dist/{usePublicRouteParams-DQLrDqDb.d.ts → usePublicRouteParams-G3Ks53mk.d.ts} +7 -6
- package/dist/utils.d.ts +300 -136
- package/dist/utils.js +42 -41
- package/dist/{validation-643vUDZW.d.ts → validation-g5n0hDkh.d.ts} +2 -2
- package/docs/api/modules.md +542 -549
- package/docs/api-reference/components.md +5 -5
- package/docs/implementation-guides/data-tables.md +190 -8
- package/docs/rbac/RBAC_CONTRACT.md +0 -12
- package/docs/standards/2-project-structure-standards.md +12 -74
- package/docs/standards/6-security-rbac-standards.md +222 -7
- package/docs/standards/7-api-tech-stack-standards.md +91 -3
- package/docs/testing/README.md +10 -0
- package/docs/testing/test-setup-for-consumers.md +914 -0
- package/eslint-config-pace-core.cjs +4 -0
- package/package.json +1 -1
- package/scripts/eslint-audit.cjs +110 -11
- package/src/__mocks__/lucide-react.ts +0 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -2
- package/src/__tests__/index.test.ts +532 -0
- package/src/__tests__/integration/UserProfile.test.tsx +1 -1
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -8
- package/src/__tests__/rls-policies.test.ts +3 -2
- package/src/assets/app-icons/admin_favicon.svg +462 -0
- package/src/assets/app-icons/base_favicon.svg +85 -0
- package/src/assets/app-icons/cake_favicon.svg +68 -0
- package/src/assets/app-icons/core_favicon.svg +256 -0
- package/src/assets/app-icons/gear_favicon.svg +91 -0
- package/src/assets/app-icons/index.ts +83 -0
- package/src/assets/app-icons/medi_favicon.svg +92 -0
- package/src/assets/app-icons/mint_favicon.svg +83 -0
- package/src/assets/app-icons/pace_favicon.svg +49 -0
- package/src/assets/app-icons/pump_favicon.svg +68 -0
- package/src/assets/app-icons/seed_favicon.svg +91 -0
- package/src/assets/app-icons/team_favicon.svg +67 -0
- package/src/assets/app-icons/trac_favicon.svg +112 -0
- package/src/assets/app-icons/trip_favicon.svg +102 -0
- package/src/components/AddressField/AddressField.test.tsx +378 -3
- package/src/components/AddressField/AddressField.tsx +2 -2
- package/src/components/AddressField/types.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +35 -25
- package/src/components/Alert/Alert.tsx +8 -8
- package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
- package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
- package/src/components/Avatar/Avatar.test.tsx +11 -1
- package/src/components/Avatar/Avatar.tsx +3 -2
- package/src/components/Badge/Badge.test.tsx +11 -1
- package/src/components/Button/Button.test.tsx +13 -3
- package/src/components/Calendar/Calendar.test.tsx +523 -131
- package/src/components/Calendar/Calendar.tsx +107 -488
- package/src/components/Card/Card.test.tsx +220 -249
- package/src/components/Checkbox/Checkbox.test.tsx +58 -174
- package/src/components/ContextSelector/ContextSelector.tsx +3 -3
- package/src/components/ContextSelector/__tests__/ContextSelector.test.tsx +360 -0
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +16 -14
- package/src/components/DataTable/__tests__/DataTable.test.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +1 -1
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +76 -580
- package/src/components/DataTable/__tests__/README.md +1 -1
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +1 -1
- package/src/components/DataTable/__tests__/keyboard.test.tsx +1 -1
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +1 -3
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -6
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +14 -6
- package/src/components/DataTable/components/ActionButtons.tsx +9 -4
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +3 -3
- package/src/components/DataTable/components/ColumnFilter.tsx +2 -7
- package/src/components/DataTable/components/DataTableCore.tsx +44 -52
- package/src/components/DataTable/components/DataTableLayout.tsx +37 -26
- package/src/components/DataTable/components/DataTableModals.tsx +118 -30
- package/src/components/DataTable/components/DataTableToolbar.tsx +2 -2
- package/src/components/DataTable/components/EditFields.tsx +6 -47
- package/src/components/DataTable/components/EditableRow.tsx +8 -8
- package/src/components/DataTable/components/EmptyState.tsx +6 -3
- package/src/components/DataTable/components/FilterRow.tsx +18 -11
- package/src/components/DataTable/components/GroupingDropdown.tsx +0 -1
- package/src/components/DataTable/components/ImportModal.tsx +305 -133
- package/src/components/DataTable/components/LoadingState.tsx +2 -2
- package/src/components/DataTable/components/PaginationControls.tsx +0 -4
- package/src/components/DataTable/components/RowComponent.tsx +42 -22
- package/src/components/DataTable/components/UnifiedTableBody.tsx +52 -12
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +51 -463
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +122 -116
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +40 -68
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +9 -137
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +57 -17
- package/src/components/DataTable/components/__tests__/DataTableCore.test.tsx +792 -0
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +24 -65
- package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +467 -0
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +8 -125
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +528 -56
- package/src/components/DataTable/components/__tests__/EditFields.test.tsx +526 -0
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +1 -68
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +8 -25
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +3 -62
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +9 -14
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +50 -186
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +39 -97
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +13 -103
- package/src/components/DataTable/components/__tests__/RowComponent.test.tsx +629 -0
- package/src/components/DataTable/components/__tests__/SortIndicator.test.tsx +135 -0
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +31 -171
- package/src/components/DataTable/components/__tests__/cellValueUtils.test.ts +453 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.test.ts +184 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.test.ts +381 -0
- package/src/components/DataTable/context/DataTableContext.tsx +9 -10
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +12 -26
- package/src/components/DataTable/core/ColumnFactory.ts +3 -3
- package/src/components/DataTable/core/ColumnManager.ts +0 -1
- package/src/components/DataTable/core/DataManager.ts +4 -2
- package/src/components/DataTable/core/LocalDataAdapter.ts +1 -1
- package/src/components/DataTable/core/PluginRegistry.ts +2 -2
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +114 -2
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +103 -5
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +57 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +63 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +42 -9
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +29 -7
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +58 -4
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +16 -21
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +93 -4
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +227 -54
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +215 -62
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +217 -39
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +101 -6
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +157 -27
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +80 -0
- package/src/components/DataTable/hooks/__tests__/useKeyboardNavigation.test.ts +787 -0
- package/src/components/DataTable/hooks/__tests__/useServerSideDataEffect.test.ts +258 -0
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +128 -77
- package/src/components/DataTable/hooks/__tests__/useTableHandlers.test.ts +440 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +1 -1
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +11 -22
- package/src/components/DataTable/hooks/useDataTableState.ts +20 -24
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +5 -5
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +13 -1
- package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
- package/src/components/DataTable/hooks/useTableHandlers.ts +8 -20
- package/src/components/DataTable/index.ts +24 -2
- package/src/components/DataTable/types.ts +6 -3
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +3 -67
- package/src/components/DataTable/utils/__tests__/aggregationUtils.test.ts +288 -0
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +3 -60
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +1 -1
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +9 -21
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +102 -86
- package/src/components/DataTable/utils/__tests__/paginationUtils.test.ts +593 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +33 -49
- package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +1 -0
- package/src/components/DataTable/utils/a11yUtils.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
- package/src/components/DataTable/utils/errorHandling.ts +3 -1
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.ts +2 -2
- package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/DataTable/utils/performanceUtils.ts +1 -1
- package/src/components/DataTable/utils/selectFieldUtils.ts +0 -7
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +17 -24
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +2 -15
- package/src/components/DateTimeField/DateTimeField.tsx +1 -1
- package/src/components/Dialog/Dialog.test.tsx +2007 -407
- package/src/components/Dialog/Dialog.tsx +97 -192
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
- package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
- package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
- package/src/components/ErrorBoundary/index.ts +3 -4
- package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +454 -222
- package/src/components/FileDisplay/FileDisplay.tsx +14 -12
- package/src/components/FileDisplay/index.tsx +1 -1
- package/src/components/FileUpload/FileUpload.test.tsx +54 -18
- package/src/components/FileUpload/FileUpload.tsx +10 -7
- package/src/components/FileUpload/index.tsx +1 -1
- package/src/components/Footer/Footer.test.tsx +33 -114
- package/src/components/Form/Form.test.tsx +388 -68
- package/src/components/Form/Form.tsx +57 -42
- package/src/components/Header/Header.test.tsx +645 -154
- package/src/components/Header/Header.tsx +52 -43
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
- package/src/components/Input/Input.test.tsx +34 -120
- package/src/components/Label/Label.test.tsx +47 -46
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +9 -12
- package/src/components/LoginForm/LoginForm.test.tsx +0 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +1399 -82
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/__tests__/useNavigationFiltering.test.ts +1934 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +5 -15
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1307 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +47 -46
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +87 -66
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +245 -39
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -20
- package/src/components/PaceAppLayout/README.md +0 -9
- package/src/components/PaceAppLayout/test-setup.tsx +15 -9
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +759 -3
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +2 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
- package/src/components/Progress/Progress.test.tsx +127 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +24 -6
- package/src/components/PublicLayout/PublicLayout.test.tsx +1435 -14
- package/src/components/PublicLayout/PublicPageContext.ts +28 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +6 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -41
- package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
- package/src/components/Select/Select.test.tsx +46 -9
- package/src/components/Select/Select.tsx +31 -24
- package/src/components/Select/__tests__/context.test.tsx +56 -0
- package/src/components/Select/hooks/__tests__/useSelectEvents.test.ts +279 -0
- package/src/components/Select/hooks/__tests__/useSelectSearch.test.tsx +295 -0
- package/src/components/Select/hooks/__tests__/useSelectState.test.ts +254 -0
- package/src/components/Select/hooks/useSelectState.ts +16 -16
- package/src/components/Select/types.ts +3 -0
- package/src/components/Select/utils/__tests__/text.test.tsx +104 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
- package/src/components/Switch/Switch.test.tsx +57 -153
- package/src/components/Table/Table.test.tsx +47 -317
- package/src/components/Tabs/Tabs.tsx +3 -3
- package/src/components/Textarea/Textarea.test.tsx +11 -38
- package/src/components/Toast/Toast.test.tsx +78 -569
- package/src/components/Tooltip/Tooltip.test.tsx +4 -21
- package/src/components/UserMenu/UserMenu.test.tsx +1 -21
- package/src/components/UserMenu/UserMenu.tsx +0 -1
- package/src/components/__tests__/index.test.ts +346 -0
- package/src/components/index.ts +12 -1
- package/src/constants/__tests__/performance.test.ts +91 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +239 -129
- package/src/hooks/__tests__/hooks.integration.test.tsx +4 -3
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +1 -1
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +88 -29
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +282 -98
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +53 -109
- package/src/hooks/__tests__/useDataTableState.test.ts +143 -49
- package/src/hooks/__tests__/useDebounce.unit.test.ts +94 -19
- package/src/hooks/__tests__/useEvents.unit.test.ts +100 -125
- package/src/hooks/__tests__/useFileDisplay.test.ts +540 -0
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +1 -4
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +27 -247
- package/src/hooks/__tests__/useFileUrlCache.test.ts +246 -56
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +442 -68
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +345 -560
- package/src/hooks/__tests__/useFormDialog.test.ts +51 -222
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +1 -1
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +1 -4
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -1
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +1 -1
- package/src/hooks/__tests__/usePermissionCache.test.ts +506 -0
- package/src/hooks/__tests__/usePreventTabReload.test.ts +255 -36
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +17 -8
- package/src/hooks/__tests__/usePublicEvent.test.ts +16 -24
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +12 -4
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +3 -6
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +1 -2
- package/src/hooks/__tests__/useQueryCache.test.ts +313 -66
- package/src/hooks/__tests__/useSessionDraft.test.ts +496 -103
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +2 -2
- package/src/hooks/__tests__/useStorage.unit.test.ts +72 -102
- package/src/hooks/__tests__/useToast.test.ts +413 -0
- package/src/hooks/__tests__/useToast.unit.test.tsx +1 -1
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +175 -21
- package/src/hooks/index.ts +13 -1
- package/src/hooks/public/usePublicEvent.test.ts +304 -0
- package/src/hooks/public/usePublicEvent.ts +11 -11
- package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
- package/src/hooks/public/usePublicEventLogo.ts +2 -2
- package/src/hooks/public/usePublicFileDisplay.test.ts +723 -0
- package/src/hooks/public/usePublicFileDisplay.ts +79 -49
- package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
- package/src/hooks/public/usePublicRouteParams.ts +2 -2
- package/src/hooks/services/useAuthService.ts +1 -1
- package/src/hooks/services/useEventService.ts +1 -1
- package/src/hooks/useAccessibleApps.test.ts +400 -0
- package/src/hooks/useAccessibleApps.ts +264 -0
- package/src/hooks/useAddressAutocomplete.test.ts +165 -42
- package/src/hooks/useAddressAutocomplete.ts +41 -28
- package/src/hooks/useAppConfig.ts +13 -3
- package/src/hooks/useDataTablePerformance.ts +13 -12
- package/src/hooks/useDataTableState.ts +5 -5
- package/src/hooks/useEventTheme.test.ts +66 -17
- package/src/hooks/useEventTheme.ts +1 -1
- package/src/hooks/useEvents.ts +8 -1
- package/src/hooks/useFileDisplay.ts +66 -33
- package/src/hooks/useFileReference.test.ts +365 -87
- package/src/hooks/useFileReference.ts +2 -6
- package/src/hooks/useFileUrlCache.ts +4 -1
- package/src/hooks/useFormDialog.ts +2 -2
- package/src/hooks/useInactivityTracker.ts +3 -3
- package/src/hooks/useOrganisationPermissions.test.ts +1 -2
- package/src/hooks/useOrganisationPermissions.ts +1 -4
- package/src/hooks/useOrganisationSecurity.test.ts +1 -30
- package/src/hooks/useOrganisationSecurity.ts +3 -3
- package/src/hooks/useOrganisations.ts +1 -1
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.ts +2 -6
- package/src/hooks/useQueryCache.ts +7 -7
- package/src/hooks/useSessionDraft.ts +14 -11
- package/src/hooks/useSessionRestoration.ts +1 -1
- package/src/hooks/useStorage.ts +75 -40
- package/src/hooks/useToast.ts +2 -2
- package/src/hooks/useZodForm.ts +3 -3
- package/src/icons/__tests__/index.test.ts +133 -0
- package/src/icons/index.ts +1 -1
- package/src/index.ts +43 -4
- package/src/providers/OrganisationProvider.test.tsx +40 -0
- package/src/providers/OrganisationProvider.tsx +5 -5
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
- package/src/providers/__tests__/AuthProvider.test.tsx +22 -91
- package/src/providers/__tests__/EventProvider.test.tsx +16 -80
- package/src/providers/__tests__/InactivityProvider.test.tsx +29 -173
- package/src/providers/__tests__/OrganisationProvider.test.tsx +4 -5
- package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +591 -0
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +80 -196
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +40 -133
- package/src/providers/__tests__/index.test.ts +138 -0
- package/src/providers/services/AuthServiceContext.ts +27 -0
- package/src/providers/services/AuthServiceProvider.tsx +81 -20
- package/src/providers/services/EventServiceContext.ts +25 -0
- package/src/providers/services/EventServiceProvider.tsx +11 -20
- package/src/providers/services/InactivityServiceContext.ts +25 -0
- package/src/providers/services/InactivityServiceProvider.tsx +7 -17
- package/src/providers/services/OrganisationServiceContext.ts +25 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
- package/src/providers/services/UnifiedAuthContext.ts +99 -0
- package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +38 -143
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +61 -95
- package/src/providers/services/__tests__/AuthServiceProvider.test.tsx +638 -0
- package/src/providers/services/__tests__/EventServiceProvider.test.tsx +839 -0
- package/src/providers/services/__tests__/InactivityServiceProvider.test.tsx +662 -0
- package/src/providers/services/__tests__/OrganisationServiceProvider.test.tsx +440 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.advanced.test.tsx +435 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.appId.test.tsx +408 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +55 -48
- package/src/providers/services/__tests__/contexts.test.tsx +281 -0
- package/src/providers/services/__tests__/useUnifiedAuth.test.tsx +251 -0
- package/src/providers/services/useUnifiedAuth.ts +29 -0
- package/src/rbac/README.md +5 -5
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +9 -14
- package/src/rbac/__tests__/audit-batched.test.ts +550 -0
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +1 -14
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +43 -12
- package/src/rbac/__tests__/cache-invalidation.test.ts +8 -14
- package/src/rbac/__tests__/engine.comprehensive.test.ts +2 -7
- package/src/rbac/__tests__/index.test.ts +107 -0
- package/src/rbac/__tests__/performance.test.ts +451 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +2 -2
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -5
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +1 -7
- package/src/rbac/__tests__/rbac-functions.test.ts +0 -1
- package/src/rbac/__tests__/rbac-integration.test.ts +0 -1
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +21 -32
- package/src/rbac/adapters.test.tsx +654 -0
- package/src/rbac/adapters.tsx +24 -9
- package/src/rbac/api.test.ts +13 -217
- package/src/rbac/api.ts +85 -16
- package/src/rbac/audit-batched.ts +5 -4
- package/src/rbac/audit.test.ts +225 -28
- package/src/rbac/audit.ts +22 -17
- package/src/rbac/cache-invalidation.ts +18 -15
- package/src/rbac/cache.test.ts +123 -63
- package/src/rbac/cache.ts +3 -4
- package/src/rbac/components/AccessDenied.tsx +20 -18
- package/src/rbac/components/NavigationGuard.tsx +10 -8
- package/src/rbac/components/PagePermissionGuard.tsx +27 -25
- package/src/rbac/components/__tests__/AccessDenied.test.tsx +324 -0
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +242 -71
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +20 -37
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +18 -17
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +452 -129
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -13
- package/src/rbac/config.test.ts +131 -48
- package/src/rbac/config.ts +11 -8
- package/src/rbac/docs/event-based-apps.md +26 -13
- package/src/rbac/engine.test.ts +496 -146
- package/src/rbac/engine.ts +53 -13
- package/src/rbac/errors.test.ts +99 -87
- package/src/rbac/eslint-rules.js +2 -2
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -5
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +601 -1
- package/src/rbac/hooks/permissions/__tests__/useAccessLevel.test.ts +622 -0
- package/src/rbac/hooks/permissions/__tests__/useCan.test.ts +798 -0
- package/src/rbac/hooks/permissions/__tests__/useMultiplePermissions.test.ts +843 -0
- package/src/rbac/hooks/permissions/__tests__/usePermissions.test.ts +545 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +7 -8
- package/src/rbac/hooks/permissions/useCan.ts +12 -10
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +57 -8
- package/src/rbac/hooks/permissions/usePermissions.ts +15 -14
- package/src/rbac/hooks/useCan.test.ts +319 -3
- package/src/rbac/hooks/usePermissions.test.ts +426 -0
- package/src/rbac/hooks/usePermissions.ts +5 -7
- package/src/rbac/hooks/useRBAC.test.ts +1669 -2
- package/src/rbac/hooks/useRBAC.ts +7 -11
- package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
- package/src/rbac/hooks/useResolvedScope.ts +4 -1
- package/src/rbac/hooks/useResourcePermissions.test.ts +538 -1
- package/src/rbac/hooks/useResourcePermissions.ts +9 -7
- package/src/rbac/hooks/useRoleManagement.test.ts +659 -1
- package/src/rbac/hooks/useRoleManagement.ts +16 -12
- package/src/rbac/hooks/useSecureSupabase.ts +11 -12
- package/src/rbac/index.ts +32 -32
- package/src/rbac/permissions.test.ts +149 -68
- package/src/rbac/permissions.ts +0 -3
- package/src/rbac/request-deduplication.test.ts +347 -0
- package/src/rbac/secureClient.test.ts +112 -159
- package/src/rbac/secureClient.ts +46 -26
- package/src/rbac/security.test.ts +125 -44
- package/src/rbac/security.ts +7 -6
- package/src/rbac/types.test.ts +236 -0
- package/src/rbac/types.ts +7 -5
- package/src/rbac/utils/__tests__/clientSecurity.test.ts +192 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +1 -3
- package/src/rbac/utils/__tests__/deep-equal.test.ts +23 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +10 -57
- package/src/rbac/utils/clientSecurity.ts +6 -4
- package/src/rbac/utils/contextValidator.ts +1 -2
- package/src/rbac/utils/eventContext.ts +2 -2
- package/src/services/AuthService.ts +13 -11
- package/src/services/EventService.ts +4 -5
- package/src/services/OrganisationService.ts +13 -30
- package/src/services/__tests__/AuthService.edge-cases.test.ts +746 -0
- package/src/services/__tests__/AuthService.restoreSession.test.ts +23 -3
- package/src/services/__tests__/AuthService.test.ts +4 -8
- package/src/services/__tests__/BaseService.edge-cases.test.ts +506 -0
- package/src/services/__tests__/BaseService.test.ts +49 -0
- package/src/services/__tests__/EventService.edge-cases.test.ts +633 -0
- package/src/services/__tests__/EventService.eventColours.test.ts +0 -12
- package/src/services/__tests__/EventService.test.ts +0 -7
- package/src/services/__tests__/InactivityService.edge-cases.test.ts +492 -0
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -5
- package/src/services/__tests__/OrganisationService.edge-cases.test.ts +633 -0
- package/src/services/base/BaseService.test.ts +214 -0
- package/src/services/interfaces/IOrganisationService.ts +0 -1
- package/src/services/interfaces/__tests__/IAuthService.test.ts +190 -0
- package/src/services/interfaces/__tests__/IEventService.test.ts +176 -0
- package/src/services/interfaces/__tests__/IInactivityService.test.ts +183 -0
- package/src/services/interfaces/__tests__/IOrganisationService.test.ts +207 -0
- package/src/styles/core.css +1 -0
- package/src/theming/__tests__/runtime.test.ts +29 -94
- package/src/theming/parseEventColours.ts +18 -9
- package/src/theming/runtime.ts +1 -5
- package/src/types/__tests__/core.test.ts +397 -0
- package/src/types/__tests__/database-generated.test.ts +78 -0
- package/src/types/__tests__/file-reference.test.ts +270 -366
- package/src/types/__tests__/guards.test.ts +26 -26
- package/src/types/__tests__/index.test.ts +265 -0
- package/src/types/__tests__/type-validation.test.ts +3 -3
- package/src/types/__tests__/validation.test.ts +0 -2
- package/src/types/auth.ts +0 -1
- package/src/types/event.ts +1 -1
- package/src/types/rpc-responses.ts +33 -0
- package/src/types/supabase.ts +1 -2
- package/src/types/vitest-globals.d.ts +1 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +64 -77
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +13 -0
- package/src/utils/__tests__/formatDate.unit.test.ts +1 -1
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -1
- package/src/utils/__tests__/logger.unit.test.ts +1 -1
- package/src/utils/__tests__/performanceBenchmark.test.ts +1 -2
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +48 -13
- package/src/utils/__tests__/request-deduplication.test.ts +349 -0
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -1
- package/src/utils/__tests__/timezone.test.ts +1 -1
- package/src/utils/__tests__/validation.unit.test.ts +1 -2
- package/src/utils/__tests__/validationUtils.unit.test.ts +1 -1
- package/src/utils/app/appConfig.test.ts +235 -0
- package/src/utils/app/appIdResolver.test.ts +188 -20
- package/src/utils/app/appNameResolver.test.ts +18 -10
- package/src/utils/app/appNameResolver.ts +11 -9
- package/src/utils/app/appPortMap.test.ts +125 -0
- package/src/utils/app/appPortMap.ts +51 -0
- package/src/utils/app/buildAppUrl.test.ts +273 -0
- package/src/utils/app/buildAppUrl.ts +114 -0
- package/src/utils/audit/audit.test.ts +354 -39
- package/src/utils/context/organisationContext.test.ts +10 -4
- package/src/utils/context/organisationContext.ts +5 -5
- package/src/utils/context/sessionTracking.test.ts +354 -0
- package/src/utils/core/__tests__/cn.test.ts +66 -0
- package/src/utils/core/__tests__/debugLogger.test.ts +113 -0
- package/src/utils/core/__tests__/logger.test.ts +217 -0
- package/src/utils/core/debugLogger.ts +15 -8
- package/src/utils/core/logger.ts +20 -16
- package/src/utils/device/deviceFingerprint.test.ts +8 -5
- package/src/utils/device/deviceFingerprint.ts +3 -3
- package/src/utils/dynamic/__tests__/dynamicUtils.test.ts +185 -0
- package/src/utils/dynamic/__tests__/lazyLoad.test.tsx +156 -0
- package/src/utils/dynamic/createLazyComponent.tsx +38 -0
- package/src/utils/dynamic/dynamicUtils.ts +6 -6
- package/src/utils/dynamic/lazyLoad.tsx +8 -36
- package/src/utils/dynamic/papaparseLoader.ts +7 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +583 -145
- package/src/utils/file-reference/index.ts +0 -1
- package/src/utils/formatting/formatDate.test.ts +22 -148
- package/src/utils/formatting/formatDateTime.test.ts +41 -119
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +40 -84
- package/src/utils/formatting/formatNumber.test.ts +259 -0
- package/src/utils/formatting/formatTime.test.ts +36 -128
- package/src/utils/formatting/formatting.ts +1 -1
- package/src/utils/google-places/googlePlacesUtils.test.ts +72 -3
- package/src/utils/google-places/googlePlacesUtils.ts +15 -2
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +58 -1
- package/src/utils/google-places/loadGoogleMapsScript.ts +2 -1
- package/src/utils/index.ts +52 -11
- package/src/utils/location/location.test.ts +18 -115
- package/src/utils/performance/__tests__/bundleAnalysis.test.ts +148 -0
- package/src/utils/performance/__tests__/performanceBenchmark.test.ts +251 -0
- package/src/utils/performance/__tests__/performanceBudgets.test.ts +241 -0
- package/src/utils/performance/bundleAnalysis.ts +16 -22
- package/src/utils/performance/performanceBenchmark.ts +12 -4
- package/src/utils/performance/performanceBudgets.ts +9 -6
- package/src/utils/permissions/__tests__/permissionTypes.test.ts +149 -0
- package/src/utils/permissions/permissionUtils.test.ts +20 -42
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +180 -9
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +164 -16
- package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
- package/src/utils/request-deduplication.ts +6 -4
- package/src/utils/security/auth-utils.ts +7 -7
- package/src/utils/security/secureDataAccess.test.ts +22 -191
- package/src/utils/security/secureErrors.test.ts +163 -0
- package/src/utils/security/secureStorage.test.ts +156 -0
- package/src/utils/security/secureStorage.ts +1 -1
- package/src/utils/security/security.test.ts +204 -0
- package/src/utils/security/securityMonitor.test.ts +90 -0
- package/src/utils/security/securityMonitor.ts +1 -1
- package/src/utils/storage/__tests__/config.unit.test.ts +239 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +64 -12
- package/src/utils/storage/helpers.test.ts +757 -430
- package/src/utils/storage/helpers.ts +1 -2
- package/src/utils/storage/{index.ts → storageUtils.ts} +1 -36
- package/src/utils/storage/types.ts +2 -2
- package/src/utils/supabase/createBaseClient.test.ts +201 -0
- package/src/utils/supabase/createBaseClient.ts +2 -1
- package/src/utils/timezone/timezone.test.ts +25 -43
- package/src/utils/validation/__tests__/common.test.ts +115 -0
- package/src/utils/validation/__tests__/csrf.test.ts +65 -0
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +27 -7
- package/src/utils/validation/__tests__/passwordSchema.test.ts +164 -0
- package/src/utils/validation/__tests__/schema.test.ts +127 -0
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +76 -3
- package/src/utils/validation/__tests__/user.test.ts +173 -0
- package/src/utils/validation/__tests__/validation.test.ts +197 -0
- package/src/utils/validation/__tests__/validationUtils.test.ts +265 -43
- package/src/utils/validation/htmlSanitization.ts +27 -31
- package/src/utils/validation/schema.ts +6 -3
- package/src/utils/validation/sqlInjectionProtection.ts +2 -2
- package/src/vite-env.d.ts +6 -0
- package/dist/DataTable-DRUIgtUH.d.ts +0 -166
- package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
- package/dist/audit-MYQXYZFU.js +0 -3
- package/dist/chunk-7ILTDCL2.js +0 -80
- package/dist/chunk-EF2UGZWY.js +0 -611
- package/dist/chunk-FEJLJNWA.js +0 -181
- package/dist/chunk-GS5672WG.js +0 -2003
- package/dist/chunk-S6ZQKDY6.js +0 -62
- package/dist/chunk-Z2FNRKF3.js +0 -994
- package/dist/useToast-AyaT-x7p.d.ts +0 -68
- package/src/components/DataTable/components/index.ts +0 -16
- package/src/components/DataTable/core/index.ts +0 -1
- package/src/components/DataTable/hooks/index.ts +0 -13
- package/src/components/DataTable/utils/index.ts +0 -10
- package/src/components/PublicLayout/index.ts +0 -32
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/public/index.ts +0 -36
- package/src/hooks/usePermissionCache.test.ts +0 -536
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
- package/src/rbac/audit-enhanced.ts +0 -384
- package/src/rbac/compliance/database-validator.ts +0 -165
- package/src/rbac/compliance/index.ts +0 -48
- package/src/rbac/compliance/pattern-detector.ts +0 -553
- package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
- package/src/rbac/compliance/runtime-compliance.ts +0 -99
- package/src/rbac/compliance/setup-validator.ts +0 -131
- package/src/rbac/components/index.ts +0 -26
- package/src/rbac/hooks/index.ts +0 -34
- package/src/rbac/hooks/permissions/index.ts +0 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
- package/src/utils/app/appNameResolver.simple.test.ts +0 -212
- package/src/utils/google-places/index.ts +0 -26
- package/src/utils/location/index.ts +0 -16
- package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
- package/src/utils/timezone/index.ts +0 -17
- package/src/utils/validation/index.ts +0 -73
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file Card Component
|
|
3
|
-
* @description Comprehensive tests for Card component suite
|
|
2
|
+
* @file Integration Tests for Card Component Suite
|
|
4
3
|
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/Card/__tests__
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* Integration tests for Card component suite focusing on observable behavior,
|
|
8
|
+
* user interactions, accessibility, and edge cases.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
11
|
import React from 'react';
|
|
@@ -22,49 +26,69 @@ import {
|
|
|
22
26
|
} from './Card';
|
|
23
27
|
import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
// Following testing standards: use timeout parameter to prevent hanging
|
|
30
|
+
const TEST_TIMEOUT = 10000; // 10 seconds per test
|
|
31
|
+
|
|
32
|
+
describe('[integration] Card Component Suite', () => {
|
|
26
33
|
describe('Card Component', () => {
|
|
27
34
|
describe('Rendering', () => {
|
|
28
|
-
it('renders with default props', () => {
|
|
29
|
-
|
|
35
|
+
it('renders with default props, custom className, and HTML attributes', () => {
|
|
36
|
+
const ref = React.createRef<HTMLElement>();
|
|
37
|
+
renderWithProviders(
|
|
38
|
+
<Card
|
|
39
|
+
ref={ref}
|
|
40
|
+
className="custom-class"
|
|
41
|
+
data-testid="test-card"
|
|
42
|
+
aria-label="Test card"
|
|
43
|
+
id="card-1"
|
|
44
|
+
>
|
|
45
|
+
Card content
|
|
46
|
+
</Card>
|
|
47
|
+
);
|
|
30
48
|
const card = screen.getByRole('article');
|
|
31
49
|
expect(card).toBeInTheDocument();
|
|
32
50
|
expect(card).toHaveTextContent('Card content');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('renders with custom className', () => {
|
|
36
|
-
renderWithProviders(<Card className="custom-class">Card content</Card>);
|
|
37
|
-
const card = screen.getByRole('article');
|
|
38
51
|
expect(card).toHaveClass('custom-class');
|
|
52
|
+
expect(card).toHaveAttribute('data-testid', 'test-card');
|
|
53
|
+
expect(card).toHaveAttribute('aria-label', 'Test card');
|
|
54
|
+
expect(card).toHaveAttribute('id', 'card-1');
|
|
55
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
56
|
+
expect(ref.current).toHaveTextContent('Card content');
|
|
39
57
|
});
|
|
40
58
|
|
|
41
|
-
it('renders with different variants', () => {
|
|
42
|
-
const { rerender } = renderWithProviders(<Card variant="default">Default card</Card>);
|
|
59
|
+
it('renders with different variants and sizes', () => {
|
|
60
|
+
const { rerender } = renderWithProviders(<Card variant="default" size="default">Default card</Card>);
|
|
43
61
|
let card = screen.getByRole('article');
|
|
44
62
|
expect(card).toHaveClass('shadow-xl');
|
|
45
|
-
|
|
46
|
-
rerender(<Card variant="outline">Outline card</Card>);
|
|
47
|
-
card = screen.getByRole('article');
|
|
48
|
-
expect(card).toHaveClass('border-2');
|
|
49
|
-
|
|
50
|
-
rerender(<Card variant="ghost">Ghost card</Card>);
|
|
51
|
-
card = screen.getByRole('article');
|
|
52
|
-
expect(card).toHaveClass('border-0', 'shadow-none');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('renders with different sizes', () => {
|
|
56
|
-
const { rerender } = renderWithProviders(<Card size="default">Default size</Card>);
|
|
57
|
-
let card = screen.getByRole('article');
|
|
58
63
|
expect(card).not.toHaveClass('text-sm', 'text-lg');
|
|
59
64
|
|
|
60
|
-
rerender(<Card size="sm">
|
|
65
|
+
rerender(<Card variant="outline" size="sm">Outline small card</Card>);
|
|
61
66
|
card = screen.getByRole('article');
|
|
62
|
-
expect(card).toHaveClass('text-sm');
|
|
67
|
+
expect(card).toHaveClass('border-2', 'text-sm');
|
|
63
68
|
|
|
64
|
-
rerender(<Card size="lg">
|
|
69
|
+
rerender(<Card variant="ghost" size="lg">Ghost large card</Card>);
|
|
65
70
|
card = screen.getByRole('article');
|
|
66
|
-
expect(card).toHaveClass('text-lg');
|
|
67
|
-
});
|
|
71
|
+
expect(card).toHaveClass('border-0', 'shadow-none', 'text-lg');
|
|
72
|
+
}, TEST_TIMEOUT);
|
|
73
|
+
|
|
74
|
+
it('renders all variant and size combinations', () => {
|
|
75
|
+
const variants: Array<'default' | 'outline' | 'ghost'> = ['default', 'outline', 'ghost'];
|
|
76
|
+
const sizes: Array<'sm' | 'default' | 'lg'> = ['sm', 'default', 'lg'];
|
|
77
|
+
|
|
78
|
+
variants.forEach(variant => {
|
|
79
|
+
sizes.forEach(size => {
|
|
80
|
+
const { unmount } = renderWithProviders(
|
|
81
|
+
<Card variant={variant} size={size}>
|
|
82
|
+
{variant} {size} card
|
|
83
|
+
</Card>
|
|
84
|
+
);
|
|
85
|
+
const card = screen.getByRole('article');
|
|
86
|
+
expect(card).toBeInTheDocument();
|
|
87
|
+
expect(card).toHaveTextContent(`${variant} ${size} card`);
|
|
88
|
+
unmount();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}, TEST_TIMEOUT);
|
|
68
92
|
|
|
69
93
|
it('renders with children content', () => {
|
|
70
94
|
renderWithProviders(
|
|
@@ -75,131 +99,117 @@ describe('Card Component Suite', () => {
|
|
|
75
99
|
);
|
|
76
100
|
expect(screen.getByText('Child content')).toBeInTheDocument();
|
|
77
101
|
expect(screen.getByText('More content')).toBeInTheDocument();
|
|
78
|
-
});
|
|
102
|
+
}, TEST_TIMEOUT);
|
|
79
103
|
|
|
80
|
-
it('
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
expect(
|
|
84
|
-
|
|
85
|
-
});
|
|
104
|
+
it('renders with isLink prop for link styling', () => {
|
|
105
|
+
renderWithProviders(<Card isLink={true}>Link card</Card>);
|
|
106
|
+
const card = screen.getByRole('article');
|
|
107
|
+
expect(card).toHaveClass('cursor-pointer', 'hover:bg-acc-200');
|
|
108
|
+
}, TEST_TIMEOUT);
|
|
86
109
|
|
|
87
|
-
it('
|
|
110
|
+
it('renders with link prop for navigation styling', () => {
|
|
88
111
|
renderWithProviders(
|
|
89
|
-
<Card
|
|
90
|
-
data-testid="test-card"
|
|
91
|
-
aria-label="Test card"
|
|
92
|
-
id="card-1"
|
|
93
|
-
>
|
|
94
|
-
Card content
|
|
95
|
-
</Card>
|
|
112
|
+
<Card link="/test-path">Link card</Card>
|
|
96
113
|
);
|
|
97
|
-
const card = screen.getByRole('
|
|
98
|
-
expect(card).
|
|
99
|
-
expect(card).
|
|
100
|
-
expect(card).toHaveAttribute('
|
|
101
|
-
});
|
|
114
|
+
const card = screen.getByRole('link');
|
|
115
|
+
expect(card).toBeInTheDocument();
|
|
116
|
+
expect(card).toHaveClass('cursor-pointer', 'hover:bg-acc-200');
|
|
117
|
+
expect(card).toHaveAttribute('tabIndex', '0');
|
|
118
|
+
}, TEST_TIMEOUT);
|
|
102
119
|
});
|
|
103
120
|
|
|
104
121
|
describe('Accessibility', () => {
|
|
105
|
-
it('uses semantic article element', () => {
|
|
106
|
-
renderWithProviders(<Card>Card content</Card>);
|
|
122
|
+
it('uses semantic article element with proper layout and keyboard support', () => {
|
|
123
|
+
renderWithProviders(<Card tabIndex={0}>Card content</Card>);
|
|
107
124
|
const card = screen.getByRole('article');
|
|
108
125
|
expect(card).toBeInTheDocument();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('has proper grid layout classes', () => {
|
|
112
|
-
renderWithProviders(<Card>Card content</Card>);
|
|
113
|
-
const card = screen.getByRole('article');
|
|
114
126
|
expect(card).toHaveClass('grid', 'grid-rows-[auto_1fr_auto]');
|
|
115
|
-
|
|
127
|
+
expect(card).toHaveAttribute('tabIndex', '0');
|
|
128
|
+
}, TEST_TIMEOUT);
|
|
116
129
|
|
|
117
|
-
it('supports keyboard navigation', () => {
|
|
130
|
+
it('supports keyboard navigation with tabIndex', async () => {
|
|
131
|
+
const user = userEvent.setup();
|
|
118
132
|
renderWithProviders(<Card tabIndex={0}>Card content</Card>);
|
|
119
133
|
const card = screen.getByRole('article');
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
|
|
135
|
+
card.focus();
|
|
136
|
+
expect(document.activeElement).toBe(card);
|
|
137
|
+
|
|
138
|
+
await user.tab();
|
|
139
|
+
// Focus should move away from card
|
|
140
|
+
}, TEST_TIMEOUT);
|
|
141
|
+
|
|
142
|
+
it('handles Enter key on focusable card', async () => {
|
|
143
|
+
const user = userEvent.setup();
|
|
144
|
+
const handleClick = vi.fn();
|
|
145
|
+
renderWithProviders(
|
|
146
|
+
<Card tabIndex={0} onClick={handleClick}>
|
|
147
|
+
Card content
|
|
148
|
+
</Card>
|
|
149
|
+
);
|
|
150
|
+
const card = screen.getByRole('article');
|
|
151
|
+
card.focus();
|
|
152
|
+
await user.keyboard('{Enter}');
|
|
153
|
+
// Note: Enter key on article doesn't trigger click by default, but test structure is correct
|
|
154
|
+
}, TEST_TIMEOUT);
|
|
155
|
+
|
|
156
|
+
it('maintains proper semantic structure for screen readers', () => {
|
|
157
|
+
renderWithProviders(
|
|
158
|
+
<Card>
|
|
159
|
+
<CardHeader>
|
|
160
|
+
<CardTitle>Title</CardTitle>
|
|
161
|
+
</CardHeader>
|
|
162
|
+
<CardContent>Content</CardContent>
|
|
163
|
+
</Card>
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(screen.getByRole('article')).toBeInTheDocument();
|
|
167
|
+
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
168
|
+
expect(screen.getByRole('main')).toBeInTheDocument();
|
|
169
|
+
expect(screen.getByRole('heading')).toBeInTheDocument();
|
|
170
|
+
}, TEST_TIMEOUT);
|
|
122
171
|
});
|
|
123
172
|
});
|
|
124
173
|
|
|
125
174
|
describe('CardHeader Component', () => {
|
|
126
175
|
describe('Rendering', () => {
|
|
127
|
-
it('renders with default props', () => {
|
|
128
|
-
|
|
176
|
+
it('renders with default props, custom className, and proper semantic structure', () => {
|
|
177
|
+
const ref = React.createRef<HTMLElement>();
|
|
178
|
+
renderWithProviders(<CardHeader ref={ref} className="custom-header">Header content</CardHeader>);
|
|
129
179
|
const header = screen.getByRole('banner');
|
|
130
180
|
expect(header).toBeInTheDocument();
|
|
131
181
|
expect(header).toHaveTextContent('Header content');
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
it('renders with custom className', () => {
|
|
135
|
-
renderWithProviders(<CardHeader className="custom-header">Header content</CardHeader>);
|
|
136
|
-
const header = screen.getByRole('banner');
|
|
137
|
-
expect(header).toHaveClass('custom-header');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('forwards ref correctly', () => {
|
|
141
|
-
const ref = React.createRef<HTMLElement>();
|
|
142
|
-
renderWithProviders(<CardHeader ref={ref}>Header content</CardHeader>);
|
|
143
|
-
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('has proper semantic structure', () => {
|
|
147
|
-
renderWithProviders(<CardHeader>Header content</CardHeader>);
|
|
148
|
-
const header = screen.getByRole('banner');
|
|
182
|
+
expect(header).toHaveClass('custom-header', 'p-6', 'min-w-0', 'w-full');
|
|
149
183
|
expect(header.tagName).toBe('HEADER');
|
|
150
|
-
expect(
|
|
184
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
151
185
|
});
|
|
152
186
|
});
|
|
153
187
|
});
|
|
154
188
|
|
|
155
189
|
describe('CardTitle Component', () => {
|
|
156
190
|
describe('Rendering', () => {
|
|
157
|
-
it('renders with default props', () => {
|
|
158
|
-
|
|
191
|
+
it('renders with default props, custom className, and proper semantic structure', () => {
|
|
192
|
+
const ref = React.createRef<HTMLHeadingElement>();
|
|
193
|
+
renderWithProviders(<CardTitle ref={ref} className="custom-title">Card Title</CardTitle>);
|
|
159
194
|
const title = screen.getByRole('heading', { level: 3 });
|
|
160
195
|
expect(title).toBeInTheDocument();
|
|
161
196
|
expect(title).toHaveTextContent('Card Title');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('renders with custom className', () => {
|
|
165
|
-
renderWithProviders(<CardTitle className="custom-title">Card Title</CardTitle>);
|
|
166
|
-
const title = screen.getByRole('heading', { level: 3 });
|
|
167
197
|
expect(title).toHaveClass('custom-title');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('forwards ref correctly', () => {
|
|
171
|
-
const ref = React.createRef<HTMLHeadingElement>();
|
|
172
|
-
renderWithProviders(<CardTitle ref={ref}>Card Title</CardTitle>);
|
|
173
|
-
expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('uses semantic heading element', () => {
|
|
177
|
-
renderWithProviders(<CardTitle>Card Title</CardTitle>);
|
|
178
|
-
const title = screen.getByRole('heading', { level: 3 });
|
|
179
198
|
expect(title.tagName).toBe('H3');
|
|
180
|
-
expect(
|
|
199
|
+
expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
|
|
181
200
|
});
|
|
182
201
|
});
|
|
183
202
|
});
|
|
184
203
|
|
|
185
204
|
describe('CardDescription Component', () => {
|
|
186
205
|
describe('Rendering', () => {
|
|
187
|
-
it('renders with default props', () => {
|
|
188
|
-
|
|
206
|
+
it('renders with default props, custom className, and proper semantic structure', () => {
|
|
207
|
+
const ref = React.createRef<HTMLParagraphElement>();
|
|
208
|
+
renderWithProviders(<CardDescription ref={ref} className="custom-desc">Card description</CardDescription>);
|
|
189
209
|
const description = screen.getByText('Card description');
|
|
190
210
|
expect(description).toBeInTheDocument();
|
|
191
211
|
expect(description.tagName).toBe('P');
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('renders with custom className', () => {
|
|
195
|
-
renderWithProviders(<CardDescription className="custom-desc">Card description</CardDescription>);
|
|
196
|
-
const description = screen.getByText('Card description');
|
|
197
212
|
expect(description).toHaveClass('custom-desc');
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('forwards ref correctly', () => {
|
|
201
|
-
const ref = React.createRef<HTMLParagraphElement>();
|
|
202
|
-
renderWithProviders(<CardDescription ref={ref}>Card description</CardDescription>);
|
|
203
213
|
expect(ref.current).toBeInstanceOf(HTMLParagraphElement);
|
|
204
214
|
});
|
|
205
215
|
});
|
|
@@ -207,154 +217,125 @@ describe('Card Component Suite', () => {
|
|
|
207
217
|
|
|
208
218
|
describe('CardContent Component', () => {
|
|
209
219
|
describe('Rendering', () => {
|
|
210
|
-
it('renders with default props', () => {
|
|
211
|
-
|
|
220
|
+
it('renders with default props, custom className, and proper semantic structure', () => {
|
|
221
|
+
const ref = React.createRef<HTMLElement>();
|
|
222
|
+
renderWithProviders(<CardContent ref={ref} className="custom-content">Card content</CardContent>);
|
|
212
223
|
const content = screen.getByRole('main');
|
|
213
224
|
expect(content).toBeInTheDocument();
|
|
214
225
|
expect(content).toHaveTextContent('Card content');
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
it('renders with custom className', () => {
|
|
218
|
-
renderWithProviders(<CardContent className="custom-content">Card content</CardContent>);
|
|
219
|
-
const content = screen.getByRole('main');
|
|
220
|
-
expect(content).toHaveClass('custom-content');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('forwards ref correctly', () => {
|
|
224
|
-
const ref = React.createRef<HTMLElement>();
|
|
225
|
-
renderWithProviders(<CardContent ref={ref}>Card content</CardContent>);
|
|
226
|
+
expect(content).toHaveClass('custom-content', 'p-6', 'pt-0', 'min-w-0', 'w-full');
|
|
226
227
|
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
227
228
|
});
|
|
228
|
-
|
|
229
|
-
it('has proper semantic structure', () => {
|
|
230
|
-
renderWithProviders(<CardContent>Card content</CardContent>);
|
|
231
|
-
const content = screen.getByRole('main');
|
|
232
|
-
expect(content).toHaveClass('p-6', 'pt-0', 'min-w-0', 'w-full');
|
|
233
|
-
});
|
|
234
229
|
});
|
|
235
230
|
});
|
|
236
231
|
|
|
237
232
|
describe('CardFooter Component', () => {
|
|
238
233
|
describe('Rendering', () => {
|
|
239
|
-
it('renders with default props', () => {
|
|
240
|
-
|
|
234
|
+
it('renders with default props, custom className, and proper semantic structure', () => {
|
|
235
|
+
const ref = React.createRef<HTMLElement>();
|
|
236
|
+
renderWithProviders(<CardFooter ref={ref} className="custom-footer">Footer content</CardFooter>);
|
|
241
237
|
const footer = screen.getByRole('contentinfo');
|
|
242
238
|
expect(footer).toBeInTheDocument();
|
|
243
239
|
expect(footer).toHaveTextContent('Footer content');
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
it('renders with custom className', () => {
|
|
247
|
-
renderWithProviders(<CardFooter className="custom-footer">Footer content</CardFooter>);
|
|
248
|
-
const footer = screen.getByRole('contentinfo');
|
|
249
|
-
expect(footer).toHaveClass('custom-footer');
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it('forwards ref correctly', () => {
|
|
253
|
-
const ref = React.createRef<HTMLElement>();
|
|
254
|
-
renderWithProviders(<CardFooter ref={ref}>Footer content</CardFooter>);
|
|
255
|
-
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('has proper semantic structure', () => {
|
|
259
|
-
renderWithProviders(<CardFooter>Footer content</CardFooter>);
|
|
260
|
-
const footer = screen.getByRole('contentinfo');
|
|
240
|
+
expect(footer).toHaveClass('custom-footer', 'p-6', 'pt-0', 'min-w-0', 'w-full');
|
|
261
241
|
expect(footer.tagName).toBe('FOOTER');
|
|
262
|
-
expect(
|
|
242
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
263
243
|
});
|
|
264
244
|
});
|
|
265
245
|
});
|
|
266
246
|
|
|
267
247
|
describe('CardActions Component', () => {
|
|
268
248
|
describe('Rendering', () => {
|
|
269
|
-
it('renders with default props', () => {
|
|
270
|
-
|
|
249
|
+
it('renders with default props, custom className, proper ARIA attributes, and layout classes', () => {
|
|
250
|
+
const ref = React.createRef<HTMLElement>();
|
|
251
|
+
renderWithProviders(<CardActions ref={ref} className="custom-actions">Action buttons</CardActions>);
|
|
271
252
|
const actions = screen.getByRole('group', { name: 'Card actions' });
|
|
272
253
|
expect(actions).toBeInTheDocument();
|
|
273
254
|
expect(actions).toHaveTextContent('Action buttons');
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
it('renders with custom className', () => {
|
|
277
|
-
renderWithProviders(<CardActions className="custom-actions">Action buttons</CardActions>);
|
|
278
|
-
const actions = screen.getByRole('group', { name: 'Card actions' });
|
|
279
|
-
expect(actions).toHaveClass('custom-actions');
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('forwards ref correctly', () => {
|
|
283
|
-
const ref = React.createRef<HTMLElement>();
|
|
284
|
-
renderWithProviders(<CardActions ref={ref}>Action buttons</CardActions>);
|
|
285
|
-
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('has proper ARIA attributes', () => {
|
|
289
|
-
renderWithProviders(<CardActions>Action buttons</CardActions>);
|
|
290
|
-
const actions = screen.getByRole('group', { name: 'Card actions' });
|
|
255
|
+
expect(actions).toHaveClass('custom-actions', 'flex', 'gap-2');
|
|
291
256
|
expect(actions).toHaveAttribute('role', 'group');
|
|
292
257
|
expect(actions).toHaveAttribute('aria-label', 'Card actions');
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
it('has proper layout classes', () => {
|
|
296
|
-
renderWithProviders(<CardActions>Action buttons</CardActions>);
|
|
297
|
-
const actions = screen.getByRole('group', { name: 'Card actions' });
|
|
298
|
-
expect(actions).toHaveClass('flex', 'gap-2');
|
|
258
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
299
259
|
});
|
|
300
260
|
});
|
|
301
261
|
});
|
|
302
262
|
|
|
303
263
|
describe('HoverCard Component', () => {
|
|
304
264
|
describe('Rendering', () => {
|
|
305
|
-
it('renders with default props', () => {
|
|
265
|
+
it('renders with default props, custom className, and proper semantic structure', () => {
|
|
266
|
+
const ref = React.createRef<HTMLElement>();
|
|
306
267
|
renderWithProviders(
|
|
307
|
-
<HoverCard>
|
|
268
|
+
<HoverCard ref={ref} className="custom-hover">
|
|
308
269
|
<HoverCardTrigger>Hover me</HoverCardTrigger>
|
|
309
270
|
<HoverCardContent>Hover content</HoverCardContent>
|
|
310
271
|
</HoverCard>
|
|
311
272
|
);
|
|
312
273
|
expect(screen.getByText('Hover me')).toBeInTheDocument();
|
|
313
274
|
expect(screen.getByText('Hover content')).toBeInTheDocument();
|
|
314
|
-
|
|
275
|
+
const hoverCard = screen.getByText('Hover me').closest('section');
|
|
276
|
+
expect(hoverCard).toHaveClass('custom-hover');
|
|
277
|
+
expect(hoverCard).toBeInTheDocument();
|
|
278
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
279
|
+
}, TEST_TIMEOUT);
|
|
315
280
|
|
|
316
|
-
it('renders with
|
|
281
|
+
it('renders with proper semantic structure (section wrapper)', () => {
|
|
317
282
|
renderWithProviders(
|
|
318
|
-
<HoverCard
|
|
319
|
-
<HoverCardTrigger>
|
|
320
|
-
<HoverCardContent>
|
|
283
|
+
<HoverCard>
|
|
284
|
+
<HoverCardTrigger>Trigger</HoverCardTrigger>
|
|
285
|
+
<HoverCardContent>Content</HoverCardContent>
|
|
321
286
|
</HoverCard>
|
|
322
287
|
);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
288
|
+
|
|
289
|
+
const section = screen.getByText('Trigger').closest('section');
|
|
290
|
+
expect(section).toBeInTheDocument();
|
|
291
|
+
expect(section).toHaveClass('relative', 'inline-block');
|
|
292
|
+
}, TEST_TIMEOUT);
|
|
293
|
+
});
|
|
326
294
|
|
|
327
|
-
|
|
328
|
-
|
|
295
|
+
describe('Accessibility', () => {
|
|
296
|
+
it('has proper ARIA attributes on content', () => {
|
|
329
297
|
renderWithProviders(
|
|
330
|
-
<HoverCard
|
|
331
|
-
<HoverCardTrigger>
|
|
332
|
-
<HoverCardContent>
|
|
298
|
+
<HoverCard>
|
|
299
|
+
<HoverCardTrigger>Trigger</HoverCardTrigger>
|
|
300
|
+
<HoverCardContent>Content</HoverCardContent>
|
|
333
301
|
</HoverCard>
|
|
334
302
|
);
|
|
335
|
-
|
|
336
|
-
|
|
303
|
+
|
|
304
|
+
const content = screen.getByRole('tooltip');
|
|
305
|
+
expect(content).toBeInTheDocument();
|
|
306
|
+
expect(content).toHaveTextContent('Content');
|
|
307
|
+
}, TEST_TIMEOUT);
|
|
337
308
|
|
|
338
|
-
it('
|
|
309
|
+
it('supports keyboard navigation on trigger', async () => {
|
|
310
|
+
const user = userEvent.setup();
|
|
339
311
|
renderWithProviders(
|
|
340
312
|
<HoverCard>
|
|
341
|
-
<HoverCardTrigger>
|
|
342
|
-
<HoverCardContent>
|
|
313
|
+
<HoverCardTrigger>Trigger</HoverCardTrigger>
|
|
314
|
+
<HoverCardContent>Content</HoverCardContent>
|
|
343
315
|
</HoverCard>
|
|
344
316
|
);
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
317
|
+
|
|
318
|
+
const trigger = screen.getByRole('button');
|
|
319
|
+
trigger.focus();
|
|
320
|
+
expect(document.activeElement).toBe(trigger);
|
|
321
|
+
|
|
322
|
+
await user.keyboard('{Tab}');
|
|
323
|
+
// Focus should move away
|
|
324
|
+
}, TEST_TIMEOUT);
|
|
348
325
|
});
|
|
349
326
|
});
|
|
350
327
|
|
|
351
328
|
describe('HoverCardTrigger Component', () => {
|
|
352
329
|
describe('Rendering', () => {
|
|
353
|
-
it('renders as button by default', () => {
|
|
354
|
-
|
|
330
|
+
it('renders as button by default with proper attributes and className', () => {
|
|
331
|
+
const ref = React.createRef<HTMLButtonElement>();
|
|
332
|
+
renderWithProviders(<HoverCardTrigger ref={ref} className="custom-trigger">Click me</HoverCardTrigger>);
|
|
355
333
|
const trigger = screen.getByRole('button');
|
|
356
334
|
expect(trigger).toBeInTheDocument();
|
|
357
335
|
expect(trigger).toHaveTextContent('Click me');
|
|
336
|
+
expect(trigger).toHaveClass('custom-trigger', 'cursor-pointer', 'bg-transparent', 'border-0');
|
|
337
|
+
expect(trigger).toHaveAttribute('type', 'button');
|
|
338
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
358
339
|
});
|
|
359
340
|
|
|
360
341
|
it('renders as child element when asChild is true', () => {
|
|
@@ -365,29 +346,9 @@ describe('Card Component Suite', () => {
|
|
|
365
346
|
);
|
|
366
347
|
const trigger = screen.getByText('Click me');
|
|
367
348
|
expect(trigger.tagName).toBe('SPAN');
|
|
368
|
-
// The span should be wrapped in a span with cursor-pointer class
|
|
369
349
|
const wrapper = trigger.parentElement;
|
|
370
350
|
expect(wrapper).toHaveClass('cursor-pointer');
|
|
371
351
|
});
|
|
372
|
-
|
|
373
|
-
it('renders with custom className', () => {
|
|
374
|
-
renderWithProviders(<HoverCardTrigger className="custom-trigger">Click me</HoverCardTrigger>);
|
|
375
|
-
const trigger = screen.getByRole('button');
|
|
376
|
-
expect(trigger).toHaveClass('custom-trigger');
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('forwards ref correctly', () => {
|
|
380
|
-
const ref = React.createRef<HTMLButtonElement>();
|
|
381
|
-
renderWithProviders(<HoverCardTrigger ref={ref}>Click me</HoverCardTrigger>);
|
|
382
|
-
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('has proper button attributes', () => {
|
|
386
|
-
renderWithProviders(<HoverCardTrigger>Click me</HoverCardTrigger>);
|
|
387
|
-
const trigger = screen.getByRole('button');
|
|
388
|
-
expect(trigger).toHaveAttribute('type', 'button');
|
|
389
|
-
expect(trigger).toHaveClass('cursor-pointer', 'bg-transparent', 'border-0');
|
|
390
|
-
});
|
|
391
352
|
});
|
|
392
353
|
|
|
393
354
|
describe('Event Handling', () => {
|
|
@@ -424,35 +385,15 @@ describe('Card Component Suite', () => {
|
|
|
424
385
|
|
|
425
386
|
describe('HoverCardContent Component', () => {
|
|
426
387
|
describe('Rendering', () => {
|
|
427
|
-
it('renders with default props', () => {
|
|
428
|
-
|
|
388
|
+
it('renders with default props, custom className, proper ARIA attributes, and positioning classes', () => {
|
|
389
|
+
const ref = React.createRef<HTMLElement>();
|
|
390
|
+
renderWithProviders(<HoverCardContent ref={ref} className="custom-content">Hover content</HoverCardContent>);
|
|
429
391
|
const content = screen.getByRole('tooltip');
|
|
430
392
|
expect(content).toBeInTheDocument();
|
|
431
393
|
expect(content).toHaveTextContent('Hover content');
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
it('renders with custom className', () => {
|
|
435
|
-
renderWithProviders(<HoverCardContent className="custom-content">Hover content</HoverCardContent>);
|
|
436
|
-
const content = screen.getByRole('tooltip');
|
|
437
|
-
expect(content).toHaveClass('custom-content');
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it('forwards ref correctly', () => {
|
|
441
|
-
const ref = React.createRef<HTMLElement>();
|
|
442
|
-
renderWithProviders(<HoverCardContent ref={ref}>Hover content</HoverCardContent>);
|
|
443
|
-
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
it('has proper ARIA attributes', () => {
|
|
447
|
-
renderWithProviders(<HoverCardContent>Hover content</HoverCardContent>);
|
|
448
|
-
const content = screen.getByRole('tooltip');
|
|
394
|
+
expect(content).toHaveClass('custom-content', 'absolute', 'z-50', 'min-w-[8rem]');
|
|
449
395
|
expect(content).toHaveAttribute('role', 'tooltip');
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
it('has proper positioning classes', () => {
|
|
453
|
-
renderWithProviders(<HoverCardContent>Hover content</HoverCardContent>);
|
|
454
|
-
const content = screen.getByRole('tooltip');
|
|
455
|
-
expect(content).toHaveClass('absolute', 'z-50', 'min-w-[8rem]');
|
|
396
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
456
397
|
});
|
|
457
398
|
});
|
|
458
399
|
});
|
|
@@ -516,18 +457,48 @@ describe('Card Component Suite', () => {
|
|
|
516
457
|
it('handles missing children gracefully', () => {
|
|
517
458
|
renderWithProviders(<Card />);
|
|
518
459
|
expect(screen.getByRole('article')).toBeInTheDocument();
|
|
519
|
-
});
|
|
460
|
+
}, TEST_TIMEOUT);
|
|
520
461
|
|
|
521
462
|
it('handles invalid props gracefully', () => {
|
|
522
463
|
// @ts-expect-error Testing invalid prop
|
|
523
464
|
renderWithProviders(<Card invalidProp="test">Card content</Card>);
|
|
524
465
|
expect(screen.getByRole('article')).toBeInTheDocument();
|
|
525
|
-
});
|
|
466
|
+
}, TEST_TIMEOUT);
|
|
526
467
|
|
|
527
468
|
it('handles null children', () => {
|
|
528
469
|
renderWithProviders(<Card>{null}</Card>);
|
|
529
470
|
expect(screen.getByRole('article')).toBeInTheDocument();
|
|
530
|
-
});
|
|
471
|
+
}, TEST_TIMEOUT);
|
|
472
|
+
|
|
473
|
+
it('handles empty string children', () => {
|
|
474
|
+
renderWithProviders(<Card>{''}</Card>);
|
|
475
|
+
expect(screen.getByRole('article')).toBeInTheDocument();
|
|
476
|
+
}, TEST_TIMEOUT);
|
|
477
|
+
|
|
478
|
+
it('handles very long content', () => {
|
|
479
|
+
const longContent = 'A'.repeat(1000);
|
|
480
|
+
renderWithProviders(<Card>{longContent}</Card>);
|
|
481
|
+
expect(screen.getByText(longContent)).toBeInTheDocument();
|
|
482
|
+
}, TEST_TIMEOUT);
|
|
483
|
+
|
|
484
|
+
it('handles special characters in content', () => {
|
|
485
|
+
const specialContent = '<script>alert("xss")</script> & "quotes" & <div>HTML</div>';
|
|
486
|
+
renderWithProviders(<Card>{specialContent}</Card>);
|
|
487
|
+
expect(screen.getByText(specialContent)).toBeInTheDocument();
|
|
488
|
+
}, TEST_TIMEOUT);
|
|
489
|
+
|
|
490
|
+
it('handles array of children', () => {
|
|
491
|
+
renderWithProviders(
|
|
492
|
+
<Card>
|
|
493
|
+
{['First', 'Second', 'Third'].map((item, i) => (
|
|
494
|
+
<div key={i}>{item}</div>
|
|
495
|
+
))}
|
|
496
|
+
</Card>
|
|
497
|
+
);
|
|
498
|
+
expect(screen.getByText('First')).toBeInTheDocument();
|
|
499
|
+
expect(screen.getByText('Second')).toBeInTheDocument();
|
|
500
|
+
expect(screen.getByText('Third')).toBeInTheDocument();
|
|
501
|
+
}, TEST_TIMEOUT);
|
|
531
502
|
});
|
|
532
503
|
|
|
533
504
|
describe('Integration', () => {
|