@jmruthers/pace-core 0.5.114 → 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.
Files changed (236) hide show
  1. package/dist/{AuthService-CVgsgtaZ.d.ts → AuthService-D4646R4b.d.ts} +9 -4
  2. package/dist/{DataTable-3JRLZXER.js → DataTable-ZOAKQ3SU.js} +10 -9
  3. package/dist/{UnifiedAuthProvider-KZZUO27W.js → UnifiedAuthProvider-YFN7YGVN.js} +4 -3
  4. package/dist/{api-PKU4PUBO.js → api-TNIBJWLM.js} +3 -3
  5. package/dist/{audit-H4YJJF7R.js → audit-T36HM7IM.js} +2 -2
  6. package/dist/{chunk-4OX5PXHX.js → chunk-2GJ5GL77.js} +4 -5
  7. package/dist/chunk-2GJ5GL77.js.map +1 -0
  8. package/dist/{chunk-5YIZFEUQ.js → chunk-2LM4QQGH.js} +31 -35
  9. package/dist/chunk-2LM4QQGH.js.map +1 -0
  10. package/dist/{chunk-3OGQLOJM.js → chunk-3DBFLLLU.js} +30 -1
  11. package/dist/chunk-3DBFLLLU.js.map +1 -0
  12. package/dist/{chunk-KTHLNIMA.js → chunk-ECOVPXYS.js} +13 -62
  13. package/dist/chunk-ECOVPXYS.js.map +1 -0
  14. package/dist/{chunk-OO3V7W4H.js → chunk-KA3PSVNV.js} +87 -40
  15. package/dist/chunk-KA3PSVNV.js.map +1 -0
  16. package/dist/{chunk-HKWQN44G.js → chunk-KMPWND3F.js} +15 -15
  17. package/dist/{chunk-L36JW4KV.js → chunk-LFS45U62.js} +2 -2
  18. package/dist/{chunk-NEONKMTU.js → chunk-LZYHAL7Y.js} +9 -4
  19. package/dist/{chunk-NEONKMTU.js.map → chunk-LZYHAL7Y.js.map} +1 -1
  20. package/dist/{chunk-BUN7NMV7.js → chunk-O3FTRYEU.js} +2 -2
  21. package/dist/{chunk-F6QB26OS.js → chunk-P3PUOL6B.js} +80 -8
  22. package/dist/chunk-P3PUOL6B.js.map +1 -0
  23. package/dist/{chunk-ZPXWJA4H.js → chunk-PHDAXDHB.js} +131 -5
  24. package/dist/chunk-PHDAXDHB.js.map +1 -0
  25. package/dist/chunk-UJI6WSMD.js +201 -0
  26. package/dist/{chunk-5CDJCTOO.js.map → chunk-UJI6WSMD.js.map} +1 -1
  27. package/dist/{chunk-JHWQNJP3.js → chunk-UKZWNQMB.js} +65 -19
  28. package/dist/{chunk-JHWQNJP3.js.map → chunk-UKZWNQMB.js.map} +1 -1
  29. package/dist/{chunk-7H75SHXZ.js → chunk-VN3OOE35.js} +2 -2
  30. package/dist/{chunk-QKIVSZ2O.js → chunk-WP5I5GLN.js} +2 -2
  31. package/dist/components.d.ts +1 -1
  32. package/dist/components.js +12 -11
  33. package/dist/components.js.map +1 -1
  34. package/dist/hooks.d.ts +1 -1
  35. package/dist/hooks.js +10 -9
  36. package/dist/hooks.js.map +1 -1
  37. package/dist/index.d.ts +4 -4
  38. package/dist/index.js +19 -16
  39. package/dist/index.js.map +1 -1
  40. package/dist/providers.d.ts +2 -2
  41. package/dist/providers.js +3 -2
  42. package/dist/rbac/index.d.ts +82 -1
  43. package/dist/rbac/index.js +13 -10
  44. package/dist/{useToast-DRah6K-g.d.ts → useToast-Cs_g32bg.d.ts} +8 -6
  45. package/dist/utils.js +6 -4
  46. package/dist/utils.js.map +1 -1
  47. package/dist/validation.js +3 -1
  48. package/dist/validation.js.map +1 -1
  49. package/docs/README.md +4 -0
  50. package/docs/api/classes/ColumnFactory.md +1 -1
  51. package/docs/api/classes/ErrorBoundary.md +1 -1
  52. package/docs/api/classes/InvalidScopeError.md +1 -1
  53. package/docs/api/classes/MissingUserContextError.md +1 -1
  54. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  55. package/docs/api/classes/PermissionDeniedError.md +1 -1
  56. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  57. package/docs/api/classes/RBACAuditManager.md +35 -12
  58. package/docs/api/classes/RBACCache.md +1 -1
  59. package/docs/api/classes/RBACEngine.md +1 -1
  60. package/docs/api/classes/RBACError.md +1 -1
  61. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  62. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  63. package/docs/api/classes/StorageUtils.md +1 -1
  64. package/docs/api/enums/FileCategory.md +1 -1
  65. package/docs/api/interfaces/AggregateConfig.md +1 -1
  66. package/docs/api/interfaces/ButtonProps.md +1 -1
  67. package/docs/api/interfaces/CardProps.md +1 -1
  68. package/docs/api/interfaces/ColorPalette.md +1 -1
  69. package/docs/api/interfaces/ColorShade.md +1 -1
  70. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  71. package/docs/api/interfaces/DataRecord.md +1 -1
  72. package/docs/api/interfaces/DataTableAction.md +1 -1
  73. package/docs/api/interfaces/DataTableColumn.md +1 -1
  74. package/docs/api/interfaces/DataTableProps.md +1 -1
  75. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  76. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  77. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  78. package/docs/api/interfaces/EventAppRoleData.md +71 -0
  79. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  80. package/docs/api/interfaces/FileMetadata.md +1 -1
  81. package/docs/api/interfaces/FileReference.md +1 -1
  82. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  83. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  84. package/docs/api/interfaces/FileUploadProps.md +1 -1
  85. package/docs/api/interfaces/FooterProps.md +1 -1
  86. package/docs/api/interfaces/GrantEventAppRoleParams.md +122 -0
  87. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  88. package/docs/api/interfaces/InputProps.md +1 -1
  89. package/docs/api/interfaces/LabelProps.md +1 -1
  90. package/docs/api/interfaces/LoginFormProps.md +1 -1
  91. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  92. package/docs/api/interfaces/NavigationContextType.md +1 -1
  93. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  94. package/docs/api/interfaces/NavigationItem.md +1 -1
  95. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  97. package/docs/api/interfaces/Organisation.md +1 -1
  98. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  99. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  100. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  101. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  102. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  103. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  104. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  105. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  106. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  107. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  108. package/docs/api/interfaces/PaletteData.md +1 -1
  109. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  110. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  111. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  112. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  113. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  114. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  115. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  116. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  117. package/docs/api/interfaces/RBACConfig.md +1 -1
  118. package/docs/api/interfaces/RBACLogger.md +1 -1
  119. package/docs/api/interfaces/RevokeEventAppRoleParams.md +100 -0
  120. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  121. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  122. package/docs/api/interfaces/RoleManagementResult.md +52 -0
  123. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  124. package/docs/api/interfaces/RouteConfig.md +1 -1
  125. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  126. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  127. package/docs/api/interfaces/StorageConfig.md +1 -1
  128. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  129. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  130. package/docs/api/interfaces/StorageListOptions.md +1 -1
  131. package/docs/api/interfaces/StorageListResult.md +1 -1
  132. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  133. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  134. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  135. package/docs/api/interfaces/StyleImport.md +1 -1
  136. package/docs/api/interfaces/SwitchProps.md +1 -1
  137. package/docs/api/interfaces/ToastActionElement.md +1 -1
  138. package/docs/api/interfaces/ToastProps.md +1 -1
  139. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  140. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  141. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  142. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  143. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  144. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  145. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  146. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  147. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  148. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  149. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  150. package/docs/api/interfaces/UserEventAccess.md +1 -1
  151. package/docs/api/interfaces/UserMenuProps.md +1 -1
  152. package/docs/api/interfaces/UserProfile.md +1 -1
  153. package/docs/api/modules.md +43 -16
  154. package/docs/architecture/rpc-function-standards.md +193 -0
  155. package/package.json +1 -1
  156. package/src/__tests__/TEST_STANDARD.md +244 -2
  157. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +46 -16
  158. package/src/components/DataTable/__tests__/keyboard.test.tsx +276 -217
  159. package/src/components/DataTable/components/DataTableCore.tsx +32 -17
  160. package/src/components/DataTable/components/DataTableToolbar.tsx +3 -2
  161. package/src/components/DataTable/components/EditableRow.tsx +18 -1
  162. package/src/components/DataTable/components/ImportModal.tsx +25 -2
  163. package/src/components/DataTable/components/ViewRowModal.tsx +1 -1
  164. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +735 -0
  165. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +572 -0
  166. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +708 -0
  167. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +451 -0
  168. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +456 -0
  169. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +454 -0
  170. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +462 -0
  171. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +423 -0
  172. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +393 -0
  173. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +617 -0
  174. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +734 -0
  175. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +412 -0
  176. package/src/components/DataTable/hooks/useTableHandlers.ts +4 -0
  177. package/src/components/EventSelector/EventSelector.tsx +5 -25
  178. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +12 -7
  179. package/src/components/PaceAppLayout/PaceAppLayout.tsx +4 -0
  180. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +7 -2
  181. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +13 -8
  182. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +109 -100
  183. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +18 -13
  184. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +17 -12
  185. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +2 -0
  186. package/src/components/PaceLoginPage/PaceLoginPage.tsx +11 -1
  187. package/src/components/PasswordReset/PasswordChangeForm.test.tsx +2 -2
  188. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +648 -0
  189. package/src/components/ProtectedRoute/ProtectedRoute.tsx +10 -7
  190. package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +4 -12
  191. package/src/components/Select/Select.tsx +8 -0
  192. package/src/components/Toast/Toast.test.tsx +8 -7
  193. package/src/components/Toast/Toast.tsx +4 -4
  194. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +367 -3
  195. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +916 -0
  196. package/src/hooks/useEventTheme.ts +49 -18
  197. package/src/hooks/usePermissionCache.ts +5 -3
  198. package/src/hooks/useSecureDataAccess.ts +11 -1
  199. package/src/hooks/useToast.ts +11 -12
  200. package/src/providers/services/EventServiceProvider.tsx +15 -8
  201. package/src/rbac/__tests__/cache-invalidation.test.ts +385 -0
  202. package/src/rbac/audit.test.ts +206 -0
  203. package/src/rbac/audit.ts +37 -2
  204. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +26 -23
  205. package/src/rbac/errors.test.ts +340 -0
  206. package/src/rbac/hooks/index.ts +9 -0
  207. package/src/rbac/hooks/useResolvedScope.test.ts +1063 -0
  208. package/src/rbac/hooks/useRoleManagement.test.ts +908 -0
  209. package/src/rbac/hooks/useRoleManagement.ts +255 -0
  210. package/src/services/AuthService.ts +10 -0
  211. package/src/services/EventService.ts +111 -50
  212. package/src/services/__tests__/AuthService.test.ts +1 -1
  213. package/src/services/__tests__/EventService.test.ts +60 -45
  214. package/src/services/interfaces/IEventService.ts +1 -1
  215. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +320 -0
  216. package/src/utils/__tests__/logger.unit.test.ts +398 -0
  217. package/src/utils/__tests__/validation.unit.test.ts +225 -1
  218. package/src/utils/file-reference.test.ts +214 -0
  219. package/dist/chunk-3OGQLOJM.js.map +0 -1
  220. package/dist/chunk-4OX5PXHX.js.map +0 -1
  221. package/dist/chunk-5CDJCTOO.js +0 -190
  222. package/dist/chunk-5YIZFEUQ.js.map +0 -1
  223. package/dist/chunk-F6QB26OS.js.map +0 -1
  224. package/dist/chunk-KTHLNIMA.js.map +0 -1
  225. package/dist/chunk-OO3V7W4H.js.map +0 -1
  226. package/dist/chunk-ZPXWJA4H.js.map +0 -1
  227. package/src/rbac/audit-enhanced.ts +0 -351
  228. /package/dist/{DataTable-3JRLZXER.js.map → DataTable-ZOAKQ3SU.js.map} +0 -0
  229. /package/dist/{UnifiedAuthProvider-KZZUO27W.js.map → UnifiedAuthProvider-YFN7YGVN.js.map} +0 -0
  230. /package/dist/{api-PKU4PUBO.js.map → api-TNIBJWLM.js.map} +0 -0
  231. /package/dist/{audit-H4YJJF7R.js.map → audit-T36HM7IM.js.map} +0 -0
  232. /package/dist/{chunk-HKWQN44G.js.map → chunk-KMPWND3F.js.map} +0 -0
  233. /package/dist/{chunk-L36JW4KV.js.map → chunk-LFS45U62.js.map} +0 -0
  234. /package/dist/{chunk-BUN7NMV7.js.map → chunk-O3FTRYEU.js.map} +0 -0
  235. /package/dist/{chunk-7H75SHXZ.js.map → chunk-VN3OOE35.js.map} +0 -0
  236. /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: 5000 }); // Slow operations only
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.skip('should have proper aria-selected on selectable rows', () => {
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 we have data rows and they have aria-selected
272
- expect(dataRows.length).toBeGreaterThan(0);
273
-
274
- dataRows.forEach((row, index) => {
275
- // Initially, rows should not be selected
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'); // Should be false initially
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.skip('should have proper tab order', () => {
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
- // Check that sortable headers are focusable
432
- const sortableHeaders = screen.getAllByRole('columnheader').filter(header =>
433
- header.hasAttribute('tabindex') && header.getAttribute('tabindex') === '0'
434
- );
435
-
436
- expect(sortableHeaders.length).toBeGreaterThan(0);
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