Intelligent waiting mechanisms for handling dynamic web applications and timing-sensitive operations with built-in conditions and custom condition support.
Wait for various page state changes including title, URL, and alert presence.
/**
* Wait for exact page title match
* @param title - Expected title string
* @returns Condition that resolves when title matches
*/
function titleIs(title: string): Condition<boolean>;
/**
* Wait for page title to contain substring
* @param substr - Substring to search for in title
* @returns Condition that resolves when title contains substring
*/
function titleContains(substr: string): Condition<boolean>;
/**
* Wait for page title to match regular expression
* @param regex - Regular expression to match against title
* @returns Condition that resolves when title matches regex
*/
function titleMatches(regex: RegExp): Condition<boolean>;
/**
* Wait for exact URL match
* @param url - Expected URL string
* @returns Condition that resolves when URL matches
*/
function urlIs(url: string): Condition<boolean>;
/**
* Wait for URL to contain substring
* @param substrUrl - Substring to search for in URL
* @returns Condition that resolves when URL contains substring
*/
function urlContains(substrUrl: string): Condition<boolean>;
/**
* Wait for URL to match regular expression
* @param regex - Regular expression to match against URL
* @returns Condition that resolves when URL matches regex
*/
function urlMatches(regex: RegExp): Condition<boolean>;
/**
* Wait for alert dialog to be present
* @returns Condition that resolves to Alert when present
*/
function alertIsPresent(): Condition<Alert>;Usage Examples:
const { Builder, By, until } = require('selenium-webdriver');
let driver = await new Builder().forBrowser('chrome').build();
// Wait for page title changes
await driver.get('https://example.com');
await driver.wait(until.titleIs('Welcome to Example'), 5000);
// Wait for title to contain text
await driver.wait(until.titleContains('Loading'), 3000);
// Wait for URL changes after navigation
await driver.findElement(By.linkText('Next Page')).click();
await driver.wait(until.urlContains('/page2'), 5000);
// Wait for URL pattern match
await driver.wait(until.urlMatches(/\/user\/\d+/), 3000);
// Wait for alert and handle it
await driver.findElement(By.id('show-alert')).click();
let alert = await driver.wait(until.alertIsPresent(), 2000);
console.log('Alert text:', await alert.getText());
await alert.accept();Wait for elements to appear or disappear from the DOM.
/**
* Wait for element to be located in DOM
* @param locator - Element locator
* @returns Condition that resolves to WebElement when located
*/
function elementLocated(locator: By): Condition<WebElement>;
/**
* Wait for elements to be located in DOM
* @param locator - Element locator
* @returns Condition that resolves to WebElement array when located
*/
function elementsLocated(locator: By): Condition<WebElement[]>;
/**
* Wait for element to become stale (removed from DOM)
* @param element - WebElement to wait for staleness
* @returns Condition that resolves when element becomes stale
*/
function stalenessOf(element: WebElement): Condition<boolean>;Usage Examples:
// Wait for element to appear in DOM
let dynamicElement = await driver.wait(
until.elementLocated(By.id('dynamic-content')),
10000
);
// Wait for multiple elements
let listItems = await driver.wait(
until.elementsLocated(By.css('.list-item')),
5000
);
console.log(`Found ${listItems.length} list items`);
// Wait for element to be removed
let loadingSpinner = await driver.findElement(By.id('loading'));
await driver.wait(until.stalenessOf(loadingSpinner), 8000);
console.log('Loading spinner disappeared');Wait for elements to reach specific states like visibility, interactability, and selection.
/**
* Wait for element to be visible
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves to WebElement when visible
*/
function elementIsVisible(elementOrLocator: WebElement | By): Condition<WebElement>;
/**
* Wait for element to be invisible/hidden
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves when element is not visible
*/
function elementIsNotVisible(elementOrLocator: WebElement | By): Condition<boolean>;
/**
* Wait for element to be enabled
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves to WebElement when enabled
*/
function elementIsEnabled(elementOrLocator: WebElement | By): Condition<WebElement>;
/**
* Wait for element to be disabled
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves when element is disabled
*/
function elementIsDisabled(elementOrLocator: WebElement | By): Condition<boolean>;
/**
* Wait for element to be selected (checkboxes, radio buttons, options)
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves when element is selected
*/
function elementIsSelected(elementOrLocator: WebElement | By): Condition<boolean>;
/**
* Wait for element to be deselected
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves when element is not selected
*/
function elementIsNotSelected(elementOrLocator: WebElement | By): Condition<boolean>;
/**
* Wait for element to be clickable (visible and enabled)
* @param elementOrLocator - WebElement or locator
* @returns Condition that resolves to WebElement when clickable
*/
function elementIsClickable(elementOrLocator: WebElement | By): Condition<WebElement>;Usage Examples:
// Wait for element to become visible
let hiddenPanel = await driver.wait(
until.elementIsVisible(By.id('info-panel')),
5000
);
// Wait for loading indicator to disappear
await driver.wait(
until.elementIsNotVisible(By.className('loading-spinner')),
10000
);
// Wait for form field to be enabled
let submitButton = await driver.wait(
until.elementIsEnabled(By.id('submit-btn')),
3000
);
// Wait for checkbox to be selected
await driver.findElement(By.id('terms')).click();
await driver.wait(
until.elementIsSelected(By.id('terms')),
1000
);
// Wait for button to be clickable
let actionButton = await driver.wait(
until.elementIsClickable(By.id('action-btn')),
5000
);
await actionButton.click();Wait for element text content to match specific patterns.
/**
* Wait for element text to exactly match
* @param elementOrLocator - WebElement or locator
* @param text - Expected text content
* @returns Condition that resolves when text matches exactly
*/
function elementTextIs(elementOrLocator: WebElement | By, text: string): Condition<boolean>;
/**
* Wait for element text to contain substring
* @param elementOrLocator - WebElement or locator
* @param substr - Substring to search for
* @returns Condition that resolves when text contains substring
*/
function elementTextContains(elementOrLocator: WebElement | By, substr: string): Condition<boolean>;
/**
* Wait for element text to match regular expression
* @param elementOrLocator - WebElement or locator
* @param regex - Regular expression to match
* @returns Condition that resolves when text matches regex
*/
function elementTextMatches(elementOrLocator: WebElement | By, regex: RegExp): Condition<boolean>;Usage Examples:
// Wait for exact text match
await driver.wait(
until.elementTextIs(By.id('status'), 'Operation Complete'),
8000
);
// Wait for text to contain substring
await driver.wait(
until.elementTextContains(By.id('counter'), 'items processed'),
5000
);
// Wait for text pattern match
await driver.wait(
until.elementTextMatches(By.id('timestamp'), /\d{4}-\d{2}-\d{2}/),
3000
);
// Wait for dynamic content updates
let messageElement = await driver.findElement(By.id('message'));
await driver.findElement(By.id('update-btn')).click();
await driver.wait(
until.elementTextContains(messageElement, 'Updated successfully'),
4000
);Wait for frame availability and switching.
/**
* Wait until able to switch to frame
* @param frame - Frame locator, index, name, or WebElement
* @returns Condition that resolves when frame is available
*/
function ableToSwitchToFrame(frame: number | string | By | WebElement): Condition<boolean>;Usage Examples:
// Wait for frame to be available by index
await driver.wait(until.ableToSwitchToFrame(0), 5000);
// Wait for frame by name
await driver.wait(until.ableToSwitchToFrame('content-frame'), 3000);
// Wait for frame by locator
await driver.wait(
until.ableToSwitchToFrame(By.id('payment-frame')),
8000
);
// Wait for frame element
let frameElement = await driver.findElement(By.tagName('iframe'));
await driver.wait(until.ableToSwitchToFrame(frameElement), 4000);Create custom wait conditions for specific application needs.
/**
* Custom condition interface
*/
interface Condition<T> {
/** Description of what the condition waits for */
description(): string;
}
/**
* Create custom condition
* @param message - Description of the condition
* @param fn - Function that returns truthy value when condition is met
* @returns Custom condition
*/
function Condition<T>(message: string, fn: (driver: WebDriver) => T | Promise<T>): Condition<T>;Usage Examples:
const { Condition } = require('selenium-webdriver');
// Custom condition for page load
let pageFullyLoaded = new Condition('page fully loaded', async (driver) => {
let readyState = await driver.executeScript('return document.readyState');
let jQueryReady = await driver.executeScript('return typeof jQuery !== "undefined" ? jQuery.active === 0 : true');
return readyState === 'complete' && jQueryReady;
});
await driver.wait(pageFullyLoaded, 10000);
// Custom condition for element count
let minimumItemsLoaded = new Condition('at least 5 items loaded', async (driver) => {
let items = await driver.findElements(By.className('item'));
return items.length >= 5 ? items : false;
});
let items = await driver.wait(minimumItemsLoaded, 15000);
console.log(`Found ${items.length} items`);
// Custom condition for API response
let dataLoaded = new Condition('data loaded from API', async (driver) => {
let isLoaded = await driver.executeScript(`
return window.apiData && window.apiData.loaded === true;
`);
return isLoaded;
});
await driver.wait(dataLoaded, 30000);
// Custom condition with element interaction
let modalClosed = new Condition('modal dialog closed', async (driver) => {
let modals = await driver.findElements(By.className('modal-backdrop'));
return modals.length === 0;
});
await driver.findElement(By.className('modal-close')).click();
await driver.wait(modalClosed, 5000);The WebDriver wait method with timeout and custom error messages.
/**
* Wait for condition to be satisfied
* @param condition - Condition to wait for
* @param timeout - Maximum time to wait in milliseconds (default: 5000)
* @param message - Custom error message for timeout
* @returns Promise that resolves with condition result
*/
WebDriver.prototype.wait<T>(
condition: Condition<T>,
timeout?: number,
message?: string
): Promise<T>;Usage Examples:
// Basic wait with default timeout (5 seconds)
let element = await driver.wait(until.elementLocated(By.id('target')));
// Wait with custom timeout
let button = await driver.wait(
until.elementIsClickable(By.id('submit')),
10000 // 10 seconds
);
// Wait with custom error message
let result = await driver.wait(
until.titleContains('Dashboard'),
8000,
'Dashboard page did not load within 8 seconds'
);
// Combining multiple conditions
let loginComplete = new Condition('login process complete', async (driver) => {
try {
// Check if we're redirected to dashboard
let url = await driver.getCurrentUrl();
if (!url.includes('/dashboard')) return false;
// Check if user menu is visible
let menus = await driver.findElements(By.id('user-menu'));
if (menus.length === 0) return false;
// Check if loading indicators are gone
let loading = await driver.findElements(By.className('loading'));
return loading.length === 0;
} catch (error) {
return false;
}
});
await driver.wait(loginComplete, 15000, 'Login process did not complete successfully');Advanced waiting patterns for complex scenarios.
Usage Examples:
// Retry pattern with custom intervals
async function waitWithRetry(condition, maxAttempts = 5, interval = 1000) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await driver.wait(condition, interval);
} catch (error) {
if (attempt === maxAttempts) throw error;
console.log(`Attempt ${attempt} failed, retrying...`);
await driver.sleep(interval);
}
}
}
// Usage
let element = await waitWithRetry(
until.elementLocated(By.id('flaky-element')),
3,
2000
);
// Exponential backoff waiting
async function waitWithBackoff(condition, maxWait = 30000) {
let timeout = 1000;
let totalTime = 0;
while (totalTime < maxWait) {
try {
return await driver.wait(condition, timeout);
} catch (error) {
totalTime += timeout;
timeout = Math.min(timeout * 2, 5000); // Max 5 second intervals
if (totalTime >= maxWait) throw error;
await driver.sleep(500);
}
}
}
// Conditional waiting based on page state
let conditionalWait = new Condition('conditional element check', async (driver) => {
let pageType = await driver.executeScript('return document.body.dataset.pageType');
if (pageType === 'loading') {
// Wait for loading to complete
let loading = await driver.findElements(By.className('spinner'));
return loading.length === 0;
} else if (pageType === 'form') {
// Wait for form validation
let errors = await driver.findElements(By.className('error'));
return errors.length === 0;
} else {
// Default condition
return true;
}
});
await driver.wait(conditionalWait, 10000);