aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2022-11-01 21:25:30 -0700
committerGravatar GitHub <noreply@github.com> 2022-11-01 21:25:30 -0700
commit54e7a6f57a80ac3a9e96348f0f796b219c958b73 (patch)
tree5155a900fb7d371a564c7e5c79d78f5518d19348 /test
parent32826119fc69feb07faa581bed0b3fd76a154744 (diff)
downloadbun-54e7a6f57a80ac3a9e96348f0f796b219c958b73.tar.gz
bun-54e7a6f57a80ac3a9e96348f0f796b219c958b73.tar.zst
bun-54e7a6f57a80ac3a9e96348f0f796b219c958b73.zip
typescript decorators round 2 (#1445)
* __decorateClass, __decorateParam, lit test, wiptest test * decorator factories test * parameter decorator test * test for decorators with constructor parameter properties * Fix issue with `CryptoKey` and `SubtleCrypto` constructors * Limit concurrency for GitHub Actions due to issues with cache poisoning If multiple actions are running, sometimes the cache is poisoned from another action. We need to fix this, but this is an interim measure to make actions less flaky * Make these tests better * Move this to dependencies so the .a files upload * Fixup * temporary fix * Limit concurrency for MacOS Object actions * try againn * againn * Make `"tls"` an explicit object we pass instead of implicit top-level options cc @Electroid @colinhacks * Update server.zig * Prefer `BUN_PORT` over `PORT` * Fix typo in homebrew action * Run homebrew action when release is edited * Check published_at instead of draft in GitHub action * Implement `process.release` * Add missing dependencies to `make devcontainer` * Allow overriding node polyfills via `BUN_OVERRIDE_MODULE_PATH` * Add a stub for io_darwin on linux cc @sno2 hopefully this helps but i'm not sure * Add missing `break` * Download more RAM * feat(core): optimize zig slice (#1408) * feat(core): optimize zig slice * address concerns * Remove webcrypto from vendor-without-check * Update default tsconfig (#1418) Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu> * Enable `BUN_OVERRIDE_MODULE_PATH` in `bun wiptest` * Upgrade WebKit * space * Support getting cached values and pending activity in the bindings generator * Remove :scissors: function * constructor creation, initializers, handle static and computed fields with decorators * Updating libuwebsockets C API (#1423) * fix for repeated regex match calls and fix for '^' in character classes (#1419) * tests and formatting * fix for ^ in character class * formatting * test for repeated match and exec calls * create oniguruma regex for each exec/test * check errorCode from creating oniguruma regexp and always return {} on failure * oops * call onig_initialize once * fix incorrect escaping, removed unnecessary oniguruma settings * tests for "-" and "^" in character classes * free regex object before returns * force gc for some tests * Update React fizz server (#1432) * Update fizz server * Use production build Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu> * more decorator tests * optional setup function for loading elements, simulate clicks in lit test * fix createWriteStream (#1433) * fix createWriteStream * remove comment * Update build docs and commands for dev containers (#1438) * Update build documentation for dev containers * Add devcontainer-rebuild make target * Add make devcontainer-sh target * Fix missing .PHONY for vendor-without-check (#1437) * Fix check for ninja on Debian/Ubuntu (#1436) Even though the package is named ninja-build, the ninja binary is still named ninja, so use `which ninja` to check for it * Fix #1410 woops * await on DOMContentLoaded for elements instead of setup function * avoid lowering class if no decorators Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Ashcon Partovi <ashcon@partovi.net> Co-authored-by: Carter Snook <cartersnook04@gmail.com> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu> Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com> Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Co-authored-by: Joรฃo Paquim <jpaquim@users.noreply.github.com>
Diffstat (limited to 'test')
-rw-r--r--test/bun.js/decorators.test.ts787
-rw-r--r--test/scripts/snippets.json1
-rw-r--r--test/snippets/package.json1
-rw-r--r--test/snippets/simple-lit-example.ts76
4 files changed, 865 insertions, 0 deletions
diff --git a/test/bun.js/decorators.test.ts b/test/bun.js/decorators.test.ts
new file mode 100644
index 000000000..545030ef1
--- /dev/null
+++ b/test/bun.js/decorators.test.ts
@@ -0,0 +1,787 @@
+import { test, expect } from "bun:test";
+
+test("decorator order of evaluation", () => {
+ let counter = 0;
+ const computedProp: unique symbol = Symbol("computedProp");
+
+ @decorator1
+ @decorator2
+ class BugReport {
+ @decorator7
+ type: string;
+
+ @decorator3
+ x: number = 20;
+
+ @decorator5
+ private _y: number = 12;
+
+ @decorator10
+ get y() {
+ return this._y;
+ }
+ @decorator11
+ set y(newY: number) {
+ this._y = newY;
+ }
+
+ @decorator9
+ [computedProp]: string = "yes";
+
+ constructor(@decorator8 type: string) {
+ this.type = type;
+ }
+
+ @decorator6
+ move(newX: number, @decorator12 newY: number) {
+ this.x = newX;
+ this._y = newY;
+ }
+
+ @decorator4
+ jump() {
+ this._y += 30;
+ }
+ }
+
+ function decorator1(target, propertyKey) {
+ expect(counter++).toBe(11);
+ expect(target === BugReport).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ }
+
+ function decorator2(target, propertyKey) {
+ expect(counter++).toBe(10);
+ expect(target === BugReport).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ }
+
+ function decorator3(target, propertyKey) {
+ expect(counter++).toBe(1);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("x");
+ }
+
+ function decorator4(target, propertyKey) {
+ expect(counter++).toBe(8);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("jump");
+ }
+
+ function decorator5(target, propertyKey) {
+ expect(counter++).toBe(2);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("_y");
+ }
+
+ function decorator6(target, propertyKey) {
+ expect(counter++).toBe(7);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("move");
+ }
+
+ function decorator7(target, propertyKey) {
+ expect(counter++).toBe(0);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("type");
+ }
+
+ function decorator8(target, propertyKey) {
+ expect(counter++).toBe(9);
+ expect(target === BugReport).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ }
+
+ function decorator9(target, propertyKey) {
+ expect(counter++).toBe(5);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe(computedProp);
+ }
+
+ function decorator10(target, propertyKey) {
+ expect(counter++).toBe(3);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("y");
+ }
+
+ function decorator11(target, propertyKey) {
+ expect(counter++).toBe(4);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("y");
+ }
+
+ function decorator12(target, propertyKey) {
+ expect(counter++).toBe(6);
+ expect(target === BugReport.prototype).toBe(true);
+ expect(propertyKey).toBe("move");
+ }
+});
+
+test("decorator factories order of evaluation", () => {
+ let counter = 0;
+ const computedProp: unique symbol = Symbol("computedProp");
+
+ @decorator1()
+ @decorator2()
+ class BugReport {
+ @decorator7()
+ type: string;
+
+ @decorator3()
+ x: number = 20;
+
+ @decorator5()
+ private _y: number = 12;
+
+ @decorator10()
+ get y() {
+ return this._y;
+ }
+ @decorator11()
+ set y(newY: number) {
+ this._y = newY;
+ }
+
+ @decorator9()
+ [computedProp]: string = "yes";
+
+ constructor(@decorator8() type: string) {
+ this.type = type;
+ }
+
+ @decorator6()
+ move(newX: number, @decorator12() newY: number) {
+ this.x = newX;
+ this._y = newY;
+ }
+
+ @decorator4()
+ jump() {
+ this._y += 30;
+ }
+ }
+
+ function decorator1() {
+ expect(counter++).toBe(18);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(23);
+ };
+ }
+
+ function decorator2() {
+ expect(counter++).toBe(19);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(22);
+ };
+ }
+
+ function decorator3() {
+ expect(counter++).toBe(2);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(3);
+ };
+ }
+
+ function decorator4() {
+ expect(counter++).toBe(16);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(17);
+ };
+ }
+
+ function decorator5() {
+ expect(counter++).toBe(4);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(5);
+ };
+ }
+
+ function decorator6() {
+ expect(counter++).toBe(12);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(15);
+ };
+ }
+
+ function decorator7() {
+ expect(counter++).toBe(0);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(1);
+ };
+ }
+
+ function decorator8() {
+ expect(counter++).toBe(20);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(21);
+ };
+ }
+
+ function decorator9() {
+ expect(counter++).toBe(10);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(11);
+ };
+ }
+
+ function decorator10() {
+ expect(counter++).toBe(6);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(7);
+ };
+ }
+
+ function decorator11() {
+ expect(counter++).toBe(8);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(9);
+ };
+ }
+
+ function decorator12() {
+ expect(counter++).toBe(13);
+ return function (target, descriptorKey) {
+ expect(counter++).toBe(14);
+ };
+ }
+});
+
+test("parameter decorators", () => {
+ let counter = 0;
+ class HappyDecorator {
+ width: number;
+ height: number;
+ x: number;
+ y: number;
+
+ move(@d4 x: number, @d5 @d6 y: number) {
+ this.x = x;
+ this.y = y;
+ }
+
+ constructor(
+ one: number,
+ two: string,
+ three: boolean,
+ @d1 @d2 width: number,
+ @d3 height: number
+ ) {
+ this.width = width;
+ this.height = height;
+ }
+
+ dance(@d7 @d8 intensity: number) {
+ this.width *= intensity;
+ this.height *= intensity;
+ }
+ }
+
+ function d1(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(7);
+ expect(target === HappyDecorator).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ expect(parameterIndex).toBe(3);
+ }
+
+ function d2(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(6);
+ expect(target === HappyDecorator).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ expect(parameterIndex).toBe(3);
+ }
+
+ function d3(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(5);
+ expect(target === HappyDecorator).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ expect(parameterIndex).toBe(4);
+ }
+
+ function d4(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(2);
+ expect(target === HappyDecorator.prototype).toBe(true);
+ expect(propertyKey).toBe("move");
+ expect(parameterIndex).toBe(0);
+ }
+
+ function d5(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(1);
+ expect(target === HappyDecorator.prototype).toBe(true);
+ expect(propertyKey).toBe("move");
+ expect(parameterIndex).toBe(1);
+ }
+
+ function d6(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(0);
+ expect(target === HappyDecorator.prototype).toBe(true);
+ expect(propertyKey).toBe("move");
+ expect(parameterIndex).toBe(1);
+ }
+
+ function d7(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(4);
+ expect(target === HappyDecorator.prototype).toBe(true);
+ expect(propertyKey).toBe("dance");
+ expect(parameterIndex).toBe(0);
+ }
+
+ function d8(target, propertyKey, parameterIndex) {
+ expect(counter++).toBe(3);
+ expect(target === HappyDecorator.prototype).toBe(true);
+ expect(propertyKey).toBe("dance");
+ expect(parameterIndex).toBe(0);
+ }
+
+ class Maybe {
+ constructor(
+ @m1 private x: number,
+ @m2 public y: boolean,
+ @m3 protected z: string
+ ) {}
+ }
+
+ function m1(target, propertyKey, index) {
+ expect(target === Maybe).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ expect(index).toBe(0);
+ }
+
+ function m2(target, propertyKey, index) {
+ expect(target === Maybe).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ expect(index).toBe(1);
+ }
+
+ function m3(target, propertyKey, index) {
+ expect(target === Maybe).toBe(true);
+ expect(propertyKey).toBe(undefined);
+ expect(index).toBe(2);
+ }
+});
+
+test("decorators random", () => {
+ @Frozen
+ class IceCream {}
+
+ function Frozen(constructor: Function) {
+ Object.freeze(constructor);
+ Object.freeze(constructor.prototype);
+ }
+
+ expect(Object.isFrozen(IceCream)).toBe(true);
+
+ class IceCreamComponent {
+ @Emoji()
+ flavor = "vanilla";
+ }
+
+ // Property Decorator
+ function Emoji() {
+ return function (target: Object, key: string | symbol) {
+ let val = target[key];
+
+ const getter = () => {
+ return val;
+ };
+ const setter = (next) => {
+ val = `๐Ÿฆ ${next} ๐Ÿฆ`;
+ };
+
+ Object.defineProperty(target, key, {
+ get: getter,
+ set: setter,
+ enumerable: true,
+ configurable: true,
+ });
+ };
+ }
+
+ const iceCream = new IceCreamComponent();
+ expect(iceCream.flavor === "๐Ÿฆ vanilla ๐Ÿฆ").toBe(true);
+ iceCream.flavor = "chocolate";
+ expect(iceCream.flavor === "๐Ÿฆ chocolate ๐Ÿฆ").toBe(true);
+
+ const i: unique symbol = Symbol.for("i");
+ const h: unique symbol = Symbol.for("h");
+ const t: unique symbol = Symbol.for("t");
+ const q: unique symbol = Symbol.for("q");
+ const p: unique symbol = Symbol.for("p");
+ const u3: unique symbol = Symbol.for("u3");
+ const u5: unique symbol = Symbol.for("u5");
+ const u6: unique symbol = Symbol.for("u6");
+ const u8: unique symbol = Symbol.for("u8");
+
+ class S {
+ @StringAppender("๐Ÿ˜›") k = 35;
+ @StringAppender("๐Ÿค ") static j = 4;
+ @StringAppender("๐Ÿ˜ตโ€๐Ÿ’ซ") private static [h] = 30;
+ @StringAppender("๐Ÿคฏ") private static u = 60;
+ @StringAppender("๐Ÿคช") private [t] = 32;
+ @StringAppender("๐Ÿค‘") [i] = 8;
+ @StringAppender("๐ŸŽƒ") private e = 10;
+ @StringAppender("๐Ÿ‘ป") static [q] = 202;
+ @StringAppender("๐Ÿ˜‡") r = S[h];
+ _y: number;
+ @StringAppender("๐Ÿคก") get y() {
+ return this._y;
+ }
+ set y(next) {
+ this._y = next;
+ }
+ #o = 100;
+
+ @StringAppender("๐Ÿ˜") u1: number;
+ @StringAppender("๐Ÿฅณ") static u2: number;
+ @StringAppender("๐Ÿค“") private static [u3]: number;
+ @StringAppender("๐Ÿฅบ") private static u4: number;
+ @StringAppender("๐Ÿคฏ") private [u5]: number;
+ @StringAppender("๐Ÿคฉ") [u6]: number;
+ @StringAppender("โ˜น๏ธ") private u7: number;
+ @StringAppender("๐Ÿ™ƒ") static [u8]: number;
+
+ @StringAppender("๐Ÿค”") u9 = this.u1;
+ @StringAppender("๐Ÿคจ") u10 = this.u2;
+ @StringAppender("๐Ÿ™‚") u11 = S[u3];
+ @StringAppender("๐Ÿ™") u12 = S.u4;
+ @StringAppender("๐Ÿ˜") u13 = this[u5];
+ @StringAppender("๐Ÿ˜‘") u14 = this[u6];
+ @StringAppender("๐Ÿ˜ถ") u15 = this.u7;
+ @StringAppender("๐Ÿ˜") u16 = S[u8];
+
+ constructor() {
+ this.k = 3;
+ expect(this.k).toBe("3 ๐Ÿ˜›");
+ expect(S.j).toBe(4);
+ expect(this[i]).toBe("8 ๐Ÿค‘");
+ expect(this.e).toBe("10 ๐ŸŽƒ");
+ expect(S[h]).toBe(30);
+ expect(S.u).toBe(60);
+ expect(this[t]).toBe("32 ๐Ÿคช");
+ expect(S[q]).toBe(202);
+ expect(this.#o).toBe(100);
+ expect(this.r).toBe("30 ๐Ÿ˜‡");
+ expect(this.y).toBe(undefined);
+ this.y = 100;
+ expect(this.y).toBe(100);
+
+ expect(this.u1).toBe(undefined);
+ expect(S.u2).toBe(undefined);
+ expect(S[u3]).toBe(undefined);
+ expect(S.u4).toBe(undefined);
+ expect(this[u5]).toBe(undefined);
+ expect(this[u6]).toBe(undefined);
+ expect(this.u7).toBe(undefined);
+ expect(S[u8]).toBe(undefined);
+
+ expect(this.u9).toBe("undefined ๐Ÿค”");
+ expect(this.u10).toBe("undefined ๐Ÿคจ");
+ expect(this.u11).toBe("undefined ๐Ÿ™‚");
+ expect(this.u12).toBe("undefined ๐Ÿ™");
+ expect(this.u13).toBe("undefined ๐Ÿ˜");
+ expect(this.u14).toBe("undefined ๐Ÿ˜‘");
+ expect(this.u15).toBe("undefined ๐Ÿ˜ถ");
+ expect(this.u16).toBe("undefined ๐Ÿ˜");
+
+ this.u1 = 100;
+ expect(this.u1).toBe("100 ๐Ÿ˜");
+ S.u2 = 100;
+ expect(S.u2).toBe("100 ๐Ÿฅณ");
+ S[u3] = 100;
+ expect(S[u3]).toBe("100 ๐Ÿค“");
+ S.u4 = 100;
+ expect(S.u4).toBe("100 ๐Ÿฅบ");
+ this[u5] = 100;
+ expect(this[u5]).toBe("100 ๐Ÿคฏ");
+ this[u6] = 100;
+ expect(this[u6]).toBe("100 ๐Ÿคฉ");
+ this.u7 = 100;
+ expect(this.u7).toBe("100 โ˜น๏ธ");
+ S[u8] = 100;
+ expect(S[u8]).toBe("100 ๐Ÿ™ƒ");
+
+ expect(this.u9).toBe("undefined ๐Ÿค”");
+ expect(this.u10).toBe("undefined ๐Ÿคจ");
+ expect(this.u11).toBe("undefined ๐Ÿ™‚");
+ expect(this.u12).toBe("undefined ๐Ÿ™");
+ expect(this.u13).toBe("undefined ๐Ÿ˜");
+ expect(this.u14).toBe("undefined ๐Ÿ˜‘");
+ expect(this.u15).toBe("undefined ๐Ÿ˜ถ");
+ expect(this.u16).toBe("undefined ๐Ÿ˜");
+ }
+ }
+
+ let s = new S();
+ expect(s.u9).toBe("undefined ๐Ÿค”");
+ expect(s.u10).toBe("undefined ๐Ÿคจ");
+ expect(s.u11).toBe("undefined ๐Ÿ™‚");
+ expect(s.u12).toBe("undefined ๐Ÿ™");
+ expect(s.u13).toBe("undefined ๐Ÿ˜");
+ expect(s.u14).toBe("undefined ๐Ÿ˜‘");
+ expect(s.u15).toBe("undefined ๐Ÿ˜ถ");
+ expect(s.u16).toBe("undefined ๐Ÿ˜");
+
+ s.u9 = 35;
+ expect(s.u9).toBe("35 ๐Ÿค”");
+ s.u10 = 36;
+ expect(s.u10).toBe("36 ๐Ÿคจ");
+ s.u11 = 37;
+ expect(s.u11).toBe("37 ๐Ÿ™‚");
+ s.u12 = 38;
+ expect(s.u12).toBe("38 ๐Ÿ™");
+ s.u13 = 39;
+ expect(s.u13).toBe("39 ๐Ÿ˜");
+ s.u14 = 40;
+ expect(s.u14).toBe("40 ๐Ÿ˜‘");
+ s.u15 = 41;
+ expect(s.u15).toBe("41 ๐Ÿ˜ถ");
+ s.u16 = 42;
+ expect(s.u16).toBe("42 ๐Ÿ˜");
+
+ function StringAppender(emoji: string) {
+ return function (target: Object, key: string | symbol) {
+ let val = target[key];
+
+ const getter = () => {
+ return val;
+ };
+ const setter = (value) => {
+ val = `${value} ${emoji}`;
+ };
+
+ Object.defineProperty(target, key, {
+ get: getter,
+ set: setter,
+ enumerable: true,
+ configurable: true,
+ });
+ };
+ }
+});
+
+test("class field order", () => {
+ class N {
+ l = 455;
+ }
+ class M {
+ u = 4;
+ @d1 w = 9;
+ constructor() {
+ // this.w = 9 should be moved here
+ expect(this.u).toBe(4);
+ expect(this.w).toBe(9);
+ this.u = 3;
+ this.w = 6;
+ expect(this.u).toBe(3);
+ expect(this.w).toBe(6);
+ }
+ }
+
+ function d1(target, propertyKey) {
+ expect(target === M.prototype).toBe(true);
+ expect(propertyKey).toBe("w");
+ }
+
+ let m = new M();
+ expect(m.u).toBe(3);
+ expect(m.w).toBe(6);
+});
+
+test("changing static method", () => {
+ class A {
+ static bar() {
+ return 1;
+ }
+ }
+
+ @changeMethodReturn("bar", 5)
+ class A_2 {
+ static bar() {
+ return 7;
+ }
+ }
+
+ function changeMethodReturn(method, value) {
+ return function (target) {
+ target[method] = function () {
+ return value;
+ };
+ return target;
+ };
+ }
+
+ @changeMethodReturn("bar", 2)
+ class B extends A {}
+
+ @changeMethodReturn("bar", 9)
+ class C extends B {}
+
+ expect(A_2.bar()).toBe(5);
+ expect(A.bar()).toBe(1);
+ expect(B.bar()).toBe(2);
+ expect(C.bar()).toBe(9);
+});
+
+test("class extending from another class", () => {
+ class A {
+ a: number;
+ constructor() {
+ this.a = 3;
+ }
+ }
+
+ class B extends A {
+ a: number = 9;
+ }
+
+ expect(new A().a).toBe(3);
+ expect(new B().a).toBe(9);
+
+ class C {
+ a: number = 80;
+ }
+
+ class D extends C {
+ a: number = 32;
+ constructor() {
+ super();
+ }
+ }
+
+ expect(new C().a).toBe(80);
+ expect(new D().a).toBe(32);
+
+ class E {
+ a: number = 40;
+ constructor() {
+ expect(this.a).toBe(40);
+ }
+ }
+
+ class F extends E {
+ @d1 a: number = 50;
+ constructor() {
+ super();
+ expect(this.a).toBe(50);
+ this.a = 60;
+ expect(this.a).toBe(60);
+ }
+ }
+
+ function d1(target) {
+ target.a = 100;
+ }
+});
+
+test("decorated fields moving to constructor", () => {
+ class A {
+ @d1 a = 3;
+ @d2 b = 4;
+ @d3 c = 5;
+ }
+
+ function d1(target, propertyKey) {
+ expect(target === A.prototype).toBe(true);
+ expect(propertyKey).toBe("a");
+ }
+
+ function d2(target, propertyKey) {
+ expect(target === A.prototype).toBe(true);
+ expect(propertyKey).toBe("b");
+ }
+
+ function d3(target, propertyKey) {
+ expect(target === A.prototype).toBe(true);
+ expect(propertyKey).toBe("c");
+ }
+
+ let a = new A();
+ expect(a.a).toBe(3);
+ expect(a.b).toBe(4);
+ expect(a.c).toBe(5);
+});
+
+test("only class decorator", () => {
+ let a = 0;
+ @d1
+ class A {}
+
+ let aa = new A();
+
+ function d1(target) {
+ a = 1;
+ expect(target).toBe(A);
+ }
+
+ expect(a).toBe(1);
+});
+
+test("only property decorators", () => {
+ let a = 0;
+ class A {
+ @d1 a() {}
+ }
+
+ let b = 0;
+ class B {
+ @d2 b = 3;
+ }
+
+ let c = 0;
+ class C {
+ @d3 get c() {
+ return 3;
+ }
+ }
+
+ function d1(target, propertyKey) {
+ a = 1;
+ expect(target === A.prototype).toBe(true);
+ expect(propertyKey).toBe("a");
+ }
+ expect(a).toBe(1);
+
+ function d2(target, propertyKey) {
+ b = 1;
+ expect(target === B.prototype).toBe(true);
+ expect(propertyKey).toBe("b");
+ }
+ expect(b).toBe(1);
+
+ function d3(target, propertyKey) {
+ c = 1;
+ expect(target === C.prototype).toBe(true);
+ expect(propertyKey).toBe("c");
+ }
+ expect(c).toBe(1);
+});
+
+test("only argument decorators", () => {
+ let a = 0;
+ class A {
+ a(@d1 a: string) {}
+ }
+
+ function d1(target, propertyKey, parameterIndex) {
+ a = 1;
+ expect(target === A.prototype).toBe(true);
+ expect(propertyKey).toBe("a");
+ expect(parameterIndex).toBe(0);
+ }
+
+ expect(a).toBe(1);
+});
+
+test("no decorators", () => {
+ let a = 0;
+ class A {
+ b: number;
+ constructor() {
+ a = 1;
+ this.b = 300000;
+ }
+ }
+
+ let aa = new A();
+ expect(a).toBe(1);
+ expect(aa.b).toBe(300000);
+});
diff --git a/test/scripts/snippets.json b/test/scripts/snippets.json
index ebdec23d3..1829eb9c4 100644
--- a/test/scripts/snippets.json
+++ b/test/scripts/snippets.json
@@ -25,6 +25,7 @@
"/optional-chain-with-function.js",
"/template-literal.js",
"/number-literal-bug.js",
+ "/simple-lit-example.ts",
"/caught-require.js",
"/package-json-utf8.js",
"/multiple-var.js",
diff --git a/test/snippets/package.json b/test/snippets/package.json
index 07b349f86..0c05b97be 100644
--- a/test/snippets/package.json
+++ b/test/snippets/package.json
@@ -4,6 +4,7 @@
"dependencies": {
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.4.1",
+ "lit": "^2.4.0",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
diff --git a/test/snippets/simple-lit-example.ts b/test/snippets/simple-lit-example.ts
new file mode 100644
index 000000000..34446e418
--- /dev/null
+++ b/test/snippets/simple-lit-example.ts
@@ -0,0 +1,76 @@
+import { LitElement, html, css } from "lit";
+import { customElement, property, eventOptions } from "lit/decorators.js";
+
+var loadedResolve;
+var loadedPromise = new Promise((resolve) => {
+ loadedResolve = resolve;
+});
+
+if (document?.readyState === "loading") {
+ document.addEventListener(
+ "DOMContentLoaded",
+ () => {
+ loadedResolve();
+ },
+ { once: true }
+ );
+} else {
+ loadedResolve();
+}
+
+@customElement("my-element")
+export class MyElement extends LitElement {
+ static styles = css`
+ :host {
+ display: inline-block;
+ padding: 10px;
+ background: lightgray;
+ }
+ .planet {
+ color: var(--planet-color, blue);
+ }
+ `;
+
+ @property() planet = "Earth";
+
+ render() {
+ return html`
+ <span @click=${this.togglePlanet} class="planet" id="planet-id"
+ >${this.planet}</span
+ >
+ `;
+ }
+
+ @eventOptions({ once: true })
+ togglePlanet() {
+ this.planet = this.planet === "Earth" ? "Mars" : "Earth";
+ }
+}
+
+function setup() {
+ let element = document.createElement("my-element");
+ element.id = "my-element-id";
+ document.body.appendChild(element);
+}
+
+export async function test() {
+ setup();
+ await loadedPromise;
+
+ let element = document.getElementById("my-element-id");
+ let shadowRoot = element.shadowRoot;
+ let planet = shadowRoot.getElementById("planet-id");
+ if (element.__planet !== "Earth") {
+ throw new Error("Unexpected planet name: " + element.__planet);
+ }
+ planet.click();
+ if (element.__planet !== "Mars") {
+ throw new Error("Unexpected planet name: " + element.__planet);
+ }
+ planet.click();
+ if (element.__planet !== "Mars") {
+ throw new Error("Unexpected planet name: " + element.__planet);
+ }
+
+ return testDone(import.meta.url);
+}