1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Largely ported from
6// https://github.com/tc39/Array.prototype.includes/tree/master/test
7// using https://www.npmjs.org/package/test262-to-mjsunit with further edits
8
9
10// Array.prototype.includes sees a new element added by a getter that is hit
11// during iteration
12(function() {
13  var arrayLike = {
14    length: 5,
15    0: "a",
16
17    get 1() {
18      this[2] = "c";
19      return "b";
20    }
21  };
22
23  assertTrue(Array.prototype.includes.call(arrayLike, "c"));
24})();
25
26
27// Array.prototype.includes works on array-like objects
28(function() {
29  var arrayLike1 = {
30    length: 5,
31    0: "a",
32    1: "b"
33  };
34
35  assertTrue(Array.prototype.includes.call(arrayLike1, "a"));
36  assertFalse(Array.prototype.includes.call(arrayLike1, "c"));
37
38  var arrayLike2 = {
39    length: 2,
40    0: "a",
41    1: "b",
42    2: "c"
43  };
44
45  assertTrue(Array.prototype.includes.call(arrayLike2, "b"));
46  assertFalse(Array.prototype.includes.call(arrayLike2, "c"));
47})();
48
49
50// Array.prototype.includes should fail if used on a null or undefined this
51(function() {
52  assertThrows(function() {
53    Array.prototype.includes.call(null, "a");
54  }, TypeError);
55
56  assertThrows(function() {
57    Array.prototype.includes.call(undefined, "a");
58  }, TypeError);
59})();
60
61
62// Array.prototype.includes should terminate if getting an index throws an
63// exception
64(function() {
65  function Test262Error() {}
66
67  var trappedZero = {
68    length: 2,
69
70    get 0() {
71      throw new Test262Error();
72    },
73
74    get 1() {
75      assertUnreachable("Should not try to get the first element");
76    }
77  };
78
79  assertThrows(function() {
80    Array.prototype.includes.call(trappedZero, "a");
81  }, Test262Error);
82})();
83
84
85// Array.prototype.includes should terminate if ToNumber ends up being called on
86// a symbol fromIndex
87(function() {
88  var trappedZero = {
89    length: 1,
90
91    get 0() {
92      assertUnreachable("Should not try to get the zeroth element");
93    }
94  };
95
96  assertThrows(function() {
97    Array.prototype.includes.call(trappedZero, "a", Symbol());
98  }, TypeError);
99})();
100
101
102// Array.prototype.includes should terminate if an exception occurs converting
103// the fromIndex to a number
104(function() {
105  function Test262Error() {}
106
107  var fromIndex = {
108    valueOf: function() {
109      throw new Test262Error();
110    }
111  };
112
113  var trappedZero = {
114    length: 1,
115
116    get 0() {
117      assertUnreachable("Should not try to get the zeroth element");
118    }
119  };
120
121  assertThrows(function() {
122    Array.prototype.includes.call(trappedZero, "a", fromIndex);
123  }, Test262Error);
124})();
125
126
127// Array.prototype.includes should terminate if an exception occurs getting the
128// length
129(function() {
130  function Test262Error() {}
131
132  var fromIndexTrap = {
133    valueOf: function() {
134      assertUnreachable("Should not try to call ToInteger on valueOf");
135    }
136  };
137
138  var throwingLength = {
139    get length() {
140      throw new Test262Error();
141    },
142
143    get 0() {
144      assertUnreachable("Should not try to get the zeroth element");
145    }
146  };
147
148  assertThrows(function() {
149    Array.prototype.includes.call(throwingLength, "a", fromIndexTrap);
150  }, Test262Error);
151})();
152
153
154// Array.prototype.includes should terminate if ToLength ends up being called on
155// a symbol length
156(function() {
157  var fromIndexTrap = {
158    valueOf: function() {
159      assertUnreachable("Should not try to call ToInteger on valueOf");
160    }
161  };
162
163  var badLength = {
164    length: Symbol(),
165
166    get 0() {
167      assertUnreachable("Should not try to get the zeroth element");
168    }
169  };
170
171  assertThrows(function() {
172    Array.prototype.includes.call(badLength, "a", fromIndexTrap);
173  }, TypeError);
174})();
175
176
177// Array.prototype.includes should terminate if an exception occurs converting
178// the length to a number
179(function() {
180  function Test262Error() {}
181
182  var fromIndexTrap = {
183    valueOf: function() {
184      assertUnreachable("Should not try to call ToInteger on valueOf");
185    }
186  };
187
188  var badLength = {
189    length: {
190      valueOf: function() {
191        throw new Test262Error();
192      }
193    },
194
195    get 0() {
196      assertUnreachable("Should not try to get the zeroth element");
197    }
198  };
199
200  assertThrows(function() {
201    Array.prototype.includes.call(badLength, "a", fromIndexTrap);
202  }, Test262Error);
203})();
204
205
206// Array.prototype.includes should search the whole array, as the optional
207// second argument fromIndex defaults to 0
208(function() {
209  assertTrue([10, 11].includes(10));
210  assertTrue([10, 11].includes(11));
211
212  var arrayLike = {
213    length: 2,
214
215    get 0() {
216      return "1";
217    },
218
219    get 1() {
220      return "2";
221    }
222  };
223
224  assertTrue(Array.prototype.includes.call(arrayLike, "1"));
225  assertTrue(Array.prototype.includes.call(arrayLike, "2"));
226})();
227
228
229// Array.prototype.includes returns false if fromIndex is greater or equal to
230// the length of the array
231(function() {
232  assertFalse([1, 2].includes(2, 3));
233  assertFalse([1, 2].includes(2, 2));
234
235  var arrayLikeWithTrap = {
236    length: 2,
237
238    get 0() {
239      assertUnreachable("Getter for 0 was called");
240    },
241
242    get 1() {
243      assertUnreachable("Getter for 1 was called");
244    }
245  };
246
247  assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, "c", 2));
248  assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, "c", 3));
249})();
250
251
252// Array.prototype.includes searches the whole array if the computed index from
253// the given negative fromIndex argument is less than 0
254(function() {
255  assertTrue([1, 3].includes(1, -4));
256  assertTrue([1, 3].includes(3, -4));
257
258  var arrayLike = {
259    length: 2,
260    0: "a",
261
262    get 1() {
263      return "b";
264    },
265
266    get "-1"() {
267      assertUnreachable("Should not try to get the element at index -1");
268    }
269  };
270
271  assertTrue(Array.prototype.includes.call(arrayLike, "a", -4));
272  assertTrue(Array.prototype.includes.call(arrayLike, "b", -4));
273})();
274
275
276// Array.prototype.includes should use a negative value as the offset from the
277// end of the array to compute fromIndex
278(function() {
279  assertTrue([12, 13].includes(13, -1));
280  assertFalse([12, 13].includes(12, -1));
281  assertTrue([12, 13].includes(12, -2));
282
283  var arrayLike = {
284    length: 2,
285
286    get 0() {
287      return "a";
288    },
289
290    get 1() {
291      return "b";
292    }
293  };
294
295  assertTrue(Array.prototype.includes.call(arrayLike, "b", -1));
296  assertFalse(Array.prototype.includes.call(arrayLike, "a", -1));
297  assertTrue(Array.prototype.includes.call(arrayLike, "a", -2));
298})();
299
300
301// Array.prototype.includes converts its fromIndex parameter to an integer
302(function() {
303  assertFalse(["a", "b"].includes("a", 2.3));
304
305  var arrayLikeWithTraps = {
306    length: 2,
307
308    get 0() {
309      assertUnreachable("Getter for 0 was called");
310    },
311
312    get 1() {
313      assertUnreachable("Getter for 1 was called");
314    }
315  };
316
317  assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "c", 2.1));
318  assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "c", +Infinity));
319  assertTrue(["a", "b", "c"].includes("a", -Infinity));
320  assertTrue(["a", "b", "c"].includes("c", 2.9));
321  assertTrue(["a", "b", "c"].includes("c", NaN));
322
323  var arrayLikeWithTrapAfterZero = {
324    length: 2,
325
326    get 0() {
327      return "a";
328    },
329
330    get 1() {
331      assertUnreachable("Getter for 1 was called");
332    }
333  };
334
335  assertTrue(Array.prototype.includes.call(arrayLikeWithTrapAfterZero, "a", NaN));
336
337  var numberLike = {
338    valueOf: function() {
339      return 2;
340    }
341  };
342
343  assertFalse(["a", "b", "c"].includes("a", numberLike));
344  assertFalse(["a", "b", "c"].includes("a", "2"));
345  assertTrue(["a", "b", "c"].includes("c", numberLike));
346  assertTrue(["a", "b", "c"].includes("c", "2"));
347})();
348
349
350// Array.prototype.includes should have length 1
351(function() {
352  assertEquals(1, Array.prototype.includes.length);
353})();
354
355
356// Array.prototype.includes should have name property with value 'includes'
357(function() {
358  assertEquals("includes", Array.prototype.includes.name);
359})();
360
361
362// !!! Test failed to convert:
363// Cannot convert tests with includes.
364// !!!
365
366
367// Array.prototype.includes does not skip holes; if the array has a prototype it
368// gets from that
369(function() {
370  var holesEverywhere = [,,,];
371
372  holesEverywhere.__proto__ = {
373    1: "a"
374  };
375
376  holesEverywhere.__proto__.__proto__ = Array.prototype;
377  assertTrue(holesEverywhere.includes("a"));
378  var oneHole = ["a", "b",, "d"];
379
380  oneHole.__proto__ = {
381    get 2() {
382      return "c";
383    }
384  };
385
386  assertTrue(Array.prototype.includes.call(oneHole, "c"));
387})();
388
389
390// Array.prototype.includes does not skip holes; instead it treates them as
391// undefined
392(function() {
393  assertTrue([,,,].includes(undefined));
394  assertTrue(["a", "b",, "d"].includes(undefined));
395})();
396
397
398// Array.prototype.includes gets length property from the prototype if it's
399// available
400(function() {
401  var proto = {
402    length: 1
403  };
404
405  var arrayLike = Object.create(proto);
406  arrayLike[0] = "a";
407
408  Object.defineProperty(arrayLike, "1", {
409    get: function() {
410      assertUnreachable("Getter for 1 was called");
411    }
412  });
413
414  assertTrue(Array.prototype.includes.call(arrayLike, "a"));
415})();
416
417
418// Array.prototype.includes treats a missing length property as zero
419(function() {
420  var arrayLikeWithTraps = {
421    get 0() {
422      assertUnreachable("Getter for 0 was called");
423    },
424
425    get 1() {
426      assertUnreachable("Getter for 1 was called");
427    }
428  };
429
430  assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "a"));
431})();
432
433
434// Array.prototype.includes should always return false on negative-length
435// objects
436(function() {
437  assertFalse(Array.prototype.includes.call({
438    length: -1
439  }, 2));
440
441  assertFalse(Array.prototype.includes.call({
442    length: -2
443  }));
444
445  assertFalse(Array.prototype.includes.call({
446    length: -Infinity
447  }, undefined));
448
449  assertFalse(Array.prototype.includes.call({
450    length: -Math.pow(2, 53)
451  }, NaN));
452
453  assertFalse(Array.prototype.includes.call({
454    length: -1,
455    "-1": 2
456  }, 2));
457
458  assertFalse(Array.prototype.includes.call({
459    length: -3,
460    "-1": 2
461  }, 2));
462
463  assertFalse(Array.prototype.includes.call({
464    length: -Infinity,
465    "-1": 2
466  }, 2));
467
468  var arrayLikeWithTrap = {
469    length: -1,
470
471    get 0() {
472      assertUnreachable("Getter for 0 was called");
473    }
474  };
475
476  assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, 2));
477})();
478
479
480// Array.prototype.includes should clamp positive lengths to 2^53 - 1
481(function() {
482  var fromIndexForLargeIndexTests = 9007199254740990;
483
484  assertFalse(Array.prototype.includes.call({
485    length: 1
486  }, 2));
487
488  assertTrue(Array.prototype.includes.call({
489    length: 1,
490    0: "a"
491  }, "a"));
492
493  assertTrue(Array.prototype.includes.call({
494    length: +Infinity,
495    0: "a"
496  }, "a"));
497
498  assertFalse(Array.prototype.includes.call({
499    length: +Infinity
500  }, "a", fromIndexForLargeIndexTests));
501
502  var arrayLikeWithTrap = {
503    length: +Infinity,
504
505    get 9007199254740992() {
506      assertUnreachable("Getter for 9007199254740992 (i.e. 2^53) was called");
507    },
508
509    "9007199254740993": "a"
510  };
511
512  assertFalse(
513    Array.prototype.includes.call(arrayLikeWithTrap, "a", fromIndexForLargeIndexTests)
514  );
515
516  var arrayLikeWithTooBigLength = {
517    length: 9007199254740996,
518    "9007199254740992": "a"
519  };
520
521  assertFalse(
522    Array.prototype.includes.call(arrayLikeWithTooBigLength, "a", fromIndexForLargeIndexTests)
523  );
524})();
525
526
527// Array.prototype.includes should always return false on zero-length objects
528(function() {
529  assertFalse([].includes(2));
530  assertFalse([].includes());
531  assertFalse([].includes(undefined));
532  assertFalse([].includes(NaN));
533
534  assertFalse(Array.prototype.includes.call({
535    length: 0
536  }, 2));
537
538  assertFalse(Array.prototype.includes.call({
539    length: 0
540  }));
541
542  assertFalse(Array.prototype.includes.call({
543    length: 0
544  }, undefined));
545
546  assertFalse(Array.prototype.includes.call({
547    length: 0
548  }, NaN));
549
550  assertFalse(Array.prototype.includes.call({
551    length: 0,
552    0: 2
553  }, 2));
554
555  assertFalse(Array.prototype.includes.call({
556    length: 0,
557    0: undefined
558  }));
559
560  assertFalse(Array.prototype.includes.call({
561    length: 0,
562    0: undefined
563  }, undefined));
564
565  assertFalse(Array.prototype.includes.call({
566    length: 0,
567    0: NaN
568  }, NaN));
569
570  var arrayLikeWithTrap = {
571    length: 0,
572
573    get 0() {
574      assertUnreachable("Getter for 0 was called");
575    }
576  };
577
578  Array.prototype.includes.call(arrayLikeWithTrap);
579
580  var trappedFromIndex = {
581    valueOf: function() {
582      assertUnreachable("Should not try to convert fromIndex to a number on a zero-length array");
583    }
584  };
585
586  [].includes("a", trappedFromIndex);
587
588  Array.prototype.includes.call({
589    length: 0
590  }, trappedFromIndex);
591})();
592
593
594// Array.prototype.includes works on objects
595(function() {
596  assertFalse(["a", "b", "c"].includes({}));
597  assertFalse([{}, {}].includes({}));
598  var obj = {};
599  assertTrue([obj].includes(obj));
600  assertFalse([obj].includes(obj, 1));
601  assertTrue([obj, obj].includes(obj, 1));
602
603  var stringyObject = {
604    toString: function() {
605      return "a";
606    }
607  };
608
609  assertFalse(["a", "b", obj].includes(stringyObject));
610})();
611
612
613// Array.prototype.includes does not see an element removed by a getter that is
614// hit during iteration
615(function() {
616  var arrayLike = {
617    length: 5,
618    0: "a",
619
620    get 1() {
621      delete this[2];
622      return "b";
623    },
624
625    2: "c"
626  };
627
628  assertFalse(Array.prototype.includes.call(arrayLike, "c"));
629})();
630
631
632// Array.prototype.includes should use the SameValueZero algorithm to compare
633(function() {
634  assertTrue([1, 2, 3].includes(2));
635  assertFalse([1, 2, 3].includes(4));
636  assertTrue([1, 2, NaN].includes(NaN));
637  assertTrue([1, 2, -0].includes(+0));
638  assertTrue([1, 2, -0].includes(-0));
639  assertTrue([1, 2, +0].includes(-0));
640  assertTrue([1, 2, +0].includes(+0));
641  assertFalse([1, 2, -Infinity].includes(+Infinity));
642  assertTrue([1, 2, -Infinity].includes(-Infinity));
643  assertFalse([1, 2, +Infinity].includes(-Infinity));
644  assertTrue([1, 2, +Infinity].includes(+Infinity));
645})();
646
647
648// Array.prototype.includes stops once it hits the length of an array-like, even
649// if there are more after
650(function() {
651  var arrayLike = {
652    length: 2,
653    0: "a",
654    1: "b",
655
656    get 2() {
657      assertUnreachable("Should not try to get the second element");
658    }
659  };
660
661  assertFalse(Array.prototype.includes.call(arrayLike, "c"));
662})();
663
664
665// Array.prototype.includes works on typed arrays
666(function() {
667  assertTrue(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 2));
668
669  assertTrue(
670    Array.prototype.includes.call(new Float32Array([2.5, 3.14, Math.PI]), 3.1415927410125732)
671  );
672
673  assertFalse(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 4));
674  assertFalse(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 2, 2));
675})();
676