This commit is contained in:
Dan Abramov 2015-09-28 18:38:33 +03:00
parent 26c66fef63
commit 4428e8818d
10 changed files with 142 additions and 169 deletions

View File

@ -28,8 +28,8 @@
"node-libs-browser": "^0.5.2", "node-libs-browser": "^0.5.2",
"react-dock": "^0.1.0", "react-dock": "^0.1.0",
"react-hot-loader": "^1.3.0", "react-hot-loader": "^1.3.0",
"redux-devtools": "^3.0.0-alpha-6", "redux-devtools": "^3.0.0-alpha-7",
"redux-devtools-log-monitor": "^1.0.0-alpha-6", "redux-devtools-log-monitor": "^1.0.0-alpha-7",
"webpack": "^1.9.11", "webpack": "^1.9.11",
"webpack-dev-server": "^1.9.0" "webpack-dev-server": "^1.9.0"
} }

View File

@ -6,7 +6,7 @@ import DevTools from '../containers/DevTools';
const finalCreateStore = compose( const finalCreateStore = compose(
applyMiddleware(thunk), applyMiddleware(thunk),
DevTools.enhance, DevTools.instrument(),
persistState( persistState(
window.location.href.match( window.location.href.match(
/[?&]debug_session=([^&]+)\b/ /[?&]debug_session=([^&]+)\b/

View File

@ -1,13 +1,13 @@
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
export default function bindActionCreatorsRecursively(actionCreators, dispatch) { export default function bindActionCreatorsDeep(actionCreators, dispatch) {
return Object.keys(actionCreators).reduce((result, key) => { return Object.keys(actionCreators).reduce((result, key) => {
if (!actionCreators[key]) { if (!actionCreators[key]) {
return result; return result;
} }
switch (typeof actionCreators[key]) { switch (typeof actionCreators[key]) {
case 'object': case 'object':
result[key] = bindActionCreatorsRecursively(actionCreators[key], dispatch); result[key] = bindActionCreatorsDeep(actionCreators[key], dispatch);
break; break;
case 'function': case 'function':
result[key] = bindActionCreators(actionCreators[key], dispatch); result[key] = bindActionCreators(actionCreators[key], dispatch);

View File

@ -1,7 +1,7 @@
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import bindActionCreatorsRecursively from './bindActionCreatorsRecursively'; import bindActionCreatorsDeep from './bindActionCreatorsDeep';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ActionCreators as devToolsActionCreators } from './enhance'; import { ActionCreators as historyActionCreators } from './instrument';
export default function connectMonitor({ export default function connectMonitor({
component, component,
@ -10,15 +10,15 @@ export default function connectMonitor({
}) { }) {
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
devToolsState: state.devToolsState, historyState: state.historyState,
monitorState: state.monitorState monitorState: state.monitorState
}; };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
devToolsActions: bindActionCreators(devToolsActionCreators, dispatch), historyActions: bindActionCreators(historyActionCreators, dispatch),
monitorActions: bindActionCreatorsRecursively(actionCreators, dispatch) monitorActions: bindActionCreatorsDeep(actionCreators, dispatch)
}; };
} }

View File

@ -1,5 +1,5 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import enhance from './enhance'; import instrument from './instrument';
import connectMonitor from './connectMonitor'; import connectMonitor from './connectMonitor';
export default function createDevTools(monitor) { export default function createDevTools(monitor) {
@ -10,12 +10,12 @@ export default function createDevTools(monitor) {
store: PropTypes.object.isRequired store: PropTypes.object.isRequired
}; };
static enhance = enhance(Monitor.reducer); static instrument = () => instrument(Monitor.reducer);
render() { render() {
return ( return (
<Monitor {...this.props} <Monitor {...this.props}
store={this.context.store.devToolsStore} /> store={this.context.store.instrumentedStore} />
); );
} }
}; };

View File

@ -1,3 +1,3 @@
export { default, ActionCreators, ActionTypes } from './enhance'; export { default as instrument, ActionTypes } from './instrument';
export { default as persistState } from './persistState'; export { default as persistState } from './persistState';
export { default as createDevTools } from './createDevTools'; export { default as createDevTools } from './createDevTools';

View File

@ -1,6 +1,6 @@
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
const ActionTypes = { export const ActionTypes = {
PERFORM_ACTION: 'PERFORM_ACTION', PERFORM_ACTION: 'PERFORM_ACTION',
RESET: 'RESET', RESET: 'RESET',
ROLLBACK: 'ROLLBACK', ROLLBACK: 'ROLLBACK',
@ -10,10 +10,41 @@ const ActionTypes = {
JUMP_TO_STATE: 'JUMP_TO_STATE' JUMP_TO_STATE: 'JUMP_TO_STATE'
}; };
const INIT_ACTION = { /**
type: '@@INIT' * Action creators to change the History state.
*/
export const ActionCreators = {
performAction(action) {
return { type: ActionTypes.PERFORM_ACTION, action, timestamp: Date.now() };
},
reset() {
return { type: ActionTypes.RESET, timestamp: Date.now() };
},
rollback() {
return { type: ActionTypes.ROLLBACK, timestamp: Date.now() };
},
commit() {
return { type: ActionTypes.COMMIT, timestamp: Date.now() };
},
sweep() {
return { type: ActionTypes.SWEEP };
},
toggleAction(index) {
return { type: ActionTypes.TOGGLE_ACTION, index };
},
jumpToState(index) {
return { type: ActionTypes.JUMP_TO_STATE, index };
}
}; };
const INIT_ACTION = { type: '@@INIT' };
function toggle(obj, key) { function toggle(obj, key) {
const clone = { ...obj }; const clone = { ...obj };
if (clone[key]) { if (clone[key]) {
@ -52,8 +83,6 @@ function computeNextEntry(reducer, action, state, error) {
/** /**
* Runs the reducer on all actions to get a fresh computation log. * Runs the reducer on all actions to get a fresh computation log.
* It's probably a good idea to do this only if the code has changed,
* but until we have some tests we'll just do it every time an action fires.
*/ */
function recomputeStates(reducer, committedState, stagedActions, skippedActions) { function recomputeStates(reducer, committedState, stagedActions, skippedActions) {
const computedStates = []; const computedStates = [];
@ -77,10 +106,10 @@ function recomputeStates(reducer, committedState, stagedActions, skippedActions)
} }
/** /**
* Lifts the app state reducer into a DevTools state reducer. * Creates a history state reducer from an app's reducer.
*/ */
function createDevToolsStateReducer(reducer, initialCommittedState) { function createHistoryReducer(reducer, initialCommittedState) {
const initialState = { const initialHistoryState = {
committedState: initialCommittedState, committedState: initialCommittedState,
stagedActions: [INIT_ACTION], stagedActions: [INIT_ACTION],
skippedActions: {}, skippedActions: {},
@ -89,9 +118,9 @@ function createDevToolsStateReducer(reducer, initialCommittedState) {
}; };
/** /**
* Manages how the DevTools actions modify the DevTools state. * Manages how the history actions modify the history state.
*/ */
return function devToolsState(state = initialState, action) { return (historyState = initialHistoryState, historyAction) => {
let shouldRecomputeStates = true; let shouldRecomputeStates = true;
let { let {
committedState, committedState,
@ -100,34 +129,34 @@ function createDevToolsStateReducer(reducer, initialCommittedState) {
computedStates, computedStates,
currentStateIndex, currentStateIndex,
timestamps timestamps
} = state; } = historyState;
switch (action.type) { switch (historyAction.type) {
case ActionTypes.RESET: case ActionTypes.RESET:
committedState = initialState; committedState = initialCommittedState;
stagedActions = [INIT_ACTION]; stagedActions = [INIT_ACTION];
skippedActions = {}; skippedActions = {};
currentStateIndex = 0; currentStateIndex = 0;
timestamps = [action.timestamp]; timestamps = [historyAction.timestamp];
break; break;
case ActionTypes.COMMIT: case ActionTypes.COMMIT:
committedState = computedStates[currentStateIndex].state; committedState = computedStates[currentStateIndex].state;
stagedActions = [INIT_ACTION]; stagedActions = [INIT_ACTION];
skippedActions = {}; skippedActions = {};
currentStateIndex = 0; currentStateIndex = 0;
timestamps = [action.timestamp]; timestamps = [historyAction.timestamp];
break; break;
case ActionTypes.ROLLBACK: case ActionTypes.ROLLBACK:
stagedActions = [INIT_ACTION]; stagedActions = [INIT_ACTION];
skippedActions = {}; skippedActions = {};
currentStateIndex = 0; currentStateIndex = 0;
timestamps = [action.timestamp]; timestamps = [historyAction.timestamp];
break; break;
case ActionTypes.TOGGLE_ACTION: case ActionTypes.TOGGLE_ACTION:
skippedActions = toggle(skippedActions, action.index); skippedActions = toggle(skippedActions, historyAction.index);
break; break;
case ActionTypes.JUMP_TO_STATE: case ActionTypes.JUMP_TO_STATE:
currentStateIndex = action.index; currentStateIndex = historyAction.index;
// Optimization: we know the history has not changed. // Optimization: we know the history has not changed.
shouldRecomputeStates = false; shouldRecomputeStates = false;
break; break;
@ -142,8 +171,8 @@ function createDevToolsStateReducer(reducer, initialCommittedState) {
currentStateIndex++; currentStateIndex++;
} }
stagedActions = [...stagedActions, action.action]; stagedActions = [...stagedActions, historyAction.action];
timestamps = [...timestamps, action.timestamp]; timestamps = [...timestamps, historyAction.timestamp];
// Optimization: we know that the past has not changed. // Optimization: we know that the past has not changed.
shouldRecomputeStates = false; shouldRecomputeStates = false;
@ -151,7 +180,7 @@ function createDevToolsStateReducer(reducer, initialCommittedState) {
const previousEntry = computedStates[computedStates.length - 1]; const previousEntry = computedStates[computedStates.length - 1];
const nextEntry = computeNextEntry( const nextEntry = computeNextEntry(
reducer, reducer,
action.action, historyAction.action,
previousEntry.state, previousEntry.state,
previousEntry.error previousEntry.error
); );
@ -182,44 +211,32 @@ function createDevToolsStateReducer(reducer, initialCommittedState) {
} }
/** /**
* Lifts an app action to a DevTools action. * Provides a view into the History state that matches the current app state.
*/ */
function liftAction(action) { function selectAppState(instrumentedState) {
const liftedAction = { const { computedStates, currentStateIndex } = instrumentedState.historyState;
type: ActionTypes.PERFORM_ACTION,
action,
timestamp: Date.now()
};
return liftedAction;
}
/**
* Unlifts the DevTools state to the app state.
*/
function unliftState(liftedState) {
const { computedStates, currentStateIndex } = liftedState.devToolsState;
const { state } = computedStates[currentStateIndex]; const { state } = computedStates[currentStateIndex];
return state; return state;
} }
/** /**
* Unlifts the DevTools store to act like the app's store. * Deinstruments the History store to act like the app's store.
*/ */
function mapToComputedStateStore(devToolsStore, wrapReducer) { function selectAppStore(instrumentedStore, instrumentReducer) {
let lastDefinedState; let lastDefinedState;
return { return {
...devToolsStore, ...instrumentedStore,
devToolsStore, instrumentedStore,
dispatch(action) { dispatch(action) {
devToolsStore.dispatch(liftAction(action)); instrumentedStore.dispatch(ActionCreators.performAction(action));
return action; return action;
}, },
getState() { getState() {
const state = unliftState(devToolsStore.getState()); const state = selectAppState(instrumentedStore.getState());
if (state !== undefined) { if (state !== undefined) {
lastDefinedState = state; lastDefinedState = state;
} }
@ -227,51 +244,25 @@ function mapToComputedStateStore(devToolsStore, wrapReducer) {
}, },
replaceReducer(nextReducer) { replaceReducer(nextReducer) {
devToolsStore.replaceReducer(wrapReducer(nextReducer)); instrumentedStore.replaceReducer(instrumentReducer(nextReducer));
} }
}; };
} }
/** /**
* Action creators to change the DevTools state. * Redux History store enhancer.
*/ */
export const ActionCreators = { export default function instrument(monitorReducer = () => null) {
reset() { return createStore => (reducer, initialState) => {
return { type: ActionTypes.RESET, timestamp: Date.now() }; function instrumentReducer(r) {
}, const historyReducer = createHistoryReducer(r, initialState);
return ({ historyState, monitorState } = {}, action) => ({
historyState: historyReducer(historyState, action),
monitorState: monitorReducer(monitorState, action)
});
}
rollback() { const instrumentedStore = createStore(instrumentReducer(reducer));
return { type: ActionTypes.ROLLBACK, timestamp: Date.now() }; return selectAppStore(instrumentedStore, instrumentReducer);
},
commit() {
return { type: ActionTypes.COMMIT, timestamp: Date.now() };
},
sweep() {
return { type: ActionTypes.SWEEP };
},
toggleAction(index) {
return { type: ActionTypes.TOGGLE_ACTION, index };
},
jumpToState(index) {
return { type: ActionTypes.JUMP_TO_STATE, index };
}
};
/**
* Redux DevTools store enhancer.
*/
export default function enhance(monitorReducer = () => null) {
return next => (reducer, initialState) => {
const wrapReducer = (r) => combineReducers({
devToolsState: createDevToolsStateReducer(r, initialState),
monitorState: monitorReducer
});
const devToolsStore = next(wrapReducer(reducer));
return mapToComputedStateStore(devToolsStore, wrapReducer);
}; };
} }

View File

@ -1,51 +1,34 @@
export default function persistState(sessionId, stateDeserializer = null, actionDeserializer = null) { const identity = _ => _;
export default function persistState(sessionId, deserializeState = identity, deserializeAction = identity) {
if (!sessionId) { if (!sessionId) {
return next => (...args) => next(...args); return next => (...args) => next(...args);
} }
function deserializeState(fullState) { function deserialize({ historyState, ...rest }) {
return { return {
...fullState, ...rest,
committedState: stateDeserializer(fullState.committedState), historyState: {
computedStates: fullState.computedStates.map((computedState) => { ...historyState,
return { stagedActions: historyState.stagedActions.map(deserializeAction),
committedState: deserializeState(historyState.committedState),
computedStates: historyState.computedStates.map(computedState => ({
...computedState, ...computedState,
state: stateDeserializer(computedState.state) state: deserializeState(computedState.state)
}; }))
}) }
}; };
} }
function deserializeActions(fullState) {
return {
...fullState,
stagedActions: fullState.stagedActions.map((action) => {
return actionDeserializer(action);
})
};
}
function deserialize(fullState) {
if (!fullState) {
return fullState;
}
let deserializedState = fullState;
if (typeof stateDeserializer === 'function') {
deserializedState = deserializeState(deserializedState);
}
if (typeof actionDeserializer === 'function') {
deserializedState = deserializeActions(deserializedState);
}
return deserializedState;
}
return next => (reducer, initialState) => { return next => (reducer, initialState) => {
const key = `redux-dev-session-${sessionId}`; const key = `redux-dev-session-${sessionId}`;
let finalInitialState; let finalInitialState;
try { try {
finalInitialState = deserialize(JSON.parse(localStorage.getItem(key))) || initialState; const json = localStorage.getItem(key);
next(reducer, initialState); if (json) {
finalInitialState = deserialize(JSON.parse(json)) || initialState;
next(reducer, initialState);
}
} catch (e) { } catch (e) {
console.warn('Could not read debug session from localStorage:', e); console.warn('Could not read debug session from localStorage:', e);
try { try {

View File

@ -1,6 +1,6 @@
import expect, { spyOn } from 'expect'; import expect, { spyOn } from 'expect';
import { createStore } from 'redux'; import { createStore } from 'redux';
import devTools, { ActionCreators } from '../src/devTools'; import instrument, { ActionCreators } from '../src/instrument';
function counter(state = 0, action) { function counter(state = 0, action) {
switch (action.type) { switch (action.type) {
@ -27,13 +27,13 @@ function doubleCounter(state = 0, action) {
} }
} }
describe('devTools', () => { describe('instrument', () => {
let store; let store;
let devToolsStore; let instrumentedStore;
beforeEach(() => { beforeEach(() => {
store = devTools()(createStore)(counter); store = instrument()(createStore)(counter);
devToolsStore = store.devToolsStore; instrumentedStore = store.instrumentedStore;
}); });
it('should perform actions', () => { it('should perform actions', () => {
@ -49,20 +49,20 @@ describe('devTools', () => {
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
devToolsStore.dispatch(ActionCreators.commit()); instrumentedStore.dispatch(ActionCreators.commit());
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(4); expect(store.getState()).toBe(4);
devToolsStore.dispatch(ActionCreators.rollback()); instrumentedStore.dispatch(ActionCreators.rollback());
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
store.dispatch({ type: 'DECREMENT' }); store.dispatch({ type: 'DECREMENT' });
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
devToolsStore.dispatch(ActionCreators.rollback()); instrumentedStore.dispatch(ActionCreators.rollback());
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
}); });
@ -70,19 +70,19 @@ describe('devTools', () => {
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
devToolsStore.dispatch(ActionCreators.commit()); instrumentedStore.dispatch(ActionCreators.commit());
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
devToolsStore.dispatch(ActionCreators.rollback()); instrumentedStore.dispatch(ActionCreators.rollback());
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
devToolsStore.dispatch(ActionCreators.reset()); instrumentedStore.dispatch(ActionCreators.reset());
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
}); });
@ -93,10 +93,10 @@ describe('devTools', () => {
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
devToolsStore.dispatch(ActionCreators.toggleAction(2)); instrumentedStore.dispatch(ActionCreators.toggleAction(2));
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
devToolsStore.dispatch(ActionCreators.toggleAction(2)); instrumentedStore.dispatch(ActionCreators.toggleAction(2));
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
}); });
@ -108,16 +108,16 @@ describe('devTools', () => {
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
devToolsStore.dispatch(ActionCreators.toggleAction(2)); instrumentedStore.dispatch(ActionCreators.toggleAction(2));
expect(store.getState()).toBe(3); expect(store.getState()).toBe(3);
devToolsStore.dispatch(ActionCreators.sweep()); instrumentedStore.dispatch(ActionCreators.sweep());
expect(store.getState()).toBe(3); expect(store.getState()).toBe(3);
devToolsStore.dispatch(ActionCreators.toggleAction(2)); instrumentedStore.dispatch(ActionCreators.toggleAction(2));
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
devToolsStore.dispatch(ActionCreators.sweep()); instrumentedStore.dispatch(ActionCreators.sweep());
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
}); });
@ -127,19 +127,19 @@ describe('devTools', () => {
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
devToolsStore.dispatch(ActionCreators.jumpToState(0)); instrumentedStore.dispatch(ActionCreators.jumpToState(0));
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
devToolsStore.dispatch(ActionCreators.jumpToState(1)); instrumentedStore.dispatch(ActionCreators.jumpToState(1));
expect(store.getState()).toBe(1); expect(store.getState()).toBe(1);
devToolsStore.dispatch(ActionCreators.jumpToState(2)); instrumentedStore.dispatch(ActionCreators.jumpToState(2));
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' });
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
devToolsStore.dispatch(ActionCreators.jumpToState(4)); instrumentedStore.dispatch(ActionCreators.jumpToState(4));
expect(store.getState()).toBe(2); expect(store.getState()).toBe(2);
}); });
@ -155,17 +155,17 @@ describe('devTools', () => {
it('should catch and record errors', () => { it('should catch and record errors', () => {
let spy = spyOn(console, 'error'); let spy = spyOn(console, 'error');
let storeWithBug = devTools()(createStore)(counterWithBug); let storeWithBug = instrument()(createStore)(counterWithBug);
storeWithBug.dispatch({ type: 'INCREMENT' }); storeWithBug.dispatch({ type: 'INCREMENT' });
storeWithBug.dispatch({ type: 'DECREMENT' }); storeWithBug.dispatch({ type: 'DECREMENT' });
storeWithBug.dispatch({ type: 'INCREMENT' }); storeWithBug.dispatch({ type: 'INCREMENT' });
let devStoreState = storeWithBug.devToolsStore.getState(); let historyState = storeWithBug.instrumentedStore.getState().historyState;
expect(devStoreState.computedStates[2].error).toMatch( expect(historyState.computedStates[2].error).toMatch(
/ReferenceError/ /ReferenceError/
); );
expect(devStoreState.computedStates[3].error).toMatch( expect(historyState.computedStates[3].error).toMatch(
/Interrupted by an error up the chain/ /Interrupted by an error up the chain/
); );
expect(spy.calls[0].arguments[0]).toMatch( expect(spy.calls[0].arguments[0]).toMatch(
@ -176,7 +176,7 @@ describe('devTools', () => {
}); });
it('should return the last non-undefined state from getState', () => { it('should return the last non-undefined state from getState', () => {
let storeWithBug = devTools()(createStore)(counterWithBug); let storeWithBug = instrument()(createStore)(counterWithBug);
storeWithBug.dispatch({ type: 'INCREMENT' }); storeWithBug.dispatch({ type: 'INCREMENT' });
storeWithBug.dispatch({ type: 'INCREMENT' }); storeWithBug.dispatch({ type: 'INCREMENT' });
expect(storeWithBug.getState()).toBe(2); expect(storeWithBug.getState()).toBe(2);
@ -187,7 +187,7 @@ describe('devTools', () => {
it('should not recompute states on every action', () => { it('should not recompute states on every action', () => {
let reducerCalls = 0; let reducerCalls = 0;
let monitoredStore = devTools()(createStore)(() => reducerCalls++); let monitoredStore = instrument()(createStore)(() => reducerCalls++);
expect(reducerCalls).toBe(1); expect(reducerCalls).toBe(1);
monitoredStore.dispatch({ type: 'INCREMENT' }); monitoredStore.dispatch({ type: 'INCREMENT' });
monitoredStore.dispatch({ type: 'INCREMENT' }); monitoredStore.dispatch({ type: 'INCREMENT' });
@ -197,8 +197,8 @@ describe('devTools', () => {
it('should not recompute states when jumping to state', () => { it('should not recompute states when jumping to state', () => {
let reducerCalls = 0; let reducerCalls = 0;
let monitoredStore = devTools()(createStore)(() => reducerCalls++); let monitoredStore = instrument()(createStore)(() => reducerCalls++);
let monitoredDevToolsStore = monitoredStore.devToolsStore; let monitoredInstrumentedStore = monitoredStore.instrumentedStore;
expect(reducerCalls).toBe(1); expect(reducerCalls).toBe(1);
monitoredStore.dispatch({ type: 'INCREMENT' }); monitoredStore.dispatch({ type: 'INCREMENT' });
@ -206,13 +206,13 @@ describe('devTools', () => {
monitoredStore.dispatch({ type: 'INCREMENT' }); monitoredStore.dispatch({ type: 'INCREMENT' });
expect(reducerCalls).toBe(4); expect(reducerCalls).toBe(4);
monitoredDevToolsStore.dispatch(ActionCreators.jumpToState(0)); monitoredInstrumentedStore.dispatch(ActionCreators.jumpToState(0));
expect(reducerCalls).toBe(4); expect(reducerCalls).toBe(4);
monitoredDevToolsStore.dispatch(ActionCreators.jumpToState(1)); monitoredInstrumentedStore.dispatch(ActionCreators.jumpToState(1));
expect(reducerCalls).toBe(4); expect(reducerCalls).toBe(4);
monitoredDevToolsStore.dispatch(ActionCreators.jumpToState(3)); monitoredInstrumentedStore.dispatch(ActionCreators.jumpToState(3));
expect(reducerCalls).toBe(4); expect(reducerCalls).toBe(4);
}); });
}); });

View File

@ -1,6 +1,5 @@
import expect from 'expect'; import expect from 'expect';
import devTools from '../src/devTools'; import { instrument, persistState } from '../src';
import persistState from '../src/persistState';
import { compose, createStore } from 'redux'; import { compose, createStore } from 'redux';
describe('persistState', () => { describe('persistState', () => {
@ -40,7 +39,7 @@ describe('persistState', () => {
}; };
it('should persist state', () => { it('should persist state', () => {
const finalCreateStore = compose(devTools(), persistState('id'))(createStore); const finalCreateStore = compose(instrument(), persistState('id'))(createStore);
const store = finalCreateStore(reducer); const store = finalCreateStore(reducer);
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
@ -53,7 +52,7 @@ describe('persistState', () => {
}); });
it('should not persist state if no session id', () => { it('should not persist state if no session id', () => {
const finalCreateStore = compose(devTools(), persistState())(createStore); const finalCreateStore = compose(instrument(), persistState())(createStore);
const store = finalCreateStore(reducer); const store = finalCreateStore(reducer);
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
@ -67,7 +66,7 @@ describe('persistState', () => {
it('should run with a custom state deserializer', () => { it('should run with a custom state deserializer', () => {
const oneLess = state => state === undefined ? -1 : state - 1; const oneLess = state => state === undefined ? -1 : state - 1;
const finalCreateStore = compose(devTools(), persistState('id', oneLess))(createStore); const finalCreateStore = compose(instrument(), persistState('id', oneLess))(createStore);
const store = finalCreateStore(reducer); const store = finalCreateStore(reducer);
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
@ -81,7 +80,7 @@ describe('persistState', () => {
it('should run with a custom action deserializer', () => { it('should run with a custom action deserializer', () => {
const incToDec = action => action.type === 'INCREMENT' ? { type: 'DECREMENT' } : action; const incToDec = action => action.type === 'INCREMENT' ? { type: 'DECREMENT' } : action;
const finalCreateStore = compose(devTools(), persistState('id', null, incToDec))(createStore); const finalCreateStore = compose(instrument(), persistState('id', undefined, incToDec))(createStore);
const store = finalCreateStore(reducer); const store = finalCreateStore(reducer);
expect(store.getState()).toBe(0); expect(store.getState()).toBe(0);
@ -95,7 +94,7 @@ describe('persistState', () => {
it('should warn if read from localStorage fails', () => { it('should warn if read from localStorage fails', () => {
const spy = expect.spyOn(console, 'warn'); const spy = expect.spyOn(console, 'warn');
const finalCreateStore = compose(devTools(), persistState('id'))(createStore); const finalCreateStore = compose(instrument(), persistState('id'))(createStore);
delete global.localStorage.getItem; delete global.localStorage.getItem;
finalCreateStore(reducer); finalCreateStore(reducer);
@ -108,7 +107,7 @@ describe('persistState', () => {
}); });
it('should warn if write to localStorage fails', () => { it('should warn if write to localStorage fails', () => {
const spy = expect.spyOn(console, 'warn'); const spy = expect.spyOn(console, 'warn');
const finalCreateStore = compose(devTools(), persistState('id'))(createStore); const finalCreateStore = compose(instrument(), persistState('id'))(createStore);
delete global.localStorage.setItem; delete global.localStorage.setItem;
const store = finalCreateStore(reducer); const store = finalCreateStore(reducer);