Path: blob/trunk/javascript/selenium-webdriver/test/lib/promise_test.js
2885 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617'use strict'1819const assert = require('node:assert')2021const testutil = require('./testutil')22const promise = require('selenium-webdriver/lib/promise')2324// Aliases for readability.25const NativePromise = Promise26const StubError = testutil.StubError27const assertIsStubError = testutil.assertIsStubError28const callbackPair = testutil.callbackPair29const throwStubError = testutil.throwStubError30const fail = () => assert.fail()3132// Refer to promise_aplus_test for promise compliance with standard behavior.33describe('promise', function () {34let app, uncaughtExceptions3536beforeEach(function setUp() {37if (promise.USE_PROMISE_MANAGER) {38promise.LONG_STACK_TRACES = false39uncaughtExceptions = []4041app = promise.controlFlow()42app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, (e) => uncaughtExceptions.push(e))43}44})4546afterEach(function tearDown() {47if (promise.USE_PROMISE_MANAGER) {48app.reset()49promise.setDefaultFlow(new promise.ControlFlow())50assert.deepStrictEqual([], uncaughtExceptions, 'Did not expect any uncaught exceptions')51promise.LONG_STACK_TRACES = false52}53})5455it('isPromise', () => {56const v = () => {}57const x = new Promise(v, v)58const p = createRejectedPromise('reject')59const q = Promise.resolve('resolved')60const t = {61then() {},62}63const f = () => {}64f.then = () => {}65assert.equal(true, promise.isPromise(x))66assert.equal(true, promise.isPromise(p))67assert.equal(true, promise.isPromise(q))68assert.equal(true, promise.isPromise(t))69assert.equal(true, promise.isPromise(f))70assert.equal(false, promise.isPromise(0))71assert.equal(false, promise.isPromise(false))72assert.equal(false, promise.isPromise(true))73assert.equal(false, promise.isPromise(null))74assert.equal(false, promise.isPromise(undefined))75assert.equal(false, promise.isPromise(''))76assert.equal(false, promise.isPromise('promise'))77assert.equal(false, promise.isPromise(v))78})7980function defer() {81let d = {}82let promise = new Promise((resolve, reject) => {83Object.assign(d, { resolve, reject })84})85d.promise = promise86return d87}8889function createRejectedPromise(reason) {90var p = Promise.reject(reason)91p.catch(function () {}) // Silence unhandled rejection handlers.92return p93}9495describe('fullyResolved', function () {96it('primitives', function () {97function runTest(value) {98return promise.fullyResolved(value).then((resolved) => assert.strictEqual(value, resolved))99}100101return runTest(true)102.then(() => runTest(function () {}))103.then(() => runTest(null))104.then(() => runTest(123))105.then(() => runTest('foo bar'))106.then(() => runTest(undefined))107})108109it('arrayOfPrimitives', function () {110var fn = function () {}111const array = [true, fn, null, 123, '', undefined, 1]112return promise.fullyResolved(array).then(function (resolved) {113assert.strictEqual(array, resolved)114assert.deepStrictEqual([true, fn, null, 123, '', undefined, 1], resolved)115})116})117118it('nestedArrayOfPrimitives', function () {119var fn = function () {}120const array = [true, [fn, null, 123], '', undefined]121return promise.fullyResolved(array).then(function (resolved) {122assert.strictEqual(array, resolved)123assert.deepStrictEqual([true, [fn, null, 123], '', undefined], resolved)124assert.deepStrictEqual([fn, null, 123], resolved[1])125})126})127128it('arrayWithPromisedPrimitive', function () {129return promise.fullyResolved([Promise.resolve(123)]).then(function (resolved) {130assert.deepStrictEqual([123], resolved)131})132})133134it('promiseResolvesToPrimitive', function () {135return promise.fullyResolved(Promise.resolve(123)).then((resolved) => assert.strictEqual(123, resolved))136})137138it('promiseResolvesToArray', function () {139var fn = function () {}140const array = [true, [fn, null, 123], '', undefined]141const aPromise = Promise.resolve(array)142143var result = promise.fullyResolved(aPromise)144return result.then(function (resolved) {145assert.strictEqual(array, resolved)146assert.deepStrictEqual([true, [fn, null, 123], '', undefined], resolved)147assert.deepStrictEqual([fn, null, 123], resolved[1])148})149})150151it('promiseResolvesToArrayWithPromises', function () {152var nestedPromise = Promise.resolve(123)153const aPromise = Promise.resolve([true, nestedPromise])154return promise.fullyResolved(aPromise).then(function (resolved) {155assert.deepStrictEqual([true, 123], resolved)156})157})158159it('rejectsIfArrayPromiseRejects', function () {160var nestedPromise = createRejectedPromise(new StubError())161const aPromise = Promise.resolve([true, nestedPromise])162163return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)164})165166it('rejectsOnFirstArrayRejection', function () {167var e1 = new Error('foo')168var e2 = new Error('bar')169const aPromise = Promise.resolve([createRejectedPromise(e1), createRejectedPromise(e2)])170171return promise.fullyResolved(aPromise).then(assert.fail, function (error) {172assert.strictEqual(e1, error)173})174})175176it('rejectsIfNestedArrayPromiseRejects', function () {177const aPromise = Promise.resolve([Promise.resolve([createRejectedPromise(new StubError())])])178179return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)180})181182it('simpleHash', function () {183var hash = { a: 123 }184return promise.fullyResolved(hash).then(function (resolved) {185assert.strictEqual(hash, resolved)186assert.deepStrictEqual(hash, { a: 123 })187})188})189190it('nestedHash', function () {191var nestedHash = { foo: 'bar' }192var hash = { a: 123, b: nestedHash }193194return promise.fullyResolved(hash).then(function (resolved) {195assert.strictEqual(hash, resolved)196assert.deepStrictEqual({ a: 123, b: { foo: 'bar' } }, resolved)197assert.strictEqual(nestedHash, resolved['b'])198})199})200201it('promiseResolvesToSimpleHash', function () {202var hash = { a: 123 }203const aPromise = Promise.resolve(hash)204205return promise.fullyResolved(aPromise).then((resolved) => assert.strictEqual(hash, resolved))206})207208it('promiseResolvesToNestedHash', function () {209var nestedHash = { foo: 'bar' }210var hash = { a: 123, b: nestedHash }211const aPromise = Promise.resolve(hash)212213return promise.fullyResolved(aPromise).then(function (resolved) {214assert.strictEqual(hash, resolved)215assert.strictEqual(nestedHash, resolved['b'])216assert.deepStrictEqual(hash, { a: 123, b: { foo: 'bar' } })217})218})219220it('promiseResolvesToHashWithPromises', function () {221const aPromise = Promise.resolve({222a: Promise.resolve(123),223})224225return promise.fullyResolved(aPromise).then(function (resolved) {226assert.deepStrictEqual({ a: 123 }, resolved)227})228})229230it('rejectsIfHashPromiseRejects', function () {231const aPromise = Promise.resolve({232a: createRejectedPromise(new StubError()),233})234235return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)236})237238it('rejectsIfNestedHashPromiseRejects', function () {239const aPromise = Promise.resolve({240a: { b: createRejectedPromise(new StubError()) },241})242243return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)244})245246it('instantiatedObject', function () {247function Foo() {248this.bar = 'baz'249}250251var foo = new Foo()252253return promise.fullyResolved(foo).then(function (resolvedFoo) {254assert.strictEqual(foo, resolvedFoo)255assert.ok(resolvedFoo instanceof Foo)256assert.deepStrictEqual(new Foo(), resolvedFoo)257})258})259260it('withEmptyArray', function () {261return promise.fullyResolved([]).then(function (resolved) {262assert.deepStrictEqual([], resolved)263})264})265266it('withEmptyHash', function () {267return promise.fullyResolved({}).then(function (resolved) {268assert.deepStrictEqual({}, resolved)269})270})271272it('arrayWithPromisedHash', function () {273var obj = { foo: 'bar' }274const array = [Promise.resolve(obj)]275276return promise.fullyResolved(array).then(function (resolved) {277assert.deepStrictEqual(resolved, [obj])278})279})280})281282describe('finally', function () {283it('successful callback does not suppress original error', async () => {284let p = Promise.reject(new StubError())285let called = false286287try {288await promise.finally(p, function () {289called = true290})291fail('should have thrown')292} catch (e) {293assertIsStubError(e)294assert.ok(called)295}296})297298it('failing callback suppresses original error', async () => {299let p = Promise.reject(Error('original'))300301try {302await promise.finally(p, throwStubError)303fail('should have thrown')304} catch (e) {305assertIsStubError(e)306}307})308309it('callback throws after fulfilled promise', async () => {310try {311await promise.finally(Promise.resolve(), throwStubError)312fail('should have thrown')313} catch (e) {314assertIsStubError(e)315}316})317318it('callback returns rejected promise', async () => {319try {320await promise.finally(Promise.resolve(), () => Promise.reject(new StubError()))321fail('should have thrown')322} catch (e) {323assertIsStubError(e)324}325})326327it('returned promise resolves with callback result', async () => {328let value = await promise.finally(Promise.resolve(1), () => 2)329assert.strictEqual(value, 2)330})331})332333describe('checkedNodeCall', function () {334it('functionThrows', function () {335return promise.checkedNodeCall(throwStubError).then(assert.fail, assertIsStubError)336})337338it('functionReturnsAnError', function () {339return promise340.checkedNodeCall(function (callback) {341callback(new StubError())342})343.then(assert.fail, assertIsStubError)344})345346it('functionReturnsSuccess', function () {347var success = 'success!'348return promise349.checkedNodeCall(function (callback) {350callback(null, success)351})352.then((value) => assert.strictEqual(success, value))353})354355it('functionReturnsAndThrows', function () {356var error = new Error('boom')357var error2 = new Error('boom again')358return promise359.checkedNodeCall(function (callback) {360callback(error)361throw error2362})363.then(assert.fail, (e) => assert.strictEqual(error, e))364})365366it('functionThrowsAndReturns', function () {367var error = new Error('boom')368var error2 = new Error('boom again')369return promise370.checkedNodeCall(function (callback) {371setTimeout(() => callback(error), 10)372throw error2373})374.then(assert.fail, (e) => assert.strictEqual(error2, e))375})376})377378describe('map', function () {379it('(base case)', function () {380const a = [1, 2, 3]381return promise382.map(a, function (value, index, a2) {383assert.strictEqual(a, a2)384assert.strictEqual('number', typeof index, 'not a number')385return value + 1386})387.then(function (value) {388assert.deepStrictEqual([2, 3, 4], value)389})390})391392it('omitsDeleted', function () {393const a = [0, 1, 2, 3, 4, 5, 6]394delete a[1]395delete a[3]396delete a[4]397delete a[6]398399const expected = [0, NaN, 4, NaN, NaN, 25, NaN]400401return promise402.map(a, function (value) {403return value * value404})405.then(function (value) {406assert.deepStrictEqual(expected, value)407})408})409410it('emptyArray', function () {411return promise412.map([], function (value) {413return value + 1414})415.then(function (value) {416assert.deepStrictEqual([], value)417})418})419420it('inputIsPromise', function () {421var input = defer()422var result = promise.map(input.promise, function (value) {423return value + 1424})425426var pair = callbackPair(function (value) {427assert.deepStrictEqual([2, 3, 4], value)428})429result = result.then(pair.callback, pair.errback)430431setTimeout(function () {432pair.assertNeither()433input.resolve([1, 2, 3])434}, 10)435436return result437})438439it('waitsForFunctionResultToResolve', function () {440var innerResults = [defer(), defer()]441442var result = promise.map([1, 2], function (_value, index) {443return innerResults[index].promise444})445446var pair = callbackPair(function (value) {447assert.deepStrictEqual(['a', 'b'], value)448})449result = result.then(pair.callback, pair.errback)450451return NativePromise.resolve()452.then(function () {453pair.assertNeither()454innerResults[0].resolve('a')455})456.then(function () {457pair.assertNeither()458innerResults[1].resolve('b')459return result460})461.then(pair.assertCallback)462})463464it('rejectsPromiseIfFunctionThrows', function () {465return promise.map([1], throwStubError).then(assert.fail, assertIsStubError)466})467468it('rejectsPromiseIfFunctionReturnsRejectedPromise', function () {469return promise470.map([1], function () {471return createRejectedPromise(new StubError())472})473.then(assert.fail, assertIsStubError)474})475476it('stopsCallingFunctionIfPreviousIterationFailed', function () {477var count = 0478return promise479.map([1, 2, 3, 4], function () {480count++481if (count === 3) {482throw new StubError()483}484})485.then(assert.fail, function (e) {486assertIsStubError(e)487assert.strictEqual(3, count)488})489})490491it('rejectsWithFirstRejectedPromise', function () {492var innerResult = [493Promise.resolve(),494createRejectedPromise(new StubError()),495createRejectedPromise(Error('should be ignored')),496]497var count = 0498return promise499.map([1, 2, 3, 4], function (_value, index) {500count += 1501return innerResult[index]502})503.then(assert.fail, function (e) {504assertIsStubError(e)505assert.strictEqual(2, count)506})507})508509it('preservesOrderWhenMapReturnsPromise', function () {510var deferreds = [defer(), defer(), defer(), defer()]511var result = promise.map(deferreds, function (value) {512return value.promise513})514515var pair = callbackPair(function (value) {516assert.deepStrictEqual([0, 1, 2, 3], value)517})518result = result.then(pair.callback, pair.errback)519520return Promise.resolve()521.then(function () {522pair.assertNeither()523for (let i = deferreds.length; i > 0; i -= 1) {524deferreds[i - 1].resolve(i - 1)525}526return result527})528.then(pair.assertCallback)529})530})531532describe('filter', function () {533it('basicFiltering', function () {534const a = [0, 1, 2, 3]535return promise536.filter(a, function (val, index, a2) {537assert.strictEqual(a, a2)538assert.strictEqual('number', typeof index, 'not a number')539return val > 1540})541.then(function (val) {542assert.deepStrictEqual([2, 3], val)543})544})545546it('omitsDeleted', function () {547const a = [0, 1, 2, 3, 4, 5, 6]548delete a[3]549delete a[4]550551return promise552.filter(a, function (value) {553return value > 1 && value < 6554})555.then(function (val) {556assert.deepStrictEqual([2, 5], val)557})558})559560it('preservesInputs', function () {561const a = [0, 1, 2, 3]562563return promise564.filter(a, function (_value, i, a2) {565assert.strictEqual(a, a2)566// Even if a function modifies the input array, the original value567// should be inserted into the new array.568a2[i] = a2[i] - 1569return a2[i] >= 1570})571.then(function (val) {572assert.deepStrictEqual([2, 3], val)573})574})575576it('inputIsPromise', function () {577const input = defer()578let result = promise.filter(input.promise, function (value) {579return value > 1 && value < 3580})581582const pair = callbackPair(function (value) {583assert.deepStrictEqual([2], value)584})585result = result.then(pair.callback, pair.errback)586return NativePromise.resolve()587.then(function () {588pair.assertNeither()589input.resolve([1, 2, 3])590return result591})592.then(pair.assertCallback)593})594595it('waitsForFunctionResultToResolve', function () {596const innerResults = [defer(), defer()]597598let result = promise.filter([1, 2], function (_value, index) {599return innerResults[index].promise600})601602const pair = callbackPair(function (value) {603assert.deepStrictEqual([2], value)604})605result = result.then(pair.callback, pair.errback)606return NativePromise.resolve()607.then(function () {608pair.assertNeither()609innerResults[0].resolve(false)610})611.then(function () {612pair.assertNeither()613innerResults[1].resolve(true)614return result615})616.then(pair.assertCallback)617})618619it('rejectsPromiseIfFunctionReturnsRejectedPromise', function () {620return promise621.filter([1], function () {622return createRejectedPromise(new StubError())623})624.then(assert.fail, assertIsStubError)625})626627it('stopsCallingFunctionIfPreviousIterationFailed', function () {628var count = 0629return promise630.filter([1, 2, 3, 4], function () {631count++632if (count === 3) {633throw new StubError()634}635})636.then(assert.fail, function (e) {637assertIsStubError(e)638assert.strictEqual(3, count)639})640})641642it('rejectsWithFirstRejectedPromise', function () {643var innerResult = [644Promise.resolve(),645createRejectedPromise(new StubError()),646createRejectedPromise(Error('should be ignored')),647]648649return promise650.filter([1, 2, 3, 4], function (_value, index) {651assert.ok(index < innerResult.length)652return innerResult[index]653})654.then(assert.fail, assertIsStubError)655})656657it('preservesOrderWhenFilterReturnsPromise', function () {658const deferreds = [defer(), defer(), defer(), defer()]659let result = promise.filter([0, 1, 2, 3], function (_value, index) {660return deferreds[index].promise661})662663const pair = callbackPair(function (value) {664assert.deepStrictEqual([1, 2], value)665})666result = result.then(pair.callback, pair.errback)667668return NativePromise.resolve()669.then(function () {670pair.assertNeither()671for (let i = deferreds.length - 1; i >= 0; i -= 1) {672deferreds[i].resolve(i > 0 && i < 3)673}674return result675})676.then(pair.assertCallback)677})678})679})680681682