]> Lady’s Gitweb - Pisces/blob - function.test.js
dd9b642a48ba85353cadf607787ecb6152314674
[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 createCallableFunction,
24 createIllegalConstructor,
25 identity,
26 isCallable,
27 isConstructor,
28 ordinaryHasInstance,
29 toFunctionName,
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("createCallableFunction", () => {
269 it("[[Call]] transfers the first argument to this", () => {
270 assertStrictEquals(
271 createCallableFunction(
272 function () {
273 return this;
274 },
275 ).call("fail", "pass"),
276 "pass",
277 );
278 });
279
280 it("[[Call]] transfers the remaining arguments", () => {
281 assertEquals(
282 createCallableFunction(
283 function (...args) {
284 return [this, ...args];
285 },
286 ).call("failure", "etaoin", "shrdlu", "cmfwyp"),
287 ["etaoin", "shrdlu", "cmfwyp"],
288 );
289 });
290
291 it("[[Call]] correctly sets the length", () => {
292 assertStrictEquals(
293 createCallableFunction(
294 function (_a, _b, _c) {},
295 ).length,
296 4,
297 );
298 });
299
300 it("[[Call]] correctly sets the name", () => {
301 assertStrictEquals(
302 createCallableFunction(
303 function etaoin() {},
304 ).name,
305 "etaoin",
306 );
307 });
308
309 it("[[Call]] allows the name to be overridden", () => {
310 assertStrictEquals(
311 createCallableFunction(
312 function etaoin() {},
313 "shrdlu",
314 ).name,
315 "shrdlu",
316 );
317 });
318
319 it("[[Construct]] throws an error", () => {
320 assertThrows(() => new createCallableFunction(function () {}));
321 });
322
323 describe(".length", () => {
324 it("[[Get]] returns the correct length", () => {
325 assertStrictEquals(createCallableFunction.length, 1);
326 });
327 });
328
329 describe(".name", () => {
330 it("[[Get]] returns the correct name", () => {
331 assertStrictEquals(
332 createCallableFunction.name,
333 "createCallableFunction",
334 );
335 });
336 });
337 });
338
339 describe("createIllegalConstructor", () => {
340 it("[[Call]] returns a constructor", () => {
341 assert(isConstructor(createIllegalConstructor()));
342 });
343
344 it("[[Call]] works as expected when provided with no arguments", () => {
345 const constructor = createIllegalConstructor();
346 assertStrictEquals(
347 Object.getPrototypeOf(constructor),
348 Function.prototype,
349 );
350 assertStrictEquals(
351 Object.getPrototypeOf(constructor.prototype),
352 Object.prototype,
353 );
354 assertEquals(constructor.prototype, {});
355 assert(
356 !Object.getOwnPropertyDescriptor(constructor, "prototype")
357 .writable,
358 );
359 assertStrictEquals(constructor.name, "");
360 });
361
362 it("[[Call]] returns a correctly‐formed constructor when provided one argument", () => {
363 const constructorPrototype = Object.create(null, {
364 name: { value: "etaoin" },
365 prototype: { value: {} },
366 });
367 const constructor = createIllegalConstructor(
368 Object.create(constructorPrototype),
369 );
370 assert(isConstructor(constructor));
371 assertStrictEquals(
372 Object.getPrototypeOf(constructor),
373 constructorPrototype,
374 );
375 assert(Object.hasOwn(constructor, "prototype"));
376 assertEquals(
377 constructor.prototype,
378 constructorPrototype.prototype,
379 );
380 assert(
381 !Object.getOwnPropertyDescriptor(constructor, "prototype")
382 .writable,
383 );
384 assertStrictEquals(constructor.name, "etaoin");
385 });
386
387 it("[[Call]] allows the second argument to override the prototype of the constructor", () => {
388 const constructorPrototype = Object.create(null, {
389 name: { value: "etaoin" },
390 prototype: { value: {} },
391 });
392 const expectedPrototype = Object.create(null, {
393 name: { value: "shrdlu" },
394 prototype: { value: {} },
395 });
396 const constructor = createIllegalConstructor(
397 Object.create(constructorPrototype),
398 expectedPrototype,
399 );
400 assert(isConstructor(constructor));
401 assertStrictEquals(
402 Object.getPrototypeOf(constructor),
403 expectedPrototype,
404 );
405 assert(Object.hasOwn(constructor, "prototype"));
406 assertEquals(
407 constructor.prototype,
408 constructorPrototype.prototype,
409 );
410 assert(
411 !Object.getOwnPropertyDescriptor(constructor, "prototype")
412 .writable,
413 );
414 assertStrictEquals(constructor.name, "etaoin");
415 });
416
417 it("[[Construct]] throws an error", () => {
418 assertThrows(() => new createIllegalConstructor(function () {}));
419 });
420
421 describe("~", () => {
422 it("[[Call]] throws an error", () => {
423 assertThrows(() => {
424 createIllegalConstructor(function () {})();
425 });
426 });
427
428 it("[[Construct]] throws an error", () => {
429 assertThrows(() => {
430 createIllegalConstructor(function () {})();
431 });
432 });
433 });
434
435 describe(".length", () => {
436 it("[[Get]] returns the correct length", () => {
437 assertStrictEquals(createIllegalConstructor.length, 1);
438 });
439 });
440
441 describe(".name", () => {
442 it("[[Get]] returns the correct name", () => {
443 assertStrictEquals(
444 createIllegalConstructor.name,
445 "createIllegalConstructor",
446 );
447 });
448 });
449 });
450
451 describe("identity", () => {
452 it("[[Call]] returns what it is given", () => {
453 const value = {};
454 assertStrictEquals(identity(value), value);
455 });
456
457 it("[[Construct]] is constructable", () => {
458 const value = {};
459 assertStrictEquals(new identity(value), value);
460 });
461
462 it("[[Construct]] is subclassable", () => {
463 const value = {};
464 assertStrictEquals(new class extends identity {}(value), value);
465 });
466
467 describe(".length", () => {
468 it("[[Get]] returns the correct length", () => {
469 assertStrictEquals(identity.length, 1);
470 });
471 });
472
473 describe(".name", () => {
474 it("[[Get]] returns the correct name", () => {
475 assertStrictEquals(identity.name, "identity");
476 });
477 });
478 });
479
480 describe("isCallable", () => {
481 it("[[Call]] returns true for ordinary functions", () => {
482 assertStrictEquals(isCallable(function () {}), true);
483 });
484
485 it("[[Call]] returns true for arrow functions", () => {
486 assertStrictEquals(isCallable(() => {}), true);
487 });
488
489 it("[[Call]] returns true for generator functions", () => {
490 assertStrictEquals(isCallable(function* () {}), true);
491 });
492
493 it("[[Call]] returns true for classes", () => {
494 assertStrictEquals(isCallable(class {}), true);
495 });
496
497 it("[[Call]] returns true for builtin functions", () => {
498 assertStrictEquals(isCallable(Math.ceil), true);
499 });
500
501 it("[[Call]] returns true for getters", () => {
502 assertStrictEquals(
503 isCallable(
504 Object.getOwnPropertyDescriptor({
505 get foo() {
506 return undefined;
507 },
508 }, "foo").get,
509 ),
510 true,
511 );
512 });
513
514 it("[[Call]] returns true for setters", () => {
515 assertStrictEquals(
516 isCallable(
517 Object.getOwnPropertyDescriptor({
518 set foo($) {
519 /* do nothing */
520 },
521 }, "foo").set,
522 ),
523 true,
524 );
525 });
526
527 it("[[Call]] returns false for null", () => {
528 assertStrictEquals(isCallable(null), false);
529 });
530
531 it("[[Call]] returns false for objects", () => {
532 assertStrictEquals(isCallable({}), false);
533 });
534
535 it("[[Construct]] throws an error", () => {
536 assertThrows(() => new isCallable(function () {}));
537 });
538
539 describe(".length", () => {
540 it("[[Get]] returns the correct length", () => {
541 assertStrictEquals(isCallable.length, 1);
542 });
543 });
544
545 describe(".name", () => {
546 it("[[Get]] returns the correct name", () => {
547 assertStrictEquals(isCallable.name, "isCallable");
548 });
549 });
550 });
551
552 describe("isConstructor", () => {
553 it("[[Call]] returns true for ordinary functions", () => {
554 assertStrictEquals(isConstructor(function () {}), true);
555 });
556
557 it("[[Call]] returns false for arrow functions", () => {
558 assertStrictEquals(isConstructor(() => {}), false);
559 });
560
561 it("[[Call]] returns false for generator functions", () => {
562 assertStrictEquals(isConstructor(function* () {}), false);
563 });
564
565 it("[[Call]] returns true for classes", () => {
566 assertStrictEquals(isConstructor(class {}), true);
567 });
568
569 it("[[Call]] returns false for builtin functions", () => {
570 assertStrictEquals(isConstructor(Math.ceil), false);
571 });
572
573 it("[[Call]] returns false for getters", () => {
574 assertStrictEquals(
575 isConstructor(
576 Object.getOwnPropertyDescriptor({
577 get foo() {
578 return undefined;
579 },
580 }, "foo").get,
581 ),
582 false,
583 );
584 });
585
586 it("[[Call]] returns false for setters", () => {
587 assertStrictEquals(
588 isConstructor(
589 Object.getOwnPropertyDescriptor({
590 set foo($) {
591 /* do nothing */
592 },
593 }, "foo").set,
594 ),
595 false,
596 );
597 });
598
599 it("[[Call]] returns false for null", () => {
600 assertStrictEquals(isConstructor(null), false);
601 });
602
603 it("[[Call]] returns false for objects", () => {
604 assertStrictEquals(isConstructor({}), false);
605 });
606
607 it("[[Construct]] throws an error", () => {
608 assertThrows(() => new isConstructor(function () {}));
609 });
610
611 describe(".length", () => {
612 it("[[Get]] returns the correct length", () => {
613 assertStrictEquals(isConstructor.length, 1);
614 });
615 });
616
617 describe(".name", () => {
618 it("[[Get]] returns the correct name", () => {
619 assertStrictEquals(isConstructor.name, "isConstructor");
620 });
621 });
622 });
623
624 describe("ordinaryHasInstance", () => {
625 it("[[Call]] walks the prototype chain", () => {
626 const constructor = class {
627 [Symbol.hasInstance]() {
628 return false;
629 }
630 };
631 assertStrictEquals(
632 ordinaryHasInstance(
633 constructor,
634 new class extends constructor {}(),
635 ),
636 true,
637 );
638 });
639
640 it("[[Construct]] throws an error", () => {
641 assertThrows(() => new ordinaryHasInstance(function () {}, {}));
642 });
643
644 describe(".length", () => {
645 it("[[Get]] returns the correct length", () => {
646 assertStrictEquals(ordinaryHasInstance.length, 2);
647 });
648 });
649
650 describe(".name", () => {
651 it("[[Get]] returns the correct name", () => {
652 assertStrictEquals(
653 ordinaryHasInstance.name,
654 "ordinaryHasInstance",
655 );
656 });
657 });
658 });
659
660 describe("toFunctionName", () => {
661 it("[[Call]] works with strings and no prefix", () => {
662 assertStrictEquals(toFunctionName("etaoin"), "etaoin");
663 });
664
665 it("[[Call]] works with objects and no prefix", () => {
666 assertStrictEquals(
667 toFunctionName({
668 toString() {
669 return "etaoin";
670 },
671 }),
672 "etaoin",
673 );
674 });
675
676 it("[[Call]] works with descriptionless symbols and no prefix", () => {
677 assertStrictEquals(toFunctionName(Symbol()), "");
678 });
679
680 it("[[Call]] works with empty description symbols and no prefix", () => {
681 assertStrictEquals(toFunctionName(Symbol("")), "[]");
682 });
683
684 it("[[Call]] works with described symbols and no prefix", () => {
685 assertStrictEquals(toFunctionName(Symbol("etaoin")), "[etaoin]");
686 });
687
688 it("[[Call]] works with strings and a prefix", () => {
689 assertStrictEquals(toFunctionName("etaoin", "foo"), "foo etaoin");
690 });
691
692 it("[[Call]] works with objects and no prefix", () => {
693 assertStrictEquals(
694 toFunctionName({
695 toString() {
696 return "etaoin";
697 },
698 }, "foo"),
699 "foo etaoin",
700 );
701 });
702
703 it("[[Call]] works with descriptionless symbols and no prefix", () => {
704 assertStrictEquals(toFunctionName(Symbol(), "foo"), "foo ");
705 });
706
707 it("[[Call]] works with empty description symbols and no prefix", () => {
708 assertStrictEquals(toFunctionName(Symbol(""), "foo"), "foo []");
709 });
710
711 it("[[Call]] works with described symbols and no prefix", () => {
712 assertStrictEquals(
713 toFunctionName(Symbol("etaoin"), "foo"),
714 "foo [etaoin]",
715 );
716 });
717
718 it("[[Construct]] throws an error", () => {
719 assertThrows(() => new toFunctionName(""));
720 });
721
722 describe(".length", () => {
723 it("[[Get]] returns the correct length", () => {
724 assertStrictEquals(toFunctionName.length, 1);
725 });
726 });
727
728 describe(".name", () => {
729 it("[[Get]] returns the correct name", () => {
730 assertStrictEquals(
731 toFunctionName.name,
732 "toFunctionName",
733 );
734 });
735 });
736 });
This page took 0.132643 seconds and 3 git commands to generate.