Skip to content

Commit 6bc8e2e

Browse files
authored
feat(waitFor): add onTimeout which adds DOM output to timeout errors (#671)
Closes #559
1 parent 86205e5 commit 6bc8e2e

6 files changed

+38
-8
lines changed

src/__tests__/base-queries-warn-on-invalid-container.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ describe('asynchronous queries throw on invalid container type', () => {
112112
['findAllByTestId', findAllByTestId],
113113
])('%s', (_queryName, query) => {
114114
const queryOptions = {}
115-
const waitOptions = {timeout: 1}
115+
const waitOptions = {timeout: 1, onTimeout: e => e}
116116
return expect(
117117
query(
118118
'invalid type for container',

src/__tests__/fake-timers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ test('fake timer timeout', async () => {
4242
() => {
4343
throw new Error('always throws')
4444
},
45-
{timeout: 10},
45+
{timeout: 10, onTimeout: e => e},
4646
),
4747
).rejects.toMatchInlineSnapshot(`[Error: always throws]`)
4848
})
@@ -53,7 +53,7 @@ test('times out after 1000ms by default', async () => {
5353
// there's a bug with this rule here...
5454
// eslint-disable-next-line jest/valid-expect
5555
await expect(
56-
waitForElementToBeRemoved(() => container),
56+
waitForElementToBeRemoved(() => container, {onTimeout: e => e}),
5757
).rejects.toThrowErrorMatchingInlineSnapshot(
5858
`"Timed out in waitForElementToBeRemoved."`,
5959
)

src/__tests__/wait-for-element-to-be-removed.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ test('rethrows non-testing-lib errors', () => {
9696
test('logs timeout error when it times out', async () => {
9797
const div = document.createElement('div')
9898
await expect(
99-
waitForElementToBeRemoved(() => div, {timeout: 1}),
99+
waitForElementToBeRemoved(() => div, {timeout: 1, onTimeout: e => e}),
100100
).rejects.toThrowErrorMatchingInlineSnapshot(
101101
`"Timed out in waitForElementToBeRemoved."`,
102102
)

src/__tests__/wait-for.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test('if no error is thrown then throws a timeout error', async () => {
3535
// eslint-disable-next-line no-throw-literal
3636
throw undefined
3737
},
38-
{timeout: 8, interval: 5},
38+
{timeout: 8, interval: 5, onTimeout: e => e},
3939
).catch(e => e)
4040
expect(result).toMatchInlineSnapshot(`[Error: Timed out in waitFor.]`)
4141
})
@@ -101,3 +101,27 @@ test('throws nice error if provided callback is not a function', () => {
101101
'Received `callback` arg must be a function',
102102
)
103103
})
104+
105+
test('timeout logs a pretty DOM', async () => {
106+
renderIntoDocument(`<div id="pretty">how pretty</div>`)
107+
const error = await waitFor(
108+
() => {
109+
throw new Error('always throws')
110+
},
111+
{timeout: 1},
112+
).catch(e => e)
113+
expect(error.message).toMatchInlineSnapshot(`
114+
"always throws
115+
116+
<html>
117+
<head />
118+
<body>
119+
<div
120+
id="pretty"
121+
>
122+
how pretty
123+
</div>
124+
</body>
125+
</html>"
126+
`)
127+
})

src/wait-for.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
clearTimeout,
1111
} from './helpers'
1212
import {getConfig, runWithExpensiveErrorDiagnosticsDisabled} from './config'
13+
import {prettyDOM} from './pretty-dom'
1314

1415
// This is so the stack trace the developer sees is one that's
1516
// closer to their code (because async stack traces are hard to follow).
@@ -25,6 +26,10 @@ function waitFor(
2526
showOriginalStackTrace = getConfig().showOriginalStackTrace,
2627
stackTraceError,
2728
interval = 50,
29+
onTimeout = error => {
30+
error.message = `${error.message}\n\n${prettyDOM(container)}`
31+
return error
32+
},
2833
mutationObserverOptions = {
2934
subtree: true,
3035
childList: true,
@@ -41,7 +46,7 @@ function waitFor(
4146
let lastError, intervalId, observer
4247
let finished = false
4348

44-
const overallTimeoutTimer = setTimeout(onTimeout, timeout)
49+
const overallTimeoutTimer = setTimeout(handleTimeout, timeout)
4550

4651
const usingFakeTimers = jestFakeTimersAreEnabled()
4752
if (usingFakeTimers) {
@@ -106,7 +111,7 @@ function waitFor(
106111
}
107112
}
108113

109-
function onTimeout() {
114+
function handleTimeout() {
110115
let error
111116
if (lastError) {
112117
error = lastError
@@ -122,7 +127,7 @@ function waitFor(
122127
copyStackTrace(error, stackTraceError)
123128
}
124129
}
125-
onDone(error, null)
130+
onDone(onTimeout(error), null)
126131
}
127132
})
128133
}

types/wait-for.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export interface waitForOptions {
22
container?: HTMLElement
33
timeout?: number
44
interval?: number
5+
onTimeout?: (error: Error) => Error
56
mutationObserverOptions?: MutationObserverInit
67
}
78

0 commit comments

Comments
 (0)