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