|
|
@@ -0,0 +1,213 @@
|
|
|
+//-------------------------------------------------------------------------------------------------------
|
|
|
+// 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");
|
|
|
+
|
|
|
+function verifyProperties(obj, property, value)
|
|
|
+{
|
|
|
+ const descriptor = Object.getOwnPropertyDescriptor(obj, property);
|
|
|
+ assert.areEqual(value, obj[property], "Object.fromEntries should set correct valued");
|
|
|
+ assert.isTrue(descriptor.enumerable, "Object.fromEntries should create enumerable properties");
|
|
|
+ assert.isTrue(descriptor.configurable, "Object.fromEntries should create configurable properties");
|
|
|
+ assert.isTrue(descriptor.writable, "Object.fromEntries should create writable properties");
|
|
|
+ obj[property] = "other value";
|
|
|
+ assert.areEqual("other value", obj[property], "should actually be able to write to properties created by Object.fromEntries");
|
|
|
+ assert.doesNotThrow(()=>{"use strict"; delete obj[property];}, "deleting properties created by Object.fromEntries should not throw");
|
|
|
+ assert.isUndefined(obj[property], "deleting properties created by Object.fromEntries should succeed");
|
|
|
+}
|
|
|
+
|
|
|
+function verifyObject(expected, actual)
|
|
|
+{
|
|
|
+ for (let i in actual)
|
|
|
+ {
|
|
|
+ assert.isTrue(expected.hasOwnProperty(i), "Object.fromEntries shouldn't create unexpected properties");
|
|
|
+ }
|
|
|
+ for (let i in expected)
|
|
|
+ {
|
|
|
+ verifyProperties(actual, i, expected[i]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const tests = [
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries invalid parameters",
|
|
|
+ body : function () {
|
|
|
+ assert.throws(()=>{Object.fromEntries(null);}, TypeError, "Object.fromEntries throws when called with null parameter");
|
|
|
+ assert.throws(()=>{Object.fromEntries(undefined);}, TypeError, "Object.fromEntries throws when called with undefined parameter");
|
|
|
+ assert.throws(()=>{Object.fromEntries("something");}, TypeError, "Object.fromEntries throws when called with string literal parameter");
|
|
|
+ assert.throws(()=>{Object.fromEntries(456);}, TypeError, "Object.fromEntries throws when called with number literal parameter");
|
|
|
+ assert.throws(()=>{Object.fromEntries(Number());}, TypeError, "Object.fromEntries throws when called with Number Object parameter");
|
|
|
+ assert.doesNotThrow(()=>{Object.fromEntries(String());}, "Object.fromEntries does not throw when called with String Object parameter with length 0");
|
|
|
+ assert.throws(()=>{Object.fromEntries(String("anything"));}, TypeError, "Object.fromEntries throws when called with String Object parameter with length > 0");
|
|
|
+ assert.throws(()=>{Object.fromEntries({});}, TypeError, "Object.fromEntries throws when called with Object literal");
|
|
|
+ assert.throws(()=>{Object.fromEntries({a : "5", b : "10"});}, TypeError, "Object.fromEntries throws when called with Object literal");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries basic cases",
|
|
|
+ body : function () {
|
|
|
+ const obj1 = Object.fromEntries([["first", 50], ["second", 30], ["third", 60], ["fourth", 70]]);
|
|
|
+ verifyObject({first : 50, second : 30, third : 60, fourth : 70}, obj1);
|
|
|
+ const obj2 = Object.fromEntries([Object("a12234"),Object("b2kls"),Object("c3deg")]);
|
|
|
+ verifyObject({a : "1", b : "2", c : "3"}, obj2);
|
|
|
+ function testArguments()
|
|
|
+ {
|
|
|
+ verifyObject(expected, Object.fromEntries(arguments));
|
|
|
+ }
|
|
|
+ const expected = { abc : "one", bcd : "two", hsa : "three"};
|
|
|
+ testArguments(["abc", "one"], ["bcd", "two"], ["hsa", "three"]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries with array-like object",
|
|
|
+ body : function ()
|
|
|
+ {
|
|
|
+ const arrayLike = {0 : ["abc", "one"], 1 : ["bcd", "two"], 2 : ["hsa", "three"], length : 3, current : 0}
|
|
|
+ assert.throws(()=>{ Object.fromEntries(arrayLike); }, TypeError, "Object.fromEntries throws when parameter has no iterator");
|
|
|
+ arrayLike[Symbol.iterator] = function () {
|
|
|
+ const array = this;
|
|
|
+ return {
|
|
|
+ next : function () {
|
|
|
+ const value = array[String(array.current)];
|
|
|
+ ++array.current;
|
|
|
+ return {
|
|
|
+ value : value,
|
|
|
+ done : array.length < array.current
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
+ };
|
|
|
+ verifyObject({ abc : "one", bcd : "two", hsa : "three"}, Object.fromEntries(arrayLike));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries does not call setters",
|
|
|
+ body : function () {
|
|
|
+ let calledSet = false;
|
|
|
+ Object.defineProperty(Object.prototype, "prop", {
|
|
|
+ set : function () { calledSet = true; }
|
|
|
+ });
|
|
|
+ const obj = Object.fromEntries([["prop", 10]]);
|
|
|
+ verifyProperties(obj, "prop", 10);
|
|
|
+ assert.isFalse(calledSet, "Object.fromEntries should not call setters");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries iterates over generators",
|
|
|
+ body : function () {
|
|
|
+ function* gen1 ()
|
|
|
+ {
|
|
|
+ yield ["val1", 10];
|
|
|
+ yield ["val2", 50];
|
|
|
+ yield ["val3", 60, "other stuff"];
|
|
|
+ }
|
|
|
+ const obj = Object.fromEntries(gen1());
|
|
|
+ verifyObject({val1 : 10, val2 : 50, val3: 60}, obj);
|
|
|
+ let unreachable = false;
|
|
|
+ function* gen2 ()
|
|
|
+ {
|
|
|
+ yield ["val1", 10];
|
|
|
+ yield "val2";
|
|
|
+ unreachable = true;
|
|
|
+ yield ["val3", 60, "other stuff"];
|
|
|
+ }
|
|
|
+ assert.throws(()=>{Object.fromEntries(gen2())}, TypeError, "When generator provides invalid case Object.fromEntries should throw");
|
|
|
+ assert.isFalse(unreachable, "Object.fromEntries does not continue after invalid case provided");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries accesses properties in correct order from generator",
|
|
|
+ body : function () {
|
|
|
+ const accessedProps = [];
|
|
|
+ const handler = {
|
|
|
+ get : function (target, prop, receiver) {
|
|
|
+ accessedProps.push(prop + Reflect.get(target, prop));
|
|
|
+ return Reflect.get(target, prop);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function* gen () {
|
|
|
+ yield new Proxy(["a", "b", "c"], handler);
|
|
|
+ yield new Proxy(["e", "g", "h", "j"], handler);
|
|
|
+ }
|
|
|
+ const obj = Object.fromEntries(gen());
|
|
|
+ verifyObject({a : "b", e : "g"}, obj);
|
|
|
+ const expected = ["0a", "1b", "0e", "1g"];
|
|
|
+ const len = accessedProps.length;
|
|
|
+ assert.areEqual(4, len, "Object.fromEntries accesses correct number of properties");
|
|
|
+ for (let i = 0; i < len; ++i)
|
|
|
+ {
|
|
|
+ assert.areEqual(expected[i], accessedProps[i], "Object.fromEntries accesses the correct properties");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries accesses Proxy properties correctly",
|
|
|
+ body : function () {
|
|
|
+ const accessedProps = [];
|
|
|
+ const handler = {
|
|
|
+ get : function (target, prop, receiver) {
|
|
|
+ accessedProps.push(String(prop));
|
|
|
+ return Reflect.get(target, prop);
|
|
|
+ },
|
|
|
+ set : function () {
|
|
|
+ throw new Error ("Should not be called");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let result;
|
|
|
+ assert.doesNotThrow(()=>{result = Object.fromEntries(new Proxy([["a", 5], ["b", 2], ["c", 4]], handler)); });
|
|
|
+ verifyObject({a : 5, b : 2, c : 4}, result);
|
|
|
+ expected = ["Symbol(Symbol.iterator)", "length", "0", "length", "1", "length", "2", "length"];
|
|
|
+ for (let i = 0; i < 3; ++i)
|
|
|
+ {
|
|
|
+ assert.areEqual(expected[i], accessedProps[i], "Object.fromEntries accesses the correct properties");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries uses overridden array iterator",
|
|
|
+ body : function () {
|
|
|
+ let calls = 0;
|
|
|
+ Array.prototype[Symbol.iterator] = function () {
|
|
|
+ return {
|
|
|
+ next : function () {
|
|
|
+ switch (calls)
|
|
|
+ {
|
|
|
+ case 0:
|
|
|
+ calls = 1;
|
|
|
+ return { done : false, value : ["key", "value"]}
|
|
|
+ case 1:
|
|
|
+ calls = 2;
|
|
|
+ return { done : true, value : null }
|
|
|
+ case 2:
|
|
|
+ throw new Error ("Should not be reached");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let result;
|
|
|
+ assert.doesNotThrow(()=>{ result = Object.fromEntries([1, 2, 3, 4]);}, "Once iterator is done should not be called again");
|
|
|
+ verifyObject({key : "value"}, result);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name : "Object.fromEntries properties",
|
|
|
+ body : function () {
|
|
|
+ assert.areEqual("fromEntries", Object.fromEntries.name, "Object.fromEntries.name should be 'fromEntries'");
|
|
|
+ assert.areEqual(1, Object.fromEntries.length, "Object.fromEntries.length should be 1");
|
|
|
+ const descriptor = Object.getOwnPropertyDescriptor(Object, "fromEntries");
|
|
|
+ assert.isFalse(descriptor.enumerable, "Object.fromEntries should be enumerable");
|
|
|
+ assert.isTrue(descriptor.writable, "Object.fromEntries should be writable");
|
|
|
+ assert.isTrue(descriptor.configurable, "Object.fromEntries should be configurable");
|
|
|
+ assert.doesNotThrow(()=>{"use strict"; delete Object.fromEntries.length;}, "Deleting Object.fromEntries.length should succeed");
|
|
|
+ assert.areEqual(0, Object.fromEntries.length, "After deletion Object.fromEntries.length should be 0");
|
|
|
+ assert.doesNotThrow(()=>{"use strict"; delete Object.fromEntries;}, "Deleting Object.fromEntries should succeed");
|
|
|
+ assert.isUndefined(Object.fromEntries, "After deletion Object.fromEntries should be undefined");
|
|
|
+ }
|
|
|
+ }
|
|
|
+];
|
|
|
+
|
|
|
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
|