UNPKG

easy-peasy

Version:

Vegetarian friendly state for React

585 lines (502 loc) 12.2 kB
import React from 'react'; import { act } from 'react-dom/test-utils'; import { render } from '@testing-library/react'; import { action, computed, createStore, createTransform, persist, StoreProvider, useStoreRehydrated, createContextStore, } from '../index'; jest.mock('debounce', () => fn => fn); const createMemoryStorage = (initial = {}, config = { async: false }) => { const store = initial; const { async } = config; return { setItem: (key, data) => { store[key] = data; if (async) { return Promise.resolve({}); } }, getItem: key => { const data = store[key]; return async ? Promise.resolve(data) : data; }, removeItem: key => { delete store[key]; return Promise.resolve(); }, store, }; }; const makeStore = (config = {}, model, storeConfig) => createStore( persist( model || { counter: 0, msg: 'hello world', change: action((_, payload) => { return payload; }), }, config, ), storeConfig, ); beforeEach(() => { localStorage.clear(); sessionStorage.clear(); }); afterEach(() => { process.env.NODE_ENV = 'test'; }); test('default storage', () => { // ARRANGE const store = makeStore(); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 1, msg: 'hello universe', }); }); test('local storage', () => { // ARRANGE const persistConfig = { storage: 'localStorage', }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 1, msg: 'hello universe', }); }); test('session storage', () => { // ARRANGE const persistConfig = { storage: 'sessionStorage', }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 1, msg: 'hello universe', }); }); test('invalid storage', () => { // ARRANGE process.env.NODE_ENV = 'development'; const persistConfig = { storage: 'invalidStorage', }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 0, msg: 'hello world', }); }); test('custom sync storage', () => { // ARRANGE const memoryStorage = createMemoryStorage(); const persistConfig = { storage: memoryStorage }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 1, msg: 'hello universe', }); }); test('whitelist', () => { // ARRANGE const memoryStorage = createMemoryStorage(); const persistConfig = { storage: memoryStorage, whitelist: ['msg'] }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 0, msg: 'hello universe', }); }); test('blacklist', () => { // ARRANGE const memoryStorage = createMemoryStorage(); const persistConfig = { storage: memoryStorage, blacklist: ['counter'] }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 0, msg: 'hello universe', }); }); test('nested', () => { // ARRANGE const makeStore = (config = {}) => createStore( { foo: 'bar', nested: persist( { counter: 0, msg: 'hello world', change: action((_, payload) => payload), }, config, ), }, { disableImmer: true, }, ); const memoryStorage = createMemoryStorage(); const store = makeStore({ storage: memoryStorage }); // ACT store.getActions().nested.change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore({ storage: memoryStorage }); // ASSERT expect(rehydratedStore.getState()).toEqual({ foo: 'bar', nested: { counter: 1, msg: 'hello universe', }, }); }); test('overwrite', () => { // ARRANGE const memoryStorage = createMemoryStorage(); const persistConfig = { storage: memoryStorage, whitelist: ['msg'], mergeStrategy: 'overwrite', }; const store = makeStore(persistConfig); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore(persistConfig); // ASSERT expect(rehydratedStore.getState()).toEqual({ msg: 'hello universe', }); }); test('mergeDeep', () => { // ARRANGE const memoryStorage = createMemoryStorage({ '[EasyPeasyStore]@counter': 1, '[EasyPeasyStore]@nested': { msg: 'hello universe', }, }); // ACT const rehydratedStore = createStore( persist( { counter: 0, nested: { msg: 'hello world', foo: 'bar', }, }, { storage: memoryStorage, mergeStrategy: 'mergeDeep', }, ), ); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 1, nested: { msg: 'hello universe', foo: 'bar', }, }); }); test('asynchronous storage', async () => { // ARRANGE const memoryStorage = createMemoryStorage(undefined, { async: true }); const store = makeStore({ storage: memoryStorage }); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); const rehydratedStore = makeStore({ storage: memoryStorage }); await rehydratedStore.persist.resolveRehydration(); // ASSERT expect(rehydratedStore.getState()).toEqual({ counter: 1, msg: 'hello universe', }); }); test('clear', async () => { // ARRANGE const memoryStorage = createMemoryStorage(undefined, { async: true }); const store = makeStore({ storage: memoryStorage }); // ACT store.getActions().change({ counter: 1, msg: 'hello universe', }); // ASSERT expect(memoryStorage.store).toEqual({ '[EasyPeasyStore]@counter': 1, '[EasyPeasyStore]@msg': 'hello universe', }); // ACT await store.persist.clear(); // ASSERT expect(memoryStorage.store).toEqual({}); }); test('transformers', () => { // ARRANGE const upperCaseTransformer = createTransform( (data, key) => { expect(key).toBe('one'); return data.toUpperCase(); }, (data, key) => { expect(key).toBe('one'); return data.toLowerCase(); }, { whitelist: ['one'], }, ); const padTransformer = createTransform( (data, key) => { expect(key).toBe('one'); return `_${data}_`; }, (data, key) => { expect(key).toBe('one'); return data.substr(1, data.length - 2); }, { blacklist: ['two'], }, ); const memoryStorage = createMemoryStorage(); const makeStore = () => createStore( persist( { one: null, two: null, change: action((_, payload) => { return payload; }), }, { storage: memoryStorage, transformers: [upperCaseTransformer, padTransformer], }, ), ); const store = makeStore(); // ACT store.getActions().change({ one: 'item one', two: 'item two', }); // ASSERT expect(memoryStorage.store).toEqual({ '[EasyPeasyStore]@one': '_ITEM ONE_', '[EasyPeasyStore]@two': 'item two', }); // ACT const rehydratedStore = makeStore(); // ASSERT expect(rehydratedStore.getState()).toEqual({ one: 'item one', two: 'item two', }); }); test('multiple stores', () => { // ARRANGE const memoryStorage = createMemoryStorage(); const persistConfig = { storage: memoryStorage, }; const store1 = makeStore(persistConfig, undefined, { name: 'Store1' }); const store2 = makeStore(persistConfig, undefined, { name: 'Store2' }); // ACT store1.getActions().change({ counter: 1, msg: 'i am store 1', }); store2.getActions().change({ counter: 99, msg: 'i am store 2', }); const rehydratedStore1 = makeStore(persistConfig, undefined, { name: 'Store1', }); const rehydratedStore2 = makeStore(persistConfig, undefined, { name: 'Store2', }); // ASSERT expect(rehydratedStore1.getState()).toEqual({ counter: 1, msg: 'i am store 1', }); expect(rehydratedStore2.getState()).toEqual({ counter: 99, msg: 'i am store 2', }); }); test('useStoreRehydrated', async () => { // ARRANGE const memoryStorage = createMemoryStorage(undefined, { async: true }); const store = makeStore({ storage: memoryStorage, }); const App = () => { const rehydrated = useStoreRehydrated(); return ( <div> <h1>App Title</h1> {rehydrated ? <div>Loaded</div> : <p>Loading...</p>} </div> ); }; const wrapper = render( <StoreProvider store={store}> <App /> </StoreProvider>, ); // ASSERT expect(wrapper.queryByText('Loading...')).not.toBeNull(); expect(wrapper.queryByText('Loaded')).toBeNull(); // ACT await act(async () => { await store.persist.resolveRehydration(); }); // ASSERT expect(wrapper.queryByText('Loading...')).toBeNull(); expect(wrapper.queryByText('Loaded')).not.toBeNull(); }); test('useStoreRehydrated + createContextStore', async () => { // ARRANGE const memoryStorage = createMemoryStorage(undefined, { async: true }); const store = makeStore({ storage: memoryStorage, }); const MyContextStore = createContextStore({ foo: 'bar', }); const App = () => { const rehydrated = MyContextStore.useStoreRehydrated(); return ( <div> <h1>App Title</h1> {rehydrated ? <div>Loaded</div> : <p>Loading...</p>} </div> ); }; const wrapper = render( <MyContextStore.Provider> <App /> </MyContextStore.Provider>, ); // ASSERT expect(wrapper.queryByText('Loading...')).not.toBeNull(); expect(wrapper.queryByText('Loaded')).toBeNull(); // ACT await act(async () => { await store.persist.resolveRehydration(); }); // ASSERT expect(wrapper.queryByText('Loading...')).toBeNull(); expect(wrapper.queryByText('Loaded')).not.toBeNull(); }); test('computed properties', () => { // ARRANGE const memoryStorage = createMemoryStorage(); const makeStore = () => createStore( persist( { todos: ['write tests'], todoCount: computed(state => state.todos.length), addTodo: action((state, payload) => { state.todos.push(payload); }), nested: { todos: ['write tests'], todoCount: computed(state => state.todos.length), addTodo: action((state, payload) => { state.todos.push(payload); }), }, }, { storage: memoryStorage }, ), ); const store = makeStore(); // ACT store.getActions().addTodo('write more tests'); store.getActions().nested.addTodo('write more tests'); const rehydratedStore = makeStore(); // ASSERT expect(rehydratedStore.getState()).toEqual({ todos: ['write tests', 'write more tests'], todoCount: 2, nested: { todos: ['write tests', 'write more tests'], todoCount: 2, }, }); });