| // 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('assert') |
| |
| const By = require('../../lib/by').By |
| const CommandName = require('../../lib/command').Name |
| const error = require('../../lib/error') |
| const until = require('../../lib/until') |
| const webdriver = require('../../lib/webdriver'), |
| 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 |
| ) |
| } |
| ) |
| }) |
| }) |
| }) |