1 // SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: MPL-2.0
4 * ⁌ ♓🧩 Piscēs ∷ collection.js
6 * Copyright © 2022–2023, 2025 Lady [@ Ladys Computer].
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
22 } from "./dev-deps.js";
25 concatSpreadableCatenate
,
31 findFirstIndexedEntry
,
37 flatten as _flatten
/* TK */,
38 getFirstIndex as _getFirstIndex
/* TK */,
39 getItem as _getItem
/* TK */,
40 getLastIndex as _getLastIndex
/* TK */,
41 hasEvery as _hasEvery
/* TK */,
42 hasSome as _hasSome
/* TK */,
43 includes as _includes
/* TK */,
44 indexedEntries as _indexedEntries
/* TK */,
45 indices as _indices
/* TK */,
46 isArray as _isArray
/* TK */,
49 items as _items
/* TK */,
52 push as _push
/* TK */,
53 reduce as _reduce
/* TK */,
54 reverse as _reverse
/* TK */,
55 shift as _shift
/* TK */,
56 slice as _slice
/* TK */,
57 sort as _sort
/* TK */,
58 splice as _splice
/* TK */,
61 unshift as _unshift
/* TK */,
62 } from "./collection.js";
64 describe("array", () => {
65 it("[[Call]] returns an array of the provided values", () => {
66 assertEquals(array("etaoin", [], true), ["etaoin", [], true]);
69 it("[[Call]] returns an empty array with no arguments", () => {
70 assertEquals(array(), []);
73 it("[[Construct]] throws an error", () => {
74 assertThrows(() => new array());
77 describe(".length", () => {
78 it("[[Get]] returns the correct length", () => {
79 assertStrictEquals(array
.length
, 0);
83 describe(".name", () => {
84 it("[[Get]] returns the correct name", () => {
85 assertStrictEquals(array
.name
, "array");
90 describe("concatSpreadableCatenate", () => {
91 it("[[Call]] catenates concat spreadable values", () => {
93 concatSpreadableCatenate([1, 2], [2, [3]], {
95 [Symbol
.isConcatSpreadable
]: true,
101 it("[[Call]] does not catenate other values", () => {
102 assertEquals(concatSpreadableCatenate({}, "etaoin"), [
108 it("[[Call]] allows a nullish first argument", () => {
109 assertEquals(concatSpreadableCatenate(null), [null]);
110 assertEquals(concatSpreadableCatenate(undefined), [undefined]);
113 it("[[Call]] returns an empty array with no arguments", () => {
114 assertEquals(concatSpreadableCatenate(), []);
117 it("[[Construct]] throws an error", () => {
118 assertThrows(() => new concatSpreadableCatenate());
121 describe(".length", () => {
122 it("[[Get]] returns the correct length", () => {
123 assertStrictEquals(concatSpreadableCatenate
.length
, 2);
127 describe(".name", () => {
128 it("[[Get]] returns the correct name", () => {
130 concatSpreadableCatenate
.name
,
131 "concatSpreadableCatenate",
137 describe("copyWithin", () => {
138 it("[[Call]] copies within", () => {
140 copyWithin(["a", "b", "c", , "e"], 0, 3, 4),
144 copyWithin(["a", "b", "c", , "e"], 1, 3),
149 it("[[Construct]] throws an error", () => {
150 assertThrows(() => new copyWithin([], 0, 0));
153 describe(".length", () => {
154 it("[[Get]] returns the correct length", () => {
155 assertStrictEquals(copyWithin
.length
, 3);
159 describe(".name", () => {
160 it("[[Get]] returns the correct name", () => {
161 assertStrictEquals(copyWithin
.name
, "copyWithin");
166 describe("denseProxy", () => {
167 const makeUnderlying
= () =>
168 Object
.create({ 2: "inherited" }, {
170 1: { value: "static", configurable: true, writable: true },
171 // 2 is not an own property, but is present on the prototype
172 3: { configurable: true, get: () => "dynamic" },
173 length: { value: "4", configurable: true, writable: true },
176 it("[[Call]] returns an object which inherits from the provided object", () => {
177 const underlying
= makeUnderlying();
178 const proxy
= denseProxy(underlying
);
180 Object
.getPrototypeOf(proxy
),
185 it("[[Construct]] throws an error", () => {
186 assertThrows(() => new denseProxy([]));
189 it("[[OwnPropertyKeys]] lists integer indices, then the length, then other keys", () => {
190 const underlying
= makeUnderlying();
191 const proxy
= denseProxy(underlying
);
192 const sym
= Symbol();
193 proxy
[sym
] = "shrdlu";
194 proxy
["1.2.3"] = "etaion";
196 Reflect
.ownKeys(proxy
),
197 ["0", "1", "2", "3", "length", "1.2.3", sym
],
201 it("[[PreventExtensions]] fails if the underlying object is extensible", () => {
202 const underlying
= makeUnderlying();
203 const proxy
= denseProxy(underlying
);
205 Reflect
.preventExtensions(proxy
),
210 it("[[PreventExtensions]] fails if the underlying object has a nonconstant length", () => {
211 const underlying
= makeUnderlying();
212 const proxy
= denseProxy(underlying
);
213 Object
.defineProperty(underlying
, "length", { get: () => 4 });
214 Object
.freeze(underlying
);
216 Reflect
.preventExtensions(proxy
),
221 it("[[PreventExtensions]] succeeds if the underlying object is non·extensible and has a constant length", () => {
222 const underlying
= makeUnderlying();
223 const proxy
= denseProxy(underlying
);
224 Object
.defineProperty(underlying
, "length", {
228 Object
.preventExtensions(underlying
);
230 Reflect
.preventExtensions(proxy
),
235 it("[[SetPrototypeOf]] fails to change the prototype", () => {
236 const underlying
= makeUnderlying();
237 const proxy
= denseProxy(underlying
);
239 Reflect
.setPrototypeOf(proxy
, {}),
244 it("[[SetPrototypeOf]] succeeds keeping the prototype the same", () => {
245 const underlying
= makeUnderlying();
246 const proxy
= denseProxy(underlying
);
248 Reflect
.setPrototypeOf(proxy
, underlying
),
253 describe(".length", () => {
254 it("[[Get]] returns the correct length", () => {
255 assertStrictEquals(denseProxy
.length
, 1);
259 describe(".name", () => {
260 it("[[Get]] returns the correct name", () => {
261 assertStrictEquals(denseProxy
.name
, "denseProxy");
265 describe("~[]", () => {
266 it("[[DefineProperty]] allows changes when the property is not an index property or length", () => {
267 const underlying
= makeUnderlying();
268 const proxy
= denseProxy(underlying
);
269 const newValue
= Symbol();
270 proxy
.etaoin
= newValue
;
277 it("[[DefineProperty]] allows changing nothing when the property is not an own property", () => {
278 const underlying
= makeUnderlying();
279 const proxy
= denseProxy(underlying
);
281 Reflect
.defineProperty(
294 Reflect
.defineProperty(
306 /* test nonconfigurable versions too */
307 Object
.freeze(underlying
);
308 Object
.defineProperty(proxy
, "0", { configurable: false });
309 Object
.defineProperty(proxy
, "2", { configurable: false });
311 Reflect
.defineProperty(
314 Object
.getOwnPropertyDescriptor(proxy
, "0"),
319 Reflect
.defineProperty(
322 Object
.getOwnPropertyDescriptor(proxy
, "2"),
328 it("[[DefineProperty]] allows changing nothing when the property is a data index property", () => {
329 const underlying
= makeUnderlying();
330 const proxy
= denseProxy(underlying
);
332 Reflect
.defineProperty(
339 value: underlying
[1],
344 /* test nonconfigurable versions too */
345 Object
.freeze(underlying
);
346 Object
.defineProperty(proxy
, "1", { configurable: false });
348 Reflect
.defineProperty(
351 Object
.getOwnPropertyDescriptor(proxy
, "1"),
357 it("[[DefineProperty]] allows changing nothing when the property is an accessor index property", () => {
358 const underlying
= makeUnderlying();
359 const proxy
= denseProxy(underlying
);
361 Reflect
.defineProperty(
368 value: underlying
[3],
373 /* test nonconfigurable versions too */
374 Object
.freeze(underlying
);
375 Object
.defineProperty(proxy
, "1", { configurable: false });
377 Reflect
.defineProperty(
380 Object
.getOwnPropertyDescriptor(proxy
, "1"),
386 it("[[DefineProperty]] does not allow a change in enumerablility on index properties", () => {
387 const underlying
= makeUnderlying();
388 const proxy
= denseProxy(underlying
);
389 for (let i
= 0; i
< 4; ++i
) {
391 Reflect
.defineProperty(proxy
, i
, { enumerable: false }),
397 it("[[DefineProperty]] does not allow a change in value on index properties", () => {
398 const underlying
= makeUnderlying();
399 const proxy
= denseProxy(underlying
);
400 for (let i
= 0; i
< 4; ++i
) {
402 Reflect
.defineProperty(proxy
, i
, { value: "new value" })
411 it("[[DefineProperty]] does not allow a change in getter on index properties", () => {
412 const underlying
= makeUnderlying();
413 const proxy
= denseProxy(underlying
);
414 for (let i
= 0; i
< 4; ++i
) {
416 Reflect
.defineProperty(proxy
, i
, {
417 get: () => underlying
[i
],
426 it("[[DefineProperty]] does not allow a change in setter on index properties", () => {
427 const underlying
= makeUnderlying();
428 const proxy
= denseProxy(underlying
);
429 for (let i
= 0; i
< 4; ++i
) {
431 Reflect
.defineProperty(proxy
, i
, { set: () => undefined }),
437 it("[[DefineProperty]] does not allow a change in configurability on index properties if the property in the underlying object may change", () => {
438 const underlying
= makeUnderlying();
439 const proxy
= denseProxy(underlying
);
440 Object
.defineProperty(underlying
, "length", {
444 for (let i
= 0; i
< 4; ++i
) {
446 Reflect
.defineProperty(proxy
, i
, { configurable: false }),
452 it("[[DefineProperty]] does not allow a change in configurability on index properties if the length of the underlying object may change", () => {
453 const underlying
= makeUnderlying();
454 const proxy
= denseProxy(underlying
);
455 Object
.seal(underlying
);
456 for (let i
= 0; i
< 4; ++i
) {
458 Reflect
.defineProperty(proxy
, i
, { configurable: false }),
464 it("[[DefineProperty]] allows a change in configurability on index properties when it is safe to do so", () => {
465 const underlying
= makeUnderlying();
466 const proxy
= denseProxy(underlying
);
467 Object
.defineProperty(underlying
, "length", {
471 Object
.defineProperty(underlying
, "1", {
475 Object
.defineProperty(underlying
, "3", { configurable: false });
476 Object
.preventExtensions(underlying
);
477 for (let i
= 0; i
< 4; ++i
) {
479 Reflect
.defineProperty(proxy
, i
, { configurable: false }),
485 it("[[Delete]] is allowed when the property is not an index property or length", () => {
486 const underlying
= makeUnderlying();
487 const proxy
= denseProxy(underlying
);
490 Reflect
.deleteProperty(proxy
, "a"),
494 Reflect
.has(proxy
, "a"),
499 it("[[Delete]] is forbidden for index properties less than the length", () => {
500 const underlying
= makeUnderlying();
501 const proxy
= denseProxy(underlying
);
502 for (let i
= 0; i
< 4; ++i
) {
504 Reflect
.deleteProperty(proxy
, i
),
510 it("[[Delete]] is allowed for index properties greater than or equal to the length", () => {
511 const underlying
= makeUnderlying();
512 const proxy
= denseProxy(underlying
);
514 Reflect
.deleteProperty(proxy
, "4"),
518 Reflect
.deleteProperty(proxy
, "5"),
523 it("[[GetOwnProperty]] gives the value of an index property as a data property by default", () => {
524 const underlying
= makeUnderlying();
525 const proxy
= denseProxy(underlying
);
527 Object
.getOwnPropertyDescriptor(proxy
, 1),
531 value: underlying
[1],
536 Object
.getOwnPropertyDescriptor(proxy
, 3),
540 value: underlying
[3],
544 /* test nonconfigurable data properties too */
545 Object
.freeze(underlying
);
546 Object
.defineProperty(proxy
, "1", { configurable: false });
548 Object
.getOwnPropertyDescriptor(proxy
, 1),
552 value: underlying
[1],
558 it("[[GetOwnProperty]] gives a value of undefined if the underlying object does not have an index property as an own property", () => {
559 const underlying
= makeUnderlying();
560 const proxy
= denseProxy(underlying
);
562 Object
.getOwnPropertyDescriptor(proxy
, 0),
571 Object
.getOwnPropertyDescriptor(proxy
, 2),
581 it("[[GetOwnProperty]] gives a value of undefined for index properties less than the length", () => {
582 const underlying
= makeUnderlying();
583 underlying
.length
= 0;
584 const proxy
= denseProxy(underlying
);
585 for (let i
= 0; i
< 4; ++i
) {
587 Object
.getOwnPropertyDescriptor(proxy
, i
),
593 it("[[GetOwnProperty]] gives a getter when the underlying object has a getter and an index property is not configurable", () => {
594 const underlying
= makeUnderlying();
595 const proxy
= denseProxy(underlying
);
596 Object
.freeze(underlying
);
597 Object
.defineProperty(proxy
, "3", { configurable: false });
598 const descriptor
= Object
.getOwnPropertyDescriptor(proxy
, 3);
610 describe("[[GetOwnProperty]].get.length", () => {
611 it("[[Get]] returns the correct length", () => {
612 const underlying
= makeUnderlying();
613 const proxy
= denseProxy(underlying
);
614 Object
.freeze(underlying
);
615 Object
.defineProperty(proxy
, "3", { configurable: false });
617 Object
.getOwnPropertyDescriptor(
626 describe("[[GetOwnProperty]].get.name", () => {
627 it("[[Get]] returns the correct name", () => {
628 const underlying
= makeUnderlying();
629 const proxy
= denseProxy(underlying
);
630 Object
.freeze(underlying
);
631 Object
.defineProperty(proxy
, "3", { configurable: false });
633 Object
.getOwnPropertyDescriptor(
642 it("[[HasProperty]] works when the property is not an index property or length", () => {
643 const underlying
= makeUnderlying();
644 const proxy
= denseProxy(underlying
);
646 Object
.getPrototypeOf(underlying
).b
= "shrdlu";
648 Reflect
.has(proxy
, "a"),
652 Reflect
.has(proxy
, "b"),
656 Reflect
.has(proxy
, "z"),
661 it("[[HasProperty]] works for index properties less than the length", () => {
662 const underlying
= makeUnderlying();
663 const proxy
= denseProxy(underlying
);
664 for (let i
= 0; i
< 4; ++i
) {
666 Reflect
.has(proxy
, i
),
670 delete underlying
.length
;
671 for (let i
= 0; i
< 4; ++i
) {
673 Reflect
.has(proxy
, i
),
674 Reflect
.has(underlying
, i
),
679 it("[[HasProperty]] works for index properties greater than or equal to the length", () => {
680 const underlying
= makeUnderlying();
681 const proxy
= denseProxy(underlying
);
683 Reflect
.has(proxy
, "4"),
687 Reflect
.has(proxy
, "5"),
690 underlying
[4] = "inherited now";
692 Reflect
.has(proxy
, "4"),
698 describe("~length", () => {
699 it("[[DefineProperty]] allows changing nothing", () => {
700 const underlying
= makeUnderlying();
701 const proxy
= denseProxy(underlying
);
703 Reflect
.defineProperty(
710 value: underlying
.length
>>> 0,
715 /* test nonconfigurable versions too */
716 Object
.freeze(underlying
);
717 Object
.defineProperty(proxy
, "length", { configurable: false });
719 Reflect
.defineProperty(
722 Object
.getOwnPropertyDescriptor(proxy
, "length"),
728 it("[[DefineProperty]] does not allow a change in enumerablility", () => {
729 const underlying
= makeUnderlying();
730 const proxy
= denseProxy(underlying
);
732 Reflect
.defineProperty(proxy
, "length", { enumerable: true }),
737 it("[[DefineProperty]] does not allow a change in value", () => {
738 const underlying
= makeUnderlying();
739 const proxy
= denseProxy(underlying
);
741 Reflect
.defineProperty(proxy
, "length", { value: 0 }),
746 it("[[DefineProperty]] does not allow a change in getter", () => {
747 const underlying
= makeUnderlying();
748 const proxy
= denseProxy(underlying
);
750 Reflect
.defineProperty(proxy
, "length", {
751 get: () => underlying
.length
>>> 0,
757 it("[[DefineProperty]] does not allow a change in setter", () => {
758 const underlying
= makeUnderlying();
759 const proxy
= denseProxy(underlying
);
761 Reflect
.defineProperty(proxy
, "length", { set: () => {} }),
766 it("[[DefineProperty]] does not allow a change in configurability if the length of the underlying object may change", () => {
767 const underlying
= makeUnderlying();
768 const proxy
= denseProxy(underlying
);
770 Reflect
.defineProperty(proxy
, "length", {
775 Object
.defineProperty(underlying
, "length", {
780 Reflect
.defineProperty(proxy
, "length", {
787 it("[[DefineProperty]] allows a change in configurability when it is safe to do so", () => {
788 const underlying
= makeUnderlying();
789 const proxy
= denseProxy(underlying
);
790 Object
.defineProperty(underlying
, "length", {
795 Reflect
.defineProperty(proxy
, "length", {
802 it("[[Delete]] is forbidden", () => {
803 const underlying
= makeUnderlying();
804 const proxy
= denseProxy(underlying
);
806 Reflect
.deleteProperty(
814 it("[[GetOwnProperty]] gives the value as a data property", () => {
815 const underlying
= makeUnderlying();
816 const proxy
= denseProxy(underlying
);
818 Object
.getOwnPropertyDescriptor(proxy
, "length"),
822 value: underlying
.length
>>> 0,
826 /* test nonconfigurable data properties too */
827 Object
.freeze(underlying
);
828 Object
.defineProperty(proxy
, "length", { configurable: false });
830 Object
.getOwnPropertyDescriptor(proxy
, "length"),
834 value: underlying
.length
>>> 0,
840 it("[[GetOwnProperty]] gives 0 if the underlying object does not have the property as an own property", () => {
841 const underlying
= makeUnderlying();
842 const proxy
= denseProxy(underlying
);
843 delete underlying
.length
;
844 Object
.getPrototypeOf(underlying
).length
= 3;
846 Object
.getOwnPropertyDescriptor(proxy
, "length"),
856 it("[[HasProperty]] is always true", () => {
857 const underlying
= makeUnderlying();
858 const proxy
= denseProxy(underlying
);
860 Reflect
.has(proxy
, "length"),
863 delete underlying
.length
;
865 Reflect
.has(proxy
, "length"),
872 describe("fill", () => {
873 it("[[Call]] fills", () => {
875 fill({ 1: "failure", length: 3 }, "success"),
876 { 0: "success", 1: "success", 2: "success", length: 3 },
880 it("[[Call]] can fill a range", () => {
882 fill({ 1: "failure", length: 4 }, "success", 1, 3),
883 { 1: "success", 2: "success", length: 4 },
887 it("[[Construct]] throws an error", () => {
888 assertThrows(() => new fill([], null));
891 describe(".length", () => {
892 it("[[Get]] returns the correct length", () => {
893 assertStrictEquals(fill
.length
, 2);
897 describe(".name", () => {
898 it("[[Get]] returns the correct name", () => {
899 assertStrictEquals(fill
.name
, "fill");
904 describe("filter", () => {
905 it("[[Call]] filters", () => {
907 filter(["failure", "success", ,], function ($) {
908 return !$ || $ == this;
914 it("[[Construct]] throws an error", () => {
915 assertThrows(() => new filter([], () => {}));
918 describe(".length", () => {
919 it("[[Get]] returns the correct length", () => {
920 assertStrictEquals(filter
.length
, 2);
924 describe(".name", () => {
925 it("[[Get]] returns the correct name", () => {
926 assertStrictEquals(filter
.name
, "filter");
931 describe("findFirstIndex", () => {
932 it("[[Call]] returns undefined if no matching entry exists", () => {
933 assertStrictEquals(findFirstIndex([], () => true), undefined);
934 assertStrictEquals(findFirstIndex([1], () => false), undefined);
937 it("[[Call]] returns an index for the first match", () => {
939 findFirstIndex([, true, false], ($) => $ ?? true),
944 ["failure", "success", "success"],
945 ($) => $ == "success",
951 it("[[Call]] works on arraylike objects", () => {
953 findFirstIndex({ 1: "success", length: 2 }, ($) => $),
957 findFirstIndex({ 1: "failure", length: 1 }, ($) => $),
962 it("[[Call]] only gets the value once", () => {
963 const get1
= spy(() => true);
970 assertSpyCalls(get1
, 1);
973 it("[[Call]] passes the value, index, and this value to the callback", () => {
974 const arr
= [, "failure", "success", "success"];
975 const callback
= spy(($) => $ === "success");
977 findFirstIndex(arr
, callback
, thisArg
);
978 assertSpyCalls(callback
, 2);
979 assertSpyCall(callback
, 0, {
980 args: ["failure", 1, arr
],
983 assertSpyCall(callback
, 1, {
984 args: ["success", 2, arr
],
989 it("[[Construct]] throws an error", () => {
990 assertThrows(() => new findFirstIndex([], () => {}));
993 describe(".length", () => {
994 it("[[Get]] returns the correct length", () => {
995 assertStrictEquals(findFirstIndex
.length
, 2);
999 describe(".name", () => {
1000 it("[[Get]] returns the correct name", () => {
1001 assertStrictEquals(findFirstIndex
.name
, "findFirstIndex");
1006 describe("findFirstIndexedEntry", () => {
1007 it("[[Call]] returns undefined if no matching entry exists", () => {
1009 findFirstIndexedEntry([], () => true),
1013 findFirstIndexedEntry([1], () => false),
1018 it("[[Call]] returns an entry for the first match", () => {
1020 findFirstIndexedEntry([, true, false], ($) => $ ?? true),
1024 findFirstIndexedEntry(
1025 ["failure", "success", "success"],
1026 ($) => $ == "success",
1032 it("[[Call]] works on arraylike objects", () => {
1034 findFirstIndexedEntry({ 1: "success", length: 2 }, ($) => $),
1038 findFirstIndexedEntry({ 1: "failure", length: 1 }, ($) => $),
1043 it("[[Call]] only gets the value once", () => {
1044 const get1
= spy(() => true);
1045 findFirstIndexedEntry({
1051 assertSpyCalls(get1
, 1);
1054 it("[[Call]] passes the value, index, and this value to the callback", () => {
1055 const arr
= [, "failure", "success", "success"];
1056 const callback
= spy(($) => $ === "success");
1058 findFirstIndexedEntry(arr
, callback
, thisArg
);
1059 assertSpyCalls(callback
, 2);
1060 assertSpyCall(callback
, 0, {
1061 args: ["failure", 1, arr
],
1064 assertSpyCall(callback
, 1, {
1065 args: ["success", 2, arr
],
1070 it("[[Construct]] throws an error", () => {
1071 assertThrows(() => new findFirstIndexedEntry([], () => {}));
1074 describe(".length", () => {
1075 it("[[Get]] returns the correct length", () => {
1076 assertStrictEquals(findFirstIndexedEntry
.length
, 2);
1080 describe(".name", () => {
1081 it("[[Get]] returns the correct name", () => {
1083 findFirstIndexedEntry
.name
,
1084 "findFirstIndexedEntry",
1090 describe("findFirstItem", () => {
1091 it("[[Call]] returns undefined if no matching item exists", () => {
1092 assertStrictEquals(findFirstItem([], () => true), undefined);
1093 assertStrictEquals(findFirstItem([1], () => false), undefined);
1096 it("[[Call]] returns the first match", () => {
1098 findFirstItem([, true, false], ($) => $ ?? true),
1103 ["failure", "success", "success"],
1104 ($) => $ == "success",
1110 it("[[Call]] works on arraylike objects", () => {
1112 findFirstItem({ 1: "success", length: 2 }, ($) => $),
1116 findFirstItem({ 1: "failure", length: 1 }, ($) => $),
1121 it("[[Call]] only gets the value once", () => {
1122 const get1
= spy(() => true);
1129 assertSpyCalls(get1
, 1);
1132 it("[[Call]] passes the value, index, and this value to the callback", () => {
1133 const arr
= [, "failure", "success", "success"];
1134 const callback
= spy(($) => $ === "success");
1136 findFirstItem(arr
, callback
, thisArg
);
1137 assertSpyCalls(callback
, 2);
1138 assertSpyCall(callback
, 0, {
1139 args: ["failure", 1, arr
],
1142 assertSpyCall(callback
, 1, {
1143 args: ["success", 2, arr
],
1148 it("[[Construct]] throws an error", () => {
1149 assertThrows(() => new findFirstItem([], () => {}));
1152 describe(".length", () => {
1153 it("[[Get]] returns the correct length", () => {
1154 assertStrictEquals(findFirstItem
.length
, 2);
1158 describe(".name", () => {
1159 it("[[Get]] returns the correct name", () => {
1160 assertStrictEquals(findFirstItem
.name
, "findFirstItem");
1165 describe("findLastIndex", () => {
1166 it("[[Call]] returns undefined if no matching entry exists", () => {
1167 assertStrictEquals(findLastIndex([], () => true), undefined);
1168 assertStrictEquals(findLastIndex([1], () => false), undefined);
1171 it("[[Call]] returns an index for the first match", () => {
1173 findLastIndex([true, false, ,], ($) => $ ?? true),
1178 ["success", "success", "failure"],
1179 ($) => $ == "success",
1185 it("[[Call]] works on arraylike objects", () => {
1187 findLastIndex({ 1: "success", length: 2 }, ($) => $),
1191 findLastIndex({ 1: "failure", length: 1 }, ($) => $),
1196 it("[[Call]] only gets the value once", () => {
1197 const get1
= spy(() => true);
1204 assertSpyCalls(get1
, 1);
1207 it("[[Call]] passes the value, index, and this value to the callback", () => {
1208 const arr
= ["success", "success", "failure", ,];
1209 const callback
= spy(($) => $ === "success");
1211 findLastIndex(arr
, callback
, thisArg
);
1212 assertSpyCalls(callback
, 2);
1213 assertSpyCall(callback
, 0, {
1214 args: ["failure", 2, arr
],
1217 assertSpyCall(callback
, 1, {
1218 args: ["success", 1, arr
],
1223 it("[[Construct]] throws an error", () => {
1224 assertThrows(() => new findLastIndex([], () => {}));
1227 describe(".length", () => {
1228 it("[[Get]] returns the correct length", () => {
1229 assertStrictEquals(findLastIndex
.length
, 2);
1233 describe(".name", () => {
1234 it("[[Get]] returns the correct name", () => {
1235 assertStrictEquals(findLastIndex
.name
, "findLastIndex");
1240 describe("findLastIndexedEntry", () => {
1241 it("[[Call]] returns undefined if no matching entry exists", () => {
1243 findLastIndexedEntry([], () => true),
1247 findLastIndexedEntry([1], () => false),
1252 it("[[Call]] returns an index for the first match", () => {
1254 findLastIndexedEntry([true, false, ,], ($) => $ ?? true),
1258 findLastIndexedEntry(
1259 ["success", "success", "failure"],
1260 ($) => $ == "success",
1266 it("[[Call]] works on arraylike objects", () => {
1268 findLastIndexedEntry({ 1: "success", length: 2 }, ($) => $),
1272 findLastIndexedEntry({ 1: "failure", length: 1 }, ($) => $),
1277 it("[[Call]] only gets the value once", () => {
1278 const get1
= spy(() => true);
1279 findLastIndexedEntry({
1285 assertSpyCalls(get1
, 1);
1288 it("[[Call]] passes the value, index, and this value to the callback", () => {
1289 const arr
= ["success", "success", "failure", ,];
1290 const callback
= spy(($) => $ === "success");
1292 findLastIndexedEntry(arr
, callback
, thisArg
);
1293 assertSpyCalls(callback
, 2);
1294 assertSpyCall(callback
, 0, {
1295 args: ["failure", 2, arr
],
1298 assertSpyCall(callback
, 1, {
1299 args: ["success", 1, arr
],
1304 it("[[Construct]] throws an error", () => {
1305 assertThrows(() => new findLastIndexedEntry([], () => {}));
1308 describe(".length", () => {
1309 it("[[Get]] returns the correct length", () => {
1310 assertStrictEquals(findLastIndexedEntry
.length
, 2);
1314 describe(".name", () => {
1315 it("[[Get]] returns the correct name", () => {
1317 findLastIndexedEntry
.name
,
1318 "findLastIndexedEntry",
1324 describe("findLastItem", () => {
1325 it("[[Call]] returns undefined if no matching entry exists", () => {
1326 assertStrictEquals(findLastItem([], () => true), undefined);
1327 assertStrictEquals(findLastItem([1], () => false), undefined);
1330 it("[[Call]] returns an index for the first match", () => {
1332 findLastItem([true, false, ,], ($) => $ ?? true),
1337 ["success", "success", "failure"],
1338 ($) => $ == "success",
1344 it("[[Call]] works on arraylike objects", () => {
1346 findLastItem({ 1: "success", length: 2 }, ($) => $),
1350 findLastItem({ 1: "failure", length: 1 }, ($) => $),
1355 it("[[Call]] only gets the value once", () => {
1356 const get1
= spy(() => true);
1363 assertSpyCalls(get1
, 1);
1366 it("[[Call]] passes the value, index, and this value to the callback", () => {
1367 const arr
= ["success", "success", "failure", ,];
1368 const callback
= spy(($) => $ === "success");
1370 findLastItem(arr
, callback
, thisArg
);
1371 assertSpyCalls(callback
, 2);
1372 assertSpyCall(callback
, 0, {
1373 args: ["failure", 2, arr
],
1376 assertSpyCall(callback
, 1, {
1377 args: ["success", 1, arr
],
1382 it("[[Construct]] throws an error", () => {
1383 assertThrows(() => new findLastItem([], () => {}));
1386 describe(".length", () => {
1387 it("[[Get]] returns the correct length", () => {
1388 assertStrictEquals(findLastItem
.length
, 2);
1392 describe(".name", () => {
1393 it("[[Get]] returns the correct name", () => {
1394 assertStrictEquals(findLastItem
.name
, "findLastItem");
1399 describe("flatmap", () => {
1400 it("[[Call]] flatmaps", () => {
1402 flatmap([, "a", ["b"], [["c"]]], ($) => $),
1407 it("[[Construct]] throws an error", () => {
1408 assertThrows(() => new flatmap([], () => {}));
1411 describe(".length", () => {
1412 it("[[Get]] returns the correct length", () => {
1413 assertStrictEquals(flatmap
.length
, 2);
1417 describe(".name", () => {
1418 it("[[Get]] returns the correct name", () => {
1419 assertStrictEquals(flatmap
.name
, "flatmap");
1424 describe("isCollection", () => {
1425 it("[[Call]] returns false for primitives", () => {
1426 assertStrictEquals(isCollection("failure"), false);
1429 it("[[Call]] returns false if length throws", () => {
1440 it("[[Call]] returns false if length is not an integer index and cannot be converted to one", () => {
1442 isCollection({ length: -1, [Symbol
.isConcatSpreadable
]: true }),
1448 [Symbol
.isConcatSpreadable
]: true,
1454 length: 9007199254740992,
1455 [Symbol
.isConcatSpreadable
]: true,
1461 it("[[Call]] returns true if length is an integer index and the object is concat spreadable", () => {
1463 isCollection({ length: 1, [Symbol
.isConcatSpreadable
]: true }),
1467 isCollection({ length: 0, [Symbol
.isConcatSpreadable
]: true }),
1472 length: 9007199254740991,
1473 [Symbol
.isConcatSpreadable
]: true,
1479 it("[[Call]] returns true if length can be converted to an index without throwing an error and the object is concat spreadable", () => {
1481 isCollection({ length: -0, [Symbol
.isConcatSpreadable
]: true }),
1485 isCollection({ length: NaN
, [Symbol
.isConcatSpreadable
]: true }),
1491 describe("isDenseProxy", () => {
1492 it("[[Call]] returns true for dense proxies", () => {
1493 const underlying
= [];
1494 const proxy
= denseProxy(underlying
);
1496 isDenseProxy(proxy
),
1501 it("[[Call]] returns false for others", () => {
1508 it("[[Construct]] throws an error", () => {
1509 assertThrows(() => new isDenseProxy([]));
1512 describe(".length", () => {
1513 it("[[Get]] returns the correct length", () => {
1514 assertStrictEquals(isDenseProxy
.length
, 1);
1518 describe(".name", () => {
1519 it("[[Get]] returns the correct name", () => {
1520 assertStrictEquals(isDenseProxy
.name
, "isDenseProxy");
1525 describe("toArray", () => {
1526 it("[[Call]] returns an array of the values in a provided arraylike", () => {
1528 toArray({ 1: "success", length: 3 }),
1534 describe("toDenseArray", () => {
1535 it("[[Call]] returns a dense array of the values in a provided arraylike", () => {
1537 toDenseArray({ 1: "success", length: 3 }),
1538 [undefined, "success", undefined],