@jmruthers/pace-core 0.6.9 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/audit-tool/00-dependencies.cjs +46 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
- package/audit-tool/audits/02-project-structure.cjs +74 -2
- package/audit-tool/audits/03-architecture.cjs +220 -20
- package/audit-tool/audits/04-code-quality.cjs +95 -3
- package/audit-tool/audits/05-styling.cjs +19 -7
- package/audit-tool/audits/06-security-rbac.cjs +214 -25
- package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
- package/audit-tool/audits/08-testing-documentation.cjs +11 -3
- package/audit-tool/audits/09-operations.cjs +19 -7
- package/audit-tool/index.cjs +22 -11
- package/audit-tool/utils/report-utils.cjs +4 -0
- package/cursor-rules/01-pace-core-compliance.mdc +1 -0
- package/cursor-rules/02-project-structure.mdc +3 -26
- package/cursor-rules/03-architecture.mdc +3 -1
- package/cursor-rules/04-code-quality.mdc +1 -0
- package/cursor-rules/05-styling.mdc +120 -8
- package/cursor-rules/06-security-rbac.mdc +126 -2
- package/cursor-rules/07-api-tech-stack.mdc +1 -0
- package/cursor-rules/08-testing-documentation.mdc +1 -0
- package/cursor-rules/09-operations.mdc +1 -0
- package/dist/DataTable-EFYP2QLE.js +16 -0
- package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
- package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
- package/dist/api-BZR2CYXL.js +5 -0
- package/dist/api-result-USV1Czr-.d.ts +51 -0
- 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-HI2DHUVU.js +4 -0
- package/dist/auth-JvdRVaud.d.ts +49 -0
- package/dist/chunk-2DL2WSOE.js +327 -0
- package/dist/chunk-2OEVOGGR.js +9598 -0
- package/dist/chunk-44CNXN4P.js +15 -0
- package/dist/chunk-4R3T5ENU.js +2943 -0
- package/dist/chunk-7A6IMHH2.js +2321 -0
- package/dist/chunk-BTHN5MKC.js +121 -0
- package/dist/chunk-CU2BU2MQ.js +2 -0
- package/dist/chunk-D6BMFMQZ.js +200 -0
- package/dist/chunk-DDMPHZ3D.js +58 -0
- package/dist/chunk-ENLXB7GP.js +721 -0
- package/dist/chunk-J2KQK6DG.js +2159 -0
- package/dist/chunk-KJXRL3XE.js +6434 -0
- package/dist/chunk-L5LFKKLJ.js +61 -0
- package/dist/chunk-PCSHBLPB.js +811 -0
- package/dist/chunk-QRYSEPHB.js +429 -0
- package/dist/chunk-RMLY6KB5.js +187 -0
- package/dist/chunk-SACF5YSM.js +31 -0
- package/dist/chunk-UZNAFKGW.js +125 -0
- package/dist/chunk-V7FTM2LU.js +1080 -0
- package/dist/chunk-WY6Y7KC3.js +264 -0
- package/dist/chunk-XOJME5T7.js +407 -0
- package/dist/chunk-XPFVT3GN.js +492 -0
- package/dist/chunk-YFTFFJIV.js +529 -0
- package/dist/chunk-YYTWKVHO.js +1334 -0
- package/dist/components.d.ts +12 -89
- package/dist/components.js +23 -55
- package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
- package/dist/eslint-rules/index.cjs +3 -0
- package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
- package/dist/eslint-rules/rules/05-styling.cjs +507 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
- package/dist/event-BfCox3N2.d.ts +265 -0
- package/dist/file-reference-DU1hcawx.d.ts +164 -0
- package/dist/functions-DH45k8ec.d.ts +208 -0
- package/dist/hooks.d.ts +28 -14
- package/dist/hooks.js +90 -56
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +392 -155
- package/dist/index.js +337 -347
- package/dist/pagination-BW1mqywp.d.ts +201 -0
- package/dist/papaparseLoader-WG2UXQ22.js +7 -0
- package/dist/providers.d.ts +29 -14
- package/dist/providers.js +7 -5
- package/dist/rbac/eslint-rules.js +2 -2
- package/dist/rbac/index.d.ts +180 -351
- package/dist/rbac/index.js +13 -11
- package/dist/theming/runtime.d.ts +28 -5
- package/dist/theming/runtime.js +2 -2
- package/dist/timezone-BTWWXKVY.d.ts +696 -0
- package/dist/types-BE2sEHKd.d.ts +55 -0
- package/dist/types-CvOPXWWZ.d.ts +111 -0
- package/dist/types-Dr8sNhER.d.ts +50 -0
- package/dist/types.d.ts +20 -13
- package/dist/types.js +1 -0
- package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
- package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
- package/dist/utils.d.ts +338 -156
- package/dist/utils.js +78 -60
- package/dist/validation-g5n0hDkh.d.ts +177 -0
- package/docs/api/modules.md +1226 -1094
- package/docs/api-reference/components.md +5 -5
- package/docs/api-reference/rpc-functions.md +12 -3
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/getting-started/cursor-rules.md +17 -20
- package/docs/getting-started/dependencies.md +1 -1
- package/docs/getting-started/setup.md +235 -0
- package/docs/implementation-guides/authentication.md +27 -0
- package/docs/implementation-guides/data-tables.md +365 -10
- package/docs/migration/ApiResult-migration.md +25 -0
- package/docs/rbac/RBAC_CONTRACT.md +0 -12
- package/docs/rbac/api-reference.md +33 -31
- package/docs/standards/0-standards-overview.md +50 -15
- package/docs/standards/1-pace-core-compliance-standards.md +62 -57
- package/docs/standards/2-project-structure-standards.md +45 -90
- package/docs/standards/3-architecture-standards.md +41 -1
- package/docs/standards/4-code-quality-standards.md +26 -6
- package/docs/standards/5-styling-standards.md +35 -1
- package/docs/standards/6-security-rbac-standards.md +288 -7
- package/docs/standards/7-api-tech-stack-standards.md +116 -17
- package/docs/standards/8-testing-documentation-standards.md +31 -0
- package/docs/standards/9-operations-standards.md +19 -0
- package/docs/standards/README.md +20 -201
- package/docs/testing/README.md +10 -0
- package/docs/testing/test-setup-for-consumers.md +916 -0
- package/docs/troubleshooting/common-issues.md +17 -1
- package/docs/troubleshooting/organisation-context-setup.md +8 -0
- package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
- package/eslint-config-pace-core.cjs +24 -0
- package/package.json +14 -20
- package/scripts/build-docs.js +180 -0
- package/scripts/setup.cjs +536 -0
- package/scripts/validate.cjs +480 -0
- package/src/__mocks__/lucide-react.ts +0 -2
- package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
- package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
- package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
- package/src/__tests__/helpers/test-providers.test.tsx +99 -0
- package/src/__tests__/helpers/test-providers.tsx +37 -39
- package/src/__tests__/helpers/test-utils.test.tsx +447 -0
- package/src/__tests__/helpers/timer-utils.test.ts +371 -0
- 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.test.ts +304 -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 +379 -4
- package/src/components/AddressField/AddressField.tsx +239 -213
- 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/Button/Button.tsx +1 -1
- package/src/components/Calendar/Calendar.test.tsx +523 -131
- package/src/components/Calendar/Calendar.tsx +107 -488
- package/src/components/Card/Card.test.tsx +384 -258
- package/src/components/Card/Card.tsx +19 -10
- package/src/components/Checkbox/Checkbox.test.tsx +58 -174
- package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
- package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
- package/src/components/ContextSelector/ContextSelector.tsx +66 -280
- package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
- package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
- package/src/components/DataTable/AUDIT_REPORT.md +59 -44
- package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
- package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
- package/src/components/DataTable/DataTable.export.test.tsx +705 -0
- package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
- package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
- package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
- package/src/components/DataTable/DataTable.test.tsx +787 -416
- package/src/components/DataTable/DataTable.tsx +14 -14
- package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
- package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
- package/src/components/DataTable/DataTableCore.test.tsx +970 -0
- package/src/components/DataTable/README.md +155 -0
- package/src/components/DataTable/TESTING.md +101 -0
- package/src/components/DataTable/a11y.basic.test.tsx +788 -0
- package/src/components/DataTable/components/DataTableCore.tsx +126 -894
- package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
- package/src/components/DataTable/components/ImportModal.tsx +82 -408
- package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
- package/src/components/DataTable/context/DataTableContext.tsx +13 -13
- package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
- package/src/components/DataTable/core/ColumnFactory.ts +3 -3
- package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
- package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
- package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
- package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
- package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
- package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
- package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
- package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
- package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
- package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
- package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
- package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
- package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
- package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
- package/src/components/DataTable/index.ts +28 -5
- package/src/components/DataTable/keyboard.test.tsx +734 -0
- package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
- package/src/components/DataTable/pagination.modes.test.tsx +728 -0
- package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
- package/src/components/DataTable/styles.test.ts +379 -0
- package/src/components/DataTable/styles.ts +0 -1
- package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
- package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
- package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
- package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
- package/src/components/DataTable/test-utils.ts +94 -0
- package/src/components/DataTable/types/actions.ts +71 -0
- package/src/components/DataTable/types/base.ts +39 -0
- package/src/components/DataTable/types/columns.ts +125 -0
- package/src/components/DataTable/types/export.ts +32 -0
- package/src/components/DataTable/types/features.ts +81 -0
- package/src/components/DataTable/types/hierarchical.ts +44 -0
- package/src/components/DataTable/types/index.ts +43 -0
- package/src/components/DataTable/types/pagination.ts +85 -0
- package/src/components/DataTable/types/performance.ts +47 -0
- package/src/components/DataTable/types/props.ts +62 -0
- package/src/components/DataTable/types/rbac.ts +45 -0
- package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
- package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
- package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
- package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
- package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
- package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
- package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
- package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
- package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
- package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
- package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
- package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
- package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
- package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
- package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
- package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
- package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
- package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
- package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
- package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
- package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
- package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
- package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
- package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
- package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
- package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
- package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
- package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
- package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
- package/src/components/DataTable/utils/a11yUtils.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
- package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
- package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/csvParse.test.ts +74 -0
- package/src/components/DataTable/utils/csvParse.ts +65 -0
- package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
- package/src/components/DataTable/utils/errorHandling.ts +3 -1
- package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
- package/src/components/DataTable/utils/flexibleImport.ts +3 -186
- package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
- package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
- package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
- package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
- package/src/components/DataTable/utils/importDateParser.ts +114 -0
- package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
- package/src/components/DataTable/utils/importValueParser.ts +91 -0
- package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
- package/src/components/DataTable/utils/paginationUtils.ts +7 -4
- package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
- package/src/components/DataTable/utils/performanceUtils.ts +1 -1
- package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
- package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
- package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
- package/src/components/DateTimeField/DateTimeField.tsx +1 -1
- package/src/components/Dialog/Dialog.test-utils.ts +49 -0
- package/src/components/Dialog/Dialog.test.tsx +2865 -458
- package/src/components/Dialog/Dialog.tsx +183 -986
- package/src/components/Dialog/dialogLock.test.ts +238 -0
- package/src/components/Dialog/dialogLock.ts +98 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
- package/src/components/Dialog/useDialogDimensions.ts +140 -0
- package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
- package/src/components/Dialog/useDialogLifecycle.ts +135 -0
- package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
- package/src/components/Dialog/useDialogPersistence.ts +357 -0
- 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 +479 -247
- package/src/components/FileDisplay/FileDisplay.tsx +29 -659
- package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
- package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
- package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
- package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
- package/src/components/FileDisplay/fallbackUtils.ts +44 -0
- package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
- package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
- package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
- package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
- package/src/components/FileDisplay/index.tsx +1 -1
- package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
- package/src/components/FileDisplay/useFileDisplay.ts +515 -0
- package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
- package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
- package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
- package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
- package/src/components/FileUpload/FileUpload.test.tsx +69 -27
- package/src/components/FileUpload/FileUpload.tsx +112 -527
- package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
- package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
- package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
- package/src/components/FileUpload/index.tsx +1 -1
- package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
- package/src/components/FileUpload/useFileUploadManager.ts +454 -0
- package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
- package/src/components/FileUpload/useResolvedAppId.ts +77 -0
- package/src/components/Footer/Footer.test.tsx +15 -382
- package/src/components/Footer/Footer.tsx +8 -125
- package/src/components/Form/Form.test.tsx +425 -88
- package/src/components/Form/Form.tsx +91 -299
- package/src/components/Form/useFormPersistence.ts +257 -0
- package/src/components/Header/Header.test.tsx +653 -163
- package/src/components/Header/Header.tsx +62 -44
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
- package/src/components/Input/Input.test.tsx +34 -120
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.test.tsx +46 -45
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
- package/src/components/LoginForm/LoginForm.test.tsx +0 -1
- package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
- package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
- package/src/components/NavigationMenu/index.ts +6 -1
- package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
- package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
- package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
- package/src/components/PaceAppLayout/README.md +0 -9
- package/src/components/PaceAppLayout/test-setup.tsx +15 -9
- package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
- package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
- package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
- package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
- package/src/components/Progress/Progress.test.tsx +127 -1
- package/src/components/Progress/Progress.tsx +1 -2
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
- package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
- package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
- package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
- package/src/components/PublicLayout/PublicPageContext.ts +28 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
- package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
- package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
- package/src/components/Select/Select.test.tsx +45 -8
- package/src/components/Select/Select.tsx +57 -40
- package/src/components/Select/context.test.tsx +56 -0
- package/src/components/Select/text.test.tsx +104 -0
- package/src/components/Select/text.ts +26 -0
- package/src/components/Select/types.ts +3 -0
- package/src/components/Select/useSelectEvents.test.ts +279 -0
- package/src/components/Select/useSelectEvents.ts +87 -0
- package/src/components/Select/useSelectSearch.test.tsx +295 -0
- package/src/components/Select/useSelectSearch.ts +91 -0
- package/src/components/Select/useSelectState.test.ts +268 -0
- package/src/components/Select/useSelectState.ts +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 +395 -317
- package/src/components/Tabs/Tabs.test.tsx +270 -0
- package/src/components/Tabs/Tabs.tsx +4 -4
- package/src/components/Textarea/Textarea.test.tsx +11 -38
- package/src/components/Toast/Toast.test.tsx +425 -496
- 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/index.test.ts +346 -0
- package/src/components/index.ts +12 -1
- package/src/constants/performance.test.ts +91 -0
- package/src/hooks/ServiceHooks.test.tsx +725 -0
- package/src/hooks/hooks.integration.test.tsx +608 -0
- package/src/hooks/index.ts +18 -3
- package/src/hooks/index.unit.test.ts +220 -0
- 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/usePublicRouteParams.test.ts +595 -0
- package/src/hooks/public/usePublicRouteParams.ts +2 -2
- package/src/hooks/services/useAuth.ts +9 -7
- 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 +170 -47
- package/src/hooks/useAddressAutocomplete.ts +109 -81
- package/src/hooks/useApiFetch.unit.test.ts +111 -0
- package/src/hooks/useAppConfig.ts +13 -3
- package/src/hooks/useAppConfig.unit.test.ts +712 -0
- package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
- package/src/hooks/useDataTablePerformance.ts +111 -130
- package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
- package/src/hooks/useDataTableState.test.ts +170 -0
- package/src/hooks/useDataTableState.ts +5 -5
- package/src/hooks/useDebounce.unit.test.ts +157 -0
- package/src/hooks/useEventTheme.test.ts +70 -18
- package/src/hooks/useEventTheme.ts +50 -22
- package/src/hooks/useEvents.ts +49 -2
- package/src/hooks/useEvents.unit.test.ts +227 -0
- package/src/hooks/useFileReference.test.ts +388 -107
- package/src/hooks/useFileReference.ts +184 -179
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useFileUrl.unit.test.ts +686 -0
- package/src/hooks/useFileUrlCache.test.ts +319 -0
- package/src/hooks/useFileUrlCache.ts +5 -2
- package/src/hooks/useFocusManagement.unit.test.ts +604 -0
- package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
- package/src/hooks/useFormDialog.test.ts +307 -0
- package/src/hooks/useFormDialog.ts +2 -2
- package/src/hooks/useInactivityTracker.ts +141 -134
- package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
- package/src/hooks/useIsMobile.unit.test.ts +317 -0
- package/src/hooks/useIsPrint.ts +62 -0
- package/src/hooks/useIsPrint.unit.test.ts +545 -0
- package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
- package/src/hooks/useOrganisationPermissions.test.ts +1 -2
- package/src/hooks/useOrganisationPermissions.ts +1 -4
- package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
- package/src/hooks/useOrganisationSecurity.test.ts +4 -33
- package/src/hooks/useOrganisationSecurity.ts +192 -203
- package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
- package/src/hooks/useOrganisations.ts +1 -1
- package/src/hooks/useOrganisations.unit.test.ts +369 -0
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
- package/src/hooks/usePermissionCache.test.ts +298 -329
- package/src/hooks/usePermissionCache.ts +277 -276
- package/src/hooks/usePreventTabReload.test.ts +307 -0
- package/src/hooks/usePublicEvent.simple.test.ts +794 -0
- package/src/hooks/usePublicEvent.test.ts +670 -0
- package/src/hooks/usePublicEvent.unit.test.ts +638 -0
- package/src/hooks/usePublicFileDisplay.test.ts +948 -0
- package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
- package/src/hooks/useQueryCache.test.ts +391 -0
- package/src/hooks/useQueryCache.ts +7 -9
- package/src/hooks/useRBAC.unit.test.ts +253 -0
- package/src/hooks/useSessionDraft.test.ts +556 -0
- package/src/hooks/useSessionDraft.ts +14 -11
- package/src/hooks/useSessionRestoration.ts +1 -1
- package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
- package/src/hooks/useStorage.ts +94 -54
- package/src/hooks/useStorage.unit.test.ts +684 -0
- package/src/hooks/useToast.test.ts +413 -0
- package/src/hooks/useToast.ts +2 -2
- package/src/hooks/useToast.unit.test.tsx +481 -0
- package/src/hooks/useZodForm.ts +3 -3
- package/src/hooks/useZodForm.unit.test.tsx +191 -0
- package/src/icons/index.test.ts +133 -0
- package/src/icons/index.ts +3 -1
- package/src/index.test.ts +528 -0
- package/src/index.ts +56 -9
- package/src/providers/AuthProvider.test.tsx +218 -0
- package/src/providers/EventProvider.test.tsx +487 -0
- package/src/providers/InactivityProvider.test-helper.tsx +40 -0
- package/src/providers/InactivityProvider.test.tsx +421 -0
- package/src/providers/ProviderLifecycle.test.tsx +308 -0
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
- package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
- package/src/providers/index.test.ts +138 -0
- package/src/providers/services/AuthServiceContext.ts +27 -0
- package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
- package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
- package/src/providers/services/AuthServiceProvider.tsx +81 -20
- package/src/providers/services/EventServiceContext.ts +25 -0
- package/src/providers/services/EventServiceProvider.test.tsx +839 -0
- package/src/providers/services/EventServiceProvider.tsx +11 -20
- package/src/providers/services/InactivityServiceContext.ts +25 -0
- package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
- package/src/providers/services/InactivityServiceProvider.tsx +7 -17
- package/src/providers/services/OrganisationServiceContext.ts +25 -0
- package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
- package/src/providers/services/UnifiedAuthContext.ts +102 -0
- package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
- package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
- package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
- package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
- package/src/providers/services/contexts.test.tsx +281 -0
- package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
- package/src/providers/services/useUnifiedAuth.ts +29 -0
- package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
- package/src/providers/useInactivity.test-helper.ts +27 -0
- package/src/rbac/README.md +5 -5
- package/src/rbac/adapters.comprehensive.test.tsx +429 -0
- package/src/rbac/adapters.test.tsx +654 -0
- package/src/rbac/adapters.tsx +53 -38
- package/src/rbac/api.test.ts +986 -259
- package/src/rbac/api.ts +260 -216
- package/src/rbac/audit-batched.test.ts +550 -0
- package/src/rbac/audit-batched.ts +5 -4
- package/src/rbac/audit.test.ts +225 -28
- package/src/rbac/audit.ts +26 -18
- package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
- package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
- package/src/rbac/cache-invalidation.test.ts +715 -0
- 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.test.tsx +324 -0
- package/src/rbac/components/AccessDenied.tsx +20 -18
- package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
- package/src/rbac/components/NavigationGuard.tsx +10 -8
- package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
- package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
- package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
- package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
- package/src/rbac/components/PagePermissionGuard.tsx +188 -381
- package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
- package/src/rbac/config.test.ts +131 -48
- package/src/rbac/config.ts +69 -26
- package/src/rbac/docs/event-based-apps.md +26 -13
- package/src/rbac/engine.comprehensive.test.ts +808 -0
- package/src/rbac/engine.test.ts +974 -130
- package/src/rbac/engine.ts +53 -13
- package/src/rbac/errors.test.ts +99 -87
- package/src/rbac/errors.ts +89 -55
- package/src/rbac/eslint-rules.js +2 -2
- package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
- package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
- package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
- package/src/rbac/hooks/permissions/useCan.ts +173 -253
- package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
- package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
- package/src/rbac/hooks/useCan.test.ts +348 -32
- package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
- package/src/rbac/hooks/usePageGuardScope.ts +117 -0
- package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
- package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
- package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
- package/src/rbac/hooks/usePermissions.test.ts +459 -33
- package/src/rbac/hooks/usePermissions.ts +5 -7
- package/src/rbac/hooks/useRBAC.test.ts +1784 -21
- package/src/rbac/hooks/useRBAC.ts +148 -88
- package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
- package/src/rbac/hooks/useResolvedScope.ts +4 -1
- package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
- package/src/rbac/hooks/useResourcePermissions.ts +76 -140
- package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
- package/src/rbac/hooks/useRoleManagement.ts +158 -586
- package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
- package/src/rbac/hooks/useSecureSupabase.ts +21 -14
- package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
- package/src/rbac/index.test.ts +107 -0
- package/src/rbac/index.ts +32 -32
- package/src/rbac/performance.test.ts +451 -0
- package/src/rbac/permissions.test.ts +149 -68
- package/src/rbac/permissions.ts +0 -3
- package/src/rbac/rbac-core.test.tsx +276 -0
- package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
- package/src/rbac/rbac-engine-simplified.test.ts +252 -0
- package/src/rbac/rbac-functions.test.ts +703 -0
- package/src/rbac/rbac-integration.test.ts +523 -0
- package/src/rbac/rbac-role-isolation.test.ts +456 -0
- package/src/rbac/request-deduplication.test.ts +352 -0
- package/src/rbac/request-deduplication.ts +5 -4
- package/src/rbac/scenarios.user-role.test.tsx +271 -0
- package/src/rbac/secureClient.test.ts +499 -115
- package/src/rbac/secureClient.ts +54 -28
- package/src/rbac/security.test.ts +448 -44
- package/src/rbac/security.ts +7 -6
- package/src/rbac/types/roleManagement.ts +66 -0
- package/src/rbac/types.test.ts +236 -0
- package/src/rbac/types.ts +7 -5
- package/src/rbac/utils/clientSecurity.test.ts +192 -0
- package/src/rbac/utils/clientSecurity.ts +6 -4
- package/src/rbac/utils/contextValidator.test.ts +126 -0
- package/src/rbac/utils/contextValidator.ts +6 -3
- package/src/rbac/utils/deep-equal.test.ts +76 -0
- package/src/rbac/utils/eventContext.test.ts +401 -0
- package/src/rbac/utils/eventContext.ts +38 -34
- package/src/rbac/utils/fetchPermissionMap.ts +13 -0
- package/src/rbac/utils/permissionMapHelpers.ts +34 -0
- package/src/rbac/utils/roleManagementRpc.ts +303 -0
- package/src/services/AuthService.edge-cases.test.ts +746 -0
- package/src/services/AuthService.restoreSession.test.ts +59 -0
- package/src/services/AuthService.test.ts +1362 -0
- package/src/services/AuthService.ts +197 -216
- package/src/services/BaseService.edge-cases.test.ts +506 -0
- package/src/services/BaseService.test.ts +363 -0
- package/src/services/EventService.edge-cases.test.ts +636 -0
- package/src/services/EventService.eventColours.test.ts +64 -0
- package/src/services/EventService.test.ts +1250 -0
- package/src/services/EventService.ts +244 -315
- package/src/services/InactivityService.edge-cases.test.ts +492 -0
- package/src/services/InactivityService.lifecycle.test.ts +406 -0
- package/src/services/InactivityService.test.ts +829 -0
- package/src/services/InactivityService.ts +172 -213
- package/src/services/OrganisationService.edge-cases.test.ts +633 -0
- package/src/services/OrganisationService.pagination.test.ts +409 -0
- package/src/services/OrganisationService.test.ts +1579 -0
- package/src/services/OrganisationService.ts +186 -257
- package/src/services/base/BaseService.test.ts +214 -0
- package/src/services/interfaces/IAuthService.test.ts +184 -0
- package/src/services/interfaces/IAuthService.ts +10 -9
- package/src/services/interfaces/IEventService.test.ts +176 -0
- package/src/services/interfaces/IInactivityService.test.ts +183 -0
- package/src/services/interfaces/IOrganisationService.test.ts +207 -0
- package/src/services/interfaces/IOrganisationService.ts +0 -1
- package/src/styles/core.css +244 -12
- package/src/theming/parseEventColours.test.ts +321 -0
- package/src/theming/parseEventColours.ts +18 -9
- package/src/theming/runtime.test.ts +495 -0
- package/src/theming/runtime.ts +72 -7
- package/src/types/api-result.ts +53 -0
- package/src/types/auth.ts +0 -1
- package/src/types/core.test.ts +397 -0
- package/src/types/database-generated.test.ts +78 -0
- package/src/types/database.generated.ts +45 -10
- package/src/types/event.ts +39 -19
- package/src/types/file-reference.test.ts +351 -0
- package/src/types/file-reference.ts +37 -12
- package/src/types/guards.test.ts +246 -0
- package/src/types/index.test.ts +265 -0
- package/src/types/index.ts +3 -0
- package/src/types/organisation.roles.test.ts +55 -0
- package/src/types/organisation.test.ts +1105 -0
- package/src/types/organisation.ts +15 -15
- package/src/types/rpc-responses.ts +33 -0
- package/src/types/supabase.ts +14 -6
- package/src/types/theme.test.ts +830 -0
- package/src/types/type-validation.test.ts +526 -0
- package/src/types/validation.test.ts +729 -0
- package/src/types/vitest-globals.d.ts +1 -1
- package/src/utils/app/appConfig.test.ts +235 -0
- package/src/utils/app/appIdResolver.test.ts +252 -57
- package/src/utils/app/appIdResolver.ts +31 -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/appConfig.unit.test.ts +55 -0
- package/src/utils/audit/audit.test.ts +354 -39
- package/src/utils/audit.unit.test.ts +69 -0
- package/src/utils/auth-utils.unit.test.ts +69 -0
- package/src/utils/bundleAnalysis.unit.test.ts +326 -0
- package/src/utils/cn.unit.test.ts +34 -0
- package/src/utils/context/organisationContext.test.ts +115 -95
- package/src/utils/context/organisationContext.ts +32 -43
- package/src/utils/context/sessionTracking.test.ts +354 -0
- package/src/utils/core/cn.test.ts +66 -0
- package/src/utils/core/debugLogger.test.ts +113 -0
- package/src/utils/core/debugLogger.ts +15 -8
- package/src/utils/core/logger.test.ts +217 -0
- package/src/utils/core/logger.ts +20 -16
- package/src/utils/core/mergeRefs.ts +24 -0
- package/src/utils/debugLogger.test.ts +417 -0
- package/src/utils/device/deviceFingerprint.test.ts +8 -5
- package/src/utils/device/deviceFingerprint.ts +3 -3
- package/src/utils/deviceFingerprint.unit.test.ts +818 -0
- package/src/utils/dynamic/createLazyComponent.tsx +46 -0
- package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
- package/src/utils/dynamic/dynamicUtils.ts +6 -6
- package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
- package/src/utils/dynamic/lazyLoad.tsx +8 -36
- package/src/utils/dynamic/papaparseLoader.ts +7 -0
- package/src/utils/dynamicUtils.unit.test.ts +331 -0
- package/src/utils/file-reference/file-reference.test.ts +1238 -0
- package/src/utils/file-reference/index.ts +330 -348
- package/src/utils/formatDate.unit.test.ts +109 -0
- 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 +41 -85
- 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/formatting.unit.test.ts +99 -0
- package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
- package/src/utils/google-places/googlePlacesUtils.ts +67 -86
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
- package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
- package/src/utils/index.ts +52 -11
- package/src/utils/index.unit.test.ts +251 -0
- package/src/utils/lazyLoad.unit.test.tsx +319 -0
- package/src/utils/location/location.test.ts +19 -116
- package/src/utils/logger.unit.test.ts +398 -0
- package/src/utils/organisationContext.unit.test.ts +180 -0
- package/src/utils/performance/bundleAnalysis.test.ts +148 -0
- package/src/utils/performance/bundleAnalysis.ts +16 -22
- package/src/utils/performance/performanceBenchmark.test.ts +251 -0
- package/src/utils/performance/performanceBenchmark.ts +12 -4
- package/src/utils/performance/performanceBudgets.test.ts +241 -0
- package/src/utils/performance/performanceBudgets.ts +9 -6
- package/src/utils/performanceBenchmark.test.ts +174 -0
- package/src/utils/performanceBudgets.unit.test.ts +288 -0
- package/src/utils/permissionTypes.unit.test.ts +250 -0
- package/src/utils/permissionUtils.unit.test.ts +362 -0
- package/src/utils/permissions/permissionTypes.test.ts +149 -0
- package/src/utils/permissions/permissionUtils.test.ts +20 -42
- package/src/utils/persistence/keyDerivation.test.ts +306 -0
- package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
- package/src/utils/request-deduplication.test.ts +349 -0
- package/src/utils/request-deduplication.ts +6 -4
- package/src/utils/sanitization.unit.test.ts +346 -0
- package/src/utils/schemaUtils.unit.test.ts +441 -0
- package/src/utils/secureDataAccess.unit.test.ts +334 -0
- package/src/utils/secureErrors.unit.test.ts +390 -0
- package/src/utils/secureStorage.unit.test.ts +289 -0
- package/src/utils/security/auth-utils.ts +38 -27
- package/src/utils/security/secureDataAccess.test.ts +22 -191
- package/src/utils/security/secureDataAccess.ts +241 -281
- 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 +212 -0
- package/src/utils/security/security.ts +15 -18
- package/src/utils/security/securityMonitor.test.ts +90 -0
- package/src/utils/security/securityMonitor.ts +1 -1
- package/src/utils/security.unit.test.ts +155 -0
- package/src/utils/securityMonitor.unit.test.ts +276 -0
- package/src/utils/sessionTracking.unit.test.ts +218 -0
- package/src/utils/storage/config.unit.test.ts +239 -0
- package/src/utils/storage/helpers.test.ts +769 -456
- package/src/utils/storage/helpers.ts +174 -253
- package/src/utils/storage/index.unit.test.ts +68 -0
- package/src/utils/storage/storageUtils.ts +32 -0
- package/src/utils/storage/types.ts +9 -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 +26 -44
- package/src/utils/timezone.test.ts +345 -0
- package/src/utils/validation/common.test.ts +115 -0
- package/src/utils/validation/csrf.test.ts +198 -0
- package/src/utils/validation/csrf.ts +42 -41
- package/src/utils/validation/htmlSanitization.ts +27 -31
- package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
- package/src/utils/validation/passwordSchema.test.ts +164 -0
- package/src/utils/validation/schema.test.ts +127 -0
- package/src/utils/validation/schema.ts +6 -3
- package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
- package/src/utils/validation/sqlInjectionProtection.ts +2 -2
- package/src/utils/validation/user.test.ts +173 -0
- package/src/utils/validation/validation.test.ts +197 -0
- package/src/utils/validation/validationUtils.test.ts +294 -0
- package/src/utils/validation.unit.test.ts +307 -0
- package/src/utils/validationUtils.unit.test.ts +558 -0
- package/src/vite-env.d.ts +6 -0
- package/dist/AuthService-DmfO5rGS.d.ts +0 -524
- package/dist/DataTable-DRUIgtUH.d.ts +0 -166
- package/dist/DataTable-SOAFXIWY.js +0 -15
- package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
- package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
- package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
- package/dist/api-7P7DI652.js +0 -4
- package/dist/audit-MYQXYZFU.js +0 -3
- package/dist/auth-BZOJqrdd.d.ts +0 -49
- package/dist/chunk-4DDCYDQ3.js +0 -544
- package/dist/chunk-5HNSDQWH.js +0 -5046
- package/dist/chunk-5W2A3DRC.js +0 -164
- package/dist/chunk-6GLLNA6U.js +0 -31
- package/dist/chunk-7ILTDCL2.js +0 -80
- package/dist/chunk-A3W6LW53.js +0 -70
- package/dist/chunk-AHU7G2R5.js +0 -423
- package/dist/chunk-C7ZQ5O4C.js +0 -481
- package/dist/chunk-EF2UGZWY.js +0 -611
- package/dist/chunk-FEJLJNWA.js +0 -181
- package/dist/chunk-FYHN4DD5.js +0 -415
- package/dist/chunk-GS5672WG.js +0 -2003
- package/dist/chunk-HF6O3O37.js +0 -187
- package/dist/chunk-J2U36LHD.js +0 -8517
- package/dist/chunk-LX6U42O3.js +0 -2177
- package/dist/chunk-MPBLMWVR.js +0 -2161
- package/dist/chunk-OJ4SKRSV.js +0 -105
- package/dist/chunk-S6ZQKDY6.js +0 -62
- package/dist/chunk-S7DKJPLT.js +0 -699
- package/dist/chunk-T5CVK4R3.js +0 -2816
- package/dist/chunk-TTRFSOKR.js +0 -121
- package/dist/chunk-Z2FNRKF3.js +0 -994
- package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
- package/dist/event-CW5YB_2p.d.ts +0 -239
- package/dist/file-reference-BavO2eQj.d.ts +0 -148
- package/dist/functions-lBy5L2ry.d.ts +0 -208
- package/dist/timezone-0AyangqX.d.ts +0 -697
- package/dist/types-BeoeWV5I.d.ts +0 -110
- package/dist/types-DXstZpNI.d.ts +0 -614
- package/dist/types-t9H8qKRw.d.ts +0 -55
- package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
- package/dist/useToast-AyaT-x7p.d.ts +0 -68
- package/dist/validation-643vUDZW.d.ts +0 -177
- package/scripts/build-docs-incremental.js +0 -179
- package/scripts/eslint-audit.cjs +0 -123
- package/scripts/generate-docs.js +0 -157
- package/scripts/install-cursor-rules.cjs +0 -255
- package/scripts/install-eslint-config.cjs +0 -349
- package/scripts/setup-build-cache.js +0 -73
- package/scripts/validate-pre-publish.js +0 -145
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
- package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
- package/src/__tests__/hooks/usePermissions.test.ts +0 -268
- package/src/__tests__/integration/UserProfile.test.tsx +0 -124
- package/src/__tests__/public-recipe-view.test.ts +0 -228
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
- package/src/__tests__/rls-policies.test.ts +0 -471
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
- package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
- package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
- package/src/components/DataTable/__tests__/README.md +0 -145
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
- package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
- package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
- package/src/components/DataTable/__tests__/styles.test.ts +0 -382
- package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
- package/src/components/DataTable/__tests__/test-utils.ts +0 -94
- package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
- package/src/components/DataTable/components/ActionButtons.tsx +0 -190
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
- package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
- package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
- package/src/components/DataTable/components/DataTableModals.tsx +0 -245
- package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
- package/src/components/DataTable/components/EditFields.tsx +0 -327
- package/src/components/DataTable/components/EditableRow.tsx +0 -462
- package/src/components/DataTable/components/EmptyState.tsx +0 -79
- package/src/components/DataTable/components/FilterRow.tsx +0 -141
- package/src/components/DataTable/components/LoadingState.tsx +0 -17
- package/src/components/DataTable/components/PaginationControls.tsx +0 -289
- package/src/components/DataTable/components/RowComponent.tsx +0 -403
- package/src/components/DataTable/components/SortIndicator.tsx +0 -50
- package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
- package/src/components/DataTable/components/cellValueUtils.ts +0 -40
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
- package/src/components/DataTable/components/index.ts +0 -16
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
- 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/LocalDataAdapter.ts +0 -274
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -312
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
- package/src/components/DataTable/core/index.ts +0 -1
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
- package/src/components/DataTable/hooks/index.ts +0 -13
- package/src/components/DataTable/types.ts +0 -761
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
- package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
- package/src/components/DataTable/utils/index.ts +0 -10
- package/src/components/PublicLayout/index.ts +0 -32
- package/src/components/Select/hooks/useSelectEvents.ts +0 -87
- package/src/components/Select/hooks/useSelectSearch.ts +0 -91
- package/src/components/Select/hooks/useSelectState.ts +0 -104
- package/src/components/Select/utils/text.ts +0 -26
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
- package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
- package/src/hooks/__tests__/index.unit.test.ts +0 -220
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
- package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
- package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
- package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
- package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
- package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
- package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
- package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
- package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
- package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
- package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
- package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
- package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
- package/src/hooks/public/index.ts +0 -36
- package/src/hooks/public/usePublicFileDisplay.ts +0 -504
- package/src/hooks/useFileDisplay.ts +0 -715
- package/src/providers/OrganisationProvider.tsx +0 -92
- package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
- package/src/providers/__tests__/EventProvider.test.tsx +0 -551
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
- package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
- package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
- package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
- package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
- package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
- package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
- package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
- 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/__tests__/NavigationGuard.test.tsx +0 -975
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
- package/src/rbac/components/index.ts +0 -26
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
- 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__/contextValidator.test.ts +0 -128
- package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
- package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
- package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
- package/src/services/__tests__/AuthService.test.ts +0 -1332
- package/src/services/__tests__/BaseService.test.ts +0 -314
- package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
- package/src/services/__tests__/EventService.test.ts +0 -1025
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
- package/src/services/__tests__/InactivityService.test.ts +0 -654
- package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
- package/src/services/__tests__/OrganisationService.test.ts +0 -1176
- package/src/theming/__tests__/parseEventColours.test.ts +0 -321
- package/src/theming/__tests__/runtime.test.ts +0 -569
- package/src/types/__tests__/file-reference.test.ts +0 -447
- package/src/types/__tests__/guards.test.ts +0 -246
- package/src/types/__tests__/organisation.roles.test.ts +0 -55
- package/src/types/__tests__/organisation.test.ts +0 -1133
- package/src/types/__tests__/theme.test.ts +0 -830
- package/src/types/__tests__/type-validation.test.ts +0 -526
- package/src/types/__tests__/validation.test.ts +0 -731
- package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
- package/src/utils/__tests__/audit.unit.test.ts +0 -69
- package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
- package/src/utils/__tests__/cn.unit.test.ts +0 -34
- package/src/utils/__tests__/debugLogger.test.ts +0 -417
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
- package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
- package/src/utils/__tests__/formatting.unit.test.ts +0 -99
- package/src/utils/__tests__/index.unit.test.ts +0 -251
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
- package/src/utils/__tests__/logger.unit.test.ts +0 -398
- package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
- package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
- package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
- package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
- package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
- package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
- package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
- package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
- package/src/utils/__tests__/security.unit.test.ts +0 -149
- package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
- package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
- package/src/utils/__tests__/timezone.test.ts +0 -345
- package/src/utils/__tests__/validation.unit.test.ts +0 -308
- package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
- package/src/utils/app/appNameResolver.simple.test.ts +0 -212
- package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
- package/src/utils/google-places/index.ts +0 -26
- package/src/utils/location/index.ts +0 -16
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
- package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
- package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
- package/src/utils/storage/index.ts +0 -67
- package/src/utils/timezone/index.ts +0 -17
- package/src/utils/validation/__tests__/csrf.test.ts +0 -105
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
- package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
- package/src/utils/validation/index.ts +0 -73
- /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
- /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
- /package/src/providers/{__tests__/README.md → README.md} +0 -0
- /package/src/types/{__tests__/README.md → README.md} +0 -0
|
@@ -1,1093 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Import Modal Component Tests
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module Components/DataTable/Components/__tests__
|
|
5
|
-
* @since 0.4.0
|
|
6
|
-
*
|
|
7
|
-
* Comprehensive test suite for ImportModal component following testing guidelines.
|
|
8
|
-
* Tests cover all major functionality, edge cases, and user interactions.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import React from 'react';
|
|
12
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
13
|
-
import userEvent from '@testing-library/user-event';
|
|
14
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
-
import { ImportModal } from '../ImportModal';
|
|
16
|
-
|
|
17
|
-
// Helper function to wait for dialog to be accessible
|
|
18
|
-
// Native dialog elements are only accessible after showModal() completes
|
|
19
|
-
// In test environments, we use querySelector as fallback since getByRole may not work
|
|
20
|
-
// Note: In test environments (jsdom), dialog.open may not be set even when dialog is rendered
|
|
21
|
-
// Also note: Dialog uses requestAnimationFrame before showModal(), so we need to wait for content
|
|
22
|
-
const waitForDialog = async (): Promise<HTMLElement> => {
|
|
23
|
-
return await waitFor(
|
|
24
|
-
() => {
|
|
25
|
-
// Try getByRole first (works in browsers with full dialog support)
|
|
26
|
-
try {
|
|
27
|
-
const dialog = screen.getByRole('dialog');
|
|
28
|
-
expect(dialog).toBeInTheDocument();
|
|
29
|
-
return dialog;
|
|
30
|
-
} catch (e) {
|
|
31
|
-
// Fallback: use querySelector for test environments that don't fully support dialog accessibility
|
|
32
|
-
const dialog = document.querySelector('dialog[role="dialog"]') as HTMLDialogElement;
|
|
33
|
-
if (!dialog) {
|
|
34
|
-
throw new Error('Dialog not found in DOM');
|
|
35
|
-
}
|
|
36
|
-
// In test environments, dialog.open may not be set even when dialog is rendered
|
|
37
|
-
// Just check that dialog exists in DOM - that's sufficient for testing
|
|
38
|
-
return dialog;
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
{ timeout: 5000 }
|
|
42
|
-
);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Helper function to find buttons in dialogs (more reliable than getByRole in test environments)
|
|
46
|
-
const findButtonByText = (text: string | RegExp): HTMLButtonElement | null => {
|
|
47
|
-
// Try getByRole first
|
|
48
|
-
try {
|
|
49
|
-
const button = screen.getByRole('button', { name: text });
|
|
50
|
-
return button as HTMLButtonElement;
|
|
51
|
-
} catch (e) {
|
|
52
|
-
// Fallback: search all buttons by text content
|
|
53
|
-
const buttons = Array.from(document.querySelectorAll('button'));
|
|
54
|
-
const regex = typeof text === 'string' ? new RegExp(text, 'i') : text;
|
|
55
|
-
return buttons.find(btn => regex.test(btn.textContent || '')) as HTMLButtonElement || null;
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// Helper function to upload a file and wait for preview to appear
|
|
60
|
-
const uploadFileAndWaitForPreview = async (user: ReturnType<typeof userEvent.setup>, file: File) => {
|
|
61
|
-
// Wait for dialog and file input
|
|
62
|
-
await waitForDialog();
|
|
63
|
-
await waitFor(() => {
|
|
64
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
65
|
-
expect(fileInput).toBeInTheDocument();
|
|
66
|
-
}, { timeout: 5000 });
|
|
67
|
-
|
|
68
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
69
|
-
|
|
70
|
-
// Upload the file
|
|
71
|
-
await user.upload(fileInput, file);
|
|
72
|
-
|
|
73
|
-
// Wait for preview table to appear (file processing is async)
|
|
74
|
-
// Try multiple ways to find the table since queryByRole may not work in all test environments
|
|
75
|
-
await waitFor(() => {
|
|
76
|
-
const table = screen.queryByRole('table') ||
|
|
77
|
-
document.querySelector('table') ||
|
|
78
|
-
document.querySelector('table.min-w-full');
|
|
79
|
-
expect(table).toBeInTheDocument();
|
|
80
|
-
}, { timeout: 10000 });
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// Mock Button component
|
|
84
|
-
vi.mock('../../Button/Button', () => ({
|
|
85
|
-
Button: ({ children, onClick, variant, size, disabled, className }: any) => (
|
|
86
|
-
<button
|
|
87
|
-
onClick={onClick}
|
|
88
|
-
disabled={disabled}
|
|
89
|
-
data-variant={variant}
|
|
90
|
-
data-size={size}
|
|
91
|
-
className={className}
|
|
92
|
-
>
|
|
93
|
-
{children}
|
|
94
|
-
</button>
|
|
95
|
-
),
|
|
96
|
-
}));
|
|
97
|
-
|
|
98
|
-
// Mock Input component
|
|
99
|
-
vi.mock('../../Input/Input', () => ({
|
|
100
|
-
Input: React.forwardRef(({ type, accept, onChange, className, ...props }: any, ref: any) => (
|
|
101
|
-
<input
|
|
102
|
-
ref={ref}
|
|
103
|
-
type={type}
|
|
104
|
-
accept={accept}
|
|
105
|
-
onChange={onChange}
|
|
106
|
-
className={className}
|
|
107
|
-
{...props}
|
|
108
|
-
/>
|
|
109
|
-
)),
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
// Mock lucide-react icons
|
|
113
|
-
vi.mock('lucide-react', () => ({
|
|
114
|
-
Upload: ({ className }: { className?: string }) => (
|
|
115
|
-
<span data-testid="upload-icon" className={className}>Upload</span>
|
|
116
|
-
),
|
|
117
|
-
FileText: ({ className }: { className?: string }) => (
|
|
118
|
-
<span data-testid="file-text-icon" className={className}>File</span>
|
|
119
|
-
),
|
|
120
|
-
AlertCircle: ({ className }: { className?: string }) => (
|
|
121
|
-
<span data-testid="alert-circle-icon" className={className}>Alert</span>
|
|
122
|
-
),
|
|
123
|
-
X: ({ className }: { className?: string }) => (
|
|
124
|
-
<span data-testid="x-icon" className={className}>X</span>
|
|
125
|
-
),
|
|
126
|
-
}));
|
|
127
|
-
|
|
128
|
-
// Mock logger
|
|
129
|
-
vi.mock('../../../utils/core/logger', () => ({
|
|
130
|
-
createLogger: () => ({
|
|
131
|
-
debug: vi.fn(),
|
|
132
|
-
info: vi.fn(),
|
|
133
|
-
warn: vi.fn(),
|
|
134
|
-
error: vi.fn(),
|
|
135
|
-
}),
|
|
136
|
-
}));
|
|
137
|
-
|
|
138
|
-
describe('[component] ImportModal', () => {
|
|
139
|
-
const baseProps = {
|
|
140
|
-
isOpen: true,
|
|
141
|
-
onClose: vi.fn(),
|
|
142
|
-
onImport: vi.fn(),
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const createCSVFile = (content: string, filename = 'test.csv'): File => {
|
|
146
|
-
const blob = new Blob([content], { type: 'text/csv' });
|
|
147
|
-
const file = new File([blob], filename, { type: 'text/csv' });
|
|
148
|
-
// Store content for File.text() mock to access
|
|
149
|
-
(file as any)._content = content;
|
|
150
|
-
return file;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
beforeEach(() => {
|
|
154
|
-
vi.clearAllMocks();
|
|
155
|
-
|
|
156
|
-
// Mock showModal for dialog elements (needed for test environments)
|
|
157
|
-
HTMLDialogElement.prototype.showModal = vi.fn(function(this: HTMLDialogElement) {
|
|
158
|
-
this.setAttribute('open', '');
|
|
159
|
-
this.dispatchEvent(new Event('show', { bubbles: true }));
|
|
160
|
-
});
|
|
161
|
-
HTMLDialogElement.prototype.close = vi.fn(function(this: HTMLDialogElement) {
|
|
162
|
-
this.removeAttribute('open');
|
|
163
|
-
this.dispatchEvent(new Event('close', { bubbles: true }));
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Mock File.text() method for jsdom compatibility
|
|
167
|
-
// File.text() reads the file content asynchronously
|
|
168
|
-
// In tests, we read directly from the Blob content synchronously
|
|
169
|
-
if (!File.prototype.text) {
|
|
170
|
-
Object.defineProperty(File.prototype, 'text', {
|
|
171
|
-
writable: true,
|
|
172
|
-
configurable: true,
|
|
173
|
-
value: async function(this: File) {
|
|
174
|
-
// For test files, read from the stored content or from the Blob
|
|
175
|
-
const file = this as any;
|
|
176
|
-
if (file._content) {
|
|
177
|
-
// Use stored content if available
|
|
178
|
-
return Promise.resolve(file._content);
|
|
179
|
-
}
|
|
180
|
-
// Otherwise, try to read from Blob using FileReader
|
|
181
|
-
return new Promise((resolve, reject) => {
|
|
182
|
-
const reader = new FileReader();
|
|
183
|
-
reader.onload = (e) => {
|
|
184
|
-
resolve(e.target?.result as string);
|
|
185
|
-
};
|
|
186
|
-
reader.onerror = () => {
|
|
187
|
-
reject(new Error('Failed to read file'));
|
|
188
|
-
};
|
|
189
|
-
// Read the file as text
|
|
190
|
-
reader.readAsText(this);
|
|
191
|
-
});
|
|
192
|
-
},
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
afterEach(() => {
|
|
198
|
-
vi.clearAllMocks();
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('Rendering', () => {
|
|
202
|
-
it('returns null when modal is closed', () => {
|
|
203
|
-
const { container } = render(
|
|
204
|
-
<ImportModal {...baseProps} isOpen={false} />
|
|
205
|
-
);
|
|
206
|
-
expect(container.firstChild).toBeNull();
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('renders modal when open', async () => {
|
|
210
|
-
render(<ImportModal {...baseProps} />);
|
|
211
|
-
|
|
212
|
-
// Wait for dialog to be accessible
|
|
213
|
-
await waitForDialog();
|
|
214
|
-
// Check for content instead of testids
|
|
215
|
-
expect(screen.getByText('Import Data')).toBeInTheDocument();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('renders default title', async () => {
|
|
219
|
-
render(<ImportModal {...baseProps} />);
|
|
220
|
-
|
|
221
|
-
// Wait for dialog content to be rendered (showModal is async via requestAnimationFrame)
|
|
222
|
-
await waitFor(() => {
|
|
223
|
-
// Try by role first, fallback to text content
|
|
224
|
-
try {
|
|
225
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
226
|
-
} catch (e) {
|
|
227
|
-
expect(screen.getByText('Import Data')).toBeInTheDocument();
|
|
228
|
-
}
|
|
229
|
-
}, { timeout: 5000 });
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('renders custom title from config', async () => {
|
|
233
|
-
render(
|
|
234
|
-
<ImportModal
|
|
235
|
-
{...baseProps}
|
|
236
|
-
config={{ title: 'Custom Import Title' }}
|
|
237
|
-
/>
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
// Wait for dialog content to be rendered (showModal is async via requestAnimationFrame)
|
|
241
|
-
await waitFor(() => {
|
|
242
|
-
// Try by role first, fallback to text content
|
|
243
|
-
try {
|
|
244
|
-
expect(screen.getByRole('heading', { name: 'Custom Import Title' })).toBeInTheDocument();
|
|
245
|
-
} catch (e) {
|
|
246
|
-
expect(screen.getByText('Custom Import Title')).toBeInTheDocument();
|
|
247
|
-
}
|
|
248
|
-
}, { timeout: 5000 });
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('renders default description', async () => {
|
|
252
|
-
render(<ImportModal {...baseProps} />);
|
|
253
|
-
|
|
254
|
-
// Wait for dialog to be accessible
|
|
255
|
-
await waitForDialog();
|
|
256
|
-
// Description is rendered as p in DialogHeader
|
|
257
|
-
expect(screen.getByText('Upload a CSV file to import multiple records at once.')).toBeInTheDocument();
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('renders custom description from config', async () => {
|
|
261
|
-
render(
|
|
262
|
-
<ImportModal
|
|
263
|
-
{...baseProps}
|
|
264
|
-
config={{ description: 'Custom description' }}
|
|
265
|
-
/>
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
// Wait for dialog to be accessible
|
|
269
|
-
await waitForDialog();
|
|
270
|
-
// Description is rendered as p in DialogHeader
|
|
271
|
-
expect(screen.getByText('Custom description')).toBeInTheDocument();
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('renders file upload area', async () => {
|
|
275
|
-
render(<ImportModal {...baseProps} />);
|
|
276
|
-
|
|
277
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
278
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
279
|
-
await waitFor(() => {
|
|
280
|
-
try {
|
|
281
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
282
|
-
} catch (e) {
|
|
283
|
-
// Fallback: check if any element with "Import Data" exists
|
|
284
|
-
const elements = screen.getAllByText('Import Data');
|
|
285
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
286
|
-
}
|
|
287
|
-
}, { timeout: 5000 });
|
|
288
|
-
|
|
289
|
-
// Then check for upload area text and button (use querySelector as fallback for buttons in dialogs)
|
|
290
|
-
await waitFor(() => {
|
|
291
|
-
expect(screen.getByText(/choose a csv file/i)).toBeInTheDocument();
|
|
292
|
-
const selectFileButton = findButtonByText(/select file/i);
|
|
293
|
-
expect(selectFileButton).toBeInTheDocument();
|
|
294
|
-
}, { timeout: 5000 });
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('renders cancel button', async () => {
|
|
298
|
-
render(<ImportModal {...baseProps} />);
|
|
299
|
-
|
|
300
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
301
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
302
|
-
await waitFor(() => {
|
|
303
|
-
try {
|
|
304
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
305
|
-
} catch (e) {
|
|
306
|
-
// Fallback: check if any element with "Import Data" exists
|
|
307
|
-
const elements = screen.getAllByText('Import Data');
|
|
308
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
309
|
-
}
|
|
310
|
-
}, { timeout: 5000 });
|
|
311
|
-
|
|
312
|
-
// Then check for cancel button (use querySelector as fallback for buttons in dialogs)
|
|
313
|
-
await waitFor(() => {
|
|
314
|
-
const cancelButton = screen.queryByRole('button', { name: /cancel/i })
|
|
315
|
-
|| Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.match(/cancel/i));
|
|
316
|
-
expect(cancelButton).toBeInTheDocument();
|
|
317
|
-
}, { timeout: 5000 });
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it('renders import button', async () => {
|
|
321
|
-
render(<ImportModal {...baseProps} />);
|
|
322
|
-
|
|
323
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
324
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
325
|
-
await waitFor(() => {
|
|
326
|
-
try {
|
|
327
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
328
|
-
} catch (e) {
|
|
329
|
-
// Fallback: check if any element with "Import Data" exists
|
|
330
|
-
const elements = screen.getAllByText('Import Data');
|
|
331
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
332
|
-
}
|
|
333
|
-
}, { timeout: 5000 });
|
|
334
|
-
|
|
335
|
-
// Then check for import button (use querySelector as fallback for buttons in dialogs)
|
|
336
|
-
await waitFor(() => {
|
|
337
|
-
const importButton = screen.queryByRole('button', { name: /import/i })
|
|
338
|
-
|| Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.match(/^import$/i));
|
|
339
|
-
expect(importButton).toBeInTheDocument();
|
|
340
|
-
}, { timeout: 5000 });
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
describe('File Selection', () => {
|
|
345
|
-
it('displays selected file name', async () => {
|
|
346
|
-
const user = userEvent.setup();
|
|
347
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
348
|
-
const file = createCSVFile(csvContent);
|
|
349
|
-
|
|
350
|
-
render(<ImportModal {...baseProps} />);
|
|
351
|
-
|
|
352
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
353
|
-
await user.upload(fileInput, file);
|
|
354
|
-
|
|
355
|
-
await waitFor(() => {
|
|
356
|
-
expect(screen.getByText(`Selected: ${file.name}`)).toBeInTheDocument();
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
it('resets file when modal closes and reopens', async () => {
|
|
361
|
-
const user = userEvent.setup();
|
|
362
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
363
|
-
const file = createCSVFile(csvContent);
|
|
364
|
-
|
|
365
|
-
const { rerender } = render(<ImportModal {...baseProps} />);
|
|
366
|
-
|
|
367
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
368
|
-
await waitForDialog();
|
|
369
|
-
await waitFor(() => {
|
|
370
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
371
|
-
expect(fileInput).toBeInTheDocument();
|
|
372
|
-
}, { timeout: 5000 });
|
|
373
|
-
|
|
374
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
375
|
-
await user.upload(fileInput, file);
|
|
376
|
-
|
|
377
|
-
await waitFor(() => {
|
|
378
|
-
expect(screen.getByText(`Selected: ${file.name}`)).toBeInTheDocument();
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
rerender(<ImportModal {...baseProps} isOpen={false} />);
|
|
382
|
-
|
|
383
|
-
// Wait for dialog to close
|
|
384
|
-
await waitFor(() => {
|
|
385
|
-
const dialog = document.querySelector('dialog[role="dialog"]');
|
|
386
|
-
expect(dialog).not.toBeInTheDocument();
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
rerender(<ImportModal {...baseProps} isOpen={true} />);
|
|
390
|
-
|
|
391
|
-
// Wait for dialog to reopen
|
|
392
|
-
await waitForDialog();
|
|
393
|
-
|
|
394
|
-
// File should be reset
|
|
395
|
-
expect(screen.queryByText(`Selected: ${file.name}`)).not.toBeInTheDocument();
|
|
396
|
-
});
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
describe('CSV Parsing', () => {
|
|
400
|
-
it('parses valid CSV file correctly', async () => {
|
|
401
|
-
const user = userEvent.setup();
|
|
402
|
-
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
|
|
403
|
-
const file = createCSVFile(csvContent);
|
|
404
|
-
|
|
405
|
-
render(<ImportModal {...baseProps} />);
|
|
406
|
-
|
|
407
|
-
// Upload file and wait for preview
|
|
408
|
-
await uploadFileAndWaitForPreview(user, file);
|
|
409
|
-
|
|
410
|
-
// Once preview table is visible, check for table headers
|
|
411
|
-
await waitFor(() => {
|
|
412
|
-
expect(screen.getByText(/name/i)).toBeInTheDocument();
|
|
413
|
-
expect(screen.getByText(/email/i)).toBeInTheDocument();
|
|
414
|
-
}, { timeout: 5000 });
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('shows preview table with parsed data', async () => {
|
|
418
|
-
const user = userEvent.setup();
|
|
419
|
-
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
|
|
420
|
-
const file = createCSVFile(csvContent);
|
|
421
|
-
|
|
422
|
-
render(<ImportModal {...baseProps} />);
|
|
423
|
-
|
|
424
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
425
|
-
await waitForDialog();
|
|
426
|
-
await waitFor(() => {
|
|
427
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
428
|
-
expect(fileInput).toBeInTheDocument();
|
|
429
|
-
}, { timeout: 5000 });
|
|
430
|
-
|
|
431
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
432
|
-
await user.upload(fileInput, file);
|
|
433
|
-
|
|
434
|
-
// Wait for preview table to appear
|
|
435
|
-
await waitFor(() => {
|
|
436
|
-
const table = screen.queryByRole('table') ||
|
|
437
|
-
document.querySelector('table') ||
|
|
438
|
-
document.querySelector('table.min-w-full');
|
|
439
|
-
expect(table).toBeInTheDocument();
|
|
440
|
-
}, { timeout: 10000 });
|
|
441
|
-
|
|
442
|
-
// Then check for data
|
|
443
|
-
await waitFor(() => {
|
|
444
|
-
expect(screen.getByText('John')).toBeInTheDocument();
|
|
445
|
-
expect(screen.getByText('john@example.com')).toBeInTheDocument();
|
|
446
|
-
}, { timeout: 2000 });
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it('displays total row count', async () => {
|
|
450
|
-
const user = userEvent.setup();
|
|
451
|
-
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com\nBob,bob@example.com';
|
|
452
|
-
const file = createCSVFile(csvContent);
|
|
453
|
-
|
|
454
|
-
render(<ImportModal {...baseProps} />);
|
|
455
|
-
|
|
456
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
457
|
-
await waitForDialog();
|
|
458
|
-
await waitFor(() => {
|
|
459
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
460
|
-
expect(fileInput).toBeInTheDocument();
|
|
461
|
-
}, { timeout: 5000 });
|
|
462
|
-
|
|
463
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
464
|
-
await user.upload(fileInput, file);
|
|
465
|
-
|
|
466
|
-
// Wait for preview to appear first
|
|
467
|
-
await waitFor(() => {
|
|
468
|
-
const table = screen.queryByRole('table') ||
|
|
469
|
-
document.querySelector('table') ||
|
|
470
|
-
document.querySelector('table.min-w-full');
|
|
471
|
-
expect(table).toBeInTheDocument();
|
|
472
|
-
}, { timeout: 10000 });
|
|
473
|
-
|
|
474
|
-
// Then check for total row count
|
|
475
|
-
await waitFor(() => {
|
|
476
|
-
expect(screen.getByText(/total rows to import: 3/i)).toBeInTheDocument();
|
|
477
|
-
}, { timeout: 2000 });
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
it('handles CSV with quoted values', async () => {
|
|
481
|
-
const user = userEvent.setup();
|
|
482
|
-
const csvContent = 'name,email\n"John Doe","john@example.com"\n"Jane Smith","jane@example.com"';
|
|
483
|
-
const file = createCSVFile(csvContent);
|
|
484
|
-
|
|
485
|
-
render(<ImportModal {...baseProps} />);
|
|
486
|
-
|
|
487
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
488
|
-
await waitForDialog();
|
|
489
|
-
await waitFor(() => {
|
|
490
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
491
|
-
expect(fileInput).toBeInTheDocument();
|
|
492
|
-
}, { timeout: 5000 });
|
|
493
|
-
|
|
494
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
495
|
-
await user.upload(fileInput, file);
|
|
496
|
-
|
|
497
|
-
// Wait for preview table first
|
|
498
|
-
await waitFor(() => {
|
|
499
|
-
const table = screen.queryByRole('table') ||
|
|
500
|
-
document.querySelector('table') ||
|
|
501
|
-
document.querySelector('table.min-w-full');
|
|
502
|
-
expect(table).toBeInTheDocument();
|
|
503
|
-
}, { timeout: 10000 });
|
|
504
|
-
|
|
505
|
-
// Then check for data
|
|
506
|
-
await waitFor(() => {
|
|
507
|
-
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
508
|
-
}, { timeout: 2000 });
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
it('handles CSV with commas in quoted values', async () => {
|
|
512
|
-
const user = userEvent.setup();
|
|
513
|
-
const csvContent = 'name,description\nJohn,"Description, with comma"\nJane,"Another, description"';
|
|
514
|
-
const file = createCSVFile(csvContent);
|
|
515
|
-
|
|
516
|
-
render(<ImportModal {...baseProps} />);
|
|
517
|
-
|
|
518
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
519
|
-
await waitForDialog();
|
|
520
|
-
await waitFor(() => {
|
|
521
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
522
|
-
expect(fileInput).toBeInTheDocument();
|
|
523
|
-
}, { timeout: 5000 });
|
|
524
|
-
|
|
525
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
526
|
-
await user.upload(fileInput, file);
|
|
527
|
-
|
|
528
|
-
// Wait for preview table first
|
|
529
|
-
await waitFor(() => {
|
|
530
|
-
const table = screen.queryByRole('table') ||
|
|
531
|
-
document.querySelector('table') ||
|
|
532
|
-
document.querySelector('table.min-w-full');
|
|
533
|
-
expect(table).toBeInTheDocument();
|
|
534
|
-
}, { timeout: 10000 });
|
|
535
|
-
|
|
536
|
-
// Then check for data
|
|
537
|
-
await waitFor(() => {
|
|
538
|
-
expect(screen.getByText(/description, with comma/i)).toBeInTheDocument();
|
|
539
|
-
}, { timeout: 2000 });
|
|
540
|
-
});
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
describe('Error Handling', () => {
|
|
544
|
-
it('displays error for invalid CSV file', async () => {
|
|
545
|
-
const user = userEvent.setup();
|
|
546
|
-
// Create a file that will cause parsing to fail
|
|
547
|
-
// CSV with only header row (no data rows) will trigger the error
|
|
548
|
-
const invalidContent = 'name,email';
|
|
549
|
-
const file = createCSVFile(invalidContent);
|
|
550
|
-
|
|
551
|
-
render(<ImportModal {...baseProps} />);
|
|
552
|
-
|
|
553
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
554
|
-
await waitForDialog();
|
|
555
|
-
await waitFor(() => {
|
|
556
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
557
|
-
expect(fileInput).toBeInTheDocument();
|
|
558
|
-
}, { timeout: 5000 });
|
|
559
|
-
|
|
560
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
561
|
-
await user.upload(fileInput, file);
|
|
562
|
-
|
|
563
|
-
// Wait for error to appear - CSV with only header should throw error
|
|
564
|
-
await waitFor(() => {
|
|
565
|
-
// Error should be displayed with AlertCircle icon or error message
|
|
566
|
-
const errorIcon = screen.queryByTestId('alert-circle-icon');
|
|
567
|
-
const errorText = screen.queryByText(/must have at least|error|failed/i);
|
|
568
|
-
expect(errorIcon || errorText).toBeInTheDocument();
|
|
569
|
-
}, { timeout: 5000 });
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
it('displays error for CSV with only header row', async () => {
|
|
573
|
-
const user = userEvent.setup();
|
|
574
|
-
const csvContent = 'name,email';
|
|
575
|
-
const file = createCSVFile(csvContent);
|
|
576
|
-
|
|
577
|
-
render(<ImportModal {...baseProps} />);
|
|
578
|
-
|
|
579
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
580
|
-
await waitForDialog();
|
|
581
|
-
await waitFor(() => {
|
|
582
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
583
|
-
expect(fileInput).toBeInTheDocument();
|
|
584
|
-
}, { timeout: 5000 });
|
|
585
|
-
|
|
586
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
587
|
-
await user.upload(fileInput, file);
|
|
588
|
-
|
|
589
|
-
await waitFor(() => {
|
|
590
|
-
expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
|
|
591
|
-
});
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
it('clears error when new file is selected', async () => {
|
|
595
|
-
const user = userEvent.setup();
|
|
596
|
-
const invalidFile = createCSVFile('invalid');
|
|
597
|
-
const validFile = createCSVFile('name,email\nJohn,john@example.com');
|
|
598
|
-
|
|
599
|
-
render(<ImportModal {...baseProps} />);
|
|
600
|
-
|
|
601
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
602
|
-
await user.upload(fileInput, invalidFile);
|
|
603
|
-
|
|
604
|
-
await waitFor(() => {
|
|
605
|
-
expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
|
|
606
|
-
}, { timeout: 3000 });
|
|
607
|
-
|
|
608
|
-
// Clear the file input value first
|
|
609
|
-
fileInput.value = '';
|
|
610
|
-
await user.upload(fileInput, validFile);
|
|
611
|
-
|
|
612
|
-
await waitFor(() => {
|
|
613
|
-
expect(screen.queryByTestId('alert-circle-icon')).not.toBeInTheDocument();
|
|
614
|
-
}, { timeout: 3000 });
|
|
615
|
-
});
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
describe('Import Action', () => {
|
|
619
|
-
it('calls onImport with parsed data when import button is clicked', async () => {
|
|
620
|
-
const user = userEvent.setup();
|
|
621
|
-
const onImport = vi.fn();
|
|
622
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
623
|
-
const file = createCSVFile(csvContent);
|
|
624
|
-
|
|
625
|
-
render(<ImportModal {...baseProps} onImport={onImport} />);
|
|
626
|
-
|
|
627
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
628
|
-
await waitForDialog();
|
|
629
|
-
await waitFor(() => {
|
|
630
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
631
|
-
expect(fileInput).toBeInTheDocument();
|
|
632
|
-
}, { timeout: 5000 });
|
|
633
|
-
|
|
634
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
635
|
-
await user.upload(fileInput, file);
|
|
636
|
-
|
|
637
|
-
// Wait for preview to appear
|
|
638
|
-
await waitFor(() => {
|
|
639
|
-
const table = screen.queryByRole('table') ||
|
|
640
|
-
document.querySelector('table') ||
|
|
641
|
-
document.querySelector('table.min-w-full');
|
|
642
|
-
expect(table).toBeInTheDocument();
|
|
643
|
-
}, { timeout: 10000 });
|
|
644
|
-
|
|
645
|
-
await waitFor(() => {
|
|
646
|
-
expect(screen.getByText('John')).toBeInTheDocument();
|
|
647
|
-
}, { timeout: 2000 });
|
|
648
|
-
|
|
649
|
-
const importButton = findButtonByText(/^import$/i);
|
|
650
|
-
expect(importButton).toBeInTheDocument();
|
|
651
|
-
if (importButton) {
|
|
652
|
-
await user.click(importButton);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
await waitFor(() => {
|
|
656
|
-
expect(onImport).toHaveBeenCalledTimes(1);
|
|
657
|
-
expect(onImport).toHaveBeenCalledWith(
|
|
658
|
-
expect.arrayContaining([
|
|
659
|
-
expect.objectContaining({
|
|
660
|
-
name: 'John',
|
|
661
|
-
email: 'john@example.com',
|
|
662
|
-
}),
|
|
663
|
-
])
|
|
664
|
-
);
|
|
665
|
-
}, { timeout: 3000 });
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
it('disables import button when no file is selected', async () => {
|
|
669
|
-
render(<ImportModal {...baseProps} />);
|
|
670
|
-
|
|
671
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
672
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
673
|
-
await waitFor(() => {
|
|
674
|
-
try {
|
|
675
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
676
|
-
} catch (e) {
|
|
677
|
-
// Fallback: check if any element with "Import Data" exists
|
|
678
|
-
const elements = screen.getAllByText('Import Data');
|
|
679
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
680
|
-
}
|
|
681
|
-
}, { timeout: 5000 });
|
|
682
|
-
|
|
683
|
-
// Then check for import button and its disabled state
|
|
684
|
-
await waitFor(() => {
|
|
685
|
-
const importButton = findButtonByText(/^import$/i);
|
|
686
|
-
expect(importButton).toBeInTheDocument();
|
|
687
|
-
expect(importButton).toBeDisabled();
|
|
688
|
-
}, { timeout: 5000 });
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
it('disables import button while processing', async () => {
|
|
692
|
-
const user = userEvent.setup();
|
|
693
|
-
const onImport = vi.fn(async () => {
|
|
694
|
-
await new Promise<void>(resolve => setTimeout(resolve, 100));
|
|
695
|
-
});
|
|
696
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
697
|
-
const file = createCSVFile(csvContent);
|
|
698
|
-
|
|
699
|
-
render(<ImportModal {...baseProps} onImport={onImport} />);
|
|
700
|
-
|
|
701
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
702
|
-
await waitForDialog();
|
|
703
|
-
await waitFor(() => {
|
|
704
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
705
|
-
expect(fileInput).toBeInTheDocument();
|
|
706
|
-
}, { timeout: 5000 });
|
|
707
|
-
|
|
708
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
709
|
-
await user.upload(fileInput, file);
|
|
710
|
-
|
|
711
|
-
// Wait for preview to appear
|
|
712
|
-
await waitFor(() => {
|
|
713
|
-
const table = screen.queryByRole('table') ||
|
|
714
|
-
document.querySelector('table') ||
|
|
715
|
-
document.querySelector('table.min-w-full');
|
|
716
|
-
expect(table).toBeInTheDocument();
|
|
717
|
-
}, { timeout: 10000 });
|
|
718
|
-
|
|
719
|
-
await waitFor(() => {
|
|
720
|
-
expect(screen.getByText('John')).toBeInTheDocument();
|
|
721
|
-
}, { timeout: 2000 });
|
|
722
|
-
|
|
723
|
-
const importButton = findButtonByText(/^import$/i);
|
|
724
|
-
expect(importButton).toBeInTheDocument();
|
|
725
|
-
if (importButton) {
|
|
726
|
-
await user.click(importButton);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
await waitFor(() => {
|
|
730
|
-
expect(importButton).toBeDisabled();
|
|
731
|
-
}, { timeout: 1000 });
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
it('shows processing text while importing', async () => {
|
|
735
|
-
const user = userEvent.setup();
|
|
736
|
-
const onImport = vi.fn(async () => {
|
|
737
|
-
await new Promise<void>(resolve => setTimeout(resolve, 100));
|
|
738
|
-
});
|
|
739
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
740
|
-
const file = createCSVFile(csvContent);
|
|
741
|
-
|
|
742
|
-
render(<ImportModal {...baseProps} onImport={onImport} />);
|
|
743
|
-
|
|
744
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
745
|
-
await waitForDialog();
|
|
746
|
-
await waitFor(() => {
|
|
747
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
748
|
-
expect(fileInput).toBeInTheDocument();
|
|
749
|
-
}, { timeout: 5000 });
|
|
750
|
-
|
|
751
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
752
|
-
await user.upload(fileInput, file);
|
|
753
|
-
|
|
754
|
-
// Wait for preview to appear
|
|
755
|
-
await waitFor(() => {
|
|
756
|
-
const table = screen.queryByRole('table') ||
|
|
757
|
-
document.querySelector('table') ||
|
|
758
|
-
document.querySelector('table.min-w-full');
|
|
759
|
-
expect(table).toBeInTheDocument();
|
|
760
|
-
}, { timeout: 10000 });
|
|
761
|
-
|
|
762
|
-
await waitFor(() => {
|
|
763
|
-
expect(screen.getByText('John')).toBeInTheDocument();
|
|
764
|
-
}, { timeout: 2000 });
|
|
765
|
-
|
|
766
|
-
const importButton = findButtonByText(/^import$/i);
|
|
767
|
-
expect(importButton).toBeInTheDocument();
|
|
768
|
-
if (importButton) {
|
|
769
|
-
await user.click(importButton);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// Button text changes to "Processing..." when isProcessing is true
|
|
773
|
-
await waitFor(() => {
|
|
774
|
-
const processingButton = findButtonByText(/processing/i);
|
|
775
|
-
expect(processingButton).toBeInTheDocument();
|
|
776
|
-
}, { timeout: 1000 });
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
it('calls onClose after successful import', async () => {
|
|
780
|
-
const user = userEvent.setup();
|
|
781
|
-
const onClose = vi.fn();
|
|
782
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
783
|
-
const file = createCSVFile(csvContent);
|
|
784
|
-
|
|
785
|
-
render(<ImportModal {...baseProps} onClose={onClose} />);
|
|
786
|
-
|
|
787
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
788
|
-
await waitForDialog();
|
|
789
|
-
await waitFor(() => {
|
|
790
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
791
|
-
expect(fileInput).toBeInTheDocument();
|
|
792
|
-
}, { timeout: 5000 });
|
|
793
|
-
|
|
794
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
795
|
-
await user.upload(fileInput, file);
|
|
796
|
-
|
|
797
|
-
// Wait for preview to appear
|
|
798
|
-
await waitFor(() => {
|
|
799
|
-
const table = screen.queryByRole('table') ||
|
|
800
|
-
document.querySelector('table') ||
|
|
801
|
-
document.querySelector('table.min-w-full');
|
|
802
|
-
expect(table).toBeInTheDocument();
|
|
803
|
-
}, { timeout: 10000 });
|
|
804
|
-
|
|
805
|
-
await waitFor(() => {
|
|
806
|
-
expect(screen.getByText('John')).toBeInTheDocument();
|
|
807
|
-
}, { timeout: 2000 });
|
|
808
|
-
|
|
809
|
-
const importButton = findButtonByText(/^import$/i);
|
|
810
|
-
expect(importButton).toBeInTheDocument();
|
|
811
|
-
if (importButton) {
|
|
812
|
-
await user.click(importButton);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
await waitFor(() => {
|
|
816
|
-
expect(onClose).toHaveBeenCalled();
|
|
817
|
-
}, { timeout: 3000 });
|
|
818
|
-
});
|
|
819
|
-
});
|
|
820
|
-
|
|
821
|
-
describe('Close Action', () => {
|
|
822
|
-
it('calls onClose when cancel button is clicked', async () => {
|
|
823
|
-
const user = userEvent.setup();
|
|
824
|
-
const onClose = vi.fn();
|
|
825
|
-
|
|
826
|
-
render(<ImportModal {...baseProps} onClose={onClose} />);
|
|
827
|
-
|
|
828
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
829
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
830
|
-
await waitFor(() => {
|
|
831
|
-
try {
|
|
832
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
833
|
-
} catch (e) {
|
|
834
|
-
// Fallback: check if any element with "Import Data" exists
|
|
835
|
-
const elements = screen.getAllByText('Import Data');
|
|
836
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
837
|
-
}
|
|
838
|
-
}, { timeout: 5000 });
|
|
839
|
-
|
|
840
|
-
// Then wait for cancel button
|
|
841
|
-
await waitFor(() => {
|
|
842
|
-
const cancelButton = findButtonByText(/cancel/i);
|
|
843
|
-
expect(cancelButton).toBeInTheDocument();
|
|
844
|
-
}, { timeout: 5000 });
|
|
845
|
-
|
|
846
|
-
const cancelButton = findButtonByText(/cancel/i);
|
|
847
|
-
expect(cancelButton).toBeInTheDocument();
|
|
848
|
-
if (cancelButton) {
|
|
849
|
-
await user.click(cancelButton);
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
expect(onClose).toHaveBeenCalledTimes(1);
|
|
853
|
-
});
|
|
854
|
-
|
|
855
|
-
it('resets state when modal closes', async () => {
|
|
856
|
-
const user = userEvent.setup();
|
|
857
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
858
|
-
const file = createCSVFile(csvContent);
|
|
859
|
-
|
|
860
|
-
const { rerender } = render(<ImportModal {...baseProps} />);
|
|
861
|
-
|
|
862
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
863
|
-
await waitForDialog();
|
|
864
|
-
await waitFor(() => {
|
|
865
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
866
|
-
expect(fileInput).toBeInTheDocument();
|
|
867
|
-
}, { timeout: 5000 });
|
|
868
|
-
|
|
869
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
870
|
-
await user.upload(fileInput, file);
|
|
871
|
-
|
|
872
|
-
// Wait for preview to appear
|
|
873
|
-
await waitFor(() => {
|
|
874
|
-
const table = screen.queryByRole('table') ||
|
|
875
|
-
document.querySelector('table') ||
|
|
876
|
-
document.querySelector('table.min-w-full');
|
|
877
|
-
expect(table).toBeInTheDocument();
|
|
878
|
-
}, { timeout: 10000 });
|
|
879
|
-
|
|
880
|
-
await waitFor(() => {
|
|
881
|
-
expect(screen.getByText('John')).toBeInTheDocument();
|
|
882
|
-
}, { timeout: 2000 });
|
|
883
|
-
|
|
884
|
-
rerender(<ImportModal {...baseProps} isOpen={false} />);
|
|
885
|
-
rerender(<ImportModal {...baseProps} isOpen={true} />);
|
|
886
|
-
|
|
887
|
-
// State should be reset - wait for modal to reopen
|
|
888
|
-
await waitFor(() => {
|
|
889
|
-
expect(screen.queryByText('John')).not.toBeInTheDocument();
|
|
890
|
-
}, { timeout: 1000 });
|
|
891
|
-
});
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
describe('Custom Configuration', () => {
|
|
895
|
-
it('uses custom button texts from config', async () => {
|
|
896
|
-
render(
|
|
897
|
-
<ImportModal
|
|
898
|
-
{...baseProps}
|
|
899
|
-
config={{
|
|
900
|
-
selectFileButtonText: 'Browse Files',
|
|
901
|
-
importButtonText: 'Import Data',
|
|
902
|
-
cancelButtonText: 'Close',
|
|
903
|
-
}}
|
|
904
|
-
/>
|
|
905
|
-
);
|
|
906
|
-
|
|
907
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
908
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
909
|
-
await waitFor(() => {
|
|
910
|
-
try {
|
|
911
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
912
|
-
} catch (e) {
|
|
913
|
-
// Fallback: check if any element with "Import Data" exists
|
|
914
|
-
const elements = screen.getAllByText('Import Data');
|
|
915
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
916
|
-
}
|
|
917
|
-
}, { timeout: 5000 });
|
|
918
|
-
|
|
919
|
-
// Then wait for buttons
|
|
920
|
-
await waitFor(() => {
|
|
921
|
-
const browseButton = findButtonByText(/browse files/i);
|
|
922
|
-
const importDataButton = findButtonByText(/import data/i);
|
|
923
|
-
expect(browseButton).toBeInTheDocument();
|
|
924
|
-
expect(importDataButton).toBeInTheDocument();
|
|
925
|
-
}, { timeout: 5000 });
|
|
926
|
-
// Dialog has a close button too, so find the Cancel one by text
|
|
927
|
-
const cancelButton = findButtonByText(/^close$/i);
|
|
928
|
-
expect(cancelButton).toBeInTheDocument();
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
it('uses custom preview header text from config', async () => {
|
|
932
|
-
const user = userEvent.setup();
|
|
933
|
-
const csvContent = 'name,email\nJohn,john@example.com';
|
|
934
|
-
const file = createCSVFile(csvContent);
|
|
935
|
-
|
|
936
|
-
render(
|
|
937
|
-
<ImportModal
|
|
938
|
-
{...baseProps}
|
|
939
|
-
config={{ previewHeaderText: 'Data Preview' }}
|
|
940
|
-
/>
|
|
941
|
-
);
|
|
942
|
-
|
|
943
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
944
|
-
await user.upload(fileInput, file);
|
|
945
|
-
|
|
946
|
-
// Wait for preview to appear
|
|
947
|
-
await waitFor(() => {
|
|
948
|
-
expect(screen.getByText('Data Preview')).toBeInTheDocument();
|
|
949
|
-
}, { timeout: 5000 });
|
|
950
|
-
});
|
|
951
|
-
|
|
952
|
-
it('uses custom total rows text from config', async () => {
|
|
953
|
-
const user = userEvent.setup();
|
|
954
|
-
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
|
|
955
|
-
const file = createCSVFile(csvContent);
|
|
956
|
-
|
|
957
|
-
render(
|
|
958
|
-
<ImportModal
|
|
959
|
-
{...baseProps}
|
|
960
|
-
config={{ totalRowsText: 'Found {count} records' }}
|
|
961
|
-
/>
|
|
962
|
-
);
|
|
963
|
-
|
|
964
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
965
|
-
await waitForDialog();
|
|
966
|
-
await waitFor(() => {
|
|
967
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
968
|
-
expect(fileInput).toBeInTheDocument();
|
|
969
|
-
}, { timeout: 5000 });
|
|
970
|
-
|
|
971
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
972
|
-
await user.upload(fileInput, file);
|
|
973
|
-
|
|
974
|
-
// Wait for preview to appear
|
|
975
|
-
await waitFor(() => {
|
|
976
|
-
const table = screen.queryByRole('table') ||
|
|
977
|
-
document.querySelector('table') ||
|
|
978
|
-
document.querySelector('table.min-w-full');
|
|
979
|
-
expect(table).toBeInTheDocument();
|
|
980
|
-
}, { timeout: 10000 });
|
|
981
|
-
|
|
982
|
-
await waitFor(() => {
|
|
983
|
-
expect(screen.getByText(/found 2 records/i)).toBeInTheDocument();
|
|
984
|
-
}, { timeout: 2000 });
|
|
985
|
-
});
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
describe('Edge Cases', () => {
|
|
989
|
-
it('handles empty CSV file', async () => {
|
|
990
|
-
const user = userEvent.setup();
|
|
991
|
-
const file = createCSVFile('');
|
|
992
|
-
|
|
993
|
-
render(<ImportModal {...baseProps} />);
|
|
994
|
-
|
|
995
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
996
|
-
await waitForDialog();
|
|
997
|
-
await waitFor(() => {
|
|
998
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
999
|
-
expect(fileInput).toBeInTheDocument();
|
|
1000
|
-
}, { timeout: 5000 });
|
|
1001
|
-
|
|
1002
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
1003
|
-
await user.upload(fileInput, file);
|
|
1004
|
-
|
|
1005
|
-
await waitFor(() => {
|
|
1006
|
-
expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
|
|
1007
|
-
}, { timeout: 3000 });
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
it('handles CSV with only whitespace', async () => {
|
|
1011
|
-
const user = userEvent.setup();
|
|
1012
|
-
const file = createCSVFile(' \n \n ');
|
|
1013
|
-
|
|
1014
|
-
render(<ImportModal {...baseProps} />);
|
|
1015
|
-
|
|
1016
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
1017
|
-
await waitForDialog();
|
|
1018
|
-
await waitFor(() => {
|
|
1019
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
1020
|
-
expect(fileInput).toBeInTheDocument();
|
|
1021
|
-
}, { timeout: 5000 });
|
|
1022
|
-
|
|
1023
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
1024
|
-
await user.upload(fileInput, file);
|
|
1025
|
-
|
|
1026
|
-
await waitFor(() => {
|
|
1027
|
-
expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
|
|
1028
|
-
}, { timeout: 3000 });
|
|
1029
|
-
});
|
|
1030
|
-
|
|
1031
|
-
it('handles very large CSV files', async () => {
|
|
1032
|
-
const user = userEvent.setup();
|
|
1033
|
-
const headers = 'name,email\n';
|
|
1034
|
-
const rows = Array.from({ length: 1000 }, (_, i) => `User${i},user${i}@example.com`).join('\n');
|
|
1035
|
-
const csvContent = headers + rows;
|
|
1036
|
-
const file = createCSVFile(csvContent);
|
|
1037
|
-
|
|
1038
|
-
render(<ImportModal {...baseProps} />);
|
|
1039
|
-
|
|
1040
|
-
// Wait for dialog to be accessible first, then wait for file input
|
|
1041
|
-
await waitForDialog();
|
|
1042
|
-
await waitFor(() => {
|
|
1043
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
1044
|
-
expect(fileInput).toBeInTheDocument();
|
|
1045
|
-
}, { timeout: 5000 });
|
|
1046
|
-
|
|
1047
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
1048
|
-
await user.upload(fileInput, file);
|
|
1049
|
-
|
|
1050
|
-
await waitFor(() => {
|
|
1051
|
-
expect(screen.getByText(/total rows to import: 1000/i)).toBeInTheDocument();
|
|
1052
|
-
}, { timeout: 5000 });
|
|
1053
|
-
});
|
|
1054
|
-
});
|
|
1055
|
-
|
|
1056
|
-
describe('Accessibility', () => {
|
|
1057
|
-
it('provides accessible file input', () => {
|
|
1058
|
-
render(<ImportModal {...baseProps} />);
|
|
1059
|
-
|
|
1060
|
-
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
1061
|
-
expect(fileInput).toBeInTheDocument();
|
|
1062
|
-
expect(fileInput).toHaveAttribute('type', 'file');
|
|
1063
|
-
expect(fileInput).toHaveAttribute('accept', '.csv');
|
|
1064
|
-
});
|
|
1065
|
-
|
|
1066
|
-
it('provides accessible button labels', async () => {
|
|
1067
|
-
render(<ImportModal {...baseProps} />);
|
|
1068
|
-
|
|
1069
|
-
// Wait for dialog title first (most reliable indicator dialog is rendered)
|
|
1070
|
-
// Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
|
|
1071
|
-
await waitFor(() => {
|
|
1072
|
-
try {
|
|
1073
|
-
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
1074
|
-
} catch (e) {
|
|
1075
|
-
// Fallback: check if any element with "Import Data" exists
|
|
1076
|
-
const elements = screen.getAllByText('Import Data');
|
|
1077
|
-
expect(elements.length).toBeGreaterThan(0);
|
|
1078
|
-
}
|
|
1079
|
-
}, { timeout: 5000 });
|
|
1080
|
-
|
|
1081
|
-
// Then wait for buttons
|
|
1082
|
-
await waitFor(() => {
|
|
1083
|
-
const selectFileButton = findButtonByText(/select file/i);
|
|
1084
|
-
const importButton = findButtonByText(/^import$/i);
|
|
1085
|
-
const cancelButton = findButtonByText(/cancel/i);
|
|
1086
|
-
expect(selectFileButton).toBeInTheDocument();
|
|
1087
|
-
expect(importButton).toBeInTheDocument();
|
|
1088
|
-
expect(cancelButton).toBeInTheDocument();
|
|
1089
|
-
}, { timeout: 5000 });
|
|
1090
|
-
});
|
|
1091
|
-
});
|
|
1092
|
-
});
|
|
1093
|
-
|