fireEvent doesn't click button on React component

1.6k views Asked by At

I'm writing tests for a React component. It's a timer that starts counting down when you press a button, then stops when you press the same button. I have a test case that tries to press the pause button, wait a second, then press the pause button again, checking the timer to make sure that a second has elapsed:

Timer.test.js

render(<Timer />)
const pauseButton = screen.getByText('pause')
const timerOutput = screen.getAllByRole('heading')[1]

describe('Timer', () => {
  test('Timer starts counting down when unpaused', done => {
    function fetchTime(callback) {
      fireEvent.click(pauseButton)
      setTimeout(
        fireEvent.click(pauseButton),
        1250
      )
      return callback(timerOutput)
    }

    function callback(data) {
      try {
        expect(data).toHaveTextContent('24:59')
        done()
      } catch(error) {
        done(error)
      }
    }

    fetchTime(callback)
  })
})

The problem is, the test doesn't seem to be hitting click on pauseButton the way I want it to. Jest tells me in the terminal when I run my test that timerOutput turns out to be '25:00' rather than '24:59', and it seems as if the component failed the test. But this is a problem with the test, not the component; when I run the app in my browser and press the button myself, it works the way it should. How do I get this test to work properly, and hit the buttons the way I want it to?

1

There are 1 answers

0
Agustin Moles On BEST ANSWER

It's difficult to have an accurate answer with few information about the component itself.

First I would recommend use async arrow function inside test() whenever you need to handle async calls so you don't rely in callback hell.

Besides that, I would try to use jest.useFakeTimers() so you can advance the setTimeout timer in order to test properly. It seems that your second fireEvent.click never gets fired since the test checks it synchronously.

And I just noticed you requested the timerOutput at first but didn't request it after the click events.

I would suggest something like:

test('Timer starts counting down when unpaused', async () => {
      jest.useFakeTimers();
      fireEvent.click(pauseButton)
      setTimeout(
        () => {fireEvent.click(pauseButton)},
        1250
      )
      jest.runPendingTimers(); // This would run the above function
   
        expect(screen.getAllByRole('heading')[1]).toHaveTextContent('24:59')
    }
  })

Indeed, the expect statement would be better from a user perspective assertion, like:

expect(screen.getByText("24:59")).toBeVisible();

Since you don't matter about the HTML elements that contains that text content