Skip to content

Commit 8214a50

Browse files
committed
feat(waitForElementToBeRemoved): support passing an element directly (#460)
1 parent ab3e691 commit 8214a50

File tree

2 files changed

+69
-19
lines changed

2 files changed

+69
-19
lines changed

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

+49-7
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,35 @@ test('resolves on mutation if callback throws an error', async () => {
4545
await waitForElementToBeRemoved(() => getByTestId('div'), {timeout: 100})
4646
})
4747

48-
test('requires a function as the first parameter', () => {
48+
test('requires an element to exist first', () => {
4949
return expect(
50-
waitForElementToBeRemoved(),
50+
waitForElementToBeRemoved(null),
5151
).rejects.toThrowErrorMatchingInlineSnapshot(
52-
`"waitForElementToBeRemoved requires a callback as the first parameter"`,
52+
`"The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal."`,
5353
)
5454
})
5555

56-
test('requires an element to exist first', () => {
56+
test('requires an unempty array of elements to exist first', () => {
57+
return expect(
58+
waitForElementToBeRemoved([]),
59+
).rejects.toThrowErrorMatchingInlineSnapshot(
60+
`"The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal."`,
61+
)
62+
})
63+
64+
test('requires an element to exist first (function form)', () => {
5765
return expect(
5866
waitForElementToBeRemoved(() => null),
5967
).rejects.toThrowErrorMatchingInlineSnapshot(
60-
`"The callback function which was passed did not return an element or non-empty array of elements. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal."`,
68+
`"The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal."`,
6169
)
6270
})
6371

64-
test('requires an unempty array of elements to exist first', () => {
72+
test('requires an unempty array of elements to exist first (function form)', () => {
6573
return expect(
6674
waitForElementToBeRemoved(() => []),
6775
).rejects.toThrowErrorMatchingInlineSnapshot(
68-
`"The callback function which was passed did not return an element or non-empty array of elements. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal."`,
76+
`"The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal."`,
6977
)
7078
})
7179

@@ -132,3 +140,37 @@ test('rethrows non-testing-lib errors', () => {
132140
}),
133141
).rejects.toBe(error)
134142
})
143+
144+
test('accepts an element as an argument and waits for it to be removed from its top-most parent', async () => {
145+
const {queryByTestId} = renderIntoDocument(`
146+
<div data-testid="div"></div>
147+
`)
148+
const div = queryByTestId('div')
149+
setTimeout(() => {
150+
div.parentElement.removeChild(div)
151+
}, 20)
152+
153+
await waitForElementToBeRemoved(div, {timeout: 200})
154+
})
155+
156+
test('accepts an array of elements as an argument and waits for those elements to be removed from their top-most parent', async () => {
157+
const {queryAllByTestId} = renderIntoDocument(`
158+
<div>
159+
<div>
160+
<div data-testid="div"></div>
161+
</div>
162+
<div>
163+
<div data-testid="div"></div>
164+
</div>
165+
</div>
166+
`)
167+
const [div1, div2] = queryAllByTestId('div')
168+
setTimeout(() => {
169+
div1.parentElement.removeChild(div1)
170+
}, 20)
171+
172+
setTimeout(() => {
173+
div2.parentElement.removeChild(div2)
174+
}, 50)
175+
await waitForElementToBeRemoved([div1, div2], {timeout: 200})
176+
})

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

+20-12
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,31 @@ import {waitFor} from './wait-for'
22

33
const isRemoved = result => !result || (Array.isArray(result) && !result.length)
44

5-
async function waitForElementToBeRemoved(callback, options) {
6-
if (!callback) {
7-
return Promise.reject(
8-
new Error(
9-
'waitForElementToBeRemoved requires a callback as the first parameter',
10-
),
5+
// Check if the element is not present.
6+
// As the name implies, waitForElementToBeRemoved should check `present` --> `removed`
7+
function initialCheck(elements) {
8+
if (isRemoved(elements)) {
9+
throw new Error(
10+
'The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal.',
1111
)
1212
}
13+
}
1314

14-
// Check if the element is not present synchronously,
15-
// As the name implies, waitForElementToBeRemoved should check `present` --> `removed`
16-
if (isRemoved(callback())) {
17-
throw new Error(
18-
'The callback function which was passed did not return an element or non-empty array of elements. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal.',
19-
)
15+
async function waitForElementToBeRemoved(callback, options) {
16+
if (typeof callback !== 'function') {
17+
// await waitForElementToBeRemoved(getAllByText('Hello'))
18+
initialCheck(callback)
19+
const elements = Array.isArray(callback) ? callback : [callback]
20+
const getRemainingElements = elements.map(element => {
21+
let parent = element.parentElement
22+
while (parent.parentElement) parent = parent.parentElement
23+
return () => (parent.contains(element) ? element : null)
24+
})
25+
callback = () => getRemainingElements.map(c => c()).filter(Boolean)
2026
}
2127

28+
initialCheck(callback())
29+
2230
return waitFor(() => {
2331
let result
2432
try {

0 commit comments

Comments
 (0)