@jmruthers/pace-core 0.5.3 → 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 (170) 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/styles/core.css +3 -0
  8. package/dist/utils.js +1 -1
  9. package/docs/api/classes/ErrorBoundary.md +1 -1
  10. package/docs/api/classes/InvalidScopeError.md +1 -1
  11. package/docs/api/classes/MissingUserContextError.md +1 -1
  12. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  13. package/docs/api/classes/PermissionDeniedError.md +1 -1
  14. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  15. package/docs/api/classes/RBACAuditManager.md +1 -1
  16. package/docs/api/classes/RBACCache.md +1 -1
  17. package/docs/api/classes/RBACEngine.md +1 -1
  18. package/docs/api/classes/RBACError.md +1 -1
  19. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  20. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  21. package/docs/api/interfaces/AggregateConfig.md +1 -1
  22. package/docs/api/interfaces/ButtonProps.md +1 -1
  23. package/docs/api/interfaces/CardProps.md +1 -1
  24. package/docs/api/interfaces/ColorPalette.md +1 -1
  25. package/docs/api/interfaces/ColorShade.md +1 -1
  26. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  27. package/docs/api/interfaces/DataTableAction.md +1 -1
  28. package/docs/api/interfaces/DataTableColumn.md +1 -1
  29. package/docs/api/interfaces/DataTableProps.md +34 -34
  30. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  31. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  32. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  33. package/docs/api/interfaces/EventContextType.md +1 -1
  34. package/docs/api/interfaces/EventLogoProps.md +1 -1
  35. package/docs/api/interfaces/EventProviderProps.md +1 -1
  36. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  37. package/docs/api/interfaces/FileUploadProps.md +1 -1
  38. package/docs/api/interfaces/FooterProps.md +1 -1
  39. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  40. package/docs/api/interfaces/InputProps.md +1 -1
  41. package/docs/api/interfaces/LabelProps.md +1 -1
  42. package/docs/api/interfaces/LoginFormProps.md +1 -1
  43. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  44. package/docs/api/interfaces/NavigationContextType.md +1 -1
  45. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  46. package/docs/api/interfaces/NavigationItem.md +1 -1
  47. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  48. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  49. package/docs/api/interfaces/Organisation.md +1 -1
  50. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  51. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  52. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  53. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  54. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  55. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  56. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  57. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  58. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  59. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  60. package/docs/api/interfaces/PaletteData.md +1 -1
  61. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  62. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  63. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  64. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  65. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  66. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  67. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  68. package/docs/api/interfaces/RBACConfig.md +1 -1
  69. package/docs/api/interfaces/RBACContextType.md +1 -1
  70. package/docs/api/interfaces/RBACLogger.md +1 -1
  71. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  72. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  73. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  74. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  75. package/docs/api/interfaces/RouteConfig.md +1 -1
  76. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  77. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  78. package/docs/api/interfaces/StorageConfig.md +1 -1
  79. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  80. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  81. package/docs/api/interfaces/StorageListOptions.md +1 -1
  82. package/docs/api/interfaces/StorageListResult.md +1 -1
  83. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  84. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  85. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  86. package/docs/api/interfaces/StyleImport.md +1 -1
  87. package/docs/api/interfaces/ToastActionElement.md +1 -1
  88. package/docs/api/interfaces/ToastProps.md +1 -1
  89. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  90. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  91. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  92. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  93. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  94. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  95. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  96. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  97. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  98. package/docs/api/interfaces/UserEventAccess.md +1 -1
  99. package/docs/api/interfaces/UserMenuProps.md +1 -1
  100. package/docs/api/interfaces/UserProfile.md +1 -1
  101. package/docs/api/modules.md +3 -3
  102. package/docs/implementation-guides/data-tables.md +20 -0
  103. package/docs/quick-reference.md +9 -0
  104. package/docs/rbac/examples.md +4 -0
  105. package/package.json +1 -1
  106. package/src/__tests__/helpers/test-utils.tsx +147 -1
  107. package/src/components/DataTable/DataTable.tsx +20 -0
  108. package/src/components/DataTable/__tests__/DataTable.hooks.test 2.tsx +191 -0
  109. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +191 -0
  110. package/src/components/DataTable/components/DataTableCore.tsx +167 -138
  111. package/src/components/Header/Header.test.tsx +1 -1
  112. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +1 -1
  113. package/src/hooks/__tests__/hooks.integration.test.tsx +575 -0
  114. package/src/hooks/__tests__/useApiFetch.unit.test.ts +115 -0
  115. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +133 -0
  116. package/src/hooks/__tests__/useDebounce.unit.test.ts +82 -0
  117. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +293 -0
  118. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +385 -0
  119. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +286 -0
  120. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +838 -0
  121. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +104 -0
  122. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +633 -0
  123. package/src/hooks/__tests__/useRBAC.unit.test.ts +856 -0
  124. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +537 -0
  125. package/src/hooks/__tests__/useToast.unit.test.tsx +62 -0
  126. package/src/hooks/__tests__/useZodForm.unit.test.tsx +37 -0
  127. package/src/rbac/api.test.ts +511 -0
  128. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +843 -0
  129. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +1007 -0
  130. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +806 -0
  131. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +741 -0
  132. package/src/rbac/hooks/useCan.test.ts +1 -1
  133. package/src/rbac/hooks/usePermissions.test.ts +10 -5
  134. package/src/rbac/hooks/useRBAC.test.ts +141 -93
  135. package/src/rbac/utils/__tests__/eventContext.test.ts +428 -0
  136. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +428 -0
  137. package/src/styles/core.css +3 -0
  138. package/src/utils/__tests__/appConfig.unit.test.ts +55 -0
  139. package/src/utils/__tests__/audit.unit.test.ts +69 -0
  140. package/src/utils/__tests__/auth-utils.unit.test.ts +70 -0
  141. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +317 -0
  142. package/src/utils/__tests__/cn.unit.test.ts +34 -0
  143. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +503 -0
  144. package/src/utils/__tests__/dynamicUtils.unit.test.ts +322 -0
  145. package/src/utils/__tests__/formatDate.unit.test.ts +109 -0
  146. package/src/utils/__tests__/formatting.unit.test.ts +66 -0
  147. package/src/utils/__tests__/index.unit.test.ts +251 -0
  148. package/src/utils/__tests__/lazyLoad.unit.test.tsx +309 -0
  149. package/src/utils/__tests__/organisationContext.unit.test.ts +192 -0
  150. package/src/utils/__tests__/performanceBudgets.unit.test.ts +259 -0
  151. package/src/utils/__tests__/permissionTypes.unit.test.ts +250 -0
  152. package/src/utils/__tests__/permissionUtils.unit.test.ts +362 -0
  153. package/src/utils/__tests__/sanitization.unit.test.ts +346 -0
  154. package/src/utils/__tests__/schemaUtils.unit.test.ts +441 -0
  155. package/src/utils/__tests__/secureDataAccess.unit.test.ts +334 -0
  156. package/src/utils/__tests__/secureErrors.unit.test.ts +377 -0
  157. package/src/utils/__tests__/secureStorage.unit.test.ts +293 -0
  158. package/src/utils/__tests__/security.unit.test.ts +127 -0
  159. package/src/utils/__tests__/securityMonitor.unit.test.ts +280 -0
  160. package/src/utils/__tests__/sessionTracking.unit.test.ts +356 -0
  161. package/src/utils/__tests__/validation.unit.test.ts +84 -0
  162. package/src/utils/__tests__/validationUtils.unit.test.ts +571 -0
  163. package/src/validation/__tests__/common.unit.test.ts +101 -0
  164. package/src/validation/__tests__/csrf.unit.test.ts +302 -0
  165. package/src/validation/__tests__/passwordSchema.unit.test 2.ts +98 -0
  166. package/src/validation/__tests__/passwordSchema.unit.test.ts +98 -0
  167. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +466 -0
  168. package/dist/chunk-M4RW7PIP.js.map +0 -1
  169. /package/dist/{DataTable-ZQDRE46Q.js.map → DataTable-3SSI644S.js.map} +0 -0
  170. /package/dist/{chunk-5H3C2SWM.js.map → chunk-RTCA5ZNK.js.map} +0 -0
@@ -0,0 +1,317 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+
3
+ describe('bundleAnalysis', () => {
4
+ let consoleSpy: {
5
+ log: ReturnType<typeof vi.spyOn>;
6
+ warn: ReturnType<typeof vi.spyOn>;
7
+ group: ReturnType<typeof vi.spyOn>;
8
+ groupEnd: ReturnType<typeof vi.spyOn>;
9
+ };
10
+
11
+ beforeEach(() => {
12
+ // Mock console methods before each test
13
+ consoleSpy = {
14
+ log: vi.spyOn(console, 'log').mockImplementation(() => {}),
15
+ warn: vi.spyOn(console, 'warn').mockImplementation(() => {}),
16
+ group: vi.spyOn(console, 'group').mockImplementation(() => {}),
17
+ groupEnd: vi.spyOn(console, 'groupEnd').mockImplementation(() => {})
18
+ };
19
+ });
20
+
21
+ afterEach(() => {
22
+ vi.restoreAllMocks();
23
+ });
24
+
25
+ describe('bundleAnalyzer.analyzeBundle', () => {
26
+ it('should return null in production mode', async () => {
27
+ const originalEnv = process.env.NODE_ENV;
28
+ process.env.NODE_ENV = 'production';
29
+
30
+ // Clear module cache and re-import
31
+ vi.resetModules();
32
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
33
+ const result = bundleAnalyzer.analyzeBundle();
34
+
35
+ expect(result).toBeNull();
36
+
37
+ process.env.NODE_ENV = originalEnv;
38
+ });
39
+
40
+ it('should return bundle stats in development mode', async () => {
41
+ const originalEnv = process.env.NODE_ENV;
42
+ process.env.NODE_ENV = 'development';
43
+
44
+ // Clear module cache and re-import to pick up environment change
45
+ vi.resetModules();
46
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
47
+ const result = bundleAnalyzer.analyzeBundle();
48
+
49
+ expect(result).toEqual({
50
+ totalSize: 0,
51
+ gzippedSize: 0,
52
+ chunks: [],
53
+ treeshakingEffectiveness: 0
54
+ });
55
+
56
+ process.env.NODE_ENV = originalEnv;
57
+ });
58
+
59
+ it('should log bundle analysis instructions in development mode', async () => {
60
+ const originalEnv = process.env.NODE_ENV;
61
+ process.env.NODE_ENV = 'development';
62
+
63
+ // Clear module cache and re-import to pick up environment change
64
+ vi.resetModules();
65
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
66
+ bundleAnalyzer.analyzeBundle();
67
+
68
+ expect(consoleSpy.log).toHaveBeenCalledWith('Bundle analysis would run here in development');
69
+ expect(consoleSpy.log).toHaveBeenCalledWith('To enable real bundle analysis:');
70
+ expect(consoleSpy.log).toHaveBeenCalledWith('1. Install webpack-bundle-analyzer');
71
+ expect(consoleSpy.log).toHaveBeenCalledWith('2. Add bundle analysis to build scripts');
72
+ expect(consoleSpy.log).toHaveBeenCalledWith('3. Integrate with performance budgets');
73
+
74
+ process.env.NODE_ENV = originalEnv;
75
+ });
76
+ });
77
+
78
+ describe('bundleAnalyzer.checkTreeshaking', () => {
79
+ it('should do nothing in production mode', async () => {
80
+ const originalEnv = process.env.NODE_ENV;
81
+ process.env.NODE_ENV = 'production';
82
+
83
+ vi.resetModules();
84
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
85
+ bundleAnalyzer.checkTreeshaking();
86
+
87
+ expect(consoleSpy.group).not.toHaveBeenCalled();
88
+
89
+ process.env.NODE_ENV = originalEnv;
90
+ });
91
+
92
+ it('should log treeshaking analysis in development mode', async () => {
93
+ const originalEnv = process.env.NODE_ENV;
94
+ process.env.NODE_ENV = 'development';
95
+
96
+ // Clear module cache and re-import to pick up environment change
97
+ vi.resetModules();
98
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
99
+ bundleAnalyzer.checkTreeshaking();
100
+
101
+ expect(consoleSpy.group).toHaveBeenCalledWith('Tree-shaking Analysis');
102
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Check for unused exports in index files');
103
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Verify side-effect free modules in package.json');
104
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Ensure proper ES module syntax');
105
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Check for circular dependencies');
106
+ expect(consoleSpy.groupEnd).toHaveBeenCalled();
107
+
108
+ process.env.NODE_ENV = originalEnv;
109
+ });
110
+ });
111
+
112
+ describe('bundleAnalyzer.validateExports', () => {
113
+ it('should do nothing in production mode', async () => {
114
+ const originalEnv = process.env.NODE_ENV;
115
+ process.env.NODE_ENV = 'production';
116
+
117
+ vi.resetModules();
118
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
119
+ bundleAnalyzer.validateExports();
120
+
121
+ expect(consoleSpy.group).not.toHaveBeenCalled();
122
+
123
+ process.env.NODE_ENV = originalEnv;
124
+ });
125
+
126
+ it('should log export validation in development mode', async () => {
127
+ const originalEnv = process.env.NODE_ENV;
128
+ process.env.NODE_ENV = 'development';
129
+
130
+ // Clear module cache and re-import to pick up environment change
131
+ vi.resetModules();
132
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
133
+ bundleAnalyzer.validateExports();
134
+
135
+ expect(consoleSpy.group).toHaveBeenCalledWith('Export Validation');
136
+ expect(consoleSpy.log).toHaveBeenCalledWith('Checking for optimal export patterns...');
137
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Prefer named exports over default exports');
138
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Avoid barrel exports for large modules');
139
+ expect(consoleSpy.log).toHaveBeenCalledWith('- Use dynamic imports for large dependencies');
140
+ expect(consoleSpy.groupEnd).toHaveBeenCalled();
141
+
142
+ process.env.NODE_ENV = originalEnv;
143
+ });
144
+ });
145
+
146
+ describe('bundleAnalyzer.generateReport', () => {
147
+ it('should generate a comprehensive bundle analysis report', async () => {
148
+ vi.resetModules();
149
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
150
+ const report = bundleAnalyzer.generateReport();
151
+
152
+ expect(report).toContain('# Bundle Analysis Report');
153
+ expect(report).toContain('## Recommendations for @jmruthers/pace-core');
154
+ expect(report).toContain('### 1. Code Splitting Opportunities');
155
+ expect(report).toContain('### 2. Tree-shaking Optimization');
156
+ expect(report).toContain('### 3. Bundle Size Targets');
157
+ expect(report).toContain('### 4. Performance Monitoring');
158
+ expect(report).toContain('✅ DataTable virtualization is lazy-loaded');
159
+ expect(report).toContain('⚠️ Consider splitting auth providers by use case');
160
+ expect(report).toContain('Core components: < 50KB gzipped');
161
+ expect(report).toContain('Total package: < 150KB gzipped');
162
+ });
163
+
164
+ it('should include specific recommendations', async () => {
165
+ vi.resetModules();
166
+ const { bundleAnalyzer } = await import('../bundleAnalysis');
167
+ const report = bundleAnalyzer.generateReport();
168
+
169
+ expect(report).toContain('Lazy load heavy UI components (charts, complex forms)');
170
+ expect(report).toContain('Check for unused utility functions');
171
+ expect(report).toContain('Monitor component render times');
172
+ expect(report).toContain('Track bundle size changes');
173
+ expect(report).toContain('Validate tree-shaking effectiveness');
174
+ });
175
+ });
176
+
177
+ describe('validateImportPattern', () => {
178
+ it('should do nothing in production mode', async () => {
179
+ const originalEnv = process.env.NODE_ENV;
180
+ process.env.NODE_ENV = 'production';
181
+
182
+ vi.resetModules();
183
+ const { validateImportPattern } = await import('../bundleAnalysis');
184
+ validateImportPattern('lodash', ['debounce', 'throttle']);
185
+
186
+ expect(consoleSpy.warn).not.toHaveBeenCalled();
187
+
188
+ process.env.NODE_ENV = originalEnv;
189
+ });
190
+
191
+ it('should warn about large imports from large modules', async () => {
192
+ const originalEnv = process.env.NODE_ENV;
193
+ process.env.NODE_ENV = 'development';
194
+
195
+ vi.resetModules();
196
+ const { validateImportPattern } = await import('../bundleAnalysis');
197
+ validateImportPattern('lodash', ['debounce', 'throttle', 'map', 'filter', 'reduce', 'forEach']);
198
+
199
+ expect(consoleSpy.warn).toHaveBeenCalledWith(
200
+ 'Large import detected: importing 6 items from lodash. ' +
201
+ 'Consider splitting imports or using more specific imports.'
202
+ );
203
+
204
+ process.env.NODE_ENV = originalEnv;
205
+ });
206
+
207
+ it('should not warn for small imports from large modules', async () => {
208
+ const originalEnv = process.env.NODE_ENV;
209
+ process.env.NODE_ENV = 'development';
210
+
211
+ vi.resetModules();
212
+ const { validateImportPattern } = await import('../bundleAnalysis');
213
+ validateImportPattern('lodash', ['debounce', 'throttle']);
214
+
215
+ expect(consoleSpy.warn).not.toHaveBeenCalled();
216
+
217
+ process.env.NODE_ENV = originalEnv;
218
+ });
219
+
220
+ it('should not warn for large imports from small modules', async () => {
221
+ const originalEnv = process.env.NODE_ENV;
222
+ process.env.NODE_ENV = 'development';
223
+
224
+ vi.resetModules();
225
+ const { validateImportPattern } = await import('../bundleAnalysis');
226
+ validateImportPattern('small-module', ['func1', 'func2', 'func3', 'func4', 'func5', 'func6']);
227
+
228
+ expect(consoleSpy.warn).not.toHaveBeenCalled();
229
+
230
+ process.env.NODE_ENV = originalEnv;
231
+ });
232
+
233
+ it('should detect lodash as large module', async () => {
234
+ const originalEnv = process.env.NODE_ENV;
235
+ process.env.NODE_ENV = 'development';
236
+
237
+ vi.resetModules();
238
+ const { validateImportPattern } = await import('../bundleAnalysis');
239
+ validateImportPattern('lodash-es', ['debounce', 'throttle', 'map', 'filter', 'reduce', 'forEach']);
240
+
241
+ expect(consoleSpy.warn).toHaveBeenCalled();
242
+
243
+ process.env.NODE_ENV = originalEnv;
244
+ });
245
+
246
+ it('should detect moment as large module', async () => {
247
+ const originalEnv = process.env.NODE_ENV;
248
+ process.env.NODE_ENV = 'development';
249
+
250
+ vi.resetModules();
251
+ const { validateImportPattern } = await import('../bundleAnalysis');
252
+ validateImportPattern('moment', ['format', 'parse', 'add', 'subtract', 'diff', 'isValid']);
253
+
254
+ expect(consoleSpy.warn).toHaveBeenCalled();
255
+
256
+ process.env.NODE_ENV = originalEnv;
257
+ });
258
+
259
+ it('should detect rxjs as large module', async () => {
260
+ const originalEnv = process.env.NODE_ENV;
261
+ process.env.NODE_ENV = 'development';
262
+
263
+ vi.resetModules();
264
+ const { validateImportPattern } = await import('../bundleAnalysis');
265
+ validateImportPattern('rxjs', ['Observable', 'Subject', 'BehaviorSubject', 'map', 'filter', 'switchMap']);
266
+
267
+ expect(consoleSpy.warn).toHaveBeenCalled();
268
+
269
+ process.env.NODE_ENV = originalEnv;
270
+ });
271
+ });
272
+
273
+ describe('trackDynamicImport', () => {
274
+ it('should do nothing in production mode', async () => {
275
+ const originalEnv = process.env.NODE_ENV;
276
+ process.env.NODE_ENV = 'production';
277
+
278
+ vi.resetModules();
279
+ const { trackDynamicImport } = await import('../bundleAnalysis');
280
+ trackDynamicImport('some-module');
281
+
282
+ expect(consoleSpy.log).not.toHaveBeenCalled();
283
+
284
+ process.env.NODE_ENV = originalEnv;
285
+ });
286
+
287
+ it('should log dynamic imports in development mode', async () => {
288
+ const originalEnv = process.env.NODE_ENV;
289
+ process.env.NODE_ENV = 'development';
290
+
291
+ vi.resetModules();
292
+ const { trackDynamicImport } = await import('../bundleAnalysis');
293
+ trackDynamicImport('react-hook-form');
294
+
295
+ expect(consoleSpy.log).toHaveBeenCalledWith(
296
+ 'Dynamic import: react-hook-form - Good for code splitting!'
297
+ );
298
+
299
+ process.env.NODE_ENV = originalEnv;
300
+ });
301
+
302
+ it('should log different module names', async () => {
303
+ const originalEnv = process.env.NODE_ENV;
304
+ process.env.NODE_ENV = 'development';
305
+
306
+ vi.resetModules();
307
+ const { trackDynamicImport } = await import('../bundleAnalysis');
308
+ trackDynamicImport('date-fns');
309
+
310
+ expect(consoleSpy.log).toHaveBeenCalledWith(
311
+ 'Dynamic import: date-fns - Good for code splitting!'
312
+ );
313
+
314
+ process.env.NODE_ENV = originalEnv;
315
+ });
316
+ });
317
+ });
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { cn } from '../cn';
3
+
4
+ describe('cn utility', () => {
5
+ it('should merge class names correctly', () => {
6
+ expect(cn('class1', 'class2')).toBe('class1 class2');
7
+ });
8
+
9
+ it('should handle conditional classes', () => {
10
+ expect(cn('base', true && 'conditional')).toBe('base conditional');
11
+ expect(cn('base', false && 'conditional')).toBe('base');
12
+ });
13
+
14
+ it('should filter out falsy values', () => {
15
+ expect(cn('base', null, undefined, false, '', 'valid')).toBe('base valid');
16
+ });
17
+
18
+ it('should handle arrays', () => {
19
+ expect(cn('base', ['class1', 'class2'])).toBe('base class1 class2');
20
+ });
21
+
22
+ it('should handle objects', () => {
23
+ expect(cn('base', { conditional: true, ignored: false })).toBe('base conditional');
24
+ });
25
+
26
+ it('should handle mixed inputs', () => {
27
+ expect(cn('base', 'static', { conditional: true }, ['array1', 'array2'])).toBe('base static conditional array1 array2');
28
+ });
29
+
30
+ it('should handle empty inputs', () => {
31
+ expect(cn()).toBe('');
32
+ expect(cn('')).toBe('');
33
+ });
34
+ });