Skip to content

Commit 4e5e533

Browse files
authored
feat(waitFor): allow returning a promise (#782)
You can now return a promise in the waitFor callback and your callback will not be called again until the promise rejects. If the promise rejects, then the callback will be called again until it gets a promise which resolves or it times out.
1 parent 03cfef8 commit 4e5e533

File tree

4 files changed

+66
-3
lines changed

4 files changed

+66
-3
lines changed

src/__tests__/wait-for.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ import {waitFor} from '../'
22
import {configure, getConfig} from '../config'
33
import {renderIntoDocument} from './helpers/test-utils'
44

5+
function deferred() {
6+
let resolve, reject
7+
const promise = new Promise((res, rej) => {
8+
resolve = res
9+
reject = rej
10+
})
11+
return {promise, resolve, reject}
12+
}
13+
514
test('waits callback to not throw an error', async () => {
615
const spy = jest.fn()
716
// we are using random timeout here to simulate a real-time example
@@ -145,3 +154,41 @@ test('should delegate to config.getElementError', async () => {
145154
expect(error.message).toMatchInlineSnapshot(`"Custom element error"`)
146155
configure(originalConfig)
147156
})
157+
158+
test('when a promise is returned, it does not call the callback again until that promise rejects', async () => {
159+
const sleep = t => new Promise(r => setTimeout(r, t))
160+
const p1 = deferred()
161+
const waitForCb = jest.fn(() => p1.promise)
162+
const waitForPromise = waitFor(waitForCb, {interval: 1})
163+
expect(waitForCb).toHaveBeenCalledTimes(1)
164+
waitForCb.mockClear()
165+
await sleep(50)
166+
expect(waitForCb).toHaveBeenCalledTimes(0)
167+
168+
const p2 = deferred()
169+
waitForCb.mockImplementation(() => p2.promise)
170+
171+
p1.reject('p1 rejection (should not fail this test)')
172+
await sleep(50)
173+
174+
expect(waitForCb).toHaveBeenCalledTimes(1)
175+
p2.resolve()
176+
177+
await waitForPromise
178+
})
179+
180+
test('when a promise is returned, if that is not resolved within the timeout, then waitFor is rejected', async () => {
181+
const sleep = t => new Promise(r => setTimeout(r, t))
182+
const {promise} = deferred()
183+
const waitForPromise = waitFor(() => promise, {timeout: 1}).catch(e => e)
184+
await sleep(5)
185+
186+
expect((await waitForPromise).message).toMatchInlineSnapshot(`
187+
"Timed out in waitFor.
188+
189+
<html>
190+
<head />
191+
<body />
192+
</html>"
193+
`)
194+
})

src/wait-for.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function waitFor(
4747
return new Promise(async (resolve, reject) => {
4848
let lastError, intervalId, observer
4949
let finished = false
50+
let promiseStatus = 'idle'
5051

5152
const overallTimeoutTimer = setTimeout(handleTimeout, timeout)
5253

@@ -104,8 +105,24 @@ function waitFor(
104105
}
105106

106107
function checkCallback() {
108+
if (promiseStatus === 'pending') return
107109
try {
108-
onDone(null, runWithExpensiveErrorDiagnosticsDisabled(callback))
110+
const result = runWithExpensiveErrorDiagnosticsDisabled(callback)
111+
if (typeof result?.then === 'function') {
112+
promiseStatus = 'pending'
113+
result.then(
114+
resolvedValue => {
115+
promiseStatus = 'resolved'
116+
onDone(null, resolvedValue)
117+
},
118+
rejectedValue => {
119+
promiseStatus = 'rejected'
120+
lastError = rejectedValue
121+
},
122+
)
123+
} else {
124+
onDone(null, result)
125+
}
109126
// If `callback` throws, wait for the next mutation, interval, or timeout.
110127
} catch (error) {
111128
// Save the most recent callback error to reject the promise with it in the event of a timeout

types/__tests__/type-tests.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,5 @@ async function testWaitFors() {
174174
await waitForElementToBeRemoved(getByText(element, 'apple'))
175175
await waitForElementToBeRemoved(getAllByText(element, 'apple'))
176176

177-
// $ExpectError
178177
await waitFor(async () => {})
179178
}

types/wait-for.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ export interface waitForOptions {
77
}
88

99
export function waitFor<T>(
10-
callback: () => T extends Promise<any> ? never : T,
10+
callback: () => T | Promise<T>,
1111
options?: waitForOptions,
1212
): Promise<T>

0 commit comments

Comments
 (0)