destructuring_obj.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  6. var tests = [
  7. {
  8. name: "Basic object destructuring syntax",
  9. body: function () {
  10. assert.doesNotThrow(function () { eval("var {} = {};"); }, "var object declaration pattern with no identifier is valid syntax");
  11. assert.doesNotThrow(function () { eval("let {} = {};"); }, "let object declaration pattern with no identifier is valid syntax");
  12. assert.doesNotThrow(function () { eval("const {} = {};"); }, "const object declaration pattern with no identifier is valid syntax");
  13. assert.doesNotThrow(function () { eval("({} = {});"); }, "Object pattern as an expression with no identifier is valid syntax");
  14. assert.doesNotThrow(function () { eval("var {x:y} = {};"); }, "var object declaration pattern with a single member is valid syntax");
  15. assert.doesNotThrow(function () { eval("let {x:y} = {};"); }, "let object declaration pattern with a single member is valid syntax");
  16. assert.doesNotThrow(function () { eval("const {x:y} = {};"); }, "const object declaration pattern with a single member is valid syntax");
  17. assert.doesNotThrow(function () { eval("({x:y} = {});"); }, "Object pattern as an expression with a single member is valid syntax");
  18. assert.doesNotThrow(function () { eval("var {x} = {};"); }, "var object declaration pattern with a single member as shorthand is valid syntax");
  19. assert.doesNotThrow(function () { eval("({x:y} = {});"); }, "Object pattern as an expression with a single member as shorthand is valid syntax");
  20. assert.doesNotThrow(function () { eval("var {x} = {}, {y} = {};"); }, "Multiple object pattern in a single var declaration is valid syntax");
  21. }
  22. },
  23. {
  24. name: "Basic object destructuring invalid syntax",
  25. body: function () {
  26. assert.throws(function () { eval("var {};"); }, SyntaxError, "var empty object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
  27. assert.throws(function () { eval("let {};"); }, SyntaxError, "let empty object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
  28. assert.throws(function () { eval("const {};"); }, SyntaxError, "const empty object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
  29. assert.throws(function () { eval("var {a};"); }, SyntaxError, "var object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
  30. assert.throws(function () { eval("let {a};"); }, SyntaxError, "let object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
  31. assert.throws(function () { eval("const {a};"); }, SyntaxError, "const object declaration pattern without an initializer is not valid syntax", "Destructuring declarations must have an initializer");
  32. assert.throws(function () { eval("var {,} = {}"); }, SyntaxError, "Object declaration pattern without an identifier is not valid syntax", "Expected identifier, string or number");
  33. assert.throws(function () { eval("({,} = {});"); }, SyntaxError, "Object expression pattern without an identifier is not valid syntax", "Expected identifier, string or number");
  34. assert.throws(function () { eval("var {x:y--} = {};"); }, SyntaxError, "Object declaration pattern with an operator -- is not valid syntax", "Unexpected operator in destructuring expression");
  35. assert.throws(function () { eval("var {x:y+1} = {};"); }, SyntaxError, "Object declaration pattern with an operator + is not valid syntax", "Unexpected operator in destructuring expression");
  36. assert.throws(function () { eval("var y; ({x:y--} = {});"); }, SyntaxError, "Object expression pattern with an operator -- is not valid syntax", "Unexpected operator in destructuring expression");
  37. 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");
  38. }
  39. },
  40. {
  41. name: "Object destructuring syntax with initializer",
  42. body: function () {
  43. assert.doesNotThrow(function () { eval("var {x:x = 20} = {};"); }, "var object declaration pattern with default is valid syntax");
  44. assert.doesNotThrow(function () { eval("let {x:x = 20} = {};"); }, "let object declaration pattern with default is valid syntax");
  45. assert.doesNotThrow(function () { eval("const {x:x = 20} = {};"); }, "const object declaration pattern with default is valid syntax");
  46. assert.doesNotThrow(function () { eval("var x; ({x:x = 20} = {});"); }, "Object declaration pattern with default is valid syntax");
  47. assert.doesNotThrow(function () { eval("var {x, x1:y = 20} = {};"); }, "Object declaration pattern with default other than first is valid syntax");
  48. assert.doesNotThrow(function () { eval("var {x:z = 1, x1:y = 20} = {};"); }, "Object declaration pattern with defaults on more than one is valid syntax");
  49. assert.doesNotThrow(function () { eval("var x, y; ({x, x1:y = 20} = {});"); }, "Object expression pattern with default other than first is valid syntax");
  50. 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");
  51. }
  52. },
  53. {
  54. name: "Object destructuring syntax with identifier reference",
  55. body: function () {
  56. 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");
  57. 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");
  58. 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");
  59. assert.doesNotThrow(function () { eval("var a = {}; ({x:a.x} = {});"); }, "Object expresion pattern with a property reference is valid syntax");
  60. assert.doesNotThrow(function () { eval("let a = {}; ({x:a['x']} = {});"); }, "Object expression pattern with a property reference as index is valid syntax");
  61. assert.doesNotThrow(function () { eval("function foo() { return {}; }; ({x:foo().x} = {});"); }, "Object declaration pattern with property reference on call is valid syntax");
  62. assert.doesNotThrow(function () { eval("function foo() { return {}; }; ({x:foo()['x']} = {});"); }, "Object declaration pattern with property reference as index on call is valid syntax");
  63. 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");
  64. 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");
  65. 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");
  66. assert.doesNotThrow(function () { eval("class foo { method() { ({x:super.x} = {}); } }"); }, "Object expression pattern with a property reference on super is valid syntax");
  67. 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");
  68. assert.doesNotThrow(function () { eval("var a = [1], i = 0; ({x:a[i++]} = {});"); }, "Object Destructuring pattern assignment operators inside an identifier reference is valid syntax");
  69. }
  70. },
  71. {
  72. name: "Object destructuring syntax with computed property name",
  73. body: function () {
  74. assert.doesNotThrow(function () { eval("var zee = 'x'; var {[zee]:x1} = {}"); }, "Object declaration pattern with computed property name is valid syntax");
  75. assert.doesNotThrow(function () { eval("var zee = 'x'; var x1; ({[zee]:x1} = {})"); }, "Object expression pattern with computed property name is valid syntax");
  76. assert.doesNotThrow(function () { eval("var zee = 'x'; var {[zee + 'foo']:x1} = {}"); }, "Object declaration pattern with computed property name with add operator is valid syntax");
  77. 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");
  78. }
  79. },
  80. {
  81. name: "Destructing syntax - having rest element as pattern",
  82. body: function () {
  83. assert.doesNotThrow(function () { eval("let [...[a]] = [[]];"); }, "Under declaration, having rest element as array pattern is valid syntax");
  84. assert.doesNotThrow(function () { eval("let a; [...[a]] = [[]];"); }, "Under expression, having rest element as array pattern is valid syntax");
  85. assert.doesNotThrow(function () { eval("let [...{a}] = [{}];"); }, "Under declaration, having rest element as object pattern is valid syntax");
  86. assert.doesNotThrow(function () { eval("let a; [...{a}] = [{}];"); }, "Under expression, having rest element as object pattern is valid syntax");
  87. assert.doesNotThrow(function () { eval("let a; [...[a = 1]] = [[]];"); }, "Under expression, having rest element as array pattern has initializer is valid syntax");
  88. assert.doesNotThrow(function () { eval("let a; [...{a:a = 1}] = [{}];"); }, "Under expression, having rest element as object pattern has initializer is valid syntax");
  89. assert.doesNotThrow(function () { eval("let obj = {x:1}; [...obj.x] = [10];"); }, "Rest element being property reference is valid syntax");
  90. assert.doesNotThrow(function () { eval("let obj = {x:1}; [...obj['x']] = [10];"); }, "Rest element being property reference as index is valid syntax");
  91. assert.doesNotThrow(function () { eval("function foo() { return {x:1}; }; [...foo().x] = [10];"); }, "Rest element being property reference on call expression is valid syntax");
  92. 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");
  93. assert.doesNotThrow(function () { eval("let [...[...[...a]]] = [[[]]];"); }, "Nesting rest element inside another rest element is valid syntax");
  94. 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");
  95. assert.throws(function () { eval("let a; [...1+a] = [{}];"); }, SyntaxError, "Under declaration, rest element has operator is not valid syntax", "Invalid destructuring assignment target");
  96. 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");
  97. 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");
  98. assert.throws(function () { eval("let [...[a] = []] = [[]];"); }, SyntaxError, "Under declaration - rest as array pattern cannot have initializer", "The rest parameter cannot have a default initializer.");
  99. assert.throws(function () { eval("let [...{x} = {}] = [{}];"); }, SyntaxError, "Under declaration - rest as object pattern cannot have initializer", "The rest parameter cannot have a default initializer.");
  100. 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.");
  101. 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.");
  102. }
  103. },
  104. {
  105. name: "Object destructuring syntax with repeated identifier",
  106. body: function () {
  107. assert.doesNotThrow(function () { eval("var {a:a, a:a} = {};"); }, "var declaration pattern with a repeated identifier is valid syntax");
  108. assert.throws(function () { eval("let {a:a, a:a} = {};"); }, SyntaxError, "let declaration pattern with a repeated identifier is not valid syntax", "Let/Const redeclaration");
  109. assert.throws(function () { eval("const {a:a, a:a} = {};"); }, SyntaxError, "const declaration pattern with a repeated identifier is not valid syntax", "Let/Const redeclaration");
  110. assert.throws(function () { eval("let {b, b} = {};"); }, SyntaxError, "let declaration pattern with a repeated identifier as shorthand is not valid syntax", "Let/Const redeclaration");
  111. assert.throws(function () { eval("const {b, b} = {};"); }, SyntaxError, "const declaration pattern with a repeated identifier as shorthand is not valid syntax", "Let/Const redeclaration");
  112. 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");
  113. 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");
  114. assert.doesNotThrow(function () { eval("let a; ({a:a, a:a} = {});"); }, "Object expression pattern with a repeated identifier is valid syntax");
  115. }
  116. },
  117. {
  118. name: "Object destructuring syntax on misc expressions",
  119. body: function () {
  120. assert.doesNotThrow(function () { eval("let a; ({a:((((a1))))} = {a:20})"); }, "Object expression pattern with parens is valid syntax");
  121. assert.doesNotThrow(function () { eval("let a, r1; ({a:a1 = r1} = {})"); }, "Object expression pattern with defaults as reference is valid syntax");
  122. assert.doesNotThrow(function () { eval("let a, r1; ({a:a1 = r1 = 44} = {})"); }, "Object expression pattern with chained assignments as defaults is valid syntax");
  123. 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", "Expected ')'");
  124. assert.doesNotThrow(function () { eval("var a; `${({a} = {})}`"); }, "Object expression pattern inside a string template is valid syntax");
  125. 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");
  126. 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");
  127. 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");
  128. 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");
  129. assert.doesNotThrow(function () { eval("var a; [a = class aClass {}] = []"); }, "Expression pattern has class as initializer is valid syntax");
  130. assert.doesNotThrow(function () { eval("var a; for ({x:x = class aClass {}} of []) {}"); }, "for.of's expression pattern has class as initializer is valid syntax");
  131. assert.doesNotThrow(function () { eval("var {x:[...y]} = {x:[1]}"); }, "rest element nesting under object pattern is valid syntax");
  132. 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");
  133. 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");
  134. 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");
  135. 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");
  136. 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");
  137. 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");
  138. 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");
  139. 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");
  140. 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");
  141. 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");
  142. 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");
  143. assert.throws(function () { eval("({a: {d = 1,c = 1}.c = 2} = {});"); }, SyntaxError, "Object expression pattern with dot node should not identify as object literal", "Expected ':'");
  144. assert.throws(function () { eval("({a: ({d = 1,c = 1}.c) = 2} = {});"); }, SyntaxError, "Object expression pattern with dot node under paren should not identify as object literal", "Destructuring expressions can only have identifier references");
  145. assert.doesNotThrow(function () { eval("({a: [b = 1, c = 2][1]} = {a:[]});"); }, "index node instead of array literal is a valid syntax");
  146. assert.doesNotThrow(function () { eval("({a: [b = 1, c = 2].b} = {a:[]});"); }, "dot node instead of array literal is a valid syntax");
  147. }
  148. },
  149. {
  150. name: "Object destructuring with `get` and `set` identifiers",
  151. body: function () {
  152. var { get } = { get: 1 };
  153. let { set } = { set: 2 };
  154. assert.areEqual(1, get, "`get` is a valid object destructuring name");
  155. assert.areEqual(2, set, "`set` is a valid object destructuring name");
  156. assert.throws(function () { eval("var { get foo() { } } = { get: 1 };"); }, SyntaxError, "getter accessor is not a valid object destructuring name", "Invalid destructuring assignment target");
  157. assert.throws(function () { eval("var { set bar(x) { } } = { set: 2 };"); }, SyntaxError, "setter accessor is not a valid object destructuring name", "Invalid destructuring assignment target");
  158. const { get: x } = { get: 3 };
  159. var { set: y } = { set: 4 };
  160. assert.areEqual(3, x, "`get` is a valid object destructuring name mapping");
  161. assert.areEqual(4, y, "`set` is a valid object destructuring name mapping");
  162. }
  163. },
  164. {
  165. name: "Object destructuring with shorthand initializer",
  166. body: function () {
  167. assert.doesNotThrow(function () { eval("({x = 1} = {});"); }, "Object pattern has shorthand with initializer is a valid syntax");
  168. assert.doesNotThrow(function () { eval("({x, y = 1, z = 2} = {});"); }, "Object pattern has multiple shorthands with initializer is a valid syntax");
  169. 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 ':'");
  170. 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 ':'");
  171. assert.doesNotThrow(function () { eval("var a = 1; ({x = {a = 1} = {}} = {});"); }, "Chained object patterns have shorthand initializers is a valid syntax");
  172. assert.doesNotThrow(function () { eval("var a = 1; var {x = {a = 1} = {}} = {};"); }, "Chained object declaration pattern have shorthand initializers is a valid syntax");
  173. assert.doesNotThrow(function () { eval("[{x : [{y:{z = 1}}] }] = [{x:[{y:{}}]}];"); }, "Mixed nesting pattern has shorthand initializer is a valid syntax");
  174. assert.doesNotThrow(function () { eval("[{x : [{y:{z = 1}, z1 = 2}] }, {x2 = 3}, {x3 : {y3:[{z3 = 4}]}} ] = [{x:[{y:{}}]}, {}, {x3:{y3:[{}]}}];"); },
  175. "Mixed object patterns both on nested and on same level have initializers on shorthand and is a valid syntax");
  176. assert.doesNotThrow(function () { eval("var [{x : [{y:{z = 1}, z1 = 2}] }, {x2 = 3}, {x3 : {y3:[{z3 = 4}]}} ] = [{x:[{y:{}}]}, {}, {x3:{y3:[{}]}}];"); },
  177. "Declaration - mixed object patterns both on nested and on same level have initializers on shorthand and is a valid syntax");
  178. assert.doesNotThrow(function () { eval("[...{x = 1}] = [{}]"); }, "Object pattern following rest has shorthand initializer is a valid syntax");
  179. {
  180. let a1 = 1;
  181. ({x:{a1 = 2}} = {x:{}});
  182. assert.areEqual(a1, 2);
  183. }
  184. 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 ':'");
  185. }
  186. },
  187. {
  188. name: "Object destructuring basic functionality",
  189. body: function () {
  190. {
  191. var x3, x4;
  192. let {x:x1} = {x:20};
  193. assert.areEqual(x1, 20, "Object declaration pattern should match the pattern and initializes that variable correctly");
  194. let {x2} = {x2:20};
  195. assert.areEqual(x2, 20, "Object declaration pattern (shorthand) should match the pattern and initializes that variable correctly");
  196. ({x:x3} = {x:20});
  197. assert.areEqual(x3, 20, "Object expression pattern should match the pattern and initializes that variable correctly");
  198. ({x4} = {x4:20});
  199. assert.areEqual(x4, 20, "Object expression pattern (shorthand) should match the pattern and initializes that variable correctly even under a paren");
  200. }
  201. {
  202. let {x:x1} = {};
  203. assert.areEqual(x1, undefined, "Object declaration pattern find no match pattern on rhs and initialize to undefined");
  204. let {x2} = {zee:20};
  205. assert.areEqual(x2, undefined, "Object declaration pattern does not match pattern on rhs and initialize to undefined");
  206. let x3, x4;
  207. ({x:x3} = {});
  208. assert.areEqual(x3, undefined, "Object expression pattern find no match pattern on rhs and initialize to undefined");
  209. ({x4} = {foobar:20});
  210. assert.areEqual(x4, undefined, "Object expression pattern does not match pattern on rhs and initialize to undefined");
  211. }
  212. {
  213. let {x:x1, y: y1, z: z1} = {x:11, y:22, z:33, foo:44};
  214. assert.areEqual(x1, 11, "Object declaration pattern with multiple members should match the first member correctly");
  215. assert.areEqual(y1, 22, "Object declaration pattern with multiple members should match the second member correctly");
  216. assert.areEqual(z1, 33, "Object declaration pattern with multiple members should match the third member correctly");
  217. let x2, y2, z2;
  218. ({x:x2, y: y2, z: z2} = {x:11, bar:44, y:22, z:33});
  219. assert.areEqual(x2, 11, "Object expression pattern with multiple members should match the first member correctly");
  220. assert.areEqual(y2, 22, "Object expression pattern with multiple members should match the second member correctly");
  221. assert.areEqual(z2, 33, "Object expression pattern with multiple members should match the third member correctly");
  222. }
  223. {
  224. var y1, x1;
  225. var z = {x:x1} = {y:y1} = {x:10, y:20};
  226. assert.areEqual(x1, 10, "Object declaration pattern with chained assignments should match first member to rhs correctly");
  227. assert.areEqual(y1, 20, "Object expression pattern with chained assignments should match second member to rhs correctly");
  228. }
  229. }
  230. },
  231. {
  232. name: "Object destructuring functionality with initializer",
  233. body : function () {
  234. let {x:a = 1} = {x:undefined};
  235. assert.areEqual(a, 1, "Object declaration pattern should match the pattern but it is evaluated to undefined so assign default correctly");
  236. let {x:a1 = 1} = {x:null};
  237. assert.areEqual(a1, null, "Object declaration pattern should match the pattern and assigned null correctly");
  238. let {x:x1 = 1, y:y1 = 2, z: z1 = 3} = {};
  239. assert.areEqual(x1, 1, "Object declaration pattern - first member initialized with default when no match found on rhs");
  240. assert.areEqual(y1, 2, "Object declaration pattern - second member initialized with default when no match found on rhs");
  241. assert.areEqual(z1, 3, "Object declaration pattern - third member initialized with default when no match found on rhs");
  242. let x2, y2, z2;
  243. ({x:x2 = 1, y:y2 = 2, z:z2 = 3} = {z:11});
  244. assert.areEqual(x2, 1, "Object expression pattern - first member initialized with default when no match found on rhs");
  245. assert.areEqual(y2, 2, "Object expression pattern - second member initialized with default when no match found on rhs");
  246. assert.areEqual(z2, 11, "Object expression pattern - third member has pattern match on rhs and should have assigned correctly");
  247. let { x: { y:z } = { y:21 } } = {};
  248. assert.areEqual(z, 21, "Object declaration pattern has default on nested");
  249. let {
  250. x:{
  251. y:{
  252. z:{
  253. k:k2 = 31
  254. } = { k:21 }
  255. } = { z:{ k:20 } }
  256. } = { y: { z:{} } }
  257. } = { x:{ y:{ z:{} } } };
  258. assert.areEqual(k2, 31, "Object declaration pattern has defaults on different level and got the inner most default");
  259. ({
  260. x:{
  261. y:{
  262. z:{
  263. k:k2 = 31
  264. } = {k:21}
  265. } = {z:{k:20}}
  266. } = {y:{z:{}}}
  267. } = {x:{y:{z:undefined}}});
  268. assert.areEqual(k2, 21,"Object expression pattern has default on different level but got the rhs");
  269. }
  270. },
  271. {
  272. name: "Object destructuring functionality with non-name pattern",
  273. body : function () {
  274. let {1:x1, 0:y1} = [11, 22];
  275. let {0:x2} = {"0":33};
  276. let {function:x3} = {function:44};
  277. assert.areEqual(x1, 22, "Object declaration pattern should match the second index on rhs array");
  278. assert.areEqual(y1, 11, "Object declaration pattern should match the first index on rhs array");
  279. assert.areEqual(x2, 33, "Object declaration pattern should match '0' on rhs");
  280. assert.areEqual(x3, 44, "Object declaration pattern should match the name even though it is a keyword");
  281. }
  282. },
  283. {
  284. name: "Object destructuring functionality with computed property",
  285. body : function () {
  286. {
  287. let key = 'x', x2;
  288. let {[key]:x1} = {x:20};
  289. assert.areEqual(x1, 20, "Object declaration pattern should match the computed property name as a pattern");
  290. ({[key]:x2} = {x:20});
  291. assert.areEqual(x2, 20, "Object expression pattern should match the computed property name as a pattern");
  292. ({[`abc${"def"}`]:x2} = {abcdef:30});
  293. assert.areEqual(x2, 30, "Object expression pattern should match the the complex computed property name as a pattern");
  294. }
  295. {
  296. let [i,j] = [0,0];
  297. function getName() {
  298. if (i++ == 0) return 'x';
  299. else return 'y'
  300. }
  301. function getData() {
  302. assert.areEqual(i, 0, "RHS object should be initialized before");
  303. if (j++ == 0) return 'this is x';
  304. else return 'this is y';
  305. }
  306. let {[getName()]:x1, [getName()]:y1} = {x:getData(), y:getData()};
  307. assert.areEqual(x1, 'this is x', "Object declaration pattern depicting initializing order should match first pattern evaluated on runtime");
  308. assert.areEqual(y1, 'this is y', "Object declaration pattern depicting initializing order should match second pattern evaluated on runtime");
  309. }
  310. }
  311. },
  312. {
  313. name: "Object destructuring functionality with property reference",
  314. body : function () {
  315. let a = {};
  316. ({x:a.x} = {x:10});
  317. assert.areEqual(10, a.x, "Object expression pattern should assign value on property reference on object correctly");
  318. ({x:a['x']} = {x:20});
  319. assert.areEqual(20, a["x"], "Object expression pattern should assign value on property reference as index on object correctly");
  320. var obj = { x: 10, y: 20 };
  321. function foo() { return obj };
  322. ({x:foo().x, y:foo().y} = {x:20, y:30});
  323. assert.areEqual(20, obj.x, "Object expression pattern should assign value on first property reference on a call expression correctly");
  324. assert.areEqual(30, obj.y, "Object expression pattern should assign value on second property reference on a call expression correctly");
  325. (((((({x:foo().x, y:foo().y} = {x:201, y:301}))))));
  326. assert.areEqual(201, obj.x, "Object expression pattern (under deep parens) should assign value on first property reference on a call expression correctly");
  327. assert.areEqual(301, obj.y, "Object expression pattern (under deep parens) should assign value on second property reference on a call expression correctly");
  328. }
  329. },
  330. {
  331. name: "Destructing functionality - rest element as pattern",
  332. body : function () {
  333. let [...[a]] = [1, 2, 3];
  334. assert.areEqual(a, 1, "Having rest element as array pattern and the identifier is initialized with first value");
  335. let [...{1:x1}] = [1, 2, 3];
  336. assert.areEqual(x1, 2, "Having rest element as object pattern and the identifier is initialized with second value");
  337. let [...[,...[[x2]]]] = [[1, 2], [3, 4], 5];
  338. assert.areEqual(x2, 3, "Rest element nesting another rest element and initialized with correct value");
  339. }
  340. },
  341. {
  342. name: "Object destructuring functionality with mixed array and object pattern",
  343. body : function () {
  344. let {first:f1, second : [,{y}]} = {first:'first', second:[{y:20, z:30}, {y:21, z:31}, {y:22, z:32}]};
  345. assert.areEqual(f1, 'first', "Destructing declaration pattern should match first pattern on rhs");
  346. assert.areEqual(y, 21, "Destructing declaration pattern should match second but nested array pattern on rhs");
  347. let metadata = {
  348. title: "Foobar",
  349. copies: [
  350. {
  351. locale: "en",
  352. title: "first"
  353. },
  354. {
  355. locale: "de",
  356. title: "second"
  357. },
  358. {
  359. locale: "ps",
  360. title: "third"
  361. },
  362. ],
  363. url: "http://foobar.com"
  364. };
  365. let { title: englishTitle, copies: [{locale : myLocale}] } = metadata;
  366. assert.areEqual(englishTitle, 'Foobar', "Destructing declaration pattern should match first pattern on rhs");
  367. assert.areEqual(myLocale, 'en', "Destructing declaration pattern should match second pattern on rhs");
  368. ({ copies: [,{locale : myLocale}] } = metadata);
  369. assert.areEqual(myLocale, 'de', "Destructing expression pattern should skip first array item and match second item on rhs");
  370. let [{x}, {z}] = [{x:1, z:20}, {x:2, z:30}, {x:3,z:40}];
  371. assert.areEqual(x, 1, "Object under array declaration pattern should match the first pattern");
  372. assert.areEqual(z, 30, "Object under array declaration pattern should match the second pattern");
  373. [{x:x}, , {z:z}] = [{x:11, z:201}, {x:21, z:301}, {x:31,z:401}];
  374. assert.areEqual(x, 11, "Object under array expression pattern should match the first pattern");
  375. assert.areEqual(z, 401, "Object under array expression pattern should match the third pattern");
  376. }
  377. },
  378. {
  379. name: "Object destructuring functionality with for/while",
  380. body : function () {
  381. let i = 0, data = [20, 30];
  382. for ( let {x:item} of [{x:20}, {x:30}]) {
  383. assert.areEqual(item, data[i++], "Object declaration pattern under for..of should match pattern correctly");
  384. }
  385. function data2() {
  386. return {x:[{y:[20]}, {y:[30]}]};
  387. }
  388. i = 0;
  389. for ({y:[item]} of data2().x) {
  390. assert.areEqual(item, data[i++], "Object expression pattern under for..of should match pattern correctly");
  391. }
  392. i = 0; data = [10, 12, 14, 16, 18];
  393. for (let {x, y} = {x:10, y:20}; x<y; {x:x} = {x:x+2}) {
  394. assert.areEqual(x, data[i++], "Object declaration pattern under native..for should match pattern correctly");
  395. }
  396. let y2 = 20;
  397. i = 0; data = [18, 16, 14, 12, 10];
  398. while ({y:y2} = {y:y2-2}) {
  399. assert.areEqual(y2, data[i++], "Object expression pattern under while should match pattern correctly");
  400. if (y2 == 10) {
  401. break;
  402. }
  403. }
  404. }
  405. }
  406. ];
  407. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });