Skip to content

Instantly share code, notes, and snippets.

@darekzak
Last active June 24, 2022 01:04
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save darekzak/0c56bd9f1ad6e876fd21837feee79c50 to your computer and use it in GitHub Desktop.
Save darekzak/0c56bd9f1ad6e876fd21837feee79c50 to your computer and use it in GitHub Desktop.
import rootReducer from '../my-rootreducer-dir';

function renderWithRedux(ui, { initialState, store = createStore(rootReducer, initialState) } = {}, renderFn = render) {
  const obj = {
    ...renderFn(<Provider store={store}>{ui}</Provider>),
    store,
  };
  obj.rerenderWithRedux = (el, nextState) => {
    if (nextState) {
      store.replaceReducer(() => nextState);
      store.dispatch({ type: '__TEST_ACTION_REPLACE_STATE__' });
      store.replaceReducer(rootReducer);
    }
    return renderWithRedux(el, { store }, obj.rerender);
  };
  return obj;
}

Usage

// FileCmp.js

export default ({ isUploading, sadFace }) => <>
  {isUploading ? 'upload in progress' : '...'}
  {sadFace && ':('}
</> 


// FileContainter.js

import React from 'react';
import { connect } from 'react-redux';

import FileCmp from 'dir-to/FileCmp';
import { makeSelectIsUploading } from '../selectors';

const mapStateToProps = (state, { id }) => ({
  isUploading: makeSelectIsUploading(id)(state),
})

export default connect(mapStateToProps)(FileCmp)


// FileCmp.test.js
test('some crazy test', () => {
  const initialState = {
    isUploading: false
  }
  const { rerenderWithRedux, store } = renderWithRedux(<FileCmp />, { initialState })

  // we can update store by dispatching some action eg
  store.dispatch({ type: '__FILE_UPLOAD_ACTION__', id: 1 }) // produces new state --> { isUploading: true }
  expect(getByText('upload in progress')).toBeInTheDOM()

  // we can also update store by providing next state
  const nextState = { isUploading: false }
  rerenderWithRedux(<FileCmp />, nextState)
  expect(() => getByText('upload in progress')).toThrow()

  // and last but not least, if we invoke rerenderWithRedux only with one arg -> component
  // it will use store as expected
  rerenderWithRedux(<FileCmp sadFace />)
  expect(getByText(':(')).toBeInTheDOM()
  expect(() => getByText('upload in progress')).toThrow()
})
@DannyMichaels
Copy link

where is getByText coming from?

@Fraitz
Copy link

Fraitz commented Sep 1, 2021

where is getByText coming from?

From render, inside renderWIthRedux, but its missing in the destructuring.

const { rerenderWithRedux, store, getByText } = renderWithRedux(<FileCmp />, { initialState })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment