jest-react-hooks-shallow icon indicating copy to clipboard operation
jest-react-hooks-shallow copied to clipboard

withHooks and withoutHooks do not support async tests / promises

Open d-luk opened this issue 4 years ago • 12 comments

Thanks for this package. I'm currently trying to get this to work with an async test, but that doesn't seem to be supported yet. Could this be added?

Example usage:

it('does something async', async () => {
    await withHooks(async () => {
        const wrapper = shallow(<App />);

        await something();
    });
});

I'm currently using this as a workaround:

export default function withHooksAsync(test: () => Promise<void>): Promise<void> {
    return new Promise<void>((resolve, reject) => {
        withHooks(() => {
            test().then(resolve).catch(reject);
        });
    });
}

d-luk avatar Jul 29 '20 11:07 d-luk

Hi @d-luk ,

Thanks for reporting this. I'll have a look!

mikeborozdin avatar Aug 01 '20 09:08 mikeborozdin

Did you by any chance found a solution for this? I'm stuck at the same problem.

dunklesToast avatar Oct 05 '20 22:10 dunklesToast

Hi @dunklesToast ,

Sorry, I've been busy with work. I'll have a look at it tonight.

mikeborozdin avatar Oct 06 '20 08:10 mikeborozdin

Okay awesome. I tried to understand the library yesterday to create a Pull Request but unfortunately did not :( If you could also add some comments that would be awesome 👍🏻

dunklesToast avatar Oct 06 '20 08:10 dunklesToast

@d-luk , @dunklesToast ,

Could you possibly provide a more specific example that outlines:

  • expected behaviour
  • actual behaviour / error, if any

@d-luk,

Looking at your code snippet, it's hard to say what was expected and what went wrong. I tried a similar example - and it worked.

mikeborozdin avatar Oct 06 '20 20:10 mikeborozdin

Hey!

I’ll send you the snippet tomorrow. It could also be that I have a wrong understanding of what this library does but I guess we’ll see tomorrow

dunklesToast avatar Oct 06 '20 20:10 dunklesToast

So let's say you've got this component:

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

function someAsyncCode() {
    return new Promise((resolve) =>
        setTimeout(() => {
            console.log('Resolving');
            resolve(true);
        }, 3000)
    );
}

export default function Test(): JSX.Element {
    const [text, setText] = useState('Not Effected');

    useEffect(() => {
        someAsyncCode().then(() => {
            console.log('Hooked');
            setText('Effected');
        });
    }, []);

    return <p>{text}</p>;
}

This will show the Text "Effected" after three seconds (i sped up the video):

example

If I use this test-suite: Test Setup

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import enableHooks from 'jest-react-hooks-shallow';

configure({ adapter: new Adapter() });
enableHooks(jest);

Component.test.tsx

import * as React from 'react';
import { mount } from 'enzyme';
import Test from './Test';

describe('Test Test', () => {
    const wrapper = mount(<Test />);
    it('Should render private route', async () => {
        console.log(wrapper.html());
        expect(wrapper..text).toBe("Effected");
    });
});

The expected result is that the test succeeds and it'll wait for the useEffect to run through. Unfortunately, it does not.

Could you have a look at this?

dunklesToast avatar Oct 07 '20 08:10 dunklesToast

+1! As a workaround, disableHooks and reenableHooks do the trick.

it("does what it should do", async () => {
  disableHooks();
  await ...
  reenableHooks();
});

kajoik avatar Sep 24 '21 13:09 kajoik

+1! As a workaround, disableHooks and reenableHooks do the trick.

it("does what it should do", async () => {
  disableHooks();
  await ...
  reenableHooks();
});

But it's recommended not to use it.

@dunklesToast have you found a solution for this?

orlandovallejos avatar Jan 03 '22 00:01 orlandovallejos

I have a need for the proposed solution as well.

syang-greatvines avatar Jan 15 '22 03:01 syang-greatvines

Here's my workaround for the time being:

async function withoutHooksPromise(fn) {
	const mockedConsole = jest.spyOn(console, 'warn').mockImplementation();
	disableHooks();
	await fn().finally(() => {
		reenableHooks();
		mockedConsole.mockRestore();
	});
}

syang-greatvines avatar Jan 15 '22 03:01 syang-greatvines

Is there any news related to this? It is a crucial functionality, as often there will be async calls being ran in useEffect. A very "cheap" workaround would be to wrap what you expect to happen after a mock promise resolves in a "setTimeout" set to run after zero milliseconds, this will run the code after the jest mock promise resolves, sort of like "awaiting".

radu1337 avatar Jun 07 '22 13:06 radu1337