]> Lady’s Gitweb - Pisces/blob - object.test.js
Define names for LazyLoader getters & setters
[Pisces] / object.test.js
1 // ♓🌟 Piscēs ∷ object.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 assertSpyCall,
14 assertSpyCalls,
15 assertStrictEquals,
16 assertThrows,
17 describe,
18 it,
19 spy,
20 } from "./dev-deps.js";
21 import {
22 defineOwnProperties,
23 deleteOwnProperty,
24 frozenCopy,
25 getMethod,
26 getOwnPropertyKeys,
27 getPropertyValue,
28 hasProperty,
29 LazyLoader,
30 PropertyDescriptor,
31 setPropertyValue,
32 toObject,
33 toPropertyKey,
34 } from "./object.js";
35
36 describe("LazyLoader", () => {
37 const symbol = Symbol("foo");
38 const prototype = {};
39 const etaoinMethod = spy(() => "success");
40 const shrdluMethod = spy(() => "success");
41 const cmfwypMethod = spy(() => "success");
42 const vbgkqjMethod = spy(() => "success");
43 const methodsObject = Object.create(
44 prototype,
45 {
46 etaoin: {
47 configurable: false,
48 enumerable: true,
49 value: etaoinMethod,
50 writable: false,
51 },
52 shrdlu: {
53 configurable: true,
54 enumerable: false,
55 value: shrdluMethod,
56 writable: false,
57 },
58 cmfwyp: {
59 configurable: true,
60 enumerable: false,
61 get() {
62 return cmfwypMethod;
63 },
64 },
65 vbgkqj: {
66 configurable: false,
67 enumerable: true,
68 get() {
69 return vbgkqjMethod;
70 },
71 set(_) {},
72 },
73 xzfiflffffi: { configurable: true, enumerable: false, set(_) {} },
74 [symbol]: {
75 configurable: true,
76 enumerable: false,
77 value: "failure",
78 writable: true,
79 },
80 },
81 );
82
83 it("[[Construct]] creates a new object which inherits from the correct prototype", () => {
84 assertStrictEquals(
85 Object.getPrototypeOf(new LazyLoader(methodsObject)),
86 prototype,
87 );
88 });
89
90 it("[[Construct]] creates a new object with the desired properties", () => {
91 assertEquals(
92 Reflect.ownKeys(new LazyLoader(methodsObject)),
93 ["etaoin", "shrdlu", "cmfwyp", "vbgkqj", "xzfiflffffi", symbol],
94 );
95 });
96
97 it("[[Construct]] creates a new object with configurable properties", () => {
98 assertEquals(
99 Object.fromEntries(
100 function* (ll) {
101 for (const key of Reflect.ownKeys(ll)) {
102 yield [
103 key,
104 Object.getOwnPropertyDescriptor(ll, key).configurable,
105 ];
106 }
107 }(new LazyLoader(methodsObject)),
108 ),
109 {
110 etaoin: true,
111 shrdlu: true,
112 cmfwyp: true,
113 vbgkqj: true,
114 xzfiflffffi: true,
115 [symbol]: true,
116 },
117 );
118 });
119
120 it("[[Construct]] creates a new object with the correct enumerability", () => {
121 assertEquals(
122 Object.fromEntries(
123 function* (ll) {
124 for (const key of Reflect.ownKeys(ll)) {
125 yield [
126 key,
127 Object.getOwnPropertyDescriptor(ll, key).enumerable,
128 ];
129 }
130 }(new LazyLoader(methodsObject)),
131 ),
132 {
133 etaoin: true,
134 shrdlu: false,
135 cmfwyp: false,
136 vbgkqj: true,
137 xzfiflffffi: false,
138 [symbol]: false,
139 },
140 );
141 });
142
143 it("[[Construct]] creates a new object with defined getters", () => {
144 assertEquals(
145 Object.fromEntries(
146 function* (ll) {
147 for (const key of Reflect.ownKeys(ll)) {
148 yield [
149 key,
150 Object.getOwnPropertyDescriptor(ll, key).get?.name,
151 ];
152 }
153 }(new LazyLoader(methodsObject)),
154 ),
155 {
156 etaoin: "get etaoin",
157 shrdlu: "get shrdlu",
158 cmfwyp: "get cmfwyp",
159 vbgkqj: "get vbgkqj",
160 xzfiflffffi: "get xzfiflffffi",
161 [symbol]: `get [${symbol.description}]`,
162 },
163 );
164 });
165
166 it("[[Construct]] creates a new object with defined setters for writable properties only", () => {
167 assertEquals(
168 Object.fromEntries(
169 function* (ll) {
170 for (const key of Reflect.ownKeys(ll)) {
171 yield [
172 key,
173 Object.getOwnPropertyDescriptor(ll, key).set?.name,
174 ];
175 }
176 }(new LazyLoader(methodsObject)),
177 ),
178 {
179 etaoin: undefined,
180 shrdlu: undefined,
181 cmfwyp: undefined,
182 vbgkqj: undefined,
183 xzfiflffffi: undefined,
184 [symbol]: `set [${symbol.description}]`,
185 },
186 );
187 });
188
189 it("[[Construct]] creates a new object with correct getter behaviour", () => {
190 const ll = new LazyLoader(methodsObject);
191 ll.etaoin;
192 assertEquals(
193 Object.getOwnPropertyDescriptor(ll, "etaoin"),
194 {
195 configurable: false,
196 enumerable: true,
197 value: "success",
198 writable: false,
199 },
200 );
201 assertSpyCalls(etaoinMethod, 1);
202 assertSpyCall(etaoinMethod, 0, {
203 args: [],
204 self: ll,
205 returned: "success",
206 });
207 ll.shrdlu;
208 assertEquals(
209 Object.getOwnPropertyDescriptor(ll, "shrdlu"),
210 {
211 configurable: true,
212 enumerable: false,
213 value: "success",
214 writable: false,
215 },
216 );
217 assertSpyCalls(shrdluMethod, 1);
218 assertSpyCall(shrdluMethod, 0, {
219 args: [],
220 self: ll,
221 returned: "success",
222 });
223 ll.cmfwyp;
224 assertEquals(
225 Object.getOwnPropertyDescriptor(ll, "cmfwyp"),
226 {
227 configurable: true,
228 enumerable: false,
229 value: "success",
230 writable: false,
231 },
232 );
233 assertSpyCalls(cmfwypMethod, 1);
234 assertSpyCall(cmfwypMethod, 0, {
235 args: [],
236 self: ll,
237 returned: "success",
238 });
239 ll.vbgkqj;
240 assertEquals(
241 Object.getOwnPropertyDescriptor(ll, "vbgkqj"),
242 {
243 configurable: false,
244 enumerable: true,
245 value: "success",
246 writable: false,
247 },
248 );
249 assertSpyCalls(vbgkqjMethod, 1);
250 assertSpyCall(vbgkqjMethod, 0, {
251 args: [],
252 self: ll,
253 returned: "success",
254 });
255 assertThrows(() => ll.xzfiflffffi);
256 assertThrows(() => ll[symbol]);
257 });
258
259 it("[[Construct]] creates a new object with correct setter behaviour", () => {
260 const ll = new LazyLoader(methodsObject);
261 ll[symbol] = "success";
262 assertEquals(
263 Object.getOwnPropertyDescriptor(ll, symbol),
264 {
265 configurable: true,
266 enumerable: false,
267 value: "success",
268 writable: true,
269 },
270 );
271 });
272 });
273
274 describe("PropertyDescriptor", () => {
275 it("[[Construct]] creates a new PropertyDescriptor", () => {
276 assertStrictEquals(
277 Object.getPrototypeOf(new PropertyDescriptor({})),
278 PropertyDescriptor.prototype,
279 );
280 });
281
282 it("[[Construct]] throws for primitives", () => {
283 assertThrows(() => new PropertyDescriptor("failure"));
284 });
285
286 describe("::complete", () => {
287 it("[[Call]] completes a generic descriptor", () => {
288 const desc = {};
289 PropertyDescriptor.prototype.complete.call(desc);
290 assertEquals(desc, {
291 configurable: false,
292 enumerable: false,
293 value: undefined,
294 writable: false,
295 });
296 });
297
298 it("[[Call]] completes a data descriptor", () => {
299 const desc = { value: undefined };
300 PropertyDescriptor.prototype.complete.call(desc);
301 assertEquals(desc, {
302 configurable: false,
303 enumerable: false,
304 value: undefined,
305 writable: false,
306 });
307 });
308
309 it("[[Call]] completes an accessor descriptor", () => {
310 const desc = { get: undefined };
311 PropertyDescriptor.prototype.complete.call(desc);
312 assertEquals(desc, {
313 configurable: false,
314 enumerable: false,
315 get: undefined,
316 set: undefined,
317 });
318 });
319 });
320
321 describe("::isAccessorDescriptor", () => {
322 it("[[Get]] returns false for a generic descriptor", () => {
323 assertStrictEquals(
324 Reflect.get(
325 PropertyDescriptor.prototype,
326 "isAccessorDescriptor",
327 {},
328 ),
329 false,
330 );
331 });
332
333 it("[[Get]] returns false for a data descriptor", () => {
334 assertStrictEquals(
335 Reflect.get(
336 PropertyDescriptor.prototype,
337 "isAccessorDescriptor",
338 { value: undefined },
339 ),
340 false,
341 );
342 });
343
344 it("[[Get]] returns true for an accessor descriptor", () => {
345 assertStrictEquals(
346 Reflect.get(
347 PropertyDescriptor.prototype,
348 "isAccessorDescriptor",
349 { get: undefined },
350 ),
351 true,
352 );
353 });
354 });
355
356 describe("::isDataDescriptor", () => {
357 it("[[Get]] returns false for a generic descriptor", () => {
358 assertStrictEquals(
359 Reflect.get(
360 PropertyDescriptor.prototype,
361 "isDataDescriptor",
362 {},
363 ),
364 false,
365 );
366 });
367
368 it("[[Get]] returns true for a data descriptor", () => {
369 assertStrictEquals(
370 Reflect.get(
371 PropertyDescriptor.prototype,
372 "isDataDescriptor",
373 { value: undefined },
374 ),
375 true,
376 );
377 });
378
379 it("[[Get]] returns false for an accessor descriptor", () => {
380 assertStrictEquals(
381 Reflect.get(
382 PropertyDescriptor.prototype,
383 "isDataDescriptor",
384 { get: undefined },
385 ),
386 false,
387 );
388 });
389 });
390
391 describe("::isFullyPopulated", () => {
392 it("[[Get]] returns false for a generic descriptor", () => {
393 assertStrictEquals(
394 Reflect.get(
395 PropertyDescriptor.prototype,
396 "isFullyPopulated",
397 {},
398 ),
399 false,
400 );
401 });
402
403 it("[[Get]] returns false for a non‐fully‐populated data descriptor", () => {
404 assertStrictEquals(
405 Reflect.get(
406 PropertyDescriptor.prototype,
407 "isFullyPopulated",
408 { value: undefined },
409 ),
410 false,
411 );
412 });
413
414 it("[[Get]] returns true for a fully‐populated data descriptor", () => {
415 assertStrictEquals(
416 Reflect.get(PropertyDescriptor.prototype, "isFullyPopulated", {
417 configurable: true,
418 enumerable: true,
419 value: undefined,
420 writable: true,
421 }),
422 true,
423 );
424 });
425
426 it("[[Get]] returns false for a non‐fully‐populated accessor descriptor", () => {
427 assertStrictEquals(
428 Reflect.get(
429 PropertyDescriptor.prototype,
430 "isFullyPopulated",
431 { get: undefined },
432 ),
433 false,
434 );
435 });
436
437 it("[[Get]] returns true for a fully‐populated accessor descriptor", () => {
438 assertStrictEquals(
439 Reflect.get(PropertyDescriptor.prototype, "isFullyPopulated", {
440 configurable: true,
441 enumerable: true,
442 get: undefined,
443 set: undefined,
444 }),
445 true,
446 );
447 });
448 });
449
450 describe("::isGenericDescriptor", () => {
451 it("[[Get]] returns true for a generic descriptor", () => {
452 assertStrictEquals(
453 Reflect.get(
454 PropertyDescriptor.prototype,
455 "isGenericDescriptor",
456 {},
457 ),
458 true,
459 );
460 });
461
462 it("[[Get]] returns true for a data descriptor", () => {
463 assertStrictEquals(
464 Reflect.get(
465 PropertyDescriptor.prototype,
466 "isGenericDescriptor",
467 { value: undefined },
468 ),
469 false,
470 );
471 });
472
473 it("[[Get]] returns false for an accessor descriptor", () => {
474 assertStrictEquals(
475 Reflect.get(
476 PropertyDescriptor.prototype,
477 "isGenericDescriptor",
478 { get: undefined },
479 ),
480 false,
481 );
482 });
483 });
484
485 describe("~configurable", () => {
486 it("[[DefineOwnProperty]] coerces to a boolean", () => {
487 const desc = new PropertyDescriptor({});
488 Object.defineProperty(desc, "configurable", {});
489 assertStrictEquals(desc.configurable, false);
490 });
491
492 it("[[DefineOwnProperty]] throws for accessor properties", () => {
493 const desc = new PropertyDescriptor({});
494 assertThrows(() =>
495 Object.defineProperty(desc, "configurable", { get: undefined })
496 );
497 });
498
499 it("[[Set]] coerces to a boolean", () => {
500 const desc = new PropertyDescriptor({});
501 desc.configurable = undefined;
502 assertStrictEquals(desc.configurable, false);
503 });
504
505 it("[[Delete]] works", () => {
506 const desc = new PropertyDescriptor({ configurable: false });
507 delete desc.configurable;
508 assert(!("configurable" in desc));
509 });
510 });
511
512 describe("~enumerable", () => {
513 it("[[DefineOwnProperty]] coerces to a boolean", () => {
514 const desc = new PropertyDescriptor({});
515 Object.defineProperty(desc, "enumerable", {});
516 assertStrictEquals(desc.enumerable, false);
517 });
518
519 it("[[DefineOwnProperty]] throws for accessor properties", () => {
520 const desc = new PropertyDescriptor({});
521 assertThrows(() =>
522 Object.defineProperty(desc, "enumerable", { get: undefined })
523 );
524 });
525
526 it("[[Set]] coerces to a boolean", () => {
527 const desc = new PropertyDescriptor({});
528 desc.enumerable = undefined;
529 assertStrictEquals(desc.enumerable, false);
530 });
531
532 it("[[Delete]] works", () => {
533 const desc = new PropertyDescriptor({ enumerable: false });
534 delete desc.enumerable;
535 assert(!("enumerable" in desc));
536 });
537 });
538
539 describe("~get", () => {
540 it("[[DefineOwnProperty]] works", () => {
541 const desc = new PropertyDescriptor({});
542 Object.defineProperty(desc, "get", {});
543 assertStrictEquals(desc.get, undefined);
544 });
545
546 it("[[DefineOwnProperty]] throws for accessor properties", () => {
547 const desc = new PropertyDescriptor({});
548 assertThrows(() =>
549 Object.defineProperty(desc, "get", { get: undefined })
550 );
551 });
552
553 it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
554 const desc = new PropertyDescriptor({});
555 assertThrows(
556 () => Object.defineProperty(desc, "get", { value: null }),
557 );
558 });
559
560 it("[[DefineOwnProperty]] throws if a data property is defined", () => {
561 const desc = new PropertyDescriptor({ value: undefined });
562 assertThrows(() => Object.defineProperty(desc, "get", {}));
563 });
564
565 it("[[Set]] works", () => {
566 const desc = new PropertyDescriptor({});
567 const fn = () => {};
568 desc.get = fn;
569 assertStrictEquals(desc.get, fn);
570 });
571
572 it("[[Set]] throws if not callable or undefined", () => {
573 const desc = new PropertyDescriptor({});
574 assertThrows(() => desc.get = null);
575 });
576
577 it("[[Set]] throws if a data property is defined", () => {
578 const desc = new PropertyDescriptor({ value: undefined });
579 assertThrows(() => desc.get = undefined);
580 });
581
582 it("[[Delete]] works", () => {
583 const desc = new PropertyDescriptor({ get: undefined });
584 delete desc.get;
585 assert(!("get" in desc));
586 });
587 });
588
589 describe("~set", () => {
590 it("[[DefineOwnProperty]] works", () => {
591 const desc = new PropertyDescriptor({});
592 Object.defineProperty(desc, "set", {});
593 assertStrictEquals(desc.set, undefined);
594 });
595
596 it("[[DefineOwnProperty]] throws for accessor properties", () => {
597 const desc = new PropertyDescriptor({});
598 assertThrows(() =>
599 Object.defineProperty(desc, "set", { get: undefined })
600 );
601 });
602
603 it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
604 const desc = new PropertyDescriptor({});
605 assertThrows(
606 () => Object.defineProperty(desc, "set", { value: null }),
607 );
608 });
609
610 it("[[DefineOwnProperty]] throws if a data property is defined", () => {
611 const desc = new PropertyDescriptor({ value: undefined });
612 assertThrows(() => Object.defineProperty(desc, "set", {}));
613 });
614
615 it("[[Set]] works", () => {
616 const desc = new PropertyDescriptor({});
617 const fn = (_) => {};
618 desc.set = fn;
619 assertStrictEquals(desc.set, fn);
620 });
621
622 it("[[Set]] throws if not callable or undefined", () => {
623 const desc = new PropertyDescriptor({});
624 assertThrows(() => desc.set = null);
625 });
626
627 it("[[Set]] throws if a data property is defined", () => {
628 const desc = new PropertyDescriptor({ value: undefined });
629 assertThrows(() => desc.set = undefined);
630 });
631
632 it("[[Delete]] works", () => {
633 const desc = new PropertyDescriptor({ set: undefined });
634 delete desc.set;
635 assert(!("set" in desc));
636 });
637 });
638
639 describe("~value", () => {
640 it("[[DefineOwnProperty]] works", () => {
641 const desc = new PropertyDescriptor({});
642 Object.defineProperty(desc, "value", {});
643 assertStrictEquals(desc.value, undefined);
644 });
645
646 it("[[DefineOwnProperty]] throws for accessor properties", () => {
647 const desc = new PropertyDescriptor({});
648 assertThrows(() =>
649 Object.defineProperty(desc, "value", { get: undefined })
650 );
651 });
652
653 it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
654 const desc = new PropertyDescriptor({ get: undefined });
655 assertThrows(() => Object.defineProperty(desc, "value", {}));
656 });
657
658 it("[[Set]] works", () => {
659 const desc = new PropertyDescriptor({});
660 desc.value = "success";
661 assertStrictEquals(desc.value, "success");
662 });
663
664 it("[[Set]] throws if an accessor property is defined", () => {
665 const desc = new PropertyDescriptor({ get: undefined });
666 assertThrows(() => desc.value = null);
667 });
668
669 it("[[Delete]] works", () => {
670 const desc = new PropertyDescriptor({ value: undefined });
671 delete desc.value;
672 assert(!("value" in desc));
673 });
674 });
675
676 describe("~writable", () => {
677 it("[[DefineOwnProperty]] coerces to a boolean", () => {
678 const desc = new PropertyDescriptor({});
679 Object.defineProperty(desc, "writable", {});
680 assertStrictEquals(desc.writable, false);
681 });
682
683 it("[[DefineOwnProperty]] throws for accessor properties", () => {
684 const desc = new PropertyDescriptor({});
685 assertThrows(() =>
686 Object.defineProperty(desc, "writable", { get: undefined })
687 );
688 });
689
690 it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
691 const desc = new PropertyDescriptor({ get: undefined });
692 assertThrows(() => Object.defineProperty(desc, "writable", {}));
693 });
694
695 it("[[Set]] coerces to a boolean", () => {
696 const desc = new PropertyDescriptor({});
697 desc.writable = undefined;
698 assertStrictEquals(desc.writable, false);
699 });
700
701 it("[[Set]] throws if an accessor property is defined", () => {
702 const desc = new PropertyDescriptor({ get: undefined });
703 assertThrows(() => desc.writable = false);
704 });
705
706 it("[[Delete]] works", () => {
707 const desc = new PropertyDescriptor({ writable: false });
708 delete desc.writable;
709 assert(!("writable" in desc));
710 });
711 });
712 });
713
714 describe("defineOwnProperties", () => {
715 it("[[Call]] defines properties from the provided objects", () => {
716 const obj = {};
717 defineOwnProperties(obj, {
718 etaoin: {},
719 shrdlu: {},
720 }, { cmfwyp: {} });
721 assert("etaoin" in obj);
722 assert("shrdlu" in obj);
723 assert("cmfwyp" in obj);
724 });
725
726 it("[[Call]] overrides earlier declarations with later ones", () => {
727 const obj = { etaoin: undefined };
728 defineOwnProperties(obj, {
729 etaoin: { value: "failure" },
730 }, {
731 etaoin: { value: "success" },
732 });
733 assertStrictEquals(obj.etaoin, "success");
734 });
735
736 it("[[Call]] returns the provided object", () => {
737 const obj = {};
738 assertStrictEquals(defineOwnProperties(obj), obj);
739 });
740 });
741
742 describe("deleteOwnProperty", () => {
743 it("[[Call]] deletes the provided property on the provided object", () => {
744 const obj = { failure: undefined };
745 deleteOwnProperty(obj, "failure");
746 assert(!("failure" in obj));
747 });
748
749 it("[[Call]] does nothing if the property doesn’t exist", () => {
750 const obj = Object.freeze({});
751 deleteOwnProperty(obj, "failure");
752 assert(!("failure" in obj));
753 });
754
755 it("[[Call]] throws if the property can’t be deleted", () => {
756 const obj = Object.seal({ failure: undefined });
757 assertThrows(() => deleteOwnProperty(obj, "failure"));
758 });
759
760 it("[[Call]] returns the provided object", () => {
761 const obj = {};
762 assertStrictEquals(deleteOwnProperty(obj, ""), obj);
763 });
764 });
765
766 describe("frozenCopy", () => {
767 it("[[Call]] returns a frozen object", () => {
768 assert(
769 Object.isFrozen(
770 frozenCopy(Object.create(null), {
771 data: {
772 configurable: true,
773 enumerable: true,
774 value: undefined,
775 writable: true,
776 },
777 accessor: {
778 configurable: true,
779 enumerable: true,
780 get: undefined,
781 },
782 }),
783 ),
784 );
785 });
786
787 it("[[Call]] ignores non·enumerable properties", () => {
788 assertEquals(
789 frozenCopy(
790 Object.create(null, {
791 data: { value: undefined },
792 accessor: { get: undefined },
793 }),
794 ),
795 {},
796 );
797 });
798
799 it("[[Call]] preserves accessor properties", () => {
800 const properties = {
801 both: {
802 configurable: false,
803 enumerable: true,
804 get: () => {},
805 set: (_) => {},
806 },
807 empty: {
808 configurable: false,
809 enumerable: true,
810 get: undefined,
811 set: undefined,
812 },
813 getter: {
814 configurable: false,
815 enumerable: true,
816 get: () => {},
817 set: undefined,
818 },
819 setter: {
820 configurable: false,
821 enumerable: true,
822 get: undefined,
823 set: (_) => {},
824 },
825 };
826 assertEquals(
827 Object.getOwnPropertyDescriptors(
828 frozenCopy(Object.create(null, properties)),
829 ),
830 properties,
831 );
832 });
833
834 it("[[Call]] does not copy properties on the prototype", () => {
835 assert(
836 !("failure" in
837 frozenCopy(Object.create({ failure: undefined }), {
838 data: {
839 configurable: true,
840 value: undefined,
841 writable: true,
842 },
843 accessor: { configurable: true, get: undefined },
844 })),
845 );
846 });
847
848 it("[[Call]] uses the species of the constructor", () => {
849 const species = { prototype: {} };
850 assertStrictEquals(
851 Object.getPrototypeOf(
852 frozenCopy({}, { [Symbol.species]: species }),
853 ),
854 species.prototype,
855 );
856 });
857
858 it("[[Call]] uses constructor if no species is defined", () => {
859 const constructor = { [Symbol.species]: null, prototype: {} };
860 assertStrictEquals(
861 Object.getPrototypeOf(frozenCopy({}, constructor)),
862 constructor.prototype,
863 );
864 });
865
866 it("[[Call]] uses the constructor on the object if none is provided", () => {
867 const constructor = { [Symbol.species]: null, prototype: {} };
868 assertStrictEquals(
869 Object.getPrototypeOf(frozenCopy({ constructor })),
870 constructor.prototype,
871 );
872 });
873
874 it("[[Call]] allows a null constructor", () => {
875 assertStrictEquals(
876 Object.getPrototypeOf(frozenCopy({}, null)),
877 null,
878 );
879 });
880 });
881
882 describe("getMethod", () => {
883 it("[[Call]] gets a method", () => {
884 const method = () => {};
885 assertStrictEquals(getMethod({ method }, "method"), method);
886 });
887
888 it("[[Call]] works for values coercible to objects", () => {
889 assertEquals(getMethod("", "toString"), String.prototype.toString);
890 });
891
892 it("[[Call]] throws for null and undefined", () => {
893 assertThrows(() => getMethod(null, "valueOf"));
894 assertThrows(() => getMethod(undefined, "valueOf"));
895 });
896
897 it("[[Call]] throws if the resulting value isn’t callable", () => {
898 assertThrows(() => getMethod({ "failure": true }, "failure"));
899 });
900 });
901
902 describe("getOwnPropertyKeys", () => {
903 it("[[Call]] gets own (but not inherited) property keys", () => {
904 assertEquals(getOwnPropertyKeys({ success: true }), ["success"]);
905 });
906
907 it("[[Call]] works for values coercible to objects", () => {
908 assertEquals(getOwnPropertyKeys("foo"), ["0", "1", "2", "length"]);
909 });
910
911 it("[[Call]] throws for null and undefined", () => {
912 assertThrows(() => getOwnPropertyKeys(null));
913 assertThrows(() => getOwnPropertyKeys(undefined));
914 });
915 });
916
917 describe("getPropertyValue", () => {
918 it("[[Call]] gets property values on the provided object", () => {
919 assertStrictEquals(
920 getPropertyValue({ success: true }, "success"),
921 true,
922 );
923 });
924
925 it("[[Call]] works for values coercible to objects", () => {
926 assertStrictEquals(
927 getPropertyValue("", "toString"),
928 String.prototype.toString,
929 );
930 });
931
932 it("[[Call]] throws for null and undefined", () => {
933 assertThrows(() => getPropertyValue(null, "valueOf"));
934 assertThrows(() => getPropertyValue(undefined, "valueOf"));
935 });
936 });
937
938 describe("hasProperty", () => {
939 it("[[Call]] gets whether a property exists on the provided object", () => {
940 assertStrictEquals(
941 hasProperty({ success: "etaoin" }, "success"),
942 true,
943 );
944 });
945
946 it("[[Call]] works for values coercible to objects", () => {
947 assertStrictEquals(hasProperty("", "toString"), true);
948 });
949
950 it("[[Call]] throws for null and undefined", () => {
951 assertThrows(() => hasProperty(null, "valueOf"));
952 assertThrows(() => hasProperty(undefined, "valueOf"));
953 });
954 });
955
956 describe("setPropertyValue", () => {
957 it("[[Call]] sets the provided property on the provided object", () => {
958 const obj = {};
959 setPropertyValue(obj, "success", true);
960 assertStrictEquals(obj.success, true);
961 });
962
963 it("[[Call]] calls setters", () => {
964 const setter = spy((_) => {});
965 const obj = Object.create(null, { success: { set: setter } });
966 setPropertyValue(obj, "success", true);
967 assertSpyCalls(setter, 1);
968 assertSpyCall(setter, 0, {
969 args: [true],
970 self: obj,
971 });
972 });
973
974 it("[[Call]] walks the prototype chain", () => {
975 const setter = spy((_) => {});
976 const obj = Object.create(
977 Object.create(null, { success: { set: setter } }),
978 );
979 setPropertyValue(obj, "success", true);
980 assertSpyCalls(setter, 1);
981 assertSpyCall(setter, 0, {
982 args: [true],
983 self: obj,
984 });
985 });
986
987 it("[[Call]] uses the provided receiver", () => {
988 const setter = spy((_) => {});
989 const obj = Object.create(null, { success: { set: setter } });
990 const receiver = {};
991 setPropertyValue(obj, "success", true, receiver);
992 assertSpyCalls(setter, 1);
993 assertSpyCall(setter, 0, {
994 args: [true],
995 self: receiver,
996 });
997 });
998
999 it("[[Call]] throws if the property can’t be set", () => {
1000 const obj = Object.freeze({ failure: undefined });
1001 assertThrows(() => setPropertyValue(obj, "failure", true));
1002 });
1003
1004 it("[[Call]] returns the provided object", () => {
1005 const obj = {};
1006 assertStrictEquals(setPropertyValue(obj, "", undefined), obj);
1007 });
1008 });
1009
1010 describe("toObject", () => {
1011 it("returns the input for objects", () => {
1012 const obj = {};
1013 assertStrictEquals(toObject(obj), obj);
1014 });
1015
1016 it("throws for nullish values", () => {
1017 assertThrows(() => toObject(null));
1018 assertThrows(() => toObject(void {}));
1019 });
1020
1021 it("returns a wrapper object for other primitives", () => {
1022 const sym = Symbol();
1023 assertStrictEquals(typeof toObject(sym), "object");
1024 assertStrictEquals(toObject(sym).valueOf(), sym);
1025 });
1026 });
1027
1028 describe("toPropertyKey", () => {
1029 it("returns a string or symbol", () => {
1030 const sym = Symbol();
1031 assertStrictEquals(toPropertyKey(sym), sym);
1032 assertStrictEquals(
1033 toPropertyKey(new String("success")),
1034 "success",
1035 );
1036 });
1037
1038 it("favours the `toString` representation", () => {
1039 assertStrictEquals(
1040 toPropertyKey({
1041 toString() {
1042 return "success";
1043 },
1044 valueOf() {
1045 return "failure";
1046 },
1047 }),
1048 "success",
1049 );
1050 });
1051 });
This page took 0.303656 seconds and 5 git commands to generate.