]> Lady’s Gitweb - Pisces/blobdiff - object.test.js
Add methods for own entries and values to object.js
[Pisces] / object.test.js
index a2e718b189a93ba31179f29dc12b7f9086752f35..cba44b948824ba411ecb966ae3e80a1265a3f5c4 100644 (file)
@@ -10,6 +10,7 @@
 import {
   assert,
   assertEquals,
+  assertNotStrictEquals,
   assertSpyCall,
   assertSpyCalls,
   assertStrictEquals,
@@ -19,6 +20,8 @@ import {
   spy,
 } from "./dev-deps.js";
 import {
+  defineOwnDataProperty,
+  defineOwnNonenumerableDataProperty,
   defineOwnProperties,
   defineOwnProperty,
   deleteOwnProperty,
@@ -27,9 +30,12 @@ import {
   getMethod,
   getOwnPropertyDescriptor,
   getOwnPropertyDescriptors,
+  getOwnPropertyEntries,
   getOwnPropertyKeys,
   getOwnPropertyStrings,
   getOwnPropertySymbols,
+  getOwnPropertyValue,
+  getOwnPropertyValues,
   getPropertyValue,
   getPrototype,
   hasOwnProperty,
@@ -37,6 +43,7 @@ import {
   isArraylikeObject,
   isConcatSpreadableObject,
   isExtensibleObject,
+  isPropertyDescriptorRecord,
   isUnfrozenObject,
   isUnsealedObject,
   LazyLoader,
@@ -47,13 +54,12 @@ import {
   objectCreate,
   objectFromEntries,
   preventExtensions,
-  PropertyDescriptor,
   seal,
   setPropertyValue,
   setPropertyValues,
   setPrototype,
   toObject,
-  toPropertyKey,
+  toPropertyDescriptorRecord,
 } from "./object.js";
 
 describe("LazyLoader", () => {
@@ -310,576 +316,105 @@ describe("LazyLoader", () => {
   });
 });
 
-describe("PropertyDescriptor", () => {
-  it("[[Call]] throws an error", () => {
-    assertThrows(() => PropertyDescriptor({}));
+describe("defineOwnDataProperty", () => {
+  it("[[Call]] defines the property", () => {
+    const obj = {};
+    defineOwnDataProperty(obj, "etaoin", "success");
+    assert(Object.hasOwn(obj, "etaoin"));
+    assertStrictEquals(obj.etaoin, "success");
+  });
+
+  it("[[Call]] defines a configurable, enumerable, writable property", () => {
+    const obj = {};
+    defineOwnDataProperty(obj, "etaoin", "success");
+    assertEquals(
+      Object.getOwnPropertyDescriptor(obj, "etaoin"),
+      {
+        configurable: true,
+        enumerable: true,
+        value: "success",
+        writable: true,
+      },
+    );
   });
 
-  it("[[Construct]] creates a new PropertyDescriptor", () => {
+  it("[[Call]] returns the provided object", () => {
+    const obj = {};
     assertStrictEquals(
-      Object.getPrototypeOf(new PropertyDescriptor({})),
-      PropertyDescriptor.prototype,
+      defineOwnDataProperty(obj, "etaoin", null),
+      obj,
     );
   });
 
-  it("[[Construct]] throws for primitives", () => {
-    assertThrows(() => new PropertyDescriptor("failure"));
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new defineOwnDataProperty(obj, "etaoin", null));
   });
 
   describe(".length", () => {
     it("[[Get]] returns the correct length", () => {
-      assertStrictEquals(PropertyDescriptor.length, 1);
+      assertStrictEquals(defineOwnDataProperty.length, 3);
     });
   });
 
   describe(".name", () => {
     it("[[Get]] returns the correct name", () => {
       assertStrictEquals(
-        PropertyDescriptor.name,
-        "PropertyDescriptor",
+        defineOwnDataProperty.name,
+        "defineOwnDataProperty",
       );
     });
   });
+});
 
-  describe("::complete", () => {
-    it("[[Call]] completes a generic descriptor", () => {
-      const desc = {};
-      PropertyDescriptor.prototype.complete.call(desc);
-      assertEquals(desc, {
-        configurable: false,
-        enumerable: false,
-        value: undefined,
-        writable: false,
-      });
-    });
-
-    it("[[Call]] completes a data descriptor", () => {
-      const desc = { value: undefined };
-      PropertyDescriptor.prototype.complete.call(desc);
-      assertEquals(desc, {
-        configurable: false,
-        enumerable: false,
-        value: undefined,
-        writable: false,
-      });
-    });
+describe("defineOwnNonenumerableDataProperty", () => {
+  it("[[Call]] defines the property", () => {
+    const obj = {};
+    defineOwnNonenumerableDataProperty(obj, "etaoin", "success");
+    assert(Object.hasOwn(obj, "etaoin"));
+    assertStrictEquals(obj.etaoin, "success");
+  });
 
-    it("[[Call]] completes an accessor descriptor", () => {
-      const desc = { get: undefined };
-      PropertyDescriptor.prototype.complete.call(desc);
-      assertEquals(desc, {
-        configurable: false,
+  it("[[Call]] defines a configurable, non·enumerable, writable property", () => {
+    const obj = {};
+    defineOwnNonenumerableDataProperty(obj, "etaoin", "success");
+    assertEquals(
+      Object.getOwnPropertyDescriptor(obj, "etaoin"),
+      {
+        configurable: true,
         enumerable: false,
-        get: undefined,
-        set: undefined,
-      });
-    });
-
-    describe(".length", () => {
-      it("[[Get]] returns the correct length", () => {
-        assertStrictEquals(
-          PropertyDescriptor.prototype.complete.length,
-          0,
-        );
-      });
-    });
-
-    describe(".name", () => {
-      it("[[Get]] returns the correct name", () => {
-        assertStrictEquals(
-          PropertyDescriptor.prototype.complete.name,
-          "complete",
-        );
-      });
-    });
+        value: "success",
+        writable: true,
+      },
+    );
   });
 
-  describe("::isAccessorDescriptor", () => {
-    it("[[Get]] returns false for a generic descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isAccessorDescriptor",
-          {},
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns false for a data descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isAccessorDescriptor",
-          { value: undefined },
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns true for an accessor descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isAccessorDescriptor",
-          { get: undefined },
-        ),
-        true,
-      );
-    });
-
-    describe("[[GetOwnProperty]].get.length", () => {
-      it("[[Get]] returns the correct length", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isAccessorDescriptor",
-          ).get.length,
-          0,
-        );
-      });
-    });
-
-    describe("[[GetOwnProperty]].get.name", () => {
-      it("[[Get]] returns the correct name", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isAccessorDescriptor",
-          ).get.name,
-          "get isAccessorDescriptor",
-        );
-      });
-    });
+  it("[[Call]] returns the provided object", () => {
+    const obj = {};
+    assertStrictEquals(
+      defineOwnNonenumerableDataProperty(obj, "etaoin", null),
+      obj,
+    );
   });
 
-  describe("::isDataDescriptor", () => {
-    it("[[Get]] returns false for a generic descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isDataDescriptor",
-          {},
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns true for a data descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isDataDescriptor",
-          { value: undefined },
-        ),
-        true,
-      );
-    });
-
-    it("[[Get]] returns false for an accessor descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isDataDescriptor",
-          { get: undefined },
-        ),
-        false,
-      );
-    });
-
-    describe("[[GetOwnProperty]].get.length", () => {
-      it("[[Get]] returns the correct length", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isDataDescriptor",
-          ).get.length,
-          0,
-        );
-      });
-    });
-
-    describe("[[GetOwnProperty]].get.name", () => {
-      it("[[Get]] returns the correct name", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isDataDescriptor",
-          ).get.name,
-          "get isDataDescriptor",
-        );
-      });
-    });
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() =>
+      new defineOwnNonenumerableDataProperty(obj, "etaoin", null)
+    );
   });
 
-  describe("::isFullyPopulated", () => {
-    it("[[Get]] returns false for a generic descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isFullyPopulated",
-          {},
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns false for a non‐fully‐populated data descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isFullyPopulated",
-          { value: undefined },
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns true for a fully‐populated data descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(PropertyDescriptor.prototype, "isFullyPopulated", {
-          configurable: true,
-          enumerable: true,
-          value: undefined,
-          writable: true,
-        }),
-        true,
-      );
-    });
-
-    it("[[Get]] returns false for a non‐fully‐populated accessor descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isFullyPopulated",
-          { get: undefined },
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns true for a fully‐populated accessor descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(PropertyDescriptor.prototype, "isFullyPopulated", {
-          configurable: true,
-          enumerable: true,
-          get: undefined,
-          set: undefined,
-        }),
-        true,
-      );
-    });
-
-    describe("[[GetOwnProperty]].get.length", () => {
-      it("[[Get]] returns the correct length", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isFullyPopulated",
-          ).get.length,
-          0,
-        );
-      });
-    });
-
-    describe("[[GetOwnProperty]].get.name", () => {
-      it("[[Get]] returns the correct name", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isFullyPopulated",
-          ).get.name,
-          "get isFullyPopulated",
-        );
-      });
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(defineOwnNonenumerableDataProperty.length, 3);
     });
   });
 
