| export function isConstructor(o) { |
| assert_equals(typeof o, "function", "Must be a function according to typeof"); |
| assert_true(isConstructorTest(o), "Must be a constructor according to the meta-object protocol"); |
| assert_throws(new TypeError(), () => o(), "Attempting to call (not construct) must throw"); |
| } |
| |
| export function functionLength(o, expected, label) { |
| const lengthExpected = { writable: false, enumerable: false, configurable: true }; |
| const { value } = propertyDescriptor(o, "length", lengthExpected); |
| |
| assert_equals(value, expected, `${formatLabel(label)}length value`); |
| } |
| |
| export function functionName(o, expected, label) { |
| const lengthExpected = { writable: false, enumerable: false, configurable: true }; |
| const { value } = propertyDescriptor(o, "name", lengthExpected); |
| |
| assert_equals(value, expected, `${formatLabel(label)}name value`); |
| } |
| |
| export function hasClassPrototype(o) { |
| const prototypeExpected = { writable: false, enumerable: false, configurable: false }; |
| const { value } = propertyDescriptor(o, "prototype", prototypeExpected); |
| assert_equals(typeof value, "object", "prototype must be an object"); |
| assert_not_equals(value, null, "prototype must not be null"); |
| } |
| |
| export function hasPrototypeConstructorLink(klass) { |
| const constructorExpected = { writable: true, enumerable: false, configurable: true }; |
| const { value } = propertyDescriptor(klass.prototype, "constructor", constructorExpected); |
| assert_equals(value, klass, "constructor property must match"); |
| } |
| |
| export function propertyKeys(o, expectedNames, expectedSymbols, label) { |
| label = formatLabel(label); |
| assert_array_equals(Object.getOwnPropertyNames(o), expectedNames, `${label}property names`); |
| assert_array_equals(Object.getOwnPropertySymbols(o), expectedSymbols, |
| `${label}property symbols`); |
| } |
| |
| export function methods(o, expectedMethods) { |
| for (const [name, length] of Object.entries(expectedMethods)) { |
| method(o, name, length); |
| } |
| } |
| |
| export function accessors(o, expectedAccessors) { |
| for (const [name, accessorTypes] of Object.entries(expectedAccessors)) { |
| accessor(o, name, accessorTypes); |
| } |
| } |
| |
| function method(o, prop, length) { |
| const methodExpected = { writable: true, enumerable: false, configurable: true }; |
| const { value } = propertyDescriptor(o, prop, methodExpected); |
| |
| assert_equals(typeof value, "function", `${prop} method must be a function according to typeof`); |
| assert_false(isConstructorTest(value), |
| `${prop} method must not be a constructor according to the meta-object protocol`); |
| functionLength(value, length, prop); |
| functionName(value, prop, prop); |
| propertyKeys(value, ["length", "name"], [], prop); |
| } |
| |
| function accessor(o, prop, expectedAccessorTypes) { |
| const accessorExpected = { enumerable: false, configurable: true }; |
| const propDesc = propertyDescriptor(o, prop, accessorExpected); |
| |
| for (const possibleType of ["get", "set"]) { |
| const accessorFunc = propDesc[possibleType]; |
| if (expectedAccessorTypes.includes(possibleType)) { |
| const label = `${prop}'s ${possibleType}ter`; |
| |
| assert_equals(typeof accessorFunc, "function", |
| `${label} must be a function according to typeof`); |
| assert_false(isConstructorTest(accessorFunc), |
| `${label} must not be a constructor according to the meta-object protocol`); |
| |
| functionLength(accessorFunc, possibleType === "get" ? 0 : 1, label); |
| functionName(accessorFunc, `${possibleType} ${prop}`, label); |
| propertyKeys(accessorFunc, ["length", "name"], [], label); |
| } else { |
| assert_equals(accessorFunc, undefined, `${prop} must not have a ${possibleType}ter`); |
| } |
| } |
| } |
| |
| function propertyDescriptor(obj, prop, mustMatch) { |
| const propDesc = Object.getOwnPropertyDescriptor(obj, prop); |
| for (const key in Object.keys(mustMatch)) { |
| assert_equals(propDesc[key], mustMatch[key], `${prop} ${key}`); |
| } |
| return propDesc; |
| } |
| |
| function isConstructorTest(o) { |
| try { |
| new (new Proxy(o, {construct: () => ({})})); |
| return true; |
| } catch (e) { |
| return false; |
| } |
| } |
| |
| function formatLabel(label) { |
| return label !== undefined ? ` ${label}` : ""; |
| } |