diff --git a/src/persistState.js b/src/persistState.js index 976a3b05..c12e49ce 100644 --- a/src/persistState.js +++ b/src/persistState.js @@ -65,7 +65,7 @@ export default function persistState(sessionId, stateDeserializer = null, action try { localStorage.setItem(key, JSON.stringify(store.getState())); } catch (e) { - console.warn('Could not write debug session from localStorage:', e); + console.warn('Could not write debug session to localStorage:', e); } return action; diff --git a/test/persistState.spec.js b/test/persistState.spec.js new file mode 100644 index 00000000..4cafe1c5 --- /dev/null +++ b/test/persistState.spec.js @@ -0,0 +1,123 @@ +import expect from 'expect'; +import devTools from '../src/devTools'; +import persistState from '../src/persistState'; +import { compose, createStore } from 'redux'; + +describe('persistState', () => { + let savedLocalStorage = global.localStorage; + + beforeEach(() => { + global.localStorage = { + store: {}, + getItem(key) { + return this.store[key] || null; + }, + setItem(key, value) { + this.store[key] = value; + }, + removeItem(key) { + delete this.store[key]; + }, + clear() { + this.store = {}; + } + }; + }); + + after(() => { + global.localStorage = savedLocalStorage; + }); + + const reducer = (state = 0, action) => { + switch (action.type) { + case 'INCREMENT': + return state + 1; + case 'DECREMENT': + return state - 1; + default: + return state; + } + }; + + it('should persist state', () => { + const finalCreateStore = compose(devTools(), persistState('id'))(createStore); + const store = finalCreateStore(reducer); + expect(store.getState()).toBe(0); + + store.dispatch({ type: 'INCREMENT' }); + store.dispatch({ type: 'INCREMENT' }); + expect(store.getState()).toBe(2); + + const store2 = finalCreateStore(reducer); + expect(store2.getState()).toBe(2); + }); + + it('should not persist state if no session id', () => { + const finalCreateStore = compose(devTools(), persistState())(createStore); + const store = finalCreateStore(reducer); + expect(store.getState()).toBe(0); + + store.dispatch({ type: 'INCREMENT' }); + store.dispatch({ type: 'INCREMENT' }); + expect(store.getState()).toBe(2); + + const store2 = finalCreateStore(reducer); + expect(store2.getState()).toBe(0); + }); + + it('should run with a custom state deserializer', () => { + const oneLess = state => state === undefined ? -1 : state - 1; + const finalCreateStore = compose(devTools(), persistState('id', oneLess))(createStore); + const store = finalCreateStore(reducer); + expect(store.getState()).toBe(0); + + store.dispatch({ type: 'INCREMENT' }); + store.dispatch({ type: 'INCREMENT' }); + expect(store.getState()).toBe(2); + + const store2 = finalCreateStore(reducer); + expect(store2.getState()).toBe(1); + }); + + it('should run with a custom action deserializer', () => { + const incToDec = action => action.type === 'INCREMENT' ? { type: 'DECREMENT' } : action; + const finalCreateStore = compose(devTools(), persistState('id', null, incToDec))(createStore); + const store = finalCreateStore(reducer); + expect(store.getState()).toBe(0); + + store.dispatch({ type: 'INCREMENT' }); + store.dispatch({ type: 'INCREMENT' }); + expect(store.getState()).toBe(2); + + const store2 = finalCreateStore(reducer); + expect(store2.getState()).toBe(-2); + }); + + it('should warn if read from localStorage fails', () => { + const spy = expect.spyOn(console, 'warn'); + const finalCreateStore = compose(devTools(), persistState('id'))(createStore); + delete global.localStorage.getItem; + finalCreateStore(reducer); + + expect(spy.calls).toContain( + /Could not read debug session from localStorage/, + (call, errMsg) => call.arguments[0].match(errMsg) + ); + + spy.restore(); + }); + it('should warn if write to localStorage fails', () => { + const spy = expect.spyOn(console, 'warn'); + const finalCreateStore = compose(devTools(), persistState('id'))(createStore); + delete global.localStorage.setItem; + const store = finalCreateStore(reducer); + + store.dispatch({ type: 'INCREMENT' }); + expect(spy.calls).toContain( + /Could not write debug session to localStorage/, + (call, errMsg) => call.arguments[0].match(errMsg) + ); + + spy.restore(); + }); +});