///
module ts {
describe("Transpile", () => {
interface TranspileTestSettings {
options?: TranspileOptions;
expectedOutput?: string;
expectedDiagnosticCodes?: number[];
}
function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) {
if(!expectedDiagnosticCodes) {
return;
}
for (let i = 0; i < expectedDiagnosticCodes.length; i++) {
assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`);
}
assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected");
}
function test(input: string, testSettings: TranspileTestSettings): void {
let transpileOptions: TranspileOptions = testSettings.options || {};
if (!transpileOptions.compilerOptions) {
transpileOptions.compilerOptions = {};
}
if(transpileOptions.compilerOptions.newLine === undefined) {
// use \r\n as default new line
transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed;
}
let canUseOldTranspile = !transpileOptions.renamedDependencies;
transpileOptions.reportDiagnostics = true;
let transpileModuleResult = transpileModule(input, transpileOptions);
checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes);
if (testSettings.expectedOutput !== undefined) {
assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput);
}
if (canUseOldTranspile) {
let diagnostics: Diagnostic[] = [];
let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName);
checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes);
if (testSettings.expectedOutput) {
assert.equal(transpileResult, testSettings.expectedOutput);
}
}
// check source maps
if (!transpileOptions.compilerOptions) {
transpileOptions.compilerOptions = {};
}
if (!transpileOptions.fileName) {
transpileOptions.fileName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts";
}
transpileOptions.compilerOptions.sourceMap = true;
let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions);
assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined);
let expectedSourceMapFileName = removeFileExtension(getBaseFileName(normalizeSlashes(transpileOptions.fileName))) + ".js.map";
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
if (testSettings.expectedOutput !== undefined) {
assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine);
}
else {
// expected output is not set, just verify that output text has sourceMappingURL as a last line
let output = transpileModuleResultWithSourceMap.outputText;
assert.isTrue(output.length >= expectedSourceMappingUrlLine.length);
if (output.length === expectedSourceMappingUrlLine.length) {
assert.equal(output, expectedSourceMappingUrlLine);
}
else {
let suffix = getNewLineCharacter(transpileOptions.compilerOptions) + expectedSourceMappingUrlLine
assert.isTrue(output.indexOf(suffix, output.length - suffix.length) !== -1);
}
}
}
it("Generates correct compilerOptions diagnostics", () => {
// Expecting 5047: "Option 'isolatedModules' can only be used when either option'--module' is provided or option 'target' is 'ES6' or higher."
test(`var x = 0;`, { expectedDiagnosticCodes: [5047] });
});
it("Generates no diagnostics with valid inputs", () => {
// No errors
test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates no diagnostics for missing file references", () => {
test(`///
var x = 0;`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates no diagnostics for missing module imports", () => {
test(`import {a} from "module2";`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates expected syntactic diagnostics", () => {
test(`a b`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } }, expectedDiagnosticCodes: [1005] }); /// 1005: ';' Expected
});
it("Does not generate semantic diagnostics", () => {
test(`var x: string = 0;`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates module output", () => {
test(`var x = 0;`,
{
options: { compilerOptions: { module: ModuleKind.AMD } },
expectedOutput: `define(["require", "exports"], function (require, exports) {\r\n "use strict";\r\n var x = 0;\r\n});\r\n`
});
});
it("Uses correct newLine character", () => {
test(`var x = 0;`,
{
options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } },
expectedOutput: `"use strict";\nvar x = 0;\n`
});
});
it("Sets module name", () => {
let output =
`System.register("NamedModule", [], function(exports_1) {\n "use strict";\n var x;\n` +
` return {\n` +
` setters:[],\n` +
` execute: function() {\n` +
` var x = 1;\n` +
` }\n` +
` }\n` +
`});\n`;
test("var x = 1;",
{
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" },
expectedOutput: output
})
});
it("No extra errors for file without extension", () => {
test(`"use strict";\r\nvar x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" } });
});
it("Rename dependencies - System", () => {
let input =
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`
let output =
`System.register(["SomeOtherName"], function(exports_1) {\n` +
` "use strict";\n` +
` var SomeName_1;\n` +
` return {\n` +
` setters:[\n` +
` function (SomeName_1_1) {\n` +
` SomeName_1 = SomeName_1_1;\n` +
` }],\n` +
` execute: function() {\n` +
` use(SomeName_1.foo);\n` +
` }\n` +
` }\n` +
`});\n`
test(input,
{
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } },
expectedOutput: output
});
});
it("Rename dependencies - AMD", () => {
let input =
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`
let output =
`define(["require", "exports", "SomeOtherName"], function (require, exports, SomeName_1) {\n` +
` "use strict";\n` +
` use(SomeName_1.foo);\n` +
`});\n`;
test(input,
{
options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } },
expectedOutput: output
});
});
it("Rename dependencies - UMD", () => {
let input =
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`
let output =
`(function (factory) {\n` +
` if (typeof module === 'object' && typeof module.exports === 'object') {\n` +
` var v = factory(require, exports); if (v !== undefined) module.exports = v;\n` +
` }\n` +
` else if (typeof define === 'function' && define.amd) {\n` +
` define(["require", "exports", "SomeOtherName"], factory);\n` +
` }\n` +
`})(function (require, exports) {\n` +
` "use strict";\n` +
` var SomeName_1 = require("SomeOtherName");\n` +
` use(SomeName_1.foo);\n` +
`});\n`;
test(input,
{
options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } },
expectedOutput: output
});
});
it("Transpile with emit decorators and emit metadata", () => {
let input =
`import {db} from './db';\n` +
`function someDecorator(target) {\n` +
` return target;\n` +
`} \n` +
`@someDecorator\n` +
`class MyClass {\n` +
` db: db;\n` +
` constructor(db: db) {\n` +
` this.db = db;\n` +
` this.db.doSomething(); \n` +
` }\n` +
`}\n` +
`export {MyClass}; \n`
let output =
`"use strict";\n` +
`var db_1 = require(\'./db\');\n` +
`function someDecorator(target) {\n` +
` return target;\n` +
`}\n` +
`var MyClass = (function () {\n` +
` function MyClass(db) {\n` +
` this.db = db;\n` +
` this.db.doSomething();\n` +
` }\n` +
` MyClass = __decorate([\n` +
` someDecorator, \n` +
` __metadata(\'design:paramtypes\', [(typeof (_a = typeof db_1.db !== \'undefined\' && db_1.db) === \'function\' && _a) || Object])\n` +
` ], MyClass);\n` +
` return MyClass;\n` +
` var _a;\n` +
`}());\n` +
`exports.MyClass = MyClass;\n`;
test(input,
{
options: {
compilerOptions: {
module: ModuleKind.CommonJS,
newLine: NewLineKind.LineFeed,
noEmitHelpers: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
target: ScriptTarget.ES5,
}
},
expectedOutput: output
});
});
it("Supports backslashes in file name", () => {
test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "a\\b.ts" }});
});
it("transpile file as 'tsx' if 'jsx' is specified", () => {
let input = `var x =
`;
let output = `"use strict";\nvar x = React.createElement("div", null);\n`;
test(input, {
expectedOutput: output,
options: { compilerOptions: { jsx: JsxEmit.React, newLine: NewLineKind.LineFeed } }
})
});
});
}