The waitForElementToDisappear extension waits for a target element to disappear before the journey continues. It can work with element details stored from the page or with a selector string supplied directly to the extension.
This is useful when testing dynamic applications where loaders, progress bars, toast messages, blocking overlays, or temporary confirmation elements must no longer be visible before the next assertion or interaction is attempted.
Parameters:
-
elementrequired, the target element to wait for. This can be stored Virtuoso element details fromstore element details of ... in $element, a CSS selector string such as#loader, or an XPath selector string beginning with/.
Note: This extension waits until the supplied element can no longer be found in the current page context, or until it is found but is no longer visibly rendered according to its layout dimensions. Pass either stored Virtuoso element details or a valid CSS/XPath selector string.
How to apply this to your journey
Use the extension in a journey by calling waitForElementToDisappear after the action that should make the element disappear. Pass the stored element details or selector value to the element input.
Note: The extension runs asynchronously and reports completion through done(). If the element remains visible for the full retry window, the step fails with Element was still visible.
store element details of "Element that will disappear" in $element
execute "waitForElementToDisappear" using "$element" as elementexecute "waitForElementToDisappear" using "#loading-bar" as element
execute "waitForElementToDisappear" using "//*[@data-testid='saving-spinner']" as elementYou can also pass the element details from a variable when the target was identified earlier in the journey.
execute "waitForElementToDisappear" using "$element" as elementstore element details of "Loading spinner" in $element
click on "Submit"
execute "waitForElementToDisappear" using "$element" as element
look for "Saved successfully"This extension does not return a data value. If the element disappears or becomes non-visible within the retry window, the step completes successfully. If it remains visible, the step fails.
Step completed successfully when the element is no longer found or no longer visibly rendered.This extension does not require any external resource.
The extension should be configured as:
- Run asynchronously: Yes
- Scope: Global
Limitation: This extension runs in the active browser page context and depends on DOM lookup and layout-based visibility checks. It checks CSS selectors with document.querySelector(), XPath selectors with document.evaluate(), IDs with document.getElementById(), and considers an element visible when offsetWidth, offsetHeight, or getClientRects().length returns a layout result. This means it treats a missing element and a zero-layout hidden element as disappeared, but it may not match every visual or accessibility state, such as opacity-only hiding, off-screen positioning, overlays, animations, virtualized content, or elements hidden inside inaccessible frames or closed shadow DOM. The source retries 1000 times with a 50 ms delay, so its practical wait is about 50 seconds before it fails, even though async extension scripts must also complete within Virtuoso's documented 120-second maximum execution window. Cross-browser note: because this extension uses DOM querying, XPath evaluation, and layout properties, behavior can differ from Virtuoso's default Chromium-based browser in Safari, Firefox, Edge, iOS, Android, or remote-grid executions, especially for responsive layouts, mobile viewports, animations, iframes, and page re-rendering. Validate it in each browser/device configuration used by your plan.
Add the extension to your Virtuoso instance
Select the domain that matches your Virtuoso account.
View source
Last updated: 08/04/2021
Resources:
This extension does not require any external resource.
// Last updated: 08/04/2021, 16:03:54 UTC
if (!element) throw new Error('No element was supplied')
function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
function findElement(element) {
try {
element = JSON.parse(element)
} catch (e) { }
selectors = (typeof element === 'string') ? [{ type: element.startsWith('/') ? 'XPATH' : 'CSS_SELECTOR', value: element }] : element.selectors
for (var i in selectors) {
var selector = selectors[i]
if (!selector.type) continue
var el = null
switch (selector.type) {
case 'XPATH_ID':
case 'XPATH':
el = getElementByXpath(selector.value)
break
case 'CSS_SELECTOR':
el = document.querySelector(selector.value)
break
case 'ID':
el = document.getElementById(selector.value)
break
}
if (el) return el
}
return null
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
var MAX_RETRIES = 1000
var SLEEP_INTERVAL = 50 // ms
function isVisible(e) {
return !!( e.offsetWidth || e.offsetHeight || e.getClientRects().length );
}
async function waitForElementDisappear(element) {
let retries = 0
while (retries < MAX_RETRIES) {
var el = findElement(element)
if (!el || !isVisible(el)) return true
retries++
await sleep(SLEEP_INTERVAL)
}
return false
}
async function waitForElementAttribute(element) {
var el = await waitForElementDisappear(element)
if (!el) throw new Error('Element was still visible')
}
waitForElementAttribute(element).then(done).catch(doneError)
Comments
0 comments
Please sign in to leave a comment.