From d36098d2b26a8e80b483ff68bb8324c8c6714994 Mon Sep 17 00:00:00 2001 From: Matan Borenkraout Date: Sat, 4 Apr 2020 10:35:28 +0300 Subject: [PATCH 1/3] Chore: Update react-redux recipe to use custom render with wrapper --- docs/example-react-redux.md | 72 +++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/docs/example-react-redux.md b/docs/example-react-redux.md index 4687189e8..74dd36565 100644 --- a/docs/example-react-redux.md +++ b/docs/example-react-redux.md @@ -23,7 +23,7 @@ class Counter extends React.Component {

Counter

- {this.props.count} +

{this.props.count}

@@ -58,58 +58,68 @@ export function reducer(state = initialState, action) { } ``` -Now here's what your test will look like: +To test our connected component we can create a custom `render` function using +the `wrapper` option as explained in the +[setup](./react-testing-library/setup.md) page. +Our custom `render` function can look like this: -```jsx -// counter.test.js +```js +// test-utils.js import React from 'react' +import { render as rtlRender } from '@testing-library/react' import { createStore } from 'redux' import { Provider } from 'react-redux' -import { render, fireEvent } from '@testing-library/react' -import '@testing-library/jest-dom/extend-expect' -import { initialState, reducer } from './reducer.js' -import Counter from './counter.js' - -// this is a handy function that I normally make available for all my tests -// that deal with connected components. -// you can provide initialState for the entire store that the ui is rendered with -function renderWithRedux( +function render( ui, - { initialState, store = createStore(reducer, initialState) } = {} + { + initialState, + store = createStore(reducer, initialState), + ...renderOptions + } = {} ) { - return { - ...render({ui}), - // adding `store` to the returned utilities to allow us - // to reference it in our tests (just try to avoid using - // this to test implementation details). - store, + function Wrapper({ children }) { + return {children} } + return rtlRender(ui, { wrapper: Wrapper, ...renderOptions }) } +``` + +```jsx +// counter.test.js +import React from 'react' +import { createStore } from 'redux' +import { Provider } from 'react-redux' +import { fireEvent, screen } from '@testing-library/react' +// We're using our own custom render function and not RTL's render +import { render } from './test-utils.js +import '@testing-library/jest-dom/extend-expect' +import { initialState, reducer } from './reducer.js' +import Counter from './counter.js' test('can render with redux with defaults', () => { - const { getByTestId, getByText } = renderWithRedux() - fireEvent.click(getByText('+')) - expect(getByTestId('count-value')).toHaveTextContent('1') + renderWithRedux() + fireEvent.click(screen.getByText('+')) + expect(screen.getByRole('heading')).toHaveTextContent('1') }) test('can render with redux with custom initial state', () => { - const { getByTestId, getByText } = renderWithRedux(, { + renderWithRedux(, { initialState: { count: 3 }, }) - fireEvent.click(getByText('-')) - expect(getByTestId('count-value')).toHaveTextContent('2') + fireEvent.click(screen.getByText('-')) + expect(screen.getByRole('heading')).toHaveTextContent('2') }) test('can render with redux with custom store', () => { // this is a silly store that can never be changed const store = createStore(() => ({ count: 1000 })) - const { getByTestId, getByText } = renderWithRedux(, { + renderWithRedux(, { store, }) - fireEvent.click(getByText('+')) - expect(getByTestId('count-value')).toHaveTextContent('1000') - fireEvent.click(getByText('-')) - expect(getByTestId('count-value')).toHaveTextContent('1000') + fireEvent.click(screen.getByText('+')) + expect(screen.getByRole('heading')).toHaveTextContent('1000') + fireEvent.click(screen.getByText('-')) + expect(screen.getByRole('heading')).toHaveTextContent('1000') }) ``` From 1a775c6be2c29774c4c3642f6b9800c81ae8b888 Mon Sep 17 00:00:00 2001 From: Matan Borenkraout Date: Sun, 5 Apr 2020 09:30:46 +0300 Subject: [PATCH 2/3] Review fixes, removed the h1 and refactored the class component to a functional one --- docs/example-react-redux.md | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/docs/example-react-redux.md b/docs/example-react-redux.md index 74dd36565..c7ebb7d61 100644 --- a/docs/example-react-redux.md +++ b/docs/example-react-redux.md @@ -8,27 +8,25 @@ title: React Redux import React from 'react' import { connect } from 'react-redux' -class Counter extends React.Component { - increment = () => { - this.props.dispatch({ type: 'INCREMENT' }) +const Counter = ({ dispatch, count }) => { + const increment = () => { + dispatch({ type: 'INCREMENT' }) } - decrement = () => { - this.props.dispatch({ type: 'DECREMENT' }) + const decrement = () => { + dispatch({ type: 'DECREMENT' }) } - render() { - return ( + return ( +
+

Counter

-

Counter

-
- -

{this.props.count}

- -
+ + {count} +
- ) - } +
+ ) } export default connect(state => ({ count: state.count }))(Counter) @@ -100,7 +98,7 @@ import Counter from './counter.js' test('can render with redux with defaults', () => { renderWithRedux() fireEvent.click(screen.getByText('+')) - expect(screen.getByRole('heading')).toHaveTextContent('1') + expect(screen.getByTestId('count-value')).toHaveTextContent('1') }) test('can render with redux with custom initial state', () => { @@ -108,7 +106,7 @@ test('can render with redux with custom initial state', () => { initialState: { count: 3 }, }) fireEvent.click(screen.getByText('-')) - expect(screen.getByRole('heading')).toHaveTextContent('2') + expect(screen.getByTestId('count-value')).toHaveTextContent('2') }) test('can render with redux with custom store', () => { @@ -118,8 +116,8 @@ test('can render with redux with custom store', () => { store, }) fireEvent.click(screen.getByText('+')) - expect(screen.getByRole('heading')).toHaveTextContent('1000') + expect(screen.getByTestId('count-value')).toHaveTextContent('1000') fireEvent.click(screen.getByText('-')) - expect(screen.getByRole('heading')).toHaveTextContent('1000') + expect(screen.getByTestId('count-value')).toHaveTextContent('1000') }) ``` From ca746bb55df46bfff1d1f031c0b2780f885b3750 Mon Sep 17 00:00:00 2001 From: Matan Borenkraout Date: Mon, 6 Apr 2020 19:52:36 +0300 Subject: [PATCH 3/3] Added the export and updated the comment after kent's review --- docs/example-react-redux.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/example-react-redux.md b/docs/example-react-redux.md index c7ebb7d61..c759ec3ae 100644 --- a/docs/example-react-redux.md +++ b/docs/example-react-redux.md @@ -81,6 +81,12 @@ function render( } return rtlRender(ui, { wrapper: Wrapper, ...renderOptions }) } + +// re-export everything +export * from '@testing-library/react' + +// override render method +export { render } ``` ```jsx @@ -88,9 +94,11 @@ function render( import React from 'react' import { createStore } from 'redux' import { Provider } from 'react-redux' -import { fireEvent, screen } from '@testing-library/react' +import { } from '@testing-library/react' // We're using our own custom render function and not RTL's render -import { render } from './test-utils.js +// our custom utils also re-export everything from RTL +// so we can import fireEvent and screen here as well +import { render, fireEvent, screen } from './test-utils.js import '@testing-library/jest-dom/extend-expect' import { initialState, reducer } from './reducer.js' import Counter from './counter.js'