object-spread.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. const testObjSpread = obj => {
  2. expect(obj).toEqual({
  3. foo: 0,
  4. bar: 1,
  5. baz: 2,
  6. qux: 3,
  7. });
  8. };
  9. const testObjStrSpread = obj => {
  10. expect(obj).toEqual(["a", "b", "c", "d"]);
  11. };
  12. test("spread object literal inside object literal", () => {
  13. const obj = {
  14. foo: 0,
  15. ...{ bar: 1, baz: 2 },
  16. qux: 3,
  17. };
  18. testObjSpread(obj);
  19. });
  20. test("spread object with assigned property inside object literal", () => {
  21. const obj = { foo: 0, bar: 1, baz: 2 };
  22. obj.qux = 3;
  23. testObjSpread({ ...obj });
  24. });
  25. test("spread object inside object literal", () => {
  26. let a = { bar: 1, baz: 2 };
  27. const obj = { foo: 0, ...a, qux: 3 };
  28. testObjSpread(obj);
  29. });
  30. test("complex nested object spreading", () => {
  31. const obj = {
  32. ...{},
  33. ...{
  34. ...{ foo: 0, bar: 1, baz: 2 },
  35. },
  36. qux: 3,
  37. };
  38. testObjSpread(obj);
  39. });
  40. test("spread string in object literal", () => {
  41. const obj = { ..."abcd" };
  42. testObjStrSpread(obj);
  43. });
  44. test("spread array in object literal", () => {
  45. const obj = { ...["a", "b", "c", "d"] };
  46. testObjStrSpread(obj);
  47. });
  48. test("spread array with holes in object literal", () => {
  49. const obj = { ...[, , "a", , , , "b", "c", , "d", , ,] };
  50. expect(obj).toEqual({ 2: "a", 6: "b", 7: "c", 9: "d" });
  51. });
  52. test("spread string object in object literal", () => {
  53. const obj = { ...String("abcd") };
  54. testObjStrSpread(obj);
  55. });
  56. test("spread object with non-enumerable property", () => {
  57. const a = { foo: 0 };
  58. Object.defineProperty(a, "bar", {
  59. value: 1,
  60. enumerable: false,
  61. });
  62. const obj = { ...a };
  63. expect(obj.foo).toBe(0);
  64. expect(obj).not.toHaveProperty("bar");
  65. });
  66. test("spread object with symbol keys", () => {
  67. const s = Symbol("baz");
  68. const a = {
  69. foo: "bar",
  70. [s]: "qux",
  71. };
  72. const obj = { ...a };
  73. expect(obj.foo).toBe("bar");
  74. expect(obj[s]).toBe("qux");
  75. });
  76. test("spreading non-spreadable values", () => {
  77. let empty = {
  78. ...undefined,
  79. ...null,
  80. ...1,
  81. ...true,
  82. ...function () {},
  83. ...Date,
  84. };
  85. expect(Object.getOwnPropertyNames(empty)).toHaveLength(0);
  86. });
  87. test("respects custom Symbol.iterator method", () => {
  88. let o = {
  89. [Symbol.iterator]() {
  90. return {
  91. i: 0,
  92. next() {
  93. if (this.i++ == 3) {
  94. return { done: true };
  95. }
  96. return { value: this.i, done: false };
  97. },
  98. };
  99. },
  100. };
  101. let a = [...o];
  102. expect(a).toEqual([1, 2, 3]);
  103. });
  104. test("object with numeric indices", () => {
  105. const obj = { 0: 0, 1: 1, foo: "bar" };
  106. const result = { ...obj };
  107. expect(result).toHaveProperty("0", 0);
  108. expect(result).toHaveProperty("1", 1);
  109. expect(result).toHaveProperty("foo", "bar");
  110. });
  111. describe("modification of spreadable objects during spread", () => {
  112. test("spreading object", () => {
  113. const object = {
  114. 0: 0,
  115. 2: 2,
  116. 9999: 9999,
  117. bar: 44,
  118. get 3() {
  119. object[4] = 4;
  120. object[5000] = 5000;
  121. return 3;
  122. },
  123. };
  124. const result = { ...object };
  125. expect(Object.getOwnPropertyNames(result)).toHaveLength(5);
  126. expect(Object.getOwnPropertyNames(result)).not.toContain("4");
  127. expect(Object.getOwnPropertyNames(result)).toContain("bar");
  128. });
  129. test("spreading array", () => {
  130. const array = [0];
  131. array[2] = 2;
  132. array[999] = 999;
  133. Object.defineProperty(array, 3, {
  134. get() {
  135. array[4] = 4;
  136. array[1000] = 1000;
  137. return 3;
  138. },
  139. enumerable: true,
  140. });
  141. const objectResult = { ...array };
  142. expect(Object.getOwnPropertyNames(objectResult)).toHaveLength(4);
  143. expect(Object.getOwnPropertyNames(objectResult)).not.toContain("4");
  144. const arrayResult = [...array];
  145. expect(arrayResult).toHaveLength(1001);
  146. expect(arrayResult).toHaveProperty("0", 0);
  147. expect(arrayResult).toHaveProperty("2", 2);
  148. expect(arrayResult).toHaveProperty("3", 3);
  149. // Yes the in flight added items need to be here in this case! (since it uses an iterator)
  150. expect(arrayResult).toHaveProperty("4", 4);
  151. expect(arrayResult).toHaveProperty("999", 999);
  152. expect(arrayResult).toHaveProperty("1000", 1000);
  153. });
  154. });
  155. test("allows assignment expressions", () => {
  156. expect("({ ...a = { hello: 'world' } })").toEval();
  157. expect("({ ...a += 'hello' })").toEval();
  158. expect("({ ...a -= 'hello' })").toEval();
  159. expect("({ ...a **= 'hello' })").toEval();
  160. expect("({ ...a *= 'hello' })").toEval();
  161. expect("({ ...a /= 'hello' })").toEval();
  162. expect("({ ...a %= 'hello' })").toEval();
  163. expect("({ ...a <<= 'hello' })").toEval();
  164. expect("({ ...a >>= 'hello' })").toEval();
  165. expect("({ ...a >>>= 'hello' })").toEval();
  166. expect("({ ...a &= 'hello' })").toEval();
  167. expect("({ ...a ^= 'hello' })").toEval();
  168. expect("({ ...a |= 'hello' })").toEval();
  169. expect("({ ...a &&= 'hello' })").toEval();
  170. expect("({ ...a ||= 'hello' })").toEval();
  171. expect("({ ...a ??= 'hello' })").toEval();
  172. expect("function* test() { return ({ ...yield a }); }").toEval();
  173. });
  174. test("spreading null-proto objects", () => {
  175. const obj = {
  176. __proto__: null,
  177. hello: "world",
  178. friends: "well hello",
  179. toString() {
  180. expect().fail("called toString()");
  181. },
  182. valueOf() {
  183. expect().fail("called valueOf()");
  184. },
  185. };
  186. let res;
  187. expect(() => {
  188. res = { ...obj };
  189. }).not.toThrow();
  190. expect(res).toHaveProperty("hello", "world");
  191. expect(res).toHaveProperty("friends", "well hello");
  192. });