blob: f6953339e519dfd4d46f9d7ddfb882d2f0fb259a [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
var tests = [
{
name: "Basic destructuring syntax as params",
body: function () {
assert.doesNotThrow(function () { eval("function foo({x:x}) {}"); }, "Object destructuring pattern as a formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x}) {}"); }, "Object destructuring pattern (shorthand) as a formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x]) {}"); }, "Array destructuring pattern as a formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x:x}, y) {}"); }, "Object destructuring pattern as a first formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x], y) {}"); }, "Array destructuring pattern as a first formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo(y, {x:x}) {}"); }, "Object destructuring pattern as a second formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo(y, [x]) {}"); }, "Array destructuring pattern as a second formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo({}) {}"); }, "Object destructuring pattern (with empty syntax) as a formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo([]) {}"); }, "Array destructuring pattern (with empty syntax) as a formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo([,]) {}"); }, "Array destructuring pattern (with empty syntax and comma) as a formal is valid syntax");
}
},
{
name: "Destructuring syntax as params - invalid syntax",
body: function () {
assert.throws(function () { eval("function foo({,}) {}"); }, SyntaxError, "Object destructuring pattern as a formal with empty names is not valid syntax", "Expected identifier, string or number");
assert.throws(function () { eval("function foo(({})) {}"); }, SyntaxError, "Object destructuring pattern as a formal surrounded with parens is not valid syntax", "Expected identifier");
assert.throws(function () { eval("function foo(([])) {}"); }, SyntaxError, "Array destructuring pattern as a formal surrounded with parens is not valid syntax", "Expected identifier");
assert.throws(function () { eval("function foo([x}) {}"); }, SyntaxError, "Array destructuring pattern without right bracket is not valid syntax", "Unexpected operator in destructuring expression");
assert.throws(function () { eval("function foo({x:abc+1}) {}"); }, SyntaxError, "Object destructuring pattern with operator is not valid syntax", "Unexpected operator in destructuring expression");
assert.throws(function () { eval("function foo({x:abc.d}) {}"); }, SyntaxError, "Object destructuring pattern as a formal with property reference is not valid syntax", "Syntax error");
assert.throws(function () { eval("function foo([abc.d]) {}"); }, SyntaxError, "Array destructuring pattern as a formal with property reference is not valid syntax", "Syntax error");
assert.throws(function () { eval("function foo([super]) {}"); }, SyntaxError, "Array destructuring pattern as a formal identifier as super is not valid syntax", "The use of a keyword for an identifier is invalid");
assert.throws(function () { eval("function foo({x:super}) {}"); }, SyntaxError, "Object destructuring pattern as a formal identifier as super is not valid syntax", "The use of a keyword for an identifier is invalid");
assert.throws(function () { eval("function foo({x:super()}) {}"); }, SyntaxError, "Object destructuring pattern as a formal identifier as super call is not valid syntax", "The use of a keyword for an identifier is invalid");
assert.throws(function () { eval("function foo([super()]) {}"); }, SyntaxError, "Array destructuring pattern as a formal identifier as super call is not valid syntax", "The use of a keyword for an identifier is invalid");
}
},
{
name: "Destructuring syntax as params - Multiple and mixed patterns",
body: function () {
assert.doesNotThrow(function () { eval("function foo({x:x}, {y:y}, {z:z}) {}"); }, "Three params as object pattern is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x], [y], [z]) {}"); }, "Three params as array pattern is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:x1, x2:x2, x3:x3}, {y1:y1, y1:y2}) {}"); }, "Two params as object pattern, and each pattern has more than one matching name is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1, x2, x3], [y1, y2, y3]) {}"); }, "Two params as array pattern, and each pattern has more than one matching name is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:x1}, [y1]) {}"); }, "Two params with first object pattern and second array pattern is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1], {y1:y1}) {}"); }, "Two params with first array pattern and second object pattern is valid syntax");
assert.doesNotThrow(function () { eval("function foo(x1, {x2, x3}, [x4, x5], x6) {}"); }, "Multiple params with mixed of binding identifier and binding pattern is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:[y1]}) {}"); }, "Object destructuring pattern nesting array pattern as a formal is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1, {y1:y1}]) {}"); }, "Array destructuring pattern nesting object pattern as a formal is valid syntax");
}
},
{
name: "Destructuring syntax as params - class/function expression/function expression with names/lambda",
body: function () {
assert.doesNotThrow(function () { eval("({x}) => x;"); }, "Object destructuring pattern as a formal on lambda is valid syntax");
assert.doesNotThrow(function () { eval("([x]) => x;"); }, "Array destructuring pattern as a formal on lambda is valid syntax");
assert.doesNotThrow(function () { eval("class foo { constructor({x1}){} }"); }, "Object destructuring pattern as a formal on constructor of class is valid syntax");
assert.doesNotThrow(function () { eval("class foo { constructor([x1]){} }"); }, "Array destructuring pattern as a formal on constructor of class is valid syntax");
assert.doesNotThrow(function () { eval("class foo { method({x1}){ }; set prop({x1}){} }"); }, "Object destructuring pattern on method and setter of class is valid syntax");
assert.doesNotThrow(function () { eval("class foo { method([x1]){ }; set prop([x1]){} }"); }, "Array destructuring pattern on method and setter of class is valid syntax");
assert.doesNotThrow(function () { eval("let foo = function ({x1}, [x2]){};"); }, "Destructuring patterns as formals on function expression is valid syntax");
assert.doesNotThrow(function () { eval("(function({x1}, [x2]){})"); }, "Destructuring patterns as formals on anonymous function is valid syntax");
assert.doesNotThrow(function () { eval("let bar = function foo({x1}, [x2]){};"); }, "Destructuring patterns as formals on named function expression is valid syntax");
assert.doesNotThrow(function () { eval("new Function('{x}', '[y]', 'return x + y');"); }, "Destructuring patterns as formals on function constructor is valid syntax");
assert.doesNotThrow(function () { eval("let obj = { foo({x}) {}, set prop([x]) {} }"); }, "Destructuring pattern as a formal on functions of object literal is valid syntax");
}
},
{
name: "Destructuring syntax as params - Initializer",
body: function () {
assert.doesNotThrow(function () { eval("function foo({x:x = 10}) {}"); }, "One param as object destructuring pattern with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x = 20]) {}"); }, "One param as array destructuring pattern with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:x1 = 1}, {y1:y1 = 2}) {}"); }, "Two params as object destructuring pattern with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1 = 1], [y1 = 2]) {}"); }, "Two params as array destructuring pattern with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:x1 = 1, x2:x2 = 2, x3:x3 = 3}) {}"); }, "One param as object destructuring pattern has three names with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1 = 1, x2 = 2, x3 = 3]) {}"); }, "One param as array destructuring pattern has three names with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:x1 = 1}, [y1 = 2]) {}"); }, "First param as object pattern and second as array pattern with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1 = 1], {y1:y1 = 2}) {}"); }, "First param as array pattern and second as object pattern with initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x:x} = {x:1}) {}"); }, "Object destructuring pattern has default value is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x] = [1]) {}"); }, "Array destructuring pattern has default value is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x:x = 1} = {x:2}) {}"); }, "Object destructuring pattern has default and identifier has initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x = 1] = [2]) {}"); }, "Array destructuring pattern has default and identifier has initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:[y1 = 1]}) {}"); }, "Object destructuring pattern nesting array pattern which has initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([x1, {y1:y1 = 1}]) {}"); }, "Array destructuring pattern nesting object pattern which has initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo({x1:[y1 = 1] = [2]} = {x1:[3]}) {}"); }, "Object destructuring pattern has default and nesting array pattern which has initializer is valid syntax");
assert.doesNotThrow(function () { eval("function foo([{y1:y1 = 1} = {y1:2}] = [{y1:3}]) {}"); }, "Array destructuring pattern has default and nesting object pattern which has initializer is valid syntax");
}
},
{
name: "Destructuring syntax as params - redeclarations",
body: function () {
assert.throws(function () { eval("function foo({x:x, x:x}) {}"); }, SyntaxError, "One formal as object pattern has duplicate binding identifiers is not valid syntax", "Let/Const redeclaration");
assert.throws(function () { eval("function foo({x:x}, {x:x}) {}"); }, SyntaxError, "Two formals as object patterns have duplicate binding identifiers is not valid syntax", "Let/Const redeclaration");
assert.throws(function () { eval("function foo([x, x]) {}"); }, SyntaxError, "One formal as array pattern has duplicate binding identifiers is not valid syntax", "Let/Const redeclaration");
assert.throws(function () { eval("function foo([x], [x]) {}"); }, SyntaxError, "Two formals as array patterns have duplicate binding identifiers is not valid syntax", "Let/Const redeclaration");
assert.throws(function () { eval("function foo([x], {x:x}) {}"); }, SyntaxError, "Mixed array and object pattern with duplicate identifiers is not valid syntax", "Let/Const redeclaration");
assert.throws(function () { eval("function foo([x], x) {}"); }, SyntaxError, "First formal as array pattern has matching name with the second formal is not valid syntax", "Duplicate formal parameter names not allowed in this context");
assert.throws(function () { eval("function foo(x, [x]) {}"); }, SyntaxError, "First normal formal is matching with the second formal which is array pattern is not valid syntax", "Let/Const redeclaration");
assert.throws(function () { eval("function foo({x:{z:[z1]}}, z1) {}"); }, SyntaxError, "First formal as nesting object pattern has matching name with the second formal is not valid syntax", "Duplicate formal parameter names not allowed in this context");
assert.throws(function () { eval("function foo([x]) { let x = 10;}"); }, SyntaxError, "Object destructuring pattern as a formal is valid syntax", "Let/Const redeclaration");
assert.doesNotThrow(function () { eval("function foo([x]) { var x = 10;}"); }, "var declared names matching with formal (as array pattern) is valid syntax");
}
},
{
name: "Destructuring on param - basic functionality",
body : function () {
function f1({x:x}) {
return x;
}
let ret = f1({x:1});
assert.areEqual(ret, 1, "Object pattern as a formal matches with actual param and initializes the identifier correctly");
function f2([x]) {
return x;
}
ret = f2([2]);
assert.areEqual(ret, 2, "Array pattern as a formal matches with actual param and initializes the identifier correctly");
function f3({x}, [y], z) {
return x + y + z;
}
ret = f3({x:1}, [2], 3);
assert.areEqual(ret, 6, "First formal as object pattern and second formal as array pattern should match and initialize identifiers correctly");
let f4 = function ([x], {y:y}, z) {
return x + y + z;
}
ret = f4([1], {y:2}, 3);
assert.areEqual(ret, 6, "First formal as array pattern and second formal as object pattern should match and initialize identifiers correctly");
}
},
{
name: "Destructuring on param - initializer",
body : function () {
function f1({x:x1 = 11}, [x2 = 22]) {
assert.areEqual(x1, 1, "Identifier from the first pattern has initializer but matches the actual first param and initializes correctly");
assert.areEqual(x2, 2, "Identifier from the second pattern has initializer byt matches the actual second param and initializes correctly");
}
f1({x:1}, [2]);
function f2({x:x1 = 11}, [x2 = 22]) {
assert.areEqual(x1, 11, "Identifier from the first pattern gets undefined from the actual first param so initializes with initializer");
assert.areEqual(x2, 22, "Identifier from the second pattern gets undefined from the actual second param so initializes with initializer");
}
f2({}, []);
(function ({x:x1 = 11} = {x:111}, [x2 = 22] = [222]) {
assert.areEqual(x1, 111, "First pattern matches with default as the actual first param is undefined");
assert.areEqual(x2, 222, "Second pattern matches with default as the actual second param is undefined");
})(undefined, undefined);
}
},
{
name: "Destructuring on param - functionality on lambda/function expression",
body : function () {
(function({x:x1}) {
assert.areEqual(x1, 1, "Anonymous function - object pattern as a formal matches with actual param and initializes identifier correctly");
})({x:1});
let f1 = ([x1]) => x1 * 2;
assert.areEqual(f1([2]), 4, "Lambda - array pattern as a formal matches with actual param and initializes identifier correctly");
let f2 = ({x:x2}) => x2 * 4;
assert.areEqual(f2({x:2}), 8, "Lambda - object pattern as a formal matches with actual param and initializes identifier correctly");
let f3 = function foo({x:x1}) {
assert.areEqual(x1, 1, "Named function expression - object pattern as a formal matches with actual param and initializes identifier correctly");
}
f3({x:1});
let f4 = new Function("{x}", "[y]", "return x + y");
assert.areEqual(f4({x:1}, [2]), 3, "Function constructor - patterns as formals match with actual params and initialize identifiers correctly");
}
},
{
name: "Destructuring on param - captures",
body : function () {
function f1({x:x1}) {
function f1_1() {
assert.areEqual(x1, 1, "Identifier from object pattern is captured and initialized correctly");
}
f1_1();
}
f1({x:1});
function f2([x1]) {
function f2_1() {
assert.areEqual(x1, 2, "Identifier from array pattern is captured and initialized correctly");
}
f2_1();
}
f2([2]);
function f3({x:x1}, [y1], z) {
(function () {
x1++;
})();
(function () {
y1++;
})();
var k = x1 + y1 + z;
assert.areEqual(k, 62, "Identifiers from patterns are captured and initialized correctly");
}
f3({x:10}, [20], 30);
}
},
{
name: "Destructuring on param - capture due to eval",
body : function () {
function f1({x:x1}, [x2]) {
eval('');
assert.areEqual(x1, 1, "Function has eval - identifier from the object pattern is initialized correctly");
assert.areEqual(x2, 2, "Function has eval - identifier from the array pattern is initialized correctly under eval");
}
f1({x:1}, [2]);
function f2({x:x1}, [x2]) {
eval('');
(function () {
x1;
x2;
})();
assert.areEqual(x1, 3, "Function has eval and inner function - identifier from the object pattern is initialized correctly");
assert.areEqual(x2, 4, "Function has eval and inner function - identifier from the array pattern is initialized correctly");
}
f2({x:3}, [4]);
function f3({x:x1}, [x2]) {
(function () {
eval('');
assert.areEqual(x1, 5, "Function's inner function has eval - identifier from the object pattern is initialized correctly");
assert.areEqual(x2, 6, "Function's inner function has eval - identifier from the object pattern is initialized correctly");
})();
}
f3({x:5}, [6]);
}
},
{
name: "Destructuring on param - captures (eval and arguments)",
body : function () {
function f1({x:x1}, [x2]) {
assert.areEqual(arguments[0], {x:1}, "arguments[0] is initialized correctly with first actual argument");
assert.areEqual(arguments[1], [2], "arguments[1] is initialized correctly with second actual argument");
}
f1({x:1}, [2]);
function f2({x:x1}, [x2]) {
(function() {
})();
eval('');
assert.areEqual(arguments[0], {x:1}, "Function has inner function and eval - arguments[0] is initialized correctly with first actual argument");
assert.areEqual(arguments[1], [2], "Function has inner function and eval - arguments[1] is initialized correctly with second actual argument");
}
f2({x:1}, [2]);
function f3({x:x1}, [x2]) {
(function() {
eval('');
})();
assert.areEqual(arguments[0], {x:1}, "Function's inner function has eval - arguments[0] is initialized correctly with first actual argument");
assert.areEqual(arguments[1], [2], "Function's inner function has eval - arguments[1] is initialized correctly with second actual argument");
}
f3({x:1}, [2]);
(function({x:x1}, x2) {
x2 = 3;
assert.areEqual(arguments[1], 2, "arguments object is unmapped - changing second formal does not change arguments[1]");
})({x:1}, 2);
(function ([x1], x2) {
arguments[1] = 2;
assert.areEqual(x2, 1, "arguments object is unmapped - changing arguments[1] does not change second param");
})([], 1);
(function ({x:x1}, x2) {
x2 = 2;
(function() {
eval('');
})();
assert.areEqual(arguments[1], 1, "Function's inner function has eval - arguments object is unmapped - changing second formal does not change arguments[1]");
})({}, 1);
(function ([x1], x2) {
(function() {
eval('');
})();
arguments[1] = 2;
assert.areEqual(x2, 1, "Function's inner function has eval - arguments object is unmapped - changing arguments[1] does not change second param");
})([], 1);
}
},
{
name: "Destructuring on param - multiple/mixed/nested parameters",
body : function () {
(function (x1, {x2, x3}, [x4, x5], x6) {
var k = x1 + x2 + x3 + x4 + x5 + x6;
assert.areEqual(k, 21, "Identifiers under various patterns are matched and initialized correctly");
})(1, {x2:2, x3:3}, [4, 5], 6);
(function (x1, {x2, x3}, [x4, x5], x6) {
var k = x1 + x2 + x3 + x4 + x5 + x6;
eval('');
assert.areEqual(k, 21, "Function has eval - identifiers under various patterns are matched and initialized correctly");
})(1, {x2:2, x3:3}, [4, 5], 6);
(function (x1, {x2, x3}, [x4, x5], x6) {
var k = x1 + x2 + x3 + x4 + x5 + x6;
(function() {
eval('');
});
assert.areEqual(k, 21, "Function's inner function has eval - identifiers under various patterns are matched and initialized correctly");
})(1, {x2:2, x3:3}, [4, 5], 6);
}
},
{
name: "Destructuring on param - misc",
body : function () {
let f1 = function foo(x0, {x:x1}, [x2]) {
assert.areEqual(x1, 1, "Function expression with name - identifier from second formal is matched and initialized correctly");
assert.areEqual(x2, 2, "Function expression with name - identifier from third formal is matched and initialized correctly");
}
f1(0, {x:1}, [2]);
let f2 = function foo1(x0, {x:x1}, [x2]) {
eval('');
assert.areEqual(x1, 1, "Function expression with name has eval - identifier from second formal is matched and initialized correctly");
assert.areEqual(x2, 2, "Function expression with name has eval - identifier from third formal is matched and initialized correctly");
}
f2(0, {x:1}, [2]);
let f3 = function foo2(x0, {x:x1}, [x2]) {
with(x1) {
assert.areEqual(x1_1, 1, "Function expression with name has 'with' - identifier from second formal is matched and initialized correctly");
assert.areEqual(x2, 2, "Function expression with name has 'with' - identifier from third formal is matched and initialized correctly");
}
}
f3(0, {x:{x1_1:1}}, [2]);
let f4 = function foo3(x0, {x:x1}, [x2]) {
try {
throw 'abc';
}
catch(e) {
(function () {
assert.areEqual(x1, 1, "Function expression with name has 'try/catch' - identifier from second formal is matched and initialized correctly");
assert.areEqual(x2, 2, "Function expression with name has 'try/catch' - identifier from third formal is matched and initialized correctly");
})();
}
}
f4(0, {x:1}, [2]);
let f5 = function foo4(x0, {x:x1}, [x2]) {
try {
throw 'abc';
}
catch(e) {
(function () {
eval('');
assert.areEqual(x1, 1, "Function expression with name has 'try/catch and eval' - identifier from second formal is matched and initialized correctly");
assert.areEqual(x2, 2, "Function expression with name has 'try/catch and eval' - identifier from third formal is matched and initialized correctly");
})();
}
}
f5(0, {x:1}, [2]);
}
},
{
name: "Destructuring on param - class",
body : function () {
class Foo {
add([x1]) {
this.x1 += x1;
}
set prop({x1}) {
this.x1 = x1;
}
get prop() {
return this.x1;
}
static Avg({x1}, [x2], x3) {
return (x1+x2+x3)/3;
}
}
assert.areEqual(Foo.Avg({x1:3}, [4], 5), 4, "Class's static function - identifiers from formal patterns are matched and initialized correctly");
let obj = new Foo();
obj.prop = {x1:1};
assert.areEqual(obj.prop, 1, "Class's setter - identifier from the formal object pattern is matched and initialized correctly");
obj.add([2]);
assert.areEqual(obj.prop, 3, "Class's method - identifier from the formal array pattern is matched and initialized correctly");
}
}
];
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });