blob: aa4eaee50cc66092a1a6f0a41734c515ab4cd216 [file] [log] [blame]
//@ requireOptions("--useArrayGroupByMethod=1")
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`FAIL: expected '${expected}' actual '${actual}'`);
}
function shouldBeObject(actual, expected) {
function replacer(key, value) {
if (value === undefined)
return "%undefined%";
return value;
}
let actualJSON = JSON.stringify(actual, replacer).replaceAll("\"%undefined%\"", "undefined");
let expectedJSON = JSON.stringify(expected, replacer).replaceAll("\"%undefined%\"", "undefined");
shouldBe(actualJSON, expectedJSON);
}
function shouldBeMap(a, b) {
shouldBeObject(Array.from(a), b);
}
function notReached() {
throw new Error("should not reach here");
}
// Helper variables
Array.prototype.__test = 42;
let symbol = Symbol("symbol");
let sparseArrayLength = 6;
let mixPartialAndFast = new Array(sparseArrayLength);
mixPartialAndFast[sparseArrayLength - 1] = sparseArrayLength - 1;
for(let i = 0; i < 3; ++i)
mixPartialAndFast[i] = i;
function toObject(array) {
let result = {};
result.length = array.length;
for (let i in array)
result[i] = array[i];
result.groupByToMap = Array.prototype.groupByToMap;
return result;
}
function reverseInsertionOrder(array) {
let obj = toObject(array);
let props = [];
for (let i in obj)
props.push(i);
let result = {};
for (let i = props.length - 1; i >= 0; i--)
result[props[i]] = obj[props[i]];
result.groupByToMap = Array.prototype.groupByToMap;
return result;
}
let objectWithToStringThatThrows = {
toString: notReached,
};
let objectWithValueOfThatThrows = {
valueOf: notReached,
};
// Basic
shouldBe(Array.prototype.groupByToMap.length, 1);
shouldBe(Array.prototype.groupByToMap.name, "groupByToMap");
shouldBeMap([undefined].groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined]]]);
shouldBeMap([undefined].groupByToMap((x) => x === undefined), [[true, [undefined]]]);
shouldBeMap((new Array(4)).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined, undefined, undefined, undefined]]]);
shouldBeMap((new Array(4)).groupByToMap((x) => x === undefined), [[true, [undefined, undefined, undefined, undefined]]]);
shouldBeMap([0, 1, 2, 3].groupByToMap((x) => !(x & 1) ? "a" : "b"), [["a", [0, 2]], ["b", [1, 3]]]);
shouldBeMap([0, 1, 2, 3].groupByToMap((x) => !(x & 1)), [[true, [0, 2]], [false, [1, 3]]]);
shouldBeMap([0, 1, 2, 3].groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, 3]]]);
shouldBeMap([0, 1, 2, 3].groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, 3]]]);
shouldBeMap(mixPartialAndFast.groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, undefined, undefined, sparseArrayLength - 1]]]);
shouldBeMap(mixPartialAndFast.groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, undefined, undefined, sparseArrayLength - 1]]]);
// Generic Object
shouldBeMap(toObject([undefined]).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined]]]);
shouldBeMap(toObject([undefined]).groupByToMap((x) => x === undefined), [[true, [undefined]]]);
shouldBeMap(toObject(new Array(4)).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined, undefined, undefined, undefined]]]);
shouldBeMap(toObject(new Array(4)).groupByToMap((x) => x === undefined), [[true, [undefined, undefined, undefined, undefined]]]);
shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x) => !(x & 1) ? "a" : "b"), [["a", [0, 2]], ["b", [1, 3]]]);
shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x) => !(x & 1)), [[true, [0, 2]], [false, [1, 3]]]);
shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, 3]]]);
shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, 3]]]);
shouldBeMap(toObject(mixPartialAndFast).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, undefined, undefined, sparseArrayLength - 1]]]);
shouldBeMap(toObject(mixPartialAndFast).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, undefined, undefined, sparseArrayLength - 1]]]);
// Array-like object with invalid lengths
shouldBeMap(Array.prototype.groupByToMap.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: 0 }, notReached), []);
shouldBeMap(Array.prototype.groupByToMap.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: -0 }, notReached), []);
shouldBeMap(Array.prototype.groupByToMap.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: -4 }, notReached), []);
// Reversed generic Object
shouldBeMap(reverseInsertionOrder([undefined]).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined]]]);
shouldBeMap(reverseInsertionOrder([undefined]).groupByToMap((x) => x === undefined), [[true, [undefined]]]);
shouldBeMap(reverseInsertionOrder(new Array(4)).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined, undefined, undefined, undefined]]]);
shouldBeMap(reverseInsertionOrder(new Array(4)).groupByToMap((x) => x === undefined), [[true, [undefined, undefined, undefined, undefined]]]);
shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x) => !(x & 1) ? "a" : "b"), [["a", [0, 2]], ["b", [1, 3]]]);
shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x) => !(x & 1)), [[true, [0, 2]], [false, [1, 3]]]);
shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, 3]]]);
shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, 3]]]);
shouldBeMap(reverseInsertionOrder(mixPartialAndFast).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, undefined, undefined, sparseArrayLength - 1]]]);
shouldBeMap(reverseInsertionOrder(mixPartialAndFast).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, undefined, undefined, sparseArrayLength - 1]]]);
// Extra callback parameters
shouldBeMap([0, 1, 2, 3].groupByToMap((i, j, k, l, m) => m = !m), [[true, [0, 1, 2, 3]]]);
// Special keys
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => "constructor").get("constructor"), [0, 1, 2, 3]);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => "prototype").get("prototype"), [0, 1, 2, 3]);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => "__proto__").get("__proto__"), [0, 1, 2, 3]);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => -0).get(0), [0, 1, 2, 3]);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => 0).get(0), [0, 1, 2, 3]);
let objectWithToStringCounter = {
counter: 0,
toString() { return this.counter++; },
};
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => objectWithToStringCounter).get(objectWithToStringCounter), [0, 1, 2, 3]);
shouldBe(objectWithToStringCounter.counter, 0);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => objectWithToStringThatThrows).get(objectWithToStringThatThrows), [0, 1, 2, 3]);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => objectWithValueOfThatThrows).get(objectWithValueOfThatThrows), [0, 1, 2, 3]);
shouldBeObject([0, 1, 2, 3].groupByToMap((x) => symbol).get(symbol), [0, 1, 2, 3]);