-  describe("::isGenericDescriptor", () => {
-    it("[[Get]] returns true for a generic descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isGenericDescriptor",
-          {},
-        ),
-        true,
-      );
-    });
-
-    it("[[Get]] returns true for a data descriptor", () => {
-      assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isGenericDescriptor",
-          { value: undefined },
-        ),
-        false,
-      );
-    });
-
-    it("[[Get]] returns false for an accessor descriptor", () => {
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
       assertStrictEquals(
-        Reflect.get(
-          PropertyDescriptor.prototype,
-          "isGenericDescriptor",
-          { get: undefined },
-        ),
-        false,
+        defineOwnNonenumerableDataProperty.name,
+        "defineOwnNonenumerableDataProperty",
       );
     });
-
-    describe("[[GetOwnProperty]].get.length", () => {
-      it("[[Get]] returns the correct length", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isGenericDescriptor",
-          ).get.length,
-          0,
-        );
-      });
-    });
-
-    describe("[[GetOwnProperty]].get.name", () => {
-      it("[[Get]] returns the correct name", () => {
-        assertStrictEquals(
-          Object.getOwnPropertyDescriptor(
-            PropertyDescriptor.prototype,
-            "isGenericDescriptor",
-          ).get.name,
-          "get isGenericDescriptor",
-        );
-      });
-    });
-  });
-
-  describe("~configurable", () => {
-    it("[[DefineOwnProperty]] coerces to a boolean", () => {
-      const desc = new PropertyDescriptor({});
-      Object.defineProperty(desc, "configurable", {});
-      assertStrictEquals(desc.configurable, false);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "configurable", { get: undefined })
-      );
-    });
-
-    it("[[Set]] coerces to a boolean", () => {
-      const desc = new PropertyDescriptor({});
-      desc.configurable = undefined;
-      assertStrictEquals(desc.configurable, false);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = new PropertyDescriptor({ configurable: false });
-      delete desc.configurable;
-      assert(!("configurable" in desc));
-    });
-  });
-
-  describe("~enumerable", () => {
-    it("[[DefineOwnProperty]] coerces to a boolean", () => {
-      const desc = new PropertyDescriptor({});
-      Object.defineProperty(desc, "enumerable", {});
-      assertStrictEquals(desc.enumerable, false);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "enumerable", { get: undefined })
-      );
-    });
-
-    it("[[Set]] coerces to a boolean", () => {
-      const desc = new PropertyDescriptor({});
-      desc.enumerable = undefined;
-      assertStrictEquals(desc.enumerable, false);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = new PropertyDescriptor({ enumerable: false });
-      delete desc.enumerable;
-      assert(!("enumerable" in desc));
-    });
-  });
-
-  describe("~get", () => {
-    it("[[DefineOwnProperty]] works", () => {
-      const desc = new PropertyDescriptor({});
-      Object.defineProperty(desc, "get", {});
-      assertStrictEquals(desc.get, undefined);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "get", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(
-        () => Object.defineProperty(desc, "get", { value: null }),
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
-      const desc = new PropertyDescriptor({ value: undefined });
-      assertThrows(() => Object.defineProperty(desc, "get", {}));
-    });
-
-    it("[[Set]] works", () => {
-      const desc = new PropertyDescriptor({});
-      const fn = () => {};
-      desc.get = fn;
-      assertStrictEquals(desc.get, fn);
-    });
-
-    it("[[Set]] throws if not callable or undefined", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() => desc.get = null);
-    });
-
-    it("[[Set]] throws if a data property is defined", () => {
-      const desc = new PropertyDescriptor({ value: undefined });
-      assertThrows(() => desc.get = undefined);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = new PropertyDescriptor({ get: undefined });
-      delete desc.get;
-      assert(!("get" in desc));
-    });
-  });
-
-  describe("~set", () => {
-    it("[[DefineOwnProperty]] works", () => {
-      const desc = new PropertyDescriptor({});
-      Object.defineProperty(desc, "set", {});
-      assertStrictEquals(desc.set, undefined);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "set", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(
-        () => Object.defineProperty(desc, "set", { value: null }),
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
-      const desc = new PropertyDescriptor({ value: undefined });
-      assertThrows(() => Object.defineProperty(desc, "set", {}));
-    });
-
-    it("[[Set]] works", () => {
-      const desc = new PropertyDescriptor({});
-      const fn = (_) => {};
-      desc.set = fn;
-      assertStrictEquals(desc.set, fn);
-    });
-
-    it("[[Set]] throws if not callable or undefined", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() => desc.set = null);
-    });
-
-    it("[[Set]] throws if a data property is defined", () => {
-      const desc = new PropertyDescriptor({ value: undefined });
-      assertThrows(() => desc.set = undefined);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = new PropertyDescriptor({ set: undefined });
-      delete desc.set;
-      assert(!("set" in desc));
-    });
-  });
-
-  describe("~value", () => {
-    it("[[DefineOwnProperty]] works", () => {
-      const desc = new PropertyDescriptor({});
-      Object.defineProperty(desc, "value", {});
-      assertStrictEquals(desc.value, undefined);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "value", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
-      const desc = new PropertyDescriptor({ get: undefined });
-      assertThrows(() => Object.defineProperty(desc, "value", {}));
-    });
-
-    it("[[Set]] works", () => {
-      const desc = new PropertyDescriptor({});
-      desc.value = "success";
-      assertStrictEquals(desc.value, "success");
-    });
-
-    it("[[Set]] throws if an accessor property is defined", () => {
-      const desc = new PropertyDescriptor({ get: undefined });
-      assertThrows(() => desc.value = null);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = new PropertyDescriptor({ value: undefined });
-      delete desc.value;
-      assert(!("value" in desc));
-    });
-  });
-
-  describe("~writable", () => {
-    it("[[DefineOwnProperty]] coerces to a boolean", () => {
-      const desc = new PropertyDescriptor({});
-      Object.defineProperty(desc, "writable", {});
-      assertStrictEquals(desc.writable, false);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = new PropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "writable", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
-      const desc = new PropertyDescriptor({ get: undefined });
-      assertThrows(() => Object.defineProperty(desc, "writable", {}));
-    });
-
-    it("[[Set]] coerces to a boolean", () => {
-      const desc = new PropertyDescriptor({});
-      desc.writable = undefined;
-      assertStrictEquals(desc.writable, false);
-    });
-
-    it("[[Set]] throws if an accessor property is defined", () => {
-      const desc = new PropertyDescriptor({ get: undefined });
-      assertThrows(() => desc.writable = false);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = new PropertyDescriptor({ writable: false });
-      delete desc.writable;
-      assert(!("writable" in desc));
-    });
   });
 });
 
