//-------------------------------------------------------------------------------------------------------
// 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 object destructuring syntax",
    body: function () {
      assert.doesNotThrow(function () { eval("var {} = {};"); },  "var object declaration pattern with no identifier is valid syntax");
      assert.doesNotThrow(function () { eval("let {} = {};"); },  "let object declaration pattern with no identifier is valid syntax");
      assert.doesNotThrow(function () { eval("const {} = {};"); }, "const object declaration pattern with no identifier is valid syntax");
      assert.doesNotThrow(function () { eval("({} = {});"); },      "Object pattern as an expression with no identifier is valid syntax");
      assert.doesNotThrow(function () { eval("var {x:y} = {};"); }, "var object declaration pattern with a single member is valid syntax");
      assert.doesNotThrow(function () { eval("let {x:y} = {};"); }, "let object declaration pattern with a single member is valid syntax");
      assert.doesNotThrow(function () { eval("const {x:y} = {};"); }, "const object declaration pattern with a single member is valid syntax");
      assert.doesNotThrow(function () { eval("({x:y} = {});"); }, "Object pattern as an expression with a single member is valid syntax");
      assert.doesNotThrow(function () { eval("var {x} = {};"); }, "var object declaration pattern with a single member as shorthand is valid syntax");
      assert.doesNotThrow(function () { eval("({x:y} = {});"); }, "Object pattern as an expression with a single member as shorthand is valid syntax");
      assert.doesNotThrow(function () { eval("var {x} = {}, {y} = {};"); }, "Multiple object pattern in a single var declaration is valid syntax");
    }
  },

  {
    name: "Basic object destructuring invalid syntax",
    body: function () {
      assert.throws(function () { eval("var {};"); }, SyntaxError, "var empty object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
      assert.throws(function () { eval("let {};"); }, SyntaxError, "let empty object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
      assert.throws(function () { eval("const {};"); }, SyntaxError, "const empty object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
      assert.throws(function () { eval("var {a};"); }, SyntaxError, "var object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
      assert.throws(function () { eval("let {a};"); }, SyntaxError, "let object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
      assert.throws(function () { eval("const {a};"); }, SyntaxError, "const object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
      assert.throws(function () { eval("var {,} = {}"); }, SyntaxError, "Object declaration pattern without an identifier is not valid syntax", "Expected identifier, string or number");
      assert.throws(function () { eval("({,} = {});"); }, SyntaxError, "Object expression pattern without an identifier is not valid syntax", "Expected identifier, string or number");

      assert.throws(function () { eval("var {x:y--} = {};"); }, SyntaxError, "Object declaration pattern with an operator -- is not valid syntax", "Unexpected operator in destructuring expression");
      assert.throws(function () { eval("var {x:y+1} = {};"); }, SyntaxError, "Object declaration pattern with an operator + is not valid syntax", "Unexpected operator in destructuring expression");

      assert.throws(function () { eval("var y; ({x:y--} = {});"); }, SyntaxError, "Object expression pattern with an operator -- is not valid syntax",   "Unexpected operator in destructuring expression");
      assert.throws(function () { eval("var y; ({x:y+1} = {});"); }, SyntaxError, "Object expression pattern with an operator + is not valid syntax",   "Unexpected operator in destructuring expression");
    }
  },

  {
    name: "Object destructuring syntax with initializer",
    body: function () {
      assert.doesNotThrow(function () { eval("var {x:x = 20} = {};"); }, "var object declaration pattern with default is valid syntax");
      assert.doesNotThrow(function () { eval("let {x:x = 20} = {};"); }, "let object declaration pattern with default is valid syntax");
      assert.doesNotThrow(function () { eval("const {x:x = 20} = {};"); }, "const object declaration pattern with default is valid syntax");
      assert.doesNotThrow(function () { eval("var x; ({x:x = 20} = {});"); }, "Object declaration pattern with default is valid syntax");

      assert.doesNotThrow(function () { eval("var {x, x1:y = 20} = {};"); }, "Object declaration pattern with default other than first is valid syntax");
      assert.doesNotThrow(function () { eval("var {x:z = 1, x1:y = 20} = {};"); }, "Object declaration pattern with defaults on more than one is valid syntax");

      assert.doesNotThrow(function () { eval("var x, y; ({x, x1:y = 20} = {});"); }, "Object expression pattern with default other than first is valid syntax");
      assert.doesNotThrow(function () { eval("var z, y; ({x:z = 1, x1:y = 20} = {});"); }, "Object expression pattern with defaults on more than one is valid syntax");
    }
  },

  {
    name: "Object destructuring syntax with identifier reference",
    body: function () {
      assert.throws(function () { eval("function foo() { return {}; }; let {x:foo()} = {};"); }, SyntaxError,  "Object declaration pattern with a call expression is not valid syntax", "Let/Const redeclaration");
      assert.throws(function () { eval("function foo() { return {}; }; ({x:foo()} = {});"); }, SyntaxError,  "Object expression pattern with a call expression is not valid syntax", "Invalid destructuring assignment target");
      assert.throws(function () { eval("function foo() { return {}; }; var {x:foo().x} = {};"); }, SyntaxError,  "Object declaration pattern with property reference on call is not valid syntax", "Syntax error");

      assert.doesNotThrow(function () { eval("var a = {}; ({x:a.x} = {});"); }, "Object expresion pattern with a property reference is valid syntax");
      assert.doesNotThrow(function () { eval("let a = {}; ({x:a['x']} = {});"); }, "Object expression pattern with a property reference as index is valid syntax");

      assert.doesNotThrow(function () { eval("function foo() { return {}; }; ({x:foo().x} = {});"); }, "Object declaration pattern with property reference on call is valid syntax");
      assert.doesNotThrow(function () { eval("function foo() { return {}; }; ({x:foo()['x']} = {});"); }, "Object declaration pattern with property reference as index on call is valid syntax");

      assert.throws(function () { eval("class foo { method() { let {x:super()} = {}; } }"); },SyntaxError, "Object declaration pattern with a super call is not valid syntax", "The use of a keyword for an identifier is invalid");
      assert.throws(function () { eval("class foo { method() { ({x:super()} = {}); } }"); }, SyntaxError, "Object expression pattern with a super call is not valid syntax", "Invalid use of the 'super' keyword");
      assert.throws(function () { eval("class foo { method() { var {x:super.x} = {}; } }"); }, SyntaxError, "Object declaration pattern with a property reference on super is not valid syntax", "The use of a keyword for an identifier is invalid");
      assert.doesNotThrow(function () { eval("class foo { method() { ({x:super.x} = {}); } }"); }, "Object expression pattern with a property reference on super is valid syntax");
      assert.doesNotThrow(function () { eval("class foo { method() { ({x:super['x']} = {}); } }"); }, "Object expression pattern with a property reference as an index on super is valid syntax");

      assert.doesNotThrow(function () { eval("var a = [1], i = 0; ({x:a[i++]} = {});"); }, "Object Destructuring pattern assignment operators inside an identifier reference is valid syntax");
      }
   },

   {
    name: "Object destructuring syntax with computed property name",
    body: function () {

      assert.doesNotThrow(function () { eval("var zee = 'x'; var {[zee]:x1} = {}"); }, "Object declaration pattern with computed property name is valid syntax");
      assert.doesNotThrow(function () { eval("var zee = 'x'; var x1; ({[zee]:x1} = {})"); }, "Object expression pattern with computed property name is valid syntax");
      assert.doesNotThrow(function () { eval("var zee = 'x'; var {[zee + 'foo']:x1} = {}"); }, "Object declaration pattern with computed property name with add operator is valid syntax");
      assert.doesNotThrow(function () { eval("var zee = 'x'; var x1; ({[zee +'foo']:x1} = {})"); }, "Object expression pattern with computed property name with add operator is valid syntax");
    }
   },
   {
    name: "Destructing syntax - having rest element as pattern",
    body: function () {
      assert.doesNotThrow(function () { eval("let [...[a]] = [[]];"); }, "Under declaration, having rest element as array pattern is valid syntax");
      assert.doesNotThrow(function () { eval("let a; [...[a]] = [[]];"); }, "Under expression, having rest element as array pattern is valid syntax");

      assert.doesNotThrow(function () { eval("let [...{a}] = [{}];"); }, "Under declaration, having rest element as object pattern is valid syntax");
      assert.doesNotThrow(function () { eval("let a; [...{a}] = [{}];"); }, "Under expression, having rest element as object pattern is valid syntax");

      assert.doesNotThrow(function () { eval("let a; [...[a = 1]] = [[]];"); }, "Under expression, having rest element as array pattern has initializer is valid syntax");
      assert.doesNotThrow(function () { eval("let a; [...{a:a = 1}] = [{}];"); }, "Under expression, having rest element as object pattern has initializer is valid syntax");

      assert.doesNotThrow(function () { eval("let obj = {x:1}; [...obj.x] = [10];"); }, "Rest element being property reference is valid syntax");
      assert.doesNotThrow(function () { eval("let obj = {x:1}; [...obj['x']] = [10];"); }, "Rest element being property reference as index is valid syntax");

      assert.doesNotThrow(function () { eval("function foo() { return {x:1}; }; [...foo().x] = [10];"); }, "Rest element being property reference on call expression is valid syntax");
      assert.doesNotThrow(function () { eval("function foo() { return {x:1}; }; [...foo()['x']] = [10];"); }, "Rest element being property reference as index on call expression is valid syntax");

      assert.doesNotThrow(function () { eval("let [...[...[...a]]] = [[[]]];"); }, "Nesting rest element inside another rest element is valid syntax");

      assert.throws(function () { eval("let [...[a+1] = [{}];"); },   SyntaxError, "Under declaration, having rest element as pattern which has operator is not valid syntax",   "Unexpected operator in destructuring expression");
      assert.throws(function () { eval("let a; [...1+a] = [{}];"); }, SyntaxError, "Under declaration, rest element has operator is not valid syntax",   "Invalid destructuring assignment target");

      assert.throws(function () { eval("let a; [...[a+1] = [{}];"); }, SyntaxError, "Under expression, having rest element as pattern which has operator is not valid syntax",   "Unexpected operator in destructuring expression");
      assert.throws(function () { eval("function foo() { return {x:1}; }; [...foo()] = [10];"); }, SyntaxError, "Under expression, having rest element as call expression is not valid syntax", "Invalid destructuring assignment target");
      assert.throws(function () { eval("let [...[a] = []] = [[]];"); }, SyntaxError, "Under declaration - rest as array pattern cannot have initializer", "The rest parameter cannot have a default initializer.");
      assert.throws(function () { eval("let [...{x} = {}] = [{}];"); }, SyntaxError, "Under declaration - rest as object pattern cannot have initializer", "The rest parameter cannot have a default initializer.");
      assert.throws(function () { eval("let a; ([...[a] = []] = [[]]);"); }, SyntaxError, "Under assignment - rest as array pattern cannot have initializer", "The rest parameter cannot have a default initializer.");
      assert.throws(function () { eval("let x; ([...{x} = {}] = [{}]);"); }, SyntaxError, "Under assignment - rest as object pattern cannot have initializer", "The rest parameter cannot have a default initializer.");
    }
   },
   {
    name: "Object destructuring syntax with repeated identifier",
    body: function () {
      assert.doesNotThrow(function () { eval("var {a:a, a:a} = {};"); },    "var declaration pattern with a repeated identifier is valid syntax");
      assert.throws(function () { eval("let {a:a, a:a} = {};"); },   SyntaxError, "let declaration pattern with a repeated identifier is not valid syntax", "Let/Const redeclaration");
      assert.throws(function () { eval("const {a:a, a:a} = {};"); }, SyntaxError, "const declaration pattern with a repeated identifier is not valid syntax", "Let/Const redeclaration");
      assert.throws(function () { eval("let {b, b} = {};"); },   SyntaxError, "let declaration pattern with a repeated identifier as shorthand is not valid syntax", "Let/Const redeclaration");
      assert.throws(function () { eval("const {b, b} = {};"); }, SyntaxError, "const declaration pattern with a repeated identifier as shorthand is not valid syntax", "Let/Const redeclaration");
      assert.throws(function () { eval("let {x:c, y:c} = {};"); },   SyntaxError, "let declaration pattern with a repeated identifier but different matching pattern is not valid syntax", "Let/Const redeclaration");
      assert.throws(function () { eval("const {x:c, y:c} = {};"); }, SyntaxError, "const declaration pattern with a repeated identifier but different matching pattern is not valid syntax", "Let/Const redeclaration");
      assert.doesNotThrow(function () { eval("let a; ({a:a, a:a} = {});"); }, "Object expression pattern with a repeated identifier is valid syntax");
    }
   },
   {
    name: "Object destructuring syntax on misc expressions",
    body: function () {
      assert.doesNotThrow(function () { eval("let a; ({a:((((a1))))} = {a:20})"); }, "Object expression pattern with parens is valid syntax");
      assert.doesNotThrow(function () { eval("let a; ({a:((((a1 = 31))))} = {})"); }, "Object expression pattern with parens and defaults is valid syntax");
      assert.doesNotThrow(function () { eval("let a, r1; ({a:a1 = r1} = {})"); }, "Object expression pattern with defaults as reference is valid syntax");
      assert.doesNotThrow(function () { eval("let a, r1; ({a:((((a1 = r1))))} = {})"); }, "Object expression pattern with defaults as reference under parens is valid syntax");
      assert.doesNotThrow(function () { eval("let a, r1; ({a:a1 = r1 = 44} = {})"); }, "Object expression pattern with chained assignments as defaults is valid syntax");
      assert.doesNotThrow(function () { eval("let a, r1; ({a:(a1 = r1 = 44)} = {})"); }, "Object expression pattern with chained assignments as defaults under paren is valid syntax");
      assert.throws(function () { eval("let a, r1; ({a:(a1 = r1) = 44} = {})"); }, SyntaxError, "Object expression pattern with chained assignments but paren in between is not valid syntax", "Unexpected operator in destructuring expression");
      assert.doesNotThrow(function () { eval("var a; `${({a} = {})}`"); }, "Object expression pattern inside a string template is valid syntax");
      assert.throws(function () { eval("for (let {x} = {} of []) {}"); }, SyntaxError, "for.of has declaration pattern with initializer is not valid syntax", "for-of loop head declarations cannot have an initializer");
      assert.throws(function () { eval("for (let {x} = {} in []) {}"); }, SyntaxError, "for.in has declaration pattern with initializer is not valid syntax", "for-in loop head declarations cannot have an initializer");
      assert.throws(function () { eval("for (var [x] = [] of []) {}"); }, SyntaxError, "for.of has var declaration pattern with initializer is not valid syntax", "for-of loop head declarations cannot have an initializer");
      assert.throws(function () { eval("function foo() {for (let {x} = {} of []) {}; }; foo();"); }, SyntaxError, "Inside function - for.of has declaration pattern with initializer is not valid syntax", "for-of loop head declarations cannot have an initializer");
      assert.doesNotThrow(function () { eval("var a; [a = class aClass {}] = []"); }, "Expression pattern has class as initializer is valid syntax");
      assert.doesNotThrow(function () { eval("var a; for ({x:x = class aClass {}} of []) {}"); }, "for.of's expression pattern has class as initializer is valid syntax");
      assert.doesNotThrow(function () { eval("var {x:[...y]} = {x:[1]}"); }, "rest element nesting under object pattern is valid syntax");
      assert.throws(function () { eval("let {foo() {}} = {};"); }, SyntaxError, "Invalid object pattern as it has the function short-hand instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("let {get foo() {}} = {};"); }, SyntaxError, "Invalid object pattern as it has the get function short-hand instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("let {set foo() {}} = {};"); }, SyntaxError, "Invalid object pattern as it has the set function short-hand instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("let {get ['foo']() {}} = {};"); }, SyntaxError, "Invalid object pattern as it has the get function name as computed property instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("let {set ['foo'](a) {}} = {};"); }, SyntaxError, "Invalid object pattern as it has the set function name as computed property instead of binding identifier", "Invalid destructuring assignment target");

      assert.throws(function () { eval("({foo() {}} = {});"); }, SyntaxError, "Invalid object expression pattern as it has the function short-hand instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("({get foo() {}} = {});"); }, SyntaxError, "Invalid object expression pattern as it has the get function short-hand instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("({set foo(a) {}} = {});"); }, SyntaxError, "Invalid object expression pattern as it has the set function short-hand instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("({get ['foo']() {}} = {});"); }, SyntaxError, "Invalid object expression pattern as it has the get function name as computed property instead of binding identifier", "Invalid destructuring assignment target");
      assert.throws(function () { eval("({set ['foo'](a) {}} = {});"); }, SyntaxError, "Invalid object expression pattern as it has the set function name as computed property instead of binding identifier", "Invalid destructuring assignment target");

      assert.throws(function () { eval("for(var [z] = function ([a]) { } in []) {}"); }, SyntaxError, "Initializer as function expression is not valid syntax", "for-in loop head declarations cannot have an initializer");
    }
  },
  {
    name: "Object destructuring with `get` and `set` identifiers",
    body: function () {
        var { get } = { get: 1 };
        let { set } = { set: 2 };
        assert.areEqual(1, get, "`get` is a valid object destructuring name");
        assert.areEqual(2, set, "`set` is a valid object destructuring name");

        assert.throws(function () { eval("var { get foo() { } } = { get: 1 };"); }, SyntaxError, "getter accessor is not a valid object destructuring name", "Invalid destructuring assignment target");
        assert.throws(function () { eval("var { set bar(x) { } } = { set: 2 };"); }, SyntaxError, "setter accessor is not a valid object destructuring name", "Invalid destructuring assignment target");

        const { get: x } = { get: 3 };
        var { set: y } = { set: 4 };
        assert.areEqual(3, x, "`get` is a valid object destructuring name mapping");
        assert.areEqual(4, y, "`set` is a valid object destructuring name mapping");
    }
  },
  {
    name: "Object destructuring with shorthand initializer",
    body: function () {
        assert.doesNotThrow(function () { eval("({x = 1} = {});"); }, "Object pattern has shorthand with initializer is a valid syntax");
        assert.doesNotThrow(function () { eval("({x, y = 1, z = 2} = {});"); }, "Object pattern has multiple shorthands with initializer is a valid syntax");
        assert.throws(function () { eval("var a = 1; ({x, y = 1, z = 2} = {a = 2});"); }, SyntaxError,"Initializer is allowed on shorthand of object pattern but not on object literal", "Expected ':'");
        assert.throws(function () { eval("var a = 1; ({x, y = {a = 1}} = {});"); }, SyntaxError,"Object literal is within object pattern but has shorthand initializer is not valid syntax", "Expected ':'");
        assert.doesNotThrow(function () { eval("var a = 1; ({x = {a = 1} = {}} = {});"); }, "Chained object patterns have shorthand initializers is a valid syntax");
        assert.doesNotThrow(function () { eval("var a = 1; var {x = {a = 1} = {}} = {};"); }, "Chained object declaration pattern have shorthand initializers is a valid syntax");
        assert.doesNotThrow(function () { eval("[{x : [{y:{z = 1}}] }] = [{x:[{y:{}}]}];"); }, "Mixed nesting pattern has shorthand initializer is a valid syntax");
        assert.doesNotThrow(function () { eval("[{x : [{y:{z = 1}, z1 = 2}] }, {x2 = 3}, {x3 : {y3:[{z3 = 4}]}} ] = [{x:[{y:{}}]}, {}, {x3:{y3:[{}]}}];"); }, 
                                            "Mixed object patterns both on nested and on same level have initializers on shorthand and is a valid syntax");
        assert.doesNotThrow(function () { eval("var [{x : [{y:{z = 1}, z1 = 2}] }, {x2 = 3}, {x3 : {y3:[{z3 = 4}]}} ] = [{x:[{y:{}}]}, {}, {x3:{y3:[{}]}}];"); }, 
                                            "Declaration - mixed object patterns both on nested and on same level have initializers on shorthand and is a valid syntax");
        assert.doesNotThrow(function () { eval("[...{x = 1}] = [{}]"); }, "Object pattern following rest has shorthand initializer is a valid syntax");
        {
            let a1 = 1;
            ({x:{a1 = 2}} = {x:{}});
            assert.areEqual(a1, 2);
        }
        
        assert.throws(function () { eval("var a = 1; switch(true) {  case {a = 1} : break; };"); }, SyntaxError, "Object literal on case has initializer is not valid syntax", "Expected ':'");
       
    }
  },
  {
    name: "Object destructuring basic functionality",
    body: function () {
        {
           var x3, x4;
           let {x:x1} = {x:20};
           assert.areEqual(x1, 20,  "Object declaration pattern should match the pattern and initializes that variable correctly");
           let {x2} = {x2:20};
           assert.areEqual(x2, 20,  "Object declaration pattern (shorthand) should match the pattern and initializes that variable correctly");

           ({x:x3} = {x:20});
           assert.areEqual(x3, 20,  "Object expression pattern should match the pattern and initializes that variable correctly");

           ({x4} = {x4:20});
           assert.areEqual(x4, 20,  "Object expression pattern (shorthand) should match the pattern and initializes that variable correctly even under a paren");
        }

        {
           let {x:x1} = {};
           assert.areEqual(x1, undefined,  "Object declaration pattern find no match pattern on rhs and initialize to undefined");

           let {x2} = {zee:20};
           assert.areEqual(x2, undefined,  "Object declaration pattern does not match pattern on rhs and initialize to undefined");

           let x3, x4;
           ({x:x3} = {});
           assert.areEqual(x3, undefined,  "Object expression pattern find no match pattern on rhs and initialize to undefined");

           ({x4} = {foobar:20});
           assert.areEqual(x4, undefined,  "Object expression pattern does not match pattern on rhs and initialize to undefined");
        }

        {
           let {x:x1, y: y1, z: z1} = {x:11, y:22, z:33, foo:44};
           assert.areEqual(x1, 11,  "Object declaration pattern with multiple members should match the first member correctly");
           assert.areEqual(y1, 22,  "Object declaration pattern with multiple members should match the second member correctly");
           assert.areEqual(z1, 33,  "Object declaration pattern with multiple members should match the third member correctly");

           let x2, y2, z2;
           ({x:x2, y: y2, z: z2} = {x:11, bar:44, y:22, z:33});

           assert.areEqual(x2, 11,  "Object expression pattern with multiple members should match the first member correctly");
           assert.areEqual(y2, 22,  "Object expression pattern with multiple members should match the second member correctly");
           assert.areEqual(z2, 33,  "Object expression pattern with multiple members should match the third member correctly");

        }

        {
           var y1, x1;
           var z = {x:x1} = {y:y1} = {x:10, y:20};
           assert.areEqual(x1, 10,  "Object declaration pattern with chained assignments should match first member to rhs correctly");
           assert.areEqual(y1, 20,  "Object expression pattern with chained assignments should match second member to rhs correctly");
        }
    }
  },
  {
    name: "Object destructuring functionality with initializer",
    body : function () {

           let {x:a = 1} = {x:undefined};
           assert.areEqual(a, 1,  "Object declaration pattern should match the pattern but it is evaluated to undefined so assign default correctly");

           let {x:a1 = 1} = {x:null};
           assert.areEqual(a1, null,  "Object declaration pattern should match the pattern and assigned null correctly");


           let {x:x1 = 1, y:y1 = 2, z: z1 = 3} = {};
           assert.areEqual(x1, 1,  "Object declaration pattern - first member initialized with default when no match found on rhs");
           assert.areEqual(y1, 2,  "Object declaration pattern - second member initialized with default when no match found on rhs");
           assert.areEqual(z1, 3,  "Object declaration pattern - third member initialized with default when no match found on rhs");

           let x2, y2, z2;
           ({x:x2 = 1, y:y2 = 2, z:z2 = 3} = {z:11});

           assert.areEqual(x2, 1, "Object expression pattern - first member initialized with default when no match found on rhs");
           assert.areEqual(y2, 2, "Object expression pattern - second member initialized with default when no match found on rhs");
           assert.areEqual(z2, 11, "Object expression pattern - third member has pattern match on rhs and should have assigned correctly");

           let { x: { y:z } = { y:21 } } = {};
           assert.areEqual(z, 21,  "Object declaration pattern has default on nested");


           let {
                x:{
                    y:{
                        z:{
                            k:k2 = 31
                          } = { k:21 }
                      } = { z:{ k:20 } }
                  } = { y: { z:{} } }
              } = { x:{ y:{ z:{} } } };
           assert.areEqual(k2, 31,  "Object declaration pattern has defaults on different level and got the inner most default");

           ({
               x:{
                   y:{
                       z:{
                           k:k2 = 31
                         } = {k:21}
                     } = {z:{k:20}}
                  } = {y:{z:{}}}
           } = {x:{y:{z:undefined}}});

           assert.areEqual(k2, 21,"Object expression pattern has default on different level but got the rhs");
        }
    },
    {
        name: "Object destructuring functionality with non-name pattern",
        body : function () {
           let {1:x1, 0:y1} = [11, 22];
           let {0:x2} = {"0":33};
           let {function:x3} = {function:44};

           assert.areEqual(x1, 22,  "Object declaration pattern should match the second index on rhs array");
           assert.areEqual(y1, 11,  "Object declaration pattern should match the first index on rhs array");
           assert.areEqual(x2, 33,  "Object declaration pattern should match '0' on rhs");
           assert.areEqual(x3, 44,  "Object declaration pattern should match the name even though it is a keyword");
        }
    },

    {
        name: "Object destructuring functionality with computed property",
        body : function () {
            {
               let key = 'x', x2;
               let {[key]:x1} = {x:20};
               assert.areEqual(x1, 20,  "Object declaration pattern should match the computed property name as a pattern");

               ({[key]:x2} = {x:20});
               assert.areEqual(x2, 20,  "Object expression pattern should match the computed property name as a pattern");

               ({[`abc${"def"}`]:x2} = {abcdef:30});
               assert.areEqual(x2, 30,  "Object expression pattern should match the the complex computed property name as a pattern");
            }

            {
                let [i,j] = [0,0];
                function getName() {
                    if (i++ == 0) return 'x';
                    else return 'y'
                }
                function getData() {
                    assert.areEqual(i, 0,  "RHS object should be initialized before");
                    if (j++ == 0) return 'this is x';
                    else return 'this is y';
                }
                let  {[getName()]:x1, [getName()]:y1} = {x:getData(), y:getData()};

                assert.areEqual(x1, 'this is x',  "Object declaration pattern depicting initializing order should match first pattern evaluated on runtime");
                assert.areEqual(y1, 'this is y',  "Object declaration pattern depicting initializing order should match second pattern evaluated on runtime");
            }
        }
    },

    {
        name: "Object destructuring functionality with property reference",
        body : function () {
            let a    = {};
            ({x:a.x} = {x:10});
            assert.areEqual(10, a.x, "Object expression pattern should assign value on property reference on object correctly");

            ({x:a['x']} = {x:20});
            assert.areEqual(20, a["x"], "Object expression pattern should assign value on property reference as index on object correctly");

            var obj = { x: 10, y: 20 };
            function foo() { return obj };

            ({x:foo().x, y:foo().y} = {x:20, y:30});
            assert.areEqual(20, obj.x,  "Object expression pattern should assign value on first property reference on a call expression correctly");
            assert.areEqual(30, obj.y,  "Object expression pattern should assign value on second property reference on a call expression correctly");

            (((((({x:foo().x, y:foo().y} = {x:201, y:301}))))));
            assert.areEqual(201, obj.x,  "Object expression pattern (under deep parens) should assign value on first property reference on a call expression correctly");
            assert.areEqual(301, obj.y, "Object expression pattern (under deep parens) should assign value on second property reference on a call expression correctly");

        }
    },
    {
       name: "Destructing functionality - rest element as pattern",
       body : function () {
           let [...[a]] = [1, 2, 3];
           assert.areEqual(a, 1, "Having rest element as array pattern and the identifier is initialized with first value");

           let [...{1:x1}] = [1, 2, 3];
           assert.areEqual(x1, 2, "Having rest element as object pattern and the identifier is initialized with second value");

           let [...[,...[[x2]]]] = [[1, 2], [3, 4], 5];
           assert.areEqual(x2, 3, "Rest element nesting another rest element and initialized with correct value");
      }
    },
    {
        name: "Object destructuring functionality with mixed array and object pattern",
        body : function () {

            let {first:f1, second : [,{y}]} = {first:'first', second:[{y:20, z:30}, {y:21, z:31}, {y:22, z:32}]};
            assert.areEqual(f1, 'first', "Destructing declaration pattern should match first pattern on rhs");
            assert.areEqual(y, 21, "Destructing declaration pattern should match second but nested array pattern on rhs");

            let metadata = {
                title: "Foobar",
                copies: [
                   {
                    locale: "en",
                    title: "first"
                   },
                   {
                    locale: "de",
                    title: "second"
                   },
                   {
                    locale: "ps",
                    title: "third"
                   },
                ],
                url: "http://foobar.com"
            };

            let { title: englishTitle, copies: [{locale : myLocale}] } = metadata;
            assert.areEqual(englishTitle,   'Foobar',    "Destructing declaration pattern should match first pattern on rhs");
            assert.areEqual(myLocale,   'en',    "Destructing declaration pattern should match second pattern on rhs");

            ({ copies: [,{locale : myLocale}] } = metadata);
            assert.areEqual(myLocale,   'de',    "Destructing expression pattern should skip first array item and match second item on rhs");

            let [{x}, {z}] = [{x:1, z:20}, {x:2, z:30}, {x:3,z:40}];
            assert.areEqual(x,   1,    "Object under array declaration pattern should match the first pattern");
            assert.areEqual(z,   30,    "Object under array declaration pattern should match the second pattern");

            [{x:x}, , {z:z}] = [{x:11, z:201}, {x:21, z:301}, {x:31,z:401}];
            assert.areEqual(x,   11,    "Object under array expression pattern should match the first pattern");
            assert.areEqual(z,   401,    "Object under array expression pattern should match the third pattern");
        }
    },
    {
        name: "Object destructuring functionality with for/while",
        body : function () {
            let i = 0, data = [20, 30];
            for ( let {x:item} of [{x:20}, {x:30}]) {
                assert.areEqual(item,   data[i++],    "Object declaration pattern under for..of should match pattern correctly");
            }

            function data2() {
                return {x:[{y:[20]}, {y:[30]}]};
            }

            i = 0;
            for ({y:[item]} of data2().x) {
                assert.areEqual(item,   data[i++],    "Object expression pattern under for..of should match pattern correctly");
            }

            i = 0; data = [10, 12, 14, 16, 18];
            for (let {x, y} = {x:10, y:20}; x<y; {x:x} = {x:x+2}) {
                assert.areEqual(x,   data[i++],    "Object declaration pattern under native..for should match pattern correctly");
            }

            let y2 = 20;
            i = 0; data = [18, 16, 14, 12, 10];
            while ({y:y2} = {y:y2-2}) {
                assert.areEqual(y2,   data[i++],    "Object expression pattern under while should match pattern correctly");
                if (y2 == 10) {
                    break;
                }
            }
        }
    }
];

testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
