Merge pull request #1156 from ljharb/finally

Promise.prototype.finally: add tests
This commit is contained in:
Leo Balter 2017-08-11 12:38:48 -04:00 committed by GitHub
commit e467c83aa0
19 changed files with 497 additions and 0 deletions

6
.gitignore vendored
View File

@ -5,3 +5,9 @@
console/TestCases
github-deploy-key
github-deploy-key.pub
node_modules
# Only apps should have lockfiles
package-lock.json
yarn.lock
npm-shrinkwrap.json

1
.npmrc Normal file
View File

@ -0,0 +1 @@
package-lock=false

View File

@ -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

View File

@ -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`');

View File

@ -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`');

View File

@ -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'
);

View File

@ -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`'
);

View File

@ -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,
});

View File

@ -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');

View File

@ -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');

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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');

View File

@ -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');

View File

@ -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();
});

View File

@ -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();
});