-
Notifications
You must be signed in to change notification settings - Fork 470
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(wait): wait will now also run your callback on DOM changes
Closes #376
- Loading branch information
1 parent
6084f53
commit 6105400
Showing
8 changed files
with
186 additions
and
148 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,48 @@ | ||
import { | ||
getDocument, | ||
newMutationObserver, | ||
setImmediate, | ||
setTimeout, | ||
clearTimeout, | ||
runWithRealTimers, | ||
} from './helpers' | ||
import {getConfig} from './config' | ||
import {wait} from './wait' | ||
|
||
function waitForElementToBeRemoved( | ||
callback, | ||
{ | ||
container = getDocument(), | ||
timeout = getConfig().asyncUtilTimeout, | ||
mutationObserverOptions = { | ||
subtree: true, | ||
childList: true, | ||
attributes: true, | ||
characterData: true, | ||
}, | ||
} = {}, | ||
) { | ||
return new Promise((resolve, reject) => { | ||
if (typeof callback !== 'function') { | ||
reject( | ||
new Error( | ||
'waitForElementToBeRemoved requires a function as the first parameter', | ||
), | ||
) | ||
} | ||
const timer = setTimeout(onTimeout, timeout) | ||
const observer = newMutationObserver(onMutation) | ||
const isRemoved = result => !result || (Array.isArray(result) && !result.length) | ||
|
||
async function waitForElementToBeRemoved(callback, options) { | ||
if (!callback) { | ||
return Promise.reject( | ||
new Error( | ||
'waitForElementToBeRemoved requires a callback as the first parameter', | ||
), | ||
) | ||
} | ||
|
||
// Check if the element is not present synchronously, | ||
// As the name implies, waitForElementToBeRemoved should check `present` --> `removed` | ||
if (isRemoved(callback())) { | ||
throw new Error( | ||
'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.', | ||
) | ||
} | ||
|
||
// Check if the element is not present synchronously, | ||
// As the name waitForElementToBeRemoved should check `present` --> `removed` | ||
return wait(() => { | ||
let result | ||
try { | ||
const result = callback() | ||
if (!result || (Array.isArray(result) && !result.length)) { | ||
onDone( | ||
new Error( | ||
'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.', | ||
), | ||
) | ||
} else { | ||
// Only observe for mutations only if there is element while checking synchronously | ||
runWithRealTimers(() => | ||
observer.observe(container, mutationObserverOptions), | ||
) | ||
} | ||
result = callback() | ||
} catch (error) { | ||
onDone(error) | ||
} | ||
|
||
function onDone(error, result) { | ||
clearTimeout(timer) | ||
setImmediate(() => observer.disconnect()) | ||
if (error) { | ||
reject(error) | ||
} else { | ||
resolve(result) | ||
} | ||
} | ||
function onMutation() { | ||
try { | ||
const result = callback() | ||
if (!result || (Array.isArray(result) && !result.length)) { | ||
onDone(null, true) | ||
} | ||
// If `callback` returns truthy value, wait for the next mutation or timeout. | ||
} catch (error) { | ||
onDone(null, true) | ||
if (error.message && error.message.startsWith('Unable to find')) { | ||
// All of our get* queries throw an error that starts with "Unable to find" | ||
// when it fails to find an element. | ||
// TODO: make the queries throw a special kind of error | ||
// so we can be more explicit about the check. | ||
return true | ||
} | ||
throw error | ||
} | ||
function onTimeout() { | ||
onDone(new Error('Timed out in waitForElementToBeRemoved.'), null) | ||
if (!isRemoved(result)) { | ||
throw new Error('Timed out in waitForElementToBeRemoved.') | ||
} | ||
}) | ||
return true | ||
}, options) | ||
} | ||
|
||
function waitForElementToBeRemovedWrapper(...args) { | ||
return getConfig().asyncWrapper(() => waitForElementToBeRemoved(...args)) | ||
} | ||
export {waitForElementToBeRemoved} | ||
|
||
export {waitForElementToBeRemovedWrapper as waitForElementToBeRemoved} | ||
/* | ||
eslint | ||
require-await: "off" | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,71 +1,21 @@ | ||
import { | ||
newMutationObserver, | ||
getDocument, | ||
setImmediate, | ||
setTimeout, | ||
clearTimeout, | ||
runWithRealTimers, | ||
} from './helpers' | ||
import {getConfig} from './config' | ||
import {wait} from './wait' | ||
|
||
function waitForElement( | ||
callback, | ||
{ | ||
container = getDocument(), | ||
timeout = getConfig().asyncUtilTimeout, | ||
mutationObserverOptions = { | ||
subtree: true, | ||
childList: true, | ||
attributes: true, | ||
characterData: true, | ||
}, | ||
} = {}, | ||
) { | ||
return new Promise((resolve, reject) => { | ||
if (typeof callback !== 'function') { | ||
reject( | ||
new Error('waitForElement requires a callback as the first parameter'), | ||
) | ||
return | ||
async function waitForElement(callback, options) { | ||
if (!callback) { | ||
throw new Error('waitForElement requires a callback as the first parameter') | ||
} | ||
return wait(() => { | ||
const result = callback() | ||
if (!result) { | ||
throw new Error('Timed out in waitForElement.') | ||
} | ||
let lastError | ||
const timer = setTimeout(onTimeout, timeout) | ||
|
||
const observer = newMutationObserver(onMutation) | ||
runWithRealTimers(() => | ||
observer.observe(container, mutationObserverOptions), | ||
) | ||
function onDone(error, result) { | ||
clearTimeout(timer) | ||
setImmediate(() => observer.disconnect()) | ||
if (error) { | ||
reject(error) | ||
} else { | ||
resolve(result) | ||
} | ||
} | ||
function onMutation() { | ||
try { | ||
const result = callback() | ||
if (result) { | ||
onDone(null, result) | ||
} | ||
// If `callback` returns falsy value, wait for the next mutation or timeout. | ||
} catch (error) { | ||
// Save the callback error to reject the promise with it. | ||
lastError = error | ||
// If `callback` throws an error, wait for the next mutation or timeout. | ||
} | ||
} | ||
function onTimeout() { | ||
onDone(lastError || new Error('Timed out in waitForElement.'), null) | ||
} | ||
onMutation() | ||
}) | ||
return result | ||
}, options) | ||
} | ||
|
||
function waitForElementWrapper(...args) { | ||
return getConfig().asyncWrapper(() => waitForElement(...args)) | ||
} | ||
export {waitForElement} | ||
|
||
export {waitForElementWrapper as waitForElement} | ||
/* | ||
eslint | ||
require-await: "off" | ||
*/ |
Oops, something went wrong.