@codeleap/styles 5.8.2 → 5.8.4

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 (69) hide show
  1. package/package.json +9 -15
  2. package/package.json.bak +8 -14
  3. package/src/classes/Cacher.ts +169 -0
  4. package/src/classes/StaleControl.ts +125 -0
  5. package/src/classes/StyleCache.ts +116 -0
  6. package/src/classes/StylePersistor.ts +62 -0
  7. package/src/{lib → classes}/StyleRegistry.ts +7 -12
  8. package/src/classes/index.ts +2 -0
  9. package/src/classes/tests/Cache.spec.ts +371 -0
  10. package/src/classes/tests/StaleControl.spec.ts +175 -0
  11. package/src/classes/tests/StyleCache.spec.ts +452 -0
  12. package/src/classes/tests/StylePersistor.spec.ts +231 -0
  13. package/src/{lib/constants.ts → constants.ts} +1 -0
  14. package/src/hooks/index.ts +4 -0
  15. package/src/hooks/tests/useCompositionStyles.spec.ts +107 -0
  16. package/src/hooks/tests/useStyleObserver.spec.ts +89 -0
  17. package/src/hooks/useCompositionStyles.ts +33 -0
  18. package/src/hooks/useNestedStylesByKey.ts +13 -0
  19. package/src/hooks/useStyleObserver.ts +19 -0
  20. package/src/hooks/useTheme.ts +16 -0
  21. package/src/index.ts +9 -5
  22. package/src/lib/createStyles.ts +10 -1
  23. package/src/lib/createTheme.ts +22 -13
  24. package/src/lib/index.ts +1 -10
  25. package/src/lib/tests/createStyles.spec.ts +151 -0
  26. package/src/tests/colors/baseColors.ts +166 -0
  27. package/src/tests/colors/darkMode.ts +232 -0
  28. package/src/tests/colors/lightMode.ts +285 -0
  29. package/src/tests/measures.ts +31 -0
  30. package/src/tests/theme.ts +58 -0
  31. package/src/theme/generateColorScheme.ts +53 -0
  32. package/src/theme/index.ts +3 -0
  33. package/src/theme/tests/generateColorScheme.spec.ts +118 -0
  34. package/src/theme/tests/themeStore.spec.ts +698 -0
  35. package/src/theme/tests/validateTheme.spec.ts +173 -0
  36. package/src/{lib → theme}/themeStore.ts +68 -3
  37. package/src/{lib → theme}/validateTheme.ts +13 -0
  38. package/src/tools/colors.ts +83 -39
  39. package/src/tools/deepClone.ts +10 -0
  40. package/src/tools/deepmerge.ts +10 -0
  41. package/src/{lib → tools}/hashKey.ts +7 -0
  42. package/src/tools/index.ts +6 -1
  43. package/src/tools/minifier.ts +38 -0
  44. package/src/tools/tests/colors.spec.ts +233 -0
  45. package/src/tools/tests/deepClone.spec.ts +102 -0
  46. package/src/tools/tests/deepmerge.spec.ts +155 -0
  47. package/src/tools/tests/hashKey.spec.ts +69 -0
  48. package/src/tools/tests/minifier.spec.ts +173 -0
  49. package/src/types/store.ts +2 -2
  50. package/src/types/style.ts +3 -3
  51. package/src/types/theme.ts +4 -4
  52. package/src/{lib/utils.ts → utils.ts} +3 -3
  53. package/src/{lib → variants}/borderCreator.ts +2 -2
  54. package/src/{lib → variants}/createAppVariants.ts +1 -1
  55. package/src/{lib → variants}/dynamicVariants.ts +1 -1
  56. package/src/variants/index.ts +6 -0
  57. package/src/{lib → variants}/spacing.ts +37 -24
  58. package/src/variants/tests/borderCreator.spec.ts +180 -0
  59. package/src/variants/tests/dynamicVariants.spec.ts +194 -0
  60. package/src/variants/tests/spacing.spec.ts +177 -0
  61. package/src/lib/Cacher.ts +0 -112
  62. package/src/lib/StaleControl.ts +0 -73
  63. package/src/lib/StyleCache.ts +0 -81
  64. package/src/lib/StylePersistor.ts +0 -28
  65. package/src/lib/hooks.ts +0 -64
  66. package/src/lib/minifier.ts +0 -20
  67. /package/src/{lib → tools}/multiplierProperty.ts +0 -0
  68. /package/src/{lib → variants}/defaultVariants.ts +0 -0
  69. /package/src/{lib → variants}/mediaQuery.ts +0 -0
@@ -0,0 +1,452 @@
1
+ import { describe, test, expect, beforeEach } from 'bun:test'
2
+ import { StyleCache } from '../StyleCache'
3
+
4
+ class MockStateStorage {
5
+ private storage = new Map<string, any>()
6
+
7
+ getItem(key: string): any {
8
+ return this.storage.get(key) || null
9
+ }
10
+
11
+ setItem(key: string, value: any): void {
12
+ this.storage.set(key, value)
13
+ }
14
+
15
+ removeItem(key: string): void {
16
+ this.storage.delete(key)
17
+ }
18
+
19
+ clear(): void {
20
+ this.storage.clear()
21
+ }
22
+
23
+ get size(): number {
24
+ return this.storage.size
25
+ }
26
+ }
27
+
28
+ describe('StyleCache', () => {
29
+ let styleCache: StyleCache
30
+ let mockStorage: MockStateStorage
31
+
32
+ beforeEach(() => {
33
+ mockStorage = new MockStateStorage()
34
+ styleCache = new StyleCache(mockStorage)
35
+ })
36
+
37
+ describe('Constructor', () => {
38
+ test('should create StyleCache with all cache instances', () => {
39
+ expect(styleCache).toBeDefined()
40
+ expect(styleCache.styles).toBeDefined()
41
+ expect(styleCache.compositions).toBeDefined()
42
+ expect(styleCache.responsive).toBeDefined()
43
+ expect(styleCache.variants).toBeDefined()
44
+ expect(styleCache.common).toBeDefined()
45
+ expect(styleCache.components).toBeDefined()
46
+ })
47
+
48
+ test('should create non-persistent caches for styles, compositions, and responsive', () => {
49
+ expect(styleCache.styles.registryName).toBe('styles')
50
+ expect(styleCache.compositions.registryName).toBe('compositions')
51
+ expect(styleCache.responsive.registryName).toBe('responsive')
52
+
53
+ // Non-persistent caches should not have persistCache enabled
54
+ expect(styleCache.styles.persistCache).toBe(false)
55
+ expect(styleCache.compositions.persistCache).toBe(false)
56
+ expect(styleCache.responsive.persistCache).toBe(false)
57
+ })
58
+
59
+ test('should create persistent caches for variants, common, and components', () => {
60
+ expect(styleCache.variants.registryName).toBe('variants')
61
+ expect(styleCache.common.registryName).toBe('common')
62
+ expect(styleCache.components.registryName).toBe('components')
63
+
64
+ // Persistent caches should have persistCache enabled
65
+ expect(styleCache.variants.persistCache).toBe(true)
66
+ expect(styleCache.common.persistCache).toBe(true)
67
+ expect(styleCache.components.persistCache).toBe(true)
68
+ })
69
+ })
70
+
71
+ describe('registerBaseKey', () => {
72
+ test('should generate base key with provided keys and version', () => {
73
+ const keys = ['key1', 'key2', { prop: 'value' }]
74
+ const originalKeysLength = keys.length
75
+
76
+ const result = styleCache.registerBaseKey(keys)
77
+
78
+ expect(typeof result).toBe('string')
79
+ expect(result).toBeTruthy()
80
+ expect(styleCache.baseKey).toBe(result)
81
+ // Should have added the version to keys array
82
+ expect(keys.length).toBeGreaterThanOrEqual(originalKeysLength + 1)
83
+ })
84
+
85
+ test('should modify original keys array by adding version', () => {
86
+ const keys = ['test']
87
+ const originalKeys = [...keys]
88
+
89
+ styleCache.registerBaseKey(keys)
90
+
91
+ expect(keys.length).toBeGreaterThanOrEqual(originalKeys.length + 1)
92
+ expect(keys.slice(0, 1)).toEqual(originalKeys)
93
+ // Last item should be the version
94
+ expect(typeof keys[keys.length - 1]).toBe('object')
95
+ })
96
+
97
+ test('should handle empty keys array', () => {
98
+ const keys: any[] = []
99
+
100
+ const result = styleCache.registerBaseKey(keys)
101
+
102
+ expect(typeof result).toBe('string')
103
+ expect(result).toBeTruthy()
104
+ expect(keys.length).toBeGreaterThanOrEqual(1)
105
+ expect(styleCache.baseKey).toBe(result)
106
+ })
107
+
108
+ test('should generate different keys for different inputs', () => {
109
+ const keys1 = ['a', 'b']
110
+ const keys2 = ['c', 'd']
111
+
112
+ const result1 = styleCache.registerBaseKey(keys1)
113
+ const result2 = styleCache.registerBaseKey(keys2)
114
+
115
+ expect(result1).not.toBe(result2)
116
+ })
117
+ })
118
+
119
+ describe('wipeCache', () => {
120
+ test('should clear all cache instances', () => {
121
+ // First, add some data to caches
122
+ styleCache.styles.cache['test1'] = 'value1'
123
+ styleCache.compositions.cache['test2'] = 'value2'
124
+ styleCache.responsive.cache['test3'] = 'value3'
125
+ styleCache.variants.cache['test4'] = 'value4'
126
+ styleCache.common.cache['test5'] = 'value5'
127
+ styleCache.components.cache['test6'] = 'value6'
128
+
129
+ // Verify data exists
130
+ expect(Object.keys(styleCache.styles.cache).length).toBeGreaterThan(0)
131
+ expect(Object.keys(styleCache.compositions.cache).length).toBeGreaterThan(0)
132
+ expect(Object.keys(styleCache.responsive.cache).length).toBeGreaterThan(0)
133
+ expect(Object.keys(styleCache.variants.cache).length).toBeGreaterThan(0)
134
+ expect(Object.keys(styleCache.common.cache).length).toBeGreaterThan(0)
135
+ expect(Object.keys(styleCache.components.cache).length).toBeGreaterThan(0)
136
+
137
+ // Clear all caches
138
+ styleCache.wipeCache()
139
+
140
+ // Verify all caches are empty
141
+ expect(Object.keys(styleCache.styles.cache)).toHaveLength(0)
142
+ expect(Object.keys(styleCache.compositions.cache)).toHaveLength(0)
143
+ expect(Object.keys(styleCache.responsive.cache)).toHaveLength(0)
144
+ expect(Object.keys(styleCache.variants.cache)).toHaveLength(0)
145
+ expect(Object.keys(styleCache.common.cache)).toHaveLength(0)
146
+ expect(Object.keys(styleCache.components.cache)).toHaveLength(0)
147
+ })
148
+
149
+ test('should also clear persistent storage for persistent caches', () => {
150
+ // Add some data that would be persisted
151
+ styleCache.variants.cacheFor('persistent-key', 'persistent-value')
152
+ styleCache.common.cacheFor('common-key', 'common-value')
153
+ styleCache.components.cacheFor('component-key', 'component-value')
154
+
155
+ // Verify storage has data
156
+ expect(mockStorage.size).toBeGreaterThan(0)
157
+
158
+ styleCache.wipeCache()
159
+
160
+ // Storage should be cleared for persistent caches
161
+ expect(mockStorage.size).toBe(0)
162
+ })
163
+ })
164
+
165
+ describe('keyFor', () => {
166
+ beforeEach(() => {
167
+ styleCache.registerBaseKey(['test-app'])
168
+ })
169
+
170
+ test('should generate key and return null for non-existent cache value', () => {
171
+ const keyData = { prop1: 'value1', prop2: 'value2' }
172
+
173
+ const result = styleCache.keyFor('styles', keyData)
174
+
175
+ expect(typeof result.key).toBe('string')
176
+ expect(result.key).toBeTruthy()
177
+ expect(result.value).toBeNull()
178
+ })
179
+
180
+ test('should handle functions in keyData by converting to string', () => {
181
+ const testFunction = () => 'test'
182
+ const keyData = {
183
+ prop1: 'value1',
184
+ func: testFunction,
185
+ prop2: 'value2'
186
+ }
187
+
188
+ const result = styleCache.keyFor('components', keyData)
189
+
190
+ expect(typeof result.key).toBe('string')
191
+ expect(result.key).toBeTruthy()
192
+ expect(result.value).toBeNull()
193
+ })
194
+
195
+ test('should return existing cached value after decompression', () => {
196
+ const keyData = { prop: 'value' }
197
+ const testValue = { result: 'data' }
198
+
199
+ // First, get the key that would be generated
200
+ const { key } = styleCache.keyFor('styles', keyData)
201
+
202
+ // Cache the value
203
+ styleCache.cacheFor('styles', key, testValue)
204
+
205
+ // Retrieve the cached value
206
+ const result = styleCache.keyFor('styles', keyData)
207
+
208
+ expect(result.key).toBe(key)
209
+ expect(result.value).toEqual(testValue)
210
+ })
211
+
212
+ test('should work with array keyData', () => {
213
+ const keyData = ['item1', 'item2', 'item3']
214
+
215
+ const result = styleCache.keyFor('responsive', keyData)
216
+
217
+ expect(typeof result.key).toBe('string')
218
+ expect(result.key).toBeTruthy()
219
+ expect(result.value).toBeNull()
220
+ })
221
+
222
+ test('should generate different keys for different keyData', () => {
223
+ const keyData1 = { prop: 'value1' }
224
+ const keyData2 = { prop: 'value2' }
225
+
226
+ const result1 = styleCache.keyFor('styles', keyData1)
227
+ const result2 = styleCache.keyFor('styles', keyData2)
228
+
229
+ expect(result1.key).not.toBe(result2.key)
230
+ })
231
+
232
+ test('should work with different cache types', () => {
233
+ const keyData = { test: 'data' }
234
+ const cacheTypes = ['styles', 'compositions', 'responsive', 'variants', 'common', 'components'] as const
235
+
236
+ const results = cacheTypes.map(cacheType => {
237
+ return styleCache.keyFor(cacheType, keyData)
238
+ })
239
+
240
+ // All should generate keys
241
+ results.forEach(result => {
242
+ expect(typeof result.key).toBe('string')
243
+ expect(result.key).toBeTruthy()
244
+ })
245
+
246
+ // Keys should be the same since baseKey and keyData are the same
247
+ const firstKey = results[0].key
248
+ results.forEach(result => {
249
+ expect(result.key).toBe(firstKey)
250
+ })
251
+ })
252
+ })
253
+
254
+ describe('cacheFor', () => {
255
+ beforeEach(() => {
256
+ styleCache.registerBaseKey(['test-app'])
257
+ })
258
+
259
+ test('should compress and cache value when CACHE_ENABLED is true', () => {
260
+ const testValue = { data: 'test', number: 42 }
261
+
262
+ const result = styleCache.cacheFor('styles', 'test-key', testValue)
263
+
264
+ // Should return compressed value
265
+ expect(typeof result).toBe('string')
266
+
267
+ // Should be stored in cache
268
+ expect(styleCache.styles.cache['test-key']).toBeDefined()
269
+ })
270
+
271
+ test('should work with different cache types', () => {
272
+ const testValue = { test: 'data' }
273
+ const cacheTypes = ['styles', 'compositions', 'responsive', 'variants', 'common', 'components'] as const
274
+
275
+ cacheTypes.forEach(cacheType => {
276
+ const result = styleCache.cacheFor(cacheType, `${cacheType}-key`, testValue)
277
+
278
+ expect(result).toBeDefined()
279
+ expect(styleCache[cacheType].cache[`${cacheType}-key`]).toBeDefined()
280
+ })
281
+ })
282
+
283
+ test('should handle various data types', () => {
284
+ const testCases = [
285
+ { name: 'object', value: { prop: 'value' } },
286
+ { name: 'array', value: [1, 2, 3] },
287
+ { name: 'string', value: 'test string' },
288
+ { name: 'number', value: 42 },
289
+ { name: 'boolean', value: true },
290
+ ]
291
+
292
+ testCases.forEach(({ name, value }) => {
293
+ const result = styleCache.cacheFor('styles', `key-${name}`, value)
294
+ expect(result).toBeDefined()
295
+ expect(styleCache.styles.cache[`key-${name}`]).toBeDefined()
296
+ })
297
+ })
298
+
299
+ test('should persist cache for persistent cache types', () => {
300
+ const testValue = { persistent: 'data' }
301
+
302
+ styleCache.cacheFor('variants', 'persistent-key', testValue)
303
+
304
+ // Should be stored in memory cache
305
+ expect(styleCache.variants.cache['persistent-key']).toBeDefined()
306
+
307
+ // Should also be persisted to storage
308
+ expect(mockStorage.size).toBeGreaterThan(0)
309
+ })
310
+ })
311
+
312
+ describe('Integration tests', () => {
313
+ test('should handle complete cache workflow', () => {
314
+ // 1. Register base key
315
+ const baseKey = styleCache.registerBaseKey(['my-app', 'v1.0'])
316
+ expect(styleCache.baseKey).toBe(baseKey)
317
+
318
+ // 2. Generate key and check for existing value (should be null)
319
+ const keyData = { component: 'Button', variant: 'primary', size: 'large' }
320
+ let result = styleCache.keyFor('components', keyData)
321
+ expect(result.value).toBeNull()
322
+
323
+ // 3. Cache a value
324
+ const testValue = {
325
+ backgroundColor: 'blue',
326
+ color: 'white',
327
+ padding: '12px 24px',
328
+ borderRadius: '4px'
329
+ }
330
+ const cachedResult = styleCache.cacheFor('components', result.key, testValue)
331
+ expect(cachedResult).toBeDefined()
332
+
333
+ // 4. Retrieve cached value (should now exist)
334
+ result = styleCache.keyFor('components', keyData)
335
+ expect(result.value).toEqual(testValue)
336
+ })
337
+
338
+ test('should maintain separate caches for different types', () => {
339
+ const keyData = { test: 'data' }
340
+ const stylesValue = { color: 'red' }
341
+ const componentsValue = { backgroundColor: 'blue' }
342
+
343
+ // Get keys for both cache types (should be the same)
344
+ const stylesResult = styleCache.keyFor('styles', keyData)
345
+ const componentsResult = styleCache.keyFor('components', keyData)
346
+ expect(stylesResult.key).toBe(componentsResult.key)
347
+
348
+ // Cache different values in each cache type
349
+ styleCache.cacheFor('styles', stylesResult.key, stylesValue)
350
+ styleCache.cacheFor('components', componentsResult.key, componentsValue)
351
+
352
+ // Retrieve values - should be different
353
+ const retrievedStyles = styleCache.keyFor('styles', keyData)
354
+ const retrievedComponents = styleCache.keyFor('components', keyData)
355
+
356
+ expect(retrievedStyles.value).toEqual(stylesValue)
357
+ expect(retrievedComponents.value).toEqual(componentsValue)
358
+ })
359
+
360
+ test('should handle cache persistence and restoration', () => {
361
+ // Cache some values in persistent caches
362
+ const variantsValue = { variant: 'primary' }
363
+ const commonValue = { common: 'styles' }
364
+
365
+ styleCache.cacheFor('variants', 'variants-key', variantsValue)
366
+ styleCache.cacheFor('common', 'common-key', commonValue)
367
+
368
+ // Verify persistence
369
+ expect(mockStorage.size).toBeGreaterThan(0)
370
+
371
+ // Create new StyleCache with same storage
372
+ const newStyleCache = new StyleCache(mockStorage)
373
+
374
+ // Values should be restored from storage
375
+ expect(Object.keys(newStyleCache.variants.cache).length).toBeGreaterThan(0)
376
+ expect(Object.keys(newStyleCache.common.cache).length).toBeGreaterThan(0)
377
+ })
378
+
379
+ test('should handle cache clearing and restoration', () => {
380
+ // Add data to all caches
381
+ const testValue = { data: 'test' }
382
+
383
+ styleCache.cacheFor('styles', 'styles-key', testValue)
384
+ styleCache.cacheFor('components', 'components-key', testValue)
385
+ styleCache.cacheFor('variants', 'variants-key', testValue)
386
+
387
+ // Verify data exists
388
+ expect(Object.keys(styleCache.styles.cache).length).toBeGreaterThan(0)
389
+ expect(Object.keys(styleCache.components.cache).length).toBeGreaterThan(0)
390
+ expect(Object.keys(styleCache.variants.cache).length).toBeGreaterThan(0)
391
+ expect(mockStorage.size).toBeGreaterThan(0)
392
+
393
+ // Clear all caches
394
+ styleCache.wipeCache()
395
+
396
+ // Verify everything is cleared
397
+ expect(Object.keys(styleCache.styles.cache)).toHaveLength(0)
398
+ expect(Object.keys(styleCache.components.cache)).toHaveLength(0)
399
+ expect(Object.keys(styleCache.variants.cache)).toHaveLength(0)
400
+ expect(mockStorage.size).toBe(0)
401
+ })
402
+
403
+ test('should handle complex keyData with nested objects and functions', () => {
404
+ styleCache.registerBaseKey(['complex-test'])
405
+
406
+ const complexKeyData = {
407
+ theme: {
408
+ primary: '#007bff',
409
+ secondary: '#6c757d',
410
+ breakpoints: ['sm', 'md', 'lg']
411
+ },
412
+ transform: (value: string) => value.toUpperCase(),
413
+ isActive: true,
414
+ index: 0
415
+ }
416
+
417
+ // Should handle complex data without errors
418
+ const result = styleCache.keyFor('styles', complexKeyData)
419
+ expect(typeof result.key).toBe('string')
420
+ expect(result.key).toBeTruthy()
421
+
422
+ // Should be able to cache and retrieve
423
+ const complexValue = {
424
+ computed: 'styles',
425
+ rules: ['rule1', 'rule2']
426
+ }
427
+
428
+ styleCache.cacheFor('styles', result.key, complexValue)
429
+
430
+ const retrieved = styleCache.keyFor('styles', complexKeyData)
431
+ expect(retrieved.value).toEqual(complexValue)
432
+ })
433
+ })
434
+
435
+ describe('Edge cases and error handling', () => {
436
+ test('should handle very large objects', () => {
437
+ styleCache.registerBaseKey(['large-objects'])
438
+
439
+ const largeObject: Record<string, any> = {}
440
+ for (let i = 0; i < 1000; i++) {
441
+ largeObject[`key${i}`] = `value${i}`
442
+ }
443
+
444
+ const result = styleCache.keyFor('styles', largeObject)
445
+ expect(result.key).toBeTruthy()
446
+
447
+ styleCache.cacheFor('styles', result.key, largeObject)
448
+ const retrieved = styleCache.keyFor('styles', largeObject)
449
+ expect(retrieved.value).toEqual(largeObject)
450
+ })
451
+ })
452
+ })
@@ -0,0 +1,231 @@
1
+ import { test, expect, describe, beforeEach, jest, Mock } from 'bun:test'
2
+ import { StylePersistor, StoragePersistor } from '../StylePersistor'
3
+ import { minifier } from '../../tools'
4
+
5
+ interface MockStorage extends StoragePersistor {
6
+ set: Mock<(...args: any[]) => any>
7
+ get: Mock<(...args: any[]) => any>
8
+ del: Mock<(...args: any[]) => any>
9
+ }
10
+
11
+ describe('StylePersistor', () => {
12
+ let mockStorage: MockStorage
13
+ let persistor: StylePersistor
14
+
15
+ beforeEach(() => {
16
+ mockStorage = {
17
+ set: jest.fn(),
18
+ get: jest.fn(),
19
+ del: jest.fn()
20
+ } as MockStorage
21
+
22
+ persistor = new StylePersistor(mockStorage)
23
+ })
24
+
25
+ describe('setItem', () => {
26
+ test('should compress and store value using underlying storage', () => {
27
+ const testValue = 'test-value'
28
+ const compressedValue = minifier.compress(testValue)
29
+
30
+ persistor.setItem('test-key', testValue)
31
+
32
+ expect(mockStorage.set).toHaveBeenCalledWith('test-key', compressedValue)
33
+ })
34
+
35
+ test('should handle empty string values with compression', () => {
36
+ const compressedEmpty = minifier.compress('')
37
+
38
+ persistor.setItem('empty', '')
39
+
40
+ expect(mockStorage.set).toHaveBeenCalledWith('empty', compressedEmpty)
41
+ })
42
+
43
+ test('should handle special characters with compression', () => {
44
+ const value = 'value with spaces & symbols!'
45
+ const compressedValue = minifier.compress(value)
46
+
47
+ persistor.setItem('special/key:test', value)
48
+
49
+ expect(mockStorage.set).toHaveBeenCalledWith('special/key:test', compressedValue)
50
+ })
51
+ })
52
+
53
+ describe('getItem', () => {
54
+ test('should retrieve and decompress stored value', () => {
55
+ const originalData = 'test data for compression'
56
+ const compressedData = minifier.compress(originalData)
57
+
58
+ mockStorage.get.mockReturnValue(compressedData)
59
+
60
+ const result = persistor.getItem('test-key')
61
+
62
+ expect(mockStorage.get).toHaveBeenCalledWith('test-key')
63
+ expect(result).toBe(originalData)
64
+ })
65
+
66
+ test('should return null when storage returns null', () => {
67
+ mockStorage.get.mockReturnValue(null)
68
+
69
+ const result = persistor.getItem('nonexistent-key')
70
+
71
+ expect(result).toBeNull()
72
+ })
73
+
74
+ test('should return null when storage returns undefined', () => {
75
+ mockStorage.get.mockReturnValue(undefined)
76
+
77
+ const result = persistor.getItem('undefined-key')
78
+
79
+ expect(result).toBeNull()
80
+ })
81
+
82
+ test('should handle various data types with compression/decompression', () => {
83
+ const testCases = [
84
+ 'simple string',
85
+ '{"json": "data"}',
86
+ '',
87
+ 'special chars: áéíóú!@#$%',
88
+ '12345'
89
+ ]
90
+
91
+ testCases.forEach(testData => {
92
+ const compressedData = minifier.compress(testData)
93
+ mockStorage.get.mockReturnValue(compressedData)
94
+
95
+ const result = persistor.getItem('test-key')
96
+ expect(result).toBe(testData)
97
+ })
98
+ })
99
+
100
+ test('should return null when decompression fails', () => {
101
+ const invalidData = 'invalid compressed data'
102
+ mockStorage.get.mockReturnValue(invalidData)
103
+
104
+ const originalDecompress = minifier.decompress
105
+ minifier.decompress = jest.fn().mockReturnValue(null)
106
+
107
+ const result = persistor.getItem('invalid-key')
108
+
109
+ expect(result).toBeNull()
110
+
111
+ minifier.decompress = originalDecompress
112
+ })
113
+ })
114
+
115
+ describe('removeItem', () => {
116
+ test('should delete item using underlying storage', () => {
117
+ persistor.removeItem('test-key')
118
+
119
+ expect(mockStorage.del).toHaveBeenCalledWith('test-key')
120
+ })
121
+
122
+ test('should handle removal of nonexistent keys', () => {
123
+ persistor.removeItem('nonexistent-key')
124
+
125
+ expect(mockStorage.del).toHaveBeenCalledWith('nonexistent-key')
126
+ })
127
+
128
+ test('should handle special characters in key', () => {
129
+ persistor.removeItem('special/key:test')
130
+
131
+ expect(mockStorage.del).toHaveBeenCalledWith('special/key:test')
132
+ })
133
+ })
134
+
135
+ describe('integration with real minifier', () => {
136
+ test('should handle complete compression/decompression workflow', () => {
137
+ const testData = 'workflow test data'
138
+ const compressedData = minifier.compress(testData)
139
+
140
+ persistor.setItem('workflow-test', testData)
141
+ expect(mockStorage.set).toHaveBeenCalledWith('workflow-test', compressedData)
142
+
143
+ mockStorage.get.mockReturnValue(compressedData)
144
+ const retrieved = persistor.getItem('workflow-test')
145
+ expect(retrieved).toBe(testData)
146
+
147
+ persistor.removeItem('workflow-test')
148
+ expect(mockStorage.del).toHaveBeenCalledWith('workflow-test')
149
+ })
150
+
151
+ test('should verify compression actually reduces size for large data', () => {
152
+ const largeData = 'a'.repeat(1000)
153
+ const compressedData = minifier.compress(largeData)
154
+
155
+ expect(compressedData.length).toBeLessThanOrEqual(largeData.length)
156
+
157
+ mockStorage.get.mockReturnValue(compressedData)
158
+ const result = persistor.getItem('large-data')
159
+
160
+ expect(result).toBe(largeData)
161
+ })
162
+ })
163
+
164
+ describe('error handling', () => {
165
+ test('should handle storage set errors', () => {
166
+ const errorStorage: MockStorage = {
167
+ set: jest.fn().mockImplementation(() => {
168
+ throw new Error('Storage set error')
169
+ }),
170
+ get: jest.fn(),
171
+ del: jest.fn()
172
+ } as MockStorage
173
+
174
+ const errorPersistor = new StylePersistor(errorStorage)
175
+
176
+ expect(() => errorPersistor.setItem('key', 'value')).toThrow('Storage set error')
177
+ })
178
+
179
+ test('should handle storage get errors', () => {
180
+ const errorStorage: MockStorage = {
181
+ set: jest.fn(),
182
+ get: jest.fn().mockImplementation(() => {
183
+ throw new Error('Storage get error')
184
+ }),
185
+ del: jest.fn()
186
+ } as MockStorage
187
+
188
+ const errorPersistor = new StylePersistor(errorStorage)
189
+
190
+ expect(() => errorPersistor.getItem('key')).toThrow('Storage get error')
191
+ })
192
+
193
+ test('should handle storage delete errors', () => {
194
+ const errorStorage: MockStorage = {
195
+ set: jest.fn(),
196
+ get: jest.fn(),
197
+ del: jest.fn().mockImplementation(() => {
198
+ throw new Error('Storage del error')
199
+ })
200
+ } as MockStorage
201
+
202
+ const errorPersistor = new StylePersistor(errorStorage)
203
+
204
+ expect(() => errorPersistor.removeItem('key')).toThrow('Storage del error')
205
+ })
206
+
207
+ test('should handle minifier compression errors', () => {
208
+ const originalCompress = minifier.compress
209
+ minifier.compress = jest.fn().mockImplementation(() => {
210
+ throw new Error('Compression error')
211
+ })
212
+
213
+ expect(() => persistor.setItem('key', 'value')).toThrow('Compression error')
214
+
215
+ minifier.compress = originalCompress
216
+ })
217
+
218
+ test('should handle minifier decompression errors gracefully', () => {
219
+ const originalDecompress = minifier.decompress
220
+ minifier.decompress = jest.fn().mockImplementation(() => {
221
+ throw new Error('Decompression error')
222
+ })
223
+
224
+ mockStorage.get.mockReturnValue('some data')
225
+
226
+ expect(() => persistor.getItem('error-key')).toThrow('Decompression error')
227
+
228
+ minifier.decompress = originalDecompress
229
+ })
230
+ })
231
+ })
@@ -6,4 +6,5 @@ export const StyleConstants = {
6
6
  STORE_CACHE_ENABLED: true,
7
7
  CACHE_ENABLED: true,
8
8
  IS_BROWSER: isBrowser() as boolean,
9
+ LOG: false,
9
10
  }
@@ -0,0 +1,4 @@
1
+ export * from './useCompositionStyles'
2
+ export * from './useNestedStylesByKey'
3
+ export * from './useStyleObserver'
4
+ export * from './useTheme'