Skip to content

feat: wrap waitFor in (async) act to support async queries without explicit act call #344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 21, 2020
32 changes: 31 additions & 1 deletion src/waitFor.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
// @flow

import React from 'react';
import act from './act';
import { throwRemovedFunctionError } from './helpers/errors';

const DEFAULT_TIMEOUT = 4500;
const DEFAULT_INTERVAL = 50;

function checkReactVersionAtLeast(major: number, minor: number): boolean {
if (React.version === undefined) return false;
const [actualMajor, actualMinor] = React.version
.split('.')
.map((n) => parseInt(n, 10));

return actualMajor > major || (actualMajor == major && actualMinor >= minor);
}

export type WaitForOptions = {
timeout?: number,
interval?: number,
};

export default function waitFor<T>(
function waitForInternal<T>(
expectation: () => T,
options?: WaitForOptions
): Promise<T> {
Expand Down Expand Up @@ -38,6 +49,25 @@ export default function waitFor<T>(
});
}

export default async function waitFor<T>(
expectation: () => T,
options?: WaitForOptions
): Promise<T> {
if (!checkReactVersionAtLeast(16, 9)) {
return await waitForInternal(expectation, options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return await is redundant, you can omit the await

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed await

}

let result: T;

//$FlowFixMe: `act` has incorrect flow typing
await act(async () => {
result = await waitForInternal(expectation, options);
});

//$FlowFixMe: either we have result or `waitFor` threw error
return result;
}

export function waitForElement<T>(
expectation: () => T,
_timeout: number = 4500,
Expand Down
6 changes: 5 additions & 1 deletion website/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,10 @@ function waitFor<T>(

Waits for non-deterministic periods of time until your element appears or times out. `waitFor` periodically calls `expectation` every `interval` milliseconds to determine whether the element appeared or not.

:::info
In order to properly use `waitFor` you need at least React >=16.9.0 (featuring async `act`) or React Native >=0.60 (which comes with React >=16.9.0).
:::

```jsx
import { render, waitFor } from 'react-testing-library';

Expand Down Expand Up @@ -403,4 +407,4 @@ expect(submitButtons).toHaveLength(3); // expect 3 elements

## `act`

Useful function to help testing components that use hooks API. By default any `render`, `update`, and `fireEvent` calls are wrapped by this function, so there is no need to wrap it manually. This method is re-exported from [`react-test-renderer`](https://github.com/facebook/react/blob/master/packages/react-test-renderer/src/ReactTestRenderer.js#L567]).
Useful function to help testing components that use hooks API. By default any `render`, `update`, `fireEvent`, and `waitFor` calls are wrapped by this function, so there is no need to wrap it manually. This method is re-exported from [`react-test-renderer`](https://github.com/facebook/react/blob/master/packages/react-test-renderer/src/ReactTestRenderer.js#L567]).
4 changes: 4 additions & 0 deletions website/docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ This library has a peerDependencies listing for `react-test-renderer` and, of co

As you may have noticed, it's not tied to React Native at all – you can safely use it in your React components if you feel like not interacting directly with DOM.

:::info
In order to properly use helpers for async tests (`findBy` queries and `waitFor`) you need at least React >=16.9.0 (featuring async `act`) or React Native >=0.60 (which comes with React >=16.9.0).
:::

### Additional Jest matchers

In order to use addtional React Native-specific jest matchers from [@testing-library/jest-native](https://github.com/testing-library/jest-native) package add it to your project:
Expand Down
2 changes: 2 additions & 0 deletions website/docs/MigrationV2.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export default function waitFor<T>(

Both changes should improve code readibility.

`waitFor` calls (and hence also `findBy` queries) are now wrapped in `act` by default, so that you should no longer need to use `act` directly in your tests.

:::tip
You can usually avoid `waitFor` by a proper use of `findBy` asynchronous queries. It will result in more streamlined testing experience.
:::
Expand Down
4 changes: 4 additions & 0 deletions website/docs/Queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ title: Queries

`findAllBy` queries return a promise which resolves to an array when any matching elements are found. The promise is rejected if no elements match after a default timeout of 4500ms.

:::info
In order to properly use `findBy` and `findAllBy` queries you need at least React >=16.9.0 (featuring async `act`) or React Native >=0.60 (which comes with React >=16.9.0).
:::

:::info
`findBy` and `findAllBy` queries accept optional `waitForOptions` object argument which can contain `timeout` and `interval` properies which have the same meaning as respective options for [`waitFor`](https://callstack.github.io/react-native-testing-library/docs/api#waitfor) function.
:::
Expand Down