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(
});
});
+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 = {};
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", () => {