How to check what react component displays after delay in a test

Viewed 2287

I was wondering how to test what does interval dependant timer display after a few ticks with React Testing Library and Jest. Assume we have such code:

import React, { Component } from 'react';

let timer;

class Test extends Component {
    constructor() {
        super();
        this.state = {
            timeLeft: 60
        }
    }

    componentDidMount() {
        this.handleTick();
    }

    componentDidUpdate() {
        const { timeLeft } = this.state;
        if (!timeLeft) {
            clearInterval(timer);
            this.setState({
                timeLeft: 60,
            })
        }
    }

    handleTick() {
        timer = setInterval(() => {
            this.setState({timeLeft: this.state.timeLeft - 1 })
        },1000)
    }

    render() {
        return (
            <React.Fragment>
                <h3>I'm test.</h3>
                <h4>{this.state.timeLeft}</h4>
            </React.Fragment>
        )
    }
}

And now in test we want to check if Test component shows exactly what we want after let's say 15 sec. I have tried:

describe('Test Component', () => {
  test('Timer should display 45 sec left', () => {
    jest.useFakeTimers();
    const { getByText } = render(<Test />);
    setTimeout(() => {
      expect(getByText('45')).toBeInTheDocument();
    }, 15000);
    jest.runAllTimers();
  });
});

It pass the test, but if we change code line from

expect(getByText('45')).toBeInTheDocument();

to

expect(getByText('55')).toBeInTheDocument();

it passes to... So it seams that it doesn't work as I was expected. Do you have any ideas how to write this test properly? Of course I don't want to delay my tests.

1 Answers

You can use jest.advanceTimersByTime(num) to advance by num milliseconds to the future. Remember to wrap the code above in act() if the component state is updated in that time so React can update the state properly before the assertion.

test('Timer should display 45 sec left', () => {
  jest.useFakeTimers();
  const { getByText } = render(<Test />);

  act(() => {
    jest.advanceTimersByTime(1500);
  })
  expect(getByText('45')).toBeInTheDocument();
})
Related