diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts new file mode 100644 index 000000000..8d54d0925 --- /dev/null +++ b/src/__tests__/config.test.ts @@ -0,0 +1,22 @@ +import { getConfig, configure, resetToDefaults } from '../config'; + +beforeEach(() => { + resetToDefaults(); +}); + +test('getConfig() returns existing configuration', () => { + expect(getConfig().asyncUtilTimeout).toEqual(1000); +}); + +test('configure() overrides existing config values', () => { + configure({ asyncUtilTimeout: 5000 }); + expect(getConfig().asyncUtilTimeout).toEqual(5000); +}); + +test('resetToDefaults() resets config to defaults', () => { + configure({ asyncUtilTimeout: 5000 }); + expect(getConfig().asyncUtilTimeout).toEqual(5000); + + resetToDefaults(); + expect(getConfig().asyncUtilTimeout).toEqual(1000); +}); diff --git a/src/__tests__/waitFor.test.tsx b/src/__tests__/waitFor.test.tsx index 534b43bdd..a35ebf171 100644 --- a/src/__tests__/waitFor.test.tsx +++ b/src/__tests__/waitFor.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Text, TouchableOpacity, View, Pressable } from 'react-native'; -import { fireEvent, render, waitFor } from '..'; +import { fireEvent, render, waitFor, configure, resetToDefaults } from '..'; class Banana extends React.Component { changeFresh = () => { @@ -19,6 +19,10 @@ class Banana extends React.Component { } } +beforeEach(() => { + resetToDefaults(); +}); + class BananaContainer extends React.Component<{}, any> { state = { fresh: false }; @@ -64,6 +68,32 @@ test('waits for element until timeout is met', async () => { await waitFor(() => getByText('Fresh')); }); +test('waitFor defaults to asyncWaitTimeout config option', async () => { + configure({ asyncUtilTimeout: 100 }); + const { getByText } = render(); + + fireEvent.press(getByText('Change freshness!')); + await expect(waitFor(() => getByText('Fresh'))).rejects.toThrow(); + + // Async action ends after 300ms and we only waited 100ms, so we need to wait + // for the remaining async actions to finish + await waitFor(() => getByText('Fresh'), { timeout: 1000 }); +}); + +test('waitFor timeout option takes precendence over `asyncWaitTimeout` config option', async () => { + configure({ asyncUtilTimeout: 2000 }); + const { getByText } = render(); + + fireEvent.press(getByText('Change freshness!')); + await expect( + waitFor(() => getByText('Fresh'), { timeout: 100 }) + ).rejects.toThrow(); + + // Async action ends after 300ms and we only waited 100ms, so we need to wait + // for the remaining async actions to finish + await waitFor(() => getByText('Fresh')); +}); + test('waits for element with custom interval', async () => { const mockFn = jest.fn(() => { throw Error('test'); diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 000000000..940f367df --- /dev/null +++ b/src/config.ts @@ -0,0 +1,27 @@ +export type Config = { + /** Default timeout, in ms, for `waitFor` and `findBy*` queries. */ + asyncUtilTimeout: number; +}; + +const defaultConfig: Config = { + asyncUtilTimeout: 1000, +}; + +let config = { + ...defaultConfig, +}; + +export function configure(options: Partial) { + config = { + ...config, + ...options, + }; +} + +export function resetToDefaults() { + config = defaultConfig; +} + +export function getConfig() { + return config; +} diff --git a/src/pure.ts b/src/pure.ts index c3f448249..00ed3d5ea 100644 --- a/src/pure.ts +++ b/src/pure.ts @@ -16,9 +16,11 @@ export type { RenderResult as RenderAPI, } from './render'; export type { RenderHookOptions, RenderHookResult } from './renderHook'; +export type { Config } from './config'; export { act }; export { cleanup }; +export { configure, resetToDefaults } from './config'; export { fireEvent }; export { render }; export { waitFor }; diff --git a/src/waitFor.ts b/src/waitFor.ts index 69d3295db..9a59531da 100644 --- a/src/waitFor.ts +++ b/src/waitFor.ts @@ -1,5 +1,6 @@ /* globals jest */ import act, { setReactActEnvironment, getIsReactActEnvironment } from './act'; +import { getConfig } from './config'; import { ErrorWithStack, copyStackTrace } from './helpers/errors'; import { setTimeout, @@ -9,7 +10,6 @@ import { } from './helpers/timers'; import { checkReactVersionAtLeast } from './react-versions'; -const DEFAULT_TIMEOUT = 1000; const DEFAULT_INTERVAL = 50; export type WaitForOptions = { @@ -22,7 +22,7 @@ export type WaitForOptions = { function waitForInternal( expectation: () => T, { - timeout = DEFAULT_TIMEOUT, + timeout = getConfig().asyncUtilTimeout, interval = DEFAULT_INTERVAL, stackTraceError, onTimeout, diff --git a/typings/index.flow.js b/typings/index.flow.js index a0f9acbc8..2658eda2c 100644 --- a/typings/index.flow.js +++ b/typings/index.flow.js @@ -367,6 +367,13 @@ declare module '@testing-library/react-native' { declare export var waitForElementToBeRemoved: WaitForElementToBeRemovedFunction; + declare interface Config { + asyncUtilTimeout: number; + } + + declare export var configure: (options: $Shape) => void; + declare export var resetToDefaults: () => void; + declare export var act: (callback: () => void) => Thenable; declare export var within: (instance: ReactTestInstance) => Queries; declare export var getQueriesForElement: ( diff --git a/website/docs/API.md b/website/docs/API.md index bdc64622e..6d8adb34f 100644 --- a/website/docs/API.md +++ b/website/docs/API.md @@ -45,6 +45,10 @@ title: API - [Examples](#examples) - [With `initialProps`](#with-initialprops) - [With `wrapper`](#with-wrapper) +- [Configuration](#configuration) + - [`configure`](#configure) + - [`asyncUtilTimeout` option](#asyncutiltimeout-option) + - [`resetToDefaults()`](#resettodefaults) - [Accessibility](#accessibility) - [`isInaccessible`](#isinaccessible) @@ -714,6 +718,30 @@ it('should use context value', () => { }); ``` + +## Configuration + +### `configure` + +```ts +type Config = { + asyncUtilTimeout: number; +}; + +function configure(options: Partial) {} +``` + +#### `asyncUtilTimeout` option + +Default timeout, in ms, for async helper functions (`waitFor`, `waitForElementToBeRemoved`) and `findBy*` queries. Defaults to 1000 ms. + + +### `resetToDefaults()` + +```ts +function resetToDefaults() {} +``` + ## Accessibility ### `isInaccessible`