X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/2989f964f0325a2d9c9294a8d3dab722313d5518..f1283dfa8f4f1482dac6325fbcb66f8778af1002:/collection.test.js diff --git a/collection.test.js b/collection.test.js new file mode 100644 index 0000000..b490572 --- /dev/null +++ b/collection.test.js @@ -0,0 +1,401 @@ +// ♓🌟 Piscēs ∷ collection.test.js +// ==================================================================== +// +// Copyright © 2022 Lady [@ Lady’s Computer]. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at . + +import { + assertEquals, + assertSpyCall, + assertSpyCalls, + assertStrictEquals, + assertThrows, + describe, + it, + spy, +} from "./dev-deps.js"; +import { + canonicalNumericIndexString, + findIndexedEntry, + isArrayIndexString, + isCollection, + isConcatSpreadable, + isIntegerIndexString, + lengthOfArrayLike, + toIndex, + toLength, +} from "./collection.js"; + +describe("canonicalNumericIndexString", () => { + it("[[Call]] returns undefined for nonstrings", () => { + assertStrictEquals(canonicalNumericIndexString(1), void {}); + }); + + it("[[Call]] returns undefined for noncanonical strings", () => { + assertStrictEquals(canonicalNumericIndexString(""), void {}); + assertStrictEquals(canonicalNumericIndexString("01"), void {}); + assertStrictEquals( + canonicalNumericIndexString("9007199254740993"), + void {}, + ); + }); + + it('[[Call]] returns -0 for "-0"', () => { + assertStrictEquals(canonicalNumericIndexString("-0"), -0); + }); + + it("[[Call]] returns the corresponding number for canonical strings", () => { + assertStrictEquals(canonicalNumericIndexString("0"), 0); + assertStrictEquals(canonicalNumericIndexString("-0.25"), -0.25); + assertStrictEquals( + canonicalNumericIndexString("9007199254740992"), + 9007199254740992, + ); + assertStrictEquals(canonicalNumericIndexString("NaN"), 0 / 0); + assertStrictEquals(canonicalNumericIndexString("Infinity"), 1 / 0); + assertStrictEquals( + canonicalNumericIndexString("-Infinity"), + -1 / 0, + ); + }); +}); + +describe("findIndexedEntry", () => { + it("[[Call]] returns undefined if no matching entry exists", () => { + assertStrictEquals(findIndexedEntry([], () => true), void {}); + assertStrictEquals(findIndexedEntry([1], () => false), void {}); + }); + + it("[[Call]] returns an entry for the first match", () => { + assertEquals( + findIndexedEntry([, true, false], ($) => $ ?? true), + [0, void {}], + ); + assertEquals( + findIndexedEntry(["failure", "success"], ($) => $ == "success"), + [1, "success"], + ); + }); + + it("[[Call]] works on arraylike objects", () => { + assertEquals( + findIndexedEntry({ 1: "success", length: 2 }, ($) => $), + [1, "success"], + ); + assertEquals( + findIndexedEntry({ 1: "failure", length: 1 }, ($) => $), + void {}, + ); + }); + + it("[[Call]] only gets the value once", () => { + const get1 = spy(() => true); + findIndexedEntry({ + get 1() { + return get1(); + }, + length: 2, + }, ($) => $); + assertSpyCalls(get1, 1); + }); + + it("[[Call]] passes the value, index, and this value to the callback", () => { + const arr = ["failure", "success", "success"]; + const callback = spy(($) => $ === "success"); + const thisArg = {}; + findIndexedEntry(arr, callback, thisArg); + assertSpyCalls(callback, 2); + assertSpyCall(callback, 0, { + args: ["failure", 0, arr], + self: thisArg, + }); + assertSpyCall(callback, 1, { + args: ["success", 1, arr], + self: thisArg, + }); + }); +}); + +describe("isArrayIndexString", () => { + it("[[Call]] returns false for nonstrings", () => { + assertStrictEquals(isArrayIndexString(1), false); + }); + + it("[[Call]] returns false for noncanonical strings", () => { + assertStrictEquals(isArrayIndexString(""), false); + assertStrictEquals(isArrayIndexString("01"), false); + assertStrictEquals(isArrayIndexString("9007199254740993"), false); + }); + + it("[[Call]] returns false for nonfinite numbers", () => { + assertStrictEquals(isArrayIndexString("NaN"), false); + assertStrictEquals(isArrayIndexString("Infinity"), false); + assertStrictEquals(isArrayIndexString("-Infinity"), false); + }); + + it("[[Call]] returns false for negative numbers", () => { + assertStrictEquals(isArrayIndexString("-0"), false); + assertStrictEquals(isArrayIndexString("-1"), false); + }); + + it("[[Call]] returns false for nonintegers", () => { + assertStrictEquals(isArrayIndexString("0.25"), false); + assertStrictEquals(isArrayIndexString("1.1"), false); + }); + + it("[[Call]] returns false for numbers greater than or equal to -1 >>> 0", () => { + assertStrictEquals(isArrayIndexString(String(-1 >>> 0)), false); + assertStrictEquals( + isArrayIndexString(String((-1 >>> 0) + 1)), + false, + ); + }); + + it("[[Call]] returns true for array lengths less than -1 >>> 0", () => { + assertStrictEquals(isArrayIndexString("0"), true); + assertStrictEquals( + isArrayIndexString(String((-1 >>> 0) - 1)), + true, + ); + }); +}); + +describe("isCollection", () => { + it("[[Call]] returns false for primitives", () => { + assertStrictEquals(isCollection("failure"), false); + }); + + it("[[Call]] returns false if length throws", () => { + assertStrictEquals( + isCollection({ + get length() { + throw void {}; + }, + }), + false, + ); + }); + + it("[[Call]] returns false if length is not an integer index and cannot be converted to one", () => { + assertStrictEquals( + isCollection({ length: -1, [Symbol.isConcatSpreadable]: true }), + false, + ); + assertStrictEquals( + isCollection({ + length: Infinity, + [Symbol.isConcatSpreadable]: true, + }), + false, + ); + assertStrictEquals( + isCollection({ + length: 9007199254740992, + [Symbol.isConcatSpreadable]: true, + }), + false, + ); + }); + + it("[[Call]] returns true if length is an integer index and the object is concat spreadable", () => { + assertStrictEquals( + isCollection({ length: 1, [Symbol.isConcatSpreadable]: true }), + true, + ); + assertStrictEquals( + isCollection({ length: 0, [Symbol.isConcatSpreadable]: true }), + true, + ); + assertStrictEquals( + isCollection({ + length: 9007199254740991, + [Symbol.isConcatSpreadable]: true, + }), + true, + ); + }); + + it("[[Call]] returns true if length can be converted to an index without throwing an error and the object is concat spreadable", () => { + assertStrictEquals( + isCollection({ length: -0, [Symbol.isConcatSpreadable]: true }), + true, + ); + assertStrictEquals( + isCollection({ length: NaN, [Symbol.isConcatSpreadable]: true }), + true, + ); + }); +}); + +describe("isConcatSpreadable", () => { + it("[[Call]] returns false for primitives", () => { + assertStrictEquals(isConcatSpreadable("failure"), false); + }); + + it("[[Call]] returns false if [Symbol.isConcatSpreadable] is null or false", () => { + assertStrictEquals( + isConcatSpreadable( + Object.assign([], { [Symbol.isConcatSpreadable]: null }), + ), + false, + ); + assertStrictEquals( + isConcatSpreadable( + Object.assign([], { [Symbol.isConcatSpreadable]: false }), + ), + false, + ); + }); + + it("[[Call]] returns true if [Symbol.isConcatSpreadable] is undefined and the object is an array", () => { + assertStrictEquals( + isConcatSpreadable( + Object.assign([], { [Symbol.isConcatSpreadable]: undefined }), + ), + true, + ); + }); + + it("[[Call]] returns true if [Symbol.isConcatSpreadable] is true", () => { + assertStrictEquals( + isConcatSpreadable({ [Symbol.isConcatSpreadable]: true }), + true, + ); + }); +}); + +describe("isIntegerIndexString", () => { + it("[[Call]] returns false for nonstrings", () => { + assertStrictEquals(isIntegerIndexString(1), false); + }); + + it("[[Call]] returns false for noncanonical strings", () => { + assertStrictEquals(isIntegerIndexString(""), false); + assertStrictEquals(isIntegerIndexString("01"), false); + assertStrictEquals( + isIntegerIndexString("9007199254740993"), + false, + ); + }); + + it("[[Call]] returns false for nonfinite numbers", () => { + assertStrictEquals(isIntegerIndexString("NaN"), false); + assertStrictEquals(isIntegerIndexString("Infinity"), false); + assertStrictEquals(isIntegerIndexString("-Infinity"), false); + }); + + it("[[Call]] returns false for negative numbers", () => { + assertStrictEquals(isIntegerIndexString("-0"), false); + assertStrictEquals(isIntegerIndexString("-1"), false); + }); + + it("[[Call]] returns false for nonintegers", () => { + assertStrictEquals(isIntegerIndexString("0.25"), false); + assertStrictEquals(isIntegerIndexString("1.1"), false); + }); + + it("[[Call]] returns false for numbers greater than or equal to 2 ** 53", () => { + assertStrictEquals( + isIntegerIndexString("9007199254740992"), + false, + ); + }); + + it("[[Call]] returns true for safe canonical integer strings", () => { + assertStrictEquals(isIntegerIndexString("0"), true); + assertStrictEquals(isIntegerIndexString("9007199254740991"), true); + }); +}); + +describe("lengthOfArrayLike", () => { + it("[[Call]] returns the length", () => { + assertStrictEquals( + lengthOfArrayLike({ length: 9007199254740991 }), + 9007199254740991, + ); + }); + + it("[[Call]] returns a non·nan result", () => { + assertStrictEquals(lengthOfArrayLike({ length: NaN }), 0); + assertStrictEquals(lengthOfArrayLike({ length: "failure" }), 0); + }); + + it("[[Call]] returns an integral result", () => { + assertStrictEquals(lengthOfArrayLike({ length: 0.25 }), 0); + assertStrictEquals(lengthOfArrayLike({ length: 1.1 }), 1); + }); + + it("[[Call]] returns a result greater than or equal to zero", () => { + assertStrictEquals(lengthOfArrayLike({ length: -0 }), 0); + assertStrictEquals(lengthOfArrayLike({ length: -1 }), 0); + assertStrictEquals(lengthOfArrayLike({ length: -Infinity }), 0); + }); + + it("[[Call]] returns a result less than 2 ** 53", () => { + assertStrictEquals( + lengthOfArrayLike({ length: 9007199254740992 }), + 9007199254740991, + ); + assertStrictEquals( + lengthOfArrayLike({ length: Infinity }), + 9007199254740991, + ); + }); +}); + +describe("toIndex", () => { + it("[[Call]] returns an index", () => { + assertStrictEquals(toIndex(9007199254740991), 9007199254740991); + }); + + it("[[Call]] returns zero for a zerolike result", () => { + assertStrictEquals(toIndex(NaN), 0); + assertStrictEquals(toIndex("failure"), 0); + assertStrictEquals(toIndex(-0), 0); + }); + + it("[[Call]] rounds down to the nearest integer", () => { + assertStrictEquals(toIndex(0.25), 0); + assertStrictEquals(toIndex(1.1), 1); + }); + + it("[[Call]] throws when provided a negative number", () => { + assertThrows(() => toIndex(-1)); + assertThrows(() => toIndex(-Infinity)); + }); + + it("[[Call]] throws when provided a number greater than or equal to 2 ** 53", () => { + assertThrows(() => toIndex(9007199254740992)); + assertThrows(() => toIndex(Infinity)); + }); +}); + +describe("toLength", () => { + it("[[Call]] returns a length", () => { + assertStrictEquals(toLength(9007199254740991), 9007199254740991); + }); + + it("[[Call]] returns a non·nan result", () => { + assertStrictEquals(toLength(NaN), 0); + assertStrictEquals(toLength("failure"), 0); + }); + + it("[[Call]] returns an integral result", () => { + assertStrictEquals(toLength(0.25), 0); + assertStrictEquals(toLength(1.1), 1); + }); + + it("[[Call]] returns a result greater than or equal to zero", () => { + assertStrictEquals(toLength(-0), 0); + assertStrictEquals(toLength(-1), 0); + assertStrictEquals(toLength(-Infinity), 0); + }); + + it("[[Call]] returns a result less than 2 ** 53", () => { + assertStrictEquals(toLength(9007199254740992), 9007199254740991); + assertStrictEquals(toLength(Infinity), 9007199254740991); + }); +});