3.1.4 • Published 5 years ago

redux-thunk-tester v3.1.4

Weekly downloads
6
License
ISC
Repository
github
Last release
5 years ago

Redux thunk tester

Useful for tests when using synchronous/asynchronous redux actions.

npm i --save-dev redux-thunk-tester

Methods

createReduxThunkHistoryMiddleware

First need to create a store with reduxThunkTester middleware and create an instance of ReduxThunkTester.

import ReduxThunkTester from 'redux-thunk-tester'.

const createMockStore = () => {
  const reduxThunkTester = new ReduxThunkTester();

  const store = createStore(
    combineReducers({exampleSimple: reducer}),
    applyMiddleware(
      reduxThunkTester.createReduxThunkHistoryMiddleware(),
      thunk
    ),
  );

  return {reduxThunkTester, store};
};

Now reduxThunkTester have next methods:

type StringifyOptions = {
  inlineLimit?: number,
  withColor?: boolean,
}

getActionHistory(): Action[] | Promise<Action>[];
getActionHistoryAsync(): Promise<Action>[];
clearActionHistory(): Action[] | Promise<Action>[];
getActionHistoryStringify(options?: StringifyOptions): string;
getActionHistoryStringifyAsync(options?: StringifyOptions): Promise<string>;

getActionHistory(): Array of actions

Return array of actions. Use when not using thunk (async actions), for async actions use getActionHistoryAsync.

Example:

test('Test action history', () => {
  const {reduxThunkTester: {getActionHistory}, store} = createMockStore();
  
  store.dispatch({type: 'TEST_ACTION_1'});	
  store.dispatch({type: 'TEST_ACTION_2'});	
 
  expect(getActionHistory).toEqual([
   {type: 'TEST_ACTION_1'},
   {type: 'TEST_ACTION_2'},
  ])
}) 

getActionHistoryAsync(): Array of promise

Return array of promise. Use when using thunk (async actions). Example:

test('Test action history', async () => {
  const {reduxThunkTester: {getActionHistoryAsync}, store} = createMockStore();
  
  const someAsyncAction = () => async (dispatch) => {
    // here may be await for some async request
    store.dispatch({type: 'TEST_ACTION', payload: 1});	
    store.dispatch({type: 'TEST_ACTION', payload: 2});	
    store.dispatch({type: 'TEST_ACTION', payload: 3});	
  }

  store.dispatch(someAsyncAction());
  
  expect(await getActionHistoryAsync).toEqual([
    {type: 'TEST_ACTION', payload: 1},
    {type: 'TEST_ACTION', payload: 2},
    {type: 'TEST_ACTION', payload: 3},
  ])
}) 

clearActionHistory()

Call this method when you want clear action history. Example:

test('Test action history', () => {
  const {reduxThunkTester: {getActionHistory, clearActionHistory}, store} = createMockStore();
  
  store.dispatch({type: 'TEST_ACTION_1'});	
  clearActionHistory();
  store.dispatch({type: 'TEST_ACTION_2'});	
 
  expect(getActionHistory).toEqual([
   {type: 'TEST_ACTION_2'},
  ])
}) 

getActionHistoryStringify(, params): string

Return string with action history. Useful for debugging. Use when not using thunk (async actions), for async actions use getActionHistoryStringifyAsync. example:

test('Change input actions', () => {
  const {reduxThunkTester: {getActionHistoryStringify}, store} = createMockStore();
  
  store.dispatch({type: 'TEST_ACTION', payload: 1});	
  store.dispatch({type: 'TEST_ACTION', payload: 2});	
  store.dispatch({type: 'TEST_ACTION', payload: 3});	
 
  console.log(getActionHistoryStringify({withColor: true}));
}) 

Result: Screenshot result getActionHistoryStringify

getActionHistoryStringifyAsync(, params): Promise

Return string with action history. Useful for debugging. Use when use thunk (async actions). example:

test('Change input actions', async () => {
  const {reduxThunkTester: {getActionHistoryStringifyAsync}, store} = createMockStore();
  
  store.dispatch({type: 'TEST_ACTION', payload: 1});	
  store.dispatch({type: 'TEST_ACTION', payload: 2});	
  store.dispatch({type: 'TEST_ACTION', payload: 3});	
 
  console.log(await getActionHistoryStringify({withColor: true}));
}) 

Result: Screenshot result getActionHistoryStringify

static actionStringify(action, stringifyOptions)

Use when need output action in console. For example:

import ReduxThunkTester = 'redux-thunk-tester';

