@jmruthers/pace-core 0.5.4 → 0.5.5

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 (158) hide show
  1. package/dist/{DataTable-ZQDRE46Q.js → DataTable-3SSI644S.js} +2 -2
  2. package/dist/{chunk-M4RW7PIP.js → chunk-2BJFM2JC.js} +105 -81
  3. package/dist/chunk-2BJFM2JC.js.map +1 -0
  4. package/dist/{chunk-5H3C2SWM.js → chunk-RTCA5ZNK.js} +2 -2
  5. package/dist/components.js +2 -2
  6. package/dist/index.js +2 -2
  7. package/dist/utils.js +1 -1
  8. package/docs/api/classes/ErrorBoundary.md +1 -1
  9. package/docs/api/classes/InvalidScopeError.md +1 -1
  10. package/docs/api/classes/MissingUserContextError.md +1 -1
  11. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  12. package/docs/api/classes/PermissionDeniedError.md +1 -1
  13. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  14. package/docs/api/classes/RBACAuditManager.md +1 -1
  15. package/docs/api/classes/RBACCache.md +1 -1
  16. package/docs/api/classes/RBACEngine.md +1 -1
  17. package/docs/api/classes/RBACError.md +1 -1
  18. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  19. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  20. package/docs/api/interfaces/AggregateConfig.md +1 -1
  21. package/docs/api/interfaces/ButtonProps.md +1 -1
  22. package/docs/api/interfaces/CardProps.md +1 -1
  23. package/docs/api/interfaces/ColorPalette.md +1 -1
  24. package/docs/api/interfaces/ColorShade.md +1 -1
  25. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  26. package/docs/api/interfaces/DataTableAction.md +1 -1
  27. package/docs/api/interfaces/DataTableColumn.md +1 -1
  28. package/docs/api/interfaces/DataTableProps.md +34 -34
  29. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  30. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  31. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  32. package/docs/api/interfaces/EventContextType.md +1 -1
  33. package/docs/api/interfaces/EventLogoProps.md +1 -1
  34. package/docs/api/interfaces/EventProviderProps.md +1 -1
  35. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  36. package/docs/api/interfaces/FileUploadProps.md +1 -1
  37. package/docs/api/interfaces/FooterProps.md +1 -1
  38. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  39. package/docs/api/interfaces/InputProps.md +1 -1
  40. package/docs/api/interfaces/LabelProps.md +1 -1
  41. package/docs/api/interfaces/LoginFormProps.md +1 -1
  42. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  43. package/docs/api/interfaces/NavigationContextType.md +1 -1
  44. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  45. package/docs/api/interfaces/NavigationItem.md +1 -1
  46. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  47. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  48. package/docs/api/interfaces/Organisation.md +1 -1
  49. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  50. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  51. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  52. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  53. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  54. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  55. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  56. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  57. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  58. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  59. package/docs/api/interfaces/PaletteData.md +1 -1
  60. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  61. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  62. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  63. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  64. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  65. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  66. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  67. package/docs/api/interfaces/RBACConfig.md +1 -1
  68. package/docs/api/interfaces/RBACContextType.md +1 -1
  69. package/docs/api/interfaces/RBACLogger.md +1 -1
  70. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  71. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  72. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  73. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  74. package/docs/api/interfaces/RouteConfig.md +1 -1
  75. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  76. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  77. package/docs/api/interfaces/StorageConfig.md +1 -1
  78. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  79. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  80. package/docs/api/interfaces/StorageListOptions.md +1 -1
  81. package/docs/api/interfaces/StorageListResult.md +1 -1
  82. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  83. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  84. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  85. package/docs/api/interfaces/StyleImport.md +1 -1
  86. package/docs/api/interfaces/ToastActionElement.md +1 -1
  87. package/docs/api/interfaces/ToastProps.md +1 -1
  88. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  89. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  90. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  91. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  92. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  93. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  94. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  95. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  96. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  97. package/docs/api/interfaces/UserEventAccess.md +1 -1
  98. package/docs/api/interfaces/UserMenuProps.md +1 -1
  99. package/docs/api/interfaces/UserProfile.md +1 -1
  100. package/docs/api/modules.md +3 -3
  101. package/docs/implementation-guides/data-tables.md +20 -0
  102. package/docs/quick-reference.md +9 -0
  103. package/docs/rbac/examples.md +4 -0
  104. package/package.json +1 -1
  105. package/src/__tests__/helpers/test-utils.tsx +147 -1
  106. package/src/components/DataTable/DataTable.tsx +20 -0
  107. package/src/components/DataTable/__tests__/DataTable.hooks.test 2.tsx +191 -0
  108. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +191 -0
  109. package/src/components/DataTable/components/DataTableCore.tsx +167 -138
  110. package/src/hooks/__tests__/hooks.integration.test.tsx +575 -0
  111. package/src/hooks/__tests__/useApiFetch.unit.test.ts +115 -0
  112. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +133 -0
  113. package/src/hooks/__tests__/useDebounce.unit.test.ts +82 -0
  114. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +293 -0
  115. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +385 -0
  116. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +286 -0
  117. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +838 -0
  118. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +104 -0
  119. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +633 -0
  120. package/src/hooks/__tests__/useRBAC.unit.test.ts +856 -0
  121. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +537 -0
  122. package/src/hooks/__tests__/useToast.unit.test.tsx +62 -0
  123. package/src/hooks/__tests__/useZodForm.unit.test.tsx +37 -0
  124. package/src/rbac/utils/__tests__/eventContext.test.ts +428 -0
  125. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +428 -0
  126. package/src/utils/__tests__/appConfig.unit.test.ts +55 -0
  127. package/src/utils/__tests__/audit.unit.test.ts +69 -0
  128. package/src/utils/__tests__/auth-utils.unit.test.ts +70 -0
  129. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +317 -0
  130. package/src/utils/__tests__/cn.unit.test.ts +34 -0
  131. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +503 -0
  132. package/src/utils/__tests__/dynamicUtils.unit.test.ts +322 -0
  133. package/src/utils/__tests__/formatDate.unit.test.ts +109 -0
  134. package/src/utils/__tests__/formatting.unit.test.ts +66 -0
  135. package/src/utils/__tests__/index.unit.test.ts +251 -0
  136. package/src/utils/__tests__/lazyLoad.unit.test.tsx +309 -0
  137. package/src/utils/__tests__/organisationContext.unit.test.ts +192 -0
  138. package/src/utils/__tests__/performanceBudgets.unit.test.ts +259 -0
  139. package/src/utils/__tests__/permissionTypes.unit.test.ts +250 -0
  140. package/src/utils/__tests__/permissionUtils.unit.test.ts +362 -0
  141. package/src/utils/__tests__/sanitization.unit.test.ts +346 -0
  142. package/src/utils/__tests__/schemaUtils.unit.test.ts +441 -0
  143. package/src/utils/__tests__/secureDataAccess.unit.test.ts +334 -0
  144. package/src/utils/__tests__/secureErrors.unit.test.ts +377 -0
  145. package/src/utils/__tests__/secureStorage.unit.test.ts +293 -0
  146. package/src/utils/__tests__/security.unit.test.ts +127 -0
  147. package/src/utils/__tests__/securityMonitor.unit.test.ts +280 -0
  148. package/src/utils/__tests__/sessionTracking.unit.test.ts +356 -0
  149. package/src/utils/__tests__/validation.unit.test.ts +84 -0
  150. package/src/utils/__tests__/validationUtils.unit.test.ts +571 -0
  151. package/src/validation/__tests__/common.unit.test.ts +101 -0
  152. package/src/validation/__tests__/csrf.unit.test.ts +302 -0
  153. package/src/validation/__tests__/passwordSchema.unit.test 2.ts +98 -0
  154. package/src/validation/__tests__/passwordSchema.unit.test.ts +98 -0
  155. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +466 -0
  156. package/dist/chunk-M4RW7PIP.js.map +0 -1
  157. /package/dist/{DataTable-ZQDRE46Q.js.map → DataTable-3SSI644S.js.map} +0 -0
  158. /package/dist/{chunk-5H3C2SWM.js.map → chunk-RTCA5ZNK.js.map} +0 -0
