Use render() method of react-dom module to render your component into document provided by js-dom.
Use document.querySelector('button') to get the button dom, dispatch a mouse click event.
Use jest.useFakeTimers() to use fake versions of the standard timer functions(setTimeout).
After dispatching the click event, use jest.advanceTimersByTime(2000) to executes the macro task queue (tasks queued by setTimeout).
Then, make an assertion.
E.g.
App.jsx:
import React from 'react';
import Analytics from './Analytics';
export function Login({ onLoginSuccess }) {
setTimeout(function () {
console.log('TIMEOUT OVER');
onLoginSuccess();
}, 2000);
}
function App() {
function handleClick() {
console.log('Login');
Login({
onLoginSuccess: onSuccess,
});
}
function onSuccess() {
console.log('Login success');
Analytics.analyze();
}
return (
<>
<button
onClick={() => {
handleClick();
}}
>
Login
</button>
</>
);
}
export default App;
App.test.jsx:
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { act } from 'react-dom/test-utils';
import App from './App';
import Analytics from './Analytics';
describe('68400320', () => {
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
test('should pass', () => {
const analyzeSpy = jest.spyOn(Analytics, 'analyze');
jest.useFakeTimers();
act(() => {
render(<App />, container);
});
const button = document.querySelector('button');
act(() => {
button?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
jest.advanceTimersByTime(2000);
});
expect(analyzeSpy).toBeCalledTimes(1);
});
});
test result:
PASS examples/68400320/App.test.jsx (8.12 s)
68400320
✓ should pass (52 ms)
console.log
Login
at handleClick (examples/68400320/App.jsx:13:13)
console.log
TIMEOUT OVER
at examples/68400320/App.jsx:6:13
console.log
Login success
at onSuccess (examples/68400320/App.jsx:19:13)
console.log
Analysis done
at Object.analyze (examples/68400320/Analytics.js:3:13)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
Analytics.js | 100 | 100 | 100 | 100 |
App.jsx | 100 | 100 | 100 | 100 |
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.666 s, estimated 10 s
Ran all test suites related to changed files.
analyzewas called then you need to renderAppand simulate clicking the button to kick off the chain of callbacks. To be clear, this isn't a unit test, but more an integration test.react-testing-library, then you render theAppcomponent,clickthe button then you'll have youronSuccesscalled to see if the spy was called or not.