├── .gitignore
├── .prettierrc
├── README.md
├── package.json
├── public
└── index.html
└── src
├── __tests__
├── isolated-and-combined.js
├── isolated-with-before-each.js
├── isolated-with-render-fn.js
└── not-isolated.js
├── counter.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | yarn.lock
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": false,
6 | "singleQuote": true,
7 | "trailingComma": "all",
8 | "bracketSpacing": false,
9 | "jsxBracketSameLine": false
10 | }
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-test-isolation
2 |
3 | This is an example of how to isolate your tests and also how to combine your tests into a more cohesive test that focuses on use cases rather than functionality.
4 |
5 | I created it for [my newsletter](http://kcd.im/news).
6 |
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-test-isolation",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "@testing-library/react": "3.1.7",
9 | "@testing-library/jest-dom": "1.3.1",
10 | "react": "16.9.0",
11 | "react-dom": "16.9.0",
12 | "react-scripts": "3.0.1"
13 | },
14 | "devDependencies": {},
15 | "eslintConfig": {
16 | "extends": "react-app"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test --env=jsdom",
22 | "eject": "react-scripts eject"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | React App
24 |
25 |
26 |
27 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/__tests__/isolated-and-combined.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect'
2 | import React from 'react'
3 | import {render, fireEvent} from '@testing-library/react'
4 | import {Counter} from '../counter'
5 |
6 | test('allows clicks until the maxClicks is reached, then requires a reset', () => {
7 | const {getByText} = render()
8 | const counterButton = getByText(/^count/i)
9 |
10 | // the counter is initialized to the initialCount
11 | expect(counterButton).toHaveTextContent(/3/)
12 |
13 | // when clicked, the counter increments the click
14 | fireEvent.click(counterButton)
15 | expect(counterButton).toHaveTextContent(/4/)
16 |
17 | // the counter button is disabled when it's hit the maxClicks
18 | expect(counterButton).toHaveAttribute('disabled')
19 | // the counter button no longer increments the count when clicked.
20 | fireEvent.click(counterButton)
21 | expect(counterButton).toHaveTextContent(/4/)
22 |
23 | // the reset button has been rendered and is clickable
24 | fireEvent.click(getByText(/reset/i))
25 |
26 | // the counter is reset to the initialCount
27 | expect(counterButton).toHaveTextContent(/3/)
28 |
29 | // the counter can be clicked and increment the count again
30 | fireEvent.click(counterButton)
31 | expect(counterButton).toHaveTextContent(/4/)
32 | })
33 |
--------------------------------------------------------------------------------
/src/__tests__/isolated-with-before-each.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect'
2 | import React from 'react'
3 | import {render, fireEvent} from '@testing-library/react'
4 | import {Counter} from '../counter'
5 |
6 | let getByText, counterButton
7 |
8 | beforeEach(() => {
9 | const utils = render()
10 | getByText = utils.getByText
11 | counterButton = utils.getByText(/^count/i)
12 | })
13 |
14 | test('the counter is initialized to the initialCount', () => {
15 | expect(counterButton).toHaveTextContent(/3/)
16 | })
17 |
18 | test('when clicked, the counter increments the click', () => {
19 | fireEvent.click(counterButton)
20 | expect(counterButton).toHaveTextContent(/4/)
21 | })
22 |
23 | test(`the counter button is disabled when it's hit the maxClicks`, () => {
24 | fireEvent.click(counterButton)
25 | expect(counterButton).toHaveAttribute('disabled')
26 | })
27 |
28 | test(`the counter button does not increment the count when clicked when it's hit the maxClicks`, () => {
29 | fireEvent.click(counterButton)
30 | fireEvent.click(counterButton)
31 | expect(counterButton).toHaveTextContent(/4/)
32 | })
33 |
34 | test(`the reset button has been rendered and resets the count when it's hit the maxClicks`, () => {
35 | fireEvent.click(counterButton)
36 | fireEvent.click(getByText(/reset/i))
37 | expect(counterButton).toHaveTextContent(/3/)
38 | })
39 |
--------------------------------------------------------------------------------
/src/__tests__/isolated-with-render-fn.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect'
2 | import React from 'react'
3 | import {render, fireEvent} from '@testing-library/react'
4 | import {Counter} from '../counter'
5 |
6 | function renderCounter(props) {
7 | const utils = render()
8 | const counterButton = utils.getByText(/^count/i)
9 | return {...utils, counterButton}
10 | }
11 |
12 | test('the counter is initialized to the initialCount', () => {
13 | const {counterButton} = renderCounter()
14 | expect(counterButton).toHaveTextContent(/3/)
15 | })
16 |
17 | test('when clicked, the counter increments the click', () => {
18 | const {counterButton} = renderCounter()
19 | fireEvent.click(counterButton)
20 | expect(counterButton).toHaveTextContent(/4/)
21 | })
22 |
23 | test(`the counter button is disabled when it's hit the maxClicks`, () => {
24 | const {counterButton} = renderCounter({
25 | maxClicks: 4,
26 | initialCount: 4,
27 | })
28 | expect(counterButton).toHaveAttribute('disabled')
29 | })
30 |
31 | test(`the counter button does not increment the count when clicked when it's hit the maxClicks`, () => {
32 | const {counterButton} = renderCounter({
33 | maxClicks: 4,
34 | initialCount: 4,
35 | })
36 | fireEvent.click(counterButton)
37 | expect(counterButton).toHaveTextContent(/4/)
38 | })
39 |
40 | test(`the reset button has been rendered and resets the count when it's hit the maxClicks`, () => {
41 | const {getByText, counterButton} = renderCounter()
42 | fireEvent.click(counterButton)
43 | fireEvent.click(getByText(/reset/i))
44 | expect(counterButton).toHaveTextContent(/3/)
45 | })
46 |
--------------------------------------------------------------------------------
/src/__tests__/not-isolated.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect'
2 | import React from 'react'
3 | // note: importing from `pure` so we don't get an automatic cleanup between tests
4 | import {render, fireEvent} from '@testing-library/react/pure'
5 | import {Counter} from '../counter'
6 |
7 | const {getByText} = render()
8 | const counterButton = getByText(/^count/i)
9 |
10 | test('the counter is initialized to the initialCount', () => {
11 | expect(counterButton).toHaveTextContent(/3/)
12 | })
13 |
14 | test('when clicked, the counter increments the click', () => {
15 | fireEvent.click(counterButton)
16 | expect(counterButton).toHaveTextContent(/4/)
17 | })
18 |
19 | test(`the counter button is disabled when it's hit the maxClicks`, () => {
20 | fireEvent.click(counterButton)
21 | expect(counterButton).toHaveAttribute('disabled')
22 | })
23 |
24 | test(`the counter button does not increment the count when clicked when it's hit the maxClicks`, () => {
25 | expect(counterButton).toHaveTextContent(/4/)
26 | })
27 |
28 | test(`the reset button has been rendered and resets the count when it's hit the maxClicks`, () => {
29 | fireEvent.click(getByText(/reset/i))
30 | expect(counterButton).toHaveTextContent(/3/)
31 | })
32 |
--------------------------------------------------------------------------------
/src/counter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Counter(props) {
4 | const initialProps = React.useRef(props).current
5 | const {initialCount = 0, maxClicks = 3} = props
6 |
7 | const [count, setCount] = React.useState(initialCount)
8 | const tooMany = count >= maxClicks
9 |
10 | const handleReset = () => setCount(initialProps.initialCount)
11 | const handleClick = () => setCount(currentCount => currentCount + 1)
12 |
13 | return (
14 |
15 |
18 | {tooMany ? : null}
19 |
20 | )
21 | }
22 |
23 | export {Counter}
24 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | function App() {
5 | return 'checkout the tests'
6 | }
7 |
8 | ReactDOM.render(, document.getElementById('root'))
9 |
--------------------------------------------------------------------------------