From e9040135760bb10f6932be04b8d5a99f6a8ee1ff Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 28 May 2019 12:40:29 +0500 Subject: [PATCH 01/64] Add new test builder and convert few tests --- test/tsconfig.json | 1 + test/unit/classDecorator.spec.ts | 263 +++++++++++++------------- test/unit/loops.spec.ts | 61 +++--- test/unit/require.spec.ts | 25 ++- test/util.ts | 306 +++++++++++++++++++++++++++++++ tslint.json | 1 - 6 files changed, 474 insertions(+), 183 deletions(-) diff --git a/test/tsconfig.json b/test/tsconfig.json index b22f71bc9..700c9d291 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -8,6 +8,7 @@ "target": "es2017", "lib": ["es2017"], "types": ["node", "jest"], + "experimentalDecorators": true, "noEmit": true, "module": "commonjs" diff --git a/test/unit/classDecorator.spec.ts b/test/unit/classDecorator.spec.ts index 8e9eff3d7..70edb6c06 100644 --- a/test/unit/classDecorator.spec.ts +++ b/test/unit/classDecorator.spec.ts @@ -2,198 +2,181 @@ import * as util from "../util"; import { TSTLErrors } from "../../src/TSTLErrors"; test("Class decorator with no parameters", () => { - const source = ` - function SetBool(constructor: T) { - return class extends constructor { - decoratorBool = true; + util.fn` + function SetBool(constructor: T) { + return class extends constructor { + decoratorBool = true; + } } - } - - @SetBool - class TestClass { - public decoratorBool = false; - } - const classInstance = new TestClass(); - return classInstance.decoratorBool; - `; + @SetBool + class TestClass { + public decoratorBool = false; + } - const result = util.transpileAndExecute(source); - expect(result).toBe(true); + const classInstance = new TestClass(); + return classInstance.decoratorBool; + `.expectToMatchJsResult(); }); test("Class decorator with parameters", () => { - const source = ` - function SetNum(numArg: number) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNum = numArg; + util.fn` + function SetNum(numArg: number) { + return {}>(constructor: T) => { + return class extends constructor { + decoratorNum = numArg; + }; }; - }; - } - - @SetNum(420) - class TestClass { - public decoratorNum; - } + } - const classInstance = new TestClass(); - return classInstance.decoratorNum; - `; + @SetNum(420) + class TestClass { + public decoratorNum; + } - const result = util.transpileAndExecute(source); - expect(result).toBe(420); + const classInstance = new TestClass(); + return classInstance.decoratorNum; + `.expectToMatchJsResult(); }); test("Class decorator with variable parameters", () => { - const source = ` - function SetNumbers(...numArgs: number[]) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNums = new Set(numArgs); + util.fn` + function SetNumbers(...numArgs: number[]) { + return {}>(constructor: T) => { + return class extends constructor { + decoratorNums = new Set(numArgs); + }; }; - }; - } - - @SetNumbers(120, 30, 54) - class TestClass { - public decoratorNums; - } - - const classInstance = new TestClass(); - let sum = 0; - for (const value of classInstance.decoratorNums) { - sum += value; - } - return sum; - `; + } + + @SetNumbers(120, 30, 54) + class TestClass { + public decoratorNums; + } - const result = util.transpileAndExecute(source); - expect(result).toBe(204); + const classInstance = new TestClass(); + let sum = 0; + for (const value of classInstance.decoratorNums) { + sum += value; + } + return sum; + `.expectToMatchJsResult(); }); test("Multiple class decorators", () => { - const source = ` - function SetTen(constructor: T) { - return class extends constructor { - decoratorTen = 10; + util.fn` + function SetTen(constructor: T) { + return class extends constructor { + decoratorTen = 10; + } } - } - function SetNum(numArg: number) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNum = numArg; + function SetNum(numArg: number) { + return {}>(constructor: T) => { + return class extends constructor { + decoratorNum = numArg; + }; }; - }; - } - - @SetTen - @SetNum(410) - class TestClass { - public decoratorTen; - public decoratorNum; - } - - const classInstance = new TestClass(); - return classInstance.decoratorNum + classInstance.decoratorTen; - `; + } + + @SetTen + @SetNum(410) + class TestClass { + public decoratorTen; + public decoratorNum; + } - const result = util.transpileAndExecute(source); - expect(result).toBe(420); + const classInstance = new TestClass(); + return classInstance.decoratorNum + classInstance.decoratorTen; + `.expectToMatchJsResult(); }); test("Class decorator with inheritance", () => { - const source = ` - function SetTen(constructor: T) { - return class extends constructor { - decoratorTen = 10; + util.fn` + function SetTen(constructor: T) { + return class extends constructor { + decoratorTen = 10; + } } - } - function SetNum(numArg: number) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNum = numArg; + function SetNum(numArg: number) { + return {}>(constructor: T) => { + return class extends constructor { + decoratorNum = numArg; + }; }; - }; - } - - class TestClass { - public decoratorTen = 0; - public decoratorNum = 0; - } + } - @SetTen - @SetNum(410) - class SubTestClass extends TestClass {} + class TestClass { + public decoratorTen = 0; + public decoratorNum = 0; + } - const classInstance = new SubTestClass(); - return classInstance.decoratorNum + classInstance.decoratorTen; - `; + @SetTen + @SetNum(410) + class SubTestClass extends TestClass {} - const result = util.transpileAndExecute(source); - expect(result).toBe(420); + const classInstance = new SubTestClass(); + return classInstance.decoratorNum + classInstance.decoratorTen; + `.expectToMatchJsResult(); }); test("Class decorators are applied in order and executed in reverse order", () => { - const source = ` - const order = []; - - function SetString(stringArg: string) { - order.push("eval " + stringArg); - return {}>(constructor: T) => { - order.push("execute " + stringArg); - return class extends constructor { - decoratorString = stringArg; + util.fn` + const order = []; + + function SetString(stringArg: string) { + order.push("eval " + stringArg); + return {}>(constructor: T) => { + order.push("execute " + stringArg); + return class extends constructor { + decoratorString = stringArg; + }; }; - }; - } - - @SetString("fox") - @SetString("jumped") - @SetString("over dog") - class TestClass { - public static decoratorString = ""; - } - - const inst = new TestClass(); - return order.join(" "); - `; + } - const result = util.transpileAndExecute(source); - expect(result).toBe("eval fox eval jumped eval over dog execute over dog execute jumped execute fox"); + @SetString("fox") + @SetString("jumped") + @SetString("over dog") + class TestClass { + public static decoratorString = ""; + } + + const inst = new TestClass(); + return order.join(" "); + `.expectToMatchJsResult(); }); test("Throws error if decorator function has void context", () => { const source = ` - function SetBool(this: void, constructor: T) { - return class extends constructor { - decoratorBool = true; + function SetBool(this: void, constructor: T) { + return class extends constructor { + decoratorBool = true; + } } - } - @SetBool - class TestClass { - public decoratorBool = false; - } + @SetBool + class TestClass { + public decoratorBool = false; + } - const classInstance = new TestClass(); - return classInstance.decoratorBool; + const classInstance = new TestClass(); + return classInstance.decoratorBool; `; expect(() => util.transpileAndExecute(source)).toThrowExactError(TSTLErrors.InvalidDecoratorContext(util.nodeStub)); }); test("Exported class decorator", () => { - const code = ` + util.mod` function decorator(c: T): T { c.bar = "foobar"; return c; } @decorator - export class Foo {}`; - - expect(util.transpileExecuteAndReturnExport(code, "Foo.bar")).toBe("foobar"); + export class Foo {} + ` + .export("Foo.bar") + .expectToMatchJsResult(); }); diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 6c2bae455..f5170f032 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -170,53 +170,48 @@ test.each([{ inp: [0, 1, 2, 3], expected: [1, 2, 3, 4] }])("forNoCondition (%p)" expect(result).toBe(JSON.stringify(expected)); }); -test.each([{ inp: [0, 1, 2, 3], expected: [1, 2, 3, 4] }])("forNoPostExpression (%p)", ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let arrTest = ${JSON.stringify(inp)}; - let i = 0; - for (;;) { - if (i >= arrTest.length) { - break; - } - - arrTest[i] = arrTest[i] + 1; - - i++; +test("forNoPostExpression (%p)", () => { + util.fn` + let arrTest = [0, 1, 2, 3]; + let i = 0; + for (;;) { + if (i >= arrTest.length) { + break; } - return JSONStringify(arrTest);` - ); - expect(result).toBe(JSON.stringify(expected)); + arrTest[i] = arrTest[i] + 1; + + i++; + } + return arrTest; + `.expectToMatchJsResult(); }); test.each([ - { inp: [0, 1, 2, 3], expected: [1, 2, 3, 4], header: "let i = 0; i < arrTest.length; i++" }, - { inp: [0, 1, 2, 3], expected: [1, 2, 3, 4], header: "let i = 0; i <= arrTest.length - 1; i++" }, - { inp: [0, 1, 2, 3], expected: [1, 2, 3, 4], header: "let i = 0; arrTest.length > i; i++" }, - { inp: [0, 1, 2, 3], expected: [1, 2, 3, 4], header: "let i = 0; arrTest.length - 1 >= i; i++" }, - { inp: [0, 1, 2, 3], expected: [1, 1, 3, 3], header: "let i = 0; i < arrTest.length; i += 2" }, - { inp: [0, 1, 2, 3], expected: [1, 2, 3, 4], header: "let i = arrTest.length - 1; i >= 0; i--" }, - { inp: [0, 1, 2, 3], expected: [0, 2, 2, 4], header: "let i = arrTest.length - 1; i >= 0; i -= 2" }, - { inp: [0, 1, 2, 3], expected: [0, 2, 2, 4], header: "let i = arrTest.length - 1; i > 0; i -= 2" }, -])("forheader (%p)", ({ inp, expected, header }) => { - const result = util.transpileAndExecute( - `let arrTest = ${JSON.stringify(inp)}; + { inp: [0, 1, 2, 3], header: "let i = 0; i < arrTest.length; i++" }, + { inp: [0, 1, 2, 3], header: "let i = 0; i <= arrTest.length - 1; i++" }, + { inp: [0, 1, 2, 3], header: "let i = 0; arrTest.length > i; i++" }, + { inp: [0, 1, 2, 3], header: "let i = 0; arrTest.length - 1 >= i; i++" }, + { inp: [0, 1, 2, 3], header: "let i = 0; i < arrTest.length; i += 2" }, + { inp: [0, 1, 2, 3], header: "let i = arrTest.length - 1; i >= 0; i--" }, + { inp: [0, 1, 2, 3], header: "let i = arrTest.length - 1; i >= 0; i -= 2" }, + { inp: [0, 1, 2, 3], header: "let i = arrTest.length - 1; i > 0; i -= 2" }, +])("forheader (%p)", ({ inp, header }) => { + util.fn` + let arrTest = ${JSON.stringify(inp)}; for (${header}) { arrTest[i] = arrTest[i] + 1; } - return JSONStringify(arrTest);` - ); - - expect(result).toBe(JSON.stringify(expected)); + return arrTest; + `.expectToMatchJsResult(); }); test("for scope", () => { - const code = ` + util.fn` let i = 42; for (let i = 0; i < 10; ++i) {} return i; - `; - expect(util.transpileAndExecute(code)).toBe(42); + `.expectToMatchJsResult(); }); test.each([ diff --git a/test/unit/require.spec.ts b/test/unit/require.spec.ts index bb9903a1b..84ba40cf1 100644 --- a/test/unit/require.spec.ts +++ b/test/unit/require.spec.ts @@ -74,13 +74,18 @@ test.each([ ])( "require paths root from --baseUrl or --rootDir (%p)", ({ filePath, usedPath, expectedPath, options, throwsError }) => { - const input = { [filePath]: `import * as module from "${usedPath}"; module;` }; + const builder = util.mod` + import * as module from "${usedPath}"; + module; + `; + + builder.options(options).setMainFileName(filePath); + if (throwsError) { - expect(() => util.transpileString(input, options)).toThrow(); + builder.expectToHaveDiagnostics(); } else { - const lua = util.transpileString(input, options); const regex = /require\("(.*?)"\)/; - const match = regex.exec(lua); + const match = regex.exec(builder.getMainLuaCodeChunk()); if (util.expectToBeDefined(match)) { expect(match[1]).toBe(expectedPath); @@ -92,12 +97,14 @@ test.each([ test.each([{ comment: "", expectedPath: "src.fake" }, { comment: "/** @noResolution */", expectedPath: "fake" }])( "noResolution on ambient modules causes no path alterations (%p)", ({ comment, expectedPath }) => { - const lua = util.transpileString({ - "src/main.ts": `import * as fake from "fake"; fake;`, - "module.d.ts": `${comment} declare module "fake" {}`, - }); + const builder = util.mod` + import * as fake from "fake"; + fake; + `; + + builder.setMainFileName("src/main.ts").addExtraFile("module.d.ts", `${comment} declare module "fake" {}`); const regex = /require\("(.*?)"\)/; - const match = regex.exec(lua); + const match = regex.exec(builder.getMainLuaCodeChunk()); if (util.expectToBeDefined(match)) { expect(match[1]).toBe(expectedPath); diff --git a/test/util.ts b/test/util.ts index 3da13b395..5743430df 100644 --- a/test/util.ts +++ b/test/util.ts @@ -2,6 +2,8 @@ import { lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari"; import * as fs from "fs"; import * as path from "path"; import * as ts from "typescript"; +import * as vm from "vm"; +import * as prettyFormat from "pretty-format"; import * as tstl from "../src"; export const nodeStub = ts.createNode(ts.SyntaxKind.Unknown); @@ -160,3 +162,307 @@ export const valueToString = (value: unknown) => : JSON.stringify(value); export const valuesToString = (values: unknown[]) => values.map(valueToString).join(", "); + +interface TranspiledJsFile { + fileName: string; + js?: string; + sourceMap?: string; +} + +interface TranspileJsResult { + diagnostics: ts.Diagnostic[]; + transpiledFiles: TranspiledJsFile[]; +} + +function transpileJs(program: ts.Program): TranspileJsResult { + const transpiledFiles: TranspiledJsFile[] = []; + // TODO: Included in TS3.5 + type Omit = Pick>; + const updateTranspiledFile = (fileName: string, update: Omit) => { + const file = transpiledFiles.find(f => f.fileName === fileName); + if (file) { + Object.assign(file, update); + } else { + transpiledFiles.push({ fileName, ...update }); + } + }; + + const { diagnostics } = program.emit(undefined, (fileName, data, _bom, _onError, sourceFiles = []) => { + for (const sourceFile of sourceFiles) { + const isJs = fileName.endsWith(".js"); + const isSourceMap = fileName.endsWith(".js.map"); + if (isJs || isSourceMap) { + updateTranspiledFile(sourceFile.fileName, { js: data }); + } else if (isSourceMap) { + updateTranspiledFile(sourceFile.fileName, { sourceMap: data }); + } + } + }); + + return { transpiledFiles, diagnostics: [...diagnostics] }; +} + +const memoize: MethodDecorator = (_target, _propertyKey, descriptor) => { + const originalFunction = descriptor.value as any; + const memoized = new WeakMap(); + descriptor.value = function(this: any, ...args: any[]): any { + if (!memoized.has(this)) { + memoized.set(this, originalFunction.apply(this, args)); + } + + return memoized.get(this); + } as any; + return descriptor; +}; + +class ExecutionError extends Error { + public name = "ExecutionError"; + constructor(message: string) { + super(message); + } +} + +class TestBuilder { + protected _accessor = ""; + constructor(private template: TemplateStringsArray, private substitutions: any[]) {} + + // Options + + private _serialize = false; + public serialize(serialize = true): this { + expect(this._hasTsCode).toBe(false); + this._serialize = serialize; + return this; + } + + private _luaHeader = ""; + public luaHeader(luaHeader: string): this { + expect(this._hasTsCode).toBe(false); + this._luaHeader += luaHeader; + return this; + } + + private _semanticCheck = true; + public disableSemanticCheck(): this { + this._semanticCheck = false; + return this; + } + + private _options: tstl.CompilerOptions = { + luaTarget: tstl.LuaTarget.Lua53, + noHeader: true, + skipLibCheck: true, + target: ts.ScriptTarget.ESNext, + lib: ["lib.esnext.d.ts"], + experimentalDecorators: true, + }; + public options(options: tstl.CompilerOptions): this { + expect(this._hasTsCode).toBe(false); + Object.assign(this._options, options); + return this; + } + + protected _mainFileName = "main.ts"; + public setMainFileName(mainFileName: string): this { + this._mainFileName = mainFileName; + return this; + } + + private _extraFiles: Record = {}; + public addExtraFile(fileName: string, code: string): this { + expect(this._hasProgram).toBe(false); + this._extraFiles[fileName] = code; + return this; + } + + // Transpilation and execution + + private _hasTsCode = false; + @memoize + public getTsCode(): string { + this._hasTsCode = true; + const substitutions = this._serialize ? this.substitutions.map(valueToString) : this.substitutions; + + const templateString = this.template + .map((chunk, index) => (substitutions[index - 1] !== undefined ? substitutions[index - 1] : "") + chunk) + .join(""); + + return templateString; + } + + private _hasProgram = false; + @memoize + public getProgram(): ts.Program { + this._hasProgram = true; + return tstl.createVirtualProgram( + { ...this._extraFiles, [this._mainFileName]: this.getTsCode() }, + this._options + ); + } + + @memoize + public getLuaResult(): tstl.TranspileResult { + const program = this.getProgram(); + const result = tstl.transpile({ program }); + const diagnostics = ts.sortAndDeduplicateDiagnostics([ + ...ts.getPreEmitDiagnostics(program), + ...result.diagnostics, + ]); + + return { ...result, diagnostics: [...diagnostics] }; + } + + @memoize + public getMainLuaCodeChunk(): string { + const { transpiledFiles } = this.getLuaResult(); + const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); + expect(mainFile).toBeDefined(); + + return `return JSONStringify((function() + ${this._luaHeader} + ${mainFile!.lua!} +end)()${this._accessor})`; + } + + @memoize + private getLuaCodeWithWrapper(): string { + let code = this.getMainLuaCodeChunk(); + if (code.includes('require("lualib_bundle")')) { + code = `package.preload.lualib_bundle = function() + ${lualibContent} +end +${code}`; + } + + return minimalTestLib + code; + } + + @memoize + public getLuaExecutionResult(): any { + const code = this.getLuaCodeWithWrapper(); + const L = lauxlib.luaL_newstate(); + lualib.luaL_openlibs(L); + const status = lauxlib.luaL_dostring(L, to_luastring(code)); + + if (status === lua.LUA_OK) { + if (lua.lua_isstring(L, -1)) { + return JSON.parse(lua.lua_tojsstring(L, -1)); + } else { + const returnType = to_jsstring(lua.lua_typename(L, lua.lua_type(L, -1))); + throw new Error(`Unsupported Lua return type: ${returnType}`); + } + } else { + return new ExecutionError(to_jsstring(lua.lua_tostring(L, -1))); + } + } + + @memoize + public getJsResult(): TranspileJsResult { + const program = this.getProgram(); + program.getCompilerOptions().module = ts.ModuleKind.CommonJS; + return transpileJs(program); + } + + @memoize + protected getJsCode(): string { + const { transpiledFiles } = this.getJsResult(); + const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); + expect(mainFile).toBeDefined(); + return mainFile!.js! + `;exports = exports${this._accessor}`; + } + + @memoize + public getJsExecutionResult(): any { + const context = vm.createContext({ exports: {} }); + try { + return vm.runInContext(this.getJsCode(), context); + } catch (error) { + return new ExecutionError(error.message); + } + } + + // Utilities + + private getAllDiagnostics(): ts.Diagnostic[] { + const { diagnostics: luaDiagnostics } = this.getLuaResult(); + const { diagnostics: jsDiagnostics } = this.getJsResult(); + const allDiagnostics = [...ts.sortAndDeduplicateDiagnostics([...luaDiagnostics, ...jsDiagnostics])]; + return allDiagnostics.filter(diag => this._semanticCheck || diag.source === "typescript-to-lua"); + } + + // Actions + + public debug(): this { + const luaCode = this.getMainLuaCodeChunk().replace(/(^|\n)/g, "\n "); + const value = prettyFormat(this.getLuaExecutionResult()); + console.log(`Lua Code:${luaCode}\nValue: ${value}`); + return this; + } + + public expectToHaveDiagnostics(): this { + expect(this.getAllDiagnostics()).toHaveDiagnostics(); + return this; + } + + public expectToMatchJsResult(allowErrors = false): this { + expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + const luaResult = this.getLuaExecutionResult(); + const jsResult = this.getJsExecutionResult(); + expect(luaResult).toEqual(jsResult); + if (!allowErrors && luaResult instanceof ExecutionError) { + throw luaResult; + } + + return this; + } + + public expectToEqual(expected: any): this { + expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + const luaResult = this.getLuaExecutionResult(); + expect(luaResult).toEqual(expected); + return this; + } + + public expectLuaToMatchSnapshot(): this { + expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + expect(this.getMainLuaCodeChunk()).toMatchSnapshot(); + return this; + } + + public expectResultToMatchSnapshot(): this { + expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + expect(this.getLuaExecutionResult()).toMatchSnapshot(); + return this; + } +} + +class FunctionTestBuilder extends TestBuilder { + protected _accessor = ".main()"; + public getTsCode(): string { + return `export function main() {${super.getTsCode()}}`; + } +} + +class ModuleTestBuilder extends TestBuilder { + public export(name: string): this { + this._accessor = `.${name}`; + return this; + } +} + +const templateFromValue = (valueOrTemplate: any): TemplateStringsArray => + typeof valueOrTemplate === "string" + ? Object.assign([valueOrTemplate], { raw: [valueOrTemplate] }) + : valueOrTemplate; + +export function fn(value: string): FunctionTestBuilder; +export function fn(template: TemplateStringsArray, ...substitutions: any[]): FunctionTestBuilder; +export function fn(valueOrTemplate: any, ...substitutions: any[]): FunctionTestBuilder { + return new FunctionTestBuilder(templateFromValue(valueOrTemplate), substitutions); +} + +export function mod(value: string): ModuleTestBuilder; +export function mod(template: TemplateStringsArray, ...substitutions: any[]): ModuleTestBuilder; +export function mod(valueOrTemplate: any, ...substitutions: any[]): ModuleTestBuilder { + return new ModuleTestBuilder(templateFromValue(valueOrTemplate), substitutions); +} diff --git a/tslint.json b/tslint.json index 2bf168c59..be6ae2b58 100644 --- a/tslint.json +++ b/tslint.json @@ -22,7 +22,6 @@ "interface-name": [true, "never-prefix"], "jsdoc-format": true, "label-position": true, - "max-classes-per-file": [true, 1], "member-access": true, "no-angle-bracket-type-assertion": true, "no-any": false, From 28a8aa15b2a0fc441bc707f31b5bdab4c914eb29 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 28 May 2019 13:37:17 +0500 Subject: [PATCH 02/64] Rename fn and mod to testFunction and testModule --- test/unit/classDecorator.spec.ts | 14 +++++++------- test/unit/loops.spec.ts | 6 +++--- test/unit/require.spec.ts | 4 ++-- test/util.ts | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/unit/classDecorator.spec.ts b/test/unit/classDecorator.spec.ts index 70edb6c06..59396527a 100644 --- a/test/unit/classDecorator.spec.ts +++ b/test/unit/classDecorator.spec.ts @@ -2,7 +2,7 @@ import * as util from "../util"; import { TSTLErrors } from "../../src/TSTLErrors"; test("Class decorator with no parameters", () => { - util.fn` + util.testFunction` function SetBool(constructor: T) { return class extends constructor { decoratorBool = true; @@ -20,7 +20,7 @@ test("Class decorator with no parameters", () => { }); test("Class decorator with parameters", () => { - util.fn` + util.testFunction` function SetNum(numArg: number) { return {}>(constructor: T) => { return class extends constructor { @@ -40,7 +40,7 @@ test("Class decorator with parameters", () => { }); test("Class decorator with variable parameters", () => { - util.fn` + util.testFunction` function SetNumbers(...numArgs: number[]) { return {}>(constructor: T) => { return class extends constructor { @@ -64,7 +64,7 @@ test("Class decorator with variable parameters", () => { }); test("Multiple class decorators", () => { - util.fn` + util.testFunction` function SetTen(constructor: T) { return class extends constructor { decoratorTen = 10; @@ -92,7 +92,7 @@ test("Multiple class decorators", () => { }); test("Class decorator with inheritance", () => { - util.fn` + util.testFunction` function SetTen(constructor: T) { return class extends constructor { decoratorTen = 10; @@ -122,7 +122,7 @@ test("Class decorator with inheritance", () => { }); test("Class decorators are applied in order and executed in reverse order", () => { - util.fn` + util.testFunction` const order = []; function SetString(stringArg: string) { @@ -168,7 +168,7 @@ test("Throws error if decorator function has void context", () => { }); test("Exported class decorator", () => { - util.mod` + util.testModule` function decorator(c: T): T { c.bar = "foobar"; return c; diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index f5170f032..7dffa04ba 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -171,7 +171,7 @@ test.each([{ inp: [0, 1, 2, 3], expected: [1, 2, 3, 4] }])("forNoCondition (%p)" }); test("forNoPostExpression (%p)", () => { - util.fn` + util.testFunction` let arrTest = [0, 1, 2, 3]; let i = 0; for (;;) { @@ -197,7 +197,7 @@ test.each([ { inp: [0, 1, 2, 3], header: "let i = arrTest.length - 1; i >= 0; i -= 2" }, { inp: [0, 1, 2, 3], header: "let i = arrTest.length - 1; i > 0; i -= 2" }, ])("forheader (%p)", ({ inp, header }) => { - util.fn` + util.testFunction` let arrTest = ${JSON.stringify(inp)}; for (${header}) { arrTest[i] = arrTest[i] + 1; @@ -207,7 +207,7 @@ test.each([ }); test("for scope", () => { - util.fn` + util.testFunction` let i = 42; for (let i = 0; i < 10; ++i) {} return i; diff --git a/test/unit/require.spec.ts b/test/unit/require.spec.ts index 84ba40cf1..c52b72d7b 100644 --- a/test/unit/require.spec.ts +++ b/test/unit/require.spec.ts @@ -74,7 +74,7 @@ test.each([ ])( "require paths root from --baseUrl or --rootDir (%p)", ({ filePath, usedPath, expectedPath, options, throwsError }) => { - const builder = util.mod` + const builder = util.testModule` import * as module from "${usedPath}"; module; `; @@ -97,7 +97,7 @@ test.each([ test.each([{ comment: "", expectedPath: "src.fake" }, { comment: "/** @noResolution */", expectedPath: "fake" }])( "noResolution on ambient modules causes no path alterations (%p)", ({ comment, expectedPath }) => { - const builder = util.mod` + const builder = util.testModule` import * as fake from "fake"; fake; `; diff --git a/test/util.ts b/test/util.ts index 5743430df..7f2b301f9 100644 --- a/test/util.ts +++ b/test/util.ts @@ -455,14 +455,14 @@ const templateFromValue = (valueOrTemplate: any): TemplateStringsArray => ? Object.assign([valueOrTemplate], { raw: [valueOrTemplate] }) : valueOrTemplate; -export function fn(value: string): FunctionTestBuilder; -export function fn(template: TemplateStringsArray, ...substitutions: any[]): FunctionTestBuilder; -export function fn(valueOrTemplate: any, ...substitutions: any[]): FunctionTestBuilder { +export function testFunction(value: string): FunctionTestBuilder; +export function testFunction(template: TemplateStringsArray, ...substitutions: any[]): FunctionTestBuilder; +export function testFunction(valueOrTemplate: any, ...substitutions: any[]): FunctionTestBuilder { return new FunctionTestBuilder(templateFromValue(valueOrTemplate), substitutions); } -export function mod(value: string): ModuleTestBuilder; -export function mod(template: TemplateStringsArray, ...substitutions: any[]): ModuleTestBuilder; -export function mod(valueOrTemplate: any, ...substitutions: any[]): ModuleTestBuilder { +export function testModule(value: string): ModuleTestBuilder; +export function testModule(template: TemplateStringsArray, ...substitutions: any[]): ModuleTestBuilder; +export function testModule(valueOrTemplate: any, ...substitutions: any[]): ModuleTestBuilder { return new ModuleTestBuilder(templateFromValue(valueOrTemplate), substitutions); } From e14d06182798ecaa0505ef51910fcfc2c0bc9f5c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 28 May 2019 18:56:54 +0500 Subject: [PATCH 03/64] Transform some more tests --- test/unit/modules.spec.ts | 6 +- test/unit/tuples.spec.ts | 373 ++++++++++++++------------------- test/unit/typechecking.spec.ts | 111 ++++------ test/util.ts | 71 +++++-- 4 files changed, 255 insertions(+), 306 deletions(-) diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index c47409707..e6e7fcf86 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -55,9 +55,9 @@ test.each([ "export { x as default } from '...';", "export { default as x } from '...';", ])("Export default keyword disallowed (%p)", exportStatement => { - expect(() => util.transpileString(exportStatement)).toThrowExactError( - TSTLErrors.UnsupportedDefaultExport(util.nodeStub) - ); + util.testFunction(exportStatement) + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedDefaultExport(util.nodeStub)); }); test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌͼƛಠ", "_̀ः٠‿"])( diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index 0f6aa6a39..b37b520fd 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -1,205 +1,177 @@ import * as util from "../util"; test("Tuple loop", () => { - const result = util.transpileAndExecute( - `const tuple: [number, number, number] = [3,5,1]; + util.testFunction` + const tuple: [number, number, number] = [3,5,1]; let count = 0; for (const value of tuple) { count += value; } - return count;` - ); - - expect(result).toBe(9); + return count; + `.expectToMatchJsResult(); }); test("Tuple foreach", () => { - const result = util.transpileAndExecute( - `const tuple: [number, number, number] = [3,5,1]; + util.testFunction` + const tuple: [number, number, number] = [3,5,1]; let count = 0; tuple.forEach(v => count += v); - return count;` - ); - - expect(result).toBe(9); + return count; + `.expectToMatchJsResult(); }); test("Tuple access", () => { - const result = util.transpileAndExecute( - `const tuple: [number, number, number] = [3,5,1]; - return tuple[1];` - ); - - expect(result).toBe(5); + util.testFunction` + const tuple: [number, number, number] = [3,5,1]; + return tuple[1]; + `.expectToMatchJsResult(); }); test("Tuple union access", () => { - const result = util.transpileAndExecute( - `function makeTuple(): [number, number, number] | [string, string, string] { return [3,5,1]; } + util.testFunction` + function makeTuple(): [number, number, number] | [string, string, string] { return [3,5,1]; } const tuple = makeTuple(); - return tuple[1];` - ); - expect(result).toBe(5); + return tuple[1]; + `.expectToMatchJsResult(); }); test("Tuple intersection access", () => { - const result = util.transpileAndExecute( - `type I = [number, number, number] & {foo: string}; + util.testFunction` + type I = [number, number, number] & {foo: string}; function makeTuple(): I { let t = [3,5,1]; (t as I).foo = "bar"; return (t as I); } const tuple = makeTuple(); - return tuple[1];` - ); - expect(result).toBe(5); + return tuple[1]; + `.expectToMatchJsResult(); }); test("Tuple Destruct", () => { - const result = util.transpileAndExecute( - `function tuple(): [number, number, number] { return [3,5,1]; } + util.testFunction` + function tuple(): [number, number, number] { return [3,5,1]; } const [a,b,c] = tuple(); - return b;` - ); - - expect(result).toBe(5); + return b; + `.expectToMatchJsResult(); }); -test("Tuple Destruct Array Literal", () => { - const code = ` - const [a,b,c] = [3,5,1]; - return b;`; +const expectNoUnpack: util.TapCallback = b => expect(b.getMainLuaCodeChunk()).not.toContain("unpack"); - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); +test("Tuple Destruct Array Literal", () => { + util.testFunction` + const [a, b, c] = [3, 5, 1]; + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Destruct Array Literal Extra Values", () => { - const code = ` + util.testFunction` let result = ""; const set = () => { result = "bar"; }; const [a] = ["foo", set()]; - return a + result;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + return a + result; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple length", () => { - const result = util.transpileAndExecute( - `const tuple: [number, number, number] = [3,5,1]; - return tuple.length;` - ); - - expect(result).toBe(3); + util.testFunction` + const tuple: [number, number, number] = [3, 5, 1]; + return tuple.length; + `.expectToMatchJsResult(); }); test("Tuple Return Access", () => { - const code = ` + util.testFunction` /** @tupleReturn */ - function tuple(): [number, number, number] { return [3,5,1]; } - return tuple()[2];`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(1); + function tuple(): [number, number, number] { return [3, 5, 1]; } + return tuple()[2]; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return Destruct Declaration", () => { - const code = ` + util.testFunction` /** @tupleReturn */ function tuple(): [number, number, number] { return [3,5,1]; } const [a,b,c] = tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return Destruct Assignment", () => { - const code = ` + util.testFunction` /** @tupleReturn */ function tuple(): [number, number] { return [3,6]; } let [a,b] = [1,2]; [b,a] = tuple(); - return a - b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(3); + return a - b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Static Method Return Destruct", () => { - const code = ` + util.testFunction` class Test { /** @tupleReturn */ static tuple(): [number, number, number] { return [3,5,1]; } } const [a,b,c] = Test.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Static Function Property Return Destruct", () => { - const code = ` + util.testFunction` class Test { /** @tupleReturn */ static tuple: () => [number, number, number] = () => [3,5,1]; } const [a,b,c] = Test.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Non-Static Method Return Destruct", () => { - const code = ` + util.testFunction` class Test { /** @tupleReturn */ tuple(): [number, number, number] { return [3,5,1]; } } const t = new Test(); const [a,b,c] = t.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Non-Static Function Property Return Destruct", () => { - const code = ` + util.testFunction` class Test { /** @tupleReturn */ tuple: () => [number, number, number] = () => [3,5,1]; } const t = new Test(); const [a,b,c] = t.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Interface Method Return Destruct", () => { - const code = ` + util.testFunction` interface Test { /** @tupleReturn */ tuple(): [number, number, number]; @@ -208,16 +180,14 @@ test("Tuple Interface Method Return Destruct", () => { tuple() { return [3,5,1]; } }; const [a,b,c] = t.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Interface Function Property Return Destruct", () => { - const code = ` + util.testFunction` interface Test { /** @tupleReturn */ tuple: () => [number, number, number]; @@ -226,116 +196,100 @@ test("Tuple Interface Function Property Return Destruct", () => { tuple: () => [3,5,1] }; const [a,b,c] = t.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Object Literal Method Return Destruct", () => { - const code = ` + util.testFunction` const t = { /** @tupleReturn */ tuple() { return [3,5,1]; } }; const [a,b,c] = t.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Object Literal Function Property Return Destruct", () => { - const code = ` + util.testFunction` const t = { /** @tupleReturn */ tuple: () => [3,5,1] }; const [a,b,c] = t.tuple(); - return b;`; - - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(5); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Arrow Function", () => { - const code = ` + util.testFunction` const fn = /** @tupleReturn */ (s: string) => [s, "bar"]; const [a, b] = fn("foo"); return a + b; - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return Inference", () => { - const code = ` + util.testFunction` /** @tupleReturn */ interface Fn { (s: string): [string, string] } const fn: Fn = s => [s, "bar"]; const [a, b] = fn("foo"); return a + b; - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return Inference as Argument", () => { - const code = ` + util.testFunction` /** @tupleReturn */ interface Fn { (s: string): [string, string] } function foo(fn: Fn) { const [a, b] = fn("foo"); return a + b; } return foo(s => [s, "bar"]); - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return Inference as Elipsis Argument", () => { - const code = ` + util.testFunction` /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(a: number, ...fn: Fn[]) { + function foo(_: number, ...fn: Fn[]) { const [a, b] = fn[0]("foo"); return a + b; } - return foo(7, s => [s, "bar"]); - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + return foo(0, s => [s, "bar"]); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return Inference as Elipsis Tuple Argument", () => { - const code = ` + util.testFunction` /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(a: number, ...fn: [number, Fn]) { + function foo(_: number, ...fn: [number, Fn]) { const [a, b] = fn[1]("foo"); return a + b; } - return foo(7, 17, s => [s, "bar"]); - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + return foo(0, 0, s => [s, "bar"]); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return in Spread", () => { - const code = ` + util.testFunction` /** @tupleReturn */ function foo(): [string, string] { return ["foo", "bar"]; } @@ -343,56 +297,48 @@ test("Tuple Return in Spread", () => { return a + b; } return bar(...foo()); - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Type Alias", () => { - const code = ` + util.testFunction` /** @tupleReturn */ type Fn = () => [number, number]; const fn: Fn = () => [1, 2]; const [a, b] = fn(); return a + b; - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(3); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Interface", () => { - const code = ` + util.testFunction` /** @tupleReturn */ interface Fn { (): [number, number]; } const fn: Fn = () => [1, 2]; const [a, b] = fn(); return a + b; - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(3); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Interface Signature", () => { - const code = ` + util.testFunction` interface Fn { /** @tupleReturn */ (): [number, number]; } const fn: Fn = () => [1, 2]; const [a, b] = fn(); return a + b; - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe(3); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Overload", () => { - const code = ` + util.testFunction` function fn(a: number): number; /** @tupleReturn */ function fn(a: string, b: string): [string, string]; function fn(a: number | string, b?: string): number | [string, string] { @@ -405,15 +351,13 @@ test("Tuple Return on Overload", () => { const a = fn(3); const [b, c] = fn("foo", "bar"); return a + b + c - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("3foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Interface Overload", () => { - const code = ` + util.testFunction` interface Fn { (a: number): number; /** @tupleReturn */ (a: string, b: string): [string, string]; @@ -428,15 +372,13 @@ test("Tuple Return on Interface Overload", () => { const a = fn(3); const [b, c] = fn("foo", "bar"); return a + b + c - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("3foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return on Interface Method Overload", () => { - const code = ` + util.testFunction` interface Foo { foo(a: number): number; /** @tupleReturn */ foo(a: string, b: string): [string, string]; @@ -453,11 +395,9 @@ test("Tuple Return on Interface Method Overload", () => { const a = bar.foo(3); const [b, c] = bar.foo("foo", "bar"); return a + b + c - `; - const lua = util.transpileString(code); - expect(lua).not.toContain("unpack"); - const result = util.executeLua(lua); - expect(result).toBe("3foobar"); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); }); test("Tuple Return vs Non-Tuple Return Overload", () => { @@ -470,15 +410,16 @@ test("Tuple Return vs Non-Tuple Return Overload", () => { end end `; - const tsHeader = ` + + util.testModule` declare function fn(this: void, a: number): [number, number]; /** @tupleReturn */ declare function fn(this: void, a: string, b: string): [string, string]; - `; - const code = ` + const [a, b] = fn(3); const [c, d] = fn("foo", "bar"); - return (a + b) + c + d; - `; - const result = util.transpileAndExecute(code, undefined, luaHeader, tsHeader); - expect(result).toBe("7foobar"); + export const result = (a + b) + c + d; + ` + .luaHeader(luaHeader) + .export("result") + .expectToEqual("7foobar"); }); diff --git a/test/unit/typechecking.spec.ts b/test/unit/typechecking.spec.ts index 3d5014336..4f6dbf886 100644 --- a/test/unit/typechecking.spec.ts +++ b/test/unit/typechecking.spec.ts @@ -2,129 +2,108 @@ import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; test.each(["0", "30", "30_000", "30.00"])("typeof number (%p)", inp => { - const result = util.transpileAndExecute(`return typeof ${inp};`); - - expect(result).toBe("number"); + util.testExpression`typeof ${inp}`.expectToMatchJsResult(); }); test.each(['"abc"', "`abc`"])("typeof string (%p)", inp => { - const result = util.transpileAndExecute(`return typeof ${inp};`); - - expect(result).toBe("string"); + util.testExpression`typeof ${inp}`.expectToMatchJsResult(); }); test.each(["false", "true"])("typeof boolean (%p)", inp => { - const result = util.transpileAndExecute(`return typeof ${inp};`); - - expect(result).toBe("boolean"); + util.testExpression`typeof ${inp}`.expectToMatchJsResult(); }); test.each(["{}", "[]"])("typeof object literal (%p)", inp => { - const result = util.transpileAndExecute(`return typeof ${inp};`); - - expect(result).toBe("object"); + util.testExpression`typeof ${inp}`.expectToMatchJsResult(); }); test("typeof class instance", () => { - const result = util.transpileAndExecute(`class myClass {} let inst = new myClass(); return typeof inst;`); - - expect(result).toBe("object"); + util.testFunction` + class myClass {} + let inst = new myClass(); + return typeof inst; + `.expectToMatchJsResult(); }); test("typeof function", () => { - const result = util.transpileAndExecute(`return typeof (() => 3);`); - - expect(result).toBe("function"); + util.testExpression`typeof (() => 3)`.expectToMatchJsResult(); }); test.each(["null", "undefined"])("typeof undefined (%p)", inp => { - const result = util.transpileAndExecute(`return typeof ${inp};`); - - expect(result).toBe("nil"); + util.testExpression`typeof ${inp}`.expectToEqual("nil"); }); test("instanceof", () => { - const result = util.transpileAndExecute( - "class myClass {} let inst = new myClass(); return inst instanceof myClass;" - ); - - expect(result).toBe(true); + util.testFunction` + class myClass {} + let inst = new myClass(); + return inst instanceof myClass; + `.expectToMatchJsResult(); }); test("instanceof inheritance", () => { - const result = util.transpileAndExecute(` + util.testFunction` class myClass {} - class childClass extends myClass{} - let inst = new childClass(); return inst instanceof myClass; - `); - - expect(result).toBe(true); + class childClass extends myClass {} + let inst = new childClass(); + return inst instanceof myClass; + `.expectToMatchJsResult(); }); test("instanceof inheritance false", () => { - const result = util.transpileAndExecute(` + util.testFunction` class myClass {} - class childClass extends myClass{} - let inst = new myClass(); return inst instanceof childClass; - `); - - expect(result).toBe(false); + class childClass extends myClass {} + let inst = new myClass(); + return inst instanceof childClass; + `.expectToMatchJsResult(); }); test("{} instanceof Object", () => { - const result = util.transpileAndExecute("return {} instanceof Object;"); - - expect(result).toBe(true); + util.testExpression`{} instanceof Object`.expectToMatchJsResult(); }); test("function instanceof Object", () => { - const result = util.transpileAndExecute("return (() => {}) instanceof Object;"); - - expect(result).toBe(true); + util.testExpression`(() => {}) instanceof Object`.expectToMatchJsResult(); }); test("null instanceof Object", () => { - const result = util.transpileAndExecute("return (null as any) instanceof Object;"); - - expect(result).toBe(false); + util.testExpression`(null as any) instanceof Object`.expectToMatchJsResult(); }); test("instanceof undefined", () => { - expect(() => { - util.transpileAndExecute("return {} instanceof (undefined as any);"); - }).toThrow("Right-hand side of 'instanceof' is not an object"); + util.testExpression`{} instanceof (undefined as any)`.expectToMatchJsResult(true); }); test("null instanceof Class", () => { - const result = util.transpileAndExecute("class myClass {} return (null as any) instanceof myClass;"); - - expect(result).toBe(false); + util.testFunction` + class myClass {} + return (null as any) instanceof myClass; + `.expectToMatchJsResult(); }); test.each(["extension", "metaExtension"])("instanceof extension (%p)", extensionType => { - const code = ` + util.testModule` declare class A {} /** @${extensionType} **/ class B extends A {} declare const foo: any; const result = foo instanceof B; - `; - expect(() => util.transpileString(code)).toThrowExactError(TSTLErrors.InvalidInstanceOfExtension(util.nodeStub)); + `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidInstanceOfExtension(util.nodeStub)); }); test("instanceof export", () => { - const result = util.transpileExecuteAndReturnExport( - `export class myClass {} + util.testModule` + export class myClass {} let inst = new myClass(); - export const result = inst instanceof myClass;`, - "result" - ); - - expect(result).toBe(true); + export const result = inst instanceof myClass; + ` + .export("result") + .expectToMatchJsResult(); }); test("instanceof Symbol.hasInstance", () => { - const result = util.transpileAndExecute(` + util.testFunction` class myClass { static [Symbol.hasInstance]() { return false; @@ -136,7 +115,5 @@ test("instanceof Symbol.hasInstance", () => { myClass[Symbol.hasInstance] = () => true; const isInstanceNew = inst instanceof myClass; return isInstanceOld !== isInstanceNew; - `); - - expect(result).toBe(true); + `.expectToMatchJsResult(); }); diff --git a/test/util.ts b/test/util.ts index 7f2b301f9..b49331a79 100644 --- a/test/util.ts +++ b/test/util.ts @@ -222,7 +222,8 @@ class ExecutionError extends Error { } } -class TestBuilder { +export type TapCallback = (builder: TestBuilder) => void; +export class TestBuilder { protected _accessor = ""; constructor(private template: TemplateStringsArray, private substitutions: any[]) {} @@ -352,7 +353,8 @@ ${code}`; throw new Error(`Unsupported Lua return type: ${returnType}`); } } else { - return new ExecutionError(to_jsstring(lua.lua_tostring(L, -1))); + const message = to_jsstring(lua.lua_tostring(L, -1)).replace(/^\[string "--\.\.\."\]:\d+: /, ""); + return new ExecutionError(message); } } @@ -383,11 +385,9 @@ ${code}`; // Utilities - private getAllDiagnostics(): ts.Diagnostic[] { - const { diagnostics: luaDiagnostics } = this.getLuaResult(); - const { diagnostics: jsDiagnostics } = this.getJsResult(); - const allDiagnostics = [...ts.sortAndDeduplicateDiagnostics([...luaDiagnostics, ...jsDiagnostics])]; - return allDiagnostics.filter(diag => this._semanticCheck || diag.source === "typescript-to-lua"); + private getLuaDiagnostics(): ts.Diagnostic[] { + const { diagnostics } = this.getLuaResult(); + return diagnostics.filter(d => this._semanticCheck || d.source === "typescript-to-lua"); } // Actions @@ -400,12 +400,25 @@ ${code}`; } public expectToHaveDiagnostics(): this { - expect(this.getAllDiagnostics()).toHaveDiagnostics(); + expect(this.getLuaDiagnostics()).toHaveDiagnostics(); + return this; + } + + public expectToHaveDiagnosticOfError(error: tstl.TranspileError): this { + this.expectToHaveDiagnostics(); + expect(this.getLuaDiagnostics()).toHaveLength(1); + const firstDiagnostic = this.getLuaDiagnostics()[0]; + expect(firstDiagnostic).toMatchObject({ messageText: error.message }); + return this; + } + + public expectToHaveNoDiagnostics(): this { + expect(this.getLuaDiagnostics()).not.toHaveDiagnostics(); return this; } public expectToMatchJsResult(allowErrors = false): this { - expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + this.expectToHaveNoDiagnostics(); const luaResult = this.getLuaExecutionResult(); const jsResult = this.getJsExecutionResult(); expect(luaResult).toEqual(jsResult); @@ -417,23 +430,35 @@ ${code}`; } public expectToEqual(expected: any): this { - expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + this.expectToHaveNoDiagnostics(); const luaResult = this.getLuaExecutionResult(); expect(luaResult).toEqual(expected); return this; } public expectLuaToMatchSnapshot(): this { - expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + this.expectToHaveNoDiagnostics(); expect(this.getMainLuaCodeChunk()).toMatchSnapshot(); return this; } public expectResultToMatchSnapshot(): this { - expect(this.getAllDiagnostics()).not.toHaveDiagnostics(); + this.expectToHaveNoDiagnostics(); expect(this.getLuaExecutionResult()).toMatchSnapshot(); return this; } + + public tap(callback: TapCallback): this { + callback(this); + return this; + } +} + +class ModuleTestBuilder extends TestBuilder { + public export(name: string): this { + this._accessor = `.${name}`; + return this; + } } class FunctionTestBuilder extends TestBuilder { @@ -443,10 +468,10 @@ class FunctionTestBuilder extends TestBuilder { } } -class ModuleTestBuilder extends TestBuilder { - public export(name: string): this { - this._accessor = `.${name}`; - return this; +class ExpressionTestBuilder extends TestBuilder { + protected _accessor = ".main()"; + public getTsCode(): string { + return `export function main() {return ${super.getTsCode()};}`; } } @@ -455,14 +480,20 @@ const templateFromValue = (valueOrTemplate: any): TemplateStringsArray => ? Object.assign([valueOrTemplate], { raw: [valueOrTemplate] }) : valueOrTemplate; +export function testModule(value: string): ModuleTestBuilder; +export function testModule(template: TemplateStringsArray, ...substitutions: any[]): ModuleTestBuilder; +export function testModule(valueOrTemplate: any, ...substitutions: any[]): ModuleTestBuilder { + return new ModuleTestBuilder(templateFromValue(valueOrTemplate), substitutions); +} + export function testFunction(value: string): FunctionTestBuilder; export function testFunction(template: TemplateStringsArray, ...substitutions: any[]): FunctionTestBuilder; export function testFunction(valueOrTemplate: any, ...substitutions: any[]): FunctionTestBuilder { return new FunctionTestBuilder(templateFromValue(valueOrTemplate), substitutions); } -export function testModule(value: string): ModuleTestBuilder; -export function testModule(template: TemplateStringsArray, ...substitutions: any[]): ModuleTestBuilder; -export function testModule(valueOrTemplate: any, ...substitutions: any[]): ModuleTestBuilder { - return new ModuleTestBuilder(templateFromValue(valueOrTemplate), substitutions); +export function testExpression(value: string): ExpressionTestBuilder; +export function testExpression(template: TemplateStringsArray, ...substitutions: any[]): ExpressionTestBuilder; +export function testExpression(valueOrTemplate: any, ...substitutions: any[]): ExpressionTestBuilder { + return new ExpressionTestBuilder(templateFromValue(valueOrTemplate), substitutions); } From 89f2ae0426c0f50868e93e70c9a0d3e7e1ffccb2 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 30 May 2019 09:13:58 +0500 Subject: [PATCH 04/64] Transform some more tests --- test/unit/accessors.spec.ts | 146 +++++++---------- test/unit/conditionals.spec.ts | 291 ++++++++++++++------------------- test/unit/error.spec.ts | 64 ++++---- test/util.ts | 2 +- 4 files changed, 209 insertions(+), 294 deletions(-) diff --git a/test/unit/accessors.spec.ts b/test/unit/accessors.spec.ts index fd63233df..76dbf6c32 100644 --- a/test/unit/accessors.spec.ts +++ b/test/unit/accessors.spec.ts @@ -1,19 +1,18 @@ import * as util from "../util"; test("get accessor", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } } const f = new Foo(); return f.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foo"); + `.expectToMatchJsResult(); }); test("get accessor in base class", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } @@ -21,12 +20,11 @@ test("get accessor in base class", () => { class Bar extends Foo {} const b = new Bar(); return b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foo"); + `.expectToMatchJsResult(); }); -test("get accessor override", () => { - const code = ` +test.skip("get accessor override", () => { + util.testFunction` class Foo { _foo = "foo"; foo = "foo"; @@ -36,12 +34,11 @@ test("get accessor override", () => { } const b = new Bar(); return b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); -test("get accessor overridden", () => { - const code = ` +test.skip("get accessor overridden", () => { + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } @@ -51,12 +48,11 @@ test("get accessor overridden", () => { } const b = new Bar(); return b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("get accessor override accessor", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } @@ -67,12 +63,11 @@ test("get accessor override accessor", () => { } const b = new Bar(); return b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("get accessor from interface", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } @@ -82,12 +77,11 @@ test("get accessor from interface", () => { } const b: Bar = new Foo(); return b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foo"); + `.expectToMatchJsResult(); }); test("set accessor", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; set foo(val: string) { this._foo = val; } @@ -95,12 +89,11 @@ test("set accessor", () => { const f = new Foo(); f.foo = "bar" return f._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("set accessor in base class", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; set foo(val: string) { this._foo = val; } @@ -109,12 +102,11 @@ test("set accessor in base class", () => { const b = new Bar(); b.foo = "bar" return b._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("set accessor override", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; foo = "foo"; @@ -125,12 +117,11 @@ test("set accessor override", () => { const b = new Bar(); b.foo = "bar" return b._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("set accessor overridden", () => { - const code = ` + util.testFunction` class Foo { _foo = "baz"; set foo(val: string) { this._foo = val; } @@ -142,12 +133,11 @@ test("set accessor overridden", () => { const fooOriginal = b._foo; b.foo = "bar" return fooOriginal + b._foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); test("set accessor override accessor", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; set foo(val: string) { this._foo = "foo"; } @@ -158,12 +148,11 @@ test("set accessor override accessor", () => { const b = new Bar(); b.foo = "bar" return b._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("set accessor from interface", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; set foo(val: string) { this._foo = val; } @@ -175,12 +164,11 @@ test("set accessor from interface", () => { const b: Bar = new Foo(); b.foo = "bar" return b._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("get/set accessors", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } @@ -190,12 +178,11 @@ test("get/set accessors", () => { const fooOriginal = f.foo; f.foo = "bar"; return fooOriginal + f.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); test("get/set accessors in base class", () => { - const code = ` + util.testFunction` class Foo { _foo = "foo"; get foo() { return this._foo; } @@ -206,35 +193,32 @@ test("get/set accessors in base class", () => { const fooOriginal = b.foo; b.foo = "bar" return fooOriginal + b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); test("static get accessor", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } } return Foo.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foo"); + `.expectToMatchJsResult(); }); test("static get accessor in base class", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } } class Bar extends Foo {} return Bar.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foo"); + `.expectToMatchJsResult(); }); test("static get accessor override", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static foo = "foo"; @@ -243,12 +227,11 @@ test("static get accessor override", () => { static get foo() { return this._foo + "bar"; } } return Bar.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); -test("static get accessor overridden", () => { - const code = ` +test.skip("static get accessor overridden", () => { + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } @@ -257,12 +240,11 @@ test("static get accessor overridden", () => { static foo = "bar"; } return Bar.foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static get accessor override accessor", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } @@ -272,12 +254,11 @@ test("static get accessor override accessor", () => { static get foo() { return this._bar; } } return Bar.foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static get accessor from interface", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } @@ -287,24 +268,22 @@ test("static get accessor from interface", () => { } const b: Bar = Foo; return b.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foo"); + `.expectToMatchJsResult(); }); test("static set accessor", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static set foo(val: string) { this._foo = val; } } Foo.foo = "bar" return Foo._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static set accessor in base class", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static set foo(val: string) { this._foo = val; } @@ -312,12 +291,11 @@ test("static set accessor in base class", () => { class Bar extends Foo {} Bar.foo = "bar" return Bar._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static set accessor override", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static foo = "foo"; @@ -327,12 +305,11 @@ test("static set accessor override", () => { } Bar.foo = "bar" return Bar._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static set accessor overridden", () => { - const code = ` + util.testFunction` class Foo { static _foo = "baz"; static set foo(val: string) { this._foo = val; } @@ -343,12 +320,11 @@ test("static set accessor overridden", () => { const fooOriginal = Bar._foo; Bar.foo = "bar" return fooOriginal + Bar._foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); test("static set accessor override accessor", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static set foo(val: string) { this._foo = "foo"; } @@ -358,12 +334,11 @@ test("static set accessor override accessor", () => { } Bar.foo = "bar" return Bar._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static set accessor from interface", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static set foo(val: string) { this._foo = val; } @@ -375,12 +350,11 @@ test("static set accessor from interface", () => { const b: Bar = Foo; b.foo = "bar" return b._foo; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); test("static get/set accessors", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } @@ -389,12 +363,11 @@ test("static get/set accessors", () => { const fooOriginal = Foo.foo; Foo.foo = "bar"; return fooOriginal + Foo.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); test("static get/set accessors in base class", () => { - const code = ` + util.testFunction` class Foo { static _foo = "foo"; static get foo() { return this._foo; } @@ -404,6 +377,5 @@ test("static get/set accessors in base class", () => { const fooOriginal = Bar.foo; Bar.foo = "bar" return fooOriginal + Bar.foo; - `; - expect(util.transpileAndExecute(code)).toBe("foobar"); + `.expectToMatchJsResult(); }); diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index 428066db6..08f0bed09 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -2,36 +2,30 @@ import * as tstl from "../../src"; import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }])("if (%p)", ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let input: number = ${inp}; +test.each([0, 1])("if (%p)", inp => { + util.testFunction` + let input: number = ${inp}; if (input === 0) { return 0; } - return 1;` - ); - - expect(result).toBe(expected); + return 1; + `.expectToMatchJsResult(); }); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }])("ifelse (%p)", ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let input: number = ${inp}; - if (input === 0) { - return 0; - } else { - return 1; - }` - ); - - expect(result).toBe(expected); +test.each([0, 1])("ifelse (%p)", inp => { + util.testFunction` + let input: number = ${inp}; + if (input === 0) { + return 0; + } else { + return 1; + } + `.expectToMatchJsResult(); }); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: 3 }])( - "ifelseif (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let input: number = ${inp}; +test.each([0, 1, 2, 3])("ifelseif (%p)", inp => { + util.testFunction` + let input: number = ${inp}; if (input === 0) { return 0; } else if (input === 1){ @@ -39,18 +33,13 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: } else if (input === 2){ return 2; } - return 3;` - ); - - expect(result).toBe(expected); - } -); + return 3; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: 3 }])( - "ifelseifelse (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let input: number = ${inp}; +test.each([0, 1, 2, 3])("ifelseifelse (%p)", inp => { + util.testFunction` + let input: number = ${inp}; if (input === 0) { return 0; } else if (input === 1){ @@ -59,18 +48,13 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: return 2; } else { return 3; - }` - ); - - expect(result).toBe(expected); - } -); + } + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: -1 }])( - "switch (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2, 3])("switch (%p)", inp => { + util.testFunction` + let result: number = -1; switch (${inp}) { case 0: @@ -83,18 +67,13 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: result = 2; break; } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: -2 }])( - "switchdefault (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2, 3])("switchdefault (%p)", inp => { + util.testFunction` + let result: number = -1; switch (${inp}) { case 0: @@ -110,24 +89,13 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: result = -2; break; } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); -test.each([ - { inp: 0, expected: 1 }, - { inp: 0, expected: 1 }, - { inp: 2, expected: 4 }, - { inp: 3, expected: 4 }, - { inp: 4, expected: 4 }, - { inp: 5, expected: 15 }, - { inp: 7, expected: -2 }, -])("switchfallthrough (%p)", ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 0, 2, 3, 4, 5, 7])("switchfallthrough (%p)", inp => { + util.testFunction` + let result: number = -1; switch (${inp}) { case 0: @@ -152,24 +120,21 @@ test.each([ result = -2; break; } - return result;` - ); - expect(result).toBe(expected); + return result; + `.expectToMatchJsResult(); }); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: -2 }])( - "nestedSwitch (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2, 3])("nestedSwitch (%p)", inp => { + util.testFunction` + let result: number = -1; - switch (${inp}) { + switch (${inp} as number) { case 0: result = 0; break; case 1: - switch(${inp}) { + switch(${inp} as number) { case 0: result = 0; break; @@ -188,47 +153,37 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: result = -2; break; } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 2 }, { inp: 2, expected: 2 }])( - "switchLocalScope (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2])("switchLocalScope (%p)", inp => { + util.testFunction` + let result: number = -1; - switch (${inp}) { - case 0: { - let x = 0; - result = 0; - break; - } - case 1: { - let x = 1; - result = x; - } - case 2: { - let x = 2; - result = x; - break; - } + switch (${inp}) { + case 0: { + let x = 0; + result = 0; + break; } - return result;` - ); - - expect(result).toBe(expected); - } -); + case 1: { + let x = 1; + result = x; + } + case 2: { + let x = 2; + result = x; + break; + } + } + return result; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: -1 }])( - "switchReturn (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `const result: number = -1; +test.each([0, 1, 2, 3])("switchReturn (%p)", inp => { + util.testFunction` + const result: number = -1; switch (${inp}) { case 0: @@ -240,18 +195,13 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: return 2; break; } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: -1 }])( - "switchWithBrackets (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2, 3])("switchWithBrackets (%p)", inp => { + util.testFunction` + let result: number = -1; switch (${inp}) { case 0: { @@ -267,18 +217,13 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: break; } } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: 2 }, { inp: 3, expected: -1 }])( - "switchWithBracketsBreakInConditional (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2, 3])("switchWithBracketsBreakInConditional (%p)", inp => { + util.testFunction` + let result: number = -1; switch (${inp}) { case 0: { @@ -295,20 +240,15 @@ test.each([{ inp: 0, expected: 0 }, { inp: 1, expected: 1 }, { inp: 2, expected: break; } } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); -test.each([{ inp: 0, expected: 4 }, { inp: 1, expected: 0 }, { inp: 2, expected: 2 }, { inp: 3, expected: -1 }])( - "switchWithBracketsBreakInInternalLoop (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let result: number = -1; +test.each([0, 1, 2, 3])("switchWithBracketsBreakInInternalLoop (%p)", inp => { + util.testFunction` + let result: number = -1; - switch (${inp}) { + switch (${inp} as number) { case 0: { result = 0; @@ -329,29 +269,38 @@ test.each([{ inp: 0, expected: 4 }, { inp: 1, expected: 0 }, { inp: 2, expected: break; } } - return result;` - ); - - expect(result).toBe(expected); - } -); + return result; + `.expectToMatchJsResult(); +}); test("If dead code after return", () => { - const result = util.transpileAndExecute(`if (true) { return 3; const b = 8; }`); - - expect(result).toBe(3); + util.testFunction` + if (true) { + return 3; + const b = 8; + } + `.expectToMatchJsResult(); }); test("switch dead code after return", () => { - const result = util.transpileAndExecute( - `switch ("abc") { case "def": return 4; let abc = 4; case "abc": return 5; let def = 6; }` - ); - - expect(result).toBe(5); + util.testFunction` + switch ("abc" as string) { + case "def": + return 4; + let abc = 4; + case "abc": + return 5; + let def = 6; + } + `.expectToMatchJsResult(); }); test("switch not allowed in 5.1", () => { - expect(() => util.transpileString(`switch ("abc") {}`, { luaTarget: tstl.LuaTarget.Lua51 })).toThrowExactError( - TSTLErrors.UnsupportedForTarget("Switch statements", tstl.LuaTarget.Lua51, util.nodeStub) - ); + util.testFunction` + switch ("abc") {} + ` + .options({ luaTarget: tstl.LuaTarget.Lua51 }) + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedForTarget("Switch statements", tstl.LuaTarget.Lua51, util.nodeStub) + ); }); diff --git a/test/unit/error.spec.ts b/test/unit/error.spec.ts index fe5eacf5e..5cffd389d 100644 --- a/test/unit/error.spec.ts +++ b/test/unit/error.spec.ts @@ -2,51 +2,47 @@ import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; test("throwString", () => { - const lua = util.transpileString(`throw "Some Error"`); - expect(lua).toBe(`error("Some Error")`); + util.testFunction` + throw "Some Error" + `.expectToEqual(new util.ExecutionError("Some Error")); }); test("throwError", () => { - expect(() => { - util.transpileString(`throw Error("Some Error")`); - }).toThrowExactError(TSTLErrors.InvalidThrowExpression(util.nodeStub)); + util.testFunction` + throw Error("Some Error") + `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidThrowExpression(util.nodeStub)); }); -test.each([{ i: 0, expected: "A" }, { i: 1, expected: "B" }, { i: 2, expected: "C" }])( - "re-throw (%p)", - ({ i, expected }) => { - const source = ` - const i: number = ${i}; - function foo() { +test.skip.each([0, 1, 2])("re-throw (%p)", i => { + util.testFunction` + const i: number = ${i}; + function foo() { + try { try { - try { - if (i === 0) { throw "z"; } - } catch (e) { - throw "a"; - } finally { - if (i === 1) { throw "b"; } - } + if (i === 0) { throw "z"; } } catch (e) { - throw (e as string).toUpperCase(); + throw "a"; } finally { - throw "C"; + if (i === 1) { throw "b"; } } - } - let result: string = "x"; - try { - foo(); } catch (e) { - result = (e as string)[(e as string).length - 1]; + throw (e as string).toUpperCase(); + } finally { + throw "C"; } - return result; - `; - const result = util.transpileAndExecute(source); - expect(result).toBe(expected); - } -); + } + let result: string = "x"; + try { + foo(); + } catch (e) { + result = (e as string)[(e as string).length - 1]; + } + return result; + `.expectToMatchJsResult(); +}); test("re-throw (no catch var)", () => { - const source = ` + util.testFunction` let result = "x"; try { try { @@ -58,7 +54,5 @@ test("re-throw (no catch var)", () => { result = (e as string)[(e as string).length - 1]; } return result; - `; - const result = util.transpileAndExecute(source); - expect(result).toBe("z"); + `.expectToMatchJsResult(); }); diff --git a/test/util.ts b/test/util.ts index b49331a79..e4888ac5e 100644 --- a/test/util.ts +++ b/test/util.ts @@ -215,7 +215,7 @@ const memoize: MethodDecorator = (_target, _propertyKey, descriptor) => { return descriptor; }; -class ExecutionError extends Error { +export class ExecutionError extends Error { public name = "ExecutionError"; constructor(message: string) { super(message); From 701e285505968e35ea021299e345a1cd090fbfff Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 30 May 2019 13:05:00 +0500 Subject: [PATCH 05/64] Transform some more tests --- test/json.lua | 2 +- test/translation/transformation.spec.ts | 6 +- .../__snapshots__/expressions.spec.ts.snap | 404 ++++++++++++ test/unit/expressions.spec.ts | 608 +++++++----------- test/unit/functions.spec.ts | 472 ++++++-------- test/unit/tuples.spec.ts | 8 +- test/util.ts | 39 +- 7 files changed, 895 insertions(+), 644 deletions(-) create mode 100644 test/unit/__snapshots__/expressions.spec.ts.snap diff --git a/test/json.lua b/test/json.lua index 6dafe9fe4..bac05cda3 100644 --- a/test/json.lua +++ b/test/json.lua @@ -116,7 +116,7 @@ local function encode_number(val) if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end - return string.format("%.14g", val) + return string.format("%.17g", val) end diff --git a/test/translation/transformation.spec.ts b/test/translation/transformation.spec.ts index d7467a78f..06d76b1ea 100644 --- a/test/translation/transformation.spec.ts +++ b/test/translation/transformation.spec.ts @@ -11,6 +11,8 @@ const fixtures = fs .map(f => [path.parse(f).name, fs.readFileSync(path.join(fixturesPath, f), "utf8")]); test.each(fixtures)("Transformation (%s)", (_name, content) => { - const result = util.transpileString(content, { luaLibImport: tstl.LuaLibImportKind.Require }); - expect(result).toMatchSnapshot(); + util.testModule(content) + .options({ luaLibImport: tstl.LuaLibImportKind.Require }) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); }); diff --git a/test/unit/__snapshots__/expressions.spec.ts.snap b/test/unit/__snapshots__/expressions.spec.ts.snap new file mode 100644 index 000000000..db8863e35 --- /dev/null +++ b/test/unit/__snapshots__/expressions.spec.ts.snap @@ -0,0 +1,404 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Binary expressions ordering parentheses ("-1+1") 1`] = ` +"local ____exports = {} +____exports.__result = -1 + 1 +return ____exports" +`; + +exports[`Binary expressions ordering parentheses ("1*(3+4)") 1`] = ` +"local ____exports = {} +____exports.__result = 1 * (3 + 4) +return ____exports" +`; + +exports[`Binary expressions ordering parentheses ("1*(3+4*2)") 1`] = ` +"local ____exports = {} +____exports.__result = 1 * (3 + 4 * 2) +return ____exports" +`; + +exports[`Binary expressions ordering parentheses ("1*30+4") 1`] = ` +"local ____exports = {} +____exports.__result = 1 * 30 + 4 +return ____exports" +`; + +exports[`Binary expressions ordering parentheses ("1+1") 1`] = ` +"local ____exports = {} +____exports.__result = 1 + 1 +return ____exports" +`; + +exports[`Binary expressions ordering parentheses ("10-(4+5)") 1`] = ` +"local ____exports = {} +____exports.__result = 10 - (4 + 5) +return ____exports" +`; + +exports[`Bitop [5.2] ("~a") 1`] = ` +"local ____exports = {} +____exports.__result = bit32.bnot(a) +return ____exports" +`; + +exports[`Bitop [5.2] ("a&=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit32.band(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.2] ("a&b") 1`] = ` +"local ____exports = {} +____exports.__result = bit32.band(a, b) +return ____exports" +`; + +exports[`Bitop [5.2] ("a<<=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit32.lshift(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.2] ("a<>=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit32.arshift(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.2] ("a>>>=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit32.rshift(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.2] ("a>>>b") 1`] = ` +"local ____exports = {} +____exports.__result = bit32.rshift(a, b) +return ____exports" +`; + +exports[`Bitop [5.2] ("a>>b") 1`] = ` +"local ____exports = {} +____exports.__result = bit32.arshift(a, b) +return ____exports" +`; + +exports[`Bitop [5.2] ("a^=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit32.bxor(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.2] ("a^b") 1`] = ` +"local ____exports = {} +____exports.__result = bit32.bxor(a, b) +return ____exports" +`; + +exports[`Bitop [5.2] ("a|=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit32.bor(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.2] ("a|b") 1`] = ` +"local ____exports = {} +____exports.__result = bit32.bor(a, b) +return ____exports" +`; + +exports[`Bitop [5.3] ("~a") 1`] = ` +"local ____exports = {} +____exports.__result = ~a +return ____exports" +`; + +exports[`Bitop [5.3] ("a&=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = a & b + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.3] ("a&b") 1`] = ` +"local ____exports = {} +____exports.__result = a & b +return ____exports" +`; + +exports[`Bitop [5.3] ("a<<=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = a << b + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.3] ("a<>>=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = a >> b + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.3] ("a>>>b") 1`] = ` +"local ____exports = {} +____exports.__result = a >> b +return ____exports" +`; + +exports[`Bitop [5.3] ("a^=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = a ~ b + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.3] ("a^b") 1`] = ` +"local ____exports = {} +____exports.__result = a ~ b +return ____exports" +`; + +exports[`Bitop [5.3] ("a|=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = a | b + return a +end)() +return ____exports" +`; + +exports[`Bitop [5.3] ("a|b") 1`] = ` +"local ____exports = {} +____exports.__result = a | b +return ____exports" +`; + +exports[`Bitop [JIT] ("~a") 1`] = ` +"local ____exports = {} +____exports.__result = bit.bnot(a) +return ____exports" +`; + +exports[`Bitop [JIT] ("a&=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit.band(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [JIT] ("a&b") 1`] = ` +"local ____exports = {} +____exports.__result = bit.band(a, b) +return ____exports" +`; + +exports[`Bitop [JIT] ("a<<=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit.lshift(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [JIT] ("a<>=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit.arshift(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [JIT] ("a>>>=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit.rshift(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [JIT] ("a>>>b") 1`] = ` +"local ____exports = {} +____exports.__result = bit.rshift(a, b) +return ____exports" +`; + +exports[`Bitop [JIT] ("a>>b") 1`] = ` +"local ____exports = {} +____exports.__result = bit.arshift(a, b) +return ____exports" +`; + +exports[`Bitop [JIT] ("a^=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit.bxor(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [JIT] ("a^b") 1`] = ` +"local ____exports = {} +____exports.__result = bit.bxor(a, b) +return ____exports" +`; + +exports[`Bitop [JIT] ("a|=b") 1`] = ` +"local ____exports = {} +____exports.__result = (function() + a = bit.bor(a, b) + return a +end)() +return ____exports" +`; + +exports[`Bitop [JIT] ("a|b") 1`] = ` +"local ____exports = {} +____exports.__result = bit.bor(a, b) +return ____exports" +`; + +exports[`Unary expressions basic ("!a") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____ = not a +end +return ____exports" +`; + +exports[`Unary expressions basic ("++i") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + i = i + 1 +end +return ____exports" +`; + +exports[`Unary expressions basic ("+a") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____ = a +end +return ____exports" +`; + +exports[`Unary expressions basic ("--i") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + i = i - 1 +end +return ____exports" +`; + +exports[`Unary expressions basic ("-a") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____ = -a +end +return ____exports" +`; + +exports[`Unary expressions basic ("delete tbl.test") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + tbl.test = nil +end +return ____exports" +`; + +exports[`Unary expressions basic ("delete tbl['test']") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + tbl.test = nil +end +return ____exports" +`; + +exports[`Unary expressions basic ("i++") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + i = i + 1 +end +return ____exports" +`; + +exports[`Unary expressions basic ("i--") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + i = i - 1 +end +return ____exports" +`; + +exports[`Unary expressions basic ("let a = delete tbl.test") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local a = (function() + tbl.test = nil + return true + end)() +end +return ____exports" +`; + +exports[`Unary expressions basic ("let a = delete tbl['test']") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local a = (function() + tbl.test = nil + return true + end)() +end +return ____exports" +`; diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 23d137f50..5a8543c81 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -4,51 +4,33 @@ import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; test.each([ - { input: "i++", lua: "i = i + 1" }, - { input: "++i", lua: "i = i + 1" }, - { input: "i--", lua: "i = i - 1" }, - { input: "--i", lua: "i = i - 1" }, - { input: "!a", lua: "local ____ = not a" }, - { input: "-a", lua: "local ____ = -a" }, - { input: "+a", lua: "local ____ = a" }, - { input: "let a = delete tbl['test']", lua: "local a = (function()\n tbl.test = nil\n return true\nend)()" }, - { input: "delete tbl['test']", lua: "tbl.test = nil" }, - { input: "let a = delete tbl.test", lua: "local a = (function()\n tbl.test = nil\n return true\nend)()" }, - { input: "delete tbl.test", lua: "tbl.test = nil" }, -])("Unary expressions basic (%p)", ({ input, lua }) => { - expect(util.transpileString(input)).toBe(lua); -}); - -test.each([ - { input: "3+4", output: 3 + 4 }, - { input: "5-2", output: 5 - 2 }, - { input: "6*3", output: 6 * 3 }, - { input: "6**3", output: 6 ** 3 }, - { input: "20/5", output: 20 / 5 }, - { input: "15/10", output: 15 / 10 }, - { input: "15%3", output: 15 % 3 }, -])("Binary expressions basic numeric (%p)", ({ input, output }) => { - const result = util.transpileAndExecute(`return ${input}`); - - expect(result).toBe(output); -}); - -test.each([ - { input: "1==1", expected: true }, - { input: "1===1", expected: true }, - { input: "1!=1", expected: false }, - { input: "1!==1", expected: false }, - { input: "1>1", expected: false }, - { input: "1>=1", expected: true }, - { input: "1<1", expected: false }, - { input: "1<=1", expected: true }, - { input: "1&&1", expected: 1 }, - { input: "1||1", expected: 1 }, -])("Binary expressions basic boolean (%p)", ({ input, expected }) => { - const result = util.transpileAndExecute(`return ${input}`); - - expect(result).toBe(expected); -}); + "i++", + "++i", + "i--", + "--i", + "!a", + "-a", + "+a", + "let a = delete tbl['test']", + "delete tbl['test']", + "let a = delete tbl.test", + "delete tbl.test", +])("Unary expressions basic (%p)", input => { + util.testFunction(input) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); + +test.each(["3+4", "5-2", "6*3", "6**3", "20/5", "15/10", "15%3"])("Binary expressions basic numeric (%p)", input => { + util.testExpression(input).expectToMatchJsResult(); +}); + +test.each(["1==1", "1===1", "1!=1", "1!==1", "1>1", "1>=1", "1<1", "1<=1", "1&&1", "1||1"])( + "Binary expressions basic boolean (%p)", + input => { + util.testExpression(input).expectToMatchJsResult(); + } +); test.each(["'key' in obj", "'existingKey' in obj", "0 in obj", "9 in obj"])("Binary expression in (%p)", input => { const tsHeader = "declare var obj: any;"; @@ -59,143 +41,84 @@ test.each(["'key' in obj", "'existingKey' in obj", "0 in obj", "9 in obj"])("Bin expect(result).toBe(eval(`let obj = { existingKey: 1 }; ${input}`)); }); -test.each([ - { input: "a+=b", expected: 5 + 3 }, - { input: "a-=b", expected: 5 - 3 }, - { input: "a*=b", expected: 5 * 3 }, - { input: "a/=b", expected: 5 / 3 }, - { input: "a%=b", expected: 5 % 3 }, - { input: "a**=b", expected: 5 ** 3 }, -])("Binary expressions overridden operators (%p)", ({ input, expected }) => { - const result = util.transpileAndExecute(`let a = 5; let b = 3; ${input}; return a;`); - - expect(result).toBe(expected); +test.each(["a+=b", "a-=b", "a*=b", "a/=b", "a%=b", "a**=b"])("Binary expressions overridden operators (%p)", input => { + util.testFunction` + let a = 5; + let b = 3; + ${input}; + return a; + `.expectToMatchJsResult(); }); -test.each([ - { input: "~b" }, - { input: "a&b" }, - { input: "a&=b" }, - { input: "a|b" }, - { input: "a|=b" }, - { input: "a^b" }, - { input: "a^=b" }, - { input: "a<>b" }, - { input: "a>>=b" }, - { input: "a>>>b" }, - { input: "a>>>=b" }, -])("Bitop [5.1] (%p)", ({ input }) => { +const supportedInAll = ["~a", "a&b", "a&=b", "a|b", "a|=b", "a^b", "a^=b", "a<>>b", "a>>>=b"]; +const unsupportedIn53 = ["a>>b", "a>>=b"]; +const allBinaryOperators = [...supportedInAll, ...unsupportedIn53]; +test.each(allBinaryOperators)("Bitop [5.1] (%p)", input => { // Bit operations not supported in 5.1, expect an exception - expect(() => - util.transpileString(input, { - luaTarget: tstl.LuaTarget.Lua51, - luaLibImport: tstl.LuaLibImportKind.None, - }) - ).toThrow(); -}); - -test.each([ - { input: "~a", lua: "local ____ = bit.bnot(a)" }, - { input: "a&b", lua: "local ____ = bit.band(a, b)" }, - { input: "a&=b", lua: "a = bit.band(a, b)" }, - { input: "a|b", lua: "local ____ = bit.bor(a, b)" }, - { input: "a|=b", lua: "a = bit.bor(a, b)" }, - { input: "a^b", lua: "local ____ = bit.bxor(a, b)" }, - { input: "a^=b", lua: "a = bit.bxor(a, b)" }, - { input: "a<>b", lua: "local ____ = bit.arshift(a, b)" }, - { input: "a>>=b", lua: "a = bit.arshift(a, b)" }, - { input: "a>>>b", lua: "local ____ = bit.rshift(a, b)" }, - { input: "a>>>=b", lua: "a = bit.rshift(a, b)" }, -])("Bitop [JIT] (%p)", ({ input, lua }) => { - const options = { luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }; - expect(util.transpileString(input, options)).toBe(lua); -}); - -test.each([ - { input: "~a", lua: "local ____ = bit32.bnot(a)" }, - { input: "a&b", lua: "local ____ = bit32.band(a, b)" }, - { input: "a&=b", lua: "a = bit32.band(a, b)" }, - { input: "a|b", lua: "local ____ = bit32.bor(a, b)" }, - { input: "a|=b", lua: "a = bit32.bor(a, b)" }, - { input: "a^b", lua: "local ____ = bit32.bxor(a, b)" }, - { input: "a^=b", lua: "a = bit32.bxor(a, b)" }, - { input: "a<>b", lua: "local ____ = bit32.arshift(a, b)" }, - { input: "a>>=b", lua: "a = bit32.arshift(a, b)" }, - { input: "a>>>b", lua: "local ____ = bit32.rshift(a, b)" }, - { input: "a>>>=b", lua: "a = bit32.rshift(a, b)" }, -])("Bitop [5.2] (%p)", ({ input, lua }) => { - const options = { luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }; - expect(util.transpileString(input, options)).toBe(lua); -}); - -test.each([ - { input: "~a", lua: "local ____ = ~a" }, - { input: "a&b", lua: "local ____ = a & b" }, - { input: "a&=b", lua: "a = a & b" }, - { input: "a|b", lua: "local ____ = a | b" }, - { input: "a|=b", lua: "a = a | b" }, - { input: "a^b", lua: "local ____ = a ~ b" }, - { input: "a^=b", lua: "a = a ~ b" }, - { input: "a<>>b", lua: "local ____ = a >> b" }, - { input: "a>>>=b", lua: "a = a >> b" }, -])("Bitop [5.3] (%p)", ({ input, lua }) => { - const options = { luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }; - expect(util.transpileString(input, options)).toBe(lua); -}); - -test.each(["a>>b", "a>>=b"])("Unsupported bitop 5.3 (%p)", input => { - expect(() => - util.transpileString(input, { - luaTarget: tstl.LuaTarget.Lua53, - luaLibImport: tstl.LuaLibImportKind.None, - }) - ).toThrowExactError( - TSTLErrors.UnsupportedKind( - "right shift operator (use >>> instead)", - ts.SyntaxKind.GreaterThanGreaterThanToken, - util.nodeStub - ) - ); -}); - -test.each([ - { input: "1+1", lua: "1 + 1" }, - { input: "-1+1", lua: "-1 + 1" }, - { input: "1*30+4", lua: "1 * 30 + 4" }, - { input: "1*(3+4)", lua: "1 * (3 + 4)" }, - { input: "1*(3+4*2)", lua: "1 * (3 + 4 * 2)" }, - { input: "10-(4+5)", lua: "10 - (4 + 5)" }, -])("Binary expressions ordering parentheses (%p)", ({ input, lua }) => { - expect(util.transpileString(input)).toBe("local ____ = " + lua); -}); + util.testExpression(input) + .options({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedForTarget("Bitwise operations", tstl.LuaTarget.Lua51, util.nodeStub) + ); +}); + +test.each(allBinaryOperators)("Bitop [JIT] (%p)", input => { + util.testExpression(input) + .options({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); + +test.each(allBinaryOperators)("Bitop [5.2] (%p)", input => { + util.testExpression(input) + .options({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); + +test.each(supportedInAll)("Bitop [5.3] (%p)", input => { + util.testExpression(input) + .options({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); + +test.each(unsupportedIn53)("Unsupported bitop 5.3 (%p)", input => { + util.testExpression(input) + .options({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedKind( + "right shift operator (use >>> instead)", + ts.SyntaxKind.GreaterThanGreaterThanToken, + util.nodeStub + ) + ); +}); + +test.each(["1+1", "-1+1", "1*30+4", "1*(3+4)", "1*(3+4*2)", "10-(4+5)"])( + "Binary expressions ordering parentheses (%p)", + input => { + util.testExpression(input).expectLuaToMatchSnapshot(); + } +); -test.each([ - { input: "bar(),foo()", expectResult: 1 }, - { input: "foo(),bar()", expectResult: 2 }, - { input: "foo(),bar(),baz()", expectResult: 3 }, -])("Binary Comma (%p)", ({ input, expectResult }) => { - const code = `function foo() { return 1; } +test.each(["bar(),foo()", "foo(),bar()", "foo(),bar(),baz()"])("Binary Comma (%p)", input => { + util.testFunction` + function foo() { return 1; } function bar() { return 2; }; function baz() { return 3; }; - return (${input});`; - expect(util.transpileAndExecute(code)).toBe(expectResult); + return (${input}); + `.expectToMatchJsResult(); }); test("Binary Comma Statement in For Loop", () => { - const code = ` + util.testFunction` let x: number, y: number; for (x = 0, y = 17; x < 5; ++x, --y) {} return y; - `; - expect(util.transpileAndExecute(code)).toBe(12); + `.expectToMatchJsResult(); }); test("Null Expression", () => { @@ -207,52 +130,51 @@ test("Undefined Expression", () => { }); test.each([ - { input: "true ? 'a' : 'b'", expected: "a" }, - { input: "false ? 'a' : 'b'", expected: "b" }, - { input: "true ? false : true", expected: false }, - { input: "false ? false : true", expected: true }, - { input: "true ? literalValue : true", expected: "literal" }, + { input: "true ? 'a' : 'b'" }, + { input: "false ? 'a' : 'b'" }, + { input: "true ? false : true" }, + { input: "false ? false : true" }, + { input: "true ? literalValue : true" }, { input: "true ? variableValue : true" }, { input: "true ? maybeUndefinedValue : true" }, - { input: "true ? maybeBooleanValue : true", expected: false }, + { input: "true ? maybeBooleanValue : true" }, { input: "true ? maybeUndefinedValue : true", options: { strictNullChecks: true } }, - { input: "true ? maybeBooleanValue : true", expected: false, options: { strictNullChecks: true } }, + { input: "true ? maybeBooleanValue : true", options: { strictNullChecks: true } }, { input: "true ? undefined : true", options: { strictNullChecks: true } }, { input: "true ? null : true", options: { strictNullChecks: true } }, - { input: "true ? false : true", expected: false, options: { luaTarget: tstl.LuaTarget.Lua51 } }, - { input: "false ? false : true", expected: true, options: { luaTarget: tstl.LuaTarget.Lua51 } }, + { input: "true ? false : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, + { input: "false ? false : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, { input: "true ? undefined : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, - { input: "true ? false : true", expected: false, options: { luaTarget: tstl.LuaTarget.LuaJIT } }, - { input: "false ? false : true", expected: true, options: { luaTarget: tstl.LuaTarget.LuaJIT } }, + { input: "true ? false : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, + { input: "false ? false : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, { input: "true ? undefined : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, -])("Ternary operator (%p)", ({ input, expected, options }) => { - const result = util.transpileAndExecute( - `const literalValue = 'literal'; - let variableValue:string; - let maybeBooleanValue:string|boolean = false; - let maybeUndefinedValue:string|undefined; - return ${input};`, - options - ); - - expect(result).toBe(expected); +])("Ternary operator (%p)", ({ input, options }) => { + util.testFunction` + const literalValue = "literal"; + let variableValue: string; + let maybeBooleanValue: string | boolean = false; + let maybeUndefinedValue: string | undefined; + return ${input}; + ` + .options(options) + .expectToMatchJsResult(); }); test.each([ - { expression: "inst.field", expected: 8 }, - { expression: "inst.field + 3", expected: 8 + 3 }, - { expression: "inst.field * 3", expected: 8 * 3 }, - { expression: "inst.field / 2", expected: 8 / 2 }, - { expression: "inst.field && 3", expected: 8 && 3 }, - { expression: "inst.field || 3", expected: 8 || 3 }, - { expression: "(inst.field + 3) & 3", expected: (8 + 3) & 3 }, - { expression: "inst.field | 3", expected: 8 | 3 }, - { expression: "inst.field << 3", expected: 8 << 3 }, - { expression: "inst.field >>> 1", expected: 8 >> 1 }, - { expression: "inst.field = 3", expected: 3 }, - { expression: `"abc" + inst.field`, expected: "abc8" }, -])("Get accessor expression (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute(` + "inst.field", + "inst.field + 3", + "inst.field * 3", + "inst.field / 2", + "inst.field && 3", + "inst.field || 3", + "(inst.field + 3) & 3", + "inst.field | 3", + "inst.field << 3", + "inst.field >>> 1", + "inst.field = 3", + `"abc" + inst.field`, +])("Get accessor expression (%p)", expression => { + util.testFunction` class MyClass { public _field: number; public get field(): number { return this._field + 4; } @@ -261,175 +183,140 @@ test.each([ var inst = new MyClass(); inst._field = 4; return ${expression}; - `); - - expect(result).toBe(expected); -}); - -test.each([ - { expression: "= 4", expected: 4 + 4 }, - { expression: "-= 3", expected: 4 - 3 + 4 }, - { expression: "+= 3", expected: 4 + 3 + 4 }, - { expression: "*= 3", expected: 4 * 3 + 4 }, - { expression: "/= 2", expected: 4 / 2 + 4 }, - { expression: "&= 3", expected: (4 & 3) + 4 }, - { expression: "|= 3", expected: (4 | 3) + 4 }, - { expression: "<<= 3", expected: (4 << 3) + 4 }, - { expression: ">>>= 3", expected: (4 >> 3) + 4 }, -])("Set accessorExpression (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute(` - class MyClass { - public _field: number = 4; - public get field(): number { return this._field; } - public set field(v: number) { this._field = v + 4; } - } - var inst = new MyClass(); - inst.field ${expression}; - return inst._field; - `); - - expect(result).toBe(expected); -}); + `.expectToMatchJsResult(); +}); + +test.each(["= 4", "-= 3", "+= 3", "*= 3", "/= 2", "&= 3", "|= 3", "<<= 3", ">>>= 3"])( + "Set accessorExpression (%p)", + expression => { + util.testFunction` + class MyClass { + public _field: number = 4; + public get field(): number { return this._field; } + public set field(v: number) { this._field = v + 4; } + } + var inst = new MyClass(); + inst.field ${expression}; + return inst._field; + `.expectToMatchJsResult(); + } +); -test.each([ - { expression: "inst.baseField", expected: 7 }, - { expression: "inst.field", expected: 6 }, - { expression: "inst.superField", expected: 5 }, - { expression: "inst.superBaseField", expected: 4 }, -])("Inherited accessors (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute(` - class MyBaseClass { - public _baseField: number; - public get baseField(): number { return this._baseField + 6; } - public set baseField(v: number) { this._baseField = v; } - } - class MyClass extends MyBaseClass { - public _field: number; - public get field(): number { return this._field + 4; } - public set field(v: number) { this._field = v; } - } - class MySuperClass extends MyClass { - public _superField: number; - public get superField(): number { return this._superField + 2; } - public set superField(v: number) { this._superField = v; } - public get superBaseField() { return this.baseField - 3; } - } - var inst = new MySuperClass(); - inst.baseField = 1; - inst.field = 2; - inst.superField = 3; - return ${expression} - `); - expect(result).toBe(expected); -}); +test.each(["inst.baseField", "inst.field", "inst.superField", "inst.superBaseField"])( + "Inherited accessors (%p)", + expression => { + util.testFunction` + class MyBaseClass { + public _baseField: number; + public get baseField(): number { return this._baseField + 6; } + public set baseField(v: number) { this._baseField = v; } + } + class MyClass extends MyBaseClass { + public _field: number; + public get field(): number { return this._field + 4; } + public set field(v: number) { this._field = v; } + } + class MySuperClass extends MyClass { + public _superField: number; + public get superField(): number { return this._superField + 2; } + public set superField(v: number) { this._superField = v; } + public get superBaseField() { return this.baseField - 3; } + } + var inst = new MySuperClass(); + inst.baseField = 1; + inst.field = 2; + inst.superField = 3; + return ${expression}; + `.expectToMatchJsResult(); + } +); -test.each([ - { expression: "return x.value;", expected: 1 }, - { expression: "x.value = 3; return x.value;", expected: 3 }, -])("Union accessors (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `class A{ get value(){ return this.v || 1; } set value(v){ this.v = v; } v: number; } +test.each(["return x.value;", "x.value = 3; return x.value;"])("Union accessors (%p)", expression => { + util.testFunction` + class A{ get value(){ return this.v || 1; } set value(v){ this.v = v; } v: number; } class B{ get value(){ return this.v || 2; } set value(v){ this.v = v; } v: number; } let x: A|B = new A(); - ${expression}` - ); - - expect(result).toBe(expected); + ${expression} + `.expectToMatchJsResult(); }); -test.each([ - { expression: "i++", expected: 10 }, - { expression: "i--", expected: 10 }, - { expression: "++i", expected: 11 }, - { expression: "--i", expected: 9 }, -])("Incrementor value (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute(`let i = 10; return ${expression};`); - - expect(result).toBe(expected); +test.each(["i++", "i--", "++i", "--i"])("Incrementor value (%p)", expression => { + util.testFunction` + let i = 10; + return ${expression}; + `.expectToMatchJsResult(); }); -test.each([ - { lambda: "a++", expected: "val3" }, - { lambda: "a--", expected: "val3" }, - { lambda: "--a", expected: "val2" }, - { lambda: "++a", expected: "val4" }, -])("Template string expression (%p)", ({ lambda, expected }) => { - const result = util.transpileAndExecute("let a = 3; return `val${" + lambda + "}`;"); - - expect(result).toEqual(expected); +test.each(["a++", "a--", "--a", "++a"])("Template string expression (%p)", lambda => { + util.testFunction` + let a = 3; + return \`val\${${lambda}}\`; + `.expectToMatchJsResult(); }); -test.each([{ expression: "x = y", expected: "y" }, { expression: "x += y", expected: "xy" }])( - "Assignment expressions (%p)", - ({ expression, expected }) => { - const result = util.transpileAndExecute(`let x = "x"; let y = "y"; return ${expression};`); - expect(result).toBe(expected); - } -); - -test.each([ - { expression: "x = o.p", expected: "o" }, - { expression: "x = a[0]", expected: "a" }, - { expression: "x = y = o.p", expected: "o" }, - { expression: "x = o.p", expected: "o" }, -])("Assignment expressions using temp (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `let x = "x"; +test.each(["x = y", "x += y"])("Assignment expressions (%p)", expression => { + util.testFunction` + let x = "x"; let y = "y"; - let o = {p: "o"}; - let a = ["a"]; - return ${expression};` - ); - expect(result).toBe(expected); + return ${expression}; + `.expectToMatchJsResult(); }); -test.each([ - { expression: "o.p = x", expected: "x" }, - { expression: "a[0] = x", expected: "x" }, - { expression: "o.p = a[0]", expected: "a" }, - { expression: "o.p = a[0] = x", expected: "x" }, -])("Property assignment expressions (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `let x = "x"; +test.each(["x = o.p", "x = a[0]", "x = y = o.p", "x = o.p"])("Assignment expressions using temp (%p)", expression => { + util.testFunction` + let x = "x"; + let y = "y"; let o = {p: "o"}; let a = ["a"]; - return ${expression};` - ); - expect(result).toBe(expected); -}); + return ${expression}; + `.expectToMatchJsResult(); +}); + +test.each(["o.p = x", "a[0] = x", "o.p = a[0]", "o.p = a[0] = x"])( + "Property assignment expressions (%p)", + expression => { + util.testFunction` + let x = "x"; + let o = {p: "o"}; + let a = ["a"]; + return ${expression}; + `.expectToMatchJsResult(); + } +); test.each([ - { expression: "x = t()", expected: "t0,t1" }, - { expression: "x = tr()", expected: "tr0,tr1" }, - { expression: "[x[1], x[0]] = t()", expected: "t0,t1" }, - { expression: "[x[1], x[0]] = tr()", expected: "tr0,tr1" }, - { expression: "x = [y[1], y[0]]", expected: "y1,y0" }, - { expression: "[x[0], x[1]] = [y[1], y[0]]", expected: "y1,y0" }, -])("Tuple assignment expressions (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `let x: [string, string] = ["x0", "x1"]; + "x = t()", + "x = tr()", + "[x[1], x[0]] = t()", + "[x[1], x[0]] = tr()", + "x = [y[1], y[0]]", + "[x[0], x[1]] = [y[1], y[0]]", +])("Tuple assignment expressions (%p)", expression => { + util.testFunction` + let x: [string, string] = ["x0", "x1"]; let y: [string, string] = ["y0", "y1"]; function t(): [string, string] { return ["t0", "t1"] }; /** @tupleReturn */ function tr(): [string, string] { return ["tr0", "tr1"] }; const r = ${expression}; - return \`\${r[0]},\${r[1]}\`` - ); - expect(result).toBe(expected); + return \`\${r[0]},\${r[1]}\` + `.expectToMatchJsResult(); }); test("Block expression", () => { - const result = util.transpileAndExecute(`let a = 4; {let a = 42; } return a;`); - expect(result).toBe(4); + util.testFunction` + let a = 4; + { let a = 42; } + return a; + `.expectToMatchJsResult(); }); test("Non-null expression", () => { - const result = util.transpileAndExecute(` + util.testFunction` function abc(): number | undefined { return 3; } const a: number = abc()!; return a; - `); - expect(result).toBe(3); + `.expectToMatchJsResult(); }); test("Unknown unary postfix error", () => { @@ -463,27 +350,31 @@ test("Unknown unary postfix error", () => { }); test("Incompatible fromCodePoint expression error", () => { - expect(() => util.transpileString("const abc = String.fromCodePoint(123);")).toThrowExactError( - TSTLErrors.UnsupportedForTarget("string property fromCodePoint", tstl.LuaTarget.Lua53, util.nodeStub) - ); + util.testExpression`String.fromCodePoint(123)` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedForTarget("string property fromCodePoint", tstl.LuaTarget.Lua53, util.nodeStub) + ); }); test("Unknown string expression error", () => { - expect(() => util.transpileString("const abc = String.abcd();")).toThrowExactError( - TSTLErrors.UnsupportedForTarget("string property abcd", tstl.LuaTarget.Lua53, util.nodeStub) - ); + util.testExpression`String.abcd()` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedForTarget("string property abcd", tstl.LuaTarget.Lua53, util.nodeStub) + ); }); test("Unsupported array function error", () => { - expect(() => util.transpileString("const abc = [].unknownFunction();")).toThrowExactError( - TSTLErrors.UnsupportedProperty("array", "unknownFunction", util.nodeStub) - ); + util.testFunction`[].unknownFunction()` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("array", "unknownFunction", util.nodeStub)); }); test("Unsupported math property error", () => { - expect(() => util.transpileString("const abc = Math.unknownProperty;")).toThrowExactError( - TSTLErrors.UnsupportedProperty("math", "unknownProperty", util.nodeStub) - ); + util.testExpression`Math.unknownProperty` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("math", "unknownProperty", util.nodeStub)); }); test("Unsupported object literal element error", () => { @@ -519,11 +410,10 @@ test.each([ "Math.log2(2)", "Math.log10(2)", ])("Expression statements (%p)", input => { - const code = ` + util.testFunction` function foo() { return 17; } const bar = {}; ${input}; return 1; - `; - expect(util.transpileAndExecute(code)).toBe(1); + `.expectToMatchJsResult(); }); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index cbcd4ad87..a284f24ff 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -3,36 +3,31 @@ import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; test("Arrow Function Expression", () => { - const result = util.transpileAndExecute(`let add = (a, b) => a+b; return add(1,2);`); - - expect(result).toBe(3); -}); - -test.each([ - { lambda: "i++", expected: 15 }, - { lambda: "i--", expected: 5 }, - { lambda: "++i", expected: 15 }, - { lambda: "--i", expected: 5 }, -])("Arrow function unary expression (%p)", ({ lambda, expected }) => { - const result = util.transpileAndExecute(`let i = 10; [1,2,3,4,5].forEach(() => ${lambda}); return i;`); - - expect(result).toBe(expected); -}); - -test.each([ - { lambda: "b => a = b", expected: 5 }, - { lambda: "b => a += b", expected: 15 }, - { lambda: "b => a -= b", expected: 5 }, - { lambda: "b => a *= b", expected: 50 }, - { lambda: "b => a /= b", expected: 2 }, - { lambda: "b => a **= b", expected: 100000 }, - { lambda: "b => a %= b", expected: 0 }, -])("Arrow function assignment (%p)", ({ lambda, expected }) => { - const result = util.transpileAndExecute(`let a = 10; let lambda = ${lambda}; - lambda(5); return a;`); - - expect(result).toBe(expected); -}); + util.testFunction` + const add = (a, b) => a + b; + return add(1, 2); + `.expectToMatchJsResult(); +}); + +test.each(["i++", "i--", "++i", "--i"])("Arrow function unary expression (%p)", lambda => { + util.testFunction` + let i = 10; + [1,2,3,4,5].forEach(() => ${lambda}); + return i; + `.expectToMatchJsResult(); +}); + +test.each(["b => a = b", "b => a += b", "b => a -= b", "b => a *= b", "b => a /= b", "b => a **= b", "b => a %= b"])( + "Arrow function assignment (%p)", + lambda => { + util.testFunction` + let a = 10; + let lambda = ${lambda}; + lambda(5); + return a; + `.expectToMatchJsResult(); + } +); test.each([{ inp: [] }, { inp: [5] }, { inp: [1, 2] }])("Arrow Default Values (%p)", ({ inp }) => { // Default value is 3 for v1 @@ -51,24 +46,25 @@ test.each([{ inp: [] }, { inp: [5] }, { inp: [1, 2] }])("Arrow Default Values (% }); test("Function Expression", () => { - const result = util.transpileAndExecute(`let add = function(a, b) {return a+b}; return add(1,2);`); - - expect(result).toBe(3); + util.testFunction` + let add = function(a, b) {return a+b}; + return add(1,2); + `.expectToMatchJsResult(); }); test("Function definition scope", () => { - const result = util.transpileAndExecute(`function abc() { function xyz() { return 5; } }\n - function def() { function xyz() { return 3; } abc(); return xyz(); }\n - return def();`); - - expect(result).toBe(3); + util.testFunction` + function abc() { function xyz() { return 5; } } + function def() { function xyz() { return 3; } abc(); return xyz(); } + return def(); + `.expectToMatchJsResult(); }); test("Function default parameter", () => { - const result = util.transpileAndExecute(`function abc(defaultParam: string = "abc") { return defaultParam; }\n - return abc() + abc("def");`); - - expect(result).toBe("abcdef"); + util.testFunction` + function abc(defaultParam: string = "abc") { return defaultParam; } + return abc() + abc("def"); + `.expectToMatchJsResult(); }); test.each([{ inp: [] }, { inp: [5] }, { inp: [1, 2] }])("Function Default Values (%p)", ({ inp }) => { @@ -88,27 +84,25 @@ test.each([{ inp: [] }, { inp: [5] }, { inp: [1, 2] }])("Function Default Values }); test("Function default array binding parameter", () => { - const code = ` + util.testFunction` function foo([bar]: [string] = ["foobar"]) { return bar; } - return foo();`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return foo(); + `.expectToMatchJsResult(); }); test("Function default object binding parameter", () => { - const code = ` + util.testFunction` function foo({ bar }: { bar: string } = { bar: "foobar" }) { return bar; } - return foo();`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return foo(); + `.expectToMatchJsResult(); }); test("Function default binding parameter maintains order", () => { - const code = ` + util.testFunction` const resultsA = [{x: "foo"}, {x: "baz"}]; const resultsB = ["blah", "bar"]; let i = 0; @@ -117,104 +111,82 @@ test("Function default binding parameter maintains order", () => { function foo({ x }: { x: string } = a(), y = b()) { return x + y; } - return foo();`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return foo(); + `.expectToMatchJsResult(); }); test("Class method call", () => { - const returnValue = 4; - const source = `class TestClass { - public classMethod(): number { return ${returnValue}; } - } - - const classInstance = new TestClass(); - return classInstance.classMethod();`; - - const result = util.transpileAndExecute(source); + util.testFunction` + class TestClass { + public classMethod(): number { return 4; } + } - expect(result).toBe(returnValue); + const classInstance = new TestClass(); + return classInstance.classMethod(); + `.expectToMatchJsResult(); }); test("Class dot method call void", () => { - const returnValue = 4; - const source = `class TestClass { - public dotMethod: () => number = () => ${returnValue}; - } - - const classInstance = new TestClass(); - return classInstance.dotMethod();`; - - const result = util.transpileAndExecute(source); + util.testFunction` + class TestClass { + public dotMethod: () => number = () => 4; + } - expect(result).toBe(returnValue); + const classInstance = new TestClass(); + return classInstance.dotMethod(); + `.expectToMatchJsResult(); }); test("Class dot method call with parameter", () => { - const returnValue = 4; - const source = `class TestClass { - public dotMethod: (x: number) => number = x => 3 * x; - } - - const classInstance = new TestClass(); - return classInstance.dotMethod(${returnValue});`; - - const result = util.transpileAndExecute(source); + util.testFunction` + class TestClass { + public dotMethod: (x: number) => number = x => 3 * x; + } - expect(result).toBe(3 * returnValue); + const classInstance = new TestClass(); + return classInstance.dotMethod(4); + `.expectToMatchJsResult(); }); test("Class static dot method", () => { - const returnValue = 4; - const source = `class TestClass { - public static dotMethod: () => number = () => ${returnValue}; - } - - return TestClass.dotMethod();`; - - const result = util.transpileAndExecute(source); + util.testFunction` + class TestClass { + public static dotMethod: () => number = () => 4; + } - expect(result).toBe(returnValue); + return TestClass.dotMethod(); + `.expectToMatchJsResult(); }); test("Class static dot method with parameter", () => { - const returnValue = 4; - const source = `class TestClass { - public static dotMethod: (x: number) => number = x => 3 * x; - } - - return TestClass.dotMethod(${returnValue});`; - - const result = util.transpileAndExecute(source); + util.testFunction` + class TestClass { + public static dotMethod: (x: number) => number = x => 3 * x; + } - expect(result).toBe(3 * returnValue); + return TestClass.dotMethod(4); + `.expectToMatchJsResult(); }); test("Function bind", () => { - const source = `const abc = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } - return abc.bind({ a: 4 }, "b")("c");`; - - const result = util.transpileAndExecute(source); - - expect(result).toBe("4bc"); + util.testFunction` + const abc = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } + return abc.bind({ a: 4 }, "b")("c"); + `.expectToMatchJsResult(); }); test("Function apply", () => { - const source = `const abc = function (this: { a: number }, a: string) { return this.a + a; } - return abc.apply({ a: 4 }, ["b"]);`; - - const result = util.transpileAndExecute(source); - - expect(result).toBe("4b"); + util.testFunction` + const abc = function (this: { a: number }, a: string) { return this.a + a; } + return abc.apply({ a: 4 }, ["b"]); + `.expectToMatchJsResult(); }); test("Function call", () => { - const source = `const abc = function (this: { a: number }, a: string) { return this.a + a; } - return abc.call({ a: 4 }, "b");`; - - const result = util.transpileAndExecute(source); - - expect(result).toBe("4b"); + util.testFunction` + const abc = function (this: { a: number }, a: string) { return this.a + a; } + return abc.call({ a: 4 }, "b"); + `.expectToMatchJsResult(); }); test("Invalid property access call transpilation", () => { @@ -230,87 +202,85 @@ test("Invalid property access call transpilation", () => { }); test("Function dead code after return", () => { - const result = util.transpileAndExecute(`function abc() { return 3; const a = 5; } return abc();`); - - expect(result).toBe(3); + util.testFunction` + function abc() { return 3; const a = 5; } + return abc(); + `.expectToMatchJsResult(); }); test("Method dead code after return", () => { - const result = util.transpileAndExecute( - `class def { public static abc() { return 3; const a = 5; } } return def.abc();` - ); - - expect(result).toBe(3); + util.testFunction` + class def { public static abc() { return 3; const a = 5; } } + return def.abc(); + `.expectToMatchJsResult(); }); test("Recursive function definition", () => { - const result = util.transpileAndExecute(`function f() { return typeof f; }; return f();`); - - expect(result).toBe("function"); + util.testFunction` + function f() { return typeof f; }; + return f(); + `.expectToMatchJsResult(); }); test("Recursive function expression", () => { - const result = util.transpileAndExecute(`let f = function() { return typeof f; }; return f();`); - - expect(result).toBe("function"); + util.testFunction` + let f = function() { return typeof f; }; + return f(); + `.expectToMatchJsResult(); }); test("Wrapped recursive function expression", () => { - const result = util.transpileAndExecute( - `function wrap(fn: T) { return fn; } - let f = wrap(function() { return typeof f; }); return f();` - ); - - expect(result).toBe("function"); + util.testFunction` + function wrap(fn: T) { return fn; } + let f = wrap(function() { return typeof f; }); return f(); + `.expectToMatchJsResult(); }); test("Recursive arrow function", () => { - const result = util.transpileAndExecute(`let f = () => typeof f; return f();`); - - expect(result).toBe("function"); + util.testFunction` + let f = () => typeof f; + return f(); + `.expectToMatchJsResult(); }); test("Wrapped recursive arrow function", () => { - const result = util.transpileAndExecute( - `function wrap(fn: T) { return fn; } - let f = wrap(() => typeof f); return f();` - ); - - expect(result).toBe("function"); + util.testFunction` + function wrap(fn: T) { return fn; } + let f = wrap(() => typeof f); + return f(); + `.expectToMatchJsResult(); }); test("Object method declaration", () => { - const result = util.transpileAndExecute( - `let o = { v: 4, m(i: number): number { return this.v * i; } }; return o.m(3);` - ); - expect(result).toBe(12); + util.testFunction` + let o = { v: 4, m(i: number): number { return this.v * i; } }; + return o.m(3); + `.expectToMatchJsResult(); }); -test.each([{ args: ["bar"], expectResult: "foobar" }, { args: ["baz", "bar"], expectResult: "bazbar" }])( +test.each([{ args: ["bar"], expected: "foobar" }, { args: ["baz", "bar"], expected: "bazbar" }])( "Function overload (%p)", - ({ args, expectResult }) => { - const code = ` - class O { - prop = "foo"; - method(s: string): string; - method(this: void, s1: string, s2: string): string; - method(s1: string) { - if (typeof this === "string") { - return this + s1; + ({ args, expected }) => { + util.testFunction` + class O { + prop = "foo"; + method(s: string): string; + method(this: void, s1: string, s2: string): string; + method(s1: string) { + if (typeof this === "string") { + return this + s1; + } + return this.prop + s1; } - return this.prop + s1; - } - }; - const o = new O(); - return o.method(${args.map(a => '"' + a + '"').join(", ")}); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(expectResult); + }; + const o = new O(); + return o.method(${util.valuesToString(args)}); + `.expectToEqual(expected); } ); test("Nested Function", () => { - const code = ` + util.testFunction` class C { private prop = "bar"; public outer() { @@ -324,81 +294,70 @@ test("Nested Function", () => { } let c = new C(); return c.outer(); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("foobar"); + `.expectToMatchJsResult(); }); test.each([{ s1: "abc", s2: "abc" }, { s1: "abc", s2: "def" }])("Dot vs Colon method call (%p)", ({ s1, s2 }) => { - const result = util.transpileAndExecute(` - class MyClass { - dotMethod(this: void, s: string) { - return s; - } - colonMethod(s: string) { - return s; - } + util.testFunction` + class MyClass { + dotMethod(this: void, s: string) { + return s; } - const inst = new MyClass(); - return inst.dotMethod("${s1}") == inst.colonMethod("${s2}"); - `); - expect(result).toBe(s1 === s2); + colonMethod(s: string) { + return s; + } + } + const inst = new MyClass(); + return inst.dotMethod("${s1}") == inst.colonMethod("${s2}"); + `.expectToMatchJsResult(); }); test("Element access call", () => { - const code = ` + util.testFunction` class C { prop = "bar"; method(s: string) { return s + this.prop; } } const c = new C(); return c['method']("foo"); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("foobar"); + `.expectToMatchJsResult(); }); test("Element access call no args", () => { - const code = ` - class C { + util.testFunction` + class C { prop = "bar"; method() { return this.prop; } } const c = new C(); return c['method'](); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("bar"); + `.expectToMatchJsResult(); }); test("Complex element access call", () => { - const code = ` + util.testFunction` class C { prop = "bar"; method(s: string) { return s + this.prop; } } function getC() { return new C(); } return getC()['method']("foo"); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("foobar"); + `.expectToMatchJsResult(); }); test("Complex element access call no args", () => { - const code = ` + util.testFunction` class C { prop = "bar"; method() { return this.prop; } } function getC() { return new C(); } return getC()['method'](); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("bar"); + `.expectToMatchJsResult(); }); test("Complex element access call statement", () => { - const code = ` + util.testFunction` let foo: string; class C { prop = "bar"; @@ -407,55 +366,41 @@ test("Complex element access call statement", () => { function getC() { return new C(); } getC()['method']("foo"); return foo; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("foobar"); + `.expectToMatchJsResult(); }); -test.each([{ iterations: 1, expectedResult: 1 }, { iterations: 2, expectedResult: 42 }])( - "Generator functions value (%p)", - ({ iterations, expectedResult }) => { - const code = ` - function* seq(value: number) { - let a = yield value + 1; - return 42; - } - const gen = seq(0); - let ret: number; - for(let i = 0; i < ${iterations}; ++i) - { - ret = gen.next(i).value; - } - return ret; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(expectedResult); - } -); +test.each([1, 2])("Generator functions value (%p)", iterations => { + util.testFunction` + function* seq(value: number) { + let a = yield value + 1; + return 42; + } + const gen = seq(0); + let ret: number; + for(let i = 0; i < ${iterations}; ++i) { + ret = gen.next(i).value; + } + return ret; + `.expectToMatchJsResult(); +}); -test.each([{ iterations: 1, expectedResult: false }, { iterations: 2, expectedResult: true }])( - "Generator functions done (%p)", - ({ iterations, expectedResult }) => { - const code = ` - function* seq(value: number) { - let a = yield value + 1; - return 42; - } - const gen = seq(0); - let ret: boolean; - for(let i = 0; i < ${iterations}; ++i) - { - ret = gen.next(i).done; - } - return ret; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(expectedResult); - } -); +test.each([1, 2])("Generator functions done (%p)", iterations => { + util.testFunction` + function* seq(value: number) { + let a = yield value + 1; + return 42; + } + const gen = seq(0); + let ret: boolean; + for(let i = 0; i < ${iterations}; ++i) { + ret = gen.next(i).done; + } + return ret; + `.expectToMatchJsResult(); +}); test("Generator for..of", () => { - const code = ` + util.testFunction` function* seq() { yield(1); yield(2); @@ -463,44 +408,45 @@ test("Generator for..of", () => { return 4; } let result = 0; - for(let i of seq()) - { + for(let i of seq()) { result = result * 10 + i; } return result - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(123); + `.expectToMatchJsResult(); }); test("Function local overriding export", () => { - const code = ` + util.testModule` export const foo = 5; function bar(foo: number) { return foo; } export const result = bar(7); - `; - expect(util.transpileExecuteAndReturnExport(code, "result")).toBe(7); + ` + .export("result") + .expectToMatchJsResult(); }); test("Function using global as this", () => { - const code = ` + const tsHeader = ` var foo = "foo"; function bar(this: any) { return this.foo; } `; - expect(util.transpileAndExecute("return foo;", undefined, undefined, code)).toBe("foo"); + + util.testFunction` + return foo; + ` + .tsHeader(tsHeader) + .expectToMatchJsResult(); }); test("Function rest binding pattern", () => { - const result = util.transpileAndExecute(` + util.testFunction` function bar(foo: string, ...[bar, baz]: [string, string]) { return bar + baz + foo; } return bar("abc", "def", "xyz"); - `); - - expect(result).toBe("defxyzabc"); + `.expectToMatchJsResult(); }); diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index b37b520fd..b5da74998 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -411,15 +411,17 @@ test("Tuple Return vs Non-Tuple Return Overload", () => { end `; - util.testModule` + const tsHeader = ` declare function fn(this: void, a: number): [number, number]; /** @tupleReturn */ declare function fn(this: void, a: string, b: string): [string, string]; + `; + util.testFunction` const [a, b] = fn(3); const [c, d] = fn("foo", "bar"); - export const result = (a + b) + c + d; + return (a + b) + c + d; ` + .tsHeader(tsHeader) .luaHeader(luaHeader) - .export("result") .expectToEqual("7foobar"); }); diff --git a/test/util.ts b/test/util.ts index e4888ac5e..90ea02173 100644 --- a/test/util.ts +++ b/test/util.ts @@ -257,7 +257,7 @@ export class TestBuilder { lib: ["lib.esnext.d.ts"], experimentalDecorators: true, }; - public options(options: tstl.CompilerOptions): this { + public options(options: tstl.CompilerOptions = {}): this { expect(this._hasTsCode).toBe(false); Object.assign(this._options, options); return this; @@ -319,23 +319,18 @@ export class TestBuilder { const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); expect(mainFile).toBeDefined(); - return `return JSONStringify((function() - ${this._luaHeader} - ${mainFile!.lua!} -end)()${this._accessor})`; + const header = this._luaHeader ? `${this._luaHeader.trimRight()}\n` : ""; + return header + mainFile!.lua!.trimRight(); } @memoize private getLuaCodeWithWrapper(): string { let code = this.getMainLuaCodeChunk(); if (code.includes('require("lualib_bundle")')) { - code = `package.preload.lualib_bundle = function() - ${lualibContent} -end -${code}`; + code = `package.preload.lualib_bundle = function()\n${lualibContent}\nend\n${code}`; } - return minimalTestLib + code; + return `${minimalTestLib}\nreturn JSONStringify((function()\n${code}\nend)()${this._accessor})`; } @memoize @@ -347,7 +342,8 @@ ${code}`; if (status === lua.LUA_OK) { if (lua.lua_isstring(L, -1)) { - return JSON.parse(lua.lua_tojsstring(L, -1)); + const result = JSON.parse(lua.lua_tojsstring(L, -1)); + return result === null ? undefined : result; } else { const returnType = to_jsstring(lua.lua_typename(L, lua.lua_type(L, -1))); throw new Error(`Unsupported Lua return type: ${returnType}`); @@ -421,7 +417,11 @@ ${code}`; this.expectToHaveNoDiagnostics(); const luaResult = this.getLuaExecutionResult(); const jsResult = this.getJsExecutionResult(); - expect(luaResult).toEqual(jsResult); + // tslint:disable-next-line: no-null-keyword + if (luaResult !== undefined || jsResult != null) { + expect(luaResult).toEqual(jsResult); + } + if (!allowErrors && luaResult instanceof ExecutionError) { throw luaResult; } @@ -462,16 +462,23 @@ class ModuleTestBuilder extends TestBuilder { } class FunctionTestBuilder extends TestBuilder { - protected _accessor = ".main()"; + protected _accessor = ".__main()"; public getTsCode(): string { - return `export function main() {${super.getTsCode()}}`; + return `${this._tsHeader} export function __main() {${super.getTsCode()}}`; + } + + // TODO: Use testModule in these cases? + private _tsHeader = ""; + public tsHeader(tsHeader: string): this { + this._tsHeader = tsHeader; + return this; } } class ExpressionTestBuilder extends TestBuilder { - protected _accessor = ".main()"; + protected _accessor = ".__result"; public getTsCode(): string { - return `export function main() {return ${super.getTsCode()};}`; + return `export const __result = ${super.getTsCode()};`; } } From b3fe14322a5b981e97adbdceb7ed99b708f5293a Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 30 May 2019 13:14:39 +0500 Subject: [PATCH 06/64] Update require tests --- test/unit/require.spec.ts | 86 ++++++++++++++++++--------------------- test/unit/tuples.spec.ts | 2 +- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/test/unit/require.spec.ts b/test/unit/require.spec.ts index 476d3148d..974eaed1c 100644 --- a/test/unit/require.spec.ts +++ b/test/unit/require.spec.ts @@ -2,123 +2,117 @@ import * as ts from "typescript"; import * as util from "../util"; const requireRegex = /require\("(.*?)"\)/; +const expectToRequire = (expected: string): util.TapCallback => builder => { + const match = requireRegex.exec(builder.getMainLuaCodeChunk()); + if (util.expectToBeDefined(match)) { + expect(match[1]).toBe(expected); + } +}; test.each([ { filePath: "main.ts", usedPath: "./folder/Module", - expectedPath: "folder.Module", + expected: "folder.Module", options: { rootDir: "." }, throwsError: false, }, { filePath: "main.ts", usedPath: "./folder/Module", - expectedPath: "folder.Module", + expected: "folder.Module", options: { rootDir: "./" }, throwsError: false, }, { filePath: "src/main.ts", usedPath: "./folder/Module", - expectedPath: "src.folder.Module", + expected: "src.folder.Module", options: { rootDir: "." }, throwsError: false, }, { filePath: "main.ts", usedPath: "folder/Module", - expectedPath: "folder.Module", + expected: "folder.Module", options: { rootDir: ".", baseUrl: "." }, throwsError: false, }, { filePath: "main.ts", usedPath: "folder/Module", - expectedPath: "folder.Module", + expected: "folder.Module", options: { rootDir: "./", baseUrl: "." }, throwsError: false, }, { filePath: "src/main.ts", usedPath: "./folder/Module", - expectedPath: "folder.Module", + expected: "folder.Module", options: { rootDir: "src" }, throwsError: false, }, { filePath: "src/main.ts", usedPath: "./folder/Module", - expectedPath: "folder.Module", + expected: "folder.Module", options: { rootDir: "./src" }, throwsError: false, }, { filePath: "main.ts", usedPath: "../Module", - expectedPath: "", + expected: "", options: { rootDir: "./src" }, throwsError: true, }, { filePath: "src/dir/main.ts", usedPath: "../Module", - expectedPath: "Module", + expected: "Module", options: { rootDir: "./src" }, throwsError: false, }, { filePath: "src/dir/dir/main.ts", usedPath: "../../dir/Module", - expectedPath: "dir.Module", + expected: "dir.Module", options: { rootDir: "./src" }, throwsError: false, }, -])( - "require paths root from --baseUrl or --rootDir (%p)", - ({ filePath, usedPath, expectedPath, options, throwsError }) => { - const builder = util.testModule` - import * as module from "${usedPath}"; - module; - `; - - builder.options(options).setMainFileName(filePath); +])("require paths root from --baseUrl or --rootDir (%p)", ({ filePath, usedPath, expected, options, throwsError }) => { + const builder = util.testModule` + import * as module from "${usedPath}"; + module; + `; - if (throwsError) { - builder.expectToHaveDiagnostics(); - } else { - const match = requireRegex.exec(builder.getMainLuaCodeChunk()); + builder.options(options).setMainFileName(filePath); - if (util.expectToBeDefined(match)) { - expect(match[1]).toBe(expectedPath); - } - } + if (throwsError) { + builder.expectToHaveDiagnostics(); + } else { + builder.tap(expectToRequire(expected)); } -); +}); -test.each([{ comment: "", expectedPath: "src.fake" }, { comment: "/** @noResolution */", expectedPath: "fake" }])( +test.each([{ comment: "", expected: "src.fake" }, { comment: "/** @noResolution */", expected: "fake" }])( "noResolution on ambient modules causes no path alterations (%p)", - ({ comment, expectedPath }) => { - const builder = util.testModule` + ({ comment, expected }) => { + util.testModule` import * as fake from "fake"; fake; - `; - - builder.setMainFileName("src/main.ts").addExtraFile("module.d.ts", `${comment} declare module "fake" {}`); - const match = requireRegex.exec(builder.getMainLuaCodeChunk()); - - if (util.expectToBeDefined(match)) { - expect(match[1]).toBe(expectedPath); - } + ` + .setMainFileName("src/main.ts") + .addExtraFile("module.d.ts", `${comment} declare module "fake" {}`) + .tap(expectToRequire(expected)); } ); test("ImportEquals declaration require", () => { - const input = `import foo = require("./foo/bar"); foo;`; - - const lua = util.transpileString(input, { module: ts.ModuleKind.CommonJS }); - const match = requireRegex.exec(lua); - if (util.expectToBeDefined(match)) { - expect(match[1]).toBe("foo.bar"); - } + util.testModule` + import foo = require("./foo/bar"); + foo; + ` + .options({ module: ts.ModuleKind.CommonJS }) + .tap(expectToRequire("foo.bar")); }); diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index b5da74998..b23d6a38d 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -54,7 +54,7 @@ test("Tuple Destruct", () => { `.expectToMatchJsResult(); }); -const expectNoUnpack: util.TapCallback = b => expect(b.getMainLuaCodeChunk()).not.toContain("unpack"); +const expectNoUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).not.toContain("unpack"); test("Tuple Destruct Array Literal", () => { util.testFunction` From e2888e134c42ab7fce6d744cbb8dd7afa8de0c0d Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 30 May 2019 13:50:03 +0500 Subject: [PATCH 07/64] Fix tests on node 8.5.0 --- test/util.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/util.ts b/test/util.ts index 90ea02173..8f16987b7 100644 --- a/test/util.ts +++ b/test/util.ts @@ -253,7 +253,7 @@ export class TestBuilder { luaTarget: tstl.LuaTarget.Lua53, noHeader: true, skipLibCheck: true, - target: ts.ScriptTarget.ESNext, + target: ts.ScriptTarget.ES2017, lib: ["lib.esnext.d.ts"], experimentalDecorators: true, }; @@ -366,12 +366,13 @@ export class TestBuilder { const { transpiledFiles } = this.getJsResult(); const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); expect(mainFile).toBeDefined(); - return mainFile!.js! + `;exports = exports${this._accessor}`; + return mainFile!.js! + `;module.exports = exports${this._accessor}`; } @memoize public getJsExecutionResult(): any { - const context = vm.createContext({ exports: {} }); + const exports = {}; + const context = vm.createContext({ exports, module: { exports } }); try { return vm.runInContext(this.getJsCode(), context); } catch (error) { From 90cc57fe1edb7dd5ec2aa4091c348f3905eaeede Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 30 May 2019 16:34:51 +0500 Subject: [PATCH 08/64] Change float precision in json test util --- test/json.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/json.lua b/test/json.lua index bac05cda3..acbf4d304 100644 --- a/test/json.lua +++ b/test/json.lua @@ -116,7 +116,7 @@ local function encode_number(val) if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end - return string.format("%.17g", val) + return string.format("%.16g", val) end From e7e72ca4293778d8360cb9eea0f6a6ede1248190 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 30 May 2019 17:22:04 +0500 Subject: [PATCH 09/64] Transform some more tests --- test/unit/functions.spec.ts | 6 +- test/unit/lualib/set.spec.ts | 134 +++++++++-------- test/unit/lualib/symbol.spec.ts | 48 ++---- test/unit/string.spec.ts | 251 ++++++++++++-------------------- test/util.ts | 10 +- 5 files changed, 178 insertions(+), 271 deletions(-) diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index a284f24ff..e1444866b 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -435,11 +435,7 @@ test("Function using global as this", () => { } `; - util.testFunction` - return foo; - ` - .tsHeader(tsHeader) - .expectToMatchJsResult(); + util.testExpression`foo`.tsHeader(tsHeader).expectToMatchJsResult(); }); test("Function rest binding pattern", () => { diff --git a/test/unit/lualib/set.spec.ts b/test/unit/lualib/set.spec.ts index 6018d42cf..bac8773f8 100644 --- a/test/unit/lualib/set.spec.ts +++ b/test/unit/lualib/set.spec.ts @@ -1,124 +1,122 @@ import * as util from "../../util"; test("set constructor", () => { - const result = util.transpileAndExecute(`let myset = new Set(); return myset.size;`); - - expect(result).toBe(0); + util.testFunction` + let myset = new Set(); + return myset.size; + `.expectToMatchJsResult(); }); test("set iterable constructor", () => { - const result = util.transpileAndExecute( - `let myset = new Set(["a", "b"]); - return myset.has("a") || myset.has("b");` - ); - - expect(result).toBe(true); + util.testFunction` + let myset = new Set(["a", "b"]); + return myset.has("a") || myset.has("b"); + `.expectToMatchJsResult(); }); test("set iterable constructor set", () => { - const result = util.transpileAndExecute( - `let myset = new Set(new Set(["a", "b"])); - return myset.has("a") || myset.has("b");` - ); - - expect(result).toBe(true); + util.testFunction` + let myset = new Set(new Set(["a", "b"])); + return myset.has("a") || myset.has("b"); + `.expectToMatchJsResult(); }); test("set add", () => { - const has = util.transpileAndExecute(`let myset = new Set(); myset.add("a"); return myset.has("a");`); - expect(has).toBe(true); + util.testFunction` + let myset = new Set(); + myset.add("a"); + return myset.has("a"); + `.expectToMatchJsResult(); }); test("set clear", () => { - const setTS = `let myset = new Set(["a", "b"]); myset.clear();`; - const size = util.transpileAndExecute(setTS + `return myset.size;`); - expect(size).toBe(0); - - const contains = util.transpileAndExecute(setTS + `return !myset.has("a") && !myset.has("b");`); - expect(contains).toBe(true); + util.testFunction` + let myset = new Set(["a", "b"]); + myset.clear(); + return { size: myset.size, has: !myset.has("a") && !myset.has("b") }; + `.expectToMatchJsResult(); }); test("set delete", () => { - const setTS = `let myset = new Set(["a", "b"]); myset.delete("a");`; - const contains = util.transpileAndExecute(setTS + `return myset.has("b") && !myset.has("a");`); - expect(contains).toBe(true); + util.testFunction` + let myset = new Set(["a", "b"]); + myset.delete("a"); + return myset.has("b") && !myset.has("a"); + `.expectToMatchJsResult(); }); test("set entries", () => { - const result = util.transpileAndExecute( - `let myset = new Set([5, 6, 7]); + util.testFunction` + let myset = new Set([5, 6, 7]); let count = 0; for (var [key, value] of myset.entries()) { count += key + value; } - return count;` - ); - - expect(result).toBe(36); + return count; + `.expectToMatchJsResult(); }); test("set foreach", () => { - const result = util.transpileAndExecute( - `let myset = new Set([2, 3, 4]); + util.testFunction` + let myset = new Set([2, 3, 4]); let count = 0; myset.forEach(i => { count += i; }); - return count;` - ); - expect(result).toBe(9); + return count; + `.expectToMatchJsResult(); }); test("set foreach keys", () => { - const result = util.transpileAndExecute( - `let myset = new Set([2, 3, 4]); + util.testFunction` + let myset = new Set([2, 3, 4]); let count = 0; myset.forEach((value, key) => { count += key; }); - return count;` - ); - - expect(result).toBe(9); + return count; + `.expectToMatchJsResult(); }); test("set has", () => { - const contains = util.transpileAndExecute(`let myset = new Set(["a", "c"]); return myset.has("a");`); - expect(contains).toBe(true); + util.testFunction` + let myset = new Set(["a", "c"]); + return myset.has("a"); + `.expectToMatchJsResult(); }); test("set has false", () => { - const contains = util.transpileAndExecute(`let myset = new Set(); return myset.has("a");`); - expect(contains).toBe(false); + util.testFunction` + let myset = new Set(); + return myset.has("a"); + `.expectToMatchJsResult(); }); test("set has null", () => { - const contains = util.transpileAndExecute(`let myset = new Set(["a", "c"]); return myset.has(null);`); - expect(contains).toBe(false); + util.testFunction` + let myset = new Set(["a", "c"]); + return myset.has(null); + `.expectToMatchJsResult(); }); test("set keys", () => { - const result = util.transpileAndExecute( - `let myset = new Set([5, 6, 7]); + util.testFunction` + let myset = new Set([5, 6, 7]); let count = 0; for (var key of myset.keys()) { count += key; } - return count;` - ); - - expect(result).toBe(18); + return count; + `.expectToMatchJsResult(); }); test("set values", () => { - const result = util.transpileAndExecute( - `let myset = new Set([5, 6, 7]); + util.testFunction` + let myset = new Set([5, 6, 7]); let count = 0; for (var value of myset.values()) { count += value; } - return count;` - ); - - expect(result).toBe(18); + return count; + `.expectToMatchJsResult(); }); test.each([ - { code: `let m = new Set(); return m.size;`, expected: 0 }, - { code: `let m = new Set(); m.add(1); return m.size;`, expected: 1 }, - { code: `let m = new Set([1, 2]); return m.size;`, expected: 2 }, - { code: `let m = new Set([1, 2]); m.clear(); return m.size;`, expected: 0 }, - { code: `let m = new Set([1, 2]); m.delete(2); return m.size;`, expected: 1 }, -])("set size (%p)", ({ code, expected }) => { - expect(util.transpileAndExecute(code)).toBe(expected); + `let m = new Set()`, + `let m = new Set(); m.add(1)`, + `let m = new Set([1, 2])`, + `let m = new Set([1, 2]); m.clear()`, + `let m = new Set([1, 2]); m.delete(2)`, +])("set size (%p)", code => { + util.testFunction`${code}; return m.size`.expectToMatchJsResult(); }); diff --git a/test/unit/lualib/symbol.spec.ts b/test/unit/lualib/symbol.spec.ts index 857a9918a..75da67a59 100644 --- a/test/unit/lualib/symbol.spec.ts +++ b/test/unit/lualib/symbol.spec.ts @@ -1,60 +1,38 @@ import * as util from "../../util"; -test.each([{}, { description: 1 }, { description: "name" }])("symbol.toString() (%p)", ({ description }) => { - const result = util.transpileAndExecute(` - return Symbol(${JSON.stringify(description)}).toString(); - `); - - expect(result).toBe(`Symbol(${description || ""})`); +test.each([undefined, 1, "name"])("symbol.toString() (%p)", description => { + util.testExpression`Symbol(${util.valueToString(description)}).toString()`.expectToMatchJsResult(); }); -test.each([{}, { description: 1 }, { description: "name" }])("symbol.description (%p)", ({ description }) => { - const result = util.transpileAndExecute(` - return Symbol(${JSON.stringify(description)}).description; - `); - - expect(result).toBe(description); +test.each([undefined, 1, "name"])("symbol.description (%p)", description => { + // TODO: Supported since node 11 + util.testExpression`Symbol(${util.valueToString(description)}).description`.expectToEqual(description); }); test("symbol uniqueness", () => { - const result = util.transpileAndExecute(` - return Symbol("a") === Symbol("a"); - `); - - expect(result).toBe(false); + util.testExpression`Symbol("a") === Symbol("a")`.expectToMatchJsResult(); }); test("Symbol.for", () => { - const result = util.transpileAndExecute(` - return Symbol.for("name").description; - `); - - expect(result).toBe("name"); + // TODO: Supported since node 11 + util.testExpression(`Symbol.for("name").description`).expectToEqual("name"); }); test("Symbol.for non-uniqueness", () => { - const result = util.transpileAndExecute(` - return Symbol.for("a") === Symbol.for("a"); - `); - - expect(result).toBe(true); + util.testExpression`Symbol.for("a") === Symbol.for("a")`.expectToMatchJsResult(); }); test("Symbol.keyFor", () => { - const result = util.transpileAndExecute(` + util.testFunction` const sym = Symbol.for("a"); Symbol.for("b"); return Symbol.keyFor(sym); - `); - - expect(result).toBe("a"); + `.expectToMatchJsResult(); }); test("Symbol.keyFor empty", () => { - const result = util.transpileAndExecute(` + util.testFunction` Symbol.for("a"); return Symbol.keyFor(Symbol()); - `); - - expect(result).toBe(undefined); + `.expectToMatchJsResult(); }); diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index 910303891..d37f9fa90 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -1,26 +1,27 @@ import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; -test("Unsuported string function", () => { - expect(() => { - util.transpileString(`return "test".testThisIsNoMember()`); - }).toThrowExactError(TSTLErrors.UnsupportedProperty("string", "testThisIsNoMember", util.nodeStub)); +test("Unsupported string function", () => { + util.testExpression`"test".testThisIsNoMember()` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("string", "testThisIsNoMember", util.nodeStub)); }); -test("Suported lua string function", () => { - expect( - util.transpileAndExecute(`return "test".upper()`, undefined, undefined, `interface String { upper(): string; }`) - ).toBe("TEST"); -}); +test("Supported lua string function", () => { + const tsHeader = ` + declare global { + interface String { + upper(): string; + } + } + `; -test.each([{ inp: [] }, { inp: [65] }, { inp: [65, 66] }, { inp: [65, 66, 67] }])( - "String.fromCharCode (%p)", - ({ inp }) => { - const result = util.transpileAndExecute(`return String.fromCharCode(${inp.toString()})`); + util.testExpression`"test".upper()`.tsHeader(tsHeader).expectToEqual("TEST"); +}); - expect(result).toBe(String.fromCharCode(...inp)); - } -); +test.each([[], [65], [65, 66], [65, 66, 67]])("String.fromCharCode (%p)", (...args) => { + util.testExpression`String.fromCharCode(${util.valuesToString(args)})`.expectToMatchJsResult(); +}); test.each([ { a: 12, b: 23, c: 43 }, @@ -30,18 +31,14 @@ test.each([ { a: "test", b: 42, c: true }, { a: false, b: 42, c: 12 }, ])("Template Strings (%p)", ({ a, b, c }) => { - const a1 = typeof a === "string" ? `'${a}'` : a; - const b1 = typeof b === "string" ? `'${b}'` : b; - const c1 = typeof c === "string" ? `'${c}'` : c; - - const result = util.transpileAndExecute(` - let a = ${a1}; - let b = ${b1}; - let c = ${c1}; + util.testFunction` + let a = ${a}; + let b = ${b}; + let c = ${c}; return \`${a} ${b} test ${c}\`; - `); - - expect(result).toBe(`${a} ${b} test ${c}`); + ` + .serialize() + .expectToMatchJsResult(); }); test.each([ @@ -52,18 +49,14 @@ test.each([ { a: "test", b: 42, c: true }, { a: false, b: 42, c: 12 }, ])("String Concat Operator (%p)", ({ a, b, c }) => { - const a1 = typeof a === "string" ? `'${a}'` : a; - const b1 = typeof b === "string" ? `'${b}'` : b; - const c1 = typeof c === "string" ? `'${c}'` : c; - - const result = util.transpileAndExecute(` - let a = ${a1}; - let b = ${b1}; - let c = ${c1}; + util.testFunction` + let a = ${a}; + let b = ${b}; + let c = ${c}; return a + " " + b + " test " + c; - `); - - expect(result).toBe(`${a} ${b} test ${c}`); + ` + .serialize() + .expectToMatchJsResult(); }); test.each([ @@ -72,9 +65,7 @@ test.each([ { input: "abcde", index: 0 }, { input: "a", index: 0 }, ])("string index (%p)", ({ input, index }) => { - const result = util.transpileAndExecute(`return "${input}"[${index}];`); - - expect(result).toBe(input[index]); + util.testExpression`${input}[${index}]`.serialize().expectToMatchJsResult(); }); test.each([ @@ -90,41 +81,23 @@ test.each([ { inp: "hello test", searchValue: "test", replaceValue: (): string => "%a" }, { inp: "aaa", searchValue: "a", replaceValue: "b" }, ])("string.replace (%p)", ({ inp, searchValue, replaceValue }) => { - const replaceValueString = - typeof replaceValue === "string" ? JSON.stringify(replaceValue) : replaceValue.toString(); - const result = util.transpileAndExecute(`return "${inp}".replace("${searchValue}", ${replaceValueString});`); - - // https://github.com/Microsoft/TypeScript/issues/22378 - if (typeof replaceValue === "string") { - expect(result).toBe(inp.replace(searchValue, replaceValue)); - } else { - expect(result).toBe(inp.replace(searchValue, replaceValue)); - } + util.testExpression`"${inp}".replace(${util.valuesToString([searchValue, replaceValue])})`.expectToMatchJsResult(); }); -test.each([ - { inp: ["", ""], expected: "" }, - { inp: ["hello", "test"], expected: "hellotest" }, - { inp: ["hello", "test", "bye"], expected: "hellotestbye" }, - { inp: ["hello", 42], expected: "hello42" }, - { inp: [42, "hello"], expected: "42hello" }, -])("string.concat[+] (%p)", ({ inp, expected }) => { - const concatStr = inp.map(elem => (typeof elem === "string" ? `"${elem}"` : elem)).join(" + "); - - const result = util.transpileAndExecute(`return ${concatStr}`); - - expect(result).toBe(expected); -}); +test.each([["", ""], ["hello", "test"], ["hello", "test", "bye"], ["hello", 42], [42, "hello"]])( + "string.concat[+] (%p)", + (...elements) => { + util.testExpression(elements.map(e => util.valueToString(e)).join(" + ")); + } +); test.each([ - { str: "", param: ["", ""] }, - { str: "hello", param: ["test"] }, - { str: "hello", param: [] }, - { str: "hello", param: ["test", "bye"] }, -])("string.concatFct (%p)", ({ str, param }) => { - const paramStr = param.map(elem => `"${elem}"`).join(", "); - const result = util.transpileAndExecute(`return "${str}".concat(${paramStr})`); - expect(result).toBe(str.concat(...param)); + { str: "", args: ["", ""] }, + { str: "hello", args: ["test"] }, + { str: "hello", args: [] }, + { str: "hello", args: ["test", "bye"] }, +])("string.concatFct (%p)", ({ str, args }) => { + util.testExpression`"${str}".concat(${util.valuesToString(args)})`.expectToMatchJsResult(); }); test.each([ @@ -133,9 +106,7 @@ test.each([ { inp: "hello test", searchValue: "h" }, { inp: "hello test", searchValue: "invalid" }, ])("string.indexOf (%p)", ({ inp, searchValue }) => { - const result = util.transpileAndExecute(`return "${inp}".indexOf("${searchValue}")`); - - expect(result).toBe(inp.indexOf(searchValue)); + util.testExpression`${inp}.indexOf(${searchValue})`.serialize().expectToMatchJsResult(); }); test.each([ @@ -144,65 +115,50 @@ test.each([ { inp: "hello test", searchValue: "t", offset: 7 }, { inp: "hello test", searchValue: "h", offset: 4 }, ])("string.indexOf with offset (%p)", ({ inp, searchValue, offset }) => { - const result = util.transpileAndExecute(`return "${inp}".indexOf("${searchValue}", ${offset})`); - - expect(result).toBe(inp.indexOf(searchValue, offset)); + util.testExpression`${inp}.indexOf(${searchValue}, ${offset})`.serialize().expectToMatchJsResult(); }); test.each([{ inp: "hello test", searchValue: "t", x: 4, y: 3 }, { inp: "hello test", searchValue: "h", x: 3, y: 4 }])( "string.indexOf with offset expression (%p)", ({ inp, searchValue, x, y }) => { - const result = util.transpileAndExecute(`return "${inp}".indexOf("${searchValue}", 2 > 1 && ${x} || ${y})`); - - expect(result).toBe(inp.indexOf(searchValue, x)); + util.testExpression`${inp}.indexOf(${searchValue}, 2 > 1 && ${x} || ${y})`.serialize().expectToMatchJsResult(); } ); test.each([ - { inp: "hello test" }, - { inp: "hello test", start: 0 }, - { inp: "hello test", start: 1 }, - { inp: "hello test", start: 1, end: 2 }, - { inp: "hello test", start: 1, end: 5 }, -])("string.slice (%p)", ({ inp, start, end }) => { - const paramStr = start ? (end ? `${start}, ${end}` : `${start}`) : ""; - const result = util.transpileAndExecute(`return "${inp}".slice(${paramStr})`); - - expect(result).toBe(inp.slice(start, end)); + { inp: "hello test", args: [] }, + { inp: "hello test", args: [0] }, + { inp: "hello test", args: [1] }, + { inp: "hello test", args: [1, 2] }, + { inp: "hello test", args: [1, 5] }, +])("string.slice (%p)", ({ inp, args }) => { + util.testExpression`"${inp}".slice(${util.valuesToString(args)})`.expectToMatchJsResult(); }); test.each([ - { inp: "hello test", start: 0 }, - { inp: "hello test", start: 1 }, - { inp: "hello test", start: 1, end: 2 }, - { inp: "hello test", start: 1, end: 5 }, -])("string.substring (%p)", ({ inp, start, end }) => { - const paramStr = end ? `${start}, ${end}` : `${start}`; - const result = util.transpileAndExecute(`return "${inp}".substring(${paramStr})`); - - expect(result).toBe(inp.substring(start, end)); + { inp: "hello test", args: [0] }, + { inp: "hello test", args: [1] }, + { inp: "hello test", args: [1, 2] }, + { inp: "hello test", args: [1, 5] }, +])("string.substring (%p)", ({ inp, args }) => { + util.testExpression`"${inp}".substring(${util.valuesToString(args)})`.expectToMatchJsResult(); }); test.each([{ inp: "hello test", start: 1, ignored: 0 }, { inp: "hello test", start: 3, ignored: 0, end: 5 }])( "string.substring with expression (%p)", ({ inp, start, ignored, end }) => { const paramStr = `2 > 1 && ${start} || ${ignored}` + (end ? `, ${end}` : ""); - const result = util.transpileAndExecute(`return "${inp}".substring(${paramStr})`); - - expect(result).toBe(inp.substring(start, end)); + util.testExpression`"${inp}".substring(${paramStr})`.expectToMatchJsResult(); } ); test.each([ - { inp: "hello test", start: 0 }, - { inp: "hello test", start: 1 }, - { inp: "hello test", start: 1, end: 2 }, - { inp: "hello test", start: 1, end: 5 }, -])("string.substr (%p)", ({ inp, start, end }) => { - const paramStr = end ? `${start}, ${end}` : `${start}`; - const result = util.transpileAndExecute(`return "${inp}".substr(${paramStr})`); - - expect(result).toBe(inp.substr(start, end)); + { inp: "hello test", args: [0] }, + { inp: "hello test", args: [1] }, + { inp: "hello test", args: [1, 2] }, + { inp: "hello test", args: [1, 5] }, +])("string.substr (%p)", ({ inp, args }) => { + util.testExpression`"${inp}".substr(${util.valuesToString(args)})`.expectToMatchJsResult(); }); test.each([{ inp: "hello test", start: 1, ignored: 0 }, { inp: "hello test", start: 3, ignored: 0, end: 2 }])( @@ -216,21 +172,15 @@ test.each([{ inp: "hello test", start: 1, ignored: 0 }, { inp: "hello test", sta ); test.each(["", "h", "hello"])("string.length (%p)", input => { - const result = util.transpileAndExecute(`return "${input}".length`); - - expect(result).toBe(input.length); + util.testExpression`${input}.length`.serialize().expectToMatchJsResult(); }); test.each(["hello TEST"])("string.toLowerCase (%p)", inp => { - const result = util.transpileAndExecute(`return "${inp}".toLowerCase()`); - - expect(result).toBe(inp.toLowerCase()); + util.testExpression`${inp}.toLowerCase()`.serialize().expectToMatchJsResult(); }); test.each(["hello test"])("string.toUpperCase (%p)", inp => { - const result = util.transpileAndExecute(`return "${inp}".toUpperCase()`); - - expect(result).toBe(inp.toUpperCase()); + util.testExpression`${inp}.toUpperCase()`.serialize().expectToMatchJsResult(); }); test.each([ @@ -242,9 +192,7 @@ test.each([ { inp: "hello test", separator: "invalid" }, { inp: "hello test", separator: "hello test" }, ])("string.split (%p)", ({ inp, separator }) => { - const result = util.transpileAndExecute(`return JSONStringify("${inp}".split("${separator}"))`); - - expect(result).toBe(JSON.stringify(inp.split(separator))); + util.testExpression`${inp}.split(${separator})`.serialize().expectToMatchJsResult(); }); test.each([ @@ -253,17 +201,13 @@ test.each([ { inp: "hello test", index: 3 }, { inp: "hello test", index: 99 }, ])("string.charAt (%p)", ({ inp, index }) => { - const result = util.transpileAndExecute(`return "${inp}".charAt(${index})`); - - expect(result).toBe(inp.charAt(index)); + util.testExpression`${inp}.charAt(${index})`.serialize().expectToMatchJsResult(); }); test.each([{ inp: "hello test", index: 1 }, { inp: "hello test", index: 2 }, { inp: "hello test", index: 3 }])( "string.charCodeAt (%p)", ({ inp, index }) => { - const result = util.transpileAndExecute(`return "${inp}".charCodeAt(${index})`); - - expect(result).toBe(inp.charCodeAt(index)); + util.testExpression`${inp}.charCodeAt(${index})`.serialize().expectToMatchJsResult(); } ); @@ -273,9 +217,7 @@ test.each([ { inp: "hello test", index: 3, ignored: 2 }, { inp: "hello test", index: 3, ignored: 99 }, ])("string.charAt with expression (%p)", ({ inp, index, ignored }) => { - const result = util.transpileAndExecute(`return "${inp}".charAt(2 > 1 && ${index} || ${ignored})`); - - expect(result).toBe(inp.charAt(index)); + util.testExpression`${inp}.charAt(2 > 1 && ${index} || ${ignored})`.serialize().expectToMatchJsResult(); }); test.each<{ inp: string; args: Parameters }>([ @@ -284,10 +226,7 @@ test.each<{ inp: string; args: Parameters }>([ { inp: "hello test", args: ["test"] }, { inp: "hello test", args: ["test", 6] }, ])("string.startsWith (%p)", ({ inp, args }) => { - const argsString = util.valuesToString(args); - const result = util.transpileAndExecute(`return "${inp}".startsWith(${argsString})`); - - expect(result).toBe(inp.startsWith(...args)); + util.testExpression`"${inp}".startsWith(${util.valuesToString(args)})`.expectToMatchJsResult(); }); test.each<{ inp: string; args: Parameters }>([ @@ -297,9 +236,7 @@ test.each<{ inp: string; args: Parameters }>([ { inp: "hello test", args: ["hello", 5] }, ])("string.endsWith (%p)", ({ inp, args }) => { const argsString = util.valuesToString(args); - const result = util.transpileAndExecute(`return "${inp}".endsWith(${argsString})`); - - expect(result).toBe(inp.endsWith(...args)); + util.testExpression`"${inp}".endsWith(${argsString})`.expectToMatchJsResult(); }); test.each([ @@ -310,32 +247,24 @@ test.each([ { inp: "hello test", count: 1.5 }, { inp: "hello test", count: 1.9 }, ])("string.repeat (%p)", ({ inp, count }) => { - const result = util.transpileAndExecute(`return "${inp}".repeat(${count})`); - - expect(result).toBe(inp.repeat(count)); + util.testExpression`"${inp}".repeat(${count})`.expectToMatchJsResult(); }); const padCases = [ - { inp: "foo", maxLength: 0 }, - { inp: "foo", maxLength: 3 }, - { inp: "foo", maxLength: 5 }, - { inp: "foo", maxLength: 4, fillString: " " }, - { inp: "foo", maxLength: 10, fillString: " " }, - { inp: "foo", maxLength: 5, fillString: "1234" }, - { inp: "foo", maxLength: 5.9, fillString: "1234" }, - { inp: "foo", maxLength: NaN }, + { inp: "foo", args: [0] }, + { inp: "foo", args: [3] }, + { inp: "foo", args: [5] }, + { inp: "foo", args: [4, " "] }, + { inp: "foo", args: [10, " "] }, + { inp: "foo", args: [5, "1234"] }, + { inp: "foo", args: [5.9, "1234"] }, + { inp: "foo", args: [NaN] }, ]; -test.each(padCases)("string.padStart (%p)", ({ inp, maxLength, fillString }) => { - const argsString = util.valuesToString([maxLength, fillString]); - const result = util.transpileAndExecute(`return "${inp}".padStart(${argsString})`); - - expect(result).toBe(inp.padStart(maxLength, fillString)); +test.each(padCases)("string.padStart (%p)", ({ inp, args }) => { + util.testExpression`"${inp}".padStart(${util.valuesToString(args)})`.expectToMatchJsResult(); }); -test.each(padCases)("string.padEnd (%p)", ({ inp, maxLength, fillString }) => { - const argsString = util.valuesToString([maxLength, fillString]); - const result = util.transpileAndExecute(`return "${inp}".padEnd(${argsString})`); - - expect(result).toBe(inp.padEnd(maxLength, fillString)); +test.each(padCases)("string.padEnd (%p)", ({ inp, args }) => { + util.testExpression`"${inp}".padEnd(${util.valuesToString(args)})`.expectToMatchJsResult(); }); diff --git a/test/util.ts b/test/util.ts index 8f16987b7..f4a6f0784 100644 --- a/test/util.ts +++ b/test/util.ts @@ -157,7 +157,7 @@ export function expectToBeDefined(subject: T | null | undefined): subject is } export const valueToString = (value: unknown) => - value === Infinity || value === -Infinity || (typeof value === "number" && Number.isNaN(value)) + (typeof value === "number" && (!Number.isFinite(value) || Number.isNaN(value))) || typeof value === "function" ? String(value) : JSON.stringify(value); @@ -479,7 +479,13 @@ class FunctionTestBuilder extends TestBuilder { class ExpressionTestBuilder extends TestBuilder { protected _accessor = ".__result"; public getTsCode(): string { - return `export const __result = ${super.getTsCode()};`; + return `${this._tsHeader} export const __result = ${super.getTsCode()};`; + } + + private _tsHeader = ""; + public tsHeader(tsHeader: string): this { + this._tsHeader = tsHeader; + return this; } } From 0b3be7da402f89540c186b8be4317185a4fa6c10 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 3 Jun 2019 01:11:16 +0500 Subject: [PATCH 10/64] Revert "Change float precision in json test util" This reverts commit 90cc57fe1edb7dd5ec2aa4091c348f3905eaeede. --- test/json.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/json.lua b/test/json.lua index acbf4d304..bac05cda3 100644 --- a/test/json.lua +++ b/test/json.lua @@ -116,7 +116,7 @@ local function encode_number(val) if val ~= val or val <= -math.huge or val >= math.huge then error("unexpected number value '" .. tostring(val) .. "'") end - return string.format("%.16g", val) + return string.format("%.17g", val) end From a17b32989aaa591cd033a599be0219fdb677a200 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 14 Jun 2019 07:28:14 +0500 Subject: [PATCH 11/64] Transform some more tests --- .../assignmentDestructuring.spec.ts.snap | 7 + test/unit/array.spec.ts | 208 +++---- test/unit/assignmentDestructuring.spec.ts | 36 +- test/unit/identifiers.spec.ts | 109 ++-- test/unit/json.spec.ts | 17 +- test/unit/lualib/array.spec.ts | 551 +++++++----------- test/unit/modules.spec.ts | 102 ++-- test/util.ts | 27 +- 8 files changed, 433 insertions(+), 624 deletions(-) create mode 100644 test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap diff --git a/test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap b/test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap new file mode 100644 index 000000000..c390eb1b0 --- /dev/null +++ b/test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Assignment destructuring [5.1] 1`] = `"local a, b = unpack(myFunc())"`; + +exports[`Assignment destructuring [5.2] 1`] = `"local a, b = table.unpack(myFunc())"`; + +exports[`Assignment destructuring [JIT] 1`] = `"local a, b = unpack(myFunc())"`; diff --git a/test/unit/array.spec.ts b/test/unit/array.spec.ts index 44d3cb3d6..432c01fbd 100644 --- a/test/unit/array.spec.ts +++ b/test/unit/array.spec.ts @@ -1,90 +1,81 @@ import * as util from "../util"; test("Array access", () => { - const result = util.transpileAndExecute( - `const arr: Array = [3,5,1]; - return arr[1];` - ); - expect(result).toBe(5); + util.testFunction` + const arr: Array = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); }); test("ReadonlyArray access", () => { - const result = util.transpileAndExecute( - `const arr: ReadonlyArray = [3,5,1]; - return arr[1];` - ); - expect(result).toBe(5); + util.testFunction` + const arr: ReadonlyArray = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); }); test("Array literal access", () => { - const result = util.transpileAndExecute( - `const arr: number[] = [3,5,1]; - return arr[1];` - ); - expect(result).toBe(5); + util.testFunction` + const arr: number[] = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); }); test("Readonly array literal access", () => { - const result = util.transpileAndExecute( - `const arr: readonly number[] = [3,5,1]; - return arr[1];` - ); - expect(result).toBe(5); + util.testFunction` + const arr: readonly number[] = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); }); test("Array union access", () => { - const result = util.transpileAndExecute( - `function makeArray(): number[] | string[] { return [3,5,1]; } + util.testFunction` + function makeArray(): number[] | string[] { return [3, 5, 1]; } const arr = makeArray(); - return arr[1];` - ); - expect(result).toBe(5); + return arr[1]; + `.expectToMatchJsResult(); }); test("Array union access with empty tuple", () => { - const result = util.transpileAndExecute( - `function makeArray(): number[] | [] { return [3,5,1]; } + util.testFunction` + function makeArray(): number[] | [] { return [3, 5, 1]; } const arr = makeArray(); - return arr[1];` - ); - expect(result).toBe(5); + return arr[1]; + `.expectToMatchJsResult(); }); test("Array union length", () => { - const result = util.transpileAndExecute( - `function makeArray(): number[] | string[] { return [3,5,1]; } + util.testFunction` + function makeArray(): number[] | string[] { return [3, 5, 1]; } const arr = makeArray(); - return arr.length;` - ); - expect(result).toBe(3); + return arr.length; + `.expectToMatchJsResult(); }); test("Array intersection access", () => { - const result = util.transpileAndExecute( - `type I = number[] & {foo: string}; + util.testFunction` + type I = number[] & { foo: string }; function makeArray(): I { - let t = [3,5,1]; + let t = [3, 5, 1]; (t as I).foo = "bar"; return (t as I); } const arr = makeArray(); - return arr[1];` - ); - expect(result).toBe(5); + return arr[1]; + `.expectToMatchJsResult(); }); test("Array intersection length", () => { - const result = util.transpileAndExecute( - `type I = number[] & {foo: string}; + util.testFunction` + type I = number[] & { foo: string }; function makeArray(): I { - let t = [3,5,1]; + let t = [3, 5, 1]; (t as I).foo = "bar"; return (t as I); } const arr = makeArray(); - return arr.length;` - ); - expect(result).toBe(3); + return arr.length; + `.expectToMatchJsResult(); }); test.each([ @@ -92,102 +83,89 @@ test.each([ { member: "name", expected: "array" }, { member: "length", expected: 1 }, ])("Derived array access (%p)", ({ member, expected }) => { - const luaHeader = `local arr = {name="array", firstElement=function(self) return self[1]; end};`; - const typeScriptHeader = ` - interface CustomArray extends Array{ - name:string, - firstElement():number; + const luaHeader = ` + local arr = { + name = "array", + firstElement = function(self) return self[1] end + } + `; + + const tsHeader = ` + interface CustomArray extends Array { + name: string; + firstElement(): number; }; + declare const arr: CustomArray; `; - const result = util.transpileAndExecute( - ` + util.testFunction` arr[0] = 3; - return arr.${member};`, - undefined, - luaHeader, - typeScriptHeader - ); - - expect(result).toBe(expected); + return arr.${member}; + ` + .luaHeader(luaHeader) + .tsHeader(tsHeader) + .expectToEqual(expected); }); test("Array delete", () => { - const result = util.transpileAndExecute( - `const myarray = [1,2,3,4]; - delete myarray[2]; - return \`\${myarray[0]},\${myarray[1]},\${myarray[2]},\${myarray[3]}\`;` - ); - - expect(result).toBe("1,2,nil,4"); + util.testFunction` + const array = [1, 2, 3, 4]; + delete array[2]; + return { a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); }); test("Array delete return true", () => { - const result = util.transpileAndExecute( - `const myarray = [1,2,3,4]; - const exists = delete myarray[2]; - return \`\${exists}:\${myarray[0]},\${myarray[1]},\${myarray[2]},\${myarray[3]}\`;` - ); - - expect(result).toBe("true:1,2,nil,4"); + util.testFunction` + const array = [1, 2, 3, 4]; + const exists = delete array[2]; + return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); }); test("Array delete return false", () => { - const result = util.transpileAndExecute( - `const myarray = [1,2,3,4]; - const exists = delete myarray[4]; - return \`\${exists}:\${myarray[0]},\${myarray[1]},\${myarray[2]},\${myarray[3]}\`;` - ); - - expect(result).toBe("true:1,2,3,4"); + util.testFunction` + const array = [1, 2, 3, 4]; + const exists = delete array[4]; + return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); }); test("Array property access", () => { - const code = ` - type A = number[] & {foo?: string}; - const a: A = [1,2,3]; + util.testFunction` + type A = number[] & { foo?: string }; + const a: A = [1, 2, 3]; a.foo = "bar"; - return \`\${a.foo}\${a[0]}\${a[1]}\${a[2]}\`; - `; - expect(util.transpileAndExecute(code)).toBe("bar123"); + return { foo: a.foo, a: a[0], b: a[1], c: a[2] }; + `.expectToMatchJsResult(); }); -test.each([{ length: 0, result: 0 }, { length: 1, result: 1 }, { length: 7, result: 3 }])( +test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( "Array length set", - ({ length, result }) => { - const code = ` - const arr = [1, 2, 3]; - arr.length = ${length}; - return arr.length; - `; - expect(util.transpileAndExecute(code)).toBe(result); + ({ length, arrayLength }) => { + util.testFunction` + const array = [1, 2, 3]; + array.length = ${length}; + return array.length; + `.expectToEqual(arrayLength); } ); -test.each([{ length: 0, result: "0/0" }, { length: 1, result: "1/1" }, { length: 7, result: "7/3" }])( +test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( "Array length set as expression", - ({ length, result }) => { - const code = ` - const arr = [1, 2, 3]; - const l = arr.length = ${length}; - return \`\${l}/\${arr.length}\`; - `; - expect(util.transpileAndExecute(code)).toBe(result); + ({ length, arrayLength }) => { + util.testFunction` + const array = [1, 2, 3]; + const expressionValue = array.length = ${length}; + return { expressionValue, arrayLength: array.length }; + `.expectToEqual({ expressionValue: length, arrayLength }); } ); -test.each([ - { length: -1, result: -1 }, - { length: -7, result: -7 }, - { length: 0.1, result: 0.1 }, - { length: "0 / 0", result: "NaN" }, - { length: "1 / 0", result: "Infinity" }, - { length: "-1 / 0", result: "-Infinity" }, -])("Invalid array length set", ({ length, result }) => { - const code = ` +test.each([-1, -7, 0.1, NaN, Infinity, -Infinity])("Invalid array length set", length => { + util.testFunction` const arr = [1, 2, 3]; arr.length = ${length}; - `; - expect(() => util.transpileAndExecute(code)).toThrowError(`invalid array length: ${result}`); + `.expectToEqual(new util.ExecutionError(`invalid array length: ${length}`)); }); diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 0aeca594d..884961e49 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -1,32 +1,27 @@ import * as tstl from "../../src"; import * as util from "../util"; -const assignmentDestruturingTs = ` +const assignmentDestructuringCode = ` declare function myFunc(this: void): [number, string]; - let [a, b] = myFunc();`; + let [a, b] = myFunc(); +`; test("Assignment destructuring [5.1]", () => { - const lua = util.transpileString(assignmentDestruturingTs, { - luaTarget: tstl.LuaTarget.Lua51, - luaLibImport: tstl.LuaLibImportKind.None, - }); - expect(lua).toBe(`local a, b = unpack(myFunc())`); + util.testModule(assignmentDestructuringCode) + .options({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) + .expectLuaToMatchSnapshot(); }); test("Assignment destructuring [5.2]", () => { - const lua = util.transpileString(assignmentDestruturingTs, { - luaTarget: tstl.LuaTarget.Lua52, - luaLibImport: tstl.LuaLibImportKind.None, - }); - expect(lua).toBe(`local a, b = table.unpack(myFunc())`); + util.testModule(assignmentDestructuringCode) + .options({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) + .expectLuaToMatchSnapshot(); }); test("Assignment destructuring [JIT]", () => { - const lua = util.transpileString(assignmentDestruturingTs, { - luaTarget: tstl.LuaTarget.LuaJIT, - luaLibImport: tstl.LuaLibImportKind.None, - }); - expect(lua).toBe(`local a, b = unpack(myFunc())`); + util.testModule(assignmentDestructuringCode) + .options({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) + .expectLuaToMatchSnapshot(); }); test.each([ @@ -39,15 +34,14 @@ test.each([ "[] = [];", "[] = [] = [];", ])("Empty destructuring (%p)", code => { - expect(() => util.transpileAndExecute(code)).not.toThrow(); + util.testFunction(code).expectNoExecutionError(); }); test("Union destructuring", () => { - const code = ` + util.testFunction` function foo(): [string] | [] { return ["bar"]; } let x: string; [x] = foo(); return x; - `; - expect(util.transpileAndExecute(code)).toBe("bar"); + `.expectToMatchJsResult(); }); diff --git a/test/unit/identifiers.spec.ts b/test/unit/identifiers.spec.ts index 8aa2bc432..ef7e33446 100644 --- a/test/unit/identifiers.spec.ts +++ b/test/unit/identifiers.spec.ts @@ -21,56 +21,50 @@ const invalidLuaNames = [...invalidLuaCharNames, ...luaKeywords.values()]; const validTsInvalidLuaNames = [...invalidLuaCharNames, ...validTsInvalidLuaKeywordNames]; test.each(validTsInvalidLuaNames)("invalid lua identifier name (%p)", name => { - const code = ` + util.testFunction` const ${name} = "foobar"; - return ${name};`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return ${name}; + `.expectToMatchJsResult(); }); test.each([...luaKeywords.values()])("lua keyword as property name (%p)", keyword => { - const code = ` + util.testFunction` const x = { ${keyword}: "foobar" }; - return x.${keyword};`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return x.${keyword}; + `.expectToMatchJsResult(); }); test.each(validTsInvalidLuaKeywordNames)("destructuring lua keyword (%p)", keyword => { - const code = ` - const { foo: ${keyword} } = { foo: "foobar" }; - return ${keyword};`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + util.testFunction` + const { foo: ${keyword} } = { foo: "foobar" }; + return ${keyword}; + `.expectToMatchJsResult(); }); test.each(validTsInvalidLuaKeywordNames)("destructuring shorthand lua keyword (%p)", keyword => { - const code = ` - const { ${keyword} } = { ${keyword}: "foobar" }; - return ${keyword};`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + util.testFunction` + const { ${keyword} } = { ${keyword}: "foobar" }; + return ${keyword}; + `.expectToMatchJsResult(); }); test.each(invalidLuaNames)("lua keyword or invalid identifier as method call (%p)", name => { - const code = ` + util.testFunction` const foo = { ${name}(arg: string) { return "foo" + arg; } }; - return foo.${name}("bar");`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return foo.${name}("bar"); + `.expectToMatchJsResult(); }); test.each(invalidLuaNames)("lua keyword or invalid identifier as complex method call (%p)", name => { - const code = ` + util.testFunction` const foo = { ${name}(arg: string) { return "foo" + arg; } }; function getFoo() { return foo; } - return getFoo().${name}("bar");`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return getFoo().${name}("bar"); + `.expectToMatchJsResult(); }); test.each([ @@ -84,13 +78,12 @@ test.each([ "enum local {}", "function local() {}", ])("ambient identifier cannot be a lua keyword (%p)", statement => { - const code = ` + util.testModule` declare ${statement} - const foo = local;`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier("local")).message - ); + local; + ` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier("local"))); }); test.each([ @@ -102,42 +95,40 @@ test.each([ "namespace $$$ { export const bar: any; }", "module $$$ { export const bar: any; }", "enum $$$ {}", - "function $$$() {}", + "function $$$();", ])("ambient identifier must be a valid lua identifier (%p)", statement => { - const code = ` + util.testModule` declare ${statement} - const foo = $$$;`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier("$$$")).message - ); + $$$; + `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier("$$$"))); }); test.each(validTsInvalidLuaNames)( "ambient identifier must be a valid lua identifier (object literal shorthand) (%p)", name => { - const code = ` - declare var ${name}: any; - const foo = { ${name} };`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier(name)).message - ); + util.testModule` + declare var ${name}: any; + const foo = { ${name} }; + `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier(name))); } ); test.each(validTsInvalidLuaNames)("undeclared identifier must be a valid lua identifier (%p)", name => { - expect(() => util.transpileString(`const foo = ${name};`)).toThrow( - TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier(name)).message - ); + util.testModule` + const foo = ${name}; + ` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier(name))); }); test.each(validTsInvalidLuaNames)( "undeclared identifier must be a valid lua identifier (object literal shorthand) (%p)", name => { - expect(() => util.transpileString(`const foo = { ${name} };`)).toThrow( - TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier(name)).message - ); + util.testModule` + const foo = { ${name} }; + ` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.InvalidAmbientIdentifierName(ts.createIdentifier(name))); } ); @@ -149,15 +140,14 @@ test.each(validTsInvalidLuaNames)("exported values with invalid lua identifier n }); test.each(validTsInvalidLuaNames)("class with invalid lua name has correct name property", name => { - const code = ` + util.testFunction` class ${name} {} - return ${name}.name;`; - - expect(util.transpileAndExecute(code)).toBe(name); + return ${name}.name; + `.expectToMatchJsResult(); }); test.each(validTsInvalidLuaNames)("decorated class with invalid lua name", name => { - const code = ` + util.testFunction` function decorator(c: T): T { c.bar = "foobar"; return c; @@ -165,9 +155,8 @@ test.each(validTsInvalidLuaNames)("decorated class with invalid lua name", name @decorator class ${name} {} - return (${name} as any).bar;`; - - expect(util.transpileAndExecute(code)).toBe("foobar"); + return (${name} as any).bar; + `.expectToMatchJsResult(); }); test.each(validTsInvalidLuaNames)("exported decorated class with invalid lua name", name => { diff --git a/test/unit/json.spec.ts b/test/unit/json.spec.ts index d7064edcc..4e42a53ad 100644 --- a/test/unit/json.spec.ts +++ b/test/unit/json.spec.ts @@ -9,16 +9,15 @@ const jsonOptions = { }; test.each(["0", '""', "[]", '[1, "2", []]', '{ "a": "b" }', '{ "a": { "b": "c" } }'])("JSON (%p)", json => { - const lua = util - .transpileString({ "main.json": json }, jsonOptions, false) - .replace(/^return ([\s\S]+)$/, "return JSONStringify($1)"); - - const result = util.executeLua(lua); - expect(JSON.parse(result)).toEqual(JSON.parse(json)); + util.testModule(json) + .options(jsonOptions) + .setMainFileName("main.json") + .expectToEqual(JSON.parse(json)); }); test("Empty JSON", () => { - expect(() => util.transpileString({ "main.json": "" }, jsonOptions, false)).toThrowExactError( - TSTLErrors.InvalidJsonFileContent(util.nodeStub) - ); + util.testModule("") + .options(jsonOptions) + .setMainFileName("main.json") + .expectToHaveDiagnosticOfError(TSTLErrors.InvalidJsonFileContent(util.nodeStub)); }); diff --git a/test/unit/lualib/array.spec.ts b/test/unit/lualib/array.spec.ts index ad8c64db2..33b59f682 100644 --- a/test/unit/lualib/array.spec.ts +++ b/test/unit/lualib/array.spec.ts @@ -1,93 +1,79 @@ import * as util from "../../util"; -test.each([{ inp: [0, 1, 2, 3], expected: [1, 2, 3, 4] }])("forEach (%p)", ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let arrTest = ${JSON.stringify(inp)}; +test.each([[0, 1, 2, 3]])("forEach (%p)", (...array) => { + util.testFunction` + let arrTest = ${util.valueToString(array)}; arrTest.forEach((elem, index) => { arrTest[index] = arrTest[index] + 1; }) - return JSONStringify(arrTest);` - ); - - expect(JSON.parse(result)).toEqual(expected); + return arrTest; + `.expectToMatchJsResult(); }); test.each([ - { inp: [], searchEl: 3, expected: -1 }, - { inp: [0, 2, 4, 8], searchEl: 10, expected: -1 }, - { inp: [0, 2, 4, 8], searchEl: 8, expected: 3 }, -])("array.findIndex[value] (%p)", ({ inp, searchEl, expected }) => { - const result = util.transpileAndExecute( - `let arrTest = ${JSON.stringify(inp)}; - return JSONStringify(arrTest.findIndex((elem, index) => { - return elem === ${searchEl}; - }));` - ); - - expect(result).toEqual(expected); + { array: [], searchElement: 3 }, + { array: [0, 2, 4, 8], searchElement: 10 }, + { array: [0, 2, 4, 8], searchElement: 8 }, +])("array.findIndex[value] (%p)", ({ array, searchElement }) => { + util.testFunction` + let arrTest = ${util.valueToString(array)}; + return arrTest.findIndex((elem, index) => elem === ${searchElement}); + `.expectToMatchJsResult(); }); -test.each([{ inp: [0, 2, 4, 8], expected: 3, value: 8 }, { inp: [0, 2, 4, 8], expected: 1, value: 2 }])( +test.each([{ array: [0, 2, 4, 8], expected: 3, value: 8 }, { array: [0, 2, 4, 8], expected: 1, value: 2 }])( "array.findIndex[index] (%p)", - ({ inp, expected, value }) => { - const result = util.transpileAndExecute( - `let arrTest = ${JSON.stringify(inp)}; - return JSONStringify(arrTest.findIndex((elem, index, arr) => { - return index === ${expected} && arr[${expected}] === ${value}; - }));` - ); - - expect(result).toEqual(expected); + ({ array, expected, value }) => { + util.testFunction` + let array = ${array}; + return array.findIndex((elem, index, arr) => { + return index === ${expected} && arr[${expected}] === ${value}; + }); + ` + .serialize() + .expectToMatchJsResult(); } ); test.each([ - { inp: [], func: "x => x" }, - { inp: [0, 1, 2, 3], func: "x => x" }, - { inp: [0, 1, 2, 3], func: "x => x*2" }, - { inp: [1, 2, 3, 4], func: "x => -x" }, - { inp: [0, 1, 2, 3], func: "x => x+2" }, - { inp: [0, 1, 2, 3], func: "x => x%2 == 0 ? x + 1 : x - 1" }, -])("array.map (%p)", ({ inp, func }) => { - const result = util.transpileAndExecute(`return JSONStringify([${inp.toString()}].map(${func}))`); - - expect(JSON.parse(result)).toEqual(inp.map(eval(func))); + { array: [], func: "x => x" }, + { array: [0, 1, 2, 3], func: "x => x" }, + { array: [0, 1, 2, 3], func: "x => x*2" }, + { array: [1, 2, 3, 4], func: "x => -x" }, + { array: [0, 1, 2, 3], func: "x => x+2" }, + { array: [0, 1, 2, 3], func: "x => x%2 == 0 ? x + 1 : x - 1" }, +])("array.map (%p)", ({ array, func }) => { + util.testExpression`${util.valueToString(array)}.map(${func})`.expectToMatchJsResult(); }); test.each([ - { inp: [], func: "x => x > 1" }, - { inp: [0, 1, 2, 3], func: "x => x > 1" }, - { inp: [0, 1, 2, 3], func: "x => x < 3" }, - { inp: [0, 1, 2, 3], func: "x => x < 0" }, - { inp: [0, -1, -2, -3], func: "x => x < 0" }, - { inp: [0, 1, 2, 3], func: "() => true" }, - { inp: [0, 1, 2, 3], func: "() => false" }, -])("array.filter (%p)", ({ inp, func }) => { - const result = util.transpileAndExecute(`return JSONStringify([${inp.toString()}].filter(${func}))`); - - expect(JSON.parse(result)).toEqual(inp.filter(eval(func))); + { array: [], func: "x => x > 1" }, + { array: [0, 1, 2, 3], func: "x => x > 1" }, + { array: [0, 1, 2, 3], func: "x => x < 3" }, + { array: [0, 1, 2, 3], func: "x => x < 0" }, + { array: [0, -1, -2, -3], func: "x => x < 0" }, + { array: [0, 1, 2, 3], func: "() => true" }, + { array: [0, 1, 2, 3], func: "() => false" }, +])("array.filter (%p)", ({ array, func }) => { + util.testExpression`${util.valueToString(array)}.filter(${func})`.expectToMatchJsResult(); }); test.each([ - { inp: [], func: "x => x > 1" }, - { inp: [0, 1, 2, 3], func: "x => x > 1" }, - { inp: [false, true, false], func: "x => x" }, - { inp: [true, true, true], func: "x => x" }, -])("array.every (%p)", ({ inp, func }) => { - const result = util.transpileAndExecute(`return JSONStringify([${inp.toString()}].every(${func}))`); - - expect(JSON.parse(result)).toEqual(inp.every(eval(func))); + { array: [], func: "x => x > 1" }, + { array: [0, 1, 2, 3], func: "x => x > 1" }, + { array: [false, true, false], func: "x => x" }, + { array: [true, true, true], func: "x => x" }, +])("array.every (%p)", ({ array, func }) => { + util.testExpression`${util.valueToString(array)}.every(${func})`.expectToMatchJsResult(); }); test.each([ - { inp: [], func: "x => x > 1" }, - { inp: [0, 1, 2, 3], func: "x => x > 1" }, - { inp: [false, true, false], func: "x => x" }, - { inp: [true, true, true], func: "x => x" }, -])("array.some (%p)", ({ inp, func }) => { - const result = util.transpileAndExecute(`return JSONStringify([${inp.toString()}].some(${func}))`); - - expect(JSON.parse(result)).toEqual(inp.some(eval(func))); + { array: [], func: "x => x > 1" }, + { array: [0, 1, 2, 3], func: "x => x > 1" }, + { array: [false, true, false], func: "x => x" }, + { array: [true, true, true], func: "x => x" }, +])("array.some (%p)", ({ array, func }) => { + util.testExpression`${util.valueToString(array)}.some(${func})`.expectToMatchJsResult(); }); test.each([ @@ -99,283 +85,172 @@ test.each([ { inp: [0, 1, 2, 3, 4, 5], start: 1, end: 3 }, { inp: [0, 1, 2, 3, 4, 5], start: 3 }, ])("array.slice (%p)", ({ inp, start, end }) => { - const result = util.transpileAndExecute(`return JSONStringify([${inp.toString()}].slice(${start}, ${end}))`); - - expect(JSON.parse(result)).toEqual(inp.slice(start, end)); + util.testExpression`${util.valueToString(inp)}.slice(${start}, ${end})`.expectToMatchJsResult(); }); test("array.slice no argument", () => { - const input = [2, 3, 4, 5]; - const result = util.transpileAndExecute(`return JSONStringify(${JSON.stringify(input)}.slice())`); - - expect(JSON.parse(result)).toEqual(input); + util.testExpression`[2, 3, 4, 5].slice()`.expectToMatchJsResult(); }); test.each([ - { inp: [], start: 0, deleteCount: 0, newElements: [9, 10, 11] }, - { inp: [0, 1, 2, 3], start: 1, deleteCount: 0, newElements: [9, 10, 11] }, - { inp: [0, 1, 2, 3], start: 2, deleteCount: 2, newElements: [9, 10, 11] }, - { inp: [0, 1, 2, 3], start: 4, deleteCount: 1, newElements: [8, 9] }, - { inp: [0, 1, 2, 3], start: 4, deleteCount: 0, newElements: [8, 9] }, - { inp: [0, 1, 2, 3], start: -2, deleteCount: 0, newElements: [8, 9] }, - { inp: [0, 1, 2, 3], start: -3, deleteCount: 0, newElements: [8, 9] }, - { inp: [0, 1, 2, 3, 4, 5], start: 5, deleteCount: 9, newElements: [10, 11] }, - { inp: [0, 1, 2, 3, 4, 5], start: 3, deleteCount: 2, newElements: [3, 4, 5] }, -])("array.splice[Insert] (%p)", ({ inp, start, deleteCount, newElements }) => { - const result = util.transpileAndExecute( - `let spliceTestTable = [${inp.toString()}]; - spliceTestTable.splice(${start}, ${deleteCount}, ${newElements}); - return JSONStringify(spliceTestTable);` - ); - - inp.splice(start, deleteCount, ...newElements); - expect(JSON.parse(result)).toEqual(inp); + { array: [], start: 0, deleteCount: 0, newElements: [9, 10, 11] }, + { array: [0, 1, 2, 3], start: 1, deleteCount: 0, newElements: [9, 10, 11] }, + { array: [0, 1, 2, 3], start: 2, deleteCount: 2, newElements: [9, 10, 11] }, + { array: [0, 1, 2, 3], start: 4, deleteCount: 1, newElements: [8, 9] }, + { array: [0, 1, 2, 3], start: 4, deleteCount: 0, newElements: [8, 9] }, + { array: [0, 1, 2, 3], start: -2, deleteCount: 0, newElements: [8, 9] }, + { array: [0, 1, 2, 3], start: -3, deleteCount: 0, newElements: [8, 9] }, + { array: [0, 1, 2, 3, 4, 5], start: 5, deleteCount: 9, newElements: [10, 11] }, + { array: [0, 1, 2, 3, 4, 5], start: 3, deleteCount: 2, newElements: [3, 4, 5] }, +])("array.splice[Insert] (%p)", ({ array, start, deleteCount, newElements }) => { + util.testFunction` + const array = ${util.valueToString(array)}; + array.splice(${start}, ${deleteCount}, ${util.valuesToString(newElements)}); + return array; + `.expectToMatchJsResult(); }); test.each([ - { inp: [], start: 1, deleteCount: 1 }, - { inp: [0, 1, 2, 3], start: 1, deleteCount: 1 }, - { inp: [0, 1, 2, 3], start: 10, deleteCount: 1 }, - { inp: [0, 1, 2, 3], start: 1, deleteCount: undefined }, - { inp: [0, 1, 2, 3], start: 4 }, - { inp: [0, 1, 2, 3, 4, 5], start: 3 }, - { inp: [0, 1, 2, 3, 4, 5], start: -3 }, - { inp: [0, 1, 2, 3, 4, 5], start: -2 }, - { inp: [0, 1, 2, 3, 4, 5], start: 2, deleteCount: 2 }, - { inp: [0, 1, 2, 3, 4, 5, 6, 7, 8], start: 5, deleteCount: 9, newElements: [10, 11] }, -])("array.splice[Remove] (%p)", ({ inp, start, deleteCount, newElements = [] }) => { - let result; - if (deleteCount) { - result = util.transpileAndExecute( - `let spliceTestTable = [${inp.toString()}]; - spliceTestTable.splice(${start}, ${deleteCount}, ${newElements}); - return JSONStringify(spliceTestTable);` - ); - } else { - result = util.transpileAndExecute( - `let spliceTestTable = [${inp.toString()}]; - spliceTestTable.splice(${start}); - return JSONStringify(spliceTestTable);` - ); - } - - if (deleteCount) { - inp.splice(start, deleteCount, ...newElements); - expect(JSON.parse(result)).toEqual(inp); - } else { - inp.splice(start); - expect(JSON.parse(result)).toEqual(inp); - } + { array: [], start: 1, deleteCount: 1 }, + { array: [0, 1, 2, 3], start: 1, deleteCount: 1 }, + { array: [0, 1, 2, 3], start: 10, deleteCount: 1 }, + { array: [0, 1, 2, 3], start: 1, deleteCount: undefined }, + { array: [0, 1, 2, 3], start: 4 }, + { array: [0, 1, 2, 3, 4, 5], start: 3 }, + { array: [0, 1, 2, 3, 4, 5], start: -3 }, + { array: [0, 1, 2, 3, 4, 5], start: -2 }, + { array: [0, 1, 2, 3, 4, 5], start: 2, deleteCount: 2 }, + { array: [0, 1, 2, 3, 4, 5, 6, 7, 8], start: 5, deleteCount: 9, newElements: [10, 11] }, +])("array.splice[Remove] (%p)", ({ array, start, deleteCount, newElements = [] }) => { + util.testFunction` + const array = ${util.valueToString(array)}; + array.splice(${util.valuesToString([start, deleteCount, ...newElements])}); + return array; + `.expectToMatchJsResult(); }); test.each([ - { arr: [], args: [[]] }, - { arr: [1, 2, 3], args: [[]] }, - { arr: [1, 2, 3], args: [[4]] }, - { arr: [1, 2, 3], args: [[4, 5]] }, - { arr: [1, 2, 3], args: [[4, 5]] }, - { arr: [1, 2, 3], args: [4, [5]] }, - { arr: [1, 2, 3], args: [4, [5, 6]] }, - { arr: [1, 2, 3], args: [4, [5, 6], 7] }, - { arr: [1, 2, 3], args: ["test", [5, 6], 7, ["test1", "test2"]] }, - { arr: [1, 2, "test"], args: ["test", ["test1", "test2"]] }, -])("array.concat (%p)", ({ arr, args }: { arr: any[]; args: any[] }) => { - const argStr = args.map(arg => JSON.stringify(arg)).join(","); - - const result = util.transpileAndExecute( - `let concatTestTable: any[] = ${JSON.stringify(arr)}; - return JSONStringify(concatTestTable.concat(${argStr}));` - ); - - const concatArr = arr.concat(...args); - expect(JSON.parse(result)).toEqual(concatArr); + { array: [], args: [[]] }, + { array: [1, 2, 3], args: [[]] }, + { array: [1, 2, 3], args: [[4]] }, + { array: [1, 2, 3], args: [[4, 5]] }, + { array: [1, 2, 3], args: [[4, 5]] }, + { array: [1, 2, 3], args: [4, [5]] }, + { array: [1, 2, 3], args: [4, [5, 6]] }, + { array: [1, 2, 3], args: [4, [5, 6], 7] }, + { array: [1, 2, 3], args: ["test", [5, 6], 7, ["test1", "test2"]] }, + { array: [1, 2, "test"], args: ["test", ["test1", "test2"]] }, +])("array.concat (%p)", ({ array, args }) => { + util.testFunction` + let concatTestTable: any[] = ${util.valueToString(array)}; + return concatTestTable.concat(${util.valuesToString(args)}); + `.expectToMatchJsResult(); }); test.each([ - { inp: [] }, - { inp: ["test1"] }, - { inp: ["test1", "test2"] }, - { inp: ["test1", "test2"], separator: ";" }, - { inp: ["test1", "test2"], separator: "" }, -])("array.join (%p)", ({ inp, separator }) => { - let separatorLua; - if (separator === "") { - separatorLua = '""'; - } else if (separator) { - separatorLua = '"' + separator + '"'; - } else { - separatorLua = ""; - } - const result = util.transpileAndExecute( - `let joinTestTable = ${JSON.stringify(inp)}; - return joinTestTable.join(${separatorLua});` - ); - - expect(result).toEqual(inp.join(separator)); + { array: [] }, + { array: ["test1"] }, + { array: ["test1", "test2"] }, + { array: ["test1", "test2"], separator: ";" }, + { array: ["test1", "test2"], separator: "" }, +])("array.join (%p)", ({ array, separator }) => { + util.testFunction` + const joinTestTable = ${util.valueToString(array)}; + return joinTestTable.join(${util.valueToString(separator)}); + `.expectToMatchJsResult(); }); test.each([ - { inp: [], element: "test1" }, - { inp: ["test1"], element: "test1" }, - { inp: ["test1", "test2"], element: "test2" }, - { inp: ["test1", "test2", "test3"], element: "test3", fromIndex: 1 }, - { inp: ["test1", "test2", "test3"], element: "test1", fromIndex: 2 }, - { inp: ["test1", "test2", "test3"], element: "test1", fromIndex: -2 }, - { inp: ["test1", "test2", "test3"], element: "test1", fromIndex: 12 }, -])("array.indexOf (%p)", ({ inp, element, fromIndex }) => { - let str = `return ${JSON.stringify(inp)}.indexOf("${element}");`; - if (fromIndex) { - str = `return ${JSON.stringify(inp)}.indexOf("${element}", ${fromIndex});`; - } - - const result = util.transpileAndExecute(str); - - // Account for lua indexing (-1) - expect(result).toEqual(inp.indexOf(element, fromIndex)); + { array: [], args: ["test1"] }, + { array: ["test1"], args: ["test1"] }, + { array: ["test1", "test2"], args: ["test2"] }, + { array: ["test1", "test2", "test3"], args: ["test3", 1] }, + { array: ["test1", "test2", "test3"], args: ["test1", 2] }, + { array: ["test1", "test2", "test3"], args: ["test1", -2] }, + { array: ["test1", "test2", "test3"], args: ["test1", 12] }, +])("array.indexOf (%p)", ({ array, args }) => { + util.testExpression`${util.valueToString(array)}.indexOf(${util.valuesToString(args)})`.expectToMatchJsResult(); }); -test.each([{ inp: [1, 2, 3], expected: 3 }, { inp: [1, 2, 3, 4, 5], expected: 3 }])( - "array.destructuring.simple (%p)", - ({ inp, expected }) => { - const result = util.transpileAndExecute( - `let [x, y, z] = ${JSON.stringify(inp)} - return z;` - ); - - expect(result).toEqual(expected); - } -); - -test.each([{ inp: [1] }, { inp: [1, 2, 3] }])("array.push (%p)", ({ inp }) => { - const result = util.transpileAndExecute( - `let testArray = [0]; - testArray.push(${inp.join(", ")}); - return JSONStringify(testArray);` - ); +// TODO: Unrelated to lib +test.each([[1, 2, 3], [1, 2, 3, 4, 5]])("array.destructuring.simple (%p)", (...array) => { + util.testFunction` + let [x, y, z] = ${util.valueToString(array)} + return z; + `.expectToMatchJsResult(); +}); - expect(JSON.parse(result)).toEqual([0].concat(inp)); +test.each([[1], [1, 2, 3]])("array.push (%p)", (...args) => { + util.testFunction` + let testArray = [0]; + testArray.push(${util.valuesToString(args)}); + return testArray; + `.expectToMatchJsResult(); }); -test.each([{ array: "[1, 2, 3]", expected: [3, 2] }, { array: "[1, 2, 3, null]", expected: [3, 2] }])( +// tslint:disable-next-line: no-null-keyword +test.each([{ array: [1, 2, 3], expected: [3, 2] }, { array: [1, 2, 3, null], expected: [3, 2] }])( "array.pop (%p)", ({ array, expected }) => { - { - const result = util.transpileAndExecute( - `let testArray = ${array}; - let val = testArray.pop(); - return val` - ); - - expect(result).toEqual(expected[0]); - } - { - const result = util.transpileAndExecute( - `let testArray = ${array}; - testArray.pop(); - return testArray.length` - ); - - expect(result).toEqual(expected[1]); - } + util.testFunction` + let array = ${util.valueToString(array)}; + let value = array.pop(); + return [value, array.length]; + `.expectToEqual(expected); } ); -test.each([ - { array: "[1, 2, 3]", expected: [3, 2, 1] }, - { array: "[1, 2, 3, null]", expected: [3, 2, 1] }, - { array: "[1, 2, 3, 4]", expected: [4, 3, 2, 1] }, - { array: "[1]", expected: [1] }, - { array: "[]", expected: [] }, -])("array.reverse (%p)", ({ array, expected }) => { - const result = util.transpileAndExecute( - `let testArray = ${array}; - let val = testArray.reverse(); - return JSONStringify(testArray)` - ); - expect(JSON.parse(result)).toEqual(expected); +test.each([[1, 2, 3], [1, 2, 3, 4], [1], []])("array.reverse (%p)", (...array) => { + util.testFunction` + let array = ${util.valueToString(array)}; + let val = array.reverse(); + return array + `.expectToMatchJsResult(); }); -test.each([ - { array: "[1, 2, 3]", expectedArray: [2, 3], expectedValue: 1 }, - { array: "[1]", expectedArray: [], expectedValue: 1 }, - { array: "[]", expectedArray: [], expectedValue: undefined }, -])("array.shift (%p)", ({ array, expectedArray, expectedValue }) => { - { - // test array mutation - { - const result = util.transpileAndExecute( - `let testArray = ${array}; - let val = testArray.shift(); - return JSONStringify(testArray)` - ); - expect(JSON.parse(result)).toEqual(expectedArray); - } - // test return value - { - const result = util.transpileAndExecute( - `let testArray = ${array}; - let val = testArray.shift(); - return val` - ); - - expect(result).toEqual(expectedValue); - } - } +test.each([[1, 2, 3], [1], []])("array.shift (%p)", (...array) => { + util.testFunction` + let array = ${util.valueToString(array)}; + let value = array.shift(); + return { array, value } + `.expectToMatchJsResult(); }); test.each([ - { array: "[3, 4, 5]", toUnshift: [1, 2], expected: [1, 2, 3, 4, 5] }, - { array: "[]", toUnshift: [], expected: [] }, - { array: "[1]", toUnshift: [], expected: [1] }, - { array: "[]", toUnshift: [1], expected: [1] }, -])("array.unshift (%p)", ({ array, toUnshift, expected }) => { - const result = util.transpileAndExecute( - `let testArray = ${array}; - testArray.unshift(${toUnshift}); - return JSONStringify(testArray)` - ); - - expect(JSON.parse(result)).toEqual(expected); + { array: "[3, 4, 5]", args: [1, 2] }, + { array: "[]", args: [] }, + { array: "[1]", args: [] }, + { array: "[]", args: [1] }, +])("array.unshift (%p)", ({ array, args }) => { + util.testFunction` + let array = ${array}; + array.unshift(${util.valuesToString(args)}); + return array; + `.expectToMatchJsResult(); }); -test.each([ - { array: "[4, 5, 3, 2, 1]", expected: [1, 2, 3, 4, 5] }, - { array: "[1]", expected: [1] }, - { array: "[1, null]", expected: [1] }, - { array: "[]", expected: [] }, -])("array.sort (%p)", ({ array, expected }) => { - const result = util.transpileAndExecute( - `let testArray = ${array}; - testArray.sort(); - return JSONStringify(testArray)` - ); - - expect(JSON.parse(result)).toEqual(expected); +test.each([[[4, 5, 3, 2, 1]], [[1]], [[]]])("array.sort (%p)", array => { + util.testFunction` + const array = ${array}; + array.sort(); + return array; + ` + .serialize() + .expectToMatchJsResult(); }); test.each([ - { array: [1, 2, 3, 4, 5], compareStr: "a - b", compareFn: (a: any, b: any) => a - b }, - { - array: ["4", "5", "3", "2", "1"], - compareStr: "tonumber(a) - tonumber(b)", - compareFn: (a: any, b: any) => Number(a) - Number(b), - }, - { - array: ["4", "5", "3", "2", "1"], - compareStr: "tonumber(b) - tonumber(a)", - compareFn: (a: any, b: any) => Number(b) - Number(a), - }, -])("array.sort with compare function (%p)", ({ array, compareStr, compareFn }) => { - const result = util.transpileAndExecute( - `let testArray = ${JSON.stringify(array)}; - testArray.sort((a, b) => ${compareStr}); - return JSONStringify(testArray)`, - undefined, - undefined, - `declare function tonumber(this: void, e: any): number` - ); - - expect(JSON.parse(result)).toEqual(array.sort(compareFn)); + { array: [1, 2, 3, 4, 5], compare: (a: number, b: number) => a - b }, + { array: ["4", "5", "3", "2", "1"], compare: (a: string, b: string) => Number(a) - Number(b) }, + { array: ["4", "5", "3", "2", "1"], compare: (a: string, b: string) => Number(b) - Number(a) }, +])("array.sort with compare function (%p)", ({ array, compare }) => { + util.testFunction` + const array = ${array}; + array.sort(${compare}); + return array; + ` + .serialize() + .expectToMatchJsResult(); }); test.each([ @@ -384,13 +259,8 @@ test.each([ { array: [1, [[2], [3]], 4], expected: [1, [2], [3], 4] }, { array: [1, [[[2], [3]]], 4], depth: Infinity, expected: [1, 2, 3, 4] }, ])("array.flat (%p)", ({ array, depth, expected }) => { - // TODO: Remove once `Infinity` would be implemented - const luaDepth = depth === Infinity ? "1 / 0" : depth; - const result = util.transpileAndExecute(` - return JSONStringify(${JSON.stringify(array)}.flat(${luaDepth})) - `); - - expect(JSON.parse(result)).toEqual(expected); + // TODO: Node 12 + util.testExpression`${array}.flat(${depth})`.serialize().expectToEqual(expected); }); test.each([ @@ -400,59 +270,30 @@ test.each([ { array: [1, 2, 3], map: (v: number) => [v, [v]], expected: [1, [1], 2, [2], 3, [3]] }, { array: [1, 2, 3], map: (v: number, i: number) => [v * 2 * i], expected: [0, 4, 12] }, ])("array.flatMap (%p)", ({ array, map, expected }) => { - const result = util.transpileAndExecute(` - const array = ${JSON.stringify(array)}; - const result = array.flatMap(${map.toString()}); - return JSONStringify(result); - `); - - // TODO(node 12): array.flatMap(map) - expect(JSON.parse(result)).toEqual(expected); + // TODO: Node 12 + util.testExpression`${array}.flatMap(${map})`.serialize().expectToEqual(expected); }); -test.each([ - (total: number, currentItem: number) => total + currentItem, - (total: number, currentItem: number) => total * currentItem, +test.each<(total: number, currentItem: number) => number>([ + (total, currentItem) => total + currentItem, + (total, currentItem) => total * currentItem, ])("array reduce (%p)", reducer => { - const array = [1, 3, 5, 7]; - - const result = util.transpileAndExecute(` - const myArray = ${JSON.stringify(array)}; - return myArray.reduce(${reducer.toString()}); - `); - - expect(result).toEqual(array.reduce(reducer)); + util.testExpression`[1, 3, 5, 7].reduce(${reducer})`.serialize().expectToMatchJsResult(); }); -test.each([ - (total: number, currentItem: number) => total + currentItem, - (total: number, currentItem: number) => total * currentItem, +test.each<(total: number, currentItem: number) => number>([ + (total, currentItem) => total + currentItem, + (total, currentItem) => total * currentItem, ])("array reduce with initial value (%p)", reducer => { - const array = [1, 3, 5, 7]; - const initial = 10; - - const result = util.transpileAndExecute(` - const myArray = ${JSON.stringify(array)}; - return myArray.reduce(${reducer.toString()}, ${initial}); - `); - - expect(result).toEqual(array.reduce(reducer, initial)); + util.testExpression`[1, 3, 5, 7].reduce(${reducer}, 10)`.serialize().expectToMatchJsResult(); }); test("array reduce index & array arguments (%p)", () => { - const array = [1, 3, 5, 7]; - const reducer = (total: number, _: number, index: number, array: number[]) => total + array[index]; - - const result = util.transpileAndExecute(` - const myArray = ${JSON.stringify(array)}; - return myArray.reduce(${reducer.toString()}); - `); - - expect(result).toEqual(array.reduce(reducer)); + util.testExpression`[1, 3, 5, 7].reduce((total, _, index, array) => total + array[index])`.expectToMatchJsResult(); }); test("array reduce index & array arguments (%p)", () => { - expect(() => { - util.transpileAndExecute("return [].reduce((a, b) => a + b);"); - }).toThrow("Reduce of empty array with no initial value"); + util.testExpression`[].reduce((a, b) => a + b)`.expectToEqual( + new util.ExecutionError("Reduce of empty array with no initial value") + ); }); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index 4d9c7e499..e9b1dff7a 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -11,54 +11,46 @@ describe("module import/export elision", () => { } `; - const expectToElideImport = (code: string) => { - const lua = util.transpileString( - { "module.d.ts": moduleDeclaration, "main.ts": code }, - { module: ts.ModuleKind.CommonJS }, - false - ); - - expect(() => util.executeLua(lua)).not.toThrow(); + const expectToElideImport: util.TapCallback = builder => { + builder.addExtraFile("module.d.ts", moduleDeclaration).options({ module: ts.ModuleKind.CommonJS }); + expect(builder.getLuaExecutionResult()).not.toBeInstanceOf(util.ExecutionError); }; test("should elide named type imports", () => { - expectToElideImport(` + util.testModule` import { Type } from "module"; const foo: Type = "bar"; - `); + `.tap(expectToElideImport); }); test("should elide named value imports used only as a type", () => { - expectToElideImport(` + util.testModule` import { value } from "module"; const foo: typeof value = "bar"; - `); + `.tap(expectToElideImport); }); test("should elide namespace imports with unused values", () => { - expectToElideImport(` + util.testModule` import * as module from "module"; const foo: module.Type = "bar"; - `); + `.tap(expectToElideImport); }); test("should elide `import =` declarations", () => { - expectToElideImport(` + util.testModule` import module = require("module"); const foo: module.Type = "bar"; - `); + `.tap(expectToElideImport); }); test("should elide type exports", () => { - const code = ` + util.testModule` declare const _G: any; - _G.foo = true; type foo = boolean; export { foo }; - `; - - expect(util.transpileExecuteAndReturnExport(code, "foo")).toBeUndefined(); + `.expectToEqual([]); }); }); @@ -72,47 +64,48 @@ test.each([ .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedDefaultExport(util.nodeStub)); }); +test("defaultImport", () => { + util.testModule` + import Test from "test"; + ` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.DefaultImportsNotSupported(util.nodeStub)); +}); + test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌͼƛಠ", "_̀ः٠‿"])( "Import module names with invalid lua identifier characters (%p)", name => { - const code = ` - import { foo } from "${name}"; - foo; - `; - - const lua = ` - setmetatable(package.loaded, {__index = function() return {foo = "bar"} end}) - ${util.transpileString(code)} - return foo;`; - - expect(util.executeLua(lua)).toBe("bar"); + util.testModule` + import { foo } from "./${name}"; + export { foo }; + ` + .disableSemanticCheck() + .luaHeader(`setmetatable(package.loaded, { __index = function() return { foo = "bar" } end })`) + .export("foo") + .expectToEqual("bar"); } ); -test("defaultImport", () => { - expect(() => { - util.transpileString(`import TestClass from "test"`); - }).toThrowExactError(TSTLErrors.DefaultImportsNotSupported(util.nodeStub)); -}); - test("lualibRequire", () => { - const lua = util.transpileString(`let a = b instanceof c;`, { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.LuaJIT, - }); - - expect(lua.startsWith(`require("lualib_bundle")`)); + util.testExpression`b instanceof c` + .options({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.LuaJIT }) + .disableSemanticCheck() + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); }); test("lualibRequireAlways", () => { - const lua = util.transpileString(``, { - luaLibImport: tstl.LuaLibImportKind.Always, - luaTarget: tstl.LuaTarget.LuaJIT, - }); - - expect(lua).toBe(`require("lualib_bundle");`); + util.testModule`` + .options({ luaLibImport: tstl.LuaLibImportKind.Always, luaTarget: tstl.LuaTarget.LuaJIT }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); }); +test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( + "LuaLib no uses? No code (%p)", + luaLibImport => { + util.testModule``.options({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); + } +); + test("Non-exported module", () => { const result = util.transpileAndExecute( "return g.test();", @@ -124,15 +117,6 @@ test("Non-exported module", () => { expect(result).toBe(3); }); -test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( - "LuaLib no uses? No code (%p)", - luaLibImport => { - const lua = util.transpileString(``, { luaLibImport }); - - expect(lua).toBe(``); - } -); - test("Nested module with dot in name", () => { const code = `module a.b { export const foo = "foo"; diff --git a/test/util.ts b/test/util.ts index f4a6f0784..1ec99fd4b 100644 --- a/test/util.ts +++ b/test/util.ts @@ -243,6 +243,13 @@ export class TestBuilder { return this; } + private _jsHeader = ""; + public jsHeader(jsHeader: string): this { + expect(this._hasTsCode).toBe(false); + this._jsHeader += jsHeader; + return this; + } + private _semanticCheck = true; public disableSemanticCheck(): this { this._semanticCheck = false; @@ -366,13 +373,16 @@ export class TestBuilder { const { transpiledFiles } = this.getJsResult(); const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); expect(mainFile).toBeDefined(); - return mainFile!.js! + `;module.exports = exports${this._accessor}`; + + const header = this._jsHeader ? `${this._jsHeader.trimRight()}\n` : ""; + return header + mainFile!.js! + `;module.exports = exports${this._accessor}`; } @memoize public getJsExecutionResult(): any { const exports = {}; const context = vm.createContext({ exports, module: { exports } }); + context.global = context; try { return vm.runInContext(this.getJsCode(), context); } catch (error) { @@ -414,8 +424,19 @@ export class TestBuilder { return this; } + public expectNoExecutionError(): this { + const luaResult = this.getLuaExecutionResult(); + if (luaResult instanceof ExecutionError) { + throw luaResult; + } + + return this; + } + public expectToMatchJsResult(allowErrors = false): this { this.expectToHaveNoDiagnostics(); + if (!allowErrors) this.expectNoExecutionError(); + const luaResult = this.getLuaExecutionResult(); const jsResult = this.getJsExecutionResult(); // tslint:disable-next-line: no-null-keyword @@ -423,10 +444,6 @@ export class TestBuilder { expect(luaResult).toEqual(jsResult); } - if (!allowErrors && luaResult instanceof ExecutionError) { - throw luaResult; - } - return this; } From 8ff931e712d3d93cf722ab46cb66a401d3246e6e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 20 Jun 2019 20:56:31 +0500 Subject: [PATCH 12/64] Move serialize to separate template functions --- test/unit/lualib/array.spec.ts | 26 ++++----- test/unit/string.spec.ts | 34 ++++++------ test/util.ts | 97 +++++++++++++++------------------- 3 files changed, 67 insertions(+), 90 deletions(-) diff --git a/test/unit/lualib/array.spec.ts b/test/unit/lualib/array.spec.ts index 33b59f682..5f1d3b541 100644 --- a/test/unit/lualib/array.spec.ts +++ b/test/unit/lualib/array.spec.ts @@ -24,14 +24,12 @@ test.each([ test.each([{ array: [0, 2, 4, 8], expected: 3, value: 8 }, { array: [0, 2, 4, 8], expected: 1, value: 2 }])( "array.findIndex[index] (%p)", ({ array, expected, value }) => { - util.testFunction` + util.testFunctionTemplate` let array = ${array}; return array.findIndex((elem, index, arr) => { return index === ${expected} && arr[${expected}] === ${value}; }); - ` - .serialize() - .expectToMatchJsResult(); + `.expectToMatchJsResult(); } ); @@ -230,13 +228,11 @@ test.each([ }); test.each([[[4, 5, 3, 2, 1]], [[1]], [[]]])("array.sort (%p)", array => { - util.testFunction` + util.testFunctionTemplate` const array = ${array}; array.sort(); return array; - ` - .serialize() - .expectToMatchJsResult(); + `.expectToMatchJsResult(); }); test.each([ @@ -244,13 +240,11 @@ test.each([ { array: ["4", "5", "3", "2", "1"], compare: (a: string, b: string) => Number(a) - Number(b) }, { array: ["4", "5", "3", "2", "1"], compare: (a: string, b: string) => Number(b) - Number(a) }, ])("array.sort with compare function (%p)", ({ array, compare }) => { - util.testFunction` + util.testFunctionTemplate` const array = ${array}; array.sort(${compare}); return array; - ` - .serialize() - .expectToMatchJsResult(); + `.expectToMatchJsResult(); }); test.each([ @@ -260,7 +254,7 @@ test.each([ { array: [1, [[[2], [3]]], 4], depth: Infinity, expected: [1, 2, 3, 4] }, ])("array.flat (%p)", ({ array, depth, expected }) => { // TODO: Node 12 - util.testExpression`${array}.flat(${depth})`.serialize().expectToEqual(expected); + util.testExpressionTemplate`${array}.flat(${depth})`.expectToEqual(expected); }); test.each([ @@ -271,21 +265,21 @@ test.each([ { array: [1, 2, 3], map: (v: number, i: number) => [v * 2 * i], expected: [0, 4, 12] }, ])("array.flatMap (%p)", ({ array, map, expected }) => { // TODO: Node 12 - util.testExpression`${array}.flatMap(${map})`.serialize().expectToEqual(expected); + util.testExpressionTemplate`${array}.flatMap(${map})`.expectToEqual(expected); }); test.each<(total: number, currentItem: number) => number>([ (total, currentItem) => total + currentItem, (total, currentItem) => total * currentItem, ])("array reduce (%p)", reducer => { - util.testExpression`[1, 3, 5, 7].reduce(${reducer})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`[1, 3, 5, 7].reduce(${reducer})`.expectToMatchJsResult(); }); test.each<(total: number, currentItem: number) => number>([ (total, currentItem) => total + currentItem, (total, currentItem) => total * currentItem, ])("array reduce with initial value (%p)", reducer => { - util.testExpression`[1, 3, 5, 7].reduce(${reducer}, 10)`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`[1, 3, 5, 7].reduce(${reducer}, 10)`.expectToMatchJsResult(); }); test("array reduce index & array arguments (%p)", () => { diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index d37f9fa90..184e82b85 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -31,14 +31,12 @@ test.each([ { a: "test", b: 42, c: true }, { a: false, b: 42, c: 12 }, ])("Template Strings (%p)", ({ a, b, c }) => { - util.testFunction` + util.testFunctionTemplate` let a = ${a}; let b = ${b}; let c = ${c}; return \`${a} ${b} test ${c}\`; - ` - .serialize() - .expectToMatchJsResult(); + `.expectToMatchJsResult(); }); test.each([ @@ -49,14 +47,12 @@ test.each([ { a: "test", b: 42, c: true }, { a: false, b: 42, c: 12 }, ])("String Concat Operator (%p)", ({ a, b, c }) => { - util.testFunction` + util.testFunctionTemplate` let a = ${a}; let b = ${b}; let c = ${c}; return a + " " + b + " test " + c; - ` - .serialize() - .expectToMatchJsResult(); + `.expectToMatchJsResult(); }); test.each([ @@ -65,7 +61,7 @@ test.each([ { input: "abcde", index: 0 }, { input: "a", index: 0 }, ])("string index (%p)", ({ input, index }) => { - util.testExpression`${input}[${index}]`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${input}[${index}]`.expectToMatchJsResult(); }); test.each([ @@ -106,7 +102,7 @@ test.each([ { inp: "hello test", searchValue: "h" }, { inp: "hello test", searchValue: "invalid" }, ])("string.indexOf (%p)", ({ inp, searchValue }) => { - util.testExpression`${inp}.indexOf(${searchValue})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.indexOf(${searchValue})`.expectToMatchJsResult(); }); test.each([ @@ -115,13 +111,13 @@ test.each([ { inp: "hello test", searchValue: "t", offset: 7 }, { inp: "hello test", searchValue: "h", offset: 4 }, ])("string.indexOf with offset (%p)", ({ inp, searchValue, offset }) => { - util.testExpression`${inp}.indexOf(${searchValue}, ${offset})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.indexOf(${searchValue}, ${offset})`.expectToMatchJsResult(); }); test.each([{ inp: "hello test", searchValue: "t", x: 4, y: 3 }, { inp: "hello test", searchValue: "h", x: 3, y: 4 }])( "string.indexOf with offset expression (%p)", ({ inp, searchValue, x, y }) => { - util.testExpression`${inp}.indexOf(${searchValue}, 2 > 1 && ${x} || ${y})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.indexOf(${searchValue}, 2 > 1 && ${x} || ${y})`.expectToMatchJsResult(); } ); @@ -172,15 +168,15 @@ test.each([{ inp: "hello test", start: 1, ignored: 0 }, { inp: "hello test", sta ); test.each(["", "h", "hello"])("string.length (%p)", input => { - util.testExpression`${input}.length`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${input}.length`.expectToMatchJsResult(); }); test.each(["hello TEST"])("string.toLowerCase (%p)", inp => { - util.testExpression`${inp}.toLowerCase()`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.toLowerCase()`.expectToMatchJsResult(); }); test.each(["hello test"])("string.toUpperCase (%p)", inp => { - util.testExpression`${inp}.toUpperCase()`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.toUpperCase()`.expectToMatchJsResult(); }); test.each([ @@ -192,7 +188,7 @@ test.each([ { inp: "hello test", separator: "invalid" }, { inp: "hello test", separator: "hello test" }, ])("string.split (%p)", ({ inp, separator }) => { - util.testExpression`${inp}.split(${separator})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.split(${separator})`.expectToMatchJsResult(); }); test.each([ @@ -201,13 +197,13 @@ test.each([ { inp: "hello test", index: 3 }, { inp: "hello test", index: 99 }, ])("string.charAt (%p)", ({ inp, index }) => { - util.testExpression`${inp}.charAt(${index})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.charAt(${index})`.expectToMatchJsResult(); }); test.each([{ inp: "hello test", index: 1 }, { inp: "hello test", index: 2 }, { inp: "hello test", index: 3 }])( "string.charCodeAt (%p)", ({ inp, index }) => { - util.testExpression`${inp}.charCodeAt(${index})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.charCodeAt(${index})`.expectToMatchJsResult(); } ); @@ -217,7 +213,7 @@ test.each([ { inp: "hello test", index: 3, ignored: 2 }, { inp: "hello test", index: 3, ignored: 99 }, ])("string.charAt with expression (%p)", ({ inp, index, ignored }) => { - util.testExpression`${inp}.charAt(2 > 1 && ${index} || ${ignored})`.serialize().expectToMatchJsResult(); + util.testExpressionTemplate`${inp}.charAt(2 > 1 && ${index} || ${ignored})`.expectToMatchJsResult(); }); test.each<{ inp: string; args: Parameters }>([ diff --git a/test/util.ts b/test/util.ts index 1ec99fd4b..a1fb7c9d7 100644 --- a/test/util.ts +++ b/test/util.ts @@ -225,33 +225,27 @@ export class ExecutionError extends Error { export type TapCallback = (builder: TestBuilder) => void; export class TestBuilder { protected _accessor = ""; - constructor(private template: TemplateStringsArray, private substitutions: any[]) {} + constructor(protected _tsCode: string) {} // Options - private _serialize = false; - public serialize(serialize = true): this { - expect(this._hasTsCode).toBe(false); - this._serialize = serialize; - return this; - } - private _luaHeader = ""; public luaHeader(luaHeader: string): this { - expect(this._hasTsCode).toBe(false); + expect(this._hasProgram).toBe(false); this._luaHeader += luaHeader; return this; } private _jsHeader = ""; public jsHeader(jsHeader: string): this { - expect(this._hasTsCode).toBe(false); + expect(this._hasProgram).toBe(false); this._jsHeader += jsHeader; return this; } private _semanticCheck = true; public disableSemanticCheck(): this { + expect(this._hasProgram).toBe(false); this._semanticCheck = false; return this; } @@ -265,13 +259,14 @@ export class TestBuilder { experimentalDecorators: true, }; public options(options: tstl.CompilerOptions = {}): this { - expect(this._hasTsCode).toBe(false); + expect(this._hasProgram).toBe(false); Object.assign(this._options, options); return this; } protected _mainFileName = "main.ts"; public setMainFileName(mainFileName: string): this { + expect(this._hasProgram).toBe(false); this._mainFileName = mainFileName; return this; } @@ -283,19 +278,18 @@ export class TestBuilder { return this; } + // TODO: Use testModule in these cases? + protected _tsHeader = ""; + public tsHeader(tsHeader: string): this { + expect(this._hasProgram).toBe(false); + this._tsHeader = tsHeader; + return this; + } + // Transpilation and execution - private _hasTsCode = false; - @memoize public getTsCode(): string { - this._hasTsCode = true; - const substitutions = this._serialize ? this.substitutions.map(valueToString) : this.substitutions; - - const templateString = this.template - .map((chunk, index) => (substitutions[index - 1] !== undefined ? substitutions[index - 1] : "") + chunk) - .join(""); - - return templateString; + return `${this._tsHeader}${this._tsCode}`; } private _hasProgram = false; @@ -482,49 +476,42 @@ class ModuleTestBuilder extends TestBuilder { class FunctionTestBuilder extends TestBuilder { protected _accessor = ".__main()"; public getTsCode(): string { - return `${this._tsHeader} export function __main() {${super.getTsCode()}}`; - } - - // TODO: Use testModule in these cases? - private _tsHeader = ""; - public tsHeader(tsHeader: string): this { - this._tsHeader = tsHeader; - return this; + return `${this._tsHeader}export function __main() {${this._tsCode}}`; } } class ExpressionTestBuilder extends TestBuilder { protected _accessor = ".__result"; public getTsCode(): string { - return `${this._tsHeader} export const __result = ${super.getTsCode()};`; - } - - private _tsHeader = ""; - public tsHeader(tsHeader: string): this { - this._tsHeader = tsHeader; - return this; + return `${this._tsHeader}export const __result = ${this._tsCode};`; } } -const templateFromValue = (valueOrTemplate: any): TemplateStringsArray => - typeof valueOrTemplate === "string" - ? Object.assign([valueOrTemplate], { raw: [valueOrTemplate] }) - : valueOrTemplate; +const createTestBuilderFactory = ( + builderClass: new (_tsCode: string) => T, + serializeSubstitutions: boolean +) => (...args: [string] | [TemplateStringsArray, ...any[]]): T => { + let tsCode: string; + if (typeof args[0] === "string") { + expect(serializeSubstitutions).toBe(false); + tsCode = args[0]; + } else { + let [template, ...substitutions] = args; + if (serializeSubstitutions) { + substitutions = substitutions.map(valueToString); + } -export function testModule(value: string): ModuleTestBuilder; -export function testModule(template: TemplateStringsArray, ...substitutions: any[]): ModuleTestBuilder; -export function testModule(valueOrTemplate: any, ...substitutions: any[]): ModuleTestBuilder { - return new ModuleTestBuilder(templateFromValue(valueOrTemplate), substitutions); -} + tsCode = template + .map((chunk, index) => (substitutions[index - 1] !== undefined ? substitutions[index - 1] : "") + chunk) + .join(""); + } -export function testFunction(value: string): FunctionTestBuilder; -export function testFunction(template: TemplateStringsArray, ...substitutions: any[]): FunctionTestBuilder; -export function testFunction(valueOrTemplate: any, ...substitutions: any[]): FunctionTestBuilder { - return new FunctionTestBuilder(templateFromValue(valueOrTemplate), substitutions); -} + return new builderClass(tsCode); +}; -export function testExpression(value: string): ExpressionTestBuilder; -export function testExpression(template: TemplateStringsArray, ...substitutions: any[]): ExpressionTestBuilder; -export function testExpression(valueOrTemplate: any, ...substitutions: any[]): ExpressionTestBuilder { - return new ExpressionTestBuilder(templateFromValue(valueOrTemplate), substitutions); -} +export const testModule = createTestBuilderFactory(ModuleTestBuilder, false); +export const testModuleTemplate = createTestBuilderFactory(ModuleTestBuilder, true); +export const testFunction = createTestBuilderFactory(FunctionTestBuilder, false); +export const testFunctionTemplate = createTestBuilderFactory(FunctionTestBuilder, true); +export const testExpression = createTestBuilderFactory(ExpressionTestBuilder, false); +export const testExpressionTemplate = createTestBuilderFactory(ExpressionTestBuilder, true); From c0ed6eb0cc522ce68ad6472f335fc1d57300e71b Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 20 Jun 2019 20:58:50 +0500 Subject: [PATCH 13/64] Rename some builder methods --- test/translation/transformation.spec.ts | 2 +- test/unit/array.spec.ts | 4 +- test/unit/assignmentDestructuring.spec.ts | 6 +- test/unit/classDecorator.spec.ts | 2 +- test/unit/conditionals.spec.ts | 2 +- test/unit/expressions.spec.ts | 12 ++-- test/unit/functions.spec.ts | 4 +- test/unit/json.spec.ts | 4 +- test/unit/modules.spec.ts | 12 ++-- test/unit/require.spec.ts | 4 +- test/unit/string.spec.ts | 2 +- test/unit/tuples.spec.ts | 4 +- test/unit/typechecking.spec.ts | 2 +- test/util.ts | 81 +++++++++++------------ 14 files changed, 69 insertions(+), 72 deletions(-) diff --git a/test/translation/transformation.spec.ts b/test/translation/transformation.spec.ts index 06d76b1ea..7a5088fcb 100644 --- a/test/translation/transformation.spec.ts +++ b/test/translation/transformation.spec.ts @@ -12,7 +12,7 @@ const fixtures = fs test.each(fixtures)("Transformation (%s)", (_name, content) => { util.testModule(content) - .options({ luaLibImport: tstl.LuaLibImportKind.Require }) + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require }) .disableSemanticCheck() .expectLuaToMatchSnapshot(); }); diff --git a/test/unit/array.spec.ts b/test/unit/array.spec.ts index 432c01fbd..1262387a7 100644 --- a/test/unit/array.spec.ts +++ b/test/unit/array.spec.ts @@ -103,8 +103,8 @@ test.each([ arr[0] = 3; return arr.${member}; ` - .luaHeader(luaHeader) - .tsHeader(tsHeader) + .setLuaHeader(luaHeader) + .setTsHeader(tsHeader) .expectToEqual(expected); }); diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 884961e49..a66464a86 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -8,19 +8,19 @@ const assignmentDestructuringCode = ` test("Assignment destructuring [5.1]", () => { util.testModule(assignmentDestructuringCode) - .options({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) .expectLuaToMatchSnapshot(); }); test("Assignment destructuring [5.2]", () => { util.testModule(assignmentDestructuringCode) - .options({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) .expectLuaToMatchSnapshot(); }); test("Assignment destructuring [JIT]", () => { util.testModule(assignmentDestructuringCode) - .options({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) .expectLuaToMatchSnapshot(); }); diff --git a/test/unit/classDecorator.spec.ts b/test/unit/classDecorator.spec.ts index 59396527a..599b7fdb5 100644 --- a/test/unit/classDecorator.spec.ts +++ b/test/unit/classDecorator.spec.ts @@ -177,6 +177,6 @@ test("Exported class decorator", () => { @decorator export class Foo {} ` - .export("Foo.bar") + .setExport("Foo.bar") .expectToMatchJsResult(); }); diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index 08f0bed09..7974a0596 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -299,7 +299,7 @@ test("switch not allowed in 5.1", () => { util.testFunction` switch ("abc") {} ` - .options({ luaTarget: tstl.LuaTarget.Lua51 }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua51 }) .expectToHaveDiagnosticOfError( TSTLErrors.UnsupportedForTarget("Switch statements", tstl.LuaTarget.Lua51, util.nodeStub) ); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 0ae9649c5..ba318a0c9 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -56,7 +56,7 @@ const allBinaryOperators = [...supportedInAll, ...unsupportedIn53]; test.each(allBinaryOperators)("Bitop [5.1] (%p)", input => { // Bit operations not supported in 5.1, expect an exception util.testExpression(input) - .options({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) .disableSemanticCheck() .expectToHaveDiagnosticOfError( TSTLErrors.UnsupportedForTarget("Bitwise operations", tstl.LuaTarget.Lua51, util.nodeStub) @@ -65,28 +65,28 @@ test.each(allBinaryOperators)("Bitop [5.1] (%p)", input => { test.each(allBinaryOperators)("Bitop [JIT] (%p)", input => { util.testExpression(input) - .options({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) .disableSemanticCheck() .expectLuaToMatchSnapshot(); }); test.each(allBinaryOperators)("Bitop [5.2] (%p)", input => { util.testExpression(input) - .options({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) .disableSemanticCheck() .expectLuaToMatchSnapshot(); }); test.each(supportedInAll)("Bitop [5.3] (%p)", input => { util.testExpression(input) - .options({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) .disableSemanticCheck() .expectLuaToMatchSnapshot(); }); test.each(unsupportedIn53)("Unsupported bitop 5.3 (%p)", input => { util.testExpression(input) - .options({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) + .setOptions({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) .disableSemanticCheck() .expectToHaveDiagnosticOfError( TSTLErrors.UnsupportedKind( @@ -156,7 +156,7 @@ test.each([ let maybeUndefinedValue: string | undefined; return ${input}; ` - .options(options) + .setOptions(options) .expectToMatchJsResult(); }); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 4f76bf983..1d8012927 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -423,7 +423,7 @@ test("Function local overriding export", () => { } export const result = bar(7); ` - .export("result") + .setExport("result") .expectToMatchJsResult(); }); @@ -435,7 +435,7 @@ test("Function using global as this", () => { } `; - util.testExpression`foo`.tsHeader(tsHeader).expectToMatchJsResult(); + util.testExpression`foo`.setTsHeader(tsHeader).expectToMatchJsResult(); }); test("Function rest binding pattern", () => { diff --git a/test/unit/json.spec.ts b/test/unit/json.spec.ts index 4e42a53ad..9fd021097 100644 --- a/test/unit/json.spec.ts +++ b/test/unit/json.spec.ts @@ -10,14 +10,14 @@ const jsonOptions = { test.each(["0", '""', "[]", '[1, "2", []]', '{ "a": "b" }', '{ "a": { "b": "c" } }'])("JSON (%p)", json => { util.testModule(json) - .options(jsonOptions) + .setOptions(jsonOptions) .setMainFileName("main.json") .expectToEqual(JSON.parse(json)); }); test("Empty JSON", () => { util.testModule("") - .options(jsonOptions) + .setOptions(jsonOptions) .setMainFileName("main.json") .expectToHaveDiagnosticOfError(TSTLErrors.InvalidJsonFileContent(util.nodeStub)); }); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index e9b1dff7a..bdc96e4e8 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -12,7 +12,7 @@ describe("module import/export elision", () => { `; const expectToElideImport: util.TapCallback = builder => { - builder.addExtraFile("module.d.ts", moduleDeclaration).options({ module: ts.ModuleKind.CommonJS }); + builder.addExtraFile("module.d.ts", moduleDeclaration).setOptions({ module: ts.ModuleKind.CommonJS }); expect(builder.getLuaExecutionResult()).not.toBeInstanceOf(util.ExecutionError); }; @@ -80,29 +80,29 @@ test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌ export { foo }; ` .disableSemanticCheck() - .luaHeader(`setmetatable(package.loaded, { __index = function() return { foo = "bar" } end })`) - .export("foo") + .setLuaHeader(`setmetatable(package.loaded, { __index = function() return { foo = "bar" } end })`) + .setExport("foo") .expectToEqual("bar"); } ); test("lualibRequire", () => { util.testExpression`b instanceof c` - .options({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.LuaJIT }) + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.LuaJIT }) .disableSemanticCheck() .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); }); test("lualibRequireAlways", () => { util.testModule`` - .options({ luaLibImport: tstl.LuaLibImportKind.Always, luaTarget: tstl.LuaTarget.LuaJIT }) + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always, luaTarget: tstl.LuaTarget.LuaJIT }) .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); }); test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( "LuaLib no uses? No code (%p)", luaLibImport => { - util.testModule``.options({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); + util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); } ); diff --git a/test/unit/require.spec.ts b/test/unit/require.spec.ts index 5af862df2..6beb5123a 100644 --- a/test/unit/require.spec.ts +++ b/test/unit/require.spec.ts @@ -86,7 +86,7 @@ test.each([ module; `; - builder.options(options).setMainFileName(filePath); + builder.setOptions(options).setMainFileName(filePath); if (throwsError) { builder.expectToHaveDiagnostics(); @@ -182,6 +182,6 @@ test("ImportEquals declaration require", () => { import foo = require("./foo/bar"); foo; ` - .options({ module: ts.ModuleKind.CommonJS }) + .setOptions({ module: ts.ModuleKind.CommonJS }) .tap(expectToRequire("foo.bar")); }); diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index 184e82b85..339b80923 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -16,7 +16,7 @@ test("Supported lua string function", () => { } `; - util.testExpression`"test".upper()`.tsHeader(tsHeader).expectToEqual("TEST"); + util.testExpression`"test".upper()`.setTsHeader(tsHeader).expectToEqual("TEST"); }); test.each([[], [65], [65, 66], [65, 66, 67]])("String.fromCharCode (%p)", (...args) => { diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index b23d6a38d..5c62149bd 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -421,7 +421,7 @@ test("Tuple Return vs Non-Tuple Return Overload", () => { const [c, d] = fn("foo", "bar"); return (a + b) + c + d; ` - .tsHeader(tsHeader) - .luaHeader(luaHeader) + .setTsHeader(tsHeader) + .setLuaHeader(luaHeader) .expectToEqual("7foobar"); }); diff --git a/test/unit/typechecking.spec.ts b/test/unit/typechecking.spec.ts index 8254d9989..64aaa381b 100644 --- a/test/unit/typechecking.spec.ts +++ b/test/unit/typechecking.spec.ts @@ -98,7 +98,7 @@ test("instanceof export", () => { let inst = new myClass(); export const result = inst instanceof myClass; ` - .export("result") + .setExport("result") .expectToMatchJsResult(); }); diff --git a/test/util.ts b/test/util.ts index a1fb7c9d7..1e94ae75c 100644 --- a/test/util.ts +++ b/test/util.ts @@ -224,33 +224,41 @@ export class ExecutionError extends Error { export type TapCallback = (builder: TestBuilder) => void; export class TestBuilder { - protected _accessor = ""; + protected accessor = ""; constructor(protected _tsCode: string) {} // Options - private _luaHeader = ""; - public luaHeader(luaHeader: string): this { + // TODO: Use testModule in these cases? + protected tsHeader = ""; + public setTsHeader(tsHeader: string): this { + expect(this._hasProgram).toBe(false); + this.tsHeader = tsHeader; + return this; + } + + private luaHeader = ""; + public setLuaHeader(luaHeader: string): this { expect(this._hasProgram).toBe(false); - this._luaHeader += luaHeader; + this.luaHeader += luaHeader; return this; } - private _jsHeader = ""; - public jsHeader(jsHeader: string): this { + private jsHeader = ""; + public setJsHeader(jsHeader: string): this { expect(this._hasProgram).toBe(false); - this._jsHeader += jsHeader; + this.jsHeader += jsHeader; return this; } - private _semanticCheck = true; + private semanticCheck = true; public disableSemanticCheck(): this { expect(this._hasProgram).toBe(false); - this._semanticCheck = false; + this.semanticCheck = false; return this; } - private _options: tstl.CompilerOptions = { + private options: tstl.CompilerOptions = { luaTarget: tstl.LuaTarget.Lua53, noHeader: true, skipLibCheck: true, @@ -258,48 +266,37 @@ export class TestBuilder { lib: ["lib.esnext.d.ts"], experimentalDecorators: true, }; - public options(options: tstl.CompilerOptions = {}): this { + public setOptions(options: tstl.CompilerOptions = {}): this { expect(this._hasProgram).toBe(false); - Object.assign(this._options, options); + Object.assign(this.options, options); return this; } - protected _mainFileName = "main.ts"; + protected mainFileName = "main.ts"; public setMainFileName(mainFileName: string): this { expect(this._hasProgram).toBe(false); - this._mainFileName = mainFileName; + this.mainFileName = mainFileName; return this; } - private _extraFiles: Record = {}; + private extraFiles: Record = {}; public addExtraFile(fileName: string, code: string): this { expect(this._hasProgram).toBe(false); - this._extraFiles[fileName] = code; - return this; - } - - // TODO: Use testModule in these cases? - protected _tsHeader = ""; - public tsHeader(tsHeader: string): this { - expect(this._hasProgram).toBe(false); - this._tsHeader = tsHeader; + this.extraFiles[fileName] = code; return this; } // Transpilation and execution public getTsCode(): string { - return `${this._tsHeader}${this._tsCode}`; + return `${this.tsHeader}${this._tsCode}`; } private _hasProgram = false; @memoize public getProgram(): ts.Program { this._hasProgram = true; - return tstl.createVirtualProgram( - { ...this._extraFiles, [this._mainFileName]: this.getTsCode() }, - this._options - ); + return tstl.createVirtualProgram({ ...this.extraFiles, [this.mainFileName]: this.getTsCode() }, this.options); } @memoize @@ -317,10 +314,10 @@ export class TestBuilder { @memoize public getMainLuaCodeChunk(): string { const { transpiledFiles } = this.getLuaResult(); - const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); + const mainFile = transpiledFiles.find(x => x.fileName === this.mainFileName); expect(mainFile).toBeDefined(); - const header = this._luaHeader ? `${this._luaHeader.trimRight()}\n` : ""; + const header = this.luaHeader ? `${this.luaHeader.trimRight()}\n` : ""; return header + mainFile!.lua!.trimRight(); } @@ -331,7 +328,7 @@ export class TestBuilder { code = `package.preload.lualib_bundle = function()\n${lualibContent}\nend\n${code}`; } - return `${minimalTestLib}\nreturn JSONStringify((function()\n${code}\nend)()${this._accessor})`; + return `${minimalTestLib}\nreturn JSONStringify((function()\n${code}\nend)()${this.accessor})`; } @memoize @@ -365,11 +362,11 @@ export class TestBuilder { @memoize protected getJsCode(): string { const { transpiledFiles } = this.getJsResult(); - const mainFile = transpiledFiles.find(x => x.fileName === this._mainFileName); + const mainFile = transpiledFiles.find(x => x.fileName === this.mainFileName); expect(mainFile).toBeDefined(); - const header = this._jsHeader ? `${this._jsHeader.trimRight()}\n` : ""; - return header + mainFile!.js! + `;module.exports = exports${this._accessor}`; + const header = this.jsHeader ? `${this.jsHeader.trimRight()}\n` : ""; + return header + mainFile!.js! + `;module.exports = exports${this.accessor}`; } @memoize @@ -388,7 +385,7 @@ export class TestBuilder { private getLuaDiagnostics(): ts.Diagnostic[] { const { diagnostics } = this.getLuaResult(); - return diagnostics.filter(d => this._semanticCheck || d.source === "typescript-to-lua"); + return diagnostics.filter(d => this.semanticCheck || d.source === "typescript-to-lua"); } // Actions @@ -467,23 +464,23 @@ export class TestBuilder { } class ModuleTestBuilder extends TestBuilder { - public export(name: string): this { - this._accessor = `.${name}`; + public setExport(name: string): this { + this.accessor = `.${name}`; return this; } } class FunctionTestBuilder extends TestBuilder { - protected _accessor = ".__main()"; + protected accessor = ".__main()"; public getTsCode(): string { - return `${this._tsHeader}export function __main() {${this._tsCode}}`; + return `${this.tsHeader}export function __main() {${this._tsCode}}`; } } class ExpressionTestBuilder extends TestBuilder { - protected _accessor = ".__result"; + protected accessor = ".__result"; public getTsCode(): string { - return `${this._tsHeader}export const __result = ${this._tsCode};`; + return `${this.tsHeader}export const __result = ${this._tsCode};`; } } From 849fd2b2378f1987356437ef3f201f5708289339 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 20 Jun 2019 21:04:54 +0500 Subject: [PATCH 14/64] numbers.spec.ts --- test/unit/numbers.spec.ts | 53 +++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/test/unit/numbers.spec.ts b/test/unit/numbers.spec.ts index ecda5d93a..b42738563 100644 --- a/test/unit/numbers.spec.ts +++ b/test/unit/numbers.spec.ts @@ -11,59 +11,52 @@ test.each([ "1 + NaN", "1 / NaN", "NaN * 0", -])("%s", code => expect(util.transpileAndExecute(`return ${code}`)).toBe(eval(code))); -test("NaN reassignment", () => { - const result = util.transpileAndExecute(`const NaN = 1; return NaN`); + "Infinity", + "Infinity - Infinity", + "Infinity / -1", + "Infinity * -1", + "Infinity + 1", + "Infinity - 1", +])("%s", code => util.testExpression(code).expectToMatchJsResult()); - expect(result).toBe(NaN); +test("NaN reassignment", () => { + util.testFunction` + const NaN = 1; + return NaN; + `.expectToMatchJsResult(); }); -test.each(["Infinity", "Infinity - Infinity", "Infinity / -1", "Infinity * -1", "Infinity + 1", "Infinity - 1"])( - "%s", - code => expect(util.transpileAndExecute(`return ${code}`)).toBe(eval(code)) -); - test("Infinity reassignment", () => { - const result = util.transpileAndExecute(`const Infinity = 1; return Infinity`); - - expect(result).toBe(Infinity); + util.testFunction` + const Infinity = 1; + return Infinity; + `.expectToMatchJsResult(); }); const numberCases = [-1, 0, 1, 1.5, Infinity, -Infinity]; const stringCases = ["-1", "0", "1", "1.5", "Infinity", "-Infinity"]; -const restCases: any[] = [true, false, "", " ", "\t", "\n", "foo", {}]; -const cases: any[] = [...numberCases, ...stringCases, ...restCases]; +const restCases = [true, false, "", " ", "\t", "\n", "foo", {}]; +const cases = [...numberCases, ...stringCases, ...restCases]; describe("Number", () => { test.each(cases)("constructor(%p)", value => { - const result = util.transpileAndExecute(`return Number(${util.valueToString(value)})`); - expect(result).toBe(Number(value)); + util.testExpressionTemplate`Number(${value})`.expectToMatchJsResult(); }); test.each(cases)("isNaN(%p)", value => { - const result = util.transpileAndExecute(` - return Number.isNaN(${util.valueToString(value)} as any) - `); - - expect(result).toBe(Number.isNaN(value)); + util.testExpressionTemplate`Number.isNaN(${value} as any)`.expectToMatchJsResult(); }); test.each(cases)("isFinite(%p)", value => { - const result = util.transpileAndExecute(` - return Number.isFinite(${util.valueToString(value)} as any) - `); - - expect(result).toBe(Number.isFinite(value)); + util.testExpressionTemplate`Number.isFinite(${value} as any)`.expectToMatchJsResult(); }); }); test.each(cases)("isNaN(%p)", value => { - const result = util.transpileAndExecute(`return isNaN(${util.valueToString(value)} as any)`); - expect(result).toBe(isNaN(value)); + util.testExpressionTemplate`isNaN(${value} as any)`.expectToMatchJsResult(); }); test.each(cases)("isFinite(%p)", value => { - const result = util.transpileAndExecute(`return isFinite(${util.valueToString(value)} as any)`); - expect(result).toBe(isFinite(value)); + util.testExpressionTemplate`isFinite(${value} as any)`.expectToMatchJsResult(); }); From 8f184325e3e1dcf8b2f1f047454d36a1241be381 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 01:10:35 +0500 Subject: [PATCH 15/64] Serialize NaN and Infinity test results --- test/json.lua | 13 +++++++++---- test/unit/lualib/array.spec.ts | 4 ++-- test/unit/numbers.spec.ts | 15 +++++---------- test/util.ts | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/json.lua b/test/json.lua index bac05cda3..e41c3798a 100644 --- a/test/json.lua +++ b/test/json.lua @@ -112,11 +112,15 @@ end local function encode_number(val) - -- Check for NaN, -inf and inf - if val ~= val or val <= -math.huge or val >= math.huge then - error("unexpected number value '" .. tostring(val) .. "'") + if val ~= val then + return "NaN" + elseif val == math.huge then + return "Infinity" + elseif val == -math.huge then + return "-Infinity" + else + return string.format("%.17g", val) end - return string.format("%.17g", val) end @@ -139,6 +143,7 @@ encode = function(val, stack) end +-- TODO: Since it supports NaN and Infinity it is considered a superset of JSON, so it probably should be renamed function JSONStringify(val) return ( encode(val) ) end diff --git a/test/unit/lualib/array.spec.ts b/test/unit/lualib/array.spec.ts index 5f1d3b541..2fff3ead0 100644 --- a/test/unit/lualib/array.spec.ts +++ b/test/unit/lualib/array.spec.ts @@ -202,7 +202,7 @@ test.each([[1, 2, 3], [1, 2, 3, 4], [1], []])("array.reverse (%p)", (...array) = util.testFunction` let array = ${util.valueToString(array)}; let val = array.reverse(); - return array + return array; `.expectToMatchJsResult(); }); @@ -210,7 +210,7 @@ test.each([[1, 2, 3], [1], []])("array.shift (%p)", (...array) => { util.testFunction` let array = ${util.valueToString(array)}; let value = array.shift(); - return { array, value } + return { array, value }; `.expectToMatchJsResult(); }); diff --git a/test/unit/numbers.spec.ts b/test/unit/numbers.spec.ts index b42738563..95d249ac2 100644 --- a/test/unit/numbers.spec.ts +++ b/test/unit/numbers.spec.ts @@ -18,19 +18,14 @@ test.each([ "Infinity * -1", "Infinity + 1", "Infinity - 1", -])("%s", code => util.testExpression(code).expectToMatchJsResult()); - -test("NaN reassignment", () => { - util.testFunction` - const NaN = 1; - return NaN; - `.expectToMatchJsResult(); +])("%s", code => { + util.testExpression(code).expectToMatchJsResult(); }); -test("Infinity reassignment", () => { +test.skip.each(["NaN", "Infinity"])("%s reassignment", name => { util.testFunction` - const Infinity = 1; - return Infinity; + const ${name} = 1; + return ${name}; `.expectToMatchJsResult(); }); diff --git a/test/util.ts b/test/util.ts index 1e94ae75c..c3b56b68d 100644 --- a/test/util.ts +++ b/test/util.ts @@ -340,7 +340,7 @@ export class TestBuilder { if (status === lua.LUA_OK) { if (lua.lua_isstring(L, -1)) { - const result = JSON.parse(lua.lua_tojsstring(L, -1)); + const result = eval(`(${lua.lua_tojsstring(L, -1)})`); return result === null ? undefined : result; } else { const returnType = to_jsstring(lua.lua_typename(L, lua.lua_type(L, -1))); From 6b71abb56286d3cf7f3d229f8cbf4c0d4277cebb Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 01:20:24 +0500 Subject: [PATCH 16/64] math.spec.ts --- test/unit/__snapshots__/math.spec.ts.snap | 89 +++++ test/unit/math.spec.ts | 384 +++++++++++----------- 2 files changed, 277 insertions(+), 196 deletions(-) create mode 100644 test/unit/__snapshots__/math.spec.ts.snap diff --git a/test/unit/__snapshots__/math.spec.ts.snap b/test/unit/__snapshots__/math.spec.ts.snap new file mode 100644 index 000000000..21d3f80d1 --- /dev/null +++ b/test/unit/__snapshots__/math.spec.ts.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Math ("Math.PI") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____ = math.pi +end +return ____exports" +`; + +exports[`Math ("Math.atan2(2, 3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + math.atan(2 / 3) +end +return ____exports" +`; + +exports[`Math ("Math.cos()") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + math.cos() +end +return ____exports" +`; + +exports[`Math ("Math.log1p(3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + math.log(1 + 3) +end +return ____exports" +`; + +exports[`Math ("Math.log2(3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + (function() return math.log(3) / 0.6931471805599453 end)() +end +return ____exports" +`; + +exports[`Math ("Math.log10(3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + (function() return math.log(3) / 2.302585092994046 end)() +end +return ____exports" +`; + +exports[`Math ("Math.min()") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + math.min() +end +return ____exports" +`; + +exports[`Math ("Math.round(3.3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + math.floor(3.3 + 0.5) +end +return ____exports" +`; + +exports[`Math ("Math.sin()") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + math.sin() +end +return ____exports" +`; + +exports[`Math ("const x = Math.log2(3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = (math.log(3) / 0.6931471805599453) +end +return ____exports" +`; + +exports[`Math ("const x = Math.log10(3)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = (math.log(3) / 2.302585092994046) +end +return ____exports" +`; diff --git a/test/unit/math.spec.ts b/test/unit/math.spec.ts index 9ff3442f7..d2430a8a1 100644 --- a/test/unit/math.spec.ts +++ b/test/unit/math.spec.ts @@ -1,253 +1,245 @@ import * as util from "../util"; test.each([ - { inp: "Math.cos()", expected: "math.cos()" }, - { inp: "Math.sin()", expected: "math.sin()" }, - { inp: "Math.min()", expected: "math.min()" }, - { inp: "Math.atan2(2, 3)", expected: "math.atan(2 / 3)" }, - { inp: "Math.log2(3)", expected: `(function() return math.log(3) / ${Math.LN2} end)()` }, - { inp: "Math.log10(3)", expected: `(function() return math.log(3) / ${Math.LN10} end)()` }, - { inp: "const x = Math.log2(3)", expected: `local x = (math.log(3) / ${Math.LN2})` }, - { inp: "const x = Math.log10(3)", expected: `local x = (math.log(3) / ${Math.LN10})` }, - { inp: "Math.log1p(3)", expected: "math.log(1 + 3)" }, - { inp: "Math.round(3.3)", expected: "math.floor(3.3 + 0.5)" }, - { inp: "Math.PI", expected: "local ____ = math.pi" }, -])("Math (%p)", ({ inp, expected }) => { - const lua = util.transpileString(inp); - - expect(lua).toBe(expected); + "Math.cos()", + "Math.sin()", + "Math.min()", + "Math.atan2(2, 3)", + "Math.log2(3)", + "Math.log10(3)", + "const x = Math.log2(3)", + "const x = Math.log10(3)", + "Math.log1p(3)", + "Math.round(3.3)", + "Math.PI", +])("Math (%p)", code => { + // TODO: Remove? + util.testFunction(code) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); }); test.each(["E", "LN10", "LN2", "LOG10E", "LOG2E", "SQRT1_2", "SQRT2"])("Math constant (%p)", constant => { - const epsilon = 0.000001; - const jsValue: number = (Math as Math & { [key: string]: any })[constant]; - const code = `return Math.abs(Math.${constant} - ${jsValue}) <= ${epsilon}`; - expect(util.transpileAndExecute(code)).toBe(true); + util.testExpression`Math.${constant}`.tap(builder => { + expect(builder.getLuaExecutionResult()).toBeCloseTo(builder.getJsExecutionResult()); + }); }); test.each([ - { statement: "++x", expected: "x=4;y=6" }, - { statement: "x++", expected: "x=4;y=6" }, - { statement: "--x", expected: "x=2;y=6" }, - { statement: "x--", expected: "x=2;y=6" }, - { statement: "x += y", expected: "x=9;y=6" }, - { statement: "x -= y", expected: "x=-3;y=6" }, - { statement: "x *= y", expected: "x=18;y=6" }, - { statement: "y /= x", expected: "x=3;y=2.0" }, - { statement: "y %= x", expected: "x=3;y=0" }, - { statement: "y **= x", expected: "x=3;y=216.0" }, - { statement: "x |= y", expected: "x=7;y=6" }, - { statement: "x &= y", expected: "x=2;y=6" }, - { statement: "x ^= y", expected: "x=5;y=6" }, - { statement: "x <<= y", expected: "x=192;y=6" }, - { statement: "x >>>= y", expected: "x=0;y=6" }, -])("Operator assignment statements (%p)", ({ statement, expected }) => { - const result = util.transpileAndExecute( - `let x = 3; + "++x", + "x++", + "--x", + "x--", + "x += y", + "x -= y", + "x *= y", + "y /= x", + "y %= x", + "y **= x", + "x |= y", + "x &= y", + "x ^= y", + "x <<= y", + "x >>>= y", +])("Operator assignment statements (%p)", statement => { + util.testFunction` + let x = 3; let y = 6; ${statement}; - return \`x=\${x};y=\${y}\`` - ); - expect(result).toBe(expected); + return { x, y }; + `.expectToMatchJsResult(); }); test.each([ - { statement: "++o.p", expected: "o=4;a=6" }, - { statement: "o.p++", expected: "o=4;a=6" }, - { statement: "--o.p", expected: "o=2;a=6" }, - { statement: "o.p--", expected: "o=2;a=6" }, - { statement: "o.p += a[0]", expected: "o=9;a=6" }, - { statement: "o.p -= a[0]", expected: "o=-3;a=6" }, - { statement: "o.p *= a[0]", expected: "o=18;a=6" }, - { statement: "a[0] /= o.p", expected: "o=3;a=2.0" }, - { statement: "a[0] %= o.p", expected: "o=3;a=0" }, - { statement: "a[0] **= o.p", expected: "o=3;a=216.0" }, - { statement: "o.p |= a[0]", expected: "o=7;a=6" }, - { statement: "o.p &= a[0]", expected: "o=2;a=6" }, - { statement: "o.p ^= a[0]", expected: "o=5;a=6" }, - { statement: "o.p <<= a[0]", expected: "o=192;a=6" }, - { statement: "o.p >>>= a[0]", expected: "o=0;a=6" }, -])("Operator assignment to simple property statements (%p)", ({ statement, expected }) => { - const result = util.transpileAndExecute( - `let o = {p: 3}; + "++o.p", + "o.p++", + "--o.p", + "o.p--", + "o.p += a[0]", + "o.p -= a[0]", + "o.p *= a[0]", + "a[0] /= o.p", + "a[0] %= o.p", + "a[0] **= o.p", + "o.p |= a[0]", + "o.p &= a[0]", + "o.p ^= a[0]", + "o.p <<= a[0]", + "o.p >>>= a[0]", +])("Operator assignment to simple property statements (%p)", statement => { + util.testFunction` + let o = { p: 3 }; let a = [6]; ${statement}; - return \`o=\${o.p};a=\${a[0]}\`` - ); - expect(result).toBe(expected); + return { o, a }; + `.expectToMatchJsResult(); }); test.each([ - { statement: "++o.p.d", expected: "o=4;a=[6,11],[7,13]" }, - { statement: "o.p.d++", expected: "o=4;a=[6,11],[7,13]" }, - { statement: "--o.p.d", expected: "o=2;a=[6,11],[7,13]" }, - { statement: "o.p.d--", expected: "o=2;a=[6,11],[7,13]" }, - { statement: "o.p.d += a[0][0]", expected: "o=9;a=[6,11],[7,13]" }, - { statement: "o.p.d -= a[0][0]", expected: "o=-3;a=[6,11],[7,13]" }, - { statement: "o.p.d *= a[0][0]", expected: "o=18;a=[6,11],[7,13]" }, - { statement: "a[0][0] /= o.p.d", expected: "o=3;a=[2.0,11],[7,13]" }, - { statement: "a[0][0] %= o.p.d", expected: "o=3;a=[0,11],[7,13]" }, - { statement: "a[0][0] **= o.p.d", expected: "o=3;a=[216.0,11],[7,13]" }, - { statement: "o.p.d |= a[0][0]", expected: "o=7;a=[6,11],[7,13]" }, - { statement: "o.p.d &= a[0][0]", expected: "o=2;a=[6,11],[7,13]" }, - { statement: "o.p.d ^= a[0][0]", expected: "o=5;a=[6,11],[7,13]" }, - { statement: "o.p.d <<= a[0][0]", expected: "o=192;a=[6,11],[7,13]" }, - { statement: "o.p.d >>>= a[0][0]", expected: "o=0;a=[6,11],[7,13]" }, -])("Operator assignment to deep property statements (%p)", ({ statement, expected }) => { - const result = util.transpileAndExecute( - `let o = {p: {d: 3}}; + "++o.p.d", + "o.p.d++", + "--o.p.d", + "o.p.d--", + "o.p.d += a[0][0]", + "o.p.d -= a[0][0]", + "o.p.d *= a[0][0]", + "a[0][0] /= o.p.d", + "a[0][0] %= o.p.d", + "a[0][0] **= o.p.d", + "o.p.d |= a[0][0]", + "o.p.d &= a[0][0]", + "o.p.d ^= a[0][0]", + "o.p.d <<= a[0][0]", + "o.p.d >>>= a[0][0]", +])("Operator assignment to deep property statements (%p)", statement => { + util.testFunction` + let o = { p: { d: 3 } }; let a = [[6,11], [7,13]]; ${statement}; - return \`o=\${o.p.d};a=[\${a[0][0]},\${a[0][1]}],[\${a[1][0]},\${a[1][1]}]\`` - ); - expect(result).toBe(expected); + return { o, a }; + `.expectToMatchJsResult(); }); test.each([ - { statement: "++of().p", expected: "o=4;a=6" }, - { statement: "of().p++", expected: "o=4;a=6" }, - { statement: "--of().p", expected: "o=2;a=6" }, - { statement: "of().p--", expected: "o=2;a=6" }, - { statement: "of().p += af()[i()]", expected: "o=9;a=6" }, - { statement: "of().p -= af()[i()]", expected: "o=-3;a=6" }, - { statement: "of().p *= af()[i()]", expected: "o=18;a=6" }, - { statement: "af()[i()] /= of().p", expected: "o=3;a=2.0" }, - { statement: "af()[i()] %= of().p", expected: "o=3;a=0" }, - { statement: "af()[i()] **= of().p", expected: "o=3;a=216.0" }, - { statement: "of().p |= af()[i()]", expected: "o=7;a=6" }, - { statement: "of().p &= af()[i()]", expected: "o=2;a=6" }, - { statement: "of().p ^= af()[i()]", expected: "o=5;a=6" }, - { statement: "of().p <<= af()[i()]", expected: "o=192;a=6" }, - { statement: "of().p >>>= af()[i()]", expected: "o=0;a=6" }, -])("Operator assignment to complex property statements (%p)", ({ statement, expected }) => { - const result = util.transpileAndExecute( - `let o = {p: 3}; + "++of().p", + "of().p++", + "--of().p", + "of().p--", + "of().p += af()[i()]", + "of().p -= af()[i()]", + "of().p *= af()[i()]", + "af()[i()] /= of().p", + "af()[i()] %= of().p", + "af()[i()] **= of().p", + "of().p |= af()[i()]", + "of().p &= af()[i()]", + "of().p ^= af()[i()]", + "of().p <<= af()[i()]", + "of().p >>>= af()[i()]", +])("Operator assignment to complex property statements (%p)", statement => { + util.testFunction` + let o = { p: 3 }; let a = [6]; function of() { return o; } function af() { return a; } function i() { return 0; } ${statement}; - return \`o=\${o.p};a=\${a[0]}\`` - ); - expect(result).toBe(expected); + return { o, a }; + `.expectToMatchJsResult(); }); test.each([ - { statement: "++of().p.d", expected: "o=4;a=[7,6],[11,13];i=0" }, - { statement: "of().p.d++", expected: "o=4;a=[7,6],[11,13];i=0" }, - { statement: "--of().p.d", expected: "o=2;a=[7,6],[11,13];i=0" }, - { statement: "of().p.d--", expected: "o=2;a=[7,6],[11,13];i=0" }, - { statement: "of().p.d += af()[i()][i()]", expected: "o=9;a=[7,6],[11,13];i=2" }, - { statement: "of().p.d -= af()[i()][i()]", expected: "o=-3;a=[7,6],[11,13];i=2" }, - { statement: "of().p.d *= af()[i()][i()]", expected: "o=18;a=[7,6],[11,13];i=2" }, - { statement: "af()[i()][i()] /= of().p.d", expected: "o=3;a=[7,2.0],[11,13];i=2" }, - { statement: "af()[i()][i()] %= of().p.d", expected: "o=3;a=[7,0],[11,13];i=2" }, - { statement: "af()[i()][i()] **= of().p.d", expected: "o=3;a=[7,216.0],[11,13];i=2" }, - { statement: "of().p.d |= af()[i()][i()]", expected: "o=7;a=[7,6],[11,13];i=2" }, - { statement: "of().p.d &= af()[i()][i()]", expected: "o=2;a=[7,6],[11,13];i=2" }, - { statement: "of().p.d ^= af()[i()][i()]", expected: "o=5;a=[7,6],[11,13];i=2" }, - { statement: "of().p.d <<= af()[i()][i()]", expected: "o=192;a=[7,6],[11,13];i=2" }, - { statement: "of().p.d >>>= af()[i()][i()]", expected: "o=0;a=[7,6],[11,13];i=2" }, -])("Operator assignment to complex deep property statements (%p)", ({ statement, expected }) => { - const result = util.transpileAndExecute( - `let o = {p: {d: 3}}; + "++of().p.d", + "of().p.d++", + "--of().p.d", + "of().p.d--", + "of().p.d += af()[i()][i()]", + "of().p.d -= af()[i()][i()]", + "of().p.d *= af()[i()][i()]", + "af()[i()][i()] /= of().p.d", + "af()[i()][i()] %= of().p.d", + "af()[i()][i()] **= of().p.d", + "of().p.d |= af()[i()][i()]", + "of().p.d &= af()[i()][i()]", + "of().p.d ^= af()[i()][i()]", + "of().p.d <<= af()[i()][i()]", + "of().p.d >>>= af()[i()][i()]", +])("Operator assignment to complex deep property statements (%p)", statement => { + util.testFunction` + let o = { p: { d: 3 } }; let a = [[7, 6], [11, 13]]; function of() { return o; } function af() { return a; } let _i = 0; function i() { return _i++; } ${statement}; - return \`o=\${o.p.d};a=[\${a[0][0]},\${a[0][1]}],[\${a[1][0]},\${a[1][1]}];i=\${_i}\`` - ); - expect(result).toBe(expected); + return { o, a, _i }; + `.expectToMatchJsResult(); }); test.each([ - { expression: "++x", expected: "4;x=4;y=6" }, - { expression: "x++", expected: "3;x=4;y=6" }, - { expression: "--x", expected: "2;x=2;y=6" }, - { expression: "x--", expected: "3;x=2;y=6" }, - { expression: "x += y", expected: "9;x=9;y=6" }, - { expression: "x -= y", expected: "-3;x=-3;y=6" }, - { expression: "x *= y", expected: "18;x=18;y=6" }, - { expression: "y /= x", expected: "2.0;x=3;y=2.0" }, - { expression: "y %= x", expected: "0;x=3;y=0" }, - { expression: "y **= x", expected: "216.0;x=3;y=216.0" }, - { expression: "x |= y", expected: "7;x=7;y=6" }, - { expression: "x &= y", expected: "2;x=2;y=6" }, - { expression: "x ^= y", expected: "5;x=5;y=6" }, - { expression: "x <<= y", expected: "192;x=192;y=6" }, - { expression: "x >>>= y", expected: "0;x=0;y=6" }, - { expression: "x + (y += 7)", expected: "16;x=3;y=13" }, - { expression: "x + (y += 7)", expected: "16;x=3;y=13" }, - { expression: "x++ + (y += 7)", expected: "16;x=4;y=13" }, -])("Operator assignment expressions (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `let x = 3; + "++x", + "x++", + "--x", + "x--", + "x += y", + "x -= y", + "x *= y", + "y /= x", + "y %= x", + "y **= x", + "x |= y", + "x &= y", + "x ^= y", + "x <<= y", + "x >>>= y", + "x + (y += 7)", + "x + (y += 7)", + "x++ + (y += 7)", +])("Operator assignment expressions (%p)", expression => { + util.testFunction` + let x = 3; let y = 6; const r = ${expression}; - return \`\${r};x=\${x};y=\${y}\`` - ); - expect(result).toBe(expected); + return { r, x, y }; + `.expectToMatchJsResult(); }); test.each([ - { expression: "++o.p", expected: "4;o=4;a=6" }, - { expression: "o.p++", expected: "3;o=4;a=6" }, - { expression: "--o.p", expected: "2;o=2;a=6" }, - { expression: "o.p--", expected: "3;o=2;a=6" }, - { expression: "o.p += a[0]", expected: "9;o=9;a=6" }, - { expression: "o.p -= a[0]", expected: "-3;o=-3;a=6" }, - { expression: "o.p *= a[0]", expected: "18;o=18;a=6" }, - { expression: "a[0] /= o.p", expected: "2.0;o=3;a=2.0" }, - { expression: "a[0] %= o.p", expected: "0;o=3;a=0" }, - { expression: "a[0] **= o.p", expected: "216.0;o=3;a=216.0" }, - { expression: "o.p |= a[0]", expected: "7;o=7;a=6" }, - { expression: "o.p &= a[0]", expected: "2;o=2;a=6" }, - { expression: "o.p ^= a[0]", expected: "5;o=5;a=6" }, - { expression: "o.p <<= a[0]", expected: "192;o=192;a=6" }, - { expression: "o.p >>>= a[0]", expected: "0;o=0;a=6" }, - { expression: "o.p + (a[0] += 7)", expected: "16;o=3;a=13" }, - { expression: "o.p += (a[0] += 7)", expected: "16;o=16;a=13" }, - { expression: "o.p++ + (a[0] += 7)", expected: "16;o=4;a=13" }, -])("Operator assignment to simple property expressions (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `let o = {p: 3}; + "++o.p", + "o.p++", + "--o.p", + "o.p--", + "o.p += a[0]", + "o.p -= a[0]", + "o.p *= a[0]", + "a[0] /= o.p", + "a[0] %= o.p", + "a[0] **= o.p", + "o.p |= a[0]", + "o.p &= a[0]", + "o.p ^= a[0]", + "o.p <<= a[0]", + "o.p >>>= a[0]", + "o.p + (a[0] += 7)", + "o.p += (a[0] += 7)", + "o.p++ + (a[0] += 7)", +])("Operator assignment to simple property expressions (%p)", expression => { + util.testFunction` + let o = { p: 3 }; let a = [6]; const r = ${expression}; - return \`\${r};o=\${o.p};a=\${a[0]}\`` - ); - expect(result).toBe(expected); + return { r, o, a }; + `.expectToMatchJsResult(); }); test.each([ - { expression: "++of().p", expected: "4;o=4;a=6" }, - { expression: "of().p++", expected: "3;o=4;a=6" }, - { expression: "--of().p", expected: "2;o=2;a=6" }, - { expression: "of().p--", expected: "3;o=2;a=6" }, - { expression: "of().p += af()[i()]", expected: "9;o=9;a=6" }, - { expression: "of().p -= af()[i()]", expected: "-3;o=-3;a=6" }, - { expression: "of().p *= af()[i()]", expected: "18;o=18;a=6" }, - { expression: "af()[i()] /= of().p", expected: "2.0;o=3;a=2.0" }, - { expression: "af()[i()] %= of().p", expected: "0;o=3;a=0" }, - { expression: "af()[i()] **= of().p", expected: "216.0;o=3;a=216.0" }, - { expression: "of().p |= af()[i()]", expected: "7;o=7;a=6" }, - { expression: "of().p &= af()[i()]", expected: "2;o=2;a=6" }, - { expression: "of().p ^= af()[i()]", expected: "5;o=5;a=6" }, - { expression: "of().p <<= af()[i()]", expected: "192;o=192;a=6" }, - { expression: "of().p >>>= af()[i()]", expected: "0;o=0;a=6" }, - { expression: "of().p + (af()[i()] += 7)", expected: "16;o=3;a=13" }, - { expression: "of().p += (af()[i()] += 7)", expected: "16;o=16;a=13" }, - { expression: "of().p++ + (af()[i()] += 7)", expected: "16;o=4;a=13" }, -])("Operator assignment to complex property expressions (%p)", ({ expression, expected }) => { - const result = util.transpileAndExecute( - `let o = {p: 3}; + "++of().p", + "of().p++", + "--of().p", + "of().p--", + "of().p += af()[i()]", + "of().p -= af()[i()]", + "of().p *= af()[i()]", + "af()[i()] /= of().p", + "af()[i()] %= of().p", + "af()[i()] **= of().p", + "of().p |= af()[i()]", + "of().p &= af()[i()]", + "of().p ^= af()[i()]", + "of().p <<= af()[i()]", + "of().p >>>= af()[i()]", + "of().p + (af()[i()] += 7)", + "of().p += (af()[i()] += 7)", + "of().p++ + (af()[i()] += 7)", +])("Operator assignment to complex property expressions (%p)", expression => { + util.testFunction` + let o = { p: 3 }; let a = [6]; function of() { return o; } function af() { return a; } function i() { return 0; } const r = ${expression}; - return \`\${r};o=\${o.p};a=\${a[0]}\`` - ); - expect(result).toBe(expected); + return { r, o, a }; + `.expectToMatchJsResult(); }); From eb53e170dd2a53d0be19f53fceda0fb2ad1fc31f Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 01:29:00 +0500 Subject: [PATCH 17/64] console.spec.ts --- test/unit/console.spec.ts | 83 ------------ .../lualib/__snapshots__/console.spec.ts.snap | 121 ++++++++++++++++++ test/unit/lualib/console.spec.ts | 56 ++++++++ 3 files changed, 177 insertions(+), 83 deletions(-) delete mode 100644 test/unit/console.spec.ts create mode 100644 test/unit/lualib/__snapshots__/console.spec.ts.snap create mode 100644 test/unit/lualib/console.spec.ts diff --git a/test/unit/console.spec.ts b/test/unit/console.spec.ts deleted file mode 100644 index 729a4440c..000000000 --- a/test/unit/console.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as util from "../util"; - -const compilerOptions = { lib: ["lib.es2015.d.ts", "lib.dom.d.ts"] }; - -test.each([ - { inp: "console.log()", expected: "print()" }, - { inp: 'console.log("Hello")', expected: 'print("Hello")' }, - { inp: 'console.log("Hello %s", "there")', expected: 'print(string.format("Hello %s", "there"))' }, - { inp: 'console.log("Hello %%s", "there")', expected: 'print(string.format("Hello %%s", "there"))' }, - { inp: 'console.log("Hello", "There")', expected: 'print("Hello", "There")' }, -])("console.log (%p)", ({ inp, expected }) => { - expect(util.transpileString(inp, compilerOptions)).toBe(expected); -}); - -test.each([ - { - inp: "console.trace()", - expected: "print(debug.traceback())", - }, - { - inp: 'console.trace("message")', - expected: 'print(debug.traceback("message"))', - }, - { - inp: 'console.trace("Hello %s", "there")', - expected: 'print(debug.traceback(string.format("Hello %s", "there")))', - }, - { - inp: 'console.trace("Hello %%s", "there")', - expected: 'print(debug.traceback(string.format("Hello %%s", "there")))', - }, - { - inp: 'console.trace("Hello", "there")', - expected: 'print(debug.traceback("Hello", "there"))', - }, -])("console.trace (%p)", ({ inp, expected }) => { - expect(util.transpileString(inp, compilerOptions)).toBe(expected); -}); - -test.each([ - { - inp: "console.assert(false)", - expected: "assert(false)", - }, - { - inp: 'console.assert(false, "message")', - expected: 'assert(false, "message")', - }, - { - inp: 'console.assert(false, "message %s", "info")', - expected: 'assert(false, string.format("message %s", "info"))', - }, - { - inp: 'console.assert(false, "message %%s", "info")', - expected: 'assert(false, string.format("message %%s", "info"))', - }, - { - inp: 'console.assert(false, "message", "more")', - expected: 'assert(false, "message", "more")', - }, -])("console.assert (%p)", ({ inp, expected }) => { - expect(util.transpileString(inp, compilerOptions)).toBe(expected); -}); - -test("console.differentiation", () => { - const result = util.transpileExecuteAndReturnExport( - ` - export class Console { - test() { return 42; } - } - - function test() { - const console = new Console(); - return console.test(); - } - - export const result = test(); - `, - "result", - compilerOptions - ); - expect(result).toBe(42); -}); diff --git a/test/unit/lualib/__snapshots__/console.spec.ts.snap b/test/unit/lualib/__snapshots__/console.spec.ts.snap new file mode 100644 index 000000000..084cfbea9 --- /dev/null +++ b/test/unit/lualib/__snapshots__/console.spec.ts.snap @@ -0,0 +1,121 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`console.assert ("console.assert(false)") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + assert(false) +end +return ____exports" +`; + +exports[`console.assert ("console.assert(false, \\"message %%s\\", \\"info\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + assert(false, string.format(\\"message %%s\\", \\"info\\")) +end +return ____exports" +`; + +exports[`console.assert ("console.assert(false, \\"message %s\\", \\"info\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + assert(false, string.format(\\"message %s\\", \\"info\\")) +end +return ____exports" +`; + +exports[`console.assert ("console.assert(false, \\"message\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + assert(false, \\"message\\") +end +return ____exports" +`; + +exports[`console.assert ("console.assert(false, \\"message\\", \\"more\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + assert(false, \\"message\\", \\"more\\") +end +return ____exports" +`; + +exports[`console.log ("console.log()") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print() +end +return ____exports" +`; + +exports[`console.log ("console.log(\\"Hello %%s\\", \\"there\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(string.format(\\"Hello %%s\\", \\"there\\")) +end +return ____exports" +`; + +exports[`console.log ("console.log(\\"Hello %s\\", \\"there\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(string.format(\\"Hello %s\\", \\"there\\")) +end +return ____exports" +`; + +exports[`console.log ("console.log(\\"Hello\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(\\"Hello\\") +end +return ____exports" +`; + +exports[`console.log ("console.log(\\"Hello\\", \\"There\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(\\"Hello\\", \\"There\\") +end +return ____exports" +`; + +exports[`console.trace ("console.trace()") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(debug.traceback()) +end +return ____exports" +`; + +exports[`console.trace ("console.trace(\\"Hello %%s\\", \\"there\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(debug.traceback(string.format(\\"Hello %%s\\", \\"there\\"))) +end +return ____exports" +`; + +exports[`console.trace ("console.trace(\\"Hello %s\\", \\"there\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(debug.traceback(string.format(\\"Hello %s\\", \\"there\\"))) +end +return ____exports" +`; + +exports[`console.trace ("console.trace(\\"Hello\\", \\"there\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(debug.traceback(\\"Hello\\", \\"there\\")) +end +return ____exports" +`; + +exports[`console.trace ("console.trace(\\"message\\")") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + print(debug.traceback(\\"message\\")) +end +return ____exports" +`; diff --git a/test/unit/lualib/console.spec.ts b/test/unit/lualib/console.spec.ts new file mode 100644 index 000000000..66de0e807 --- /dev/null +++ b/test/unit/lualib/console.spec.ts @@ -0,0 +1,56 @@ +import * as util from "../../util"; + +const compilerOptions = { lib: ["lib.esnext.d.ts", "lib.dom.d.ts"] }; + +test.each([ + "console.log()", + 'console.log("Hello")', + 'console.log("Hello %s", "there")', + 'console.log("Hello %%s", "there")', + 'console.log("Hello", "There")', +])("console.log (%p)", code => { + util.testFunction(code) + .setOptions(compilerOptions) + .expectLuaToMatchSnapshot(); +}); + +test.each([ + "console.trace()", + 'console.trace("message")', + 'console.trace("Hello %s", "there")', + 'console.trace("Hello %%s", "there")', + 'console.trace("Hello", "there")', +])("console.trace (%p)", code => { + util.testFunction(code) + .setOptions(compilerOptions) + .expectLuaToMatchSnapshot(); +}); + +test.each([ + "console.assert(false)", + 'console.assert(false, "message")', + 'console.assert(false, "message %s", "info")', + 'console.assert(false, "message %%s", "info")', + 'console.assert(false, "message", "more")', +])("console.assert (%p)", code => { + util.testFunction(code) + .setOptions(compilerOptions) + .expectLuaToMatchSnapshot(); +}); + +test("console.differentiation", () => { + util.testModule` + export class Console { + test() { return 42; } + } + + function test() { + const console = new Console(); + return console.test(); + } + + export const result = test(); + ` + .setExport("result") + .expectToMatchJsResult(); +}); From ea457585b08ea0c65d5db53c59b832e7d1fe39fc Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 01:34:02 +0500 Subject: [PATCH 18/64] Merge array.spec.ts files --- test/unit/array.spec.ts | 171 ------------------------------- test/unit/lualib/array.spec.ts | 180 +++++++++++++++++++++++++++++++-- 2 files changed, 171 insertions(+), 180 deletions(-) delete mode 100644 test/unit/array.spec.ts diff --git a/test/unit/array.spec.ts b/test/unit/array.spec.ts deleted file mode 100644 index 1262387a7..000000000 --- a/test/unit/array.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import * as util from "../util"; - -test("Array access", () => { - util.testFunction` - const arr: Array = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("ReadonlyArray access", () => { - util.testFunction` - const arr: ReadonlyArray = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array literal access", () => { - util.testFunction` - const arr: number[] = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Readonly array literal access", () => { - util.testFunction` - const arr: readonly number[] = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array union access", () => { - util.testFunction` - function makeArray(): number[] | string[] { return [3, 5, 1]; } - const arr = makeArray(); - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array union access with empty tuple", () => { - util.testFunction` - function makeArray(): number[] | [] { return [3, 5, 1]; } - const arr = makeArray(); - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array union length", () => { - util.testFunction` - function makeArray(): number[] | string[] { return [3, 5, 1]; } - const arr = makeArray(); - return arr.length; - `.expectToMatchJsResult(); -}); - -test("Array intersection access", () => { - util.testFunction` - type I = number[] & { foo: string }; - function makeArray(): I { - let t = [3, 5, 1]; - (t as I).foo = "bar"; - return (t as I); - } - const arr = makeArray(); - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array intersection length", () => { - util.testFunction` - type I = number[] & { foo: string }; - function makeArray(): I { - let t = [3, 5, 1]; - (t as I).foo = "bar"; - return (t as I); - } - const arr = makeArray(); - return arr.length; - `.expectToMatchJsResult(); -}); - -test.each([ - { member: "firstElement()", expected: 3 }, - { member: "name", expected: "array" }, - { member: "length", expected: 1 }, -])("Derived array access (%p)", ({ member, expected }) => { - const luaHeader = ` - local arr = { - name = "array", - firstElement = function(self) return self[1] end - } - `; - - const tsHeader = ` - interface CustomArray extends Array { - name: string; - firstElement(): number; - }; - - declare const arr: CustomArray; - `; - - util.testFunction` - arr[0] = 3; - return arr.${member}; - ` - .setLuaHeader(luaHeader) - .setTsHeader(tsHeader) - .expectToEqual(expected); -}); - -test("Array delete", () => { - util.testFunction` - const array = [1, 2, 3, 4]; - delete array[2]; - return { a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); -}); - -test("Array delete return true", () => { - util.testFunction` - const array = [1, 2, 3, 4]; - const exists = delete array[2]; - return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); -}); - -test("Array delete return false", () => { - util.testFunction` - const array = [1, 2, 3, 4]; - const exists = delete array[4]; - return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); -}); - -test("Array property access", () => { - util.testFunction` - type A = number[] & { foo?: string }; - const a: A = [1, 2, 3]; - a.foo = "bar"; - return { foo: a.foo, a: a[0], b: a[1], c: a[2] }; - `.expectToMatchJsResult(); -}); - -test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( - "Array length set", - ({ length, arrayLength }) => { - util.testFunction` - const array = [1, 2, 3]; - array.length = ${length}; - return array.length; - `.expectToEqual(arrayLength); - } -); - -test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( - "Array length set as expression", - ({ length, arrayLength }) => { - util.testFunction` - const array = [1, 2, 3]; - const expressionValue = array.length = ${length}; - return { expressionValue, arrayLength: array.length }; - `.expectToEqual({ expressionValue: length, arrayLength }); - } -); - -test.each([-1, -7, 0.1, NaN, Infinity, -Infinity])("Invalid array length set", length => { - util.testFunction` - const arr = [1, 2, 3]; - arr.length = ${length}; - `.expectToEqual(new util.ExecutionError(`invalid array length: ${length}`)); -}); diff --git a/test/unit/lualib/array.spec.ts b/test/unit/lualib/array.spec.ts index 2fff3ead0..120489291 100644 --- a/test/unit/lualib/array.spec.ts +++ b/test/unit/lualib/array.spec.ts @@ -1,6 +1,176 @@ import * as util from "../../util"; -test.each([[0, 1, 2, 3]])("forEach (%p)", (...array) => { +test("Array access", () => { + util.testFunction` + const arr: Array = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("ReadonlyArray access", () => { + util.testFunction` + const arr: ReadonlyArray = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("Array literal access", () => { + util.testFunction` + const arr: number[] = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("Readonly array literal access", () => { + util.testFunction` + const arr: readonly number[] = [3, 5, 1]; + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("Array union access", () => { + util.testFunction` + function makeArray(): number[] | string[] { return [3, 5, 1]; } + const arr = makeArray(); + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("Array union access with empty tuple", () => { + util.testFunction` + function makeArray(): number[] | [] { return [3, 5, 1]; } + const arr = makeArray(); + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("Array union length", () => { + util.testFunction` + function makeArray(): number[] | string[] { return [3, 5, 1]; } + const arr = makeArray(); + return arr.length; + `.expectToMatchJsResult(); +}); + +test("Array intersection access", () => { + util.testFunction` + type I = number[] & { foo: string }; + function makeArray(): I { + let t = [3, 5, 1]; + (t as I).foo = "bar"; + return (t as I); + } + const arr = makeArray(); + return arr[1]; + `.expectToMatchJsResult(); +}); + +test("Array intersection length", () => { + util.testFunction` + type I = number[] & { foo: string }; + function makeArray(): I { + let t = [3, 5, 1]; + (t as I).foo = "bar"; + return (t as I); + } + const arr = makeArray(); + return arr.length; + `.expectToMatchJsResult(); +}); + +test.each([ + { member: "firstElement()", expected: 3 }, + { member: "name", expected: "array" }, + { member: "length", expected: 1 }, +])("Derived array access (%p)", ({ member, expected }) => { + const luaHeader = ` + local arr = { + name = "array", + firstElement = function(self) return self[1] end + } + `; + + const tsHeader = ` + interface CustomArray extends Array { + name: string; + firstElement(): number; + }; + + declare const arr: CustomArray; + `; + + util.testFunction` + arr[0] = 3; + return arr.${member}; + ` + .setLuaHeader(luaHeader) + .setTsHeader(tsHeader) + .expectToEqual(expected); +}); + +test("Array delete", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + delete array[2]; + return { a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); +}); + +test("Array delete return true", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + const exists = delete array[2]; + return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); +}); + +test("Array delete return false", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + const exists = delete array[4]; + return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); +}); + +test("Array property access", () => { + util.testFunction` + type A = number[] & { foo?: string }; + const a: A = [1, 2, 3]; + a.foo = "bar"; + return { foo: a.foo, a: a[0], b: a[1], c: a[2] }; + `.expectToMatchJsResult(); +}); + +test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( + "Array length set", + ({ length, arrayLength }) => { + util.testFunction` + const array = [1, 2, 3]; + array.length = ${length}; + return array.length; + `.expectToEqual(arrayLength); + } +); + +test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( + "Array length set as expression", + ({ length, arrayLength }) => { + util.testFunction` + const array = [1, 2, 3]; + const expressionValue = array.length = ${length}; + return { expressionValue, arrayLength: array.length }; + `.expectToEqual({ expressionValue: length, arrayLength }); + } +); + +test.each([-1, -7, 0.1, NaN, Infinity, -Infinity])("Invalid array length set", length => { + util.testFunction` + const arr = [1, 2, 3]; + arr.length = ${length}; + `.expectToEqual(new util.ExecutionError(`invalid array length: ${length}`)); +}); + +test.each([[0, 1, 2, 3]])("array.forEach (%p)", (...array) => { util.testFunction` let arrTest = ${util.valueToString(array)}; arrTest.forEach((elem, index) => { @@ -170,14 +340,6 @@ test.each([ util.testExpression`${util.valueToString(array)}.indexOf(${util.valuesToString(args)})`.expectToMatchJsResult(); }); -// TODO: Unrelated to lib -test.each([[1, 2, 3], [1, 2, 3, 4, 5]])("array.destructuring.simple (%p)", (...array) => { - util.testFunction` - let [x, y, z] = ${util.valueToString(array)} - return z; - `.expectToMatchJsResult(); -}); - test.each([[1], [1, 2, 3]])("array.push (%p)", (...args) => { util.testFunction` let testArray = [0]; From d531fd681386044ec2a655fe8f3e559b98a51552 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 01:44:57 +0500 Subject: [PATCH 19/64] Move some math tests to lualib --- test/unit/expressions.spec.ts | 220 ++++++++++++++++ .../__snapshots__/math.spec.ts.snap | 22 +- test/unit/lualib/math.spec.ts | 26 ++ test/unit/math.spec.ts | 245 ------------------ 4 files changed, 257 insertions(+), 256 deletions(-) rename test/unit/{ => lualib}/__snapshots__/math.spec.ts.snap (75%) create mode 100644 test/unit/lualib/math.spec.ts delete mode 100644 test/unit/math.spec.ts diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index ba318a0c9..a40d13b25 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -480,3 +480,223 @@ test("not operator precedence (%p)", () => { expect(util.transpileAndExecute(code)).toBe(false); }); + +// TODO: It probably should be in a different file +test.each([ + "++x", + "x++", + "--x", + "x--", + "x += y", + "x -= y", + "x *= y", + "y /= x", + "y %= x", + "y **= x", + "x |= y", + "x &= y", + "x ^= y", + "x <<= y", + "x >>>= y", +])("Operator assignment statements (%p)", statement => { + util.testFunction` + let x = 3; + let y = 6; + ${statement}; + return { x, y }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++o.p", + "o.p++", + "--o.p", + "o.p--", + "o.p += a[0]", + "o.p -= a[0]", + "o.p *= a[0]", + "a[0] /= o.p", + "a[0] %= o.p", + "a[0] **= o.p", + "o.p |= a[0]", + "o.p &= a[0]", + "o.p ^= a[0]", + "o.p <<= a[0]", + "o.p >>>= a[0]", +])("Operator assignment to simple property statements (%p)", statement => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + ${statement}; + return { o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++o.p.d", + "o.p.d++", + "--o.p.d", + "o.p.d--", + "o.p.d += a[0][0]", + "o.p.d -= a[0][0]", + "o.p.d *= a[0][0]", + "a[0][0] /= o.p.d", + "a[0][0] %= o.p.d", + "a[0][0] **= o.p.d", + "o.p.d |= a[0][0]", + "o.p.d &= a[0][0]", + "o.p.d ^= a[0][0]", + "o.p.d <<= a[0][0]", + "o.p.d >>>= a[0][0]", +])("Operator assignment to deep property statements (%p)", statement => { + util.testFunction` + let o = { p: { d: 3 } }; + let a = [[6,11], [7,13]]; + ${statement}; + return { o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++of().p", + "of().p++", + "--of().p", + "of().p--", + "of().p += af()[i()]", + "of().p -= af()[i()]", + "of().p *= af()[i()]", + "af()[i()] /= of().p", + "af()[i()] %= of().p", + "af()[i()] **= of().p", + "of().p |= af()[i()]", + "of().p &= af()[i()]", + "of().p ^= af()[i()]", + "of().p <<= af()[i()]", + "of().p >>>= af()[i()]", +])("Operator assignment to complex property statements (%p)", statement => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + function of() { return o; } + function af() { return a; } + function i() { return 0; } + ${statement}; + return { o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++of().p.d", + "of().p.d++", + "--of().p.d", + "of().p.d--", + "of().p.d += af()[i()][i()]", + "of().p.d -= af()[i()][i()]", + "of().p.d *= af()[i()][i()]", + "af()[i()][i()] /= of().p.d", + "af()[i()][i()] %= of().p.d", + "af()[i()][i()] **= of().p.d", + "of().p.d |= af()[i()][i()]", + "of().p.d &= af()[i()][i()]", + "of().p.d ^= af()[i()][i()]", + "of().p.d <<= af()[i()][i()]", + "of().p.d >>>= af()[i()][i()]", +])("Operator assignment to complex deep property statements (%p)", statement => { + util.testFunction` + let o = { p: { d: 3 } }; + let a = [[7, 6], [11, 13]]; + function of() { return o; } + function af() { return a; } + let _i = 0; + function i() { return _i++; } + ${statement}; + return { o, a, _i }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++x", + "x++", + "--x", + "x--", + "x += y", + "x -= y", + "x *= y", + "y /= x", + "y %= x", + "y **= x", + "x |= y", + "x &= y", + "x ^= y", + "x <<= y", + "x >>>= y", + "x + (y += 7)", + "x + (y += 7)", + "x++ + (y += 7)", +])("Operator assignment expressions (%p)", expression => { + util.testFunction` + let x = 3; + let y = 6; + const r = ${expression}; + return { r, x, y }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++o.p", + "o.p++", + "--o.p", + "o.p--", + "o.p += a[0]", + "o.p -= a[0]", + "o.p *= a[0]", + "a[0] /= o.p", + "a[0] %= o.p", + "a[0] **= o.p", + "o.p |= a[0]", + "o.p &= a[0]", + "o.p ^= a[0]", + "o.p <<= a[0]", + "o.p >>>= a[0]", + "o.p + (a[0] += 7)", + "o.p += (a[0] += 7)", + "o.p++ + (a[0] += 7)", +])("Operator assignment to simple property expressions (%p)", expression => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + const r = ${expression}; + return { r, o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++of().p", + "of().p++", + "--of().p", + "of().p--", + "of().p += af()[i()]", + "of().p -= af()[i()]", + "of().p *= af()[i()]", + "af()[i()] /= of().p", + "af()[i()] %= of().p", + "af()[i()] **= of().p", + "of().p |= af()[i()]", + "of().p &= af()[i()]", + "of().p ^= af()[i()]", + "of().p <<= af()[i()]", + "of().p >>>= af()[i()]", + "of().p + (af()[i()] += 7)", + "of().p += (af()[i()] += 7)", + "of().p++ + (af()[i()] += 7)", +])("Operator assignment to complex property expressions (%p)", expression => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + function of() { return o; } + function af() { return a; } + function i() { return 0; } + const r = ${expression}; + return { r, o, a }; + `.expectToMatchJsResult(); +}); diff --git a/test/unit/__snapshots__/math.spec.ts.snap b/test/unit/lualib/__snapshots__/math.spec.ts.snap similarity index 75% rename from test/unit/__snapshots__/math.spec.ts.snap rename to test/unit/lualib/__snapshots__/math.spec.ts.snap index 21d3f80d1..e5be3c320 100644 --- a/test/unit/__snapshots__/math.spec.ts.snap +++ b/test/unit/lualib/__snapshots__/math.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Math ("Math.PI") 1`] = ` +exports[`Math.PI 1`] = ` "local ____exports = {} function ____exports.__main(self) local ____ = math.pi @@ -8,7 +8,7 @@ end return ____exports" `; -exports[`Math ("Math.atan2(2, 3)") 1`] = ` +exports[`Math.atan2(2, 3) 1`] = ` "local ____exports = {} function ____exports.__main(self) math.atan(2 / 3) @@ -16,7 +16,7 @@ end return ____exports" `; -exports[`Math ("Math.cos()") 1`] = ` +exports[`Math.cos() 1`] = ` "local ____exports = {} function ____exports.__main(self) math.cos() @@ -24,7 +24,7 @@ end return ____exports" `; -exports[`Math ("Math.log1p(3)") 1`] = ` +exports[`Math.log1p(3) 1`] = ` "local ____exports = {} function ____exports.__main(self) math.log(1 + 3) @@ -32,7 +32,7 @@ end return ____exports" `; -exports[`Math ("Math.log2(3)") 1`] = ` +exports[`Math.log2(3) 1`] = ` "local ____exports = {} function ____exports.__main(self) (function() return math.log(3) / 0.6931471805599453 end)() @@ -40,7 +40,7 @@ end return ____exports" `; -exports[`Math ("Math.log10(3)") 1`] = ` +exports[`Math.log10(3) 1`] = ` "local ____exports = {} function ____exports.__main(self) (function() return math.log(3) / 2.302585092994046 end)() @@ -48,7 +48,7 @@ end return ____exports" `; -exports[`Math ("Math.min()") 1`] = ` +exports[`Math.min() 1`] = ` "local ____exports = {} function ____exports.__main(self) math.min() @@ -56,7 +56,7 @@ end return ____exports" `; -exports[`Math ("Math.round(3.3)") 1`] = ` +exports[`Math.round(3.3) 1`] = ` "local ____exports = {} function ____exports.__main(self) math.floor(3.3 + 0.5) @@ -64,7 +64,7 @@ end return ____exports" `; -exports[`Math ("Math.sin()") 1`] = ` +exports[`Math.sin() 1`] = ` "local ____exports = {} function ____exports.__main(self) math.sin() @@ -72,7 +72,7 @@ end return ____exports" `; -exports[`Math ("const x = Math.log2(3)") 1`] = ` +exports[`const x = Math.log2(3) 1`] = ` "local ____exports = {} function ____exports.__main(self) local x = (math.log(3) / 0.6931471805599453) @@ -80,7 +80,7 @@ end return ____exports" `; -exports[`Math ("const x = Math.log10(3)") 1`] = ` +exports[`const x = Math.log10(3) 1`] = ` "local ____exports = {} function ____exports.__main(self) local x = (math.log(3) / 2.302585092994046) diff --git a/test/unit/lualib/math.spec.ts b/test/unit/lualib/math.spec.ts new file mode 100644 index 000000000..6dcd0749a --- /dev/null +++ b/test/unit/lualib/math.spec.ts @@ -0,0 +1,26 @@ +import * as util from "../../util"; + +test.each([ + "Math.cos()", + "Math.sin()", + "Math.min()", + "Math.atan2(2, 3)", + "Math.log2(3)", + "Math.log10(3)", + "const x = Math.log2(3)", + "const x = Math.log10(3)", + "Math.log1p(3)", + "Math.round(3.3)", + "Math.PI", +])("%s", code => { + // TODO: Remove? + util.testFunction(code) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); + +test.each(["E", "LN10", "LN2", "LOG10E", "LOG2E", "SQRT1_2", "SQRT2"])("Math.%s", constant => { + util.testExpression`Math.${constant}`.tap(builder => { + expect(builder.getLuaExecutionResult()).toBeCloseTo(builder.getJsExecutionResult()); + }); +}); diff --git a/test/unit/math.spec.ts b/test/unit/math.spec.ts deleted file mode 100644 index d2430a8a1..000000000 --- a/test/unit/math.spec.ts +++ /dev/null @@ -1,245 +0,0 @@ -import * as util from "../util"; - -test.each([ - "Math.cos()", - "Math.sin()", - "Math.min()", - "Math.atan2(2, 3)", - "Math.log2(3)", - "Math.log10(3)", - "const x = Math.log2(3)", - "const x = Math.log10(3)", - "Math.log1p(3)", - "Math.round(3.3)", - "Math.PI", -])("Math (%p)", code => { - // TODO: Remove? - util.testFunction(code) - .disableSemanticCheck() - .expectLuaToMatchSnapshot(); -}); - -test.each(["E", "LN10", "LN2", "LOG10E", "LOG2E", "SQRT1_2", "SQRT2"])("Math constant (%p)", constant => { - util.testExpression`Math.${constant}`.tap(builder => { - expect(builder.getLuaExecutionResult()).toBeCloseTo(builder.getJsExecutionResult()); - }); -}); - -test.each([ - "++x", - "x++", - "--x", - "x--", - "x += y", - "x -= y", - "x *= y", - "y /= x", - "y %= x", - "y **= x", - "x |= y", - "x &= y", - "x ^= y", - "x <<= y", - "x >>>= y", -])("Operator assignment statements (%p)", statement => { - util.testFunction` - let x = 3; - let y = 6; - ${statement}; - return { x, y }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++o.p", - "o.p++", - "--o.p", - "o.p--", - "o.p += a[0]", - "o.p -= a[0]", - "o.p *= a[0]", - "a[0] /= o.p", - "a[0] %= o.p", - "a[0] **= o.p", - "o.p |= a[0]", - "o.p &= a[0]", - "o.p ^= a[0]", - "o.p <<= a[0]", - "o.p >>>= a[0]", -])("Operator assignment to simple property statements (%p)", statement => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - ${statement}; - return { o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++o.p.d", - "o.p.d++", - "--o.p.d", - "o.p.d--", - "o.p.d += a[0][0]", - "o.p.d -= a[0][0]", - "o.p.d *= a[0][0]", - "a[0][0] /= o.p.d", - "a[0][0] %= o.p.d", - "a[0][0] **= o.p.d", - "o.p.d |= a[0][0]", - "o.p.d &= a[0][0]", - "o.p.d ^= a[0][0]", - "o.p.d <<= a[0][0]", - "o.p.d >>>= a[0][0]", -])("Operator assignment to deep property statements (%p)", statement => { - util.testFunction` - let o = { p: { d: 3 } }; - let a = [[6,11], [7,13]]; - ${statement}; - return { o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++of().p", - "of().p++", - "--of().p", - "of().p--", - "of().p += af()[i()]", - "of().p -= af()[i()]", - "of().p *= af()[i()]", - "af()[i()] /= of().p", - "af()[i()] %= of().p", - "af()[i()] **= of().p", - "of().p |= af()[i()]", - "of().p &= af()[i()]", - "of().p ^= af()[i()]", - "of().p <<= af()[i()]", - "of().p >>>= af()[i()]", -])("Operator assignment to complex property statements (%p)", statement => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - function of() { return o; } - function af() { return a; } - function i() { return 0; } - ${statement}; - return { o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++of().p.d", - "of().p.d++", - "--of().p.d", - "of().p.d--", - "of().p.d += af()[i()][i()]", - "of().p.d -= af()[i()][i()]", - "of().p.d *= af()[i()][i()]", - "af()[i()][i()] /= of().p.d", - "af()[i()][i()] %= of().p.d", - "af()[i()][i()] **= of().p.d", - "of().p.d |= af()[i()][i()]", - "of().p.d &= af()[i()][i()]", - "of().p.d ^= af()[i()][i()]", - "of().p.d <<= af()[i()][i()]", - "of().p.d >>>= af()[i()][i()]", -])("Operator assignment to complex deep property statements (%p)", statement => { - util.testFunction` - let o = { p: { d: 3 } }; - let a = [[7, 6], [11, 13]]; - function of() { return o; } - function af() { return a; } - let _i = 0; - function i() { return _i++; } - ${statement}; - return { o, a, _i }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++x", - "x++", - "--x", - "x--", - "x += y", - "x -= y", - "x *= y", - "y /= x", - "y %= x", - "y **= x", - "x |= y", - "x &= y", - "x ^= y", - "x <<= y", - "x >>>= y", - "x + (y += 7)", - "x + (y += 7)", - "x++ + (y += 7)", -])("Operator assignment expressions (%p)", expression => { - util.testFunction` - let x = 3; - let y = 6; - const r = ${expression}; - return { r, x, y }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++o.p", - "o.p++", - "--o.p", - "o.p--", - "o.p += a[0]", - "o.p -= a[0]", - "o.p *= a[0]", - "a[0] /= o.p", - "a[0] %= o.p", - "a[0] **= o.p", - "o.p |= a[0]", - "o.p &= a[0]", - "o.p ^= a[0]", - "o.p <<= a[0]", - "o.p >>>= a[0]", - "o.p + (a[0] += 7)", - "o.p += (a[0] += 7)", - "o.p++ + (a[0] += 7)", -])("Operator assignment to simple property expressions (%p)", expression => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - const r = ${expression}; - return { r, o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++of().p", - "of().p++", - "--of().p", - "of().p--", - "of().p += af()[i()]", - "of().p -= af()[i()]", - "of().p *= af()[i()]", - "af()[i()] /= of().p", - "af()[i()] %= of().p", - "af()[i()] **= of().p", - "of().p |= af()[i()]", - "of().p &= af()[i()]", - "of().p ^= af()[i()]", - "of().p <<= af()[i()]", - "of().p >>>= af()[i()]", - "of().p + (af()[i()] += 7)", - "of().p += (af()[i()] += 7)", - "of().p++ + (af()[i()] += 7)", -])("Operator assignment to complex property expressions (%p)", expression => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - function of() { return o; } - function af() { return a; } - function i() { return 0; } - const r = ${expression}; - return { r, o, a }; - `.expectToMatchJsResult(); -}); From 9d8c37b61147c53e37d9b9dd6999f3b8c37cb726 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 02:07:35 +0500 Subject: [PATCH 20/64] Move around some lualib tests --- test/unit/expressions.spec.ts | 17 ++++ test/unit/lualib/lualib.spec.ts | 160 ++++---------------------------- test/unit/lualib/object.spec.ts | 32 +++++++ test/unit/modules.spec.ts | 21 ----- 4 files changed, 67 insertions(+), 163 deletions(-) create mode 100644 test/unit/lualib/object.spec.ts diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index a40d13b25..07b06aaab 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -160,6 +160,23 @@ test.each([ .expectToMatchJsResult(); }); +test.each([ + { condition: true, lhs: 4, rhs: 5 }, + { condition: false, lhs: 4, rhs: 5 }, + { condition: 3, lhs: 4, rhs: 5 }, +])("Ternary Conditional (%p)", ({ condition, lhs, rhs }) => { + util.testExpressionTemplate`${condition} ? ${lhs} : ${rhs}`.expectToMatchJsResult(); +}); + +test.each(["true", "false", "a < 4", "a == 8"])("Ternary Conditional Delayed (%p)", condition => { + util.testFunction` + let a = 3; + let delay = () => ${condition} ? a + 3 : a + 5; + a = 8; + return delay(); + `.expectToMatchJsResult(); +}); + test.each([ "inst.field", "inst.field + 3", diff --git a/test/unit/lualib/lualib.spec.ts b/test/unit/lualib/lualib.spec.ts index 7cb051b35..9526394c8 100644 --- a/test/unit/lualib/lualib.spec.ts +++ b/test/unit/lualib/lualib.spec.ts @@ -1,152 +1,28 @@ +import * as tstl from "../../../src"; import * as util from "../../util"; -test.each([ - { condition: "true", lhs: "4", rhs: "5", expected: 4 }, - { condition: "false", lhs: "4", rhs: "5", expected: 5 }, - { condition: "3", lhs: "4", rhs: "5", expected: 4 }, -])("Ternary Conditional (%p)", ({ condition, lhs, rhs, expected }) => { - const result = util.transpileAndExecute(`return ${condition} ? ${lhs} : ${rhs};`); - - expect(result).toBe(expected); +test("lualibRequire", () => { + util.testExpression`b instanceof c` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.LuaJIT }) + .disableSemanticCheck() + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); }); -test.each([ - { condition: "true", expected: 11 }, - { condition: "false", expected: 13 }, - { condition: "a < 4", expected: 13 }, - { condition: "a == 8", expected: 11 }, -])("Ternary Conditional Delayed (%p)", ({ condition, expected }) => { - const result = util.transpileAndExecute( - `let a = 3; - let delay = () => ${condition} ? a + 3 : a + 5; - a = 8; - return delay();` - ); - - expect(result).toBe(expected); +test("lualibRequireAlways", () => { + util.testModule`` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always, luaTarget: tstl.LuaTarget.LuaJIT }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); }); -test.each([ - { initial: "{a: 3}", parameters: "{}", expected: { a: 3 } }, - { initial: "{}", parameters: "{a: 3}", expected: { a: 3 } }, - { initial: "{a: 3}", parameters: "{a: 5}", expected: { a: 5 } }, - { initial: "{a: 3}", parameters: "{b: 5},{c: 7}", expected: { a: 3, b: 5, c: 7 } }, -])("Object.assign (%p)", ({ initial, parameters, expected }) => { - const jsonResult = util.transpileAndExecute(` - return JSONStringify(Object.assign(${initial},${parameters})); - `); - - const result = JSON.parse(jsonResult); - expect(result).toEqual(expected); -}); - -test.each([ - { obj: "{}", expected: [] }, - { obj: "{abc: 3}", expected: ["abc,3"] }, - { obj: "{abc: 3, def: 'xyz'}", expected: ["abc,3", "def,xyz"] }, -])("Object.entries (%p)", ({ obj, expected }) => { - const result = util.transpileAndExecute(` - const obj = ${obj}; - return Object.entries(obj).map(e => e.join(",")).join(";"); - `) as string; - - const foundKeys = result.split(";"); - if (expected.length === 0) { - expect(foundKeys.length).toBe(1); - expect(foundKeys[0]).toBe(""); - } else { - expect(foundKeys.length).toBe(expected.length); - for (const key of expected) { - expect(foundKeys.indexOf(key) >= 0).toBeTruthy(); - } +test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( + "LuaLib no uses? No code (%p)", + luaLibImport => { + util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); } -}); - -test.each([ - { obj: "{}", expected: [] }, - { obj: "{abc: 3}", expected: ["abc"] }, - { obj: "{abc: 3, def: 'xyz'}", expected: ["abc", "def"] }, -])("Object.keys (%p)", ({ obj, expected }) => { - const result = util.transpileAndExecute(` - const obj = ${obj}; - return Object.keys(obj).join(","); - `) as string; - - const foundKeys = result.split(","); - if (expected.length === 0) { - expect(foundKeys.length).toBe(1); - expect(foundKeys[0]).toBe(""); - } else { - expect(foundKeys.length).toBe(expected.length); - for (const key of expected) { - expect(foundKeys.indexOf(key) >= 0).toBeTruthy(); - } - } -}); - -test.each([ - { obj: "{}", expected: [] }, - { obj: "{abc: 'def'}", expected: ["def"] }, - { obj: "{abc: 3, def: 'xyz'}", expected: ["3", "xyz"] }, -])("Object.values (%p)", ({ obj, expected }) => { - const result = util.transpileAndExecute(` - const obj = ${obj}; - return Object.values(obj).join(","); - `) as string; - - const foundValues = result.split(","); - if (expected.length === 0) { - expect(foundValues.length).toBe(1); - expect(foundValues[0]).toBe(""); - } else { - expect(foundValues.length).toBe(expected.length); - for (const key of expected) { - expect(foundValues.indexOf(key) >= 0).toBeTruthy(); - } - } -}); - -// https://github.com/Microsoft/TypeScript/pull/26149 -const objectFromEntriesDeclaration = ` - interface ObjectConstructor { - fromEntries(entries: ReadonlyArray<[string, T]> | Iterable<[string, T]>): Record; - fromEntries(entries: ReadonlyArray<[string, any]> | Iterable<[string, any]>): Record; - } -`; - -test.each([ - { entries: [], expected: [] }, - { entries: [["a", 1], ["b", 2]], expected: { a: 1, b: 2 } }, - { entries: [["a", 1], ["a", 2]], expected: { a: 2 } }, -])("Object.fromEntries (%p)", ({ entries, expected }) => { - const result = util.transpileAndExecute( - `const obj = Object.fromEntries(${JSON.stringify(entries)}); - return JSONStringify(obj);`, - undefined, - undefined, - objectFromEntriesDeclaration - ); - - expect(JSON.parse(result)).toEqual(expected); -}); - -test("Object.fromEntries (Map)", () => { - const result = util.transpileAndExecute( - `const map = new Map([["foo", "bar"]]); - const obj = Object.fromEntries(map); - return JSONStringify(obj);`, - undefined, - undefined, - objectFromEntriesDeclaration - ); - - expect(JSON.parse(result)).toEqual({ foo: "bar" }); -}); - +); test("lualibs should not include tstl header", () => { - const code = ` + util.testModule` const arr = [1, 2, 3]; - arr.push(4);`; - - expect(util.transpileString(code)).not.toMatch("Generated with"); + arr.push(4); + `.tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("Generated with")); }); diff --git a/test/unit/lualib/object.spec.ts b/test/unit/lualib/object.spec.ts new file mode 100644 index 000000000..718ee1573 --- /dev/null +++ b/test/unit/lualib/object.spec.ts @@ -0,0 +1,32 @@ +import * as util from "../../util"; + +test.each([ + { initial: { a: 3 }, args: [{}] }, + { initial: {}, args: [{ a: 3 }] }, + { initial: { a: 3 }, args: [{ a: 5 }] }, + { initial: { a: 3 }, args: [{ b: 5 }, { c: 7 }] }, +])("Object.assign (%p)", ({ initial, args }) => { + util.testExpression`Object.assign(${util.valueToString(initial)}, ${util.valuesToString( + args + )})`.expectToMatchJsResult(); +}); + +test.each([{}, { abc: 3 }, { abc: 3, def: "xyz" }])("Object.entries (%p)", obj => { + util.testExpressionTemplate`Object.entries(${obj})`.expectToMatchJsResult(); +}); + +test.each([{}, { abc: 3 }, { abc: 3, def: "xyz" }])("Object.keys (%p)", obj => { + util.testExpressionTemplate`Object.keys(${obj})`.expectToMatchJsResult(); +}); + +test.each([{}, { abc: "def" }, { abc: 3, def: "xyz" }])("Object.values (%p)", obj => { + util.testExpressionTemplate`Object.values(${obj})`.expectToMatchJsResult(); +}); + +test.each([[[]], [[["a", 1], ["b", 2]]], [[["a", 1], ["a", 2]]]])("Object.fromEntries (%p)", entries => { + util.testExpressionTemplate`Object.fromEntries(${entries})`.expectToMatchJsResult(); +}); + +test("Object.fromEntries (Map)", () => { + util.testExpression`Object.fromEntries(new Map([["foo", "bar"]]))`.expectToMatchJsResult(); +}); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index bdc96e4e8..01e22b604 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -1,5 +1,4 @@ import * as ts from "typescript"; -import * as tstl from "../../src"; import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; @@ -86,26 +85,6 @@ test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌ } ); -test("lualibRequire", () => { - util.testExpression`b instanceof c` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.LuaJIT }) - .disableSemanticCheck() - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); -}); - -test("lualibRequireAlways", () => { - util.testModule`` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always, luaTarget: tstl.LuaTarget.LuaJIT }) - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); -}); - -test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( - "LuaLib no uses? No code (%p)", - luaLibImport => { - util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); - } -); - test("Non-exported module", () => { const result = util.transpileAndExecute( "return g.test();", From aa2a01d24d5657ead2ba8cb2712070b57fd99504 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 13:17:38 +0500 Subject: [PATCH 21/64] Move numbers.spec.ts to lualib tests --- test/unit/{ => lualib}/numbers.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/unit/{ => lualib}/numbers.spec.ts (97%) diff --git a/test/unit/numbers.spec.ts b/test/unit/lualib/numbers.spec.ts similarity index 97% rename from test/unit/numbers.spec.ts rename to test/unit/lualib/numbers.spec.ts index 95d249ac2..4369f22c6 100644 --- a/test/unit/numbers.spec.ts +++ b/test/unit/lualib/numbers.spec.ts @@ -1,4 +1,4 @@ -import * as util from "../util"; +import * as util from "../../util"; test.each([ "NaN === NaN", From 953f58de7027845b6348b1f64faf050a6ea115c8 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 13:28:01 +0500 Subject: [PATCH 22/64] Move inlining tests to other luaLibImport tests --- test/unit/lualib/inlining.spec.ts | 44 ------------------------------- test/unit/lualib/lualib.spec.ts | 29 +++++++++++++------- 2 files changed, 19 insertions(+), 54 deletions(-) delete mode 100644 test/unit/lualib/inlining.spec.ts diff --git a/test/unit/lualib/inlining.spec.ts b/test/unit/lualib/inlining.spec.ts deleted file mode 100644 index b7a73fc57..000000000 --- a/test/unit/lualib/inlining.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { LuaLibImportKind, LuaTarget } from "../../../src"; -import * as util from "../../util"; - -test("map constructor", () => { - const result = util.transpileAndExecute(`let mymap = new Map(); return mymap.size;`, { - luaLibImport: LuaLibImportKind.Inline, - luaTarget: LuaTarget.Lua53, - }); - - expect(result).toBe(0); -}); - -test("map foreach keys", () => { - const result = util.transpileAndExecute( - `let mymap = new Map([[5, 2],[6, 3],[7, 4]]); - let count = 0; - mymap.forEach((value, key) => { count += key; }); - return count;`, - { luaLibImport: LuaLibImportKind.Inline } - ); - - expect(result).toBe(18); -}); - -test("set constructor", () => { - const result = util.transpileAndExecute( - `class abc {} let def = new abc(); let myset = new Set(); return myset.size;`, - { luaLibImport: LuaLibImportKind.Inline } - ); - - expect(result).toBe(0); -}); - -test("set foreach keys", () => { - const result = util.transpileAndExecute( - `let myset = new Set([2, 3, 4]); - let count = 0; - myset.forEach((value, key) => { count += key; }); - return count;`, - { luaLibImport: LuaLibImportKind.Inline } - ); - - expect(result).toBe(9); -}); diff --git a/test/unit/lualib/lualib.spec.ts b/test/unit/lualib/lualib.spec.ts index 9526394c8..7d2d09e35 100644 --- a/test/unit/lualib/lualib.spec.ts +++ b/test/unit/lualib/lualib.spec.ts @@ -1,17 +1,25 @@ import * as tstl from "../../../src"; import * as util from "../../util"; -test("lualibRequire", () => { - util.testExpression`b instanceof c` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.LuaJIT }) - .disableSemanticCheck() - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); -}); +describe("luaLibImport", () => { + test("require", () => { + util.testExpression`b instanceof c` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require }) + .disableSemanticCheck() + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); + }); + + test("always", () => { + util.testModule`` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); + }); -test("lualibRequireAlways", () => { - util.testModule`` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always, luaTarget: tstl.LuaTarget.LuaJIT }) - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); + test("inline", () => { + util.testExpression`new Map().size` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Inline }) + .expectToMatchJsResult(); + }); }); test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( @@ -20,6 +28,7 @@ test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLib util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); } ); + test("lualibs should not include tstl header", () => { util.testModule` const arr = [1, 2, 3]; From 0159d41d8e2ace1b1c4009fa0b2d0c4cbf484dc4 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 13:53:58 +0500 Subject: [PATCH 23/64] Fix node 8 tests --- test/unit/lualib/object.spec.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/unit/lualib/object.spec.ts b/test/unit/lualib/object.spec.ts index 718ee1573..bb5ff8a48 100644 --- a/test/unit/lualib/object.spec.ts +++ b/test/unit/lualib/object.spec.ts @@ -23,10 +23,13 @@ test.each([{}, { abc: "def" }, { abc: 3, def: "xyz" }])("Object.values (%p)", ob util.testExpressionTemplate`Object.values(${obj})`.expectToMatchJsResult(); }); -test.each([[[]], [[["a", 1], ["b", 2]]], [[["a", 1], ["a", 2]]]])("Object.fromEntries (%p)", entries => { - util.testExpressionTemplate`Object.fromEntries(${entries})`.expectToMatchJsResult(); -}); - -test("Object.fromEntries (Map)", () => { - util.testExpression`Object.fromEntries(new Map([["foo", "bar"]]))`.expectToMatchJsResult(); +// TODO: Jest 25: as const +test.each([ + ["[]", {}] as const, + ['[["a", 1], ["b", 2]]', { a: 1, b: 2 }] as const, + ['[["a", 1], ["a", 2]]', { a: 2 }] as const, + ['new Map([["foo", "bar"]])', { foo: "bar" }] as const, +])("Object.fromEntries(%s)", ([entries, expected]) => { + // TODO: Node 12 + util.testExpression`Object.fromEntries(${entries})`.expectToEqual(expected); }); From 469cb9fce40d55d0beea90d00cc2e8fb6babd06e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 14:02:16 +0500 Subject: [PATCH 24/64] Merge curry.spec.ts into functions.spec.ts --- test/unit/curry.spec.ts | 10 ---------- test/unit/functions.spec.ts | 7 +++++++ 2 files changed, 7 insertions(+), 10 deletions(-) delete mode 100644 test/unit/curry.spec.ts diff --git a/test/unit/curry.spec.ts b/test/unit/curry.spec.ts deleted file mode 100644 index b39d2f3c6..000000000 --- a/test/unit/curry.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as util from "../util"; - -test.each([{ x: 2, y: 3 }, { x: 5, y: 4 }])("curryingAdd (%p)", ({ x, y }) => { - const result = util.transpileAndExecute( - `let add = (x: number) => (y: number) => x + y; - return add(${x})(${y})` - ); - - expect(result).toBe(x + y); -}); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 1d8012927..68603efea 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -9,6 +9,13 @@ test("Arrow Function Expression", () => { `.expectToMatchJsResult(); }); +test("Returning arrow function from arrow function (%p)", () => { + util.testFunction` + const add = (x: number) => (y: number) => x + y; + return add(1)(2); + `.expectToMatchJsResult(); +}); + test.each(["i++", "i--", "++i", "--i"])("Arrow function unary expression (%p)", lambda => { util.testFunction` let i = 10; From 641bdeebff2a26e9062ebbecae912e91856a9b15 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Fri, 21 Jun 2019 14:04:08 +0500 Subject: [PATCH 25/64] Fix object tests --- test/unit/lualib/object.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/unit/lualib/object.spec.ts b/test/unit/lualib/object.spec.ts index bb5ff8a48..ca1516254 100644 --- a/test/unit/lualib/object.spec.ts +++ b/test/unit/lualib/object.spec.ts @@ -24,12 +24,12 @@ test.each([{}, { abc: "def" }, { abc: 3, def: "xyz" }])("Object.values (%p)", ob }); // TODO: Jest 25: as const -test.each([ - ["[]", {}] as const, - ['[["a", 1], ["b", 2]]', { a: 1, b: 2 }] as const, - ['[["a", 1], ["a", 2]]', { a: 2 }] as const, - ['new Map([["foo", "bar"]])', { foo: "bar" }] as const, -])("Object.fromEntries(%s)", ([entries, expected]) => { +test.each<[string, object]>([ + ["[]", []], + ['[["a", 1], ["b", 2]]', { a: 1, b: 2 }], + ['[["a", 1], ["a", 2]]', { a: 2 }], + ['new Map([["foo", "bar"]])', { foo: "bar" }], +])("Object.fromEntries(%s)", (entries, expected) => { // TODO: Node 12 util.testExpression`Object.fromEntries(${entries})`.expectToEqual(expected); }); From d98048efebe49b1de183164c7650517059139858 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 01:57:00 +0500 Subject: [PATCH 26/64] Refactor array tests --- test/unit/assignmentDestructuring.spec.ts | 8 + test/unit/lualib/array.spec.ts | 478 +++++++++------------- 2 files changed, 203 insertions(+), 283 deletions(-) diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index a66464a86..53e0dbac6 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -24,6 +24,14 @@ test("Assignment destructuring [JIT]", () => { .expectLuaToMatchSnapshot(); }); +test("OmittedExpression in Array Binding Assignment Statement", () => { + util.testFunction` + let a, c; + [a, , c] = [1, 2, 3]; + return { a, c }; + `.expectToMatchJsResult(); +}); + test.each([ "function foo(): [] { return []; }; let [] = foo();", "let [] = ['a', 'b', 'c'];", diff --git a/test/unit/lualib/array.spec.ts b/test/unit/lualib/array.spec.ts index edd40c82b..72154fd66 100644 --- a/test/unit/lualib/array.spec.ts +++ b/test/unit/lualib/array.spec.ts @@ -1,182 +1,163 @@ import * as util from "../../util"; -test("Array access", () => { +test("omitted expression", () => { util.testFunction` - const arr: Array = [3, 5, 1]; - return arr[1]; + const array = [1, , 2]; + return { a: array[0], b: array[1], c: array[2] }; `.expectToMatchJsResult(); }); -test("ReadonlyArray access", () => { - util.testFunction` - const arr: ReadonlyArray = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array literal access", () => { - util.testFunction` - const arr: number[] = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Readonly array literal access", () => { - util.testFunction` - const arr: readonly number[] = [3, 5, 1]; - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array union access", () => { - util.testFunction` - function makeArray(): number[] | string[] { return [3, 5, 1]; } - const arr = makeArray(); - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array union access with empty tuple", () => { - util.testFunction` - function makeArray(): number[] | [] { return [3, 5, 1]; } - const arr = makeArray(); - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array union length", () => { - util.testFunction` - function makeArray(): number[] | string[] { return [3, 5, 1]; } - const arr = makeArray(); - return arr.length; - `.expectToMatchJsResult(); -}); - -test("Array intersection access", () => { - util.testFunction` - type I = number[] & { foo: string }; - function makeArray(): I { - let t = [3, 5, 1]; - (t as I).foo = "bar"; - return (t as I); - } - const arr = makeArray(); - return arr[1]; - `.expectToMatchJsResult(); -}); - -test("Array intersection length", () => { - util.testFunction` - type I = number[] & { foo: string }; - function makeArray(): I { - let t = [3, 5, 1]; - (t as I).foo = "bar"; - return (t as I); - } - const arr = makeArray(); - return arr.length; - `.expectToMatchJsResult(); -}); +describe("access", () => { + test("Array", () => { + util.testFunction` + const array: Array = [3, 5, 1]; + return array[1]; + `.expectToMatchJsResult(); + }); -test.each([ - { member: "firstElement()", expected: 3 }, - { member: "name", expected: "array" }, - { member: "length", expected: 1 }, -])("Derived array access (%p)", ({ member, expected }) => { - const luaHeader = ` - local arr = { - name = "array", - firstElement = function(self) return self[1] end - } - `; - - const tsHeader = ` - interface CustomArray extends Array { - name: string; - firstElement(): number; - }; - - declare const arr: CustomArray; - `; + test("ReadonlyArray", () => { + util.testFunction` + const array: ReadonlyArray = [3, 5, 1]; + return array[1]; + `.expectToMatchJsResult(); + }); - util.testFunction` - arr[0] = 3; - return arr.${member}; - ` - .setLuaHeader(luaHeader) - .setTsHeader(tsHeader) - .expectToEqual(expected); -}); + test("array literal", () => { + util.testExpression`[3, 5, 1][1]`.expectToMatchJsResult(); + }); -test("Array delete", () => { - util.testFunction` - const array = [1, 2, 3, 4]; - delete array[2]; - return { a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); -}); + test.skip("const array literal", () => { + util.testExpression`([3, 5, 1] as const)[1]`.expectToMatchJsResult(); + }); -test("Array delete return true", () => { - util.testFunction` - const array = [1, 2, 3, 4]; - const exists = delete array[2]; - return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); -}); + test("union", () => { + util.testFunction` + const array: number[] | string[] = [3, 5, 1]; + return array[1]; + `.expectToMatchJsResult(); + }); -test("Array delete return false", () => { - util.testFunction` - const array = [1, 2, 3, 4]; - const exists = delete array[4]; - return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); -}); + test("union with empty tuple", () => { + util.testFunction` + const array: number[] | [] = [3, 5, 1]; + return array[1]; + `.expectToMatchJsResult(); + }); -test("Array property access", () => { - util.testFunction` - type A = number[] & { foo?: string }; - const a: A = [1, 2, 3]; - a.foo = "bar"; - return { foo: a.foo, a: a[0], b: a[1], c: a[2] }; - `.expectToMatchJsResult(); -}); + test("access in call", () => { + util.testExpression`[() => "foo", () => "bar"][0]()`.expectToMatchJsResult(); + }); -test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( - "Array length set", - ({ length, arrayLength }) => { + test("intersection", () => { util.testFunction` - const array = [1, 2, 3]; - array.length = ${length}; - return array.length; - `.expectToEqual(arrayLength); - } -); + const array = Object.assign([3, 5, 1], { foo: "bar" }); + return { foo: array.foo, a: array[0], b: array[1], c: array[2] }; + `.expectToMatchJsResult(); + }); + + test.each([ + { member: "firstElement()", expected: 3 }, + { member: "name", expected: "array" }, + { member: "length", expected: 1 }, + ])("derived array (.%p)", ({ member, expected }) => { + const luaHeader = ` + local array = { + name = "array", + firstElement = function(self) return self[1] end + } + `; + + util.testModule` + interface CustomArray extends Array { + name: string; + firstElement(): number; + }; + + declare const array: CustomArray; + + array[0] = 3; + export const result = array.${member}; + ` + .setExport("result") + .setLuaHeader(luaHeader) + .expectToEqual(expected); + }); +}); + +describe(".length", () => { + describe("get", () => { + test("union", () => { + util.testFunction` + const array: number[] | string[] = [3, 5, 1]; + return array.length; + `.expectToMatchJsResult(); + }); + + test("intersection", () => { + util.testFunction` + const array = Object.assign([3, 5, 1], { foo: "bar" }); + return array.length; + `.expectToMatchJsResult(); + }); + }); + + describe("set", () => { + test.each([{ length: 0, newLength: 0 }, { length: 1, newLength: 1 }, { length: 7, newLength: 3 }])( + "removes extra elements", + ({ length, newLength }) => { + util.testFunction` + const array = [1, 2, 3]; + array.length = ${length}; + return array.length; + `.expectToEqual(newLength); + } + ); + + test.each([0, 1, 7])("returns right-hand side value", length => { + util.testExpression`[1, 2, 3].length = ${length}`.expectToEqual(length); + }); + + test.each([-1, -7, 0.1, NaN, Infinity, -Infinity])("throws on invalid values (%p)", length => { + util.testFunction` + [1, 2, 3].length = ${length}; + `.expectToEqual(new util.ExecutionError(`invalid array length: ${length}`)); + }); + }); +}); + +describe("delete", () => { + test("deletes element", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + delete array[2]; + return { a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); + }); -test.each([{ length: 0, arrayLength: 0 }, { length: 1, arrayLength: 1 }, { length: 7, arrayLength: 3 }])( - "Array length set as expression", - ({ length, arrayLength }) => { + test("returns true when element exists", () => { util.testFunction` - const array = [1, 2, 3]; - const expressionValue = array.length = ${length}; - return { expressionValue, arrayLength: array.length }; - `.expectToEqual({ expressionValue: length, arrayLength }); - } -); + const array = [1, 2, 3, 4]; + const exists = delete array[2]; + return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); + }); -test.each([-1, -7, 0.1, NaN, Infinity, -Infinity])("Invalid array length set", length => { - util.testFunction` - const arr = [1, 2, 3]; - arr.length = ${length}; - `.expectToEqual(new util.ExecutionError(`invalid array length: ${length}`)); + test("returns false when element not exists", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + const exists = delete array[4]; + return { exists, a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); + }); }); -test.each([[0, 1, 2, 3]])("array.forEach (%p)", (...array) => { +test("array.forEach (%p)", () => { util.testFunction` - let arrTest = ${util.valueToString(array)}; - arrTest.forEach((elem, index) => { - arrTest[index] = arrTest[index] + 1; - }) - return arrTest; + const array = [0, 1, 2, 3]; + array.forEach((elem, index) => { + array[index] = array[index] + 1; + }); + return array; `.expectToMatchJsResult(); }); @@ -184,25 +165,13 @@ test.each([ { array: [], searchElement: 3 }, { array: [0, 2, 4, 8], searchElement: 10 }, { array: [0, 2, 4, 8], searchElement: 8 }, -])("array.findIndex[value] (%p)", ({ array, searchElement }) => { +])("array.findIndex (%p)", ({ array, searchElement }) => { util.testFunction` - let arrTest = ${util.valueToString(array)}; - return arrTest.findIndex((elem, index) => elem === ${searchElement}); + const array = ${util.valueToString(array)}; + return array.findIndex((elem, index, arr) => elem === ${searchElement} && arr[index] === elem); `.expectToMatchJsResult(); }); -test.each([{ array: [0, 2, 4, 8], expected: 3, value: 8 }, { array: [0, 2, 4, 8], expected: 1, value: 2 }])( - "array.findIndex[index] (%p)", - ({ array, expected, value }) => { - util.testFunctionTemplate` - let array = ${array}; - return array.findIndex((elem, index, arr) => { - return index === ${expected} && arr[${expected}] === ${value}; - }); - `.expectToMatchJsResult(); - } -); - test.each([ { array: [], func: "x => x" }, { array: [0, 1, 2, 3], func: "x => x" }, @@ -245,22 +214,20 @@ test.each([ }); test.each([ - { inp: [], start: 1, end: 2 }, - { inp: [0, 1, 2, 3], start: 1, end: 2 }, - { inp: [0, 1, 2, 3], start: 1, end: 1 }, - { inp: [0, 1, 2, 3], start: 1, end: -1 }, - { inp: [0, 1, 2, 3], start: -3, end: -1 }, - { inp: [0, 1, 2, 3, 4, 5], start: 1, end: 3 }, - { inp: [0, 1, 2, 3, 4, 5], start: 3 }, -])("array.slice (%p)", ({ inp, start, end }) => { - util.testExpression`${util.valueToString(inp)}.slice(${start}, ${end})`.expectToMatchJsResult(); -}); - -test("array.slice no argument", () => { - util.testExpression`[2, 3, 4, 5].slice()`.expectToMatchJsResult(); + { array: [2, 3, 4, 5], args: [] }, + { array: [], args: [1, 2] }, + { array: [0, 1, 2, 3], args: [1, 2] }, + { array: [0, 1, 2, 3], args: [1, 1] }, + { array: [0, 1, 2, 3], args: [1, -1] }, + { array: [0, 1, 2, 3], args: [-3, -1] }, + { array: [0, 1, 2, 3, 4, 5], args: [1, 3] }, + { array: [0, 1, 2, 3, 4, 5], args: [3] }, +])("array.slice (%p)", ({ array, args }) => { + util.testExpression`${util.valueToString(array)}.slice(${util.valuesToString(args)})`.expectToMatchJsResult(); }); test.each([ + // Insert { array: [], start: 0, deleteCount: 0, newElements: [9, 10, 11] }, { array: [0, 1, 2, 3], start: 1, deleteCount: 0, newElements: [9, 10, 11] }, { array: [0, 1, 2, 3], start: 2, deleteCount: 2, newElements: [9, 10, 11] }, @@ -270,15 +237,8 @@ test.each([ { array: [0, 1, 2, 3], start: -3, deleteCount: 0, newElements: [8, 9] }, { array: [0, 1, 2, 3, 4, 5], start: 5, deleteCount: 9, newElements: [10, 11] }, { array: [0, 1, 2, 3, 4, 5], start: 3, deleteCount: 2, newElements: [3, 4, 5] }, -])("array.splice[Insert] (%p)", ({ array, start, deleteCount, newElements }) => { - util.testFunction` - const array = ${util.valueToString(array)}; - array.splice(${start}, ${deleteCount}, ${util.valuesToString(newElements)}); - return array; - `.expectToMatchJsResult(); -}); -test.each([ + // Remove { array: [], start: 1, deleteCount: 1 }, { array: [0, 1, 2, 3], start: 1, deleteCount: 1 }, { array: [0, 1, 2, 3], start: 10, deleteCount: 1 }, @@ -289,7 +249,7 @@ test.each([ { array: [0, 1, 2, 3, 4, 5], start: -2 }, { array: [0, 1, 2, 3, 4, 5], start: 2, deleteCount: 2 }, { array: [0, 1, 2, 3, 4, 5, 6, 7, 8], start: 5, deleteCount: 9, newElements: [10, 11] }, -])("array.splice[Remove] (%p)", ({ array, start, deleteCount, newElements = [] }) => { +])("array.splice (%p)", ({ array, start, deleteCount, newElements = [] }) => { util.testFunction` const array = ${util.valueToString(array)}; array.splice(${util.valuesToString([start, deleteCount, ...newElements])}); @@ -310,8 +270,8 @@ test.each([ { array: [1, 2, "test"], args: ["test", ["test1", "test2"]] }, ])("array.concat (%p)", ({ array, args }) => { util.testFunction` - let concatTestTable: any[] = ${util.valueToString(array)}; - return concatTestTable.concat(${util.valuesToString(args)}); + const array: any[] = ${util.valueToString(array)}; + return array.concat(${util.valuesToString(args)}); `.expectToMatchJsResult(); }); @@ -322,10 +282,7 @@ test.each([ { array: ["test1", "test2"], separator: ";" }, { array: ["test1", "test2"], separator: "" }, ])("array.join (%p)", ({ array, separator }) => { - util.testFunction` - const joinTestTable = ${util.valueToString(array)}; - return joinTestTable.join(${util.valueToString(separator)}); - `.expectToMatchJsResult(); + util.testExpression`${util.valueToString(array)}.join(${util.valueToString(separator)})`.expectToMatchJsResult(); }); test.each([ @@ -340,11 +297,11 @@ test.each([ util.testExpression`${util.valueToString(array)}.indexOf(${util.valuesToString(args)})`.expectToMatchJsResult(); }); -test.each([[1], [1, 2, 3]])("array.push (%p)", (...args) => { +test.each([{ args: [1] }, { args: [1, 2, 3] }])("array.push (%p)", ({ args }) => { util.testFunction` - let testArray = [0]; - testArray.push(${util.valuesToString(args)}); - return testArray; + const array = [0]; + const value = array.push(${util.valuesToString(args)}); + return { array, value }; `.expectToMatchJsResult(); }); @@ -353,43 +310,46 @@ test.each([{ array: [1, 2, 3], expected: [3, 2] }, { array: [1, 2, 3, null], exp "array.pop (%p)", ({ array, expected }) => { util.testFunction` - let array = ${util.valueToString(array)}; - let value = array.pop(); + const array = ${util.valueToString(array)}; + const value = array.pop(); return [value, array.length]; `.expectToEqual(expected); } ); -test.each([[1, 2, 3], [1, 2, 3, 4], [1], []])("array.reverse (%p)", (...array) => { - util.testFunction` - let array = ${util.valueToString(array)}; - let val = array.reverse(); - return array; - `.expectToMatchJsResult(); -}); +test.each([{ array: [1, 2, 3] }, { array: [1, 2, 3, 4] }, { array: [1] }, { array: [] }])( + "array.reverse (%p)", + ({ array }) => { + util.testFunction` + const array = ${util.valueToString(array)}; + array.reverse(); + return array; + `.expectToMatchJsResult(); + } +); -test.each([[1, 2, 3], [1], []])("array.shift (%p)", (...array) => { +test.each([{ array: [1, 2, 3] }, { array: [1] }, { array: [] }])("array.shift (%p)", ({ array }) => { util.testFunction` - let array = ${util.valueToString(array)}; - let value = array.shift(); + const array = ${util.valueToString(array)}; + const value = array.shift(); return { array, value }; `.expectToMatchJsResult(); }); test.each([ - { array: "[3, 4, 5]", args: [1, 2] }, - { array: "[]", args: [] }, - { array: "[1]", args: [] }, - { array: "[]", args: [1] }, + { array: [3, 4, 5], args: [1, 2] }, + { array: [], args: [] }, + { array: [1], args: [] }, + { array: [], args: [1] }, ])("array.unshift (%p)", ({ array, args }) => { util.testFunction` - let array = ${array}; - array.unshift(${util.valuesToString(args)}); - return array; + const array = ${util.valueToString(array)}; + const value = array.unshift(${util.valuesToString(args)}); + return { array, value }; `.expectToMatchJsResult(); }); -test.each([[[4, 5, 3, 2, 1]], [[1]], [[]]])("array.sort (%p)", array => { +test.each([{ array: [4, 5, 3, 2, 1] }, { array: [1] }, { array: [] }])("array.sort (%p)", ({ array }) => { util.testFunctionTemplate` const array = ${array}; array.sort(); @@ -430,61 +390,13 @@ test.each([ util.testExpressionTemplate`${array}.flatMap(${map})`.expectToEqual(expected); }); -test.each<(total: number, currentItem: number) => number>([ - (total, currentItem) => total + currentItem, - (total, currentItem) => total * currentItem, -])("array reduce (%p)", reducer => { - util.testExpressionTemplate`[1, 3, 5, 7].reduce(${reducer})`.expectToMatchJsResult(); -}); - -test.each<(total: number, currentItem: number) => number>([ - (total, currentItem) => total + currentItem, - (total, currentItem) => total * currentItem, -])("array reduce with initial value (%p)", reducer => { - util.testExpressionTemplate`[1, 3, 5, 7].reduce(${reducer}, 10)`.expectToMatchJsResult(); -}); - -test("array reduce index & array arguments (%p)", () => { - util.testExpression`[1, 3, 5, 7].reduce((total, _, index, array) => total + array[index])`.expectToMatchJsResult(); +test.each<[[(total: number, currentItem: number, index: number, array: number[]) => number, number?]]>([ + [[(total, currentItem) => total + currentItem]], + [[(total, currentItem) => total * currentItem]], + [[(total, currentItem) => total + currentItem, 10]], + [[(total, currentItem) => total * currentItem, 10]], + [[(total, _, index, array) => total + array[index]]], + [[(a, b) => a + b]], +])("array.reduce (%p)", args => { + util.testExpression`[1, 3, 5, 7].reduce(${util.valuesToString(args)})`.expectToMatchJsResult(); }); - -test("array reduce index & array arguments (%p)", () => { - util.testExpression`[].reduce((a, b) => a + b)`.expectToEqual( - new util.ExecutionError("Reduce of empty array with no initial value") - ); -}); - -test.each([0, 1, 2])("Array with OmittedExpression", index => { - const result = util.transpileAndExecute( - `const myarray = [1, , 2]; - return myarray[${index}];` - ); - - expect(result).toBe([1, , 2][index]); -}); - -test("OmittedExpression in Array Binding Assignment Statement", () => { - const result = util.transpileAndExecute( - `let a, c; - [a, , c] = [1, 2, 3]; - return a + c;` - ); - - expect(result).toBe(4); -}); - -test("array access call", () => { - const code = ` - const arr = [() => "foo", () => "bar"]; - return arr[1]();`; - expect(util.transpileAndExecute(code)).toBe("bar"); -}); - -test.each([`["foo", "bar"].length`, `["foo", "bar"][0]`, `[() => "foo", () => "bar"][0]()`])( - "array literal property access (%p)", - expression => { - const code = `return ${expression}`; - const expectResult = eval(expression); - expect(util.transpileAndExecute(code)).toBe(expectResult); - } -); From b4ab66b3ac084670b5a323cb75a43ae53257971c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 02:40:16 +0500 Subject: [PATCH 27/64] Move custom decorator tests to separate directory --- test/unit/assignments/assignments.spec.ts | 152 +---- test/unit/class.spec.ts | 22 - test/unit/declarations.spec.ts | 18 - .../customConstructor.spec.ts} | 4 +- test/unit/decorators/extension.spec.ts | 34 ++ test/unit/decorators/forRange.spec.ts | 112 ++++ test/unit/decorators/luaIterator.spec.ts | 262 +++++++++ test/unit/{ => decorators}/luaTable.spec.ts | 4 +- .../metaExtension.spec.ts} | 4 +- test/unit/decorators/tupleReturn.spec.ts | 546 ++++++++++++++++++ test/unit/decorators/vararg.spec.ts | 55 ++ test/unit/functions.spec.ts | 54 -- test/unit/loops.spec.ts | 367 ------------ test/unit/tuples.spec.ts | 343 ----------- test/unit/typechecking.spec.ts | 11 - 15 files changed, 1025 insertions(+), 963 deletions(-) rename test/unit/{decoratorCustomConstructor.spec.ts => decorators/customConstructor.spec.ts} (91%) create mode 100644 test/unit/decorators/extension.spec.ts create mode 100644 test/unit/decorators/forRange.spec.ts create mode 100644 test/unit/decorators/luaIterator.spec.ts rename test/unit/{ => decorators}/luaTable.spec.ts (98%) rename test/unit/{decoratorMetaExtension.spec.ts => decorators/metaExtension.spec.ts} (93%) create mode 100644 test/unit/decorators/tupleReturn.spec.ts create mode 100644 test/unit/decorators/vararg.spec.ts diff --git a/test/unit/assignments/assignments.spec.ts b/test/unit/assignments/assignments.spec.ts index c5da9beff..955fda458 100644 --- a/test/unit/assignments/assignments.spec.ts +++ b/test/unit/assignments/assignments.spec.ts @@ -1,40 +1,19 @@ import * as TSTLErrors from "../../../src/TSTLErrors"; import * as util from "../../util"; -test.each([ - { inp: `"abc"`, out: `"abc"` }, - { inp: "3", out: "3" }, - { inp: "[1,2,3]", out: "{1, 2, 3}" }, - { inp: "true", out: "true" }, - { inp: "false", out: "false" }, - { inp: `{a:3,b:"4"}`, out: `{a = 3, b = "4"}` }, -])("Const assignment (%p)", ({ inp, out }) => { - const lua = util.transpileString(`const myvar = ${inp}`); - expect(lua).toBe(`local myvar = ${out}`); +test("Const assignment (%p)", () => { + const lua = util.transpileString(`const foo = true;`); + expect(lua).toBe(`local foo = true`); }); -test.each([ - { inp: `"abc"`, out: `"abc"` }, - { inp: "3", out: "3" }, - { inp: "[1,2,3]", out: "{1, 2, 3}" }, - { inp: "true", out: "true" }, - { inp: "false", out: "false" }, - { inp: `{a:3,b:"4"}`, out: `{a = 3, b = "4"}` }, -])("Let assignment (%p)", ({ inp, out }) => { - const lua = util.transpileString(`let myvar = ${inp}`); - expect(lua).toBe(`local myvar = ${out}`); +test("Let assignment (%p)", () => { + const lua = util.transpileString(`let foo = true;`); + expect(lua).toBe(`local foo = true`); }); -test.each([ - { inp: `"abc"`, out: `"abc"` }, - { inp: "3", out: "3" }, - { inp: "[1,2,3]", out: "{1, 2, 3}" }, - { inp: "true", out: "true" }, - { inp: "false", out: "false" }, - { inp: `{a:3,b:"4"}`, out: `{a = 3, b = "4"}` }, -])("Var assignment (%p)", ({ inp, out }) => { - const lua = util.transpileString(`var myvar = ${inp}`); - expect(lua).toBe(`myvar = ${out}`); +test("Var assignment (%p)", () => { + const lua = util.transpileString(`var foo = true;`); + expect(lua).toBe(`foo = true`); }); test.each(["var myvar;", "let myvar;", "const myvar = null;", "const myvar = undefined;"])( @@ -60,121 +39,10 @@ test.each([ }); test("Ellipsis binding pattern", () => { - expect(() => util.transpileString("let [a,b,...c] = [1,2,3];")).toThrowExactError( + expect(() => util.transpileString("let [a, b, ...c] = [1,2,3];")).toThrowExactError( TSTLErrors.ForbiddenEllipsisDestruction(util.nodeStub) ); }); - -test("Tuple Assignment", () => { - const code = ` - function abc(): [number, number] { return [1, 2]; }; - let t: [number, number] = abc(); - return t[0] + t[1]; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(3); -}); - -test("TupleReturn assignment", () => { - const code = ` - /** @tupleReturn */ - declare function abc(this: void): number[] - let [a,b] = abc(); - `; - - const lua = util.transpileString(code); - expect(lua).toBe("local a, b = abc()"); -}); - -test("TupleReturn Single assignment", () => { - const code = ` - /** @tupleReturn */ - declare function abc(this: void): [number, string]; - let a = abc(); - a = abc(); - `; - - const lua = util.transpileString(code); - expect(lua).toBe("local a = ({\n abc()\n})\na = ({\n abc()\n})"); -}); - -test("TupleReturn interface assignment", () => { - const code = ` - interface def { - /** @tupleReturn */ - abc(); - } declare const jkl : def; - let [a,b] = jkl.abc(); - `; - - const lua = util.transpileString(code); - expect(lua).toBe("local a, b = jkl:abc()"); -}); - -test("TupleReturn namespace assignment", () => { - const code = ` - declare namespace def { - /** @tupleReturn */ - function abc(this: void) {} - } - let [a,b] = def.abc(); - `; - - const lua = util.transpileString(code); - expect(lua).toBe("local a, b = def.abc()"); -}); - -test("TupleReturn method assignment", () => { - const code = ` - declare class def { - /** @tupleReturn */ - abc() { return [1,2,3]; } - } const jkl = new def(); - let [a,b] = jkl.abc(); - `; - - const lua = util.transpileString(code); - expect(lua).toBe("local jkl = def.new()\nlocal a, b = jkl:abc()"); -}); - -test("TupleReturn functional", () => { - const code = ` - /** @tupleReturn */ - function abc(): [number, string] { return [3, "a"]; } - const [a, b] = abc(); - return b + a; - `; - - const result = util.transpileAndExecute(code); - - expect(result).toBe("a3"); -}); - -test("TupleReturn single", () => { - const code = ` - /** @tupleReturn */ - function abc(): [number, string] { return [3, "a"]; } - const res = abc(); - return res.length - `; - - const result = util.transpileAndExecute(code); - - expect(result).toBe(2); -}); - -test("TupleReturn in expression", () => { - const code = ` - /** @tupleReturn */ - function abc(): [number, string] { return [3, "a"]; } - return abc()[1] + abc()[0]; - `; - - const result = util.transpileAndExecute(code); - - expect(result).toBe("a3"); -}); - test("String table access", () => { const code = ` const dict : {[key:string]:any} = {}; diff --git a/test/unit/class.spec.ts b/test/unit/class.spec.ts index 37f1db79a..d83ae1262 100644 --- a/test/unit/class.spec.ts +++ b/test/unit/class.spec.ts @@ -733,28 +733,6 @@ test.each([{ input: "(new Foo())", expectResult: "foo" }, { input: "Foo", expect } ); -test.each(["extension", "metaExtension"])("Class extends extension (%p)", extensionType => { - const code = ` - declare class A {} - /** @${extensionType} **/ - class B extends A {} - class C extends B {} - `; - expect(() => util.transpileString(code)).toThrowExactError(TSTLErrors.InvalidExtendsExtension(util.nodeStub)); -}); - -test.each(["extension", "metaExtension"])("Class construct extension (%p)", extensionType => { - const code = ` - declare class A {} - /** @${extensionType} **/ - class B extends A {} - const b = new B(); - `; - expect(() => util.transpileString(code)).toThrowExactError( - TSTLErrors.InvalidNewExpressionOnExtension(util.nodeStub) - ); -}); - test("Class static instance of self", () => { const code = ` class Foo { diff --git a/test/unit/declarations.spec.ts b/test/unit/declarations.spec.ts index 415153e2c..353c864ea 100644 --- a/test/unit/declarations.spec.ts +++ b/test/unit/declarations.spec.ts @@ -11,24 +11,6 @@ test("Declaration function call", () => { expect(result).toBe(10); }); -test("Declaration function call tupleReturn", () => { - const libLua = `function declaredFunction(x) return x, 2*x + 1 end`; - - const tsHeader = ` - /** @tupleReturn */ - declare function declaredFunction(this: void, x: number): [number, number]; - `; - - const source = ` - const tuple = declaredFunction(3); - const [destructedLeft, destructedRight] = declaredFunction(2); - return \`\${tuple[0] + destructedLeft},\${tuple[1] + destructedRight}\`; - `; - - const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); - expect(result).toBe("5,12"); -}); - test("Declaration namespace function call", () => { const libLua = ` myNameSpace = {} diff --git a/test/unit/decoratorCustomConstructor.spec.ts b/test/unit/decorators/customConstructor.spec.ts similarity index 91% rename from test/unit/decoratorCustomConstructor.spec.ts rename to test/unit/decorators/customConstructor.spec.ts index 126c44e4f..48c473943 100644 --- a/test/unit/decoratorCustomConstructor.spec.ts +++ b/test/unit/decorators/customConstructor.spec.ts @@ -1,5 +1,5 @@ -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; test("CustomCreate", () => { const luaHeader = ` diff --git a/test/unit/decorators/extension.spec.ts b/test/unit/decorators/extension.spec.ts new file mode 100644 index 000000000..e7717792e --- /dev/null +++ b/test/unit/decorators/extension.spec.ts @@ -0,0 +1,34 @@ +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; + +test.each(["extension", "metaExtension"])("Class extends extension (%p)", extensionType => { + const code = ` + declare class A {} + /** @${extensionType} **/ + class B extends A {} + class C extends B {} + `; + expect(() => util.transpileString(code)).toThrowExactError(TSTLErrors.InvalidExtendsExtension(util.nodeStub)); +}); + +test.each(["extension", "metaExtension"])("Class construct extension (%p)", extensionType => { + const code = ` + declare class A {} + /** @${extensionType} **/ + class B extends A {} + const b = new B(); + `; + expect(() => util.transpileString(code)).toThrowExactError( + TSTLErrors.InvalidNewExpressionOnExtension(util.nodeStub) + ); +}); + +test.each(["extension", "metaExtension"])("instanceof extension (%p)", extensionType => { + util.testModule` + declare class A {} + /** @${extensionType} **/ + class B extends A {} + declare const foo: any; + const result = foo instanceof B; + `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidInstanceOfExtension(util.nodeStub)); +}); diff --git a/test/unit/decorators/forRange.spec.ts b/test/unit/decorators/forRange.spec.ts new file mode 100644 index 000000000..11f8aec85 --- /dev/null +++ b/test/unit/decorators/forRange.spec.ts @@ -0,0 +1,112 @@ +import * as ts from "typescript"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; + +test.each([ + { args: [1, 10], expectResult: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }, + { args: [1, 10, 2], expectResult: [1, 3, 5, 7, 9] }, + { args: [10, 1, -1], expectResult: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] }, + { args: [10, 1, -2], expectResult: [10, 8, 6, 4, 2] }, +])("@forRange loop", ({ args, expectResult }) => { + const tsHeader = "/** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[];"; + const code = ` + const results: number[] = []; + for (const i of luaRange(${args})) { + results.push(i); + } + return JSONStringify(results);`; + + const result = util.transpileAndExecute(code, undefined, undefined, tsHeader); + expect(JSON.parse(result)).toEqual(expectResult); +}); + +test("invalid non-ambient @forRange function", () => { + const code = ` + /** @forRange **/ function luaRange(i: number, j: number, k?: number): number[] { return []; } + for (const i of luaRange(1, 10, 2)) {}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall( + ts.createEmptyStatement(), + "@forRange function can only be used as an iterable in a for...of loop." + ).message + ); +}); + +test.each([[1], [1, 2, 3, 4]])("invalid @forRange argument count", args => { + const code = ` + /** @forRange **/ declare function luaRange(...args: number[]): number[] { return []; } + for (const i of luaRange(${args})) {}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall(ts.createEmptyStatement(), "@forRange function must take 2 or 3 arguments.") + .message + ); +}); + +test("invalid @forRange control variable", () => { + const code = ` + /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[]; + let i: number; + for (i of luaRange(1, 10, 2)) {}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall( + ts.createEmptyStatement(), + "@forRange loop must declare its own control variable." + ).message + ); +}); + +test("invalid @forRange argument type", () => { + const code = ` + /** @forRange **/ declare function luaRange(i: string, j: number): number[] { return []; } + for (const i of luaRange("foo", 2)) {}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall(ts.createEmptyStatement(), "@forRange arguments must be number types.").message + ); +}); + +test("invalid @forRange destructuring", () => { + const code = ` + /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[][]; + for (const [i] of luaRange(1, 10, 2)) {}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall(ts.createEmptyStatement(), "@forRange loop cannot use destructuring.").message + ); +}); + +test("invalid @forRange return type", () => { + const code = ` + /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): string[]; + for (const i of luaRange(1, 10)) {}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall( + ts.createEmptyStatement(), + "@forRange function must return Iterable or Array." + ).message + ); +}); + +test.each([ + "const range = luaRange(1, 10);", + "console.log(luaRange);", + "luaRange.call(null, 0, 0, 0);", + "let array = [0, luaRange, 1];", + "const call: any; call(luaRange);", + "for (const i of [...luaRange(1, 10)]) {}", +])("invalid @forRange reference (%p)", statement => { + const code = ` + /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[]; + ${statement}`; + + expect(() => util.transpileString(code)).toThrow( + TSTLErrors.InvalidForRangeCall( + ts.createEmptyStatement(), + "@forRange function can only be used as an iterable in a for...of loop." + ).message + ); +}); diff --git a/test/unit/decorators/luaIterator.spec.ts b/test/unit/decorators/luaIterator.spec.ts new file mode 100644 index 000000000..5aa24d8ae --- /dev/null +++ b/test/unit/decorators/luaIterator.spec.ts @@ -0,0 +1,262 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; + +test("forof lua iterator", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + interface Iter extends Iterable {} + function luaIter(): Iter { + let i = 0; + return (() => arr[i++]) as any; + } + let result = ""; + for (let e of luaIter()) { result += e; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("abc"); +}); + +test("forof array lua iterator", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + interface Iter extends Array {} + function luaIter(): Iter { + let i = 0; + return (() => arr[i++]) as any; + } + let result = ""; + for (let e of luaIter()) { result += e; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("abc"); +}); + +test("forof lua iterator with existing variable", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + interface Iter extends Iterable {} + function luaIter(): Iter { + let i = 0; + return (() => arr[i++]) as any; + } + let result = ""; + let e: string; + for (e of luaIter()) { result += e; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("abc"); +}); + +test("forof lua iterator destructuring", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + interface Iter extends Iterable<[string, string]> {} + function luaIter(): Iter { + let i = 0; + return (() => arr[i] && [i.toString(), arr[i++]]) as any; + } + let result = ""; + for (let [a, b] of luaIter()) { result += a + b; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("0a1b2c"); +}); + +test("forof lua iterator destructuring with existing variables", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + interface Iter extends Iterable<[string, string]> {} + function luaIter(): Iter { + let i = 0; + return (() => arr[i] && [i.toString(), arr[i++]]) as any; + } + let result = ""; + let a: string; + let b: string; + for ([a, b] of luaIter()) { result += a + b; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("0a1b2c"); +}); + +test("forof lua iterator tuple-return", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + /** @tupleReturn */ + interface Iter extends Iterable<[string, string]> {} + function luaIter(): Iter { + let i = 0; + /** @tupleReturn */ + function iter() { return arr[i] && [i.toString(), arr[i++]] || []; } + return iter as any; + } + let result = ""; + for (let [a, b] of luaIter()) { result += a + b; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("0a1b2c"); +}); + +test("forof lua iterator tuple-return with existing variables", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + /** @tupleReturn */ + interface Iter extends Iterable<[string, string]> {} + function luaIter(): Iter { + let i = 0; + /** @tupleReturn */ + function iter() { return arr[i] && [i.toString(), arr[i++]] || []; } + return iter as any; + } + let result = ""; + let a: string; + let b: string; + for ([a, b] of luaIter()) { result += a + b; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("0a1b2c"); +}); + +test("forof lua iterator tuple-return single variable", () => { + const code = ` + /** @luaIterator */ + /** @tupleReturn */ + interface Iter extends Iterable<[string, string]> {} + declare function luaIter(): Iter; + for (let x of luaIter()) {} + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + expect(() => util.transpileString(code, compilerOptions)).toThrowExactError( + TSTLErrors.UnsupportedNonDestructuringLuaIterator(util.nodeStub) + ); +}); + +test("forof lua iterator tuple-return single existing variable", () => { + const code = ` + /** @luaIterator */ + /** @tupleReturn */ + interface Iter extends Iterable<[string, string]> {} + declare function luaIter(): Iter; + let x: [string, string]; + for (x of luaIter()) {} + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + expect(() => util.transpileString(code, compilerOptions)).toThrowExactError( + TSTLErrors.UnsupportedNonDestructuringLuaIterator(util.nodeStub) + ); +}); + +test("forof forwarded lua iterator", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + interface Iter extends Iterable {} + function luaIter(): Iter { + let i = 0; + function iter() { return arr[i++]; } + return iter as any; + } + function forward() { + const iter = luaIter(); + return iter; + } + let result = ""; + for (let a of forward()) { result += a; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("abc"); +}); + +test("forof forwarded lua iterator with tupleReturn", () => { + const code = ` + const arr = ["a", "b", "c"]; + /** @luaIterator */ + /** @tupleReturn */ + interface Iter extends Iterable<[string, string]> {} + function luaIter(): Iter { + let i = 0; + /** @tupleReturn */ + function iter() { return arr[i] && [i.toString(), arr[i++]] || []; } + return iter as any; + } + function forward() { + const iter = luaIter(); + return iter; + } + let result = ""; + for (let [a, b] of forward()) { result += a + b; } + return result; + `; + const compilerOptions = { + luaLibImport: tstl.LuaLibImportKind.Require, + luaTarget: tstl.LuaTarget.Lua53, + target: ts.ScriptTarget.ES2015, + }; + const result = util.transpileAndExecute(code, compilerOptions); + expect(result).toBe("0a1b2c"); +}); diff --git a/test/unit/luaTable.spec.ts b/test/unit/decorators/luaTable.spec.ts similarity index 98% rename from test/unit/luaTable.spec.ts rename to test/unit/decorators/luaTable.spec.ts index ddc5f81a3..6419cc735 100644 --- a/test/unit/luaTable.spec.ts +++ b/test/unit/decorators/luaTable.spec.ts @@ -1,5 +1,5 @@ -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; const tableLibClass = ` /** @luaTable */ diff --git a/test/unit/decoratorMetaExtension.spec.ts b/test/unit/decorators/metaExtension.spec.ts similarity index 93% rename from test/unit/decoratorMetaExtension.spec.ts rename to test/unit/decorators/metaExtension.spec.ts index 20bf7fb4d..cac222c97 100644 --- a/test/unit/decoratorMetaExtension.spec.ts +++ b/test/unit/decorators/metaExtension.spec.ts @@ -1,5 +1,5 @@ -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; test("MetaExtension", () => { const tsHeader = ` diff --git a/test/unit/decorators/tupleReturn.spec.ts b/test/unit/decorators/tupleReturn.spec.ts new file mode 100644 index 000000000..49caae73a --- /dev/null +++ b/test/unit/decorators/tupleReturn.spec.ts @@ -0,0 +1,546 @@ +import * as util from "../../util"; + +const expectNoUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).not.toContain("unpack"); + +test("Tuple Return Access", () => { + util.testFunction` + /** @tupleReturn */ + function tuple(): [number, number, number] { return [3, 5, 1]; } + return tuple()[2]; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return Destruct Declaration", () => { + util.testFunction` + /** @tupleReturn */ + function tuple(): [number, number, number] { return [3,5,1]; } + const [a,b,c] = tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return Destruct Assignment", () => { + util.testFunction` + /** @tupleReturn */ + function tuple(): [number, number] { return [3,6]; } + let [a,b] = [1,2]; + [b,a] = tuple(); + return a - b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Static Method Return Destruct", () => { + util.testFunction` + class Test { + /** @tupleReturn */ + static tuple(): [number, number, number] { return [3,5,1]; } + } + const [a,b,c] = Test.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Static Function Property Return Destruct", () => { + util.testFunction` + class Test { + /** @tupleReturn */ + static tuple: () => [number, number, number] = () => [3,5,1]; + } + const [a,b,c] = Test.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Non-Static Method Return Destruct", () => { + util.testFunction` + class Test { + /** @tupleReturn */ + tuple(): [number, number, number] { return [3,5,1]; } + } + const t = new Test(); + const [a,b,c] = t.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Non-Static Function Property Return Destruct", () => { + util.testFunction` + class Test { + /** @tupleReturn */ + tuple: () => [number, number, number] = () => [3,5,1]; + } + const t = new Test(); + const [a,b,c] = t.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Interface Method Return Destruct", () => { + util.testFunction` + interface Test { + /** @tupleReturn */ + tuple(): [number, number, number]; + } + const t: Test = { + tuple() { return [3,5,1]; } + }; + const [a,b,c] = t.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Interface Function Property Return Destruct", () => { + util.testFunction` + interface Test { + /** @tupleReturn */ + tuple: () => [number, number, number]; + } + const t: Test = { + tuple: () => [3,5,1] + }; + const [a,b,c] = t.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Object Literal Method Return Destruct", () => { + util.testFunction` + const t = { + /** @tupleReturn */ + tuple() { return [3,5,1]; } + }; + const [a,b,c] = t.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Object Literal Function Property Return Destruct", () => { + util.testFunction` + const t = { + /** @tupleReturn */ + tuple: () => [3,5,1] + }; + const [a,b,c] = t.tuple(); + return b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Arrow Function", () => { + util.testFunction` + const fn = /** @tupleReturn */ (s: string) => [s, "bar"]; + const [a, b] = fn("foo"); + return a + b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return Inference", () => { + util.testFunction` + /** @tupleReturn */ interface Fn { (s: string): [string, string] } + const fn: Fn = s => [s, "bar"]; + const [a, b] = fn("foo"); + return a + b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return Inference as Argument", () => { + util.testFunction` + /** @tupleReturn */ interface Fn { (s: string): [string, string] } + function foo(fn: Fn) { + const [a, b] = fn("foo"); + return a + b; + } + return foo(s => [s, "bar"]); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return Inference as Elipsis Argument", () => { + util.testFunction` + /** @tupleReturn */ interface Fn { (s: string): [string, string] } + function foo(_: number, ...fn: Fn[]) { + const [a, b] = fn[0]("foo"); + return a + b; + } + return foo(0, s => [s, "bar"]); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return Inference as Elipsis Tuple Argument", () => { + util.testFunction` + /** @tupleReturn */ interface Fn { (s: string): [string, string] } + function foo(_: number, ...fn: [number, Fn]) { + const [a, b] = fn[1]("foo"); + return a + b; + } + return foo(0, 0, s => [s, "bar"]); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return in Spread", () => { + util.testFunction` + /** @tupleReturn */ function foo(): [string, string] { + return ["foo", "bar"]; + } + function bar(a: string, b: string) { + return a + b; + } + return bar(...foo()); + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Type Alias", () => { + util.testFunction` + /** @tupleReturn */ type Fn = () => [number, number]; + const fn: Fn = () => [1, 2]; + const [a, b] = fn(); + return a + b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Interface", () => { + util.testFunction` + /** @tupleReturn */ interface Fn { (): [number, number]; } + const fn: Fn = () => [1, 2]; + const [a, b] = fn(); + return a + b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Interface Signature", () => { + util.testFunction` + interface Fn { + /** @tupleReturn */ (): [number, number]; + } + const fn: Fn = () => [1, 2]; + const [a, b] = fn(); + return a + b; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Overload", () => { + util.testFunction` + function fn(a: number): number; + /** @tupleReturn */ function fn(a: string, b: string): [string, string]; + function fn(a: number | string, b?: string): number | [string, string] { + if (typeof a === "number") { + return a; + } else { + return [a, b as string]; + } + } + const a = fn(3); + const [b, c] = fn("foo", "bar"); + return a + b + c + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Interface Overload", () => { + util.testFunction` + interface Fn { + (a: number): number; + /** @tupleReturn */ (a: string, b: string): [string, string]; + } + const fn = ((a: number | string, b?: string): number | [string, string] => { + if (typeof a === "number") { + return a; + } else { + return [a, b as string]; + } + }) as Fn; + const a = fn(3); + const [b, c] = fn("foo", "bar"); + return a + b + c + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return on Interface Method Overload", () => { + util.testFunction` + interface Foo { + foo(a: number): number; + /** @tupleReturn */ foo(a: string, b: string): [string, string]; + } + const bar = { + foo: (a: number | string, b?: string): number | [string, string] => { + if (typeof a === "number") { + return a; + } else { + return [a, b as string]; + } + } + } as Foo; + const a = bar.foo(3); + const [b, c] = bar.foo("foo", "bar"); + return a + b + c; + ` + .tap(expectNoUnpack) + .expectToMatchJsResult(); +}); + +test("Tuple Return vs Non-Tuple Return Overload", () => { + const luaHeader = ` + function fn(a, b) + if type(a) == "number" then + return {a, a + 1} + else + return a, b + end + end + `; + + const tsHeader = ` + declare function fn(this: void, a: number): [number, number]; + /** @tupleReturn */ declare function fn(this: void, a: string, b: string): [string, string]; + `; + + util.testFunction` + const [a, b] = fn(3); + const [c, d] = fn("foo", "bar"); + return (a + b) + c + d; + ` + .setTsHeader(tsHeader) + .setLuaHeader(luaHeader) + .expectToEqual("7foobar"); +}); + +test("TupleReturn assignment", () => { + const code = ` + /** @tupleReturn */ + declare function abc(this: void): number[] + let [a,b] = abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a, b = abc()"); +}); + +test("TupleReturn Single assignment", () => { + const code = ` + /** @tupleReturn */ + declare function abc(this: void): [number, string]; + let a = abc(); + a = abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a = ({\n abc()\n})\na = ({\n abc()\n})"); +}); + +test("TupleReturn interface assignment", () => { + const code = ` + interface def { + /** @tupleReturn */ + abc(); + } declare const jkl : def; + let [a,b] = jkl.abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a, b = jkl:abc()"); +}); + +test("TupleReturn namespace assignment", () => { + const code = ` + declare namespace def { + /** @tupleReturn */ + function abc(this: void) {} + } + let [a,b] = def.abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a, b = def.abc()"); +}); + +test("TupleReturn method assignment", () => { + const code = ` + declare class def { + /** @tupleReturn */ + abc() { return [1,2,3]; } + } const jkl = new def(); + let [a,b] = jkl.abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local jkl = def.new()\nlocal a, b = jkl:abc()"); +}); + +test("TupleReturn functional", () => { + const code = ` + /** @tupleReturn */ + function abc(): [number, string] { return [3, "a"]; } + const [a, b] = abc(); + return b + a; + `; + + const result = util.transpileAndExecute(code); + + expect(result).toBe("a3"); +}); + +test("TupleReturn single", () => { + const code = ` + /** @tupleReturn */ + function abc(): [number, string] { return [3, "a"]; } + const res = abc(); + return res.length + `; + + const result = util.transpileAndExecute(code); + + expect(result).toBe(2); +}); + +test("TupleReturn in expression", () => { + const code = ` + /** @tupleReturn */ + function abc(): [number, string] { return [3, "a"]; } + return abc()[1] + abc()[0]; + `; + + const result = util.transpileAndExecute(code); + + expect(result).toBe("a3"); +}); + +test("TupleReturn assignment", () => { + const code = ` + /** @tupleReturn */ + declare function abc(this: void): number[] + let [a,b] = abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a, b = abc()"); +}); + +test("TupleReturn Single assignment", () => { + const code = ` + /** @tupleReturn */ + declare function abc(this: void): [number, string]; + let a = abc(); + a = abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a = ({\n abc()\n})\na = ({\n abc()\n})"); +}); + +test("TupleReturn interface assignment", () => { + const code = ` + interface def { + /** @tupleReturn */ + abc(); + } declare const jkl : def; + let [a,b] = jkl.abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a, b = jkl:abc()"); +}); + +test("TupleReturn namespace assignment", () => { + const code = ` + declare namespace def { + /** @tupleReturn */ + function abc(this: void) {} + } + let [a,b] = def.abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local a, b = def.abc()"); +}); + +test("TupleReturn method assignment", () => { + const code = ` + declare class def { + /** @tupleReturn */ + abc() { return [1,2,3]; } + } const jkl = new def(); + let [a,b] = jkl.abc(); + `; + + const lua = util.transpileString(code); + expect(lua).toBe("local jkl = def.new()\nlocal a, b = jkl:abc()"); +}); + +test("TupleReturn functional", () => { + const code = ` + /** @tupleReturn */ + function abc(): [number, string] { return [3, "a"]; } + const [a, b] = abc(); + return b + a; + `; + + const result = util.transpileAndExecute(code); + + expect(result).toBe("a3"); +}); + +test("TupleReturn single", () => { + const code = ` + /** @tupleReturn */ + function abc(): [number, string] { return [3, "a"]; } + const res = abc(); + return res.length + `; + + const result = util.transpileAndExecute(code); + + expect(result).toBe(2); +}); + +test("TupleReturn in expression", () => { + const code = ` + /** @tupleReturn */ + function abc(): [number, string] { return [3, "a"]; } + return abc()[1] + abc()[0]; + `; + + const result = util.transpileAndExecute(code); + + expect(result).toBe("a3"); +}); diff --git a/test/unit/decorators/vararg.spec.ts b/test/unit/decorators/vararg.spec.ts new file mode 100644 index 000000000..2a99fa4a7 --- /dev/null +++ b/test/unit/decorators/vararg.spec.ts @@ -0,0 +1,55 @@ +import * as util from "../../util"; + +test.each([{}, { noHoisting: true }])("@vararg", compilerOptions => { + const code = ` + /** @vararg */ type LuaVarArg = A & { __luaVarArg?: never }; + function foo(a: unknown, ...b: LuaVarArg) { + const c = [...b]; + return c.join(""); + } + function bar(a: unknown, ...b: LuaVarArg) { + return foo(a, ...b); + } + return bar("A", "B", "C", "D"); + `; + + const lua = util.transpileString(code, compilerOptions); + expect(lua).not.toMatch("b = ({...})"); + expect(lua).not.toMatch("unpack"); + expect(util.transpileAndExecute(code, compilerOptions)).toBe("BCD"); +}); + +test.each([{}, { noHoisting: true }])("@vararg array access", compilerOptions => { + const code = ` + /** @vararg */ type LuaVarArg = A & { __luaVarArg?: never }; + function foo(a: unknown, ...b: LuaVarArg) { + const c = [...b]; + return c.join("") + b[0]; + } + return foo("A", "B", "C", "D"); + `; + + expect(util.transpileAndExecute(code, compilerOptions)).toBe("BCDB"); +}); + +test.each([{}, { noHoisting: true }])("@vararg global", compilerOptions => { + const code = ` + /** @vararg */ type LuaVarArg = A & { __luaVarArg?: never }; + declare const arg: LuaVarArg; + const arr = [...arg]; + const result = arr.join(""); + `; + + const luaBody = util.transpileString(code, compilerOptions, false); + expect(luaBody).not.toMatch("unpack"); + + const lua = ` + function test(...) + ${luaBody} + return result + end + return test("A", "B", "C", "D") + `; + + expect(util.executeLua(lua)).toBe("ABCD"); +}); diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index ddfd2e7c5..bf2261f12 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -505,57 +505,3 @@ test.each([{}, { noHoisting: true }])("Function rest parameter (unreferenced)", expect(util.transpileString(code, compilerOptions)).not.toMatch("b = ({...})"); expect(util.transpileAndExecute(code, compilerOptions)).toBe("foobar"); }); - -test.each([{}, { noHoisting: true }])("@vararg", compilerOptions => { - const code = ` - /** @vararg */ type LuaVarArg = A & { __luaVarArg?: never }; - function foo(a: unknown, ...b: LuaVarArg) { - const c = [...b]; - return c.join(""); - } - function bar(a: unknown, ...b: LuaVarArg) { - return foo(a, ...b); - } - return bar("A", "B", "C", "D"); - `; - - const lua = util.transpileString(code, compilerOptions); - expect(lua).not.toMatch("b = ({...})"); - expect(lua).not.toMatch("unpack"); - expect(util.transpileAndExecute(code, compilerOptions)).toBe("BCD"); -}); - -test.each([{}, { noHoisting: true }])("@vararg array access", compilerOptions => { - const code = ` - /** @vararg */ type LuaVarArg = A & { __luaVarArg?: never }; - function foo(a: unknown, ...b: LuaVarArg) { - const c = [...b]; - return c.join("") + b[0]; - } - return foo("A", "B", "C", "D"); - `; - - expect(util.transpileAndExecute(code, compilerOptions)).toBe("BCDB"); -}); - -test.each([{}, { noHoisting: true }])("@vararg global", compilerOptions => { - const code = ` - /** @vararg */ type LuaVarArg = A & { __luaVarArg?: never }; - declare const arg: LuaVarArg; - const arr = [...arg]; - const result = arr.join(""); - `; - - const luaBody = util.transpileString(code, compilerOptions, false); - expect(luaBody).not.toMatch("unpack"); - - const lua = ` - function test(...) - ${luaBody} - return result - end - return test("A", "B", "C", "D") - `; - - expect(util.executeLua(lua)).toBe("ABCD"); -}); diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 4f57598a2..7ae825aa0 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -491,264 +491,6 @@ test("forof with array typed as iterable", () => { expect(util.transpileAndExecute(code)).toBe("ABC"); }); -test("forof lua iterator", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Iterable {} - function luaIter(): Iter { - let i = 0; - return (() => arr[i++]) as any; - } - let result = ""; - for (let e of luaIter()) { result += e; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("abc"); -}); - -test("forof array lua iterator", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Array {} - function luaIter(): Iter { - let i = 0; - return (() => arr[i++]) as any; - } - let result = ""; - for (let e of luaIter()) { result += e; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("abc"); -}); - -test("forof lua iterator with existing variable", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Iterable {} - function luaIter(): Iter { - let i = 0; - return (() => arr[i++]) as any; - } - let result = ""; - let e: string; - for (e of luaIter()) { result += e; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("abc"); -}); - -test("forof lua iterator destructuring", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Iterable<[string, string]> {} - function luaIter(): Iter { - let i = 0; - return (() => arr[i] && [i.toString(), arr[i++]]) as any; - } - let result = ""; - for (let [a, b] of luaIter()) { result += a + b; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("0a1b2c"); -}); - -test("forof lua iterator destructuring with existing variables", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Iterable<[string, string]> {} - function luaIter(): Iter { - let i = 0; - return (() => arr[i] && [i.toString(), arr[i++]]) as any; - } - let result = ""; - let a: string; - let b: string; - for ([a, b] of luaIter()) { result += a + b; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("0a1b2c"); -}); - -test("forof lua iterator tuple-return", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - /** @tupleReturn */ - interface Iter extends Iterable<[string, string]> {} - function luaIter(): Iter { - let i = 0; - /** @tupleReturn */ - function iter() { return arr[i] && [i.toString(), arr[i++]] || []; } - return iter as any; - } - let result = ""; - for (let [a, b] of luaIter()) { result += a + b; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("0a1b2c"); -}); - -test("forof lua iterator tuple-return with existing variables", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - /** @tupleReturn */ - interface Iter extends Iterable<[string, string]> {} - function luaIter(): Iter { - let i = 0; - /** @tupleReturn */ - function iter() { return arr[i] && [i.toString(), arr[i++]] || []; } - return iter as any; - } - let result = ""; - let a: string; - let b: string; - for ([a, b] of luaIter()) { result += a + b; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("0a1b2c"); -}); - -test("forof lua iterator tuple-return single variable", () => { - const code = ` - /** @luaIterator */ - /** @tupleReturn */ - interface Iter extends Iterable<[string, string]> {} - declare function luaIter(): Iter; - for (let x of luaIter()) {} - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - expect(() => util.transpileString(code, compilerOptions)).toThrowExactError( - TSTLErrors.UnsupportedNonDestructuringLuaIterator(util.nodeStub) - ); -}); - -test("forof lua iterator tuple-return single existing variable", () => { - const code = ` - /** @luaIterator */ - /** @tupleReturn */ - interface Iter extends Iterable<[string, string]> {} - declare function luaIter(): Iter; - let x: [string, string]; - for (x of luaIter()) {} - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - expect(() => util.transpileString(code, compilerOptions)).toThrowExactError( - TSTLErrors.UnsupportedNonDestructuringLuaIterator(util.nodeStub) - ); -}); - -test("forof forwarded lua iterator", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Iterable {} - function luaIter(): Iter { - let i = 0; - function iter() { return arr[i++]; } - return iter as any; - } - function forward() { - const iter = luaIter(); - return iter; - } - let result = ""; - for (let a of forward()) { result += a; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("abc"); -}); - -test("forof forwarded lua iterator with tupleReturn", () => { - const code = ` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - /** @tupleReturn */ - interface Iter extends Iterable<[string, string]> {} - function luaIter(): Iter { - let i = 0; - /** @tupleReturn */ - function iter() { return arr[i] && [i.toString(), arr[i++]] || []; } - return iter as any; - } - function forward() { - const iter = luaIter(); - return iter; - } - let result = ""; - for (let [a, b] of forward()) { result += a + b; } - return result; - `; - const compilerOptions = { - luaLibImport: tstl.LuaLibImportKind.Require, - luaTarget: tstl.LuaTarget.Lua53, - target: ts.ScriptTarget.ES2015, - }; - const result = util.transpileAndExecute(code, compilerOptions); - expect(result).toBe("0a1b2c"); -}); - test.each([ "while (a < b) { i++; continue; }", "do { i++; continue; } while (a < b)", @@ -792,112 +534,3 @@ test("while dead code after return", () => { expect(result).toBe(3); }); - -test.each([ - { args: [1, 10], expectResult: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }, - { args: [1, 10, 2], expectResult: [1, 3, 5, 7, 9] }, - { args: [10, 1, -1], expectResult: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] }, - { args: [10, 1, -2], expectResult: [10, 8, 6, 4, 2] }, -])("@forRange loop", ({ args, expectResult }) => { - const tsHeader = "/** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[];"; - const code = ` - const results: number[] = []; - for (const i of luaRange(${args})) { - results.push(i); - } - return JSONStringify(results);`; - - const result = util.transpileAndExecute(code, undefined, undefined, tsHeader); - expect(JSON.parse(result)).toEqual(expectResult); -}); - -test("invalid non-ambient @forRange function", () => { - const code = ` - /** @forRange **/ function luaRange(i: number, j: number, k?: number): number[] { return []; } - for (const i of luaRange(1, 10, 2)) {}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall( - ts.createEmptyStatement(), - "@forRange function can only be used as an iterable in a for...of loop." - ).message - ); -}); - -test.each([[1], [1, 2, 3, 4]])("invalid @forRange argument count", args => { - const code = ` - /** @forRange **/ declare function luaRange(...args: number[]): number[] { return []; } - for (const i of luaRange(${args})) {}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall(ts.createEmptyStatement(), "@forRange function must take 2 or 3 arguments.") - .message - ); -}); - -test("invalid @forRange control variable", () => { - const code = ` - /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[]; - let i: number; - for (i of luaRange(1, 10, 2)) {}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall( - ts.createEmptyStatement(), - "@forRange loop must declare its own control variable." - ).message - ); -}); - -test("invalid @forRange argument type", () => { - const code = ` - /** @forRange **/ declare function luaRange(i: string, j: number): number[] { return []; } - for (const i of luaRange("foo", 2)) {}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall(ts.createEmptyStatement(), "@forRange arguments must be number types.").message - ); -}); - -test("invalid @forRange destructuring", () => { - const code = ` - /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[][]; - for (const [i] of luaRange(1, 10, 2)) {}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall(ts.createEmptyStatement(), "@forRange loop cannot use destructuring.").message - ); -}); - -test("invalid @forRange return type", () => { - const code = ` - /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): string[]; - for (const i of luaRange(1, 10)) {}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall( - ts.createEmptyStatement(), - "@forRange function must return Iterable or Array." - ).message - ); -}); - -test.each([ - "const range = luaRange(1, 10);", - "console.log(luaRange);", - "luaRange.call(null, 0, 0, 0);", - "let array = [0, luaRange, 1];", - "const call: any; call(luaRange);", - "for (const i of [...luaRange(1, 10)]) {}", -])("invalid @forRange reference (%p)", statement => { - const code = ` - /** @forRange **/ declare function luaRange(i: number, j: number, k?: number): number[]; - ${statement}`; - - expect(() => util.transpileString(code)).toThrow( - TSTLErrors.InvalidForRangeCall( - ts.createEmptyStatement(), - "@forRange function can only be used as an iterable in a for...of loop." - ).message - ); -}); diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index 5c62149bd..9d1fa0387 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -82,346 +82,3 @@ test("Tuple length", () => { return tuple.length; `.expectToMatchJsResult(); }); - -test("Tuple Return Access", () => { - util.testFunction` - /** @tupleReturn */ - function tuple(): [number, number, number] { return [3, 5, 1]; } - return tuple()[2]; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Destruct Declaration", () => { - util.testFunction` - /** @tupleReturn */ - function tuple(): [number, number, number] { return [3,5,1]; } - const [a,b,c] = tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Destruct Assignment", () => { - util.testFunction` - /** @tupleReturn */ - function tuple(): [number, number] { return [3,6]; } - let [a,b] = [1,2]; - [b,a] = tuple(); - return a - b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Static Method Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - static tuple(): [number, number, number] { return [3,5,1]; } - } - const [a,b,c] = Test.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Static Function Property Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - static tuple: () => [number, number, number] = () => [3,5,1]; - } - const [a,b,c] = Test.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Non-Static Method Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - tuple(): [number, number, number] { return [3,5,1]; } - } - const t = new Test(); - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Non-Static Function Property Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - tuple: () => [number, number, number] = () => [3,5,1]; - } - const t = new Test(); - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Interface Method Return Destruct", () => { - util.testFunction` - interface Test { - /** @tupleReturn */ - tuple(): [number, number, number]; - } - const t: Test = { - tuple() { return [3,5,1]; } - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Interface Function Property Return Destruct", () => { - util.testFunction` - interface Test { - /** @tupleReturn */ - tuple: () => [number, number, number]; - } - const t: Test = { - tuple: () => [3,5,1] - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Object Literal Method Return Destruct", () => { - util.testFunction` - const t = { - /** @tupleReturn */ - tuple() { return [3,5,1]; } - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Object Literal Function Property Return Destruct", () => { - util.testFunction` - const t = { - /** @tupleReturn */ - tuple: () => [3,5,1] - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Arrow Function", () => { - util.testFunction` - const fn = /** @tupleReturn */ (s: string) => [s, "bar"]; - const [a, b] = fn("foo"); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - const fn: Fn = s => [s, "bar"]; - const [a, b] = fn("foo"); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference as Argument", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(fn: Fn) { - const [a, b] = fn("foo"); - return a + b; - } - return foo(s => [s, "bar"]); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference as Elipsis Argument", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(_: number, ...fn: Fn[]) { - const [a, b] = fn[0]("foo"); - return a + b; - } - return foo(0, s => [s, "bar"]); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference as Elipsis Tuple Argument", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(_: number, ...fn: [number, Fn]) { - const [a, b] = fn[1]("foo"); - return a + b; - } - return foo(0, 0, s => [s, "bar"]); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return in Spread", () => { - util.testFunction` - /** @tupleReturn */ function foo(): [string, string] { - return ["foo", "bar"]; - } - function bar(a: string, b: string) { - return a + b; - } - return bar(...foo()); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Type Alias", () => { - util.testFunction` - /** @tupleReturn */ type Fn = () => [number, number]; - const fn: Fn = () => [1, 2]; - const [a, b] = fn(); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (): [number, number]; } - const fn: Fn = () => [1, 2]; - const [a, b] = fn(); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface Signature", () => { - util.testFunction` - interface Fn { - /** @tupleReturn */ (): [number, number]; - } - const fn: Fn = () => [1, 2]; - const [a, b] = fn(); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Overload", () => { - util.testFunction` - function fn(a: number): number; - /** @tupleReturn */ function fn(a: string, b: string): [string, string]; - function fn(a: number | string, b?: string): number | [string, string] { - if (typeof a === "number") { - return a; - } else { - return [a, b as string]; - } - } - const a = fn(3); - const [b, c] = fn("foo", "bar"); - return a + b + c - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface Overload", () => { - util.testFunction` - interface Fn { - (a: number): number; - /** @tupleReturn */ (a: string, b: string): [string, string]; - } - const fn = ((a: number | string, b?: string): number | [string, string] => { - if (typeof a === "number") { - return a; - } else { - return [a, b as string]; - } - }) as Fn; - const a = fn(3); - const [b, c] = fn("foo", "bar"); - return a + b + c - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface Method Overload", () => { - util.testFunction` - interface Foo { - foo(a: number): number; - /** @tupleReturn */ foo(a: string, b: string): [string, string]; - } - const bar = ({ - foo: (a: number | string, b?: string): number | [string, string] => { - if (typeof a === "number") { - return a; - } else { - return [a, b as string]; - } - } - }) as Foo; - const a = bar.foo(3); - const [b, c] = bar.foo("foo", "bar"); - return a + b + c - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return vs Non-Tuple Return Overload", () => { - const luaHeader = ` - function fn(a, b) - if type(a) == "number" then - return {a, a + 1} - else - return a, b - end - end - `; - - const tsHeader = ` - declare function fn(this: void, a: number): [number, number]; - /** @tupleReturn */ declare function fn(this: void, a: string, b: string): [string, string]; - `; - - util.testFunction` - const [a, b] = fn(3); - const [c, d] = fn("foo", "bar"); - return (a + b) + c + d; - ` - .setTsHeader(tsHeader) - .setLuaHeader(luaHeader) - .expectToEqual("7foobar"); -}); diff --git a/test/unit/typechecking.spec.ts b/test/unit/typechecking.spec.ts index 2c8c81fc9..e03bae89c 100644 --- a/test/unit/typechecking.spec.ts +++ b/test/unit/typechecking.spec.ts @@ -1,4 +1,3 @@ -import * as TSTLErrors from "../../src/TSTLErrors"; import * as util from "../util"; test.each(["0", "30", "30_000", "30.00"])("typeof number (%p)", inp => { @@ -82,16 +81,6 @@ test("null instanceof Class", () => { `.expectToMatchJsResult(); }); -test.each(["extension", "metaExtension"])("instanceof extension (%p)", extensionType => { - util.testModule` - declare class A {} - /** @${extensionType} **/ - class B extends A {} - declare const foo: any; - const result = foo instanceof B; - `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidInstanceOfExtension(util.nodeStub)); -}); - test("instanceof export", () => { util.testModule` export class myClass {} From ec1db380544368a68fb556e06de118d0912426b8 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 04:23:42 +0500 Subject: [PATCH 28/64] Merge tagged template literal and simple template literal tests --- test/unit/expressions.spec.ts | 7 ------ test/unit/string.spec.ts | 16 ------------- ...erals.spec.ts => templateLiterals.spec.ts} | 23 +++++++++++++++++++ 3 files changed, 23 insertions(+), 23 deletions(-) rename test/unit/{taggedTemplateLiterals.spec.ts => templateLiterals.spec.ts} (85%) diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 625301b50..5d52123c7 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -264,13 +264,6 @@ test.each(["i++", "i--", "++i", "--i"])("Incrementor value (%p)", expression => `.expectToMatchJsResult(); }); -test.each(["a++", "a--", "--a", "++a"])("Template string expression (%p)", lambda => { - util.testFunction` - let a = 3; - return \`val\${${lambda}}\`; - `.expectToMatchJsResult(); -}); - test.each(["x = y", "x += y"])("Assignment expressions (%p)", expression => { util.testFunction` let x = "x"; diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index 0ffc3710f..57316bfc1 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -23,22 +23,6 @@ test.each([[], [65], [65, 66], [65, 66, 67]])("String.fromCharCode (%p)", (...ar util.testExpression`String.fromCharCode(${util.valuesToString(args)})`.expectToMatchJsResult(); }); -test.each([ - { a: 12, b: 23, c: 43 }, - { a: "test", b: "hello", c: "bye" }, - { a: "test", b: 42, c: "bye" }, - { a: "test", b: 42, c: 12 }, - { a: "test", b: 42, c: true }, - { a: false, b: 42, c: 12 }, -])("Template Strings (%p)", ({ a, b, c }) => { - util.testFunctionTemplate` - let a = ${a}; - let b = ${b}; - let c = ${c}; - return \`${a} ${b} test ${c}\`; - `.expectToMatchJsResult(); -}); - test.each([ { a: 12, b: 23, c: 43 }, { a: "test", b: "hello", c: "bye" }, diff --git a/test/unit/taggedTemplateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts similarity index 85% rename from test/unit/taggedTemplateLiterals.spec.ts rename to test/unit/templateLiterals.spec.ts index 6ab4e68cd..34aed1180 100644 --- a/test/unit/taggedTemplateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -1,5 +1,28 @@ import * as util from "../util"; +test.each(["a++", "a--", "--a", "++a"])("Template string expression (%p)", lambda => { + util.testFunction` + let a = 3; + return \`val\${${lambda}}\`; + `.expectToMatchJsResult(); +}); + +test.each([ + { a: 12, b: 23, c: 43 }, + { a: "test", b: "hello", c: "bye" }, + { a: "test", b: 42, c: "bye" }, + { a: "test", b: 42, c: 12 }, + { a: "test", b: 42, c: true }, + { a: false, b: 42, c: 12 }, +])("Template Strings (%p)", ({ a, b, c }) => { + util.testFunctionTemplate` + let a = ${a}; + let b = ${b}; + let c = ${c}; + return \`${a} ${b} test ${c}\`; + `.expectToMatchJsResult(); +}); + const testCases = [ { callExpression: "func``", From 470f16bd9b9b94bb99d24a829f5ba3f6e3984432 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 04:24:02 +0500 Subject: [PATCH 29/64] Update globalThis tests --- test/unit/identifiers.spec.ts | 63 --------------------------------- test/unit/lualib/global.spec.ts | 38 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 63 deletions(-) create mode 100644 test/unit/lualib/global.spec.ts diff --git a/test/unit/identifiers.spec.ts b/test/unit/identifiers.spec.ts index 9e0114f82..bde4477fa 100644 --- a/test/unit/identifiers.spec.ts +++ b/test/unit/identifiers.spec.ts @@ -777,66 +777,3 @@ test("exported variable with lua keyword as name is not renamed", () => { expect(util.transpileExecuteAndReturnExport(code, "print")).toBe("foobar"); }); - -describe("globalThis translation", () => { - test("globalThis to _G (expression)", () => { - const code = ` - var foo = "bar"; - return globalThis.foo;`; - - const lua = util.transpileString(code); - - expect(util.executeLua(lua)).toBe("bar"); - }); - - test("globalThis to _G (assign)", () => { - const code = ` - globalThis.foo = "bar"; - return globalThis.foo;`; - - expect(util.transpileAndExecute(code)).toBe("bar"); - }); - - test("globalThis to _G (reassign)", () => { - const code = ` - globalThis.foo = "bar"; - globalThis.foo = "baz"; - return globalThis.foo;`; - - expect(util.transpileAndExecute(code)).toBe("baz"); - }); - - test("globalThis to _G (function)", () => { - const code = ` - globalThis.foo = () => "bar"; - return globalThis.foo();`; - - expect(util.transpileAndExecute(code)).toBe("bar"); - }); - - test("globalThis to _G (assign + noImplicitAny)", () => { - const code = ` - (globalThis).foo = "bar"; - return (globalThis).foo;`; - - expect(util.transpileAndExecute(code)).toBe("bar"); - }); - - test("globalThis to _G (var)", () => { - const code = ` - var globalFoo = "bar"; - return globalThis.globalFoo;`; - - const lua = util.transpileString(code); - - expect(util.executeLua(lua)).toBe("bar"); - }); - - test("globalThis to _G (let)", () => { - const code = ` - let NotAGlobalFoo = "bar"; - return (globalThis).NotAGlobalFoo;`; - - expect(util.transpileAndExecute(code)).toBe(undefined); - }); -}); diff --git a/test/unit/lualib/global.spec.ts b/test/unit/lualib/global.spec.ts new file mode 100644 index 000000000..935f0ea53 --- /dev/null +++ b/test/unit/lualib/global.spec.ts @@ -0,0 +1,38 @@ +import * as util from "../../util"; + +describe("globalThis", () => { + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/660 + test.skip("equals _G", () => { + util.testExpression`_G` + .setTsHeader("declare global { const _G: typeof globalThis }") + .debug() + .expectToEqual(true); + }); + + test("registers global symbol", () => { + util.testFunction` + globalThis.foo = "bar"; + return foo; + ` + .setTsHeader("declare global { var foo: string }") + .expectToEqual("bar"); + }); + + test("uses global symbol", () => { + util.testFunction` + foo = "bar"; + return globalThis.foo; + ` + .setTsHeader("declare global { var foo: string }") + .expectToEqual("bar"); + }); + + test("function call", () => { + util.testFunction` + foo = () => "bar"; + return globalThis.foo(); + ` + .setTsHeader("declare global { var foo: () => string }") + .expectToEqual("bar"); + }); +}); From d9ad58aaeb0ad2fbac9ce8a5a3d1db2023884e1b Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 05:08:24 +0500 Subject: [PATCH 30/64] Move and remove some tests from expressions.spec.ts --- test/unit/conditionals.spec.ts | 48 +++++++ test/unit/expressions.spec.ts | 226 ++----------------------------- test/unit/lualib/console.spec.ts | 4 +- test/unit/lualib/lualib.spec.ts | 29 ++++ 4 files changed, 91 insertions(+), 216 deletions(-) diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index 1d3b67de6..33ad65662 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -304,3 +304,51 @@ test("switch not allowed in 5.1", () => { TSTLErrors.UnsupportedForTarget("Switch statements", tstl.LuaTarget.Lua51, util.nodeStub) ); }); + +test.each([ + { input: "true ? 'a' : 'b'" }, + { input: "false ? 'a' : 'b'" }, + { input: "true ? false : true" }, + { input: "false ? false : true" }, + { input: "true ? literalValue : true" }, + { input: "true ? variableValue : true" }, + { input: "true ? maybeUndefinedValue : true" }, + { input: "true ? maybeBooleanValue : true" }, + { input: "true ? maybeUndefinedValue : true", options: { strictNullChecks: true } }, + { input: "true ? maybeBooleanValue : true", options: { strictNullChecks: true } }, + { input: "true ? undefined : true", options: { strictNullChecks: true } }, + { input: "true ? null : true", options: { strictNullChecks: true } }, + { input: "true ? false : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, + { input: "false ? false : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, + { input: "true ? undefined : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, + { input: "true ? false : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, + { input: "false ? false : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, + { input: "true ? undefined : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, +])("Ternary operator (%p)", ({ input, options }) => { + util.testFunction` + const literalValue = "literal"; + let variableValue: string; + let maybeBooleanValue: string | boolean = false; + let maybeUndefinedValue: string | undefined; + return ${input}; + ` + .setOptions(options) + .expectToMatchJsResult(); +}); + +test.each([ + { condition: true, lhs: 4, rhs: 5 }, + { condition: false, lhs: 4, rhs: 5 }, + { condition: 3, lhs: 4, rhs: 5 }, +])("Ternary Conditional (%p)", ({ condition, lhs, rhs }) => { + util.testExpressionTemplate`${condition} ? ${lhs} : ${rhs}`.expectToMatchJsResult(); +}); + +test.each(["true", "false", "a < 4", "a == 8"])("Ternary Conditional Delayed (%p)", condition => { + util.testFunction` + let a = 3; + let delay = () => ${condition} ? a + 3 : a + 5; + a = 8; + return delay(); + `.expectToMatchJsResult(); +}); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 5d52123c7..8ecaec919 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -3,6 +3,15 @@ import * as tstl from "../../src"; import * as TSTLErrors from "../../src/TSTLErrors"; import * as util from "../util"; +// TODO: +test("Block statement", () => { + util.testFunction` + let a = 4; + { let a = 42; } + return a; + `.expectToMatchJsResult(); +}); + test.each([ "i++", "++i", @@ -129,134 +138,6 @@ test("Undefined Expression", () => { expect(util.transpileString("undefined")).toBe("local ____ = nil"); }); -test.each([ - { input: "true ? 'a' : 'b'" }, - { input: "false ? 'a' : 'b'" }, - { input: "true ? false : true" }, - { input: "false ? false : true" }, - { input: "true ? literalValue : true" }, - { input: "true ? variableValue : true" }, - { input: "true ? maybeUndefinedValue : true" }, - { input: "true ? maybeBooleanValue : true" }, - { input: "true ? maybeUndefinedValue : true", options: { strictNullChecks: true } }, - { input: "true ? maybeBooleanValue : true", options: { strictNullChecks: true } }, - { input: "true ? undefined : true", options: { strictNullChecks: true } }, - { input: "true ? null : true", options: { strictNullChecks: true } }, - { input: "true ? false : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, - { input: "false ? false : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, - { input: "true ? undefined : true", options: { luaTarget: tstl.LuaTarget.Lua51 } }, - { input: "true ? false : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, - { input: "false ? false : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, - { input: "true ? undefined : true", options: { luaTarget: tstl.LuaTarget.LuaJIT } }, -])("Ternary operator (%p)", ({ input, options }) => { - util.testFunction` - const literalValue = "literal"; - let variableValue: string; - let maybeBooleanValue: string | boolean = false; - let maybeUndefinedValue: string | undefined; - return ${input}; - ` - .setOptions(options) - .expectToMatchJsResult(); -}); - -test.each([ - { condition: true, lhs: 4, rhs: 5 }, - { condition: false, lhs: 4, rhs: 5 }, - { condition: 3, lhs: 4, rhs: 5 }, -])("Ternary Conditional (%p)", ({ condition, lhs, rhs }) => { - util.testExpressionTemplate`${condition} ? ${lhs} : ${rhs}`.expectToMatchJsResult(); -}); - -test.each(["true", "false", "a < 4", "a == 8"])("Ternary Conditional Delayed (%p)", condition => { - util.testFunction` - let a = 3; - let delay = () => ${condition} ? a + 3 : a + 5; - a = 8; - return delay(); - `.expectToMatchJsResult(); -}); - -test.each([ - "inst.field", - "inst.field + 3", - "inst.field * 3", - "inst.field / 2", - "inst.field && 3", - "inst.field || 3", - "(inst.field + 3) & 3", - "inst.field | 3", - "inst.field << 3", - "inst.field >>> 1", - "inst.field = 3", - `"abc" + inst.field`, -])("Get accessor expression (%p)", expression => { - util.testFunction` - class MyClass { - public _field: number; - public get field(): number { return this._field + 4; } - public set field(v: number) { this._field = v; } - } - var inst = new MyClass(); - inst._field = 4; - return ${expression}; - `.expectToMatchJsResult(); -}); - -test.each(["= 4", "-= 3", "+= 3", "*= 3", "/= 2", "&= 3", "|= 3", "<<= 3", ">>>= 3"])( - "Set accessorExpression (%p)", - expression => { - util.testFunction` - class MyClass { - public _field: number = 4; - public get field(): number { return this._field; } - public set field(v: number) { this._field = v + 4; } - } - var inst = new MyClass(); - inst.field ${expression}; - return inst._field; - `.expectToMatchJsResult(); - } -); - -test.each(["inst.baseField", "inst.field", "inst.superField", "inst.superBaseField"])( - "Inherited accessors (%p)", - expression => { - util.testFunction` - class MyBaseClass { - public _baseField: number; - public get baseField(): number { return this._baseField + 6; } - public set baseField(v: number) { this._baseField = v; } - } - class MyClass extends MyBaseClass { - public _field: number; - public get field(): number { return this._field + 4; } - public set field(v: number) { this._field = v; } - } - class MySuperClass extends MyClass { - public _superField: number; - public get superField(): number { return this._superField + 2; } - public set superField(v: number) { this._superField = v; } - public get superBaseField() { return this.baseField - 3; } - } - var inst = new MySuperClass(); - inst.baseField = 1; - inst.field = 2; - inst.superField = 3; - return ${expression}; - `.expectToMatchJsResult(); - } -); - -test.each(["return x.value;", "x.value = 3; return x.value;"])("Union accessors (%p)", expression => { - util.testFunction` - class A{ get value(){ return this.v || 1; } set value(v){ this.v = v; } v: number; } - class B{ get value(){ return this.v || 2; } set value(v){ this.v = v; } v: number; } - let x: A|B = new A(); - ${expression} - `.expectToMatchJsResult(); -}); - test.each(["i++", "i--", "++i", "--i"])("Incrementor value (%p)", expression => { util.testFunction` let i = 10; @@ -313,14 +194,6 @@ test.each([ `.expectToMatchJsResult(); }); -test("Block expression", () => { - util.testFunction` - let a = 4; - { let a = 42; } - return a; - `.expectToMatchJsResult(); -}); - test("Non-null expression", () => { util.testFunction` function abc(): number | undefined { return 3; } @@ -329,81 +202,6 @@ test("Non-null expression", () => { `.expectToMatchJsResult(); }); -test("Unknown unary postfix error", () => { - const transformer = util.makeTestTransformer(); - - const mockExpression: any = { - operand: ts.createLiteral(false), - operator: ts.SyntaxKind.AsteriskToken, - }; - - expect(() => - transformer.transformPostfixUnaryExpression(mockExpression as ts.PostfixUnaryExpression) - ).toThrowExactError( - TSTLErrors.UnsupportedKind("unary postfix operator", ts.SyntaxKind.AsteriskToken, util.nodeStub) - ); -}); - -test("Unknown unary postfix error", () => { - const transformer = util.makeTestTransformer(); - - const mockExpression: any = { - operand: ts.createLiteral(false), - operator: ts.SyntaxKind.AsteriskToken, - }; - - expect(() => - transformer.transformPrefixUnaryExpression(mockExpression as ts.PrefixUnaryExpression) - ).toThrowExactError( - TSTLErrors.UnsupportedKind("unary prefix operator", ts.SyntaxKind.AsteriskToken, util.nodeStub) - ); -}); - -test("Incompatible fromCodePoint expression error", () => { - util.testExpression`String.fromCodePoint(123)` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError( - TSTLErrors.UnsupportedForTarget("string property fromCodePoint", tstl.LuaTarget.Lua53, util.nodeStub) - ); -}); - -test("Unknown string expression error", () => { - util.testExpression`String.abcd()` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError( - TSTLErrors.UnsupportedForTarget("string property abcd", tstl.LuaTarget.Lua53, util.nodeStub) - ); -}); - -test("Unsupported array function error", () => { - util.testFunction`[].unknownFunction()` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("array", "unknownFunction", util.nodeStub)); -}); - -test("Unsupported math property error", () => { - util.testExpression`Math.unknownProperty` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("math", "unknownProperty", util.nodeStub)); -}); - -test("Unsupported object literal element error", () => { - const transformer = util.makeTestTransformer(); - - const mockObject: any = { - properties: [ - { - kind: ts.SyntaxKind.FalseKeyword, - name: ts.createIdentifier("testProperty"), - }, - ], - }; - - expect(() => transformer.transformObjectLiteral(mockObject as ts.ObjectLiteralExpression)).toThrowExactError( - TSTLErrors.UnsupportedKind("object literal element", ts.SyntaxKind.FalseKeyword, util.nodeStub) - ); -}); - test.each([ '"foobar"', "17", @@ -415,17 +213,15 @@ test.each([ "!foo()", "foo()", "typeof foo", - '"bar" in bar', + '"bar" in foo', "foo as Function", "Math.log2(2)", "Math.log10(2)", ])("Expression statements (%p)", input => { util.testFunction` function foo() { return 17; } - const bar = {}; ${input}; - return 1; - `.expectToMatchJsResult(); + `.expectNoExecutionError(); }); test("binary expression with 'as' type assertion wrapped in parenthesis", () => { diff --git a/test/unit/lualib/console.spec.ts b/test/unit/lualib/console.spec.ts index 66de0e807..c8466e74f 100644 --- a/test/unit/lualib/console.spec.ts +++ b/test/unit/lualib/console.spec.ts @@ -41,7 +41,9 @@ test.each([ test("console.differentiation", () => { util.testModule` export class Console { - test() { return 42; } + public test() { + return 42; + } } function test() { diff --git a/test/unit/lualib/lualib.spec.ts b/test/unit/lualib/lualib.spec.ts index 7d2d09e35..3232308e3 100644 --- a/test/unit/lualib/lualib.spec.ts +++ b/test/unit/lualib/lualib.spec.ts @@ -1,4 +1,5 @@ import * as tstl from "../../../src"; +import * as TSTLErrors from "../../../src/TSTLErrors"; import * as util from "../../util"; describe("luaLibImport", () => { @@ -35,3 +36,31 @@ test("lualibs should not include tstl header", () => { arr.push(4); `.tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("Generated with")); }); + +test("Incompatible fromCodePoint expression error", () => { + util.testExpression`String.fromCodePoint(123)` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedForTarget("string property fromCodePoint", tstl.LuaTarget.Lua53, util.nodeStub) + ); +}); + +test("Unknown string expression error", () => { + util.testExpression`String.abcd()` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError( + TSTLErrors.UnsupportedForTarget("string property abcd", tstl.LuaTarget.Lua53, util.nodeStub) + ); +}); + +test("Unsupported array function error", () => { + util.testFunction`[].unknownFunction()` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("array", "unknownFunction", util.nodeStub)); +}); + +test("Unsupported math property error", () => { + util.testExpression`Math.unknownProperty` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("math", "unknownProperty", util.nodeStub)); +}); From 33e36b45448c36b1c7f305d4a6f09d26718cf05e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 05:21:49 +0500 Subject: [PATCH 31/64] Move class-related tests to a directory --- test/unit/{ => classes}/accessors.spec.ts | 2 +- test/unit/{class.spec.ts => classes/classes.spec.ts} | 4 ++-- .../{classDecorator.spec.ts => classes/decorators.spec.ts} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename test/unit/{ => classes}/accessors.spec.ts (99%) rename test/unit/{class.spec.ts => classes/classes.spec.ts} (99%) rename test/unit/{classDecorator.spec.ts => classes/decorators.spec.ts} (98%) diff --git a/test/unit/accessors.spec.ts b/test/unit/classes/accessors.spec.ts similarity index 99% rename from test/unit/accessors.spec.ts rename to test/unit/classes/accessors.spec.ts index 76dbf6c32..5f9232246 100644 --- a/test/unit/accessors.spec.ts +++ b/test/unit/classes/accessors.spec.ts @@ -1,4 +1,4 @@ -import * as util from "../util"; +import * as util from "../../util"; test("get accessor", () => { util.testFunction` diff --git a/test/unit/class.spec.ts b/test/unit/classes/classes.spec.ts similarity index 99% rename from test/unit/class.spec.ts rename to test/unit/classes/classes.spec.ts index d83ae1262..3f156c93a 100644 --- a/test/unit/class.spec.ts +++ b/test/unit/classes/classes.spec.ts @@ -1,6 +1,6 @@ import * as ts from "typescript"; -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; test("ClassFieldInitializer", () => { const result = util.transpileAndExecute( diff --git a/test/unit/classDecorator.spec.ts b/test/unit/classes/decorators.spec.ts similarity index 98% rename from test/unit/classDecorator.spec.ts rename to test/unit/classes/decorators.spec.ts index 780cc51c5..cb79d1761 100644 --- a/test/unit/classDecorator.spec.ts +++ b/test/unit/classes/decorators.spec.ts @@ -1,5 +1,5 @@ -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; test("Class decorator with no parameters", () => { util.testFunction` From 66f2aa0c07c3c403684778f206045e7188ca7ed5 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sat, 13 Jul 2019 05:52:10 +0500 Subject: [PATCH 32/64] Move namespace tests from modules --- test/unit/declarations.spec.ts | 55 ----------------- test/unit/modules.spec.ts | 59 ------------------ test/unit/namespaces.spec.ts | 108 +++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 114 deletions(-) create mode 100644 test/unit/namespaces.spec.ts diff --git a/test/unit/declarations.spec.ts b/test/unit/declarations.spec.ts index 353c864ea..42923e325 100644 --- a/test/unit/declarations.spec.ts +++ b/test/unit/declarations.spec.ts @@ -11,20 +11,6 @@ test("Declaration function call", () => { expect(result).toBe(10); }); -test("Declaration namespace function call", () => { - const libLua = ` - myNameSpace = {} - function myNameSpace.declaredFunction(x) return 3*x end - `; - - const tsHeader = `declare namespace myNameSpace { function declaredFunction(this: void, x: number): number; }`; - - const source = `return myNameSpace.declaredFunction(2) + 4;`; - - const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); - expect(result).toBe(10); -}); - test("Declaration interface function call", () => { const libLua = ` myInterfaceInstance = {} @@ -74,44 +60,3 @@ test("Declaration instance function callback", () => { const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); expect(result).toBe(20); }); - -test("ImportEquals declaration", () => { - const header = ` - namespace outerNamespace { - export namespace innerNamespace { - export function func() { return "foo" } - } - }; - - import importedFunc = outerNamespace.innerNamespace.func; - `; - - const execution = `return importedFunc();`; - - const result = util.transpileAndExecute(execution, undefined, undefined, header); - expect(result).toEqual("foo"); -}); - -test("ImportEquals declaration ambient", () => { - const header = ` - declare namespace outerNamespace { - namespace innerNamespace { - function func(): string; - } - }; - - import importedFunc = outerNamespace.innerNamespace.func; - `; - - const luaHeader = `outerNamespace = { - innerNamespace = { - func = function() return "foo" end - } - } - `; - - const execution = `return importedFunc();`; - - const result = util.transpileAndExecute(execution, undefined, luaHeader, header); - expect(result).toEqual("foo"); -}); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index ad1ad3c41..c4b235760 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -84,62 +84,3 @@ test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌ .expectToEqual("bar"); } ); - -test("Non-exported module", () => { - const result = util.transpileAndExecute( - "return g.test();", - undefined, - undefined, - "module g { export function test() { return 3; } }" - ); - - expect(result).toBe(3); -}); - -test("Nested module with dot in name", () => { - const code = `module a.b { - export const foo = "foo"; - }`; - expect(util.transpileAndExecute("return a.b.foo;", undefined, undefined, code)).toBe("foo"); -}); - -test("Access this in module", () => { - const header = ` - module M { - export const foo = "foo"; - export function bar() { return this.foo + "bar"; } - } - `; - const code = `return M.bar();`; - expect(util.transpileAndExecute(code, undefined, undefined, header)).toBe("foobar"); -}); - -test("Module merged with interface", () => { - const header = ` - interface Foo {} - module Foo { - export function bar() { return "foobar"; } - }`; - const code = `return Foo.bar();`; - expect(util.transpileAndExecute(code, undefined, undefined, header)).toBe("foobar"); -}); - -test("module merged across files", () => { - const testA = ` - namespace NS { - export namespace Inner { - export const foo = "foo"; - } - } - `; - const testB = ` - namespace NS { - export namespace Inner { - export const bar = "bar"; - } - } - `; - const { transpiledFiles } = util.transpileStringsAsProject({ "testA.ts": testA, "testB.ts": testB }); - const lua = transpiledFiles.map(f => f.lua).join("\n") + "\nreturn NS.Inner.foo .. NS.Inner.bar"; - expect(util.executeLua(lua)).toBe("foobar"); -}); diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts new file mode 100644 index 000000000..973e62c3f --- /dev/null +++ b/test/unit/namespaces.spec.ts @@ -0,0 +1,108 @@ +import * as util from "../util"; + +test("legacy internal module syntax", () => { + util.testModule` + module Foo { + export const foo = "bar"; + } + + export const foo = Foo.foo; + `.expectToMatchJsResult(); +}); + +test("global scoping", () => { + const result = util.transpileAndExecute( + "return a.foo();", + undefined, + undefined, + `namespace a { export function foo() { return "bar"; } }` + ); + + expect(result).toBe("bar"); +}); + +test("nested namespace with dot in name", () => { + util.testModule` + namespace a.b { + export const foo = "foo"; + } + + export const foo = a.b.foo; + `.expectToMatchJsResult(); +}); + +test("context in namespace function", () => { + util.testModule` + namespace a { + export const foo = "foo"; + export function bar() { return this.foo + "bar"; } + } + + export const result = a.bar(); + `.expectToMatchJsResult(); +}); + +test("namespace merged with interface", () => { + util.testModule` + interface Foo {} + namespace Foo { + export function bar() { return "foobar"; } + } + + export const result = Foo.bar(); + `.expectToMatchJsResult(); +}); + +test("namespace merged across files", () => { + const testA = ` + namespace NS { + export namespace Inner { + export const foo = "foo"; + } + } + `; + + const testB = ` + namespace NS { + export namespace Inner { + export const bar = "bar"; + } + } + `; + + const { transpiledFiles } = util.transpileStringsAsProject({ "testA.ts": testA, "testB.ts": testB }); + const lua = transpiledFiles.map(f => f.lua).join("\n") + "\nreturn NS.Inner.foo .. NS.Inner.bar"; + expect(util.executeLua(lua)).toBe("foobar"); +}); + +test("declared namespace function call", () => { + const luaHeader = ` + myNameSpace = {} + function myNameSpace.declaredFunction(x) return 3*x end + `; + + util.testModule` + declare namespace myNameSpace { + function declaredFunction(this: void, x: number): number; + } + + export const result = myNameSpace.declaredFunction(2); + ` + .setLuaHeader(luaHeader) + .expectToEqual(6); +}); + +test("`import =` on a namespace", () => { + util.testModule` + namespace outerNamespace { + export namespace innerNamespace { + export function func() { + return "foo"; + } + } + } + + import importedFunc = outerNamespace.innerNamespace.func; + export const result = importedFunc(); + `.expectToMatchJsResult(); +}); From a4ec2926599b13c3d7767280b669cd154c28aef3 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 01:54:42 +0500 Subject: [PATCH 33/64] Move modules tests to a subdirectory --- test/unit/{ => modules}/modules.spec.ts | 21 ++-- .../resolution.spec.ts} | 104 +++++------------- 2 files changed, 40 insertions(+), 85 deletions(-) rename test/unit/{ => modules}/modules.spec.ts (84%) rename test/unit/{require.spec.ts => modules/resolution.spec.ts} (55%) diff --git a/test/unit/modules.spec.ts b/test/unit/modules/modules.spec.ts similarity index 84% rename from test/unit/modules.spec.ts rename to test/unit/modules/modules.spec.ts index c4b235760..f445cda4c 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -1,6 +1,6 @@ import * as ts from "typescript"; -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; describe("module import/export elision", () => { const moduleDeclaration = ` @@ -45,8 +45,7 @@ describe("module import/export elision", () => { test("should elide type exports", () => { util.testModule` - declare const _G: any; - _G.foo = true; + (globalThis as any).foo = true; type foo = boolean; export { foo }; `.expectToEqual([]); @@ -54,18 +53,18 @@ describe("module import/export elision", () => { }); test.each([ - "export { default } from '...'", - "export { x as default } from '...';", - "export { default as x } from '...';", -])("Export default keyword disallowed (%p)", exportStatement => { - util.testFunction(exportStatement) + `export { default } from "..."`, + `export { x as default } from "...";`, + `export { default as x } from "...";`, +])("Export default disallowed (%p)", exportStatement => { + util.testModule(exportStatement) .disableSemanticCheck() .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedDefaultExport(util.nodeStub)); }); -test("defaultImport", () => { +test("Import default disallowed", () => { util.testModule` - import Test from "test"; + import Test from "..."; ` .disableSemanticCheck() .expectToHaveDiagnosticOfError(TSTLErrors.DefaultImportsNotSupported(util.nodeStub)); diff --git a/test/unit/require.spec.ts b/test/unit/modules/resolution.spec.ts similarity index 55% rename from test/unit/require.spec.ts rename to test/unit/modules/resolution.spec.ts index 6beb5123a..6c1dbe105 100644 --- a/test/unit/require.spec.ts +++ b/test/unit/modules/resolution.spec.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import * as util from "../util"; +import * as util from "../../util"; const requireRegex = /require\("(.*?)"\)/; const expectToRequire = (expected: string): util.TapCallback => builder => { @@ -15,169 +15,125 @@ test.each([ usedPath: "./folder/Module", expected: "folder.Module", options: { rootDir: "." }, - throwsError: false, }, { filePath: "main.ts", usedPath: "./folder/Module", expected: "folder.Module", options: { rootDir: "./" }, - throwsError: false, }, { filePath: "src/main.ts", usedPath: "./folder/Module", expected: "src.folder.Module", options: { rootDir: "." }, - throwsError: false, }, { filePath: "main.ts", usedPath: "folder/Module", expected: "folder.Module", options: { rootDir: ".", baseUrl: "." }, - throwsError: false, }, { filePath: "main.ts", usedPath: "folder/Module", expected: "folder.Module", options: { rootDir: "./", baseUrl: "." }, - throwsError: false, }, { filePath: "src/main.ts", usedPath: "./folder/Module", expected: "folder.Module", options: { rootDir: "src" }, - throwsError: false, }, { filePath: "src/main.ts", usedPath: "./folder/Module", expected: "folder.Module", options: { rootDir: "./src" }, - throwsError: false, - }, - { - filePath: "main.ts", - usedPath: "../Module", - expected: "", - options: { rootDir: "./src" }, - throwsError: true, }, { filePath: "src/dir/main.ts", usedPath: "../Module", expected: "Module", options: { rootDir: "./src" }, - throwsError: false, }, { filePath: "src/dir/dir/main.ts", usedPath: "../../dir/Module", expected: "dir.Module", options: { rootDir: "./src" }, - throwsError: false, }, -])("require paths root from --baseUrl or --rootDir (%p)", ({ filePath, usedPath, expected, options, throwsError }) => { - const builder = util.testModule` +])("resolve paths with baseUrl or rootDir (%p)", ({ filePath, usedPath, expected, options }) => { + util.testModule` import * as module from "${usedPath}"; module; - `; - - builder.setOptions(options).setMainFileName(filePath); + ` + .setMainFileName(filePath) + .setOptions(options) + .tap(expectToRequire(expected)); +}); - if (throwsError) { - builder.expectToHaveDiagnostics(); - } else { - builder.tap(expectToRequire(expected)); - } +test("doesn't resolve paths out of root dir", () => { + util.testModule` + import * as module from "../module"; + module; + ` + .setMainFileName("src/main.ts") + .setOptions({ rootDir: "./src" }) + .disableSemanticCheck() + .expectToHaveDiagnostics(); }); test.each([ - { - declarationStatement: ` - declare module 'fake' {} - `, - mainCode: "import * as fake from 'fake'; fake;", - expectedPath: "src.fake", - }, { declarationStatement: ` /** @noResolution */ - declare module 'fake' {} + declare module "fake" {} `, - mainCode: "import * as fake from 'fake'; fake;", + mainCode: `import "fake";`, expectedPath: "fake", }, - { - declarationStatement: ` - declare module 'fake' { - export const x: number; - } - `, - mainCode: "import { x } from 'fake'; x;", - expectedPath: "src.fake", - }, { declarationStatement: ` /** @noResolution */ - declare module 'fake' { - export const x: number; - } + declare module "fake" {} `, - mainCode: "import { x } from 'fake'; x;", + mainCode: `import * as fake from "fake"; fake;`, expectedPath: "fake", }, { declarationStatement: ` /** @noResolution */ - declare module 'fake' { + declare module "fake" { export const x: number; } - declare module 'fake' { - export const y: number; - } `, - mainCode: "import { y } from 'fake'; y;", + mainCode: `import { x } from "fake"; x;`, expectedPath: "fake", }, { declarationStatement: ` - declare module 'fake' { + /** @noResolution */ + declare module "fake" { export const x: number; } - declare module 'fake' { + + declare module "fake" { export const y: number; } `, - mainCode: "import { y } from 'fake'; y;", - expectedPath: "src.fake", - }, - { - declarationStatement: ` - declare module 'fake' {} - `, - mainCode: "import 'fake';", - expectedPath: "src.fake", - }, - { - declarationStatement: ` - /** @noResolution */ - declare module 'fake' {} - `, - mainCode: "import 'fake';", + mainCode: `import { y } from "fake"; y;`, expectedPath: "fake", }, -])("noResolution prevents any module path resolution behaviour", ({ declarationStatement, mainCode, expectedPath }) => { +])("noResolution prevents any module path resolution behavior", ({ declarationStatement, mainCode, expectedPath }) => { util.testModule(mainCode) .setMainFileName("src/main.ts") .addExtraFile("module.d.ts", declarationStatement) .tap(expectToRequire(expectedPath)); }); -test("ImportEquals declaration require", () => { +test("import = require", () => { util.testModule` import foo = require("./foo/bar"); foo; From 0302c6935d06749f0769a9bfb74e7747e2e36628 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 02:35:59 +0500 Subject: [PATCH 34/64] Transform tagged template literals tests --- test/unit/templateLiterals.spec.ts | 163 +++++++---------------------- 1 file changed, 38 insertions(+), 125 deletions(-) diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index 34aed1180..9ae221b03 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -1,12 +1,5 @@ import * as util from "../util"; -test.each(["a++", "a--", "--a", "++a"])("Template string expression (%p)", lambda => { - util.testFunction` - let a = 3; - return \`val\${${lambda}}\`; - `.expectToMatchJsResult(); -}); - test.each([ { a: 12, b: 23, c: 43 }, { a: "test", b: "hello", c: "bye" }, @@ -14,133 +7,53 @@ test.each([ { a: "test", b: 42, c: 12 }, { a: "test", b: 42, c: true }, { a: false, b: 42, c: 12 }, -])("Template Strings (%p)", ({ a, b, c }) => { - util.testFunctionTemplate` - let a = ${a}; - let b = ${b}; - let c = ${c}; - return \`${a} ${b} test ${c}\`; - `.expectToMatchJsResult(); +])("template literal (%p)", ({ a, b, c }) => { + util.testExpressionTemplate`\`\${${a}} \${${b}} test \${${c}}\``.expectToMatchJsResult(); }); -const testCases = [ - { - callExpression: "func``", - joinAllResult: "", - joinRawResult: "", - }, - { - callExpression: "func`hello`", - joinAllResult: "hello", - joinRawResult: "hello", - }, - { - callExpression: "func`hello ${1} ${2} ${3}`", - joinAllResult: "hello 1 2 3", - joinRawResult: "hello ", - }, - { - callExpression: "func`hello ${(() => 'iife')()}`", - joinAllResult: "hello iife", - joinRawResult: "hello ", - }, - { - callExpression: "func`hello ${1 + 2 + 3} arithmetic`", - joinAllResult: "hello 6 arithmetic", - joinRawResult: "hello arithmetic", - }, - { - callExpression: "func`begin ${'middle'} end`", - joinAllResult: "begin middle end", - joinRawResult: "begin end", - }, - { - callExpression: "func`hello ${func`hello`}`", - joinAllResult: "hello hello", - joinRawResult: "hello ", - }, - { - callExpression: "func`hello \\u00A9`", - joinAllResult: "hello ©", - joinRawResult: "hello \\u00A9", - }, - { - callExpression: "func`hello $ { }`", - joinAllResult: "hello $ { }", - joinRawResult: "hello $ { }", - }, - { - callExpression: "func`hello { ${'brackets'} }`", - joinAllResult: "hello { brackets }", - joinRawResult: "hello { }", - }, - { - callExpression: "func`hello \\``", - joinAllResult: "hello `", - joinRawResult: "hello \\`", - }, - { - callExpression: "obj.func`hello ${'propertyAccessExpression'}`", - joinAllResult: "hello propertyAccessExpression", - joinRawResult: "hello ", - }, - { - callExpression: "obj['func']`hello ${'elementAccessExpression'}`", - joinAllResult: "hello elementAccessExpression", - joinRawResult: "hello ", - }, -]; - -test.each(testCases)("TaggedTemplateLiteral call (%p)", ({ callExpression, joinAllResult }) => { - const result = util.transpileAndExecute(` - function func(strings: TemplateStringsArray, ...expressions: any[]) { - const toJoin = []; - for (let i = 0; i < strings.length; ++i) { - if (strings[i]) { - toJoin.push(strings[i]); - } - if (expressions[i]) { - toJoin.push(expressions[i]); - } - } - return toJoin.join(""); - } - const obj = { - func - }; - return ${callExpression}; - `); - - expect(result).toBe(joinAllResult); +test.each(["a++", "a--", "--a", "++a"])("template literal with expression (%p)", expression => { + util.testFunction` + let a = 3; + return \`value\${${expression}}\`; + `.expectToMatchJsResult(); }); -test.each(testCases)("TaggedTemplateLiteral raw preservation (%p)", ({ callExpression, joinRawResult }) => { - const result = util.transpileAndExecute(` - function func(strings: TemplateStringsArray, ...expressions: any[]) { - return strings.raw.join(""); - } - const obj = { - func - }; - return ${callExpression}; - `); +test.each([ + "func``", + "func`hello`", + "func`hello ${1} ${2} ${3}`", + "func`hello ${(() => 'iife')()}`", + "func`hello ${1 + 2 + 3} arithmetic`", + "func`begin ${'middle'} end`", + "func`hello ${func`hello`}`", + "func`hello \\u00A9`", + "func`hello $ { }`", + "func`hello { ${'brackets'} }`", + "func`hello \\``", + "obj.func`hello ${'propertyAccessExpression'}`", + "obj['func']`hello ${'elementAccessExpression'}`", +])("tagged template literal (%p)", expression => { + // TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/663 + util.testFunction` + function func(strings: TemplateStringsArray, ...expressions: any[]) { + return { strings: [...strings], raw: strings.raw, expressions: expressions }; + } - expect(result).toBe(joinRawResult); + const obj = { func }; + return ${expression}; + `.expectToMatchJsResult(); }); test.each(["func`noSelfParameter`", "obj.func`noSelfParameter`", "obj[`func`]`noSelfParameter`"])( - "TaggedTemplateLiteral no self parameter", - callExpression => { - const result = util.transpileAndExecute(` - function func(this: void, strings: TemplateStringsArray, ...expressions: any[]) { - return strings.join(""); + "tagged template literal function context (%p)", + expression => { + util.testFunction` + function func(this: void, strings: TemplateStringsArray) { + return [...strings]; } - const obj = { - func - }; - return ${callExpression}; - `); - expect(result).toBe("noSelfParameter"); + const obj = { func }; + return ${expression}; + `.expectToMatchJsResult(); } ); From aa5d5e53095c776f6389c43f61c9b7aa9037cb4d Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 03:18:03 +0500 Subject: [PATCH 35/64] Transform sourcemaps tests --- test/unit/sourcemaps.spec.ts | 144 +++++++++++++++++------------------ test/util.ts | 81 ++++++++++++-------- 2 files changed, 117 insertions(+), 108 deletions(-) diff --git a/test/unit/sourcemaps.spec.ts b/test/unit/sourcemaps.spec.ts index 51d01bbc8..4819c9bc5 100644 --- a/test/unit/sourcemaps.spec.ts +++ b/test/unit/sourcemaps.spec.ts @@ -4,11 +4,12 @@ import * as util from "../util"; test.each([ { - typeScriptSource: ` + code: ` const abc = "foo"; const def = "bar"; - const xyz = "baz";`, + const xyz = "baz"; + `, assertPatterns: [ { luaPattern: "abc", typeScriptPattern: "abc" }, @@ -20,26 +21,28 @@ test.each([ ], }, { - typeScriptSource: ` + code: ` function abc() { return def(); } + function def() { return "foo"; } - return abc();`, + `, assertPatterns: [ { luaPattern: "function abc(", typeScriptPattern: "function abc() {" }, { luaPattern: "function def(", typeScriptPattern: "function def() {" }, - { luaPattern: "return abc(", typeScriptPattern: "return abc(" }, + { luaPattern: "return def(", typeScriptPattern: "return def(" }, { luaPattern: "end", typeScriptPattern: "function def() {" }, ], }, { - typeScriptSource: ` + code: ` const enum abc { foo = 2, bar = 4 }; - const xyz = abc.foo;`, + const xyz = abc.foo; + `, assertPatterns: [ { luaPattern: "xyz", typeScriptPattern: "xyz" }, @@ -47,8 +50,9 @@ test.each([ ], }, { - typeScriptSource: ` - import {Foo} from "foo"; + code: ` + // @ts-ignore + import { Foo } from "foo"; Foo; `, @@ -58,7 +62,8 @@ test.each([ ], }, { - typeScriptSource: ` + code: ` + // @ts-ignore import * as Foo from "foo"; Foo; `, @@ -69,7 +74,8 @@ test.each([ ], }, { - typeScriptSource: ` + code: ` + // @ts-ignore class Bar extends Foo { constructor() { super(); @@ -92,7 +98,7 @@ test.each([ ], }, { - typeScriptSource: ` + code: ` class Foo { } `, @@ -100,7 +106,7 @@ test.each([ assertPatterns: [{ luaPattern: "function Foo.prototype.____constructor", typeScriptPattern: "class Foo" }], }, { - typeScriptSource: ` + code: ` class Foo { bar = "baz"; } @@ -109,7 +115,7 @@ test.each([ assertPatterns: [{ luaPattern: "function Foo.prototype.____constructor", typeScriptPattern: "class Foo" }], }, { - typeScriptSource: ` + code: ` declare const arr: string[]; for (const element of arr) {} `, @@ -120,7 +126,7 @@ test.each([ ], }, { - typeScriptSource: ` + code: ` declare function getArr(this: void): string[]; for (const element of getArr()) {} `, @@ -132,7 +138,7 @@ test.each([ ], }, { - typeScriptSource: ` + code: ` declare const arr: string[] for (let i = 0; i < arr.length; ++i) {} `, @@ -143,31 +149,28 @@ test.each([ { luaPattern: "i + 1", typeScriptPattern: "++i" }, ], }, -])("Source map has correct mapping (%p)", async ({ typeScriptSource, assertPatterns }) => { - // Act - const { file } = util.transpileStringResult(typeScriptSource); - - // Assert - if (!util.expectToBeDefined(file.lua) || !util.expectToBeDefined(file.sourceMap)) return; +])("Source map has correct mapping (%p)", async ({ code, assertPatterns }) => { + const file = util + .testModule(code) + .expectToHaveNoDiagnostics() + .getMainLuaFileResult(); const consumer = await new SourceMapConsumer(file.sourceMap); for (const { luaPattern, typeScriptPattern } of assertPatterns) { const luaPosition = lineAndColumnOf(file.lua, luaPattern); const mappedPosition = consumer.originalPositionFor(luaPosition); + const typescriptPosition = lineAndColumnOf(code, typeScriptPattern); - const typescriptPosition = lineAndColumnOf(typeScriptSource, typeScriptPattern); - - const mappedLineColumn = { line: mappedPosition.line, column: mappedPosition.column }; - expect(mappedLineColumn).toEqual(typescriptPosition); + expect(mappedPosition).toMatchObject(typescriptPosition); } }); test("Source map has correct sources", async () => { - const code = `const foo = "foo"`; - - const { file } = util.transpileStringResult(code); - - if (!util.expectToBeDefined(file.lua) || !util.expectToBeDefined(file.sourceMap)) return; + const file = util.testModule` + const foo = "foo" + ` + .expectToHaveNoDiagnostics() + .getMainLuaFileResult(); const consumer = await new SourceMapConsumer(file.sourceMap); expect(consumer.sources.length).toBe(1); @@ -175,11 +178,11 @@ test("Source map has correct sources", async () => { }); test("Source map has correct source root", async () => { - const code = `const foo = "foo"`; - - const { file } = util.transpileStringResult(code); - - if (!util.expectToBeDefined(file.lua) || !util.expectToBeDefined(file.sourceMap)) return; + const file = util.testModule` + const foo = "foo" + ` + .expectToHaveNoDiagnostics() + .getMainLuaFileResult(); const sourceMap = JSON.parse(file.sourceMap); expect(sourceMap.sourceRoot).toBe("."); @@ -189,14 +192,15 @@ test.each([ { code: `const type = "foobar";`, name: "type" }, { code: `const and = "foobar";`, name: "and" }, { code: `const $$$ = "foobar";`, name: "$$$" }, - { code: `const foo = { bar() { console.log(this); } };`, name: "this" }, + { code: `const foo = { bar() { this; } };`, name: "this" }, { code: `function foo($$$: unknown) {}`, name: "$$$" }, { code: `class $$$ {}`, name: "$$$" }, { code: `namespace $$$ { const foo = "bar"; }`, name: "$$$" }, ])("Source map has correct name mappings (%p)", async ({ code, name }) => { - const { file } = util.transpileStringResult(code); - - if (!util.expectToBeDefined(file.lua) || !util.expectToBeDefined(file.sourceMap)) return; + const file = util + .testModule(code) + .expectToHaveNoDiagnostics() + .getMainLuaFileResult(); const consumer = await new SourceMapConsumer(file.sourceMap); const typescriptPosition = lineAndColumnOf(code, name); @@ -206,41 +210,30 @@ test.each([ mappedName = mapping.name; } }); + expect(mappedName).toBe(name); }); test("sourceMapTraceback saves sourcemap in _G", () => { - // Arrange - const typeScriptSource = ` + const code = ` function abc() { return "foo"; } - return JSONStringify(_G.__TS__sourcemap);`; - - const options: tstl.CompilerOptions = { - sourceMapTraceback: true, - luaLibImport: tstl.LuaLibImportKind.Inline, - }; - // Act - const transpiledLua = util.transpileString(typeScriptSource, options); + return (globalThis as any).__TS__sourcemap; + `; - const sourceMapJson = util.transpileAndExecute( - typeScriptSource, - options, - undefined, - "declare const _G: {__TS__sourcemap: any};" - ); + const builder = util + .testFunction(code) + .setOptions({ sourceMapTraceback: true, luaLibImport: tstl.LuaLibImportKind.Inline }); - // Assert - expect(sourceMapJson).toBeDefined(); - - const sourceMap = JSON.parse(sourceMapJson); + const sourceMap = builder.getLuaExecutionResult(); + const transpiledLua = builder.getMainLuaCodeChunk(); + expect(sourceMap).toEqual(expect.any(Object)); const sourceMapFiles = Object.keys(sourceMap); - - expect(sourceMapFiles.length).toBe(1); - expect(sourceMap[sourceMapFiles[0]]).toBeDefined(); + expect(sourceMapFiles).toHaveLength(1); + const mainSourceMap = sourceMap[sourceMapFiles[0]]; const assertPatterns = [ { luaPattern: "function abc(", typeScriptPattern: "function abc() {" }, @@ -249,37 +242,36 @@ test("sourceMapTraceback saves sourcemap in _G", () => { for (const { luaPattern, typeScriptPattern } of assertPatterns) { const luaPosition = lineAndColumnOf(transpiledLua, luaPattern); - const mappedLine = sourceMap[sourceMapFiles[0]][luaPosition.line.toString()]; + const mappedLine = mainSourceMap[luaPosition.line.toString()]; - const typescriptPosition = lineAndColumnOf(typeScriptSource, typeScriptPattern); - - // Add 1 to account for transpiledAndExecute-added function header - expect(mappedLine).toEqual(typescriptPosition.line + 1); + const typescriptPosition = lineAndColumnOf(code, typeScriptPattern); + expect(mappedLine).toEqual(typescriptPosition.line); } }); test("Inline sourcemaps", () => { - const typeScriptSource = ` + const code = ` function abc() { return def(); } + function def() { return "foo"; } - return abc();`; - const compilerOptions: tstl.CompilerOptions = { inlineSourceMap: true }; + return abc(); + `; - const { file } = util.transpileStringResult(typeScriptSource, compilerOptions); - if (!util.expectToBeDefined(file.lua)) return; + const file = util + .testFunction(code) + .setOptions({ inlineSourceMap: true }) + .expectToMatchJsResult() + .getMainLuaFileResult(); const inlineSourceMapMatch = file.lua.match(/--# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)/); - if (util.expectToBeDefined(inlineSourceMapMatch)) { const inlineSourceMap = Buffer.from(inlineSourceMapMatch[1], "base64").toString(); expect(file.sourceMap).toBe(inlineSourceMap); - - expect(util.executeLua(file.lua)).toBe("foo"); } }); diff --git a/test/util.ts b/test/util.ts index 22b9fd8cd..b1cdc80aa 100644 --- a/test/util.ts +++ b/test/util.ts @@ -229,9 +229,9 @@ export class ExecutionError extends Error { } } +export type ExecutableTranspiledFile = tstl.TranspiledFile & { lua: string; sourceMap: string }; export type TapCallback = (builder: TestBuilder) => void; -export class TestBuilder { - protected accessor = ""; +export abstract class TestBuilder { constructor(protected _tsCode: string) {} // Options @@ -239,28 +239,28 @@ export class TestBuilder { // TODO: Use testModule in these cases? protected tsHeader = ""; public setTsHeader(tsHeader: string): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); this.tsHeader = tsHeader; return this; } private luaHeader = ""; public setLuaHeader(luaHeader: string): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); this.luaHeader += luaHeader; return this; } - private jsHeader = ""; + protected jsHeader = ""; public setJsHeader(jsHeader: string): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); this.jsHeader += jsHeader; return this; } private semanticCheck = true; public disableSemanticCheck(): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); this.semanticCheck = false; return this; } @@ -274,21 +274,21 @@ export class TestBuilder { experimentalDecorators: true, }; public setOptions(options: tstl.CompilerOptions = {}): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); Object.assign(this.options, options); return this; } protected mainFileName = "main.ts"; public setMainFileName(mainFileName: string): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); this.mainFileName = mainFileName; return this; } private extraFiles: Record = {}; public addExtraFile(fileName: string, code: string): this { - expect(this._hasProgram).toBe(false); + expect(this.hasProgram).toBe(false); this.extraFiles[fileName] = code; return this; } @@ -299,10 +299,10 @@ export class TestBuilder { return `${this.tsHeader}${this._tsCode}`; } - private _hasProgram = false; + private hasProgram = false; @memoize public getProgram(): ts.Program { - this._hasProgram = true; + this.hasProgram = true; return tstl.createVirtualProgram({ ...this.extraFiles, [this.mainFileName]: this.getTsCode() }, this.options); } @@ -319,25 +319,21 @@ export class TestBuilder { } @memoize - public getMainLuaCodeChunk(): string { + public getMainLuaFileResult(): ExecutableTranspiledFile { const { transpiledFiles } = this.getLuaResult(); const mainFile = transpiledFiles.find(x => x.fileName === this.mainFileName); - expect(mainFile).toBeDefined(); - - const header = this.luaHeader ? `${this.luaHeader.trimRight()}\n` : ""; - return header + mainFile!.lua!.trimRight(); + expect(mainFile).toMatchObject({ lua: expect.any(String), sourceMap: expect.any(String) }); + return mainFile as ExecutableTranspiledFile; } @memoize - private getLuaCodeWithWrapper(): string { - let code = this.getMainLuaCodeChunk(); - if (code.includes('require("lualib_bundle")')) { - code = `package.preload.lualib_bundle = function()\n${lualibContent}\nend\n${code}`; - } - - return `${minimalTestLib}\nreturn JSONStringify((function()\n${code}\nend)()${this.accessor})`; + public getMainLuaCodeChunk(): string { + const header = this.luaHeader ? `${this.luaHeader.trimRight()}\n` : ""; + return header + this.getMainLuaFileResult().lua.trimRight(); } + public abstract getLuaCodeWithWrapper(): string; + @memoize public getLuaExecutionResult(): any { const code = this.getLuaCodeWithWrapper(); @@ -367,22 +363,24 @@ export class TestBuilder { } @memoize - protected getJsCode(): string { + protected getMainJsCodeChunk(): string { const { transpiledFiles } = this.getJsResult(); const mainFile = transpiledFiles.find(x => x.fileName === this.mainFileName); expect(mainFile).toBeDefined(); const header = this.jsHeader ? `${this.jsHeader.trimRight()}\n` : ""; - return header + mainFile!.js! + `;module.exports = exports${this.accessor}`; + return header + mainFile!.js!; } + protected abstract getJsCodeWithWrapper(): string; + @memoize public getJsExecutionResult(): any { const exports = {}; const context = vm.createContext({ exports, module: { exports } }); context.global = context; try { - return vm.runInContext(this.getJsCode(), context); + return vm.runInContext(this.getJsCodeWithWrapper(), context); } catch (error) { return new ExecutionError(error.message); } @@ -470,21 +468,40 @@ export class TestBuilder { } } -class ModuleTestBuilder extends TestBuilder { +class AccessorTestBuilder extends TestBuilder { + protected accessor = ""; + + @memoize + public getLuaCodeWithWrapper(): string { + let code = this.getMainLuaCodeChunk(); + if (code.includes('require("lualib_bundle")')) { + code = `package.preload.lualib_bundle = function()\n${lualibContent}\nend\n${code}`; + } + + return `${minimalTestLib}\nreturn JSONStringify((function()\n${code}\nend)()${this.accessor})`; + } + + @memoize + protected getJsCodeWithWrapper(): string { + return this.getMainJsCodeChunk() + `\n;module.exports = exports${this.accessor}`; + } +} + +class ModuleTestBuilder extends AccessorTestBuilder { public setExport(name: string): this { this.accessor = `.${name}`; return this; } } -class FunctionTestBuilder extends TestBuilder { +class FunctionTestBuilder extends AccessorTestBuilder { protected accessor = ".__main()"; public getTsCode(): string { return `${this.tsHeader}export function __main() {${this._tsCode}}`; } } -class ExpressionTestBuilder extends TestBuilder { +class ExpressionTestBuilder extends AccessorTestBuilder { protected accessor = ".__result"; public getTsCode(): string { return `${this.tsHeader}export const __result = ${this._tsCode};`; @@ -492,7 +509,7 @@ class ExpressionTestBuilder extends TestBuilder { } const createTestBuilderFactory = ( - builderClass: new (_tsCode: string) => T, + builder: new (_tsCode: string) => T, serializeSubstitutions: boolean ) => (...args: [string] | [TemplateStringsArray, ...any[]]): T => { let tsCode: string; @@ -510,7 +527,7 @@ const createTestBuilderFactory = ( .join(""); } - return new builderClass(tsCode); + return new builder(tsCode); }; export const testModule = createTestBuilderFactory(ModuleTestBuilder, false); From 2b67650d1f142a077c611fdea3aeaefbae0c1c23 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 04:19:14 +0500 Subject: [PATCH 36/64] Move string tests to lualib directory --- test/unit/{ => lualib}/string.spec.ts | 15 +++------------ test/unit/templateLiterals.spec.ts | 4 ++++ 2 files changed, 7 insertions(+), 12 deletions(-) rename test/unit/{ => lualib}/string.spec.ts (95%) diff --git a/test/unit/string.spec.ts b/test/unit/lualib/string.spec.ts similarity index 95% rename from test/unit/string.spec.ts rename to test/unit/lualib/string.spec.ts index 57316bfc1..de58b112f 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/lualib/string.spec.ts @@ -1,5 +1,5 @@ -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; test("Unsupported string function", () => { util.testExpression`"test".testThisIsNoMember()` @@ -67,7 +67,7 @@ test.each([ test.each([["", ""], ["hello", "test"], ["hello", "test", "bye"], ["hello", 42], [42, "hello"]])( "string.concat[+] (%p)", (...elements) => { - util.testExpression(elements.map(e => util.valueToString(e)).join(" + ")); + util.testExpression(elements.map(e => util.valueToString(e)).join(" + ")).expectToMatchJsResult(); } ); @@ -248,12 +248,3 @@ test.each(padCases)("string.padStart (%p)", ({ inp, args }) => { test.each(padCases)("string.padEnd (%p)", ({ inp, args }) => { util.testExpression`"${inp}".padEnd(${util.valuesToString(args)})`.expectToMatchJsResult(); }); - -test.each([`"foobar".length`, `"foobar".repeat(2)`, "`foo${'bar'}`.length", "`foo${'bar'}`.repeat(2)"])( - "string literal property access (%p)", - expression => { - const code = `return ${expression}`; - const expectResult = eval(expression); - expect(util.transpileAndExecute(code)).toBe(expectResult); - } -); diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index 9ae221b03..3cc638ebe 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -18,6 +18,10 @@ test.each(["a++", "a--", "--a", "++a"])("template literal with expression (%p)", `.expectToMatchJsResult(); }); +test.each(["`foo${'bar'}`.length", "`foo${'bar'}`.repeat(2)"])("template literal property access (%p)", expression => { + util.testExpression(expression).expectToMatchJsResult(); +}); + test.each([ "func``", "func`hello`", From 25385c7183d26872f761d6cbbe434b87d8ad061c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 05:51:00 +0500 Subject: [PATCH 37/64] Refactor spread.spec.ts --- test/unit/assignmentDestructuring.spec.ts | 38 ++++++------- test/unit/spread.spec.ts | 60 +++++++++++++++++++++ test/unit/spreadElement.spec.ts | 66 ----------------------- test/util.ts | 18 +++++++ 4 files changed, 94 insertions(+), 88 deletions(-) create mode 100644 test/unit/spread.spec.ts delete mode 100644 test/unit/spreadElement.spec.ts diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts index 53e0dbac6..86145ad08 100644 --- a/test/unit/assignmentDestructuring.spec.ts +++ b/test/unit/assignmentDestructuring.spec.ts @@ -1,28 +1,22 @@ import * as tstl from "../../src"; import * as util from "../util"; -const assignmentDestructuringCode = ` - declare function myFunc(this: void): [number, string]; - let [a, b] = myFunc(); -`; - -test("Assignment destructuring [5.1]", () => { - util.testModule(assignmentDestructuringCode) - .setOptions({ luaTarget: tstl.LuaTarget.Lua51, luaLibImport: tstl.LuaLibImportKind.None }) - .expectLuaToMatchSnapshot(); -}); - -test("Assignment destructuring [5.2]", () => { - util.testModule(assignmentDestructuringCode) - .setOptions({ luaTarget: tstl.LuaTarget.Lua52, luaLibImport: tstl.LuaLibImportKind.None }) - .expectLuaToMatchSnapshot(); -}); - -test("Assignment destructuring [JIT]", () => { - util.testModule(assignmentDestructuringCode) - .setOptions({ luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.None }) - .expectLuaToMatchSnapshot(); -}); +util.testEachVersion( + "Assignment destructuring", + () => + util.testModule` + declare function myFunc(this: void): [number, string]; + let [a, b] = myFunc(); + ` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.None }) + .expectLuaToMatchSnapshot(), + { + [tstl.LuaTarget.LuaJIT]: builder => builder, + [tstl.LuaTarget.Lua51]: builder => builder, + [tstl.LuaTarget.Lua52]: builder => builder, + [tstl.LuaTarget.Lua53]: false, + } +); test("OmittedExpression in Array Binding Assignment Statement", () => { util.testFunction` diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts new file mode 100644 index 000000000..2f14122aa --- /dev/null +++ b/test/unit/spread.spec.ts @@ -0,0 +1,60 @@ +import * as tstl from "../../src"; +import * as util from "../util"; + +// TODO: Make some utils for testing other targets +const expectUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toMatch(/[^.]unpack\(/); +const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("table.unpack"); + +describe("in function call", () => { + util.testEachVersion( + undefined, + // TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/663 + // TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/664 + () => util.testFunction` + function foo(a: number, b: number, ...rest: number[]) { + return { a, b, rest: rest } + } + + const array: [number, number, number, number] = [0, 1, 2, 3]; + return foo(...array); + `, + { + [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), + [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + } + ); +}); + +describe("in array literal", () => { + test("of iterable", () => { + util.testFunction` + const it = { + i: -1, + [Symbol.iterator]() { + return this; + }, + next() { + ++this.i; + return { + value: 2 ** this.i, + done: this.i == 9, + } + } + }; + + return [...it] + `.expectToMatchJsResult(); + }); + + test.todo("of generator"); + test.todo("of string"); + + util.testEachVersion("of array literal", () => util.testExpression`[...[0, 1, 2]]`, { + [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), + [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + }); +}); diff --git a/test/unit/spreadElement.spec.ts b/test/unit/spreadElement.spec.ts deleted file mode 100644 index 28201f12e..000000000 --- a/test/unit/spreadElement.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as tstl from "../../src"; -import * as util from "../util"; - -test.each([{ inp: [] }, { inp: [1, 2, 3] }, { inp: [1, "test", 3] }])("Spread Element Push (%p)", ({ inp }) => { - const result = util.transpileAndExecute( - `return JSONStringify(([] as Array).push(...${JSON.stringify(inp)}));` - ); - expect(result).toBe(([] as Array).push(...inp)); -}); - -test("Spread Element Lua 5.1", () => { - // Cant test functional because our VM doesn't run on 5.1 - const options: tstl.CompilerOptions = { - luaTarget: tstl.LuaTarget.Lua51, - luaLibImport: tstl.LuaLibImportKind.None, - }; - const lua = util.transpileString(`[].push(...${JSON.stringify([1, 2, 3])});`, options); - expect(lua).toBe("__TS__ArrayPush(\n {},\n unpack({1, 2, 3})\n)"); -}); - -test("Spread Element Lua 5.2", () => { - const options: tstl.CompilerOptions = { - luaTarget: tstl.LuaTarget.Lua52, - luaLibImport: tstl.LuaLibImportKind.None, - }; - const lua = util.transpileString(`[...[0, 1, 2]]`, options); - expect(lua).toBe("local ____ = {\n table.unpack({0, 1, 2})\n}"); -}); - -test("Spread Element Lua 5.3", () => { - const options: tstl.CompilerOptions = { - luaTarget: tstl.LuaTarget.Lua53, - luaLibImport: tstl.LuaLibImportKind.None, - }; - const lua = util.transpileString(`[...[0, 1, 2]]`, options); - expect(lua).toBe("local ____ = {\n table.unpack({0, 1, 2})\n}"); -}); - -test("Spread Element Lua JIT", () => { - const options: tstl.CompilerOptions = { - luaTarget: tstl.LuaTarget.LuaJIT, - luaLibImport: tstl.LuaLibImportKind.None, - }; - const lua = util.transpileString(`[...[0, 1, 2]]`, options); - expect(lua).toBe("local ____ = {\n unpack({0, 1, 2})\n}"); -}); - -test("Spread Element Iterable", () => { - const code = ` - const it = { - i: -1, - [Symbol.iterator]() { - return this; - }, - next() { - ++this.i; - return { - value: 2 ** this.i, - done: this.i == 9, - } - } - }; - const arr = [...it]; - return JSONStringify(arr)`; - expect(JSON.parse(util.transpileAndExecute(code))).toEqual([1, 2, 4, 8, 16, 32, 64, 128, 256]); -}); diff --git a/test/util.ts b/test/util.ts index b1cdc80aa..ed26d155d 100644 --- a/test/util.ts +++ b/test/util.ts @@ -170,6 +170,24 @@ export const valueToString = (value: unknown) => export const valuesToString = (values: unknown[]) => values.map(valueToString).join(", "); +export function testEachVersion( + name: string | undefined, + common: () => T, + special: Record T) | false> +): void { + for (const version of Object.values(tstl.LuaTarget) as tstl.LuaTarget[]) { + const specialBuilder = special[version]; + if (specialBuilder === false) return; + + const testName = name === undefined ? version : `${name} [${version}]`; + test(testName, () => { + const builder = common(); + builder.setOptions({ luaTarget: version }); + specialBuilder(builder); + }); + } +} + interface TranspiledJsFile { fileName: string; js?: string; From 54c36682f615bd2a5ff8c571d3b13474971f1653 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 06:43:01 +0500 Subject: [PATCH 38/64] Refactor bindingpatterns.spec.ts --- test/unit/bindingpatterns.spec.ts | 111 +++++++++++------------------- 1 file changed, 39 insertions(+), 72 deletions(-) diff --git a/test/unit/bindingpatterns.spec.ts b/test/unit/bindingpatterns.spec.ts index aad38661b..106fbe88e 100644 --- a/test/unit/bindingpatterns.spec.ts +++ b/test/unit/bindingpatterns.spec.ts @@ -1,84 +1,51 @@ import * as util from "../util"; +const allBindings = "x, y, z"; const testCases = [ - { bindingString: "{x}", objectString: "{x: true}", returnVariable: "x" }, - { bindingString: "[x, y]", objectString: "[false, true]", returnVariable: "y" }, - { bindingString: "{x: [y, z]}", objectString: "{x: [false, true]}", returnVariable: "z" }, - { bindingString: "{x: [, z]}", objectString: "{x: [false, true]}", returnVariable: "z" }, - { bindingString: "{x: [{y}]}", objectString: "{x: [{y: true}]}", returnVariable: "y" }, - { bindingString: "[[y, z]]", objectString: "[[false, true]]", returnVariable: "z" }, - { bindingString: "{x, y}", objectString: "{x: false, y: true}", returnVariable: "y" }, - { bindingString: "{x: foo, y}", objectString: "{x: true, y: false}", returnVariable: "foo" }, - { bindingString: "{x: foo, y: bar}", objectString: "{x: false, y: true}", returnVariable: "bar" }, - { bindingString: "{x: {x, y}, z}", objectString: "{x: {x: true, y: false}, z: false}", returnVariable: "x" }, - { bindingString: "{x: {x, y}, z}", objectString: "{x: {x: false, y: true}, z: false}", returnVariable: "y" }, - { bindingString: "{x: {x, y}, z}", objectString: "{x: {x: false, y: false}, z: true}", returnVariable: "z" }, -]; + { binding: "{ x }", value: { x: true } }, + { binding: "{ x, y }", value: { x: false, y: true } }, + { binding: "{ x: z, y }", value: { x: true, y: false } }, + { binding: "{ x: { x, y }, z }", value: { x: { x: true, y: false }, z: false } }, + { binding: "{ x, y = true }", value: { x: false, y: false } }, + { binding: "{ x = true }", value: {} }, + { binding: "{ x, y = true }", value: { x: false } }, -const testCasesDefault = [ - { bindingString: "{x = true}", objectString: "{}", returnVariable: "x" }, - { bindingString: "{x, y = true}", objectString: "{x: false}", returnVariable: "y" }, -]; + { binding: "[x, y]", value: [false, true] }, + { binding: "[[x, y]]", value: [[false, true]] }, + { binding: "[x = true]", value: [false] }, + + { binding: "{ x: [y, z] }", value: { x: [false, true] } }, + { binding: "{ x: [{ y }] }", value: { x: [{ y: true }] } }, + { binding: "{ x, y: [z = true] }", value: { x: false, y: [false] } }, +].map(({ binding, value }) => ({ binding, value: util.valueToString(value) })); test.each([ - { bindingString: "{x, y}, z", objectString: "{x: false, y: false}, true", returnVariable: "z" }, - { bindingString: "{x, y}, {z}", objectString: "{x: false, y: false}, {z: true}", returnVariable: "z" }, + { binding: "{ x, y }, z", value: "{ x: false, y: false }, true" }, + { binding: "{ x, y }, { z }", value: "{ x: false, y: false }, { z: true }" }, ...testCases, - ...testCasesDefault, -])("Object bindings in functions (%p)", ({ bindingString, objectString, returnVariable }) => { - const result = util.transpileAndExecute(` - function test(${bindingString}) { - return ${returnVariable}; +])("in function parameter (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + function test(${binding}) { + return { ${allBindings} }; } - return test(${objectString}); - `); - expect(result).toBe(true); -}); -test.each([...testCases, ...testCasesDefault])( - "testBindingPatternDeclarations (%p)", - ({ bindingString, objectString, returnVariable }) => { - const result = util.transpileAndExecute(` - let ${bindingString} = ${objectString}; - return ${returnVariable}; - `); - expect(result).toBe(true); - } -); - -test.each([...testCases, ...testCasesDefault])( - "testBindingPatternExportDeclarations (%p)", - ({ bindingString, objectString, returnVariable }) => { - const result = util.transpileExecuteAndReturnExport( - `export const ${bindingString} = ${objectString};`, - returnVariable - ); - expect(result).toBe(true); - } -); + return test(${value}); + `.expectToMatchJsResult(); +}); -test.each(testCases)( - "Object bindings with call expressions (%p)", - ({ bindingString, objectString, returnVariable }) => { - const result = util.transpileAndExecute(` - function call() { - return ${objectString}; - } - let ${bindingString} = call(); - return ${returnVariable}; - `); - expect(result).toBe(true); - } -); +test.each(testCases)("in variable declaration (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + { + const ${binding} = ${value}; + return { ${allBindings} }; + } + `.expectToMatchJsResult(); +}); -test.each([ - { bindingString: "{x, y = true}", objectString: "{x: false, y: false}", returnVariable: "y" }, - { bindingString: "{x, y: [z = true]}", objectString: "{x: false, y: [false]}", returnVariable: "z" }, - { bindingString: "[x = true]", objectString: "[false]", returnVariable: "x" }, -])("Binding patterns handle false correctly (%p)", ({ bindingString, objectString, returnVariable }) => { - const result = util.transpileExecuteAndReturnExport( - `export const ${bindingString} = ${objectString};`, - returnVariable - ); - expect(result).toBe(false); +test.each(testCases)("in exported variable declaration (%p)", ({ binding, value }) => { + util.testModule` + export const ${binding} = ${value}; + `.expectToMatchJsResult(); }); From 93d8f9cc9664e1ca2588735dadc22179cf53d62e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 08:37:08 +0500 Subject: [PATCH 39/64] Merge and refactor destructuring tests --- .../assignmentDestructuring.spec.ts.snap | 19 ---- test/unit/assignmentDestructuring.spec.ts | 49 -------- test/unit/bindingpatterns.spec.ts | 51 --------- test/unit/destructuring.spec.ts | 107 ++++++++++++++++++ test/unit/tuples.spec.ts | 30 ----- test/util.ts | 13 ++- 6 files changed, 118 insertions(+), 151 deletions(-) delete mode 100644 test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap delete mode 100644 test/unit/assignmentDestructuring.spec.ts delete mode 100644 test/unit/bindingpatterns.spec.ts create mode 100644 test/unit/destructuring.spec.ts diff --git a/test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap b/test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap deleted file mode 100644 index 96d4c8956..000000000 --- a/test/unit/__snapshots__/assignmentDestructuring.spec.ts.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Assignment destructuring [5.1] 1`] = ` -"local a, b = unpack( - myFunc() -)" -`; - -exports[`Assignment destructuring [5.2] 1`] = ` -"local a, b = table.unpack( - myFunc() -)" -`; - -exports[`Assignment destructuring [JIT] 1`] = ` -"local a, b = unpack( - myFunc() -)" -`; diff --git a/test/unit/assignmentDestructuring.spec.ts b/test/unit/assignmentDestructuring.spec.ts deleted file mode 100644 index 86145ad08..000000000 --- a/test/unit/assignmentDestructuring.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as tstl from "../../src"; -import * as util from "../util"; - -util.testEachVersion( - "Assignment destructuring", - () => - util.testModule` - declare function myFunc(this: void): [number, string]; - let [a, b] = myFunc(); - ` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.None }) - .expectLuaToMatchSnapshot(), - { - [tstl.LuaTarget.LuaJIT]: builder => builder, - [tstl.LuaTarget.Lua51]: builder => builder, - [tstl.LuaTarget.Lua52]: builder => builder, - [tstl.LuaTarget.Lua53]: false, - } -); - -test("OmittedExpression in Array Binding Assignment Statement", () => { - util.testFunction` - let a, c; - [a, , c] = [1, 2, 3]; - return { a, c }; - `.expectToMatchJsResult(); -}); - -test.each([ - "function foo(): [] { return []; }; let [] = foo();", - "let [] = ['a', 'b', 'c'];", - "let [] = [];", - "let [] = [] = [];", - "function foo(): [] { return []; }; [] = foo();", - "[] = ['a', 'b', 'c'];", - "[] = [];", - "[] = [] = [];", -])("Empty destructuring (%p)", code => { - util.testFunction(code).expectNoExecutionError(); -}); - -test("Union destructuring", () => { - util.testFunction` - function foo(): [string] | [] { return ["bar"]; } - let x: string; - [x] = foo(); - return x; - `.expectToMatchJsResult(); -}); diff --git a/test/unit/bindingpatterns.spec.ts b/test/unit/bindingpatterns.spec.ts deleted file mode 100644 index 106fbe88e..000000000 --- a/test/unit/bindingpatterns.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as util from "../util"; - -const allBindings = "x, y, z"; -const testCases = [ - { binding: "{ x }", value: { x: true } }, - { binding: "{ x, y }", value: { x: false, y: true } }, - { binding: "{ x: z, y }", value: { x: true, y: false } }, - { binding: "{ x: { x, y }, z }", value: { x: { x: true, y: false }, z: false } }, - { binding: "{ x, y = true }", value: { x: false, y: false } }, - { binding: "{ x = true }", value: {} }, - { binding: "{ x, y = true }", value: { x: false } }, - - { binding: "[x, y]", value: [false, true] }, - { binding: "[[x, y]]", value: [[false, true]] }, - { binding: "[x = true]", value: [false] }, - - { binding: "{ x: [y, z] }", value: { x: [false, true] } }, - { binding: "{ x: [{ y }] }", value: { x: [{ y: true }] } }, - { binding: "{ x, y: [z = true] }", value: { x: false, y: [false] } }, -].map(({ binding, value }) => ({ binding, value: util.valueToString(value) })); - -test.each([ - { binding: "{ x, y }, z", value: "{ x: false, y: false }, true" }, - { binding: "{ x, y }, { z }", value: "{ x: false, y: false }, { z: true }" }, - ...testCases, -])("in function parameter (%p)", ({ binding, value }) => { - util.testFunction` - let ${allBindings}; - function test(${binding}) { - return { ${allBindings} }; - } - - return test(${value}); - `.expectToMatchJsResult(); -}); - -test.each(testCases)("in variable declaration (%p)", ({ binding, value }) => { - util.testFunction` - let ${allBindings}; - { - const ${binding} = ${value}; - return { ${allBindings} }; - } - `.expectToMatchJsResult(); -}); - -test.each(testCases)("in exported variable declaration (%p)", ({ binding, value }) => { - util.testModule` - export const ${binding} = ${value}; - `.expectToMatchJsResult(); -}); diff --git a/test/unit/destructuring.spec.ts b/test/unit/destructuring.spec.ts new file mode 100644 index 000000000..2872f051c --- /dev/null +++ b/test/unit/destructuring.spec.ts @@ -0,0 +1,107 @@ +import * as util from "../util"; + +const allBindings = "x, y, z"; +const testCases = [ + { binding: "{ x }", value: { x: true } }, + { binding: "{ x, y }", value: { x: false, y: true } }, + { binding: "{ x: z, y }", value: { x: true, y: false } }, + { binding: "{ x: { x, y }, z }", value: { x: { x: true, y: false }, z: false } }, + { binding: "{ x, y = true }", value: { x: false, y: false } }, + { binding: "{ x = true }", value: {} }, + { binding: "{ x, y = true }", value: { x: false } }, + + { binding: "[]", value: [] }, + { binding: "[x, y]", value: ["x", "y"] }, + { binding: "[x, , y]", value: ["x", "", "y"] }, + { binding: "[x = true]", value: [false] }, + { binding: "[[x, y]]", value: [["x", "y"]] }, + + { binding: "{ y: [z = true] }", value: { y: [false] } }, + { binding: "{ x: [x, y] }", value: { x: ["x", "y"] } }, + { binding: "{ x: [{ y }] }", value: { x: [{ y: "y" }] } }, +].map(({ binding, value }) => ({ binding, value: util.valueToString(value) })); + +test.each([ + { binding: "{ x, y }, z", value: "{ x: false, y: false }, true" }, + { binding: "{ x, y }, { z }", value: "{ x: false, y: false }, { z: true }" }, + ...testCases, +])("in function parameter (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + function test(${binding}) { + return { ${allBindings} }; + } + + return test(${value}); + `.expectToMatchJsResult(); +}); + +test.each(testCases)("in variable declaration (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + { + const ${binding} = ${value}; + return { ${allBindings} }; + } + `.expectToMatchJsResult(); +}); + +// TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/574 +test.skip.each(testCases)("in assignment (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + (${binding} = ${value}); + return { ${allBindings} }; + `.expectToMatchJsResult(); +}); + +test.each(testCases)("in exported variable declaration (%p)", ({ binding, value }) => { + util.testModule` + export const ${binding} = ${value}; + `.expectToMatchJsResult(); +}); + +describe("array destructuring optimization", () => { + // TODO: Try to generalize optimization logic between declaration and assignment and make more generic tests + + test("array", () => { + util.testFunction` + const array = [3, 5, 1]; + const [a, b, c] = array; + return { a, b, c }; + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain("unpack")) + .expectToMatchJsResult(); + }); + + test("array literal", () => { + util.testFunction` + const [a, b, c] = [3, 5, 1]; + return { a, b, c }; + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("unpack")) + .expectToMatchJsResult(); + }); + + test("array literal with extra values", () => { + util.testFunction` + let called = false; + const set = () => { called = true; }; + const [head] = ["foo", set()]; + return { head, called }; + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("unpack")) + .expectToMatchJsResult(); + }); + + test("array union", () => { + util.testFunction` + const array: [string] | [] = ["bar"]; + let x: string; + [x] = array; + return x; + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain("unpack")) + .expectToMatchJsResult(); + }); +}); diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts index 9d1fa0387..f82f1602f 100644 --- a/test/unit/tuples.spec.ts +++ b/test/unit/tuples.spec.ts @@ -46,36 +46,6 @@ test("Tuple intersection access", () => { `.expectToMatchJsResult(); }); -test("Tuple Destruct", () => { - util.testFunction` - function tuple(): [number, number, number] { return [3,5,1]; } - const [a,b,c] = tuple(); - return b; - `.expectToMatchJsResult(); -}); - -const expectNoUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).not.toContain("unpack"); - -test("Tuple Destruct Array Literal", () => { - util.testFunction` - const [a, b, c] = [3, 5, 1]; - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Destruct Array Literal Extra Values", () => { - util.testFunction` - let result = ""; - const set = () => { result = "bar"; }; - const [a] = ["foo", set()]; - return a + result; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - test("Tuple length", () => { util.testFunction` const tuple: [number, number, number] = [3, 5, 1]; diff --git a/test/util.ts b/test/util.ts index ed26d155d..532ea487d 100644 --- a/test/util.ts +++ b/test/util.ts @@ -453,8 +453,17 @@ export abstract class TestBuilder { const luaResult = this.getLuaExecutionResult(); const jsResult = this.getJsExecutionResult(); - // tslint:disable-next-line: no-null-keyword - if (luaResult !== undefined || jsResult != null) { + if ( + // tslint:disable-next-line: no-null-keyword + !(luaResult === undefined && jsResult == null) && + // Assume {} and [] to be equal + !( + typeof luaResult === "object" && + typeof jsResult === "object" && + Object.values(luaResult).filter(x => x !== undefined).length === 0 && + Object.values(jsResult).filter(x => x !== undefined).length === 0 + ) + ) { expect(luaResult).toEqual(jsResult); } From af47a8356d4c68d4d1742bb1fefe410b8bd3ad5f Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 08:43:25 +0500 Subject: [PATCH 40/64] Fix expressions tests --- test/unit/expressions.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 8ecaec919..ad97bd88a 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -213,13 +213,14 @@ test.each([ "!foo()", "foo()", "typeof foo", - '"bar" in foo', + '"foo" in bar', "foo as Function", "Math.log2(2)", "Math.log10(2)", ])("Expression statements (%p)", input => { util.testFunction` function foo() { return 17; } + const bar = { foo }; ${input}; `.expectNoExecutionError(); }); From ef035fee31f554d1ba83be1a667f09b17615abcd Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 08:44:30 +0500 Subject: [PATCH 41/64] Fix declared namespace function call test --- test/unit/namespaces.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts index 973e62c3f..1b6751b51 100644 --- a/test/unit/namespaces.spec.ts +++ b/test/unit/namespaces.spec.ts @@ -88,6 +88,7 @@ test("declared namespace function call", () => { export const result = myNameSpace.declaredFunction(2); ` + .setExport("result") .setLuaHeader(luaHeader) .expectToEqual(6); }); From 404a1d748bcc1bfd2b9f3ddc5c06996764a6210e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 09:08:37 +0500 Subject: [PATCH 42/64] Remove/move to other files tuples tests --- test/unit/loops.spec.ts | 9 ++++ test/unit/lualib/array.spec.ts | 34 ++++++++++++- test/unit/transformers.spec.ts | 87 ---------------------------------- test/unit/tuples.spec.ts | 54 --------------------- 4 files changed, 42 insertions(+), 142 deletions(-) delete mode 100644 test/unit/transformers.spec.ts delete mode 100644 test/unit/tuples.spec.ts diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 04421036c..357d5c4c8 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -274,6 +274,15 @@ test.each([{ inp: [0, 1, 2], expected: [1, 2, 3] }])("forof (%p)", ({ inp, expec expect(result).toBe(JSON.stringify(expected)); }); +test("Tuple loop", () => { + util.testFunction` + const tuple: [number, number, number] = [3,5,1]; + let count = 0; + for (const value of tuple) { count += value; } + return count; + `.expectToMatchJsResult(); +}); + test.each([{ inp: [0, 1, 2], expected: [1, 2, 3] }])("forof existing variable (%p)", ({ inp, expected }) => { const result = util.transpileAndExecute( `let objTest = ${JSON.stringify(inp)}; diff --git a/test/unit/lualib/array.spec.ts b/test/unit/lualib/array.spec.ts index 72154fd66..5c5a0f670 100644 --- a/test/unit/lualib/array.spec.ts +++ b/test/unit/lualib/array.spec.ts @@ -44,6 +44,20 @@ describe("access", () => { `.expectToMatchJsResult(); }); + test("tuple", () => { + util.testFunction` + const tuple: [number, number, number] = [3, 5, 1]; + return tuple[1]; + `.expectToMatchJsResult(); + }); + + test("union with tuple", () => { + util.testFunction` + const tuple: number[] | [number, number, number] = [3, 5, 1]; + return tuple[1]; + `.expectToMatchJsResult(); + }); + test("access in call", () => { util.testExpression`[() => "foo", () => "bar"][0]()`.expectToMatchJsResult(); }); @@ -84,7 +98,7 @@ describe("access", () => { }); }); -describe(".length", () => { +describe("array.length", () => { describe("get", () => { test("union", () => { util.testFunction` @@ -99,6 +113,13 @@ describe(".length", () => { return array.length; `.expectToMatchJsResult(); }); + + test("tuple", () => { + util.testFunction` + const tuple: [number, number, number] = [3, 5, 1]; + return tuple.length; + `.expectToMatchJsResult(); + }); }); describe("set", () => { @@ -151,6 +172,17 @@ describe("delete", () => { }); }); +test("tuple.forEach", () => { + util.testFunction` + const tuple: [number, number, number] = [3, 5, 1]; + let count = 0; + tuple.forEach(value => { + count += value; + }); + return count; + `.expectToMatchJsResult(); +}); + test("array.forEach (%p)", () => { util.testFunction` const array = [0, 1, 2, 3]; diff --git a/test/unit/transformers.spec.ts b/test/unit/transformers.spec.ts deleted file mode 100644 index cb7f143fe..000000000 --- a/test/unit/transformers.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as path from "path"; -import * as tstl from "../../src"; -import * as util from "../util"; - -const optionsOfTransformer = (transformer: tstl.TransformerImport): tstl.CompilerOptions => ({ - plugins: [transformer], -}); - -test("should ignore language service plugins", () => { - const options: tstl.CompilerOptions = { - plugins: [{ name: path.join(__dirname, "transformers/resolve.ts") }], - }; - - expect(util.transpileAndExecute("return", options)).toBe(undefined); -}); - -describe("resolution", () => { - const testTransform = (transformer: tstl.TransformerImport) => { - const options = optionsOfTransformer(transformer); - expect(util.transpileAndExecute("return", options)).toBe(true); - }; - - test("should resolve relative transformer paths", () => { - jest.spyOn(process, "cwd").mockReturnValue(__dirname); - testTransform({ transform: "./transformers/resolve.ts" }); - }); - - test("should load js transformers", () => { - testTransform({ transform: path.join(__dirname, "transformers/resolve.js") }); - }); - - test("should load ts transformers", () => { - testTransform({ transform: path.join(__dirname, "transformers/resolve.ts") }); - }); - - test('should support "import" option', () => { - testTransform({ - transform: path.join(__dirname, "transformers/import.ts"), - import: "transformer", - }); - }); - - test("should error if transformer could not be resolved", () => { - const transform = path.join(__dirname, "transformers/error.ts"); - const options = optionsOfTransformer({ transform }); - const { diagnostics } = util.transpileStringResult("", options); - expect(diagnostics).toHaveDiagnostics(); - }); -}); - -describe("factory types", () => { - const value = "foo"; - const getOptions = (options: Partial) => - optionsOfTransformer({ - transform: path.join(__dirname, "transformers/types.ts"), - ...options, - }); - - test('should support "program" type', () => { - const options = getOptions({ type: "program", import: "program", value }); - expect(util.transpileAndExecute("return false", options)).toBe(value); - }); - - test('should support "config" type', () => { - const options = getOptions({ type: "config", import: "config", value }); - expect(util.transpileAndExecute("return", options)).toBe(value); - }); - - test('should support "checker" type', () => { - const options = getOptions({ type: "checker", import: "checker", value }); - expect(util.transpileAndExecute("return false", options)).toBe(value); - }); - - test('should support "raw" type', () => { - const options = getOptions({ type: "raw", import: "raw" }); - expect(util.transpileAndExecute("return", options)).toBe(true); - }); - - test('should support "compilerOptions" type', () => { - const options: tstl.CompilerOptions = { - ...getOptions({ type: "compilerOptions", import: "compilerOptions" }), - luaTarget: tstl.LuaTarget.LuaJIT, - }; - - expect(util.transpileAndExecute("return", options)).toBe(true); - }); -}); diff --git a/test/unit/tuples.spec.ts b/test/unit/tuples.spec.ts deleted file mode 100644 index f82f1602f..000000000 --- a/test/unit/tuples.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as util from "../util"; - -test("Tuple loop", () => { - util.testFunction` - const tuple: [number, number, number] = [3,5,1]; - let count = 0; - for (const value of tuple) { count += value; } - return count; - `.expectToMatchJsResult(); -}); - -test("Tuple foreach", () => { - util.testFunction` - const tuple: [number, number, number] = [3,5,1]; - let count = 0; - tuple.forEach(v => count += v); - return count; - `.expectToMatchJsResult(); -}); - -test("Tuple access", () => { - util.testFunction` - const tuple: [number, number, number] = [3,5,1]; - return tuple[1]; - `.expectToMatchJsResult(); -}); - -test("Tuple union access", () => { - util.testFunction` - function makeTuple(): [number, number, number] | [string, string, string] { return [3,5,1]; } - const tuple = makeTuple(); - return tuple[1]; - `.expectToMatchJsResult(); -}); - -test("Tuple intersection access", () => { - util.testFunction` - type I = [number, number, number] & {foo: string}; - function makeTuple(): I { - let t = [3,5,1]; - (t as I).foo = "bar"; - return (t as I); - } - const tuple = makeTuple(); - return tuple[1]; - `.expectToMatchJsResult(); -}); - -test("Tuple length", () => { - util.testFunction` - const tuple: [number, number, number] = [3, 5, 1]; - return tuple.length; - `.expectToMatchJsResult(); -}); From a605fb393f3c6c3b4402e59bbeeb8e5f09c70f44 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 09:13:34 +0500 Subject: [PATCH 43/64] Move transformers.spec.ts to subdirectory --- test/unit/transformers/transformers.spec.ts | 87 +++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 test/unit/transformers/transformers.spec.ts diff --git a/test/unit/transformers/transformers.spec.ts b/test/unit/transformers/transformers.spec.ts new file mode 100644 index 000000000..db36c27dc --- /dev/null +++ b/test/unit/transformers/transformers.spec.ts @@ -0,0 +1,87 @@ +import * as path from "path"; +import * as tstl from "../../../src"; +import * as util from "../../util"; + +const optionsOfTransformer = (transformer: tstl.TransformerImport): tstl.CompilerOptions => ({ + plugins: [transformer], +}); + +test("should ignore language service plugins", () => { + const options: tstl.CompilerOptions = { + plugins: [{ name: path.join(__dirname, "resolve.ts") }], + }; + + expect(util.transpileAndExecute("return", options)).toBe(undefined); +}); + +describe("resolution", () => { + const testTransform = (transformer: tstl.TransformerImport) => { + const options = optionsOfTransformer(transformer); + expect(util.transpileAndExecute("return", options)).toBe(true); + }; + + test("should resolve relative transformer paths", () => { + jest.spyOn(process, "cwd").mockReturnValue(__dirname); + testTransform({ transform: "./resolve.ts" }); + }); + + test("should load js transformers", () => { + testTransform({ transform: path.join(__dirname, "resolve.js") }); + }); + + test("should load ts transformers", () => { + testTransform({ transform: path.join(__dirname, "resolve.ts") }); + }); + + test('should support "import" option', () => { + testTransform({ + transform: path.join(__dirname, "import.ts"), + import: "transformer", + }); + }); + + test("should error if transformer could not be resolved", () => { + const transform = path.join(__dirname, "error.ts"); + const options = optionsOfTransformer({ transform }); + const { diagnostics } = util.transpileStringResult("", options); + expect(diagnostics).toHaveDiagnostics(); + }); +}); + +describe("factory types", () => { + const value = "foo"; + const getOptions = (options: Partial) => + optionsOfTransformer({ + transform: path.join(__dirname, "types.ts"), + ...options, + }); + + test('should support "program" type', () => { + const options = getOptions({ type: "program", import: "program", value }); + expect(util.transpileAndExecute("return false", options)).toBe(value); + }); + + test('should support "config" type', () => { + const options = getOptions({ type: "config", import: "config", value }); + expect(util.transpileAndExecute("return", options)).toBe(value); + }); + + test('should support "checker" type', () => { + const options = getOptions({ type: "checker", import: "checker", value }); + expect(util.transpileAndExecute("return false", options)).toBe(value); + }); + + test('should support "raw" type', () => { + const options = getOptions({ type: "raw", import: "raw" }); + expect(util.transpileAndExecute("return", options)).toBe(true); + }); + + test('should support "compilerOptions" type', () => { + const options: tstl.CompilerOptions = { + ...getOptions({ type: "compilerOptions", import: "compilerOptions" }), + luaTarget: tstl.LuaTarget.LuaJIT, + }; + + expect(util.transpileAndExecute("return", options)).toBe(true); + }); +}); From 9d820662ba6941827e6c9884c37d4b924ef909c4 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 09:26:27 +0500 Subject: [PATCH 44/64] Move some tests to printer subdirectory --- test/unit/conditionals.spec.ts | 22 -------- test/unit/functions.spec.ts | 14 ----- test/unit/loops.spec.ts | 24 --------- test/unit/printer/deadCodeAfterReturn.ts | 61 ++++++++++++++++++++++ test/unit/{ => printer}/semicolons.spec.ts | 14 ++--- test/unit/{ => printer}/sourcemaps.spec.ts | 4 +- 6 files changed, 70 insertions(+), 69 deletions(-) create mode 100644 test/unit/printer/deadCodeAfterReturn.ts rename test/unit/{ => printer}/semicolons.spec.ts (56%) rename test/unit/{ => printer}/sourcemaps.spec.ts (99%) diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index 33ad65662..d5bc14792 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -273,28 +273,6 @@ test.each([0, 1, 2, 3])("switchWithBracketsBreakInInternalLoop (%p)", inp => { `.expectToMatchJsResult(); }); -test("If dead code after return", () => { - util.testFunction` - if (true) { - return 3; - const b = 8; - } - `.expectToMatchJsResult(); -}); - -test("switch dead code after return", () => { - util.testFunction` - switch ("abc" as string) { - case "def": - return 4; - let abc = 4; - case "abc": - return 5; - let def = 6; - } - `.expectToMatchJsResult(); -}); - test("switch not allowed in 5.1", () => { util.testFunction` switch ("abc") {} diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index bf2261f12..d3cb3f8a7 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -208,20 +208,6 @@ test("Invalid property access call transpilation", () => { ); }); -test("Function dead code after return", () => { - util.testFunction` - function abc() { return 3; const a = 5; } - return abc(); - `.expectToMatchJsResult(); -}); - -test("Method dead code after return", () => { - util.testFunction` - class def { public static abc() { return 3; const a = 5; } } - return def.abc(); - `.expectToMatchJsResult(); -}); - test("Recursive function definition", () => { util.testFunction` function f() { return typeof f; }; diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 357d5c4c8..34e92998a 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -595,30 +595,6 @@ test.each([ expect(util.transpileString(loop, luajit).indexOf("::__continue1::") !== -1).toBe(true); }); -test("for dead code after return", () => { - const result = util.transpileAndExecute(`for (let i = 0; i < 10; i++) { return 3; const b = 8; }`); - - expect(result).toBe(3); -}); - -test("for..in dead code after return", () => { - const result = util.transpileAndExecute(`for (let a in {"a": 5, "b": 8}) { return 3; const b = 8; }`); - - expect(result).toBe(3); -}); - -test("for..of dead code after return", () => { - const result = util.transpileAndExecute(`for (let a of [1,2,4]) { return 3; const b = 8; }`); - - expect(result).toBe(3); -}); - -test("while dead code after return", () => { - const result = util.transpileAndExecute(`while (true) { return 3; const b = 8; }`); - - expect(result).toBe(3); -}); - test("do...while", () => { const code = ` let result = 0; diff --git a/test/unit/printer/deadCodeAfterReturn.ts b/test/unit/printer/deadCodeAfterReturn.ts new file mode 100644 index 000000000..08c88658d --- /dev/null +++ b/test/unit/printer/deadCodeAfterReturn.ts @@ -0,0 +1,61 @@ +import * as util from "../../util"; + +test("If dead code after return", () => { + util.testFunction` + if (true) { + return 3; + const b = 8; + } + `.expectToMatchJsResult(); +}); + +test("switch dead code after return", () => { + util.testFunction` + switch ("abc" as string) { + case "def": + return 4; + let abc = 4; + case "abc": + return 5; + let def = 6; + } + `.expectToMatchJsResult(); +}); + +test("Function dead code after return", () => { + util.testFunction` + function abc() { return 3; const a = 5; } + return abc(); + `.expectToMatchJsResult(); +}); + +test("Method dead code after return", () => { + util.testFunction` + class def { public static abc() { return 3; const a = 5; } } + return def.abc(); + `.expectToMatchJsResult(); +}); + +test("for dead code after return", () => { + const result = util.transpileAndExecute(`for (let i = 0; i < 10; i++) { return 3; const b = 8; }`); + + expect(result).toBe(3); +}); + +test("for..in dead code after return", () => { + const result = util.transpileAndExecute(`for (let a in {"a": 5, "b": 8}) { return 3; const b = 8; }`); + + expect(result).toBe(3); +}); + +test("for..of dead code after return", () => { + const result = util.transpileAndExecute(`for (let a of [1,2,4]) { return 3; const b = 8; }`); + + expect(result).toBe(3); +}); + +test("while dead code after return", () => { + const result = util.transpileAndExecute(`while (true) { return 3; const b = 8; }`); + + expect(result).toBe(3); +}); diff --git a/test/unit/semicolons.spec.ts b/test/unit/printer/semicolons.spec.ts similarity index 56% rename from test/unit/semicolons.spec.ts rename to test/unit/printer/semicolons.spec.ts index 2cbe3ecb6..ae3a79244 100644 --- a/test/unit/semicolons.spec.ts +++ b/test/unit/printer/semicolons.spec.ts @@ -1,15 +1,15 @@ -import * as util from "../util"; +import * as util from "../../util"; test.each(["const a = 1; const b = a;", "const a = 1; let b: number; b = a;", "{}", "function bar() {} bar();"])( "semicolon insertion (%p)", leadingStatement => { const code = ` - let result = ""; - function foo() { result = "foo"; } - ${leadingStatement} - (foo)(); - return result; - `; + let result = ""; + function foo() { result = "foo"; } + ${leadingStatement} + (foo)(); + return result; + `; expect(util.transpileAndExecute(code)).toEqual("foo"); } ); diff --git a/test/unit/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts similarity index 99% rename from test/unit/sourcemaps.spec.ts rename to test/unit/printer/sourcemaps.spec.ts index 4819c9bc5..4023dad19 100644 --- a/test/unit/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -1,6 +1,6 @@ import { Position, SourceMapConsumer } from "source-map"; -import * as tstl from "../../src"; -import * as util from "../util"; +import * as tstl from "../../../src"; +import * as util from "../../util"; test.each([ { From b018ee98aea927b91791f75a4df4e6e4137e7f83 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 10:08:41 +0500 Subject: [PATCH 45/64] Remove declarations.spec.ts --- test/unit/declarations.spec.ts | 62 ---------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 test/unit/declarations.spec.ts diff --git a/test/unit/declarations.spec.ts b/test/unit/declarations.spec.ts deleted file mode 100644 index 42923e325..000000000 --- a/test/unit/declarations.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as util from "../util"; - -test("Declaration function call", () => { - const libLua = `function declaredFunction(x) return 3*x end`; - - const tsHeader = `declare function declaredFunction(this: void, x: number): number;`; - - const source = `return declaredFunction(2) + 4;`; - - const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); - expect(result).toBe(10); -}); - -test("Declaration interface function call", () => { - const libLua = ` - myInterfaceInstance = {} - myInterfaceInstance.x = 10 - function myInterfaceInstance:declaredFunction(x) return self.x + 3*x end - `; - - const tsHeader = ` - declare interface MyInterface { - declaredFunction(x: number): number; - } - declare var myInterfaceInstance: MyInterface; - `; - - const source = `return myInterfaceInstance.declaredFunction(3);`; - - const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); - expect(result).toBe(19); -}); - -test("Declaration function callback", () => { - const libLua = `function declaredFunction(callback) return callback(4) end`; - const tsHeader = `declare function declaredFunction(this: void, callback: (this: void, x: number) => number): number;`; - - const source = `return declaredFunction(x => 2 * x);`; - - const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); - expect(result).toBe(8); -}); - -test("Declaration instance function callback", () => { - const libLua = ` - myInstance = {} - myInstance.x = 10 - function myInstance:declaredFunction(callback) return callback(self.x) end - `; - - const tsHeader = ` - declare interface MyInterface { - declaredFunction(callback: (this: void, x: number) => number): number; - } - declare var myInstance: MyInterface; - `; - - const source = `return myInstance.declaredFunction(x => 2 * x);`; - - const result = util.transpileAndExecute(source, undefined, libLua, tsHeader); - expect(result).toBe(20); -}); From 358d7a20fcf197f251647ff49370ff1fd727cbd3 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 10:10:07 +0500 Subject: [PATCH 46/64] Split typechecking into instanceof and typeof --- test/unit/classes/instanceof.spec.ts | 76 +++++++++++++++++++ .../{typechecking.spec.ts => typeof.spec.ts} | 75 ------------------ 2 files changed, 76 insertions(+), 75 deletions(-) create mode 100644 test/unit/classes/instanceof.spec.ts rename test/unit/{typechecking.spec.ts => typeof.spec.ts} (63%) diff --git a/test/unit/classes/instanceof.spec.ts b/test/unit/classes/instanceof.spec.ts new file mode 100644 index 000000000..44e0773ed --- /dev/null +++ b/test/unit/classes/instanceof.spec.ts @@ -0,0 +1,76 @@ +import * as util from "../../util"; + +test("instanceof", () => { + util.testFunction` + class myClass {} + let inst = new myClass(); + return inst instanceof myClass; + `.expectToMatchJsResult(); +}); + +test("instanceof inheritance", () => { + util.testFunction` + class myClass {} + class childClass extends myClass {} + let inst = new childClass(); + return inst instanceof myClass; + `.expectToMatchJsResult(); +}); + +test("instanceof inheritance false", () => { + util.testFunction` + class myClass {} + class childClass extends myClass {} + let inst = new myClass(); + return inst instanceof childClass; + `.expectToMatchJsResult(); +}); + +test("{} instanceof Object", () => { + util.testExpression`{} instanceof Object`.expectToMatchJsResult(); +}); + +test("function instanceof Object", () => { + util.testExpression`(() => {}) instanceof Object`.expectToMatchJsResult(); +}); + +test("null instanceof Object", () => { + util.testExpression`(null as any) instanceof Object`.expectToMatchJsResult(); +}); + +test("instanceof undefined", () => { + util.testExpression`{} instanceof (undefined as any)`.expectToMatchJsResult(true); +}); + +test("null instanceof Class", () => { + util.testFunction` + class myClass {} + return (null as any) instanceof myClass; + `.expectToMatchJsResult(); +}); + +test("instanceof export", () => { + util.testModule` + export class myClass {} + let inst = new myClass(); + export const result = inst instanceof myClass; + ` + .setExport("result") + .expectToMatchJsResult(); +}); + +test("instanceof Symbol.hasInstance", () => { + util.testFunction` + class myClass { + static [Symbol.hasInstance]() { + return false; + } + } + + const inst = new myClass(); + const isInstanceOld = inst instanceof myClass; + myClass[Symbol.hasInstance] = () => true; + const isInstanceNew = inst instanceof myClass; + return isInstanceOld !== isInstanceNew; + `.expectToMatchJsResult(); +}); diff --git a/test/unit/typechecking.spec.ts b/test/unit/typeof.spec.ts similarity index 63% rename from test/unit/typechecking.spec.ts rename to test/unit/typeof.spec.ts index e03bae89c..1541884b1 100644 --- a/test/unit/typechecking.spec.ts +++ b/test/unit/typeof.spec.ts @@ -32,81 +32,6 @@ test.each(["null", "undefined"])("typeof undefined (%p)", inp => { util.testExpression`typeof ${inp}`.expectToEqual("undefined"); }); -test("instanceof", () => { - util.testFunction` - class myClass {} - let inst = new myClass(); - return inst instanceof myClass; - `.expectToMatchJsResult(); -}); - -test("instanceof inheritance", () => { - util.testFunction` - class myClass {} - class childClass extends myClass {} - let inst = new childClass(); - return inst instanceof myClass; - `.expectToMatchJsResult(); -}); - -test("instanceof inheritance false", () => { - util.testFunction` - class myClass {} - class childClass extends myClass {} - let inst = new myClass(); - return inst instanceof childClass; - `.expectToMatchJsResult(); -}); - -test("{} instanceof Object", () => { - util.testExpression`{} instanceof Object`.expectToMatchJsResult(); -}); - -test("function instanceof Object", () => { - util.testExpression`(() => {}) instanceof Object`.expectToMatchJsResult(); -}); - -test("null instanceof Object", () => { - util.testExpression`(null as any) instanceof Object`.expectToMatchJsResult(); -}); - -test("instanceof undefined", () => { - util.testExpression`{} instanceof (undefined as any)`.expectToMatchJsResult(true); -}); - -test("null instanceof Class", () => { - util.testFunction` - class myClass {} - return (null as any) instanceof myClass; - `.expectToMatchJsResult(); -}); - -test("instanceof export", () => { - util.testModule` - export class myClass {} - let inst = new myClass(); - export const result = inst instanceof myClass; - ` - .setExport("result") - .expectToMatchJsResult(); -}); - -test("instanceof Symbol.hasInstance", () => { - util.testFunction` - class myClass { - static [Symbol.hasInstance]() { - return false; - } - } - - const inst = new myClass(); - const isInstanceOld = inst instanceof myClass; - myClass[Symbol.hasInstance] = () => true; - const isInstanceNew = inst instanceof myClass; - return isInstanceOld !== isInstanceNew; - `.expectToMatchJsResult(); -}); - test.each([ { expression: "{}", operator: "===", compareTo: "object", expectResult: true }, { expression: "{}", operator: "!==", compareTo: "object", expectResult: false }, From 14b3fbe269eae3cd0f0f6fada2fce59fe35abc2b Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 10:16:27 +0500 Subject: [PATCH 47/64] Rename lualib tests to builtins --- test/unit/{lualib => builtins}/__snapshots__/console.spec.ts.snap | 0 test/unit/{lualib => builtins}/__snapshots__/math.spec.ts.snap | 0 test/unit/{lualib => builtins}/array.spec.ts | 0 test/unit/{lualib => builtins}/console.spec.ts | 0 test/unit/{lualib => builtins}/global.spec.ts | 0 test/unit/{lualib/lualib.spec.ts => builtins/loading.ts} | 0 test/unit/{lualib => builtins}/map.spec.ts | 0 test/unit/{lualib => builtins}/math.spec.ts | 0 test/unit/{lualib => builtins}/numbers.spec.ts | 0 test/unit/{lualib => builtins}/object.spec.ts | 0 test/unit/{lualib => builtins}/set.spec.ts | 0 test/unit/{lualib => builtins}/string.spec.ts | 0 test/unit/{lualib => builtins}/symbol.spec.ts | 0 test/unit/{lualib => builtins}/weakMap.spec.ts | 0 test/unit/{lualib => builtins}/weakSet.spec.ts | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename test/unit/{lualib => builtins}/__snapshots__/console.spec.ts.snap (100%) rename test/unit/{lualib => builtins}/__snapshots__/math.spec.ts.snap (100%) rename test/unit/{lualib => builtins}/array.spec.ts (100%) rename test/unit/{lualib => builtins}/console.spec.ts (100%) rename test/unit/{lualib => builtins}/global.spec.ts (100%) rename test/unit/{lualib/lualib.spec.ts => builtins/loading.ts} (100%) rename test/unit/{lualib => builtins}/map.spec.ts (100%) rename test/unit/{lualib => builtins}/math.spec.ts (100%) rename test/unit/{lualib => builtins}/numbers.spec.ts (100%) rename test/unit/{lualib => builtins}/object.spec.ts (100%) rename test/unit/{lualib => builtins}/set.spec.ts (100%) rename test/unit/{lualib => builtins}/string.spec.ts (100%) rename test/unit/{lualib => builtins}/symbol.spec.ts (100%) rename test/unit/{lualib => builtins}/weakMap.spec.ts (100%) rename test/unit/{lualib => builtins}/weakSet.spec.ts (100%) diff --git a/test/unit/lualib/__snapshots__/console.spec.ts.snap b/test/unit/builtins/__snapshots__/console.spec.ts.snap similarity index 100% rename from test/unit/lualib/__snapshots__/console.spec.ts.snap rename to test/unit/builtins/__snapshots__/console.spec.ts.snap diff --git a/test/unit/lualib/__snapshots__/math.spec.ts.snap b/test/unit/builtins/__snapshots__/math.spec.ts.snap similarity index 100% rename from test/unit/lualib/__snapshots__/math.spec.ts.snap rename to test/unit/builtins/__snapshots__/math.spec.ts.snap diff --git a/test/unit/lualib/array.spec.ts b/test/unit/builtins/array.spec.ts similarity index 100% rename from test/unit/lualib/array.spec.ts rename to test/unit/builtins/array.spec.ts diff --git a/test/unit/lualib/console.spec.ts b/test/unit/builtins/console.spec.ts similarity index 100% rename from test/unit/lualib/console.spec.ts rename to test/unit/builtins/console.spec.ts diff --git a/test/unit/lualib/global.spec.ts b/test/unit/builtins/global.spec.ts similarity index 100% rename from test/unit/lualib/global.spec.ts rename to test/unit/builtins/global.spec.ts diff --git a/test/unit/lualib/lualib.spec.ts b/test/unit/builtins/loading.ts similarity index 100% rename from test/unit/lualib/lualib.spec.ts rename to test/unit/builtins/loading.ts diff --git a/test/unit/lualib/map.spec.ts b/test/unit/builtins/map.spec.ts similarity index 100% rename from test/unit/lualib/map.spec.ts rename to test/unit/builtins/map.spec.ts diff --git a/test/unit/lualib/math.spec.ts b/test/unit/builtins/math.spec.ts similarity index 100% rename from test/unit/lualib/math.spec.ts rename to test/unit/builtins/math.spec.ts diff --git a/test/unit/lualib/numbers.spec.ts b/test/unit/builtins/numbers.spec.ts similarity index 100% rename from test/unit/lualib/numbers.spec.ts rename to test/unit/builtins/numbers.spec.ts diff --git a/test/unit/lualib/object.spec.ts b/test/unit/builtins/object.spec.ts similarity index 100% rename from test/unit/lualib/object.spec.ts rename to test/unit/builtins/object.spec.ts diff --git a/test/unit/lualib/set.spec.ts b/test/unit/builtins/set.spec.ts similarity index 100% rename from test/unit/lualib/set.spec.ts rename to test/unit/builtins/set.spec.ts diff --git a/test/unit/lualib/string.spec.ts b/test/unit/builtins/string.spec.ts similarity index 100% rename from test/unit/lualib/string.spec.ts rename to test/unit/builtins/string.spec.ts diff --git a/test/unit/lualib/symbol.spec.ts b/test/unit/builtins/symbol.spec.ts similarity index 100% rename from test/unit/lualib/symbol.spec.ts rename to test/unit/builtins/symbol.spec.ts diff --git a/test/unit/lualib/weakMap.spec.ts b/test/unit/builtins/weakMap.spec.ts similarity index 100% rename from test/unit/lualib/weakMap.spec.ts rename to test/unit/builtins/weakMap.spec.ts diff --git a/test/unit/lualib/weakSet.spec.ts b/test/unit/builtins/weakSet.spec.ts similarity index 100% rename from test/unit/lualib/weakSet.spec.ts rename to test/unit/builtins/weakSet.spec.ts From 6fd490a73f406b1027c173910c31817d3bb28d38 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 10:40:44 +0500 Subject: [PATCH 48/64] Move parenthesis removal tests to printer subdirectory --- test/unit/expressions.spec.ts | 63 ------------------ ...rReturn.ts => deadCodeAfterReturn.spec.ts} | 0 test/unit/printer/parenthesis.spec.ts | 64 +++++++++++++++++++ 3 files changed, 64 insertions(+), 63 deletions(-) rename test/unit/printer/{deadCodeAfterReturn.ts => deadCodeAfterReturn.spec.ts} (100%) create mode 100644 test/unit/printer/parenthesis.spec.ts diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index ad97bd88a..65eabf29b 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -225,69 +225,6 @@ test.each([ `.expectNoExecutionError(); }); -test("binary expression with 'as' type assertion wrapped in parenthesis", () => { - expect(util.transpileAndExecute("return 2 * (3 - 2 as number);")).toBe(2); -}); - -test.each([ - "(x as any).foo;", - "(y.x as any).foo;", - "(y['x'] as any).foo;", - "(z() as any).foo;", - "(y.z() as any).foo;", - "(x).foo;", - "(y.x).foo;", - "(y['x']).foo;", - "(z()).foo;", - "(y.z()).foo;", - "(x as unknown as any).foo;", - "(x as any).foo;", - "((x as unknown) as any).foo;", - "((x) as any).foo;", -])("'as' type assertion should strip parenthesis (%p)", expression => { - const code = ` - declare let x: unknown; - declare let y: { x: unknown; z(this: void): unknown; }; - declare function z(this: void): unknown; - ${expression}`; - - const lua = util.transpileString(code, undefined, false); - expect(lua).not.toMatch(/\(.+\)/); -}); - -test.each([ - "(x + 1 as any).foo;", - "(!x as any).foo;", - "(x ** 2 as any).foo;", - "(x < 2 as any).foo;", - "(x in y as any).foo;", - "(x + 1).foo;", - "(!x).foo;", - "(x + 1 as unknown as any).foo;", - "((x + 1 as unknown) as any).foo;", - "(!x as unknown as any).foo;", - "((!x as unknown) as any).foo;", - "(!x as any).foo;", - "((!x) as any).foo;", -])("'as' type assertion should not strip parenthesis (%p)", expression => { - const code = ` - declare let x: number; - declare let y: {}; - ${expression}`; - - const lua = util.transpileString(code, undefined, false); - expect(lua).toMatch(/\(.+\)/); -}); - -test("not operator precedence (%p)", () => { - const code = ` - const a = true; - const b = false; - return !a && b;`; - - expect(util.transpileAndExecute(code)).toBe(false); -}); - // TODO: It probably should be in a different file test.each([ "++x", diff --git a/test/unit/printer/deadCodeAfterReturn.ts b/test/unit/printer/deadCodeAfterReturn.spec.ts similarity index 100% rename from test/unit/printer/deadCodeAfterReturn.ts rename to test/unit/printer/deadCodeAfterReturn.spec.ts diff --git a/test/unit/printer/parenthesis.spec.ts b/test/unit/printer/parenthesis.spec.ts new file mode 100644 index 000000000..ef08a6e3d --- /dev/null +++ b/test/unit/printer/parenthesis.spec.ts @@ -0,0 +1,64 @@ +import * as util from "../../util"; + +test("binary expression with 'as' type assertion wrapped in parenthesis", () => { + expect(util.transpileAndExecute("return 2 * (3 - 2 as number);")).toBe(2); +}); + +test.each([ + "(x as any).foo;", + "(y.x as any).foo;", + "(y['x'] as any).foo;", + "(z() as any).foo;", + "(y.z() as any).foo;", + "(x).foo;", + "(y.x).foo;", + "(y['x']).foo;", + "(z()).foo;", + "(y.z()).foo;", + "(x as unknown as any).foo;", + "(x as any).foo;", + "((x as unknown) as any).foo;", + "((x) as any).foo;", +])("'as' type assertion should strip parenthesis (%p)", expression => { + const code = ` + declare let x: unknown; + declare let y: { x: unknown; z(this: void): unknown; }; + declare function z(this: void): unknown; + ${expression}`; + + const lua = util.transpileString(code, undefined, false); + expect(lua).not.toMatch(/\(.+\)/); +}); + +test.each([ + "(x + 1 as any).foo;", + "(!x as any).foo;", + "(x ** 2 as any).foo;", + "(x < 2 as any).foo;", + "(x in y as any).foo;", + "(x + 1).foo;", + "(!x).foo;", + "(x + 1 as unknown as any).foo;", + "((x + 1 as unknown) as any).foo;", + "(!x as unknown as any).foo;", + "((!x as unknown) as any).foo;", + "(!x as any).foo;", + "((!x) as any).foo;", +])("'as' type assertion should not strip parenthesis (%p)", expression => { + const code = ` + declare let x: number; + declare let y: {}; + ${expression}`; + + const lua = util.transpileString(code, undefined, false); + expect(lua).toMatch(/\(.+\)/); +}); + +test("not operator precedence (%p)", () => { + const code = ` + const a = true; + const b = false; + return !a && b;`; + + expect(util.transpileAndExecute(code)).toBe(false); +}); From f6b40ebf46793966af63b8ec0335ac2f60bb0010 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 10:55:52 +0500 Subject: [PATCH 49/64] Remove some translation tests --- .../__snapshots__/transformation.spec.ts.snap | 288 ------------------ test/translation/transformation/continue.ts | 5 - .../transformation/continueConcurrent.ts | 9 - .../transformation/continueNested.ts | 11 - .../continueNestedConcurrent.ts | 15 - test/translation/transformation/do.ts | 4 - test/translation/transformation/enum.ts | 5 - .../transformation/enumHeterogeneous.ts | 5 - test/translation/transformation/enumString.ts | 5 - test/translation/transformation/for.ts | 1 - test/translation/transformation/forIn.ts | 7 - test/translation/transformation/forOf.ts | 2 - .../transformation/getSetAccessors.ts | 14 - test/translation/transformation/luaTable.ts | 23 -- test/translation/transformation/tryCatch.ts | 5 - .../transformation/tryCatchFinally.ts | 7 - test/translation/transformation/tryFinally.ts | 5 - .../translation/transformation/tupleReturn.ts | 34 --- test/translation/transformation/while.ts | 4 - 19 files changed, 449 deletions(-) delete mode 100644 test/translation/transformation/continue.ts delete mode 100644 test/translation/transformation/continueConcurrent.ts delete mode 100644 test/translation/transformation/continueNested.ts delete mode 100644 test/translation/transformation/continueNestedConcurrent.ts delete mode 100644 test/translation/transformation/do.ts delete mode 100644 test/translation/transformation/enum.ts delete mode 100644 test/translation/transformation/enumHeterogeneous.ts delete mode 100644 test/translation/transformation/enumString.ts delete mode 100644 test/translation/transformation/for.ts delete mode 100644 test/translation/transformation/forIn.ts delete mode 100644 test/translation/transformation/forOf.ts delete mode 100644 test/translation/transformation/getSetAccessors.ts delete mode 100644 test/translation/transformation/luaTable.ts delete mode 100644 test/translation/transformation/tryCatch.ts delete mode 100644 test/translation/transformation/tryCatchFinally.ts delete mode 100644 test/translation/transformation/tryFinally.ts delete mode 100644 test/translation/transformation/tupleReturn.ts delete mode 100644 test/translation/transformation/while.ts diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 7e5ae77f0..600618cc1 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -57,125 +57,6 @@ function ClassB.prototype.____constructor(self) end" `; -exports[`Transformation (continue) 1`] = ` -"do - local i = 0 - while i < 10 do - do - if i < 5 then - goto __continue1 - end - end - ::__continue1:: - i = i + 1 - end -end" -`; - -exports[`Transformation (continueConcurrent) 1`] = ` -"do - local i = 0 - while i < 10 do - do - if i < 5 then - goto __continue1 - end - if i == 7 then - goto __continue1 - end - end - ::__continue1:: - i = i + 1 - end -end" -`; - -exports[`Transformation (continueNested) 1`] = ` -"do - local i = 0 - while i < 5 do - do - if i % 2 == 0 then - goto __continue1 - end - do - local j = 0 - while j < 2 do - do - if j == 1 then - goto __continue3 - end - end - ::__continue3:: - j = j + 1 - end - end - end - ::__continue1:: - i = i + 1 - end -end" -`; - -exports[`Transformation (continueNestedConcurrent) 1`] = ` -"do - local i = 0 - while i < 5 do - do - if i % 2 == 0 then - goto __continue1 - end - do - local j = 0 - while j < 2 do - do - if j == 1 then - goto __continue3 - end - end - ::__continue3:: - j = j + 1 - end - end - if i == 4 then - goto __continue1 - end - end - ::__continue1:: - i = i + 1 - end -end" -`; - -exports[`Transformation (do) 1`] = ` -"local e = 10 -repeat - do - e = e - 1 - end -until not (e > 0)" -`; - -exports[`Transformation (enum) 1`] = ` -"TestEnum = {} -TestEnum.val1 = 0 -TestEnum[0] = \\"val1\\" -TestEnum.val2 = 2 -TestEnum[2] = \\"val2\\" -TestEnum.val3 = 3 -TestEnum[3] = \\"val3\\"" -`; - -exports[`Transformation (enumHeterogeneous) 1`] = ` -"TestEnum = {} -TestEnum.val1 = 0 -TestEnum[0] = \\"val1\\" -TestEnum.val2 = 3 -TestEnum[3] = \\"val2\\" -TestEnum.val3 = \\"baz\\" -TestEnum.baz = \\"val3\\"" -`; - exports[`Transformation (enumMembersOnly) 1`] = ` "val1 = 0 val2 = 2 @@ -184,16 +65,6 @@ val4 = \\"bye\\" local a = val1" `; -exports[`Transformation (enumString) 1`] = ` -"TestEnum = {} -TestEnum.val1 = \\"foo\\" -TestEnum.foo = \\"val1\\" -TestEnum.val2 = \\"bar\\" -TestEnum.bar = \\"val2\\" -TestEnum.val3 = \\"baz\\" -TestEnum.baz = \\"val3\\"" -`; - exports[`Transformation (exportStatement) 1`] = ` "local ____exports = {} local xyz = 4 @@ -220,25 +91,6 @@ end return ____exports" `; -exports[`Transformation (for) 1`] = ` -"do - local i = 1 - while i <= 100 do - i = i + 1 - end -end" -`; - -exports[`Transformation (forIn) 1`] = ` -"for i in pairs({a = 1, b = 2, c = 3, d = 4}) do -end" -`; - -exports[`Transformation (forOf) 1`] = ` -"for ____, i in ipairs({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) do -end" -`; - exports[`Transformation (functionRestArguments) 1`] = ` "function varargsFunction(self, a, ...) local b = ({...}) @@ -246,51 +98,11 @@ exports[`Transformation (functionRestArguments) 1`] = ` end" `; -exports[`Transformation (getSetAccessors) 1`] = ` -"require(\\"lualib_bundle\\"); -MyClass = {} -MyClass.name = \\"MyClass\\" -MyClass.__index = MyClass -MyClass.prototype = {} -MyClass.prototype.____getters = {} -MyClass.prototype.__index = __TS__Index(MyClass.prototype) -MyClass.prototype.____setters = {} -MyClass.prototype.__newindex = __TS__NewIndex(MyClass.prototype) -MyClass.prototype.constructor = MyClass -function MyClass.new(...) - local self = setmetatable({}, MyClass.prototype) - self:____constructor(...) - return self -end -function MyClass.prototype.____constructor(self) -end -function MyClass.prototype.____getters.field(self) - return self._field + 4 -end -function MyClass.prototype.____setters.field(self, v) - self._field = v * 2 -end -local instance = MyClass.new() -instance.field = 4 -local b = instance.field -local c = (4 + instance.field) * 3" -`; - exports[`Transformation (interfaceIndex) 1`] = ` "local a = {} a.abc = \\"def\\"" `; -exports[`Transformation (luaTable) 1`] = ` -"tbl = {} -tbl.value = 5 -local value = tbl.value -local tblLength = #tbl -itbl.value = 5 -local ivalue = itbl.value -local ilength = #tbl" -`; - exports[`Transformation (methodRestArguments) 1`] = ` "MyClass = {} MyClass.name = \\"MyClass\\" @@ -562,107 +374,7 @@ exports[`Transformation (shorthandPropertyAssignment) 1`] = ` f = function(____, x) return ({x = x}) end" `; -exports[`Transformation (tryCatch) 1`] = ` -"do - local ____try, er = pcall( - function() - local a = 42 - end - ) - if not ____try then - local b = \\"fail\\" - end -end" -`; - -exports[`Transformation (tryCatchFinally) 1`] = ` -"do - local ____try, er = pcall( - function() - local a = 42 - end - ) - if not ____try then - local b = \\"fail\\" - end - do - local c = \\"finally\\" - end -end" -`; - -exports[`Transformation (tryFinally) 1`] = ` -"do - pcall( - function() - local a = 42 - end - ) - do - local b = \\"finally\\" - end -end" -`; - -exports[`Transformation (tupleReturn) 1`] = ` -"function tupleReturn(self) - return 0, \\"foobar\\" -end -tupleReturn(_G) -noTupleReturn(_G) -local a, b = tupleReturn(_G) -local c, d = table.unpack( - noTupleReturn(_G) -) -a, b = tupleReturn(_G) -c, d = table.unpack( - noTupleReturn(_G) -) -local e = ({ - tupleReturn(_G) -}) -local f = noTupleReturn(_G) -e = ({ - tupleReturn(_G) -}) -f = noTupleReturn(_G) -foo( - _G, - ({ - tupleReturn(_G) - }) -) -foo( - _G, - noTupleReturn(_G) -) -function tupleReturnFromVar(self) - local r = {1, \\"baz\\"} - return table.unpack(r) -end -function tupleReturnForward(self) - return tupleReturn(_G) -end -function tupleNoForward(self) - return ({ - tupleReturn(_G) - }) -end -function tupleReturnUnpack(self) - return table.unpack( - tupleNoForward(_G) - ) -end" -`; - exports[`Transformation (typeAssert) 1`] = ` "local test1 = 10 local test2 = 10" `; - -exports[`Transformation (while) 1`] = ` -"local d = 10 -while d > 0 do - d = d - 1 -end" -`; diff --git a/test/translation/transformation/continue.ts b/test/translation/transformation/continue.ts deleted file mode 100644 index 64830718d..000000000 --- a/test/translation/transformation/continue.ts +++ /dev/null @@ -1,5 +0,0 @@ -for (let i = 0; i < 10; i++) { - if (i < 5) { - continue; - } -} diff --git a/test/translation/transformation/continueConcurrent.ts b/test/translation/transformation/continueConcurrent.ts deleted file mode 100644 index ef1b20c68..000000000 --- a/test/translation/transformation/continueConcurrent.ts +++ /dev/null @@ -1,9 +0,0 @@ -for (let i = 0; i < 10; i++) { - if (i < 5) { - continue; - } - - if (i === 7) { - continue; - } -} diff --git a/test/translation/transformation/continueNested.ts b/test/translation/transformation/continueNested.ts deleted file mode 100644 index 563b7d0f1..000000000 --- a/test/translation/transformation/continueNested.ts +++ /dev/null @@ -1,11 +0,0 @@ -for (let i = 0; i < 5; i++) { - if (i % 2 === 0) { - continue; - } - - for (let j = 0; j < 2; j++) { - if (j === 1) { - continue; - } - } -} diff --git a/test/translation/transformation/continueNestedConcurrent.ts b/test/translation/transformation/continueNestedConcurrent.ts deleted file mode 100644 index fe0bd0966..000000000 --- a/test/translation/transformation/continueNestedConcurrent.ts +++ /dev/null @@ -1,15 +0,0 @@ -for (let i = 0; i < 5; i++) { - if (i % 2 === 0) { - continue; - } - - for (let j = 0; j < 2; j++) { - if (j === 1) { - continue; - } - } - - if (i === 4) { - continue; - } -} diff --git a/test/translation/transformation/do.ts b/test/translation/transformation/do.ts deleted file mode 100644 index c17e3d1aa..000000000 --- a/test/translation/transformation/do.ts +++ /dev/null @@ -1,4 +0,0 @@ -let e = 10; -do { - e--; -} while (e > 0); diff --git a/test/translation/transformation/enum.ts b/test/translation/transformation/enum.ts deleted file mode 100644 index 3d48fbcfb..000000000 --- a/test/translation/transformation/enum.ts +++ /dev/null @@ -1,5 +0,0 @@ -enum TestEnum { - val1 = 0, - val2 = 2, - val3, -} diff --git a/test/translation/transformation/enumHeterogeneous.ts b/test/translation/transformation/enumHeterogeneous.ts deleted file mode 100644 index d4e69f10b..000000000 --- a/test/translation/transformation/enumHeterogeneous.ts +++ /dev/null @@ -1,5 +0,0 @@ -enum TestEnum { - val1, - val2 = 3, - val3 = "baz", -} diff --git a/test/translation/transformation/enumString.ts b/test/translation/transformation/enumString.ts deleted file mode 100644 index 3d7d29747..000000000 --- a/test/translation/transformation/enumString.ts +++ /dev/null @@ -1,5 +0,0 @@ -enum TestEnum { - val1 = "foo", - val2 = "bar", - val3 = "baz", -} diff --git a/test/translation/transformation/for.ts b/test/translation/transformation/for.ts deleted file mode 100644 index a9dd48620..000000000 --- a/test/translation/transformation/for.ts +++ /dev/null @@ -1 +0,0 @@ -for (let i = 1; i <= 100; i++) {} diff --git a/test/translation/transformation/forIn.ts b/test/translation/transformation/forIn.ts deleted file mode 100644 index 8d1bff356..000000000 --- a/test/translation/transformation/forIn.ts +++ /dev/null @@ -1,7 +0,0 @@ -for (let i in { - a: 1, - b: 2, - c: 3, - d: 4, -}) { -} diff --git a/test/translation/transformation/forOf.ts b/test/translation/transformation/forOf.ts deleted file mode 100644 index c017d5c0c..000000000 --- a/test/translation/transformation/forOf.ts +++ /dev/null @@ -1,2 +0,0 @@ -for (let i of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) { -} diff --git a/test/translation/transformation/getSetAccessors.ts b/test/translation/transformation/getSetAccessors.ts deleted file mode 100644 index d61f592a2..000000000 --- a/test/translation/transformation/getSetAccessors.ts +++ /dev/null @@ -1,14 +0,0 @@ -class MyClass { - private _field: number; - public get field(): number { - return this._field + 4; - } - public set field(v: number) { - this._field = v * 2; - } -} - -let instance = new MyClass(); -instance.field = 4; -const b = instance.field; -const c = (4 + instance.field) * 3; diff --git a/test/translation/transformation/luaTable.ts b/test/translation/transformation/luaTable.ts deleted file mode 100644 index 06bd03cd2..000000000 --- a/test/translation/transformation/luaTable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** @luaTable */ -declare class Table { - public readonly length: number; - public set(key: K, value: V): void; - public get(key: K): V; -} -declare let tbl: Table; -tbl = new Table(); -tbl.set("value", 5); -const value = tbl.get("value"); -const tblLength = tbl.length; - -/** @luaTable */ -declare interface InterfaceTable { - readonly length: number; - set(key: K, value: V): void; - get(key: K): V; -} - -declare const itbl: InterfaceTable; -itbl.set("value", 5); -const ivalue = itbl.get("value"); -const ilength = tbl.length; diff --git a/test/translation/transformation/tryCatch.ts b/test/translation/transformation/tryCatch.ts deleted file mode 100644 index d5e2f1310..000000000 --- a/test/translation/transformation/tryCatch.ts +++ /dev/null @@ -1,5 +0,0 @@ -try { - let a = 42; -} catch (er) { - let b = "fail"; -} diff --git a/test/translation/transformation/tryCatchFinally.ts b/test/translation/transformation/tryCatchFinally.ts deleted file mode 100644 index dfbd57c7f..000000000 --- a/test/translation/transformation/tryCatchFinally.ts +++ /dev/null @@ -1,7 +0,0 @@ -try { - let a = 42; -} catch (er) { - let b = "fail"; -} finally { - let c = "finally"; -} diff --git a/test/translation/transformation/tryFinally.ts b/test/translation/transformation/tryFinally.ts deleted file mode 100644 index d33f4b95a..000000000 --- a/test/translation/transformation/tryFinally.ts +++ /dev/null @@ -1,5 +0,0 @@ -try { - let a = 42; -} finally { - let b = "finally"; -} diff --git a/test/translation/transformation/tupleReturn.ts b/test/translation/transformation/tupleReturn.ts deleted file mode 100644 index 75b7fbf01..000000000 --- a/test/translation/transformation/tupleReturn.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** @tupleReturn */ -function tupleReturn(): [number, string] { - return [0, "foobar"]; -} -declare function noTupleReturn(): [number, string]; -declare function foo(a: [number, string]): void; -tupleReturn(); -noTupleReturn(); -let [a, b] = tupleReturn(); -let [c, d] = noTupleReturn(); -[a, b] = tupleReturn(); -[c, d] = noTupleReturn(); -let e = tupleReturn(); -let f = noTupleReturn(); -e = tupleReturn(); -f = noTupleReturn(); -foo(tupleReturn()); -foo(noTupleReturn()); -/** @tupleReturn */ -function tupleReturnFromVar(): [number, string] { - const r: [number, string] = [1, "baz"]; - return r; -} -/** @tupleReturn */ -function tupleReturnForward(): [number, string] { - return tupleReturn(); -} -function tupleNoForward(): [number, string] { - return tupleReturn(); -} -/** @tupleReturn */ -function tupleReturnUnpack(): [number, string] { - return tupleNoForward(); -} diff --git a/test/translation/transformation/while.ts b/test/translation/transformation/while.ts deleted file mode 100644 index 4014a4add..000000000 --- a/test/translation/transformation/while.ts +++ /dev/null @@ -1,4 +0,0 @@ -let d = 10; -while (d > 0) { - d--; -} From bd9899c2d7a111d41bf1fad7ebde4cd12f77b18c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Sun, 14 Jul 2019 11:12:09 +0500 Subject: [PATCH 50/64] Move around assignments and expressions --- test/unit/assignments.spec.ts | 292 ++++++++++++++++++ test/unit/assignments/assignments.spec.ts | 54 ---- test/unit/expressions.spec.ts | 269 ---------------- test/unit/{ => functions}/functions.spec.ts | 6 +- .../functionExpressionTypeInference.spec.ts | 2 +- .../validation}/functionPermutations.ts | 0 .../invalidFunctionAssignments.spec.ts | 4 +- .../validFunctionAssignments.spec.ts | 18 +- 8 files changed, 307 insertions(+), 338 deletions(-) create mode 100644 test/unit/assignments.spec.ts delete mode 100644 test/unit/assignments/assignments.spec.ts rename test/unit/{ => functions}/functions.spec.ts (98%) rename test/unit/{assignments => functions/validation}/functionExpressionTypeInference.spec.ts (99%) rename test/unit/{assignments => functions/validation}/functionPermutations.ts (100%) rename test/unit/{assignments => functions/validation}/invalidFunctionAssignments.spec.ts (98%) rename test/unit/{assignments => functions/validation}/validFunctionAssignments.spec.ts (99%) diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts new file mode 100644 index 000000000..9bb53c78c --- /dev/null +++ b/test/unit/assignments.spec.ts @@ -0,0 +1,292 @@ +import * as util from "../util"; + +test("Const assignment (%p)", () => { + const lua = util.transpileString(`const foo = true;`); + expect(lua).toBe(`local foo = true`); +}); + +test("Let assignment (%p)", () => { + const lua = util.transpileString(`let foo = true;`); + expect(lua).toBe(`local foo = true`); +}); + +test("Var assignment (%p)", () => { + const lua = util.transpileString(`var foo = true;`); + expect(lua).toBe(`foo = true`); +}); + +test.each(["var myvar;", "let myvar;", "const myvar = null;", "const myvar = undefined;"])( + "Null assignments (%p)", + declaration => { + const result = util.transpileAndExecute(declaration + " return myvar;"); + expect(result).toBe(undefined); + } +); + +test.each(["x = y", "x += y"])("Assignment expressions (%p)", expression => { + util.testFunction` + let x = "x"; + let y = "y"; + return ${expression}; + `.expectToMatchJsResult(); +}); + +test.each(["x = o.p", "x = a[0]", "x = y = o.p", "x = o.p"])("Assignment expressions using temp (%p)", expression => { + util.testFunction` + let x = "x"; + let y = "y"; + let o = {p: "o"}; + let a = ["a"]; + return ${expression}; + `.expectToMatchJsResult(); +}); + +test.each(["o.p = x", "a[0] = x", "o.p = a[0]", "o.p = a[0] = x"])( + "Property assignment expressions (%p)", + expression => { + util.testFunction` + let x = "x"; + let o = {p: "o"}; + let a = ["a"]; + return ${expression}; + `.expectToMatchJsResult(); + } +); + +test.each([ + "x = t()", + "x = tr()", + "[x[1], x[0]] = t()", + "[x[1], x[0]] = tr()", + "x = [y[1], y[0]]", + "[x[0], x[1]] = [y[1], y[0]]", +])("Tuple assignment expressions (%p)", expression => { + util.testFunction` + let x: [string, string] = ["x0", "x1"]; + let y: [string, string] = ["y0", "y1"]; + function t(): [string, string] { return ["t0", "t1"] }; + /** @tupleReturn */ + function tr(): [string, string] { return ["tr0", "tr1"] }; + const r = ${expression}; + return \`\${r[0]},\${r[1]}\` + `.expectToMatchJsResult(); +}); + +test.each([ + "++x", + "x++", + "--x", + "x--", + "x += y", + "x -= y", + "x *= y", + "y /= x", + "y %= x", + "y **= x", + "x |= y", + "x &= y", + "x ^= y", + "x <<= y", + "x >>>= y", +])("Operator assignment statements (%p)", statement => { + util.testFunction` + let x = 3; + let y = 6; + ${statement}; + return { x, y }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++o.p", + "o.p++", + "--o.p", + "o.p--", + "o.p += a[0]", + "o.p -= a[0]", + "o.p *= a[0]", + "a[0] /= o.p", + "a[0] %= o.p", + "a[0] **= o.p", + "o.p |= a[0]", + "o.p &= a[0]", + "o.p ^= a[0]", + "o.p <<= a[0]", + "o.p >>>= a[0]", +])("Operator assignment to simple property statements (%p)", statement => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + ${statement}; + return { o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++o.p.d", + "o.p.d++", + "--o.p.d", + "o.p.d--", + "o.p.d += a[0][0]", + "o.p.d -= a[0][0]", + "o.p.d *= a[0][0]", + "a[0][0] /= o.p.d", + "a[0][0] %= o.p.d", + "a[0][0] **= o.p.d", + "o.p.d |= a[0][0]", + "o.p.d &= a[0][0]", + "o.p.d ^= a[0][0]", + "o.p.d <<= a[0][0]", + "o.p.d >>>= a[0][0]", +])("Operator assignment to deep property statements (%p)", statement => { + util.testFunction` + let o = { p: { d: 3 } }; + let a = [[6,11], [7,13]]; + ${statement}; + return { o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++of().p", + "of().p++", + "--of().p", + "of().p--", + "of().p += af()[i()]", + "of().p -= af()[i()]", + "of().p *= af()[i()]", + "af()[i()] /= of().p", + "af()[i()] %= of().p", + "af()[i()] **= of().p", + "of().p |= af()[i()]", + "of().p &= af()[i()]", + "of().p ^= af()[i()]", + "of().p <<= af()[i()]", + "of().p >>>= af()[i()]", +])("Operator assignment to complex property statements (%p)", statement => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + function of() { return o; } + function af() { return a; } + function i() { return 0; } + ${statement}; + return { o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++of().p.d", + "of().p.d++", + "--of().p.d", + "of().p.d--", + "of().p.d += af()[i()][i()]", + "of().p.d -= af()[i()][i()]", + "of().p.d *= af()[i()][i()]", + "af()[i()][i()] /= of().p.d", + "af()[i()][i()] %= of().p.d", + "af()[i()][i()] **= of().p.d", + "of().p.d |= af()[i()][i()]", + "of().p.d &= af()[i()][i()]", + "of().p.d ^= af()[i()][i()]", + "of().p.d <<= af()[i()][i()]", + "of().p.d >>>= af()[i()][i()]", +])("Operator assignment to complex deep property statements (%p)", statement => { + util.testFunction` + let o = { p: { d: 3 } }; + let a = [[7, 6], [11, 13]]; + function of() { return o; } + function af() { return a; } + let _i = 0; + function i() { return _i++; } + ${statement}; + return { o, a, _i }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++x", + "x++", + "--x", + "x--", + "x += y", + "x -= y", + "x *= y", + "y /= x", + "y %= x", + "y **= x", + "x |= y", + "x &= y", + "x ^= y", + "x <<= y", + "x >>>= y", + "x + (y += 7)", + "x + (y += 7)", + "x++ + (y += 7)", +])("Operator assignment expressions (%p)", expression => { + util.testFunction` + let x = 3; + let y = 6; + const r = ${expression}; + return { r, x, y }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++o.p", + "o.p++", + "--o.p", + "o.p--", + "o.p += a[0]", + "o.p -= a[0]", + "o.p *= a[0]", + "a[0] /= o.p", + "a[0] %= o.p", + "a[0] **= o.p", + "o.p |= a[0]", + "o.p &= a[0]", + "o.p ^= a[0]", + "o.p <<= a[0]", + "o.p >>>= a[0]", + "o.p + (a[0] += 7)", + "o.p += (a[0] += 7)", + "o.p++ + (a[0] += 7)", +])("Operator assignment to simple property expressions (%p)", expression => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + const r = ${expression}; + return { r, o, a }; + `.expectToMatchJsResult(); +}); + +test.each([ + "++of().p", + "of().p++", + "--of().p", + "of().p--", + "of().p += af()[i()]", + "of().p -= af()[i()]", + "of().p *= af()[i()]", + "af()[i()] /= of().p", + "af()[i()] %= of().p", + "af()[i()] **= of().p", + "of().p |= af()[i()]", + "of().p &= af()[i()]", + "of().p ^= af()[i()]", + "of().p <<= af()[i()]", + "of().p >>>= af()[i()]", + "of().p + (af()[i()] += 7)", + "of().p += (af()[i()] += 7)", + "of().p++ + (af()[i()] += 7)", +])("Operator assignment to complex property expressions (%p)", expression => { + util.testFunction` + let o = { p: 3 }; + let a = [6]; + function of() { return o; } + function af() { return a; } + function i() { return 0; } + const r = ${expression}; + return { r, o, a }; + `.expectToMatchJsResult(); +}); diff --git a/test/unit/assignments/assignments.spec.ts b/test/unit/assignments/assignments.spec.ts deleted file mode 100644 index 955fda458..000000000 --- a/test/unit/assignments/assignments.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as TSTLErrors from "../../../src/TSTLErrors"; -import * as util from "../../util"; - -test("Const assignment (%p)", () => { - const lua = util.transpileString(`const foo = true;`); - expect(lua).toBe(`local foo = true`); -}); - -test("Let assignment (%p)", () => { - const lua = util.transpileString(`let foo = true;`); - expect(lua).toBe(`local foo = true`); -}); - -test("Var assignment (%p)", () => { - const lua = util.transpileString(`var foo = true;`); - expect(lua).toBe(`foo = true`); -}); - -test.each(["var myvar;", "let myvar;", "const myvar = null;", "const myvar = undefined;"])( - "Null assignments (%p)", - declaration => { - const result = util.transpileAndExecute(declaration + " return myvar;"); - expect(result).toBe(undefined); - } -); - -test.each([ - { input: ["a", "b"], values: ["e", "f"] }, - { input: ["a", "b"], values: ["e", "f", "g"] }, - { input: ["a", "b", "c"], values: ["e", "f", "g"] }, -])("Binding pattern assignment (%p)", ({ input, values }) => { - const pattern = input.join(","); - const initializer = values.map(v => `"${v}"`).join(","); - - const tsCode = `const [${pattern}] = [${initializer}]; return [${pattern}].join("-");`; - const result = util.transpileAndExecute(tsCode); - - expect(result).toBe(values.slice(0, input.length).join("-")); -}); - -test("Ellipsis binding pattern", () => { - expect(() => util.transpileString("let [a, b, ...c] = [1,2,3];")).toThrowExactError( - TSTLErrors.ForbiddenEllipsisDestruction(util.nodeStub) - ); -}); -test("String table access", () => { - const code = ` - const dict : {[key:string]:any} = {}; - dict["a b"] = 3; - return dict["a b"]; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(3); -}); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 65eabf29b..dd598e05b 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -145,55 +145,6 @@ test.each(["i++", "i--", "++i", "--i"])("Incrementor value (%p)", expression => `.expectToMatchJsResult(); }); -test.each(["x = y", "x += y"])("Assignment expressions (%p)", expression => { - util.testFunction` - let x = "x"; - let y = "y"; - return ${expression}; - `.expectToMatchJsResult(); -}); - -test.each(["x = o.p", "x = a[0]", "x = y = o.p", "x = o.p"])("Assignment expressions using temp (%p)", expression => { - util.testFunction` - let x = "x"; - let y = "y"; - let o = {p: "o"}; - let a = ["a"]; - return ${expression}; - `.expectToMatchJsResult(); -}); - -test.each(["o.p = x", "a[0] = x", "o.p = a[0]", "o.p = a[0] = x"])( - "Property assignment expressions (%p)", - expression => { - util.testFunction` - let x = "x"; - let o = {p: "o"}; - let a = ["a"]; - return ${expression}; - `.expectToMatchJsResult(); - } -); - -test.each([ - "x = t()", - "x = tr()", - "[x[1], x[0]] = t()", - "[x[1], x[0]] = tr()", - "x = [y[1], y[0]]", - "[x[0], x[1]] = [y[1], y[0]]", -])("Tuple assignment expressions (%p)", expression => { - util.testFunction` - let x: [string, string] = ["x0", "x1"]; - let y: [string, string] = ["y0", "y1"]; - function t(): [string, string] { return ["t0", "t1"] }; - /** @tupleReturn */ - function tr(): [string, string] { return ["tr0", "tr1"] }; - const r = ${expression}; - return \`\${r[0]},\${r[1]}\` - `.expectToMatchJsResult(); -}); - test("Non-null expression", () => { util.testFunction` function abc(): number | undefined { return 3; } @@ -224,223 +175,3 @@ test.each([ ${input}; `.expectNoExecutionError(); }); - -// TODO: It probably should be in a different file -test.each([ - "++x", - "x++", - "--x", - "x--", - "x += y", - "x -= y", - "x *= y", - "y /= x", - "y %= x", - "y **= x", - "x |= y", - "x &= y", - "x ^= y", - "x <<= y", - "x >>>= y", -])("Operator assignment statements (%p)", statement => { - util.testFunction` - let x = 3; - let y = 6; - ${statement}; - return { x, y }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++o.p", - "o.p++", - "--o.p", - "o.p--", - "o.p += a[0]", - "o.p -= a[0]", - "o.p *= a[0]", - "a[0] /= o.p", - "a[0] %= o.p", - "a[0] **= o.p", - "o.p |= a[0]", - "o.p &= a[0]", - "o.p ^= a[0]", - "o.p <<= a[0]", - "o.p >>>= a[0]", -])("Operator assignment to simple property statements (%p)", statement => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - ${statement}; - return { o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++o.p.d", - "o.p.d++", - "--o.p.d", - "o.p.d--", - "o.p.d += a[0][0]", - "o.p.d -= a[0][0]", - "o.p.d *= a[0][0]", - "a[0][0] /= o.p.d", - "a[0][0] %= o.p.d", - "a[0][0] **= o.p.d", - "o.p.d |= a[0][0]", - "o.p.d &= a[0][0]", - "o.p.d ^= a[0][0]", - "o.p.d <<= a[0][0]", - "o.p.d >>>= a[0][0]", -])("Operator assignment to deep property statements (%p)", statement => { - util.testFunction` - let o = { p: { d: 3 } }; - let a = [[6,11], [7,13]]; - ${statement}; - return { o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++of().p", - "of().p++", - "--of().p", - "of().p--", - "of().p += af()[i()]", - "of().p -= af()[i()]", - "of().p *= af()[i()]", - "af()[i()] /= of().p", - "af()[i()] %= of().p", - "af()[i()] **= of().p", - "of().p |= af()[i()]", - "of().p &= af()[i()]", - "of().p ^= af()[i()]", - "of().p <<= af()[i()]", - "of().p >>>= af()[i()]", -])("Operator assignment to complex property statements (%p)", statement => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - function of() { return o; } - function af() { return a; } - function i() { return 0; } - ${statement}; - return { o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++of().p.d", - "of().p.d++", - "--of().p.d", - "of().p.d--", - "of().p.d += af()[i()][i()]", - "of().p.d -= af()[i()][i()]", - "of().p.d *= af()[i()][i()]", - "af()[i()][i()] /= of().p.d", - "af()[i()][i()] %= of().p.d", - "af()[i()][i()] **= of().p.d", - "of().p.d |= af()[i()][i()]", - "of().p.d &= af()[i()][i()]", - "of().p.d ^= af()[i()][i()]", - "of().p.d <<= af()[i()][i()]", - "of().p.d >>>= af()[i()][i()]", -])("Operator assignment to complex deep property statements (%p)", statement => { - util.testFunction` - let o = { p: { d: 3 } }; - let a = [[7, 6], [11, 13]]; - function of() { return o; } - function af() { return a; } - let _i = 0; - function i() { return _i++; } - ${statement}; - return { o, a, _i }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++x", - "x++", - "--x", - "x--", - "x += y", - "x -= y", - "x *= y", - "y /= x", - "y %= x", - "y **= x", - "x |= y", - "x &= y", - "x ^= y", - "x <<= y", - "x >>>= y", - "x + (y += 7)", - "x + (y += 7)", - "x++ + (y += 7)", -])("Operator assignment expressions (%p)", expression => { - util.testFunction` - let x = 3; - let y = 6; - const r = ${expression}; - return { r, x, y }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++o.p", - "o.p++", - "--o.p", - "o.p--", - "o.p += a[0]", - "o.p -= a[0]", - "o.p *= a[0]", - "a[0] /= o.p", - "a[0] %= o.p", - "a[0] **= o.p", - "o.p |= a[0]", - "o.p &= a[0]", - "o.p ^= a[0]", - "o.p <<= a[0]", - "o.p >>>= a[0]", - "o.p + (a[0] += 7)", - "o.p += (a[0] += 7)", - "o.p++ + (a[0] += 7)", -])("Operator assignment to simple property expressions (%p)", expression => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - const r = ${expression}; - return { r, o, a }; - `.expectToMatchJsResult(); -}); - -test.each([ - "++of().p", - "of().p++", - "--of().p", - "of().p--", - "of().p += af()[i()]", - "of().p -= af()[i()]", - "of().p *= af()[i()]", - "af()[i()] /= of().p", - "af()[i()] %= of().p", - "af()[i()] **= of().p", - "of().p |= af()[i()]", - "of().p &= af()[i()]", - "of().p ^= af()[i()]", - "of().p <<= af()[i()]", - "of().p >>>= af()[i()]", - "of().p + (af()[i()] += 7)", - "of().p += (af()[i()] += 7)", - "of().p++ + (af()[i()] += 7)", -])("Operator assignment to complex property expressions (%p)", expression => { - util.testFunction` - let o = { p: 3 }; - let a = [6]; - function of() { return o; } - function af() { return a; } - function i() { return 0; } - const r = ${expression}; - return { r, o, a }; - `.expectToMatchJsResult(); -}); diff --git a/test/unit/functions.spec.ts b/test/unit/functions/functions.spec.ts similarity index 98% rename from test/unit/functions.spec.ts rename to test/unit/functions/functions.spec.ts index d3cb3f8a7..832000184 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -1,6 +1,6 @@ import * as ts from "typescript"; -import * as TSTLErrors from "../../src/TSTLErrors"; -import * as util from "../util"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; test("Arrow Function Expression", () => { util.testFunction` @@ -9,7 +9,7 @@ test("Arrow Function Expression", () => { `.expectToMatchJsResult(); }); -test("Returning arrow function from arrow function (%p)", () => { +test("Returning arrow function from arrow function", () => { util.testFunction` const add = (x: number) => (y: number) => x + y; return add(1)(2); diff --git a/test/unit/assignments/functionExpressionTypeInference.spec.ts b/test/unit/functions/validation/functionExpressionTypeInference.spec.ts similarity index 99% rename from test/unit/assignments/functionExpressionTypeInference.spec.ts rename to test/unit/functions/validation/functionExpressionTypeInference.spec.ts index aefc24427..767fe0b30 100644 --- a/test/unit/assignments/functionExpressionTypeInference.spec.ts +++ b/test/unit/functions/validation/functionExpressionTypeInference.spec.ts @@ -1,4 +1,4 @@ -import * as util from "../../util"; +import * as util from "../../../util"; test.each(["noSelf", "noSelfInFile"])("noSelf function method argument (%p)", noSelfTag => { const header = ` diff --git a/test/unit/assignments/functionPermutations.ts b/test/unit/functions/validation/functionPermutations.ts similarity index 100% rename from test/unit/assignments/functionPermutations.ts rename to test/unit/functions/validation/functionPermutations.ts diff --git a/test/unit/assignments/invalidFunctionAssignments.spec.ts b/test/unit/functions/validation/invalidFunctionAssignments.spec.ts similarity index 98% rename from test/unit/assignments/invalidFunctionAssignments.spec.ts rename to test/unit/functions/validation/invalidFunctionAssignments.spec.ts index 97002fd7d..06050ac5e 100644 --- a/test/unit/assignments/invalidFunctionAssignments.spec.ts +++ b/test/unit/functions/validation/invalidFunctionAssignments.spec.ts @@ -1,5 +1,5 @@ -import * as TSTLErrors from "../../../src/TSTLErrors"; -import * as util from "../../util"; +import * as TSTLErrors from "../../../../src/TSTLErrors"; +import * as util from "../../../util"; import { invalidTestFunctionAssignments, invalidTestFunctionCasts } from "./functionPermutations"; test.each(invalidTestFunctionAssignments)( diff --git a/test/unit/assignments/validFunctionAssignments.spec.ts b/test/unit/functions/validation/validFunctionAssignments.spec.ts similarity index 99% rename from test/unit/assignments/validFunctionAssignments.spec.ts rename to test/unit/functions/validation/validFunctionAssignments.spec.ts index be553b956..be1130ba6 100644 --- a/test/unit/assignments/validFunctionAssignments.spec.ts +++ b/test/unit/functions/validation/validFunctionAssignments.spec.ts @@ -1,17 +1,17 @@ -import * as util from "../../util"; +import * as util from "../../../util"; import { - validTestFunctionAssignments, - validTestFunctionCasts, - selfTestFunctions, + anonTestFunctionExpressions, + anonTestFunctionType, + noSelfTestFunctionExpressions, noSelfTestFunctions, - TestFunctionAssignment, + noSelfTestFunctionType, selfTestFunctionExpressions, - noSelfTestFunctionExpressions, + selfTestFunctions, selfTestFunctionType, - anonTestFunctionType, - noSelfTestFunctionType, - anonTestFunctionExpressions, TestFunction, + TestFunctionAssignment, + validTestFunctionAssignments, + validTestFunctionCasts, } from "./functionPermutations"; test.each(validTestFunctionAssignments)("Valid function variable declaration (%p)", (testFunction, functionType) => { From fa614c78dc85ff3a9e93c19ade11b53071c69e4d Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 22 Jul 2019 05:44:57 +0500 Subject: [PATCH 51/64] Fix loading tests --- src/LuaTransformer.ts | 18 ++++---- test/unit/builtins/loading.spec.ts | 53 ++++++++++++++++++++++++ test/unit/builtins/loading.ts | 66 ------------------------------ 3 files changed, 60 insertions(+), 77 deletions(-) create mode 100644 test/unit/builtins/loading.spec.ts delete mode 100644 test/unit/builtins/loading.ts diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 1e0686e41..31dd2aa71 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -4132,7 +4132,7 @@ export class LuaTransformer { return tstl.createNumericLiteral(Math[name], identifier); default: - throw TSTLErrors.UnsupportedProperty("math", name, identifier); + throw TSTLErrors.UnsupportedProperty("Math", name, identifier); } } @@ -4205,7 +4205,7 @@ export class LuaTransformer { } default: - throw TSTLErrors.UnsupportedProperty("math", expressionName, expression); + throw TSTLErrors.UnsupportedProperty("Math", expressionName, expression); } } @@ -4452,11 +4452,7 @@ export class LuaTransformer { tstl.createStringLiteral("char") ); default: - throw TSTLErrors.UnsupportedForTarget( - `string property ${identifierString}`, - this.luaTarget, - identifier - ); + throw TSTLErrors.UnsupportedProperty("String", identifierString, identifier); } } @@ -4478,7 +4474,7 @@ export class LuaTransformer { case "values": return this.transformLuaLibFunction(LuaLibFeature.ObjectValues, expression, ...parameters); default: - throw TSTLErrors.UnsupportedForTarget(`object property ${methodName}`, this.luaTarget, expression); + throw TSTLErrors.UnsupportedProperty("Object", methodName, expression); } } @@ -4549,7 +4545,7 @@ export class LuaTransformer { ); return tstl.createCallExpression(tstl.createIdentifier("print"), [debugTracebackCall]); default: - throw TSTLErrors.UnsupportedForTarget(`console property ${methodName}`, this.luaTarget, expression); + throw TSTLErrors.UnsupportedProperty("console", methodName, expression); } } @@ -4572,7 +4568,7 @@ export class LuaTransformer { const functionIdentifier = tstl.createIdentifier(`__TS__SymbolRegistry${upperMethodName}`); return tstl.createCallExpression(functionIdentifier, parameters, expression); default: - throw TSTLErrors.UnsupportedForTarget(`symbol property ${methodName}`, this.luaTarget, expression); + throw TSTLErrors.UnsupportedProperty("Symbol", methodName, expression); } } @@ -4588,7 +4584,7 @@ export class LuaTransformer { case "isFinite": return this.transformLuaLibFunction(LuaLibFeature.NumberIsFinite, expression, ...parameters); default: - throw TSTLErrors.UnsupportedForTarget(`number property ${methodName}`, this.luaTarget, expression); + throw TSTLErrors.UnsupportedProperty("Number", methodName, expression); } } diff --git a/test/unit/builtins/loading.spec.ts b/test/unit/builtins/loading.spec.ts new file mode 100644 index 000000000..d08f846e9 --- /dev/null +++ b/test/unit/builtins/loading.spec.ts @@ -0,0 +1,53 @@ +import * as tstl from "../../../src"; +import * as TSTLErrors from "../../../src/TSTLErrors"; +import * as util from "../../util"; + +describe("luaLibImport", () => { + test("inline", () => { + util.testExpression`[0].push(1)` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Inline }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain(`require("lualib_bundle")`)) + .expectToMatchJsResult(); + }); + + test("require", () => { + util.testExpression`[0].push(1)` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)) + .expectToMatchJsResult(); + }); + + test("always", () => { + util.testModule`` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)) + .expectToEqual(undefined); + }); +}); + +test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( + "should not include lualib without code (%p)", + luaLibImport => { + util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); + } +); + +test("lualib should not include tstl header", () => { + util.testExpression`[0].push(1)`.tap(builder => + expect(builder.getMainLuaCodeChunk()).not.toContain("Generated with") + ); +}); + +describe("Unknown builtin property", () => { + test("access", () => { + util.testExpression`Math.unknownProperty` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("Math", "unknownProperty", util.nodeStub)); + }); + + test("function call", () => { + util.testExpression`[].unknownFunction()` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("array", "unknownFunction", util.nodeStub)); + }); +}); diff --git a/test/unit/builtins/loading.ts b/test/unit/builtins/loading.ts deleted file mode 100644 index 3232308e3..000000000 --- a/test/unit/builtins/loading.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as tstl from "../../../src"; -import * as TSTLErrors from "../../../src/TSTLErrors"; -import * as util from "../../util"; - -describe("luaLibImport", () => { - test("require", () => { - util.testExpression`b instanceof c` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require }) - .disableSemanticCheck() - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); - }); - - test("always", () => { - util.testModule`` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always }) - .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain(`require("lualib_bundle")`)); - }); - - test("inline", () => { - util.testExpression`new Map().size` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Inline }) - .expectToMatchJsResult(); - }); -}); - -test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( - "LuaLib no uses? No code (%p)", - luaLibImport => { - util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); - } -); - -test("lualibs should not include tstl header", () => { - util.testModule` - const arr = [1, 2, 3]; - arr.push(4); - `.tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("Generated with")); -}); - -test("Incompatible fromCodePoint expression error", () => { - util.testExpression`String.fromCodePoint(123)` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError( - TSTLErrors.UnsupportedForTarget("string property fromCodePoint", tstl.LuaTarget.Lua53, util.nodeStub) - ); -}); - -test("Unknown string expression error", () => { - util.testExpression`String.abcd()` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError( - TSTLErrors.UnsupportedForTarget("string property abcd", tstl.LuaTarget.Lua53, util.nodeStub) - ); -}); - -test("Unsupported array function error", () => { - util.testFunction`[].unknownFunction()` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("array", "unknownFunction", util.nodeStub)); -}); - -test("Unsupported math property error", () => { - util.testExpression`Math.unknownProperty` - .disableSemanticCheck() - .expectToHaveDiagnosticOfError(TSTLErrors.UnsupportedProperty("math", "unknownProperty", util.nodeStub)); -}); From 302a4f4a437f9773aafb6a2dea2cec0a1cbcff1a Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 24 Jul 2019 08:27:34 +0500 Subject: [PATCH 52/64] enum.spec.ts --- test/unit/enum.spec.ts | 238 +++++++++++++++++------------------------ 1 file changed, 101 insertions(+), 137 deletions(-) diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index 383d9708f..976990e35 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -1,174 +1,138 @@ import * as TSTLErrors from "../../src/TSTLErrors"; import * as util from "../util"; -test("Declare const enum", () => { - const testCode = ` - declare const enum TestEnum { - MEMBER_ONE = "test", - MEMBER_TWO = "test2" - } - - const valueOne = TestEnum.MEMBER_ONE; - `; - - expect(util.transpileString(testCode)).toBe(`local valueOne = "test"`); -}); - -test("Const enum", () => { - const testCode = ` - const enum TestEnum { - MEMBER_ONE = "test", - MEMBER_TWO = "test2" +// TODO: string.toString() +const serializeAndReturnTestEnum = () => ` + const mappedTestEnum: any = {}; + for (const key in TestEnum) { + mappedTestEnum[(key as any).toString()] = TestEnum[key]; + } + return mappedTestEnum; +`; + +test("without initializer", () => { + util.testFunction` + enum TestEnum { + A, + B, + C, } - const valueOne = TestEnum.MEMBER_TWO; - `; - - expect(util.transpileString(testCode)).toBe(`local valueOne = "test2"`); + ${serializeAndReturnTestEnum} + `.expectToMatchJsResult(); }); -test("Const enum without initializer", () => { - const testCode = ` - const enum TestEnum { - MEMBER_ONE, - MEMBER_TWO +test("expression initializer", () => { + util.testFunction` + const value = 6; + enum TestEnum { + A, + B = value, } - const valueOne = TestEnum.MEMBER_TWO; - `; - - expect(util.transpileString(testCode)).toBe(`local valueOne = 1`); + ${serializeAndReturnTestEnum} + `.expectToMatchJsResult(); }); -test("Const enum without initializer in some values", () => { - const testCode = ` +test("initializer inference", () => { + util.testFunction` const enum TestEnum { - MEMBER_ONE = 3, - MEMBER_TWO, - MEMBER_THREE = 5 + A = 3, + B, + C = 5, } - const valueOne = TestEnum.MEMBER_TWO; - `; - - expect(util.transpileString(testCode)).toBe(`local valueOne = 4`); + return TestEnum.B; + `.expectToMatchJsResult(); }); -test("Invalid heterogeneous enum", () => { - expect(() => { - util.transpileString(` - enum TestEnum { - a, - b = "ok", - c, - } - `); - }).toThrowExactError(TSTLErrors.HeterogeneousEnum(util.nodeStub)); -}); - -test("String literal name in enum", () => { - const code = ` +test("initializer referencing other member", () => { + util.testFunction` enum TestEnum { - ["name"] = "foo" + A, + B = A, + C, } - return TestEnum["name"]; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("foo"); -}); -test("Enum identifier value internal", () => { - const result = util.transpileAndExecute( - `enum testEnum { - abc, - def, - ghi = def, - jkl, - } - return \`\${testEnum.abc},\${testEnum.def},\${testEnum.ghi},\${testEnum.jkl}\`;` - ); - - expect(result).toBe("0,1,1,2"); + ${serializeAndReturnTestEnum} + `.expectToMatchJsResult(); }); -test("Enum identifier value internal recursive", () => { - const result = util.transpileAndExecute( - `enum testEnum { - abc, - def, - ghi = def, - jkl = ghi, +test("initializer referencing other member with initializer referencing other member", () => { + util.testFunction` + enum TestEnum { + A, + B = A, + C = B, } - return \`\${testEnum.abc},\${testEnum.def},\${testEnum.ghi},\${testEnum.jkl}\`;` - ); - expect(result).toBe("0,1,1,1"); + ${serializeAndReturnTestEnum} + `.expectToMatchJsResult(); }); -test("Enum identifier value external", () => { - const result = util.transpileAndExecute( - `const ext = 6; - enum testEnum { - abc, - def, - ghi = ext, +test.skip("string literal member name", () => { + util.testFunction` + enum TestEnum { + ["A"] = "foo", } - return \`\${testEnum.abc},\${testEnum.def},\${testEnum.ghi}\`;` - ); - expect(result).toBe("0,1,6"); + ${serializeAndReturnTestEnum} + `.expectToMatchJsResult(); }); -test("Enum reverse mapping", () => { - const result = util.transpileAndExecute( - `enum testEnum { - abc, - def, - ghi +test("invalid heterogeneous enum", () => { + util.testFunction` + enum TestEnum { + A, + B = "B", + C, } - return testEnum[testEnum.abc] + testEnum[testEnum.ghi]` - ); - - expect(result).toBe("abcghi"); + ` + .disableSemanticCheck() + .expectToHaveDiagnosticOfError(TSTLErrors.HeterogeneousEnum(util.nodeStub)); }); -test("Const enum index", () => { - const result = util.transpileAndExecute( - `const enum testEnum { - abc, - def, - ghi - } - return testEnum["def"];` - ); +describe("const enum", () => { + const expectToBeConst: util.TapCallback = builder => + expect(builder.getMainLuaCodeChunk()).not.toContain("TestEnum"); - expect(result).toBe(1); -}); - -test("Const enum index identifier value", () => { - const result = util.transpileAndExecute( - `const enum testEnum { - abc, - def = 4, - ghi, - jkl = ghi - } - return testEnum["jkl"];` - ); + test.each(["", "declare"])("%s without initializer", () => { + util.testFunction` + const enum TestEnum { + A, + B, + } - expect(result).toBe(5); -}); + return TestEnum.A; + ` + .tap(expectToBeConst) + .expectToMatchJsResult(); + }); + + test("with initializer", () => { + util.testFunction` + const enum TestEnum { + A = "ONE", + B = "TWO", + } -test("Const enum index identifier chain", () => { - const result = util.transpileAndExecute( - `const enum testEnum { - abc = 3, - def, - ghi = def, - jkl = ghi, - } - return testEnum["ghi"];` - ); + return TestEnum.A; + ` + .tap(expectToBeConst) + .expectToMatchJsResult(); + }); + + test("access with string literal", () => { + util.testFunction` + const enum TestEnum { + A, + B, + C, + } - expect(result).toBe(4); + return TestEnum["C"]; + ` + .tap(expectToBeConst) + .expectToMatchJsResult(); + }); }); From 698a26df10146bd3c54e4439fab8732572a17231 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 24 Jul 2019 08:27:44 +0500 Subject: [PATCH 53/64] globalThis.spec.ts --- test/unit/builtins/global.spec.ts | 38 --------------------------- test/unit/builtins/globalThis.spec.ts | 33 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 38 deletions(-) delete mode 100644 test/unit/builtins/global.spec.ts create mode 100644 test/unit/builtins/globalThis.spec.ts diff --git a/test/unit/builtins/global.spec.ts b/test/unit/builtins/global.spec.ts deleted file mode 100644 index 935f0ea53..000000000 --- a/test/unit/builtins/global.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as util from "../../util"; - -describe("globalThis", () => { - // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/660 - test.skip("equals _G", () => { - util.testExpression`_G` - .setTsHeader("declare global { const _G: typeof globalThis }") - .debug() - .expectToEqual(true); - }); - - test("registers global symbol", () => { - util.testFunction` - globalThis.foo = "bar"; - return foo; - ` - .setTsHeader("declare global { var foo: string }") - .expectToEqual("bar"); - }); - - test("uses global symbol", () => { - util.testFunction` - foo = "bar"; - return globalThis.foo; - ` - .setTsHeader("declare global { var foo: string }") - .expectToEqual("bar"); - }); - - test("function call", () => { - util.testFunction` - foo = () => "bar"; - return globalThis.foo(); - ` - .setTsHeader("declare global { var foo: () => string }") - .expectToEqual("bar"); - }); -}); diff --git a/test/unit/builtins/globalThis.spec.ts b/test/unit/builtins/globalThis.spec.ts new file mode 100644 index 000000000..0735fda56 --- /dev/null +++ b/test/unit/builtins/globalThis.spec.ts @@ -0,0 +1,33 @@ +import * as util from "../../util"; + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/660 +test("equals _G", () => { + util.testExpression`globalThis === _G`.setTsHeader("declare const _G: typeof globalThis;").expectToEqual(true); +}); + +test("registers global symbol", () => { + util.testFunction` + globalThis.foo = "bar"; + return foo; + ` + .setTsHeader("declare global { var foo: string }") + .expectToEqual("bar"); +}); + +test("uses global symbol", () => { + util.testFunction` + foo = "bar"; + return globalThis.foo; + ` + .setTsHeader("declare global { var foo: string }") + .expectToEqual("bar"); +}); + +test("function call", () => { + util.testFunction` + foo = () => "bar"; + return globalThis.foo(); + ` + .setTsHeader("declare global { var foo: () => string }") + .expectToEqual("bar"); +}); From d820fd1b1756a1ef026ffa7530acc841400d1a56 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 25 Jul 2019 03:20:15 +0500 Subject: [PATCH 54/64] Rearrange some enum tests --- .../__snapshots__/transformation.spec.ts.snap | 22 ---- .../transformation/enumMembersOnly.ts | 9 -- .../modulesNamespaceExportEnum.ts | 6 - .../decorators/compileMembersOnly.spec.ts | 40 ++++-- test/unit/enum.spec.ts | 117 ++++++++++-------- test/util.ts | 5 +- 6 files changed, 97 insertions(+), 102 deletions(-) delete mode 100644 test/translation/transformation/enumMembersOnly.ts delete mode 100644 test/translation/transformation/modulesNamespaceExportEnum.ts diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 600618cc1..96e809dfd 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -57,14 +57,6 @@ function ClassB.prototype.____constructor(self) end" `; -exports[`Transformation (enumMembersOnly) 1`] = ` -"val1 = 0 -val2 = 2 -val3 = 3 -val4 = \\"bye\\" -local a = val1" -`; - exports[`Transformation (exportStatement) 1`] = ` "local ____exports = {} local xyz = 4 @@ -240,20 +232,6 @@ ____exports.TestSpace = {} return ____exports" `; -exports[`Transformation (modulesNamespaceExportEnum) 1`] = ` -"local ____exports = {} -____exports.test = {} -local test = ____exports.test -do - test.TestEnum = {} - test.TestEnum.foo = \\"foo\\" - test.TestEnum.foo = \\"foo\\" - test.TestEnum.bar = \\"bar\\" - test.TestEnum.bar = \\"bar\\" -end -return ____exports" -`; - exports[`Transformation (modulesNamespaceNestedWithMemberExport) 1`] = ` "local ____exports = {} ____exports.TestSpace = {} diff --git a/test/translation/transformation/enumMembersOnly.ts b/test/translation/transformation/enumMembersOnly.ts deleted file mode 100644 index 9d384dc6c..000000000 --- a/test/translation/transformation/enumMembersOnly.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** @compileMembersOnly */ -enum TestEnum { - val1 = 0, - val2 = 2, - val3, - val4 = "bye", -} - -const a = TestEnum.val1; diff --git a/test/translation/transformation/modulesNamespaceExportEnum.ts b/test/translation/transformation/modulesNamespaceExportEnum.ts deleted file mode 100644 index 0c85b6872..000000000 --- a/test/translation/transformation/modulesNamespaceExportEnum.ts +++ /dev/null @@ -1,6 +0,0 @@ -export namespace test { - export enum TestEnum { - foo = "foo", - bar = "bar", - } -} diff --git a/test/unit/decorators/compileMembersOnly.spec.ts b/test/unit/decorators/compileMembersOnly.spec.ts index ba551fabc..4e20b57b1 100644 --- a/test/unit/decorators/compileMembersOnly.spec.ts +++ b/test/unit/decorators/compileMembersOnly.spec.ts @@ -1,18 +1,34 @@ import * as util from "../../util"; -test("compileMembersOnly in namespace", () => { - const header = ` - namespace wifi { +test("@compileMembersOnly", () => { + util.testFunction` + /** @compileMembersOnly */ + enum TestEnum { + A = 0, + B = 2, + C, + D = "D", + } + + return { A: TestEnum.A, B: TestEnum.B, C: TestEnum.C, D: TestEnum.D }; + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("TestEnum")) + .expectToMatchJsResult(); +}); + +test("@compileMembersOnly in a namespace", () => { + util.testModule` + namespace Test { /** @compileMembersOnly */ - export enum WifiMode { - NULLMODE = 0, - STATION = 1, - SOFTAP = 2 + export enum TestEnum { + A = "A", + B = "B", } - }`; - const code = ` - return wifi.WifiMode.STATION; - `; + } - expect(util.transpileAndExecute(code, undefined, undefined, header)).toBe(1); + export const A = Test.TestEnum.A; + ` + .setExport("A") + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain("Test.A")) + .expectToEqual("A"); }); diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index 976990e35..c1b7aa73e 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -2,82 +2,97 @@ import * as TSTLErrors from "../../src/TSTLErrors"; import * as util from "../util"; // TODO: string.toString() -const serializeAndReturnTestEnum = () => ` +const serializeAndReturn = (identifier: string) => ` const mappedTestEnum: any = {}; - for (const key in TestEnum) { - mappedTestEnum[(key as any).toString()] = TestEnum[key]; + for (const key in ${identifier}) { + mappedTestEnum[(key as any).toString()] = ${identifier}[key]; } return mappedTestEnum; `; -test("without initializer", () => { +// TODO: Move to namespace tests? +test("in a namespace", () => { util.testFunction` - enum TestEnum { - A, - B, - C, + namespace Test { + export enum TestEnum { + A, + B, + } } - ${serializeAndReturnTestEnum} + ${serializeAndReturn("Test.TestEnum")} `.expectToMatchJsResult(); }); -test("expression initializer", () => { +test.skip("string literal as a member name", () => { util.testFunction` - const value = 6; enum TestEnum { - A, - B = value, + ["A"], } - ${serializeAndReturnTestEnum} + ${serializeAndReturn("TestEnum")} `.expectToMatchJsResult(); }); -test("initializer inference", () => { - util.testFunction` - const enum TestEnum { - A = 3, - B, - C = 5, - } +describe("initializers", () => { + test("expression", () => { + util.testFunction` + const value = 6; + enum TestEnum { + A, + B = value, + } - return TestEnum.B; - `.expectToMatchJsResult(); -}); + ${serializeAndReturn("TestEnum")} + `.expectToMatchJsResult(); + }); -test("initializer referencing other member", () => { - util.testFunction` - enum TestEnum { - A, - B = A, - C, - } + test("inference", () => { + util.testFunction` + enum TestEnum { + A, + B, + C, + } - ${serializeAndReturnTestEnum} - `.expectToMatchJsResult(); -}); + ${serializeAndReturn("TestEnum")} + `.expectToMatchJsResult(); + }); -test("initializer referencing other member with initializer referencing other member", () => { - util.testFunction` - enum TestEnum { - A, - B = A, - C = B, - } + test("partial inference", () => { + util.testFunction` + const enum TestEnum { + A = 3, + B, + C = 5, + } - ${serializeAndReturnTestEnum} - `.expectToMatchJsResult(); -}); + return TestEnum.B; + `.expectToMatchJsResult(); + }); -test.skip("string literal member name", () => { - util.testFunction` - enum TestEnum { - ["A"] = "foo", - } + test("other member reference", () => { + util.testFunction` + enum TestEnum { + A, + B = A, + C, + } - ${serializeAndReturnTestEnum} - `.expectToMatchJsResult(); + ${serializeAndReturn("TestEnum")} + `.expectToMatchJsResult(); + }); + + test.skip("string literal member reference", () => { + util.testFunction` + enum TestEnum { + ["A"], + B = A, + } + + ${serializeAndReturn("TestEnum")} + `.expectToMatchJsResult(); + }); }); test("invalid heterogeneous enum", () => { diff --git a/test/util.ts b/test/util.ts index 532ea487d..0cf6cff60 100644 --- a/test/util.ts +++ b/test/util.ts @@ -317,7 +317,7 @@ export abstract class TestBuilder { return `${this.tsHeader}${this._tsCode}`; } - private hasProgram = false; + protected hasProgram = false; @memoize public getProgram(): ts.Program { this.hasProgram = true; @@ -510,12 +510,13 @@ class AccessorTestBuilder extends TestBuilder { @memoize protected getJsCodeWithWrapper(): string { - return this.getMainJsCodeChunk() + `\n;module.exports = exports${this.accessor}`; + return this.getMainJsCodeChunk() + `\n;module.exports = module.exports${this.accessor}`; } } class ModuleTestBuilder extends AccessorTestBuilder { public setExport(name: string): this { + expect(this.hasProgram).toBe(false); this.accessor = `.${name}`; return this; } From 4f2a7f1e7e1ce89736faa0780703daab48b96b63 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 25 Jul 2019 03:20:33 +0500 Subject: [PATCH 55/64] Refactor some tests --- test/unit/classes/decorators.spec.ts | 123 ++++++++------------------- test/unit/classes/instanceof.spec.ts | 24 +++--- test/unit/json.spec.ts | 6 +- 3 files changed, 49 insertions(+), 104 deletions(-) diff --git a/test/unit/classes/decorators.spec.ts b/test/unit/classes/decorators.spec.ts index cb79d1761..28fef7365 100644 --- a/test/unit/classes/decorators.spec.ts +++ b/test/unit/classes/decorators.spec.ts @@ -3,121 +3,83 @@ import * as util from "../../util"; test("Class decorator with no parameters", () => { util.testFunction` - function SetBool(constructor: T) { + function setBool {}>(constructor: T) { return class extends constructor { decoratorBool = true; } } - @SetBool + @setBool class TestClass { public decoratorBool = false; } - const classInstance = new TestClass(); - return classInstance.decoratorBool; + return new TestClass(); `.expectToMatchJsResult(); }); test("Class decorator with parameters", () => { util.testFunction` - function SetNum(numArg: number) { - return {}>(constructor: T) => { + function setNum(numArg: number) { + return {}>(constructor: T) => { return class extends constructor { decoratorNum = numArg; }; }; } - @SetNum(420) + @setNum(420) class TestClass { public decoratorNum; } - const classInstance = new TestClass(); - return classInstance.decoratorNum; - `.expectToMatchJsResult(); -}); - -test("Class decorator with variable parameters", () => { - util.testFunction` - function SetNumbers(...numArgs: number[]) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNums = new Set(numArgs); - }; - }; - } - - @SetNumbers(120, 30, 54) - class TestClass { - public decoratorNums; - } - - const classInstance = new TestClass(); - let sum = 0; - for (const value of classInstance.decoratorNums) { - sum += value; - } - return sum; + return new TestClass(); `.expectToMatchJsResult(); }); test("Multiple class decorators", () => { util.testFunction` - function SetTen(constructor: T) { + function setTen {}>(constructor: T) { return class extends constructor { decoratorTen = 10; } } - function SetNum(numArg: number) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNum = numArg; - }; - }; + function setNum {}>(constructor: T) { + return class extends constructor { + decoratorNum = 410; + } } - @SetTen - @SetNum(410) + @setTen + @setNum class TestClass { public decoratorTen; public decoratorNum; } - const classInstance = new TestClass(); - return classInstance.decoratorNum + classInstance.decoratorTen; + return new TestClass(); `.expectToMatchJsResult(); }); test("Class decorator with inheritance", () => { util.testFunction` - function SetTen(constructor: T) { + function setTen {}>(constructor: T) { return class extends constructor { decoratorTen = 10; } } - function SetNum(numArg: number) { - return {}>(constructor: T) => { - return class extends constructor { - decoratorNum = numArg; - }; - }; - } - class TestClass { public decoratorTen = 0; - public decoratorNum = 0; } - @SetTen - @SetNum(410) - class SubTestClass extends TestClass {} + @setTen + class SubTestClass extends TestClass { + public decoratorTen = 5; + } - const classInstance = new SubTestClass(); - return classInstance.decoratorNum + classInstance.decoratorTen; + return new SubTestClass(); `.expectToMatchJsResult(); }); @@ -125,46 +87,29 @@ test("Class decorators are applied in order and executed in reverse order", () = util.testFunction` const order = []; - function SetString(stringArg: string) { - order.push("eval " + stringArg); - return {}>(constructor: T) => { - order.push("execute " + stringArg); - return class extends constructor { - decoratorString = stringArg; - }; + function pushOrder(index: number) { + order.push("eval " + index); + return (constructor: new (...args: any[]) => {}) => { + order.push("execute " + index); }; } - @SetString("fox") - @SetString("jumped") - @SetString("over dog") - class TestClass { - public static decoratorString = ""; - } + @pushOrder(1) + @pushOrder(2) + @pushOrder(3) + class TestClass {} - const inst = new TestClass(); - return order.join(" "); + return order; `.expectToMatchJsResult(); }); test("Throws error if decorator function has void context", () => { - const source = ` - function SetBool(this: void, constructor: T) { - return class extends constructor { - decoratorBool = true; - } - } + util.testFunction` + function SetBool(this: void, constructor: new (...args: any[]) => {}) {} @SetBool - class TestClass { - public decoratorBool = false; - } - - const classInstance = new TestClass(); - return classInstance.decoratorBool; - `; - - expect(() => util.transpileAndExecute(source)).toThrowExactError(TSTLErrors.InvalidDecoratorContext(util.nodeStub)); + class TestClass {} + `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidDecoratorContext(util.nodeStub)); }); test("Exported class decorator", () => { diff --git a/test/unit/classes/instanceof.spec.ts b/test/unit/classes/instanceof.spec.ts index 44e0773ed..d22237515 100644 --- a/test/unit/classes/instanceof.spec.ts +++ b/test/unit/classes/instanceof.spec.ts @@ -3,8 +3,8 @@ import * as util from "../../util"; test("instanceof", () => { util.testFunction` class myClass {} - let inst = new myClass(); - return inst instanceof myClass; + const instance = new myClass(); + return instance instanceof myClass; `.expectToMatchJsResult(); }); @@ -12,8 +12,8 @@ test("instanceof inheritance", () => { util.testFunction` class myClass {} class childClass extends myClass {} - let inst = new childClass(); - return inst instanceof myClass; + const instance = new childClass(); + return instance instanceof myClass; `.expectToMatchJsResult(); }); @@ -21,8 +21,8 @@ test("instanceof inheritance false", () => { util.testFunction` class myClass {} class childClass extends myClass {} - let inst = new myClass(); - return inst instanceof childClass; + const instance = new myClass(); + return instance instanceof childClass; `.expectToMatchJsResult(); }); @@ -52,8 +52,8 @@ test("null instanceof Class", () => { test("instanceof export", () => { util.testModule` export class myClass {} - let inst = new myClass(); - export const result = inst instanceof myClass; + const instance = new myClass(); + export const result = instance instanceof myClass; ` .setExport("result") .expectToMatchJsResult(); @@ -67,10 +67,10 @@ test("instanceof Symbol.hasInstance", () => { } } - const inst = new myClass(); - const isInstanceOld = inst instanceof myClass; + const instance = new myClass(); + const isInstanceOld = instance instanceof myClass; myClass[Symbol.hasInstance] = () => true; - const isInstanceNew = inst instanceof myClass; - return isInstanceOld !== isInstanceNew; + const isInstanceNew = instance instanceof myClass; + return { isInstanceOld, isInstanceNew }; `.expectToMatchJsResult(); }); diff --git a/test/unit/json.spec.ts b/test/unit/json.spec.ts index 9bbbec5d2..e822fc1ae 100644 --- a/test/unit/json.spec.ts +++ b/test/unit/json.spec.ts @@ -8,11 +8,11 @@ const jsonOptions = { moduleResolution: ts.ModuleResolutionKind.NodeJs, }; -test.each(["0", '""', "[]", '[1, "2", []]', '{ "a": "b" }', '{ "a": { "b": "c" } }'])("JSON (%p)", json => { - util.testModule(json) +test.each([0, "", [], [1, "2", []], { a: "b" }, { a: { b: "c" } }])("JSON (%p)", json => { + util.testModule(JSON.stringify(json)) .setOptions(jsonOptions) .setMainFileName("main.json") - .expectToEqual(JSON.parse(json)); + .expectToEqual(json); }); test("Empty JSON", () => { From 3117d6f02af4097b27b82ffd218997a881e06867 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Thu, 25 Jul 2019 06:55:24 +0500 Subject: [PATCH 56/64] Fix enum tests --- test/unit/enum.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index a252aceca..f04b2bc08 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -2,17 +2,17 @@ import * as TSTLErrors from "../../src/TSTLErrors"; import * as util from "../util"; // TODO: string.toString() -const serializeAndReturn = (identifier: string) => ` +const serializeEnum = (identifier: string) => `(() => { const mappedTestEnum: any = {}; for (const key in ${identifier}) { mappedTestEnum[(key as any).toString()] = ${identifier}[key]; } return mappedTestEnum; -`; +})()`; // TODO: Move to namespace tests? test("in a namespace", () => { - util.testFunction` + util.testModule` namespace Test { export enum TestEnum { A, @@ -20,7 +20,7 @@ test("in a namespace", () => { } } - ${serializeAndReturn("Test.TestEnum")} + export const result = ${serializeEnum("Test.TestEnum")} `.expectToMatchJsResult(); }); @@ -30,7 +30,7 @@ test.skip("string literal as a member name", () => { ["A"], } - ${serializeAndReturn("TestEnum")} + return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); @@ -43,7 +43,7 @@ describe("initializers", () => { B = value, } - ${serializeAndReturn("TestEnum")} + return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); @@ -55,19 +55,19 @@ describe("initializers", () => { C, } - ${serializeAndReturn("TestEnum")} + return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); test("partial inference", () => { util.testFunction` - const enum TestEnum { + enum TestEnum { A = 3, B, C = 5, } - return TestEnum.B; + return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); @@ -76,10 +76,10 @@ describe("initializers", () => { enum TestEnum { A, B = A, - C, + C = B, } - ${serializeAndReturn("TestEnum")} + return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); @@ -90,7 +90,7 @@ describe("initializers", () => { B = A, } - ${serializeAndReturn("TestEnum")} + return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); }); From 30f7f083b675474bfbe288e3e545c226d82240a8 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 29 Jul 2019 15:18:08 +0500 Subject: [PATCH 57/64] Remove todos for #663 --- test/unit/spread.spec.ts | 5 ++--- test/unit/templateLiterals.spec.ts | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 2f14122aa..8189146c2 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -8,11 +8,10 @@ const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLua describe("in function call", () => { util.testEachVersion( undefined, - // TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/663 - // TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/664 + // TODO: as const () => util.testFunction` function foo(a: number, b: number, ...rest: number[]) { - return { a, b, rest: rest } + return { a, b, rest } } const array: [number, number, number, number] = [0, 1, 2, 3]; diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index 3cc638ebe..7db192a88 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -37,10 +37,9 @@ test.each([ "obj.func`hello ${'propertyAccessExpression'}`", "obj['func']`hello ${'elementAccessExpression'}`", ])("tagged template literal (%p)", expression => { - // TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/663 util.testFunction` function func(strings: TemplateStringsArray, ...expressions: any[]) { - return { strings: [...strings], raw: strings.raw, expressions: expressions }; + return { strings: [...strings], raw: strings.raw, expressions }; } const obj = { func }; From 0f6e75b30b86d5cffa205a2d585487674f4227ff Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 29 Jul 2019 15:23:22 +0500 Subject: [PATCH 58/64] Move enum array index tests to array.spec.ts --- test/unit/builtins/array.spec.ts | 14 ++++++++++++++ test/unit/enum.spec.ts | 31 ------------------------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/test/unit/builtins/array.spec.ts b/test/unit/builtins/array.spec.ts index 7f69daa38..15535fe8c 100644 --- a/test/unit/builtins/array.spec.ts +++ b/test/unit/builtins/array.spec.ts @@ -76,6 +76,20 @@ describe("access", () => { `.expectToMatchJsResult(); }); + test("with enum value index", () => { + util.testFunction` + enum TestEnum { + A, + B, + C, + } + + const array = ["a", "b", "c"]; + let index = TestEnum.A; + return array[index]; + `.expectToMatchJsResult(); + }); + test.each([ { member: "firstElement()", expected: 3 }, { member: "name", expected: "array" }, diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index 108cf6f51..f04b2bc08 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -175,34 +175,3 @@ test("enum concat", () => { return test + "_foobar";`; expect(util.transpileAndExecute(code)).toBe("0_foobar"); }); - -test("enum value as array index", () => { - const code = ` - enum TestEnum { - A, - B, - C, - } - const arr = ["a", "b", "c"]; - let i = TestEnum.A; - return arr[i];`; - expect(util.transpileAndExecute(code)).toBe("a"); -}); - -test("enum property value as array index", () => { - const code = ` - enum TestEnum { - A, - B, - C, - } - - class Foo { - i = TestEnum.A; - } - const foo = new Foo(); - - const arr = ["a", "b", "c"]; - return arr[foo.i];`; - expect(util.transpileAndExecute(code)).toBe("a"); -}); From d6d517dd1b13358fa472dd41d0b04803256207d5 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 29 Jul 2019 15:29:26 +0500 Subject: [PATCH 59/64] Remove some translation tests --- .../__snapshots__/transformation.spec.ts.snap | 87 ------------------- .../transformation/callNamespace.ts | 4 - .../transformation/exportEquals.ts | 1 - .../transformation/functionRestArguments.ts | 3 - .../transformation/interfaceIndex.ts | 6 -- test/translation/transformation/namespace.ts | 3 - .../transformation/namespaceMerge.ts | 24 ----- .../transformation/namespaceNested.ts | 5 -- .../shorthandPropertyAssignment.ts | 1 - test/translation/transformation/typeAssert.ts | 2 - test/unit/namespaces.spec.ts | 16 +++- 11 files changed, 14 insertions(+), 138 deletions(-) delete mode 100644 test/translation/transformation/callNamespace.ts delete mode 100644 test/translation/transformation/exportEquals.ts delete mode 100644 test/translation/transformation/functionRestArguments.ts delete mode 100644 test/translation/transformation/interfaceIndex.ts delete mode 100644 test/translation/transformation/namespace.ts delete mode 100644 test/translation/transformation/namespaceMerge.ts delete mode 100644 test/translation/transformation/namespaceNested.ts delete mode 100644 test/translation/transformation/shorthandPropertyAssignment.ts delete mode 100644 test/translation/transformation/typeAssert.ts diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 836609dd5..044e1bf82 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -1,7 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Transformation (callNamespace) 1`] = `"Namespace:myFunction()"`; - exports[`Transformation (characterEscapeSequence) 1`] = ` "local quoteInDoubleQuotes = \\"\\\\' \\\\' \\\\'\\" local quoteInTemplateString = \\"\\\\' \\\\' \\\\'\\" @@ -57,11 +55,6 @@ function ClassB.prototype.____constructor(self) end" `; -exports[`Transformation (exportEquals) 1`] = ` -"local ____exports = true -return ____exports" -`; - exports[`Transformation (exportStatement) 1`] = ` "local ____exports = {} local xyz = 4 @@ -88,18 +81,6 @@ end return ____exports" `; -exports[`Transformation (functionRestArguments) 1`] = ` -"function varargsFunction(self, a, ...) - local b = ({...}) - local c = b -end" -`; - -exports[`Transformation (interfaceIndex) 1`] = ` -"local a = {} -a.abc = \\"def\\"" -`; - exports[`Transformation (methodRestArguments) 1`] = ` "MyClass = {} MyClass.name = \\"MyClass\\" @@ -283,64 +264,6 @@ return ____exports" exports[`Transformation (modulesVariableNoExport) 1`] = `"local foo = \\"bar\\""`; -exports[`Transformation (namespace) 1`] = ` -"myNamespace = myNamespace or {} -do - local function nsMember(self) - end -end" -`; - -exports[`Transformation (namespaceMerge) 1`] = ` -"MergedClass = {} -MergedClass.name = \\"MergedClass\\" -MergedClass.__index = MergedClass -MergedClass.prototype = {} -MergedClass.prototype.__index = MergedClass.prototype -MergedClass.prototype.constructor = MergedClass -function MergedClass.new(...) - local self = setmetatable({}, MergedClass.prototype) - self:____constructor(...) - return self -end -function MergedClass.prototype.____constructor(self) - self.propertyFunc = function() - end -end -function MergedClass.staticMethodA(self) -end -function MergedClass.staticMethodB(self) - self:staticMethodA() -end -function MergedClass.prototype.methodA(self) -end -function MergedClass.prototype.methodB(self) - self:methodA() - self:propertyFunc() -end -MergedClass = MergedClass or {} -do - function MergedClass.namespaceFunc(self) - end -end -local mergedClass = MergedClass.new() -mergedClass:methodB() -mergedClass:propertyFunc() -MergedClass:staticMethodB() -MergedClass:namespaceFunc()" -`; - -exports[`Transformation (namespaceNested) 1`] = ` -"myNamespace = myNamespace or {} -do - local myNestedNamespace = {} - do - local function nsMember(self) - end - end -end" -`; - exports[`Transformation (namespacePhantom) 1`] = ` "function nsMember(self) end" @@ -352,16 +275,6 @@ exports[`Transformation (returnDefault) 1`] = ` end" `; -exports[`Transformation (shorthandPropertyAssignment) 1`] = ` -"local f -f = function(____, x) return ({x = x}) end" -`; - -exports[`Transformation (typeAssert) 1`] = ` -"local test1 = 10 -local test2 = 10" -`; - exports[`Transformation (unusedDefaultWithNamespaceImport) 1`] = ` "local x = require(\\"module\\") local ____ = x" diff --git a/test/translation/transformation/callNamespace.ts b/test/translation/transformation/callNamespace.ts deleted file mode 100644 index 43ed32d6d..000000000 --- a/test/translation/transformation/callNamespace.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare namespace Namespace { - function myFunction(); -} -Namespace.myFunction(); diff --git a/test/translation/transformation/exportEquals.ts b/test/translation/transformation/exportEquals.ts deleted file mode 100644 index ba27c6482..000000000 --- a/test/translation/transformation/exportEquals.ts +++ /dev/null @@ -1 +0,0 @@ -export = true; diff --git a/test/translation/transformation/functionRestArguments.ts b/test/translation/transformation/functionRestArguments.ts deleted file mode 100644 index 63649f09b..000000000 --- a/test/translation/transformation/functionRestArguments.ts +++ /dev/null @@ -1,3 +0,0 @@ -function varargsFunction(a: string, ...b: string[]): void { - const c = b; -} diff --git a/test/translation/transformation/interfaceIndex.ts b/test/translation/transformation/interfaceIndex.ts deleted file mode 100644 index f209e153e..000000000 --- a/test/translation/transformation/interfaceIndex.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare interface Dictionary { - [index: string]: T; -} - -let a: Dictionary = {}; -a["abc"] = "def"; diff --git a/test/translation/transformation/namespace.ts b/test/translation/transformation/namespace.ts deleted file mode 100644 index 24f4a78e0..000000000 --- a/test/translation/transformation/namespace.ts +++ /dev/null @@ -1,3 +0,0 @@ -namespace myNamespace { - function nsMember() {} -} diff --git a/test/translation/transformation/namespaceMerge.ts b/test/translation/transformation/namespaceMerge.ts deleted file mode 100644 index b6905def9..000000000 --- a/test/translation/transformation/namespaceMerge.ts +++ /dev/null @@ -1,24 +0,0 @@ -class MergedClass { - public static staticMethodA(): void {} - public static staticMethodB(): void { - this.staticMethodA(); - } - - public propertyFunc: () => void = () => {}; - - public methodA(): void {} - public methodB(): void { - this.methodA(); - this.propertyFunc(); - } -} - -namespace MergedClass { - export function namespaceFunc(): void {} -} - -const mergedClass = new MergedClass(); -mergedClass.methodB(); -mergedClass.propertyFunc(); -MergedClass.staticMethodB(); -MergedClass.namespaceFunc(); diff --git a/test/translation/transformation/namespaceNested.ts b/test/translation/transformation/namespaceNested.ts deleted file mode 100644 index 85869547f..000000000 --- a/test/translation/transformation/namespaceNested.ts +++ /dev/null @@ -1,5 +0,0 @@ -namespace myNamespace { - namespace myNestedNamespace { - function nsMember() {} - } -} diff --git a/test/translation/transformation/shorthandPropertyAssignment.ts b/test/translation/transformation/shorthandPropertyAssignment.ts deleted file mode 100644 index 4b2aa7e82..000000000 --- a/test/translation/transformation/shorthandPropertyAssignment.ts +++ /dev/null @@ -1 +0,0 @@ -const f = x => ({ x }); diff --git a/test/translation/transformation/typeAssert.ts b/test/translation/transformation/typeAssert.ts deleted file mode 100644 index 4b654d851..000000000 --- a/test/translation/transformation/typeAssert.ts +++ /dev/null @@ -1,2 +0,0 @@ -const test1 = 10; -const test2 = 10 as number; diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts index 1b6751b51..4a1bd6598 100644 --- a/test/unit/namespaces.spec.ts +++ b/test/unit/namespaces.spec.ts @@ -21,13 +21,25 @@ test("global scoping", () => { expect(result).toBe("bar"); }); +test("nested namespace", () => { + util.testModule` + namespace A { + namespace B { + export const foo = "foo"; + } + } + + export const foo = A.B.foo; + `.expectToMatchJsResult(); +}); + test("nested namespace with dot in name", () => { util.testModule` - namespace a.b { + namespace A.B { export const foo = "foo"; } - export const foo = a.b.foo; + export const foo = A.B.foo; `.expectToMatchJsResult(); }); From 9198618d1ed40606afa3fcd2c9cec18d54d6ba1e Mon Sep 17 00:00:00 2001 From: ark120202 Date: Mon, 29 Jul 2019 15:38:37 +0500 Subject: [PATCH 60/64] Fix nested namespace test --- test/unit/namespaces.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts index 4a1bd6598..dfce3fabf 100644 --- a/test/unit/namespaces.spec.ts +++ b/test/unit/namespaces.spec.ts @@ -24,7 +24,7 @@ test("global scoping", () => { test("nested namespace", () => { util.testModule` namespace A { - namespace B { + export namespace B { export const foo = "foo"; } } From 32acca8f0603298f3147ce116f7ae85171c47ae1 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 30 Jul 2019 18:16:18 +0500 Subject: [PATCH 61/64] Fix as const cast creating diagnostics --- src/LuaTransformer.ts | 13 ++++++++----- test/unit/builtins/array.spec.ts | 2 +- test/unit/builtins/globalThis.spec.ts | 1 - test/unit/spread.spec.ts | 3 +-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index 9addc631a..defcdd366 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -5147,11 +5147,14 @@ export class LuaTransformer { } public transformAssertionExpression(expression: ts.AssertionExpression): ExpressionVisitResult { - this.validateFunctionAssignment( - expression, - this.checker.getTypeAtLocation(expression.expression), - this.checker.getTypeAtLocation(expression.type) - ); + if (!ts.isConstTypeReference(expression.type)) { + this.validateFunctionAssignment( + expression, + this.checker.getTypeAtLocation(expression.expression), + this.checker.getTypeAtLocation(expression.type) + ); + } + return this.transformExpression(expression.expression); } diff --git a/test/unit/builtins/array.spec.ts b/test/unit/builtins/array.spec.ts index 15535fe8c..d583eb192 100644 --- a/test/unit/builtins/array.spec.ts +++ b/test/unit/builtins/array.spec.ts @@ -26,7 +26,7 @@ describe("access", () => { util.testExpression`[3, 5, 1][1]`.expectToMatchJsResult(); }); - test.skip("const array literal", () => { + test("const array literal", () => { util.testExpression`([3, 5, 1] as const)[1]`.expectToMatchJsResult(); }); diff --git a/test/unit/builtins/globalThis.spec.ts b/test/unit/builtins/globalThis.spec.ts index 0735fda56..85463f7ff 100644 --- a/test/unit/builtins/globalThis.spec.ts +++ b/test/unit/builtins/globalThis.spec.ts @@ -1,6 +1,5 @@ import * as util from "../../util"; -// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/660 test("equals _G", () => { util.testExpression`globalThis === _G`.setTsHeader("declare const _G: typeof globalThis;").expectToEqual(true); }); diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 8189146c2..af8db54f3 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -8,13 +8,12 @@ const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLua describe("in function call", () => { util.testEachVersion( undefined, - // TODO: as const () => util.testFunction` function foo(a: number, b: number, ...rest: number[]) { return { a, b, rest } } - const array: [number, number, number, number] = [0, 1, 2, 3]; + const array = [0, 1, 2, 3] as const; return foo(...array); `, { From 2c34c131d2ebd5e230ae2d79b58ca142fcbf9158 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 30 Jul 2019 18:23:48 +0500 Subject: [PATCH 62/64] Address feedback --- test/legacy-utils.ts | 162 +++++++++++++++++ test/unit/builtins/array.spec.ts | 2 +- test/unit/builtins/console.spec.ts | 2 +- test/unit/classes/decorators.spec.ts | 2 +- test/unit/classes/instanceof.spec.ts | 2 +- .../decorators/compileMembersOnly.spec.ts | 2 +- test/unit/enum.spec.ts | 21 --- test/unit/functions/functions.spec.ts | 2 +- test/unit/modules/modules.spec.ts | 2 +- test/unit/namespaces.spec.ts | 2 +- test/unit/spread.spec.ts | 3 - test/util.ts | 167 +----------------- 12 files changed, 176 insertions(+), 193 deletions(-) create mode 100644 test/legacy-utils.ts diff --git a/test/legacy-utils.ts b/test/legacy-utils.ts new file mode 100644 index 000000000..4b9815d17 --- /dev/null +++ b/test/legacy-utils.ts @@ -0,0 +1,162 @@ +import { lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari"; +import * as fs from "fs"; +import * as path from "path"; +import * as ts from "typescript"; +import * as tstl from "../src"; +import * as tsHelper from "../src/TSHelper"; + +export function transpileString( + str: string | { [filename: string]: string }, + options: tstl.CompilerOptions = {}, + ignoreDiagnostics = true +): string { + const { diagnostics, file } = transpileStringResult(str, options); + expect(file.lua).toBeDefined(); + + const errors = diagnostics.filter(d => !ignoreDiagnostics || d.source === "typescript-to-lua"); + expect(errors).not.toHaveDiagnostics(); + + return file.lua!.trim(); +} + +export function transpileStringsAsProject( + input: Record, + options: tstl.CompilerOptions = {} +): tstl.TranspileResult { + const optionsWithDefaults = { + luaTarget: tstl.LuaTarget.Lua53, + noHeader: true, + skipLibCheck: true, + target: ts.ScriptTarget.ESNext, + lib: ["lib.esnext.d.ts"], + experimentalDecorators: true, + ...options, + }; + + return tstl.transpileVirtualProject(input, optionsWithDefaults); +} + +export function transpileStringResult( + input: string | Record, + options: tstl.CompilerOptions = {} +): Required { + const { diagnostics, transpiledFiles } = transpileStringsAsProject( + typeof input === "string" ? { "main.ts": input } : input, + options + ); + + const file = transpiledFiles.find(({ fileName }) => /\bmain\.[a-z]+$/.test(fileName)); + if (file === undefined) { + throw new Error('Program should have a file named "main"'); + } + + return { diagnostics, file }; +} + +const lualibContent = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); +const minimalTestLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8") + "\n"; +export function executeLua(luaStr: string, withLib = true): any { + luaStr = luaStr.replace(/require\("lualib_bundle"\)/g, lualibContent); + if (withLib) { + luaStr = minimalTestLib + luaStr; + } + + const L = lauxlib.luaL_newstate(); + lualib.luaL_openlibs(L); + const status = lauxlib.luaL_dostring(L, to_luastring(luaStr)); + + if (status === lua.LUA_OK) { + // Read the return value from stack depending on its type. + if (lua.lua_isboolean(L, -1)) { + return lua.lua_toboolean(L, -1); + } else if (lua.lua_isnil(L, -1)) { + return undefined; + } else if (lua.lua_isnumber(L, -1)) { + return lua.lua_tonumber(L, -1); + } else if (lua.lua_isstring(L, -1)) { + return lua.lua_tojsstring(L, -1); + } else { + throw new Error("Unsupported lua return type: " + to_jsstring(lua.lua_typename(L, lua.lua_type(L, -1)))); + } + } else { + // If the lua VM did not terminate with status code LUA_OK an error occurred. + // Throw a JS error with the message, retrieved by reading a string from the stack. + + // Filter control characters out of string which are in there because ???? + throw new Error("LUA ERROR: " + to_jsstring(lua.lua_tostring(L, -1).filter(c => c >= 20))); + } +} + +export function transpileAndExecute( + tsStr: string, + compilerOptions?: tstl.CompilerOptions, + luaHeader?: string, + tsHeader?: string +): any { + const wrappedTsString = `${tsHeader ? tsHeader : ""} + declare function JSONStringify(this: void, p: any): string; + function __runTest(this: void): any {${tsStr}}`; + + const lua = `${luaHeader ? luaHeader : ""} + ${transpileString(wrappedTsString, compilerOptions, false)} + return __runTest();`; + + return executeLua(lua); +} + +export function transpileAndExecuteProjectReturningMainExport( + typeScriptFiles: Record, + exportName: string, + options: tstl.CompilerOptions = {} +): [any, string] { + const mainFile = Object.keys(typeScriptFiles).find(typeScriptFileName => typeScriptFileName === "main.ts"); + if (!mainFile) { + throw new Error("An entry point file needs to be specified. This should be called main.ts"); + } + + const joinedTranspiledFiles = Object.keys(typeScriptFiles) + .filter(typeScriptFileName => typeScriptFileName !== "main.ts") + .map(typeScriptFileName => { + const modulePath = tsHelper.getExportPath(typeScriptFileName, options); + const tsCode = typeScriptFiles[typeScriptFileName]; + const luaCode = transpileString(tsCode, options); + return `package.preload["${modulePath}"] = function() + ${luaCode} + end`; + }) + .join("\n"); + + const luaCode = `return (function() + ${joinedTranspiledFiles} + ${transpileString(typeScriptFiles[mainFile])} + end)().${exportName}`; + + try { + return [executeLua(luaCode), luaCode]; + } catch (err) { + throw new Error(` + Encountered an error when executing the following Lua code: + + ${luaCode} + + ${err} + `); + } +} + +export function transpileExecuteAndReturnExport( + tsStr: string, + returnExport: string, + compilerOptions?: tstl.CompilerOptions, + luaHeader?: string +): any { + const wrappedTsString = `declare function JSONStringify(this: void, p: any): string; + ${tsStr}`; + + const lua = `return (function() + ${luaHeader ? luaHeader : ""} + ${transpileString(wrappedTsString, compilerOptions, false)} + end)().${returnExport}`; + + return executeLua(lua); +} diff --git a/test/unit/builtins/array.spec.ts b/test/unit/builtins/array.spec.ts index d583eb192..edac44090 100644 --- a/test/unit/builtins/array.spec.ts +++ b/test/unit/builtins/array.spec.ts @@ -113,7 +113,7 @@ describe("access", () => { array[0] = 3; export const result = array.${member}; ` - .setExport("result") + .setReturnExport("result") .setLuaHeader(luaHeader) .expectToEqual(expected); }); diff --git a/test/unit/builtins/console.spec.ts b/test/unit/builtins/console.spec.ts index c8466e74f..7e7f68e9e 100644 --- a/test/unit/builtins/console.spec.ts +++ b/test/unit/builtins/console.spec.ts @@ -53,6 +53,6 @@ test("console.differentiation", () => { export const result = test(); ` - .setExport("result") + .setReturnExport("result") .expectToMatchJsResult(); }); diff --git a/test/unit/classes/decorators.spec.ts b/test/unit/classes/decorators.spec.ts index 28fef7365..e5c44e51d 100644 --- a/test/unit/classes/decorators.spec.ts +++ b/test/unit/classes/decorators.spec.ts @@ -122,6 +122,6 @@ test("Exported class decorator", () => { @decorator export class Foo {} ` - .setExport("Foo.bar") + .setReturnExport("Foo.bar") .expectToMatchJsResult(); }); diff --git a/test/unit/classes/instanceof.spec.ts b/test/unit/classes/instanceof.spec.ts index d22237515..fdbb6e36e 100644 --- a/test/unit/classes/instanceof.spec.ts +++ b/test/unit/classes/instanceof.spec.ts @@ -55,7 +55,7 @@ test("instanceof export", () => { const instance = new myClass(); export const result = instance instanceof myClass; ` - .setExport("result") + .setReturnExport("result") .expectToMatchJsResult(); }); diff --git a/test/unit/decorators/compileMembersOnly.spec.ts b/test/unit/decorators/compileMembersOnly.spec.ts index 4e20b57b1..5db0fe974 100644 --- a/test/unit/decorators/compileMembersOnly.spec.ts +++ b/test/unit/decorators/compileMembersOnly.spec.ts @@ -28,7 +28,7 @@ test("@compileMembersOnly in a namespace", () => { export const A = Test.TestEnum.A; ` - .setExport("A") + .setReturnExport("A") .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain("Test.A")) .expectToEqual("A"); }); diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index f04b2bc08..6fd75556b 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -24,16 +24,6 @@ test("in a namespace", () => { `.expectToMatchJsResult(); }); -test.skip("string literal as a member name", () => { - util.testFunction` - enum TestEnum { - ["A"], - } - - return ${serializeEnum("TestEnum")} - `.expectToMatchJsResult(); -}); - describe("initializers", () => { test("expression", () => { util.testFunction` @@ -82,17 +72,6 @@ describe("initializers", () => { return ${serializeEnum("TestEnum")} `.expectToMatchJsResult(); }); - - test.skip("string literal member reference", () => { - util.testFunction` - enum TestEnum { - ["A"], - B = A, - } - - return ${serializeEnum("TestEnum")} - `.expectToMatchJsResult(); - }); }); test("invalid heterogeneous enum", () => { diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index 567406ef3..8214e6bfd 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -416,7 +416,7 @@ test("Function local overriding export", () => { } export const result = bar(7); ` - .setExport("result") + .setReturnExport("result") .expectToMatchJsResult(); }); diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index 3534396fd..9209a0819 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -60,7 +60,7 @@ test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌ ` .disableSemanticCheck() .setLuaHeader(`setmetatable(package.loaded, { __index = function() return { foo = "bar" } end })`) - .setExport("foo") + .setReturnExport("foo") .expectToEqual("bar"); } ); diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts index dfce3fabf..21272ae8e 100644 --- a/test/unit/namespaces.spec.ts +++ b/test/unit/namespaces.spec.ts @@ -100,7 +100,7 @@ test("declared namespace function call", () => { export const result = myNameSpace.declaredFunction(2); ` - .setExport("result") + .setReturnExport("result") .setLuaHeader(luaHeader) .expectToEqual(6); }); diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index af8db54f3..f1627936d 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -46,9 +46,6 @@ describe("in array literal", () => { `.expectToMatchJsResult(); }); - test.todo("of generator"); - test.todo("of string"); - util.testEachVersion("of array literal", () => util.testExpression`[...[0, 1, 2]]`, { [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), diff --git a/test/util.ts b/test/util.ts index 8cf745bb2..42f53e58f 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,175 +1,20 @@ -import * as tsHelper from "../src/TSHelper"; import { lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari"; import * as fs from "fs"; import * as path from "path"; +import * as prettyFormat from "pretty-format"; import * as ts from "typescript"; import * as vm from "vm"; -import * as prettyFormat from "pretty-format"; import * as tstl from "../src"; -export const nodeStub = ts.createNode(ts.SyntaxKind.Unknown); - -export function transpileString( - str: string | { [filename: string]: string }, - options: tstl.CompilerOptions = {}, - ignoreDiagnostics = true -): string { - const { diagnostics, file } = transpileStringResult(str, options); - if (!expectToBeDefined(file) || !expectToBeDefined(file.lua)) return ""; - - const errors = diagnostics.filter(d => !ignoreDiagnostics || d.source === "typescript-to-lua"); - expect(errors).not.toHaveDiagnostics(); - - return file.lua.trim(); -} - -export function transpileStringsAsProject( - input: Record, - options: tstl.CompilerOptions = {} -): tstl.TranspileResult { - const optionsWithDefaults = { - luaTarget: tstl.LuaTarget.Lua53, - noHeader: true, - skipLibCheck: true, - target: ts.ScriptTarget.ESNext, - lib: ["lib.esnext.d.ts"], - experimentalDecorators: true, - ...options, - }; - - return tstl.transpileVirtualProject(input, optionsWithDefaults); -} - -export function transpileStringResult( - input: string | Record, - options: tstl.CompilerOptions = {} -): Required { - const { diagnostics, transpiledFiles } = transpileStringsAsProject( - typeof input === "string" ? { "main.ts": input } : input, - options - ); +export * from "./legacy-utils"; - const file = transpiledFiles.find(({ fileName }) => /\bmain\.[a-z]+$/.test(fileName)); - if (file === undefined) { - throw new Error('Program should have a file named "main"'); - } - - return { diagnostics, file }; -} - -const lualibContent = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); -const minimalTestLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8") + "\n"; -export function executeLua(luaStr: string, withLib = true): any { - luaStr = luaStr.replace(/require\("lualib_bundle"\)/g, lualibContent); - if (withLib) { - luaStr = minimalTestLib + luaStr; - } - - const L = lauxlib.luaL_newstate(); - lualib.luaL_openlibs(L); - const status = lauxlib.luaL_dostring(L, to_luastring(luaStr)); - - if (status === lua.LUA_OK) { - // Read the return value from stack depending on its type. - if (lua.lua_isboolean(L, -1)) { - return lua.lua_toboolean(L, -1); - } else if (lua.lua_isnil(L, -1)) { - return undefined; - } else if (lua.lua_isnumber(L, -1)) { - return lua.lua_tonumber(L, -1); - } else if (lua.lua_isstring(L, -1)) { - return lua.lua_tojsstring(L, -1); - } else { - throw new Error("Unsupported lua return type: " + to_jsstring(lua.lua_typename(L, lua.lua_type(L, -1)))); - } - } else { - // If the lua VM did not terminate with status code LUA_OK an error occurred. - // Throw a JS error with the message, retrieved by reading a string from the stack. - - // Filter control characters out of string which are in there because ???? - throw new Error("LUA ERROR: " + to_jsstring(lua.lua_tostring(L, -1).filter(c => c >= 20))); - } -} +export const nodeStub = ts.createNode(ts.SyntaxKind.Unknown); // Get a mock transformer to use for testing export function makeTestTransformer(luaTarget = tstl.LuaTarget.Lua53): tstl.LuaTransformer { return new tstl.LuaTransformer(ts.createProgram([], { luaTarget })); } -export function transpileAndExecute( - tsStr: string, - compilerOptions?: tstl.CompilerOptions, - luaHeader?: string, - tsHeader?: string -): any { - const wrappedTsString = `${tsHeader ? tsHeader : ""} - declare function JSONStringify(this: void, p: any): string; - function __runTest(this: void): any {${tsStr}}`; - - const lua = `${luaHeader ? luaHeader : ""} - ${transpileString(wrappedTsString, compilerOptions, false)} - return __runTest();`; - - return executeLua(lua); -} - -export function transpileAndExecuteProjectReturningMainExport( - typeScriptFiles: Record, - exportName: string, - options: tstl.CompilerOptions = {} -): [any, string] { - const mainFile = Object.keys(typeScriptFiles).find(typeScriptFileName => typeScriptFileName === "main.ts"); - if (!mainFile) { - throw new Error("An entry point file needs to be specified. This should be called main.ts"); - } - - const joinedTranspiledFiles = Object.keys(typeScriptFiles) - .filter(typeScriptFileName => typeScriptFileName !== "main.ts") - .map(typeScriptFileName => { - const modulePath = tsHelper.getExportPath(typeScriptFileName, options); - const tsCode = typeScriptFiles[typeScriptFileName]; - const luaCode = transpileString(tsCode, options); - return `package.preload["${modulePath}"] = function() - ${luaCode} - end`; - }) - .join("\n"); - - const luaCode = `return (function() - ${joinedTranspiledFiles} - ${transpileString(typeScriptFiles[mainFile])} - end)().${exportName}`; - - try { - return [executeLua(luaCode), luaCode]; - } catch (err) { - throw new Error(` - Encountered an error when executing the following Lua code: - - ${luaCode} - - ${err} - `); - } -} - -export function transpileExecuteAndReturnExport( - tsStr: string, - returnExport: string, - compilerOptions?: tstl.CompilerOptions, - luaHeader?: string -): any { - const wrappedTsString = `declare function JSONStringify(this: void, p: any): string; - ${tsStr}`; - - const lua = `return (function() - ${luaHeader ? luaHeader : ""} - ${transpileString(wrappedTsString, compilerOptions, false)} - end)().${returnExport}`; - - return executeLua(lua); -} - export function parseTypeScript( typescript: string, target: tstl.LuaTarget = tstl.LuaTarget.Lua53 @@ -242,8 +87,6 @@ interface TranspileJsResult { function transpileJs(program: ts.Program): TranspileJsResult { const transpiledFiles: TranspiledJsFile[] = []; - // TODO: Included in TS3.5 - type Omit = Pick>; const updateTranspiledFile = (fileName: string, update: Omit) => { const file = transpiledFiles.find(f => f.fileName === fileName); if (file) { @@ -552,6 +395,8 @@ export abstract class TestBuilder { } } +const lualibContent = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); +const minimalTestLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8") + "\n"; class AccessorTestBuilder extends TestBuilder { protected accessor = ""; @@ -572,7 +417,7 @@ class AccessorTestBuilder extends TestBuilder { } class ModuleTestBuilder extends AccessorTestBuilder { - public setExport(name: string): this { + public setReturnExport(name: string): this { expect(this.hasProgram).toBe(false); this.accessor = `.${name}`; return this; From 5d6a5ee7d012596767e0e097434a866c4c4f527b Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 31 Jul 2019 13:16:23 +0500 Subject: [PATCH 63/64] Refactor new spread tests --- test/unit/spread.spec.ts | 89 +++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 6a023181c..9f6f19ae4 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -26,6 +26,17 @@ describe("in function call", () => { }); describe("in array literal", () => { + util.testEachVersion("of array literal", () => util.testExpression`[...[0, 1, 2]]`, { + [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), + [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + }); + + test.each(["", "string", "string with spaces", "string 1 2 3"])("of string literal (%p)", str => { + util.testExpressionTemplate`[...${str}]`.expectToMatchJsResult(); + }); + test("of iterable", () => { util.testFunction` const it = { @@ -45,60 +56,34 @@ describe("in array literal", () => { return [...it] `.expectToMatchJsResult(); }); - - util.testEachVersion("of array literal", () => util.testExpression`[...[0, 1, 2]]`, { - [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), - [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), - [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), - [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), - }); -}); - -test.each(["", "string", "string with spaces", "string 1 2 3"])('Spread Element String "%s"', str => { - const code = ` - const arr = [..."${str}"]; - return JSONStringify(arr)`; - expect(JSON.parse(util.transpileAndExecute(code))).toEqual([...str]); -}); - -test.each([ - "{ value: false, ...{ value: true } }", - "{ ...{ value: false }, value: true }", - "{ ...{ value: false }, value: false, ...{ value: true } }", - "{ ...{ x: true, y: true } }", - "{ x: true, ...{ y: true, z: true } }", - "{ ...{ x: true }, ...{ y: true, z: true } }", -])('SpreadAssignment "%s"', expression => { - const code = `return JSONStringify(${expression});`; - expect(JSON.parse(util.transpileAndExecute(code))).toEqual(eval(`(${expression})`)); }); -test("SpreadAssignment Destructure", () => { - const code = `let obj = { x: 0, y: 1, z: 2 };`; - const luaCode = ` - ${code} - return JSONStringify({ a: 0, ...obj, b: 1, c: 2 });`; - const jsCode = ` - ${code} - ({ a: 0, ...obj, b: 1, c: 2 })`; - expect(JSON.parse(util.transpileAndExecute(luaCode))).toStrictEqual(eval(jsCode)); -}); +describe("in object literal", () => { + test.each([ + "{ x: false, ...{ x: true, y: true } }", + "{ ...{ x: true, y: true } }", + "{ ...{ x: true }, ...{ y: true, z: true } }", + "{ ...{ x: false }, x: true }", + "{ ...{ x: false }, x: false, ...{ x: true } }", + ])("of object literal (%p)", expression => { + util.testExpression(expression).expectToMatchJsResult(); + }); -test("SpreadAssignment No Mutation", () => { - const code = ` - const obj: { x: number, y: number, z?: number } = { x: 0, y: 1 }; - const merge = { ...obj, z: 2 }; - return obj.z;`; - expect(util.transpileAndExecute(code)).toBe(undefined); -}); + test("of object reference", () => { + util.testFunction` + const object = { x: 0, y: 1 }; + const result = { ...object, z: 2 }; + return { object, result }; + `.expectToMatchJsResult(); + }); -test.each([ - "function spread() { return [0, 1, 2] } const object = { ...spread() };", - "const object = { ...[0, 1, 2] };", -])('SpreadAssignment Array "%s"', expressionToCreateObject => { - const code = ` - ${expressionToCreateObject} - return JSONStringify([object[0], object[1], object[2]]);`; - expect(JSON.parse(util.transpileAndExecute(code))).toEqual([0, 1, 2]); + test.each([ + ["literal", "const object = { ...[0, 1, 2] };"], + ["reference", "const array = [0, 1, 2]; const object = { ...array };"], + ])("of array %p", (_name, expressionToCreateObject) => { + util.testFunction` + ${expressionToCreateObject} + return { "0": object[0], "1": object[1], "2": object[2] }; + `.expectToMatchJsResult(); + }); }); - From 9cb9f6733309cd8ec6e9adcd5bc6e2db4baf0047 Mon Sep 17 00:00:00 2001 From: ark120202 Date: Wed, 31 Jul 2019 13:16:51 +0500 Subject: [PATCH 64/64] Remove spreadAssignment translation test --- test/translation/__snapshots__/transformation.spec.ts.snap | 7 ------- test/translation/transformation/spreadAssignment.ts | 3 --- 2 files changed, 10 deletions(-) delete mode 100644 test/translation/transformation/spreadAssignment.ts diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 530d9ab03..044e1bf82 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -275,13 +275,6 @@ exports[`Transformation (returnDefault) 1`] = ` end" `; -exports[`Transformation (spreadAssignment) 1`] = ` -"require(\\"lualib_bundle\\"); -local xy = __TS__ObjectAssign({x = 0, y = 1}) -local xyz = __TS__ObjectAssign({x = 0, y = 1}, {z = 2}) -local xyz2 = __TS__ObjectAssign({z = 2}, {x = 0, y = 1})" -`; - exports[`Transformation (unusedDefaultWithNamespaceImport) 1`] = ` "local x = require(\\"module\\") local ____ = x" diff --git a/test/translation/transformation/spreadAssignment.ts b/test/translation/transformation/spreadAssignment.ts deleted file mode 100644 index 0ad87be82..000000000 --- a/test/translation/transformation/spreadAssignment.ts +++ /dev/null @@ -1,3 +0,0 @@ -const xy = { ...{ x: 0, y: 1 } }; -const xyz = { ...{ x: 0, y: 1 }, z: 2 }; -const xyz2 = { z: 2, ...{ x: 0, y: 1 } };