@@ -0,0 +1,251 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import * as Utils from '../index';
3
+
4
+ describe('utils index exports', () => {
5
+ describe('Core utilities', () => {
6
+ it('should export cn function', () => {
7
+ expect(Utils).toHaveProperty('cn');
8
+ expect(typeof Utils.cn).toBe('function');
9
+ });
10
+ });
11
+
12
+ describe('Validation and sanitization', () => {
13
+ it('should export validation utilities', () => {
14
+ // These should be available from the validation module
15
+ expect(Utils).toHaveProperty('validateUserInput');
16
+ expect(Utils).toHaveProperty('emailSchema');
17
+ expect(Utils).toHaveProperty('passwordSchema');
18
+ expect(Utils).toHaveProperty('usernameSchema');
19
+ expect(Utils).toHaveProperty('nameSchema');
20
+ expect(Utils).toHaveProperty('phoneSchema');
21
+ expect(Utils).toHaveProperty('urlSchema');
22
+ });
23
+
24
+ it('should export sanitization utilities', () => {
25
+ // These should be available from the sanitization module
26
+ expect(Utils).toHaveProperty('sanitizeUserInput');
27
+ expect(Utils).toHaveProperty('sanitizeFormData');
28
+ });
29
+ });
30
+
31
+ describe('Security utilities', () => {
32
+ it('should export security functions', () => {
33
+ expect(Utils).toHaveProperty('getSecurityHeaders');
34
+ expect(Utils).toHaveProperty('validateSecurityHeaders');
35
+ expect(Utils).toHaveProperty('generateDeviceFingerprint');
36
+ expect(Utils).toHaveProperty('validateDeviceFingerprint');
37
+ expect(Utils).toHaveProperty('logSecurityEvent');
38
+ });
39
+ });
40
+
41
+ describe('Accessibility testing utilities', () => {
42
+ it('should not export accessibility helpers in production builds', () => {
43
+ // Accessibility helpers are only available in test environments
44
+ // They are not exported in production builds to avoid including test dependencies
45
+ expect(Utils).not.toHaveProperty('a11yHelpers');
46
+ expect(Utils).not.toHaveProperty('runAccessibilityTests');
47
+ expect(Utils).not.toHaveProperty('screenReaderHelpers');
48
+ });
49
+ });
50
+
51
+ describe('Performance utilities', () => {
52
+ it('should export performance monitoring functions', () => {
53
+ expect(Utils).toHaveProperty('useComponentPerformance');
54
+ expect(Utils).toHaveProperty('measureRenderPerformance');
55
+ expect(Utils).toHaveProperty('createPerformanceBenchmark');
56
+ expect(Utils).toHaveProperty('PERFORMANCE_THRESHOLDS');
57
+ // PerformanceMetrics is a type, not a runtime export
58
+ });
59
+ });
60
+
61
+ describe('Bundle analysis utilities', () => {
62
+ it('should export bundle analysis functions', () => {
63
+ expect(Utils).toHaveProperty('bundleAnalyzer');
64
+ expect(Utils).toHaveProperty('validateImportPattern');
65
+ expect(Utils).toHaveProperty('trackDynamicImport');
66
+ });
67
+ });
68
+
69
+ describe('Dynamic utility loaders', () => {
70
+ it('should export dynamic loading functions', () => {
71
+ expect(Utils).toHaveProperty('loadLodash');
72
+ expect(Utils).toHaveProperty('loadDateUtils');
73
+ expect(Utils).toHaveProperty('loadChartUtils');
74
+ expect(Utils).toHaveProperty('loadFormUtils');
75
+ expect(Utils).toHaveProperty('loadCSVUtils');
76
+ expect(Utils).toHaveProperty('createLazyUtility');
77
+ expect(Utils).toHaveProperty('lazyLodash');
78
+ expect(Utils).toHaveProperty('lazyDateUtils');
79
+ expect(Utils).toHaveProperty('lazyChartUtils');
80
+ expect(Utils).toHaveProperty('lazyFormUtils');
81
+ expect(Utils).toHaveProperty('lazyCSVUtils');
82
+ });
83
+ });
84
+
85
+ describe('Performance budget utilities', () => {
86
+ it('should export performance budget monitor', () => {
87
+ expect(Utils).toHaveProperty('performanceBudgetMonitor');
88
+ });
89
+ });
90
+
91
+ describe('Lazy loading utilities', () => {
92
+ it('should export lazy loading functions', () => {
93
+ expect(Utils).toHaveProperty('createLazyComponent');
94
+ expect(Utils).toHaveProperty('LazyDataTable');
95
+ });
96
+ });
97
+
98
+ describe('App configuration utilities', () => {
99
+ it('should export app config functions', () => {
100
+ expect(Utils).toHaveProperty('setAppConfig');
101
+ expect(Utils).toHaveProperty('getAppConfig');
102
+ expect(Utils).toHaveProperty('getCurrentAppName');
103
+ expect(Utils).toHaveProperty('getCurrentAppId');
104
+ });
105
+ });
106
+
107
+ describe('Permission utilities', () => {
108
+ it('should export permission functions', () => {
109
+ expect(Utils).toHaveProperty('transformPermissionMapToBoolean');
110
+ expect(Utils).toHaveProperty('hasPermission');
111
+ expect(Utils).toHaveProperty('hasAnyPermission');
112
+ expect(Utils).toHaveProperty('hasAllPermissions');
113
+ expect(Utils).toHaveProperty('PermissionType');
114
+ expect(Utils).toHaveProperty('parsePermission');
115
+ });
116
+ });
117
+
118
+ describe('Schema utilities', () => {
119
+ it('should export schema functions', () => {
120
+ expect(Utils).toHaveProperty('pickSchema');
121
+ expect(Utils).toHaveProperty('combineSchemas');
122
+ });
123
+ });
124
+
125
+ describe('Security monitoring utilities', () => {
126
+ it('should export security monitoring functions', () => {
127
+ expect(Utils).toHaveProperty('securityMonitor');
128
+ expect(Utils).toHaveProperty('logSecurityEvent');
129
+ });
130
+ });
131
+
132
+ describe('Session tracking utilities', () => {
133
+ it('should export session tracking functions', () => {
134
+ expect(Utils).toHaveProperty('useSessionTracking');
135
+ });
136
+ });
137
+
138
+ describe('Audit utilities', () => {
139
+ it('should export audit functions', () => {
140
+ expect(Utils).toHaveProperty('auditLog');
141
+ expect(Utils).toHaveProperty('logAuditEvent');
142
+ });
143
+ });
144
+
145
+ describe('Device fingerprinting utilities', () => {
146
+ it('should export device fingerprinting functions', () => {
147
+ expect(Utils).toHaveProperty('generateDeviceFingerprint');
148
+ expect(Utils).toHaveProperty('validateDeviceFingerprint');
149
+ });
150
+ });
151
+
152
+
153
+ describe('Formatting utilities', () => {
154
+ it('should export formatting functions', () => {
155
+ expect(Utils).toHaveProperty('formatDate');
156
+ expect(Utils).toHaveProperty('formatCurrency');
157
+ expect(Utils).toHaveProperty('formatNumber');
158
+ });
159
+ });
160
+
161
+ describe('Organisation context utilities', () => {
162
+ it('should export organisation context functions', () => {
163
+ expect(Utils).toHaveProperty('setOrganisationContext');
164
+ expect(Utils).toHaveProperty('clearOrganisationContext');
165
+ expect(Utils).toHaveProperty('getOrganisationContext');
166
+ expect(Utils).toHaveProperty('isOrganisationContextAvailable');
167
+ });
168
+ });
169
+
170
+ describe('Integration tests', () => {
171
+ it('should allow importing specific utilities', () => {
172
+ // Test that we can import specific utilities
173
+ const { cn, validateUserInput, emailSchema } = Utils;
174
+
175
+ expect(typeof cn).toBe('function');
176
+ expect(typeof validateUserInput).toBe('function');
177
+ expect(emailSchema).toBeDefined();
178
+ });
179
+
180
+ it('should allow importing utility groups', () => {
181
+ // Test that we can import groups of related utilities
182
+ const {
183
+ // Security
184
+ getSecurityHeaders,
185
+ generateDeviceFingerprint,
186
+ // Validation
187
+ emailSchema,
188
+ passwordSchema,
189
+ // Performance
190
+ useComponentPerformance,
191
+ // Permissions
192
+ hasPermission,
193
+ hasAnyPermission,
194
+ // Schema
195
+ pickSchema,
196
+ combineSchemas
197
+ } = Utils;
198
+
199
+ expect(typeof getSecurityHeaders).toBe('function');
200
+ expect(typeof generateDeviceFingerprint).toBe('function');
201
+ expect(emailSchema).toBeDefined();
202
+ expect(passwordSchema).toBeDefined();
203
+ expect(typeof useComponentPerformance).toBe('function');
204
+ expect(typeof hasPermission).toBe('function');
205
+ expect(typeof hasAnyPermission).toBe('function');
206
+ expect(typeof pickSchema).toBe('function');
207
+ expect(typeof combineSchemas).toBe('function');
208
+ });
209
+
210
+ it('should maintain function signatures', () => {
211
+ // Test that exported functions have the expected signatures
212
+ const { cn, validateUserInput, hasPermission } = Utils;
213
+
214
+ // cn should accept class names
215
+ expect(() => cn('class1', 'class2')).not.toThrow();
216
+
217
+ // validateUserInput should accept schema and data
218
+ expect(typeof validateUserInput).toBe('function');
219
+
220
+ // hasPermission should accept permissions and permission string
221
+ expect(typeof hasPermission).toBe('function');
222
+ });
223
+
224
+ it('should export all required utilities for a complete application', () => {
225
+ // Test that all major utility categories are exported
226
+ const categories = [
227
+ 'cn', // Core utilities
228
+ 'validateUserInput', // Validation
229
+ 'sanitizeUserInput', // Sanitization
230
+ 'getSecurityHeaders', // Security
231
+ 'useComponentPerformance', // Performance
232
+ 'bundleAnalyzer', // Bundle analysis
233
+ 'loadLodash', // Dynamic loading
234
+ 'performanceBudgetMonitor', // Performance budgets
235
+ 'setAppConfig', // App config
236
+ 'hasPermission', // Permissions
237
+ 'pickSchema', // Schema utilities
238
+ 'securityMonitor', // Security monitoring
239
+ 'useSessionTracking', // Session tracking
240
+ 'auditLog', // Audit
241
+ 'generateDeviceFingerprint', // Device fingerprinting
242
+ 'formatDate', // Formatting
243
+ 'setOrganisationContext' // Organisation context
244
+ ];
245
+
246
+ categories.forEach(utility => {
247
+ expect(Utils).toHaveProperty(utility);
248
+ });
249
+ });
250
+ });
251
+ });
@@ -0,0 +1,309 @@
1
+ /**
2
+ * @file LazyLoad Utility Unit Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/LazyLoad
5
+ * @since 0.4.0
6
+ */
7
+
8
+ import React from 'react';
9
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
10
+ import { screen, waitFor } from '@testing-library/react';
11
+ import '@testing-library/jest-dom';
12
+ import { createLazyComponent, LazyDataTable } from '../lazyLoad';
13
+ import { LoadingSpinner } from '../../components/LoadingSpinner/LoadingSpinner';
14
+ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
15
+
16
+ // Mock the DataTable component
17
+ vi.mock('../../components/DataTable', () => ({
18
+ DataTable: vi.fn(() => <div data-testid="data-table">DataTable Component</div>)
19
+ }));
20
+
21
+ // Mock the LoadingSpinner component
22
+ vi.mock('../../components/LoadingSpinner/LoadingSpinner', () => ({
23
+ LoadingSpinner: vi.fn(() => <div data-testid="loading-spinner">Loading...</div>)
24
+ }));
25
+
26
+ describe('LazyLoad Utility', () => {
27
+ beforeEach(() => {
28
+ vi.clearAllMocks();
29
+ });
30
+
31
+ describe('createLazyComponent', () => {
32
+ it('should create a lazy component with default fallback', async () => {
33
+ const mockImportFn = vi.fn().mockResolvedValue({
34
+ default: () => <div data-testid="lazy-component">Lazy Component</div>
35
+ });
36
+
37
+ const LazyTestComponent = createLazyComponent(
38
+ mockImportFn,
39
+ 'TestComponent'
40
+ );
41
+
42
+ renderWithProviders(<LazyTestComponent />);
43
+
44
+ // Should show loading spinner initially
45
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
46
+
47
+ // Wait for the component to load
48
+ await waitFor(() => {
49
+ expect(screen.getByTestId('lazy-component')).toBeInTheDocument();
50
+ });
51
+
52
+ expect(mockImportFn).toHaveBeenCalledTimes(1);
53
+ });
54
+
55
+ it('should create a lazy component with custom fallback', async () => {
56
+ const mockImportFn = vi.fn().mockResolvedValue({
57
+ default: () => <div data-testid="lazy-component">Lazy Component</div>
58
+ });
59
+
60
+ const customFallback = <div data-testid="custom-fallback">Custom Loading...</div>;
61
+
62
+ const LazyTestComponent = createLazyComponent(
63
+ mockImportFn,
64
+ 'TestComponent',
65
+ { fallback: customFallback }
66
+ );
67
+
68
+ renderWithProviders(<LazyTestComponent />);
69
+
70
+ // Should show custom fallback initially
71
+ expect(screen.getByTestId('custom-fallback')).toBeInTheDocument();
72
+
73
+ // Wait for the component to load
74
+ await waitFor(() => {
75
+ expect(screen.getByTestId('lazy-component')).toBeInTheDocument();
76
+ });
77
+ });
78
+
79
+ it('should create a lazy component with error boundary', async () => {
80
+ const mockImportFn = vi.fn().mockResolvedValue({
81
+ default: () => <div data-testid="lazy-component">Lazy Component</div>
82
+ });
83
+
84
+ const ErrorBoundary = ({ children }: { children: React.ReactNode }) => (
85
+ <div data-testid="error-boundary">{children}</div>
86
+ );
87
+
88
+ const LazyTestComponent = createLazyComponent(
89
+ mockImportFn,
90
+ 'TestComponent',
91
+ { errorBoundary: ErrorBoundary }
92
+ );
93
+
94
+ renderWithProviders(<LazyTestComponent />);
95
+
96
+ // Should show loading spinner initially
97
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
98
+
99
+ // Wait for the component to load
100
+ await waitFor(() => {
101
+ expect(screen.getByTestId('lazy-component')).toBeInTheDocument();
102
+ });
103
+
104
+ // Error boundary should wrap the component
105
+ expect(screen.getByTestId('error-boundary')).toBeInTheDocument();
106
+ });
107
+
108
+ it.skip('should handle import errors gracefully', async () => {
109
+ const mockImportFn = vi.fn().mockRejectedValue(new Error('Import failed'));
110
+
111
+ const LazyTestComponent = createLazyComponent(
112
+ mockImportFn,
113
+ 'TestComponent'
114
+ );
115
+
116
+ // Suppress console.error and unhandledRejection for this test
117
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
118
+ const originalUnhandledRejection = process.listeners('unhandledRejection');
119
+
120
+ // Add a handler to catch the unhandled rejection
121
+ const unhandledRejectionHandler = (reason: any) => {
122
+ if (reason?.message === 'Import failed') {
123
+ // Expected error, ignore it
124
+ return;
125
+ }
126
+ // Re-throw other errors
127
+ throw reason;
128
+ };
129
+ process.on('unhandledRejection', unhandledRejectionHandler);
130
+
131
+ renderWithProviders(<LazyTestComponent />);
132
+
133
+ // Should show loading spinner initially
134
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
135
+
136
+ // Wait for the error to be handled
137
+ await waitFor(() => {
138
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
139
+ }, { timeout: 1000 });
140
+
141
+ // Clean up
142
+ process.removeListener('unhandledRejection', unhandledRejectionHandler);
143
+ consoleSpy.mockRestore();
144
+ });
145
+
146
+ it('should set display name correctly', async () => {
147
+ const mockImportFn = vi.fn().mockResolvedValue({
148
+ default: () => <div data-testid="lazy-component">Lazy Component</div>
149
+ });
150
+
151
+ const LazyTestComponent = createLazyComponent(
152
+ mockImportFn,
153
+ 'TestComponent'
154
+ );
155
+
156
+ expect(LazyTestComponent.displayName).toBe('LazyTestComponent');
157
+ });
158
+
159
+ it('should pass props to the lazy component', async () => {
160
+ const mockImportFn = vi.fn().mockResolvedValue({
161
+ default: ({ title, count }: { title: string; count: number }) => (
162
+ <div data-testid="lazy-component">
163
+ {title}: {count}
164
+ </div>
165
+ )
166
+ });
167
+
168
+ const LazyTestComponent = createLazyComponent(
169
+ mockImportFn,
170
+ 'TestComponent'
171
+ );
172
+
173
+ renderWithProviders(<LazyTestComponent title="Test" count={42} />);
174
+
175
+ await waitFor(() => {
176
+ expect(screen.getByTestId('lazy-component')).toHaveTextContent('Test: 42');
177
+ });
178
+ });
179
+
180
+ it('should handle multiple instances of the same lazy component', async () => {
181
+ const mockImportFn = vi.fn().mockResolvedValue({
182
+ default: ({ id }: { id: string }) => (
183
+ <div data-testid={`lazy-component-${id}`}>Component {id}</div>
184
+ )
185
+ });
186
+
187
+ const LazyTestComponent = createLazyComponent(
188
+ mockImportFn,
189
+ 'TestComponent'
190
+ );
191
+
192
+ renderWithProviders(
193
+ <div>
194
+ <LazyTestComponent id="1" />
195
+ <LazyTestComponent id="2" />
196
+ </div>
197
+ );
198
+
199
+ await waitFor(() => {
200
+ expect(screen.getByTestId('lazy-component-1')).toBeInTheDocument();
201
+ expect(screen.getByTestId('lazy-component-2')).toBeInTheDocument();
202
+ });
203
+
204
+ // Import function should only be called once due to React.lazy caching
205
+ expect(mockImportFn).toHaveBeenCalledTimes(1);
206
+ });
207
+ });
208
+
209
+ describe('LazyDataTable', () => {
210
+ it('should render the DataTable component when loaded', async () => {
211
+ const mockProps = {
212
+ data: [{ id: 1, name: 'Test' }],
213
+ columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }]
214
+ };
215
+
216
+ renderWithProviders(<LazyDataTable {...mockProps} />);
217
+
218
+ // Should show loading spinner initially
219
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
220
+
221
+ // Wait for the DataTable to load
222
+ await waitFor(() => {
223
+ expect(screen.getByTestId('data-table')).toBeInTheDocument();
224
+ });
225
+ });
226
+
227
+ it('should pass props to the DataTable component', async () => {
228
+ const mockProps = {
229
+ data: [{ id: 1, name: 'Test' }],
230
+ columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }]
231
+ };
232
+
233
+ renderWithProviders(<LazyDataTable {...mockProps} />);
234
+
235
+ await waitFor(() => {
236
+ expect(screen.getByTestId('data-table')).toBeInTheDocument();
237
+ });
238
+ });
239
+
240
+ it('should render without errors', () => {
241
+ const mockProps = {
242
+ data: [{ id: 1, name: 'Test' }],
243
+ columns: [{ id: 'name', accessorKey: 'name', header: 'Name' }]
244
+ };
245
+
246
+ expect(() => renderWithProviders(<LazyDataTable {...mockProps} />)).not.toThrow();
247
+ });
248
+ });
249
+
250
+ describe('Component Integration', () => {
251
+ it('should work with React Suspense boundaries', async () => {
252
+ const mockImportFn = vi.fn().mockResolvedValue({
253
+ default: () => <div data-testid="lazy-component">Lazy Component</div>
254
+ });
255
+
256
+ const LazyTestComponent = createLazyComponent(
257
+ mockImportFn,
258
+ 'TestComponent'
259
+ );
260
+
261
+ const TestWrapper = () => (
262
+ <div data-testid="wrapper">
263
+ <LazyTestComponent />
264
+ </div>
265
+ );
266
+
267
+ renderWithProviders(<TestWrapper />);
268
+
269
+ // Should show loading spinner initially
270
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
271
+
272
+ // Wait for the component to load
273
+ await waitFor(() => {
274
+ expect(screen.getByTestId('lazy-component')).toBeInTheDocument();
275
+ });
276
+
277
+ // Wrapper should still be present
278
+ expect(screen.getByTestId('wrapper')).toBeInTheDocument();
279
+ });
280
+
281
+ it('should handle unmounting before load completes', async () => {
282
+ const mockImportFn = vi.fn().mockImplementation(() =>
283
+ new Promise(resolve => {
284
+ setTimeout(() => {
285
+ resolve({
286
+ default: () => <div data-testid="lazy-component">Lazy Component</div>
287
+ });
288
+ }, 100);
289
+ })
290
+ );
291
+
292
+ const LazyTestComponent = createLazyComponent(
293
+ mockImportFn,
294
+ 'TestComponent'
295
+ );
296
+
297
+ const { unmount } = renderWithProviders(<LazyTestComponent />);
298
+
299
+ // Should show loading spinner initially
300
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
301
+
302
+ // Unmount before load completes
303
+ unmount();
304
+
305
+ // Should not throw any errors
306
+ expect(mockImportFn).toHaveBeenCalledTimes(1);
307
+ });
308
+ });
309
+ });