Skip to content

Commit

Permalink
Merge pull request #18 from rstgroup/persistStore
Browse files Browse the repository at this point in the history
Persist store
  • Loading branch information
mprzodala authored Jun 24, 2022
2 parents 17b626c + 04166bb commit 258c333
Show file tree
Hide file tree
Showing 15 changed files with 531 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ coverage/
/decorators
/types
/dist
/demo
package-lock.json
yarn.lock
2 changes: 1 addition & 1 deletion src/Eventrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Eventrix<InitialStateI = any> implements EventrixI {
this.useReceiver = this.useReceiver.bind(this);
this.removeReceiver = this.removeReceiver.bind(this);
}
getState<StateI>(path: string): StateI {
getState<StateI>(path?: string): StateI {
return this.stateManager.getState(path);
}
mapEmitArguments<EventDataI>(name: string | [string, EventDataI], value?: EventDataI): EmitArgumentsI<EventDataI> {
Expand Down
4 changes: 2 additions & 2 deletions src/RequestsHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ describe('RequestHandler', () => {
expect.assertions(4);
const requestPromise = requestHandler.handleRequest<string>(request, requestId);

expect(eventrix.eventsEmitter.listeners[abortRequestEventName].length).toEqual(1);
expect(eventrix.eventsEmitter.listeners[resolveRequestEventName].length).toEqual(1);
expect(eventrix.eventsEmitter.listeners[abortRequestEventName]).toHaveLength(1);
expect(eventrix.eventsEmitter.listeners[resolveRequestEventName]).toHaveLength(1);

return requestPromise.then(() => {
expect(eventrix.eventsEmitter.listeners[abortRequestEventName]).toEqual(undefined);
Expand Down
1 change: 1 addition & 0 deletions src/eventsNames/persistStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const GET_PERSIST_STORE_STATE = 'EventrixPersistStore:getState';
2 changes: 1 addition & 1 deletion src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EventrixI, EventsListenerI, UnregisterListenerMethod } from './interfaces';

export const isPromise = (value: any): boolean => typeof value === 'object' && typeof value.then === 'function';
export const isPromise = (value: any): value is Promise<any> => typeof value === 'object' && typeof value.then === 'function';

export const isNumber = (value: any): boolean => {
if (value === '' || value === null || value !== value || Array.isArray(value)) {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
export { default as Eventrix } from './Eventrix';
export { default as EventsReceiver, fetchToStateReceiver, fetchStateReceiver, fetchHandler } from './EventsReceiver';
export { default as EventrixDebugger } from './EventrixDebugger';
export { default as connectPersistStore } from './persistStore';
export { default as RequestsHandler } from './RequestsHandler';

/*** REACT ***/
export {
EventrixContext,
EventrixProvider,
PersistStoreGate,
withEventrixState,
withEventrix,
useEventrixState,
Expand Down
30 changes: 29 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ export interface FetchHandler {
export interface EventrixI {
listen<EventData = any>(name: string, listener: EventsListenerI<EventData>): void;
unlisten(name: string, listener: EventsListenerI): void;
emit<EventData>(name: string, data: EventData): Promise<any>;
emit<EventData>(name: string, data?: EventData): Promise<any>;
getState<StateI>(path?: string): StateI;
useReceiver(eventReceiver: EventsReceiverI): void;
removeReceiver(eventReceiver: EventsReceiverI): void;
persistStoreLoadPromise?: Promise<void>;
}

export interface EmitArgumentsI<EventDataI> {
Expand Down Expand Up @@ -248,6 +249,33 @@ export enum FetchStateStatus {
Success = 'success',
}

export type StorageDataItem = [string, any];

export interface SyncStorage {
setItem(key: string, value: string): void;
getItem(key: string): string;
[key: string]: any;
}

export interface AsyncStorage {
setItem(key: string, value: string): Promise<void>;
getItem(key: string): Promise<string>;
[key: string]: any;
}

export type StateKeys<StateI> = keyof StateI;

export type StateKeysList<StateI> = Array<StateKeys<StateI>>;

export interface PersistStoreConfig<StateI> {
blackList?: StateKeysList<StateI>;
whiteList?: StateKeysList<StateI>;
parseFromStorage?(state: any, stateName: string): any;
parseToStorage?(state: any, stateName: string): any;
storage: AsyncStorage | SyncStorage;
storageKey: string;
}

export interface RequestI {
request: Promise<any>;
rejectHandler: EventsListenerI;
Expand Down
203 changes: 203 additions & 0 deletions src/persistStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import connectPersistStore from './persistStore';
import Eventrix from './Eventrix';
import { PersistStoreConfig, SyncStorage, AsyncStorage } from './interfaces';

interface InitialStateI {
a: string;
b: string;
c: string;
d: string;
e: string;
f: {
g: string;
};
}

describe('persistStore', () => {
describe('sync', () => {
let storage: SyncStorage;
let initialState: InitialStateI;
let eventrix: Eventrix;

beforeEach(() => {
storage = {
setItem: jest.fn(),
getItem: jest.fn(() => ''),
};
initialState = {
a: 'a state',
b: 'b state',
c: 'c state',
d: 'd state',
e: 'e state',
f: {
g: 'g state',
},
};
eventrix = new Eventrix(initialState);
});

it('should add state to localstorage when setState was invoked with item from whiteList', () => {
const config: PersistStoreConfig<InitialStateI> = {
whiteList: ['a', 'c'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('c', 'c new state');
const serializedData = JSON.stringify({
data: [
['a', 'a state'],
['c', 'c new state'],
],
});
expect(storage.setItem).toHaveBeenCalledWith('myStorageKey', serializedData);
});

it('should not add state when setState was invoked with item not from whiteList', () => {
const config: PersistStoreConfig<InitialStateI> = {
whiteList: ['c'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('d', 'd new state');
const serializedData = JSON.stringify({
data: [['d', 'd new state']],
});
expect(storage.setItem).not.toHaveBeenCalledWith('myStorageKey', serializedData);
});

it('should save all states from initialState except states from blackList', () => {
const config: PersistStoreConfig<InitialStateI> = {
blackList: ['a', 'e'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('b', 'b new state');
const serializedData = JSON.stringify({
data: [
['b', 'b new state'],
['c', 'c state'],
['d', 'd state'],
['f', { g: 'g state' }],
],
});
expect(storage.setItem).toHaveBeenCalledWith('myStorageKey', serializedData);
});

it('should not add states to localstorage if setState was invoked with item from blackList', () => {
const config: PersistStoreConfig<InitialStateI> = {
blackList: ['a', 'e'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('a', 'a new state');
expect(storage.setItem).not.toHaveBeenCalled();
});

it('should not add state to localstorage if setState was invoked with nested state from blackList', () => {
const config: PersistStoreConfig<InitialStateI> = {
blackList: ['a', 'e', 'f'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('f.g', 'g new state');
expect(storage.setItem).not.toHaveBeenCalled();
});
});

describe('async', () => {
let storage: AsyncStorage;
let initialState: InitialStateI;
let eventrix: Eventrix;

beforeEach(() => {
storage = {
setItem: jest.fn(() => Promise.resolve()),
getItem: jest.fn(() => Promise.resolve('')),
};
initialState = {
a: 'a state',
b: 'b state',
c: 'c state',
d: 'd state',
e: 'e state',
f: { g: 'g state' },
};
eventrix = new Eventrix(initialState);
});

it('should add state to localstorage when setState was invoked with item from whiteList', () => {
const config: PersistStoreConfig<InitialStateI> = {
whiteList: ['c'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('c', 'c new state');
const serializedData = JSON.stringify({
data: [['c', 'c new state']],
});
expect(storage.setItem).toHaveBeenCalledWith('myStorageKey', serializedData);
});

it('should not add state when setState was invoked with item not from whiteList', () => {
const config: PersistStoreConfig<InitialStateI> = {
whiteList: ['c'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('d', 'd new state');
const serializedData = JSON.stringify({
data: [['d', 'd new state']],
});
expect(storage.setItem).not.toHaveBeenCalledWith('myStorageKey', serializedData);
});

it('should save all states from initialState except states from blackList', () => {
const config: PersistStoreConfig<InitialStateI> = {
blackList: ['a', 'e'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('b', 'b new state');
const serializedData = JSON.stringify({
data: [
['b', 'b new state'],
['c', 'c state'],
['d', 'd state'],
['f', { g: 'g state' }],
],
});
expect(storage.setItem).toHaveBeenCalledWith('myStorageKey', serializedData);
});

it('should not add states to localstorage if setState was invoked with item from blackList', () => {
const config: PersistStoreConfig<InitialStateI> = {
blackList: ['a', 'e'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('a', 'a new state');
expect(storage.setItem).not.toHaveBeenCalled();
});

it('should not add state to localstorage if setState was invoked with nested state from blackList', () => {
const config: PersistStoreConfig<InitialStateI> = {
blackList: ['a', 'e', 'f'],
storage,
storageKey: 'myStorageKey',
};
connectPersistStore(eventrix, config);
eventrix.stateManager.setState('f.g', 'g new state');
expect(storage.setItem).not.toHaveBeenCalled();
});
});
});
Loading

0 comments on commit 258c333

Please sign in to comment.