]> Lady’s Gitweb - Pisces/blob - function.test.js
439f9d38c2ad771d0529415c6f5f29d8735f35d3
[Pisces] / function.test.js
1 // ♓🌟 Piscēs ∷ function.test.js
2 // ====================================================================
3 //
4 // Copyright © 2022–2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import {
11 assert,
12 assertEquals,
13 assertStrictEquals,
14 assertThrows,
15 describe,
16 it,
17 } from "./dev-deps.js";
18 import {
19 bind,
20 call,
21 completesNormally,
22 construct,
23 createArrowFunction,
24 createCallableFunction,
25 createIllegalConstructor,
26 identity,
27 isCallable,
28 isConstructor,
29 ordinaryHasInstance,
30 } from "./function.js";
31
32 describe("bind", () => {
33 it("[[Call]] binds this", () => {
34 assertStrictEquals(
35 bind(
36 function () {
37 return this;
38 },
39 "pass",
40 [],
41 ).call("fail"),
42 "pass",
43 );
44 });
45
46 it("[[Call]] binds arguments", () => {
47 assertEquals(
48 bind(
49 function (...args) {
50 return [this, ...args];
51 },
52 "etaoin",
53 ["shrdlu"],
54 ).call("failure", "cmfwyp"),
55 ["etaoin", "shrdlu", "cmfwyp"],
56 );
57 });
58
59 it("[[Call]] works with any arraylike third argument", () => {
60 assertEquals(
61 bind(
62 function (...args) {
63 return [this, ...args];
64 },
65 "etaoin",
66 {
67 1: "shrdlu",
68 3: "failure",
69 length: 3,
70 },
71 ).call("failure", "cmfwyp"),
72 ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
73 );
74 });
75
76 it("[[Construct]] throws an error", () => {
77 assertThrows(() => new bind(function () {}));
78 });
79
80 describe(".length", () => {
81 it("[[Get]] returns the correct length", () => {
82 assertStrictEquals(bind.length, 3);
83 });
84 });
85
86 describe(".name", () => {
87 it("[[Get]] returns the correct name", () => {
88 assertStrictEquals(bind.name, "bind");
89 });
90 });
91 });
92
93 describe("call", () => {
94 it("[[Call]] calls with the provided this value", () => {
95 assertStrictEquals(
96 call(
97 function () {
98 return this;
99 },
100 "pass",
101 [],
102 ),
103 "pass",
104 );
105 });
106
107 it("[[Call]] calls with the provided arguments", () => {
108 assertEquals(
109 call(
110 function (...args) {
111 return [this, ...args];
112 },
113 "etaoin",
114 ["shrdlu", "cmfwyp"],
115 ),
116 ["etaoin", "shrdlu", "cmfwyp"],
117 );
118 });
119
120 it("[[Call]] works with any arraylike third argument", () => {
121 assertEquals(
122 call(
123 function (...args) {
124 return [this, ...args];
125 },
126 "etaoin",
127 {
128 1: "shrdlu",
129 3: "cmfwyp",
130 4: "failure",
131 length: 4,
132 },
133 ),
134 ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
135 );
136 });
137
138 it("[[Construct]] throws an error", () => {
139 assertThrows(() => new call(function () {}, null, []));
140 });
141
142 describe(".length", () => {
143 it("[[Get]] returns the correct length", () => {
144 assertStrictEquals(call.length, 3);
145 });
146 });
147
148 describe(".name", () => {
149 it("[[Get]] returns the correct name", () => {
150 assertStrictEquals(call.name, "call");
151 });
152 });
153 });
154
155 describe("completesNormally", () => {
156 it("[[Call]] returns true for functions which complete normally", () => {
157 assertStrictEquals(completesNormally(() => {}), true);
158 });
159
160 it("[[Call]] returns false for functions which throw", () => {
161 assertStrictEquals(
162 completesNormally(() => {
163 throw null;
164 }),
165 false,
166 );
167 });
168
169 it("[[Call]] throws when the argument is not callable", () => {
170 assertThrows(() => completesNormally(null));
171 });
172
173 it("[[Call]] throws when the argument is not provided", () => {
174 assertThrows(() => completesNormally());
175 });
176
177 it("[[Construct]] throws an error", () => {
178 assertThrows(() => new completesNormally(function () {}));
179 });
180
181 describe(".length", () => {
182 it("[[Get]] returns the correct length", () => {
183 assertStrictEquals(completesNormally.length, 1);
184 });
185 });
186
187 describe(".name", () => {
188 it("[[Get]] returns the correct name", () => {
189 assertStrictEquals(completesNormally.name, "completesNormally");
190 });
191 });
192 });
193
194 describe("construct", () => {
195 it("[[Call]] defaults to the constructor as the target", () => {
196 const constructor = class {};
197 assertStrictEquals(
198 Object.getPrototypeOf(construct(
199 constructor,
200 [],
201 )),
202 constructor.prototype,
203 );
204 });
205
206 it("[[Call]] constructs with the provided new target", () => {
207 const target = function () {};
208 assertStrictEquals(
209 construct(
210 function () {
211 return new.target;
212 },
213 [],
214 target,
215 ),
216 target,
217 );
218 });
219
220 it("[[Call]] constructs with the provided arguments", () => {
221 assertEquals(
222 construct(
223 function (...args) {
224 return [new.target.value, ...args];
225 },
226 ["shrdlu", "cmfwyp"],
227 Object.assign(function () {}, { value: "etaoin" }),
228 ),
229 ["etaoin", "shrdlu", "cmfwyp"],
230 );
231 });
232
233 it("[[Call]] works with any arraylike third argument", () => {
234 assertEquals(
235 construct(
236 function (...args) {
237 return [new.target.value, ...args];
238 },
239 {
240 1: "shrdlu",
241 3: "cmfwyp",
242 4: "failure",
243 length: 4,
244 },
245 Object.assign(function () {}, { value: "etaoin" }),
246 ),
247 ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
248 );
249 });
250
251 it("[[Construct]] throws an error", () => {
252 assertThrows(() => new construct(function () {}, []));
253 });
254
255 describe(".length", () => {
256 it("[[Get]] returns the correct length", () => {
257 assertStrictEquals(construct.length, 2);
258 });
259 });
260
261 describe(".name", () => {
262 it("[[Get]] returns the correct name", () => {
263 assertStrictEquals(construct.name, "construct");
264 });
265 });
266 });
267
268 describe("createArrowFunction", () => {
269 it("[[Call]] sets this to undefined", () => {
270 assertStrictEquals(
271 createArrowFunction(
272 function () {
273 return this;
274 },
275 ).call("fail"),
276 undefined,
277 );
278 });
279
280 it("[[Call]] transfers the provided arguments", () => {
281 assertEquals(
282 createArrowFunction(
283 function (...args) {
284 return [this, ...args];
285 },
286 ).call("failure", "etaoin", "shrdlu", "cmfwyp"),
287 [undefined, "etaoin", "shrdlu", "cmfwyp"],
288 );
289 });
290
291 it("[[Call]] correctly sets the length", () => {
292 assertStrictEquals(
293 createArrowFunction(
294 function (_a, _b, _c) {},
295 ).length,
296 3,
297 );
298 });
299
300 it("[[Call]] correctly sets the name", () => {
301 assertStrictEquals(
302 createArrowFunction(
303 function etaoin() {},
304 ).name,
305 "etaoin",
306 );
307 });
308
309 it("[[Call]] allows the length to be overridden", () => {
310 assertStrictEquals(
311 createArrowFunction(
312 function etaoin() {},
313 { length: 100 },
314 ).length,
315 100,
316 );
317 });
318
319 it("[[Call]] allows the name to be overridden", () => {
320 assertStrictEquals(
321 createArrowFunction(
322 function etaoin() {},
323 { name: "shrdlu" },
324 ).name,
325 "shrdlu",
326 );
327 });
328
329 it("[[Call]] returns an arrow function", () => {
330 assertThrows(() => new (createArrowFunction(function () {}))());
331 });
332
333 it("[[Call]] returns a function with no prototype property", () => {
334 assert(
335 !("prototype" in
336 createArrowFunction(function () {}, { prototype: {} })),
337 );
338 });
339
340 it("[[Construct]] throws an error", () => {
341 assertThrows(() => new createArrowFunction(function () {}));
342 });
343
344 describe(".length", () => {
345 it("[[Get]] returns the correct length", () => {
346 assertStrictEquals(createArrowFunction.length, 1);
347 });
348 });
349
350 describe(".name", () => {
351 it("[[Get]] returns the correct name", () => {
352 assertStrictEquals(
353 createArrowFunction.name,
354 "createArrowFunction",
355 );
356 });
357 });
358 });
359
360 describe("createCallableFunction", () => {
361 it("[[Call]] transfers the first argument to this", () => {
362 assertStrictEquals(
363 createCallableFunction(
364 function () {
365 return this;
366 },
367 ).call("fail", "pass"),
368 "pass",
369 );
370 });
371
372 it("[[Call]] transfers the remaining arguments", () => {
373 assertEquals(
374 createCallableFunction(
375 function (...args) {
376 return [this, ...args];
377 },
378 ).call("failure", "etaoin", "shrdlu", "cmfwyp"),
379 ["etaoin", "shrdlu", "cmfwyp"],
380 );
381 });
382
383 it("[[Call]] correctly sets the length", () => {
384 assertStrictEquals(
385 createCallableFunction(
386 function (_a, _b, _c) {},
387 ).length,
388 4,
389 );
390 });
391
392 it("[[Call]] correctly sets the name", () => {
393 assertStrictEquals(
394 createCallableFunction(
395 function etaoin() {},
396 ).name,
397 "etaoin",
398 );
399 });
400
401 it("[[Call]] allows the length to be overridden", () => {
402 assertStrictEquals(
403 createCallableFunction(
404 function etaoin() {},
405 { length: 100 },
406 ).length,
407 100,
408 );
409 });
410
411 it("[[Call]] allows the name to be overridden", () => {
412 assertStrictEquals(
413 createCallableFunction(
414 function etaoin() {},
415 { name: "shrdlu" },
416 ).name,
417 "shrdlu",
418 );
419 });
420
421 it("[[Call]] returns an arrow function", () => {
422 assertThrows(() => new (createCallableFunction(function () {}))());
423 });
424
425 it("[[Call]] returns a function with no prototype property", () => {
426 assert(
427 !("prototype" in
428 createCallableFunction(function () {}, { prototype: {} })),
429 );
430 });
431
432 it("[[Construct]] throws an error", () => {
433 assertThrows(() => new createCallableFunction(function () {}));
434 });
435
436 describe(".length", () => {
437 it("[[Get]] returns the correct length", () => {
438 assertStrictEquals(createCallableFunction.length, 1);
439 });
440 });
441
442 describe(".name", () => {
443 it("[[Get]] returns the correct name", () => {
444 assertStrictEquals(
445 createCallableFunction.name,
446 "createCallableFunction",
447 );
448 });
449 });
450 });
451
452 describe("createIllegalConstructor", () => {
453 it("[[Call]] returns a constructor", () => {
454 assert(isConstructor(createIllegalConstructor()));
455 });
456
457 it("[[Call]] works as expected when provided with no arguments", () => {
458 const constructor = createIllegalConstructor();
459 assertStrictEquals(
460 Object.getPrototypeOf(constructor),
461 Function.prototype,
462 );
463 assertStrictEquals(
464 Object.getPrototypeOf(constructor.prototype),
465 Object.prototype,
466 );
467 console.dir(
468 Object.getOwnPropertyDescriptors(constructor.prototype),
469 );
470 assertEquals(
471 constructor.prototype,
472 Object.create(Object.prototype, {
473 constructor: {
474 configurable: true,
475 enumerable: false,
476 value: constructor,
477 writable: true,
478 },
479 }),
480 );
481 assert(
482 !Object.getOwnPropertyDescriptor(constructor, "prototype")
483 .writable,
484 );
485 assertStrictEquals(constructor.name, "");
486 });
487
488 it("[[Call]] returns a correctly‐formed constructor when provided one argument", () => {
489 const constructorPrototype = Object.create(null, {
490 name: { value: "etaoin" },
491 prototype: { value: {} },
492 });
493 const constructor = createIllegalConstructor(
494 Object.create(constructorPrototype),
495 );
496 assert(isConstructor(constructor));
497 assertStrictEquals(
498 Object.getPrototypeOf(constructor),
499 constructorPrototype,
500 );
501 assert(Object.hasOwn(constructor, "prototype"));
502 assertEquals(
503 constructor.prototype,
504 constructorPrototype.prototype,
505 );
506 assert(
507 !Object.getOwnPropertyDescriptor(constructor, "prototype")
508 .writable,
509 );
510 assertStrictEquals(constructor.name, "etaoin");
511 });
512
513 it("[[Construct]] throws an error", () => {
514 assertThrows(() => new createIllegalConstructor(function () {}));
515 });
516
517 describe("~", () => {
518 it("[[Call]] throws an error", () => {
519 assertThrows(() => {
520 createIllegalConstructor(function () {})();
521 });
522 });
523
524 it("[[Construct]] throws an error", () => {
525 assertThrows(() => {
526 createIllegalConstructor(function () {})();
527 });
528 });
529 });
530
531 describe(".length", () => {
532 it("[[Get]] returns the correct length", () => {
533 assertStrictEquals(createIllegalConstructor.length, 1);
534 });
535 });
536
537 describe(".name", () => {
538 it("[[Get]] returns the correct name", () => {
539 assertStrictEquals(
540 createIllegalConstructor.name,
541 "createIllegalConstructor",
542 );
543 });
544 });
545 });
546
547 describe("identity", () => {
548 it("[[Call]] returns what it is given", () => {
549 const value = {};
550 assertStrictEquals(identity(value), value);
551 });
552
553 it("[[Construct]] is constructable", () => {
554 const value = {};
555 assertStrictEquals(new identity(value), value);
556 });
557
558 it("[[Construct]] is subclassable", () => {
559 const value = {};
560 assertStrictEquals(new class extends identity {}(value), value);
561 });
562
563 describe(".length", () => {
564 it("[[Get]] returns the correct length", () => {
565 assertStrictEquals(identity.length, 1);
566 });
567 });
568
569 describe(".name", () => {
570 it("[[Get]] returns the correct name", () => {
571 assertStrictEquals(identity.name, "identity");
572 });
573 });
574 });
575
576 describe("isCallable", () => {
577 it("[[Call]] returns true for ordinary functions", () => {
578 assertStrictEquals(isCallable(function () {}), true);
579 });
580
581 it("[[Call]] returns true for arrow functions", () => {
582 assertStrictEquals(isCallable(() => {}), true);
583 });
584
585 it("[[Call]] returns true for generator functions", () => {
586 assertStrictEquals(isCallable(function* () {}), true);
587 });
588
589 it("[[Call]] returns true for classes", () => {
590 assertStrictEquals(isCallable(class {}), true);
591 });
592
593 it("[[Call]] returns true for builtin functions", () => {
594 assertStrictEquals(isCallable(Math.ceil), true);
595 });
596
597 it("[[Call]] returns true for getters", () => {
598 assertStrictEquals(
599 isCallable(
600 Object.getOwnPropertyDescriptor({
601 get foo() {
602 return undefined;
603 },
604 }, "foo").get,
605 ),
606 true,
607 );
608 });
609
610 it("[[Call]] returns true for setters", () => {
611 assertStrictEquals(
612 isCallable(
613 Object.getOwnPropertyDescriptor({
614 set foo($) {
615 /* do nothing */
616 },
617 }, "foo").set,
618 ),
619 true,
620 );
621 });
622
623 it("[[Call]] returns false for null", () => {
624 assertStrictEquals(isCallable(null), false);
625 });
626
627 it("[[Call]] returns false for objects", () => {
628 assertStrictEquals(isCallable({}), false);
629 });
630
631 it("[[Construct]] throws an error", () => {
632 assertThrows(() => new isCallable(function () {}));
633 });
634
635 describe(".length", () => {
636 it("[[Get]] returns the correct length", () => {
637 assertStrictEquals(isCallable.length, 1);
638 });
639 });
640
641 describe(".name", () => {
642 it("[[Get]] returns the correct name", () => {
643 assertStrictEquals(isCallable.name, "isCallable");
644 });
645 });
646 });
647
648 describe("isConstructor", () => {
649 it("[[Call]] returns true for ordinary functions", () => {
650 assertStrictEquals(isConstructor(function () {}), true);
651 });
652
653 it("[[Call]] returns false for arrow functions", () => {
654 assertStrictEquals(isConstructor(() => {}), false);
655 });
656
657 it("[[Call]] returns false for generator functions", () => {
658 assertStrictEquals(isConstructor(function* () {}), false);
659 });
660
661 it("[[Call]] returns true for classes", () => {
662 assertStrictEquals(isConstructor(class {}), true);
663 });
664
665 it("[[Call]] returns false for builtin functions", () => {
666 assertStrictEquals(isConstructor(Math.ceil), false);
667 });
668
669 it("[[Call]] returns false for getters", () => {
670 assertStrictEquals(
671 isConstructor(
672 Object.getOwnPropertyDescriptor({
673 get foo() {
674 return undefined;
675 },
676 }, "foo").get,
677 ),
678 false,
679 );
680 });
681
682 it("[[Call]] returns false for setters", () => {
683 assertStrictEquals(
684 isConstructor(
685 Object.getOwnPropertyDescriptor({
686 set foo($) {
687 /* do nothing */
688 },
689 }, "foo").set,
690 ),
691 false,
692 );
693 });
694
695 it("[[Call]] returns false for null", () => {
696 assertStrictEquals(isConstructor(null), false);
697 });
698
699 it("[[Call]] returns false for objects", () => {
700 assertStrictEquals(isConstructor({}), false);
701 });
702
703 it("[[Construct]] throws an error", () => {
704 assertThrows(() => new isConstructor(function () {}));
705 });
706
707 describe(".length", () => {
708 it("[[Get]] returns the correct length", () => {
709 assertStrictEquals(isConstructor.length, 1);
710 });
711 });
712
713 describe(".name", () => {
714 it("[[Get]] returns the correct name", () => {
715 assertStrictEquals(isConstructor.name, "isConstructor");
716 });
717 });
718 });
719
720 describe("ordinaryHasInstance", () => {
721 it("[[Call]] walks the prototype chain", () => {
722 const constructor = class {
723 [Symbol.hasInstance]() {
724 return false;
725 }
726 };
727 assertStrictEquals(
728 ordinaryHasInstance(
729 constructor,
730 new class extends constructor {}(),
731 ),
732 true,
733 );
734 });
735
736 it("[[Construct]] throws an error", () => {
737 assertThrows(() => new ordinaryHasInstance(function () {}, {}));
738 });
739
740 describe(".length", () => {
741 it("[[Get]] returns the correct length", () => {
742 assertStrictEquals(ordinaryHasInstance.length, 2);
743 });
744 });
745
746 describe(".name", () => {
747 it("[[Get]] returns the correct name", () => {
748 assertStrictEquals(
749 ordinaryHasInstance.name,
750 "ordinaryHasInstance",
751 );
752 });
753 });
754 });
This page took 0.164887 seconds and 3 git commands to generate.