Skip to content

Commit

Permalink
fix the code after code review and clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
Lei Chen authored and Lei Chen committed Sep 9, 2021
1 parent 437f684 commit 6e6b5a2
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 15 deletions.
6 changes: 3 additions & 3 deletions src/__tests__/asyncHook.fakeTimers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('async hook (fake timers) tests', () => {
jest.useRealTimers()
})

runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => {
runForRenderers(['default'], ({ renderHook }) => {
test('should wait for arbitrary expectation to pass when using advanceTimersByTime()', async () => {
const { waitFor } = renderHook(() => null)

Expand Down Expand Up @@ -77,9 +77,9 @@ describe('async hook (fake timers) tests', () => {

test('should waitFor arbitrary expectation to pass when fake timers are not advanced explicitly', async () => {
const fn = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true)

const { waitFor } = renderHook(() => null)

await waitFor(() => {
expect(fn()).toBe(true)
})
Expand Down
19 changes: 11 additions & 8 deletions src/core/asyncUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,35 @@ const DEFAULT_TIMEOUT = 1000
const DEFAULT_INTERVAL = 50

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 = () => {
const callbackResult = callback()
return callbackResult ?? callbackResult === undefined
}

const timeoutSignal = createTimeoutController(timeout as number | boolean, false)
const timeoutController = createTimeoutController(timeout, { allowFakeTimers: !interval })

const waitForResult = async () => {
while (true) {
const intervalSignal = createTimeoutController(interval as number | boolean, true)
timeoutSignal.onTimeout(() => intervalSignal.cancel())
const intervalController = createTimeoutController(interval, { allowFakeTimers: true })
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
}
}
}

if (!checkResult()) {
await act(() => timeoutSignal.wrap(waitForResult()))
await act(() => timeoutController.wrap(waitForResult()))
}

return !timeoutSignal.timedOut
return !timeoutController.timedOut
}

const waitFor = async (
Expand Down
28 changes: 24 additions & 4 deletions src/helpers/createTimeoutController.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { jestFakeTimersAreEnabled } from './jestFakeTimersAreEnabled'

function createTimeoutController(timeout: number | boolean, allowFakeTimers: boolean) {
function createTimeoutController(timeout: number | false, options: { allowFakeTimers: boolean }) {
let timeoutId: NodeJS.Timeout
const timeoutCallbacks: Array<() => void> = []
let finished = false

const { allowFakeTimers = false } = options

const advanceTime = async (currentMs: number) => {
if (currentMs < timeout) {
jest.advanceTimersByTime(1)

await Promise.resolve()

if (finished) {
return
}
await advanceTime(currentMs + 1)
}
}

const timeoutController = {
onTimeout(callback: () => void) {
Expand All @@ -18,20 +34,24 @@ function createTimeoutController(timeout: number | boolean, allowFakeTimers: boo
timeoutController.timedOut = true
timeoutCallbacks.forEach((callback) => callback())
resolve()
}, timeout as number)
}, timeout)

if (jestFakeTimersAreEnabled() && allowFakeTimers) {
jest.advanceTimersByTime(timeout as number)
advanceTime(0)
}
}

promise
.then(resolve)
.catch(reject)
.finally(() => timeoutController.cancel())
.finally(() => {
finished = true
timeoutController.cancel()
})
})
},
cancel() {
finished = true
clearTimeout(timeoutId)
},
timedOut: false
Expand Down

0 comments on commit 6e6b5a2

Please sign in to comment.