UNPKG

selenium-webdriver

Version:

The official WebDriver JavaScript bindings from the Selenium project

893 lines (725 loc) 22.9 kB
// 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. /** * @fileoverview Contains tests against promise error handling. Many tests use * goog.Promise to control test termination independent of webdriver.promise * (and notably webdriver.promise.ControlFlow). */ 'use strict'; goog.require('goog.Promise'); goog.require('goog.async.run'); goog.require('goog.testing.jsunit'); goog.require('goog.userAgent'); goog.require('goog.userAgent.product'); goog.require('webdriver.promise'); goog.require('webdriver.test.testutil'); var StubError = webdriver.test.testutil.StubError, throwStubError = webdriver.test.testutil.throwStubError, assertIsStubError = webdriver.test.testutil.assertIsStubError; var flow, uncaughtExceptions; function longStackTracesAreBroken() { // Safari 8.0 is "Safari/538.35.8" in the user agent. return goog.userAgent.product.SAFARI && !goog.userAgent.isVersionOrHigher(538); } function shouldRunTests() { return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); } function setUp() { flow = webdriver.promise.controlFlow(); uncaughtExceptions = []; flow.on('uncaughtException', onUncaughtException); } function tearDown() { return waitForIdle(flow).then(function() { assertArrayEquals('There were uncaught exceptions', [], uncaughtExceptions); flow.reset(); }); } function onUncaughtException(e) { uncaughtExceptions.push(e); } function waitForAbort(opt_flow, opt_n) { var n = opt_n || 1; var theFlow = opt_flow || flow; theFlow.removeAllListeners( webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); return new goog.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 waitForIdle(opt_flow) { var theFlow = opt_flow || flow; return new goog.Promise(function(fulfill, reject) { if (theFlow.isIdle()) { fulfill(); return; } theFlow.once('idle', fulfill); theFlow.once('uncaughtException', reject); }); } function testRejectedPromiseTriggersErrorCallback() { return webdriver.promise.rejected(new StubError). then(fail, assertIsStubError); } function testCallbackThrowsTriggersSubsequentErrorCallback_fulfilledPromise() { return webdriver.promise.fulfilled(). then(throwStubError). then(fail, assertIsStubError); } function testCallbackThrowsTriggersSubsequentErrorCallback_rejectedPromise() { var e = Error('not the droids you are looking for'); return webdriver.promise.rejected(e). then(fail, throwStubError). then(fail, assertIsStubError); } function testCallbackReturnsRejectedPromiseTriggersSubsequentErrback_fulfilled() { return webdriver.promise.fulfilled().then(function() { return webdriver.promise.rejected(new StubError); }).then(fail, assertIsStubError); } function testCallbackReturnsRejectedPromiseTriggersSubsequentErrback_rejected() { var e = Error('not the droids you are looking for'); return webdriver.promise.rejected(e). then(fail, function() { return webdriver.promise.rejected(new StubError); }). then(fail, assertIsStubError); } function testReportsUnhandledRejectionsThroughTheControlFlow() { webdriver.promise.rejected(new StubError); return waitForAbort().then(assertIsStubError); } function testMultipleUnhandledRejectionsOutsideATask_reportedInOrderOccurred() { var e1 = Error('error 1'); var e2 = Error('error 2'); webdriver.promise.rejected(e1); webdriver.promise.rejected(e2); return waitForAbort(flow).then(function(error) { assertTrue( error instanceof webdriver.promise.MultipleUnhandledRejectionError); // TODO: switch to Array.from when we drop node 0.12.x var errors = []; for (var e of error.errors) { errors.push(e); } assertArrayEquals([e1, e2], errors); }); } function testDoesNotReportUnhandledErrorIfHandlerAddedBeforeNextTick() { var promise = webdriver.promise.rejected(new StubError); promise.then(fail, assertIsStubError); return waitForIdle(); } function testDoesNotReportUnhandledErrorIfHandlerAddedAsyncBeforeReport() { var called = false; return new goog.Promise(function(fulfill, reject) { var promise; goog.async.run(function() { promise.then(fail, function(e) { called = true; assertIsStubError(e); }); waitForIdle().then(fulfill, reject); }); promise = webdriver.promise.rejected(new StubError); }).then(function() { assertTrue(called); }) } function testTaskThrows() { return flow.execute(throwStubError).then(fail, assertIsStubError); } function testTaskReturnsRejectedPromise() { return flow.execute(function() { return webdriver.promise.rejected(new StubError) }).then(fail, assertIsStubError); } function testTaskHasUnhandledRejection() { return flow.execute(function() { webdriver.promise.rejected(new StubError) }).then(fail, assertIsStubError); } function testTaskFails_returnedPromiseIsUnhandled() { flow.execute(throwStubError); return waitForAbort().then(assertIsStubError); } function testTaskHasUnhandledRejection_cancelsRemainingSubTasks() { var seen = []; flow.execute(function() { webdriver.promise.rejected(new StubError); flow.execute(() => seen.push('a')) .then(() => seen.push('b'), (e) => seen.push(e)); flow.execute(() => seen.push('c')) .then(() => seen.push('b'), (e) => seen.push(e)); }); return waitForAbort() .then(assertIsStubError) .then(() => assertArrayEquals([], seen)); } function testSubTaskFails_caughtByParentTask() { return flow.execute(function() { flow.execute(throwStubError); }).then(fail, assertIsStubError); } function testSubTaskFails_uncaughtErrorBubblesUpToFlow() { flow.execute(function() { flow.execute(throwStubError); }); return waitForAbort().then(assertIsStubError); } function testSubTasilFails_cancelsRemainingSubTasks() { var seen = []; flow.execute(function() { flow.execute(() => webdriver.promise.rejected(new StubError)); flow.execute(() => seen.push('a')) .then(() => seen.push('b'), (e) => seen.push(e)); flow.execute(() => seen.push('c')) .then(() => seen.push('b'), (e) => seen.push(e)); }); return waitForAbort() .then(assertIsStubError) .then(() => assertArrayEquals([], seen)); } function testNestedTaskFails_returnsUpToParent() { return flow.execute(function() { return flow.execute(function() { return flow.execute(throwStubError); }); }).then(fail, assertIsStubError); } function testNestedTaskFails_uncaughtErrorBubblesUp_taskThrows() { flow.execute(function() { flow.execute(function() { flow.execute(throwStubError); }); }); return waitForAbort().then(assertIsStubError); } function testNestedTaskFails_uncaughtErrorBubblesUp_taskThrows_caughtAtRoot() { flow.execute(function() { flow.execute(function() { flow.execute(throwStubError); }); }).then(fail, assertIsStubError); return waitForIdle(); } function testNestedTaskFails_uncaughtErrorBubblesUp_promise() { flow.execute(function() { flow.execute(function() { flow.execute(function() { webdriver.promise.rejected(new StubError); }); }); }); return waitForAbort().then(assertIsStubError); } function testNestedTaskFails_uncaughtErrorBubblesUp_promise_caughtAtRoot() { flow.execute(function() { flow.execute(function() { webdriver.promise.rejected(new StubError); }); }).then(fail, assertIsStubError); return waitForIdle(); } function testNestedTaskFails_mixtureOfHangingAndFreeSubTasks() { flow.execute(function() { return flow.execute(function() { flow.execute(throwStubError); }); }); return waitForAbort().then(assertIsStubError); } function testTaskReturnsPromiseLikeObjectThatInvokesErrback() { return flow.execute(function() { return { 'then': function(_, errback) { errback('abc123'); } }; }).then(fail, function(value) { assertEquals('abc123', value); }); } function testWaitConditionThrows_waitFailureIsCaught() { return flow.wait(throwStubError, 50).then(fail, assertIsStubError); } function testWaitConditionThrows_waitFailureIsNotCaught() { flow.wait(throwStubError, 50); return waitForAbort().then(assertIsStubError); } function testWaitConditionReturnsRejectedPromise_waitFailureIsCaught() { return flow.wait(function() { return webdriver.promise.rejected(new StubError); }, 50).then(fail, assertIsStubError); } function testWaitConditionReturnsRejectedPromise_waitFailureIsNotCaught() { flow.wait(function() { return webdriver.promise.rejected(new StubError); }, 50); return waitForAbort().then(assertIsStubError); } function testWaitConditionHasUnhandledPromiseRejection_waitFailureCaught() { return flow.wait(function() { webdriver.promise.rejected(new StubError); }, 50).then(fail, assertIsStubError); } function testWaitConditionHasUnhandledPromiseRejection_waitFailureNotCaught() { flow.wait(function() { webdriver.promise.rejected(new StubError); }, 50); return waitForAbort().then(assertIsStubError); } function testWaitConditionHasSubTaskFailure_caughtByWait() { return flow.wait(function() { flow.execute(function() { flow.execute(throwStubError); }); }, 50).then(fail, assertIsStubError); } function testWaitConditionHasSubTaskFailure_notCaughtByWait() { flow.wait(function() { flow.execute(function() { flow.execute(throwStubError); }); }, 50); return waitForAbort().then(assertIsStubError); } function testErrbackMayThrowANewError_startWithNormalPromise() { var error = Error('an error'); return webdriver.promise.rejected(error). thenCatch(function(e) { assertEquals(e, error); throw new StubError; }). thenCatch(assertIsStubError); } function testErrbackMayThrowANewError_startWithTaskResult() { var error = Error('an error'); return flow.execute(function() { throw error; }). thenCatch(function(e) { assertEquals(e, error); throw new StubError; }). thenCatch(assertIsStubError); } function testErrbackMayThrowANewError_uncaught_startWithNormalPromise() { var error = Error('an error'); webdriver.promise.rejected(error). thenCatch(function(e) { assertEquals(e, error); throw new StubError; }); return waitForAbort().then(assertIsStubError); } function testErrbackMayThrowANewError_uncaught_startWithTaskResult() { var error = Error('an error'); flow.execute(function() { throw error; }). thenCatch(function(e) { assertEquals(e, error); throw new StubError; }); return waitForAbort().then(assertIsStubError); } function testThrownPromiseIsHandledSameAsReturningPromise_promiseIsFulfilled() { return webdriver.promise.fulfilled().then(function() { throw webdriver.promise.fulfilled(1234); }).then(function(value) { assertEquals(1234, value); }); } function testTaskThrowsPromise_promiseWasFulfiled() { var toThrow = webdriver.promise.fulfilled(1234); flow.execute(function() { throw toThrow; }).then(fail, function(value) { assertEquals(toThrow, value); return toThrow; }).then(function(value) { assertEquals(1234, value); }); return waitForIdle(); } function testTaskThrowsPromise_promiseWasRejected() { var toThrow = webdriver.promise.rejected(new StubError); toThrow.thenCatch(goog.nullFunction); // For tearDown. flow.execute(function() { throw toThrow; }).then(fail, function(e) { assertEquals(toThrow, e); return e; }).then(fail, assertIsStubError); return waitForIdle(); } function testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult() { var d = webdriver.promise.defer(); flow.execute(function() { webdriver.promise.rejected(new StubError); return d.promise; }).then(fail, assertIsStubError); return waitForIdle().then(function() { return d.promise; }).then(fail, function(e) { assertEquals('CancellationError: StubError', e.toString()); }); } function testFailsParentTaskIfAsyncScheduledTaskFails() { var d = webdriver.promise.defer(); flow.execute(function() { flow.execute(throwStubError); return d.promise; }).then(fail, assertIsStubError); return waitForIdle().then(function() { return d.promise; }).then(fail, function(e) { assertEquals('CancellationError: StubError', e.toString()); }); } function testLongStackTraces_alwaysIncludesTaskStacksInFailures() { if (longStackTracesAreBroken()) { return; } webdriver.promise.LONG_STACK_TRACES = false; flow.execute(function() { flow.execute(function() { flow.execute(throwStubError, 'throw error'); }, 'two'); }, 'three'). then(fail, function(e) { assertIsStubError(e); if (!goog.isString(e.stack)) { return; } var messages = goog.array.filter( webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { return /^From: /.test(line); }); assertArrayEquals([ 'From: Task: throw error', 'From: Task: two', 'From: Task: three' ], messages); }); return waitForIdle(); } function testLongStackTraces_doesNotIncludeCompletedTasks() { if (longStackTracesAreBroken()) { return; } flow.execute(goog.nullFunction, 'succeeds'); flow.execute(throwStubError, 'kaboom').then(fail, function(e) { assertIsStubError(e); if (!goog.isString(e.stack)) { return; } var messages = goog.array.filter( webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { return /^From: /.test(line); }); assertArrayEquals(['From: Task: kaboom'], messages); }); return waitForIdle(); } function testLongStackTraces_doesNotIncludePromiseChainWhenDisabled() { if (longStackTracesAreBroken()) { return; } webdriver.promise.LONG_STACK_TRACES = false; flow.execute(function() { flow.execute(function() { return webdriver.promise.fulfilled(). then(goog.nullFunction). then(goog.nullFunction). then(throwStubError); }, 'eventually fails'); }, 'start'). then(fail, function(e) { assertIsStubError(e); if (!goog.isString(e.stack)) { return; } var messages = goog.array.filter( webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { return /^From: /.test(line); }); assertArrayEquals([ 'From: Task: eventually fails', 'From: Task: start' ], messages); }); return waitForIdle(); } function testLongStackTraces_includesPromiseChainWhenEnabled() { if (longStackTracesAreBroken()) { return; } webdriver.promise.LONG_STACK_TRACES = true; flow.execute(function() { flow.execute(function() { return webdriver.promise.fulfilled(). then(goog.nullFunction). then(goog.nullFunction). then(throwStubError); }, 'eventually fails'); }, 'start'). then(fail, function(e) { assertIsStubError(e); if (!goog.isString(e.stack)) { return; } var messages = goog.array.filter( webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { return /^From: /.test(line); }); assertArrayEquals([ 'From: Promise: then', 'From: Task: eventually fails', 'From: Task: start' ], messages); }); return waitForIdle(); } function testFrameCancelsRemainingTasks_onUnhandledTaskFailure() { var run = false; return flow.execute(function() { flow.execute(throwStubError); flow.execute(function() { run = true; }); }).then(fail, function(e) { assertIsStubError(e); assertFalse(run); }); } function testFrameCancelsRemainingTasks_onUnhandledPromiseRejection() { var run = false; return flow.execute(function() { webdriver.promise.rejected(new StubError); flow.execute(function() { run = true; }); }).then(fail, function(e) { assertIsStubError(e); assertFalse(run); }); } function testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return() { var seen = []; return flow.execute(function() { flow.execute(throwStubError); flow.execute(function() { seen.push(1); }).then(function() { seen.push(2); }, function() { seen.push(3); }); }).then(fail, function(e) { assertIsStubError(e); assertArrayEquals([], seen); }); } function testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn() { var seen = []; return flow.execute(function() { flow.execute(throwStubError); return flow.execute(function() { seen.push(1); }).then(function() { seen.push(2); }, function() { seen.push(3); }); }).then(fail, function(e) { assertIsStubError(e); assertArrayEquals([], seen); }); } function testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted() { var seen = []; return flow.execute(function() { flow.execute(throwStubError); // None of the callbacks on this promise should execute because the // task failure above is never handled, causing the containing task to // abort. webdriver.promise.fulfilled().then(function() { seen.push(1); return flow.execute(function() { seen.push(2); }); }).thenFinally(function() { seen.push(3); }); }).then(fail, function(e) { assertIsStubError(e); assertArrayEquals([], seen); }); } function testTaskIsCancelledAfterWaitTimeout() { var seen = []; return flow.execute(function() { flow.wait(function() { return webdriver.promise.delayed(50); }, 5); return flow.execute(function() { seen.push(1); }).then(function() { seen.push(2); }, function() { seen.push(3); }); }).then(fail, function() { assertArrayEquals([], seen); }); } function testTaskCallbacksGetCancellationErrorIfRegisteredAfterTaskIsCancelled_1() { var task; flow.execute(function() { flow.execute(throwStubError); task = flow.execute(goog.nullFunction); }).then(fail, assertIsStubError); return waitForIdle().then(function() { return task.then(fail, function(e) { assertTrue(e instanceof webdriver.promise.CancellationError); }); }); } function testTaskCallbacksGetCancellationErrorIfRegisteredAfterTaskIsCancelled_2() { var seen = []; var task; flow.execute(function() { flow.execute(throwStubError); task = flow.execute(goog.nullFunction); task.then(() => seen.push(1)) .then(() => seen.push(2)); task.then(() => seen.push(3)) .then(() => seen.push(4)); }).then(fail, assertIsStubError); return waitForIdle().then(function() { return task.then(fail, function(e) { seen.push(5); assertTrue(e instanceof webdriver.promise.CancellationError); }); }).then(() => assertArrayEquals([5], seen)); } function testUnhandledRejectionInParallelTaskQueue() { var seen = []; function schedule(name) { return flow.execute(() => seen.push(name), name); } flow.async(function() { schedule('a.1'); flow.execute(throwStubError, 'a.2 (throws)'); }); var b3; flow.async(function() { schedule('b.1'); schedule('b.2'); b3 = schedule('b.3'); }); var c3; flow.async(function() { schedule('c.1'); schedule('c.2'); c3 = schedule('c.3'); }); function assertWasCancelled(p) { return p.then(fail, function(e) { assertTrue(e instanceof webdriver.promise.CancellationError); }); } return waitForAbort() .then(function() { assertArrayEquals(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen); assertFalse(b3.isPending()); assertFalse(c3.isPending()); }) .then(() => assertWasCancelled(b3)) .then(() => assertWasCancelled(c3)); } function testErrorsInAsyncFunctionsAreReportedAsUnhandledRejection() { flow.removeAllListeners(); // For tearDown. var task; return new Promise(function(fulfill) { flow.once('uncaughtException', fulfill); flow.async(function() { task = flow.execute(function() {}); throw Error('boom'); }); }).then(function(error) { assertTrue(error instanceof webdriver.promise.CancellationError); assertFalse(task.isPending()); return task.thenCatch(function(error) { assertTrue(error instanceof webdriver.promise.CancellationError); }); }); } function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_1() { var p1 = webdriver.promise.fulfilled(); var reason = webdriver.promise.fulfilled('should not see me'); return p1.then(function() { throw reason; }).then(fail, function(e) { assertEquals(reason, e); }); } function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_2() { var p1 = webdriver.promise.fulfilled(); var reason = webdriver.promise.rejected('should not see me'); reason.thenCatch(goog.nullFunction); // For tearDown. return p1.then(function() { throw reason; }).then(fail, function(e) { assertEquals(reason, e); }); } function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_3() { var p1 = webdriver.promise.fulfilled(); var reason = webdriver.promise.defer(); setTimeout(() => reason.fulfill('should not see me'), 100); return p1.then(function() { throw reason.promise; }).then(fail, function(e) { assertEquals(reason.promise, e); }); } function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_4() { var p1 = webdriver.promise.fulfilled(); var reason = {then: function() {}}; // A thenable like object. return p1.then(function() { throw reason; }).then(fail, function(e) { assertEquals(reason, e); }); }