I'm trying to test a component where its value changes based on the result of a promise from a callback that is called after a certain duration.
My component looks like this. I have verified that it works as expected in my browser.
import React, { useEffect, useState } from 'react';
export interface TestComponentProps {
  callback: () => Promise<string>
}
const TestComponent: React.FC<TestComponentProps> = ({
  callback,
}: TestComponentProps) => {
  const [textDisplay, setTextDisplay] = useState('not resolved');
  const [timeLeft, setTimeLeft] = useState(1000);
  const [timerDone, setTimerDone] = useState(false);
  const timeStart = Date.now();
  const receiveText = async (text: Promise<string>) => {
    setTextDisplay(await text);
  }
  useEffect(() => {
    if (timerDone) {
      return () => undefined;
    }
    // Update screen every 50 ms
    const interval = setInterval(() => {
      const newTimeLeft = 1000 - Date.now() + timeStart;
      setTimeLeft(newTimeLeft);
      if (newTimeLeft <= 0) {
        setTimerDone(true);
        receiveText(callback());
      }
    }, 50);
    return () => clearInterval(interval);
  }, [timerDone]);
  if (!timerDone) {
    return (
    <p>{timeLeft} ms to go</p>
    );
  } else {
    return (
      <p>{textDisplay}</p>
    )
  }
};
export default TestComponent;
My test looks like this:
import { render, screen, act } from '@testing-library/react';
import React from 'react';
import TestComponent from './TestComponent';
it('renders the required information after the promise resolves', () => {
  jest.useFakeTimers();
  const callback = jest.fn().mockResolvedValue('Hello world');
  render(<TestComponent callback={callback} />);
  // Perform actions to update the state before waiting for the timer as required
  // ...
  // Now wait for the timer to finish - give it an extra 50ms just in case
  act(() => jest.advanceTimersByTime(1050));
  // The promise should have resolved and the text should have appeared in the document
  expect(screen.getByText('Hello world')).toBeInTheDocument();
  // The promise only resolves HERE
  // after the test will already have failed
  jest.clearAllTimers();
  jest.useRealTimers();
});
I cannot await new Promise(process.nextTick) (as per this question) as this combined with fake timers causes a deadlock.
I have tried instead using waitFor(() => expect(callback).toBeCalled()); as per this question, but it also fails the test - the breakpoints in my code that call the callback are never hit.
How can I ensure that the promise resolves and the component rerenders before I look for the text in the component whilst using fake timers?
