Path: blob/trunk/javascript/selenium-webdriver/test/lib/webdriver_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 { StubError, assertIsInstance, assertIsStubError, throwStubError } = require('./testutil')2021const error = require('selenium-webdriver/lib/error')22const logging = require('selenium-webdriver/lib/logging')23const promise = require('selenium-webdriver/lib/promise')24const until = require('selenium-webdriver/lib/until')25const { Alert, AlertPromise, WebDriver, WebElement, WebElementPromise } = require('selenium-webdriver/lib/webdriver')26const { By } = require('selenium-webdriver/lib/by')27const { Capabilities } = require('selenium-webdriver/lib/capabilities')28const { Name } = require('selenium-webdriver/lib/command')29const { Session } = require('selenium-webdriver/lib/session')30const assert = require('node:assert')3132const CName = Name33const SESSION_ID = 'test_session_id'34const fail = (msg) => assert.fail(msg)3536describe('WebDriver', function () {37const LOG = logging.getLogger('webdriver.test')3839function defer() {40let d = {}41let promise = new Promise((resolve, reject) => {42Object.assign(d, { resolve, reject })43})44d.promise = promise45return d46}4748function expectedError(ctor, message) {49return function (e) {50assertIsInstance(ctor, e)51assert.strictEqual(message, e.message)52}53}5455class Expectation {56constructor(executor, name, opt_parameters) {57this.executor_ = executor58this.name_ = name59this.times_ = 160this.sessionId_ = SESSION_ID61this.check_ = null62this.toDo_ = null63this.withParameters(opt_parameters || {})64}6566anyTimes() {67this.times_ = Infinity68return this69}7071times(n) {72this.times_ = n73return this74}7576withParameters(parameters) {77this.parameters_ = parameters78if (this.name_ !== CName.NEW_SESSION) {79this.parameters_['sessionId'] = this.sessionId_80}81return this82}8384andReturn(code, opt_value) {85this.toDo_ = function (command) {86LOG.info('executing ' + command.getName() + '; returning ' + code)87return Promise.resolve(opt_value !== void 0 ? opt_value : null)88}89return this90}9192andReturnSuccess(opt_value) {93this.toDo_ = function (command) {94LOG.info('executing ' + command.getName() + '; returning success')95return Promise.resolve(opt_value !== void 0 ? opt_value : null)96}97return this98}99100andReturnError(error) {101if (typeof error === 'number') {102throw Error('need error type')103}104this.toDo_ = function (command) {105LOG.info('executing ' + command.getName() + '; returning failure')106return Promise.reject(error)107}108return this109}110111expect(name, opt_parameters) {112this.end()113return this.executor_.expect(name, opt_parameters)114}115116end() {117if (!this.toDo_) {118this.andReturnSuccess(null)119}120return this.executor_121}122123execute(command) {124assert.deepStrictEqual(command.getParameters(), this.parameters_)125return this.toDo_(command)126}127}128129class FakeExecutor {130constructor() {131this.commands_ = new Map()132}133134execute(command) {135let expectations = this.commands_.get(command.getName())136if (!expectations || !expectations.length) {137assert.fail('unexpected command: ' + command.getName())138return139}140141let next = expectations[0]142let result = next.execute(command)143if (next.times_ !== Infinity) {144next.times_ -= 1145if (!next.times_) {146expectations.shift()147}148}149return result150}151152expect(commandName, opt_parameters) {153if (!this.commands_.has(commandName)) {154this.commands_.set(commandName, [])155}156let e = new Expectation(this, commandName, opt_parameters)157this.commands_.get(commandName).push(e)158return e159}160161createDriver(opt_session) {162let session = opt_session || new Session(SESSION_ID, {})163return new WebDriver(session, this)164}165}166167/////////////////////////////////////////////////////////////////////////////168//169// Tests170//171/////////////////////////////////////////////////////////////////////////////172173describe('testCreateSession', function () {174it('happyPathWithCapabilitiesHashObject', function () {175let aSession = new Session(SESSION_ID, { browserName: 'firefox' })176let executor = new FakeExecutor()177.expect(CName.NEW_SESSION)178.withParameters({179capabilities: {180alwaysMatch: { browserName: 'firefox' },181firstMatch: [{}],182},183})184.andReturnSuccess(aSession)185.end()186187const driver = WebDriver.createSession(executor, {188browserName: 'firefox',189})190return driver.getSession().then((v) => assert.strictEqual(v, aSession))191})192193it('happy Path With Capabilities Instance', function () {194let aSession = new Session(SESSION_ID, { browserName: 'firefox' })195let executor = new FakeExecutor()196.expect(CName.NEW_SESSION)197.withParameters({198capabilities: {199alwaysMatch: {200'moz:debuggerAddress': true,201browserName: 'firefox',202},203firstMatch: [{}],204},205})206.andReturnSuccess(aSession)207.end()208209const driver = WebDriver.createSession(executor, Capabilities.firefox())210return driver.getSession().then((v) => assert.strictEqual(v, aSession))211})212213it('drops non-W3C capability names from W3C capabilities', function () {214let aSession = new Session(SESSION_ID, { browserName: 'firefox' })215let executor = new FakeExecutor()216.expect(CName.NEW_SESSION)217.withParameters({218capabilities: {219alwaysMatch: { browserName: 'firefox' },220firstMatch: [{}],221},222})223.andReturnSuccess(aSession)224.end()225226const driver = WebDriver.createSession(executor, {227browserName: 'firefox',228foo: 'bar',229})230return driver.getSession().then((v) => assert.strictEqual(v, aSession))231})232233it('failsToCreateSession', function () {234let executor = new FakeExecutor()235.expect(CName.NEW_SESSION)236.withParameters({237capabilities: {238alwaysMatch: { browserName: 'firefox' },239firstMatch: [{}],240},241})242.andReturnError(new StubError())243.end()244245const driver = WebDriver.createSession(executor, {246browserName: 'firefox',247})248return driver.getSession().then(fail, assertIsStubError)249})250251it('invokes quit callback if it fails to create a session', function () {252let called = false253let executor = new FakeExecutor()254.expect(CName.NEW_SESSION)255.withParameters({256capabilities: {257alwaysMatch: { browserName: 'firefox' },258firstMatch: [{}],259},260})261.andReturnError(new StubError())262.end()263264let driver = WebDriver.createSession(executor, { browserName: 'firefox' }, () => (called = true))265return driver.getSession().then(fail, (err) => {266assert.ok(called)267assertIsStubError(err)268})269})270})271272it('testDoesNotExecuteCommandIfSessionDoesNotResolve', function () {273const session = Promise.reject(new StubError())274return new FakeExecutor()275.createDriver(session)276.getTitle()277.then((_) => assert.fail('should have failed'), assertIsStubError)278})279280it('testCommandReturnValuesArePassedToFirstCallback', function () {281let executor = new FakeExecutor().expect(CName.GET_TITLE).andReturnSuccess('Google Search').end()282283const driver = executor.createDriver()284return driver.getTitle().then((title) => assert.strictEqual('Google Search', title))285})286287it('testStopsCommandExecutionWhenAnErrorOccurs', function () {288let e = new error.NoSuchWindowError('window not found')289let executor = new FakeExecutor()290.expect(CName.SWITCH_TO_WINDOW)291.withParameters({292name: 'foo',293handle: 'foo',294})295.andReturnError(e)296.end()297298let driver = executor.createDriver()299return driver300.switchTo()301.window('foo')302.then(303(_) => driver.getTitle(), // mock should blow if this gets executed304(v) => assert.strictEqual(v, e),305)306})307308it('testReportsErrorWhenExecutingCommandsAfterExecutingAQuit', function () {309let executor = new FakeExecutor().expect(CName.QUIT).end()310311let verifyError = expectedError(312error.NoSuchSessionError,313'This driver instance does not have a valid session ID ' +314'(did you call WebDriver.quit()?) and may no longer be used.',315)316317let driver = executor.createDriver()318return driver319.quit()320.then((_) => driver.get('http://www.google.com'))321.then(assert.fail, verifyError)322})323324describe('returningAPromise', function () {325it('fromACallback', function () {326let executor = new FakeExecutor()327.expect(CName.GET_TITLE)328.expect(CName.GET_CURRENT_URL)329.andReturnSuccess('http://www.google.com')330.end()331332const driver = executor.createDriver()333return driver334.getTitle()335.then(function () {336return driver.getCurrentUrl()337})338.then(function (value) {339assert.strictEqual('http://www.google.com', value)340})341})342343it('fromAnErrbackSuppressesTheError', function () {344let executor = new FakeExecutor()345.expect(CName.SWITCH_TO_WINDOW, {346name: 'foo',347handle: 'foo',348})349.andReturnError(new StubError())350.expect(CName.GET_CURRENT_URL)351.andReturnSuccess('http://www.google.com')352.end()353354const driver = executor.createDriver()355return driver356.switchTo()357.window('foo')358.catch(function (e) {359assertIsStubError(e)360return driver.getCurrentUrl()361})362.then((url) => assert.strictEqual('http://www.google.com', url))363})364})365366describe('WebElementPromise', function () {367let driver = new FakeExecutor().createDriver()368369it('resolvesWhenUnderlyingElementDoes', function () {370let el = new WebElement(driver, { ELEMENT: 'foo' })371return new WebElementPromise(driver, Promise.resolve(el)).then((e) => assert.strictEqual(e, el))372})373374it('resolvesBeforeCallbacksOnWireValueTrigger', function () {375const el = defer()376377const element = new WebElementPromise(driver, el.promise)378const messages = []379380let steps = [381element.then((_) => messages.push('element resolved')),382element.getId().then((_) => messages.push('wire value resolved')),383]384385el.resolve(new WebElement(driver, { ELEMENT: 'foo' }))386return Promise.all(steps).then(function () {387assert.deepStrictEqual(['element resolved', 'wire value resolved'], messages)388})389})390391it('isRejectedIfUnderlyingIdIsRejected', function () {392let element = new WebElementPromise(driver, Promise.reject(new StubError()))393return element.then(fail, assertIsStubError)394})395})396397describe('executeScript', function () {398it('nullReturnValue', function () {399let executor = new FakeExecutor()400.expect(CName.EXECUTE_SCRIPT)401.withParameters({402script: 'return document.body;',403args: [],404})405.andReturnSuccess(null)406.end()407408const driver = executor.createDriver()409return driver.executeScript('return document.body;').then((result) => assert.strictEqual(null, result))410})411412it('primitiveReturnValue', function () {413let executor = new FakeExecutor()414.expect(CName.EXECUTE_SCRIPT)415.withParameters({416script: 'return document.body;',417args: [],418})419.andReturnSuccess(123)420.end()421422const driver = executor.createDriver()423return driver.executeScript('return document.body;').then((result) => assert.strictEqual(123, result))424})425426it('webElementReturnValue', function () {427const json = WebElement.buildId('foo')428429let executor = new FakeExecutor()430.expect(CName.EXECUTE_SCRIPT)431.withParameters({432script: 'return document.body;',433args: [],434})435.andReturnSuccess(json)436.end()437438const driver = executor.createDriver()439return driver440.executeScript('return document.body;')441.then((element) => element.getId())442.then((id) => assert.strictEqual(id, 'foo'))443})444445it('arrayReturnValue', function () {446const json = [WebElement.buildId('foo')]447448let executor = new FakeExecutor()449.expect(CName.EXECUTE_SCRIPT)450.withParameters({451script: 'return document.body;',452args: [],453})454.andReturnSuccess(json)455.end()456457const driver = executor.createDriver()458return driver459.executeScript('return document.body;')460.then(function (array) {461assert.strictEqual(1, array.length)462return array[0].getId()463})464.then((id) => assert.strictEqual('foo', id))465})466467it('objectReturnValue', function () {468const json = { foo: WebElement.buildId('foo') }469470let executor = new FakeExecutor()471.expect(CName.EXECUTE_SCRIPT)472.withParameters({473script: 'return document.body;',474args: [],475})476.andReturnSuccess(json)477.end()478479const driver = executor.createDriver()480return driver481.executeScript('return document.body;')482.then((obj) => obj['foo'].getId())483.then((id) => assert.strictEqual(id, 'foo'))484})485486it('scriptAsFunction', function () {487let executor = new FakeExecutor()488.expect(CName.EXECUTE_SCRIPT)489.withParameters({490script: 'return (' + function () {} + ').apply(null, arguments);',491args: [],492})493.andReturnSuccess(null)494.end()495496const driver = executor.createDriver()497return driver.executeScript(function () {})498})499500it('simpleArgumentConversion', function () {501let executor = new FakeExecutor()502.expect(CName.EXECUTE_SCRIPT)503.withParameters({504script: 'return 1;',505args: ['abc', 123, true, [123, { foo: 'bar' }]],506})507.andReturnSuccess(null)508.end()509510const driver = executor.createDriver()511return driver.executeScript('return 1;', 'abc', 123, true, [123, { foo: 'bar' }])512})513514it('webElementArgumentConversion', function () {515const elementJson = WebElement.buildId('fefifofum')516517let executor = new FakeExecutor()518.expect(CName.EXECUTE_SCRIPT)519.withParameters({520script: 'return 1;',521args: [elementJson],522})523.andReturnSuccess(null)524.end()525526const driver = executor.createDriver()527return driver.executeScript('return 1;', new WebElement(driver, 'fefifofum'))528})529530it('webElementPromiseArgumentConversion', function () {531const elementJson = WebElement.buildId('bar')532533let executor = new FakeExecutor()534.expect(CName.FIND_ELEMENT, {535using: 'css selector',536value: '*[id="foo"]',537})538.andReturnSuccess(elementJson)539.expect(CName.EXECUTE_SCRIPT)540.withParameters({541script: 'return 1;',542args: [elementJson],543})544.andReturnSuccess(null)545.end()546547const driver = executor.createDriver()548const element = driver.findElement(By.id('foo'))549return driver.executeScript('return 1;', element)550})551552it('argumentConversion', function () {553const elementJson = WebElement.buildId('fefifofum')554555let executor = new FakeExecutor()556.expect(CName.EXECUTE_SCRIPT)557.withParameters({558script: 'return 1;',559args: ['abc', 123, true, elementJson, [123, { foo: 'bar' }]],560})561.andReturnSuccess(null)562.end()563564const driver = executor.createDriver()565const element = new WebElement(driver, 'fefifofum')566return driver.executeScript('return 1;', 'abc', 123, true, element, [123, { foo: 'bar' }])567})568569it('scriptReturnsAnError', function () {570let executor = new FakeExecutor()571.expect(CName.EXECUTE_SCRIPT)572.withParameters({573script: 'throw Error(arguments[0]);',574args: ['bam'],575})576.andReturnError(new StubError())577.end()578const driver = executor.createDriver()579return driver.executeScript('throw Error(arguments[0]);', 'bam').then(fail, assertIsStubError)580})581582it('failsIfArgumentIsARejectedPromise', function () {583let executor = new FakeExecutor()584585const arg = Promise.reject(new StubError())586arg.catch(function () {}) // Suppress default handler.587588const driver = executor.createDriver()589return driver.executeScript(function () {}, arg).then(fail, assertIsStubError)590})591})592593describe('executeAsyncScript', function () {594it('failsIfArgumentIsARejectedPromise', function () {595const arg = Promise.reject(new StubError())596arg.catch(function () {}) // Suppress default handler.597598const driver = new FakeExecutor().createDriver()599return driver.executeAsyncScript(function () {}, arg).then(fail, assertIsStubError)600})601})602603describe('findElement', function () {604it('elementNotFound', function () {605let executor = new FakeExecutor()606.expect(CName.FIND_ELEMENT, {607using: 'css selector',608value: '*[id="foo"]',609})610.andReturnError(new StubError())611.end()612613const driver = executor.createDriver()614return driver.findElement(By.id('foo')).then(assert.fail, assertIsStubError)615})616617it('elementNotFoundInACallback', function () {618let executor = new FakeExecutor()619.expect(CName.FIND_ELEMENT, {620using: 'css selector',621value: '*[id="foo"]',622})623.andReturnError(new StubError())624.end()625626const driver = executor.createDriver()627return Promise.resolve()628.then((_) => driver.findElement(By.id('foo')))629.then(assert.fail, assertIsStubError)630})631632it('elementFound', function () {633let executor = new FakeExecutor()634.expect(CName.FIND_ELEMENT, {635using: 'css selector',636value: '*[id="foo"]',637})638.andReturnSuccess(WebElement.buildId('bar'))639.expect(CName.CLICK_ELEMENT, { id: WebElement.buildId('bar') })640.andReturnSuccess()641.end()642643const driver = executor.createDriver()644const element = driver.findElement(By.id('foo'))645return element.click()646})647648it('canUseElementInCallback', function () {649let executor = new FakeExecutor()650.expect(CName.FIND_ELEMENT, {651using: 'css selector',652value: '*[id="foo"]',653})654.andReturnSuccess(WebElement.buildId('bar'))655.expect(CName.CLICK_ELEMENT, { id: WebElement.buildId('bar') })656.andReturnSuccess()657.end()658659const driver = executor.createDriver()660return driver.findElement(By.id('foo')).then((e) => e.click())661})662663it('byJs', function () {664let executor = new FakeExecutor()665.expect(CName.EXECUTE_SCRIPT, {666script: 'return document.body',667args: [],668})669.andReturnSuccess(WebElement.buildId('bar'))670.expect(CName.CLICK_ELEMENT, { id: WebElement.buildId('bar') })671.end()672673const driver = executor.createDriver()674return driver.findElement(By.js('return document.body')).then((e) => e.click())675})676677it('byJs_returnsNonWebElementValue', function () {678let executor = new FakeExecutor()679.expect(CName.EXECUTE_SCRIPT, { script: 'return 123', args: [] })680.andReturnSuccess(123)681.end()682683const driver = executor.createDriver()684return driver.findElement(By.js('return 123')).then(assert.fail, function (e) {685assertIsInstance(TypeError, e)686assert.strictEqual('Custom locator did not return a WebElement', e.message)687})688})689690it('byJs_canPassArguments', function () {691const script = 'return document.getElementsByTagName(arguments[0]);'692let executor = new FakeExecutor()693.expect(CName.EXECUTE_SCRIPT, {694script: script,695args: ['div'],696})697.andReturnSuccess(WebElement.buildId('one'))698.end()699const driver = executor.createDriver()700return driver.findElement(By.js(script, 'div'))701})702703it('customLocator', function () {704let executor = new FakeExecutor()705.expect(CName.FIND_ELEMENTS, { using: 'css selector', value: '.a' })706.andReturnSuccess([WebElement.buildId('foo'), WebElement.buildId('bar')])707.expect(CName.CLICK_ELEMENT, { id: WebElement.buildId('foo') })708.andReturnSuccess()709.end()710711const driver = executor.createDriver()712const element = driver.findElement(function (d) {713assert.strictEqual(driver, d)714return d.findElements(By.className('a'))715})716return element.click()717})718719it('customLocatorThrowsIfresultIsNotAWebElement', function () {720const driver = new FakeExecutor().createDriver()721return driver722.findElement((_) => 1)723.then(assert.fail, function (e) {724assertIsInstance(TypeError, e)725assert.strictEqual('Custom locator did not return a WebElement', e.message)726})727})728})729730describe('findElements', function () {731it('returnsMultipleElements', function () {732const ids = ['foo', 'bar', 'baz']733let executor = new FakeExecutor()734.expect(CName.FIND_ELEMENTS, { using: 'css selector', value: '.a' })735.andReturnSuccess(ids.map(WebElement.buildId))736.end()737738const driver = executor.createDriver()739return driver740.findElements(By.className('a'))741.then(function (elements) {742return Promise.all(743elements.map(function (e) {744assert.ok(e instanceof WebElement)745return e.getId()746}),747)748})749.then((actual) => assert.deepStrictEqual(ids, actual))750})751752it('byJs', function () {753const ids = ['foo', 'bar', 'baz']754let executor = new FakeExecutor()755.expect(CName.EXECUTE_SCRIPT, {756script: 'return document.getElementsByTagName("div");',757args: [],758})759.andReturnSuccess(ids.map(WebElement.buildId))760.end()761762const driver = executor.createDriver()763764return driver765.findElements(By.js('return document.getElementsByTagName("div");'))766.then(function (elements) {767return Promise.all(768elements.map(function (e) {769assert.ok(e instanceof WebElement)770return e.getId()771}),772)773})774.then((actual) => assert.deepStrictEqual(ids, actual))775})776777it('byJs_filtersOutNonWebElementResponses', function () {778const ids = ['foo', 'bar', 'baz']779const json = [780WebElement.buildId(ids[0]),781123,782'a',783false,784WebElement.buildId(ids[1]),785{ 'not a web element': 1 },786WebElement.buildId(ids[2]),787]788let executor = new FakeExecutor()789.expect(CName.EXECUTE_SCRIPT, {790script: 'return document.getElementsByTagName("div");',791args: [],792})793.andReturnSuccess(json)794.end()795796const driver = executor.createDriver()797return driver798.findElements(By.js('return document.getElementsByTagName("div");'))799.then(function (elements) {800return Promise.all(801elements.map(function (e) {802assert.ok(e instanceof WebElement)803return e.getId()804}),805)806})807.then((actual) => assert.deepStrictEqual(ids, actual))808})809810it('byJs_convertsSingleWebElementResponseToArray', function () {811let executor = new FakeExecutor()812.expect(CName.EXECUTE_SCRIPT, {813script: 'return document.getElementsByTagName("div");',814args: [],815})816.andReturnSuccess(WebElement.buildId('foo'))817.end()818819const driver = executor.createDriver()820return driver821.findElements(By.js('return document.getElementsByTagName("div");'))822.then(function (elements) {823return Promise.all(824elements.map(function (e) {825assert.ok(e instanceof WebElement)826return e.getId()827}),828)829})830.then((actual) => assert.deepStrictEqual(['foo'], actual))831})832833it('byJs_canPassScriptArguments', function () {834const script = 'return document.getElementsByTagName(arguments[0]);'835let executor = new FakeExecutor()836.expect(CName.EXECUTE_SCRIPT, {837script: script,838args: ['div'],839})840.andReturnSuccess([WebElement.buildId('one'), WebElement.buildId('two')])841.end()842843const driver = executor.createDriver()844return driver845.findElements(By.js(script, 'div'))846.then(function (elements) {847return Promise.all(848elements.map(function (e) {849assert.ok(e instanceof WebElement)850return e.getId()851}),852)853})854.then((actual) => assert.deepStrictEqual(['one', 'two'], actual))855})856})857858describe('sendKeys', function () {859it('convertsVarArgsIntoStrings_simpleArgs', function () {860let executor = new FakeExecutor()861.expect(CName.SEND_KEYS_TO_ELEMENT, {862id: WebElement.buildId('one'),863text: '12abc3',864value: '12abc3'.split(''),865})866.andReturnSuccess()867.end()868869const driver = executor.createDriver()870const element = new WebElement(driver, 'one')871return element.sendKeys(1, 2, 'abc', 3)872})873874it('sendKeysWithEmojiRepresentedByPairOfCodePoints', function () {875let executor = new FakeExecutor()876.expect(CName.SEND_KEYS_TO_ELEMENT, {877id: WebElement.buildId('one'),878text: '\uD83D\uDE00',879value: ['\uD83D\uDE00'],880})881.andReturnSuccess()882.end()883884const driver = executor.createDriver()885const element = new WebElement(driver, 'one')886return element.sendKeys('\uD83D\uDE00')887})888889it('convertsVarArgsIntoStrings_promisedArgs', function () {890let executor = new FakeExecutor()891.expect(CName.FIND_ELEMENT, {892using: 'css selector',893value: '*[id="foo"]',894})895.andReturnSuccess(WebElement.buildId('one'))896.expect(CName.SEND_KEYS_TO_ELEMENT, {897id: WebElement.buildId('one'),898text: 'abc123def',899value: 'abc123def'.split(''),900})901.andReturnSuccess()902.end()903904const driver = executor.createDriver()905const element = driver.findElement(By.id('foo'))906return element.sendKeys(Promise.resolve('abc'), 123, Promise.resolve('def'))907})908909it('sendKeysWithAFileDetector', function () {910let executor = new FakeExecutor()911.expect(CName.FIND_ELEMENT, {912using: 'css selector',913value: '*[id="foo"]',914})915.andReturnSuccess(WebElement.buildId('one'))916.expect(CName.SEND_KEYS_TO_ELEMENT, {917id: WebElement.buildId('one'),918text: 'modified/path',919value: 'modified/path'.split(''),920})921.andReturnSuccess()922.end()923924let driver = executor.createDriver()925let handleFile = function (d, path) {926assert.strictEqual(driver, d)927assert.strictEqual(path, 'original/path')928return Promise.resolve('modified/path')929}930driver.setFileDetector({ handleFile })931932return driver.findElement(By.id('foo')).sendKeys('original/', 'path')933})934935it('sendKeysWithAFileDetector_handlerError', function () {936let executor = new FakeExecutor()937.expect(CName.FIND_ELEMENT, {938using: 'css selector',939value: '*[id="foo"]',940})941.andReturnSuccess(WebElement.buildId('one'))942.expect(CName.SEND_KEYS_TO_ELEMENT, {943id: WebElement.buildId('one'),944text: 'original/path',945value: 'original/path'.split(''),946})947.andReturnSuccess()948.end()949950let driver = executor.createDriver()951let handleFile = function (d, path) {952assert.strictEqual(driver, d)953assert.strictEqual(path, 'original/path')954return Promise.reject('unhandled file error')955}956driver.setFileDetector({ handleFile })957958return driver.findElement(By.id('foo')).sendKeys('original/', 'path')959})960})961962describe('switchTo()', function () {963describe('window', function () {964it('should return a resolved promise when the window is found', function () {965let executor = new FakeExecutor()966.expect(CName.SWITCH_TO_WINDOW)967.withParameters({968name: 'foo',969handle: 'foo',970})971.andReturnSuccess()972.end()973974return executor.createDriver().switchTo().window('foo')975})976977it('should propagate exceptions', function () {978let e = new error.NoSuchWindowError('window not found')979let executor = new FakeExecutor()980.expect(CName.SWITCH_TO_WINDOW)981.withParameters({982name: 'foo',983handle: 'foo',984})985.andReturnError(e)986.end()987988return executor989.createDriver()990.switchTo()991.window('foo')992.then(assert.fail, (v) => assert.strictEqual(v, e))993})994})995})996997describe('elementEquality', function () {998it('isReflexive', function () {999const a = new WebElement(new FakeExecutor().createDriver(), 'foo')1000return WebElement.equals(a, a).then(assert.ok)1001})10021003it('failsIfAnInputElementCouldNotBeFound', function () {1004let id = Promise.reject(new StubError())10051006const driver = new FakeExecutor().createDriver()1007const a = new WebElement(driver, 'foo')1008const b = new WebElementPromise(driver, id)10091010return WebElement.equals(a, b).then(fail, assertIsStubError)1011})1012})10131014describe('waiting', function () {1015it('on a condition that always returns true', function () {1016let executor = new FakeExecutor()1017let driver = executor.createDriver()1018let count = 010191020function condition() {1021count++1022return true1023}10241025return driver.wait(condition, 1).then(() => assert.strictEqual(1, count))1026})10271028it('on a simple counting condition', function () {1029let executor = new FakeExecutor()1030let driver = executor.createDriver()1031let count = 010321033function condition() {1034return ++count === 31035}10361037return driver.wait(condition, 250).then(() => assert.strictEqual(3, count))1038})10391040it('on a condition that returns a promise that resolves to true after a short timeout', function () {1041let executor = new FakeExecutor()1042let driver = executor.createDriver()10431044let count = 010451046function condition() {1047count += 11048return new Promise((resolve) => {1049setTimeout(() => resolve(true), 50)1050})1051}10521053return driver.wait(condition, 75).then(() => assert.strictEqual(1, count))1054})10551056it('on a condition that returns a promise', function () {1057let executor = new FakeExecutor()1058let driver = executor.createDriver()10591060let count = 010611062function condition() {1063count += 11064return new Promise((resolve) => {1065setTimeout(() => resolve(count === 3), 25)1066})1067}10681069return driver.wait(condition, 100, null, 25).then(() => assert.strictEqual(3, count))1070})10711072it('fails if condition throws', function () {1073let executor = new FakeExecutor()1074let driver = executor.createDriver()1075return driver.wait(throwStubError, 0, 'goes boom').then(fail, assertIsStubError)1076})10771078it('fails if condition returns a rejected promise', function () {1079let executor = new FakeExecutor()1080let driver = executor.createDriver()10811082function condition() {1083return new Promise((_, reject) => reject(new StubError()))1084}10851086return driver.wait(condition, 0, 'goes boom').then(fail, assertIsStubError)1087})10881089it('supports message function if condition exceeds timeout', function () {1090let executor = new FakeExecutor()1091let driver = executor.createDriver()1092let message = () => 'goes boom'1093return driver1094.wait(() => false, 0.001, message)1095.then(fail, (e) => {1096assert.ok(/^goes boom\nWait timed out after \d+ms$/.test(e.message))1097})1098})10991100it('handles if the message function throws an error after a condition exceeds timeout', function () {1101let executor = new FakeExecutor()1102let driver = executor.createDriver()1103let message = () => {1104throw new Error('message function error')1105}1106return driver1107.wait(() => false, 0.001, message)1108.then(fail, (e) => {1109assert.ok(/^message function error\nWait timed out after \d+ms$/.test(e.message))1110})1111})11121113it('supports message function if condition returns a rejected promise', function () {1114let executor = new FakeExecutor()1115let driver = executor.createDriver()1116let condition = new Promise((res) => setTimeout(res, 100))1117let message = () => 'goes boom'1118return driver.wait(condition, 1, message).then(fail, (e) => {1119assert.ok(/^goes boom\nTimed out waiting for promise to resolve after \d+ms$/.test(e.message))1120})1121})11221123it('handles if the message function returns an error after a rejected promise', function () {1124let executor = new FakeExecutor()1125let driver = executor.createDriver()1126let condition = new Promise((res) => setTimeout(res, 100))1127let message = () => {1128throw new Error('message function error')1129}1130return driver.wait(condition, 1, message).then(fail, (e) => {1131assert.ok(/^message function error\nTimed out waiting for promise to resolve after \d+ms$/.test(e.message))1132})1133})11341135it('waits forever on a zero timeout', function () {1136let done = false1137setTimeout(() => (done = true), 150)11381139let executor = new FakeExecutor()1140let driver = executor.createDriver()1141let waitResult = driver.wait(() => done, 0)11421143return driver1144.sleep(75)1145.then(function () {1146assert.ok(!done)1147return driver.sleep(100)1148})1149.then(function () {1150assert.ok(done)1151return waitResult1152})1153})11541155it('waits forever if timeout omitted', function () {1156let done = false1157setTimeout(() => (done = true), 150)11581159let executor = new FakeExecutor()1160let driver = executor.createDriver()1161let waitResult = driver.wait(() => done)11621163return driver1164.sleep(75)1165.then(function () {1166assert.ok(!done)1167return driver.sleep(100)1168})1169.then(function () {1170assert.ok(done)1171return waitResult1172})1173})11741175it('times out when timer expires', function () {1176let executor = new FakeExecutor()1177let driver = executor.createDriver()11781179let count = 01180let wait = driver.wait(1181function () {1182count += 11183let ms = count === 2 ? 65 : 51184return promise.delayed(ms).then(function () {1185return false1186})1187},118860,1189'counting to 3',1190)11911192return wait.then(fail, function (e) {1193assert.strictEqual(2, count)1194assert.ok(e instanceof error.TimeoutError, 'Unexpected error: ' + e)1195assert.ok(/^counting to 3\nWait timed out after \d+ms$/.test(e.message))1196})1197})11981199it('requires condition to be a promise or function', function () {1200let executor = new FakeExecutor()1201let driver = executor.createDriver()1202assert.throws(() => driver.wait(1234, 0))1203})12041205it('promise that does not resolve before timeout', function () {1206let d = defer()12071208let executor = new FakeExecutor()1209let driver = executor.createDriver()1210return driver.wait(d.promise, 5).then(fail, (e) => {1211assert.ok(e instanceof error.TimeoutError, 'Unexpected error: ' + e)1212assert.ok(1213/Timed out waiting for promise to resolve after \d+ms/.test(e.message),1214'unexpected error message: ' + e.message,1215)1216})1217})12181219it('unbounded wait on promise resolution', function () {1220let messages = []1221let d = defer()12221223let executor = new FakeExecutor()1224let driver = executor.createDriver()1225let waitResult = driver.wait(d.promise).then(function (value) {1226messages.push('b')1227assert.strictEqual(1234, value)1228})12291230setTimeout(() => messages.push('a'), 5)1231return driver1232.sleep(10)1233.then(function () {1234assert.deepStrictEqual(['a'], messages)1235d.resolve(1234)1236return waitResult1237})1238.then(function () {1239assert.deepStrictEqual(['a', 'b'], messages)1240})1241})12421243describe('supports custom wait functions', function () {1244it('waitSucceeds', function () {1245let executor = new FakeExecutor()1246.expect(CName.FIND_ELEMENTS, {1247using: 'css selector',1248value: '*[id="foo"]',1249})1250.andReturnSuccess([])1251.times(2)1252.expect(CName.FIND_ELEMENTS, {1253using: 'css selector',1254value: '*[id="foo"]',1255})1256.andReturnSuccess([WebElement.buildId('bar')])1257.end()12581259const driver = executor.createDriver()1260return driver.wait(1261function () {1262return driver.findElements(By.id('foo')).then((els) => els.length > 0)1263},1264200,1265null,126625,1267)1268})12691270it('waitTimesout_timeoutCaught', function () {1271let executor = new FakeExecutor()1272.expect(CName.FIND_ELEMENTS, {1273using: 'css selector',1274value: '*[id="foo"]',1275})1276.andReturnSuccess([])1277.anyTimes()1278.end()12791280const driver = executor.createDriver()1281return driver1282.wait(function () {1283return driver.findElements(By.id('foo')).then((els) => els.length > 0)1284}, 25)1285.then(fail, function (e) {1286assert.strictEqual('Wait timed out after ', e.message.substring(0, 'Wait timed out after '.length))1287})1288})1289})12901291describe('supports condition objects', function () {1292it('wait succeeds', function () {1293let executor = new FakeExecutor()1294.expect(CName.FIND_ELEMENTS, {1295using: 'css selector',1296value: '*[id="foo"]',1297})1298.andReturnSuccess([])1299.times(2)1300.expect(CName.FIND_ELEMENTS, {1301using: 'css selector',1302value: '*[id="foo"]',1303})1304.andReturnSuccess([WebElement.buildId('bar')])1305.end()13061307let driver = executor.createDriver()1308return driver.wait(until.elementLocated(By.id('foo')), 200, null, 25)1309})13101311it('wait times out', function () {1312let executor = new FakeExecutor()1313.expect(CName.FIND_ELEMENTS, {1314using: 'css selector',1315value: '*[id="foo"]',1316})1317.andReturnSuccess([])1318.anyTimes()1319.end()13201321let driver = executor.createDriver()1322return driver1323.wait(until.elementLocated(By.id('foo')), 5)1324.then(fail, (err) => assert.ok(err instanceof error.TimeoutError))1325})1326})13271328describe('supports promise objects', function () {1329it('wait succeeds', function () {1330let promise = new Promise((resolve) => {1331setTimeout(() => resolve(1), 10)1332})13331334let driver = new FakeExecutor().createDriver()1335return driver.wait(promise, 200).then((v) => assert.strictEqual(v, 1))1336})13371338it('wait times out', function () {1339let promise = new Promise(() => {1340/* never resolves */1341})13421343let driver = new FakeExecutor().createDriver()1344return driver.wait(promise, 5).then(fail, (err) => assert.ok(err instanceof error.TimeoutError))1345})13461347it('wait fails if promise is rejected', function () {1348let err = Error('boom')1349let driver = new FakeExecutor().createDriver()1350return driver.wait(Promise.reject(err), 5).then(fail, (e) => assert.strictEqual(e, err))1351})1352})13531354it('fails if not supported condition type provided', function () {1355let driver = new FakeExecutor().createDriver()1356assert.throws(() => driver.wait({}, 5), TypeError)1357})1358})13591360describe('alert handling', function () {1361it('alertResolvesWhenPromisedTextResolves', function () {1362let driver = new FakeExecutor().createDriver()1363let deferredText = defer()13641365let alert = new AlertPromise(driver, deferredText.promise)13661367deferredText.resolve(new Alert(driver, 'foo'))1368return alert.getText().then((text) => assert.strictEqual(text, 'foo'))1369})13701371it('cannotSwitchToAlertThatIsNotPresent', function () {1372let e = new error.NoSuchAlertError()1373let executor = new FakeExecutor().expect(CName.GET_ALERT_TEXT).andReturnError(e).end()13741375return executor1376.createDriver()1377.switchTo()1378.alert()1379.then(assert.fail, (v) => assert.strictEqual(v, e))1380})13811382it('commandsFailIfAlertNotPresent', function () {1383let e = new error.NoSuchAlertError()1384let executor = new FakeExecutor().expect(CName.GET_ALERT_TEXT).andReturnError(e).end()13851386const driver = executor.createDriver()1387const alert = driver.switchTo().alert()13881389const expectError = (v) => assert.strictEqual(v, e)13901391return alert1392.getText()1393.then(fail, expectedError)1394.then(() => alert.accept())1395.then(fail, expectedError)1396.then(() => alert.dismiss())1397.then(fail, expectError)1398.then(() => alert.sendKeys('hi'))1399.then(fail, expectError)1400})1401})14021403it('testFetchingLogs', function () {1404let executor = new FakeExecutor()1405.expect(CName.GET_LOG, { type: 'browser' })1406.andReturnSuccess([1407{ level: 'INFO', message: 'hello', timestamp: 1234 },1408{ level: 'DEBUG', message: 'abc123', timestamp: 5678 },1409])1410.end()14111412const driver = executor.createDriver()1413return driver1414.manage()1415.logs()1416.get('browser')1417.then(function (entries) {1418assert.strictEqual(2, entries.length)14191420assert.ok(entries[0] instanceof logging.Entry)1421assert.strictEqual(logging.Level.INFO.value, entries[0].level.value)1422assert.strictEqual('hello', entries[0].message)1423assert.strictEqual(1234, entries[0].timestamp)14241425assert.ok(entries[1] instanceof logging.Entry)1426assert.strictEqual(logging.Level.DEBUG.value, entries[1].level.value)1427assert.strictEqual('abc123', entries[1].message)1428assert.strictEqual(5678, entries[1].timestamp)1429})1430})14311432it('testCommandsFailIfInitialSessionCreationFailed', function () {1433const session = Promise.reject(new StubError())14341435const driver = new FakeExecutor().createDriver(session)1436const navigateResult = driver.get('some-url').then(fail, assertIsStubError)1437const quitResult = driver.quit().then(fail, assertIsStubError)1438return Promise.all([navigateResult, quitResult])1439})14401441it('testWebElementCommandsFailIfInitialDriverCreationFailed', function () {1442const session = Promise.reject(new StubError())1443const driver = new FakeExecutor().createDriver(session)1444return driver.findElement(By.id('foo')).click().then(fail, assertIsStubError)1445})14461447it('testWebElementCommansFailIfElementCouldNotBeFound', function () {1448let e = new error.NoSuchElementError('Unable to find element')1449let executor = new FakeExecutor()1450.expect(CName.FIND_ELEMENT, {1451using: 'css selector',1452value: '*[id="foo"]',1453})1454.andReturnError(e)1455.end()14561457const driver = executor.createDriver()1458return driver1459.findElement(By.id('foo'))1460.click()1461.then(fail, (v) => assert.strictEqual(v, e))1462})14631464it('testCannotFindChildElementsIfParentCouldNotBeFound', function () {1465let e = new error.NoSuchElementError('Unable to find element')1466let executor = new FakeExecutor()1467.expect(CName.FIND_ELEMENT, {1468using: 'css selector',1469value: '*[id="foo"]',1470})1471.andReturnError(e)1472.end()14731474const driver = executor.createDriver()1475return driver1476.findElement(By.id('foo'))1477.findElement(By.id('bar'))1478.findElement(By.id('baz'))1479.then(fail, (v) => assert.strictEqual(v, e))1480})14811482describe('actions()', function () {1483describe('move()', function () {1484it('no origin', function () {1485let executor = new FakeExecutor()1486.expect(CName.ACTIONS, {1487actions: [1488{1489type: 'pointer',1490id: 'default mouse',1491parameters: {1492pointerType: 'mouse',1493},1494actions: [1495{1496duration: 100,1497origin: 'viewport',1498type: 'pointerMove',1499x: 0,1500y: 125,1501altitudeAngle: 0,1502azimuthAngle: 0,1503width: 0,1504height: 0,1505pressure: 0,1506tangentialPressure: 0,1507tiltX: 0,1508tiltY: 0,1509twist: 0,1510},1511],1512},1513],1514})1515.andReturnSuccess()1516.end()15171518let driver = executor.createDriver()1519return driver.actions().move({ x: 0, y: 125 }).perform()1520})15211522it('origin = element', function () {1523let executor = new FakeExecutor()1524.expect(CName.FIND_ELEMENT, {1525using: 'css selector',1526value: '*[id="foo"]',1527})1528.andReturnSuccess(WebElement.buildId('abc123'))1529.expect(CName.ACTIONS, {1530actions: [1531{1532type: 'pointer',1533id: 'default mouse',1534parameters: {1535pointerType: 'mouse',1536},1537actions: [1538{1539duration: 100,1540origin: WebElement.buildId('abc123'),1541type: 'pointerMove',1542x: 0,1543y: 125,1544altitudeAngle: 0,1545azimuthAngle: 0,1546width: 0,1547height: 0,1548pressure: 0,1549tangentialPressure: 0,1550tiltX: 0,1551tiltY: 0,1552twist: 0,1553},1554],1555},1556],1557})1558.end()15591560let driver = executor.createDriver()1561let element = driver.findElement(By.id('foo'))1562return driver.actions().move({ x: 0, y: 125, origin: element }).perform()1563})1564})1565})15661567describe('manage()', function () {1568describe('setTimeouts()', function () {1569describe('throws if no timeouts are specified', function () {1570let driver1571before(() => (driver = new FakeExecutor().createDriver()))15721573it('; no arguments', function () {1574assert.throws(() => driver.manage().setTimeouts(), TypeError)1575})15761577it('; ignores unrecognized timeout keys', function () {1578assert.throws(() => driver.manage().setTimeouts({ foo: 123 }), TypeError)1579})15801581it('; ignores positional arguments', function () {1582assert.throws(() => driver.manage().setTimeouts(1234, 56), TypeError)1583})1584})15851586describe('throws timeout is not a number, null, or undefined', () => {1587let driver1588before(() => (driver = new FakeExecutor().createDriver()))15891590function checkError(e) {1591return e instanceof TypeError && /expected "(script|pageLoad|implicit)" to be a number/.test(e.message)1592}15931594it('script', function () {1595assert.throws(() => driver.manage().setTimeouts({ script: 'abc' }), checkError)1596})15971598it('pageLoad', function () {1599assert.throws(() => driver.manage().setTimeouts({ pageLoad: 'abc' }), checkError)1600})16011602it('implicit', function () {1603assert.throws(() => driver.manage().setTimeouts({ implicit: 'abc' }), checkError)1604})1605})16061607it('can set multiple timeouts', function () {1608let executor = new FakeExecutor()1609.expect(CName.SET_TIMEOUT, { script: 1, pageLoad: 2, implicit: 3 })1610.andReturnSuccess()1611.end()1612let driver = executor.createDriver()1613return driver.manage().setTimeouts({ script: 1, pageLoad: 2, implicit: 3 })1614})16151616it('falls back to legacy wire format if W3C version fails', () => {1617let executor = new FakeExecutor()1618.expect(CName.SET_TIMEOUT, { implicit: 3 })1619.andReturnError(Error('oops'))1620.expect(CName.SET_TIMEOUT, { type: 'implicit', ms: 3 })1621.andReturnSuccess()1622.end()1623let driver = executor.createDriver()1624return driver.manage().setTimeouts({ implicit: 3 })1625})1626})1627})1628})162916301631