Mocking the fetching state of URQL in Jest

Viewed 1272

I'm trying to mock the fetching state in my tests. The state with data and state with an error were successfully mocked. Below you can find an example:

const createMenuWithGraphQL = (graphqlData: MockGraphQLResponse): [JSX.Element, MockGraphQLClient] => {
    const mockGraphQLClient = {
        executeQuery: jest.fn(() => fromValue(graphqlData)),
        executeMutation: jest.fn(() => never),
        executeSubscription: jest.fn(() => never),
    };

    return [
        <GraphQLProvider key="provider" value={mockGraphQLClient}>
            <Menu {...menuConfig} />
        </GraphQLProvider>,
        mockGraphQLClient,
    ];
};

it('displays submenu with section in loading state after clicking on an item with children', async () => {
    const [Component, client] = createMenuWithGraphQL({
        fetching: true,
        error: false,
    });
    const { container } = render(Component);
    const itemConfig = menuConfig.links[0];

    fireEvent.click(screen.getByText(label));

    await waitFor(() => {
        const alertItem = screen.getByRole('alert');

        expect(client.executeQuery).toBeCalledTimes(1);
        expect(container).toContainElement(alertItem);
        expect(alertItem).toHaveAttribute('aria-busy', 'false');
    });
});

The component looks like the following:

export const Component: FC<Props> = ({ label, identifier }) => {
    const [result] = useQuery({ query });

    return (
        <div role="group">
            {result.fetching && (
                <p role="alert" aria-busy={true}>
                   Loading
                </p>
            )}
            {result.error && (
                <p role="alert" aria-busy={false}>
                   Error
                </p>
            )}
            {!result.fetching && !result.error && <p>Has data</p>}
        </div>
    );
};

I have no idea what am I doing wrong. When I run the test it says the fetching is false. Any help would be appreciated.

1 Answers

maintainer here,

I think the issue here is that you are synchronously returning in executeQuery. Let's look at what happens.

  • Your component mounts and checks whether or not there is synchronous state in cache.
  • This is the case since we only do executeQuery: jest.fn(() => fromValue(graphqlData)),

We can see that this actually comes down to the same result as if the result would just come out of cache (we never need to hit the API so we never hit fetching).

We could solve this by adding a setTimeout, ... but Wonka (the streaming library used by urql) has built-in solutions for this.

We can utilise the delay operator to simulate a fetching state:

    const mockGraphQLClient = {
        executeQuery: jest.fn(() => pipe(fromValue(data), delay(100)),
    };

Now we can check the fetching state correctly in the first 100ms and after that we are able to utilise waitFor to check the subsequent state.

Related