...

console.log(ReduxThunkTester.actionStringify({
  type: 'SOME_ACTION',
  payload: {message: 'some payload'},
}))

Simple example

Repository: https://github.com/GTOsss/redux-thunk-tester/tree/master/example-simple/src

example.js

const TOGGLE_LOADING = 'TOGGLE_LOADING';
const SOME_BACKEND_REQUEST = 'SOME_BACKEND_REQUEST';

const request = (ms) => new Promise((resolve) => {
  setTimeout(() => resolve('success response'), ms);
});

const resultRequestAction = (value) => ({ type: SOME_BACKEND_REQUEST, payload: value });
const toggleLoadingAction = (value) => ({ type: TOGGLE_LOADING, payload: value });


export const reducer = (state = {}, { type, payload } = {}) => {
  switch (type) {
    case TOGGLE_LOADING: return { ...state, loading: payload };
    case SOME_BACKEND_REQUEST: return { ...state, result: payload };
    default: return state;
  }
};

export const asyncThunkWithRequest = () => async (dispatch) => {
  try {
    dispatch(toggleLoadingAction(true));
    const result = await request(200);
    dispatch(resultRequestAction(result));
  } finally {
    dispatch(toggleLoadingAction(false));
  }
};

example.test.js

import React from 'react';
import {createStore, applyMiddleware, combineReducers} from 'redux';
import {asyncThunkWithRequest, reducer} from './example';
import ReduxThunkTester from 'redux-thunk-tester';
import thunk from 'redux-thunk';

const createMockStore = () => {
  const reduxThunkTester = new ReduxThunkTester();

  const store = createStore(
    combineReducers({exampleSimple: reducer}),
    applyMiddleware(
      reduxThunkTester.createReduxThunkHistoryMiddleware(),
      thunk
    ),
  );

  return {reduxThunkTester, store};
};

describe('Simple example.', () => {
  test('Success request.', async () => {
    const {store, reduxThunkTester: {getActionHistoryAsync, getActionHistoryStringifyAsync}} = createMockStore();

    store.dispatch(asyncThunkWithRequest());

    const actionHistory = await getActionHistoryAsync(); // need to wait async thunk (all inner dispatch)

    expect(actionHistory).toEqual([
      {type: 'TOGGLE_LOADING', payload: true},
      {type: 'SOME_BACKEND_REQUEST', payload: 'success response'},
      {type: 'TOGGLE_LOADING', payload: false},
    ]);

    expect(store.getState().exampleSimple).toEqual({
      loading: false,
      result: 'success response'
    });

    console.log(ReduxThunkTester.actionStringify(actionHistory[2]));
    console.log(await getActionHistoryStringifyAsync({withColor: true}));
  });
});

Example for react

Repository: https://github.com/GTOsss/redux-thunk-tester/tree/master/example/src

example.js:

const FOCUS = 'FOCUS';
const CHANGE = 'CHANGE';
const BLUR = 'BLUR';

const sleep = (ms) => new Promise((resolve) => { setTimeout(resolve, ms) });

const focusAction = () => ({ type: FOCUS });
const changeAction = (value) => ({ type: CHANGE, payload: { value } });
const blurAction = () => ({ type: BLUR });

const blurThunk = () => async (dispatch) => {
  await sleep(2000);
  dispatch(blurAction());
};

const reducer = (state = {}, { type, payload } = {}) => {
  switch (type) {
    case FOCUS: return { ...state, active: true };
    case CHANGE: return { ...state, value: payload.value };
    case BLUR: return { ...state, active: false };
    default: return state;
  }
};

class Example extends Component {
  onFocus = () => {
    this.props.focusAction();
  };

  onChange = (e) => {
    const { target: { value } } = e;
    this.props.changeAction(value);
  };

  onBlur = () => {
    this.props.blurThunk();
  };

  render() {
    return (
      <input type="text" onFocus={this.onFocus} onChange={this.onChange} onBlur={this.onBlur} />
    );
  }
}

const mapDispatchToProps = (dispatch) => ({
  focusAction: bindActionCreators(focusAction, dispatch),
  changeAction: bindActionCreators(changeAction, dispatch),
  blurThunk: bindActionCreators(blurThunk, dispatch)
});

export default connect(null, mapDispatchToProps)(Example);

example.test.js

import React from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, combineReducers} from 'redux';
import stringifyObject from 'stringify-object';
import ReduxThunkTester from 'redux-thunk-tester';
import thunk from 'redux-thunk';
import ExampleConnectedComponent, {reducer} from './example';

