@jmruthers/pace-core 0.5.87 → 0.5.88

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 (242) hide show
  1. package/dist/{AuthService-Df3IozMG.d.ts → AuthService-DcTI5Ov4.d.ts} +9 -0
  2. package/dist/{DataTable-FA6EUX5M.js → DataTable-PWBMKMOG.js} +7 -7
  3. package/dist/{PublicLoadingSpinner-DecuJBX0.d.ts → PublicLoadingSpinner-BQXD1fbO.d.ts} +160 -130
  4. package/dist/{UnifiedAuthProvider-K2IZAY5F.js → UnifiedAuthProvider-5D3HEQND.js} +4 -4
  5. package/dist/{UnifiedAuthProvider-B391Aqum.d.ts → UnifiedAuthProvider-BVKmQd9u.d.ts} +4 -0
  6. package/dist/auth-DReDSLq9.d.ts +16 -0
  7. package/dist/{chunk-CBSD3BZ3.js → chunk-3RZBKQ5Y.js} +2 -6
  8. package/dist/{chunk-CBSD3BZ3.js.map → chunk-3RZBKQ5Y.js.map} +1 -1
  9. package/dist/{chunk-NTW3KGS4.js → chunk-6UHXQH7P.js} +5 -5
  10. package/dist/{chunk-YVUZWLQG.js → chunk-AQGF5OG7.js} +3 -3
  11. package/dist/{chunk-CVMVPYAL.js → chunk-BDZUMRBD.js} +3 -5
  12. package/dist/chunk-BDZUMRBD.js.map +1 -0
  13. package/dist/{chunk-KAY3K5TP.js → chunk-BNXBJOGL.js} +4 -4
  14. package/dist/{chunk-I7O3RSMN.js → chunk-CJIZS3UE.js} +1298 -769
  15. package/dist/chunk-CJIZS3UE.js.map +1 -0
  16. package/dist/{chunk-S3JKDMD5.js → chunk-CXKMRKRF.js} +4 -4
  17. package/dist/{chunk-5BN3YGNK.js → chunk-DP5X5ORK.js} +217 -27
  18. package/dist/chunk-DP5X5ORK.js.map +1 -0
  19. package/dist/{chunk-ZFLOV3OM.js → chunk-H3P2RGKZ.js} +352 -16
  20. package/dist/chunk-H3P2RGKZ.js.map +1 -0
  21. package/dist/{chunk-RIXPZJUB.js → chunk-KTPG5VCH.js} +2 -2
  22. package/dist/{chunk-WUXCWRL6.js → chunk-XJ2HZOBU.js} +6 -1
  23. package/dist/chunk-XJ2HZOBU.js.map +1 -0
  24. package/dist/{chunk-2FQEQUJT.js → chunk-XXVM53P4.js} +4 -4
  25. package/dist/{chunk-I2VVV5PQ.js → chunk-YY4YYM3E.js} +2 -2
  26. package/dist/components.d.ts +6 -55
  27. package/dist/components.js +24 -205
  28. package/dist/components.js.map +1 -1
  29. package/dist/{file-reference-9xUOnwyt.d.ts → file-reference-C9isKNPn.d.ts} +67 -2
  30. package/dist/hooks.js +9 -8
  31. package/dist/hooks.js.map +1 -1
  32. package/dist/index.d.ts +152 -26
  33. package/dist/index.js +64 -194
  34. package/dist/index.js.map +1 -1
  35. package/dist/providers.d.ts +5 -3
  36. package/dist/providers.js +3 -3
  37. package/dist/rbac/index.js +8 -8
  38. package/dist/types.d.ts +2 -1
  39. package/dist/types.js +3 -3
  40. package/dist/utils.js +2 -2
  41. package/docs/DOCUMENTATION_AUDIT.md +6 -6
  42. package/docs/DOCUMENTATION_STANDARD.md +137 -0
  43. package/docs/README.md +1 -1
  44. package/docs/api/classes/ColumnFactory.md +1 -1
  45. package/docs/api/classes/ErrorBoundary.md +1 -1
  46. package/docs/api/classes/InvalidScopeError.md +1 -1
  47. package/docs/api/classes/MissingUserContextError.md +1 -1
  48. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  49. package/docs/api/classes/PermissionDeniedError.md +1 -1
  50. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  51. package/docs/api/classes/RBACAuditManager.md +1 -1
  52. package/docs/api/classes/RBACCache.md +1 -1
  53. package/docs/api/classes/RBACEngine.md +1 -1
  54. package/docs/api/classes/RBACError.md +1 -1
  55. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  56. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  57. package/docs/api/classes/StorageUtils.md +83 -40
  58. package/docs/api/enums/FileCategory.md +56 -1
  59. package/docs/api/interfaces/AggregateConfig.md +1 -1
  60. package/docs/api/interfaces/ButtonProps.md +1 -1
  61. package/docs/api/interfaces/CardProps.md +1 -1
  62. package/docs/api/interfaces/ColorPalette.md +1 -1
  63. package/docs/api/interfaces/ColorShade.md +1 -1
  64. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  65. package/docs/api/interfaces/DataRecord.md +1 -1
  66. package/docs/api/interfaces/DataTableAction.md +1 -1
  67. package/docs/api/interfaces/DataTableColumn.md +1 -1
  68. package/docs/api/interfaces/DataTableProps.md +1 -1
  69. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  70. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  71. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  72. package/docs/api/interfaces/EventLogoProps.md +11 -11
  73. package/docs/api/interfaces/FileDisplayProps.md +10 -10
  74. package/docs/api/interfaces/FileMetadata.md +1 -1
  75. package/docs/api/interfaces/FileReference.md +1 -1
  76. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  77. package/docs/api/interfaces/FileUploadOptions.md +8 -8
  78. package/docs/api/interfaces/FileUploadProps.md +137 -42
  79. package/docs/api/interfaces/FooterProps.md +1 -1
  80. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  81. package/docs/api/interfaces/InputProps.md +1 -1
  82. package/docs/api/interfaces/LabelProps.md +1 -1
  83. package/docs/api/interfaces/LoginFormProps.md +1 -1
  84. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  85. package/docs/api/interfaces/NavigationContextType.md +1 -1
  86. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  87. package/docs/api/interfaces/NavigationItem.md +1 -1
  88. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  89. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  90. package/docs/api/interfaces/Organisation.md +1 -1
  91. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  92. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  93. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  94. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  95. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  96. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  97. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  98. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  99. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  100. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  101. package/docs/api/interfaces/PaletteData.md +1 -1
  102. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  103. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  104. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  105. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  106. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  107. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  108. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  109. package/docs/api/interfaces/RBACConfig.md +1 -1
  110. package/docs/api/interfaces/RBACLogger.md +1 -1
  111. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  112. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  113. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  114. package/docs/api/interfaces/RouteConfig.md +1 -1
  115. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  116. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  117. package/docs/api/interfaces/StorageConfig.md +1 -1
  118. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  119. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  120. package/docs/api/interfaces/StorageListOptions.md +1 -1
  121. package/docs/api/interfaces/StorageListResult.md +1 -1
  122. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  123. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  124. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  125. package/docs/api/interfaces/StyleImport.md +1 -1
  126. package/docs/api/interfaces/SwitchProps.md +1 -1
  127. package/docs/api/interfaces/ToastActionElement.md +1 -1
  128. package/docs/api/interfaces/ToastProps.md +1 -1
  129. package/docs/api/interfaces/UnifiedAuthContextType.md +83 -50
  130. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  131. package/docs/api/interfaces/UseEventLogoOptions.md +74 -0
  132. package/docs/api/interfaces/UseEventLogoReturn.md +81 -0
  133. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  134. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  135. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  136. package/docs/api/interfaces/UsePublicEventLogoReturn.md +6 -6
  137. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  138. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  139. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  140. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  141. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  142. package/docs/api/interfaces/UserEventAccess.md +11 -11
  143. package/docs/api/interfaces/UserMenuProps.md +1 -1
  144. package/docs/api/interfaces/UserProfile.md +1 -1
  145. package/docs/api/modules.md +290 -95
  146. package/docs/api-reference/components.md +1 -18
  147. package/docs/api-reference/hooks.md +1 -4
  148. package/docs/best-practices/testing.md +2 -0
  149. package/docs/documentation-index.md +1 -1
  150. package/docs/getting-started/faq.md +1 -1
  151. package/docs/implementation-guides/file-reference-system.md +592 -58
  152. package/docs/implementation-guides/file-upload-storage.md +137 -73
  153. package/docs/rbac/super-admin-guide.md +18 -70
  154. package/docs/testing/README.md +2 -0
  155. package/package.json +1 -1
  156. package/src/__tests__/TEST_STANDARD.md +674 -0
  157. package/src/__tests__/helpers/test-utils.tsx +3 -2
  158. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx.skip → DataTable.comprehensive.test.tsx} +17 -18
  159. package/src/components/DataTable/__tests__/{DataTable.test.tsx.skip → DataTable.test.tsx} +14 -22
  160. package/src/components/DataTable/__tests__/{ssr.strict-mode.test.tsx.skip → ssr.strict-mode.test.tsx} +42 -47
  161. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +1 -1
  162. package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +13 -4
  163. package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +1 -1
  164. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +10 -6
  165. package/src/components/FileDisplay/FileDisplay.test.tsx +257 -0
  166. package/src/components/{FileDisplay.tsx → FileDisplay/FileDisplay.tsx} +111 -10
  167. package/src/components/FileDisplay/index.tsx +4 -0
  168. package/src/components/FileUpload/FileUpload.test.tsx +171 -621
  169. package/src/components/FileUpload/FileUpload.tsx +512 -168
  170. package/src/components/FileUpload/index.tsx +4 -0
  171. package/src/components/Progress/Progress.test.tsx +38 -0
  172. package/src/components/PublicLayout/EventLogo.tsx +6 -4
  173. package/src/components/Select/Select.test.tsx +1 -1
  174. package/src/components/SessionRestorationLoader.tsx +48 -0
  175. package/src/components/Toast/Toast.tsx +13 -8
  176. package/src/components/index.ts +16 -16
  177. package/src/hooks/__tests__/ServiceHooks.test.tsx +615 -0
  178. package/src/hooks/public/usePublicEventLogo.ts +16 -20
  179. package/src/hooks/useEventLogo.ts +316 -0
  180. package/src/hooks/useEvents.ts +0 -5
  181. package/src/hooks/useFileReference.test.ts +659 -0
  182. package/src/hooks/useFileReference.ts +207 -3
  183. package/src/hooks/useSessionRestoration.ts +64 -0
  184. package/src/index.ts +17 -5
  185. package/src/providers/{UnifiedAuthProvider.test.simple.tsx → UnifiedAuthProvider.smoke.test.tsx} +81 -60
  186. package/src/providers/services/AuthServiceProvider.tsx +27 -3
  187. package/src/providers/services/UnifiedAuthProvider.tsx +34 -5
  188. package/src/rbac/{engine.test.simple.ts → RBACEngine.smoke.test.ts} +17 -12
  189. package/src/services/AuthService.ts +142 -20
  190. package/src/services/EventService.ts +0 -4
  191. package/src/types/auth.ts +15 -0
  192. package/src/types/file-reference.ts +73 -1
  193. package/src/types/index.ts +1 -0
  194. package/src/utils/__tests__/organisationContext.unit.test.ts +2 -4
  195. package/src/utils/appNameResolver.simple.test.ts +99 -29
  196. package/src/utils/file-reference.test.ts +535 -0
  197. package/src/utils/file-reference.ts +200 -30
  198. package/src/utils/organisationContext.test.ts +5 -19
  199. package/src/utils/organisationContext.ts +3 -5
  200. package/src/utils/storage/README.md +269 -262
  201. package/src/utils/storage/config.ts +9 -0
  202. package/src/utils/storage/helpers.test.ts +631 -0
  203. package/src/utils/storage/helpers.ts +112 -14
  204. package/src/utils/storage/index.ts +3 -0
  205. package/src/validation/__tests__/sanitization.unit.test.ts +1 -1
  206. package/src/validation/__tests__/schemaUtils.unit.test.ts +1 -1
  207. package/src/validation/__tests__/user.unit.test.ts +1 -1
  208. package/dist/chunk-5BN3YGNK.js.map +0 -1
  209. package/dist/chunk-CVMVPYAL.js.map +0 -1
  210. package/dist/chunk-I7O3RSMN.js.map +0 -1
  211. package/dist/chunk-WUXCWRL6.js.map +0 -1
  212. package/dist/chunk-ZFLOV3OM.js.map +0 -1
  213. package/docs/CONTENT_AUDIT_REPORT.md +0 -253
  214. package/docs/STYLE_GUIDE.md +0 -37
  215. package/examples/RBAC/__tests__/PermissionExample.test.tsx +0 -150
  216. package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +0 -159
  217. package/src/__tests__/TEST_GUIDE_CURSOR.md +0 -1605
  218. package/src/__tests__/TEST_GUIDE_HUMAN.md +0 -103
  219. package/src/components/FileUpload/FileUpload.example.tsx +0 -218
  220. package/src/components/FileUpload/index.ts +0 -6
  221. package/src/components/FileUpload.tsx +0 -176
  222. package/src/components/Progress/index.ts +0 -3
  223. package/src/components/PublicLayout/__tests__/EventLogo.test.tsx +0 -666
  224. package/src/components/SuperAdminGuard.tsx +0 -116
  225. package/src/components/__tests__/FileDisplay.test.tsx +0 -575
  226. package/src/components/__tests__/FileUpload.test.tsx +0 -446
  227. package/src/components/__tests__/SuperAdminGuard.test.tsx +0 -627
  228. package/src/components/examples/PermissionExample.tsx +0 -173
  229. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -583
  230. package/src/hooks/__tests__/usePublicEventLogo.unit.test.ts +0 -640
  231. package/src/types/__tests__/file-reference.test.ts +0 -447
  232. package/src/utils/__tests__/file-reference.test.ts +0 -383
  233. /package/dist/{DataTable-FA6EUX5M.js.map → DataTable-PWBMKMOG.js.map} +0 -0
  234. /package/dist/{UnifiedAuthProvider-K2IZAY5F.js.map → UnifiedAuthProvider-5D3HEQND.js.map} +0 -0
  235. /package/dist/{chunk-NTW3KGS4.js.map → chunk-6UHXQH7P.js.map} +0 -0
  236. /package/dist/{chunk-YVUZWLQG.js.map → chunk-AQGF5OG7.js.map} +0 -0
  237. /package/dist/{chunk-KAY3K5TP.js.map → chunk-BNXBJOGL.js.map} +0 -0
  238. /package/dist/{chunk-S3JKDMD5.js.map → chunk-CXKMRKRF.js.map} +0 -0
  239. /package/dist/{chunk-RIXPZJUB.js.map → chunk-KTPG5VCH.js.map} +0 -0
  240. /package/dist/{chunk-2FQEQUJT.js.map → chunk-XXVM53P4.js.map} +0 -0
  241. /package/dist/{chunk-I2VVV5PQ.js.map → chunk-YY4YYM3E.js.map} +0 -0
  242. /package/src/providers/{OrganisationProvider.test.simple.tsx → OrganisationProvider.context.test.tsx} +0 -0
@@ -0,0 +1,674 @@
1
+ # Testing Standard
2
+
3
+ The authoritative guide for writing world-class tests in the pace-core project. Every test contribution should follow these standards to ensure reliability, maintainability, and comprehensive coverage.
4
+
5
+ ## 🎯 Testing Philosophy
6
+
7
+ ### Core Principles
8
+
9
+ 1. **Test Behavior, Not Implementation** - Focus on what users see and do, not internal code structure
10
+ 2. **Fast & Reliable** - Tests should run quickly and produce consistent results
11
+ 3. **Independent & Isolated** - Tests should not depend on each other or external state
12
+ 4. **Self-Documenting** - Tests should clearly express intent and expected behavior
13
+ 5. **Maintainable** - Tests should be easy to update when requirements change
14
+
15
+ ### The Testing Pyramid
16
+
17
+ ```
18
+ /\
19
+ / \ E2E Tests (Few)
20
+ /____\
21
+ / \ Integration Tests (Some)
22
+ /________\
23
+ Unit Tests (Many)
24
+ ```
25
+
26
+ **Distribution Guidelines:**
27
+ - **Unit Tests (80%)**: Fast, isolated tests for individual functions/components
28
+ - **Integration Tests (15%)**: Test component interactions and data flow
29
+ - **E2E Tests (5%)**: Full user journey validation
30
+
31
+ ## 🏗️ Test Organization
32
+
33
+ ### File Structure
34
+
35
+ **✅ Preferred: Colocated Tests**
36
+ ```
37
+ src/
38
+ ├── components/
39
+ │ ├── Button/
40
+ │ │ ├── Button.tsx
41
+ │ │ └── Button.test.tsx ✅ Next to source
42
+ │ └── DataTable/
43
+ │ ├── DataTable.tsx
44
+ │ ├── DataTable.test.tsx ✅ Main component tests
45
+ │ └── __tests__/ ✅ For multiple test files
46
+ │ └── DataTable.integration.test.tsx
47
+ ├── hooks/
48
+ │ ├── useCounter.ts
49
+ │ └── useCounter.test.ts ✅ Colocated
50
+ └── services/
51
+ ├── AuthService.ts
52
+ └── AuthService.test.ts ✅ Colocated
53
+ ```
54
+
55
+ **When to Use `__tests__/` Directory:**
56
+ - Multiple test files for one source file (unit + integration)
57
+ - Shared test utilities within a module
58
+ - Integration tests spanning multiple components
59
+
60
+ **Central `src/__tests__/` Directory - ONLY for:**
61
+ - Cross-module integration tests
62
+ - Shared test utilities and fixtures
63
+ - Test configuration files
64
+
65
+ ### File Naming Conventions
66
+
67
+ - `ComponentName.test.tsx` - Component tests
68
+ - `hookName.test.ts` - Hook tests
69
+ - `serviceName.test.ts` - Service tests
70
+ - `feature.integration.test.tsx` - Integration tests
71
+ - `utils.unit.test.ts` - Utility function tests
72
+
73
+ ## 📝 Test Structure & Naming
74
+
75
+ ### Describe Block Organization
76
+
77
+ ```typescript
78
+ describe('ComponentName', () => {
79
+ describe('Rendering', () => {
80
+ it('renders with default props', () => {});
81
+ it('renders with custom content', () => {});
82
+ });
83
+
84
+ describe('User Interactions', () => {
85
+ it('handles click events', () => {});
86
+ it('handles keyboard navigation', () => {});
87
+ });
88
+
89
+ describe('State Management', () => {
90
+ it('updates state on user input', () => {});
91
+ it('resets state on form submission', () => {});
92
+ });
93
+
94
+ describe('Error Handling', () => {
95
+ it('displays error message on validation failure', () => {});
96
+ it('recovers from network errors', () => {});
97
+ });
98
+ });
99
+ ```
100
+
101
+ ### Test Naming Best Practices
102
+
103
+ ```typescript
104
+ // ✅ Good - Descriptive and behavior-focused
105
+ it('renders submit button when form is valid', () => {});
106
+ it('disables submit button when required fields are empty', () => {});
107
+ it('shows loading spinner during form submission', () => {});
108
+
109
+ // ❌ Bad - Vague or implementation-focused
110
+ it('works correctly', () => {});
111
+ it('test button component', () => {});
112
+ it('should call onClick prop', () => {});
113
+ ```
114
+
115
+ ## 🧪 Testing Patterns
116
+
117
+ ### 1. Component Testing (AAA Pattern)
118
+
119
+ ```typescript
120
+ describe('Button Component', () => {
121
+ it('handles click events with custom handler', async () => {
122
+ // Arrange
123
+ const handleClick = vi.fn();
124
+ const user = userEvent.setup();
125
+
126
+ render(<Button onClick={handleClick}>Click me</Button>);
127
+
128
+ // Act
129
+ await user.click(screen.getByRole('button', { name: 'Click me' }));
130
+
131
+ // Assert
132
+ expect(handleClick).toHaveBeenCalledTimes(1);
133
+ });
134
+ });
135
+ ```
136
+
137
+ ### 2. Hook Testing
138
+
139
+ ```typescript
140
+ describe('useCounter Hook', () => {
141
+ it('initializes with provided value', () => {
142
+ const { result } = renderHook(() => useCounter(5));
143
+
144
+ expect(result.current.count).toBe(5);
145
+ });
146
+
147
+ it('increments count when increment is called', () => {
148
+ const { result } = renderHook(() => useCounter(0));
149
+
150
+ act(() => {
151
+ result.current.increment();
152
+ });
153
+
154
+ expect(result.current.count).toBe(1);
155
+ });
156
+ });
157
+ ```
158
+
159
+ ### 3. Service Testing
160
+
161
+ ```typescript
162
+ describe('AuthService', () => {
163
+ let mockSupabase: ReturnType<typeof createMockSupabaseClient>;
164
+ let authService: AuthService;
165
+
166
+ beforeEach(() => {
167
+ mockSupabase = createMockSupabaseClient();
168
+ authService = new AuthService(mockSupabase);
169
+ });
170
+
171
+ it('signs in user with valid credentials', async () => {
172
+ const mockUser = { id: '123', email: 'user@example.com' };
173
+
174
+ mockSupabase.auth.signInWithPassword.mockResolvedValue({
175
+ data: { user: mockUser, session: {} },
176
+ error: null
177
+ });
178
+
179
+ const result = await authService.signIn({
180
+ email: 'user@example.com',
181
+ password: 'password'
182
+ });
183
+
184
+ expect(result.user).toEqual(mockUser);
185
+ });
186
+ });
187
+ ```
188
+
189
+ ### 4. Integration Testing
190
+
191
+ ```typescript
192
+ describe('User Registration Flow', () => {
193
+ it('completes full registration workflow', async () => {
194
+ const user = userEvent.setup();
195
+
196
+ render(<RegistrationForm />);
197
+
198
+ // Fill form
199
+ await user.type(screen.getByLabelText('Email'), 'user@example.com');
200
+ await user.type(screen.getByLabelText('Password'), 'password123');
201
+
202
+ // Submit
203
+ await user.click(screen.getByRole('button', { name: 'Register' }));
204
+
205
+ // Verify success
206
+ expect(await screen.findByText('Registration successful')).toBeInTheDocument();
207
+ });
208
+ });
209
+ ```
210
+
211
+ ## 🔍 Query Best Practices
212
+
213
+ ### Semantic Queries (Preferred)
214
+
215
+ ```typescript
216
+ // ✅ Excellent - Semantic and accessible
217
+ screen.getByRole('button', { name: 'Submit' });
218
+ screen.getByLabelText('Email address');
219
+ screen.getByText('Welcome back');
220
+ screen.getByPlaceholderText('Enter your name');
221
+
222
+ // ✅ Good - Content-based
223
+ screen.getByDisplayValue('current-value');
224
+ screen.getByTitle('Close dialog');
225
+
226
+ // ⚠️ Use sparingly - When semantic queries aren't sufficient
227
+ screen.getByTestId('complex-widget');
228
+ ```
229
+
230
+ ### Query Priority Order
231
+
232
+ 1. **getByRole** - Most accessible and semantic
233
+ 2. **getByLabelText** - Form elements with labels
234
+ 3. **getByText** - Visible text content
235
+ 4. **getByDisplayValue** - Form elements with values
236
+ 5. **getByAltText** - Images with alt text
237
+ 6. **getByTitle** - Elements with title attribute
238
+ 7. **getByTestId** - Last resort for complex cases
239
+
240
+ ### Async Query Patterns
241
+
242
+ ```typescript
243
+ // ✅ Good - Built-in waiting with findBy
244
+ const button = await screen.findByRole('button', { name: 'Submit' });
245
+
246
+ // ✅ Good - Wait for specific condition
247
+ await waitFor(() => {
248
+ expect(screen.getByText('Data loaded')).toBeInTheDocument();
249
+ });
250
+
251
+ // ❌ Bad - Waiting for synchronous operations
252
+ await waitFor(() => {
253
+ expect(result.current.count).toBe(0); // This is immediate!
254
+ });
255
+ ```
256
+
257
+ ## 🔧 Mocking & Test Doubles
258
+
259
+ ### Mock Strategy
260
+
261
+ ```typescript
262
+ // ✅ Good - Mock external dependencies only
263
+ vi.mock('../api/users', () => ({
264
+ fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'John' })
265
+ }));
266
+
267
+ // ✅ Good - Mock complex external libraries
268
+ vi.mock('react-router-dom', () => ({
269
+ useNavigate: () => vi.fn(),
270
+ useLocation: () => ({ pathname: '/test' })
271
+ }));
272
+
273
+ // ❌ Bad - Over-mocking internal utilities
274
+ vi.mock('../utils/formatDate'); // Usually not necessary
275
+ ```
276
+
277
+ ### Multi-Call Mocking
278
+
279
+ ```typescript
280
+ // ✅ Good - Handle different calls with mockImplementation
281
+ mockSupabase.rpc.mockImplementation((functionName, params) => {
282
+ if (functionName === 'get_user') {
283
+ return Promise.resolve({ data: { id: '1' }, error: null });
284
+ }
285
+ if (functionName === 'get_permissions') {
286
+ return Promise.resolve({ data: permissions, error: null });
287
+ }
288
+ return Promise.resolve({ data: null, error: null });
289
+ });
290
+ ```
291
+
292
+ ### Cleanup Best Practices
293
+
294
+ ```typescript
295
+ describe('Component with Resources', () => {
296
+ beforeEach(() => {
297
+ vi.useFakeTimers();
298
+ });
299
+
300
+ afterEach(() => {
301
+ // Clean up timers
302
+ vi.clearAllTimers();
303
+ vi.useRealTimers();
304
+
305
+ // React Testing Library cleanup
306
+ cleanup();
307
+
308
+ // Clear all mocks
309
+ vi.clearAllMocks();
310
+
311
+ // Clear storage
312
+ localStorage.clear();
313
+ sessionStorage.clear();
314
+ });
315
+ });
316
+ ```
317
+
318
+ ## 🚫 Anti-Patterns to Avoid
319
+
320
+ ### 1. Testing Implementation Details
321
+
322
+ ```typescript
323
+ // ❌ Bad - Testing internal state
324
+ expect(component.state.isVisible).toBe(true);
325
+ expect(wrapper.find('.hidden-class')).toHaveLength(0);
326
+
327
+ // ✅ Good - Testing observable behavior
328
+ expect(screen.getByText('Visible content')).toBeInTheDocument();
329
+ expect(screen.queryByText('Hidden content')).not.toBeInTheDocument();
330
+ ```
331
+
332
+ ### 2. Brittle Selectors
333
+
334
+ ```typescript
335
+ // ❌ Bad - Fragile and non-semantic
336
+ screen.getByClassName('btn-primary');
337
+ container.querySelector('.form > div:nth-child(2)');
338
+
339
+ // ✅ Good - Semantic and stable
340
+ screen.getByRole('button', { name: 'Submit' });
341
+ screen.getByLabelText('Email address');
342
+ ```
343
+
344
+ ### 3. Testing Multiple Concerns
345
+
346
+ ```typescript
347
+ // ❌ Bad - Too many assertions in one test
348
+ it('renders form and validates and submits and shows success', () => {
349
+ // Multiple unrelated assertions
350
+ });
351
+
352
+ // ✅ Good - Single responsibility
353
+ it('renders form with all required fields', () => {});
354
+ it('validates email format on blur', () => {});
355
+ it('submits form with valid data', () => {});
356
+ it('shows success message after submission', () => {});
357
+ ```
358
+
359
+ ## 📊 Coverage Standards
360
+
361
+ ### Coverage Thresholds
362
+
363
+ | Component Type | Target | CI Blocking | Rationale |
364
+ |----------------|--------|-------------|-----------|
365
+ | **Utils/Hooks** | 95% | Yes | Core business logic, high reuse |
366
+ | **UI Components** | 90% | Yes | User-facing, must be reliable |
367
+ | **Services** | 85% | Yes | API interactions, critical paths |
368
+ | **Validation** | 95% | Yes | Security and data integrity |
369
+ | **Overall Project** | 82% | Yes | Quality baseline |
370
+
371
+ ### What to Test
372
+
373
+ - ✅ **Happy paths** - Normal user workflows
374
+ - ✅ **Error conditions** - Network failures, validation errors
375
+ - ✅ **Edge cases** - Empty states, boundary values
376
+ - ✅ **User interactions** - Clicks, form submissions, navigation
377
+ - ✅ **State changes** - Loading states, data updates
378
+ - ✅ **Accessibility** - Keyboard navigation, screen reader support
379
+
380
+ ### What NOT to Test
381
+
382
+ - ❌ **Third-party libraries** - Already tested by maintainers
383
+ - ❌ **Generated code** - Auto-generated types, configs
384
+ - ❌ **Pure styling** - CSS classes without behavior
385
+ - ❌ **Type definitions** - Interfaces and type-only exports
386
+
387
+ ## 🔐 RBAC Testing Patterns
388
+
389
+ ### Provider Testing
390
+
391
+ ```typescript
392
+ describe('RBACProvider', () => {
393
+ it('requires organisation context when configured', () => {
394
+ const { result } = renderHook(() => useRBAC());
395
+
396
+ expect(() => result.current.requireOrganisationContext()).toThrow(
397
+ 'Organisation context is required'
398
+ );
399
+ });
400
+
401
+ it('refreshes permissions when event changes', async () => {
402
+ const { result } = renderHook(() => useRBAC());
403
+
404
+ act(() => {
405
+ result.current.setSelectedEventId('new-event-id');
406
+ });
407
+
408
+ await waitFor(() => {
409
+ expect(result.current.rbacLoading).toBe(false);
410
+ });
411
+ });
412
+ });
413
+ ```
414
+
415
+ ### Permission Hook Testing
416
+
417
+ ```typescript
418
+ describe('useHasPermission', () => {
419
+ it('returns true when user has required permission', async () => {
420
+ setupRBACMock([
421
+ { permission_type: 'read:users', role_name: 'viewer' }
422
+ ]);
423
+
424
+ const { result } = renderHook(() =>
425
+ useHasPermission('user-123', scope, 'read:users')
426
+ );
427
+
428
+ await waitFor(() => {
429
+ expect(result.current.hasPermission).toBe(true);
430
+ });
431
+ });
432
+ });
433
+ ```
434
+
435
+ ## 🎨 Component-Specific Patterns
436
+
437
+ ### DataTable Testing Strategy
438
+
439
+ DataTable subcomponents intentionally have low unit test coverage because they're comprehensively tested through integration tests. This is **correct and intentional**.
440
+
441
+ ```typescript
442
+ // ✅ Good - Integration test for DataTable workflows
443
+ describe('DataTable - Complete Workflow', () => {
444
+ it('allows editing, sorting, and filtering data', async () => {
445
+ const user = userEvent.setup();
446
+
447
+ render(<DataTable data={mockData} onSave={mockSave} />);
448
+
449
+ // Test editing
450
+ await user.click(screen.getByRole('button', { name: 'Edit' }));
451
+ await user.type(screen.getByRole('textbox'), 'Updated Value');
452
+ await user.click(screen.getByRole('button', { name: 'Save' }));
453
+
454
+ // Test sorting
455
+ await user.click(screen.getByRole('columnheader', { name: 'Name' }));
456
+
457
+ // Test filtering
458
+ await user.type(screen.getByPlaceholderText('Filter...'), 'search term');
459
+
460
+ expect(mockSave).toHaveBeenCalledWith(expectedData);
461
+ });
462
+ });
463
+ ```
464
+
465
+ ### Service Hook Testing
466
+
467
+ ```typescript
468
+ describe('useEventService', () => {
469
+ it('throws error when used outside provider', () => {
470
+ expect(() => {
471
+ renderHook(() => useEventService());
472
+ }).toThrow('useEventService must be used within EventServiceProvider');
473
+ });
474
+
475
+ it('subscribes to service updates and cleans up', () => {
476
+ const unsubscribe = vi.fn();
477
+ const mockService = {
478
+ subscribe: vi.fn(() => unsubscribe)
479
+ };
480
+
481
+ const { unmount } = renderHook(() => useEventService(), {
482
+ wrapper: ({ children }) => (
483
+ <EventServiceContext.Provider value={{ eventService: mockService }}>
484
+ {children}
485
+ </EventServiceContext.Provider>
486
+ )
487
+ });
488
+
489
+ expect(mockService.subscribe).toHaveBeenCalled();
490
+
491
+ unmount();
492
+ expect(unsubscribe).toHaveBeenCalled();
493
+ });
494
+ });
495
+ ```
496
+
497
+ ## ⚡ Performance & Memory
498
+
499
+ ### Memory Leak Prevention
500
+
501
+ ```typescript
502
+ describe('Component with Async Operations', () => {
503
+ beforeEach(() => {
504
+ vi.useFakeTimers();
505
+ });
506
+
507
+ afterEach(() => {
508
+ // Prevent memory leaks
509
+ vi.clearAllTimers();
510
+ vi.useRealTimers();
511
+ cleanup();
512
+ vi.clearAllMocks();
513
+ localStorage.clear();
514
+ sessionStorage.clear();
515
+ });
516
+
517
+ it('handles cleanup properly', async () => {
518
+ // Test implementation
519
+ });
520
+ });
521
+ ```
522
+
523
+ ### Async Testing Guidelines
524
+
525
+ ```typescript
526
+ // ✅ Good - Appropriate timeouts based on operation speed
527
+ await waitFor(() => {
528
+ expect(result.current.isLoading).toBe(false);
529
+ }, { timeout: 100, interval: 10 }); // Fast operations
530
+
531
+ await waitFor(() => {
532
+ expect(result.current.data).toBeDefined();
533
+ }, { timeout: 5000 }); // Slow operations only
534
+ ```
535
+
536
+ ## 🔧 Test Configuration
537
+
538
+ ### Vitest Configuration
539
+
540
+ ```typescript
541
+ // vitest.config.ts
542
+ export default defineConfig({
543
+ test: {
544
+ environment: 'jsdom',
545
+ environmentOptions: {
546
+ jsdom: {
547
+ pretendToBeVisual: true, // Enables getComputedStyle
548
+ resources: 'usable' // Enables resource loading
549
+ }
550
+ },
551
+ coverage: {
552
+ thresholds: {
553
+ lines: 82,
554
+ branches: 80,
555
+ functions: 80,
556
+ statements: 82
557
+ }
558
+ }
559
+ }
560
+ });
561
+ ```
562
+
563
+ ### Test Utilities Setup
564
+
565
+ ```typescript
566
+ // test-utils.tsx
567
+ export function renderWithProviders(
568
+ ui: React.ReactElement,
569
+ options: { organisationId?: string } = {}
570
+ ) {
571
+ function Wrapper({ children }: { children: React.ReactNode }) {
572
+ return (
573
+ <OrganisationProvider organisationId={options.organisationId}>
574
+ <RBACProvider>
575
+ {children}
576
+ </RBACProvider>
577
+ </OrganisationProvider>
578
+ );
579
+ }
580
+
581
+ return render(ui, { wrapper: Wrapper, ...options });
582
+ }
583
+ ```
584
+
585
+ ## 🚀 Running Tests
586
+
587
+ ### Commands
588
+
589
+ ```bash
590
+ # Run all tests
591
+ npm test
592
+
593
+ # Run with coverage
594
+ npm test -- --coverage
595
+
596
+ # Run specific test file
597
+ npm test -- Button.test.tsx
598
+
599
+ # Run tests in watch mode
600
+ npm test -- --watch
601
+
602
+ # Run tests with timeout (prevents hanging)
603
+ timeout 60 npm test
604
+ ```
605
+
606
+ ### Debugging
607
+
608
+ ```typescript
609
+ // Debug output
610
+ screen.debug();
611
+
612
+ // Debug specific element
613
+ screen.debug(screen.getByRole('button'));
614
+
615
+ // Log available queries
616
+ screen.logTestingPlaygroundURL();
617
+ ```
618
+
619
+ ## 📋 Pre-Merge Checklist
620
+
621
+ - [ ] All tests pass without `it.only` or `test.skip`
622
+ - [ ] Coverage thresholds are met
623
+ - [ ] Tests use semantic queries (avoid test IDs)
624
+ - [ ] Async operations use proper waiting patterns
625
+ - [ ] Mocks are cleaned up in `afterEach`
626
+ - [ ] Tests focus on behavior, not implementation
627
+ - [ ] Error scenarios are covered
628
+ - [ ] Tests are colocated with source files
629
+
630
+ ## 🎯 Test Categories & Tags
631
+
632
+ Use explicit tags for test organization:
633
+
634
+ ```typescript
635
+ describe('[unit] useDebounce', () => {
636
+ it('delays function execution', () => {});
637
+ });
638
+
639
+ describe('[component] Button', () => {
640
+ it('renders with correct styling', () => {});
641
+ });
642
+
643
+ describe('[integration] User Registration', () => {
644
+ it('completes full signup flow', () => {});
645
+ });
646
+ ```
647
+
648
+ ## 📚 Resources
649
+
650
+ ### Documentation
651
+ - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
652
+ - [Vitest Documentation](https://vitest.dev/)
653
+ - [Jest DOM Matchers](https://github.com/testing-library/jest-dom)
654
+
655
+ ### Internal Guides
656
+ - [Documentation Standard](../../docs/DOCUMENTATION_STANDARD.md)
657
+ - [Memory Performance Analysis](../../../MEMORY_PERFORMANCE_ANALYSIS.md)
658
+ - [Test Failure Analysis](../../../TEST_FAILURE_ANALYSIS.md)
659
+
660
+ ---
661
+
662
+ ## 🎖️ Excellence Indicators
663
+
664
+ A world-class test suite demonstrates:
665
+
666
+ - **Comprehensive Coverage** - All critical paths tested
667
+ - **Behavioral Focus** - Tests what users experience
668
+ - **Maintainable Structure** - Easy to update and extend
669
+ - **Fast Execution** - Quick feedback loops
670
+ - **Clear Intent** - Tests serve as living documentation
671
+ - **Reliable Results** - Consistent across environments
672
+ - **Accessibility Awareness** - Tests include a11y considerations
673
+
674
+ Remember: **Tests are not just about catching bugs—they're about building confidence, enabling refactoring, and documenting expected behavior for future developers.**
@@ -192,13 +192,14 @@ const createQueryBuilder = () => {
192
192
  sharedQueryBuilder = {
193
193
  select: vi.fn(() => sharedQueryBuilder),
194
194
  eq: vi.fn(() => sharedQueryBuilder),
195
+ in: vi.fn(() => Promise.resolve({ data: [], error: null })),
195
196
  single: vi.fn(() => Promise.resolve({ data: { id: 'test-app-id' }, error: null })),
196
197
  maybeSingle: vi.fn(() => Promise.resolve({ data: null, error: null })),
197
198
  order: vi.fn(() => sharedQueryBuilder),
198
199
  limit: vi.fn(() => Promise.resolve({ data: [], error: null })),
199
200
  insert: vi.fn(() => Promise.resolve({ data: null, error: null })),
200
- update: vi.fn(() => Promise.resolve({ data: null, error: null })),
201
- delete: vi.fn(() => Promise.resolve({ data: null, error: null })),
201
+ update: vi.fn(() => sharedQueryBuilder),
202
+ delete: vi.fn(() => sharedQueryBuilder),
202
203
  };
203
204
  }
204
205
  return sharedQueryBuilder;