Skip to content

Commit 4bff80b

Browse files
author
Sebastian Silbermann
committed
Port tests from waitFor DOM
1 parent ff44db7 commit 4bff80b

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

src/__tests__/waitFor.test.js

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/**
2+
* @jest-environment node
3+
*/
4+
5+
import * as prettyFormat from 'pretty-format'
6+
import {waitFor} from '../'
7+
8+
function deferred() {
9+
let resolve, reject
10+
const promise = new Promise((res, rej) => {
11+
resolve = res
12+
reject = rej
13+
})
14+
return {promise, resolve, reject}
15+
}
16+
17+
beforeEach(() => {
18+
jest.useRealTimers()
19+
})
20+
21+
test('waits callback to not throw an error', async () => {
22+
const spy = jest.fn()
23+
// we are using random timeout here to simulate a real-time example
24+
// of an async operation calling a callback at a non-deterministic time
25+
const randomTimeout = Math.floor(Math.random() * 60)
26+
setTimeout(spy, randomTimeout)
27+
28+
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1))
29+
expect(spy).toHaveBeenCalledWith()
30+
})
31+
32+
// we used to have a limitation where we had to set an interval of 0 to 1
33+
// otherwise there would be problems. I don't think this limitation exists
34+
// anymore, but we'll keep this test around to make sure a problem doesn't
35+
// crop up.
36+
test('can accept an interval of 0', () => waitFor(() => {}, {interval: 0}))
37+
38+
test('can timeout after the given timeout time', async () => {
39+
const error = new Error('throws every time')
40+
const result = await waitFor(
41+
() => {
42+
throw error
43+
},
44+
{timeout: 8, interval: 5},
45+
).catch(e => e)
46+
expect(result).toBe(error)
47+
})
48+
49+
test('if no error is thrown then throws a timeout error', async () => {
50+
const result = await waitFor(
51+
() => {
52+
// eslint-disable-next-line no-throw-literal
53+
throw undefined
54+
},
55+
{timeout: 8, interval: 5, onTimeout: e => e},
56+
).catch(e => e)
57+
expect(result).toMatchInlineSnapshot(`[Error: Timed out in waitFor.]`)
58+
})
59+
60+
test('if showOriginalStackTrace on a timeout error then the stack trace does not include this file', async () => {
61+
const result = await waitFor(
62+
() => {
63+
// eslint-disable-next-line no-throw-literal
64+
throw undefined
65+
},
66+
{timeout: 8, interval: 5, showOriginalStackTrace: true},
67+
).catch(e => e)
68+
expect(result.stack).not.toMatch(__dirname)
69+
})
70+
71+
test('uses full stack error trace when showOriginalStackTrace present', async () => {
72+
const error = new Error('Throws the full stack trace')
73+
// even if the error is a TestingLibraryElementError
74+
error.name = 'TestingLibraryElementError'
75+
const originalStackTrace = error.stack
76+
const result = await waitFor(
77+
() => {
78+
throw error
79+
},
80+
{timeout: 8, interval: 5, showOriginalStackTrace: true},
81+
).catch(e => e)
82+
expect(result.stack).toBe(originalStackTrace)
83+
})
84+
85+
test('throws nice error if provided callback is not a function', () => {
86+
const someElement = {}
87+
expect(() => waitFor(someElement)).toThrow(
88+
'Received `callback` arg must be a function',
89+
)
90+
})
91+
92+
test('when a promise is returned, it does not call the callback again until that promise rejects', async () => {
93+
const sleep = t => new Promise(r => setTimeout(r, t))
94+
const p1 = deferred()
95+
const waitForCb = jest.fn(() => p1.promise)
96+
const waitForPromise = waitFor(waitForCb, {interval: 1})
97+
expect(waitForCb).toHaveBeenCalledTimes(1)
98+
waitForCb.mockClear()
99+
await sleep(50)
100+
expect(waitForCb).toHaveBeenCalledTimes(0)
101+
102+
const p2 = deferred()
103+
waitForCb.mockImplementation(() => p2.promise)
104+
105+
p1.reject('p1 rejection (should not fail this test)')
106+
await sleep(50)
107+
108+
expect(waitForCb).toHaveBeenCalledTimes(1)
109+
p2.resolve()
110+
111+
await waitForPromise
112+
})
113+
114+
test('when a promise is returned, if that is not resolved within the timeout, then waitFor is rejected', async () => {
115+
const sleep = t => new Promise(r => setTimeout(r, t))
116+
const {promise} = deferred()
117+
const waitForError = waitFor(() => promise, {timeout: 1}).catch(e => e)
118+
await sleep(5)
119+
120+
expect((await waitForError).message).toMatchInlineSnapshot(
121+
`Timed out in waitFor.`,
122+
)
123+
})
124+
125+
test('does not work after it resolves', async () => {
126+
jest.useFakeTimers('modern')
127+
let context = 'initial'
128+
129+
/** @type {import('../').FakeClock} */
130+
const clock = {
131+
// @testing-library/react usage to ensure `IS_REACT_ACT_ENVIRONMENT` is set when acting.
132+
advanceTimersByTime: async timeoutMS => {
133+
const originalContext = context
134+
context = 'no-act'
135+
try {
136+
jest.advanceTimersByTime(timeoutMS)
137+
} finally {
138+
context = originalContext
139+
}
140+
},
141+
}
142+
143+
let data = null
144+
setTimeout(() => {
145+
data = 'resolved'
146+
}, 100)
147+
148+
await waitFor(
149+
() => {
150+
// eslint-disable-next-line jest/no-conditional-in-test -- false-positive
151+
if (data === null) {
152+
throw new Error('not found')
153+
}
154+
},
155+
{clock, interval: 50},
156+
)
157+
158+
expect(context).toEqual('initial')
159+
160+
await Promise.resolve()
161+
162+
expect(context).toEqual('initial')
163+
})

0 commit comments

Comments
 (0)