]> Lady’s Gitweb - Pisces/blob - collection.test.js
Add denseProxy, various collection.js improvements
[Pisces] / collection.test.js
1 // SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: MPL-2.0
3 /**
4 * ⁌ ♓🧩 Piscēs ∷ collection.js
5 *
6 * Copyright © 2022–2023, 2025 Lady [@ Ladys Computer].
7 *
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/>.
11 */
12
13 import {
14 assertEquals,
15 assertSpyCall,
16 assertSpyCalls,
17 assertStrictEquals,
18 assertThrows,
19 describe,
20 it,
21 spy,
22 } from "./dev-deps.js";
23 import {
24 array,
25 concatSpreadableCatenate,
26 copyWithin,
27 denseProxy,
28 fill,
29 filter,
30 findFirstIndex,
31 findFirstIndexedEntry,
32 findFirstItem,
33 findLastIndex,
34 findLastIndexedEntry,
35 findLastItem,
36 flatmap,
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 */,
47 isCollection,
48 isDenseProxy,
49 items as _items /* TK */,
50 map as _map /* TK */,
51 pop as _pop /* 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 */,
59 toArray,
60 toDenseArray,
61 unshift as _unshift /* TK */,
62 } from "./collection.js";
63
64 describe("array", () => {
65 it("[[Call]] returns an array of the provided values", () => {
66 assertEquals(array("etaoin", [], true), ["etaoin", [], true]);
67 });
68
69 it("[[Call]] returns an empty array with no arguments", () => {
70 assertEquals(array(), []);
71 });
72
73 it("[[Construct]] throws an error", () => {
74 assertThrows(() => new array());
75 });
76
77 describe(".length", () => {
78 it("[[Get]] returns the correct length", () => {
79 assertStrictEquals(array.length, 0);
80 });
81 });
82
83 describe(".name", () => {
84 it("[[Get]] returns the correct name", () => {
85 assertStrictEquals(array.name, "array");
86 });
87 });
88 });
89
90 describe("concatSpreadableCatenate", () => {
91 it("[[Call]] catenates concat spreadable values", () => {
92 assertEquals(
93 concatSpreadableCatenate([1, 2], [2, [3]], {
94 length: 1,
95 [Symbol.isConcatSpreadable]: true,
96 }),
97 [1, 2, 2, [3], ,],
98 );
99 });
100
101 it("[[Call]] does not catenate other values", () => {
102 assertEquals(concatSpreadableCatenate({}, "etaoin"), [
103 {},
104 "etaoin",
105 ]);
106 });
107
108 it("[[Call]] allows a nullish first argument", () => {
109 assertEquals(concatSpreadableCatenate(null), [null]);
110 assertEquals(concatSpreadableCatenate(undefined), [undefined]);
111 });
112
113 it("[[Call]] returns an empty array with no arguments", () => {
114 assertEquals(concatSpreadableCatenate(), []);
115 });
116
117 it("[[Construct]] throws an error", () => {
118 assertThrows(() => new concatSpreadableCatenate());
119 });
120
121 describe(".length", () => {
122 it("[[Get]] returns the correct length", () => {
123 assertStrictEquals(concatSpreadableCatenate.length, 2);
124 });
125 });
126
127 describe(".name", () => {
128 it("[[Get]] returns the correct name", () => {
129 assertStrictEquals(
130 concatSpreadableCatenate.name,
131 "concatSpreadableCatenate",
132 );
133 });
134 });
135 });
136
137 describe("copyWithin", () => {
138 it("[[Call]] copies within", () => {
139 assertEquals(
140 copyWithin(["a", "b", "c", , "e"], 0, 3, 4),
141 [, "b", "c", , "e"],
142 );
143 assertEquals(
144 copyWithin(["a", "b", "c", , "e"], 1, 3),
145 ["a", , "e", , "e"],
146 );
147 });
148
149 it("[[Construct]] throws an error", () => {
150 assertThrows(() => new copyWithin([], 0, 0));
151 });
152
153 describe(".length", () => {
154 it("[[Get]] returns the correct length", () => {
155 assertStrictEquals(copyWithin.length, 3);
156 });
157 });
158
159 describe(".name", () => {
160 it("[[Get]] returns the correct name", () => {
161 assertStrictEquals(copyWithin.name, "copyWithin");
162 });
163 });
164 });
165
166 describe("denseProxy", () => {
167 const makeUnderlying = () =>
168 Object.create({ 2: "inherited" }, {
169 // 0 is not present
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 },
174 });
175
176 it("[[Call]] returns an object which inherits from the provided object", () => {
177 const underlying = makeUnderlying();
178 const proxy = denseProxy(underlying);
179 assertStrictEquals(
180 Object.getPrototypeOf(proxy),
181 underlying,
182 );
183 });
184
185 it("[[Construct]] throws an error", () => {
186 assertThrows(() => new denseProxy([]));
187 });
188
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";
195 assertEquals(
196 Reflect.ownKeys(proxy),
197 ["0", "1", "2", "3", "length", "1.2.3", sym],
198 );
199 });
200
201 it("[[PreventExtensions]] fails if the underlying object is extensible", () => {
202 const underlying = makeUnderlying();
203 const proxy = denseProxy(underlying);
204 assertStrictEquals(
205 Reflect.preventExtensions(proxy),
206 false,
207 );
208 });
209
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);
215 assertStrictEquals(
216 Reflect.preventExtensions(proxy),
217 false,
218 );
219 });
220
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", {
225 configurable: false,
226 writable: false,
227 });
228 Object.preventExtensions(underlying);
229 assertStrictEquals(
230 Reflect.preventExtensions(proxy),
231 true,
232 );
233 });
234
235 it("[[SetPrototypeOf]] fails to change the prototype", () => {
236 const underlying = makeUnderlying();
237 const proxy = denseProxy(underlying);
238 assertStrictEquals(
239 Reflect.setPrototypeOf(proxy, {}),
240 false,
241 );
242 });
243
244 it("[[SetPrototypeOf]] succeeds keeping the prototype the same", () => {
245 const underlying = makeUnderlying();
246 const proxy = denseProxy(underlying);
247 assertStrictEquals(
248 Reflect.setPrototypeOf(proxy, underlying),
249 true,
250 );
251 });
252
253 describe(".length", () => {
254 it("[[Get]] returns the correct length", () => {
255 assertStrictEquals(denseProxy.length, 1);
256 });
257 });
258
259 describe(".name", () => {
260 it("[[Get]] returns the correct name", () => {
261 assertStrictEquals(denseProxy.name, "denseProxy");
262 });
263 });
264
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;
271 assertStrictEquals(
272 proxy.etaoin,
273 newValue,
274 );
275 });
276
277 it("[[DefineProperty]] allows changing nothing when the property is not an own property", () => {
278 const underlying = makeUnderlying();
279 const proxy = denseProxy(underlying);
280 assertStrictEquals(
281 Reflect.defineProperty(
282 proxy,
283 "0",
284 {
285 configurable: true,
286 enumerable: true,
287 writable: false,
288 value: undefined,
289 },
290 ),
291 true,
292 );
293 assertStrictEquals(
294 Reflect.defineProperty(
295 proxy,
296 "2",
297 {
298 configurable: true,
299 enumerable: true,
300 writable: false,
301 value: undefined,
302 },
303 ),
304 true,
305 );
306 /* test nonconfigurable versions too */
307 Object.freeze(underlying);
308 Object.defineProperty(proxy, "0", { configurable: false });
309 Object.defineProperty(proxy, "2", { configurable: false });
310 assertStrictEquals(
311 Reflect.defineProperty(
312 proxy,
313 "0",
314 Object.getOwnPropertyDescriptor(proxy, "0"),
315 ),
316 true,
317 );
318 assertStrictEquals(
319 Reflect.defineProperty(
320 proxy,
321 "2",
322 Object.getOwnPropertyDescriptor(proxy, "2"),
323 ),
324 true,
325 );
326 });
327
328 it("[[DefineProperty]] allows changing nothing when the property is a data index property", () => {
329 const underlying = makeUnderlying();
330 const proxy = denseProxy(underlying);
331 assertStrictEquals(
332 Reflect.defineProperty(
333 proxy,
334 "1",
335 {
336 configurable: true,
337 enumerable: true,
338 writable: false,
339 value: underlying[1],
340 },
341 ),
342 true,
343 );
344 /* test nonconfigurable versions too */
345 Object.freeze(underlying);
346 Object.defineProperty(proxy, "1", { configurable: false });
347 assertStrictEquals(
348 Reflect.defineProperty(
349 proxy,
350 "1",
351 Object.getOwnPropertyDescriptor(proxy, "1"),
352 ),
353 true,
354 );
355 });
356
357 it("[[DefineProperty]] allows changing nothing when the property is an accessor index property", () => {
358 const underlying = makeUnderlying();
359 const proxy = denseProxy(underlying);
360 assertStrictEquals(
361 Reflect.defineProperty(
362 proxy,
363 "3",
364 {
365 configurable: true,
366 enumerable: true,
367 writable: false,
368 value: underlying[3],
369 },
370 ),
371 true,
372 );
373 /* test nonconfigurable versions too */
374 Object.freeze(underlying);
375 Object.defineProperty(proxy, "1", { configurable: false });
376 assertStrictEquals(
377 Reflect.defineProperty(
378 proxy,
379 "1",
380 Object.getOwnPropertyDescriptor(proxy, "1"),
381 ),
382 true,
383 );
384 });
385
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) {
390 assertStrictEquals(
391 Reflect.defineProperty(proxy, i, { enumerable: false }),
392 false,
393 );
394 }
395 });
396
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) {
401 assertStrictEquals(
402 Reflect.defineProperty(proxy, i, { value: "new value" })
403 && (() => {
404 throw i;
405 })(),
406 false,
407 );
408 }
409 });
410
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) {
415 assertStrictEquals(
416 Reflect.defineProperty(proxy, i, {
417 get: () => underlying[i],
418 }) && (() => {
419 throw i;
420 })(),
421 false,
422 );
423 }
424 });
425
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) {
430 assertStrictEquals(
431 Reflect.defineProperty(proxy, i, { set: () => undefined }),
432 false,
433 );
434 }
435 });
436
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", {
441 configurable: false,
442 writable: false,
443 });
444 for (let i = 0; i < 4; ++i) {
445 assertStrictEquals(
446 Reflect.defineProperty(proxy, i, { configurable: false }),
447 false,
448 );
449 }
450 });
451
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) {
457 assertStrictEquals(
458 Reflect.defineProperty(proxy, i, { configurable: false }),
459 false,
460 );
461 }
462 });
463
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", {
468 configurable: false,
469 writable: false,
470 });
471 Object.defineProperty(underlying, "1", {
472 configurable: false,
473 writable: false,
474 });
475 Object.defineProperty(underlying, "3", { configurable: false });
476 Object.preventExtensions(underlying);
477 for (let i = 0; i < 4; ++i) {
478 assertStrictEquals(
479 Reflect.defineProperty(proxy, i, { configurable: false }),
480 true,
481 );
482 }
483 });
484
485 it("[[Delete]] is allowed when the property is not an index property or length", () => {
486 const underlying = makeUnderlying();
487 const proxy = denseProxy(underlying);
488 proxy.a = "etaoin";
489 assertStrictEquals(
490 Reflect.deleteProperty(proxy, "a"),
491 true,
492 );
493 assertStrictEquals(
494 Reflect.has(proxy, "a"),
495 false,
496 );
497 });
498
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) {
503 assertStrictEquals(
504 Reflect.deleteProperty(proxy, i),
505 false,
506 );
507 }
508 });
509
510 it("[[Delete]] is allowed for index properties greater than or equal to the length", () => {
511 const underlying = makeUnderlying();
512 const proxy = denseProxy(underlying);
513 assertStrictEquals(
514 Reflect.deleteProperty(proxy, "4"),
515 true,
516 );
517 assertStrictEquals(
518 Reflect.deleteProperty(proxy, "5"),
519 true,
520 );
521 });
522
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);
526 assertEquals(
527 Object.getOwnPropertyDescriptor(proxy, 1),
528 {
529 configurable: true,
530 enumerable: true,
531 value: underlying[1],
532 writable: false,
533 },
534 );
535 assertEquals(
536 Object.getOwnPropertyDescriptor(proxy, 3),
537 {
538 configurable: true,
539 enumerable: true,
540 value: underlying[3],
541 writable: false,
542 },
543 );
544 /* test nonconfigurable data properties too */
545 Object.freeze(underlying);
546 Object.defineProperty(proxy, "1", { configurable: false });
547 assertEquals(
548 Object.getOwnPropertyDescriptor(proxy, 1),
549 {
550 configurable: false,
551 enumerable: true,
552 value: underlying[1],
553 writable: false,
554 },
555 );
556 });
557
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);
561 assertEquals(
562 Object.getOwnPropertyDescriptor(proxy, 0),
563 {
564 configurable: true,
565 enumerable: true,
566 value: undefined,
567 writable: false,
568 },
569 );
570 assertEquals(
571 Object.getOwnPropertyDescriptor(proxy, 2),
572 {
573 configurable: true,
574 enumerable: true,
575 value: undefined,
576 writable: false,
577 },
578 );
579 });
580
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) {
586 assertStrictEquals(
587 Object.getOwnPropertyDescriptor(proxy, i),
588 undefined,
589 );
590 }
591 });
592
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);
599 assertEquals(
600 descriptor,
601 {
602 configurable: false,
603 enumerable: true,
604 get: descriptor.get,
605 set: undefined,
606 },
607 );
608 });
609
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 });
616 assertStrictEquals(
617 Object.getOwnPropertyDescriptor(
618 proxy,
619 "3",
620 ).get.length,
621 0,
622 );
623 });
624 });
625
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 });
632 assertStrictEquals(
633 Object.getOwnPropertyDescriptor(
634 proxy,
635 "3",
636 ).get.name,
637 "get 3",
638 );
639 });
640 });
641
642 it("[[HasProperty]] works when the property is not an index property or length", () => {
643 const underlying = makeUnderlying();
644 const proxy = denseProxy(underlying);
645 proxy.a = "etaoin";
646 Object.getPrototypeOf(underlying).b = "shrdlu";
647 assertStrictEquals(
648 Reflect.has(proxy, "a"),
649 true,
650 );
651 assertStrictEquals(
652 Reflect.has(proxy, "b"),
653 true,
654 );
655 assertStrictEquals(
656 Reflect.has(proxy, "z"),
657 false,
658 );
659 });
660
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) {
665 assertStrictEquals(
666 Reflect.has(proxy, i),
667 true,
668 );
669 }
670 delete underlying.length;
671 for (let i = 0; i < 4; ++i) {
672 assertStrictEquals(
673 Reflect.has(proxy, i),
674 Reflect.has(underlying, i),
675 );
676 }
677 });
678
679 it("[[HasProperty]] works for index properties greater than or equal to the length", () => {
680 const underlying = makeUnderlying();
681 const proxy = denseProxy(underlying);
682 assertStrictEquals(
683 Reflect.has(proxy, "4"),
684 false,
685 );
686 assertStrictEquals(
687 Reflect.has(proxy, "5"),
688 false,
689 );
690 underlying[4] = "inherited now";
691 assertStrictEquals(
692 Reflect.has(proxy, "4"),
693 true,
694 );
695 });
696 });
697
698 describe("~length", () => {
699 it("[[DefineProperty]] allows changing nothing", () => {
700 const underlying = makeUnderlying();
701 const proxy = denseProxy(underlying);
702 assertStrictEquals(
703 Reflect.defineProperty(
704 proxy,
705 "length",
706 {
707 configurable: true,
708 enumerable: false,
709 writable: false,
710 value: underlying.length >>> 0,
711 },
712 ),
713 true,
714 );
715 /* test nonconfigurable versions too */
716 Object.freeze(underlying);
717 Object.defineProperty(proxy, "length", { configurable: false });
718 assertStrictEquals(
719 Reflect.defineProperty(
720 proxy,
721 "length",
722 Object.getOwnPropertyDescriptor(proxy, "length"),
723 ),
724 true,
725 );
726 });
727
728 it("[[DefineProperty]] does not allow a change in enumerablility", () => {
729 const underlying = makeUnderlying();
730 const proxy = denseProxy(underlying);
731 assertStrictEquals(
732 Reflect.defineProperty(proxy, "length", { enumerable: true }),
733 false,
734 );
735 });
736
737 it("[[DefineProperty]] does not allow a change in value", () => {
738 const underlying = makeUnderlying();
739 const proxy = denseProxy(underlying);
740 assertStrictEquals(
741 Reflect.defineProperty(proxy, "length", { value: 0 }),
742 false,
743 );
744 });
745
746 it("[[DefineProperty]] does not allow a change in getter", () => {
747 const underlying = makeUnderlying();
748 const proxy = denseProxy(underlying);
749 assertStrictEquals(
750 Reflect.defineProperty(proxy, "length", {
751 get: () => underlying.length >>> 0,
752 }),
753 false,
754 );
755 });
756
757 it("[[DefineProperty]] does not allow a change in setter", () => {
758 const underlying = makeUnderlying();
759 const proxy = denseProxy(underlying);
760 assertStrictEquals(
761 Reflect.defineProperty(proxy, "length", { set: () => {} }),
762 false,
763 );
764 });
765
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);
769 assertStrictEquals(
770 Reflect.defineProperty(proxy, "length", {
771 configurable: false,
772 }),
773 false,
774 );
775 Object.defineProperty(underlying, "length", {
776 configurable: false,
777 get: () => 0,
778 });
779 assertStrictEquals(
780 Reflect.defineProperty(proxy, "length", {
781 configurable: false,
782 }),
783 false,
784 );
785 });
786
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", {
791 configurable: false,
792 writable: false,
793 });
794 assertStrictEquals(
795 Reflect.defineProperty(proxy, "length", {
796 configurable: false,
797 }),
798 true,
799 );
800 });
801
802 it("[[Delete]] is forbidden", () => {
803 const underlying = makeUnderlying();
804 const proxy = denseProxy(underlying);
805 assertStrictEquals(
806 Reflect.deleteProperty(
807 proxy,
808 "length",
809 ),
810 false,
811 );
812 });
813
814 it("[[GetOwnProperty]] gives the value as a data property", () => {
815 const underlying = makeUnderlying();
816 const proxy = denseProxy(underlying);
817 assertEquals(
818 Object.getOwnPropertyDescriptor(proxy, "length"),
819 {
820 configurable: true,
821 enumerable: false,
822 value: underlying.length >>> 0,
823 writable: false,
824 },
825 );
826 /* test nonconfigurable data properties too */
827 Object.freeze(underlying);
828 Object.defineProperty(proxy, "length", { configurable: false });
829 assertEquals(
830 Object.getOwnPropertyDescriptor(proxy, "length"),
831 {
832 configurable: false,
833 enumerable: false,
834 value: underlying.length >>> 0,
835 writable: false,
836 },
837 );
838 });
839
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;
845 assertEquals(
846 Object.getOwnPropertyDescriptor(proxy, "length"),
847 {
848 configurable: true,
849 enumerable: false,
850 value: 0,
851 writable: false,
852 },
853 );
854 });
855
856 it("[[HasProperty]] is always true", () => {
857 const underlying = makeUnderlying();
858 const proxy = denseProxy(underlying);
859 assertStrictEquals(
860 Reflect.has(proxy, "length"),
861 true,
862 );
863 delete underlying.length;
864 assertStrictEquals(
865 Reflect.has(proxy, "length"),
866 true,
867 );
868 });
869 });
870 });
871
872 describe("fill", () => {
873 it("[[Call]] fills", () => {
874 assertEquals(
875 fill({ 1: "failure", length: 3 }, "success"),
876 { 0: "success", 1: "success", 2: "success", length: 3 },
877 );
878 });
879
880 it("[[Call]] can fill a range", () => {
881 assertEquals(
882 fill({ 1: "failure", length: 4 }, "success", 1, 3),
883 { 1: "success", 2: "success", length: 4 },
884 );
885 });
886
887 it("[[Construct]] throws an error", () => {
888 assertThrows(() => new fill([], null));
889 });
890
891 describe(".length", () => {
892 it("[[Get]] returns the correct length", () => {
893 assertStrictEquals(fill.length, 2);
894 });
895 });
896
897 describe(".name", () => {
898 it("[[Get]] returns the correct name", () => {
899 assertStrictEquals(fill.name, "fill");
900 });
901 });
902 });
903
904 describe("filter", () => {
905 it("[[Call]] filters", () => {
906 assertEquals(
907 filter(["failure", "success", ,], function ($) {
908 return !$ || $ == this;
909 }, "success"),
910 ["success"],
911 );
912 });
913
914 it("[[Construct]] throws an error", () => {
915 assertThrows(() => new filter([], () => {}));
916 });
917
918 describe(".length", () => {
919 it("[[Get]] returns the correct length", () => {
920 assertStrictEquals(filter.length, 2);
921 });
922 });
923
924 describe(".name", () => {
925 it("[[Get]] returns the correct name", () => {
926 assertStrictEquals(filter.name, "filter");
927 });
928 });
929 });
930
931 describe("findFirstIndex", () => {
932 it("[[Call]] returns undefined if no matching entry exists", () => {
933 assertStrictEquals(findFirstIndex([], () => true), undefined);
934 assertStrictEquals(findFirstIndex([1], () => false), undefined);
935 });
936
937 it("[[Call]] returns an index for the first match", () => {
938 assertStrictEquals(
939 findFirstIndex([, true, false], ($) => $ ?? true),
940 1,
941 );
942 assertStrictEquals(
943 findFirstIndex(
944 ["failure", "success", "success"],
945 ($) => $ == "success",
946 ),
947 1,
948 );
949 });
950
951 it("[[Call]] works on arraylike objects", () => {
952 assertStrictEquals(
953 findFirstIndex({ 1: "success", length: 2 }, ($) => $),
954 1,
955 );
956 assertStrictEquals(
957 findFirstIndex({ 1: "failure", length: 1 }, ($) => $),
958 undefined,
959 );
960 });
961
962 it("[[Call]] only gets the value once", () => {
963 const get1 = spy(() => true);
964 findFirstIndex({
965 get 1() {
966 return get1();
967 },
968 length: 2,
969 }, ($) => $);
970 assertSpyCalls(get1, 1);
971 });
972
973 it("[[Call]] passes the value, index, and this value to the callback", () => {
974 const arr = [, "failure", "success", "success"];
975 const callback = spy(($) => $ === "success");
976 const thisArg = {};
977 findFirstIndex(arr, callback, thisArg);
978 assertSpyCalls(callback, 2);
979 assertSpyCall(callback, 0, {
980 args: ["failure", 1, arr],
981 self: thisArg,
982 });
983 assertSpyCall(callback, 1, {
984 args: ["success", 2, arr],
985 self: thisArg,
986 });
987 });
988
989 it("[[Construct]] throws an error", () => {
990 assertThrows(() => new findFirstIndex([], () => {}));
991 });
992
993 describe(".length", () => {
994 it("[[Get]] returns the correct length", () => {
995 assertStrictEquals(findFirstIndex.length, 2);
996 });
997 });
998
999 describe(".name", () => {
1000 it("[[Get]] returns the correct name", () => {
1001 assertStrictEquals(findFirstIndex.name, "findFirstIndex");
1002 });
1003 });
1004 });
1005
1006 describe("findFirstIndexedEntry", () => {
1007 it("[[Call]] returns undefined if no matching entry exists", () => {
1008 assertStrictEquals(
1009 findFirstIndexedEntry([], () => true),
1010 undefined,
1011 );
1012 assertStrictEquals(
1013 findFirstIndexedEntry([1], () => false),
1014 undefined,
1015 );
1016 });
1017
1018 it("[[Call]] returns an entry for the first match", () => {
1019 assertEquals(
1020 findFirstIndexedEntry([, true, false], ($) => $ ?? true),
1021 [1, true],
1022 );
1023 assertEquals(
1024 findFirstIndexedEntry(
1025 ["failure", "success", "success"],
1026 ($) => $ == "success",
1027 ),
1028 [1, "success"],
1029 );
1030 });
1031
1032 it("[[Call]] works on arraylike objects", () => {
1033 assertEquals(
1034 findFirstIndexedEntry({ 1: "success", length: 2 }, ($) => $),
1035 [1, "success"],
1036 );
1037 assertEquals(
1038 findFirstIndexedEntry({ 1: "failure", length: 1 }, ($) => $),
1039 undefined,
1040 );
1041 });
1042
1043 it("[[Call]] only gets the value once", () => {
1044 const get1 = spy(() => true);
1045 findFirstIndexedEntry({
1046 get 1() {
1047 return get1();
1048 },
1049 length: 2,
1050 }, ($) => $);
1051 assertSpyCalls(get1, 1);
1052 });
1053
1054 it("[[Call]] passes the value, index, and this value to the callback", () => {
1055 const arr = [, "failure", "success", "success"];
1056 const callback = spy(($) => $ === "success");
1057 const thisArg = {};
1058 findFirstIndexedEntry(arr, callback, thisArg);
1059 assertSpyCalls(callback, 2);
1060 assertSpyCall(callback, 0, {
1061 args: ["failure", 1, arr],
1062 self: thisArg,
1063 });
1064 assertSpyCall(callback, 1, {
1065 args: ["success", 2, arr],
1066 self: thisArg,
1067 });
1068 });
1069
1070 it("[[Construct]] throws an error", () => {
1071 assertThrows(() => new findFirstIndexedEntry([], () => {}));
1072 });
1073
1074 describe(".length", () => {
1075 it("[[Get]] returns the correct length", () => {
1076 assertStrictEquals(findFirstIndexedEntry.length, 2);
1077 });
1078 });
1079
1080 describe(".name", () => {
1081 it("[[Get]] returns the correct name", () => {
1082 assertStrictEquals(
1083 findFirstIndexedEntry.name,
1084 "findFirstIndexedEntry",
1085 );
1086 });
1087 });
1088 });
1089
1090 describe("findFirstItem", () => {
1091 it("[[Call]] returns undefined if no matching item exists", () => {
1092 assertStrictEquals(findFirstItem([], () => true), undefined);
1093 assertStrictEquals(findFirstItem([1], () => false), undefined);
1094 });
1095
1096 it("[[Call]] returns the first match", () => {
1097 assertStrictEquals(
1098 findFirstItem([, true, false], ($) => $ ?? true),
1099 true,
1100 );
1101 assertStrictEquals(
1102 findFirstItem(
1103 ["failure", "success", "success"],
1104 ($) => $ == "success",
1105 ),
1106 "success",
1107 );
1108 });
1109
1110 it("[[Call]] works on arraylike objects", () => {
1111 assertStrictEquals(
1112 findFirstItem({ 1: "success", length: 2 }, ($) => $),
1113 "success",
1114 );
1115 assertStrictEquals(
1116 findFirstItem({ 1: "failure", length: 1 }, ($) => $),
1117 undefined,
1118 );
1119 });
1120
1121 it("[[Call]] only gets the value once", () => {
1122 const get1 = spy(() => true);
1123 findFirstItem({
1124 get 1() {
1125 return get1();
1126 },
1127 length: 2,
1128 }, ($) => $);
1129 assertSpyCalls(get1, 1);
1130 });
1131
1132 it("[[Call]] passes the value, index, and this value to the callback", () => {
1133 const arr = [, "failure", "success", "success"];
1134 const callback = spy(($) => $ === "success");
1135 const thisArg = {};
1136 findFirstItem(arr, callback, thisArg);
1137 assertSpyCalls(callback, 2);
1138 assertSpyCall(callback, 0, {
1139 args: ["failure", 1, arr],
1140 self: thisArg,
1141 });
1142 assertSpyCall(callback, 1, {
1143 args: ["success", 2, arr],
1144 self: thisArg,
1145 });
1146 });
1147
1148 it("[[Construct]] throws an error", () => {
1149 assertThrows(() => new findFirstItem([], () => {}));
1150 });
1151
1152 describe(".length", () => {
1153 it("[[Get]] returns the correct length", () => {
1154 assertStrictEquals(findFirstItem.length, 2);
1155 });
1156 });
1157
1158 describe(".name", () => {
1159 it("[[Get]] returns the correct name", () => {
1160 assertStrictEquals(findFirstItem.name, "findFirstItem");
1161 });
1162 });
1163 });
1164
1165 describe("findLastIndex", () => {
1166 it("[[Call]] returns undefined if no matching entry exists", () => {
1167 assertStrictEquals(findLastIndex([], () => true), undefined);
1168 assertStrictEquals(findLastIndex([1], () => false), undefined);
1169 });
1170
1171 it("[[Call]] returns an index for the first match", () => {
1172 assertStrictEquals(
1173 findLastIndex([true, false, ,], ($) => $ ?? true),
1174 0,
1175 );
1176 assertStrictEquals(
1177 findLastIndex(
1178 ["success", "success", "failure"],
1179 ($) => $ == "success",
1180 ),
1181 1,
1182 );
1183 });
1184
1185 it("[[Call]] works on arraylike objects", () => {
1186 assertStrictEquals(
1187 findLastIndex({ 1: "success", length: 2 }, ($) => $),
1188 1,
1189 );
1190 assertStrictEquals(
1191 findLastIndex({ 1: "failure", length: 1 }, ($) => $),
1192 undefined,
1193 );
1194 });
1195
1196 it("[[Call]] only gets the value once", () => {
1197 const get1 = spy(() => true);
1198 findLastIndex({
1199 get 1() {
1200 return get1();
1201 },
1202 length: 2,
1203 }, ($) => $);
1204 assertSpyCalls(get1, 1);
1205 });
1206
1207 it("[[Call]] passes the value, index, and this value to the callback", () => {
1208 const arr = ["success", "success", "failure", ,];
1209 const callback = spy(($) => $ === "success");
1210 const thisArg = {};
1211 findLastIndex(arr, callback, thisArg);
1212 assertSpyCalls(callback, 2);
1213 assertSpyCall(callback, 0, {
1214 args: ["failure", 2, arr],
1215 self: thisArg,
1216 });
1217 assertSpyCall(callback, 1, {
1218 args: ["success", 1, arr],
1219 self: thisArg,
1220 });
1221 });
1222
1223 it("[[Construct]] throws an error", () => {
1224 assertThrows(() => new findLastIndex([], () => {}));
1225 });
1226
1227 describe(".length", () => {
1228 it("[[Get]] returns the correct length", () => {
1229 assertStrictEquals(findLastIndex.length, 2);
1230 });
1231 });
1232
1233 describe(".name", () => {
1234 it("[[Get]] returns the correct name", () => {
1235 assertStrictEquals(findLastIndex.name, "findLastIndex");
1236 });
1237 });
1238 });
1239
1240 describe("findLastIndexedEntry", () => {
1241 it("[[Call]] returns undefined if no matching entry exists", () => {
1242 assertStrictEquals(
1243 findLastIndexedEntry([], () => true),
1244 undefined,
1245 );
1246 assertStrictEquals(
1247 findLastIndexedEntry([1], () => false),
1248 undefined,
1249 );
1250 });
1251
1252 it("[[Call]] returns an index for the first match", () => {
1253 assertEquals(
1254 findLastIndexedEntry([true, false, ,], ($) => $ ?? true),
1255 [0, true],
1256 );
1257 assertEquals(
1258 findLastIndexedEntry(
1259 ["success", "success", "failure"],
1260 ($) => $ == "success",
1261 ),
1262 [1, "success"],
1263 );
1264 });
1265
1266 it("[[Call]] works on arraylike objects", () => {
1267 assertEquals(
1268 findLastIndexedEntry({ 1: "success", length: 2 }, ($) => $),
1269 [1, "success"],
1270 );
1271 assertEquals(
1272 findLastIndexedEntry({ 1: "failure", length: 1 }, ($) => $),
1273 undefined,
1274 );
1275 });
1276
1277 it("[[Call]] only gets the value once", () => {
1278 const get1 = spy(() => true);
1279 findLastIndexedEntry({
1280 get 1() {
1281 return get1();
1282 },
1283 length: 2,
1284 }, ($) => $);
1285 assertSpyCalls(get1, 1);
1286 });
1287
1288 it("[[Call]] passes the value, index, and this value to the callback", () => {
1289 const arr = ["success", "success", "failure", ,];
1290 const callback = spy(($) => $ === "success");
1291 const thisArg = {};
1292 findLastIndexedEntry(arr, callback, thisArg);
1293 assertSpyCalls(callback, 2);
1294 assertSpyCall(callback, 0, {
1295 args: ["failure", 2, arr],
1296 self: thisArg,
1297 });
1298 assertSpyCall(callback, 1, {
1299 args: ["success", 1, arr],
1300 self: thisArg,
1301 });
1302 });
1303
1304 it("[[Construct]] throws an error", () => {
1305 assertThrows(() => new findLastIndexedEntry([], () => {}));
1306 });
1307
1308 describe(".length", () => {
1309 it("[[Get]] returns the correct length", () => {
1310 assertStrictEquals(findLastIndexedEntry.length, 2);
1311 });
1312 });
1313
1314 describe(".name", () => {
1315 it("[[Get]] returns the correct name", () => {
1316 assertStrictEquals(
1317 findLastIndexedEntry.name,
1318 "findLastIndexedEntry",
1319 );
1320 });
1321 });
1322 });
1323
1324 describe("findLastItem", () => {
1325 it("[[Call]] returns undefined if no matching entry exists", () => {
1326 assertStrictEquals(findLastItem([], () => true), undefined);
1327 assertStrictEquals(findLastItem([1], () => false), undefined);
1328 });
1329
1330 it("[[Call]] returns an index for the first match", () => {
1331 assertStrictEquals(
1332 findLastItem([true, false, ,], ($) => $ ?? true),
1333 true,
1334 );
1335 assertStrictEquals(
1336 findLastItem(
1337 ["success", "success", "failure"],
1338 ($) => $ == "success",
1339 ),
1340 "success",
1341 );
1342 });
1343
1344 it("[[Call]] works on arraylike objects", () => {
1345 assertStrictEquals(
1346 findLastItem({ 1: "success", length: 2 }, ($) => $),
1347 "success",
1348 );
1349 assertStrictEquals(
1350 findLastItem({ 1: "failure", length: 1 }, ($) => $),
1351 undefined,
1352 );
1353 });
1354
1355 it("[[Call]] only gets the value once", () => {
1356 const get1 = spy(() => true);
1357 findLastItem({
1358 get 1() {
1359 return get1();
1360 },
1361 length: 2,
1362 }, ($) => $);
1363 assertSpyCalls(get1, 1);
1364 });
1365
1366 it("[[Call]] passes the value, index, and this value to the callback", () => {
1367 const arr = ["success", "success", "failure", ,];
1368 const callback = spy(($) => $ === "success");
1369 const thisArg = {};
1370 findLastItem(arr, callback, thisArg);
1371 assertSpyCalls(callback, 2);
1372 assertSpyCall(callback, 0, {
1373 args: ["failure", 2, arr],
1374 self: thisArg,
1375 });
1376 assertSpyCall(callback, 1, {
1377 args: ["success", 1, arr],
1378 self: thisArg,
1379 });
1380 });
1381
1382 it("[[Construct]] throws an error", () => {
1383 assertThrows(() => new findLastItem([], () => {}));
1384 });
1385
1386 describe(".length", () => {
1387 it("[[Get]] returns the correct length", () => {
1388 assertStrictEquals(findLastItem.length, 2);
1389 });
1390 });
1391
1392 describe(".name", () => {
1393 it("[[Get]] returns the correct name", () => {
1394 assertStrictEquals(findLastItem.name, "findLastItem");
1395 });
1396 });
1397 });
1398
1399 describe("flatmap", () => {
1400 it("[[Call]] flatmaps", () => {
1401 assertEquals(
1402 flatmap([, "a", ["b"], [["c"]]], ($) => $),
1403 ["a", "b", ["c"]],
1404 );
1405 });
1406
1407 it("[[Construct]] throws an error", () => {
1408 assertThrows(() => new flatmap([], () => {}));
1409 });
1410
1411 describe(".length", () => {
1412 it("[[Get]] returns the correct length", () => {
1413 assertStrictEquals(flatmap.length, 2);
1414 });
1415 });
1416
1417 describe(".name", () => {
1418 it("[[Get]] returns the correct name", () => {
1419 assertStrictEquals(flatmap.name, "flatmap");
1420 });
1421 });
1422 });
1423
1424 describe("isCollection", () => {
1425 it("[[Call]] returns false for primitives", () => {
1426 assertStrictEquals(isCollection("failure"), false);
1427 });
1428
1429 it("[[Call]] returns false if length throws", () => {
1430 assertStrictEquals(
1431 isCollection({
1432 get length() {
1433 throw void {};
1434 },
1435 }),
1436 false,
1437 );
1438 });
1439
1440 it("[[Call]] returns false if length is not an integer index and cannot be converted to one", () => {
1441 assertStrictEquals(
1442 isCollection({ length: -1, [Symbol.isConcatSpreadable]: true }),
1443 false,
1444 );
1445 assertStrictEquals(
1446 isCollection({
1447 length: Infinity,
1448 [Symbol.isConcatSpreadable]: true,
1449 }),
1450 false,
1451 );
1452 assertStrictEquals(
1453 isCollection({
1454 length: 9007199254740992,
1455 [Symbol.isConcatSpreadable]: true,
1456 }),
1457 false,
1458 );
1459 });
1460
1461 it("[[Call]] returns true if length is an integer index and the object is concat spreadable", () => {
1462 assertStrictEquals(
1463 isCollection({ length: 1, [Symbol.isConcatSpreadable]: true }),
1464 true,
1465 );
1466 assertStrictEquals(
1467 isCollection({ length: 0, [Symbol.isConcatSpreadable]: true }),
1468 true,
1469 );
1470 assertStrictEquals(
1471 isCollection({
1472 length: 9007199254740991,
1473 [Symbol.isConcatSpreadable]: true,
1474 }),
1475 true,
1476 );
1477 });
1478
1479 it("[[Call]] returns true if length can be converted to an index without throwing an error and the object is concat spreadable", () => {
1480 assertStrictEquals(
1481 isCollection({ length: -0, [Symbol.isConcatSpreadable]: true }),
1482 true,
1483 );
1484 assertStrictEquals(
1485 isCollection({ length: NaN, [Symbol.isConcatSpreadable]: true }),
1486 true,
1487 );
1488 });
1489 });
1490
1491 describe("isDenseProxy", () => {
1492 it("[[Call]] returns true for dense proxies", () => {
1493 const underlying = [];
1494 const proxy = denseProxy(underlying);
1495 assertStrictEquals(
1496 isDenseProxy(proxy),
1497 true,
1498 );
1499 });
1500
1501 it("[[Call]] returns false for others", () => {
1502 assertStrictEquals(
1503 isDenseProxy([]),
1504 false,
1505 );
1506 });
1507
1508 it("[[Construct]] throws an error", () => {
1509 assertThrows(() => new isDenseProxy([]));
1510 });
1511
1512 describe(".length", () => {
1513 it("[[Get]] returns the correct length", () => {
1514 assertStrictEquals(isDenseProxy.length, 1);
1515 });
1516 });
1517
1518 describe(".name", () => {
1519 it("[[Get]] returns the correct name", () => {
1520 assertStrictEquals(isDenseProxy.name, "isDenseProxy");
1521 });
1522 });
1523 });
1524
1525 describe("toArray", () => {
1526 it("[[Call]] returns an array of the values in a provided arraylike", () => {
1527 assertEquals(
1528 toArray({ 1: "success", length: 3 }),
1529 [, "success", ,],
1530 );
1531 });
1532 });
1533
1534 describe("toDenseArray", () => {
1535 it("[[Call]] returns a dense array of the values in a provided arraylike", () => {
1536 assertEquals(
1537 toDenseArray({ 1: "success", length: 3 }),
1538 [undefined, "success", undefined],
1539 );
1540 });
1541 });
This page took 0.516851 seconds and 5 git commands to generate.