| // Licensed to the Software Freedom Conservancy (SFC) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The SFC licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| /** |
| * @fileoverview Defines common conditions for use with |
| * {@link webdriver.WebDriver#wait WebDriver wait}. |
| * |
| * Sample usage: |
| * |
| * driver.get('http://www.google.com/ncr'); |
| * |
| * var query = driver.wait(until.elementLocated(By.name('q'))); |
| * query.sendKeys('webdriver\n'); |
| * |
| * driver.wait(until.titleIs('webdriver - Google Search')); |
| * |
| * To define a custom condition, simply call WebDriver.wait with a function |
| * that will eventually return a truthy-value (neither null, undefined, false, |
| * 0, or the empty string): |
| * |
| * driver.wait(function() { |
| * return driver.getTitle().then(function(title) { |
| * return title === 'webdriver - Google Search'; |
| * }); |
| * }, 1000); |
| */ |
| |
| 'use strict' |
| |
| const by = require('./by') |
| const error = require('./error') |
| const webdriver = require('./webdriver'), |
| Condition = webdriver.Condition, |
| WebElementCondition = webdriver.WebElementCondition |
| |
| /** |
| * Creates a condition that will wait until the input driver is able to switch |
| * to the designated frame. The target frame may be specified as |
| * |
| * 1. a numeric index into |
| * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames) |
| * for the currently selected frame. |
| * 2. a {@link ./webdriver.WebElement}, which must reference a FRAME or IFRAME |
| * element on the current page. |
| * 3. a locator which may be used to first locate a FRAME or IFRAME on the |
| * current page before attempting to switch to it. |
| * |
| * Upon successful resolution of this condition, the driver will be left |
| * focused on the new frame. |
| * |
| * @param {!(number|./webdriver.WebElement|By| |
| * function(!./webdriver.WebDriver): !./webdriver.WebElement)} frame |
| * The frame identifier. |
| * @return {!Condition<boolean>} A new condition. |
| */ |
| function ableToSwitchToFrame(frame) { |
| let condition |
| if (typeof frame === 'number' || frame instanceof webdriver.WebElement) { |
| condition = (driver) => attemptToSwitchFrames(driver, frame) |
| } else { |
| condition = function (driver) { |
| let locator = /** @type {!(By|Function)} */ (frame) |
| return driver.findElements(locator).then(function (els) { |
| if (els.length) { |
| return attemptToSwitchFrames(driver, els[0]) |
| } |
| }) |
| } |
| } |
| |
| return new Condition('to be able to switch to frame', condition) |
| |
| function attemptToSwitchFrames(driver, frame) { |
| return driver |
| .switchTo() |
| .frame(frame) |
| .then( |
| function () { |
| return true |
| }, |
| function (e) { |
| if (!(e instanceof error.NoSuchFrameError)) { |
| throw e |
| } |
| }, |
| ) |
| } |
| } |
| |
| /** |
| * Creates a condition that waits for an alert to be opened. Upon success, the |
| * returned promise will be fulfilled with the handle for the opened alert. |
| * |
| * @return {!Condition<!./webdriver.Alert>} The new condition. |
| */ |
| function alertIsPresent() { |
| return new Condition('for alert to be present', function (driver) { |
| return driver |
| .switchTo() |
| .alert() |
| .catch(function (e) { |
| if ( |
| !( |
| e instanceof error.NoSuchAlertError || |
| // XXX: Workaround for GeckoDriver error `TypeError: can't convert null |
| // to object`. For more details, see |
| // https://github.com/SeleniumHQ/selenium/pull/2137 |
| (e instanceof error.WebDriverError && e.message === `can't convert null to object`) |
| ) |
| ) { |
| throw e |
| } |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the current page's title to match the |
| * given value. |
| * |
| * @param {string} title The expected page title. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function titleIs(title) { |
| return new Condition('for title to be ' + JSON.stringify(title), function (driver) { |
| return driver.getTitle().then(function (t) { |
| return t === title |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the current page's title to contain |
| * the given substring. |
| * |
| * @param {string} substr The substring that should be present in the page |
| * title. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function titleContains(substr) { |
| return new Condition('for title to contain ' + JSON.stringify(substr), function (driver) { |
| return driver.getTitle().then(function (title) { |
| return title.indexOf(substr) !== -1 |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the current page's title to match the |
| * given regular expression. |
| * |
| * @param {!RegExp} regex The regular expression to test against. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function titleMatches(regex) { |
| return new Condition('for title to match ' + regex, function (driver) { |
| return driver.getTitle().then(function (title) { |
| return regex.test(title) |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the current page's url to match the |
| * given value. |
| * |
| * @param {string} url The expected page url. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function urlIs(url) { |
| return new Condition('for URL to be ' + JSON.stringify(url), function (driver) { |
| return driver.getCurrentUrl().then(function (u) { |
| return u === url |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the current page's url to contain |
| * the given substring. |
| * |
| * @param {string} substrUrl The substring that should be present in the current |
| * URL. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function urlContains(substrUrl) { |
| return new Condition('for URL to contain ' + JSON.stringify(substrUrl), function (driver) { |
| return driver.getCurrentUrl().then(function (url) { |
| return url && url.includes(substrUrl) |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the current page's url to match the |
| * given regular expression. |
| * |
| * @param {!RegExp} regex The regular expression to test against. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function urlMatches(regex) { |
| return new Condition('for URL to match ' + regex, function (driver) { |
| return driver.getCurrentUrl().then(function (url) { |
| return regex.test(url) |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will loop until an element is |
| * {@link ./webdriver.WebDriver#findElement found} with the given locator. |
| * |
| * @param {!(By|Function)} locator The locator to use. |
| * @return {!WebElementCondition} The new condition. |
| */ |
| function elementLocated(locator) { |
| locator = by.checkedLocator(locator) |
| let locatorStr = typeof locator === 'function' ? 'by function()' : locator + '' |
| return new WebElementCondition('for element to be located ' + locatorStr, function (driver) { |
| return driver.findElements(locator).then(function (elements) { |
| return elements[0] |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will loop until at least one element is |
| * {@link ./webdriver.WebDriver#findElement found} with the given locator. |
| * |
| * @param {!(By|Function)} locator The locator to use. |
| * @return {!Condition<!Array<!./webdriver.WebElement>>} The new |
| * condition. |
| */ |
| function elementsLocated(locator) { |
| locator = by.checkedLocator(locator) |
| let locatorStr = typeof locator === 'function' ? 'by function()' : locator + '' |
| return new Condition('for at least one element to be located ' + locatorStr, function (driver) { |
| return driver.findElements(locator).then(function (elements) { |
| return elements.length > 0 ? elements : null |
| }) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to become stale. An |
| * element is considered stale once it is removed from the DOM, or a new page |
| * has loaded. |
| * |
| * @param {!./webdriver.WebElement} element The element that should become stale. |
| * @return {!Condition<boolean>} The new condition. |
| */ |
| function stalenessOf(element) { |
| return new Condition('element to become stale', function () { |
| return element.getTagName().then( |
| function () { |
| return false |
| }, |
| function (e) { |
| if (e instanceof error.StaleElementReferenceError) { |
| return true |
| } |
| throw e |
| }, |
| ) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to become visible. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @return {!WebElementCondition} The new condition. |
| * @see ./webdriver.WebDriver#isDisplayed |
| */ |
| function elementIsVisible(element) { |
| return new WebElementCondition('until element is visible', function () { |
| return element.isDisplayed().then((v) => (v ? element : null)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to be in the DOM, |
| * yet not visible to the user. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @return {!WebElementCondition} The new condition. |
| * @see ./webdriver.WebDriver#isDisplayed |
| */ |
| function elementIsNotVisible(element) { |
| return new WebElementCondition('until element is not visible', function () { |
| return element.isDisplayed().then((v) => (v ? null : element)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to be enabled. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#isEnabled |
| */ |
| function elementIsEnabled(element) { |
| return new WebElementCondition('until element is enabled', function () { |
| return element.isEnabled().then((v) => (v ? element : null)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to be disabled. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#isEnabled |
| */ |
| function elementIsDisabled(element) { |
| return new WebElementCondition('until element is disabled', function () { |
| return element.isEnabled().then((v) => (v ? null : element)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to be selected. |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#isSelected |
| */ |
| function elementIsSelected(element) { |
| return new WebElementCondition('until element is selected', function () { |
| return element.isSelected().then((v) => (v ? element : null)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element to be deselected. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#isSelected |
| */ |
| function elementIsNotSelected(element) { |
| return new WebElementCondition('until element is not selected', function () { |
| return element.isSelected().then((v) => (v ? null : element)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element's |
| * {@link webdriver.WebDriver#getText visible text} to match the given |
| * {@code text} exactly. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @param {string} text The expected text. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#getText |
| */ |
| function elementTextIs(element, text) { |
| return new WebElementCondition('until element text is', function () { |
| return element.getText().then((t) => (t === text ? element : null)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element's |
| * {@link webdriver.WebDriver#getText visible text} to contain the given |
| * substring. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @param {string} substr The substring to search for. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#getText |
| */ |
| function elementTextContains(element, substr) { |
| return new WebElementCondition('until element text contains', function () { |
| return element.getText().then((t) => (t.indexOf(substr) != -1 ? element : null)) |
| }) |
| } |
| |
| /** |
| * Creates a condition that will wait for the given element's |
| * {@link webdriver.WebDriver#getText visible text} to match a regular |
| * expression. |
| * |
| * @param {!./webdriver.WebElement} element The element to test. |
| * @param {!RegExp} regex The regular expression to test against. |
| * @return {!WebElementCondition} The new condition. |
| * @see webdriver.WebDriver#getText |
| */ |
| function elementTextMatches(element, regex) { |
| return new WebElementCondition('until element text matches', function () { |
| return element.getText().then((t) => (regex.test(t) ? element : null)) |
| }) |
| } |
| |
| // PUBLIC API |
| |
| module.exports = { |
| elementTextMatches, |
| elementTextContains, |
| elementTextIs, |
| elementIsNotSelected, |
| elementIsSelected, |
| elementIsDisabled, |
| ableToSwitchToFrame, |
| elementIsEnabled, |
| elementIsNotVisible, |
| elementIsVisible, |
| stalenessOf, |
| elementsLocated, |
| elementLocated, |
| urlMatches, |
| urlContains, |
| urlIs, |
| titleMatches, |
| titleContains, |
| alertIsPresent, |
| titleIs, |
| } |