From c5bfca23517c66378c5a8e73334861c666299933 Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Mon, 12 Aug 2024 18:48:14 +0300 Subject: [PATCH] test: add test file for transform mode and feature testing --- test/snapshots/transform.test.js.snapshot | 20 +++ test/transform.test.js | 187 ++++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/test/snapshots/transform.test.js.snapshot b/test/snapshots/transform.test.js.snapshot index d3b585678..301f2ef4c 100644 --- a/test/snapshots/transform.test.js.snapshot +++ b/test/snapshots/transform.test.js.snapshot @@ -29,3 +29,23 @@ exports[`should perform transformation without source maps 1`] = ` exports[`should perform transformation without source maps and filename 1`] = ` "var Foo;\\n(function(Foo) {\\n Foo[Foo[\\"Bar\\"] = 7] = \\"Bar\\";\\n Foo[Foo[\\"Baz\\"] = 2] = \\"Baz\\";\\n})(Foo || (Foo = {}));\\nx = 7;\\n" `; + +exports[`should transform TypeScript class decorators with multiple decorators 1`] = ` +"@sealed\\n@log\\nclass BugReport {\\n type = \\"report\\";\\n title;\\n constructor(t){\\n this.title = t;\\n }\\n}\\nfunction sealed(constructor) {\\n Object.seal(constructor);\\n Object.seal(constructor.prototype);\\n}\\nfunction log(constructor) {\\n console.log(\\"Creating instance of\\", constructor.name);\\n}\\nconst report = new BugReport(\\"Test\\");\\n" +`; + +exports[`should transform TypeScript class fields 1`] = ` +"class Counter {\\n count = 0;\\n increment() {\\n this.count++;\\n }\\n}\\n" +`; + +exports[`should transform TypeScript namespaces with additional functionality 1`] = ` +"var Validation;\\n(function(Validation) {\\n const lettersRegexp = /^[A-Za-z]+$/;\\n class LettersOnlyValidator {\\n isAcceptable(s) {\\n return lettersRegexp.test(s);\\n }\\n static createValidator() {\\n return new LettersOnlyValidator();\\n }\\n }\\n Validation.LettersOnlyValidator = LettersOnlyValidator;\\n})(Validation || (Validation = {}));\\nconst validator = Validation.LettersOnlyValidator.createValidator();\\nconst isValid = validator.isAcceptable(\\"test\\");\\n// Exporting these for VM context\\nglobalThis.validator = validator;\\nglobalThis.isValid = isValid;\\n" +`; + +exports[`should transform TypeScript private class fields 1`] = ` +"class Counter {\\n #count = 0;\\n increment() {\\n this.#count++;\\n }\\n getCount() {\\n return this.#count;\\n }\\n}\\n" +`; + +exports[`should transform TypeScript type annotations and type guards 1`] = ` +"function isString(value) {\\n return typeof value === 'string';\\n}\\n" +`; diff --git a/test/transform.test.js b/test/transform.test.js index 5f7a32087..76d3c72f6 100644 --- a/test/transform.test.js +++ b/test/transform.test.js @@ -106,3 +106,190 @@ test("should perform transformation with error", (t) => { assert.strictEqual(context.x, 7); t.assert.snapshot(map); }); + +test("should transform TypeScript class fields", (t) => { + const inputCode = ` + class Counter { + count: number = 0; + increment() { + this.count++; + } + } + `; + const { code } = transformSync(inputCode, { + mode: "transform", + sourceMap: true, + }); + t.assert.snapshot(code); + + const context = { result: null }; + vm.createContext(context); + vm.runInContext( + ` + ${code} + const counter = new Counter(); + counter.increment(); + result = counter.count; + `, + context, + ); + assert.strictEqual(context.result, 1, "Counter should increment to 1"); +}); + +test("should transform TypeScript private class fields", (t) => { + const inputCode = ` + class Counter { + #count: number = 0; + increment() { + this.#count++; + } + getCount(): number { + return this.#count; + } + } + `; + const { code } = transformSync(inputCode, { + mode: "transform", + sourceMap: true, + }); + t.assert.snapshot(code); + + const context = { result: null }; + vm.createContext(context); + vm.runInContext( + ` + ${code} + const counter = new Counter(); + counter.increment(); + result = counter.getCount(); + `, + context, + ); + assert.strictEqual( + context.result, + 1, + "Counter private field should increment to 1", + ); +}); + +test("should transform TypeScript type annotations and type guards", (t) => { + const inputCode = ` + function isString(value: unknown): value is string { + return typeof value === 'string'; + } + `; + const { code } = transformSync(inputCode, { + mode: "transform", + sourceMaps: true, + }); + t.assert.snapshot(code); + + const context = { result: null }; + vm.createContext(context); + vm.runInContext( + ` + ${code} + const check = isString("hello"); + result = check; + `, + context, + ); + assert.strictEqual( + context.result, + true, + "Should recognize 'hello' as a string", + ); +}); + +test("should transform TypeScript class decorators with multiple decorators", (t) => { + const inputCode = ` + @sealed + @log + class BugReport { + type = "report"; + title: string; + constructor(t: string) { + this.title = t; + } + } + + function sealed(constructor: Function) { + Object.seal(constructor); + Object.seal(constructor.prototype); + } + + function log(constructor: Function) { + console.log("Creating instance of", constructor.name); + } + + const report = new BugReport("Test"); +`; + + const { code } = transformSync(inputCode, { + mode: "transform", + sourceMap: true, + }); + + t.assert.snapshot(code); + + try { + const script = new vm.Script(code); + const context = vm.createContext({}); + context.report = null; + script.runInContext(context); + + assert.ok(context.report, "Report instance should exist"); + assert.strictEqual( + context.report.type, + "report", + "Report type should be 'report'", + ); + assert.strictEqual( + context.report.title, + "Test", + "Report title should be 'Test'", + ); + } catch (err) { + console.error("Error executing script:", err); + } +}); + +test("should transform TypeScript namespaces with additional functionality", (t) => { + const inputCode = ` + namespace Validation { + export interface StringValidator { + isAcceptable(s: string): boolean; + } + const lettersRegexp = /^[A-Za-z]+$/; + export class LettersOnlyValidator implements StringValidator { + isAcceptable(s: string) { + return lettersRegexp.test(s); + } + static createValidator(): LettersOnlyValidator { + return new LettersOnlyValidator(); + } + } + } + + const validator = Validation.LettersOnlyValidator.createValidator(); + const isValid = validator.isAcceptable("test"); + + // Exporting these for VM context + (globalThis as any).validator = validator; + (globalThis as any).isValid = isValid; +`; + + const { code } = transformSync(inputCode, { + mode: "transform", + sourceMap: true, + }); + + t.assert.snapshot(code); + + const script = new vm.Script(code); + const context = vm.createContext({}); + script.runInContext(context); + + assert.ok(context.validator, "Validator instance should exist"); + assert.strictEqual(context.isValid, true, "String should be valid"); +});