// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --allow-natives-syntax --ignore-unhandled-promises load('test/mjsunit/test-async.js'); assertThrows(() => Promise.prototype.finally.call(5), TypeError); testAsync(assert => { assert.plan(1); Promise.resolve(3).finally().then(x => { assert.equals(3, x); }, assert.unreachable); }, "resolve/finally/then"); testAsync(assert => { assert.plan(1); Promise.reject(3).finally().then(assert.unreachable, x => { assert.equals(3, x); }); }, "reject/finally/then"); testAsync(assert => { assert.plan(1); Promise.resolve(3).finally(2).then(x => { assert.equals(3, x); }, assert.unreachable); }, "resolve/finally-return-notcallable/then"); testAsync(assert => { assert.plan(1); Promise.reject(3).finally(2).then(assert.unreachable, e => { assert.equals(3, e); }); }, "reject/finally-return-notcallable/then"); testAsync(assert => { assert.plan(1); Promise.reject(3).finally().catch(reason => { assert.equals(3, reason); }); }, "reject/finally/catch"); testAsync(assert => { assert.plan(1); Promise.reject(3).finally().then(assert.unreachable).catch(reason => { assert.equals(3, reason); }); }, "reject/finally/then/catch"); testAsync(assert => { assert.plan(2); Promise.resolve(3) .then(x => { assert.equals(3, x); return x; }) .finally() .then(x => { assert.equals(3, x); }, assert.unreachable); }, "resolve/then/finally/then"); testAsync(assert => { assert.plan(2); Promise.reject(3) .catch(x => { assert.equals(3, x); return x; }) .finally() .then(x => { assert.equals(3, x); }, assert.unreachable); }, "reject/catch/finally/then"); testAsync(assert => { assert.plan(2); Promise.resolve(3) .finally(function onFinally() { print("in finally"); assert.equals(0, arguments.length); throw 1; }) .then(assert.unreachable, function onRejected(reason) { assert.equals(1, reason); }); }, "resolve/finally-throw/then"); testAsync(assert => { assert.plan(2); Promise.reject(3) .finally(function onFinally() { assert.equals(0, arguments.length); throw 1; }) .then(assert.unreachable, function onRejected(reason) { assert.equals(1, reason); }); }, "reject/finally-throw/then"); testAsync(assert => { assert.plan(2); Promise.resolve(3) .finally(function onFinally() { assert.equals(0, arguments.length); return 4; }) .then(x => { assert.equals(x, 3); }, assert.unreachable); }, "resolve/finally-return/then"); // reject/finally-return/then testAsync(assert => { assert.plan(2); Promise.reject(3) .finally(function onFinally() { assert.equals(0, arguments.length); return 4; }) .then(assert.unreachable, x => { assert.equals(x, 3); }); }); // reject/catch-throw/finally-throw/then testAsync(assert => { assert.plan(3); Promise.reject(3) .catch(e => { assert.equals(3, e); throw e; }) .finally(function onFinally() { assert.equals(0, arguments.length); throw 4; }) .then(assert.unreachable, function onRejected(e) { assert.equals(4, e); }); }); testAsync(assert => { assert.plan(3); Promise.resolve(3) .then(e => { assert.equals(3, e); throw e; }) .finally(function onFinally() { assert.equals(0, arguments.length); throw 4; }) .then(assert.unreachable, function onRejected(e) { assert.equals(4, e); }); }, "resolve/then-throw/finally-throw/then"); testAsync(assert => { assert.plan(2); Promise.resolve(3) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.reject(4); }) .then(assert.unreachable, e => { assert.equals(4, e); }); }, "resolve/finally-return-rejected-promise/then"); testAsync(assert => { assert.plan(2); Promise.reject(3) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.reject(4); }) .then(assert.unreachable, e => { assert.equals(4, e); }); }, "reject/finally-return-rejected-promise/then"); testAsync(assert => { assert.plan(2); Promise.resolve(3) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.resolve(4); }) .then(x => { assert.equals(3, x); }, assert.unreachable); }, "resolve/finally-return-resolved-promise/then"); testAsync(assert => { assert.plan(2); Promise.reject(3) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.resolve(4); }) .then(assert.unreachable, e => { assert.equals(3, e); }); }, "reject/finally-return-resolved-promise/then"); testAsync(assert => { assert.plan(2); Promise.reject(3) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.resolve(4); }) .then(assert.unreachable, e => { assert.equals(3, e); }); }, "reject/finally-return-resolved-promise/then"); testAsync(assert => { assert.plan(2); var thenable = { then: function(onResolve, onReject) { onResolve(5); } }; Promise.resolve(5) .finally(function onFinally() { assert.equals(0, arguments.length); return thenable; }) .then(x => { assert.equals(5, x); }, assert.unreachable); }, "resolve/finally-thenable-resolve/then"); testAsync(assert => { assert.plan(2); var thenable = { then: function(onResolve, onReject) { onResolve(1); } }; Promise.reject(5) .finally(function onFinally() { assert.equals(0, arguments.length); return thenable; }) .then(assert.unreachable, e => { assert.equals(5, e); }); }, "reject/finally-thenable-resolve/then"); testAsync(assert => { assert.plan(2); var thenable = { then: function(onResolve, onReject) { onReject(1); } }; Promise.reject(5) .finally(function onFinally() { assert.equals(0, arguments.length); return thenable; }) .then(assert.unreachable, e => { assert.equals(1, e); }); }, "reject/finally-thenable-reject/then"); testAsync(assert => { assert.plan(2); var thenable = { then: function(onResolve, onReject) { onReject(1); } }; Promise.resolve(5) .finally(function onFinally() { assert.equals(0, arguments.length); return thenable; }) .then(assert.unreachable, e => { assert.equals(1, e); }); }, "resolve/finally-thenable-reject/then"); testAsync(assert => { assert.plan(3); Promise.resolve(5) .finally(function onFinally() { assert.equals(0, arguments.length); }) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(x => { assert.equals(5, x); }, assert.unreachable); }, "resolve/finally/finally/then"); testAsync(assert => { assert.plan(3); Promise.resolve(5) .finally(function onFinally() { assert.equals(0, arguments.length); throw 1; }) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(assert.unreachable, e => { assert.equals(1, e); }); }, "resolve/finally-throw/finally/then"); testAsync(assert => { assert.plan(3); Promise.resolve(5) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.reject(1); }) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(assert.unreachable, e => { assert.equals(1, e); }); }, "resolve/finally-return-rejected-promise/finally/then"); testAsync(assert => { assert.plan(3); Promise.reject(5) .finally(function onFinally() { assert.equals(0, arguments.length); }) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(assert.unreachable, e => { assert.equals(5, e); }); }, "reject/finally/finally/then"); testAsync(assert => { assert.plan(3); Promise.reject(5) .finally(function onFinally() { assert.equals(0, arguments.length); throw 1; }) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(assert.unreachable, e => { assert.equals(1, e); }); }, "reject/finally-throw/finally/then"); testAsync(assert => { assert.plan(3); Promise.reject(5) .finally(function onFinally() { assert.equals(0, arguments.length); return Promise.reject(1); }) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(assert.unreachable, e => { assert.equals(1, e); }); }, "reject/finally-return-rejected-promise/finally/then"); testAsync(assert => { assert.plan(2); var resolve, reject; var deferred = new Promise((x, y) => { resolve = x; reject = y; }); Promise.resolve(1) .finally(function onFinally() { assert.equals(0, arguments.length); return deferred; }) .then(x => { assert.equals(1, x); }, assert.unreachable); resolve(5); }, "resolve/finally-deferred-resolve/then"); // testAsync(assert => { assert.plan(2); var resolve, reject; var deferred = new Promise((x, y) => { resolve = x; reject = y; }); Promise.resolve(1) .finally(function onFinally() { assert.equals(0, arguments.length); return deferred; }) .then(assert.unreachable, e => { assert.equals(5, e); }); reject(5); }, "resolve/finally-deferred-reject/then"); testAsync(assert => { assert.plan(2); var resolve, reject; var deferred = new Promise((x, y) => { resolve = x; reject = y; }); Promise.all([deferred]) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(([x]) => { assert.equals(1, x); }, assert.unreachable); resolve(1); }, "all/finally/then"); testAsync(assert => { assert.plan(2); var resolve, reject; var d1 = new Promise((x, y) => { resolve = x; reject = y; }); var d2 = new Promise((x, y) => { resolve = x; reject = y; }); Promise.race([d1, d2]) .finally(function onFinally() { assert.equals(0, arguments.length); }) .then(x => { assert.equals(1, x); }, assert.unreachable); resolve(1); }, "race/finally/then"); testAsync(assert => { assert.plan(2); class MyPromise extends Promise { then(onFulfilled, onRejected) { assert.equals(5, onFulfilled); assert.equals(5, onRejected); return super.then(onFulfilled, onRejected); } } MyPromise.resolve(3).finally(5); }, "resolve/finally-customthen/then"); testAsync(assert => { assert.plan(2); class MyPromise extends Promise { then(onFulfilled, onRejected) { assert.equals(5, onFulfilled); assert.equals(5, onRejected); return super.then(onFulfilled, onRejected); } } MyPromise.reject(3).finally(5); }, "reject/finally-customthen/then"); var descriptor = Object.getOwnPropertyDescriptor(Promise.prototype, "finally"); assertTrue(descriptor.writable); assertTrue(descriptor.configurable); assertFalse(descriptor.enumerable); assertEquals("finally", Promise.prototype.finally.name); assertEquals(1, Promise.prototype.finally.length); var count = 0; class FooPromise extends Promise { constructor(resolve, reject) { count++; return super(resolve, reject); } } testAsync(assert => { assert.plan(1); count = 0; new FooPromise(r => r()).finally(() => {}).then(() => { assert.equals(6, count); }); }, "finally/speciesconstructor"); testAsync(assert => { assert.plan(1); count = 0; FooPromise.resolve().finally(() => {}).then(() => { assert.equals(6, count); }) }, "resolve/finally/speciesconstructor"); testAsync(assert => { assert.plan(1); count = 0; FooPromise.reject().finally(() => {}).catch(() => { assert.equals(6, count); }) }, "reject/finally/speciesconstructor"); testAsync(assert => { assert.plan(2); class MyPromise extends Promise { static get [Symbol.species]() { return Promise; } } var p = Promise .resolve() .finally(() => MyPromise.resolve()); assert.equals(true, p instanceof Promise); assert.equals(false, p instanceof MyPromise); }, "finally/Symbol.Species"); testAsync(assert => { assert.plan(3); let resolve; let value = 0; let p = new Promise(r => { resolve = r }); Promise.resolve() .finally(() => { return p; }) .then(() => { value = 1; }); // This makes sure we take the fast path in PromiseResolve that just // returns the promise it receives as value. If we had to create // another wrapper promise, that would cause an additional tick in // the microtask queue. Promise.resolve() // onFinally has run. .then(() => { resolve(); }) // thenFinally has run. .then(() => assert.equals(0, value)) // promise returned by .finally has been resolved. .then(() => assert.equals(0, value)) // onFulfilled callback of .then() has run. .then(() => assert.equals(1, value)); }, "PromiseResolve-ordering"); (function testIsObject() { var called = false; var p = new Proxy(Promise.resolve(), {}); var oldThen = Promise.prototype.then; Promise.prototype.then = () => called = true; Promise.prototype.finally.call(p); assertTrue(called); Promise.prototype.then = oldThen; })();