X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/7c3a2eda590637af9463e5a59ab798f802d274a9..6e6d4e3261c1c943fe44fa9e381bcf8bf1441fd6:/object.test.js?ds=sidebyside diff --git a/object.test.js b/object.test.js index da890d3..efef4d8 100644 --- a/object.test.js +++ b/object.test.js @@ -22,12 +22,255 @@ import { defineOwnProperties, deleteOwnProperty, frozenCopy, + getMethod, + getOwnPropertyKeys, + getPropertyValue, + hasProperty, + LazyLoader, PropertyDescriptor, setPropertyValue, toObject, toPropertyKey, } from "./object.js"; +describe("LazyLoader", () => { + const symbol = Symbol(); + const prototype = {}; + const etaoinMethod = spy(() => "success"); + const shrdluMethod = spy(() => "success"); + const cmfwypMethod = spy(() => "success"); + const vbgkqjMethod = spy(() => "success"); + const methodsObject = Object.create( + prototype, + { + etaoin: { + configurable: false, + enumerable: true, + value: etaoinMethod, + writable: false, + }, + shrdlu: { + configurable: true, + enumerable: false, + value: shrdluMethod, + writable: false, + }, + cmfwyp: { + configurable: true, + enumerable: false, + get() { + return cmfwypMethod; + }, + }, + vbgkqj: { + configurable: false, + enumerable: true, + get() { + return vbgkqjMethod; + }, + set(_) {}, + }, + xzfiflffffi: { configurable: true, enumerable: false, set(_) {} }, + [symbol]: { + configurable: true, + enumerable: false, + value: "failure", + writable: true, + }, + }, + ); + + it("[[Construct]] creates a new object which inherits from the correct prototype", () => { + assertStrictEquals( + Object.getPrototypeOf(new LazyLoader(methodsObject)), + prototype, + ); + }); + + it("[[Construct]] creates a new object with the desired properties", () => { + assertEquals( + Reflect.ownKeys(new LazyLoader(methodsObject)), + ["etaoin", "shrdlu", "cmfwyp", "vbgkqj", "xzfiflffffi", symbol], + ); + }); + + it("[[Construct]] creates a new object with configurable properties", () => { + assertEquals( + Object.fromEntries( + function* (ll) { + for (const key of Reflect.ownKeys(ll)) { + yield [ + key, + Object.getOwnPropertyDescriptor(ll, key).configurable, + ]; + } + }(new LazyLoader(methodsObject)), + ), + { + etaoin: true, + shrdlu: true, + cmfwyp: true, + vbgkqj: true, + xzfiflffffi: true, + [symbol]: true, + }, + ); + }); + + it("[[Construct]] creates a new object with the correct enumerability", () => { + assertEquals( + Object.fromEntries( + function* (ll) { + for (const key of Reflect.ownKeys(ll)) { + yield [ + key, + Object.getOwnPropertyDescriptor(ll, key).enumerable, + ]; + } + }(new LazyLoader(methodsObject)), + ), + { + etaoin: true, + shrdlu: false, + cmfwyp: false, + vbgkqj: true, + xzfiflffffi: false, + [symbol]: false, + }, + ); + }); + + it("[[Construct]] creates a new object with defined getters", () => { + assertEquals( + Object.fromEntries( + function* (ll) { + for (const key of Reflect.ownKeys(ll)) { + yield [ + key, + Object.getOwnPropertyDescriptor(ll, key).get !== void {}, + ]; + } + }(new LazyLoader(methodsObject)), + ), + { + etaoin: true, + shrdlu: true, + cmfwyp: true, + vbgkqj: true, + xzfiflffffi: true, + [symbol]: true, + }, + ); + }); + + it("[[Construct]] creates a new object with defined setters for writable properties only", () => { + assertEquals( + Object.fromEntries( + function* (ll) { + for (const key of Reflect.ownKeys(ll)) { + yield [ + key, + Object.getOwnPropertyDescriptor(ll, key).set !== void {}, + ]; + } + }(new LazyLoader(methodsObject)), + ), + { + etaoin: false, + shrdlu: false, + cmfwyp: false, + vbgkqj: false, + xzfiflffffi: false, + [symbol]: true, + }, + ); + }); + + describe("[[Construct]] creates a new object with correct getter behaviour", () => { + const ll = new LazyLoader(methodsObject); + ll.etaoin; + assertEquals( + Object.getOwnPropertyDescriptor(ll, "etaoin"), + { + configurable: false, + enumerable: true, + value: "success", + writable: false, + }, + ); + assertSpyCalls(etaoinMethod, 1); + assertSpyCall(etaoinMethod, 0, { + args: [], + self: ll, + returned: "success", + }); + ll.shrdlu; + assertEquals( + Object.getOwnPropertyDescriptor(ll, "shrdlu"), + { + configurable: true, + enumerable: false, + value: "success", + writable: false, + }, + ); + assertSpyCalls(shrdluMethod, 1); + assertSpyCall(shrdluMethod, 0, { + args: [], + self: ll, + returned: "success", + }); + ll.cmfwyp; + assertEquals( + Object.getOwnPropertyDescriptor(ll, "cmfwyp"), + { + configurable: true, + enumerable: false, + value: "success", + writable: false, + }, + ); + assertSpyCalls(cmfwypMethod, 1); + assertSpyCall(cmfwypMethod, 0, { + args: [], + self: ll, + returned: "success", + }); + ll.vbgkqj; + assertEquals( + Object.getOwnPropertyDescriptor(ll, "vbgkqj"), + { + configurable: false, + enumerable: true, + value: "success", + writable: false, + }, + ); + assertSpyCalls(vbgkqjMethod, 1); + assertSpyCall(vbgkqjMethod, 0, { + args: [], + self: ll, + returned: "success", + }); + assertThrows(() => ll.xzfiflffffi); + assertThrows(() => ll[symbol]); + }); + + describe("[[Construct]] creates a new object with correct setter behaviour", () => { + const ll = new LazyLoader(methodsObject); + ll[symbol] = "success"; + assertEquals( + Object.getOwnPropertyDescriptor(ll, symbol), + { + configurable: true, + enumerable: false, + value: "success", + writable: true, + }, + ); + }); +}); + describe("PropertyDescriptor", () => { it("[[Construct]] creates a new PropertyDescriptor", () => { assertStrictEquals( @@ -636,6 +879,80 @@ describe("frozenCopy", () => { }); }); +describe("getMethod", () => { + it("[[Call]] gets a method", () => { + const method = () => {}; + assertStrictEquals(getMethod({ method }, "method"), method); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertEquals(getMethod("", "toString"), String.prototype.toString); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => getMethod(null, "valueOf")); + assertThrows(() => getMethod(undefined, "valueOf")); + }); + + it("[[Call]] throws if the resulting value isn’t callable", () => { + assertThrows(() => getMethod({ "failure": true }, "failure")); + }); +}); + +describe("getOwnPropertyKeys", () => { + it("[[Call]] gets own (but not inherited) property keys", () => { + assertEquals(getOwnPropertyKeys({ success: true }), ["success"]); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertEquals(getOwnPropertyKeys("foo"), ["0", "1", "2", "length"]); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => getOwnPropertyKeys(null)); + assertThrows(() => getOwnPropertyKeys(undefined)); + }); +}); + +describe("getPropertyValue", () => { + it("[[Call]] gets property values on the provided object", () => { + assertStrictEquals( + getPropertyValue({ success: true }, "success"), + true, + ); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertStrictEquals( + getPropertyValue("", "toString"), + String.prototype.toString, + ); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => getPropertyValue(null, "valueOf")); + assertThrows(() => getPropertyValue(undefined, "valueOf")); + }); +}); + +describe("hasProperty", () => { + it("[[Call]] gets whether a property exists on the provided object", () => { + assertStrictEquals( + hasProperty({ success: "etaoin" }, "success"), + true, + ); + }); + + it("[[Call]] works for values coercible to objects", () => { + assertStrictEquals(hasProperty("", "toString"), true); + }); + + it("[[Call]] throws for null and undefined", () => { + assertThrows(() => hasProperty(null, "valueOf")); + assertThrows(() => hasProperty(undefined, "valueOf")); + }); +}); + describe("setPropertyValue", () => { it("[[Call]] sets the provided property on the provided object", () => { const obj = {}; @@ -696,9 +1013,9 @@ describe("toObject", () => { assertStrictEquals(toObject(obj), obj); }); - it("returns a new object for nullish values", () => { - assertEquals(toObject(null), {}); - assertEquals(toObject(void {}), {}); + it("throws for nullish values", () => { + assertThrows(() => toObject(null)); + assertThrows(() => toObject(void {})); }); it("returns a wrapper object for other primitives", () => {