]> Lady’s Gitweb - Pisces/blob - function.test.js
75ba30008df964ac424e37518f40960576abcf1a
[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(".name", () => {
81 it("[[Get]] returns the correct name", () => {
82 assertStrictEquals(bind.name, "bind");
83 });
84 });
85 });
86
87 describe("call", () => {
88 it("[[Call]] calls with the provided this value", () => {
89 assertStrictEquals(
90 call(
91 function () {
92 return this;
93 },
94 "pass",
95 [],
96 ),
97 "pass",
98 );
99 });
100
101 it("[[Call]] calls with the provided arguments", () => {
102 assertEquals(
103 call(
104 function (...args) {
105 return [this, ...args];
106 },
107 "etaoin",
108 ["shrdlu", "cmfwyp"],
109 ),
110 ["etaoin", "shrdlu", "cmfwyp"],
111 );
112 });
113
114 it("[[Call]] works with any arraylike third argument", () => {
115 assertEquals(
116 call(
117 function (...args) {
118 return [this, ...args];
119 },
120 "etaoin",
121 {
122 1: "shrdlu",
123 3: "cmfwyp",
124 4: "failure",
125 length: 4,
126 },
127 ),
128 ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
129 );
130 });
131
132 it("[[Construct]] throws an error", () => {
133 assertThrows(() => new call(function () {}, null, []));
134 });
135
136 describe(".name", () => {
137 it("[[Get]] returns the correct name", () => {
138 assertStrictEquals(call.name, "call");
139 });
140 });
141 });
142
143 describe("completesNormally", () => {
144 it("[[Call]] returns true for functions which complete normally", () => {
145 assertStrictEquals(completesNormally(() => {}), true);
146 });
147
148 it("[[Call]] returns false for functions which throw", () => {
149 assertStrictEquals(
150 completesNormally(() => {
151 throw null;
152 }),
153 false,
154 );
155 });
156
157 it("[[Call]] throws when the argument is not callable", () => {
158 assertThrows(() => completesNormally(null));
159 });
160
161 it("[[Call]] throws when the argument is not provided", () => {
162 assertThrows(() => completesNormally());
163 });
164
165 it("[[Construct]] throws an error", () => {
166 assertThrows(() => new completesNormally(function () {}));
167 });
168
169 describe(".name", () => {
170 it("[[Get]] returns the correct name", () => {
171 assertStrictEquals(completesNormally.name, "completesNormally");
172 });
173 });
174 });
175
176 describe("construct", () => {
177 it("[[Call]] defaults to the constructor as the target", () => {
178 const constructor = class {};
179 assertStrictEquals(
180 Object.getPrototypeOf(construct(
181 constructor,
182 [],
183 )),
184 constructor.prototype,
185 );
186 });
187
188 it("[[Call]] constructs with the provided new target", () => {
189 const target = function () {};
190 assertStrictEquals(
191 construct(
192 function () {
193 return new.target;
194 },
195 [],
196 target,
197 ),
198 target,
199 );
200 });
201
202 it("[[Call]] constructs with the provided arguments", () => {
203 assertEquals(
204 construct(
205 function (...args) {
206 return [new.target.value, ...args];
207 },
208 ["shrdlu", "cmfwyp"],
209 Object.assign(function () {}, { value: "etaoin" }),
210 ),
211 ["etaoin", "shrdlu", "cmfwyp"],
212 );
213 });
214
215 it("[[Call]] works with any arraylike third argument", () => {
216 assertEquals(
217 construct(
218 function (...args) {
219 return [new.target.value, ...args];
220 },
221 {
222 1: "shrdlu",
223 3: "cmfwyp",
224 4: "failure",
225 length: 4,
226 },
227 Object.assign(function () {}, { value: "etaoin" }),
228 ),
229 ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
230 );
231 });
232
233 it("[[Construct]] throws an error", () => {
234 assertThrows(() => new construct(function () {}, []));
235 });
236
237 describe(".name", () => {
238 it("[[Get]] returns the correct name", () => {
239 assertStrictEquals(construct.name, "construct");
240 });
241 });
242 });
243
244 describe("createCallableFunction", () => {
245 it("[[Call]] transfers the first argument to this", () => {
246 assertStrictEquals(
247 createCallableFunction(
248 function () {
249 return this;
250 },
251 ).call("fail", "pass"),
252 "pass",
253 );
254 });
255
256 it("[[Call]] transfers the remaining arguments", () => {
257 assertEquals(
258 createCallableFunction(
259 function (...args) {
260 return [this, ...args];
261 },
262 ).call("failure", "etaoin", "shrdlu", "cmfwyp"),
263 ["etaoin", "shrdlu", "cmfwyp"],
264 );
265 });
266
267 it("[[Construct]] throws an error", () => {
268 assertThrows(() => new createCallableFunction(function () {}));
269 });
270
271 describe(".name", () => {
272 it("[[Get]] returns the correct name", () => {
273 assertStrictEquals(
274 createCallableFunction.name,
275 "createCallableFunction",
276 );
277 });
278 });
279 });
280
281 describe("createIllegalConstructor", () => {
282 it("[[Call]] returns a constructor", () => {
283 assert(isConstructor(createIllegalConstructor()));
284 });
285
286 it("[[Call]] works as expected when provided with no arguments", () => {
287 const constructor = createIllegalConstructor();
288 assertStrictEquals(
289 Object.getPrototypeOf(constructor),
290 Function.prototype,
291 );
292 assertStrictEquals(
293 Object.getPrototypeOf(constructor.prototype),
294 Object.prototype,
295 );
296 assertEquals(constructor.prototype, {});
297 assert(
298 !Object.getOwnPropertyDescriptor(constructor, "prototype")
299 .writable,
300 );
301 assertStrictEquals(constructor.name, "");
302 });
303
304 it("[[Call]] returns a correctly‐formed constructor when provided one argument", () => {
305 const constructorPrototype = Object.create(null, {
306 name: { value: "etaoin" },
307 prototype: { value: {} },
308 });
309 const constructor = createIllegalConstructor(
310 Object.create(constructorPrototype),
311 );
312 assert(isConstructor(constructor));
313 assertStrictEquals(
314 Object.getPrototypeOf(constructor),
315 constructorPrototype,
316 );
317 assert(Object.hasOwn(constructor, "prototype"));
318 assertEquals(
319 constructor.prototype,
320 constructorPrototype.prototype,
321 );
322 assert(
323 !Object.getOwnPropertyDescriptor(constructor, "prototype")
324 .writable,
325 );
326 assertStrictEquals(constructor.name, "etaoin");
327 });
328
329 it("[[Call]] allows the second argument to override the prototype of the constructor", () => {
330 const constructorPrototype = Object.create(null, {
331 name: { value: "etaoin" },
332 prototype: { value: {} },
333 });
334 const expectedPrototype = Object.create(null, {
335 name: { value: "shrdlu" },
336 prototype: { value: {} },
337 });
338 const constructor = createIllegalConstructor(
339 Object.create(constructorPrototype),
340 expectedPrototype,
341 );
342 assert(isConstructor(constructor));
343 assertStrictEquals(
344 Object.getPrototypeOf(constructor),
345 expectedPrototype,
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("[[Construct]] throws an error", () => {
360 assertThrows(() => new createIllegalConstructor(function () {}));
361 });
362
363 describe("~", () => {
364 it("[[Call]] throws an error", () => {
365 assertThrows(() => {
366 createIllegalConstructor(function () {})();
367 });
368 });
369
370 it("[[Construct]] throws an error", () => {
371 assertThrows(() => {
372 createIllegalConstructor(function () {})();
373 });
374 });
375 });
376
377 describe(".name", () => {
378 it("[[Get]] returns the correct name", () => {
379 assertStrictEquals(
380 createIllegalConstructor.name,
381 "createIllegalConstructor",
382 );
383 });
384 });
385 });
386
387 describe("identity", () => {
388 it("[[Call]] returns what it is given", () => {
389 const value = {};
390 assertStrictEquals(identity(value), value);
391 });
392
393 it("[[Construct]] is constructable", () => {
394 const value = {};
395 assertStrictEquals(new identity(value), value);
396 });
397
398 it("[[Construct]] is subclassable", () => {
399 const value = {};
400 assertStrictEquals(new class extends identity {}(value), value);
401 });
402
403 describe(".name", () => {
404 it("[[Get]] returns the correct name", () => {
405 assertStrictEquals(identity.name, "identity");
406 });
407 });
408 });
409
410 describe("isCallable", () => {
411 it("[[Call]] returns true for ordinary functions", () => {
412 assertStrictEquals(isCallable(function () {}), true);
413 });
414
415 it("[[Call]] returns true for arrow functions", () => {
416 assertStrictEquals(isCallable(() => {}), true);
417 });
418
419 it("[[Call]] returns true for generator functions", () => {
420 assertStrictEquals(isCallable(function* () {}), true);
421 });
422
423 it("[[Call]] returns true for classes", () => {
424 assertStrictEquals(isCallable(class {}), true);
425 });
426
427 it("[[Call]] returns true for builtin functions", () => {
428 assertStrictEquals(isCallable(Math.ceil), true);
429 });
430
431 it("[[Call]] returns true for getters", () => {
432 assertStrictEquals(
433 isCallable(
434 Object.getOwnPropertyDescriptor({
435 get foo() {
436 return undefined;
437 },
438 }, "foo").get,
439 ),
440 true,
441 );
442 });
443
444 it("[[Call]] returns true for setters", () => {
445 assertStrictEquals(
446 isCallable(
447 Object.getOwnPropertyDescriptor({
448 set foo($) {
449 /* do nothing */
450 },
451 }, "foo").set,
452 ),
453 true,
454 );
455 });
456
457 it("[[Call]] returns false for null", () => {
458 assertStrictEquals(isCallable(null), false);
459 });
460
461 it("[[Call]] returns false for objects", () => {
462 assertStrictEquals(isCallable({}), false);
463 });
464
465 it("[[Construct]] throws an error", () => {
466 assertThrows(() => new isCallable(function () {}));
467 });
468
469 describe(".name", () => {
470 it("[[Get]] returns the correct name", () => {
471 assertStrictEquals(isCallable.name, "isCallable");
472 });
473 });
474 });
475
476 describe("isConstructor", () => {
477 it("[[Call]] returns true for ordinary functions", () => {
478 assertStrictEquals(isConstructor(function () {}), true);
479 });
480
481 it("[[Call]] returns false for arrow functions", () => {
482 assertStrictEquals(isConstructor(() => {}), false);
483 });
484
485 it("[[Call]] returns false for generator functions", () => {
486 assertStrictEquals(isConstructor(function* () {}), false);
487 });
488
489 it("[[Call]] returns true for classes", () => {
490 assertStrictEquals(isConstructor(class {}), true);
491 });
492
493 it("[[Call]] returns false for builtin functions", () => {
494 assertStrictEquals(isConstructor(Math.ceil), false);
495 });
496
497 it("[[Call]] returns false for getters", () => {
498 assertStrictEquals(
499 isConstructor(
500 Object.getOwnPropertyDescriptor({
501 get foo() {
502 return undefined;
503 },
504 }, "foo").get,
505 ),
506 false,
507 );
508 });
509
510 it("[[Call]] returns false for setters", () => {
511 assertStrictEquals(
512 isConstructor(
513 Object.getOwnPropertyDescriptor({
514 set foo($) {
515 /* do nothing */
516 },
517 }, "foo").set,
518 ),
519 false,
520 );
521 });
522
523 it("[[Call]] returns false for null", () => {
524 assertStrictEquals(isConstructor(null), false);
525 });
526
527 it("[[Call]] returns false for objects", () => {
528 assertStrictEquals(isConstructor({}), false);
529 });
530
531 it("[[Construct]] throws an error", () => {
532 assertThrows(() => new isConstructor(function () {}));
533 });
534
535 describe(".name", () => {
536 it("[[Get]] returns the correct name", () => {
537 assertStrictEquals(isConstructor.name, "isConstructor");
538 });
539 });
540 });
541
542 describe("ordinaryHasInstance", () => {
543 it("[[Call]] walks the prototype chain", () => {
544 const constructor = class {
545 [Symbol.hasInstance]() {
546 return false;
547 }
548 };
549 assertStrictEquals(
550 ordinaryHasInstance(
551 constructor,
552 new class extends constructor {}(),
553 ),
554 true,
555 );
556 });
557
558 it("[[Construct]] throws an error", () => {
559 assertThrows(() => new ordinaryHasInstance(function () {}, {}));
560 });
561
562 describe(".name", () => {
563 it("[[Get]] returns the correct name", () => {
564 assertStrictEquals(
565 ordinaryHasInstance.name,
566 "ordinaryHasInstance",
567 );
568 });
569 });
570 });
571
572 describe("toFunctionName", () => {
573 it("[[Call]] works with strings and no prefix", () => {
574 assertStrictEquals(toFunctionName("etaoin"), "etaoin");
575 });
576
577 it("[[Call]] works with objects and no prefix", () => {
578 assertStrictEquals(
579 toFunctionName({
580 toString() {
581 return "etaoin";
582 },
583 }),
584 "etaoin",
585 );
586 });
587
588 it("[[Call]] works with descriptionless symbols and no prefix", () => {
589 assertStrictEquals(toFunctionName(Symbol()), "");
590 });
591
592 it("[[Call]] works with empty description symbols and no prefix", () => {
593 assertStrictEquals(toFunctionName(Symbol("")), "[]");
594 });
595
596 it("[[Call]] works with described symbols and no prefix", () => {
597 assertStrictEquals(toFunctionName(Symbol("etaoin")), "[etaoin]");
598 });
599
600 it("[[Call]] works with strings and a prefix", () => {
601 assertStrictEquals(toFunctionName("etaoin", "foo"), "foo etaoin");
602 });
603
604 it("[[Call]] works with objects and no prefix", () => {
605 assertStrictEquals(
606 toFunctionName({
607 toString() {
608 return "etaoin";
609 },
610 }, "foo"),
611 "foo etaoin",
612 );
613 });
614
615 it("[[Call]] works with descriptionless symbols and no prefix", () => {
616 assertStrictEquals(toFunctionName(Symbol(), "foo"), "foo ");
617 });
618
619 it("[[Call]] works with empty description symbols and no prefix", () => {
620 assertStrictEquals(toFunctionName(Symbol(""), "foo"), "foo []");
621 });
622
623 it("[[Call]] works with described symbols and no prefix", () => {
624 assertStrictEquals(
625 toFunctionName(Symbol("etaoin"), "foo"),
626 "foo [etaoin]",
627 );
628 });
629
630 it("[[Construct]] throws an error", () => {
631 assertThrows(() => new toFunctionName(""));
632 });
633
634 describe(".name", () => {
635 it("[[Get]] returns the correct name", () => {
636 assertStrictEquals(
637 toFunctionName.name,
638 "toFunctionName",
639 );
640 });
641 });
642 });
This page took 0.089032 seconds and 3 git commands to generate.