Path: blob/trunk/javascript/selenium-webdriver/test/lib/until_test.js
2885 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617'use strict'1819const assert = require('node:assert')2021const By = require('selenium-webdriver/lib/by').By22const CommandName = require('selenium-webdriver/lib/command').Name23const error = require('selenium-webdriver/lib/error')24const until = require('selenium-webdriver/lib/until')25const webdriver = require('selenium-webdriver/lib/webdriver')26const WebElement = webdriver.WebElement2728describe('until', function () {29let driver, executor3031class TestExecutor {32constructor() {33this.handlers_ = {}34}3536on(cmd, handler) {37this.handlers_[cmd] = handler38return this39}4041execute(cmd) {42let self = this43return Promise.resolve().then(function () {44if (!self.handlers_[cmd.getName()]) {45throw new error.UnknownCommandError(cmd.getName())46}47return self.handlers_[cmd.getName()](cmd)48})49}50}5152function fail(opt_msg) {53throw new assert.AssertionError({ message: opt_msg })54}5556beforeEach(function setUp() {57executor = new TestExecutor()58driver = new webdriver.WebDriver('session-id', executor)59})6061describe('ableToSwitchToFrame', function () {62it('failsFastForNonSwitchErrors', function () {63let e = Error('boom')64executor.on(CommandName.SWITCH_TO_FRAME, function () {65throw e66})67return driver.wait(until.ableToSwitchToFrame(0), 100).then(fail, (e2) => assert.strictEqual(e2, e))68})6970const ELEMENT_ID = 'some-element-id'71const ELEMENT_INDEX = 12347273function onSwitchFrame(expectedId) {74if (typeof expectedId === 'string') {75expectedId = WebElement.buildId(expectedId)76} else {77assert.strictEqual(typeof expectedId, 'number', 'must be string or number')78}79return (cmd) => {80assert.deepStrictEqual(cmd.getParameter('id'), expectedId, 'frame ID not specified')81return true82}83}8485it('byIndex', function () {86executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_INDEX))87return driver.wait(until.ableToSwitchToFrame(ELEMENT_INDEX), 100)88})8990it('byWebElement', function () {91executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID))9293var el = new webdriver.WebElement(driver, ELEMENT_ID)94return driver.wait(until.ableToSwitchToFrame(el), 100)95})9697it('byWebElementPromise', function () {98executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID))99var el = new webdriver.WebElementPromise(driver, Promise.resolve(new webdriver.WebElement(driver, ELEMENT_ID)))100return driver.wait(until.ableToSwitchToFrame(el), 100)101})102103it('byLocator', function () {104executor.on(CommandName.FIND_ELEMENTS, () => [WebElement.buildId(ELEMENT_ID)])105executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID))106return driver.wait(until.ableToSwitchToFrame(By.id('foo')), 100)107})108109it('byLocator_elementNotInitiallyFound', function () {110let foundResponses = [[], [], [WebElement.buildId(ELEMENT_ID)]]111executor.on(CommandName.FIND_ELEMENTS, () => foundResponses.shift())112executor.on(CommandName.SWITCH_TO_FRAME, onSwitchFrame(ELEMENT_ID))113114return driver115.wait(until.ableToSwitchToFrame(By.id('foo')), 2000)116.then(() => assert.deepStrictEqual(foundResponses, []))117})118119it('timesOutIfNeverAbletoSwitchFrames', function () {120var count = 0121executor.on(CommandName.SWITCH_TO_FRAME, function () {122count += 1123throw new error.NoSuchFrameError()124})125126return driver.wait(until.ableToSwitchToFrame(0), 100).then(fail, function (e) {127assert.ok(count > 0)128assert.ok(e.message.startsWith('Waiting to be able to switch to frame'), 'Wrong message: ' + e.message)129})130})131})132133describe('alertIsPresent', function () {134it('failsFastForNonAlertSwitchErrors', function () {135return driver.wait(until.alertIsPresent(), 100).then(fail, function (e) {136assert.ok(e instanceof error.UnknownCommandError)137assert.strictEqual(e.message, CommandName.GET_ALERT_TEXT)138})139})140141it('waitsForAlert', function () {142var count = 0143executor144.on(CommandName.GET_ALERT_TEXT, function () {145if (count++ < 3) {146throw new error.NoSuchAlertError()147} else {148return true149}150})151.on(CommandName.DISMISS_ALERT, () => true)152153return driver.wait(until.alertIsPresent(), 1000).then(function (alert) {154assert.strictEqual(count, 4)155return alert.dismiss()156})157})158159// TODO: Remove once GeckoDriver doesn't throw this unwanted error.160// See https://github.com/SeleniumHQ/selenium/pull/2137161describe('workaround for GeckoDriver', function () {162it('doesNotFailWhenCannotConvertNullToObject', function () {163var count = 0164executor165.on(CommandName.GET_ALERT_TEXT, function () {166if (count++ < 3) {167throw new error.WebDriverError(`can't convert null to object`)168} else {169return true170}171})172.on(CommandName.DISMISS_ALERT, () => true)173174return driver.wait(until.alertIsPresent(), 1000).then(function (alert) {175assert.strictEqual(count, 4)176return alert.dismiss()177})178})179180it('keepsRaisingRegularWebdriverError', function () {181var webDriverError = new error.WebDriverError()182183executor.on(CommandName.GET_ALERT_TEXT, function () {184throw webDriverError185})186187return driver.wait(until.alertIsPresent(), 1000).then(188function () {189throw new Error('driver did not fail against WebDriverError')190},191function (error) {192assert.strictEqual(error, webDriverError)193},194)195})196})197})198199it('testUntilTitleIs', function () {200var titles = ['foo', 'bar', 'baz']201executor.on(CommandName.GET_TITLE, () => titles.shift())202203return driver.wait(until.titleIs('bar'), 3000).then(function () {204assert.deepStrictEqual(titles, ['baz'])205})206})207208it('testUntilTitleContains', function () {209var titles = ['foo', 'froogle', 'google']210executor.on(CommandName.GET_TITLE, () => titles.shift())211212return driver.wait(until.titleContains('oogle'), 3000).then(function () {213assert.deepStrictEqual(titles, ['google'])214})215})216217it('testUntilTitleMatches', function () {218var titles = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google']219executor.on(CommandName.GET_TITLE, () => titles.shift())220221return driver.wait(until.titleMatches(/^a{2,3}b+c$/), 3000).then(function () {222assert.deepStrictEqual(titles, ['google'])223})224})225226it('testUntilUrlIs', function () {227var urls = ['http://www.foo.com', 'https://boo.com', 'http://docs.yes.com']228executor.on(CommandName.GET_CURRENT_URL, () => urls.shift())229230return driver.wait(until.urlIs('https://boo.com'), 3000).then(function () {231assert.deepStrictEqual(urls, ['http://docs.yes.com'])232})233})234235it('testUntilUrlContains', function () {236var urls = ['http://foo.com', 'https://groups.froogle.com', 'http://google.com']237executor.on(CommandName.GET_CURRENT_URL, () => urls.shift())238239return driver.wait(until.urlContains('oogle.com'), 3000).then(function () {240assert.deepStrictEqual(urls, ['http://google.com'])241})242})243244it('testUntilUrlMatches', function () {245var urls = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google']246executor.on(CommandName.GET_CURRENT_URL, () => urls.shift())247248return driver.wait(until.urlMatches(/^a{2,3}b+c$/), 3000).then(function () {249assert.deepStrictEqual(urls, ['google'])250})251})252253it('testUntilElementLocated', function () {254var responses = [[], [WebElement.buildId('abc123'), WebElement.buildId('foo')], ['end']]255executor.on(CommandName.FIND_ELEMENTS, () => responses.shift())256257let element = driver.wait(until.elementLocated(By.id('quux')), 2000)258assert.ok(element instanceof webdriver.WebElementPromise)259return element.getId().then(function (id) {260assert.deepStrictEqual(responses, [['end']])261assert.strictEqual(id, 'abc123')262})263})264265describe('untilElementLocated, elementNeverFound', function () {266function runNoElementFoundTest(locator, locatorStr) {267executor.on(CommandName.FIND_ELEMENTS, () => [])268269function expectedFailure() {270fail('expected condition to timeout')271}272273return driver.wait(until.elementLocated(locator), 100).then(expectedFailure, function (error) {274var expected = 'Waiting for element to be located ' + locatorStr275var lines = error.message.split(/\n/, 2)276assert.strictEqual(lines[0], expected)277278let regex = /^Wait timed out after \d+ms$/279assert.ok(regex.test(lines[1]), `Lines <${lines[1]}> does not match ${regex}`)280})281}282283it('byLocator', function () {284return runNoElementFoundTest(By.id('quux'), 'By(css selector, *[id="quux"])')285})286287it('byHash', function () {288return runNoElementFoundTest({ id: 'quux' }, 'By(css selector, *[id="quux"])')289})290291it('byFunction', function () {292return runNoElementFoundTest(function () {}, 'by function()')293})294})295296it('testUntilElementsLocated', function () {297var responses = [[], [WebElement.buildId('abc123'), WebElement.buildId('foo')], ['end']]298executor.on(CommandName.FIND_ELEMENTS, () => responses.shift())299300return driver301.wait(until.elementsLocated(By.id('quux')), 2000)302.then(function (els) {303return Promise.all(els.map((e) => e.getId()))304})305.then(function (ids) {306assert.deepStrictEqual(responses, [['end']])307assert.strictEqual(ids.length, 2)308assert.strictEqual(ids[0], 'abc123')309assert.strictEqual(ids[1], 'foo')310})311})312313describe('untilElementsLocated, noElementsFound', function () {314function runNoElementsFoundTest(locator, locatorStr) {315executor.on(CommandName.FIND_ELEMENTS, () => [])316317function expectedFailure() {318fail('expected condition to timeout')319}320321return driver.wait(until.elementsLocated(locator), 100).then(expectedFailure, function (error) {322var expected = 'Waiting for at least one element to be located ' + locatorStr323var lines = error.message.split(/\n/, 2)324assert.strictEqual(lines[0], expected)325326let regex = /^Wait timed out after \d+ms$/327assert.ok(regex.test(lines[1]), `Lines <${lines[1]}> does not match ${regex}`)328})329}330331it('byLocator', function () {332return runNoElementsFoundTest(By.id('quux'), 'By(css selector, *[id="quux"])')333})334335it('byHash', function () {336return runNoElementsFoundTest({ id: 'quux' }, 'By(css selector, *[id="quux"])')337})338339it('byFunction', function () {340return runNoElementsFoundTest(function () {}, 'by function()')341})342})343344it('testUntilStalenessOf', function () {345let count = 0346executor.on(CommandName.GET_ELEMENT_TAG_NAME, function () {347while (count < 3) {348count += 1349return 'body'350}351throw new error.StaleElementReferenceError('now stale')352})353354var el = new webdriver.WebElement(driver, { ELEMENT: 'foo' })355return driver.wait(until.stalenessOf(el), 2000).then(() => assert.strictEqual(count, 3))356})357358describe('element state conditions', function () {359function runElementStateTest(predicate, command, responses, _var_args) {360let original = new webdriver.WebElement(driver, 'foo')361let predicateArgs = [original]362if (arguments.length > 3) {363predicateArgs = predicateArgs.concat(arguments[1])364command = arguments[2]365responses = arguments[3]366}367368assert.ok(responses.length > 1)369370responses = responses.concat(['end'])371executor.on(command, () => responses.shift())372373let result = driver.wait(predicate.apply(null, predicateArgs), 2000)374assert.ok(result instanceof webdriver.WebElementPromise)375return result376.then(function (value) {377assert.ok(value instanceof webdriver.WebElement)378assert.ok(!(value instanceof webdriver.WebElementPromise))379return value.getId()380})381.then(function (id) {382assert.strictEqual('foo', id)383assert.deepStrictEqual(responses, ['end'])384})385}386387it('elementIsVisible', function () {388return runElementStateTest(until.elementIsVisible, CommandName.IS_ELEMENT_DISPLAYED, [false, false, true])389})390391it('elementIsNotVisible', function () {392return runElementStateTest(until.elementIsNotVisible, CommandName.IS_ELEMENT_DISPLAYED, [true, true, false])393})394395it('elementIsEnabled', function () {396return runElementStateTest(until.elementIsEnabled, CommandName.IS_ELEMENT_ENABLED, [false, false, true])397})398399it('elementIsDisabled', function () {400return runElementStateTest(until.elementIsDisabled, CommandName.IS_ELEMENT_ENABLED, [true, true, false])401})402403it('elementIsSelected', function () {404return runElementStateTest(until.elementIsSelected, CommandName.IS_ELEMENT_SELECTED, [false, false, true])405})406407it('elementIsNotSelected', function () {408return runElementStateTest(until.elementIsNotSelected, CommandName.IS_ELEMENT_SELECTED, [true, true, false])409})410411it('elementTextIs', function () {412return runElementStateTest(until.elementTextIs, 'foobar', CommandName.GET_ELEMENT_TEXT, [413'foo',414'fooba',415'foobar',416])417})418419it('elementTextContains', function () {420return runElementStateTest(until.elementTextContains, 'bar', CommandName.GET_ELEMENT_TEXT, [421'foo',422'foobaz',423'foobarbaz',424])425})426427it('elementTextMatches', function () {428return runElementStateTest(until.elementTextMatches, /fo+bar{3}/, CommandName.GET_ELEMENT_TEXT, [429'foo',430'foobar',431'fooobarrr',432])433})434})435436describe('WebElementCondition', function () {437it('fails if wait completes with a non-WebElement value', function () {438let result = driver.wait(new webdriver.WebElementCondition('testing', () => 123), 1000)439440return result.then(441() => assert.fail('expected to fail'),442function (e) {443assert.ok(e instanceof TypeError)444assert.strictEqual('WebElementCondition did not resolve to a WebElement: ' + '[object Number]', e.message)445},446)447})448})449})450451452