const renderComponent = () => {
  const reduxThunkTester = new ReduxThunkTester();

  const store = createStore(
    combineReducers({example: reducer}),
    applyMiddleware(
      reduxThunkTester.createReduxThunkHistoryMiddleware(),
      thunk
    ),
  );

  const component = mount(<Provider store={store}><ExampleConnectedComponent /></Provider>);

  return {reduxThunkTester, component, store};
};

describe('Example.', () => {
  test('Render example.', () => {
    const {component} = renderComponent();
    expect(component).toMatchSnapshot();
  });

  test('Focus input: action', () => {
    const {component, reduxThunkTester: {getActionHistoryStringify}} = renderComponent();
    component.find('input').simulate('focus');
    expect(getActionHistoryStringify()).toMatchSnapshot();
    console.log(getActionHistoryStringify({withColor: true}));
  });

  test('Focus input: store', () => {
    const {component, store} = renderComponent();
    component.find('input').simulate('focus');
    expect(stringifyObject(store.getState().example)).toMatchSnapshot();
    console.log(stringifyObject(store.getState().example));
  });

  test('Change input: actions.', () => {
    const {component, reduxThunkTester: {
      clearActionHistory, getActionHistoryStringify
    }} = renderComponent();

    component.find('input').simulate('focus');
    clearActionHistory();
    component.find('input').simulate('change', {target: {value: 't'}});
    component.find('input').simulate('change', {target: {value: 'te'}});
    component.find('input').simulate('change', {target: {value: 'tes'}});
    component.find('input').simulate('change', {target: {value: 'test'}});

    console.log(getActionHistoryStringify({withColor: true}));
    expect(getActionHistoryStringify()).toMatchSnapshot();
  });

  test('Change input: store', () => {
    const {component, store, reduxThunkTester: {clearActionHistory}} = renderComponent();

    component.find('input').simulate('focus');
    clearActionHistory();
    component.find('input').simulate('change', {target: {value: 't'}});
    component.find('input').simulate('change', {target: {value: 'te'}});
    component.find('input').simulate('change', {target: {value: 'tes'}});
    component.find('input').simulate('change', {target: {value: 'test'}});

    expect(store.getState().example).toEqual({active: true, value: 'test'});
    console.log(stringifyObject(store.getState().example));
  });

  test('Blur input: async action', async () => {
    const {component, reduxThunkTester: {
      clearActionHistory, getActionHistoryStringifyAsync
    }} = renderComponent();

    component.find('input').simulate('focus');
    component.find('input').simulate('change', {target: {value: 't'}});
    component.find('input').simulate('change', {target: {value: 'te'}});
    component.find('input').simulate('change', {target: {value: 'tes'}});
    component.find('input').simulate('change', {target: {value: 'test'}});
    clearActionHistory();
    component.find('input').simulate('blur');

    console.log(await getActionHistoryStringifyAsync({withColor: true}));
    expect(await getActionHistoryStringifyAsync()).toMatchSnapshot();
  });

  test('Blur input: store', async () => {
    const {component, store, reduxThunkTester: {getActionHistoryAsync}} = renderComponent();

    component.find('input').simulate('focus');
    component.find('input').simulate('change', {target: {value: 't'}});
    component.find('input').simulate('change', {target: {value: 'te'}});
    component.find('input').simulate('change', {target: {value: 'tes'}});
    component.find('input').simulate('change', {target: {value: 'test'}});
    component.find('input').simulate('blur');

    await getActionHistoryAsync(); // need to wait all thunk.

    expect(store.getState().example).toEqual({active: false, value: 'test'});
    console.log(store.getState().example);
  });
});

example.test.js results:

Screenshot result 1 Screenshot result 2 Screenshot result 3 Screenshot result 4 Screenshot result 5 Screenshot result 6

3.1.4

5 years ago

3.1.3

5 years ago

3.1.2

5 years ago

3.1.1

5 years ago

3.1.0

5 years ago

3.0.5

5 years ago

3.0.4

5 years ago

3.0.3

5 years ago

3.0.2

5 years ago

3.0.1

5 years ago

3.0.0

5 years ago

2.0.9

5 years ago

2.0.8

5 years ago

2.0.7

6 years ago

2.0.6

6 years ago

2.0.5

6 years ago

2.0.4

6 years ago

2.0.3

6 years ago

2.0.2

6 years ago

2.0.1

6 years ago

2.0.0

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago