blob: f4d1222ba0f8a8cbb21cc20463faa4d26cb9b5f2 [file] [log] [blame]
// 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 assert = require('node:assert')
const testutil = require('./testutil')
const promise = require('selenium-webdriver/lib/promise')
// Aliases for readability.
const NativePromise = Promise
const StubError = testutil.StubError
const assertIsStubError = testutil.assertIsStubError
const callbackPair = testutil.callbackPair
const throwStubError = testutil.throwStubError
const fail = () => assert.fail()
// Refer to promise_aplus_test for promise compliance with standard behavior.
describe('promise', function () {
let app, uncaughtExceptions
beforeEach(function setUp() {
if (promise.USE_PROMISE_MANAGER) {
promise.LONG_STACK_TRACES = false
uncaughtExceptions = []
app = promise.controlFlow()
app.on(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, (e) => uncaughtExceptions.push(e))
}
})
afterEach(function tearDown() {
if (promise.USE_PROMISE_MANAGER) {
app.reset()
promise.setDefaultFlow(new promise.ControlFlow())
assert.deepStrictEqual([], uncaughtExceptions, 'Did not expect any uncaught exceptions')
promise.LONG_STACK_TRACES = false
}
})
it('isPromise', () => {
const v = () => {}
const x = new Promise(v, v)
const p = createRejectedPromise('reject')
const q = Promise.resolve('resolved')
const t = {
then() {},
}
const f = () => {}
f.then = () => {}
assert.equal(true, promise.isPromise(x))
assert.equal(true, promise.isPromise(p))
assert.equal(true, promise.isPromise(q))
assert.equal(true, promise.isPromise(t))
assert.equal(true, promise.isPromise(f))
assert.equal(false, promise.isPromise(0))
assert.equal(false, promise.isPromise(false))
assert.equal(false, promise.isPromise(true))
assert.equal(false, promise.isPromise(null))
assert.equal(false, promise.isPromise(undefined))
assert.equal(false, promise.isPromise(''))
assert.equal(false, promise.isPromise('promise'))
assert.equal(false, promise.isPromise(v))
})
function defer() {
let d = {}
let promise = new Promise((resolve, reject) => {
Object.assign(d, { resolve, reject })
})
d.promise = promise
return d
}
function createRejectedPromise(reason) {
var p = Promise.reject(reason)
p.catch(function () {}) // Silence unhandled rejection handlers.
return p
}
describe('fullyResolved', function () {
it('primitives', function () {
function runTest(value) {
return promise.fullyResolved(value).then((resolved) => assert.strictEqual(value, resolved))
}
return runTest(true)
.then(() => runTest(function () {}))
.then(() => runTest(null))
.then(() => runTest(123))
.then(() => runTest('foo bar'))
.then(() => runTest(undefined))
})
it('arrayOfPrimitives', function () {
var fn = function () {}
const array = [true, fn, null, 123, '', undefined, 1]
return promise.fullyResolved(array).then(function (resolved) {
assert.strictEqual(array, resolved)
assert.deepStrictEqual([true, fn, null, 123, '', undefined, 1], resolved)
})
})
it('nestedArrayOfPrimitives', function () {
var fn = function () {}
const array = [true, [fn, null, 123], '', undefined]
return promise.fullyResolved(array).then(function (resolved) {
assert.strictEqual(array, resolved)
assert.deepStrictEqual([true, [fn, null, 123], '', undefined], resolved)
assert.deepStrictEqual([fn, null, 123], resolved[1])
})
})
it('arrayWithPromisedPrimitive', function () {
return promise.fullyResolved([Promise.resolve(123)]).then(function (resolved) {
assert.deepStrictEqual([123], resolved)
})
})
it('promiseResolvesToPrimitive', function () {
return promise.fullyResolved(Promise.resolve(123)).then((resolved) => assert.strictEqual(123, resolved))
})
it('promiseResolvesToArray', function () {
var fn = function () {}
const array = [true, [fn, null, 123], '', undefined]
const aPromise = Promise.resolve(array)
var result = promise.fullyResolved(aPromise)
return result.then(function (resolved) {
assert.strictEqual(array, resolved)
assert.deepStrictEqual([true, [fn, null, 123], '', undefined], resolved)
assert.deepStrictEqual([fn, null, 123], resolved[1])
})
})
it('promiseResolvesToArrayWithPromises', function () {
var nestedPromise = Promise.resolve(123)
const aPromise = Promise.resolve([true, nestedPromise])
return promise.fullyResolved(aPromise).then(function (resolved) {
assert.deepStrictEqual([true, 123], resolved)
})
})
it('rejectsIfArrayPromiseRejects', function () {
var nestedPromise = createRejectedPromise(new StubError())
const aPromise = Promise.resolve([true, nestedPromise])
return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)
})
it('rejectsOnFirstArrayRejection', function () {
var e1 = new Error('foo')
var e2 = new Error('bar')
const aPromise = Promise.resolve([createRejectedPromise(e1), createRejectedPromise(e2)])
return promise.fullyResolved(aPromise).then(assert.fail, function (error) {
assert.strictEqual(e1, error)
})
})
it('rejectsIfNestedArrayPromiseRejects', function () {
const aPromise = Promise.resolve([Promise.resolve([createRejectedPromise(new StubError())])])
return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)
})
it('simpleHash', function () {
var hash = { a: 123 }
return promise.fullyResolved(hash).then(function (resolved) {
assert.strictEqual(hash, resolved)
assert.deepStrictEqual(hash, { a: 123 })
})
})
it('nestedHash', function () {
var nestedHash = { foo: 'bar' }
var hash = { a: 123, b: nestedHash }
return promise.fullyResolved(hash).then(function (resolved) {
assert.strictEqual(hash, resolved)
assert.deepStrictEqual({ a: 123, b: { foo: 'bar' } }, resolved)
assert.strictEqual(nestedHash, resolved['b'])
})
})
it('promiseResolvesToSimpleHash', function () {
var hash = { a: 123 }
const aPromise = Promise.resolve(hash)
return promise.fullyResolved(aPromise).then((resolved) => assert.strictEqual(hash, resolved))
})
it('promiseResolvesToNestedHash', function () {
var nestedHash = { foo: 'bar' }
var hash = { a: 123, b: nestedHash }
const aPromise = Promise.resolve(hash)
return promise.fullyResolved(aPromise).then(function (resolved) {
assert.strictEqual(hash, resolved)
assert.strictEqual(nestedHash, resolved['b'])
assert.deepStrictEqual(hash, { a: 123, b: { foo: 'bar' } })
})
})
it('promiseResolvesToHashWithPromises', function () {
const aPromise = Promise.resolve({
a: Promise.resolve(123),
})
return promise.fullyResolved(aPromise).then(function (resolved) {
assert.deepStrictEqual({ a: 123 }, resolved)
})
})
it('rejectsIfHashPromiseRejects', function () {
const aPromise = Promise.resolve({
a: createRejectedPromise(new StubError()),
})
return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)
})
it('rejectsIfNestedHashPromiseRejects', function () {
const aPromise = Promise.resolve({
a: { b: createRejectedPromise(new StubError()) },
})
return promise.fullyResolved(aPromise).then(assert.fail, assertIsStubError)
})
it('instantiatedObject', function () {
function Foo() {
this.bar = 'baz'
}
var foo = new Foo()
return promise.fullyResolved(foo).then(function (resolvedFoo) {
assert.strictEqual(foo, resolvedFoo)
assert.ok(resolvedFoo instanceof Foo)
assert.deepStrictEqual(new Foo(), resolvedFoo)
})
})
it('withEmptyArray', function () {
return promise.fullyResolved([]).then(function (resolved) {
assert.deepStrictEqual([], resolved)
})
})
it('withEmptyHash', function () {
return promise.fullyResolved({}).then(function (resolved) {
assert.deepStrictEqual({}, resolved)
})
})
it('arrayWithPromisedHash', function () {
var obj = { foo: 'bar' }
const array = [Promise.resolve(obj)]
return promise.fullyResolved(array).then(function (resolved) {
assert.deepStrictEqual(resolved, [obj])
})
})
})
describe('finally', function () {
it('successful callback does not suppress original error', async () => {
let p = Promise.reject(new StubError())
let called = false
try {
await promise.finally(p, function () {
called = true
})
fail('should have thrown')
} catch (e) {
assertIsStubError(e)
assert.ok(called)
}
})
it('failing callback suppresses original error', async () => {
let p = Promise.reject(Error('original'))
try {
await promise.finally(p, throwStubError)
fail('should have thrown')
} catch (e) {
assertIsStubError(e)
}
})
it('callback throws after fulfilled promise', async () => {
try {
await promise.finally(Promise.resolve(), throwStubError)
fail('should have thrown')
} catch (e) {
assertIsStubError(e)
}
})
it('callback returns rejected promise', async () => {
try {
await promise.finally(Promise.resolve(), () => Promise.reject(new StubError()))
fail('should have thrown')
} catch (e) {
assertIsStubError(e)
}
})
it('returned promise resolves with callback result', async () => {
let value = await promise.finally(Promise.resolve(1), () => 2)
assert.strictEqual(value, 2)
})
})
describe('checkedNodeCall', function () {
it('functionThrows', function () {
return promise.checkedNodeCall(throwStubError).then(assert.fail, assertIsStubError)
})
it('functionReturnsAnError', function () {
return promise
.checkedNodeCall(function (callback) {
callback(new StubError())
})
.then(assert.fail, assertIsStubError)
})
it('functionReturnsSuccess', function () {
var success = 'success!'
return promise
.checkedNodeCall(function (callback) {
callback(null, success)
})
.then((value) => assert.strictEqual(success, value))
})
it('functionReturnsAndThrows', function () {
var error = new Error('boom')
var error2 = new Error('boom again')
return promise
.checkedNodeCall(function (callback) {
callback(error)
throw error2
})
.then(assert.fail, (e) => assert.strictEqual(error, e))
})
it('functionThrowsAndReturns', function () {
var error = new Error('boom')
var error2 = new Error('boom again')
return promise
.checkedNodeCall(function (callback) {
setTimeout(() => callback(error), 10)
throw error2
})
.then(assert.fail, (e) => assert.strictEqual(error2, e))
})
})
describe('map', function () {
it('(base case)', function () {
const a = [1, 2, 3]
return promise
.map(a, function (value, index, a2) {
assert.strictEqual(a, a2)
assert.strictEqual('number', typeof index, 'not a number')
return value + 1
})
.then(function (value) {
assert.deepStrictEqual([2, 3, 4], value)
})
})
it('omitsDeleted', function () {
const a = [0, 1, 2, 3, 4, 5, 6]
delete a[1]
delete a[3]
delete a[4]
delete a[6]
const expected = [0, NaN, 4, NaN, NaN, 25, NaN]
return promise
.map(a, function (value) {
return value * value
})
.then(function (value) {
assert.deepStrictEqual(expected, value)
})
})
it('emptyArray', function () {
return promise
.map([], function (value) {
return value + 1
})
.then(function (value) {
assert.deepStrictEqual([], value)
})
})
it('inputIsPromise', function () {
var input = defer()
var result = promise.map(input.promise, function (value) {
return value + 1
})
var pair = callbackPair(function (value) {
assert.deepStrictEqual([2, 3, 4], value)
})
result = result.then(pair.callback, pair.errback)
setTimeout(function () {
pair.assertNeither()
input.resolve([1, 2, 3])
}, 10)
return result
})
it('waitsForFunctionResultToResolve', function () {
var innerResults = [defer(), defer()]
var result = promise.map([1, 2], function (_value, index) {
return innerResults[index].promise
})
var pair = callbackPair(function (value) {
assert.deepStrictEqual(['a', 'b'], value)
})
result = result.then(pair.callback, pair.errback)
return NativePromise.resolve()
.then(function () {
pair.assertNeither()
innerResults[0].resolve('a')
})
.then(function () {
pair.assertNeither()
innerResults[1].resolve('b')
return result
})
.then(pair.assertCallback)
})
it('rejectsPromiseIfFunctionThrows', function () {
return promise.map([1], throwStubError).then(assert.fail, assertIsStubError)
})
it('rejectsPromiseIfFunctionReturnsRejectedPromise', function () {
return promise
.map([1], function () {
return createRejectedPromise(new StubError())
})
.then(assert.fail, assertIsStubError)
})
it('stopsCallingFunctionIfPreviousIterationFailed', function () {
var count = 0
return promise
.map([1, 2, 3, 4], function () {
count++
if (count === 3) {
throw new StubError()
}
})
.then(assert.fail, function (e) {
assertIsStubError(e)
assert.strictEqual(3, count)
})
})
it('rejectsWithFirstRejectedPromise', function () {
var innerResult = [
Promise.resolve(),
createRejectedPromise(new StubError()),
createRejectedPromise(Error('should be ignored')),
]
var count = 0
return promise
.map([1, 2, 3, 4], function (_value, index) {
count += 1
return innerResult[index]
})
.then(assert.fail, function (e) {
assertIsStubError(e)
assert.strictEqual(2, count)
})
})
it('preservesOrderWhenMapReturnsPromise', function () {
var deferreds = [defer(), defer(), defer(), defer()]
var result = promise.map(deferreds, function (value) {
return value.promise
})
var pair = callbackPair(function (value) {
assert.deepStrictEqual([0, 1, 2, 3], value)
})
result = result.then(pair.callback, pair.errback)
return Promise.resolve()
.then(function () {
pair.assertNeither()
for (let i = deferreds.length; i > 0; i -= 1) {
deferreds[i - 1].resolve(i - 1)
}
return result
})
.then(pair.assertCallback)
})
})
describe('filter', function () {
it('basicFiltering', function () {
const a = [0, 1, 2, 3]
return promise
.filter(a, function (val, index, a2) {
assert.strictEqual(a, a2)
assert.strictEqual('number', typeof index, 'not a number')
return val > 1
})
.then(function (val) {
assert.deepStrictEqual([2, 3], val)
})
})
it('omitsDeleted', function () {
const a = [0, 1, 2, 3, 4, 5, 6]
delete a[3]
delete a[4]
return promise
.filter(a, function (value) {
return value > 1 && value < 6
})
.then(function (val) {
assert.deepStrictEqual([2, 5], val)
})
})
it('preservesInputs', function () {
const a = [0, 1, 2, 3]
return promise
.filter(a, function (_value, i, a2) {
assert.strictEqual(a, a2)
// Even if a function modifies the input array, the original value
// should be inserted into the new array.
a2[i] = a2[i] - 1
return a2[i] >= 1
})
.then(function (val) {
assert.deepStrictEqual([2, 3], val)
})
})
it('inputIsPromise', function () {
const input = defer()
let result = promise.filter(input.promise, function (value) {
return value > 1 && value < 3
})
const pair = callbackPair(function (value) {
assert.deepStrictEqual([2], value)
})
result = result.then(pair.callback, pair.errback)
return NativePromise.resolve()
.then(function () {
pair.assertNeither()
input.resolve([1, 2, 3])
return result
})
.then(pair.assertCallback)
})
it('waitsForFunctionResultToResolve', function () {
const innerResults = [defer(), defer()]
let result = promise.filter([1, 2], function (_value, index) {
return innerResults[index].promise
})
const pair = callbackPair(function (value) {
assert.deepStrictEqual([2], value)
})
result = result.then(pair.callback, pair.errback)
return NativePromise.resolve()
.then(function () {
pair.assertNeither()
innerResults[0].resolve(false)
})
.then(function () {
pair.assertNeither()
innerResults[1].resolve(true)
return result
})
.then(pair.assertCallback)
})
it('rejectsPromiseIfFunctionReturnsRejectedPromise', function () {
return promise
.filter([1], function () {
return createRejectedPromise(new StubError())
})
.then(assert.fail, assertIsStubError)
})
it('stopsCallingFunctionIfPreviousIterationFailed', function () {
var count = 0
return promise
.filter([1, 2, 3, 4], function () {
count++
if (count === 3) {
throw new StubError()
}
})
.then(assert.fail, function (e) {
assertIsStubError(e)
assert.strictEqual(3, count)
})
})
it('rejectsWithFirstRejectedPromise', function () {
var innerResult = [
Promise.resolve(),
createRejectedPromise(new StubError()),
createRejectedPromise(Error('should be ignored')),
]
return promise
.filter([1, 2, 3, 4], function (_value, index) {
assert.ok(index < innerResult.length)
return innerResult[index]
})
.then(assert.fail, assertIsStubError)
})
it('preservesOrderWhenFilterReturnsPromise', function () {
const deferreds = [defer(), defer(), defer(), defer()]
let result = promise.filter([0, 1, 2, 3], function (_value, index) {
return deferreds[index].promise
})
const pair = callbackPair(function (value) {
assert.deepStrictEqual([1, 2], value)
})
result = result.then(pair.callback, pair.errback)
return NativePromise.resolve()
.then(function () {
pair.assertNeither()
for (let i = deferreds.length - 1; i >= 0; i -= 1) {
deferreds[i].resolve(i > 0 && i < 3)
}
return result
})
.then(pair.assertCallback)
})
})
})