vydimitrov / react-countdown-circle-timer

Lightweight React/React Native countdown timer component with color and progress animation based on SVG
MIT License
692 stars 87 forks source link

Cannot run countdown in a test (vitest) #221

Closed AldeonMoriak closed 2 years ago

AldeonMoriak commented 2 years ago

Hello, these few days I've been trying to test a component which in it I have a countdown but I couldn't make countdown run in it. First I noticed it was a problem with not letting vitest know DOM has been updated so I added act to let it know. And I tested it by using a setInterval in a test component. But still countdown doesn't work in a test.

I've created a minimal reproduction of the issue in here.

The component:

import React, { useEffect, useState } from 'react';

import { CountdownCircleTimer } from 'react-countdown-circle-timer';

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setInterval(() => {
      setCount((v) => v + 3);
    }, 1000);
  }, []);
  return (
    <>
      <div>{count}</div>
      <CountdownCircleTimer
        isPlaying
        duration={7}
        colors={['#004777', '#F7B801', '#A30000', '#A30000']}
        colorsTime={[7, 5, 2, 0]}
      >
        {({ remainingTime }) => remainingTime}
      </CountdownCircleTimer>
    </>
  );
}

the test:

test('should render correctly', () => {
  vi.useFakeTimers();
  render(<App />);
  act(() => {
    vi.advanceTimersByTime(2000);
  });
  expect(screen.getByText('2')).toBeDefined();
});

In this test count value gets updated but the countdown doesn't.

vydimitrov commented 2 years ago

Hey, the timer is using requestAnimationFrame internally to measure the time and it seems useFakeTimers is not mocking that. You will need to find other way to test it.

AldeonMoriak commented 2 years ago

Thank you. I was able to find a solution for that:

let count = 0;
vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) =>
    setTimeout(() => cb(1000 * ++count), 100)
);
act(() => {
    vi.advanceTimersByTime(6100);
});