summaryrefslogtreecommitdiff
path: root/Libraries/LibJS/Tests/classes
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2020-07-05 17:26:26 -0700
committerAndreas Kling <kling@serenityos.org>2020-07-06 23:40:35 +0200
commit15de2eda2b8c06a59053d6a7144ea9eaf07e2210 (patch)
tree191d9459dc71e270def1ca69a02406a9498005b9 /Libraries/LibJS/Tests/classes
parent918f4affd51cdd285272770465d4d875f4ae5aa2 (diff)
downloadserenity-15de2eda2b8c06a59053d6a7144ea9eaf07e2210.zip
LibJS: Convert all remaining non-Array tests to the new system :)
Diffstat (limited to 'Libraries/LibJS/Tests/classes')
-rw-r--r--Libraries/LibJS/Tests/classes/class-advanced-extends.js35
-rw-r--r--Libraries/LibJS/Tests/classes/class-basic.js274
-rw-r--r--Libraries/LibJS/Tests/classes/class-constructor.js67
-rw-r--r--Libraries/LibJS/Tests/classes/class-errors.js95
-rw-r--r--Libraries/LibJS/Tests/classes/class-expressions.js69
-rw-r--r--Libraries/LibJS/Tests/classes/class-getters.js68
-rw-r--r--Libraries/LibJS/Tests/classes/class-inheritance.js133
-rw-r--r--Libraries/LibJS/Tests/classes/class-methods.js51
-rw-r--r--Libraries/LibJS/Tests/classes/class-setters.js100
-rw-r--r--Libraries/LibJS/Tests/classes/class-static-getters.js80
-rw-r--r--Libraries/LibJS/Tests/classes/class-static-setters.js104
-rw-r--r--Libraries/LibJS/Tests/classes/class-static.js72
-rw-r--r--Libraries/LibJS/Tests/classes/class-strict-mode.js19
13 files changed, 898 insertions, 269 deletions
diff --git a/Libraries/LibJS/Tests/classes/class-advanced-extends.js b/Libraries/LibJS/Tests/classes/class-advanced-extends.js
new file mode 100644
index 0000000000..749f65003c
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-advanced-extends.js
@@ -0,0 +1,35 @@
+test("extending function", () => {
+ class A extends function () {
+ this.foo = 10;
+ } {}
+
+ expect(new A().foo).toBe(10);
+});
+
+test("extending null", () => {
+ class A extends null {}
+
+ expect(Object.getPrototypeOf(A.prototype)).toBeNull();
+
+ expect(() => {
+ new A();
+ }).toThrowWithMessage(ReferenceError, "|this| has not been initialized");
+});
+
+test("extending String", () => {
+ class MyString extends String {}
+
+ const ms = new MyString("abc");
+ expect(ms).toBeInstanceOf(MyString);
+ expect(ms).toBeInstanceOf(String);
+ expect(ms.charAt(1)).toBe("b");
+
+ class MyString2 extends MyString {
+ charAt(i) {
+ return `#${super.charAt(i)}`;
+ }
+ }
+
+ const ms2 = new MyString2("abc");
+ expect(ms2.charAt(1)).toBe("#b");
+});
diff --git a/Libraries/LibJS/Tests/classes/class-basic.js b/Libraries/LibJS/Tests/classes/class-basic.js
index 2fd856ac36..844bbe68fc 100644
--- a/Libraries/LibJS/Tests/classes/class-basic.js
+++ b/Libraries/LibJS/Tests/classes/class-basic.js
@@ -1,269 +1,5 @@
-load("test-common.js");
-
-try {
- class X {
- constructor() {
- this.x = 3;
- }
- getX() {
- return 3;
- }
-
- init() {
- this.y = 3;
- }
- }
-
- assert(X.name === "X");
- assert(X.length === 0);
-
- class Y extends X {
- init() {
- super.init();
- this.y += 3;
- }
- }
-
- assert(new Y().getX() === 3);
- assert(new Y().x === 3);
-
- let x = new X();
- assert(x.x === 3);
- assert(x.getX() === 3);
-
- let y = new Y();
- assert(y.x === 3);
- assert(y.y === undefined);
- y.init();
- assert(y.y === 6);
- assert(y.hasOwnProperty("y"));
-
- class Foo {
- constructor(x) {
- this.x = x;
- }
- }
- assert(Foo.length === 1);
-
- class Bar extends Foo {
- constructor() {
- super(5);
- }
- }
-
- class Baz {
- constructor() {
- this.foo = 55;
- this._bar = 33;
- }
-
- get bar() {
- return this._bar;
- }
-
- set bar(value) {
- this._bar = value;
- }
-
- ["get" + "Foo"]() {
- return this.foo;
- }
-
- static get staticFoo() {
- assert(this === Baz);
- return 11;
- }
- }
-
- let barPropertyDescriptor = Object.getOwnPropertyDescriptor(Baz.prototype, "bar");
- assert(barPropertyDescriptor.get.name === "get bar");
- assert(barPropertyDescriptor.set.name === "set bar");
-
- let baz = new Baz();
- assert(baz.foo === 55);
- assert(baz.bar === 33);
- baz.bar = 22;
- assert(baz.bar === 22);
-
- assert(baz.getFoo() === 55);
- assert(Baz.staticFoo === 11);
-
- assert(new Bar().x === 5);
-
- class ExtendsFunction extends function () {
- this.foo = 22;
- } {}
- assert(new ExtendsFunction().foo === 22);
-
- class ExtendsString extends String {}
- assert(new ExtendsString() instanceof String);
- assert(new ExtendsString() instanceof ExtendsString);
- assert(new ExtendsString("abc").charAt(1) === "b");
-
- class MyWeirdString extends ExtendsString {
- charAt(i) {
- return "#" + super.charAt(i);
- }
- }
- assert(new MyWeirdString("abc").charAt(1) === "#b");
-
- class ExtendsNull extends null {}
-
- assertThrowsError(
- () => {
- new ExtendsNull();
- },
- {
- error: ReferenceError,
- }
- );
- assert(Object.getPrototypeOf(ExtendsNull.prototype) === null);
-
- class ExtendsClassExpression extends class {
- constructor(x) {
- this.x = x;
- }
- } {
- constructor(y) {
- super(5);
- this.y = 6;
- }
- }
- let extendsClassExpression = new ExtendsClassExpression();
- assert(extendsClassExpression.x === 5);
- assert(extendsClassExpression.y === 6);
-
- class InStrictMode {
- constructor() {
- assert(isStrictMode());
- }
-
- method() {
- assert(isStrictMode());
- }
- }
-
- let resultOfAnExpression = new (class {
- constructor(x) {
- this.x = x;
- }
- getX() {
- return this.x + 10;
- }
- })(55);
- assert(resultOfAnExpression.x === 55);
- assert(resultOfAnExpression.getX() === 65);
-
- let ClassExpression = class Foo {};
- assert(ClassExpression.name === "Foo");
-
- new InStrictMode().method();
- assert(!isStrictMode());
-
- assertIsSyntaxError(`
- class GetterWithArgument {
- get foo(bar) {
- return 0;
- }
- }
- `);
-
- assertIsSyntaxError(`
- class SetterWithNoArgumetns {
- set foo() {
- }
- }
- `);
-
- assertIsSyntaxError(`
- class SetterWithMoreThanOneArgument {
- set foo(bar, baz) {
- }
- }
- `);
-
- assertIsSyntaxError(`
- class FooBase {}
- class IsASyntaxError extends FooBase {
- bar() {
- function f() { super.baz; }
- }
- }
- `);
-
- assertIsSyntaxError(`
- class NoBaseSuper {
- constructor() {
- super();
- }
- }
- `);
-
- assertThrowsError(
- () => {
- class BadExtends extends 3 {}
- },
- {
- error: TypeError,
- }
- );
-
- assertThrowsError(
- () => {
- class BadExtends extends undefined {}
- },
- {
- error: TypeError,
- }
- );
-
- class SuperNotASyntaxError {
- bar() {
- () => {
- super.baz;
- };
- }
- }
-
- class SuperNoBasePropertyLookup {
- constructor() {
- super.foo;
- }
- }
-
- assertThrowsError(
- () => {
- class Base {}
- class DerivedDoesntCallSuper extends Base {
- constructor() {
- this;
- }
- }
-
- new DerivedDoesntCallSuper();
- },
- {
- error: ReferenceError,
- }
- );
- assertThrowsError(
- () => {
- class Base {}
- class CallsSuperTwice extends Base {
- constructor() {
- super();
- super();
- }
- }
-
- new CallsSuperTwice();
- },
- {
- error: ReferenceError,
- }
- );
-
- console.log("PASS");
-} catch (e) {
- console.log("FAIL: " + e);
-}
+test("class properties", () => {
+ class A {}
+ expect(A.name).toBe("A");
+ expect(A).toHaveLength(0);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-constructor.js b/Libraries/LibJS/Tests/classes/class-constructor.js
new file mode 100644
index 0000000000..367c1e77ea
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-constructor.js
@@ -0,0 +1,67 @@
+test("property initialization", () => {
+ class A {
+ constructor() {
+ this.x = 3;
+ }
+ }
+
+ expect(new A().x).toBe(3);
+});
+
+test("method initialization", () => {
+ class A {
+ constructor() {
+ this.x = () => 10;
+ }
+ }
+
+ expect(new A().x()).toBe(10);
+});
+
+test("initialize to class method", () => {
+ class A {
+ constructor() {
+ this.x = this.method;
+ }
+
+ method() {
+ return 10;
+ }
+ }
+
+ expect(new A().x()).toBe(10);
+});
+
+test("constructor length affects class length", () => {
+ class A {
+ constructor() {}
+ }
+
+ expect(A).toHaveLength(0);
+
+ class B {
+ constructor(a, b, c = 2) {}
+ }
+
+ expect(B).toHaveLength(2);
+});
+
+test.skip("must be invoked with 'new'", () => {
+ class A {
+ constructor() {}
+ }
+
+ expect(() => {
+ A();
+ }).toThrow(TypeError); // FIXME: Add message when this test works
+
+ expect(() => {
+ A.prototype.constructor();
+ }).toThrow(TypeError); // FIXME: Add message when this test works
+});
+
+test("implicit constructor", () => {
+ class A {}
+
+ expect(new A()).toBeInstanceOf(A);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-errors.js b/Libraries/LibJS/Tests/classes/class-errors.js
new file mode 100644
index 0000000000..acef9ee3c9
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-errors.js
@@ -0,0 +1,95 @@
+describe("non-syntax errors", () => {
+ test("super reference inside nested-but-same |this| scope with no base class", () => {
+ expect(`
+ class A {
+ foo() {
+ () => { super.bar; }
+ }
+ }`).toEval();
+ });
+
+ test("super reference property lookup with no base class", () => {
+ expect(`
+ class A {
+ constructor() {
+ super.foo;
+ }
+ }`).toEval();
+ });
+});
+
+describe("reference errors", () => {
+ test("derived class doesn't call super in constructor before using this", () => {
+ class Parent {}
+ class Child extends Parent {
+ constructor() {
+ this;
+ }
+ }
+
+ expect(() => {
+ new Child();
+ }).toThrowWithMessage(ReferenceError, "|this| has not been initialized");
+ });
+
+ test("derived class calls super twice in constructor", () => {
+ class Parent {}
+ class Child extends Parent {
+ constructor() {
+ super();
+ super();
+ }
+ }
+
+ expect(() => {
+ new Child();
+ }).toThrowWithMessage(ReferenceError, "|this| is already initialized");
+ });
+});
+
+describe("syntax errors", () => {
+ test("getter with argument", () => {
+ expect(`
+ class A {
+ get foo(v) {
+ return 0;
+ }
+ }`).not.toEval();
+ });
+
+ test("setter with no arguments", () => {
+ expect(`
+ class A {
+ set foo() {
+ }
+ }`).not.toEval();
+ });
+
+ test("setter with more than one argument", () => {
+ expect(`
+ class A {
+ set foo(bar, baz) {
+ }
+ }`).not.toEval();
+ });
+
+ test("super reference inside different |this| scope", () => {
+ expect(`
+ class Parent {}
+
+ class Child extends Parent {
+ foo() {
+ function f() { super.foo; }
+ }
+ }`).not.toEval();
+ });
+
+ test("super reference call with no base class", () => {
+ expect(`
+ class A {
+ constructor() {
+ super();
+ }
+ }`).not.toEval();
+ });
+});
diff --git a/Libraries/LibJS/Tests/classes/class-expressions.js b/Libraries/LibJS/Tests/classes/class-expressions.js
new file mode 100644
index 0000000000..3dbea6d8fc
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-expressions.js
@@ -0,0 +1,69 @@
+// This file must not be formatted by prettier. Make sure your IDE
+// respects the .prettierignore file!
+
+test("basic functionality", () => {
+ const A = class {
+ constructor(x) {
+ this.x = x;
+ }
+
+ getX() {
+ return this.x * 2;
+ }
+ };
+
+ expect(new A(10).getX()).toBe(20);
+});
+
+test("inline instantiation", () => {
+ const a = new class {
+ constructor() {
+ this.x = 10;
+ }
+
+ getX() {
+ return this.x * 2;
+ }
+ };
+
+ expect(a.getX()).toBe(20);
+});
+
+test("inline instantiation with argument", () => {
+ const a = new class {
+ constructor(x) {
+ this.x = x;
+ }
+
+ getX() {
+ return this.x * 2;
+ }
+ }(10);
+
+ expect(a.getX()).toBe(20);
+});
+
+test("extending class expressions", () => {
+ class A extends class {
+ constructor(x) {
+ this.x = x;
+ }
+ } {
+ constructor(y) {
+ super(y);
+ this.y = y * 2;
+ }
+ };
+
+ const a = new A(10);
+ expect(a.x).toBe(10);
+ expect(a.y).toBe(20);
+});
+
+test("class expression name", () => {
+ let A = class {}
+ expect(A.name).toBe("A");
+
+ let B = class C {}
+ expect(B.name).toBe("C");
+});
diff --git a/Libraries/LibJS/Tests/classes/class-getters.js b/Libraries/LibJS/Tests/classes/class-getters.js
new file mode 100644
index 0000000000..b1cf7ad229
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-getters.js
@@ -0,0 +1,68 @@
+test("basic functionality", () => {
+ class A {
+ get x() {
+ return 10;
+ }
+ }
+
+ expect(A).not.toHaveProperty("x");
+ expect(new A().x).toBe(10);
+});
+test("name", () => {
+ class A {
+ get x() {}
+ }
+
+ const d = Object.getOwnPropertyDescriptor(A.prototype, "x");
+ expect(d.get.name).toBe("get x");
+});
+
+test("extended name syntax", () => {
+ class A {
+ get "method with space"() {
+ return 1;
+ }
+
+ get 12() {
+ return 2;
+ }
+
+ get [`he${"llo"}`]() {
+ return 3;
+ }
+ }
+
+ const a = new A();
+ expect(a["method with space"]).toBe(1);
+ expect(a[12]).toBe(2);
+ expect(a.hello).toBe(3);
+});
+
+test("inherited getter", () => {
+ class Parent {
+ get x() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {}
+
+ expect(Child).not.toHaveProperty("x");
+ expect(new Child().x).toBe(3);
+});
+
+test("inherited getter overriding", () => {
+ class Parent {
+ get x() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {
+ get x() {
+ return 10;
+ }
+ }
+
+ expect(new Child().x).toBe(10);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-inheritance.js b/Libraries/LibJS/Tests/classes/class-inheritance.js
new file mode 100644
index 0000000000..5fb273ba52
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-inheritance.js
@@ -0,0 +1,133 @@
+test("method inheritance", () => {
+ class Parent {
+ method() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {}
+
+ const p = new Parent();
+ const c = new Child();
+ expect(p.method()).toBe(3);
+ expect(c.method()).toBe(3);
+});
+
+test("method overriding", () => {
+ class Parent {
+ method() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {
+ method() {
+ return 10;
+ }
+ }
+
+ const p = new Parent();
+ const c = new Child();
+ expect(p.method()).toBe(3);
+ expect(c.method()).toBe(10);
+});
+
+test("parent method reference with super", () => {
+ class Parent {
+ method() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {
+ method() {
+ return super.method() * 2;
+ }
+ }
+
+ const p = new Parent();
+ const c = new Child();
+ expect(p.method()).toBe(3);
+ expect(c.method()).toBe(6);
+});
+
+test("child class access to parent class initialized properties", () => {
+ class Parent {
+ constructor() {
+ this.x = 3;
+ }
+ }
+
+ class Child extends Parent {}
+
+ const p = new Parent();
+ const c = new Child();
+ expect(p.x).toBe(3);
+ expect(c.x).toBe(3);
+});
+
+test("child class modification of parent class properties", () => {
+ class Parent {
+ constructor() {
+ this.x = 3;
+ }
+ }
+
+ class Child extends Parent {
+ change() {
+ this.x = 10;
+ }
+ }
+
+ const p = new Parent();
+ const c = new Child();
+ expect(p.x).toBe(3);
+ expect(c.x).toBe(3);
+
+ c.change();
+ expect(c.x).toBe(10);
+});
+
+test("inheritance and hasOwnProperty", () => {
+ class Parent {
+ constructor() {
+ this.x = 3;
+ }
+ }
+
+ class Child extends Parent {
+ method() {
+ this.y = 10;
+ }
+ }
+
+ const p = new Parent();
+ const c = new Child();
+ expect(p.hasOwnProperty("x")).toBeTrue();
+ expect(p.hasOwnProperty("y")).toBeFalse();
+ expect(c.hasOwnProperty("x")).toBeTrue();
+ expect(c.hasOwnProperty("y")).toBeFalse();
+
+ c.method();
+ expect(c.hasOwnProperty("x")).toBeTrue();
+ expect(c.hasOwnProperty("y")).toBeTrue();
+});
+
+test("super constructor call from child class with argument", () => {
+ class Parent {
+ constructor(x) {
+ this.x = x;
+ }
+ }
+
+ class Child extends Parent {
+ constructor() {
+ super(10);
+ }
+ }
+
+ const p = new Parent(3);
+ const c = new Child(3);
+ expect(p.x).toBe(3);
+ expect(c.x).toBe(10);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-methods.js b/Libraries/LibJS/Tests/classes/class-methods.js
new file mode 100644
index 0000000000..4bc04ea5a5
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-methods.js
@@ -0,0 +1,51 @@
+test("basic functionality", () => {
+ class A {
+ number() {
+ return 2;
+ }
+
+ string() {
+ return "foo";
+ }
+ }
+
+ const a = new A();
+ expect(a.number()).toBe(2);
+ expect(a.string()).toBe("foo");
+});
+
+test("length", () => {
+ class A {
+ method1() {}
+
+ method2(a, b, c, d) {}
+
+ method3(a, b, ...c) {}
+ }
+
+ const a = new A();
+ expect(a.method1).toHaveLength(0);
+ expect(a.method2).toHaveLength(4);
+ expect(a.method3).toHaveLength(2);
+});
+
+test("extended name syntax", () => {
+ class A {
+ "method with space"() {
+ return 1;
+ }
+
+ 12() {
+ return 2;
+ }
+
+ [`he${"llo"}`]() {
+ return 3;
+ }
+ }
+
+ const a = new A();
+ expect(a["method with space"]()).toBe(1);
+ expect(a[12]()).toBe(2);
+ expect(a.hello()).toBe(3);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-setters.js b/Libraries/LibJS/Tests/classes/class-setters.js
new file mode 100644
index 0000000000..19dbd6ecd2
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-setters.js
@@ -0,0 +1,100 @@
+test("basic functionality", () => {
+ class A {
+ get x() {
+ return this._x;
+ }
+
+ set x(value) {
+ this._x = value * 2;
+ }
+ }
+
+ expect(A.x).toBeUndefined();
+ expect(A).not.toHaveProperty("_x");
+ const a = new A();
+ expect(a.x).toBeUndefined();
+ expect(a).not.toHaveProperty("_x");
+ a.x = 3;
+ expect(a.x).toBe(6);
+ expect(a).toHaveProperty("_x", 6);
+});
+
+test("name", () => {
+ class A {
+ set x(v) {}
+ }
+
+ const d = Object.getOwnPropertyDescriptor(A.prototype, "x");
+ expect(d.set.name).toBe("set x");
+});
+
+test("extended name syntax", () => {
+ class A {
+ set "method with space"(value) {
+ this.a = value;
+ }
+
+ set 12(value) {
+ this.b = value;
+ }
+
+ set [`he${"llo"}`](value) {
+ this.c = value;
+ }
+ }
+
+ const a = new A();
+ a["method with space"] = 1;
+ a[12] = 2;
+ a.hello = 3;
+ expect(a.a).toBe(1);
+ expect(a.b).toBe(2);
+ expect(a.c).toBe(3);
+});
+
+test("inherited setter", () => {
+ class Parent {
+ get x() {
+ return this._x;
+ }
+
+ set x(value) {
+ this._x = value * 2;
+ }
+ }
+
+ class Child extends Parent {}
+
+ const c = new Child();
+
+ expect(c.x).toBeUndefined();
+ c.x = 10;
+ expect(c.x).toBe(20);
+});
+
+test("inherited static setter overriding", () => {
+ class Parent {
+ get x() {
+ return this._x;
+ }
+
+ set x(value) {
+ this._x = value * 2;
+ }
+ }
+
+ class Child extends Parent {
+ get x() {
+ return this._x;
+ }
+
+ set x(value) {
+ this._x = value * 3;
+ }
+ }
+
+ const c = new Child();
+ expect(c.x).toBeUndefined();
+ c.x = 10;
+ expect(c.x).toBe(30);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-static-getters.js b/Libraries/LibJS/Tests/classes/class-static-getters.js
new file mode 100644
index 0000000000..0407bd9a48
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-static-getters.js
@@ -0,0 +1,80 @@
+describe("correct behavior", () => {
+ test("basic functionality", () => {
+ class A {
+ static get x() {
+ return 10;
+ }
+ }
+
+ expect(A.x).toBe(10);
+ expect(new A()).not.toHaveProperty("x");
+ });
+
+ test("name", () => {
+ class A {
+ static get x() {}
+ }
+
+ const d = Object.getOwnPropertyDescriptor(A, "x");
+ expect(d.get.name).toBe("get x");
+ });
+
+ test("extended name syntax", () => {
+ class A {
+ static get "method with space"() {
+ return 1;
+ }
+
+ static get 12() {
+ return 2;
+ }
+
+ static get [`he${"llo"}`]() {
+ return 3;
+ }
+ }
+
+ expect(A["method with space"]).toBe(1);
+ expect(A[12]).toBe(2);
+ expect(A.hello).toBe(3);
+ });
+
+ test("inherited static getter", () => {
+ class Parent {
+ static get x() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {}
+
+ expect(Parent.x).toBe(3);
+ expect(Child.x).toBe(3);
+ });
+
+ test("inherited static getter overriding", () => {
+ class Parent {
+ static get x() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {
+ static get x() {
+ return 10;
+ }
+ }
+
+ expect(Parent.x).toBe(3);
+ expect(Child.x).toBe(10);
+ });
+});
+
+describe("errors", () => {
+ test('"get static" is a syntax error', () => {
+ expect(`
+ class A {
+ get static foo() {}
+ }`).not.toEval();
+ });
+});
diff --git a/Libraries/LibJS/Tests/classes/class-static-setters.js b/Libraries/LibJS/Tests/classes/class-static-setters.js
new file mode 100644
index 0000000000..41d867a7e5
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-static-setters.js
@@ -0,0 +1,104 @@
+describe("correct behavior", () => {
+ test("basic functionality", () => {
+ class A {
+ static get x() {
+ return this._x;
+ }
+
+ static set x(value) {
+ this._x = value * 2;
+ }
+ }
+
+ expect(A.x).toBeUndefined();
+ expect(A).not.toHaveProperty("_x");
+ A.x = 3;
+ expect(A.x).toBe(6);
+ expect(A).toHaveProperty("_x", 6);
+ });
+
+ test("name", () => {
+ class A {
+ static set x(v) {}
+ }
+
+ const d = Object.getOwnPropertyDescriptor(A, "x");
+ expect(d.set.name).toBe("set x");
+ });
+
+ test("extended name syntax", () => {
+ class A {
+ static set "method with space"(value) {
+ this.a = value;
+ }
+
+ static set 12(value) {
+ this.b = value;
+ }
+
+ static set [`he${"llo"}`](value) {
+ this.c = value;
+ }
+ }
+
+ A["method with space"] = 1;
+ A[12] = 2;
+ A.hello = 3;
+ expect(A.a).toBe(1);
+ expect(A.b).toBe(2);
+ expect(A.c).toBe(3);
+ });
+
+ test("inherited static setter", () => {
+ class Parent {
+ static get x() {
+ return this._x;
+ }
+
+ static set x(value) {
+ this._x = value * 2;
+ }
+ }
+
+ class Child extends Parent {}
+
+ expect(Child.x).toBeUndefined();
+ Child.x = 10;
+ expect(Child.x).toBe(20);
+ });
+
+ test("inherited static setter overriding", () => {
+ class Parent {
+ static get x() {
+ return this._x;
+ }
+
+ static set x(value) {
+ this._x = value * 2;
+ }
+ }
+
+ class Child extends Parent {
+ static get x() {
+ return this._x;
+ }
+
+ static set x(value) {
+ this._x = value * 3;
+ }
+ }
+
+ expect(Child.x).toBeUndefined();
+ Child.x = 10;
+ expect(Child.x).toBe(30);
+ });
+});
+
+describe("errors", () => {
+ test('"set static" is a syntax error', () => {
+ expect(`
+ class A {
+ set static foo(value) {}
+ }`).not.toEval();
+ });
+});
diff --git a/Libraries/LibJS/Tests/classes/class-static.js b/Libraries/LibJS/Tests/classes/class-static.js
new file mode 100644
index 0000000000..a58cdd7f5d
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-static.js
@@ -0,0 +1,72 @@
+test("basic functionality", () => {
+ class A {
+ static method() {
+ return 10;
+ }
+ }
+
+ expect(A.method()).toBe(10);
+ expect(new A().method).toBeUndefined();
+});
+
+test("extended name syntax", () => {
+ class A {
+ static method() {
+ return 1;
+ }
+
+ static 12() {
+ return 2;
+ }
+
+ static [`he${"llo"}`]() {
+ return 3;
+ }
+ }
+
+ expect(A.method()).toBe(1);
+ expect(A[12]()).toBe(2);
+ expect(A.hello()).toBe(3);
+});
+
+test("bound |this|", () => {
+ class A {
+ static method() {
+ expect(this).toBe(A);
+ }
+ }
+
+ A.method();
+});
+
+test("inherited static methods", () => {
+ class Parent {
+ static method() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {}
+
+ expect(Parent.method()).toBe(3);
+ expect(Child.method()).toBe(3);
+ expect(new Parent()).not.toHaveProperty("method");
+ expect(new Child()).not.toHaveProperty("method");
+});
+
+test("static method overriding", () => {
+ class Parent {
+ static method() {
+ return 3;
+ }
+ }
+
+ class Child extends Parent {
+ static method() {
+ return 10;
+ }
+ }
+
+ expect(Parent.method()).toBe(3);
+ expect(Child.method()).toBe(10);
+});
diff --git a/Libraries/LibJS/Tests/classes/class-strict-mode.js b/Libraries/LibJS/Tests/classes/class-strict-mode.js
new file mode 100644
index 0000000000..90ead4490f
--- /dev/null
+++ b/Libraries/LibJS/Tests/classes/class-strict-mode.js
@@ -0,0 +1,19 @@
+test("constructors are always strict mode", () => {
+ class A {
+ constructor() {
+ expect(isStrictMode()).toBeTrue();
+ }
+ }
+
+ new A();
+});
+
+test("methods are always strict mode", () => {
+ class A {
+ method() {
+ expect(isStrictMode()).toBeTrue();
+ }
+ }
+
+ new A().method();
+});