export {
assert,
assertEquals,
+ assertNotStrictEquals,
assertStrictEquals,
assertThrows,
-} from "https://deno.land/std@0.188.0/testing/asserts.ts";
+} from "https://deno.land/std@0.208.0/testing/asserts.ts";
export {
describe,
it,
-} from "https://deno.land/std@0.188.0/testing/bdd.ts";
+} from "https://deno.land/std@0.208.0/testing/bdd.ts";
export {
assertSpyCall,
assertSpyCalls,
spy,
-} from "https://deno.land/std@0.188.0/testing/mock.ts";
+} from "https://deno.land/std@0.208.0/testing/mock.ts";
}
}
-/**
- * A property descriptor object.
- *
- * Actually constructing a property descriptor object using this class
- * is only necessary if you need strict guarantees about the types of
- * its properties; the resulting object is proxied to ensure the types
- * match what one would expect from composing FromPropertyDescriptor
- * and ToPropertyDescriptor in the Ecmascript specification.
- *
- * Otherwise, the instance properties and methods are generic.
- */
-export const { PropertyDescriptor } = (() => {
- class PropertyDescriptor extends null {
- /**
- * Constructs a new property descriptor object from the provided
- * object.
- *
- * The resulting object is proxied to enforce types (for example,
- * its `.enumerable` property, if defined, will always be a
- * boolean).
- */
- constructor(O) {
- if (type(O) !== "object") {
- // The provided value is not an object.
- throw new TypeError(
- `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
- );
- } else {
- // The provided value is an object.
- const desc = objectCreate(propertyDescriptorPrototype);
- if ("enumerable" in O) {
- // An enumerable property is specified.
- desc.enumerable = !!O.enumerable;
- } else {
- // An enumerable property is not specified.
- /* do nothing */
- }
- if ("configurable" in O) {
- // A configurable property is specified.
- desc.configurable = !!O.configurable;
- } else {
- // A configurable property is not specified.
- /* do nothing */
- }
- if ("value" in O) {
- // A value property is specified.
- desc.value = O.value;
- } else {
- // A value property is not specified.
- /* do nothing */
- }
- if ("writable" in O) {
- // A writable property is specified.
- desc.writable = !!O.writable;
- } else {
- // A writable property is not specified.
- /* do nothing */
- }
- if ("get" in O) {
- // A get property is specified.
- const getter = O.get;
- if (getter !== undefined && typeof getter !== "function") {
- // The getter is not callable.
- throw new TypeError("Piscēs: Getters must be callable.");
- } else {
- // The getter is callable.
- desc.get = getter;
- }
- } else {
- // A get property is not specified.
- /* do nothing */
- }
- if ("set" in O) {
- // A set property is specified.
- const setter = O.set;
- if (setter !== undefined && typeof setter !== "function") {
- // The setter is not callable.
- throw new TypeError("Piscēs: Setters must be callable.");
- } else {
- // The setter is callable.
- desc.set = setter;
- }
- } else {
- // A set property is not specified.
- /* do nothing */
- }
- if (
- ("get" in desc || "set" in desc) &&
- ("value" in desc || "writable" in desc)
- ) {
- // Both accessor and data attributes have been defined.
- throw new TypeError(
- "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
- );
- } else {
- // The property descriptor is valid.
- return new Proxy(desc, propertyDescriptorProxyHandler);
- }
- }
- }
-
- /**
- * Completes this property descriptor by setting missing values to
- * their defaults.
- *
- * This method modifies this object and returns undefined.
- */
- complete() {
- if (this !== undefined && !("get" in this || "set" in this)) {
- // This is a generic or data descriptor.
- if (!("value" in this)) {
- // `value` is not defined on this.
- this.value = undefined;
- } else {
- // `value` is already defined on this.
- /* do nothing */
- }
- if (!("writable" in this)) {
- // `writable` is not defined on this.
- this.writable = false;
- } else {
- // `writable` is already defined on this.
- /* do nothing */
- }
- } else {
- // This is not a generic or data descriptor.
- if (!("get" in this)) {
- // `get` is not defined on this.
- this.get = undefined;
- } else {
- // `get` is already defined on this.
- /* do nothing */
- }
- if (!("set" in this)) {
- // `set` is not defined on this.
- this.set = undefined;
- } else {
- // `set` is already defined on this.
- /* do nothing */
- }
- }
- if (!("enumerable" in this)) {
- // `enumerable` is not defined on this.
- this.enumerable = false;
- } else {
- // `enumerable` is already defined on this.
- /* do nothing */
- }
- if (!("configurable" in this)) {
- // `configurable` is not defined on this.
- this.configurable = false;
- } else {
- // `configurable` is already defined on this.
- /* do nothing */
- }
- }
-
- /** Gets whether this is an accessor descrtiptor. */
- get isAccessorDescriptor() {
- return this !== undefined && ("get" in this || "set" in this);
- }
-
- /** Gets whether this is a data descrtiptor. */
- get isDataDescriptor() {
- return this !== undefined &&
- ("value" in this || "writable" in this);
- }
-
- /** Gets whether this is a fully‐populated property descriptor. */
- get isFullyPopulated() {
- return this !== undefined &&
- ("value" in this && "writable" in this ||
- "get" in this && "set" in this) &&
- "enumerable" in this && "configurable" in this;
- }
-
- /**
- * Gets whether this is a generic (not accessor or data)
- * descrtiptor.
- */
- get isGenericDescriptor() {
- return this !== undefined &&
- !("get" in this || "set" in this || "value" in this ||
- "writable" in this);
- }
- }
-
- const coercePropertyDescriptorValue = (P, V) => {
- switch (P) {
- case "configurable":
- case "enumerable":
- case "writable":
- return !!V;
- case "value":
- return V;
- case "get":
- if (V !== undefined && typeof V !== "function") {
- throw new TypeError(
- "Piscēs: Getters must be callable.",
- );
- } else {
- return V;
- }
- case "set":
- if (V !== undefined && typeof V !== "function") {
- throw new TypeError(
- "Piscēs: Setters must be callable.",
- );
- } else {
- return V;
- }
- default:
- return V;
- }
- };
-
- const {
- prototype: propertyDescriptorPrototype,
- } = PropertyDescriptor;
-
- const propertyDescriptorProxyHandler = Object.assign(
- Object.create(null),
- {
- defineProperty(O, P, Desc) {
- if (
- P === "configurable" || P === "enumerable" ||
- P === "writable" || P === "value" ||
- P === "get" || P === "set"
- ) {
- // P is a property descriptor attribute.
- const desc = new PropertyDescriptor(Desc);
- if ("get" in desc || "set" in desc) {
- // Desc is an accessor property descriptor.
- throw new TypeError(
- "Piscēs: Property descriptor attributes must be data properties.",
- );
- } else if ("value" in desc || !(P in O)) {
- // Desc has a value or P does not already exist on O.
- desc.value = coercePropertyDescriptorValue(P, desc.value);
- } else {
- // Desc is not an accessor property descriptor and has no
- // value.
- /* do nothing */
- }
- const isAccessorDescriptor = "get" === P || "set" === P ||
- "get" in O || "set" in O;
- const isDataDescriptor = "value" === P || "writable" === P ||
- "value" in O || "writable" in O;
- if (isAccessorDescriptor && isDataDescriptor) {
- // Both accessor and data attributes will be present on O
- // after defining P.
- throw new TypeError(
- "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
- );
- } else {
- // P can be safely defined on O.
- return defineOwnProperty(O, P, desc);
- }
- } else {
- // P is not a property descriptor attribute.
- return defineOwnProperty(O, P, Desc);
- }
- },
- set(O, P, V, Receiver) {
- if (
- P === "configurable" || P === "enumerable" ||
- P === "writable" || P === "value" ||
- P === "get" || P === "set"
- ) {
- // P is a property descriptor attribute.
- const newValue = coercePropertyDescriptorValue(P, V);
- const isAccessorDescriptor = "get" === P || "set" === P ||
- "get" in O || "set" in O;
- const isDataDescriptor = "value" === P || "writable" === P ||
- "value" in O || "writable" in O;
- if (isAccessorDescriptor && isDataDescriptor) {
- // Both accessor and data attributes will be present on O
- // after defining P.
- throw new TypeError(
- "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
- );
- } else {
- // P can be safely defined on O.
- //
- // ☡ Receiver will be the *proxied* object, so passing it
- // through to setPropertyValue here would produce an
- // infinite loop.
- //
- // ☡ This has implications on objects with a proxied
- // PropertyDescriptor in their prototype.
- return setPropertyValue(O, P, newValue, O);
- }
- } else {
- return setPropertyValue(O, P, V, Receiver);
- }
- },
- setPrototypeOf(O, V) {
- if (V !== propertyDescriptorPrototype) {
- // V is not the property descriptor prototype.
- return false;
- } else {
- // V is the property descriptor prototype.
- return setPrototype(O, V);
- }
- },
- },
- );
-
- return { PropertyDescriptor };
-})();
-
/**
* Defines an own property on the provided object on the provided
* property key using the provided property descriptor.
objectCreate,
objectFromEntries,
preventExtensions,
- PropertyDescriptor,
seal,
setPropertyValue,
setPropertyValues,
});
});
-describe("PropertyDescriptor", () => {
- it("[[Call]] throws an error", () => {
- assertThrows(() => PropertyDescriptor({}));
- });
-
- it("[[Construct]] creates a new PropertyDescriptor", () => {
- assertStrictEquals(
- Object.getPrototypeOf(new PropertyDescriptor({})),
- PropertyDescriptor.prototype,
- );
- });
-
- it("[[Construct]] throws for primitives", () => {
- assertThrows(() => new PropertyDescriptor("failure"));
- });
-
- describe(".length", () => {
- it("[[Get]] returns the correct length", () => {
- assertStrictEquals(PropertyDescriptor.length, 1);
- });
- });
-
- describe(".name", () => {
- it("[[Get]] returns the correct name", () => {
- assertStrictEquals(
- PropertyDescriptor.name,
- "PropertyDescriptor",
- );
- });
- });
-
- 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,
- });
- });
-
- it("[[Call]] completes an accessor descriptor", () => {
- const desc = { get: undefined };
- PropertyDescriptor.prototype.complete.call(desc);
- assertEquals(desc, {
- configurable: false,
- 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",
- );
- });
- });
- });
-
- 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",
- );
- });
- });
- });
-
- 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",
- );
- });
- });
- });
-
- 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("::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", () => {
- assertStrictEquals(
- Reflect.get(
- PropertyDescriptor.prototype,
- "isGenericDescriptor",
- { get: undefined },
- ),
- false,
- );
- });
-
- 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));
- });
- });
-});
-
describe("defineOwnProperty", () => {
it("[[Call]] defines the property", () => {
const obj = {};
/** The undefined primitive. */
export const UNDEFINED = undefined;
+/**
+ * Completes the provided property descriptor by setting missing values
+ * to their defaults.
+ *
+ * ※ This method modifies the provided object and returns undefined.
+ */
+export const completePropertyDescriptor = (Desc) => {
+ if (Desc === UNDEFINED) {
+ throw new TypeError(
+ "Piscēs: Cannot complete undefined property descriptor.",
+ );
+ } else if (!("get" in Desc || "set" in Desc)) {
+ // This is a generic or data descriptor.
+ if (!("value" in Desc)) {
+ // `value` is not defined on this.
+ Desc.value = UNDEFINED;
+ } else {
+ // `value` is already defined on this.
+ /* do nothing */
+ }
+ if (!("writable" in Desc)) {
+ // `writable` is not defined on this.
+ Desc.writable = false;
+ } else {
+ // `writable` is already defined on this.
+ /* do nothing */
+ }
+ } else {
+ // This is not a generic or data descriptor.
+ if (!("get" in Desc)) {
+ // `get` is not defined on this.
+ Desc.get = UNDEFINED;
+ } else {
+ // `get` is already defined on this.
+ /* do nothing */
+ }
+ if (!("set" in Desc)) {
+ // `set` is not defined on this.
+ Desc.set = UNDEFINED;
+ } else {
+ // `set` is already defined on this.
+ /* do nothing */
+ }
+ }
+ if (!("enumerable" in Desc)) {
+ // `enumerable` is not defined on this.
+ Desc.enumerable = false;
+ } else {
+ // `enumerable` is already defined on this.
+ /* do nothing */
+ }
+ if (!("configurable" in Desc)) {
+ // `configurable` is not defined on this.
+ Desc.configurable = false;
+ } else {
+ // `configurable` is already defined on this.
+ /* do nothing */
+ }
+};
+
+/** Gets whether the provided value is an accessor descrtiptor. */
+export const isAccessorDescriptor = (Desc) =>
+ Desc !== UNDEFINED && ("get" in Desc || "set" in Desc);
+
+/** Gets whether the provided value is a data descrtiptor. */
+export const isDataDescriptor = (Desc) =>
+ Desc !== UNDEFINED && ("value" in Desc || "writable" in Desc);
+
+/**
+ * Gets whether the provided value is a fully‐populated property
+ * descriptor.
+ */
+export const isFullyPopulatedDescriptor = (Desc) =>
+ Desc !== UNDEFINED &&
+ ("value" in Desc && "writable" in Desc ||
+ "get" in Desc && "set" in Desc) &&
+ "enumerable" in Desc && "configurable" in Desc;
+
+/**
+ * Gets whether the provided value is a generic (not accessor or data)
+ * descrtiptor.
+ */
+export const isGenericDescriptor = (Desc) =>
+ Desc !== UNDEFINED &&
+ !("get" in Desc || "set" in Desc || "value" in Desc ||
+ "writable" in Desc);
+
+export const {
+ /**
+ * Returns whether the provided value is a property descriptor record
+ * as created by `toPropertyDescriptor`.
+ *
+ * ※ This function is provided to enable inspection of whether an
+ * object uses the property descriptor record proxy implementation,
+ * not as a general test of whether an object satisfies the
+ * requirements for property descriptors. In most cases, a more
+ * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
+ * `isGenericDescriptor`, is preferrable.
+ */
+ isPropertyDescriptorRecord,
+
+ /**
+ * Converts the provided value to a property descriptor record.
+ *
+ * ※ The prototype of a property descriptor record is always `null`.
+ *
+ * ※ Actually constructing a property descriptor object using this
+ * class is only necessary if you need strict guarantees about the
+ * types of its properties; the resulting object is proxied to ensure
+ * the types match what one would expect from composing
+ * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
+ * specification.
+ */
+ toPropertyDescriptor,
+} = (() => {
+ const {
+ assign: setPropertyValues,
+ create: objectCreate,
+ defineProperty: defineOwnProperty,
+ } = Object;
+ const {
+ apply: call,
+ defineProperty: reflectDefineProperty,
+ setPrototypeOf: reflectSetPrototypeOf,
+ } = Reflect;
+ const proxyConstructor = Proxy;
+ const { add: weakSetAdd, has: weakSetHas } = WeakSet.prototype;
+ const propertyDescriptorRecords = new WeakSet();
+ const coercePropertyDescriptorValue = (P, V) => {
+ switch (P) {
+ case "configurable":
+ case "enumerable":
+ case "writable":
+ return !!V;
+ case "value":
+ return V;
+ case "get":
+ if (V !== undefined && typeof V !== "function") {
+ throw new TypeError(
+ "Piscēs: Getters must be callable.",
+ );
+ } else {
+ return V;
+ }
+ case "set":
+ if (V !== undefined && typeof V !== "function") {
+ throw new TypeError(
+ "Piscēs: Setters must be callable.",
+ );
+ } else {
+ return V;
+ }
+ default:
+ return V;
+ }
+ };
+ const propertyDescriptorProxyHandler = Object.freeze(
+ Object.assign(
+ Object.create(null),
+ {
+ defineProperty(O, P, Desc) {
+ if (
+ P === "configurable" || P === "enumerable" ||
+ P === "writable" || P === "value" ||
+ P === "get" || P === "set"
+ ) {
+ // P is a property descriptor attribute.
+ const desc = setPropertyValues(objectCreate(null), Desc);
+ if ("get" in desc || "set" in desc) {
+ // Desc is an accessor property descriptor.
+ throw new TypeError(
+ "Piscēs: Property descriptor attributes must be data properties.",
+ );
+ } else if ("value" in desc || !(P in O)) {
+ // Desc has a value or P does not already exist on O.
+ desc.value = coercePropertyDescriptorValue(
+ P,
+ desc.value,
+ );
+ } else {
+ // Desc is not an accessor property descriptor and has no
+ // value, but an existing value is present on O.
+ /* do nothing */
+ }
+ const isAccessorDescriptor = "get" === P || "set" === P ||
+ "get" in O || "set" in O;
+ const isDataDescriptor = "value" === P ||
+ "writable" === P ||
+ "value" in O || "writable" in O;
+ if (isAccessorDescriptor && isDataDescriptor) {
+ // Both accessor and data attributes will be present on O
+ // after defining P.
+ throw new TypeError(
+ "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+ );
+ } else {
+ // P can be safely defined on O.
+ return reflectDefineProperty(O, P, desc);
+ }
+ } else {
+ // P is not a property descriptor attribute.
+ return reflectDefineProperty(O, P, Desc);
+ }
+ },
+ setPrototypeOf(O, V) {
+ if (V !== null) {
+ // V is not the property descriptor prototype.
+ return false;
+ } else {
+ // V is the property descriptor prototype.
+ return reflectSetPrototypeOf(O, V);
+ }
+ },
+ },
+ ),
+ );
+
+ return {
+ isPropertyDescriptorRecord: ($) =>
+ call(weakSetHas, propertyDescriptorRecords, [$]),
+ toPropertyDescriptor: (Obj) => {
+ if (type(Obj) !== "object") {
+ // The provided value is not an object.
+ throw new TypeError(
+ `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
+ );
+ } else {
+ // The provided value is an object.
+ const desc = objectCreate(null);
+ if ("enumerable" in Obj) {
+ // An enumerable property is specified.
+ defineOwnProperty(desc, "enumerable", {
+ configurable: true,
+ enumerable: true,
+ value: !!Obj.enumerable,
+ writable: true,
+ });
+ } else {
+ // An enumerable property is not specified.
+ /* do nothing */
+ }
+ if ("configurable" in Obj) {
+ // A configurable property is specified.
+ defineOwnProperty(desc, "configurable", {
+ configurable: true,
+ enumerable: true,
+ value: !!Obj.configurable,
+ writable: true,
+ });
+ } else {
+ // A configurable property is not specified.
+ /* do nothing */
+ }
+ if ("value" in Obj) {
+ // A value property is specified.
+ defineOwnProperty(desc, "value", {
+ configurable: true,
+ enumerable: true,
+ value: Obj.value,
+ writable: true,
+ });
+ } else {
+ // A value property is not specified.
+ /* do nothing */
+ }
+ if ("writable" in Obj) {
+ // A writable property is specified.
+ defineOwnProperty(desc, "writable", {
+ configurable: true,
+ enumerable: true,
+ value: !!Obj.writable,
+ writable: true,
+ });
+ } else {
+ // A writable property is not specified.
+ /* do nothing */
+ }
+ if ("get" in Obj) {
+ // A get property is specified.
+ const getter = Obj.get;
+ if (getter !== UNDEFINED && typeof getter !== "function") {
+ // The getter is not callable.
+ throw new TypeError("Piscēs: Getters must be callable.");
+ } else {
+ // The getter is callable.
+ defineOwnProperty(desc, "get", {
+ configurable: true,
+ enumerable: true,
+ value: getter,
+ writable: true,
+ });
+ }
+ } else {
+ // A get property is not specified.
+ /* do nothing */
+ }
+ if ("set" in Obj) {
+ // A set property is specified.
+ const setter = Obj.set;
+ if (setter !== UNDEFINED && typeof setter !== "function") {
+ // The setter is not callable.
+ throw new TypeError("Piscēs: Setters must be callable.");
+ } else {
+ // The setter is callable.
+ defineOwnProperty(desc, "set", {
+ configurable: true,
+ enumerable: true,
+ value: setter,
+ writable: true,
+ });
+ }
+ } else {
+ // A set property is not specified.
+ /* do nothing */
+ }
+ if (
+ ("get" in desc || "set" in desc) &&
+ ("value" in desc || "writable" in desc)
+ ) {
+ // Both accessor and data attributes have been defined.
+ throw new TypeError(
+ "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+ );
+ } else {
+ // The property descriptor is valid.
+ const record = new proxyConstructor(
+ desc,
+ propertyDescriptorProxyHandler,
+ );
+ call(weakSetAdd, propertyDescriptorRecords, [record]);
+ return record;
+ }
+ }
+ },
+ };
+})();
+
export const {
/**
* Returns the primitive value of the provided object per its
// file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
import {
+ assert,
+ assertEquals,
+ assertNotStrictEquals,
assertStrictEquals,
assertThrows,
describe,
} from "./dev-deps.js";
import {
ASYNC_ITERATOR,
+ completePropertyDescriptor,
HAS_INSTANCE,
IS_CONCAT_SPREADABLE,
+ isAccessorDescriptor,
+ isDataDescriptor,
+ isFullyPopulatedDescriptor,
+ isGenericDescriptor,
+ isPropertyDescriptorRecord,
ITERATOR,
LN10,
LN2,
toIndex,
toLength,
toPrimitive,
+ toPropertyDescriptor,
type,
UNDEFINED,
UNSCOPABLES,
});
});
-describe("Ε", () => {
- it("[[Get]] is ε", () => {
- assertStrictEquals(Ε, Number.EPSILON);
+describe("completePropertyDescriptor", () => {
+ it("[[Call]] completes a generic descriptor", () => {
+ const desc = {};
+ completePropertyDescriptor(desc);
+ assertEquals(desc, {
+ configurable: false,
+ enumerable: false,
+ value: undefined,
+ writable: false,
+ });
+ });
+
+ it("[[Call]] completes a data descriptor", () => {
+ const desc = { value: undefined };
+ completePropertyDescriptor(desc);
+ assertEquals(desc, {
+ configurable: false,
+ enumerable: false,
+ value: undefined,
+ writable: false,
+ });
+ });
+
+ it("[[Call]] completes an accessor descriptor", () => {
+ const desc = { get: undefined };
+ completePropertyDescriptor(desc);
+ assertEquals(desc, {
+ configurable: false,
+ enumerable: false,
+ get: undefined,
+ set: undefined,
+ });
+ });
+
+ it("[[Call]] throws an error when the descriptor is undefined", () => {
+ assertThrows(() => new completePropertyDescriptor(undefined));
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new completePropertyDescriptor({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ assertStrictEquals(completePropertyDescriptor.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(
+ completePropertyDescriptor.name,
+ "completePropertyDescriptor",
+ );
+ });
});
});
-describe("Π", () => {
- it("[[Get]] is π", () => {
- assertStrictEquals(Π, Math.PI);
+describe("isAccessorDescriptor", () => {
+ it("[[Call]] returns false for a generic descriptor", () => {
+ assertStrictEquals(isAccessorDescriptor({}), false);
+ });
+
+ it("[[Get]] returns false for a data descriptor", () => {
+ assertStrictEquals(
+ isAccessorDescriptor({ value: undefined }),
+ false,
+ );
+ assertStrictEquals(
+ isAccessorDescriptor({ writable: undefined }),
+ false,
+ );
+ });
+
+ it("[[Get]] returns true for an accessor descriptor", () => {
+ assertStrictEquals(isAccessorDescriptor({ get: undefined }), true);
+ assertStrictEquals(isAccessorDescriptor({ set: undefined }), true);
+ });
+
+ it("[[Get]] returns false for undefined", () => {
+ assertStrictEquals(isAccessorDescriptor(undefined), false);
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new isAccessorDescriptor({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ assertStrictEquals(isAccessorDescriptor.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(
+ isAccessorDescriptor.name,
+ "isAccessorDescriptor",
+ );
+ });
});
});
-describe("ℇ", () => {
- it("[[Get]] is ℇ", () => {
- assertStrictEquals(ℇ, Math.E);
+describe("isDataDescriptor", () => {
+ it("[[Call]] returns false for a generic descriptor", () => {
+ assertStrictEquals(isDataDescriptor({}), false);
+ });
+
+ it("[[Get]] returns true for a data descriptor", () => {
+ assertStrictEquals(isDataDescriptor({ value: undefined }), true);
+ assertStrictEquals(isDataDescriptor({ writable: true }), true);
+ });
+
+ it("[[Get]] returns false for an accessor descriptor", () => {
+ assertStrictEquals(isDataDescriptor({ get: undefined }), false);
+ assertStrictEquals(isDataDescriptor({ set: undefined }), false);
+ });
+
+ it("[[Get]] returns false for undefined", () => {
+ assertStrictEquals(isDataDescriptor(undefined), false);
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new isDataDescriptor({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ assertStrictEquals(isDataDescriptor.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(isDataDescriptor.name, "isDataDescriptor");
+ });
+ });
+});
+
+describe("isFullyPopulatedDescriptor", () => {
+ it("[[Call]] returns false for a generic descriptor", () => {
+ assertStrictEquals(isFullyPopulatedDescriptor({}), false);
+ });
+
+ it("[[Get]] returns false for a non‐fully‐populated data descriptor", () => {
+ assertStrictEquals(
+ isFullyPopulatedDescriptor({ value: undefined }),
+ false,
+ );
+ assertStrictEquals(
+ isFullyPopulatedDescriptor({ writable: true }),
+ false,
+ );
+ });
+
+ it("[[Get]] returns true for a fully‐populated data descriptor", () => {
+ assertStrictEquals(
+ isFullyPopulatedDescriptor({
+ configurable: true,
+ enumerable: true,
+ value: undefined,
+ writable: true,
+ }),
+ true,
+ );
+ });
+
+ it("[[Get]] returns false for a non‐fully‐populated accessor descriptor", () => {
+ assertStrictEquals(
+ isFullyPopulatedDescriptor({ get: undefined }),
+ false,
+ );
+ assertStrictEquals(
+ isFullyPopulatedDescriptor({ set: undefined }),
+ false,
+ );
+ });
+
+ it("[[Get]] returns true for a fully‐populated accessor descriptor", () => {
+ assertStrictEquals(
+ isFullyPopulatedDescriptor({
+ configurable: true,
+ enumerable: true,
+ get: undefined,
+ set: undefined,
+ }),
+ true,
+ );
+ });
+
+ it("[[Get]] returns false for undefined", () => {
+ assertStrictEquals(isFullyPopulatedDescriptor(undefined), false);
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new isFullyPopulatedDescriptor({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ assertStrictEquals(isFullyPopulatedDescriptor.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(
+ isFullyPopulatedDescriptor.name,
+ "isFullyPopulatedDescriptor",
+ );
+ });
+ });
+});
+
+describe("isGenericDescriptor", () => {
+ it("[[Call]] returns true for a generic descriptor", () => {
+ assertStrictEquals(isGenericDescriptor({}), true);
+ });
+
+ it("[[Get]] returns false for a data descriptor", () => {
+ assertStrictEquals(
+ isGenericDescriptor({ value: undefined }),
+ false,
+ );
+ assertStrictEquals(isGenericDescriptor({ writable: true }), false);
+ });
+
+ it("[[Get]] returns false for an accessor descriptor", () => {
+ assertStrictEquals(isGenericDescriptor({ get: undefined }), false);
+ assertStrictEquals(isGenericDescriptor({ set: undefined }), false);
+ });
+
+ it("[[Get]] returns false for undefined", () => {
+ assertStrictEquals(isGenericDescriptor(undefined), false);
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new isGenericDescriptor({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ assertStrictEquals(isGenericDescriptor.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(
+ isGenericDescriptor.name,
+ "isGenericDescriptor",
+ );
+ });
+ });
+});
+
+describe("isPropertyDescriptorRecord", () => {
+ it("[[Call]] returns true for objects created by toPropertyDescriptor", () => {
+ assertStrictEquals(
+ isPropertyDescriptorRecord(toPropertyDescriptor({})),
+ 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("toPropertyDescriptor", () => {
+ it("[[Call]] creates a new property descriptor record", () => {
+ const obj = {};
+ const desc = toPropertyDescriptor(obj);
+ assertEquals(obj, desc);
+ assertNotStrictEquals(obj, desc);
+ });
+
+ it("[[Call]] coerces the values", () => {
+ assertEquals(
+ toPropertyDescriptor({
+ configurable: undefined,
+ enumerable: undefined,
+ writable: undefined,
+ }),
+ { configurable: false, enumerable: false, writable: false },
+ );
+ });
+
+ it("[[Construct]] throws for primitives", () => {
+ assertThrows(() => toPropertyDescriptor(undefined));
+ assertThrows(() => toPropertyDescriptor("failure"));
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new toPropertyDescriptor({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ assertStrictEquals(toPropertyDescriptor.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(
+ toPropertyDescriptor.name,
+ "toPropertyDescriptor",
+ );
+ });
+ });
+
+ describe("~configurable", () => {
+ it("[[DefineOwnProperty]] coerces to a boolean", () => {
+ const desc = toPropertyDescriptor({});
+ Object.defineProperty(desc, "configurable", {});
+ assertStrictEquals(desc.configurable, false);
+ });
+
+ it("[[DefineOwnProperty]] throws for accessor properties", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() =>
+ Object.defineProperty(desc, "configurable", { get: undefined })
+ );
+ });
+
+ it("[[Set]] coerces to a boolean", () => {
+ const desc = toPropertyDescriptor({});
+ desc.configurable = undefined;
+ assertStrictEquals(desc.configurable, false);
+ });
+
+ it("[[Delete]] works", () => {
+ const desc = toPropertyDescriptor({ configurable: false });
+ delete desc.configurable;
+ assert(!("configurable" in desc));
+ });
+ });
+
+ describe("~enumerable", () => {
+ it("[[DefineOwnProperty]] coerces to a boolean", () => {
+ const desc = toPropertyDescriptor({});
+ Object.defineProperty(desc, "enumerable", {});
+ assertStrictEquals(desc.enumerable, false);
+ });
+
+ it("[[DefineOwnProperty]] throws for accessor properties", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() =>
+ Object.defineProperty(desc, "enumerable", { get: undefined })
+ );
+ });
+
+ it("[[Set]] coerces to a boolean", () => {
+ const desc = toPropertyDescriptor({});
+ desc.enumerable = undefined;
+ assertStrictEquals(desc.enumerable, false);
+ });
+
+ it("[[Delete]] works", () => {
+ const desc = toPropertyDescriptor({ enumerable: false });
+ delete desc.enumerable;
+ assert(!("enumerable" in desc));
+ });
+ });
+
+ describe("~get", () => {
+ it("[[DefineOwnProperty]] works", () => {
+ const desc = toPropertyDescriptor({});
+ Object.defineProperty(desc, "get", {});
+ assertStrictEquals(desc.get, undefined);
+ });
+
+ it("[[DefineOwnProperty]] throws for accessor properties", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() =>
+ Object.defineProperty(desc, "get", { get: undefined })
+ );
+ });
+
+ it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(
+ () => Object.defineProperty(desc, "get", { value: null }),
+ );
+ });
+
+ it("[[DefineOwnProperty]] throws if a data property is defined", () => {
+ const desc = toPropertyDescriptor({ value: undefined });
+ assertThrows(() => Object.defineProperty(desc, "get", {}));
+ });
+
+ it("[[Set]] works", () => {
+ const desc = toPropertyDescriptor({});
+ const fn = () => {};
+ desc.get = fn;
+ assertStrictEquals(desc.get, fn);
+ });
+
+ it("[[Set]] throws if not callable or undefined", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() => desc.get = null);
+ });
+
+ it("[[Set]] throws if a data property is defined", () => {
+ const desc = toPropertyDescriptor({ value: undefined });
+ assertThrows(() => desc.get = undefined);
+ });
+
+ it("[[Delete]] works", () => {
+ const desc = toPropertyDescriptor({ get: undefined });
+ delete desc.get;
+ assert(!("get" in desc));
+ });
+ });
+
+ describe("~set", () => {
+ it("[[DefineOwnProperty]] works", () => {
+ const desc = toPropertyDescriptor({});
+ Object.defineProperty(desc, "set", {});
+ assertStrictEquals(desc.set, undefined);
+ });
+
+ it("[[DefineOwnProperty]] throws for accessor properties", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() =>
+ Object.defineProperty(desc, "set", { get: undefined })
+ );
+ });
+
+ it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(
+ () => Object.defineProperty(desc, "set", { value: null }),
+ );
+ });
+
+ it("[[DefineOwnProperty]] throws if a data property is defined", () => {
+ const desc = toPropertyDescriptor({ value: undefined });
+ assertThrows(() => Object.defineProperty(desc, "set", {}));
+ });
+
+ it("[[Set]] works", () => {
+ const desc = toPropertyDescriptor({});
+ const fn = (_) => {};
+ desc.set = fn;
+ assertStrictEquals(desc.set, fn);
+ });
+
+ it("[[Set]] throws if not callable or undefined", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() => desc.set = null);
+ });
+
+ it("[[Set]] throws if a data property is defined", () => {
+ const desc = toPropertyDescriptor({ value: undefined });
+ assertThrows(() => desc.set = undefined);
+ });
+
+ it("[[Delete]] works", () => {
+ const desc = toPropertyDescriptor({ set: undefined });
+ delete desc.set;
+ assert(!("set" in desc));
+ });
+ });
+
+ describe("~value", () => {
+ it("[[DefineOwnProperty]] works", () => {
+ const desc = toPropertyDescriptor({});
+ Object.defineProperty(desc, "value", {});
+ assertStrictEquals(desc.value, undefined);
+ });
+
+ it("[[DefineOwnProperty]] throws for accessor properties", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() =>
+ Object.defineProperty(desc, "value", { get: undefined })
+ );
+ });
+
+ it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
+ const desc = toPropertyDescriptor({ get: undefined });
+ assertThrows(() => Object.defineProperty(desc, "value", {}));
+ });
+
+ it("[[Set]] works", () => {
+ const desc = toPropertyDescriptor({});
+ desc.value = "success";
+ assertStrictEquals(desc.value, "success");
+ });
+
+ it("[[Set]] throws if an accessor property is defined", () => {
+ const desc = toPropertyDescriptor({ get: undefined });
+ assertThrows(() => desc.value = null);
+ });
+
+ it("[[Delete]] works", () => {
+ const desc = toPropertyDescriptor({ value: undefined });
+ delete desc.value;
+ assert(!("value" in desc));
+ });
+ });
+
+ describe("~writable", () => {
+ it("[[DefineOwnProperty]] coerces to a boolean", () => {
+ const desc = toPropertyDescriptor({});
+ Object.defineProperty(desc, "writable", {});
+ assertStrictEquals(desc.writable, false);
+ });
+
+ it("[[DefineOwnProperty]] throws for accessor properties", () => {
+ const desc = toPropertyDescriptor({});
+ assertThrows(() =>
+ Object.defineProperty(desc, "writable", { get: undefined })
+ );
+ });
+
+ it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
+ const desc = toPropertyDescriptor({ get: undefined });
+ assertThrows(() => Object.defineProperty(desc, "writable", {}));
+ });
+
+ it("[[Set]] coerces to a boolean", () => {
+ const desc = toPropertyDescriptor({});
+ desc.writable = undefined;
+ assertStrictEquals(desc.writable, false);
+ });
+
+ it("[[Set]] throws if an accessor property is defined", () => {
+ const desc = toPropertyDescriptor({ get: undefined });
+ assertThrows(() => desc.writable = false);
+ });
+
+ it("[[Delete]] works", () => {
+ const desc = toPropertyDescriptor({ writable: false });
+ delete desc.writable;
+ assert(!("writable" in desc));
+ });
+ });
+});
+
describe("type", () => {
it('[[Call]] returns "null" for null', () => {
assertStrictEquals(type(null), "null");
});
});
});
+
+describe("Ε", () => {
+ it("[[Get]] is ε", () => {
+ assertStrictEquals(Ε, Number.EPSILON);
+ });
+});
+
+describe("Π", () => {
+ it("[[Get]] is π", () => {
+ assertStrictEquals(Π, Math.PI);
+ });
+});
+
+describe("ℇ", () => {
+ it("[[Get]] is ℇ", () => {
+ assertStrictEquals(ℇ, Math.E);
+ });
+});