diff --git a/.all-contributorsrc b/.all-contributorsrc
index 5ea4dc36..51bccf3d 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -582,6 +582,7 @@
"avatar_url": "https://avatars.githubusercontent.com/u/10645051?v=4",
"profile": "https://github.com/chris110408",
"contributions": [
+ "code",
"test"
]
},
diff --git a/README.md b/README.md
index 88f0944b..7eaab8b3 100644
--- a/README.md
+++ b/README.md
@@ -247,7 +247,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 andyrooger 💻 |
 Bryan Wain 🐛 👀 |
 Robert Snow ⚠️ |
-  Chris Chen ⚠️ |
+  Chris Chen 💻 ⚠️ |
 Masious 📖 |
diff --git a/src/__tests__/asyncHook.fakeTimers.test.ts b/src/__tests__/asyncHook.fakeTimers.test.ts
deleted file mode 100644
index 98d6b2c9..00000000
--- a/src/__tests__/asyncHook.fakeTimers.test.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-describe('async hook (fake timers) tests', () => {
- beforeEach(() => {
- jest.useFakeTimers()
- })
-
- afterEach(() => {
- jest.useRealTimers()
- })
-
- runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => {
- test('should wait for arbitrary expectation to pass when using advanceTimersByTime()', async () => {
- const { waitFor } = renderHook(() => null)
-
- let actual = 0
- const expected = 1
-
- setTimeout(() => {
- actual = expected
- }, 200)
-
- let complete = false
-
- jest.advanceTimersByTime(200)
-
- await waitFor(() => {
- expect(actual).toBe(expected)
- complete = true
- })
-
- expect(complete).toBe(true)
- })
-
- test('should wait for arbitrary expectation to pass when using runOnlyPendingTimers()', async () => {
- const { waitFor } = renderHook(() => null)
-
- let actual = 0
- const expected = 1
-
- setTimeout(() => {
- actual = expected
- }, 200)
-
- let complete = false
-
- jest.runOnlyPendingTimers()
-
- await waitFor(() => {
- expect(actual).toBe(expected)
- complete = true
- })
-
- expect(complete).toBe(true)
- })
- })
-})
-
-// eslint-disable-next-line jest/no-export
-export {}
diff --git a/src/__tests__/asyncHook.test.ts b/src/__tests__/asyncHook.test.ts
index 17979ae2..29869c08 100644
--- a/src/__tests__/asyncHook.test.ts
+++ b/src/__tests__/asyncHook.test.ts
@@ -21,238 +21,253 @@ describe('async hook tests', () => {
return value
}
+ 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)
+ })
+
+ test('should wait for truthy value', async () => {
+ const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third']))
- let complete = false
- await waitFor(() => {
expect(result.current).toBe('first')
- complete = true
- })
- expect(complete).toBe(true)
- })
- test('should wait for truthy value', async () => {
- const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third']))
+ await waitFor(() => result.current === 'third')
- expect(result.current).toBe('first')
+ expect(result.current).toBe('third')
+ })
- await waitFor(() => result.current === 'third')
+ test('should wait for arbitrary truthy value', async () => {
+ const { waitFor } = renderHook(() => null)
- expect(result.current).toBe('third')
- })
+ let actual = 0
+ const expected = 1
+
+ setTimeout(() => {
+ actual = expected
+ }, 200)
- test('should wait for arbitrary truthy value', async () => {
- const { waitFor } = renderHook(() => null)
+ await waitFor(() => actual === 1)
- let actual = 0
- const expected = 1
+ expect(actual).toBe(expected)
+ })
- setTimeout(() => {
- actual = expected
- }, 200)
+ test('should reject if timeout exceeded when waiting for expectation to pass', async () => {
+ const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third']))
- await waitFor(() => actual === 1)
+ expect(result.current).toBe('first')
- expect(actual).toBe(expected)
- })
+ await expect(
+ waitFor(
+ () => {
+ expect(result.current).toBe('third')
+ },
+ { timeout: 75 }
+ )
+ ).rejects.toThrow(Error('Timed out in waitFor after 75ms.'))
+ })
- test('should reject if timeout exceeded when waiting for expectation to pass', async () => {
- const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third']))
+ 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('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'))
+ })
})
})
})
diff --git a/src/core/asyncUtils.ts b/src/core/asyncUtils.ts
index a7424036..1aa2854f 100644
--- a/src/core/asyncUtils.ts
+++ b/src/core/asyncUtils.ts
@@ -14,32 +14,35 @@ const DEFAULT_INTERVAL = 50
const DEFAULT_TIMEOUT = 1000
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
+ ) => {
const checkResult = () => {
const callbackResult = callback()
return callbackResult ?? callbackResult === undefined
}
- 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(addResolver))
+ await intervalController.wrap(new Promise(addResolver))
- if (checkResult() || timeoutSignal.timedOut) {
+ if (checkResult() || timeoutController.timedOut) {
return
}
}
}
if (!checkResult()) {
- await act(() => timeoutSignal.wrap(waitForResult()))
+ await act(() => timeoutController.wrap(waitForResult()))
}
- return !timeoutSignal.timedOut
+ return !timeoutController.timedOut
}
const waitFor = async (
diff --git a/src/helpers/createTimeoutController.ts b/src/helpers/createTimeoutController.ts
index 643d3768..6a5bda2a 100644
--- a/src/helpers/createTimeoutController.ts
+++ b/src/helpers/createTimeoutController.ts
@@ -1,8 +1,9 @@
-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
const timeoutController = {
onTimeout(callback: () => void) {
@@ -12,22 +13,30 @@ function createTimeoutController(timeout: WaitOptions['timeout']) {
return new Promise((resolve, reject) => {
timeoutController.timedOut = false
timeoutController.onTimeout(resolve)
-
if (timeout) {
timeoutId = setTimeout(() => {
+ finished = true
timeoutController.timedOut = true
timeoutCallbacks.forEach((callback) => callback())
resolve()
}, timeout)
}
+ if (fakeTimersAreEnabled() && allowFakeTimers) {
+ advanceTimers(() => finished)
+ }
+
promise
.then(resolve)
.catch(reject)
- .finally(() => timeoutController.cancel())
+ .finally(() => {
+ finished = true
+ timeoutController.cancel()
+ })
})
},
cancel() {
+ finished = true
clearTimeout(timeoutId)
},
timedOut: false
diff --git a/src/helpers/fakeTimers.ts b/src/helpers/fakeTimers.ts
new file mode 100644
index 00000000..60e60dd9
--- /dev/null
+++ b/src/helpers/fakeTimers.ts
@@ -0,0 +1,24 @@
+export const fakeTimersAreEnabled = () => {
+ /* istanbul ignore else */
+ if (typeof jest !== 'undefined' && jest !== null) {
+ return (
+ // legacy timers
+ jest.isMockFunction(setTimeout) ||
+ // modern timers
+ Object.prototype.hasOwnProperty.call(setTimeout, 'clock')
+ )
+ }
+ // istanbul ignore next
+ return false
+}
+
+export function advanceTimers(checkComplete: () => boolean) {
+ const advanceTime = async () => {
+ if (!checkComplete()) {
+ jest.advanceTimersByTime(1)
+ await Promise.resolve()
+ await advanceTime()
+ }
+ }
+ return advanceTime()
+}
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy