@codeleap/styles 5.8.1 → 5.8.3
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.
- package/package.json +9 -15
- package/package.json.bak +8 -14
- package/src/classes/Cacher.ts +169 -0
- package/src/classes/StaleControl.ts +125 -0
- package/src/classes/StyleCache.ts +116 -0
- package/src/classes/StylePersistor.ts +62 -0
- package/src/{lib → classes}/StyleRegistry.ts +7 -12
- package/src/classes/index.ts +2 -0
- package/src/classes/tests/Cache.spec.ts +371 -0
- package/src/classes/tests/StaleControl.spec.ts +175 -0
- package/src/classes/tests/StyleCache.spec.ts +452 -0
- package/src/classes/tests/StylePersistor.spec.ts +231 -0
- package/src/{lib/constants.ts → constants.ts} +1 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/tests/useCompositionStyles.spec.ts +107 -0
- package/src/hooks/tests/useStyleObserver.spec.ts +89 -0
- package/src/hooks/useCompositionStyles.ts +33 -0
- package/src/hooks/useNestedStylesByKey.ts +13 -0
- package/src/hooks/useStyleObserver.ts +19 -0
- package/src/hooks/useTheme.ts +16 -0
- package/src/index.ts +9 -5
- package/src/lib/createStyles.ts +10 -1
- package/src/lib/createTheme.ts +22 -13
- package/src/lib/index.ts +1 -10
- package/src/lib/tests/createStyles.spec.ts +151 -0
- package/src/tests/colors/baseColors.ts +166 -0
- package/src/tests/colors/darkMode.ts +232 -0
- package/src/tests/colors/lightMode.ts +285 -0
- package/src/tests/measures.ts +31 -0
- package/src/tests/theme.ts +58 -0
- package/src/theme/generateColorScheme.ts +53 -0
- package/src/theme/index.ts +3 -0
- package/src/theme/tests/generateColorScheme.spec.ts +118 -0
- package/src/theme/tests/themeStore.spec.ts +698 -0
- package/src/theme/tests/validateTheme.spec.ts +173 -0
- package/src/{lib → theme}/themeStore.ts +68 -3
- package/src/{lib → theme}/validateTheme.ts +13 -0
- package/src/tools/colors.ts +83 -39
- package/src/tools/deepClone.ts +10 -0
- package/src/tools/deepmerge.ts +10 -0
- package/src/{lib → tools}/hashKey.ts +7 -0
- package/src/tools/index.ts +6 -1
- package/src/tools/minifier.ts +38 -0
- package/src/tools/tests/colors.spec.ts +233 -0
- package/src/tools/tests/deepClone.spec.ts +102 -0
- package/src/tools/tests/deepmerge.spec.ts +155 -0
- package/src/tools/tests/hashKey.spec.ts +69 -0
- package/src/tools/tests/minifier.spec.ts +173 -0
- package/src/types/store.ts +2 -2
- package/src/types/style.ts +3 -3
- package/src/types/theme.ts +4 -4
- package/src/{lib/utils.ts → utils.ts} +3 -3
- package/src/{lib → variants}/borderCreator.ts +2 -2
- package/src/{lib → variants}/createAppVariants.ts +1 -1
- package/src/{lib → variants}/dynamicVariants.ts +1 -1
- package/src/variants/index.ts +6 -0
- package/src/{lib → variants}/spacing.ts +37 -24
- package/src/variants/tests/borderCreator.spec.ts +180 -0
- package/src/variants/tests/dynamicVariants.spec.ts +194 -0
- package/src/variants/tests/spacing.spec.ts +177 -0
- package/src/lib/Cacher.ts +0 -112
- package/src/lib/StaleControl.ts +0 -73
- package/src/lib/StyleCache.ts +0 -81
- package/src/lib/StylePersistor.ts +0 -28
- package/src/lib/hooks.ts +0 -60
- package/src/lib/minifier.ts +0 -20
- /package/src/{lib → tools}/multiplierProperty.ts +0 -0
- /package/src/{lib → variants}/defaultVariants.ts +0 -0
- /package/src/{lib → variants}/mediaQuery.ts +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import { Cache } from '../Cacher'
|
|
3
|
+
import { hashKey } from '../../tools'
|
|
4
|
+
|
|
5
|
+
interface MockStateStorage {
|
|
6
|
+
getItem: (key: string) => any
|
|
7
|
+
setItem: (key: string, value: any) => void
|
|
8
|
+
removeItem: (key: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('Cache', () => {
|
|
12
|
+
let cache: Cache<string>
|
|
13
|
+
let mockStorageInstance: MockStateStorage
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
mockStorageInstance = {
|
|
17
|
+
getItem: mock(() => null),
|
|
18
|
+
setItem: mock(() => {}),
|
|
19
|
+
removeItem: mock(() => {}),
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('Constructor', () => {
|
|
24
|
+
test('should create cache without persistence when storage is null', () => {
|
|
25
|
+
cache = new Cache<string>('styles', null, false)
|
|
26
|
+
|
|
27
|
+
expect(cache.registryName).toBe('styles')
|
|
28
|
+
expect(cache.persistCache).toBe(false)
|
|
29
|
+
expect(cache.cache).toEqual({})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('should create cache with persistence enabled by default when storage is provided', () => {
|
|
33
|
+
cache = new Cache<string>('components', mockStorageInstance)
|
|
34
|
+
|
|
35
|
+
expect(cache.registryName).toBe('components')
|
|
36
|
+
expect(cache.persistCache).toBe(true)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('should load persisted cache when not staled', () => {
|
|
40
|
+
const futureDate = new Date()
|
|
41
|
+
futureDate.setDate(futureDate.getDate() + 5)
|
|
42
|
+
const persistedCache = { 'key1': 'value1' }
|
|
43
|
+
|
|
44
|
+
mockStorageInstance.getItem = mock((key: string) => {
|
|
45
|
+
if (key.includes('staleTime')) return futureDate.toISOString()
|
|
46
|
+
if (key.includes('cache')) return persistedCache
|
|
47
|
+
return null
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
51
|
+
|
|
52
|
+
expect(cache.cache).toEqual(persistedCache)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('should clear storage and not load cache when staled', () => {
|
|
56
|
+
const pastDate = new Date()
|
|
57
|
+
pastDate.setDate(pastDate.getDate() - 1)
|
|
58
|
+
const persistedCache = { 'key1': 'value1' }
|
|
59
|
+
|
|
60
|
+
mockStorageInstance.getItem = mock((key: string) => {
|
|
61
|
+
if (key.includes('staleTime')) return pastDate.toISOString()
|
|
62
|
+
if (key.includes('cache')) return persistedCache
|
|
63
|
+
return null
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
67
|
+
|
|
68
|
+
expect(mockStorageInstance.removeItem).toHaveBeenCalledTimes(2)
|
|
69
|
+
expect(cache.cache).toEqual({})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('Persistence Keys', () => {
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
cache = new Cache<string>('components', mockStorageInstance)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('should generate correct persistence key for cache', () => {
|
|
79
|
+
expect(cache.persistKeyCache).toBe('@styles.caches.components.cache')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('should generate correct persistence key for stale time', () => {
|
|
83
|
+
expect(cache.persistKeyStaleTime).toBe('@styles.caches.components.staleTime')
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe('keyFor', () => {
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
cache = new Cache<string>('styles', null, false)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('should generate cache key and return null for non-existent value', () => {
|
|
93
|
+
const result = cache.keyFor('base-key', ['data1', 'data2'])
|
|
94
|
+
const expectedKey = hashKey(['base-key', ['data1', 'data2']])
|
|
95
|
+
|
|
96
|
+
expect(result.key).toBe(expectedKey)
|
|
97
|
+
expect(result.value).toBeNull()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('should return existing cached value', () => {
|
|
101
|
+
const cacheKey = hashKey(['base-key', 'data'])
|
|
102
|
+
const testValue = 'cached-value'
|
|
103
|
+
|
|
104
|
+
// Set up the cache with a known value
|
|
105
|
+
cache.cache[cacheKey] = testValue
|
|
106
|
+
|
|
107
|
+
const result = cache.keyFor('base-key', 'data')
|
|
108
|
+
|
|
109
|
+
expect(result.key).toBe(cacheKey)
|
|
110
|
+
expect(result.value).toBe(testValue)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('cacheFor', () => {
|
|
115
|
+
test('should store value in memory cache without persistence', () => {
|
|
116
|
+
cache = new Cache<string>('styles', null, false)
|
|
117
|
+
|
|
118
|
+
const result = cache.cacheFor('test-key', 'test-value')
|
|
119
|
+
|
|
120
|
+
expect(cache.cache['test-key']).toBe('test-value')
|
|
121
|
+
expect(result).toBe('test-value')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('should store value and persist to storage when persistence enabled', () => {
|
|
125
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
126
|
+
|
|
127
|
+
const result = cache.cacheFor('test-key', 'test-value')
|
|
128
|
+
|
|
129
|
+
expect(cache.cache['test-key']).toBe('test-value')
|
|
130
|
+
expect(mockStorageInstance.setItem).toHaveBeenCalledWith(
|
|
131
|
+
'@styles.caches.styles.cache',
|
|
132
|
+
{ 'test-key': 'test-value' }
|
|
133
|
+
)
|
|
134
|
+
expect(result).toBe('test-value')
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
describe('setCache', () => {
|
|
139
|
+
beforeEach(() => {
|
|
140
|
+
cache = new Cache<string>('styles', null, false)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('should replace entire cache with new data', () => {
|
|
144
|
+
cache.cache = { 'old-key': 'old-value' }
|
|
145
|
+
const newCache = { 'new-key': 'new-value', 'another-key': 'another-value' }
|
|
146
|
+
|
|
147
|
+
cache.setCache(newCache)
|
|
148
|
+
|
|
149
|
+
expect(cache.cache).toEqual(newCache)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('should set empty object when null is passed', () => {
|
|
153
|
+
cache.cache = { 'some-key': 'some-value' }
|
|
154
|
+
|
|
155
|
+
cache.setCache(null as any)
|
|
156
|
+
|
|
157
|
+
expect(cache.cache).toEqual({})
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe('clear', () => {
|
|
162
|
+
test('should clear memory cache and storage', () => {
|
|
163
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
164
|
+
cache.cache = { 'key1': 'value1', 'key2': 'value2' }
|
|
165
|
+
|
|
166
|
+
cache.clear()
|
|
167
|
+
|
|
168
|
+
expect(cache.cache).toEqual({})
|
|
169
|
+
expect(mockStorageInstance.removeItem).toHaveBeenCalledWith('@styles.caches.styles.staleTime')
|
|
170
|
+
expect(mockStorageInstance.removeItem).toHaveBeenCalledWith('@styles.caches.styles.cache')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('should only clear memory cache when persistence disabled', () => {
|
|
174
|
+
cache = new Cache<string>('styles', null, false)
|
|
175
|
+
cache.cache = { 'key1': 'value1' }
|
|
176
|
+
|
|
177
|
+
cache.clear()
|
|
178
|
+
|
|
179
|
+
expect(cache.cache).toEqual({})
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
describe('loadStorage', () => {
|
|
184
|
+
test('should return default values when persistence disabled', () => {
|
|
185
|
+
cache = new Cache<string>('styles', null, false)
|
|
186
|
+
|
|
187
|
+
const result = cache.loadStorage()
|
|
188
|
+
|
|
189
|
+
expect(result.persistedCache).toEqual({})
|
|
190
|
+
expect(result.persistedStaleTime).toBeInstanceOf(Date)
|
|
191
|
+
expect(result.persistedStaleTime.getTime()).toBeGreaterThan(new Date().getTime())
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('should load values from storage when available', () => {
|
|
195
|
+
const testDate = new Date('2024-12-01T00:00:00Z')
|
|
196
|
+
const testCache = { 'stored-key': 'stored-value' }
|
|
197
|
+
|
|
198
|
+
mockStorageInstance.getItem = mock((key: string) => {
|
|
199
|
+
if (key.includes('staleTime')) return testDate.toISOString()
|
|
200
|
+
if (key.includes('cache')) return testCache
|
|
201
|
+
return null
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
205
|
+
const result = cache.loadStorage()
|
|
206
|
+
|
|
207
|
+
expect(result.persistedStaleTime).toEqual(testDate)
|
|
208
|
+
expect(result.persistedCache).toEqual(testCache)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('should generate new stale time when not found in storage', () => {
|
|
212
|
+
mockStorageInstance.getItem = mock((key: string) => {
|
|
213
|
+
if (key.includes('staleTime')) return null
|
|
214
|
+
if (key.includes('cache')) return { 'key': 'value' }
|
|
215
|
+
return null
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
219
|
+
const result = cache.loadStorage()
|
|
220
|
+
|
|
221
|
+
expect(result.persistedStaleTime).toBeInstanceOf(Date)
|
|
222
|
+
expect(result.persistedStaleTime.getTime()).toBeGreaterThan(new Date().getTime())
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
describe('clearStorage', () => {
|
|
227
|
+
test('should remove items from storage when persistence enabled', () => {
|
|
228
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
229
|
+
|
|
230
|
+
cache.clearStorage()
|
|
231
|
+
|
|
232
|
+
expect(mockStorageInstance.removeItem).toHaveBeenCalledWith('@styles.caches.styles.staleTime')
|
|
233
|
+
expect(mockStorageInstance.removeItem).toHaveBeenCalledWith('@styles.caches.styles.cache')
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
test('should do nothing when persistence disabled', () => {
|
|
237
|
+
cache = new Cache<string>('styles', null, false)
|
|
238
|
+
|
|
239
|
+
cache.clearStorage()
|
|
240
|
+
|
|
241
|
+
// Should not throw or call any storage methods
|
|
242
|
+
expect(true).toBe(true)
|
|
243
|
+
})
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
describe('storeCache', () => {
|
|
247
|
+
beforeEach(() => {
|
|
248
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
test('should store current cache when no parameter provided', () => {
|
|
252
|
+
cache.cache = { 'current-key': 'current-value' }
|
|
253
|
+
|
|
254
|
+
cache.storeCache()
|
|
255
|
+
|
|
256
|
+
expect(mockStorageInstance.setItem).toHaveBeenCalledWith(
|
|
257
|
+
'@styles.caches.styles.cache',
|
|
258
|
+
{ 'current-key': 'current-value' }
|
|
259
|
+
)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
test('should store provided cache parameter', () => {
|
|
263
|
+
const customCache = { 'custom-key': 'custom-value' }
|
|
264
|
+
|
|
265
|
+
cache.storeCache(customCache)
|
|
266
|
+
|
|
267
|
+
expect(mockStorageInstance.setItem).toHaveBeenCalledWith(
|
|
268
|
+
'@styles.caches.styles.cache',
|
|
269
|
+
customCache
|
|
270
|
+
)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test('should do nothing when persistence disabled', () => {
|
|
274
|
+
cache = new Cache<string>('styles', null, false)
|
|
275
|
+
cache.cache = { 'key': 'value' }
|
|
276
|
+
|
|
277
|
+
cache.storeCache()
|
|
278
|
+
|
|
279
|
+
// Should not throw or call any storage methods
|
|
280
|
+
expect(true).toBe(true)
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
describe('storeStaleTime', () => {
|
|
285
|
+
test('should store stale time as ISO string when persistence enabled', () => {
|
|
286
|
+
cache = new Cache<string>('styles', mockStorageInstance)
|
|
287
|
+
const testDate = new Date('2024-12-01T00:00:00Z')
|
|
288
|
+
|
|
289
|
+
cache.storeStaleTime(testDate)
|
|
290
|
+
|
|
291
|
+
expect(mockStorageInstance.setItem).toHaveBeenCalledWith(
|
|
292
|
+
'@styles.caches.styles.staleTime',
|
|
293
|
+
'2024-12-01T00:00:00.000Z'
|
|
294
|
+
)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
test('should do nothing when persistence disabled', () => {
|
|
298
|
+
cache = new Cache<string>('styles', null, false)
|
|
299
|
+
const testDate = new Date()
|
|
300
|
+
|
|
301
|
+
cache.storeStaleTime(testDate)
|
|
302
|
+
|
|
303
|
+
// Should not throw or call any storage methods
|
|
304
|
+
expect(true).toBe(true)
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
describe('generateStaleTime function', () => {
|
|
309
|
+
test('should generate stale time 7 days in the future', () => {
|
|
310
|
+
const startTime = new Date()
|
|
311
|
+
cache = new Cache<string>('styles', null, false)
|
|
312
|
+
|
|
313
|
+
const result = cache.loadStorage()
|
|
314
|
+
const endTime = new Date()
|
|
315
|
+
|
|
316
|
+
// Verify the stale time is approximately 7 days from now
|
|
317
|
+
const expectedMinTime = new Date(startTime)
|
|
318
|
+
expectedMinTime.setDate(expectedMinTime.getDate() + 7)
|
|
319
|
+
|
|
320
|
+
const expectedMaxTime = new Date(endTime)
|
|
321
|
+
expectedMaxTime.setDate(expectedMaxTime.getDate() + 7)
|
|
322
|
+
|
|
323
|
+
expect(result.persistedStaleTime.getTime()).toBeGreaterThanOrEqual(expectedMinTime.getTime())
|
|
324
|
+
expect(result.persistedStaleTime.getTime()).toBeLessThanOrEqual(expectedMaxTime.getTime())
|
|
325
|
+
})
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
describe('Integration tests', () => {
|
|
329
|
+
test('should handle full cache lifecycle with persistence', () => {
|
|
330
|
+
// Initial setup with fresh cache
|
|
331
|
+
mockStorageInstance.getItem = mock(() => null)
|
|
332
|
+
cache = new Cache<string>('compositions', mockStorageInstance)
|
|
333
|
+
|
|
334
|
+
// Add some data
|
|
335
|
+
const { key } = cache.keyFor('test-base', 'test-data')
|
|
336
|
+
cache.cacheFor(key, 'test-value')
|
|
337
|
+
|
|
338
|
+
// Verify the value is cached
|
|
339
|
+
const { value } = cache.keyFor('test-base', 'test-data')
|
|
340
|
+
expect(value).toBe('test-value')
|
|
341
|
+
|
|
342
|
+
// Verify storage was called
|
|
343
|
+
expect(mockStorageInstance.setItem).toHaveBeenCalled()
|
|
344
|
+
|
|
345
|
+
// Clear cache
|
|
346
|
+
cache.clear()
|
|
347
|
+
expect(cache.cache).toEqual({})
|
|
348
|
+
expect(mockStorageInstance.removeItem).toHaveBeenCalledTimes(2)
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
test('should handle cache restoration from storage', () => {
|
|
352
|
+
const futureDate = new Date()
|
|
353
|
+
futureDate.setDate(futureDate.getDate() + 5)
|
|
354
|
+
const storedCache = { 'stored-key': 'stored-value' }
|
|
355
|
+
|
|
356
|
+
mockStorageInstance.getItem = mock((key: string) => {
|
|
357
|
+
if (key.includes('staleTime')) return futureDate.toISOString()
|
|
358
|
+
if (key.includes('cache')) return storedCache
|
|
359
|
+
return null
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
cache = new Cache<string>('responsive', mockStorageInstance)
|
|
363
|
+
|
|
364
|
+
expect(cache.cache).toEqual(storedCache)
|
|
365
|
+
|
|
366
|
+
// Should be able to retrieve the stored value
|
|
367
|
+
const cachedValue = cache.cache['stored-key']
|
|
368
|
+
expect(cachedValue).toBe('stored-value')
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
})
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test'
|
|
2
|
+
import { StaleControl } from '../StaleControl'
|
|
3
|
+
|
|
4
|
+
describe('StaleControl', () => {
|
|
5
|
+
let staleControl: StaleControl
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
staleControl = new StaleControl()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
staleControl.destroy()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe('constructor', () => {
|
|
16
|
+
test('should create instance with default values', () => {
|
|
17
|
+
expect(staleControl).toBeInstanceOf(StaleControl)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('should create instance with custom values', () => {
|
|
21
|
+
const customStaleControl = new StaleControl(120, '::', 60000)
|
|
22
|
+
expect(customStaleControl).toBeInstanceOf(StaleControl)
|
|
23
|
+
customStaleControl.destroy()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('insertStaleTime', () => {
|
|
28
|
+
test('should insert stale time into value', () => {
|
|
29
|
+
const value = 'test-value'
|
|
30
|
+
const result = staleControl.insertStaleTime(value)
|
|
31
|
+
|
|
32
|
+
expect(result).toInclude(value)
|
|
33
|
+
expect(result).toInclude('//:')
|
|
34
|
+
expect(result.split('//:')).toHaveLength(2)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('should handle empty string', () => {
|
|
38
|
+
const result = staleControl.insertStaleTime('')
|
|
39
|
+
expect(result).toInclude('//:')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('extractStaleTime', () => {
|
|
44
|
+
test('should extract stale time and original value', () => {
|
|
45
|
+
const originalValue = 'test-value'
|
|
46
|
+
const valueWithStaleTime = staleControl.insertStaleTime(originalValue)
|
|
47
|
+
|
|
48
|
+
const result = staleControl.extractStaleTime(valueWithStaleTime)
|
|
49
|
+
|
|
50
|
+
expect(result.value).toBe(originalValue)
|
|
51
|
+
expect(result.staleTime).toBeInstanceOf(Date)
|
|
52
|
+
expect(result.staleTime.getTime()).toBeGreaterThan(Date.now())
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('should handle value without stale time', () => {
|
|
56
|
+
const value = 'plain-value'
|
|
57
|
+
const result = staleControl.extractStaleTime(value)
|
|
58
|
+
|
|
59
|
+
expect(result.value).toBe(value)
|
|
60
|
+
expect(result.staleTime.getTime()).toBe(0)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('should handle malformed stale time', () => {
|
|
64
|
+
const value = 'test//:invalid-date'
|
|
65
|
+
const result = staleControl.extractStaleTime(value)
|
|
66
|
+
|
|
67
|
+
expect(result.value).toBe('test')
|
|
68
|
+
expect(result.staleTime.toString()).toBe('Invalid Date')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('isStaled', () => {
|
|
73
|
+
test('should return false for fresh value', () => {
|
|
74
|
+
const value = staleControl.insertStaleTime('fresh-value')
|
|
75
|
+
const isStaled = staleControl.isStaled(value)
|
|
76
|
+
|
|
77
|
+
expect(isStaled).toBeFalse()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('should return true for expired value', () => {
|
|
81
|
+
const expiredControl = new StaleControl(-60)
|
|
82
|
+
const value = expiredControl.insertStaleTime('expired-value')
|
|
83
|
+
const isStaled = expiredControl.isStaled(value)
|
|
84
|
+
|
|
85
|
+
expect(isStaled).toBeTrue()
|
|
86
|
+
expiredControl.destroy()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('should handle value without stale time', () => {
|
|
90
|
+
const isStaled = staleControl.isStaled('plain-value')
|
|
91
|
+
expect(isStaled).toBeTrue()
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe('refreshStaleTime', () => {
|
|
96
|
+
test('should refresh stale time', () => {
|
|
97
|
+
const originalValue = 'test-value'
|
|
98
|
+
const originalStale = staleControl.insertStaleTime(originalValue)
|
|
99
|
+
|
|
100
|
+
Bun.sleepSync(10)
|
|
101
|
+
|
|
102
|
+
const refreshed = staleControl.refreshStaleTime(originalStale)
|
|
103
|
+
|
|
104
|
+
const originalExtracted = staleControl.extractStaleTime(originalStale)
|
|
105
|
+
const refreshedExtracted = staleControl.extractStaleTime(refreshed)
|
|
106
|
+
|
|
107
|
+
expect(refreshedExtracted.value).toBe(originalValue)
|
|
108
|
+
expect(refreshedExtracted.staleTime.getTime()).toBeGreaterThan(
|
|
109
|
+
originalExtracted.staleTime.getTime()
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('cacheWiper', () => {
|
|
115
|
+
test('should throw error when called', () => {
|
|
116
|
+
expect(() => staleControl.cacheWiper()).toThrow(
|
|
117
|
+
'Cache Wiper not implemented - Requires storage integration for future use'
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('interval management', () => {
|
|
123
|
+
test('should register and unregister cache wiper', () => {
|
|
124
|
+
const mockCacheWiper = mock(() => {})
|
|
125
|
+
staleControl.cacheWiper = mockCacheWiper
|
|
126
|
+
|
|
127
|
+
staleControl.registerCacheWiper()
|
|
128
|
+
staleControl.unregisterCacheWiper()
|
|
129
|
+
|
|
130
|
+
expect(mockCacheWiper).not.toHaveBeenCalled()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test('should handle multiple register calls', () => {
|
|
134
|
+
const mockCacheWiper = mock(() => {})
|
|
135
|
+
staleControl.cacheWiper = mockCacheWiper
|
|
136
|
+
|
|
137
|
+
staleControl.registerCacheWiper()
|
|
138
|
+
staleControl.registerCacheWiper()
|
|
139
|
+
staleControl.unregisterCacheWiper()
|
|
140
|
+
|
|
141
|
+
expect(mockCacheWiper).not.toHaveBeenCalled()
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test('should unregister non-existent wiper without error', () => {
|
|
145
|
+
expect(() => staleControl.unregisterCacheWiper()).not.toThrow()
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
describe('destroy', () => {
|
|
150
|
+
test('should clean up resources', () => {
|
|
151
|
+
const mockCacheWiper = mock(() => {})
|
|
152
|
+
staleControl.cacheWiper = mockCacheWiper
|
|
153
|
+
|
|
154
|
+
staleControl.registerCacheWiper()
|
|
155
|
+
staleControl.destroy()
|
|
156
|
+
|
|
157
|
+
expect(mockCacheWiper).not.toHaveBeenCalled()
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe('custom staleTimeIdentifier', () => {
|
|
162
|
+
test('should work with custom identifier', () => {
|
|
163
|
+
const customControl = new StaleControl(60, '::')
|
|
164
|
+
const value = customControl.insertStaleTime('test')
|
|
165
|
+
|
|
166
|
+
expect(value).toInclude('::')
|
|
167
|
+
expect(value.split('::')).toHaveLength(2)
|
|
168
|
+
|
|
169
|
+
const extracted = customControl.extractStaleTime(value)
|
|
170
|
+
expect(extracted.value).toBe('test')
|
|
171
|
+
|
|
172
|
+
customControl.destroy()
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
})
|