Skip to content

Commit

Permalink
Merge pull request #408 from testing-library/pr/wait-intervals
Browse files Browse the repository at this point in the history
Add interval to async utilities to supplement post render checks
  • Loading branch information
mpeyper committed Jul 23, 2020
2 parents b7a2103 + 1ddafa2 commit 50c9720
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 35 deletions.
77 changes: 59 additions & 18 deletions docs/api-reference.md
Expand Up @@ -152,50 +152,92 @@ variable to `true` before importing `@testing-library/react-hooks` will also dis
### `waitForNextUpdate`
```js
function waitForNextUpdate(options?: WaitOptions): Promise<void>
function waitForNextUpdate(options?: {
timeout?: number
}): Promise<void>
```

Returns a `Promise` that resolves the next time the hook renders, commonly when state is updated as
the result of an asynchronous update.

See the [`wait` Options](/reference/api#wait-options) section for more details on the available
`options`.
#### `timeout`

### `wait`
The maximum amount of time in milliseconds (ms) to wait. By default, no timeout is applied.

### `waitFor`

```js
function wait(callback: function(): boolean|void, options?: WaitOptions): Promise<void>
function waitFor(callback: function(): boolean|void, options?: {
interval?: number,
timeout?: number,
suppressErrors?: boolean
}): Promise<void>
```

Returns a `Promise` that resolves if the provided callback executes without exception and returns a
truthy or `undefined` value. It is safe to use the [`result` of `renderHook`](/reference/api#result)
in the callback to perform assertion or to test values.

The callback is tested after each render of the hook. By default, errors raised from the callback
will be suppressed (`suppressErrors = true`).
#### `interval`

See the [`wait` Options](/reference/api#wait-options) section for more details on the available
`options`.
The amount of time in milliseconds (ms) to wait between checks of the callback if no renders occur.
Interval checking is disabled if `interval` is not provided in the options or provided as a `falsy`
value. By default, it is disabled.

#### `timeout`

The maximum amount of time in milliseconds (ms) to wait. By default, no timeout is applied.

#### `suppressErrors`

If this option is set to `true`, any errors that occur while waiting are treated as a failed check.
If this option is set to `false`, any errors that occur while waiting cause the promise to be
rejected. By default, errors are suppressed for this utility.

### `waitForValueToChange`

```js
function waitForValueToChange(selector: function(): any, options?: WaitOptions): Promise<void>
function waitForValueToChange(selector: function(): any, options?: {
interval?: number,
timeout?: number,
suppressErrors?: boolean
}): Promise<void>
```

Returns a `Promise` that resolves if the value returned from the provided selector changes. It
expected that the [`result` of `renderHook`](/reference/api#result) to select the value for
comparison.

The value is selected for comparison after each render of the hook. By default, errors raised from
selecting the value will not be suppressed (`suppressErrors = false`).
#### `interval`

The amount of time in milliseconds (ms) to wait between checks of the callback if no renders occur.
Interval checking is disabled if `interval` is not provided in the options or provided as a `falsy`
value. By default, it is disabled.

#### `timeout`

The maximum amount of time in milliseconds (ms) to wait. By default, no timeout is applied.

#### `suppressErrors`

See the [`wait` Options](/reference/api#wait-options) section for more details on the available
`options`.
If this option is set to `true`, any errors that occur while waiting are treated as a failed check.
If this option is set to `false`, any errors that occur while waiting cause the promise to be
rejected. By default, errors are not suppressed for this utility.

### `wait` Options
### `wait`

The async utilities accept the following options:
_(DEPRECATED, use [`waitFor`](/reference/api#waitfor) instead)_

```js
function wait(callback: function(): boolean|void, options?: {
timeout?: number,
suppressErrors?: boolean
}): Promise<void>
```

Returns a `Promise` that resolves if the provided callback executes without exception and returns a
truthy or `undefined` value. It is safe to use the [`result` of `renderHook`](/reference/api#result)
in the callback to perform assertion or to test values.

#### `timeout`

Expand All @@ -205,5 +247,4 @@ The maximum amount of time in milliseconds (ms) to wait. By default, no timeout

If this option is set to `true`, any errors that occur while waiting are treated as a failed check.
If this option is set to `false`, any errors that occur while waiting cause the promise to be
rejected. Please refer to the [utility descriptions](/reference/api#async-utilities) for the default
values of this option (if applicable).
rejected. By default, errors are suppressed for this utility.
39 changes: 35 additions & 4 deletions src/asyncUtils.js
Expand Up @@ -6,6 +6,14 @@ function createTimeoutError(utilName, { timeout }) {
return timeoutError
}

function resolveAfter(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

let hasWarnedDeprecatedWait = false

function asyncUtils(addResolver) {
let nextUpdatePromise = null

Expand All @@ -30,7 +38,7 @@ function asyncUtils(addResolver) {
await nextUpdatePromise
}

const wait = async (callback, { timeout, suppressErrors = true } = {}) => {
const waitFor = async (callback, { interval, timeout, suppressErrors = true } = {}) => {
const checkResult = () => {
try {
const callbackResult = callback()
Expand All @@ -47,13 +55,18 @@ function asyncUtils(addResolver) {
while (true) {
const startTime = Date.now()
try {
await waitForNextUpdate({ timeout })
const nextCheck = interval
? Promise.race([waitForNextUpdate({ timeout }), resolveAfter(interval)])
: waitForNextUpdate({ timeout })

await nextCheck

if (checkResult()) {
return
}
} catch (e) {
if (e.timeout) {
throw createTimeoutError('wait', { timeout: initialTimeout })
throw createTimeoutError('waitFor', { timeout: initialTimeout })
}
throw e
}
Expand All @@ -69,7 +82,7 @@ function asyncUtils(addResolver) {
const waitForValueToChange = async (selector, options = {}) => {
const initialValue = selector()
try {
await wait(() => selector() !== initialValue, {
await waitFor(() => selector() !== initialValue, {
suppressErrors: false,
...options
})
Expand All @@ -81,8 +94,26 @@ function asyncUtils(addResolver) {
}
}

const wait = async (callback, { timeout, suppressErrors } = {}) => {
if (!hasWarnedDeprecatedWait) {
hasWarnedDeprecatedWait = true
console.warn(
'`wait` has been deprecated. Use `waitFor` instead: https://react-hooks-testing-library.com/reference/api#waitfor.'
)
}
try {
await waitFor(callback, { timeout, suppressErrors })
} catch (e) {
if (e.timeout) {
throw createTimeoutError('wait', { timeout })
}
throw e
}
}

return {
wait,
waitFor,
waitForNextUpdate,
waitForValueToChange
}
Expand Down

0 comments on commit 50c9720

Please sign in to comment.