Skip to content
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

Fix #648: Add setDefaultWaitOptions function #658

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
@@ -566,6 +566,18 @@
"bug",
"review"
]
},
{
"login": "orokanasaru",
"name": "Michael Hensler",
"avatar_url": "https://avatars.githubusercontent.com/u/5751627?v=4",
"profile": "https://github.com/orokanasaru",
"contributions": [
"code",
"doc",
"ideas",
"test"
]
}
],
"skipCi": true,
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -246,6 +246,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://matan.io"><img src="https://avatars.githubusercontent.com/u/12711091?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matan Borenkraout</b></sub></a><br /><a href="#maintenance-MatanBobi" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/andyrooger"><img src="https://avatars.githubusercontent.com/u/420834?v=4?s=100" width="100px;" alt=""/><br /><sub><b>andyrooger</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=andyrooger" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bdwain"><img src="https://avatars.githubusercontent.com/u/3982094?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bryan Wain</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/issues?q=author%3Abdwain" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-hooks-testing-library/pulls?q=is%3Apr+reviewed-by%3Abdwain" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/orokanasaru"><img src="https://avatars.githubusercontent.com/u/5751627?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Hensler</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=orokanasaru" title="Code">💻</a> <a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=orokanasaru" title="Documentation">📖</a> <a href="#ideas-orokanasaru" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=orokanasaru" title="Tests">⚠️</a></td>
</tr>
</table>

24 changes: 24 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
@@ -271,6 +271,30 @@ _Default: 1000_

The maximum amount of time in milliseconds (ms) to wait.

### `setDefaultWaitOptions`

```ts
function setDefaultWaitOptions({
interval?: number | false
timeout?: number | false
}): void
```

Updates the default values for `interval` and `timeout` used by the `waitFor*` functions.

#### `interval`

_Default: 50_

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 as a `falsy`.

#### `timeout`

_Default: 1000_

The maximum amount of time in milliseconds (ms) to wait.

---

## `console.error`
30 changes: 30 additions & 0 deletions src/__tests__/asyncHook.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { setDefaultWaitOptions } from 'core'
import { useState, useRef, useEffect } from 'react'

describe('async hook tests', () => {
beforeEach(() => {
setDefaultWaitOptions({ interval: 50, timeout: 1000 })
})

const useSequence = (values: string[], intervalMs = 50) => {
const [first, ...otherValues] = values
const [value, setValue] = useState(() => first)
@@ -59,6 +64,17 @@ describe('async hook tests', () => {
)
})

test('should reject if custom default timeout exceeded when waiting for next update', async () => {
setDefaultWaitOptions({ timeout: 10 })
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second']))

expect(result.current).toBe('first')

await expect(waitForNextUpdate()).rejects.toThrow(
Error('Timed out in waitForNextUpdate after 10ms.')
)
})

test('should not reject when waiting for next update if timeout has been disabled', async () => {
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'], 1100))

@@ -185,6 +201,20 @@ describe('async hook tests', () => {
expect(checks).toBe(3)
})

test('should check on custom default interval when waiting for expectation to pass', async () => {
setDefaultWaitOptions({ interval: 100 })
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third']))

let checks = 0

await waitFor(() => {
checks++
return result.current === 'third'
})

expect(checks).toBe(3)
})

test('should wait for value to change', async () => {
const { result, waitForValueToChange } = renderHook(() =>
useSequence(['first', 'second', 'third'])
19 changes: 11 additions & 8 deletions src/core/asyncUtils.ts
Original file line number Diff line number Diff line change
@@ -10,8 +10,13 @@ import {
import { resolveAfter, callAfter } from '../helpers/promises'
import { TimeoutError } from '../helpers/error'

const DEFAULT_INTERVAL = 50
const DEFAULT_TIMEOUT = 1000
let defaultInterval: number | false = 50
let defaultTimeout: number | false = 1000

function setDefaultWaitOptions({ interval, timeout }: WaitOptions) {
defaultInterval = interval ?? defaultInterval
defaultTimeout = timeout ?? defaultTimeout
}
Comment on lines -13 to +19
Copy link
Member

Choose a reason for hiding this comment

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

Do we need some way of resetting the defaults back to the initial values?

I'm just thinking about someone that wants to update the defaults for a single test file so they set it in the beforeAll and want to reset it in the afterAll. Because we don't export the initial default values, they would have to just hard code the documented default values and hope we never change them.

The options I see are:

  1. export some constants for them to use with setDefaultWaitOptions
  2. export a resetDefaultWaitOptions utility

Or maybe both? Or maybe there's another option I'm not seeing?

Copy link
Member

Choose a reason for hiding this comment

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

I think exporting the constants makes more sense than a function imo

Copy link
Member

@mpeyper mpeyper Aug 2, 2021

Choose a reason for hiding this comment

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

I've been wondering about new options into renderHook? A render helper can then be extracted like normal for shared setup.

Copy link
Author

Choose a reason for hiding this comment

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

@mpeyper, can you expand on your idea? I will export initial constants otherwise


function asyncUtils(act: Act, addResolver: (callback: () => void) => void): AsyncUtils {
const wait = async (callback: () => boolean | void, { interval, timeout }: WaitOptions) => {
@@ -55,7 +60,7 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn

const waitFor = async (
callback: () => boolean | void,
{ interval = DEFAULT_INTERVAL, timeout = DEFAULT_TIMEOUT }: WaitForOptions = {}
{ interval = defaultInterval, timeout = defaultTimeout }: WaitForOptions = {}
) => {
const safeCallback = () => {
try {
@@ -73,7 +78,7 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn

const waitForValueToChange = async (
selector: () => unknown,
{ interval = DEFAULT_INTERVAL, timeout = DEFAULT_TIMEOUT }: WaitForValueToChangeOptions = {}
{ interval = defaultInterval, timeout = defaultTimeout }: WaitForValueToChangeOptions = {}
) => {
const initialValue = selector()

@@ -83,9 +88,7 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn
}
}

const waitForNextUpdate = async ({
timeout = DEFAULT_TIMEOUT
}: WaitForNextUpdateOptions = {}) => {
const waitForNextUpdate = async ({ timeout = defaultTimeout }: WaitForNextUpdateOptions = {}) => {
let updated = false
addResolver(() => {
updated = true
@@ -104,4 +107,4 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn
}
}

export { asyncUtils }
export { asyncUtils, setDefaultWaitOptions }
11 changes: 9 additions & 2 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CreateRenderer, Renderer, RenderResult, RenderHookOptions } from '../types'

import { asyncUtils } from './asyncUtils'
import { asyncUtils, setDefaultWaitOptions } from './asyncUtils'
import { cleanup, addCleanup, removeCleanup } from './cleanup'
import { suppressErrorOutput } from './console'

@@ -82,4 +82,11 @@ function createRenderHook<
return renderHook
}

export { createRenderHook, cleanup, addCleanup, removeCleanup, suppressErrorOutput }
export {
createRenderHook,
cleanup,
addCleanup,
removeCleanup,
setDefaultWaitOptions,
suppressErrorOutput
}
8 changes: 7 additions & 1 deletion src/dom/pure.ts
Original file line number Diff line number Diff line change
@@ -37,6 +37,12 @@ const renderHook = createRenderHook(createDomRenderer)

export { renderHook, act }

export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'
export {
cleanup,
addCleanup,
removeCleanup,
setDefaultWaitOptions,
suppressErrorOutput
} from '../core'

export * from '../types/react'
8 changes: 7 additions & 1 deletion src/native/pure.ts
Original file line number Diff line number Diff line change
@@ -36,6 +36,12 @@ const renderHook = createRenderHook(createNativeRenderer)

export { renderHook, act }

export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'
export {
cleanup,
addCleanup,
removeCleanup,
setDefaultWaitOptions,
suppressErrorOutput
} from '../core'

export * from '../types/react'
22 changes: 19 additions & 3 deletions src/pure.ts
Original file line number Diff line number Diff line change
@@ -32,8 +32,24 @@ function getRenderer() {
}
}

const { renderHook, act, cleanup, addCleanup, removeCleanup, suppressErrorOutput } = getRenderer()

export { renderHook, act, cleanup, addCleanup, removeCleanup, suppressErrorOutput }
const {
renderHook,
act,
cleanup,
addCleanup,
removeCleanup,
setDefaultWaitOptions,
suppressErrorOutput
} = getRenderer()

export {
renderHook,
act,
cleanup,
addCleanup,
removeCleanup,
setDefaultWaitOptions,
suppressErrorOutput
}

export * from './types/react'
8 changes: 7 additions & 1 deletion src/server/pure.ts
Original file line number Diff line number Diff line change
@@ -61,6 +61,12 @@ const renderHook = createRenderHook(createServerRenderer)

export { renderHook, act }

export { cleanup, addCleanup, removeCleanup, suppressErrorOutput } from '../core'
export {
cleanup,
addCleanup,
removeCleanup,
setDefaultWaitOptions,
suppressErrorOutput
} from '../core'

export * from '../types/react'
4 changes: 3 additions & 1 deletion src/types/react.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@ import {
RenderHookResult,
ServerRenderHookResult,
Act,
CleanupCallback
CleanupCallback,
WaitOptions
} from '.'

export type WrapperComponent<TProps> = ComponentType<TProps>
@@ -27,6 +28,7 @@ export type ReactHooksRenderer = {
cleanup: () => Promise<void>
addCleanup: (callback: CleanupCallback) => () => void
removeCleanup: (callback: CleanupCallback) => void
setDefaultWaitOptions: (options: WaitOptions) => void
suppressErrorOutput: () => () => void
}