123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- // 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 promise = require('../../lib/promise');
- 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.equal(typeof expectedId, 'number', 'must be string or number');
- }
- return cmd => {
- assert.deepEqual(
- 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.deepEqual(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.equal(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.equal(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.equal(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.equal(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.equal(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.equal(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.equal(ids.length, 2);
- assert.equal(ids[0], 'abc123');
- assert.equal(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.equal(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.equal(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.equal('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.equal(
- 'WebElementCondition did not resolve to a WebElement: '
- + '[object Number]',
- e.message);
- });
- });
- });
- });
|