]> Lady’s Gitweb - Pisces/blob - function.test.js
Add makeIllegalConstructor to functions.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 identity,
24 isCallable,
25 isConstructor,
26 makeCallable,
27 makeIllegalConstructor,
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("identity", () => {
244 it("[[Call]] returns what it is given", () => {
245 const value = {};
246 assertStrictEquals(identity(value), value);
247 });
248
249 it("[[Construct]] is constructable", () => {
250 const value = {};
251 assertStrictEquals(new identity(value), value);
252 });
253
254 it("[[Construct]] is subclassable", () => {
255 const value = {};
256 assertStrictEquals(new class extends identity {}(value), value);
257 });
258
259 describe(".name", () => {
260 it("[[Get]] returns the correct name", () => {
261 assertStrictEquals(identity.name, "identity");
262 });
263 });
264 });
265
266 describe("isCallable", () => {
267 it("[[Call]] returns true for ordinary functions", () => {
268 assertStrictEquals(isCallable(function () {}), true);
269 });
270
271 it("[[Call]] returns true for arrow functions", () => {
272 assertStrictEquals(isCallable(() => {}), true);
273 });
274
275 it("[[Call]] returns true for generator functions", () => {
276 assertStrictEquals(isCallable(function* () {}), true);
277 });
278
279 it("[[Call]] returns true for classes", () => {
280 assertStrictEquals(isCallable(class {}), true);
281 });
282
283 it("[[Call]] returns true for builtin functions", () => {
284 assertStrictEquals(isCallable(Math.ceil), true);
285 });
286
287 it("[[Call]] returns true for getters", () => {
288 assertStrictEquals(
289 isCallable(
290 Object.getOwnPropertyDescriptor({
291 get foo() {
292 return undefined;
293 },
294 }, "foo").get,
295 ),
296 true,
297 );
298 });
299
300 it("[[Call]] returns true for setters", () => {
301 assertStrictEquals(
302 isCallable(
303 Object.getOwnPropertyDescriptor({
304 set foo($) {
305 /* do nothing */
306 },
307 }, "foo").set,
308 ),
309 true,
310 );
311 });
312
313 it("[[Call]] returns false for null", () => {
314 assertStrictEquals(isCallable(null), false);
315 });
316
317 it("[[Call]] returns false for objects", () => {
318 assertStrictEquals(isCallable({}), false);
319 });
320
321 it("[[Construct]] throws an error", () => {
322 assertThrows(() => new isCallable(function () {}));
323 });
324
325 describe(".name", () => {
326 it("[[Get]] returns the correct name", () => {
327 assertStrictEquals(isCallable.name, "isCallable");
328 });
329 });
330 });
331
332 describe("isConstructor", () => {
333 it("[[Call]] returns true for ordinary functions", () => {
334 assertStrictEquals(isConstructor(function () {}), true);
335 });
336
337 it("[[Call]] returns false for arrow functions", () => {
338 assertStrictEquals(isConstructor(() => {}), false);
339 });
340
341 it("[[Call]] returns false for generator functions", () => {
342 assertStrictEquals(isConstructor(function* () {}), false);
343 });
344
345 it("[[Call]] returns true for classes", () => {
346 assertStrictEquals(isConstructor(class {}), true);
347 });
348
349 it("[[Call]] returns false for builtin functions", () => {
350 assertStrictEquals(isConstructor(Math.ceil), false);
351 });
352
353 it("[[Call]] returns false for getters", () => {
354 assertStrictEquals(
355 isConstructor(
356 Object.getOwnPropertyDescriptor({
357 get foo() {
358 return undefined;
359 },
360 }, "foo").get,
361 ),
362 false,
363 );
364 });
365
366 it("[[Call]] returns false for setters", () => {
367 assertStrictEquals(
368 isConstructor(
369 Object.getOwnPropertyDescriptor({
370 set foo($) {
371 /* do nothing */
372 },
373 }, "foo").set,
374 ),
375 false,
376 );
377 });
378
379 it("[[Call]] returns false for null", () => {
380 assertStrictEquals(isConstructor(null), false);
381 });
382
383 it("[[Call]] returns false for objects", () => {
384 assertStrictEquals(isConstructor({}), false);
385 });
386
387 it("[[Construct]] throws an error", () => {
388 assertThrows(() => new isConstructor(function () {}));
389 });
390
391 describe(".name", () => {
392 it("[[Get]] returns the correct name", () => {
393 assertStrictEquals(isConstructor.name, "isConstructor");
394 });
395 });
396 });
397
398 describe("makeCallable", () => {
399 it("[[Call]] transfers the first argument to this", () => {
400 assertStrictEquals(
401 makeCallable(
402 function () {
403 return this;
404 },
405 ).call("fail", "pass"),
406 "pass",
407 );
408 });
409
410 it("[[Call]] transfers the remaining arguments", () => {
411 assertEquals(
412 makeCallable(
413 function (...args) {
414 return [this, ...args];
415 },
416 ).call("failure", "etaoin", "shrdlu", "cmfwyp"),
417 ["etaoin", "shrdlu", "cmfwyp"],
418 );
419 });
420
421 it("[[Construct]] throws an error", () => {
422 assertThrows(() => new makeCallable(function () {}));
423 });
424
425 describe(".name", () => {
426 it("[[Get]] returns the correct name", () => {
427 assertStrictEquals(makeCallable.name, "makeCallable");
428 });
429 });
430 });
431
432 describe("makeIllegalConstructor", () => {
433 it("[[Call]] returns a constructor", () => {
434 assert(isConstructor(makeIllegalConstructor()));
435 });
436
437 it("[[Call]] works as expected when provided with no arguments", () => {
438 const constructor = makeIllegalConstructor();
439 assertStrictEquals(
440 Object.getPrototypeOf(constructor),
441 Function.prototype,
442 );
443 assertStrictEquals(
444 Object.getPrototypeOf(constructor.prototype),
445 Object.prototype,
446 );
447 assertEquals(constructor.prototype, {});
448 assert(
449 !Object.getOwnPropertyDescriptor(constructor, "prototype")
450 .writable,
451 );
452 assertStrictEquals(constructor.name, "");
453 });
454
455 it("[[Call]] returns a correctly‐formed constructor when provided one argument", () => {
456 const constructorPrototype = Object.create(null, {
457 name: { value: "etaoin" },
458 prototype: { value: {} },
459 });
460 const constructor = makeIllegalConstructor(
461 Object.create(constructorPrototype),
462 );
463 assert(isConstructor(constructor));
464 assertStrictEquals(
465 Object.getPrototypeOf(constructor),
466 constructorPrototype,
467 );
468 assert(Object.hasOwn(constructor, "prototype"));
469 assertEquals(
470 constructor.prototype,
471 constructorPrototype.prototype,
472 );
473 assert(
474 !Object.getOwnPropertyDescriptor(constructor, "prototype")
475 .writable,
476 );
477 assertStrictEquals(constructor.name, "etaoin");
478 });
479
480 it("[[Call]] allows the second argument to override the prototype of the constructor", () => {
481 const constructorPrototype = Object.create(null, {
482 name: { value: "etaoin" },
483 prototype: { value: {} },
484 });
485 const expectedPrototype = Object.create(null, {
486 name: { value: "shrdlu" },
487 prototype: { value: {} },
488 });
489 const constructor = makeIllegalConstructor(
490 Object.create(constructorPrototype),
491 expectedPrototype,
492 );
493 assert(isConstructor(constructor));
494 assertStrictEquals(
495 Object.getPrototypeOf(constructor),
496 expectedPrototype,
497 );
498 assert(Object.hasOwn(constructor, "prototype"));
499 assertEquals(
500 constructor.prototype,
501 constructorPrototype.prototype,
502 );
503 assert(
504 !Object.getOwnPropertyDescriptor(constructor, "prototype")
505 .writable,
506 );
507 assertStrictEquals(constructor.name, "etaoin");
508 });
509
510 it("[[Construct]] throws an error", () => {
511 assertThrows(() => new makeIllegalConstructor(function () {}));
512 });
513
514 describe("~", () => {
515 it("[[Call]] throws an error", () => {
516 assertThrows(() => {
517 makeIllegalConstructor(function () {})();
518 });
519 });
520
521 it("[[Construct]] throws an error", () => {
522 assertThrows(() => {
523 makeIllegalConstructor(function () {})();
524 });
525 });
526 });
527
528 describe(".name", () => {
529 it("[[Get]] returns the correct name", () => {
530 assertStrictEquals(
531 makeIllegalConstructor.name,
532 "makeIllegalConstructor",
533 );
534 });
535 });
536 });
537
538 describe("ordinaryHasInstance", () => {
539 it("[[Call]] walks the prototype chain", () => {
540 const constructor = class {
541 [Symbol.hasInstance]() {
542 return false;
543 }
544 };
545 assertStrictEquals(
546 ordinaryHasInstance(
547 constructor,
548 new class extends constructor {}(),
549 ),
550 true,
551 );
552 });
553
554 it("[[Construct]] throws an error", () => {
555 assertThrows(() => new ordinaryHasInstance(function () {}, {}));
556 });
557
558 describe(".name", () => {
559 it("[[Get]] returns the correct name", () => {
560 assertStrictEquals(
561 ordinaryHasInstance.name,
562 "ordinaryHasInstance",
563 );
564 });
565 });
566 });
This page took 0.118233 seconds and 5 git commands to generate.