fromEntries.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. function verifyProperties(obj, property, value)
  7. {
  8. const descriptor = Object.getOwnPropertyDescriptor(obj, property);
  9. assert.areEqual(value, obj[property], "Object.fromEntries should set correct valued");
  10. assert.isTrue(descriptor.enumerable, "Object.fromEntries should create enumerable properties");
  11. assert.isTrue(descriptor.configurable, "Object.fromEntries should create configurable properties");
  12. assert.isTrue(descriptor.writable, "Object.fromEntries should create writable properties");
  13. obj[property] = "other value";
  14. assert.areEqual("other value", obj[property], "should actually be able to write to properties created by Object.fromEntries");
  15. assert.doesNotThrow(()=>{"use strict"; delete obj[property];}, "deleting properties created by Object.fromEntries should not throw");
  16. assert.isUndefined(obj[property], "deleting properties created by Object.fromEntries should succeed");
  17. }
  18. function verifyObject(expected, actual)
  19. {
  20. for (let i in actual)
  21. {
  22. assert.isTrue(expected.hasOwnProperty(i), "Object.fromEntries shouldn't create unexpected properties");
  23. }
  24. for (let i in expected)
  25. {
  26. verifyProperties(actual, i, expected[i]);
  27. }
  28. }
  29. const tests = [
  30. {
  31. name : "Object.fromEntries invalid parameters",
  32. body : function () {
  33. assert.throws(()=>{Object.fromEntries(null);}, TypeError, "Object.fromEntries throws when called with null parameter");
  34. assert.throws(()=>{Object.fromEntries(undefined);}, TypeError, "Object.fromEntries throws when called with undefined parameter");
  35. assert.throws(()=>{Object.fromEntries("something");}, TypeError, "Object.fromEntries throws when called with string literal parameter");
  36. assert.throws(()=>{Object.fromEntries(456);}, TypeError, "Object.fromEntries throws when called with number literal parameter");
  37. assert.throws(()=>{Object.fromEntries(Number());}, TypeError, "Object.fromEntries throws when called with Number Object parameter");
  38. assert.doesNotThrow(()=>{Object.fromEntries(String());}, "Object.fromEntries does not throw when called with String Object parameter with length 0");
  39. assert.throws(()=>{Object.fromEntries(String("anything"));}, TypeError, "Object.fromEntries throws when called with String Object parameter with length > 0");
  40. assert.throws(()=>{Object.fromEntries({});}, TypeError, "Object.fromEntries throws when called with Object literal");
  41. assert.throws(()=>{Object.fromEntries({a : "5", b : "10"});}, TypeError, "Object.fromEntries throws when called with Object literal");
  42. }
  43. },
  44. {
  45. name : "Object.fromEntries basic cases",
  46. body : function () {
  47. const obj1 = Object.fromEntries([["first", 50], ["second", 30], ["third", 60], ["fourth", 70]]);
  48. verifyObject({first : 50, second : 30, third : 60, fourth : 70}, obj1);
  49. const obj2 = Object.fromEntries([Object("a12234"),Object("b2kls"),Object("c3deg")]);
  50. verifyObject({a : "1", b : "2", c : "3"}, obj2);
  51. function testArguments()
  52. {
  53. verifyObject(expected, Object.fromEntries(arguments));
  54. }
  55. const expected = { abc : "one", bcd : "two", hsa : "three"};
  56. testArguments(["abc", "one"], ["bcd", "two"], ["hsa", "three"]);
  57. }
  58. },
  59. {
  60. name : "Object.fromEntries with array-like object",
  61. body : function ()
  62. {
  63. const arrayLike = {0 : ["abc", "one"], 1 : ["bcd", "two"], 2 : ["hsa", "three"], length : 3, current : 0}
  64. assert.throws(()=>{ Object.fromEntries(arrayLike); }, TypeError, "Object.fromEntries throws when parameter has no iterator");
  65. arrayLike[Symbol.iterator] = function () {
  66. const array = this;
  67. return {
  68. next : function () {
  69. const value = array[String(array.current)];
  70. ++array.current;
  71. return {
  72. value : value,
  73. done : array.length < array.current
  74. };
  75. }
  76. };
  77. };
  78. verifyObject({ abc : "one", bcd : "two", hsa : "three"}, Object.fromEntries(arrayLike));
  79. }
  80. },
  81. {
  82. name : "Object.fromEntries does not call setters",
  83. body : function () {
  84. let calledSet = false;
  85. Object.defineProperty(Object.prototype, "prop", {
  86. set : function () { calledSet = true; }
  87. });
  88. const obj = Object.fromEntries([["prop", 10]]);
  89. verifyProperties(obj, "prop", 10);
  90. assert.isFalse(calledSet, "Object.fromEntries should not call setters");
  91. }
  92. },
  93. {
  94. name : "Object.fromEntries iterates over generators",
  95. body : function () {
  96. function* gen1 ()
  97. {
  98. yield ["val1", 10];
  99. yield ["val2", 50];
  100. yield ["val3", 60, "other stuff"];
  101. }
  102. const obj = Object.fromEntries(gen1());
  103. verifyObject({val1 : 10, val2 : 50, val3: 60}, obj);
  104. let unreachable = false;
  105. function* gen2 ()
  106. {
  107. yield ["val1", 10];
  108. yield "val2";
  109. unreachable = true;
  110. yield ["val3", 60, "other stuff"];
  111. }
  112. assert.throws(()=>{Object.fromEntries(gen2())}, TypeError, "When generator provides invalid case Object.fromEntries should throw");
  113. assert.isFalse(unreachable, "Object.fromEntries does not continue after invalid case provided");
  114. }
  115. },
  116. {
  117. name : "Object.fromEntries accesses properties in correct order from generator",
  118. body : function () {
  119. const accessedProps = [];
  120. const handler = {
  121. get : function (target, prop, receiver) {
  122. accessedProps.push(prop + Reflect.get(target, prop));
  123. return Reflect.get(target, prop);
  124. }
  125. }
  126. function* gen () {
  127. yield new Proxy(["a", "b", "c"], handler);
  128. yield new Proxy(["e", "g", "h", "j"], handler);
  129. }
  130. const obj = Object.fromEntries(gen());
  131. verifyObject({a : "b", e : "g"}, obj);
  132. const expected = ["0a", "1b", "0e", "1g"];
  133. const len = accessedProps.length;
  134. assert.areEqual(4, len, "Object.fromEntries accesses correct number of properties");
  135. for (let i = 0; i < len; ++i)
  136. {
  137. assert.areEqual(expected[i], accessedProps[i], "Object.fromEntries accesses the correct properties");
  138. }
  139. }
  140. },
  141. {
  142. name : "Object.fromEntries accesses Proxy properties correctly",
  143. body : function () {
  144. const accessedProps = [];
  145. const handler = {
  146. get : function (target, prop, receiver) {
  147. accessedProps.push(String(prop));
  148. return Reflect.get(target, prop);
  149. },
  150. set : function () {
  151. throw new Error ("Should not be called");
  152. }
  153. }
  154. let result;
  155. assert.doesNotThrow(()=>{result = Object.fromEntries(new Proxy([["a", 5], ["b", 2], ["c", 4]], handler)); });
  156. verifyObject({a : 5, b : 2, c : 4}, result);
  157. expected = ["Symbol(Symbol.iterator)", "length", "0", "length", "1", "length", "2", "length"];
  158. for (let i = 0; i < 3; ++i)
  159. {
  160. assert.areEqual(expected[i], accessedProps[i], "Object.fromEntries accesses the correct properties");
  161. }
  162. }
  163. },
  164. {
  165. name : "Object.fromEntries uses overridden array iterator",
  166. body : function () {
  167. let calls = 0;
  168. Array.prototype[Symbol.iterator] = function () {
  169. return {
  170. next : function () {
  171. switch (calls)
  172. {
  173. case 0:
  174. calls = 1;
  175. return { done : false, value : ["key", "value"]}
  176. case 1:
  177. calls = 2;
  178. return { done : true, value : null }
  179. case 2:
  180. throw new Error ("Should not be reached");
  181. }
  182. }
  183. }
  184. }
  185. let result;
  186. assert.doesNotThrow(()=>{ result = Object.fromEntries([1, 2, 3, 4]);}, "Once iterator is done should not be called again");
  187. verifyObject({key : "value"}, result);
  188. }
  189. },
  190. {
  191. name : "Object.fromEntries properties",
  192. body : function () {
  193. assert.areEqual("fromEntries", Object.fromEntries.name, "Object.fromEntries.name should be 'fromEntries'");
  194. assert.areEqual(1, Object.fromEntries.length, "Object.fromEntries.length should be 1");
  195. const descriptor = Object.getOwnPropertyDescriptor(Object, "fromEntries");
  196. assert.isFalse(descriptor.enumerable, "Object.fromEntries should be enumerable");
  197. assert.isTrue(descriptor.writable, "Object.fromEntries should be writable");
  198. assert.isTrue(descriptor.configurable, "Object.fromEntries should be configurable");
  199. assert.doesNotThrow(()=>{"use strict"; delete Object.fromEntries.length;}, "Deleting Object.fromEntries.length should succeed");
  200. assert.areEqual(0, Object.fromEntries.length, "After deletion Object.fromEntries.length should be 0");
  201. assert.doesNotThrow(()=>{"use strict"; delete Object.fromEntries;}, "Deleting Object.fromEntries should succeed");
  202. assert.isUndefined(Object.fromEntries, "After deletion Object.fromEntries should be undefined");
  203. }
  204. }
  205. ];
  206. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });