From a37ce90524fc861b5f2e4592dd3e1c0f048d1522 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 25 Jul 2017 23:05:04 -0700 Subject: [PATCH 1/3] gitignore node_modules --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f00610b21d..2c616c66da 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ console/TestCases github-deploy-key github-deploy-key.pub +node_modules From 4433c428ff6ef1e6e6ce3e54cf4cb03a92afdd68 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 25 Jul 2017 23:05:46 -0700 Subject: [PATCH 2/3] Only apps should have lockfiles --- .gitignore | 5 +++++ .npmrc | 1 + 2 files changed, 6 insertions(+) create mode 100644 .npmrc diff --git a/.gitignore b/.gitignore index 2c616c66da..b93ec0eef4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,8 @@ console/TestCases github-deploy-key github-deploy-key.pub node_modules + +# Only apps should have lockfiles +package-lock.json +yarn.lock +npm-shrinkwrap.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..43c97e719a --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false From 3528601c417885edddf5c4b73b497b63bf6bdad5 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 25 Jul 2017 23:06:12 -0700 Subject: [PATCH 3/3] Promise.prototype.finally: add tests --- features.txt | 4 ++ .../finally/invokes-then-with-function.js | 53 +++++++++++++++++++ .../finally/invokes-then-with-non-function.js | 49 +++++++++++++++++ .../prototype/finally/is-a-function.js | 20 +++++++ .../Promise/prototype/finally/is-a-method.js | 16 ++++++ .../Promise/prototype/finally/length.js | 28 ++++++++++ .../Promise/prototype/finally/name.js | 28 ++++++++++ .../Promise/prototype/finally/prop-desc.js | 19 +++++++ .../finally/rejected-observable-then-calls.js | 46 ++++++++++++++++ .../finally/rejection-reason-no-fulfill.js | 23 ++++++++ .../rejection-reason-override-with-throw.js | 23 ++++++++ .../finally/resolution-value-no-override.js | 20 +++++++ .../finally/resolved-observable-then-calls.js | 46 ++++++++++++++++ .../finally/this-value-non-object.js | 17 ++++++ .../finally/this-value-then-not-callable.js | 51 ++++++++++++++++++ .../finally/this-value-then-poisoned.js | 24 +++++++++ .../finally/this-value-then-throws.js | 23 ++++++++ 17 files changed, 490 insertions(+) create mode 100644 test/built-ins/Promise/prototype/finally/invokes-then-with-function.js create mode 100644 test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js create mode 100644 test/built-ins/Promise/prototype/finally/is-a-function.js create mode 100644 test/built-ins/Promise/prototype/finally/is-a-method.js create mode 100644 test/built-ins/Promise/prototype/finally/length.js create mode 100644 test/built-ins/Promise/prototype/finally/name.js create mode 100644 test/built-ins/Promise/prototype/finally/prop-desc.js create mode 100644 test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js create mode 100644 test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js create mode 100644 test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js create mode 100644 test/built-ins/Promise/prototype/finally/resolution-value-no-override.js create mode 100644 test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js create mode 100644 test/built-ins/Promise/prototype/finally/this-value-non-object.js create mode 100644 test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js create mode 100644 test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js create mode 100644 test/built-ins/Promise/prototype/finally/this-value-then-throws.js diff --git a/features.txt b/features.txt index 41323de4ed..66ad482cfb 100644 --- a/features.txt +++ b/features.txt @@ -7,6 +7,10 @@ # # https://github.com/tc39/process-document +# Promise.prototype.finally +# https://github.com/tc39/proposal-promise-finally +Promise.prototype.finally + # Async Iteration and Generators # https://github.com/tc39/proposal-async-iteration async-iteration diff --git a/test/built-ins/Promise/prototype/finally/invokes-then-with-function.js b/test/built-ins/Promise/prototype/finally/invokes-then-with-function.js new file mode 100644 index 0000000000..d7b808611c --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/invokes-then-with-function.js @@ -0,0 +1,53 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: Promise.prototype.finally invokes `then` method +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +var target = new Promise(function () {}); +var returnValue = {}; +var callCount = 0; +var thisValue = null; +var argCount = null; +var firstArg = null; +var secondArg = null; + +target.then = function(a, b) { + callCount += 1; + + thisValue = this; + argCount = arguments.length; + firstArg = a; + secondArg = b; + + return returnValue; +}; + +var originalFinallyHandler = function () {}; + +var result = Promise.prototype.finally.call(target, originalFinallyHandler, 2, 3); + +assert.sameValue(callCount, 1, 'Invokes `then` method exactly once'); +assert.sameValue( + thisValue, + target, + 'Invokes `then` method with the instance as the `this` value' +); +assert.sameValue(argCount, 2, 'Invokes `then` method with exactly two single arguments'); +assert.sameValue( + typeof firstArg, + 'function', + 'Invokes `then` method with a function as the first argument' +); +assert.notSameValue(firstArg, originalFinallyHandler, 'Invokes `then` method with a different fulfillment handler'); +assert.sameValue( + typeof secondArg, + 'function', + 'Invokes `then` method with a function as the second argument' +); +assert.notSameValue(secondArg, originalFinallyHandler, 'Invokes `then` method with a different fulfillment handler'); + +assert.sameValue(result, returnValue, 'Returns the result of the invocation of `then`'); diff --git a/test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js b/test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js new file mode 100644 index 0000000000..1193119fbe --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js @@ -0,0 +1,49 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: Promise.prototype.finally invokes `then` method +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +var target = new Promise(function () {}); +var returnValue = {}; +var callCount = 0; +var thisValue = null; +var argCount = null; +var firstArg = null; +var secondArg = null; +var result = null; + +target.then = function(a, b) { + callCount += 1; + + thisValue = this; + argCount = arguments.length; + firstArg = a; + secondArg = b; + + return returnValue; +}; + +result = Promise.prototype.finally.call(target, 1, 2, 3); + +assert.sameValue(callCount, 1, 'Invokes `then` method exactly once'); +assert.sameValue( + thisValue, + target, + 'Invokes `then` method with the instance as the `this` value' +); +assert.sameValue(argCount, 2, 'Invokes `then` method with exactly two single arguments'); +assert.sameValue( + firstArg, + 1, + 'Invokes `then` method with the provided non-callable first argument' +); +assert.sameValue( + secondArg, + 1, + 'Invokes `then` method with the provided non-callable first argument' +); +assert.sameValue(result, returnValue, 'Returns the result of the invocation of `then`'); diff --git a/test/built-ins/Promise/prototype/finally/is-a-function.js b/test/built-ins/Promise/prototype/finally/is-a-function.js new file mode 100644 index 0000000000..5630fc3288 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/is-a-function.js @@ -0,0 +1,20 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: Promise.prototype.finally is a function +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +assert.sameValue( + Promise.prototype.finally instanceof Function, + true, + 'Expected Promise.prototype.finally to be instanceof Function' +); + +assert.sameValue( + typeof Promise.prototype.finally, + 'function', + 'Expected Promise.prototype.finally to be a function' +); diff --git a/test/built-ins/Promise/prototype/finally/is-a-method.js b/test/built-ins/Promise/prototype/finally/is-a-method.js new file mode 100644 index 0000000000..bc35f4242a --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/is-a-method.js @@ -0,0 +1,16 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: finally is a method on a Promise +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +var p = Promise.resolve(3); + +assert.sameValue( + p.finally, + Promise.prototype.finally, + 'Expected the `finally` method on a Promise to be `Promise.prototype.finally`' +); diff --git a/test/built-ins/Promise/prototype/finally/length.js b/test/built-ins/Promise/prototype/finally/length.js new file mode 100644 index 0000000000..a6010957d0 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/length.js @@ -0,0 +1,28 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: Promise.prototype.finally `length` property +esid: sec-promise.prototype.finally +info: > + ES6 Section 17: + Every built-in Function object, including constructors, has a length + property whose value is an integer. Unless otherwise specified, this value + is equal to the largest number of named arguments shown in the subclause + headings for the function description, including optional parameters. + + [...] + + Unless otherwise specified, the length property of a built-in Function + object has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Promise.prototype.finally] +---*/ + +verifyProperty(Promise.prototype.finally, "length", { + value: 1, + enumerable: false, + configurable: true, + writable: false, +}); diff --git a/test/built-ins/Promise/prototype/finally/name.js b/test/built-ins/Promise/prototype/finally/name.js new file mode 100644 index 0000000000..f2040e033e --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/name.js @@ -0,0 +1,28 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: Promise.prototype.finally `name` property +esid: sec-promise.prototype.finally +info: > + ES Section 17: + + Every built-in Function object, including constructors, that is not + identified as an anonymous function has a name property whose value is a + String. Unless otherwise specified, this value is the name that is given to + the function in this specification. + + [...] + + Unless otherwise specified, the name property of a built-in Function + object, if it exists, has the attributes { [[Writable]]: false, + [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Promise.prototype.finally] +---*/ + +assert.sameValue(Promise.prototype.finally.name, 'finally'); + +verifyNotEnumerable(Promise.prototype.finally, 'name'); +verifyNotWritable(Promise.prototype.finally, 'name'); +verifyConfigurable(Promise.prototype.finally, 'name'); diff --git a/test/built-ins/Promise/prototype/finally/prop-desc.js b/test/built-ins/Promise/prototype/finally/prop-desc.js new file mode 100644 index 0000000000..21c5b81595 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/prop-desc.js @@ -0,0 +1,19 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: Promise.prototype.finally property descriptor +esid: sec-promise.prototype.finally +info: > + Every other data property described in clauses 18 through 26 and in Annex + B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: false, + [[Configurable]]: true } unless otherwise specified. +includes: [propertyHelper.js] +features: [Promise.prototype.finally] +---*/ + +assert.sameValue(typeof Promise.prototype.finally, 'function'); + +verifyNotEnumerable(Promise.prototype, 'finally'); +verifyWritable(Promise.prototype, 'finally'); +verifyConfigurable(Promise.prototype, 'finally'); diff --git a/test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js b/test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js new file mode 100644 index 0000000000..c8ed29340c --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js @@ -0,0 +1,46 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: finally observably calls .then +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +flags: [async] +---*/ + +var initialThenCount = 0; +var noReason = {}; +var no = Promise.reject(noReason); +no.then = function () { + initialThenCount += 1; + return Promise.prototype.then.apply(this, arguments); +}; + +var onFinallyThenCount = 0; +var yesValue = {}; +var yes = Promise.resolve(yesValue); +yes.then = function () { + onFinallyThenCount += 1; + return Promise.prototype.then.apply(this, arguments); +}; + +var finallyCalled = false; +var catchCalled = false; + +no.catch(function (e) { + assert.sameValue(e, noReason); + throw e; +}).finally(function () { + finallyCalled = true; + return yes; +}).catch(function (e) { + catchCalled = true; + assert.sameValue(e, noReason); +}).then(function () { + assert.sameValue(finallyCalled, true, 'initial finally was called'); + assert.sameValue(initialThenCount, 1, 'initial finally invokes .then once'); + + assert.sameValue(catchCalled, true, 'catch was called'); + assert.sameValue(onFinallyThenCount, 1, 'onFinally return promise has .then invoked once'); + $DONE(); +}).catch($ERROR); diff --git a/test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js b/test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js new file mode 100644 index 0000000000..cb31bcd402 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js @@ -0,0 +1,23 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: finally on a rejected promise can not convert to a fulfillment +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +flags: [async] +---*/ + +var original = {}; +var replacement = {}; + +var p = Promise.reject(original); + +p.finally(function () { + assert.sameValue(arguments.length, 0, 'onFinally receives zero args'); + return replacement; +}).then(function () { + $ERROR('promise is rejected pre-finally; onFulfill should not be called'); +}).catch(function (reason) { + assert.sameValue(reason, original, 'onFinally can not override the rejection value by returning'); +}).then($DONE).catch($ERROR); diff --git a/test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js b/test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js new file mode 100644 index 0000000000..013854d104 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js @@ -0,0 +1,23 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: finally on a rejected promise can override the rejection reason +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +flags: [async] +---*/ + +var original = {}; +var thrown = {}; + +var p = Promise.reject(original); + +p.finally(function () { + assert.sameValue(arguments.length, 0, 'onFinally receives zero args'); + throw thrown; +}).then(function () { + $ERROR('promise is rejected; onFulfill should not be called'); +}).catch(function (reason) { + assert.sameValue(reason, thrown, 'onFinally can override the rejection reason by throwing'); +}).then($DONE).catch($ERROR); diff --git a/test/built-ins/Promise/prototype/finally/resolution-value-no-override.js b/test/built-ins/Promise/prototype/finally/resolution-value-no-override.js new file mode 100644 index 0000000000..9535063405 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/resolution-value-no-override.js @@ -0,0 +1,20 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: finally on a fulfilled promise can not override the resolution value +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +flags: [async] +---*/ + +var obj = {}; + +var p = Promise.resolve(obj); + +p.finally(function () { + assert.sameValue(arguments.length, 0, 'onFinally receives zero args'); + return {}; +}).then(function (x) { + assert.sameValue(x, obj, 'onFinally can not override the resolution value'); +}).then($DONE).catch($ERROR); diff --git a/test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js b/test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js new file mode 100644 index 0000000000..5c92a70359 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js @@ -0,0 +1,46 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: finally observably calls .then +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +flags: [async] +---*/ + +var initialThenCount = 0; +var yesValue = {}; +var yes = Promise.resolve(yesValue); +yes.then = function () { + initialThenCount += 1; + return Promise.prototype.then.apply(this, arguments); +}; + +var onFinallyThenCount = 0; +var noReason = {}; +var no = Promise.reject(noReason); +no.then = function () { + onFinallyThenCount += 1; + return Promise.prototype.then.apply(this, arguments); +}; + +var finallyCalled = false; +var catchCalled = false; + +yes.then(function (x) { + assert.sameValue(x, yesValue); + return x; +}).finally(function () { + finallyCalled = true; + return no; +}).catch(function (e) { + catchCalled = true; + assert.sameValue(e, noReason); +}).then(function () { + assert.sameValue(finallyCalled, true, 'initial finally was called'); + assert.sameValue(initialThenCount, 1, 'initial finally invokes .then once'); + + assert.sameValue(catchCalled, true, 'catch was called'); + assert.sameValue(onFinallyThenCount, 1, 'onFinally return promise has .then invoked once'); + $DONE(); +}).catch($ERROR); diff --git a/test/built-ins/Promise/prototype/finally/this-value-non-object.js b/test/built-ins/Promise/prototype/finally/this-value-non-object.js new file mode 100644 index 0000000000..1e8467b9b8 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/this-value-non-object.js @@ -0,0 +1,17 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: > + Promise.prototype.finally called with a non-object-coercible `this` value +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +assert.throws(TypeError, function() { + Promise.prototype.finally.call(undefined); +}, 'undefined'); + +assert.throws(TypeError, function() { + Promise.prototype.finally.call(null); +}, 'null'); diff --git a/test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js b/test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js new file mode 100644 index 0000000000..cdeccdadd3 --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js @@ -0,0 +1,51 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: > + Promise.prototype.finally called with a `this` value that does not define a + callable `then` property +esid: sec-promise.prototype.finally +features: [Symbol, Promise.prototype.finally] +---*/ + +var symbol = Symbol(); + +var thrower = function () { throw new Test262Error('this should never happen'); }; + +var p = new Promise(function () {}); + +p.then = undefined; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'undefined'); + +p.then = null; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'null'); + +p.then = 1; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'number'); + +p.then = ''; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'string'); + +p.then = true; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'boolean'); + +p.then = symbol; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'symbol'); + +p.then = {}; +assert.throws(TypeError, function() { + Promise.prototype.finally.call(p, thrower); +}, 'ordinary object'); diff --git a/test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js b/test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js new file mode 100644 index 0000000000..d8afd2612c --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js @@ -0,0 +1,24 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: > + Promise.prototype.finally called with a `this` value whose `then` property is + an accessor property that returns an abrupt completion +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +var poisonedThen = Object.defineProperty(new Promise(function () {}), 'then', { + get: function() { + throw new Test262Error(); + } +}); + +assert.throws(Test262Error, function() { + Promise.prototype.finally.call(poisonedThen); +}); + +assert.throws(Test262Error, function() { + poisonedThen.finally(); +}); diff --git a/test/built-ins/Promise/prototype/finally/this-value-then-throws.js b/test/built-ins/Promise/prototype/finally/this-value-then-throws.js new file mode 100644 index 0000000000..7fff8d51aa --- /dev/null +++ b/test/built-ins/Promise/prototype/finally/this-value-then-throws.js @@ -0,0 +1,23 @@ +// Copyright (C) 2017 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +author: Jordan Harband +description: > + Promise.prototype.finally called with a `this` value that defines a `then` + method which returns an abrupt completion. +esid: sec-promise.prototype.finally +features: [Promise.prototype.finally] +---*/ + +var thrower = new Promise(function () {}); +thrower.then = function() { + throw new Test262Error(); +}; + +assert.throws(Test262Error, function() { + Promise.prototype.finally.call(thrower); +}); + +assert.throws(Test262Error, function() { + thrower.finally(); +});