From 449a1cf3a28f58d761fddcd0f1c7fff0c7591c79 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Fri, 3 Jul 2020 22:43:08 -0700 Subject: [PATCH] LibJS: Convert Proxy tests --- .../builtins/Proxy/Proxy.handler-apply.js | 72 +++-- .../builtins/Proxy/Proxy.handler-construct.js | 116 ++++---- .../Proxy/Proxy.handler-defineProperty.js | 187 +++++++------ .../Proxy/Proxy.handler-deleteProperty.js | 96 +++---- .../Tests/builtins/Proxy/Proxy.handler-get.js | 131 ++++----- .../Proxy.handler-getOwnPropertyDescriptor.js | 262 +++++++++--------- .../Proxy/Proxy.handler-getPrototypeOf.js | 154 +++++----- .../Tests/builtins/Proxy/Proxy.handler-has.js | 112 ++++---- .../Proxy/Proxy.handler-isExtensible.js | 71 ++--- .../Proxy/Proxy.handler-preventExtensions.js | 92 +++--- .../Tests/builtins/Proxy/Proxy.handler-set.js | 117 ++++---- .../Proxy/Proxy.handler-setPrototypeOf.js | 163 ++++++----- Libraries/LibJS/Tests/builtins/Proxy/Proxy.js | 49 ++-- Userland/test-js.cpp | 13 + 14 files changed, 820 insertions(+), 815 deletions(-) diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-apply.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-apply.js index 669994cdd5..a22d9729b4 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-apply.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-apply.js @@ -1,39 +1,37 @@ -load("test-common.js"); - -try { - let p = new Proxy(() => 5, { apply: null }); - assert(p() === 5); - let p = new Proxy(() => 5, { apply: undefined }); - assert(p() === 5); - let p = new Proxy(() => 5, {}); - assert(p() === 5); - - const f = (a, b) => a + b; - const handler = { - apply(target, this_, arguments) { - assert(target === f); - assert(this_ === handler); - if (arguments[2]) - return arguments[0] * arguments[1]; - return f(...arguments); - }, - }; - p = new Proxy(f, handler); - - assert(p(2, 4) === 6); - assert(p(2, 4, true) === 8); - - // Invariants - [{}, [], new Proxy({}, {})].forEach(item => { - assertThrowsError(() => { - new Proxy(item, {})(); - }, { - error: TypeError, - message: "[object ProxyObject] is not a function", - }); +describe("[[Call]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + let p = new Proxy(() => 5, { apply: null }); + expect(p()).toBe(5); + p = new Proxy(() => 5, { apply: undefined }); + expect(p()).toBe(5); + p = new Proxy(() => 5, {}); + expect(p()).toBe(5); }); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + test("correct arguments supplied to trap", () => { + const f = (a, b) => a + b; + const handler = { + apply(target, this_, arguments) { + expect(target).toBe(f); + expect(this_).toBe(handler); + if (arguments[2]) + return arguments[0] * arguments[1]; + return f(...arguments); + }, + }; + p = new Proxy(f, handler); + + expect(p(2, 4)).toBe(6); + expect(p(2, 4, true)).toBe(8); + }); +}); + +describe("[[Call]] invariants", () => { + test("target must have a [[Call]] slot", () => { + [{}, [], new Proxy({}, {})].forEach(item => { + expect(() => { + new Proxy(item, {})(); + }).toThrowWithMessage(TypeError, "[object ProxyObject] is not a function"); + }); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-construct.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-construct.js index c5f7bbf5b5..b5ff0beda7 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-construct.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-construct.js @@ -1,58 +1,62 @@ -load("test-common.js"); - -try { - let p = new Proxy(function() { this.x = 5; }, { construct: null }); - assert((new p).x === 5); - let p = new Proxy(function() { this.x = 5; }, { construct: undefined }); - assert((new p).x === 5); - let p = new Proxy(function() { this.x = 5; }, {}); - assert((new p).x === 5); - - function f(value) { - this.x = value; - } - - let p; - const handler = { - construct(target, arguments, newTarget) { - assert(target === f); - assert(newTarget === p); - if (arguments[1]) - return Reflect.construct(target, [arguments[0] * 2], newTarget); - return Reflect.construct(target, arguments, newTarget); - }, - }; - p = new Proxy(f, handler); - - assert(new p(15).x === 15); - assert(new p(15, true).x === 30); - - let p; - function theNewTarget() {}; - const handler = { - construct(target, arguments, newTarget) { - assert(target === f); - assert(newTarget === theNewTarget); - if (arguments[1]) - return Reflect.construct(target, [arguments[0] * 2], newTarget); - return Reflect.construct(target, arguments, newTarget); - }, - }; - p = new Proxy(f, handler); - - Reflect.construct(p, [15], theNewTarget); - - // Invariants - [{}, [], new Proxy({}, {})].forEach(item => { - assertThrowsError(() => { - new (new Proxy(item, {})); - }, { - error: TypeError, - message: "[object ProxyObject] is not a constructor", - }); +describe("[[Construct]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + let p = new Proxy(function() { this.x = 5; }, { construct: null }); + expect((new p).x).toBe(5); + p = new Proxy(function() { this.x = 5; }, { construct: undefined }); + expect((new p).x).toBe(5); + p = new Proxy(function() { this.x = 5; }, {}); + expect((new p).x).toBe(5); }); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + test("trapping 'new'", () => { + function f(value) { + this.x = value; + } + + let p; + const handler = { + construct(target, arguments, newTarget) { + expect(target).toBe(f); + expect(newTarget).toBe(p); + if (arguments[1]) + return Reflect.construct(target, [arguments[0] * 2], newTarget); + return Reflect.construct(target, arguments, newTarget); + }, + }; + p = new Proxy(f, handler); + + expect(new p(15).x).toBe(15); + expect(new p(15, true).x).toBe(30); + }); + + test("trapping Reflect.construct", () => { + function f(value) { + this.x = value; + } + + let p; + function theNewTarget() {}; + const handler = { + construct(target, arguments, newTarget) { + expect(target).toBe(f); + expect(newTarget).toBe(theNewTarget); + if (arguments[1]) + return Reflect.construct(target, [arguments[0] * 2], newTarget); + return Reflect.construct(target, arguments, newTarget); + }, + }; + p = new Proxy(f, handler); + + Reflect.construct(p, [15], theNewTarget); + }); +}); + +describe("[[Construct]] invariants", () => { + test("target must have a [[Construct]] slot", () => { + [{}, [], new Proxy({}, {})].forEach(item => { + expect(() => { + new (new Proxy(item, {})); + }).toThrowWithMessage(TypeError, "[object ProxyObject] is not a constructor"); + }); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-defineProperty.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-defineProperty.js index f40accc28a..f7312f06eb 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-defineProperty.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-defineProperty.js @@ -1,107 +1,112 @@ -load("test-common.js"); - -try { - let p = new Proxy({}, { defineProperty: null }); - assert(Object.defineProperty(p, "foo", {}) === p); - p = new Proxy({}, { defineProperty: undefined }); - assert(Object.defineProperty(p, "foo", {}) === p); - p = new Proxy({}, {}); - assert(Object.defineProperty(p, "foo", {}) == p); - - let o = {}; - p = new Proxy(o, { - defineProperty(target, name, descriptor) { - assert(target === o); - assert(name === "foo"); - assert(descriptor.configurable === true); - assert(descriptor.enumerable === undefined); - assert(descriptor.writable === true); - assert(descriptor.value === 10); - assert(descriptor.get === undefined); - assert(descriptor.set === undefined); - return true; - }, +describe("[[DefineProperty]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + let p = new Proxy({}, { defineProperty: null }); + expect(Object.defineProperty(p, "foo", {})).toBe(p); + p = new Proxy({}, { defineProperty: undefined }); + expect(Object.defineProperty(p, "foo", {})).toBe(p); + p = new Proxy({}, {}); + expect(Object.defineProperty(p, "foo", {})).toBe(p); }); - Object.defineProperty(p, "foo", { configurable: true, writable: true, value: 10 }); + test("correct arguments passed to trap", () => { + let o = {}; + p = new Proxy(o, { + defineProperty(target, name, descriptor) { + expect(target).toBe(o); + expect(name).toBe("foo"); + expect(descriptor.configurable).toBe(true); + expect(descriptor.enumerable).toBeUndefined(); + expect(descriptor.writable).toBe(true); + expect(descriptor.value).toBe(10); + expect(descriptor.get).toBeUndefined(); + expect(descriptor.set).toBeUndefined(); + return true; + }, + }); - p = new Proxy(o, { - defineProperty(target, name, descriptor) { - if (target[name] === undefined) - Object.defineProperty(target, name, descriptor); - return true; - }, + Object.defineProperty(p, "foo", { configurable: true, writable: true, value: 10 }); }); - Object.defineProperty(p, "foo", { value: 10, enumerable: true, configurable: false, writable: true }); - let d = Object.getOwnPropertyDescriptor(p, "foo"); - assert(d.enumerable === true); - assert(d.configurable === false); - assert(d.writable === true); - assert(d.value === 10); - assert(d.get === undefined); - assert(d.set === undefined); + test("optionally ignoring the define call", () => { + let o = {}; + let p = new Proxy(o, { + defineProperty(target, name, descriptor) { + if (target[name] === undefined) + Object.defineProperty(target, name, descriptor); + return true; + }, + }); - Object.defineProperty(p, "foo", { value: 20, enumerable: true, configurable: false, writable: true }); - d = Object.getOwnPropertyDescriptor(p, "foo"); - assert(d.enumerable === true); - assert(d.configurable === false); - assert(d.writable === true); - assert(d.value === 10); - assert(d.get === undefined); - assert(d.set === undefined); + Object.defineProperty(p, "foo", { value: 10, enumerable: true, configurable: false, writable: true }); + let d = Object.getOwnPropertyDescriptor(p, "foo"); + expect(d.enumerable).toBe(true); + expect(d.configurable).toBe(false); + expect(d.writable).toBe(true); + expect(d.value).toBe(10); + expect(d.get).toBeUndefined(); + expect(d.set).toBeUndefined(); + Object.defineProperty(p, "foo", { value: 20, enumerable: true, configurable: false, writable: true }); + d = Object.getOwnPropertyDescriptor(p, "foo"); + expect(d.enumerable).toBe(true); + expect(d.configurable).toBe(false); + expect(d.writable).toBe(true); + expect(d.value).toBe(10); + expect(d.get).toBeUndefined(); + expect(d.set).toBeUndefined(); + }); +}); - // Invariants +describe("[[DefineProperty]] invariants", () => { + test("trap can't return false", () => { + let p = new Proxy({}, { + defineProperty() { return false; } + }); - p = new Proxy({}, { - defineProperty() { return false; } + expect(() => { + Object.defineProperty(p, "foo", {}); + }).toThrowWithMessage(TypeError, "Object's [[DefineProperty]] method returned false"); }); - assertThrowsError(() => { - Object.defineProperty(p, "foo", {}); - }, { - error: TypeError, - message: "Object's [[DefineProperty]] method returned false", + test("trap cannot return true for a non-extensible target if the property does not exist", () => { + let o = {}; + Object.preventExtensions(o); + let p = new Proxy(o, { + defineProperty() { + return true; + } + }); + + expect(() => { + Object.defineProperty(p, "foo", {}); + }).toThrowWithMessage(TypeError, "Proxy handler's defineProperty trap violates invariant: a property cannot be reported as being defined if the property does not exist on the target and the target is non-extensible"); }); - o = {}; - Object.preventExtensions(o); - p = new Proxy(o, { - defineProperty() { - return true; - } - }); - assertThrowsError(() => { - Object.defineProperty(p, "foo", {}); - }, { - error: TypeError, - message: "Proxy handler's defineProperty trap violates invariant: a property cannot be reported as being defined if the property does not exist on the target and the target is non-extensible", + test("trap cannot return true for a non-configurable property if it doesn't already exist on the target", () => { + let o = {}; + Object.defineProperty(o, "foo", { value: 10, configurable: true }); + let p = new Proxy(o, { + defineProperty() { + return true; + }, + }); + + expect(() => { + Object.defineProperty(p, "bar", { value: 6, configurable: false }); + }).toThrowWithMessage(TypeError, "Proxy handler's defineProperty trap violates invariant: a property cannot be defined as non-configurable if it does not already exist on the target object"); }); - o = {}; - Object.defineProperty(o, "foo", { value: 10, configurable: true }); - p = new Proxy(o, { - defineProperty() { - return true; - }, - }); + test("trap cannot return true for a non-configurable property if it already exists as a configurable property", () => { + let o = {}; + Object.defineProperty(o, "foo", { value: 10, configurable: true }); + let p = new Proxy(o, { + defineProperty() { + return true; + }, + }); - assertThrowsError(() => { - Object.defineProperty(p, "bar", { value: 6, configurable: false }); - }, { - error: TypeError, - message: "Proxy handler's defineProperty trap violates invariant: a property cannot be defined as non-configurable if it does not already exist on the target object", - }); - - assertThrowsError(() => { - Object.defineProperty(p, "foo", { value: 6, configurable: false }); - }, { - error: TypeError, - message: "Proxy handler's defineProperty trap violates invariant: a property cannot be defined as non-configurable if it already exists on the target object as a configurable property", - }); - - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + expect(() => { + Object.defineProperty(p, "foo", { value: 6, configurable: false }); + }).toThrowWithMessage(TypeError, "Proxy handler's defineProperty trap violates invariant: a property cannot be defined as non-configurable if it already exists on the target object as a configurable property"); + }) +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-deleteProperty.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-deleteProperty.js index 45cd4ea910..1bd359624a 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-deleteProperty.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-deleteProperty.js @@ -1,56 +1,56 @@ -load("test-common.js"); - -try { - assert(delete (new Proxy({}, { deleteProperty: undefined })).foo === true); - assert(delete (new Proxy({}, { deleteProperty: null })).foo === true); - assert(delete (new Proxy({}, {})).foo === true); - - let o = {}; - let p = new Proxy(o, { - deleteProperty(target, property) { - assert(target === o); - assert(property === "foo"); - return true; - } +describe("[[Delete]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + expect(delete (new Proxy({}, { deleteProperty: undefined })).foo).toBe(true); + expect(delete (new Proxy({}, { deleteProperty: null })).foo).toBe(true); + expect(delete (new Proxy({}, {})).foo).toBe(true); }); - delete p.foo; - - o = { foo: 1, bar: 2 }; - p = new Proxy(o, { - deleteProperty(target, property) { - if (property === "foo") { - delete target[property]; + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + deleteProperty(target, property) { + expect(target).toBe(o); + expect(property).toBe("foo"); return true; } - return false; - } - }); + }); - assert(delete p.foo === true); - assert(delete p.bar === false); - - assert(o.foo === undefined); - assert(o.bar === 2); - - // Invariants - - o = {}; - Object.defineProperty(o, "foo", { configurable: false }); - p = new Proxy(o, { - deleteProperty() { - return true; - }, - }); - - assertThrowsError(() => { delete p.foo; - }, { - error: TypeError, - message: "Proxy handler's deleteProperty trap violates invariant: cannot report a non-configurable own property of the target as deleted", }); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + test("conditional deletion", () => { + o = { foo: 1, bar: 2 }; + p = new Proxy(o, { + deleteProperty(target, property) { + if (property === "foo") { + delete target[property]; + return true; + } + return false; + } + }); + + expect(delete p.foo).toBe(true); + expect(delete p.bar).toBe(false); + + expect(o.foo).toBe(undefined); + expect(o.bar).toBe(2); + }); +}); + + +describe("[[Delete]] invariants", () => { + test("cannot report a non-configurable own property as deleted", () => { + let o = {}; + Object.defineProperty(o, "foo", { configurable: false }); + let p = new Proxy(o, { + deleteProperty() { + return true; + }, + }); + + expect(() => { + delete p.foo; + }).toThrowWithMessage(TypeError, "Proxy handler's deleteProperty trap violates invariant: cannot report a non-configurable own property of the target as deleted"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js index 820b7f57f4..b2d6e94ec1 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js @@ -1,71 +1,76 @@ -load("test-common.js"); - -try { - assert((new Proxy({}, { get: undefined })).foo === undefined); - assert((new Proxy({}, { get: null })).foo === undefined); - assert((new Proxy({}, {})).foo === undefined); - - let o = {}; - let p = new Proxy(o, { - get(target, property, receiver) { - assert(target === o); - assert(property === "foo"); - assert(receiver === p); - }, +describe("[[Get]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + expect((new Proxy({}, { get: undefined })).foo).toBeUndefined(); + expect((new Proxy({}, { get: null })).foo).toBeUndefined(); + expect((new Proxy({}, {})).foo).toBeUndefined(); }); - p.foo; + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + get(target, property, receiver) { + expect(target).toBe(o); + expect(property).toBe("foo"); + expect(receiver).toBe(p); + }, + }); - o = { foo: 1 }; - p = new Proxy(o, { - get(target, property, receiver) { - if (property === "bar") { - return 2; - } else if (property === "baz") { - return receiver.qux; - } else if (property === "qux") { - return 3; + p.foo; + }); + + test("conditional return", () => { + let o = { foo: 1 }; + let p = new Proxy(o, { + get(target, property, receiver) { + if (property === "bar") { + return 2; + } else if (property === "baz") { + return receiver.qux; + } else if (property === "qux") { + return 3; + } + return target[property]; } - return target[property]; - } + }); + + expect(p.foo).toBe(1); + expect(p.bar).toBe(2); + expect(p.baz).toBe(3); + expect(p.qux).toBe(3); + expect(p.test).toBeUndefined(); + }); +}); + +describe("[[Get]] invariants", () => { + test("returned value must match the target property value if the property is non-configurable and non-writable", () => { + let o = {}; + Object.defineProperty(o, "foo", { value: 5, configurable: false, writable: true }); + Object.defineProperty(o, "bar", { value: 10, configurable: false, writable: false }); + + let p = new Proxy(o, { + get() { + return 8; + }, + }); + + expect(p.foo).toBe(8); + expect(() => { + p.bar; + }).toThrowWithMessage(TypeError, "Proxy handler's get trap violates invariant: the returned value must match the value on the target if the property exists on the target as a non-writable, non-configurable own data property"); }); - assert(p.foo === 1); - assert(p.bar === 2); - assert(p.baz === 3); - assert(p.qux === 3); - assert(p.test === undefined); + test("returned value must be undefined if the property is a non-configurable accessor with no getter", () => { + let o = {}; + Object.defineProperty(o, "foo", { configurable: false, set(_) {} }); - // Invariants + let p = new Proxy(o, { + get() { + return 8; + }, + }); - o = {}; - Object.defineProperty(o, "foo", { value: 5, configurable: false, writable: true }); - Object.defineProperty(o, "bar", { value: 10, configurable: false, writable: false }); - - p = new Proxy(o, { - get() { - return 8; - }, - }); - - assert(p.foo === 8); - - assertThrowsError(() => { - p.bar; - }, { - error: TypeError, - message: "Proxy handler's get trap violates invariant: the returned value must match the value on the target if the property exists on the target as a non-writable, non-configurable own data property", - }); - - Object.defineProperty(o, "baz", { configurable: false, set(_) {} }); - assertThrowsError(() => { - p.baz; - }, { - error: TypeError, - message: "Proxy handler's get trap violates invariant: the returned value must be undefined if the property exists on the target as a non-configurable accessor property with an undefined get attribute", - }); - - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + expect(() => { + p.foo; + }).toThrowWithMessage(TypeError, "Proxy handler's get trap violates invariant: the returned value must be undefined if the property exists on the target as a non-configurable accessor property with an undefined get attribute"); + }) +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getOwnPropertyDescriptor.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getOwnPropertyDescriptor.js index cafe566ae5..f077ec43c1 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getOwnPropertyDescriptor.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getOwnPropertyDescriptor.js @@ -1,159 +1,145 @@ -load("test-common.js"); - -try { - assert(Object.getOwnPropertyDescriptor(new Proxy({}, { getOwnPropertyDescriptor: null }), "a") === undefined); - assert(Object.getOwnPropertyDescriptor(new Proxy({}, { getOwnPropertyDescriptor: undefined }), "a") === undefined); - assert(Object.getOwnPropertyDescriptor(new Proxy({}, {}), "a") === undefined); - - let o = {}; - let p = new Proxy(o, { - getOwnPropertyDescriptor(target, property) { - assert(target === o); - assert(property === "foo"); - } +describe("[Call][GetOwnProperty]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + expect(Object.getOwnPropertyDescriptor(new Proxy({}, { getOwnPropertyDescriptor: null }), "a")).toBeUndefined(); + expect(Object.getOwnPropertyDescriptor(new Proxy({}, { getOwnPropertyDescriptor: undefined }), "a")).toBeUndefined(); + expect(Object.getOwnPropertyDescriptor(new Proxy({}, {}), "a")).toBeUndefined(); }); - Object.getOwnPropertyDescriptor(p, "foo"); + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + getOwnPropertyDescriptor(target, property) { + expect(target).toBe(o); + expect(property).toBe("foo"); + } + }); - o = { foo: "bar" }; - Object.defineProperty(o, "baz", { value: "qux", enumerable: false, configurable: true, writable: false }); - p = new Proxy(o, { - getOwnPropertyDescriptor(target, property) { - if (property === "baz") - return Object.getOwnPropertyDescriptor(target, "baz"); - return { value: target[property], enumerable: false, configurable: true, writable: true }; - } - }); - - let d = Object.getOwnPropertyDescriptor(p, "baz"); - assert(d.configurable === true); - assert(d.enumerable === false); - assert(d.writable === false); - assert(d.value === "qux"); - assert(d.get === undefined); - assert(d.set === undefined); - - d = Object.getOwnPropertyDescriptor(p, "foo"); - assert(d.configurable === true); - assert(d.enumerable === false); - assert(d.writable === true); - assert(d.value === "bar"); - assert(d.get === undefined); - assert(d.set === undefined); - - // Invariants - - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy({}, { - getOwnPropertyDescriptor: 1 - })); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap wasn't undefined, null, or callable", - }); - - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy({}, { - getOwnPropertyDescriptor() { - return 1; - }, - })); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: must return an object or undefined", - }); - - o = {}; - Object.defineProperty(o, "foo", { value: 10, configurable: false }); - p = new Proxy(o, { - getOwnPropertyDescriptor() { - return undefined; - }, - }); - - assert(Object.getOwnPropertyDescriptor(p, "bar") === undefined); - - assertThrowsError(() => { Object.getOwnPropertyDescriptor(p, "foo"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot return undefined for a property on the target which is a non-configurable property", }); - Object.defineProperty(o, "baz", { value: 20, configurable: true, writable: true, enumerable: true }); - Object.preventExtensions(o); + test("conditional returned descriptor", () => { + let o = { foo: "bar" }; + Object.defineProperty(o, "baz", { value: "qux", enumerable: false, configurable: true, writable: false }); - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(p, "baz"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report a property as being undefined if it exists as an own property of the target and the target is non-extensible", + let p = new Proxy(o, { + getOwnPropertyDescriptor(target, property) { + if (property === "baz") + return Object.getOwnPropertyDescriptor(target, "baz"); + return { value: target[property], enumerable: false, configurable: true, writable: true }; + } + }); + + let d = Object.getOwnPropertyDescriptor(p, "baz"); + expect(d.configurable).toBe(true); + expect(d.enumerable).toBe(false); + expect(d.writable).toBe(false); + expect(d.value).toBe("qux"); + expect(d.get).toBeUndefined(); + expect(d.set).toBeUndefined(); + + d = Object.getOwnPropertyDescriptor(p, "foo"); + expect(d.configurable).toBe(true); + expect(d.enumerable).toBe(false); + expect(d.writable).toBe(true); + expect(d.value).toBe("bar"); + expect(d.get).toBeUndefined(); + expect(d.set).toBeUndefined(); + }); +}); + +describe("[[GetOwnProperty]] invariants", () => { + test("must return an object or undefined", () => { + expect(() => { + Object.getOwnPropertyDescriptor(new Proxy({}, { + getOwnPropertyDescriptor() { + return 1; + }, + })); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: must return an object or undefined"); }); - o = {}; - Object.defineProperty(o, "v1", { value: 10, configurable: false }); - Object.defineProperty(o, "v2", { value: 10, configurable: false, enumerable: true }); - Object.defineProperty(o, "v3", { configurable: false, get() { return 1; } }); - Object.defineProperty(o, "v4", { value: 10, configurable: false, writable: false, enumerable: true }); + test("cannot return undefined for a non-configurable property", () => { + let o = {}; + Object.defineProperty(o, "foo", { value: 10, configurable: false }); - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy(o, { + let p = new Proxy(o, { getOwnPropertyDescriptor() { - return { configurable: true }; + return undefined; }, - }), "v1"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target", + }); + + expect(Object.getOwnPropertyDescriptor(p, "bar")).toBeUndefined(); + + expect(() => { + Object.getOwnPropertyDescriptor(p, "foo"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot return undefined for a property on the target which is a non-configurable property"); }); - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy(o, { + test("cannot return undefined for an existing property if the target is non-extensible", () => { + let o = {}; + Object.defineProperty(o, "baz", { value: 20, configurable: true, writable: true, enumerable: true }); + Object.preventExtensions(o); + + let p = new Proxy(o, { getOwnPropertyDescriptor() { - return { enumerable: false }; + return undefined; }, - }), "v2"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target", + }); + + expect(() => { + Object.getOwnPropertyDescriptor(p, "baz"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report a property as being undefined if it exists as an own property of the target and the target is non-extensible"); }); - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy(o, { - getOwnPropertyDescriptor() { - return { value: 10 }; - }, - }), "v3"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target", + test("invalid property descriptors", () => { + let o = {}; + Object.defineProperty(o, "v1", { value: 10, configurable: false }); + Object.defineProperty(o, "v2", { value: 10, configurable: false, enumerable: true }); + Object.defineProperty(o, "v3", { configurable: false, get() { return 1; } }); + Object.defineProperty(o, "v4", { value: 10, configurable: false, writable: false, enumerable: true }); + + expect(() => { + Object.getOwnPropertyDescriptor(new Proxy(o, { + getOwnPropertyDescriptor() { + return { configurable: true }; + }, + }), "v1"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target"); + + expect(() => { + Object.getOwnPropertyDescriptor(new Proxy(o, { + getOwnPropertyDescriptor() { + return { enumerable: false }; + }, + }), "v2"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target"); + + expect(() => { + Object.getOwnPropertyDescriptor(new Proxy(o, { + getOwnPropertyDescriptor() { + return { value: 10 }; + }, + }), "v3"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target"); + + expect(() => { + Object.getOwnPropertyDescriptor(new Proxy(o, { + getOwnPropertyDescriptor() { + return { value: 10, writable: true }; + }, + }), "v4"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target"); }); - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy(o, { - getOwnPropertyDescriptor() { - return { value: 10, writable: true }; - }, - }), "v4"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target", + test("cannot report a property as non-configurable if it does not exist or is non-configurable", () => { + let o = {}; + Object.defineProperty(o, "v", { configurable: true }); + expect(() => { + Object.getOwnPropertyDescriptor(new Proxy(o, { + getOwnPropertyDescriptor() { + return { configurable: false }; + }, + }), "v"); + }).toThrowWithMessage(TypeError, "Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report target's property as non-configurable if the property does not exist, or if it is configurable"); }); - - o = {}; - Object.defineProperty(o, "v", { configurable: true }); - assertThrowsError(() => { - Object.getOwnPropertyDescriptor(new Proxy(o, { - getOwnPropertyDescriptor() { - return { configurable: false }; - }, - }), "v"); - }, { - error: TypeError, - message: "Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report target's property as non-configurable if the property does not exist, or if it is configurable", - }); - - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getPrototypeOf.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getPrototypeOf.js index 14ef2362cd..7690a2beb6 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getPrototypeOf.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-getPrototypeOf.js @@ -1,87 +1,81 @@ -load("test-common.js"); +describe("[[GetPrototypeOf]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + let proto = {}; + let o = {}; + Object.setPrototypeOf(o, proto); -try { - const child = {}; - const childProto = { foo: "bar" }; - - Object.setPrototypeOf(child, childProto); - assert(child.foo === "bar"); - - Object.getPrototypeOf(new Proxy(child, { getPrototypeOf: null })); - Object.getPrototypeOf(new Proxy(child, { getPrototypeOf: undefined })); - - let o = {}; - let p = new Proxy(o, { - getPrototypeOf(target) { - assert(target === o); - return null; - } + let p = new Proxy(o, { prototype: null }); + expect(Object.getPrototypeOf(p)).toBe(proto); + p = new Proxy(o, { apply: undefined }); + expect(Object.getPrototypeOf(p)).toBe(proto); + p = new Proxy(o, {}); + expect(Object.getPrototypeOf(p)).toBe(proto); }); - Object.getPrototypeOf(p); + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + getPrototypeOf(target) { + expect(target).toBe(o); + return null; + } + }); - p = new Proxy(o, { - getPrototypeOf(target) { - if (target.foo) - return { bar: 1 }; - return { bar: 2 }; - }, - }); - - assert(Object.getPrototypeOf(p).bar === 2); - o.foo = 20 - assert(Object.getPrototypeOf(p).bar === 1); - - // Invariants - - assertThrowsError(() => { - Object.getPrototypeOf(new Proxy(child, { getPrototypeOf: 1 })); - }, { - error: TypeError, - message: "Proxy handler's getPrototypeOf trap wasn't undefined, null, or callable", - }); - - assertThrowsError(() => { - Object.getPrototypeOf(new Proxy(child, { getPrototypeOf() { return 1; } })); - }, { - error: TypeError, - message: "Proxy handler's getPrototypeOf trap violates invariant: must return an object or null", - }); - - p = new Proxy(child, { - getPrototypeOf(target) { - assert(target === child); - return { baz: "qux" }; - }, - }); - - assert(Object.getPrototypeOf(p).baz === "qux"); - - Object.preventExtensions(child); - p = new Proxy(child, { - getPrototypeOf(target) { - assert(target === child); - return childProto; - } - }); - - assert(Object.getPrototypeOf(p).foo === "bar"); - - p = new Proxy(child, { - getPrototypeOf(target) { - assert(target === child); - return { baz: "qux" }; - } - }); - - assertThrowsError(() => { Object.getPrototypeOf(p); - }, { - error: TypeError, - message: "Proxy handler's getPrototypeOf trap violates invariant: cannot return a different prototype object for a non-extensible target" }); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + test("conditional return", () => { + let o = {}; + let p = new Proxy(o, { + getPrototypeOf(target) { + if (target.foo) + return { bar: 1 }; + return { bar: 2 }; + }, + }); + + expect(Object.getPrototypeOf(p).bar).toBe(2); + o.foo = 20 + expect(Object.getPrototypeOf(p).bar).toBe(1); + }); + + test("non-extensible target", () => { + let o = {}; + let proto = { foo: "bar" }; + Object.setPrototypeOf(o, proto); + Object.preventExtensions(o); + + p = new Proxy(o, { + getPrototypeOf() { + return proto; + } + }); + + expect(Object.getPrototypeOf(p).foo).toBe("bar"); + }); +}); + +describe("[[GetPrototypeOf]] invariants", () => { + test("must return an object or null", () => { + expect(() => { + Object.getPrototypeOf(new Proxy({}, { getPrototypeOf() { return 1; } })); + }).toThrowWithMessage(TypeError, "Proxy handler's getPrototypeOf trap violates invariant: must return an object or null"); + }); + + test("different return object for non-extensible target", () => { + let o = {}; + let proto = {}; + Object.setPrototypeOf(o, proto); + Object.preventExtensions(o); + + let p = new Proxy(o, { + getPrototypeOf(target) { + return { baz: "qux" }; + } + }); + + expect(() => { + Object.getPrototypeOf(p); + }).toThrowWithMessage(TypeError, "Proxy handler's getPrototypeOf trap violates invariant: cannot return a different prototype object for a non-extensible target"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-has.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-has.js index 0881dbcb09..aa1f965ce4 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-has.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-has.js @@ -1,62 +1,70 @@ -load("test-common.js"); - -try { - assert("foo" in new Proxy({}, { has: null }) === false); - assert("foo" in new Proxy({}, { has: undefined}) === false); - assert("foo" in new Proxy({}, {}) === false); - - let o = {}; - let p = new Proxy(o, { - has(target, prop) { - assert(target === o); - assert(prop === "foo"); - return true; - } +describe("[[Has]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + expect("foo" in new Proxy({}, { has: null })).toBe(false); + expect("foo" in new Proxy({}, { has: undefined})).toBe(false); + expect("foo" in new Proxy({}, {})).toBe(false); }); - "foo" in p; - - p = new Proxy(o, { - has(target, prop) { - if (target.checkedFoo) + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + has(target, prop) { + expect(target).toBe(o); + expect(prop).toBe("foo"); return true; - if (prop === "foo") - target.checkedFoo = true; - return false; - } - }); + } + }); - assert("foo" in p === false); - assert("foo" in p === true); - - // Invariants - - o = {}; - Object.defineProperty(o, "foo", { configurable: false }); - Object.defineProperty(o, "bar", { value: 10, configurable: true }); - p = new Proxy(o, { - has() { - return false; - } - }); - - assertThrowsError(() => { "foo" in p; - }, { - error: TypeError, - message: "Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exists on the target as a non-configurable property", }); - Object.preventExtensions(o); + test("conditional return", () => { + let o = {}; + let p = new Proxy(o, { + has(target, prop) { + if (target.checkedFoo) + return true; + if (prop === "foo") + target.checkedFoo = true; + return false; + } + }); - assertThrowsError(() => { - "bar" in p; - }, { - error: TypeError, - message: "Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exists on the target and the target is non-extensible", + expect("foo" in p).toBe(false); + expect("foo" in p).toBe(true); + }); +}); + +describe("[[Has]] invariants", () => { + test("cannot return false if the property exists and is non-configurable", () => { + let o = {}; + Object.defineProperty(o, "foo", { configurable: false }); + + p = new Proxy(o, { + has() { + return false; + } + }); + + expect(() => { + "foo" in p; + }).toThrowWithMessage(TypeError, "Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exists on the target as a non-configurable property"); }); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + test("cannot return false if the property exists and the target is non-extensible", () => { + let o = {}; + Object.defineProperty(o, "foo", { value: 10, configurable: true }); + + let p = new Proxy(o, { + has() { + return false; + } + }); + + Object.preventExtensions(o); + + expect(() => { + "foo" in p; + }).toThrowWithMessage(TypeError, "Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exists on the target and the target is non-extensible"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-isExtensible.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-isExtensible.js index 6acab627c3..fd92597857 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-isExtensible.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-isExtensible.js @@ -1,49 +1,36 @@ -load("test-common.js"); - -try { - assert(Object.isExtensible(new Proxy({}, { isExtensible: null })) === true); - assert(Object.isExtensible(new Proxy({}, { isExtensible: undefined })) === true); - assert(Object.isExtensible(new Proxy({}, {})) === true); - - let o = {}; - let p = new Proxy(o, { - isExtensible(target) { - assert(target === o); - return true; - } +describe("[[IsExtensible]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + expect(Object.isExtensible(new Proxy({}, { isExtensible: null }))).toBe(true); + expect(Object.isExtensible(new Proxy({}, { isExtensible: undefined }))).toBe(true); + expect(Object.isExtensible(new Proxy({}, {}))).toBe(true); }); - Object.isExtensible(p); + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + isExtensible(target) { + expect(target).toBe(o); + return true; + } + }); - // Invariants - - o = {}; - p = new Proxy(o, { - isExtensible(proxyTarget) { - assert(proxyTarget === o); - return true; - }, + expect(Object.isExtensible(p)).toBe(true); }); +}); - assert(Object.isExtensible(p) === true); - Object.preventExtensions(o); +describe("[[Call]] invariants", () => { + test("return value must match the target's extensibility", () => { + let o = {}; + Object.preventExtensions(o); - assertThrowsError(() => { - Object.isExtensible(p); - }, { - error: TypeError, - message: "Proxy handler's isExtensible trap violates invariant: return value must match the target's extensibility", + let p = new Proxy(o, { + isExtensible() { + return true; + }, + }); + + expect(() => { + Object.isExtensible(p); + }).toThrowWithMessage(TypeError, "Proxy handler's isExtensible trap violates invariant: return value must match the target's extensibility"); }); - - p = new Proxy(o, { - isExtensible(proxyTarget) { - assert(proxyTarget === o); - return false; - }, - }); - assert(Object.isExtensible(p) === false); - - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-preventExtensions.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-preventExtensions.js index f8e9a62c6a..ca92a8d489 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-preventExtensions.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-preventExtensions.js @@ -1,55 +1,53 @@ -load("test-common.js"); - -try { - let p = new Proxy({}, { preventExtensions: null }); - assert(Object.preventExtensions(p) === p); - p = new Proxy({}, { preventExtensions: undefined }); - assert(Object.preventExtensions(p) === p); - p = new Proxy({}, {}); - assert(Object.preventExtensions(p) == p); - - let o = {}; - p = new Proxy(o, { - preventExtensions(target) { - assert(target === o); - return true; - } +describe("[[PreventExtension]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + let p = new Proxy({}, { preventExtensions: null }); + expect(Object.preventExtensions(p)).toBe(p); + p = new Proxy({}, { preventExtensions: undefined }); + expect(Object.preventExtensions(p)).toBe(p); + p = new Proxy({}, {}); + expect(Object.preventExtensions(p)).toBe(p); }); - Object.preventExtensions(o); - Object.preventExtensions(p); + test("correct arguments supplied to trap", () => { + let o = {}; + p = new Proxy(o, { + preventExtensions(target) { + expect(target).toBe(o); + return true; + } + }); - // Invariants - - p = new Proxy({}, { - preventExtensions() { - return false; - }, - }); - assertThrowsError(() => { + Object.preventExtensions(o); Object.preventExtensions(p); - }, { - error: TypeError, - message: "Object's [[PreventExtensions]] method returned false", + }); +}); + +describe("[[PreventExtensions]] invariants", () => { + test("cannot return false", () => { + let p = new Proxy({}, { + preventExtensions() { + return false; + }, + }); + + expect(() => { + Object.preventExtensions(p); + }).toThrowWithMessage(TypeError, "Object's [[PreventExtensions]] method returned false"); }); - o = {}; - p = new Proxy(o, { - preventExtensions() { - return true; - }, - }); - assertThrowsError(() => { - Object.preventExtensions(p); - }, { - error: TypeError, - message: "Proxy handler's preventExtensions trap violates invariant: cannot return true if the target object is extensible" - }); + test("cannot return true if the target is extensible", () => { + let o = {}; + let p = new Proxy(o, { + preventExtensions() { + return true; + }, + }); - Object.preventExtensions(o); - assert(Object.preventExtensions(p) === p); + expect(() => { + Object.preventExtensions(p); + }).toThrowWithMessage(TypeError, "Proxy handler's preventExtensions trap violates invariant: cannot return true if the target object is extensible"); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + Object.preventExtensions(o); + expect(Object.preventExtensions(p)).toBe(p); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js index c73c7badc8..6f3d83939d 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js @@ -1,66 +1,73 @@ -load("test-common.js"); - -try { - assert((new Proxy({}, { set: undefined }).foo = 1) === 1); - assert((new Proxy({}, { set: null }).foo = 1) === 1); - assert((new Proxy({}, {}).foo = 1) === 1); - - let o = {}; - let p = new Proxy(o, { - set(target, prop, value, receiver) { - assert(target === o); - assert(prop === "foo"); - assert(value === 10); - assert(receiver === p); - return true; - }, +describe("[[Set]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + expect((new Proxy({}, { set: undefined }).foo = 1)).toBe(1); + expect((new Proxy({}, { set: null }).foo = 1)).toBe(1); + expect((new Proxy({}, {}).foo = 1)).toBe(1); }); - p.foo = 10; + test("correct arguments supplied to trap", () => { + let o = {}; + let p = new Proxy(o, { + set(target, prop, value, receiver) { + expect(target).toBe(o); + expect(prop).toBe("foo"); + expect(value).toBe(10); + expect(receiver).toBe(p); + return true; + }, + }); - p = new Proxy(o, { - set(target, prop, value, receiver) { - if (target[prop] === value) { - target[prop] *= 2; - } else { - target[prop] = value; - } - }, + p.foo = 10; }); - p.foo = 10; - assert(p.foo === 10); - p.foo = 10; - assert(p.foo === 20); - p.foo = 10; - assert(p.foo === 10); + test("conditional return value", () => { + let p = new Proxy({}, { + set(target, prop, value) { + if (target[prop] === value) { + target[prop] *= 2; + } else { + target[prop] = value; + } + }, + }); - // Invariants + p.foo = 10; + expect(p.foo).toBe(10); + p.foo = 10; + expect(p.foo).toBe(20); + p.foo = 10; + expect(p.foo).toBe(10); + }); +}); - o = {}; - Object.defineProperty(o, "foo", { value: 10 }); - p = new Proxy(o, { - set() { - return true; - }, +describe("[[Set]] invariants", () => { + test("cannot return true for a non-configurable, non-writable property", () => { + let o = {}; + Object.defineProperty(o, "foo", { value: 10 }); + + let p = new Proxy(o, { + set() { + return true; + }, + }); + + expect(() => { + p.foo = 12; + }).toThrowWithMessage(TypeError, "Proxy handler's set trap violates invariant: cannot return true for a property on the target which is a non-configurable, non-writable own data property"); }); - assertThrowsError(() => { - p.foo = 12; - }, { - error: TypeError, - message: "Proxy handler's set trap violates invariant: cannot return true for a property on the target which is a non-configurable, non-writable own data property", - }); + test("cannot return true for a non-configurable accessor property with no setter", () => { + let o = {}; + Object.defineProperty(o, "foo", { get() {} }); - Object.defineProperty(o, "bar", { get() {} }); - assertThrowsError(() => { - p.bar = 12; - }, { - error: TypeError, - message: "Proxy handler's set trap violates invariant: cannot return true for a property on the target which is a non-configurable own accessor property with an undefined set attribute", - }); + let p = new Proxy(o, { + set() { + return true; + }, + }); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + expect(() => { + p.foo = 12; + }).toThrowWithMessage(TypeError, "Proxy handler's set trap violates invariant: cannot return true for a property on the target which is a non-configurable own accessor property with an undefined set attribute"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-setPrototypeOf.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-setPrototypeOf.js index 14598b750b..580cccaa3d 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-setPrototypeOf.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-setPrototypeOf.js @@ -1,95 +1,94 @@ -load("test-common.js"); +describe("[[SetPrototypeOf]] trap normal behavior", () => { + test("forwarding when not defined in handler", () => { + const o = {}; + const proto = { foo: "bar" }; + Object.setPrototypeOf(o, proto); -try { - const child = {}; - const childProto = { foo: "bar" }; - - Object.setPrototypeOf(child, childProto); - assert(child.foo === "bar"); - - Object.setPrototypeOf(new Proxy(child, { setPrototypeOf: null }), childProto); - Object.setPrototypeOf(new Proxy(child, { setPrototypeOf: undefined }), childProto); - - let o = {}; - let theNewProto = { foo: "bar" }; - let p = new Proxy(o, { - setPrototypeOf(target, newProto) { - assert(target === o); - assert(newProto === theNewProto); - return true; - } + let p = new Proxy(o, { setPrototypeOf: null }); + expect(Object.setPrototypeOf(p, proto)).toBe(p); + let p = new Proxy(o, { setPrototypeOf: undefined }); + expect(Object.setPrototypeOf(p, proto)).toBe(p); + let p = new Proxy(o, {}); + expect(Object.setPrototypeOf(p, proto)).toBe(p); }); - Object.setPrototypeOf(p, theNewProto); + test("correct arguments supplied to trap", () => { + let o = {}; + let theNewProto = { foo: "bar" }; - p = new Proxy(o, { - setPrototypeOf(target, newProto) { - if (target.shouldSet) - Object.setPrototypeOf(target, newProto); - return true; - }, + let p = new Proxy(o, { + setPrototypeOf(target, newProto) { + expect(target).toBe(o); + expect(newProto).toBe(theNewProto); + return true; + } + }); + + Object.setPrototypeOf(p, theNewProto); }); - Object.setPrototypeOf(p, { foo: 1 }); - assert(Object.getPrototypeOf(p).foo === undefined); - p.shouldSet = true; - assert(o.shouldSet === true); - Object.setPrototypeOf(p, { foo: 1 }); - assert(Object.getPrototypeOf(p).foo === 1); + test("conditional setting", () => { + let o = {}; - // Invariants + let p = new Proxy(o, { + setPrototypeOf(target, newProto) { + if (target.shouldSet) + Object.setPrototypeOf(target, newProto); + return true; + }, + }); - assertThrowsError(() => { - Object.setPrototypeOf(new Proxy(child, { setPrototypeOf: 1 }), {}); - }, { - error: TypeError, - message: "Proxy handler's setPrototypeOf trap wasn't undefined, null, or callable", + Object.setPrototypeOf(p, { foo: 1 }); + expect(Object.getPrototypeOf(p).foo).toBeUndefined(); + p.shouldSet = true; + expect(o.shouldSet).toBe(true); + Object.setPrototypeOf(p, { foo: 1 }); + expect(Object.getPrototypeOf(p).foo).toBe(1); }); - p = new Proxy(child, { - setPrototypeOf(target, newProto) { - assert(target === child); - return false; - }, + test("non-extensible targets", () => { + let o = {}; + let proto = {}; + Object.setPrototypeOf(o, proto); + Object.preventExtensions(o); + + p = new Proxy(o, { + setPrototypeOf() { + return true; + }, + }); + + expect(Object.setPrototypeOf(p, proto)).toBe(p); + expect(Object.getPrototypeOf(p)).toBe(proto); + }); +}); + +describe("[[SetPrototypeOf]] invariants", () => { + test("cannot return false", () => { + let o = {}; + p = new Proxy(o, { + setPrototypeOf() { + return false; + }, + }); + + expect(() => { + Object.setPrototypeOf(p, {}); + }).toThrowWithMessage(TypeError, "Object's [[SetPrototypeOf]] method returned false"); }); - assertThrowsError(() => { - Object.setPrototypeOf(p, {}); - }, { - error: TypeError, - message: "Object's [[SetPrototypeOf]] method returned false", + test("the argument must match the target's prototype if the target is non-extensible", () => { + let o = {}; + Object.preventExtensions(o); + + let p = new Proxy(o, { + setPrototypeOf() { + return true; + } + }); + + expect(() => { + Object.setPrototypeOf(p, {}); + }).toThrowWithMessage(TypeError, "Proxy handler's setPrototypeOf trap violates invariant: the argument must match the prototype of the target if the target is non-extensible"); }); - assert(Object.getPrototypeOf(p) === childProto); - - p = new Proxy(child, { - setPrototypeOf(target, newProto) { - assert(target === child); - return true; - }, - }); - - assert(Object.setPrototypeOf(p, {}) === p); - assert(Object.getPrototypeOf(p) === childProto); - - Object.preventExtensions(child); - p = new Proxy(child, { - setPrototypeOf(target, newProto) { - assert(target === child); - return true; - }, - }); - - assert(Object.setPrototypeOf(p, childProto) === p); - assert(Object.getPrototypeOf(p) === childProto); - - assertThrowsError(() => { - Object.setPrototypeOf(p, {}); - }, { - error: TypeError, - message: "Proxy handler's setPrototypeOf trap violates invariant: the argument must match the prototype of the target if the target is non-extensible", - }); - - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} +}); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.js index 8d6dd57afd..f3ed65886d 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.js @@ -1,30 +1,31 @@ -load("test-common.js"); +test("constructs properly", () => { + expect(() => { + new Proxy({}, {}); + }).not.toThrow(); +}); -try { - new Proxy({}, {}); - - assertThrowsError(() => { +test("constructor argument count", () => { + expect(() => { new Proxy(); - }, { - error: TypeError, - message: "Proxy constructor requires at least two arguments", - }); + }).toThrowWithMessage(TypeError, "Proxy constructor requires at least two arguments"); - assertThrowsError(() => { - Proxy(); - }, { - error: TypeError, - message: "Proxy must be called with the 'new' operator", - }); + expect(() => { + new Proxy({}); + }).toThrowWithMessage(TypeError, "Proxy constructor requires at least two arguments"); +}); - assertThrowsError(() => { +test("constructor requires objects", () => { + expect(() => { new Proxy(1, {}); - }, { - error: TypeError, - message: "Expected target argument of Proxy constructor to be object, got 1", - }); + }).toThrowWithMessage(TypeError, "Expected target argument of Proxy constructor to be object, got 1"); - console.log("PASS"); -} catch (e) { - console.log("FAIL: " + e); -} + expect(() => { + new Proxy({}, 1); + }).toThrowWithMessage(TypeError, "Expected handler argument of Proxy constructor to be object, got 1"); +}); + +test("constructor must be invoked with 'new'", () => { + expect(() => { + Proxy({}, {}); + }).toThrowWithMessage(TypeError, "Proxy must be called with the 'new' operator"); +}); diff --git a/Userland/test-js.cpp b/Userland/test-js.cpp index 73e190e931..a05f8d64e0 100644 --- a/Userland/test-js.cpp +++ b/Userland/test-js.cpp @@ -42,6 +42,19 @@ // FIXME: Will eventually not be necessary when all tests are converted Vector tests_to_run = { + "builtins/Proxy/Proxy.js", + "builtins/Proxy/Proxy.handler-apply.js", + "builtins/Proxy/Proxy.handler-construct.js", + "builtins/Proxy/Proxy.handler-defineProperty.js", + "builtins/Proxy/Proxy.handler-deleteProperty.js", + "builtins/Proxy/Proxy.handler-get.js", + "builtins/Proxy/Proxy.handler-getOwnPropertyDescriptor.js", + "builtins/Proxy/Proxy.handler-getPrototypeOf.js", + "builtins/Proxy/Proxy.handler-has.js", + "builtins/Proxy/Proxy.handler-isExtensible.js", + "builtins/Proxy/Proxy.handler-preventExtensions.js", + "builtins/Proxy/Proxy.handler-set.js", + "builtins/Proxy/Proxy.handler-setPrototypeOf.js", "add-values-to-primitive.js", "automatic-semicolon-insertion.js", "comments-basic.js",