X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/8bf43f6e1898ca921d5e63b4513e4c6b2241ebc5..f6b84c13c5c539dbac4f4b9b530eeec95d683475:/object.test.js diff --git a/object.test.js b/object.test.js index d5a60bb..3a0e1a7 100644 --- a/object.test.js +++ b/object.test.js @@ -19,6 +19,8 @@ import { spy, } from "./dev-deps.js"; import { + defineOwnDataProperty, + defineOwnNonenumerableDataProperty, defineOwnProperties, defineOwnProperty, deleteOwnProperty, @@ -34,17 +36,19 @@ import { getPrototype, hasOwnProperty, hasProperty, + isArraylikeObject, + isConcatSpreadableObject, isExtensibleObject, isUnfrozenObject, isUnsealedObject, LazyLoader, + lengthOfArraylike, namedEntries, namedKeys, namedValues, objectCreate, objectFromEntries, preventExtensions, - PropertyDescriptor, seal, setPropertyValue, setPropertyValues, @@ -307,576 +311,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, - ); - }); - - 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 }) + defineOwnNonenumerableDataProperty.name, + "defineOwnNonenumerableDataProperty", ); }); - - 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)); - }); }); }); @@ -884,7 +417,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", () => { @@ -1096,17 +629,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 }))), ); }); @@ -1534,6 +1095,114 @@ describe("hasOwnProperty", () => { }); }); +describe("isArraylikeObject", () => { + it("[[Call]] returns false for primitives", () => { + assertStrictEquals(isArraylikeObject("failure"), false); + }); + + it("[[Call]] returns false if length throws", () => { + assertStrictEquals( + isArraylikeObject({ + get length() { + throw void {}; + }, + }), + false, + ); + }); + + it("[[Call]] returns false if length is not a number and cannot be converted to one", () => { + assertStrictEquals(isArraylikeObject({ length: 1n }), false); + }); + + it("[[Call]] returns true if length is convertable to a number", () => { + assertStrictEquals(isArraylikeObject({ length: -0 }), true); + assertStrictEquals(isArraylikeObject({ length: 1 }), true); + assertStrictEquals(isArraylikeObject({ length: -1.25 }), true); + assertStrictEquals( + isArraylikeObject({ length: 9007199254740992 }), + true, + ); + assertStrictEquals(isArraylikeObject({ length: Infinity }), true); + assertStrictEquals(isArraylikeObject({ length: "success" }), true); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new isArraylikeObject({})); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(isArraylikeObject.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + isArraylikeObject.name, + "isArraylikeObject", + ); + }); + }); +}); + +describe("isConcatSpreadableObject", () => { + it("[[Call]] returns false for primitives", () => { + assertStrictEquals(isConcatSpreadableObject("failure"), false); + }); + + it("[[Call]] returns false if [Symbol.isConcatSpreadable] is null or false", () => { + assertStrictEquals( + isConcatSpreadableObject( + Object.assign([], { [Symbol.isConcatSpreadable]: null }), + ), + false, + ); + assertStrictEquals( + isConcatSpreadableObject( + Object.assign([], { [Symbol.isConcatSpreadable]: false }), + ), + false, + ); + }); + + it("[[Call]] returns true if [Symbol.isConcatSpreadable] is undefined and the object is an array", () => { + assertStrictEquals( + isConcatSpreadableObject( + Object.assign([], { [Symbol.isConcatSpreadable]: undefined }), + ), + true, + ); + }); + + it("[[Call]] returns true if [Symbol.isConcatSpreadable] is true", () => { + assertStrictEquals( + isConcatSpreadableObject({ [Symbol.isConcatSpreadable]: true }), + true, + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new isConcatSpreadableObject({})); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(isConcatSpreadableObject.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + isConcatSpreadableObject.name, + "isConcatSpreadableObject", + ); + }); + }); +}); + describe("isExtensibleObject", () => { it("[[Call]] returns true for extensible objects", () => { assertStrictEquals(isExtensibleObject({}), true); @@ -1648,6 +1317,63 @@ describe("isUnsealedObject", () => { }); }); +describe("lengthOfArraylike", () => { + it("[[Call]] returns the length", () => { + assertStrictEquals( + lengthOfArraylike({ length: 9007199254740991 }), + 9007199254740991, + ); + }); + + it("[[Call]] returns a non·nan result", () => { + assertStrictEquals(lengthOfArraylike({ length: NaN }), 0); + assertStrictEquals(lengthOfArraylike({ length: "failure" }), 0); + }); + + it("[[Call]] returns an integral result", () => { + assertStrictEquals(lengthOfArraylike({ length: 0.25 }), 0); + assertStrictEquals(lengthOfArraylike({ length: 1.1 }), 1); + }); + + it("[[Call]] returns a result greater than or equal to zero", () => { + assertStrictEquals(lengthOfArraylike({ length: -0 }), 0); + assertStrictEquals(lengthOfArraylike({ length: -1 }), 0); + assertStrictEquals(lengthOfArraylike({ length: -Infinity }), 0); + }); + + it("[[Call]] returns a result less than 2 ** 53", () => { + assertStrictEquals( + lengthOfArraylike({ length: 9007199254740992 }), + 9007199254740991, + ); + assertStrictEquals( + lengthOfArraylike({ length: Infinity }), + 9007199254740991, + ); + }); + + it("[[Call]] does not require an object argument", () => { + assertStrictEquals(lengthOfArraylike("string"), 6); + assertStrictEquals(lengthOfArraylike(Symbol()), 0); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new lengthOfArraylike("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(lengthOfArraylike.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(lengthOfArraylike.name, "lengthOfArraylike"); + }); + }); +}); + describe("namedEntries", () => { it("[[Call]] gets named entries", () => { assertEquals(namedEntries({ success: true }), [["success", true]]);