diff options
Diffstat (limited to 'node_modules/selenium-webdriver/test/lib/webdriver_test.js')
-rw-r--r-- | node_modules/selenium-webdriver/test/lib/webdriver_test.js | 2177 |
1 files changed, 2177 insertions, 0 deletions
diff --git a/node_modules/selenium-webdriver/test/lib/webdriver_test.js b/node_modules/selenium-webdriver/test/lib/webdriver_test.js new file mode 100644 index 000000000..d2d87ca3e --- /dev/null +++ b/node_modules/selenium-webdriver/test/lib/webdriver_test.js @@ -0,0 +1,2177 @@ +// 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 testutil = require('./testutil'); + +const By = require('../../lib/by').By; +const Capabilities = require('../../lib/capabilities').Capabilities; +const Executor = require('../../lib/command').Executor; +const CName = require('../../lib/command').Name; +const error = require('../../lib/error'); +const Button = require('../../lib/input').Button; +const Key = require('../../lib/input').Key; +const logging = require('../../lib/logging'); +const Session = require('../../lib/session').Session; +const promise = require('../../lib/promise'); +const Alert = require('../../lib/webdriver').Alert; +const AlertPromise = require('../../lib/webdriver').AlertPromise; +const UnhandledAlertError = require('../../lib/webdriver').UnhandledAlertError; +const WebDriver = require('../../lib/webdriver').WebDriver; +const WebElement = require('../../lib/webdriver').WebElement; +const WebElementPromise = require('../../lib/webdriver').WebElementPromise; + +const assert = require('assert'); +const sinon = require('sinon'); + +const SESSION_ID = 'test_session_id'; + +// Aliases for readability. +const NativePromise = Promise; +const StubError = testutil.StubError; +const assertIsInstance = testutil.assertIsInstance; +const assertIsStubError = testutil.assertIsStubError; +const throwStubError = testutil.throwStubError; +const fail = (msg) => assert.fail(msg); + +describe('WebDriver', function() { + const LOG = logging.getLogger('webdriver.test'); + + // before(function() { + // logging.getLogger('webdriver').setLevel(logging.Level.ALL); + // logging.installConsoleHandler(); + // }); + + // after(function() { + // logging.getLogger('webdriver').setLevel(null); + // logging.removeConsoleHandler(); + // }); + + var driver; + var flow; + var uncaughtExceptions; + + beforeEach(function setUp() { + flow = promise.controlFlow(); + uncaughtExceptions = []; + flow.on('uncaughtException', onUncaughtException); + }); + + afterEach(function tearDown() { + return waitForIdle(flow).then(function() { + assert.deepEqual([], uncaughtExceptions); + flow.reset(); + }); + }); + + function onUncaughtException(e) { + uncaughtExceptions.push(e); + } + + function waitForIdle(opt_flow) { + var theFlow = opt_flow || flow; + return new Promise(function(fulfill, reject) { + if (theFlow.isIdle()) { + fulfill(); + return; + } + theFlow.once('idle', fulfill); + theFlow.once('uncaughtException', reject); + }); + } + + function waitForAbort(opt_flow, opt_n) { + var n = opt_n || 1; + var theFlow = opt_flow || flow; + theFlow.removeAllListeners( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new Promise(function(fulfill, reject) { + theFlow.once('idle', function() { + reject(Error('expected flow to report an unhandled error')); + }); + + var errors = []; + theFlow.on('uncaughtException', onError); + function onError(e) { + errors.push(e); + if (errors.length === n) { + theFlow.removeListener('uncaughtException', onError); + fulfill(n === 1 ? errors[0] : errors); + } + } + }); + } + + function expectedError(ctor, message) { + return function(e) { + assertIsInstance(ctor, e); + assert.equal(message, e.message); + }; + } + + class Expectation { + constructor(executor, name, opt_parameters) { + this.executor_ = executor; + this.name_ = name; + this.times_ = 1; + this.sessionId_ = SESSION_ID; + this.check_ = null; + this.toDo_ = null; + this.withParameters(opt_parameters || {}); + } + + anyTimes() { + this.times_ = Infinity; + return this; + } + + times(n) { + this.times_ = n; + return this; + } + + withParameters(parameters) { + this.parameters_ = parameters; + if (this.name_ !== CName.NEW_SESSION) { + this.parameters_['sessionId'] = this.sessionId_; + } + return this; + } + + andReturn(code, opt_value) { + this.toDo_ = function(command) { + LOG.info('executing ' + command.getName() + '; returning ' + code); + return Promise.resolve(opt_value !== void(0) ? opt_value : null); + }; + return this; + } + + andReturnSuccess(opt_value) { + this.toDo_ = function(command) { + LOG.info('executing ' + command.getName() + '; returning success'); + return Promise.resolve(opt_value !== void(0) ? opt_value : null); + }; + return this; + } + + andReturnError(error) { + if (typeof error === 'number') { + throw Error('need error type'); + } + this.toDo_ = function(command) { + LOG.info('executing ' + command.getName() + '; returning failure'); + return Promise.reject(error); + }; + return this; + } + + expect(name, opt_parameters) { + this.end(); + return this.executor_.expect(name, opt_parameters); + } + + end() { + if (!this.toDo_) { + this.andReturnSuccess(null); + } + return this.executor_; + } + + execute(command) { + assert.deepEqual(this.parameters_, command.getParameters()); + return this.toDo_(command); + } + } + + class FakeExecutor { + constructor() { + this.commands_ = new Map; + } + + execute(command) { + let expectations = this.commands_.get(command.getName()); + if (!expectations || !expectations.length) { + assert.fail('unexpected command: ' + command.getName()); + return; + } + + let next = expectations[0]; + let result = next.execute(command); + if (next.times_ != Infinity) { + next.times_ -= 1; + if (!next.times_) { + expectations.shift(); + } + } + return result; + } + + expect(commandName, opt_parameters) { + if (!this.commands_.has(commandName)) { + this.commands_.set(commandName, []); + } + let e = new Expectation(this, commandName, opt_parameters); + this.commands_.get(commandName).push(e); + return e; + } + + createDriver(opt_session) { + let session = opt_session || new Session(SESSION_ID, {}); + return new WebDriver(session, this); + } + } + + + ///////////////////////////////////////////////////////////////////////////// + // + // Tests + // + ///////////////////////////////////////////////////////////////////////////// + + + describe('testAttachToSession', function() { + it('sessionIsAvailable', function() { + let aSession = new Session(SESSION_ID, {'browserName': 'firefox'}); + let executor = new FakeExecutor(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess(aSession). + end(); + + let driver = WebDriver.attachToSession(executor, SESSION_ID); + return driver.getSession().then(v => assert.strictEqual(v, aSession)); + }); + + it('failsToGetSessionInfo', function() { + let e = new Error('boom'); + let executor = new FakeExecutor(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnError(e). + end(); + + let driver = WebDriver.attachToSession(executor, SESSION_ID); + return driver.getSession() + .then(() => assert.fail('should have failed!'), + (actual) => assert.strictEqual(actual, e)); + }); + + it('remote end does not recognize DESCRIBE_SESSION command', function() { + let e = new error.UnknownCommandError; + let executor = new FakeExecutor(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnError(e). + end(); + + let driver = WebDriver.attachToSession(executor, SESSION_ID); + return driver.getSession().then(session => { + assert.ok(session instanceof Session); + assert.strictEqual(session.getId(), SESSION_ID); + assert.equal(session.getCapabilities().size, 0); + }); + }); + + it('usesActiveFlowByDefault', function() { + let executor = new FakeExecutor(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess({}). + end(); + + var driver = WebDriver.attachToSession(executor, SESSION_ID); + assert.equal(driver.controlFlow(), promise.controlFlow()); + + return waitForIdle(driver.controlFlow()); + }); + + it('canAttachInCustomFlow', function() { + let executor = new FakeExecutor(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess({}). + end(); + + var otherFlow = new promise.ControlFlow(); + var driver = WebDriver.attachToSession(executor, SESSION_ID, otherFlow); + assert.equal(otherFlow, driver.controlFlow()); + assert.notEqual(otherFlow, promise.controlFlow()); + + return waitForIdle(otherFlow); + }); + }); + + describe('testCreateSession', function() { + it('happyPathWithCapabilitiesHashObject', function() { + let aSession = new Session(SESSION_ID, {'browserName': 'firefox'}); + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({ + 'desiredCapabilities': {'browserName': 'firefox'} + }). + andReturnSuccess(aSession). + end(); + + var driver = WebDriver.createSession(executor, { + 'browserName': 'firefox' + }); + return driver.getSession().then(v => assert.strictEqual(v, aSession)); + }); + + it('happyPathWithCapabilitiesInstance', function() { + let aSession = new Session(SESSION_ID, {'browserName': 'firefox'}); + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {'browserName': 'firefox'}}). + andReturnSuccess(aSession). + end(); + + var driver = WebDriver.createSession(executor, Capabilities.firefox()); + return driver.getSession().then(v => assert.strictEqual(v, aSession)); + }); + + it('handles desired and requried capabilities', function() { + let aSession = new Session(SESSION_ID, {'browserName': 'firefox'}); + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({ + 'desiredCapabilities': {'foo': 'bar'}, + 'requiredCapabilities': {'bim': 'baz'} + }). + andReturnSuccess(aSession). + end(); + + let desired = new Capabilities().set('foo', 'bar'); + let required = new Capabilities().set('bim', 'baz'); + var driver = WebDriver.createSession(executor, {desired, required}); + return driver.getSession().then(v => assert.strictEqual(v, aSession)); + }); + + it('failsToCreateSession', function() { + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {'browserName': 'firefox'}}). + andReturnError(new StubError()). + end(); + + var driver = + WebDriver.createSession(executor, {'browserName': 'firefox'}); + return driver.getSession().then(fail, assertIsStubError); + }); + + it('usesActiveFlowByDefault', function() { + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {}}). + andReturnSuccess(new Session(SESSION_ID)). + end(); + + var driver = WebDriver.createSession(executor, {}); + assert.equal(promise.controlFlow(), driver.controlFlow()); + + return waitForIdle(driver.controlFlow()); + }); + + it('canCreateInCustomFlow', function() { + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {}}). + andReturnSuccess({}). + end(); + + var otherFlow = new promise.ControlFlow(); + var driver = WebDriver.createSession(executor, {}, otherFlow); + assert.equal(otherFlow, driver.controlFlow()); + assert.notEqual(otherFlow, promise.controlFlow()); + + return waitForIdle(otherFlow); + }); + }); + + it('testDoesNotExecuteCommandIfSessionDoesNotResolve', function() { + var session = Promise.reject(new StubError); + new FakeExecutor().createDriver(session).getTitle(); + return waitForAbort().then(assertIsStubError); + }); + + it('testCommandReturnValuesArePassedToFirstCallback', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE).andReturnSuccess('Google Search'). + end(); + + var driver = executor.createDriver(); + return driver.getTitle().then(function(title) { + assert.equal('Google Search', title); + }); + }); + + it('testStopsCommandExecutionWhenAnErrorOccurs', function() { + let e = new error.NoSuchWindowError('window not found'); + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({ + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(e). + end(); + + var driver = executor.createDriver(); + driver.switchTo().window('foo'); + driver.getTitle(); // mock should blow if this gets executed + + return waitForAbort().then(v => assert.strictEqual(v, e)); + }); + + it('testCanSuppressCommandFailures', function() { + let e = new error.NoSuchWindowError('window not found'); + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({ + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(e). + expect(CName.GET_TITLE). + andReturnSuccess('Google Search'). + end(); + + var driver = executor.createDriver(); + driver.switchTo().window('foo') + .catch(v => assert.strictEqual(v, e)); + driver.getTitle(); + return waitForIdle(); + }); + + it('testErrorsPropagateUpToTheRunningApplication', function() { + let e = new error.NoSuchWindowError('window not found'); + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({ + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(e). + end(); + + executor.createDriver().switchTo().window('foo'); + return waitForAbort().then(v => assert.strictEqual(v, e)); + }); + + it('testErrbacksThatReturnErrorsStillSwitchToCallbackChain', function() { + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({ + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(new error.NoSuchWindowError('window not found')). + end(); + + var driver = executor.createDriver(); + return driver.switchTo().window('foo'). + catch(function() { return new StubError; }); + then(assertIsStubError); + }); + + it('testErrbacksThrownCanOverrideOriginalError', function() { + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(new error.NoSuchWindowError('window not found')). + end(); + + var driver = executor.createDriver(); + driver.switchTo().window('foo').catch(throwStubError); + + return waitForAbort().then(assertIsStubError); + }); + + it('testCannotScheduleCommandsIfTheSessionIdHasBeenDeleted', function() { + var driver = new FakeExecutor().createDriver(); + delete driver.session_; + assert.throws(() => driver.get('http://www.google.com')); + }); + + it('testDeletesSessionIdAfterQuitting', function() { + var driver; + let executor = new FakeExecutor(). + expect(CName.QUIT). + end(); + + driver = executor.createDriver(); + return driver.quit().then(function() { + assert.equal(void 0, driver.session_); + }); + }); + + it('testReportsErrorWhenExecutingCommandsAfterExecutingAQuit', function() { + let executor = new FakeExecutor(). + expect(CName.QUIT). + end(); + + var driver = executor.createDriver(); + driver.quit(); + driver.get('http://www.google.com'); + return waitForAbort(). + then(expectedError( + error.NoSuchSessionError, + 'This driver instance does not have a valid session ID ' + + '(did you call WebDriver.quit()?) and may no longer be used.')); + }); + + it('testCallbackCommandsExecuteBeforeNextCommand', function() { + let executor = new FakeExecutor(). + expect(CName.GET_CURRENT_URL). + expect(CName.GET, {'url': 'http://www.google.com'}). + expect(CName.CLOSE). + expect(CName.GET_TITLE). + end(); + + var driver = executor.createDriver(); + driver.getCurrentUrl().then(function() { + driver.get('http://www.google.com').then(function() { + driver.close(); + }); + }); + driver.getTitle(); + + return waitForIdle(); + }); + + it('testEachCallbackFrameRunsToCompletionBeforeTheNext', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.GET_CURRENT_URL). + expect(CName.GET_CURRENT_WINDOW_HANDLE). + expect(CName.CLOSE). + expect(CName.QUIT). + end(); + + var driver = executor.createDriver(); + driver.getTitle(). + // Everything in this callback... + then(function() { + driver.getCurrentUrl(); + driver.getWindowHandle(); + }). + // ...should execute before everything in this callback. + then(function() { + driver.close(); + }); + // This should execute after everything above + driver.quit(); + + return waitForIdle(); + }); + + describe('nestedCommandFailures', function() { + it('bubbleUpToGlobalHandlerIfUnsuppressed', function() { + let e = new error.NoSuchWindowError('window not found'); + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(e). + end(); + + var driver = executor.createDriver(); + driver.getTitle().then(function() { + driver.switchTo().window('foo'); + }); + + return waitForAbort().then(v => assert.strictEqual(v, e)); + }); + + it('canBeSuppressWhenTheyOccur', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(new error.NoSuchWindowError('window not found')). + expect(CName.CLOSE). + end(); + + var driver = executor.createDriver(); + driver.getTitle().then(function() { + driver.switchTo().window('foo').catch(function() {}); + }); + driver.close(); + + return waitForIdle(); + }); + + it('bubbleUpThroughTheFrameStack', function() { + let e = new error.NoSuchWindowError('window not found'); + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(e). + end(); + + var driver = executor.createDriver(); + driver.getTitle(). + then(function() { + return driver.switchTo().window('foo'); + }). + catch(v => assert.strictEqual(v, e)); + + return waitForIdle(); + }); + + it('canBeCaughtAndSuppressed', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.GET_CURRENT_URL). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(new StubError()). + expect(CName.CLOSE). + end(); + + var driver = executor.createDriver(); + driver.getTitle().then(function() { + driver.getCurrentUrl(). + then(function() { + return driver.switchTo().window('foo'); + }). + catch(function() {}); + driver.close(); + }); + + return waitForIdle(); + }); + }); + + describe('returningAPromise', function() { + it('fromACallback', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.GET_CURRENT_URL). + andReturnSuccess('http://www.google.com'). + end(); + + var driver = executor.createDriver(); + driver.getTitle(). + then(function() { + return driver.getCurrentUrl(); + }). + then(function(value) { + assert.equal('http://www.google.com', value); + }); + return waitForIdle(); + }); + + it('fromAnErrbackSuppressesTheError', function() { + var count = 0; + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(new StubError()). + expect(CName.GET_CURRENT_URL). + andReturnSuccess('http://www.google.com'). + end(); + + var driver = executor.createDriver(); + driver.switchTo().window('foo'). + catch(function(e) { + assertIsStubError(e); + count += 1; + return driver.getCurrentUrl(); + }). + then(function(url) { + count += 1; + assert.equal('http://www.google.com', url); + }); + return waitForIdle().then(function() { + assert.equal(2, count); + }); + }); + }); + + describe('customFunctions', function() { + it('returnsANonPromiseValue', function() { + var driver = new FakeExecutor().createDriver(); + return driver.call(() => 'abc123').then(function(value) { + assert.equal('abc123', value); + }); + }); + + it('executionOrderWithCustomFunctions', function() { + var msg = []; + let executor = new FakeExecutor(). + expect(CName.GET_TITLE).andReturnSuccess('cheese '). + expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty'). + end(); + + var driver = executor.createDriver(); + + var pushMsg = msg.push.bind(msg); + driver.getTitle().then(pushMsg); + driver.call(() => 'is ').then(pushMsg); + driver.getCurrentUrl().then(pushMsg); + driver.call(() => '!').then(pushMsg); + + return waitForIdle().then(function() { + assert.equal('cheese is tasty!', msg.join('')); + }); + }); + + it('passingArgumentsToACustomFunction', function() { + var add = function(a, b) { + return a + b; + }; + var driver = new FakeExecutor().createDriver(); + return driver.call(add, null, 1, 2).then(function(value) { + assert.equal(3, value); + }); + }); + + it('passingPromisedArgumentsToACustomFunction', function() { + var promisedArg = promise.fulfilled(2); + var add = function(a, b) { + return a + b; + }; + var driver = new FakeExecutor().createDriver(); + return driver.call(add, null, 1, promisedArg).then(function(value) { + assert.equal(3, value); + }); + }); + + it('passingArgumentsAndScopeToACustomFunction', function() { + function Foo(name) { + this.name = name; + } + Foo.prototype.getName = function() { + return this.name; + }; + var foo = new Foo('foo'); + + var driver = new FakeExecutor().createDriver(); + return driver.call(foo.getName, foo).then(function(value) { + assert.equal('foo', value); + }); + }); + + it('customFunctionThrowsAnError', function() { + var driver = new FakeExecutor().createDriver(); + return driver.call(throwStubError).then(fail, assertIsStubError); + }); + + it('customFunctionSchedulesCommands', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.CLOSE). + expect(CName.QUIT). + end(); + + var driver = executor.createDriver(); + driver.call(function() { + driver.getTitle(); + driver.close(); + }); + driver.quit(); + return waitForIdle(); + }); + + it('returnsATaskResultAfterSchedulingAnother', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + andReturnSuccess('Google Search'). + expect(CName.CLOSE). + end(); + + var driver = executor.createDriver(); + return driver.call(function() { + var title = driver.getTitle(); + driver.close(); + return title; + }).then(function(title) { + assert.equal('Google Search', title); + }); + }); + + it('hasANestedCommandThatFails', function() { + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW, { + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(new StubError()). + end(); + + var driver = executor.createDriver(); + return driver.call(function() { + return driver.switchTo().window('foo'); + }).then(fail, assertIsStubError); + }); + + it('doesNotCompleteUntilReturnedPromiseIsResolved', function() { + var order = []; + var driver = new FakeExecutor().createDriver(); + + var d = promise.defer(); + d.promise.then(function() { + order.push('b'); + }); + + driver.call(function() { + order.push('a'); + return d.promise; + }); + driver.call(function() { + order.push('c'); + }); + + // timeout to ensure the first function starts its execution before we + // trigger d's callbacks. + return new Promise(f => setTimeout(f, 0)).then(function() { + assert.deepEqual(['a'], order); + d.fulfill(); + return waitForIdle().then(function() { + assert.deepEqual(['a', 'b', 'c'], order); + }); + }); + }); + + it('returnsADeferredAction', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE).andReturnSuccess('Google'). + end(); + + var driver = executor.createDriver(); + driver.call(function() { + return driver.getTitle(); + }).then(function(title) { + assert.equal('Google', title); + }); + return waitForIdle(); + }); + }); + + describe('nestedCommands', function() { + it('commandExecutionOrder', function() { + var msg = []; + var driver = new FakeExecutor().createDriver(); + driver.call(msg.push, msg, 'a'); + driver.call(function() { + driver.call(msg.push, msg, 'c'); + driver.call(function() { + driver.call(msg.push, msg, 'e'); + driver.call(msg.push, msg, 'f'); + }); + driver.call(msg.push, msg, 'd'); + }); + driver.call(msg.push, msg, 'b'); + return waitForIdle().then(function() { + assert.equal('acefdb', msg.join('')); + }); + }); + + it('basicUsage', function() { + var msg = []; + var driver = new FakeExecutor().createDriver(); + var pushMsg = msg.push.bind(msg); + driver.call(() => 'cheese ').then(pushMsg); + driver.call(function() { + driver.call(() => 'is ').then(pushMsg); + driver.call(() => 'tasty').then(pushMsg); + }); + driver.call(() => '!').then(pushMsg); + return waitForIdle().then(function() { + assert.equal('cheese is tasty!', msg.join('')); + }); + }); + + it('canReturnValueFromNestedFunction', function() { + var driver = new FakeExecutor().createDriver(); + return driver.call(function() { + return driver.call(function() { + return driver.call(() => 'foobar'); + }); + }).then(function(value) { + assert.equal('foobar', value); + }); + }); + + it('normalCommandAfterNestedCommandThatReturnsAnAction', function() { + var msg = []; + let executor = new FakeExecutor(). + expect(CName.CLOSE). + end(); + var driver = executor.createDriver(); + driver.call(function() { + return driver.call(function() { + msg.push('a'); + return driver.call(() => 'foobar'); + }); + }); + driver.close().then(function() { + msg.push('b'); + }); + return waitForIdle().then(function() { + assert.equal('ab', msg.join('')); + }); + }); + + it('errorsBubbleUp_caught', function() { + var driver = new FakeExecutor().createDriver(); + var result = driver.call(function() { + return driver.call(function() { + return driver.call(throwStubError); + }); + }).then(fail, assertIsStubError); + return Promise.all([waitForIdle(), result]); + }); + + it('errorsBubbleUp_uncaught', function() { + var driver = new FakeExecutor().createDriver(); + driver.call(function() { + return driver.call(function() { + return driver.call(throwStubError); + }); + }); + return waitForAbort().then(assertIsStubError); + }); + + it('canScheduleCommands', function() { + let executor = new FakeExecutor(). + expect(CName.GET_TITLE). + expect(CName.CLOSE). + end(); + + var driver = executor.createDriver(); + driver.call(function() { + driver.call(function() { + driver.getTitle(); + }); + driver.close(); + }); + return waitForIdle(); + }); + }); + + describe('WebElementPromise', function() { + it('resolvesWhenUnderlyingElementDoes', function() { + var el = new WebElement({}, {'ELEMENT': 'foo'}); + return new WebElementPromise({}, promise.fulfilled(el)).then(function(e) { + assert.strictEqual(e, el); + }); + }); + + it('resolvesBeforeCallbacksOnWireValueTrigger', function() { + var el = new promise.Deferred(); + + var element = new WebElementPromise({}, el.promise); + var messages = []; + + element.then(function() { + messages.push('element resolved'); + }); + element.getId().then(function() { + messages.push('wire value resolved'); + }); + + assert.deepEqual([], messages); + el.fulfill(new WebElement({}, {'ELEMENT': 'foo'})); + return waitForIdle().then(function() { + assert.deepEqual([ + 'element resolved', + 'wire value resolved' + ], messages); + }); + }); + + it('isRejectedIfUnderlyingIdIsRejected', function() { + var element = new WebElementPromise({}, promise.rejected(new StubError)); + return element.then(fail, assertIsStubError); + }); + }); + + describe('executeScript', function() { + it('nullReturnValue', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(null). + end(); + + var driver = executor.createDriver(); + return driver.executeScript('return document.body;') + .then((result) => assert.equal(null, result)); + }); + + it('primitiveReturnValue', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(123). + end(); + + var driver = executor.createDriver(); + return driver.executeScript('return document.body;') + .then((result) => assert.equal(123, result)); + }); + + it('webElementReturnValue', function() { + var json = WebElement.buildId('foo'); + + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(json). + end(); + + var driver = executor.createDriver(); + return driver.executeScript('return document.body;') + .then((element) => element.getId()) + .then((id) => assert.equal(id, 'foo')); + }); + + it('arrayReturnValue', function() { + var json = [WebElement.buildId('foo')]; + + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(json). + end(); + + var driver = executor.createDriver(); + return driver.executeScript('return document.body;') + .then(function(array) { + assert.equal(1, array.length); + return array[0].getId(); + }) + .then((id) => assert.equal('foo', id)); + }); + + it('objectReturnValue', function() { + var json = {'foo': WebElement.buildId('foo')}; + + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(json). + end(); + + var driver = executor.createDriver(); + var callback; + return driver.executeScript('return document.body;') + .then((obj) => obj['foo'].getId()) + .then((id) => assert.equal(id, 'foo')); + }); + + it('scriptAsFunction', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return (' + function() {} + + ').apply(null, arguments);', + 'args': [] + }). + andReturnSuccess(null). + end(); + + var driver = executor.createDriver(); + return driver.executeScript(function() {}); + }); + + it('simpleArgumentConversion', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': ['abc', 123, true, [123, {'foo': 'bar'}]] + }). + andReturnSuccess(null). + end(); + + var driver = executor.createDriver(); + return driver.executeScript( + 'return 1;', 'abc', 123, true, [123, {'foo': 'bar'}]); + }); + + it('webElementArgumentConversion', function() { + var elementJson = WebElement.buildId('fefifofum'); + + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': [elementJson] + }). + andReturnSuccess(null). + end(); + + var driver = executor.createDriver(); + return driver.executeScript('return 1;', + new WebElement(driver, 'fefifofum')); + }); + + it('webElementPromiseArgumentConversion', function() { + var elementJson = WebElement.buildId('bar'); + + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {'using': 'css selector', 'value': '*[id="foo"]'}). + andReturnSuccess(elementJson). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': [elementJson] + }). + andReturnSuccess(null). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.id('foo')); + return driver.executeScript('return 1;', element); + }); + + it('argumentConversion', function() { + var elementJson = WebElement.buildId('fefifofum'); + + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]] + }). + andReturnSuccess(null). + end(); + + var driver = executor.createDriver(); + var element = new WebElement(driver, 'fefifofum'); + return driver.executeScript('return 1;', + 'abc', 123, true, element, [123, {'foo': 'bar'}]); + }); + + it('scriptReturnsAnError', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'throw Error(arguments[0]);', + 'args': ['bam'] + }). + andReturnError(new StubError). + end(); + var driver = executor.createDriver(); + return driver.executeScript('throw Error(arguments[0]);', 'bam'). + then(fail, assertIsStubError); + }); + + it('failsIfArgumentIsARejectedPromise', function() { + let executor = new FakeExecutor(); + + var arg = promise.rejected(new StubError); + arg.catch(function() {}); // Suppress default handler. + + var driver = executor.createDriver(); + return driver.executeScript(function() {}, arg). + then(fail, assertIsStubError); + }); + }); + + describe('executeAsyncScript', function() { + it('failsIfArgumentIsARejectedPromise', function() { + var arg = promise.rejected(new StubError); + arg.catch(function() {}); // Suppress default handler. + + var driver = new FakeExecutor().createDriver(); + return driver.executeAsyncScript(function() {}, arg). + then(fail, assertIsStubError); + }); + }); + + describe('findElement', function() { + it('elementNotFound', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnError(new StubError). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.id('foo')); + element.click(); // This should never execute. + return waitForAbort().then(assertIsStubError); + }); + + it('elementNotFoundInACallback', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnError(new StubError). + end(); + + var driver = executor.createDriver(); + promise.fulfilled().then(function() { + var element = driver.findElement(By.id('foo')); + return element.click(); // Should not execute. + }); + return waitForAbort().then(assertIsStubError); + }); + + it('elementFound', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess(WebElement.buildId('bar')). + expect(CName.CLICK_ELEMENT, {'id': 'bar'}). + andReturnSuccess(). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.id('foo')); + element.click(); + return waitForIdle(); + }); + + it('canUseElementInCallback', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess(WebElement.buildId('bar')). + expect(CName.CLICK_ELEMENT, {'id': 'bar'}). + andReturnSuccess(). + end(); + + var driver = executor.createDriver(); + driver.findElement(By.id('foo')).then(function(element) { + element.click(); + }); + return waitForIdle(); + }); + + it('byJs', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.body', + 'args': [] + }). + andReturnSuccess(WebElement.buildId('bar')). + expect(CName.CLICK_ELEMENT, {'id': 'bar'}). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.js('return document.body')); + element.click(); // just to make sure + return waitForIdle(); + }); + + it('byJs_returnsNonWebElementValue', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, {'script': 'return 123', 'args': []}). + andReturnSuccess(123). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.js('return 123')); + element.click(); // Should not execute. + return waitForAbort().then(function(e) { + assertIsInstance(TypeError, e); + assert.equal('Custom locator did not return a WebElement', e.message); + }); + }); + + it('byJs_canPassArguments', function() { + var script = 'return document.getElementsByTagName(arguments[0]);'; + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, { + 'script': script, + 'args': ['div'] + }). + andReturnSuccess({'ELEMENT':'one'}). + end(); + var driver = executor.createDriver(); + driver.findElement(By.js(script, 'div')); + return waitForIdle(); + }); + + it('customLocator', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, {'using': 'css selector', 'value': 'a'}). + andReturnSuccess([ + WebElement.buildId('foo'), + WebElement.buildId('bar')]). + expect(CName.CLICK_ELEMENT, {'id': 'foo'}). + andReturnSuccess(). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(function(d) { + assert.equal(driver, d); + return d.findElements(By.tagName('a')); + }); + element.click(); + return waitForIdle(); + }); + + it('customLocatorThrowsIfresultIsNotAWebElement', function() { + var driver = new FakeExecutor().createDriver(); + driver.findElement(function() { + return 1; + }); + return waitForAbort().then(function(e) { + assertIsInstance(TypeError, e); + assert.equal('Custom locator did not return a WebElement', e.message); + }); + }); + }); + + describe('findElements', function() { + it('returnsMultipleElements', function() { + var ids = ['foo', 'bar', 'baz']; + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, {'using':'css selector', 'value':'a'}). + andReturnSuccess(ids.map(WebElement.buildId)). + end(); + + var driver = executor.createDriver(); + return driver.findElements(By.tagName('a')) + .then(function(elements) { + return promise.all(elements.map(function(e) { + assert.ok(e instanceof WebElement); + return e.getId(); + })); + }) + .then((actual) => assert.deepEqual(ids, actual)); + }); + + it('byJs', function() { + var ids = ['foo', 'bar', 'baz']; + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.getElementsByTagName("div");', + 'args': [] + }). + andReturnSuccess(ids.map(WebElement.buildId)). + end(); + + var driver = executor.createDriver(); + + return driver. + findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { + return promise.all(elements.map(function(e) { + assert.ok(e instanceof WebElement); + return e.getId(); + })); + }). + then((actual) => assert.deepEqual(ids, actual)); + }); + + it('byJs_filtersOutNonWebElementResponses', function() { + var ids = ['foo', 'bar', 'baz']; + var json = [ + WebElement.buildId(ids[0]), + 123, + 'a', + false, + WebElement.buildId(ids[1]), + {'not a web element': 1}, + WebElement.buildId(ids[2]) + ]; + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.getElementsByTagName("div");', + 'args': [] + }). + andReturnSuccess(json). + end(); + + var driver = executor.createDriver(); + driver.findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { + return promise.all(elements.map(function(e) { + assert.ok(e instanceof WebElement); + return e.getId(); + })); + }). + then((actual) => assert.deepEqual(ids, actual)); + return waitForIdle(); + }); + + it('byJs_convertsSingleWebElementResponseToArray', function() { + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.getElementsByTagName("div");', + 'args': [] + }). + andReturnSuccess(WebElement.buildId('foo')). + end(); + + var driver = executor.createDriver(); + return driver. + findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { + return promise.all(elements.map(function(e) { + assert.ok(e instanceof WebElement); + return e.getId(); + })); + }). + then((actual) => assert.deepEqual(['foo'], actual)); + }); + + it('byJs_canPassScriptArguments', function() { + var script = 'return document.getElementsByTagName(arguments[0]);'; + let executor = new FakeExecutor(). + expect(CName.EXECUTE_SCRIPT, { + 'script': script, + 'args': ['div'] + }). + andReturnSuccess([ + WebElement.buildId('one'), + WebElement.buildId('two') + ]). + end(); + + var driver = executor.createDriver(); + return driver.findElements(By.js(script, 'div')) + then(function(elements) { + return promise.all(elements.map(function(e) { + assert.ok(e instanceof WebElement); + return e.getId(); + })); + }). + then((actual) => assert.deepEqual(['one', 'two'], actual)); + }); + }); + + describe('sendKeys', function() { + it('convertsVarArgsIntoStrings_simpleArgs', function() { + let executor = new FakeExecutor(). + expect(CName.SEND_KEYS_TO_ELEMENT, + {'id': 'one', 'value':'12abc3'.split('')}). + andReturnSuccess(). + end(); + + var driver = executor.createDriver(); + var element = new WebElement(driver, 'one'); + element.sendKeys(1, 2, 'abc', 3); + return waitForIdle(); + }); + + it('convertsVarArgsIntoStrings_promisedArgs', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {'using':'css selector', 'value':'*[id="foo"]'}). + andReturnSuccess(WebElement.buildId('one')). + expect(CName.SEND_KEYS_TO_ELEMENT, + {'id':'one', 'value':'abc123def'.split('')}). + andReturnSuccess(). + end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.id('foo')); + element.sendKeys( + promise.fulfilled('abc'), 123, + promise.fulfilled('def')); + return waitForIdle(); + }); + + it('sendKeysWithAFileDetector', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {'using':'css selector', 'value':'*[id="foo"]'}). + andReturnSuccess(WebElement.buildId('one')). + expect(CName.SEND_KEYS_TO_ELEMENT, + {'id': 'one', 'value':'modified/path'.split('')}). + andReturnSuccess(). + end(); + + let driver = executor.createDriver(); + let handleFile = function(d, path) { + assert.strictEqual(driver, d); + assert.equal(path, 'original/path'); + return promise.fulfilled('modified/path'); + }; + driver.setFileDetector({handleFile}); + + var element = driver.findElement(By.id('foo')); + element.sendKeys('original/', 'path'); + return waitForIdle(); + }); + }); + + describe("switchTo()", function() { + describe("window", function() { + it('should return a resolved promise when the window is found', function() { + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({ + 'name': 'foo', + 'handle': 'foo' + }). + andReturnSuccess(). + end(); + + executor.createDriver().switchTo().window('foo'); + return waitForIdle(); + }); + + it('should propogate exceptions', function() { + let e = new error.NoSuchWindowError('window not found'); + let executor = new FakeExecutor(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({ + 'name': 'foo', + 'handle': 'foo' + }). + andReturnError(e). + end(); + + executor.createDriver().switchTo().window('foo'); + return waitForAbort().then(v => assert.strictEqual(v, e)); + }); + }); + }); + + describe('elementEquality', function() { + it('isReflexive', function() { + var a = new WebElement({}, 'foo'); + return WebElement.equals(a, a).then(assert.ok); + }); + + it('failsIfAnInputElementCouldNotBeFound', function() { + var id = promise.rejected(new StubError); + id.catch(function() {}); // Suppress default handler. + + var driver = new FakeExecutor().createDriver(); + var a = new WebElement(driver, 'foo'); + var b = new WebElementPromise(driver, id); + + return WebElement.equals(a, b).then(fail, assertIsStubError); + }); + }); + + describe('waiting', function() { + it('waitSucceeds', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([]). + times(2). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([WebElement.buildId('bar')]). + end(); + + var driver = executor.createDriver(); + driver.wait(function() { + return driver.findElements(By.id('foo')).then(els => els.length > 0); + }, 200); + return waitForIdle(); + }); + + it('waitTimesout_timeoutCaught', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([]). + anyTimes(). + end(); + + var driver = executor.createDriver(); + return driver.wait(function() { + return driver.findElements(By.id('foo')).then(els => els.length > 0); + }, 25).then(fail, function(e) { + assert.equal('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); + }); + + it('waitTimesout_timeoutNotCaught', function() { + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENTS, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnSuccess([]). + anyTimes(). + end(); + + var driver = executor.createDriver(); + driver.wait(function() { + return driver.findElements(By.id('foo')).then(els => els.length > 0); + }, 25); + return waitForAbort().then(function(e) { + assert.equal('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); + }); + }); + + describe('alert handling', function() { + it('alertResolvesWhenPromisedTextResolves', function() { + var deferredText = new promise.Deferred(); + + var alert = new AlertPromise({}, deferredText.promise); + assert.ok(alert.isPending()); + + deferredText.fulfill(new Alert({}, 'foo')); + return alert.getText().then(function(text) { + assert.equal('foo', text); + }); + }); + + it('cannotSwitchToAlertThatIsNotPresent', function() { + let e = new error.NoSuchAlertError; + let executor = new FakeExecutor() + .expect(CName.GET_ALERT_TEXT) + .andReturnError(e) + .end(); + + var driver = executor.createDriver(); + var alert = driver.switchTo().alert(); + alert.dismiss(); // Should never execute. + return waitForAbort().then(v => assert.strictEqual(v, e)); + }); + + it('alertsBelongToSameFlowAsParentDriver', function() { + let executor = new FakeExecutor() + .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello') + .end(); + + var driver = executor.createDriver(); + var otherFlow = new promise.ControlFlow(); + otherFlow.execute(function() { + driver.switchTo().alert().then(function() { + assert.strictEqual( + driver.controlFlow(), promise.controlFlow(), + 'Alert should belong to the same flow as its parent driver'); + }); + }); + + assert.notEqual(otherFlow, driver.controlFlow); + return Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); + }); + + it('commandsFailIfAlertNotPresent', function() { + let e = new error.NoSuchAlertError; + let executor = new FakeExecutor() + .expect(CName.GET_ALERT_TEXT) + .andReturnError(e) + .end(); + + var driver = executor.createDriver(); + var alert = driver.switchTo().alert(); + + var expectError = (v) => assert.strictEqual(v, e); + + return alert.getText() + .then(fail, expectedError) + .then(() => alert.accept()) + .then(fail, expectedError) + .then(() => alert.dismiss()) + .then(fail, expectError) + .then(() => alert.sendKeys('hi')) + .then(fail, expectError); + }); + }); + + it('testWebElementsBelongToSameFlowAsParentDriver', function() { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}) + .andReturnSuccess(WebElement.buildId('abc123')) + .end(); + + var driver = executor.createDriver(); + var otherFlow = new promise.ControlFlow(); + otherFlow.execute(function() { + driver.findElement({id: 'foo'}).then(function() { + assert.equal(driver.controlFlow(), promise.controlFlow()); + }); + }); + + assert.notEqual(otherFlow, driver.controlFlow); + return Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); + }); + + it('testFetchingLogs', function() { + let executor = new FakeExecutor(). + expect(CName.GET_LOG, {'type': 'browser'}). + andReturnSuccess([ + {'level': 'INFO', 'message': 'hello', 'timestamp': 1234}, + {'level': 'DEBUG', 'message': 'abc123', 'timestamp': 5678} + ]). + end(); + + var driver = executor.createDriver(); + return driver.manage().logs().get('browser').then(function(entries) { + assert.equal(2, entries.length); + + assert.ok(entries[0] instanceof logging.Entry); + assert.equal(logging.Level.INFO.value, entries[0].level.value); + assert.equal('hello', entries[0].message); + assert.equal(1234, entries[0].timestamp); + + assert.ok(entries[1] instanceof logging.Entry); + assert.equal(logging.Level.DEBUG.value, entries[1].level.value); + assert.equal('abc123', entries[1].message); + assert.equal(5678, entries[1].timestamp); + }); + }); + + it('testCommandsFailIfInitialSessionCreationFailed', function() { + var session = promise.rejected(new StubError); + + var driver = new FakeExecutor().createDriver(session); + var navigateResult = driver.get('some-url').then(fail, assertIsStubError); + var quitResult = driver.quit().then(fail, assertIsStubError); + + return waitForIdle().then(function() { + return promise.all(navigateResult, quitResult); + }); + }); + + it('testWebElementCommandsFailIfInitialDriverCreationFailed', function() { + var session = Promise.reject(new StubError); + var driver = new FakeExecutor().createDriver(session); + return driver.findElement(By.id('foo')).click(). + then(fail, assertIsStubError); + }); + + it('testWebElementCommansFailIfElementCouldNotBeFound', function() { + let e = new error.NoSuchElementError('Unable to find element'); + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnError(e). + end(); + + var driver = executor.createDriver(); + return driver.findElement(By.id('foo')).click() + .then(fail, v => assert.strictEqual(v, e)); + }); + + it('testCannotFindChildElementsIfParentCouldNotBeFound', function() { + let e = new error.NoSuchElementError('Unable to find element'); + let executor = new FakeExecutor(). + expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}). + andReturnError(e). + end(); + + var driver = executor.createDriver(); + return driver.findElement(By.id('foo')) + .findElement(By.id('bar')) + .findElement(By.id('baz')) + .then(fail, v => assert.strictEqual(v, e)); + }); + + describe('actions()', function() { + it('failsIfInitialDriverCreationFailed', function() { + let session = promise.rejected(new StubError); + session.catch(function() {}); + return new FakeExecutor(). + createDriver(session). + actions(). + mouseDown(). + mouseUp(). + perform(). + catch(assertIsStubError); + }); + + describe('mouseMove', function() { + it('noElement', function() { + let executor = new FakeExecutor() + .expect(CName.MOVE_TO, {'xoffset': 0, 'yoffset': 125}) + .andReturnSuccess() + .end(); + + return executor.createDriver(). + actions(). + mouseMove({x: 0, y: 125}). + perform(); + }); + + it('element', function() { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="foo"]'}) + .andReturnSuccess(WebElement.buildId('abc123')) + .expect(CName.MOVE_TO, + {'element': 'abc123', 'xoffset': 0, 'yoffset': 125}) + .andReturnSuccess() + .end(); + + var driver = executor.createDriver(); + var element = driver.findElement(By.id('foo')); + return driver.actions() + .mouseMove(element, {x: 0, y: 125}) + .perform(); + }); + }); + + it('supportsMouseDown', function() { + let executor = new FakeExecutor() + .expect(CName.MOUSE_DOWN, {'button': Button.LEFT}) + .andReturnSuccess() + .end(); + + return executor.createDriver(). + actions(). + mouseDown(). + perform(); + }); + + it('testActionSequence', function() { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="a"]'}) + .andReturnSuccess(WebElement.buildId('id1')) + .expect(CName.FIND_ELEMENT, + {using: 'css selector', value: '*[id="b"]'}) + .andReturnSuccess(WebElement.buildId('id2')) + .expect(CName.SEND_KEYS_TO_ACTIVE_ELEMENT, + {'value': [Key.SHIFT]}) + .andReturnSuccess() + .expect(CName.MOVE_TO, {'element': 'id1'}) + .andReturnSuccess() + .expect(CName.CLICK, {'button': Button.LEFT}) + .andReturnSuccess() + .expect(CName.MOVE_TO, {'element': 'id2'}) + .andReturnSuccess() + .expect(CName.CLICK, {'button': Button.LEFT}) + .andReturnSuccess() + .end(); + + var driver = executor.createDriver(); + var element1 = driver.findElement(By.id('a')); + var element2 = driver.findElement(By.id('b')); + + return driver.actions() + .keyDown(Key.SHIFT) + .click(element1) + .click(element2) + .perform(); + }); + }); + + describe('touchActions()', function() { + it('failsIfInitialDriverCreationFailed', function() { + let session = promise.rejected(new StubError); + session.catch(function() {}); + return new FakeExecutor(). + createDriver(session). + touchActions(). + scroll({x: 3, y: 4}). + perform(). + catch(assertIsStubError); + }); + + it('testTouchActionSequence', function() { + let executor = new FakeExecutor() + .expect(CName.TOUCH_DOWN, {x: 1, y: 2}).andReturnSuccess() + .expect(CName.TOUCH_MOVE, {x: 3, y: 4}).andReturnSuccess() + .expect(CName.TOUCH_UP, {x: 5, y: 6}).andReturnSuccess() + .end(); + + var driver = executor.createDriver(); + return driver.touchActions() + .tapAndHold({x: 1, y: 2}) + .move({x: 3, y: 4}) + .release({x: 5, y: 6}) + .perform(); + }); + }); + + describe('generator support', function() { + var driver; + + beforeEach(function() { + driver = new WebDriver( + new Session('test-session', {}), + new ExplodingExecutor()); + }); + + it('canUseGeneratorsWithWebDriverCall', function() { + return driver.call(function* () { + var x = yield promise.fulfilled(1); + var y = yield promise.fulfilled(2); + return x + y; + }).then(function(value) { + assert.deepEqual(3, value); + }); + }); + + it('canDefineScopeOnGeneratorCall', function() { + return driver.call(function* () { + var x = yield promise.fulfilled(1); + return this.name + x; + }, {name: 'Bob'}).then(function(value) { + assert.deepEqual('Bob1', value); + }); + }); + + it('canSpecifyArgsOnGeneratorCall', function() { + return driver.call(function* (a, b) { + var x = yield promise.fulfilled(1); + var y = yield promise.fulfilled(2); + return [x + y, a, b]; + }, null, 'abc', 123).then(function(value) { + assert.deepEqual([3, 'abc', 123], value); + }); + }); + + it('canUseGeneratorWithWebDriverWait', function() { + var values = []; + return driver.wait(function* () { + yield values.push(1); + values.push(yield promise.delayed(10).then(function() { + return 2; + })); + yield values.push(3); + return values.length === 6; + }, 250).then(function() { + assert.deepEqual([1, 2, 3, 1, 2, 3], values); + }); + }); + + /** + * @constructor + * @implements {CommandExecutor} + */ + function ExplodingExecutor() {} + + + /** @override */ + ExplodingExecutor.prototype.execute = function(command, cb) { + cb(Error('Unsupported operation')); + }; + }); + + describe('wire format', function() { + describe('can serialize', function() { + function runSerializeTest(input, want) { + let executor = new FakeExecutor(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': want}). + andReturnSuccess({'browserName': 'firefox'}). + end(); + return WebDriver.createSession(executor, input) + .getSession(); + } + + it('function as a string', function() { + function foo() { return 'foo'; } + return runSerializeTest(foo, '' + foo); + }); + + it('object with toJSON()', function() { + return runSerializeTest( + new Date(605728511546), + '1989-03-12T17:55:11.546Z'); + }); + + it('Session', function() { + return runSerializeTest(new Session('foo', {}), 'foo'); + }); + + it('Capabilities', function() { + var prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG); + + var caps = Capabilities.chrome(); + caps.setLoggingPrefs(prefs); + + return runSerializeTest( + caps, + { + 'browserName': 'chrome', + 'loggingPrefs': {'browser': 'DEBUG'} + }); + }); + + it('WebElement', function() { + return runSerializeTest( + new WebElement({}, 'fefifofum'), + WebElement.buildId('fefifofum')); + }); + + it('WebElementPromise', function() { + return runSerializeTest( + new WebElementPromise( + {}, promise.fulfilled(new WebElement({}, 'fefifofum'))), + WebElement.buildId('fefifofum')); + }); + + describe('an array', function() { + it('with Serializable', function() { + return runSerializeTest([new Session('foo', {})], ['foo']); + }); + + it('with WebElement', function() { + return runSerializeTest( + [new WebElement({}, 'fefifofum')], + [WebElement.buildId('fefifofum')]); + }); + + it('with WebElementPromise', function() { + return runSerializeTest( + [new WebElementPromise( + {}, promise.fulfilled(new WebElement({}, 'fefifofum')))], + [WebElement.buildId('fefifofum')]); + }); + + it('complex array', function() { + var expected = [ + 'abc', 123, true, WebElement.buildId('fefifofum'), + [123, {'foo': 'bar'}] + ]; + + var element = new WebElement({}, 'fefifofum'); + var input = ['abc', 123, true, element, [123, {'foo': 'bar'}]]; + return runSerializeTest(input, expected); + }); + + it('nested promises', function() { + return runSerializeTest( + ['abc', Promise.resolve([123, Promise.resolve(true)])], + ['abc', [123, true]]); + }); + }); + + describe('an object', function() { + it('literal', function() { + var expected = {sessionId: 'foo'}; + return runSerializeTest({sessionId: 'foo'}, expected); + }); + + it('with sub-objects', function() { + var expected = {sessionId: {value: 'foo'}}; + return runSerializeTest( + {sessionId: {value: 'foo'}}, expected); + }); + + it('with values that have toJSON', function() { + return runSerializeTest( + {a: {b: new Date(605728511546)}}, + {a: {b: '1989-03-12T17:55:11.546Z'}}); + }); + + it('with a Session', function() { + return runSerializeTest( + {a: new Session('foo', {})}, + {a: 'foo'}); + }); + + it('nested', function() { + var elementJson = WebElement.buildId('fefifofum'); + var expected = { + 'script': 'return 1', + 'args': ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]], + 'sessionId': 'foo' + }; + + var element = new WebElement({}, 'fefifofum'); + var parameters = { + 'script': 'return 1', + 'args':['abc', 123, true, element, [123, {'foo': 'bar'}]], + 'sessionId': new Session('foo', {}) + }; + + return runSerializeTest(parameters, expected); + }); + }); + }); + + describe('can deserialize', function() { + function runDeserializeTest(original, want) { + let executor = new FakeExecutor() + .expect(CName.GET_CURRENT_URL) + .andReturnSuccess(original) + .end(); + let driver = executor.createDriver(); + return driver.getCurrentUrl().then(function(got) { + assert.deepEqual(got, want); + }); + } + + it('primitives', function() { + return Promise.all([ + runDeserializeTest(1, 1), + runDeserializeTest('', ''), + runDeserializeTest(true, true), + runDeserializeTest(undefined, undefined), + runDeserializeTest(null, null) + ]); + }); + + it('simple object', function() { + return runDeserializeTest( + {sessionId: 'foo'}, + {sessionId: 'foo'}); + }); + + it('nested object', function() { + return runDeserializeTest( + {'foo': {'bar': 123}}, + {'foo': {'bar': 123}}); + }); + + it('array', function() { + return runDeserializeTest( + [{'foo': {'bar': 123}}], + [{'foo': {'bar': 123}}]); + }); + + it('passes through function properties', function() { + function bar() {} + return runDeserializeTest( + [{foo: {'bar': 123}, func: bar}], + [{foo: {'bar': 123}, func: bar}]); + }); + }); + }); +}); |