Skip to content

Commit 32f6c6a

Browse files
Update tests
1 parent d95246a commit 32f6c6a

File tree

7 files changed

+589
-61
lines changed

7 files changed

+589
-61
lines changed

src/updatedAtHelper.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,27 @@ export default class UpdatedAtHelper {
8282
cache[name] = { ...cache[name], localUpdatedAt: new Date().getTime() };
8383
}
8484
}
85+
86+
/**
87+
* A testing-specific extension of UpdatedAtHelper that provides methods
88+
* to control its internal state for predictable test outcomes.
89+
* @internal
90+
*/
91+
export class TestUpdatedAtHelper extends UpdatedAtHelper {
92+
/**
93+
* Resets the internal cache to its default empty state.
94+
*/
95+
static _clearCache() {
96+
cache = {};
97+
}
98+
99+
/**
100+
* Manually sets the stored timestamp for a slice.
101+
* This is useful for simulating scenarios where storage was updated externally.
102+
* @param name - The name of the slice.
103+
* @param timestamp - The timestamp to set.
104+
*/
105+
static _setStoredUpdatedAtForTest(name: string, timestamp: number) {
106+
cache[name] = { ...(cache[name] || { localUpdatedAt: 0 }), storedUpdatedAt: timestamp };
107+
}
108+
}

test/mocks.ts

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,66 @@
1+
/**
2+
* @file This file contains shared mocks and utilities used across the test suite
3+
* for the rtk-persist library.
4+
*/
5+
16
import { PayloadAction } from "@reduxjs/toolkit";
2-
import { StorageHandler } from "../src/types";
37
import { createPersistedSlice } from "../src/slice";
8+
import { StorageHandler } from "../src/types";
49

