| // 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. |
| |
| 'use strict' |
| |
| const assert = require('node:assert') |
| |
| const By = require('selenium-webdriver/lib/by').By |
| const CommandName = require('selenium-webdriver/lib/command').Name |
| const error = require('selenium-webdriver/lib/error') |
| const until = require('selenium-webdriver/lib/until') |
| const webdriver = require('selenium-webdriver/lib/webdriver') |
| const WebElement = webdriver.WebElement |
| |
| describe('until', function () { |
| let driver, executor |
| |
| class TestExecutor { |
| constructor() { |
| this.handlers_ = {} |
| } |
| |
| on(cmd, handler) { |
| this.handlers_[cmd] = handler |
| return this |
| } |
| |
| execute(cmd) { |
| let self = this |
| return Promise.resolve().then(function () { |
| if (!self.handlers_[cmd.getName()]) { |
| throw new error.UnknownCommandError(cmd.getName()) |
| } |
| return self.handlers_[cmd.getName()](cmd) |
| }) |
| } |
| } |
| |
| function fail(opt_msg) { |
| throw new assert.AssertionError({ message: opt_msg }) |
| } |
| |
| beforeEach(function setUp() { |
| executor = new TestExecutor() |
| driver = new webdriver.WebDriver('session-id', executor) |
| }) |
| |
| describe('ableToSwitchToFrame', function () { |
| it('failsFastForNonSwitchErrors', function () { |
| let e = Error('boom') |
| executor.on(CommandName.SWITCH_TO_FRAME, function () { |
| throw e |
| }) |
| return driver.wait(until.ableToSwitchToFrame(0), 100).then(fail, (e2) => assert.strictEqual(e2, e)) |
| }) |
| |
| const ELEMENT_ID = 'some-element-id' |
| const ELEMENT_INDEX = 1234 |
| |
| function onSwitchFrame(expectedId) { |
| if (typeof expectedId === 'string') { |
| expectedId = WebElement.buildId(expectedId) |
| } else { |
| assert.strictEqual(typeof expectedId, 'number', 'must be string or number') |
| } |
| return (cmd) => { |
| assert.deepStrictEqual(cmd.getParameter('id'), expectedId, 'frame ID not specified') |
| return true |
| } |
| } |
| |
| it('byIndex', function () { |
| executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_INDEX)) |
| return driver.wait(until.ableToSwitchToFrame(ELEMENT_INDEX), 100) |
| }) |
| |
| it('byWebElement', function () { |
| executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID)) |
| |
| var el = new webdriver.WebElement(driver, ELEMENT_ID) |
| return driver.wait(until.ableToSwitchToFrame(el), 100) |
| }) |
| |
| it('byWebElementPromise', function () { |
| executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID)) |
| var el = new webdriver.WebElementPromise(driver, Promise.resolve(new webdriver.WebElement(driver, ELEMENT_ID))) |
| return driver.wait(until.ableToSwitchToFrame(el), 100) |
| }) |
| |
| it('byLocator', function () { |
| executor.on(CommandName.FIND_ELEMENTS, () => [WebElement.buildId(ELEMENT_ID)]) |
| executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID)) |
| return driver.wait(until.ableToSwitchToFrame(By.id('foo')), 100) |
| }) |
| |
| it('byLocator_elementNotInitiallyFound', function () { |
| let foundResponses = [[], [], [WebElement.buildId(ELEMENT_ID)]] |
| executor.on(CommandName.FIND_ELEMENTS, () => foundResponses.shift()) |
| executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID)) |
| |
| return driver |
| .wait(until.ableToSwitchToFrame(By.id('foo')), 2000) |
| .then(() => assert.deepStrictEqual(foundResponses, [])) |
| }) |
| |
| it('timesOutIfNeverAbletoSwitchFrames', function () { |
| var count = 0 |
| executor.on(CommandName.SWITCH_TO_FRAME, function () { |
| count += 1 |
| throw new error.NoSuchFrameError() |
| }) |
| |
| return driver.wait(until.ableToSwitchToFrame(0), 100).then(fail, function (e) { |
| assert.ok(count > 0) |
| assert.ok(e.message.startsWith('Waiting to be able to switch to frame'), 'Wrong message: ' + e.message) |
| }) |
| }) |
| }) |
| |
| describe('alertIsPresent', function () { |
| it('failsFastForNonAlertSwitchErrors', function () { |
| return driver.wait(until.alertIsPresent(), 100).then(fail, function (e) { |
| assert.ok(e instanceof error.UnknownCommandError) |
| assert.strictEqual(e.message, CommandName.GET_ALERT_TEXT) |
| }) |
| }) |
| |
| it('waitsForAlert', function () { |
| var count = 0 |
| executor |
| .on(CommandName.GET_ALERT_TEXT, function () { |
| if (count++ < 3) { |
| throw new error.NoSuchAlertError() |
| } else { |
| return true |
| } |
| }) |
| .on(CommandName.DISMISS_ALERT, () => true) |
| |
| return driver.wait(until.alertIsPresent(), 1000).then(function (alert) { |
| assert.strictEqual(count, 4) |
| return alert.dismiss() |
| }) |
| }) |
| |
| // TODO: Remove once GeckoDriver doesn't throw this unwanted error. |
| // See https://github.com/SeleniumHQ/selenium/pull/2137 |
| describe('workaround for GeckoDriver', function () { |
| it('doesNotFailWhenCannotConvertNullToObject', function () { |
| var count = 0 |
| executor |
| .on(CommandName.GET_ALERT_TEXT, function () { |
| if (count++ < 3) { |
| throw new error.WebDriverError(`can't convert null to object`) |
| } else { |
| return true |
| } |
| }) |
| .on(CommandName.DISMISS_ALERT, () => true) |
| |
| return driver.wait(until.alertIsPresent(), 1000).then(function (alert) { |
| assert.strictEqual(count, 4) |
| return alert.dismiss() |
| }) |
| }) |
| |
| it('keepsRaisingRegularWebdriverError', function () { |
| var webDriverError = new error.WebDriverError() |
| |
| executor.on(CommandName.GET_ALERT_TEXT, function () { |
| throw webDriverError |
| }) |
| |
| return driver.wait(until.alertIsPresent(), 1000).then( |
| function () { |
| throw new Error('driver did not fail against WebDriverError') |
| }, |
| function (error) { |
| assert.strictEqual(error, webDriverError) |
| }, |
| ) |
| }) |
| }) |
| }) |
| |
| it('testUntilTitleIs', function () { |
| var titles = ['foo', 'bar', 'baz'] |
| executor.on(CommandName.GET_TITLE, () => titles.shift()) |
| |
| return driver.wait(until.titleIs('bar'), 3000).then(function () { |
| assert.deepStrictEqual(titles, ['baz']) |
| }) |
| }) |
| |
| it('testUntilTitleContains', function () { |
| var titles = ['foo', 'froogle', 'google'] |
| executor.on(CommandName.GET_TITLE, () => titles.shift()) |
| |
| return driver.wait(until.titleContains('oogle'), 3000).then(function () { |
| assert.deepStrictEqual(titles, ['google']) |
| }) |
| }) |
| |
| it('testUntilTitleMatches', function () { |
| var titles = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google'] |
| executor.on(CommandName.GET_TITLE, () => titles.shift()) |
| |
| return driver.wait(until.titleMatches(/^a{2,3}b+c$/), 3000).then(function () { |
| assert.deepStrictEqual(titles, ['google']) |
| }) |
| }) |
| |
| it('testUntilUrlIs', function () { |
| var urls = ['http://www.foo.com', 'https://boo.com', 'http://docs.yes.com'] |
| executor.on(CommandName.GET_CURRENT_URL, () => urls.shift()) |
| |
| return driver.wait(until.urlIs('https://boo.com'), 3000).then(function () { |
| assert.deepStrictEqual(urls, ['http://docs.yes.com']) |
| }) |
| }) |
| |
| it('testUntilUrlContains', function () { |
| var urls = ['http://foo.com', 'https://groups.froogle.com', 'http://google.com'] |
| executor.on(CommandName.GET_CURRENT_URL, () => urls.shift()) |
| |
| return driver.wait(until.urlContains('oogle.com'), 3000).then(function () { |
| assert.deepStrictEqual(urls, ['http://google.com']) |
| }) |
| }) |
| |
| it('testUntilUrlMatches', function () { |
| var urls = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google'] |
| executor.on(CommandName.GET_CURRENT_URL, () => urls.shift()) |
| |
| return driver.wait(until.urlMatches(/^a{2,3}b+c$/), 3000).then(function () { |
| assert.deepStrictEqual(urls, ['google']) |
| }) |
| }) |
| |
| it('testUntilElementLocated', function () { |
| var responses = [[], [WebElement.buildId('abc123'), WebElement.buildId('foo')], ['end']] |
| executor.on(CommandName.FIND_ELEMENTS, () => responses.shift()) |
| |
| let element = driver.wait(until.elementLocated(By.id('quux')), 2000) |
| assert.ok(element instanceof webdriver.WebElementPromise) |
| return element.getId().then(function (id) { |
| assert.deepStrictEqual(responses, [['end']]) |
| assert.strictEqual(id, 'abc123') |
| }) |
| }) |
| |
| describe('untilElementLocated, elementNeverFound', function () { |
| function runNoElementFoundTest(locator, locatorStr) { |
| executor.on(CommandName.FIND_ELEMENTS, () => []) |
| |
| function expectedFailure() { |
| fail('expected condition to timeout') |
| } |
| |
| return driver.wait(until.elementLocated(locator), 100).then(expectedFailure, function (error) { |
| var expected = 'Waiting for element to be located ' + locatorStr |
| var lines = error.message.split(/\n/, 2) |
| assert.strictEqual(lines[0], expected) |
| |
| let regex = /^Wait timed out after \d+ms$/ |
| assert.ok(regex.test(lines[1]), `Lines <${lines[1]}> does not match ${regex}`) |
| }) |
| } |
| |
| it('byLocator', function () { |
| return runNoElementFoundTest(By.id('quux'), 'By(css selector, *[id="quux"])') |
| }) |
| |
| it('byHash', function () { |
| return runNoElementFoundTest({ id: 'quux' }, 'By(css selector, *[id="quux"])') |
| }) |
| |
| it('byFunction', function () { |
| return runNoElementFoundTest(function () {}, 'by function()') |
| }) |
| }) |
| |
| it('testUntilElementsLocated', function () { |
| var responses = [[], [WebElement.buildId('abc123'), WebElement.buildId('foo')], ['end']] |
| executor.on(CommandName.FIND_ELEMENTS, () => responses.shift()) |
| |
| return driver |
| .wait(until.elementsLocated(By.id('quux')), 2000) |
| .then(function (els) { |
| return Promise.all(els.map((e) => e.getId())) |
| }) |
| .then(function (ids) { |
| assert.deepStrictEqual(responses, [['end']]) |
| assert.strictEqual(ids.length, 2) |
| assert.strictEqual(ids[0], 'abc123') |
| assert.strictEqual(ids[1], 'foo') |
| }) |
| }) |
| |
| describe('untilElementsLocated, noElementsFound', function () { |
| function runNoElementsFoundTest(locator, locatorStr) { |
| executor.on(CommandName.FIND_ELEMENTS, () => []) |
| |
| function expectedFailure() { |
| fail('expected condition to timeout') |
| } |
| |
| return driver.wait(until.elementsLocated(locator), 100).then(expectedFailure, function (error) { |
| var expected = 'Waiting for at least one element to be located ' + locatorStr |
| var lines = error.message.split(/\n/, 2) |
| assert.strictEqual(lines[0], expected) |
| |
| let regex = /^Wait timed out after \d+ms$/ |
| assert.ok(regex.test(lines[1]), `Lines <${lines[1]}> does not match ${regex}`) |
| }) |
| } |
| |
| it('byLocator', function () { |
| return runNoElementsFoundTest(By.id('quux'), 'By(css selector, *[id="quux"])') |
| }) |
| |
| it('byHash', function () { |
| return runNoElementsFoundTest({ id: 'quux' }, 'By(css selector, *[id="quux"])') |
| }) |
| |
| it('byFunction', function () { |
| return runNoElementsFoundTest(function () {}, 'by function()') |
| }) |
| }) |
| |
| it('testUntilStalenessOf', function () { |
| let count = 0 |
| executor.on(CommandName.GET_ELEMENT_TAG_NAME, function () { |
| while (count < 3) { |
| count += 1 |
| return 'body' |
| } |
| throw new error.StaleElementReferenceError('now stale') |
| }) |
| |
| var el = new webdriver.WebElement(driver, { ELEMENT: 'foo' }) |
| return driver.wait(until.stalenessOf(el), 2000).then(() => assert.strictEqual(count, 3)) |
| }) |
| |
| describe('element state conditions', function () { |
| function runElementStateTest(predicate, command, responses, _var_args) { |
| let original = new webdriver.WebElement(driver, 'foo') |
| let predicateArgs = [original] |
| if (arguments.length > 3) { |
| predicateArgs = predicateArgs.concat(arguments[1]) |
| command = arguments[2] |
| responses = arguments[3] |
| } |
| |
| assert.ok(responses.length > 1) |
| |
| responses = responses.concat(['end']) |
| executor.on(command, () => responses.shift()) |
| |
| let result = driver.wait(predicate.apply(null, predicateArgs), 2000) |
| assert.ok(result instanceof webdriver.WebElementPromise) |
| return result |
| .then(function (value) { |
| assert.ok(value instanceof webdriver.WebElement) |
| assert.ok(!(value instanceof webdriver.WebElementPromise)) |
| return value.getId() |
| }) |
| .then(function (id) { |
| assert.strictEqual('foo', id) |
| assert.deepStrictEqual(responses, ['end']) |
| }) |
| } |
| |
| it('elementIsVisible', function () { |
| return runElementStateTest(until.elementIsVisible, CommandName.IS_ELEMENT_DISPLAYED, [false, false, true]) |
| }) |
| |
| it('elementIsNotVisible', function () { |
| return runElementStateTest(until.elementIsNotVisible, CommandName.IS_ELEMENT_DISPLAYED, [true, true, false]) |
| }) |
| |
| it('elementIsEnabled', function () { |
| return runElementStateTest(until.elementIsEnabled, CommandName.IS_ELEMENT_ENABLED, [false, false, true]) |
| }) |
| |
| it('elementIsDisabled', function () { |
| return runElementStateTest(until.elementIsDisabled, CommandName.IS_ELEMENT_ENABLED, [true, true, false]) |
| }) |
| |
| it('elementIsSelected', function () { |
| return runElementStateTest(until.elementIsSelected, CommandName.IS_ELEMENT_SELECTED, [false, false, true]) |
| }) |
| |
| it('elementIsNotSelected', function () { |
| return runElementStateTest(until.elementIsNotSelected, CommandName.IS_ELEMENT_SELECTED, [true, true, false]) |
| }) |
| |
| it('elementTextIs', function () { |
| return runElementStateTest(until.elementTextIs, 'foobar', CommandName.GET_ELEMENT_TEXT, [ |
| 'foo', |
| 'fooba', |
| 'foobar', |
| ]) |
| }) |
| |
| it('elementTextContains', function () { |
| return runElementStateTest(until.elementTextContains, 'bar', CommandName.GET_ELEMENT_TEXT, [ |
| 'foo', |
| 'foobaz', |
| 'foobarbaz', |
| ]) |
| }) |
| |
| it('elementTextMatches', function () { |
| return runElementStateTest(until.elementTextMatches, /fo+bar{3}/, CommandName.GET_ELEMENT_TEXT, [ |
| 'foo', |
| 'foobar', |
| 'fooobarrr', |
| ]) |
| }) |
| }) |
| |
| describe('WebElementCondition', function () { |
| it('fails if wait completes with a non-WebElement value', function () { |
| let result = driver.wait(new webdriver.WebElementCondition('testing', () => 123), 1000) |
| |
| return result.then( |
| () => assert.fail('expected to fail'), |
| function (e) { |
| assert.ok(e instanceof TypeError) |
| assert.strictEqual('WebElementCondition did not resolve to a WebElement: ' + '[object Number]', e.message) |
| }, |
| ) |
| }) |
| }) |
| }) |