import '@babel/polyfill'; import { createStore, compose } from 'redux'; import { insertScript, listenMessage } from '../../utils/inject'; import '../../../src/pageScript'; function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } describe('Redux enhancer', () => { it('should create the store', async () => { const message = await listenMessage(() => { window.store = createStore( counter, window.__REDUX_DEVTOOLS_EXTENSION__(), ); expect(typeof window.store).toBe('object'); }); expect(message.type).toBe('INIT_INSTANCE'); expect(window.store.getState()).toBe(0); insertScript('window.devToolsOptions = { serialize: false }'); }); it('should start monitoring', async () => { let message = await listenMessage(() => { window.postMessage({ type: 'START', source: '@devtools-extension' }, '*'); }); expect(message.type).toBe('START'); message = await listenMessage(); expect(message.type).toBe('STATE'); expect(message.actionsById).toMatch( /{"0":{"type":"PERFORM_ACTION","action":{"type":"@@INIT"},"/, ); expect(message.computedStates).toBe('[{"state":0}]'); }); it('should perform actions', async () => { let message = await listenMessage(() => { window.store.dispatch({ type: 'INCREMENT' }); expect(window.store.getState()).toBe(1); }); expect(message.type).toBe('ACTION'); expect(message.action).toMatch( /{"type":"PERFORM_ACTION","action":{"type":"INCREMENT"},/, ); expect(message.payload).toBe('1'); message = await listenMessage(() => { window.store.dispatch({ type: 'INCREMENT' }); expect(window.store.getState()).toBe(2); }); expect(message.type).toBe('ACTION'); expect(message.action).toMatch( /{"type":"PERFORM_ACTION","action":{"type":"INCREMENT"},/, ); expect(message.payload).toBe('2'); }); it('should dispatch actions remotely', async () => { let message = await listenMessage(() => { window.postMessage( { type: 'ACTION', payload: "{ type: 'INCREMENT' }", source: '@devtools-extension', }, '*', ); }); expect(message.type).toBe('ACTION'); message = await listenMessage(); expect(message.type).toBe('ACTION'); expect(message.action).toMatch( /{"type":"PERFORM_ACTION","action":{"type":"INCREMENT"},/, ); expect(message.payload).toBe('3'); }); it('should cancel (toggle) action', async () => { let message = await listenMessage(() => { window.postMessage( { type: 'DISPATCH', payload: { type: 'TOGGLE_ACTION', id: 1 }, source: '@devtools-extension', }, '*', ); }); expect(message.type).toBe('DISPATCH'); message = await listenMessage(); expect(message.type).toBe('STATE'); expect(window.store.getState()).toBe(2); message = await listenMessage(() => { window.postMessage( { type: 'DISPATCH', payload: { type: 'TOGGLE_ACTION', id: 1 }, source: '@devtools-extension', }, '*', ); }); expect(message.type).toBe('DISPATCH'); message = await listenMessage(); expect(message.type).toBe('STATE'); expect(window.store.getState()).toBe(3); }); it('should move back and forward (time travel)', async () => { let message = await listenMessage(() => { window.postMessage( { type: 'DISPATCH', payload: { type: 'JUMP_TO_STATE', index: 2, actionId: 2 }, source: '@devtools-extension', }, '*', ); }); expect(message.type).toBe('DISPATCH'); expect(window.store.getState()).toBe(2); message = await listenMessage(() => { window.postMessage( { type: 'DISPATCH', payload: { type: 'JUMP_TO_STATE', index: 3, actionId: 3 }, source: '@devtools-extension', }, '*', ); }); expect(message.type).toBe('DISPATCH'); expect(window.store.getState()).toBe(3); }); it('should import state history', async () => { let message = await listenMessage(() => { window.postMessage( { type: 'IMPORT', state: JSON.stringify({ monitorState: {}, actionsById: { 0: { type: 'PERFORM_ACTION', action: { type: '@@INIT' } }, 1: { type: 'PERFORM_ACTION', action: { type: 'INCREMENT' } }, 2: { type: 'PERFORM_ACTION', action: { type: 'INCREMENT' } }, }, nextActionId: 3, stagedActionIds: [0, 1, 2], skippedActionIds: [], currentStateIndex: 2, computedStates: [{ state: 0 }, { state: 1 }, { state: 2 }], }), source: '@devtools-extension', }, '*', ); }); expect(message.type).toBe('IMPORT'); message = await listenMessage(); expect(message.type).toBe('STATE'); expect(window.store.getState()).toBe(2); }); it('should create the store with config parameters', async () => { const message = await listenMessage(() => { window.store = createStore( counter, window.__REDUX_DEVTOOLS_EXTENSION__({ actionsDenylist: ['SOME_ACTION'], }), ); expect(typeof window.store).toBe('object'); }); expect(message.type).toBe('INIT_INSTANCE'); }); it('should create the store using old Redux api', async () => { const message = await listenMessage(() => { window.store = window.__REDUX_DEVTOOLS_EXTENSION__()(createStore)(counter); expect(typeof window.store).toBe('object'); }); expect(message.type).toBe('INIT_INSTANCE'); }); it('should create the store with several enhancers', async () => { const testEnhancer = (next) => (reducer, initialState, enhancer) => next(reducer, initialState, enhancer); const message = await listenMessage(() => { window.store = createStore( counter, compose(testEnhancer, window.__REDUX_DEVTOOLS_EXTENSION__()), ); expect(typeof window.store).toBe('object'); }); expect(message.type).toBe('INIT_INSTANCE'); }); });