5-
let data: Record<string, string> = {};
6-
export const mockStorageHandler: StorageHandler = {
7-
getItem: (key: string) => {
8-
if (key in data) return data[key];
9-
throw new Error();
10-
},
11-
setItem: (key: string, value: string) => {
12-
data[key] = value;
13-
},
14-
removeItem: (key: string) => {
15-
delete data[key];
16-
},
17-
};
10+
/**
11+
* A simple in-memory storage mock that implements the `StorageHandler` interface.
12+
* Each instance has its own isolated data store, making it ideal for ensuring
13+
* tests do not interfere with one another.
14+
* @public
15+
*/
16+
export class StorageMock implements StorageHandler {
17+
private data: Record<string, string> = {};
18+
19+
/**
20+
* Retrieves an item from the in-memory store.
21+
* @param key - The key of the item to retrieve.
22+
* @returns The stored value, or `null` if the key does not exist.
23+
*/
24+
getItem(key: string): Promise<string | null> | (string | null) {
25+
return key in this.data ? this.data[key] : null;
26+
}
1827

28+
/**
29+
* Saves an item to the in-memory store.
30+
* @param key - The key to associate with the value.
31+
* @param value - The string value to store.
32+
*/
33+
setItem(key: string, value: string): Promise<void> | void {
34+
this.data[key] = value;
35+
}
36+
37+
/**
38+
* Removes an item from the in-memory store.
39+
* @param key - The key of the item to remove.
40+
*/
41+
removeItem(key: string): Promise<void> | void {
42+
delete this.data[key];
43+
}
44+
45+
/**
46+
* Clears all data from the in-memory store.
47+
*/
48+
clear(): void {
49+
this.data = {};
50+
}
51+
}
52+
53+
/**
54+
* The default initial state for the mock counter slice.
55+
* @public
56+
*/
1957
export const sliceInitialState = { counter: 0 };
58+
59+
/**
60+
* A pre-configured persisted slice for use in tests.
61+
* It includes common reducers and selectors for a counter.
62+
* @public
63+
*/
2064
export const mockPersistedSlice = createPersistedSlice({
2165
name: 'test-counter',
2266
initialState: sliceInitialState,

test/reducer.test.ts

Whitespace-only changes.

test/settings.test.ts

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,98 @@
11
import { configurePersistedStore } from "../src";
22
import { TestSettings as Settings } from '../src/settings';
3-
import { mockPersistedSlice, mockStorageHandler } from "./mocks";
3+
import { StorageHandler } from "../src/types";
4+
import { mockPersistedSlice, StorageMock } from "./mocks";
45

5-
describe('Global settings', () => {
6+
describe('Settings', () => {
7+
let storage: StorageHandler;
8+
9+
// Before each test, create a new storage mock and clear all global settings
10+
// to ensure tests are isolated.
611
beforeEach(() => {
12+
storage = new StorageMock();
713
Settings._clearSettings();
814
});
9-
it('should allow to get and set static variables', () => {
10-
Settings.storageHandler = mockStorageHandler;
11-
expect(Settings.storageHandler).toEqual(mockStorageHandler);
15+
16+
describe('storageHandler', () => {
17+
it('should allow setting and getting the storage handler', () => {
18+
Settings.storageHandler = storage;
19+
expect(Settings.storageHandler).toBe(storage);
20+
});
21+
22+
it('should be initialized by configurePersistedStore', async () => {
23+
await configurePersistedStore({
24+
reducer: ({ [mockPersistedSlice.name]: mockPersistedSlice.reducer }),
25+
}, 'mockApp', storage);
26+
expect(Settings.storageHandler).toBe(storage);
27+
});
28+
29+
it('should throw a TypeError if accessed before being set', () => {
30+
expect(() => Settings.storageHandler).toThrow(TypeError);
31+
});
1232
});
1333

14-
it('should be initiated when configuring a store', async () => {
15-
await configurePersistedStore({
16-
reducer: ({ [mockPersistedSlice.name]: mockPersistedSlice.reducer }),
17-
}, 'mock', mockStorageHandler);
18-
expect(Settings.storageHandler).toEqual(mockStorageHandler);
34+
describe('applicationId', () => {
35+
it('should allow setting and getting the application ID', () => {
36+
Settings.applicationId = 'my-app';
37+
expect(Settings.applicationId).toBe('my-app');
38+
});
39+
40+
it('should be initialized by configurePersistedStore', async () => {
41+
await configurePersistedStore({
42+
reducer: ({ [mockPersistedSlice.name]: mockPersistedSlice.reducer }),
43+
}, 'mockApp', storage);
44+
expect(Settings.applicationId).toBe('mockApp');
45+
});
46+
47+
it('should throw a TypeError if accessed before being set', () => {
48+
expect(() => Settings.applicationId).toThrow(TypeError);
49+
});
1950
});
2051

21-
it('should throw an exception when requesting variables not set', () => {
22-
expect(() => Settings.storageHandler).toThrow(TypeError);
52+
describe('slice subscription', () => {
53+
it('should allow subscribing slices and retrieving the list', () => {
54+
Settings.subscribeSlice('user');
55+
Settings.subscribeSlice('posts');
56+
expect(Settings.subscribedSliceIds).toEqual(['user', 'posts']);
57+
});
58+
59+
it('should not allow duplicate slice subscriptions', () => {
60+
Settings.subscribeSlice('user');
61+
Settings.subscribeSlice('user');
62+
Settings.subscribeSlice('posts');
63+
expect(Settings.subscribedSliceIds).toEqual(['user', 'posts']);
64+
});
65+
66+
it('should be cleared by _clearSettings', () => {
67+
Settings.subscribeSlice('user');
68+
expect(Settings.subscribedSliceIds).toEqual(['user']);
69+
Settings._clearSettings();
70+
expect(Settings.subscribedSliceIds).toEqual([]);
71+
});
72+
});
73+
74+
describe('persistence pause and resume', () => {
75+
it('should be enabled by default', () => {
76+
expect(Settings.isPersistenceEnabled).toBe(true);
77+
});
78+
79+
it('should allow pausing persistence', () => {
80+
Settings.pause();
81+
expect(Settings.isPersistenceEnabled).toBe(false);
82+
});
83+
84+
it('should allow resuming persistence after being paused', () => {
85+
Settings.pause();
86+
expect(Settings.isPersistenceEnabled).toBe(false);
87+
Settings.resume();
88+
expect(Settings.isPersistenceEnabled).toBe(true);
89+
});
90+
91+
it('should be reset to enabled when settings are cleared', () => {
92+
Settings.pause();
93+
expect(Settings.isPersistenceEnabled).toBe(false);
94+
Settings._clearSettings();
95+
expect(Settings.isPersistenceEnabled).toBe(true);
96+
});
2397
});
24-
});
98+
});

0 commit comments

Comments
 (0)