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