preact-hooks-testing-library icon indicating copy to clipboard operation
preact-hooks-testing-library copied to clipboard

waitForNextUpdate times out with async function

Open mischnic opened this issue 4 years ago • 1 comments

import React from "preact/compat";
import { act, renderHook } from "@testing-library/preact-hooks";

// works:
// import React from "react";
// import { act, renderHook } from "@testing-library/react-hooks";

function useTest() {
  let [state, setState] = React.useState(1);

  return {
    value: state,
    async increment() {
      await new Promise((res) => setTimeout(res, 100));
      setState((i) => i + 1);
    },
  };
}

test("test", async () => {
  let { result, waitForNextUpdate } = renderHook(() => useTest());

  expect(result.current.value).toBe(1);
  await act(async () => {
    result.current.increment();
    await waitForNextUpdate();
  });
  expect(result.current.value).toBe(2);
});

Without await waitForNextUpdate(), the second assertion fails. With that call, Jest times out (this works as expected when using React).

mischnic avatar Sep 09 '20 15:09 mischnic

Same problem here, but with events:

import { useEffect, useState } from 'preact/hooks';

const useWindowFocus = () => {
  const [visible, setVisible] = useState('visible');
  useEffect(() => {
    const handleVisibilityChange = () => {
      setVisible(() => {
        console.log('CALLED');
        return document.visibilityState;
      });
    };
    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
  }, []);

  return visible === 'visible';
};

export { useWindowFocus };

import { renderHook, act } from '@testing-library/preact-hooks';
import { useWindowFocus } from '../use-window-focus';

Object.defineProperty(document, 'visibilityState', { value: 'visible', configurable: true, writable: true });

describe('useWindowFocus', () => {
  test('given that the visibilitychange event was triggered: should change its value accordingly', async () => {
    const { result, waitForNextUpdate } = renderHook(() => useWindowFocus());

    expect(result.current.isFocused).toBe(true);

    document.visibilityState = 'hidden';
    act(() => { document.dispatchEvent(new Event('visibilitychange')) });
    await waitForNextUpdate();
    expect(result.current.isFocused).toBe(false);

    //     document.visibilityState = 'visible';
    //     act(() => { document.dispatchEvent(new Event('visibilitychange')) });
    //     expect(result.current).toBe(true);
  });
});

EDIT: If the hook returns an object instead of a value it works. Here's the new code:
import { useEffect, useState } from 'preact/hooks';

const useWindowFocus = () => {
  const [visible, setVisible] = useState('visible');
  useEffect(() => {
    const handleVisibilityChange = () => setVisible(document.visibilityState);
    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
  }, []);

  return { isFocused: visible === 'visible' };
};

describe('useWindowFocus', () => {
  test('given that the visibilitychange event was triggered: should change its value accordingly', async () => {
    const { result } = renderHook(() => useWindowFocus());

    expect(result.current.isFocused).toBe(true);

    document.visibilityState = 'hidden';
    act(() => { document.dispatchEvent(new Event('visibilitychange')) });
    expect(result.current.isFocused).toBe(false);

    document.visibilityState = 'visible';
    act(() => { document.dispatchEvent(new Event('visibilitychange')) });
    expect(result.current.isFocused).toBe(true);
  });
});

brunoti avatar Dec 15 '20 19:12 brunoti