@@ -887,7 +422,7 @@ describe("defineOwnProperty", () => {
   it("[[Call]] defines the property", () => {
     const obj = {};
     defineOwnProperty(obj, "etaoin", {});
-    assert("etaoin" in obj);
+    assert(Object.hasOwn(obj, "etaoin"));
   });
 
   it("[[Call]] returns the provided object", () => {
@@ -1099,17 +634,45 @@ describe("frozenCopy", () => {
     );
   });
 
+  it("[[Call]] preserves data properties", () => {
+    const properties = {
+      implied: {
+        configurable: false,
+        enumerable: true,
+      },
+      writable: {
+        configurable: false,
+        enumerable: true,
+        value: "etaoin",
+        writable: true,
+      },
+      nonwritable: {
+        configurable: false,
+        enumerable: true,
+        value: "shrdlu",
+        writable: false,
+      },
+    };
+    assertEquals(
+      Object.getOwnPropertyDescriptors(
+        frozenCopy(Object.create(null, properties)),
+      ),
+      {
+        implied: {
+          ...properties.implied,
+          value: undefined,
+          writable: false,
+        },
+        writable: { ...properties.writable, writable: false },
+        nonwritable: properties.nonwritable,
+      },
+    );
+  });
+
   it("[[Call]] does not copy properties on the prototype", () => {
     assert(
       !("failure" in
-        frozenCopy(Object.create({ failure: undefined }), {
-          data: {
-            configurable: true,
-            value: undefined,
-            writable: true,
-          },
-          accessor: { configurable: true, get: undefined },
-        })),
+        frozenCopy(Object.create({ failure: undefined }))),
     );
   });
 
@@ -1280,6 +843,58 @@ describe("getOwnPropertyDescriptors", () => {
   });
 });
 
