// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Largely ported from // https://github.com/tc39/Array.prototype.includes/tree/master/test // using https://www.npmjs.org/package/test262-to-mjsunit with further edits // Array.prototype.includes sees a new element added by a getter that is hit // during iteration (function() { var arrayLike = { length: 5, 0: "a", get 1() { this[2] = "c"; return "b"; } }; assertTrue(Array.prototype.includes.call(arrayLike, "c")); })(); // Array.prototype.includes works on array-like objects (function() { var arrayLike1 = { length: 5, 0: "a", 1: "b" }; assertTrue(Array.prototype.includes.call(arrayLike1, "a")); assertFalse(Array.prototype.includes.call(arrayLike1, "c")); var arrayLike2 = { length: 2, 0: "a", 1: "b", 2: "c" }; assertTrue(Array.prototype.includes.call(arrayLike2, "b")); assertFalse(Array.prototype.includes.call(arrayLike2, "c")); })(); // Array.prototype.includes should fail if used on a null or undefined this (function() { assertThrows(function() { Array.prototype.includes.call(null, "a"); }, TypeError); assertThrows(function() { Array.prototype.includes.call(undefined, "a"); }, TypeError); })(); // Array.prototype.includes should terminate if getting an index throws an // exception (function() { function Test262Error() {} var trappedZero = { length: 2, get 0() { throw new Test262Error(); }, get 1() { assertUnreachable("Should not try to get the first element"); } }; assertThrows(function() { Array.prototype.includes.call(trappedZero, "a"); }, Test262Error); })(); // Array.prototype.includes should terminate if ToNumber ends up being called on // a symbol fromIndex (function() { var trappedZero = { length: 1, get 0() { assertUnreachable("Should not try to get the zeroth element"); } }; assertThrows(function() { Array.prototype.includes.call(trappedZero, "a", Symbol()); }, TypeError); })(); // Array.prototype.includes should terminate if an exception occurs converting // the fromIndex to a number (function() { function Test262Error() {} var fromIndex = { valueOf: function() { throw new Test262Error(); } }; var trappedZero = { length: 1, get 0() { assertUnreachable("Should not try to get the zeroth element"); } }; assertThrows(function() { Array.prototype.includes.call(trappedZero, "a", fromIndex); }, Test262Error); })(); // Array.prototype.includes should terminate if an exception occurs getting the // length (function() { function Test262Error() {} var fromIndexTrap = { valueOf: function() { assertUnreachable("Should not try to call ToInteger on valueOf"); } }; var throwingLength = { get length() { throw new Test262Error(); }, get 0() { assertUnreachable("Should not try to get the zeroth element"); } }; assertThrows(function() { Array.prototype.includes.call(throwingLength, "a", fromIndexTrap); }, Test262Error); })(); // Array.prototype.includes should terminate if ToLength ends up being called on // a symbol length (function() { var fromIndexTrap = { valueOf: function() { assertUnreachable("Should not try to call ToInteger on valueOf"); } }; var badLength = { length: Symbol(), get 0() { assertUnreachable("Should not try to get the zeroth element"); } }; assertThrows(function() { Array.prototype.includes.call(badLength, "a", fromIndexTrap); }, TypeError); })(); // Array.prototype.includes should terminate if an exception occurs converting // the length to a number (function() { function Test262Error() {} var fromIndexTrap = { valueOf: function() { assertUnreachable("Should not try to call ToInteger on valueOf"); } }; var badLength = { length: { valueOf: function() { throw new Test262Error(); } }, get 0() { assertUnreachable("Should not try to get the zeroth element"); } }; assertThrows(function() { Array.prototype.includes.call(badLength, "a", fromIndexTrap); }, Test262Error); })(); // Array.prototype.includes should search the whole array, as the optional // second argument fromIndex defaults to 0 (function() { assertTrue([10, 11].includes(10)); assertTrue([10, 11].includes(11)); var arrayLike = { length: 2, get 0() { return "1"; }, get 1() { return "2"; } }; assertTrue(Array.prototype.includes.call(arrayLike, "1")); assertTrue(Array.prototype.includes.call(arrayLike, "2")); })(); // Array.prototype.includes returns false if fromIndex is greater or equal to // the length of the array (function() { assertFalse([1, 2].includes(2, 3)); assertFalse([1, 2].includes(2, 2)); var arrayLikeWithTrap = { length: 2, get 0() { assertUnreachable("Getter for 0 was called"); }, get 1() { assertUnreachable("Getter for 1 was called"); } }; assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, "c", 2)); assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, "c", 3)); })(); // Array.prototype.includes searches the whole array if the computed index from // the given negative fromIndex argument is less than 0 (function() { assertTrue([1, 3].includes(1, -4)); assertTrue([1, 3].includes(3, -4)); var arrayLike = { length: 2, 0: "a", get 1() { return "b"; }, get "-1"() { assertUnreachable("Should not try to get the element at index -1"); } }; assertTrue(Array.prototype.includes.call(arrayLike, "a", -4)); assertTrue(Array.prototype.includes.call(arrayLike, "b", -4)); })(); // Array.prototype.includes should use a negative value as the offset from the // end of the array to compute fromIndex (function() { assertTrue([12, 13].includes(13, -1)); assertFalse([12, 13].includes(12, -1)); assertTrue([12, 13].includes(12, -2)); var arrayLike = { length: 2, get 0() { return "a"; }, get 1() { return "b"; } }; assertTrue(Array.prototype.includes.call(arrayLike, "b", -1)); assertFalse(Array.prototype.includes.call(arrayLike, "a", -1)); assertTrue(Array.prototype.includes.call(arrayLike, "a", -2)); })(); // Array.prototype.includes converts its fromIndex parameter to an integer (function() { assertFalse(["a", "b"].includes("a", 2.3)); var arrayLikeWithTraps = { length: 2, get 0() { assertUnreachable("Getter for 0 was called"); }, get 1() { assertUnreachable("Getter for 1 was called"); } }; assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "c", 2.1)); assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "c", +Infinity)); assertFalse(["a", "b", "c"].includes("a", +Infinity)); assertTrue(["a", "b", "c"].includes("a", -Infinity)); assertTrue(["a", "b", "c"].includes("c", 2.9)); assertTrue(["a", "b", "c"].includes("c", NaN)); var arrayLikeWithTrapAfterZero = { length: 2, get 0() { return "a"; }, get 1() { assertUnreachable("Getter for 1 was called"); } }; assertTrue(Array.prototype.includes.call(arrayLikeWithTrapAfterZero, "a", NaN)); var numberLike = { valueOf: function() { return 2; } }; assertFalse(["a", "b", "c"].includes("a", numberLike)); assertFalse(["a", "b", "c"].includes("a", "2")); assertTrue(["a", "b", "c"].includes("c", numberLike)); assertTrue(["a", "b", "c"].includes("c", "2")); })(); // Array.prototype.includes should have length 1 (function() { assertEquals(1, Array.prototype.includes.length); })(); // Array.prototype.includes should have name property with value 'includes' (function() { assertEquals("includes", Array.prototype.includes.name); })(); // !!! Test failed to convert: // Cannot convert tests with includes. // !!! // Array.prototype.includes does not skip holes; if the array has a prototype it // gets from that (function() { var holesEverywhere = [,,,]; holesEverywhere.__proto__ = { 1: "a" }; holesEverywhere.__proto__.__proto__ = Array.prototype; assertTrue(holesEverywhere.includes("a")); var oneHole = ["a", "b",, "d"]; oneHole.__proto__ = { get 2() { return "c"; } }; assertTrue(Array.prototype.includes.call(oneHole, "c")); })(); // Array.prototype.includes does not skip holes; instead it treates them as // undefined (function() { assertTrue([,,,].includes(undefined)); assertTrue(["a", "b",, "d"].includes(undefined)); })(); // Array.prototype.includes gets length property from the prototype if it's // available (function() { var proto = { length: 1 }; var arrayLike = Object.create(proto); arrayLike[0] = "a"; Object.defineProperty(arrayLike, "1", { get: function() { assertUnreachable("Getter for 1 was called"); } }); assertTrue(Array.prototype.includes.call(arrayLike, "a")); })(); // Array.prototype.includes treats a missing length property as zero (function() { var arrayLikeWithTraps = { get 0() { assertUnreachable("Getter for 0 was called"); }, get 1() { assertUnreachable("Getter for 1 was called"); } }; assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "a")); })(); // Array.prototype.includes should always return false on negative-length // objects (function() { assertFalse(Array.prototype.includes.call({ length: -1 }, 2)); assertFalse(Array.prototype.includes.call({ length: -2 })); assertFalse(Array.prototype.includes.call({ length: -Infinity }, undefined)); assertFalse(Array.prototype.includes.call({ length: -Math.pow(2, 53) }, NaN)); assertFalse(Array.prototype.includes.call({ length: -1, "-1": 2 }, 2)); assertFalse(Array.prototype.includes.call({ length: -3, "-1": 2 }, 2)); assertFalse(Array.prototype.includes.call({ length: -Infinity, "-1": 2 }, 2)); var arrayLikeWithTrap = { length: -1, get 0() { assertUnreachable("Getter for 0 was called"); } }; assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, 2)); })(); // Array.prototype.includes should clamp positive lengths to 2^53 - 1 (function() { var fromIndexForLargeIndexTests = 9007199254740990; assertFalse(Array.prototype.includes.call({ length: 1 }, 2)); assertTrue(Array.prototype.includes.call({ length: 1, 0: "a" }, "a")); assertTrue(Array.prototype.includes.call({ length: +Infinity, 0: "a" }, "a")); assertFalse(Array.prototype.includes.call({ length: +Infinity }, "a", fromIndexForLargeIndexTests)); var arrayLikeWithTrap = { length: +Infinity, get 9007199254740992() { assertUnreachable("Getter for 9007199254740992 (i.e. 2^53) was called"); }, "9007199254740993": "a" }; assertFalse( Array.prototype.includes.call(arrayLikeWithTrap, "a", fromIndexForLargeIndexTests) ); var arrayLikeWithTooBigLength = { length: 9007199254740996, "9007199254740992": "a" }; assertFalse( Array.prototype.includes.call(arrayLikeWithTooBigLength, "a", fromIndexForLargeIndexTests) ); })(); // Array.prototype.includes should always return false on zero-length objects (function() { assertFalse([].includes(2)); assertFalse([].includes()); assertFalse([].includes(undefined)); assertFalse([].includes(NaN)); assertFalse(Array.prototype.includes.call({ length: 0 }, 2)); assertFalse(Array.prototype.includes.call({ length: 0 })); assertFalse(Array.prototype.includes.call({ length: 0 }, undefined)); assertFalse(Array.prototype.includes.call({ length: 0 }, NaN)); assertFalse(Array.prototype.includes.call({ length: 0, 0: 2 }, 2)); assertFalse(Array.prototype.includes.call({ length: 0, 0: undefined })); assertFalse(Array.prototype.includes.call({ length: 0, 0: undefined }, undefined)); assertFalse(Array.prototype.includes.call({ length: 0, 0: NaN }, NaN)); var arrayLikeWithTrap = { length: 0, get 0() { assertUnreachable("Getter for 0 was called"); } }; Array.prototype.includes.call(arrayLikeWithTrap); var trappedFromIndex = { valueOf: function() { assertUnreachable("Should not try to convert fromIndex to a number on a zero-length array"); } }; [].includes("a", trappedFromIndex); Array.prototype.includes.call({ length: 0 }, trappedFromIndex); })(); // Array.prototype.includes works on objects (function() { assertFalse(["a", "b", "c"].includes({})); assertFalse([{}, {}].includes({})); var obj = {}; assertTrue([obj].includes(obj)); assertFalse([obj].includes(obj, 1)); assertTrue([obj, obj].includes(obj, 1)); var stringyObject = { toString: function() { return "a"; } }; assertFalse(["a", "b", obj].includes(stringyObject)); })(); // Array.prototype.includes does not see an element removed by a getter that is // hit during iteration (function() { var arrayLike = { length: 5, 0: "a", get 1() { delete this[2]; return "b"; }, 2: "c" }; assertFalse(Array.prototype.includes.call(arrayLike, "c")); })(); // Array.prototype.includes accesses out-of-bounds if length is changed late. (function () { let arr = [1, 2, 3]; assertTrue(arr.includes(undefined, { toString: function () { arr.length = 0; return 0; } })); arr = [1, 2, 3]; assertFalse(arr.includes(undefined, { toString: function () { arr.length = 0; return 10; } })); arr = [1, 2, 3]; assertFalse(arr.includes(4, { toString: function () { arr.push(4); return 0; } })); })(); // Array.prototype.includes should use the SameValueZero algorithm to compare (function() { assertTrue([1, 2, 3].includes(2)); assertFalse([1, 2, 3].includes(4)); assertTrue([1, 2, NaN].includes(NaN)); assertTrue([1, 2, -0].includes(+0)); assertTrue([1, 2, -0].includes(-0)); assertTrue([1, 2, +0].includes(-0)); assertTrue([1, 2, +0].includes(+0)); assertFalse([1, 2, -Infinity].includes(+Infinity)); assertTrue([1, 2, -Infinity].includes(-Infinity)); assertFalse([1, 2, +Infinity].includes(-Infinity)); assertTrue([1, 2, +Infinity].includes(+Infinity)); })(); // Array.prototype.includes stops once it hits the length of an array-like, even // if there are more after (function() { var arrayLike = { length: 2, 0: "a", 1: "b", get 2() { assertUnreachable("Should not try to get the second element"); } }; assertFalse(Array.prototype.includes.call(arrayLike, "c")); })(); // Array.prototype.includes works on typed arrays (function() { assertTrue(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 2)); assertTrue( Array.prototype.includes.call(new Float32Array([2.5, 3.14, Math.PI]), 3.1415927410125732) ); assertFalse(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 4)); assertFalse(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 2, 2)); })(); (function testUnscopable() { assertTrue(Array.prototype[Symbol.unscopables].includes); })();