Skip to content

Commit c020cad

Browse files
author
Kent C. Dodds
committed
feat: add asyncWrapper config
1 parent 393377a commit c020cad

File tree

6 files changed

+137
-111
lines changed

6 files changed

+137
-111
lines changed

src/config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
// './queries' are query functions.
44
let config = {
55
testIdAttribute: 'data-testid',
6+
// this is to support React's async `act` function.
7+
// forcing react-testing-library to wrap all async functions would've been
8+
// a total nightmare (consider wrapping every findBy* query and then also
9+
// updating `within` so those would be wrapped too. Total nightmare).
10+
// so we have this config option that's really only intended for
11+
// react-testing-library to use. For that reason, this feature will remain
12+
// undocumented.
13+
asyncWrapper: cb => cb(),
614
}
715

816
export function configure(newConfig) {

src/wait-for-dom-change.js

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {newMutationObserver, getDocument, getSetImmediate} from './helpers'
2+
import {getConfig} from './config'
23

34
function waitForDomChange({
45
container = getDocument(),
@@ -10,30 +11,33 @@ function waitForDomChange({
1011
characterData: true,
1112
},
1213
} = {}) {
13-
return new Promise((resolve, reject) => {
14-
const setImmediate = getSetImmediate()
15-
const timer = setTimeout(onTimeout, timeout)
16-
const observer = newMutationObserver(onMutation)
17-
observer.observe(container, mutationObserverOptions)
14+
return getConfig().asyncWrapper(
15+
() =>
16+
new Promise((resolve, reject) => {
17+
const setImmediate = getSetImmediate()
18+
const timer = setTimeout(onTimeout, timeout)
19+
const observer = newMutationObserver(onMutation)
20+
observer.observe(container, mutationObserverOptions)
1821

19-
function onDone(error, result) {
20-
clearTimeout(timer)
21-
setImmediate(() => observer.disconnect())
22-
if (error) {
23-
reject(error)
24-
} else {
25-
resolve(result)
26-
}
27-
}
22+
function onDone(error, result) {
23+
clearTimeout(timer)
24+
setImmediate(() => observer.disconnect())
25+
if (error) {
26+
reject(error)
27+
} else {
28+
resolve(result)
29+
}
30+
}
2831

29-
function onMutation(mutationsList) {
30-
onDone(null, mutationsList)
31-
}
32+
function onMutation(mutationsList) {
33+
onDone(null, mutationsList)
34+
}
3235

33-
function onTimeout() {
34-
onDone(new Error('Timed out in waitForDomChange.'), null)
35-
}
36-
})
36+
function onTimeout() {
37+
onDone(new Error('Timed out in waitForDomChange.'), null)
38+
}
39+
}),
40+
)
3741
}
3842

3943
export {waitForDomChange}

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

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {getDocument, getSetImmediate, newMutationObserver} from './helpers'
2+
import {getConfig} from './config'
23

34
function waitForElementToBeRemoved(
45
callback,
@@ -13,60 +14,63 @@ function waitForElementToBeRemoved(
1314
},
1415
} = {},
1516
) {
16-
return new Promise((resolve, reject) => {
17-
if (typeof callback !== 'function') {
18-
reject(
19-
new Error(
20-
'waitForElementToBeRemoved requires a function as the first parameter',
21-
),
22-
)
23-
}
24-
const timer = setTimeout(onTimeout, timeout)
25-
const observer = newMutationObserver(onMutation)
17+
return getConfig().asyncWrapper(
18+
() =>
19+
new Promise((resolve, reject) => {
20+
if (typeof callback !== 'function') {
21+
reject(
22+
new Error(
23+
'waitForElementToBeRemoved requires a function as the first parameter',
24+
),
25+
)
26+
}
27+
const timer = setTimeout(onTimeout, timeout)
28+
const observer = newMutationObserver(onMutation)
2629

27-
// Check if the element is not present synchronously,
28-
// As the name waitForElementToBeRemoved should check `present` --> `removed`
29-
try {
30-
const result = callback()
31-
if (!result || (Array.isArray(result) && !result.length)) {
32-
onDone(
33-
new Error(
34-
'The callback function which was passed did not return an element or non-empty array of elements. waitForElementToBeRemoved requires that the element(s) exist before waiting for removal.',
35-
),
36-
)
37-
} else {
38-
// Only observe for mutations only if there is element while checking synchronously
39-
observer.observe(container, mutationObserverOptions)
40-
}
41-
} catch (error) {
42-
onDone(error)
43-
}
30+
// Check if the element is not present synchronously,
31+
// As the name waitForElementToBeRemoved should check `present` --> `removed`
32+
try {
33+
const result = callback()
34+
if (!result || (Array.isArray(result) && !result.length)) {
35+
onDone(
36+
new Error(
37+
'The callback function which was passed did not return an element or non-empty array of elements. waitForElementToBeRemoved requires that the element(s) exist before waiting for removal.',
38+
),
39+
)
40+
} else {
41+
// Only observe for mutations only if there is element while checking synchronously
42+
observer.observe(container, mutationObserverOptions)
43+
}
44+
} catch (error) {
45+
onDone(error)
46+
}
4447

45-
function onDone(error, result) {
46-
const setImmediate = getSetImmediate()
47-
clearTimeout(timer)
48-
setImmediate(() => observer.disconnect())
49-
if (error) {
50-
reject(error)
51-
} else {
52-
resolve(result)
53-
}
54-
}
55-
function onMutation() {
56-
try {
57-
const result = callback()
58-
if (!result || (Array.isArray(result) && !result.length)) {
59-
onDone(null, true)
48+
function onDone(error, result) {
49+
const setImmediate = getSetImmediate()
50+
clearTimeout(timer)
51+
setImmediate(() => observer.disconnect())
52+
if (error) {
53+
reject(error)
54+
} else {
55+
resolve(result)
56+
}
57+
}
58+
function onMutation() {
59+
try {
60+
const result = callback()
61+
if (!result || (Array.isArray(result) && !result.length)) {
62+
onDone(null, true)
63+
}
64+
// If `callback` returns truthy value, wait for the next mutation or timeout.
65+
} catch (error) {
66+
onDone(null, true)
67+
}
68+
}
69+
function onTimeout() {
70+
onDone(new Error('Timed out in waitForElementToBeRemoved.'), null)
6071
}
61-
// If `callback` returns truthy value, wait for the next mutation or timeout.
62-
} catch (error) {
63-
onDone(null, true)
64-
}
65-
}
66-
function onTimeout() {
67-
onDone(new Error('Timed out in waitForElementToBeRemoved.'), null)
68-
}
69-
})
72+
}),
73+
)
7074
}
7175

7276
export {waitForElementToBeRemoved}

src/wait-for-element.js

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {newMutationObserver, getDocument, getSetImmediate} from './helpers'
2+
import {getConfig} from './config'
23

34
function waitForElement(
45
callback,
@@ -13,45 +14,50 @@ function waitForElement(
1314
},
1415
} = {},
1516
) {
16-
return new Promise((resolve, reject) => {
17-
if (typeof callback !== 'function') {
18-
reject(
19-
new Error('waitForElement requires a callback as the first parameter'),
20-
)
21-
return
22-
}
23-
let lastError
24-
const timer = setTimeout(onTimeout, timeout)
25-
const observer = newMutationObserver(onMutation)
26-
observer.observe(container, mutationObserverOptions)
27-
function onDone(error, result) {
28-
const setImmediate = getSetImmediate()
29-
clearTimeout(timer)
30-
setImmediate(() => observer.disconnect())
31-
if (error) {
32-
reject(error)
33-
} else {
34-
resolve(result)
35-
}
36-
}
37-
function onMutation() {
38-
try {
39-
const result = callback()
40-
if (result) {
41-
onDone(null, result)
17+
return getConfig().asyncWrapper(
18+
() =>
19+
new Promise((resolve, reject) => {
20+
if (typeof callback !== 'function') {
21+
reject(
22+
new Error(
23+
'waitForElement requires a callback as the first parameter',
24+
),
25+
)
26+
return
4227
}
43-
// If `callback` returns falsy value, wait for the next mutation or timeout.
44-
} catch (error) {
45-
// Save the callback error to reject the promise with it.
46-
lastError = error
47-
// If `callback` throws an error, wait for the next mutation or timeout.
48-
}
49-
}
50-
function onTimeout() {
51-
onDone(lastError || new Error('Timed out in waitForElement.'), null)
52-
}
53-
onMutation()
54-
})
28+
let lastError
29+
const timer = setTimeout(onTimeout, timeout)
30+
const observer = newMutationObserver(onMutation)
31+
observer.observe(container, mutationObserverOptions)
32+
function onDone(error, result) {
33+
const setImmediate = getSetImmediate()
34+
clearTimeout(timer)
35+
setImmediate(() => observer.disconnect())
36+
if (error) {
37+
reject(error)
38+
} else {
39+
resolve(result)
40+
}
41+
}
42+
function onMutation() {
43+
try {
44+
const result = callback()
45+
if (result) {
46+
onDone(null, result)
47+
}
48+
// If `callback` returns falsy value, wait for the next mutation or timeout.
49+
} catch (error) {
50+
// Save the callback error to reject the promise with it.
51+
lastError = error
52+
// If `callback` throws an error, wait for the next mutation or timeout.
53+
}
54+
}
55+
function onTimeout() {
56+
onDone(lastError || new Error('Timed out in waitForElement.'), null)
57+
}
58+
onMutation()
59+
}),
60+
)
5561
}
5662

5763
export {waitForElement}

src/wait.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import waitForExpect from 'wait-for-expect'
2+
import {getConfig} from './config'
23

34
function wait(callback = () => {}, {timeout = 4500, interval = 50} = {}) {
4-
return waitForExpect(callback, timeout, interval)
5+
return getConfig().asyncWrapper(() =>
6+
waitForExpect(callback, timeout, interval),
7+
)
58
}
69

710
export {wait}

typings/config.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface IConfig {
22
testIdAttribute: string
3+
asyncWrapper<T>(cb: Function): Promise<T>
34
}
45

56
export interface IConfigFn {

0 commit comments

Comments
 (0)