|
|
@@ -0,0 +1,339 @@
|
|
|
+//-------------------------------------------------------------------------------------------------------
|
|
|
+// Copyright (C) Microsoft. All rights reserved.
|
|
|
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
|
|
+//-------------------------------------------------------------------------------------------------------
|
|
|
+
|
|
|
+// Object Spread unit tests
|
|
|
+
|
|
|
+if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch
|
|
|
+ this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
|
|
|
+}
|
|
|
+
|
|
|
+function verifyPropertyDesc(obj, prop, desc, propName) {
|
|
|
+ var actualDesc = Object.getOwnPropertyDescriptor(obj, prop);
|
|
|
+ if (typeof propName === "undefined") { propName = prop; }
|
|
|
+ assert.areEqual(desc.configurable, actualDesc.configurable, propName+"'s attribute: configurable");
|
|
|
+ assert.areEqual(desc.enumerable, actualDesc.enumerable, propName+"'s attribute: enumerable");
|
|
|
+ assert.areEqual(desc.writable, actualDesc.writable, propName+"'s attribute: writable");
|
|
|
+}
|
|
|
+
|
|
|
+var a = {i: 1, j: 2};
|
|
|
+var b = {x: 3, y: 4, z: 5};
|
|
|
+var c = {foo: 6};
|
|
|
+
|
|
|
+var tests = [
|
|
|
+ {
|
|
|
+ name: "Test shallow cloning",
|
|
|
+ body: function() {
|
|
|
+ let aClone = {...a};
|
|
|
+ assert.areEqual(1, aClone.i);
|
|
|
+ assert.areEqual(2, aClone.j);
|
|
|
+ assert.areEqual(2, Object.keys(aClone).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Spread Object in parens",
|
|
|
+ body: function() {
|
|
|
+ let aClone = {...(a)};
|
|
|
+ assert.areEqual(1, aClone.i);
|
|
|
+ assert.areEqual(2, aClone.j);
|
|
|
+ assert.areEqual(2, Object.keys(aClone).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test merging 2 objects",
|
|
|
+ body: function() {
|
|
|
+ let merged = {...a, ...b};
|
|
|
+ assert.areEqual(1, merged.i);
|
|
|
+ assert.areEqual(2, merged.j);
|
|
|
+ assert.areEqual(3, merged.x);
|
|
|
+ assert.areEqual(4, merged.y);
|
|
|
+ assert.areEqual(5, merged.z);
|
|
|
+ assert.areEqual(5, Object.keys(merged).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test merging default properties with another object",
|
|
|
+ body: function() {
|
|
|
+ let merged = {i: 1, ...c};
|
|
|
+ assert.areEqual(1, merged.i);
|
|
|
+ assert.areEqual(6, merged.foo);
|
|
|
+ assert.areEqual(2, Object.keys(merged).length);
|
|
|
+ assert.areEqual(1, Object.keys(c).length);
|
|
|
+
|
|
|
+ // Order should not matter in this case
|
|
|
+ merged = {...c, i: 1};
|
|
|
+ assert.areEqual(1, merged.i);
|
|
|
+ assert.areEqual(6, merged.foo);
|
|
|
+ assert.areEqual(2, Object.keys(merged).length);
|
|
|
+ assert.areEqual(1, Object.keys(c).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test overrides",
|
|
|
+ body: function() {
|
|
|
+ let over = {i: 10, j: 11, ...a};
|
|
|
+ assert.areEqual(1, over.i);
|
|
|
+ assert.areEqual(2, over.j);
|
|
|
+ assert.areEqual(2, Object.keys(over).length);
|
|
|
+
|
|
|
+ over = {...a, i: 10, j: 11};
|
|
|
+ assert.areEqual(10, over.i);
|
|
|
+ assert.areEqual(11, over.j);
|
|
|
+ assert.areEqual(2, Object.keys(over).length);
|
|
|
+
|
|
|
+ over = {...a, ...{i: 10, j: 11}};
|
|
|
+ assert.areEqual(10, over.i);
|
|
|
+ assert.areEqual(11, over.j);
|
|
|
+ assert.areEqual(2, Object.keys(over).length);
|
|
|
+
|
|
|
+ let i = 10, j = 11;
|
|
|
+ over = {...a, i, j};
|
|
|
+ assert.areEqual(10, over.i);
|
|
|
+ assert.areEqual(11, over.j);
|
|
|
+ assert.areEqual(2, Object.keys(over).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Getters in Object Literal should not be evaluated",
|
|
|
+ body: function() {
|
|
|
+ let getterExecuted = false;
|
|
|
+ let objWithGetter = {get i() {getterExecuted = true;}, ...c};
|
|
|
+ assert.areEqual(6, objWithGetter.foo);
|
|
|
+ assert.isFalse(getterExecuted);
|
|
|
+ assert.areEqual(2, Object.keys(objWithGetter).length);
|
|
|
+ assert.isTrue(objWithGetter.hasOwnProperty("i"));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Getters in Spread Object should be evaluated",
|
|
|
+ body: function() {
|
|
|
+ let getterExecuted = false;
|
|
|
+ let obj = {i: 1, ...{get j() {getterExecuted = true; return 2;}}};
|
|
|
+ assert.areEqual(1, obj.i);
|
|
|
+ assert.isTrue(getterExecuted);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Spread Object modifying itself",
|
|
|
+ body: function() {
|
|
|
+ let val = 1;
|
|
|
+ let source = {get i() {val++; return 1;}, get j() {return val;}};
|
|
|
+ let obj = {...source};
|
|
|
+ assert.areEqual(1, obj.i);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Spread Object modified by other Spread Object",
|
|
|
+ body: function() {
|
|
|
+ let a = {i: 1};
|
|
|
+ let b = {get j() {a.i = 3; return 2;}};
|
|
|
+ let obj = {...b, ...a};
|
|
|
+ assert.areEqual(3, obj.i);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test multiple merges of same object",
|
|
|
+ body: function() {
|
|
|
+ let getterExecutions = 0;
|
|
|
+ let objWithGetter = {get i() {getterExecutions++; return 1;}};
|
|
|
+ let merged = {a: 2, ...objWithGetter, b: 3, ...objWithGetter};
|
|
|
+ assert.areEqual(2, merged.a);
|
|
|
+ assert.areEqual(3, merged.b);
|
|
|
+ assert.areEqual(1, merged.i);
|
|
|
+ assert.areEqual(3, Object.keys(merged).length);
|
|
|
+ assert.areEqual(2, getterExecutions, "Getters should be executed twice, once for each `...`");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Setters should not be called in Object Literal",
|
|
|
+ body: function() {
|
|
|
+ let setterExecuted = false;
|
|
|
+ let objWithSetter = {set foo(v) {setterExecuted = true;}, ...c};
|
|
|
+ assert.areEqual(6, objWithSetter.foo);
|
|
|
+ assert.areEqual(1, Object.keys(objWithSetter).length);
|
|
|
+ assert.isFalse(setterExecuted);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Null and Undefined should be ignored",
|
|
|
+ body: function() {
|
|
|
+ let empty = {...null};
|
|
|
+ assert.areEqual(0, Object.keys(empty).length);
|
|
|
+
|
|
|
+ empty = {...undefined};
|
|
|
+ assert.areEqual(0, Object.keys(empty).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test nesting",
|
|
|
+ body: function() {
|
|
|
+ let base = {name: "foo", prev: {}, num: 5};
|
|
|
+ let derived = {...base, name: "bar", prev: {...base}};
|
|
|
+ assert.areEqual("foo", base.name);
|
|
|
+ assert.areEqual({}, base.prev);
|
|
|
+ assert.areEqual(5, base.num);
|
|
|
+ assert.areEqual(3, Object.keys(base).length);
|
|
|
+ assert.areEqual("bar", derived.name);
|
|
|
+ assert.areEqual("foo", derived.prev.name);
|
|
|
+ assert.areEqual({}, derived.prev.prev);
|
|
|
+ assert.areEqual(5, derived.prev.num);
|
|
|
+ assert.areEqual(5, derived.num);
|
|
|
+ assert.areEqual(3, Object.keys(derived).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Spread with computed property names in Object Literal",
|
|
|
+ body: function() {
|
|
|
+ let obj = {[5]: 5, ["bar"]: 2, ...c};
|
|
|
+ assert.areEqual(5, obj[5]);
|
|
|
+ assert.areEqual(2, obj.bar);
|
|
|
+ assert.areEqual(6, obj.foo);
|
|
|
+ assert.areEqual(3, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Spread with functions in Object Literal",
|
|
|
+ body: function() {
|
|
|
+ let obj = {func() {return true;}, ...c};
|
|
|
+ assert.areEqual(6, obj.foo);
|
|
|
+ assert.isTrue(obj.hasOwnProperty("func"));
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Property Descriptors from Spread should be default",
|
|
|
+ body: function() {
|
|
|
+ let obj = {...c};
|
|
|
+ let defaultDesc = {
|
|
|
+ configurable: true,
|
|
|
+ enumerable: true,
|
|
|
+ writable: true
|
|
|
+ };
|
|
|
+ verifyPropertyDesc(obj, "foo", defaultDesc);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Copy only own properties",
|
|
|
+ body: function() {
|
|
|
+ let parent = {i: 1, j: 2};
|
|
|
+ let child = Object.create(parent);
|
|
|
+ child.i = 3;
|
|
|
+ let obj = {...child};
|
|
|
+
|
|
|
+ assert.areEqual(3, child.i);
|
|
|
+ assert.areEqual(2, child.j);
|
|
|
+ assert.areEqual(3, obj.i);
|
|
|
+ assert.isFalse(obj.hasOwnProperty("j"));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Spread includes symbols in properties",
|
|
|
+ body: function() {
|
|
|
+ let sym = Symbol("foo");
|
|
|
+ let a = {};
|
|
|
+ a[sym] = 1;
|
|
|
+ let obj = {...a};
|
|
|
+ assert.areEqual(1, obj[sym], "property with Symbol property name identifier should be copied over");
|
|
|
+ assert.areEqual(1, Object.getOwnPropertySymbols(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Spread after assignment",
|
|
|
+ body: function() {
|
|
|
+ let temp = {};
|
|
|
+ let obj = {...temp=a};
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ assert.areEqual(1, obj.i);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+
|
|
|
+ obj = {...temp=1};
|
|
|
+ assert.areEqual(0, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Object Spread interacting with Array Spread",
|
|
|
+ body: function() {
|
|
|
+ let arr = [1, 2];
|
|
|
+ let obj = {...[...arr, 3]};
|
|
|
+ assert.areEqual(3, Object.keys(obj).length);
|
|
|
+ assert.areEqual(1, obj[0]);
|
|
|
+ assert.areEqual(2, obj[1]);
|
|
|
+ assert.areEqual(3, obj[2]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Object Spread interacting with Numbers",
|
|
|
+ body: function() {
|
|
|
+ let obj = {...1}
|
|
|
+ assert.areEqual(0, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Object Spread interacting with Functions",
|
|
|
+ body: function() {
|
|
|
+ let obj = {...function i() {return 1;}}
|
|
|
+ assert.areEqual(0, Object.keys(obj).length);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Object Spread interacting with Strings",
|
|
|
+ body: function() {
|
|
|
+ let obj = {..."edge"};
|
|
|
+ assert.areEqual(4, Object.keys(obj).length);
|
|
|
+ assert.areEqual("e", obj[0]);
|
|
|
+ assert.areEqual("d", obj[1]);
|
|
|
+ assert.areEqual("g", obj[2]);
|
|
|
+ assert.areEqual("e", obj[3]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Proxy Object",
|
|
|
+ body: function() {
|
|
|
+ let proxy = new Proxy({i: 1, j: 2}, {});
|
|
|
+ let obj = {...proxy};
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ assert.areEqual(1, obj.i);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Proxy Object with custom getter",
|
|
|
+ body: function() {
|
|
|
+ let handler = {get: function(obj, prop) {return obj[prop];}};
|
|
|
+ let proxy = new Proxy({i: 1, j: 2}, handler);
|
|
|
+ let obj = {...proxy};
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ assert.areEqual(1, obj.i);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Test Proxy Object with custom getter and setter",
|
|
|
+ body: function() {
|
|
|
+ let setterCalled = false;
|
|
|
+ let handler = {
|
|
|
+ get: function(obj, prop) {
|
|
|
+ return obj[prop];
|
|
|
+ },
|
|
|
+ set: function(obj, prop, value) {
|
|
|
+ setterCalled = true;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let proxy = new Proxy({i: 1, j: 2}, handler);
|
|
|
+ let obj = {...proxy};
|
|
|
+ assert.areEqual(2, Object.keys(obj).length);
|
|
|
+ assert.areEqual(1, obj.i);
|
|
|
+ assert.areEqual(2, obj.j);
|
|
|
+ assert.isFalse(setterCalled);
|
|
|
+ }
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
|