I have made a dummy component for the purpose of illustration. Please find the code below-
Fetch.js
import React from "react";
import { useFetch } from "./useFetch";
const FetchPost = () => {
const { toShow, dummyAPICall } = useFetch();
return toShow ? (
<>
<button onClick={dummyAPICall} data-testid="fetch-result">
Fetch
</button>
</>
) : null;
};
export { FetchPost };
useFetch.js
import { useState } from "react";
const useFetch = () => {
const [toShow, setToShow] = useState(true);
const dummyAPICall = () => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => {
setToShow(false);
})
.catch(() => {
setToShow(true);
});
};
return {
toShow,
setToShow,
dummyAPICall,
};
};
export { useFetch };
I want to make an assertion here, that on click of the Fetch button my Fetch component shouldn't render on screen so, using React Testing Library, I am writing the test case like this:
import { fireEvent, render, screen } from "#testing-library/react";
import { FetchPost } from "../Fetch";
import { useFetch } from "../useFetch";
jest.mock("../useCounter");
describe("Use Fetch tests", () => {
it("Should fetch results and show/hide component", async () => {
useFetch.mockImplementation(() => {
return { toShow: true, dummyAPICall: jest.fn() };
});
render(<FetchPost></FetchPost>);
expect(screen.getByText(/fetch/i)).toBeInTheDocument();
fireEvent.click(screen.getByTestId("fetch-result"));
await waitFor(() =>
expect(screen.queryByText(/fetch/i)).not.toBeInTheDocument()
);
});
});
My assertion:
expect(screen.getByText(/fetch/i)).not.toBeInTheDocument();
is failing as the component is still present. How can I modify my test case to handle this?
Mock network IO side effect is better than mock useFetch hook. It seems you forget to import waitFor helper function from RTL package.
There are two options to mock network IO side effects.
msw, here is example
global.fetch = jest.fn(), don't need to install additional package and set up.
I am going to use option 2 to solve your question.
E.g.
fetch.jsx:
import React from 'react';
import { useFetch } from './useFetch';
const FetchPost = () => {
const { toShow, dummyAPICall } = useFetch();
return toShow ? (
<>
<button onClick={dummyAPICall} data-testid="fetch-result">
Fetch
</button>
</>
) : null;
};
export { FetchPost };
useFetch.js:
import { useState } from 'react';
const useFetch = () => {
const [toShow, setToShow] = useState(true);
const dummyAPICall = () => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => response.json())
.then((json) => {
setToShow(false);
})
.catch(() => {
setToShow(true);
});
};
return { toShow, setToShow, dummyAPICall };
};
export { useFetch };
fetch.test.jsx:
import React from 'react';
import { FetchPost } from './fetch';
import { fireEvent, render, screen, waitFor } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
describe('Use Fetch tests', () => {
it('Should fetch results and show/hide component', async () => {
const mResponse = { json: jest.fn() };
global.fetch = jest.fn().mockResolvedValueOnce(mResponse);
render(<FetchPost />);
expect(screen.getByText(/fetch/i)).toBeInTheDocument();
fireEvent.click(screen.getByTestId('fetch-result'));
await waitFor(() => expect(screen.queryByText(/fetch/i)).not.toBeInTheDocument());
});
});
Test result:
PASS examples/70666232/fetch.test.jsx (12.699 s)
Use Fetch tests
✓ Should fetch results and show/hide component (51 ms)
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 93.75 | 100 | 83.33 | 93.75 |
fetch.jsx | 100 | 100 | 100 | 100 |
useFetch.js | 90 | 100 | 80 | 90 | 12
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.38 s
package version:
"#testing-library/react": "^11.2.2",
"#testing-library/jest-dom": "^5.11.6",
"jest": "^26.6.3",
"react": "^16.14.0"
Related
iam having trouble with covering function inside react context
Before that, let me show you the snippet of the code
import {
useCallback,
useEffect,
useState,
createContext,
ReactNode,
FC
} from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import PropTypes from 'prop-types';
import { pushBackRoute } from '../libs/helpers/backRouteHelper';
export interface BottomBarProviderProps {
children: ReactNode;
}
export interface IBottomBarProvider {
selectedMenu: string;
handleClickMenu: (item: { name: string; route: string }) => void;
}
export const defaultValueBottomBarContext = {
selectedMenu: '',
handleClickMenu: () => {}
};
export const BottomBarContext = createContext<IBottomBarProvider>(
defaultValueBottomBarContext
);
export const BottomBarProvider: FC<BottomBarProviderProps> = ({
children,
...props
}) => {
const history = useHistory();
const location = useLocation();
const [selectedMenu, setSelectedMenu] = useState('Belanja');
const handleClickMenu = useCallback(
(item) => {
setSelectedMenu(item.name);
pushBackRoute(location.pathname);
history.push(item.route);
},
[selectedMenu, location?.pathname]
);
useEffect(() => {
Iif (location.pathname === '/marketplace/history') {
setSelectedMenu('Riwayat');
}
}, [location.pathname]);
const value: IBottomBarProvider = {
selectedMenu,
handleClickMenu
};
return (
<BottomBarContext.Provider
value={value}
{...props}
data-testid="bottom-bar-context"
>
{children}
</BottomBarContext.Provider>
);
};
BottomBarProvider.propTypes = {
children: PropTypes.node
};
BottomBarProvider.defaultProps = {
children: null
};
So i managed to cover most of the hooks and render, but the function/handle function inside this context is really hard
And this is my current test code, below :
/* eslint-disable jest/prefer-called-with */
import { render, screen } from '#testing-library/react';
import { BottomBarProvider } from './BottomBarContext';
import '#testing-library/jest-dom/extend-expect';
const mockHistoryPush = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: '/marketplace'
}),
useHistory: () => ({
push: mockHistoryPush
})
}));
describe('BottomBarProvider', () => {
it('should render the children and update the selected menu when handleClickMenu is called', () => {
//Arrange
const children = <div data-testid="children">Hello, World!</div>;
//Act
render(<BottomBarProvider>{children}</BottomBarProvider>);
//Assert
expect(screen.getByTestId('children')).toHaveTextContent('Hello, World!');
});
});
Do you guys have any idea how to cover the function inside this context ?
Mock functions, hooks, and components of the module you don't own are not recommended. The useLocation and useHistory hooks in your case. Incorrect mock will break their functions which causes your tests to pass based on incorrect mock implementation.
Use <MemoryRouter/> component for testing history change. See official example of RTL about how to test react router
You should also create a test component to consume the React Context for testing the <BottomBarProvider/> component and BottomBarContext. The key is to trigger the handleClickMenu event handler by firing a click event on some element of the test component.
E.g.
BottomBarContext.tsx:
import React from 'react';
import {
useCallback,
useEffect,
useState,
createContext,
ReactNode,
FC
} from 'react';
import { useHistory, useLocation } from 'react-router-dom';
// import { pushBackRoute } from '../libs/helpers/backRouteHelper';
export interface BottomBarProviderProps {
children: ReactNode;
}
export interface IBottomBarProvider {
selectedMenu: string;
handleClickMenu: (item: { name: string; route: string }) => void;
}
export const defaultValueBottomBarContext = {
selectedMenu: '',
handleClickMenu: () => { }
};
export const BottomBarContext = createContext<IBottomBarProvider>(
defaultValueBottomBarContext
);
export const BottomBarProvider: FC<BottomBarProviderProps> = ({
children,
...props
}) => {
const history = useHistory();
const location = useLocation();
const [selectedMenu, setSelectedMenu] = useState('Belanja');
const handleClickMenu = useCallback(
(item) => {
setSelectedMenu(item.name);
// pushBackRoute(location.pathname);
history.push(item.route);
},
[selectedMenu, location?.pathname]
);
useEffect(() => {
if (location.pathname === '/marketplace/history') {
setSelectedMenu('Riwayat');
}
}, [location.pathname]);
const value: IBottomBarProvider = {
selectedMenu,
handleClickMenu
};
return (
<BottomBarContext.Provider value={value} {...props} data-testid="bottom-bar-context">
{children}
</BottomBarContext.Provider>
);
};
BottomBarContext.test.tsx:
import { fireEvent, render, screen } from '#testing-library/react';
import '#testing-library/jest-dom';
import React, { useContext } from 'react';
import { MemoryRouter, Route, Switch } from 'react-router-dom';
import { BottomBarContext, BottomBarProvider } from './BottomBarContext';
describe('BottomBarProvider', () => {
test('should pass', async () => {
const TestComp = () => {
const bottomBarContext = useContext(BottomBarContext);
const menuItems = [
{ name: 'a', route: '/a' },
{ name: 'b', route: '/b' },
];
return (
<>
<ul>
{menuItems.map((item) => (
<li key={item.route} onClick={() => bottomBarContext.handleClickMenu(item)}>
{item.name}
</li>
))}
</ul>
<p>selected menu: {bottomBarContext.selectedMenu}</p>
<Switch>
{menuItems.map((item) => (
<Route key={item.route} path={item.route} component={() => <div>{item.name} component</div>} />
))}
</Switch>
</>
);
};
render(
<MemoryRouter initialEntries={['/']}>
<BottomBarProvider>
<TestComp />
</BottomBarProvider>
</MemoryRouter>
);
const firstListItem = screen.getAllByRole('listitem')[0];
fireEvent.click(firstListItem);
expect(screen.getByText('selected menu: a')).toBeInTheDocument();
expect(screen.getByText('a component')).toBeInTheDocument();
});
});
Test result:
PASS stackoverflow/74931928/BottomBarContext.test.tsx (16.574 s)
BottomBarProvider
✓ should pass (116 ms)
----------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------------|---------|----------|---------|---------|-------------------
All files | 95 | 66.67 | 75 | 94.74 |
BottomBarContext.tsx | 95 | 66.67 | 75 | 94.74 | 50
----------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 18.184 s, estimated 23 s
Coverage HTML reporter:
I am using mapDispatchToProps to make a dispatch call to an API in the useEffect of my functional component.
I am a little stuck as how to actually test this in my unit tests with React Testing Library.
I can pass my Redux store quite easily, however I don't think I've ever had to pass the dispatch before and I'm a little lost.
I did try to pass the dispatch function with my store, but this of course didn't work.
Component
const mapDispatchToProps = dispatch => ({
myDispatchFunction: () => dispatch(someDispatch())
});
const mapStateToProps = ({someStateProp}) => ({
myStateProp: !!someStateProp // This isn't important
});
const MyComp = ({myDispatchFunction}) => {
useEffect(() => {
!!myStateProp && myDispatchFunction();
}, []);
return ...
}
Test
it('Should trigger dispatch function on load', () => {
const mockFunc = jest.fn(); // My attempt at mocking the dispatch call
const store = {someStateProp: true, myDispatchFunction: mockFunc};
render(
<Provider store={mockStore(store)}>
<MyComponent />
</Provider>
);
expect(mockFunc).toHaveBeenCalled();
});
This fails...
Your code has no real meaning, but the same with the test method, try not to mock functions and modules that have no side effects, I/O operations, and use their original implementation.
For your example, don't mock mapStateToProps, mapDispatchToProps, and dispatch functions. We can create a test store, populate the test state data, and collect the dispatch actions in the component.
If your component uses some state slice, verify that your component is rendering correctly. This is also called behavior testing. This testing strategy is more robust if the component behaves correctly, regardless of whether your implementation changes.
Why not mock? Like mapStateToProps, where a mock implementation changes its behavior if you don't know how it is implemented, resulting in an error-based implementation of the test case. Your tests may pass but the actual code at runtime is not correct.
E.g.
index.tsx:
import { useEffect } from 'react';
import { connect } from 'react-redux';
const mapDispatchToProps = (dispatch) => ({
myDispatchFunction: () => dispatch({ type: 'SOME_ACTION' }),
});
const mapStateToProps = ({ someStateProp }) => ({
myStateProp: !!someStateProp,
});
const MyComp = ({ myDispatchFunction, myStateProp }) => {
useEffect(() => {
!!myStateProp && myDispatchFunction();
}, []);
return null;
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComp);
index.test.tsx:
import { render } from '#testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import MyComp from './';
describe('71555438', () => {
test('should pass', () => {
let dispatchedActions: any[] = [];
const store = createStore(function rootReducer(state = { someStateProp: 'fake value' }, action) {
if (!action.type.startsWith('##redux')) {
dispatchedActions.push(action);
}
return state;
});
render(
<Provider store={store}>
<MyComp />
</Provider>
);
expect(dispatchedActions).toEqual([{ type: 'SOME_ACTION' }]);
});
});
Test result:
PASS stackoverflow/71555438/index.test.tsx (9.562 s)
71555438
✓ should pass (16 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.tsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.565 s
Note: We should ignore the action dispatched by createStore internally and we only need to collect the actions dispatched by users(our code).
Having the following component:
import React, { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useRouteMatch, Link } from 'react-router-dom';
interface MyComponentProps {
myId?: string;
link?: string;
}
export const MyComponent: React.FunctionComponent<MyComponentProps> = ({
myId = 'default-id',
link,
children
}) => {
const [myOutlet, setMyOutlet] = useState<HTMLOListElement>();
const match = useRouteMatch();
useEffect(() => {
const outletElement = document.getElementById(myId) as HTMLOListElement;
if (outletElement) {
setMyOutlet(outletElement);
}
}, [myId]);
if (!myOutlet) {
return null;
}
return createPortal(
<li>
<Link to={link || match.url}>{children}</Link>
</li>,
myOutlet
);
};
export default MyComponent;
I want to write unit tests using React Testing Library for it, the problem is that it keeps throwing an error because of useRouteMatch.
Here is my code:
import { render, screen } from '#testing-library/react';
import { MyComponent } from './my-component';
describe('MyComponent', () => {
const testId = 'default-id';
const link = '/route';
it('should render MyComponent successfully', () => {
const element = render(<MyComponent myId={testId} link={link} />);
expect(element).toBeTruthy();
});
});
The error appears at the line with const match = useRouteMatch();, is there a way to include this part in the test?
You should use <MemoryRouter>:
A <Router> that keeps the history of your “URL” in memory (does not read or write to the address bar)
Provide mock locations in the history stack by using the initialEntries props.
Then, use <Route> component to render some UI when its path matches the current URL.
The following example, assuming that the location pathname in the browser current history stack is /one, <Route>'s path prop is also /one, The two matching, rendering MyComponent.
E.g.
my-component.tsx:
import React, { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useRouteMatch, Link } from 'react-router-dom';
interface MyComponentProps {
myId?: string;
link?: string;
}
export const MyComponent: React.FunctionComponent<MyComponentProps> = ({ myId = 'default-id', link, children }) => {
const [myOutlet, setMyOutlet] = useState<HTMLOListElement>();
const match = useRouteMatch();
console.log('match: ', match);
useEffect(() => {
const outletElement = document.getElementById(myId) as HTMLOListElement;
if (outletElement) {
setMyOutlet(outletElement);
}
}, [myId]);
if (!myOutlet) {
return null;
}
return createPortal(
<li>
<Link to={link || match.url}>{children}</Link>
</li>,
myOutlet
);
};
export default MyComponent;
my-component.test.tsx:
import React from 'react';
import { render, screen } from '#testing-library/react';
import { MyComponent } from './my-component';
import { MemoryRouter, Route } from 'react-router-dom';
describe('MyComponent', () => {
const testId = 'default-id';
const link = '/route';
it('should render MyComponent successfully', () => {
const element = render(
<MemoryRouter initialEntries={[{ pathname: '/one' }]}>
<Route path="/one">
<MyComponent myId={testId} link={link} />
</Route>
</MemoryRouter>
);
expect(element).toBeTruthy();
});
});
test result:
PASS examples/70077434/my-component.test.tsx (8.433 s)
MyComponent
✓ should render MyComponent successfully (46 ms)
console.log
match: { path: '/one', url: '/one', isExact: true, params: {} }
at MyComponent (examples/70077434/my-component.tsx:13:11)
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All files | 87.5 | 28.57 | 100 | 86.67 |
my-component.tsx | 87.5 | 28.57 | 100 | 86.67 | 18,26
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.951 s, estimated 9 s
package versions:
"react": "^16.14.0",
"react-router-dom": "^5.2.0"
I'm using NextJS and I'm trying to test the ClientPortal component. I'm using Jest and React Testing Library for testing.
Here's the ClientPortal component:
import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
export default function ClientPortal({ children, selector }) {
const ref = useRef();
const [mounted, setMounted] = useState(false);
useEffect(() => {
ref.current = document.querySelector(selector);
setMounted(true);
}, [selector]);
return mounted ? createPortal(children, ref.current) : null;
}
How can this be tested using Jest?
First, make sure the testEnvironment configuration is jsdom. For jestjs v26, it's jsdom by default. For jestjs v27, follow this guide to setup the testEnvironment configuration.
The test method is very straightforward. Create a DOM container to store the portal. Query the DOM element and assert whether it exists.
index.jsx:
import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
export default function ClientPortal({ children, selector }) {
const ref = useRef();
const [mounted, setMounted] = useState(false);
useEffect(() => {
ref.current = document.querySelector(selector);
setMounted(true);
}, [selector]);
return mounted ? createPortal(children, ref.current) : null;
}
index.test.jsx:
import React from 'react';
import { render, screen } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import ClientPortal from './';
function TestChild() {
return <div>child</div>;
}
describe('69550058', () => {
test('should pass', () => {
const main = document.createElement('main');
const portalContainer = document.createElement('div');
portalContainer.id = 'portal-container';
document.body.appendChild(portalContainer);
const { container } = render(
<ClientPortal selector={'#portal-container'}>
<TestChild />
</ClientPortal>,
{ container: document.body.appendChild(main) }
);
expect(screen.getByText(/child/)).toBeInTheDocument();
expect(portalContainer.innerHTML).toEqual('<div>child</div>');
expect(container).toMatchInlineSnapshot(`<main />`);
});
});
test result:
PASS examples/69550058/index.test.jsx (8.941 s)
69550058
✓ should pass (33 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.jsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 9.624 s, estimated 11 s
package versions:
"jest": "^26.6.3",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"#testing-library/react": "^11.2.2",
So I was trying to create a unit test using jest in ReactJS. The Unit test itself just to verify if the function (from action) has been called
I already tried to mock the function, but the result tells that I must mock the function
Here the code of the function that I want to create a unit test
import { testfunction } from '../../actions/auth';
handleSubmit(userParams) {
this.setState({ form: { ...this.state.form, isFetching: true } });
this.props.dispatch(testfunction(userParams,
this.successCallback.bind(this), this.errorCallback.bind(this)));
}
and for the unit test
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
import Login from '../../../components/auth/Login';
const mockStore = configureStore([thunk]);
const initialState = {
history: { },
};
const store = mockStore(initialState);
let wrapper;
let history;
let testfunction;
beforeEach(() => {
testfunction= jest.fn();
history = { push: jest.fn() };
wrapper = shallow(
<Login
history={history}
store={store}
testfunction={testfunction}
/>
);
});
describe('handleSubmit()', () => {
test('should call testfunction props', () => {
const component = wrapper.dive();
const instance = component.instance();
const sampleUserParams = {
email: 'test#test.com',
password: 'samplePassword123',
};
instance.handleSubmit(sampleUserParams);
expect(testfunction).toHaveBeenCalled();
});
});
I just want to check if the "testfunction" is called when I called handleSubmit function. But the error message is:
"Expected mock function to have been called."
it feels my way to mock the function is wrong. Does anyone know how to correct way to test that function?
Here is the solution:
index.tsx:
import React, { Component } from 'react';
import { testfunction } from './testfunction';
class Login extends Component<any, any> {
constructor(props) {
super(props);
this.state = {
form: {}
};
}
public render() {
const userParams = {};
return (
<div className="login">
<form onSubmit={() => this.handleSubmit(userParams)}>some form</form>
</div>
);
}
private handleSubmit(userParams) {
this.setState({ form: { ...this.state.form, isFetching: true } });
this.props.dispatch(testfunction(userParams, this.successCallback.bind(this), this.errorCallback.bind(this)));
}
private successCallback() {
console.log('successCallback');
}
private errorCallback() {
console.log('errorCallback');
}
}
export { Login };
testFunction.ts:
async function testfunction(userParams, successCallback, errorCallback) {
return {
type: 'ACTION_TYPE',
payload: {}
};
}
export { testfunction };
Unit test:
import React from 'react';
import { shallow } from 'enzyme';
import { Login } from './';
import { testfunction } from './testfunction';
jest.mock('./testfunction.ts');
describe('Login', () => {
const dispatch = jest.fn();
const sampleUserParams = {
email: 'test#test.com',
password: 'samplePassword123'
};
it('handleSubmit', () => {
const wrapper = shallow(<Login dispatch={dispatch} />);
expect(wrapper.is('.login')).toBeTruthy();
expect(wrapper.find('form')).toHaveLength(1);
wrapper.find('form').simulate('submit');
const cmpInstance = wrapper.instance();
expect(dispatch).toBeCalledWith(
// tslint:disable-next-line: no-string-literal
testfunction(sampleUserParams, cmpInstance['successCallback'], cmpInstance['errorCallback'])
);
// tslint:disable-next-line: no-string-literal
expect(testfunction).toBeCalledWith(sampleUserParams, cmpInstance['successCallback'], cmpInstance['errorCallback']);
});
});
Unit test with coverage report:
PASS src/stackoverflow/57847401/index.spec.tsx
Login
✓ handleSubmit (22ms)
-----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files | 86.36 | 100 | 62.5 | 85 | |
index.tsx | 90 | 100 | 71.43 | 88.89 | 27,30 |
testfunction.ts | 50 | 100 | 0 | 50 | 2 |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.201s, estimated 4s
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57847401