1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-01 00:08:27 +01:00
unleash.unleash/frontend/src/utils/storage.test.ts
andreas-unleash 1ad5b5062a
feat: Make storing to local storage robust (#6139)
Handles non serializable objects like Map and Set and makes the
serialisation/deserialisation process more robust generally.

Closes #
[UNL-322](https://linear.app/unleash/issue/UNL-322/add-types-to-local-storage-and-force-simple-types)

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
2024-02-13 09:36:15 +02:00

131 lines
3.8 KiB
TypeScript

import {
setLocalStorageItem,
getLocalStorageItem,
setSessionStorageItem,
getSessionStorageItem,
} from './storage';
import { vi } from 'vitest';
// Mocking the global localStorage
const localStorageMock = (() => {
let store: Record<string, string> = {};
return {
getItem(key: string) {
return store[key] || null;
},
setItem(key: string, value: string) {
store[key] = value.toString();
},
removeItem(key: string) {
delete store[key];
},
clear() {
store = {};
},
};
})();
const sessionStorageMock = (() => {
let store: Record<string, string> = {};
return {
getItem(key: string) {
return store[key] || null;
},
setItem(key: string, value: string) {
store[key] = value.toString();
},
removeItem(key: string) {
delete store[key];
},
clear() {
store = {};
},
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock,
});
Object.defineProperty(window, 'sessionStorage', {
value: sessionStorageMock,
});
describe('localStorage with TTL', () => {
beforeEach(() => {
localStorage.clear();
sessionStorage.clear();
vi.useFakeTimers();
});
test('object should not be retrievable after TTL expires', () => {
const testObject = { name: 'Test', number: 123 };
setLocalStorageItem('testObjectKey', testObject, 500000);
vi.advanceTimersByTime(600000);
const retrievedObject = getLocalStorageItem<{
name: string;
number: number;
}>('testObjectKey');
expect(retrievedObject).toBeUndefined();
});
test('item should be retrievable before TTL expires', () => {
setLocalStorageItem('testKey', 'testValue', 600000);
expect(getLocalStorageItem('testKey')).toBe('testValue');
});
test('item should not be retrievable after TTL expires', () => {
setLocalStorageItem('testKey', 'testValue', 500000);
vi.advanceTimersByTime(600000);
expect(getLocalStorageItem('testKey')).toBeUndefined();
});
test('object should be retrievable before TTL expires', () => {
const testObject = { name: 'Test', number: 123 };
setLocalStorageItem('testObjectKey', testObject, 600000);
const retrievedObject = getLocalStorageItem<{
name: string;
number: number;
}>('testObjectKey');
expect(retrievedObject).toEqual(testObject);
});
test('should correctly store and retrieve a Map object', () => {
const testMap = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
setLocalStorageItem('testMap', testMap);
expect(getLocalStorageItem<Map<any, any>>('testMap')).toEqual(testMap);
});
test('should correctly store and retrieve a Set object', () => {
const testSet = new Set(['value1', 'value2']);
setLocalStorageItem('testSet', testSet);
expect(getLocalStorageItem<Set<any>>('testSet')).toEqual(testSet);
});
test('should handle nested objects with arrays, Maps, and Sets', () => {
const complexObject = {
array: [1, 2, 3],
map: new Map([['nestedKey', 'nestedValue']]),
set: new Set([1, 2, 3]),
};
setLocalStorageItem('complexObject', complexObject);
expect(
getLocalStorageItem<typeof complexObject>('complexObject'),
).toEqual(complexObject);
});
test('sessionStorage item should expire as per TTL', () => {
setSessionStorageItem('sessionTTL', 'expiring', 50); // 50ms TTL
vi.advanceTimersByTime(60);
expect(getSessionStorageItem('sessionTTL')).toBeUndefined();
});
});