+describe("getOwnPropertyEntries", () => {
+  it("[[Call]] gets own (but not inherited) property entries", () => {
+    assertEquals(
+      getOwnPropertyEntries({ success: true }),
+      [["success", true]],
+    );
+  });
+
+  it("[[Call]] works for values coercible to objects", () => {
+    assertEquals(
+      getOwnPropertyEntries("foo"),
+      [["0", "f"], ["1", "o"], ["2", "o"], ["length", 3]],
+    );
+  });
+
+  it("[[Call]] uses the provided receiver", () => {
+    const target = {};
+    assertEquals(
+      getOwnPropertyEntries({
+        get success() {
+          return this;
+        },
+      }, target),
+      [["success", target]],
+    );
+  });
+
+  it("[[Call]] throws for null and undefined", () => {
+    assertThrows(() => getOwnPropertyEntries(null));
+    assertThrows(() => getOwnPropertyEntries(undefined));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getOwnPropertyEntries({}));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getOwnPropertyEntries.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        getOwnPropertyEntries.name,
+        "getOwnPropertyEntries",
+      );
+    });
+  });
+});
+
 describe("getOwnPropertyKeys", () => {
   it("[[Call]] gets own (but not inherited) property keys", () => {
     assertEquals(getOwnPropertyKeys({ success: true }), ["success"]);
@@ -1348,43 +963,152 @@ describe("getOwnPropertyStrings", () => {
   describe(".name", () => {
     it("[[Get]] returns the correct name", () => {
       assertStrictEquals(
-        getOwnPropertyStrings.name,
-        "getOwnPropertyStrings",
+        getOwnPropertyStrings.name,
+        "getOwnPropertyStrings",
+      );
+    });
+  });
+});
+
+describe("getOwnPropertySymbols", () => {
+  it("[[Call]] gets own symbol keys", () => {
+    const sym = Symbol();
+    assertEquals(getOwnPropertySymbols({ [sym]: true }), [sym]);
+  });
+
+  it("[[Call]] works for values coercible to objects", () => {
+    assertEquals(getOwnPropertySymbols("foo"), []);
+  });
+
+  it("[[Call]] throws for null and undefined", () => {
+    assertThrows(() => getOwnPropertySymbols(null));
+    assertThrows(() => getOwnPropertySymbols(undefined));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getOwnPropertySymbols({}));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getOwnPropertySymbols.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        getOwnPropertySymbols.name,
+        "getOwnPropertySymbols",
+      );
+    });
+  });
+});
+
+describe("getOwnPropertyValue", () => {
+  it("[[Call]] gets the own property value", () => {
+    assertStrictEquals(
+      getOwnPropertyValue({ success: true }, "success"),
+      true,
+    );
+  });
+
+  it("[[Call]] returns undefined for non‐own properties", () => {
+    assertStrictEquals(
+      getOwnPropertyValue(Object.create({ success: true }), "success"),
+      undefined,
+    );
+  });
+
+  it("[[Call]] works for values coercible to objects", () => {
+    assertStrictEquals(getOwnPropertyValue("foo", "length"), 3);
+  });
+
+  it("[[Call]] uses the provided receiver", () => {
+    const target = {};
+    assertStrictEquals(
+      getOwnPropertyValue(
+        {
+          get success() {
+            return this;
+          },
+        },
+        "success",
+        target,
+      ),
+      target,
+    );
+  });
+
+  it("[[Call]] throws for null and undefined", () => {
+    assertThrows(() => getOwnPropertyValue(null));
+    assertThrows(() => getOwnPropertyValue(undefined));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getOwnPropertyValue({}));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getOwnPropertyValue.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        getOwnPropertyValue.name,
+        "getOwnPropertyValue",
       );
     });
   });
 });
 
-describe("getOwnPropertySymbols", () => {
-  it("[[Call]] gets own symbol keys", () => {
-    const sym = Symbol();
-    assertEquals(getOwnPropertySymbols({ [sym]: true }), [sym]);
+describe("getOwnPropertyValues", () => {
+  it("[[Call]] gets own (but not inherited) property values", () => {
+    assertEquals(getOwnPropertyValues({ success: true }), [true]);
   });
 
   it("[[Call]] works for values coercible to objects", () => {
-    assertEquals(getOwnPropertySymbols("foo"), []);
+    assertEquals(
+      getOwnPropertyValues("foo"),
+      ["f", "o", "o", 3],
+    );
+  });
+
+  it("[[Call]] uses the provided receiver", () => {
+    const target = {};
+    assertEquals(
+      getOwnPropertyValues({
+        get success() {
+          return this;
+        },
+      }, target),
+      [target],
+    );
   });
 
   it("[[Call]] throws for null and undefined", () => {
-    assertThrows(() => getOwnPropertySymbols(null));
-    assertThrows(() => getOwnPropertySymbols(undefined));
+    assertThrows(() => getOwnPropertyValues(null));
+    assertThrows(() => getOwnPropertyValues(undefined));
   });
 
   it("[[Construct]] throws an error", () => {
-    assertThrows(() => new getOwnPropertySymbols({}));
+    assertThrows(() => new getOwnPropertyValues({}));
   });
 
   describe(".length", () => {
     it("[[Get]] returns the correct length", () => {
-      assertStrictEquals(getOwnPropertySymbols.length, 1);
+      assertStrictEquals(getOwnPropertyValues.length, 1);
     });
   });
 
   describe(".name", () => {
     it("[[Get]] returns the correct name", () => {
       assertStrictEquals(
-        getOwnPropertySymbols.name,
-        "getOwnPropertySymbols",
+        getOwnPropertyValues.name,
+        "getOwnPropertyValues",
       );
     });
   });
@@ -1687,6 +1411,45 @@ describe("isExtensibleObject", () => {
   });
 });
 
+describe("isPropertyDescriptorRecord", () => {
+  it("[[Call]] returns true for objects created by toPropertyDescriptorRecord", () => {
+    assertStrictEquals(
+      isPropertyDescriptorRecord(toPropertyDescriptorRecord({})),
+      true,
+    );
+  });
+
+  it("[[Get]] returns false for other objects", () => {
+    assertStrictEquals(
+      isPropertyDescriptorRecord(Object.create(null)),
+      false,
+    );
+  });
+
+  it("[[Get]] returns false for undefined", () => {
+    assertStrictEquals(isPropertyDescriptorRecord(undefined), false);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new isPropertyDescriptorRecord({}));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(isPropertyDescriptorRecord.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        isPropertyDescriptorRecord.name,
+        "isPropertyDescriptorRecord",
+      );
+    });
+  });
+});
+
 describe("isUnfrozenObject", () => {
   it("[[Call]] returns true for unfrozen objects", () => {
     assertStrictEquals(isUnfrozenObject({}), true);
@@ -2332,43 +2095,274 @@ describe("toObject", () => {
   });
 });
 
-describe("toPropertyKey", () => {
-  it("returns a string or symbol", () => {
-    const sym = Symbol();
-    assertStrictEquals(toPropertyKey(sym), sym);
-    assertStrictEquals(
-      toPropertyKey(new String("success")),
-      "success",
-    );
+describe("toPropertyDescriptorRecord", () => {
+  it("[[Call]] creates a new property descriptor record", () => {
+    const obj = {};
+    const desc = toPropertyDescriptorRecord(obj);
+    assertEquals(obj, desc);
+    assertNotStrictEquals(obj, desc);
   });
 
-  it("favours the `toString` representation", () => {
-    assertStrictEquals(
-      toPropertyKey({
-        toString() {
-          return "success";
-        },
-        valueOf() {
-          return "failure";
-        },
+  it("[[Call]] coerces the values", () => {
+    assertEquals(
+      toPropertyDescriptorRecord({
+        configurable: undefined,
+        enumerable: undefined,
+        writable: undefined,
       }),
-      "success",
+      { configurable: false, enumerable: false, writable: false },
     );
   });
 
+  it("[[Construct]] throws for primitives", () => {
+    assertThrows(() => toPropertyDescriptorRecord(undefined));
+    assertThrows(() => toPropertyDescriptorRecord("failure"));
+  });
+
   it("[[Construct]] throws an error", () => {
-    assertThrows(() => new toPropertyKey(""));
+    assertThrows(() => new toPropertyDescriptorRecord({}));
   });
 
   describe(".length", () => {
     it("[[Get]] returns the correct length", () => {
-      assertStrictEquals(toPropertyKey.length, 1);
+      assertStrictEquals(toPropertyDescriptorRecord.length, 1);
     });
   });
 
   describe(".name", () => {
     it("[[Get]] returns the correct name", () => {
-      assertStrictEquals(toPropertyKey.name, "toPropertyKey");
+      assertStrictEquals(
+        toPropertyDescriptorRecord.name,
+        "toPropertyDescriptorRecord",
+      );
+    });
+  });
+
+  describe("~configurable", () => {
+    it("[[DefineOwnProperty]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "configurable", {});
+      assertStrictEquals(desc.configurable, false);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "configurable", { get: undefined })
+      );
+    });
+
+    it("[[Set]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.configurable = undefined;
+      assertStrictEquals(desc.configurable, false);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ configurable: false });
+      delete desc.configurable;
+      assert(!("configurable" in desc));
+    });
+  });
+
+  describe("~enumerable", () => {
+    it("[[DefineOwnProperty]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "enumerable", {});
+      assertStrictEquals(desc.enumerable, false);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "enumerable", { get: undefined })
+      );
+    });
+
+    it("[[Set]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.enumerable = undefined;
+      assertStrictEquals(desc.enumerable, false);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ enumerable: false });
+      delete desc.enumerable;
+      assert(!("enumerable" in desc));
+    });
+  });
+
+  describe("~get", () => {
+    it("[[DefineOwnProperty]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "get", {});
+      assertStrictEquals(desc.get, undefined);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "get", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(
+        () => Object.defineProperty(desc, "get", { value: null }),
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => Object.defineProperty(desc, "get", {}));
+    });
+
+    it("[[Set]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      const fn = () => {};
+      desc.get = fn;
+      assertStrictEquals(desc.get, fn);
+    });
+
+    it("[[Set]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() => desc.get = null);
+    });
+
+    it("[[Set]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => desc.get = undefined);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      delete desc.get;
+      assert(!("get" in desc));
+    });
+  });
+
+  describe("~set", () => {
+    it("[[DefineOwnProperty]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "set", {});
+      assertStrictEquals(desc.set, undefined);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "set", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(
+        () => Object.defineProperty(desc, "set", { value: null }),
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => Object.defineProperty(desc, "set", {}));
+    });
+
+    it("[[Set]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      const fn = (_) => {};
+      desc.set = fn;
+      assertStrictEquals(desc.set, fn);
+    });
+
+    it("[[Set]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() => desc.set = null);
+    });
+
+    it("[[Set]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => desc.set = undefined);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ set: undefined });
+      delete desc.set;
+      assert(!("set" in desc));
+    });
+  });
+
+  describe("~value", () => {
+    it("[[DefineOwnProperty]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "value", {});
+      assertStrictEquals(desc.value, undefined);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "value", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => Object.defineProperty(desc, "value", {}));
+    });
+
+    it("[[Set]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.value = "success";
+      assertStrictEquals(desc.value, "success");
+    });
+
+    it("[[Set]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => desc.value = null);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      delete desc.value;
+      assert(!("value" in desc));
+    });
+  });
+
+  describe("~writable", () => {
+    it("[[DefineOwnProperty]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "writable", {});
+      assertStrictEquals(desc.writable, false);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "writable", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => Object.defineProperty(desc, "writable", {}));
+    });
+
+    it("[[Set]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.writable = undefined;
+      assertStrictEquals(desc.writable, false);
+    });
+
+    it("[[Set]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => desc.writable = false);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ writable: false });
+      delete desc.writable;
+      assert(!("writable" in desc));
     });
   });
 });
This page took 0.072367 seconds and 4 git commands to generate.