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