@jmruthers/pace-core 0.5.115 → 0.5.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AuthService-CVgsgtaZ.d.ts → AuthService-D4646R4b.d.ts} +9 -4
- package/dist/{DataTable-H5KJCAIS.js → DataTable-ZOAKQ3SU.js} +10 -9
- package/dist/{UnifiedAuthProvider-KZZUO27W.js → UnifiedAuthProvider-YFN7YGVN.js} +4 -3
- package/dist/{api-PKU4PUBO.js → api-TNIBJWLM.js} +3 -3
- package/dist/{audit-H4YJJF7R.js → audit-T36HM7IM.js} +2 -2
- package/dist/{chunk-SYXOZQ4P.js → chunk-2GJ5GL77.js} +1 -1
- package/dist/chunk-2GJ5GL77.js.map +1 -0
- package/dist/{chunk-XYRZV7R5.js → chunk-2LM4QQGH.js} +30 -34
- package/dist/chunk-2LM4QQGH.js.map +1 -0
- package/dist/{chunk-3OGQLOJM.js → chunk-3DBFLLLU.js} +30 -1
- package/dist/chunk-3DBFLLLU.js.map +1 -0
- package/dist/{chunk-KTHLNIMA.js → chunk-ECOVPXYS.js} +13 -62
- package/dist/chunk-ECOVPXYS.js.map +1 -0
- package/dist/{chunk-OO3V7W4H.js → chunk-KA3PSVNV.js} +87 -40
- package/dist/chunk-KA3PSVNV.js.map +1 -0
- package/dist/{chunk-HKWQN44G.js → chunk-KMPWND3F.js} +15 -15
- package/dist/{chunk-L36JW4KV.js → chunk-LFS45U62.js} +2 -2
- package/dist/{chunk-NEONKMTU.js → chunk-LZYHAL7Y.js} +9 -4
- package/dist/{chunk-NEONKMTU.js.map → chunk-LZYHAL7Y.js.map} +1 -1
- package/dist/{chunk-BUN7NMV7.js → chunk-O3FTRYEU.js} +2 -2
- package/dist/{chunk-F6QB26OS.js → chunk-P3PUOL6B.js} +80 -8
- package/dist/chunk-P3PUOL6B.js.map +1 -0
- package/dist/{chunk-ZPXWJA4H.js → chunk-PHDAXDHB.js} +131 -5
- package/dist/chunk-PHDAXDHB.js.map +1 -0
- package/dist/chunk-UJI6WSMD.js +201 -0
- package/dist/{chunk-5CDJCTOO.js.map → chunk-UJI6WSMD.js.map} +1 -1
- package/dist/{chunk-OUU3SP6I.js → chunk-UKZWNQMB.js} +50 -7
- package/dist/{chunk-OUU3SP6I.js.map → chunk-UKZWNQMB.js.map} +1 -1
- package/dist/{chunk-7H75SHXZ.js → chunk-VN3OOE35.js} +2 -2
- package/dist/{chunk-QKIVSZ2O.js → chunk-WP5I5GLN.js} +2 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +12 -11
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +10 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +19 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +3 -2
- package/dist/rbac/index.d.ts +82 -1
- package/dist/rbac/index.js +13 -10
- package/dist/{useToast-DVT4dMtf.d.ts → useToast-Cs_g32bg.d.ts} +1 -1
- package/dist/utils.js +6 -4
- package/dist/utils.js.map +1 -1
- package/dist/validation.js +3 -1
- package/dist/validation.js.map +1 -1
- package/docs/README.md +4 -0
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +35 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +71 -0
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +122 -0
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +100 -0
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +52 -0
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +41 -14
- package/docs/architecture/rpc-function-standards.md +193 -0
- package/package.json +1 -1
- package/src/__tests__/TEST_STANDARD.md +244 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +46 -16
- package/src/components/DataTable/__tests__/keyboard.test.tsx +276 -217
- package/src/components/DataTable/components/DataTableCore.tsx +29 -2
- package/src/components/DataTable/components/DataTableToolbar.tsx +3 -2
- package/src/components/DataTable/components/EditableRow.tsx +18 -1
- package/src/components/DataTable/components/ViewRowModal.tsx +1 -1
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +735 -0
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +572 -0
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +708 -0
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +451 -0
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +456 -0
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +454 -0
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +462 -0
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +423 -0
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +393 -0
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +617 -0
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +734 -0
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +412 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +4 -0
- package/src/components/EventSelector/EventSelector.tsx +5 -25
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +12 -7
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +4 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +7 -2
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +13 -8
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +109 -100
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +18 -13
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +17 -12
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +2 -0
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +11 -1
- package/src/components/PasswordReset/PasswordChangeForm.test.tsx +2 -2
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +648 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +10 -7
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +4 -12
- package/src/components/Select/Select.tsx +8 -0
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +367 -3
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +916 -0
- package/src/hooks/useEventTheme.ts +49 -18
- package/src/hooks/usePermissionCache.ts +5 -3
- package/src/hooks/useSecureDataAccess.ts +11 -1
- package/src/hooks/useToast.ts +1 -1
- package/src/providers/services/EventServiceProvider.tsx +15 -8
- package/src/rbac/__tests__/cache-invalidation.test.ts +385 -0
- package/src/rbac/audit.test.ts +206 -0
- package/src/rbac/audit.ts +37 -2
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +26 -23
- package/src/rbac/errors.test.ts +340 -0
- package/src/rbac/hooks/index.ts +9 -0
- package/src/rbac/hooks/useResolvedScope.test.ts +1063 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +908 -0
- package/src/rbac/hooks/useRoleManagement.ts +255 -0
- package/src/services/AuthService.ts +10 -0
- package/src/services/EventService.ts +111 -50
- package/src/services/__tests__/AuthService.test.ts +1 -1
- package/src/services/__tests__/EventService.test.ts +60 -45
- package/src/services/interfaces/IEventService.ts +1 -1
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +320 -0
- package/src/utils/__tests__/logger.unit.test.ts +398 -0
- package/src/utils/__tests__/validation.unit.test.ts +225 -1
- package/src/utils/file-reference.test.ts +214 -0
- package/dist/chunk-3OGQLOJM.js.map +0 -1
- package/dist/chunk-5CDJCTOO.js +0 -190
- package/dist/chunk-F6QB26OS.js.map +0 -1
- package/dist/chunk-KTHLNIMA.js.map +0 -1
- package/dist/chunk-OO3V7W4H.js.map +0 -1
- package/dist/chunk-SYXOZQ4P.js.map +0 -1
- package/dist/chunk-XYRZV7R5.js.map +0 -1
- package/dist/chunk-ZPXWJA4H.js.map +0 -1
- package/src/rbac/audit-enhanced.ts +0 -351
- /package/dist/{DataTable-H5KJCAIS.js.map → DataTable-ZOAKQ3SU.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-KZZUO27W.js.map → UnifiedAuthProvider-YFN7YGVN.js.map} +0 -0
- /package/dist/{api-PKU4PUBO.js.map → api-TNIBJWLM.js.map} +0 -0
- /package/dist/{audit-H4YJJF7R.js.map → audit-T36HM7IM.js.map} +0 -0
- /package/dist/{chunk-HKWQN44G.js.map → chunk-KMPWND3F.js.map} +0 -0
- /package/dist/{chunk-L36JW4KV.js.map → chunk-LFS45U62.js.map} +0 -0
- /package/dist/{chunk-BUN7NMV7.js.map → chunk-O3FTRYEU.js.map} +0 -0
- /package/dist/{chunk-7H75SHXZ.js.map → chunk-VN3OOE35.js.map} +0 -0
- /package/dist/{chunk-QKIVSZ2O.js.map → chunk-WP5I5GLN.js.map} +0 -0
|
@@ -329,6 +329,19 @@ expect(screen.getByText('Visible content')).toBeInTheDocument();
|
|
|
329
329
|
expect(screen.queryByText('Hidden content')).not.toBeInTheDocument();
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
+
### 2. Skipped Tests Without Documentation
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// ❌ Bad - Skipped test without explanation
|
|
336
|
+
it.skip('should navigate with keyboard', () => {});
|
|
337
|
+
|
|
338
|
+
// ✅ Good - Skipped test with clear documentation
|
|
339
|
+
// TODO: Enable when keyboard navigation is implemented
|
|
340
|
+
// See: https://github.com/org/repo/issues/123
|
|
341
|
+
// Expected: Q2 2025
|
|
342
|
+
it.skip('should navigate with keyboard', () => {});
|
|
343
|
+
```
|
|
344
|
+
|
|
332
345
|
### 2. Brittle Selectors
|
|
333
346
|
|
|
334
347
|
```typescript
|
|
@@ -530,9 +543,24 @@ await waitFor(() => {
|
|
|
530
543
|
|
|
531
544
|
await waitFor(() => {
|
|
532
545
|
expect(result.current.data).toBeDefined();
|
|
533
|
-
}, { timeout:
|
|
546
|
+
}, { timeout: 2000 }); // Most async operations (2-3 seconds)
|
|
547
|
+
|
|
548
|
+
// ⚠️ Only use 5+ seconds for truly slow operations
|
|
549
|
+
await waitFor(() => {
|
|
550
|
+
expect(result.current.data).toBeDefined();
|
|
551
|
+
}, { timeout: 5000 }); // Network calls, complex computations only
|
|
534
552
|
```
|
|
535
553
|
|
|
554
|
+
**Timeout Guidelines**:
|
|
555
|
+
- **Fast operations** (< 100ms): `timeout: 100-500`
|
|
556
|
+
- **Standard async** (100ms-2s): `timeout: 2000-3000`
|
|
557
|
+
- **Slow operations** (2s+): `timeout: 5000+` (with documentation)
|
|
558
|
+
|
|
559
|
+
**Avoid**:
|
|
560
|
+
- ❌ Using 5-6 second timeouts for mock operations that resolve immediately
|
|
561
|
+
- ❌ Using `waitFor` for synchronous operations
|
|
562
|
+
- ❌ Unnecessary delays in mock implementations
|
|
563
|
+
|
|
536
564
|
## 🔧 Test Configuration
|
|
537
565
|
|
|
538
566
|
### Vitest Configuration
|
|
@@ -618,7 +646,7 @@ screen.logTestingPlaygroundURL();
|
|
|
618
646
|
|
|
619
647
|
## 📋 Pre-Merge Checklist
|
|
620
648
|
|
|
621
|
-
- [ ] All tests pass without `it.only` or `test.skip`
|
|
649
|
+
- [ ] All tests pass without `it.only` or `test.skip` (see Skipped Test Policy below)
|
|
622
650
|
- [ ] Coverage thresholds are met
|
|
623
651
|
- [ ] Tests use semantic queries (avoid test IDs)
|
|
624
652
|
- [ ] Async operations use proper waiting patterns
|
|
@@ -626,6 +654,7 @@ screen.logTestingPlaygroundURL();
|
|
|
626
654
|
- [ ] Tests focus on behavior, not implementation
|
|
627
655
|
- [ ] Error scenarios are covered
|
|
628
656
|
- [ ] Tests are colocated with source files
|
|
657
|
+
- [ ] Timeout values are appropriate (see Performance Guidelines below)
|
|
629
658
|
|
|
630
659
|
## 🎯 Test Categories & Tags
|
|
631
660
|
|
|
@@ -672,3 +701,216 @@ A world-class test suite demonstrates:
|
|
|
672
701
|
- **Accessibility Awareness** - Tests include a11y considerations
|
|
673
702
|
|
|
674
703
|
Remember: **Tests are not just about catching bugs—they're about building confidence, enabling refactoring, and documenting expected behavior for future developers.**
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## 🔄 Skipped Test Policy
|
|
708
|
+
|
|
709
|
+
### When to Skip Tests
|
|
710
|
+
|
|
711
|
+
**✅ Appropriate Reasons**:
|
|
712
|
+
- Feature is planned but not yet implemented
|
|
713
|
+
- Test documents future feature requirements
|
|
714
|
+
- Feature is blocked by dependencies
|
|
715
|
+
- Test is for experimental/optional feature
|
|
716
|
+
|
|
717
|
+
**❌ Inappropriate Reasons**:
|
|
718
|
+
- Test is flaky (fix the test instead)
|
|
719
|
+
- Test is slow (optimize instead)
|
|
720
|
+
- Feature is deprecated (remove test)
|
|
721
|
+
- Test is temporarily broken (fix immediately)
|
|
722
|
+
|
|
723
|
+
### Requirements for Skipped Tests
|
|
724
|
+
|
|
725
|
+
1. **Clear Documentation**: Add comment explaining why test is skipped
|
|
726
|
+
2. **Reference Tracking**: Link to feature issue or ticket
|
|
727
|
+
3. **Expected Timeline**: Include expected implementation date (if known)
|
|
728
|
+
4. **Quarterly Review**: Review skipped tests quarterly to determine if still relevant
|
|
729
|
+
|
|
730
|
+
### Example
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
// TODO: Enable when keyboard navigation is implemented
|
|
734
|
+
// See: https://github.com/org/repo/issues/123
|
|
735
|
+
// Expected: Q2 2025
|
|
736
|
+
it.skip('should navigate between headers with arrow keys', async () => {
|
|
737
|
+
// Test implementation
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Skipped Test Review Process
|
|
742
|
+
|
|
743
|
+
1. **Quarterly Audit**: Review all skipped tests
|
|
744
|
+
2. **Decision Matrix**:
|
|
745
|
+
- Feature implemented → Enable test
|
|
746
|
+
- Feature cancelled → Remove test
|
|
747
|
+
- Feature deferred → Update timeline
|
|
748
|
+
- Still relevant → Keep skipped with updated timeline
|
|
749
|
+
3. **Documentation**: Update test comments with current status
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
## 📊 Coverage Gap Remediation
|
|
754
|
+
|
|
755
|
+
### Coverage Thresholds
|
|
756
|
+
|
|
757
|
+
| Category | Target | CI Blocking | Rationale |
|
|
758
|
+
|----------|--------|-------------|-----------|
|
|
759
|
+
| **Utils/Hooks** | 95% | Yes | Core business logic, high reuse |
|
|
760
|
+
| **UI Components** | 90% | Yes | User-facing, must be reliable |
|
|
761
|
+
| **Services** | 85% | Yes | API interactions, critical paths |
|
|
762
|
+
| **Validation** | 95% | Yes | Security and data integrity |
|
|
763
|
+
| **Overall Project** | 80% | Yes | Quality baseline |
|
|
764
|
+
|
|
765
|
+
### Identifying Coverage Gaps
|
|
766
|
+
|
|
767
|
+
1. **Run Coverage Report**:
|
|
768
|
+
```bash
|
|
769
|
+
npm test -- --coverage
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
2. **Review Coverage Report**:
|
|
773
|
+
- Check per-file coverage percentages
|
|
774
|
+
- Identify files below thresholds
|
|
775
|
+
- Review uncovered lines/branches
|
|
776
|
+
|
|
777
|
+
3. **Prioritize Gaps**:
|
|
778
|
+
- **Critical**: RBAC, auth, services
|
|
779
|
+
- **High**: Business logic, hooks
|
|
780
|
+
- **Medium**: UI components, utilities
|
|
781
|
+
- **Low**: Type definitions, simple utilities
|
|
782
|
+
|
|
783
|
+
### Remediation Process
|
|
784
|
+
|
|
785
|
+
1. **Analyze Gap**: Understand what's not covered
|
|
786
|
+
2. **Create Test Plan**: Determine test type (unit, integration)
|
|
787
|
+
3. **Write Tests**: Follow TEST_STANDARD.md patterns
|
|
788
|
+
4. **Verify Coverage**: Run coverage report to confirm improvement
|
|
789
|
+
5. **Document**: Add comments for complex test scenarios
|
|
790
|
+
|
|
791
|
+
### Coverage Improvement Checklist
|
|
792
|
+
|
|
793
|
+
- [ ] Identify files below thresholds
|
|
794
|
+
- [ ] Prioritize critical paths
|
|
795
|
+
- [ ] Create test plan for each file
|
|
796
|
+
- [ ] Write tests following TEST_STANDARD.md
|
|
797
|
+
- [ ] Verify coverage improvement
|
|
798
|
+
- [ ] Document complex test scenarios
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
## ⚡ Performance Test Guidelines
|
|
803
|
+
|
|
804
|
+
### Test Execution Time Targets
|
|
805
|
+
|
|
806
|
+
- **Unit Tests**: < 50ms average
|
|
807
|
+
- **Component Tests**: < 100ms average
|
|
808
|
+
- **Integration Tests**: < 500ms average
|
|
809
|
+
- **E2E Tests**: < 2s average
|
|
810
|
+
|
|
811
|
+
### Timeout Best Practices
|
|
812
|
+
|
|
813
|
+
**Standard Timeouts**:
|
|
814
|
+
- Fast operations: `timeout: 100-500`
|
|
815
|
+
- Standard async: `timeout: 2000-3000`
|
|
816
|
+
- Slow operations: `timeout: 5000+` (with documentation)
|
|
817
|
+
|
|
818
|
+
**Optimization Tips**:
|
|
819
|
+
1. Use immediate mock resolution when possible
|
|
820
|
+
2. Avoid unnecessary `setTimeout` in mocks
|
|
821
|
+
3. Use `findBy` queries instead of `waitFor` + `getBy`
|
|
822
|
+
4. Reduce timeout values for fast operations
|
|
823
|
+
|
|
824
|
+
### Memory Management
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
describe('Component with Resources', () => {
|
|
828
|
+
beforeEach(() => {
|
|
829
|
+
vi.useFakeTimers();
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
afterEach(() => {
|
|
833
|
+
// Prevent memory leaks
|
|
834
|
+
vi.clearAllTimers();
|
|
835
|
+
vi.useRealTimers();
|
|
836
|
+
cleanup();
|
|
837
|
+
vi.clearAllMocks();
|
|
838
|
+
localStorage.clear();
|
|
839
|
+
sessionStorage.clear();
|
|
840
|
+
});
|
|
841
|
+
});
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### Performance Monitoring
|
|
845
|
+
|
|
846
|
+
- Monitor test execution times in CI
|
|
847
|
+
- Track slow tests (> 100ms)
|
|
848
|
+
- Alert on tests exceeding thresholds
|
|
849
|
+
- Review and optimize slow tests regularly
|
|
850
|
+
|
|
851
|
+
---
|
|
852
|
+
|
|
853
|
+
## 📁 Large Test File Management
|
|
854
|
+
|
|
855
|
+
### When to Split Test Files
|
|
856
|
+
|
|
857
|
+
**Consider Splitting When**:
|
|
858
|
+
- File exceeds 800 lines
|
|
859
|
+
- File contains multiple distinct feature areas
|
|
860
|
+
- File has slow execution times
|
|
861
|
+
- File is difficult to navigate
|
|
862
|
+
|
|
863
|
+
### Splitting Strategy
|
|
864
|
+
|
|
865
|
+
**By Feature Area**:
|
|
866
|
+
```
|
|
867
|
+
Component.test.tsx → Component.rendering.test.tsx
|
|
868
|
+
→ Component.interactions.test.tsx
|
|
869
|
+
→ Component.state.test.tsx
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
**By Test Type**:
|
|
873
|
+
```
|
|
874
|
+
Component.test.tsx → Component.unit.test.tsx
|
|
875
|
+
→ Component.integration.test.tsx
|
|
876
|
+
→ Component.accessibility.test.tsx
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### Splitting Guidelines
|
|
880
|
+
|
|
881
|
+
1. **Keep Related Tests Together**: Don't split if tests are tightly coupled
|
|
882
|
+
2. **Maintain Test Organization**: Use describe blocks for organization
|
|
883
|
+
3. **Share Test Utilities**: Extract shared setup to test utilities
|
|
884
|
+
4. **Document Structure**: Add README explaining test organization
|
|
885
|
+
|
|
886
|
+
### File Size Recommendations
|
|
887
|
+
|
|
888
|
+
- **< 500 lines**: No splitting needed
|
|
889
|
+
- **500-800 lines**: Consider if organization improves
|
|
890
|
+
- **> 800 lines**: Strongly consider splitting
|
|
891
|
+
- **> 1000 lines**: Should be split
|
|
892
|
+
|
|
893
|
+
---
|
|
894
|
+
|
|
895
|
+
## 📈 Continuous Improvement
|
|
896
|
+
|
|
897
|
+
### Regular Reviews
|
|
898
|
+
|
|
899
|
+
1. **Quarterly**: Review skipped tests, coverage gaps, slow tests
|
|
900
|
+
2. **Monthly**: Review test execution times and patterns
|
|
901
|
+
3. **Per PR**: Ensure new tests follow TEST_STANDARD.md
|
|
902
|
+
|
|
903
|
+
### Metrics to Track
|
|
904
|
+
|
|
905
|
+
- Test execution time trends
|
|
906
|
+
- Coverage percentage trends
|
|
907
|
+
- Number of skipped tests
|
|
908
|
+
- Test helper usage patterns
|
|
909
|
+
- Test file sizes
|
|
910
|
+
|
|
911
|
+
### Resources
|
|
912
|
+
|
|
913
|
+
- [Coverage Gap Analysis](../../docs/testing/coverage-gap-analysis.md)
|
|
914
|
+
- [Test Quality Audit](../../docs/testing/test-quality-audit.md)
|
|
915
|
+
- [Test Helpers Audit](../../docs/testing/test-helpers-audit.md)
|
|
916
|
+
- [Skipped Tests Analysis](../../docs/testing/skipped-tests-analysis.md)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import React from 'react';
|
|
12
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
12
|
+
import { render, screen, fireEvent, waitFor, within } from '@testing-library/react';
|
|
13
13
|
import { axe, toHaveNoViolations } from 'jest-axe';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import { vi, beforeEach, afterEach } from 'vitest';
|
|
@@ -252,7 +252,10 @@ describe('DataTable Accessibility', () => {
|
|
|
252
252
|
}
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
-
it
|
|
255
|
+
it('should have proper aria-selected on selectable rows', async () => {
|
|
256
|
+
const user = userEvent.setup({ delay: 0 });
|
|
257
|
+
|
|
258
|
+
|
|
256
259
|
render(
|
|
257
260
|
<TestWrapper>
|
|
258
261
|
<DataTable
|
|
@@ -264,20 +267,41 @@ describe('DataTable Accessibility', () => {
|
|
|
264
267
|
</TestWrapper>
|
|
265
268
|
);
|
|
266
269
|
|
|
270
|
+
// Wait for table to render with selection enabled
|
|
271
|
+
await waitFor(() => {
|
|
272
|
+
const rows = screen.getAllByRole('row');
|
|
273
|
+
expect(rows.length).toBeGreaterThan(1); // Header + data rows
|
|
274
|
+
|
|
275
|
+
// Verify selection is enabled by checking for selection column header
|
|
276
|
+
const selectHeader = screen.queryByRole('columnheader', { name: /select all/i });
|
|
277
|
+
if (defaultFeatures.selection) {
|
|
278
|
+
expect(selectHeader).toBeInTheDocument();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
267
282
|
// Find data rows (exclude header row)
|
|
268
283
|
const rows = screen.getAllByRole('row');
|
|
269
284
|
const dataRows = rows.slice(1); // Skip header row
|
|
270
285
|
|
|
271
|
-
// Check that
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
286
|
+
// Check that selectable data rows expose aria-selected state
|
|
287
|
+
const selectableRows = dataRows.filter(row => within(row).queryByRole('checkbox'));
|
|
288
|
+
expect(selectableRows.length).toBeGreaterThan(0);
|
|
289
|
+
|
|
290
|
+
selectableRows.forEach(row => {
|
|
276
291
|
const ariaSelected = row.getAttribute('aria-selected');
|
|
277
|
-
console.log(`Row ${index}:`, ariaSelected);
|
|
278
292
|
expect(row).toHaveAttribute('aria-selected');
|
|
279
|
-
expect(ariaSelected).toBe('false');
|
|
293
|
+
expect(ariaSelected).toBe('false');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const firstRowCheckbox = within(selectableRows[0]).getByRole('checkbox');
|
|
297
|
+
await user.click(firstRowCheckbox);
|
|
298
|
+
|
|
299
|
+
const updatedCheckbox = within(selectableRows[0]).getByRole('checkbox');
|
|
300
|
+
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
expect(updatedCheckbox).toHaveAttribute('aria-checked', 'true');
|
|
280
303
|
});
|
|
304
|
+
|
|
281
305
|
});
|
|
282
306
|
|
|
283
307
|
it('should have aria-busy when loading', () => {
|
|
@@ -416,7 +440,8 @@ describe('DataTable Accessibility', () => {
|
|
|
416
440
|
});
|
|
417
441
|
});
|
|
418
442
|
|
|
419
|
-
it
|
|
443
|
+
it('should expose sortable headers in the tab order', () => {
|
|
444
|
+
|
|
420
445
|
render(
|
|
421
446
|
<TestWrapper>
|
|
422
447
|
<DataTable
|
|
@@ -428,12 +453,17 @@ describe('DataTable Accessibility', () => {
|
|
|
428
453
|
</TestWrapper>
|
|
429
454
|
);
|
|
430
455
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
expect(
|
|
456
|
+
const sortableHeaderButtons = screen
|
|
457
|
+
.getAllByRole('columnheader')
|
|
458
|
+
.map(header => header.querySelector<HTMLButtonElement>('button'))
|
|
459
|
+
.filter((button): button is HTMLButtonElement => Boolean(button));
|
|
460
|
+
|
|
461
|
+
expect(sortableHeaderButtons.length).toBeGreaterThan(0);
|
|
462
|
+
|
|
463
|
+
sortableHeaderButtons.forEach(button => {
|
|
464
|
+
expect(button.tabIndex).toBeGreaterThanOrEqual(0);
|
|
465
|
+
expect(button).not.toHaveAttribute('aria-hidden', 'true');
|
|
466
|
+
});
|
|
437
467
|
});
|
|
438
468
|
});
|
|
439
469
|
|