@testing-library/react-hooks
Advanced tools
Comparing version 7.0.2 to 8.0.0-alpha.1
@@ -25,11 +25,13 @@ "use strict"; | ||
const timeoutSignal = (0, _createTimeoutController.createTimeoutController)(timeout); | ||
const timeoutController = (0, _createTimeoutController.createTimeoutController)(timeout, { | ||
allowFakeTimers: true | ||
}); | ||
const waitForResult = async () => { | ||
while (true) { | ||
const intervalSignal = (0, _createTimeoutController.createTimeoutController)(interval); | ||
timeoutSignal.onTimeout(() => intervalSignal.cancel()); | ||
await intervalSignal.wrap(new Promise(addResolver)); | ||
const intervalController = (0, _createTimeoutController.createTimeoutController)(interval); | ||
timeoutController.onTimeout(() => intervalController.cancel()); | ||
await intervalController.wrap(new Promise(addResolver)); | ||
if (checkResult() || timeoutSignal.timedOut) { | ||
if (checkResult() || timeoutController.timedOut) { | ||
return; | ||
@@ -41,6 +43,6 @@ } | ||
if (!checkResult()) { | ||
await act(() => timeoutSignal.wrap(waitForResult())); | ||
await act(() => timeoutController.wrap(waitForResult())); | ||
} | ||
return !timeoutSignal.timedOut; | ||
return !timeoutController.timedOut; | ||
}; | ||
@@ -47,0 +49,0 @@ |
@@ -1,3 +0,4 @@ | ||
import { WaitOptions } from '../types'; | ||
declare function createTimeoutController(timeout: WaitOptions['timeout']): { | ||
declare function createTimeoutController(timeout: number | false, { allowFakeTimers }?: { | ||
allowFakeTimers?: boolean | undefined; | ||
}): { | ||
onTimeout(callback: () => void): void; | ||
@@ -4,0 +5,0 @@ wrap(promise: Promise<void>): Promise<void>; |
@@ -8,5 +8,10 @@ "use strict"; | ||
function createTimeoutController(timeout) { | ||
var _fakeTimers = require("./fakeTimers"); | ||
function createTimeoutController(timeout, { | ||
allowFakeTimers = false | ||
} = {}) { | ||
let timeoutId; | ||
const timeoutCallbacks = []; | ||
let finished = false; | ||
const timeoutController = { | ||
@@ -24,2 +29,3 @@ onTimeout(callback) { | ||
timeoutId = setTimeout(() => { | ||
finished = true; | ||
timeoutController.timedOut = true; | ||
@@ -31,3 +37,10 @@ timeoutCallbacks.forEach(callback => callback()); | ||
promise.then(resolve).catch(reject).finally(() => timeoutController.cancel()); | ||
if ((0, _fakeTimers.fakeTimersAreEnabled)() && allowFakeTimers) { | ||
(0, _fakeTimers.advanceTimers)(() => finished); | ||
} | ||
promise.then(resolve).catch(reject).finally(() => { | ||
finished = true; | ||
timeoutController.cancel(); | ||
}); | ||
}); | ||
@@ -37,2 +50,3 @@ }, | ||
cancel() { | ||
finished = true; | ||
clearTimeout(timeoutId); | ||
@@ -39,0 +53,0 @@ }, |
{ | ||
"name": "@testing-library/react-hooks", | ||
"version": "7.0.2", | ||
"version": "8.0.0-alpha.1", | ||
"description": "Simple and complete React hooks testing utilities that encourage good testing practices.", | ||
@@ -59,4 +59,4 @@ "main": "lib/index.js", | ||
"devDependencies": { | ||
"@typescript-eslint/eslint-plugin": "4.30.0", | ||
"@typescript-eslint/parser": "4.30.0", | ||
"@typescript-eslint/eslint-plugin": "4.31.1", | ||
"@typescript-eslint/parser": "4.31.1", | ||
"all-contributors-cli": "6.20.0", | ||
@@ -70,4 +70,4 @@ "codecov": "3.8.3", | ||
"get-pkg-repo": "4.1.1", | ||
"kcd-scripts": "11.2.0", | ||
"prettier": "2.3.2", | ||
"kcd-scripts": "11.2.2", | ||
"prettier": "2.4.1", | ||
"react": "17.0.2", | ||
@@ -77,3 +77,3 @@ "react-dom": "17.0.2", | ||
"ts-node": "10.2.1", | ||
"typescript": "4.4.2" | ||
"typescript": "4.4.3" | ||
}, | ||
@@ -80,0 +80,0 @@ "peerDependencies": { |
@@ -250,3 +250,3 @@ <div align="center"> | ||
<td align="center"><a href="https://github.com/snowystinger"><img src="https://avatars.githubusercontent.com/u/698229?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert Snow</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=snowystinger" title="Tests">⚠️</a></td> | ||
<td align="center"><a href="https://github.com/chris110408"><img src="https://avatars.githubusercontent.com/u/10645051?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Chen</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=chris110408" title="Tests">⚠️</a></td> | ||
<td align="center"><a href="https://github.com/chris110408"><img src="https://avatars.githubusercontent.com/u/10645051?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Chen</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=chris110408" title="Code">💻</a> <a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=chris110408" title="Tests">⚠️</a></td> | ||
<td align="center"><a href="https://www.facebook.com/masoud.bonabi"><img src="https://avatars.githubusercontent.com/u/6429009?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Masious</b></sub></a><br /><a href="https://github.com/testing-library/react-hooks-testing-library/commits?author=masious" title="Documentation">📖</a></td> | ||
@@ -253,0 +253,0 @@ </tr> |
@@ -24,236 +24,251 @@ import { useState, useRef, useEffect } from 'react' | ||
} | ||
describe.each([ | ||
{ timerType: 'real timer', setTimer: () => jest.useRealTimers() }, | ||
{ timerType: 'fake timer (legacy)', setTimer: () => jest.useFakeTimers('legacy') }, | ||
{ timerType: 'fake timer (modern)', setTimer: () => jest.useFakeTimers('modern') } | ||
])('$timerType', ({ setTimer }) => { | ||
beforeEach(() => { | ||
setTimer() | ||
}) | ||
runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => { | ||
test('should wait for next update', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'])) | ||
afterEach(() => { | ||
jest.useRealTimers() | ||
}) | ||
expect(result.current).toBe('first') | ||
runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => { | ||
test('should wait for next update', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'])) | ||
await waitForNextUpdate() | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('second') | ||
}) | ||
await waitForNextUpdate() | ||
test('should wait for multiple updates', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
expect(result.current).toBe('second') | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should wait for multiple updates', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
await waitForNextUpdate() | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('second') | ||
await waitForNextUpdate() | ||
await waitForNextUpdate() | ||
expect(result.current).toBe('second') | ||
expect(result.current).toBe('third') | ||
}) | ||
await waitForNextUpdate() | ||
test('should reject if timeout exceeded when waiting for next update', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'])) | ||
expect(result.current).toBe('third') | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should reject if timeout exceeded when waiting for next update', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'])) | ||
await expect(waitForNextUpdate({ timeout: 10 })).rejects.toThrow( | ||
Error('Timed out in waitForNextUpdate after 10ms.') | ||
) | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should not reject when waiting for next update if timeout has been disabled', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'], 1100)) | ||
await expect(waitForNextUpdate({ timeout: 10 })).rejects.toThrow( | ||
Error('Timed out in waitForNextUpdate after 10ms.') | ||
) | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should not reject when waiting for next update if timeout has been disabled', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => | ||
useSequence(['first', 'second'], 1100) | ||
) | ||
await waitForNextUpdate({ timeout: false }) | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('second') | ||
}) | ||
await waitForNextUpdate({ timeout: false }) | ||
test('should wait for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
expect(result.current).toBe('second') | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should wait for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
let complete = false | ||
await waitFor(() => { | ||
expect(result.current).toBe('third') | ||
complete = true | ||
expect(result.current).toBe('first') | ||
let complete = false | ||
await waitFor(() => { | ||
expect(result.current).toBe('third') | ||
complete = true | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
test('should wait for arbitrary expectation to pass', async () => { | ||
const { waitFor } = renderHook(() => null) | ||
test('should wait for arbitrary expectation to pass', async () => { | ||
const { waitFor } = renderHook(() => null) | ||
let actual = 0 | ||
const expected = 1 | ||
let actual = 0 | ||
const expected = 1 | ||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
let complete = false | ||
await waitFor(() => { | ||
expect(actual).toBe(expected) | ||
complete = true | ||
let complete = false | ||
await waitFor(() => { | ||
expect(actual).toBe(expected) | ||
complete = true | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
test('should not hang if expectation is already passing', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second'])) | ||
test('should not hang if expectation is already passing', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second'])) | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('first') | ||
let complete = false | ||
await waitFor(() => { | ||
expect(result.current).toBe('first') | ||
complete = true | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
let complete = false | ||
await waitFor(() => { | ||
test('should wait for truthy value', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
expect(result.current).toBe('first') | ||
complete = true | ||
await waitFor(() => result.current === 'third') | ||
expect(result.current).toBe('third') | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
test('should wait for truthy value', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
test('should wait for arbitrary truthy value', async () => { | ||
const { waitFor } = renderHook(() => null) | ||
expect(result.current).toBe('first') | ||
let actual = 0 | ||
const expected = 1 | ||
await waitFor(() => result.current === 'third') | ||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
expect(result.current).toBe('third') | ||
}) | ||
await waitFor(() => actual === 1) | ||
test('should wait for arbitrary truthy value', async () => { | ||
const { waitFor } = renderHook(() => null) | ||
expect(actual).toBe(expected) | ||
}) | ||
let actual = 0 | ||
const expected = 1 | ||
test('should reject if timeout exceeded when waiting for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
expect(result.current).toBe('first') | ||
await waitFor(() => actual === 1) | ||
await expect( | ||
waitFor( | ||
() => { | ||
expect(result.current).toBe('third') | ||
}, | ||
{ timeout: 75 } | ||
) | ||
).rejects.toThrow(Error('Timed out in waitFor after 75ms.')) | ||
}) | ||
expect(actual).toBe(expected) | ||
}) | ||
test('should not reject when waiting for expectation to pass if timeout has been disabled', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'], 550)) | ||
test('should reject if timeout exceeded when waiting for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('first') | ||
await expect( | ||
waitFor( | ||
await waitFor( | ||
() => { | ||
expect(result.current).toBe('third') | ||
}, | ||
{ timeout: 75 } | ||
{ timeout: false } | ||
) | ||
).rejects.toThrow(Error('Timed out in waitFor after 75ms.')) | ||
}) | ||
test('should not reject when waiting for expectation to pass if timeout has been disabled', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'], 550)) | ||
expect(result.current).toBe('third') | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should check on interval when waiting for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
await waitFor( | ||
() => { | ||
expect(result.current).toBe('third') | ||
}, | ||
{ timeout: false } | ||
) | ||
let checks = 0 | ||
expect(result.current).toBe('third') | ||
}) | ||
await waitFor( | ||
() => { | ||
checks++ | ||
return result.current === 'third' | ||
}, | ||
{ interval: 100 } | ||
) | ||
test('should check on interval when waiting for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
expect(checks).toBe(3) | ||
}) | ||
let checks = 0 | ||
test('should wait for value to change', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
await waitFor( | ||
() => { | ||
checks++ | ||
return result.current === 'third' | ||
}, | ||
{ interval: 100 } | ||
) | ||
expect(result.current).toBe('first') | ||
expect(checks).toBe(3) | ||
}) | ||
await waitForValueToChange(() => result.current === 'third') | ||
test('should wait for value to change', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
expect(result.current).toBe('third') | ||
}) | ||
expect(result.current).toBe('first') | ||
test('should wait for arbitrary value to change', async () => { | ||
const { waitForValueToChange } = renderHook(() => null) | ||
await waitForValueToChange(() => result.current === 'third') | ||
let actual = 0 | ||
const expected = 1 | ||
expect(result.current).toBe('third') | ||
}) | ||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
test('should wait for arbitrary value to change', async () => { | ||
const { waitForValueToChange } = renderHook(() => null) | ||
await waitForValueToChange(() => actual) | ||
let actual = 0 | ||
const expected = 1 | ||
expect(actual).toBe(expected) | ||
}) | ||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
test('should reject if timeout exceeded when waiting for value to change', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
await waitForValueToChange(() => actual) | ||
expect(result.current).toBe('first') | ||
expect(actual).toBe(expected) | ||
}) | ||
await expect( | ||
waitForValueToChange(() => result.current === 'third', { | ||
timeout: 75 | ||
}) | ||
).rejects.toThrow(Error('Timed out in waitForValueToChange after 75ms.')) | ||
}) | ||
test('should reject if timeout exceeded when waiting for value to change', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
test('should not reject when waiting for value to change if timeout is disabled', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third'], 550) | ||
) | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('first') | ||
await expect( | ||
waitForValueToChange(() => result.current === 'third', { | ||
timeout: 75 | ||
await waitForValueToChange(() => result.current === 'third', { | ||
timeout: false | ||
}) | ||
).rejects.toThrow(Error('Timed out in waitForValueToChange after 75ms.')) | ||
}) | ||
test('should not reject when waiting for value to change if timeout is disabled', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third'], 550) | ||
) | ||
expect(result.current).toBe('first') | ||
await waitForValueToChange(() => result.current === 'third', { | ||
timeout: false | ||
expect(result.current).toBe('third') | ||
}) | ||
expect(result.current).toBe('third') | ||
}) | ||
test('should reject if selector throws error', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => useSequence(['first', 'second'])) | ||
test('should reject if selector throws error', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => useSequence(['first', 'second'])) | ||
expect(result.current).toBe('first') | ||
expect(result.current).toBe('first') | ||
await expect( | ||
waitForValueToChange(() => { | ||
if (result.current === 'second') { | ||
throw new Error('Something Unexpected') | ||
} | ||
return result.current | ||
}) | ||
).rejects.toThrow(Error('Something Unexpected')) | ||
await expect( | ||
waitForValueToChange(() => { | ||
if (result.current === 'second') { | ||
throw new Error('Something Unexpected') | ||
} | ||
return result.current | ||
}) | ||
).rejects.toThrow(Error('Something Unexpected')) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -17,3 +17,6 @@ import { | ||
function asyncUtils(act: Act, addResolver: (callback: () => void) => void): AsyncUtils { | ||
const wait = async (callback: () => boolean | void, { interval, timeout }: WaitOptions) => { | ||
const wait = async ( | ||
callback: () => boolean | void, | ||
{ interval, timeout }: Required<WaitOptions> | ||
) => { | ||
const checkResult = () => { | ||
@@ -24,12 +27,12 @@ const callbackResult = callback() | ||
const timeoutSignal = createTimeoutController(timeout) | ||
const timeoutController = createTimeoutController(timeout, { allowFakeTimers: true }) | ||
const waitForResult = async () => { | ||
while (true) { | ||
const intervalSignal = createTimeoutController(interval) | ||
timeoutSignal.onTimeout(() => intervalSignal.cancel()) | ||
const intervalController = createTimeoutController(interval) | ||
timeoutController.onTimeout(() => intervalController.cancel()) | ||
await intervalSignal.wrap(new Promise<void>(addResolver)) | ||
await intervalController.wrap(new Promise<void>(addResolver)) | ||
if (checkResult() || timeoutSignal.timedOut) { | ||
if (checkResult() || timeoutController.timedOut) { | ||
return | ||
@@ -41,6 +44,6 @@ } | ||
if (!checkResult()) { | ||
await act(() => timeoutSignal.wrap(waitForResult())) | ||
await act(() => timeoutController.wrap(waitForResult())) | ||
} | ||
return !timeoutSignal.timedOut | ||
return !timeoutController.timedOut | ||
} | ||
@@ -47,0 +50,0 @@ |
@@ -1,6 +0,7 @@ | ||
import { WaitOptions } from '../types' | ||
import { fakeTimersAreEnabled, advanceTimers } from './fakeTimers' | ||
function createTimeoutController(timeout: WaitOptions['timeout']) { | ||
function createTimeoutController(timeout: number | false, { allowFakeTimers = false } = {}) { | ||
let timeoutId: NodeJS.Timeout | ||
const timeoutCallbacks: Array<() => void> = [] | ||
let finished = false | ||
@@ -15,5 +16,5 @@ const timeoutController = { | ||
timeoutController.onTimeout(resolve) | ||
if (timeout) { | ||
timeoutId = setTimeout(() => { | ||
finished = true | ||
timeoutController.timedOut = true | ||
@@ -25,9 +26,17 @@ timeoutCallbacks.forEach((callback) => callback()) | ||
if (fakeTimersAreEnabled() && allowFakeTimers) { | ||
advanceTimers(() => finished) | ||
} | ||
promise | ||
.then(resolve) | ||
.catch(reject) | ||
.finally(() => timeoutController.cancel()) | ||
.finally(() => { | ||
finished = true | ||
timeoutController.cancel() | ||
}) | ||
}) | ||
}, | ||
cancel() { | ||
finished = true | ||
clearTimeout(timeoutId) | ||
@@ -34,0 +43,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
138606
100
2893
2