1namespace ts { 2 describe("unittests:: services:: Transpile", () => { 3 4 interface TranspileTestSettings { 5 options?: TranspileOptions; 6 noSetFileName?: boolean; 7 } 8 9 function transpilesCorrectly(name: string, input: string, testSettings: TranspileTestSettings) { 10 describe(name, () => { 11 let transpileResult: TranspileOutput; 12 let oldTranspileResult: string; 13 let oldTranspileDiagnostics: Diagnostic[]; 14 15 const transpileOptions: TranspileOptions = testSettings.options || {}; 16 if (!transpileOptions.compilerOptions) { 17 transpileOptions.compilerOptions = { }; 18 } 19 if (transpileOptions.compilerOptions.target === undefined) { 20 transpileOptions.compilerOptions.target = ScriptTarget.ES3; 21 } 22 23 if (transpileOptions.compilerOptions.newLine === undefined) { 24 // use \r\n as default new line 25 transpileOptions.compilerOptions.newLine = NewLineKind.CarriageReturnLineFeed; 26 } 27 28 transpileOptions.compilerOptions.sourceMap = true; 29 30 let unitName = transpileOptions.fileName; 31 if (!unitName) { 32 unitName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts"; 33 if (!testSettings.noSetFileName) { 34 transpileOptions.fileName = unitName; 35 } 36 } 37 38 transpileOptions.reportDiagnostics = true; 39 40 const justName = "transpile/" + name.replace(/[^a-z0-9\-. ]/ig, "") + (transpileOptions.compilerOptions.jsx ? Extension.Tsx : Extension.Ts); 41 const toBeCompiled = [{ 42 unitName, 43 content: input 44 }]; 45 const canUseOldTranspile = !transpileOptions.renamedDependencies; 46 47 before(() => { 48 transpileResult = transpileModule(input, transpileOptions); 49 50 if (canUseOldTranspile) { 51 oldTranspileDiagnostics = []; 52 oldTranspileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, oldTranspileDiagnostics, transpileOptions.moduleName); 53 } 54 }); 55 56 after(() => { 57 transpileResult = undefined!; 58 oldTranspileResult = undefined!; 59 oldTranspileDiagnostics = undefined!; 60 }); 61 62 /* eslint-disable no-null/no-null */ 63 it("Correct errors for " + justName, () => { 64 Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".errors.txt"), 65 transpileResult.diagnostics!.length === 0 ? null : Harness.Compiler.getErrorBaseline(toBeCompiled, transpileResult.diagnostics!)); 66 }); 67 68 if (canUseOldTranspile) { 69 it("Correct errors (old transpile) for " + justName, () => { 70 Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".oldTranspile.errors.txt"), 71 oldTranspileDiagnostics.length === 0 ? null : Harness.Compiler.getErrorBaseline(toBeCompiled, oldTranspileDiagnostics)); 72 }); 73 } 74 /* eslint-enable no-null/no-null */ 75 76 it("Correct output for " + justName, () => { 77 Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, Extension.Js), transpileResult.outputText); 78 }); 79 80 if (canUseOldTranspile) { 81 it("Correct output (old transpile) for " + justName, () => { 82 Harness.Baseline.runBaseline(justName.replace(/\.tsx?$/, ".oldTranspile.js"), oldTranspileResult); 83 }); 84 } 85 }); 86 } 87 88 transpilesCorrectly("Generates no diagnostics with valid inputs", `var x = 0;`, { 89 options: { compilerOptions: { module: ModuleKind.CommonJS } } 90 }); 91 92 transpilesCorrectly("Generates no diagnostics for missing file references", `/// <reference path="file2.ts" /> 93var x = 0;`, { 94 options: { compilerOptions: { module: ModuleKind.CommonJS } } 95 }); 96 97 transpilesCorrectly("Generates no diagnostics for missing module imports", `import {a} from "module2";`, { 98 options: { compilerOptions: { module: ModuleKind.CommonJS } } 99 }); 100 101 transpilesCorrectly("Generates expected syntactic diagnostics", `a b`, { 102 options: { compilerOptions: { module: ModuleKind.CommonJS } } 103 }); 104 105 transpilesCorrectly("Does not generate semantic diagnostics", `var x: string = 0;`, { 106 options: { compilerOptions: { module: ModuleKind.CommonJS } } 107 }); 108 109 transpilesCorrectly("Generates module output", `var x = 0;`, { 110 options: { compilerOptions: { module: ModuleKind.AMD } } 111 }); 112 113 transpilesCorrectly("Uses correct newLine character", `var x = 0;`, { 114 options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } } 115 }); 116 117 transpilesCorrectly("Sets module name", "var x = 1;", { 118 options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" } 119 }); 120 121 transpilesCorrectly("No extra errors for file without extension", `"use strict";\r\nvar x = 0;`, { 122 options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" } 123 }); 124 125 transpilesCorrectly("Rename dependencies - System", 126 `import {foo} from "SomeName";\n` + 127 `declare function use(a: any);\n` + 128 `use(foo);`, { 129 options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { SomeName: "SomeOtherName" } } 130 }); 131 132 transpilesCorrectly("Rename dependencies - AMD", 133 `import {foo} from "SomeName";\n` + 134 `declare function use(a: any);\n` + 135 `use(foo);`, { 136 options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { SomeName: "SomeOtherName" } } 137 }); 138 139 transpilesCorrectly("Rename dependencies - UMD", 140 `import {foo} from "SomeName";\n` + 141 `declare function use(a: any);\n` + 142 `use(foo);`, { 143 options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { SomeName: "SomeOtherName" } } 144 }); 145 146 transpilesCorrectly("Transpile with emit decorators and emit metadata", 147 `import {db} from './db';\n` + 148 `function someDecorator(target) {\n` + 149 ` return target;\n` + 150 `} \n` + 151 `@someDecorator\n` + 152 `class MyClass {\n` + 153 ` db: db;\n` + 154 ` constructor(db: db) {\n` + 155 ` this.db = db;\n` + 156 ` this.db.doSomething(); \n` + 157 ` }\n` + 158 `}\n` + 159 `export {MyClass}; \n`, { 160 options: { 161 compilerOptions: { 162 module: ModuleKind.CommonJS, 163 newLine: NewLineKind.LineFeed, 164 noEmitHelpers: true, 165 emitDecoratorMetadata: true, 166 experimentalDecorators: true, 167 target: ScriptTarget.ES5, 168 } 169 } 170 }); 171 172 transpilesCorrectly("Supports backslashes in file name", "var x", { 173 options: { fileName: "a\\b.ts" } 174 }); 175 176 transpilesCorrectly("transpile file as 'tsx' if 'jsx' is specified", `var x = <div/>`, { 177 options: { compilerOptions: { jsx: JsxEmit.React, newLine: NewLineKind.LineFeed } } 178 }); 179 180 transpilesCorrectly("transpile .js files", "const a = 10;", { 181 options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } 182 }); 183 184 transpilesCorrectly("Supports urls in file name", "var x", { 185 options: { fileName: "http://somewhere/directory//directory2/file.ts" } 186 }); 187 188 transpilesCorrectly("Accepts string as enum values for compile-options", "export const x = 0", { 189 options: { 190 compilerOptions: { 191 module: "es6" as any as ModuleKind, 192 // Capitalization and spaces ignored 193 target: " Es6 " as any as ScriptTarget 194 } 195 } 196 }); 197 198 transpilesCorrectly("Report an error when compiler-options module-kind is out-of-range", "", { 199 options: { compilerOptions: { module: 123 as any as ModuleKind } } 200 }); 201 202 transpilesCorrectly("Report an error when compiler-options target-script is out-of-range", "", { 203 options: { compilerOptions: { module: 123 as any as ModuleKind } } 204 }); 205 206 transpilesCorrectly("Support options with lib values", "const a = 10;", { 207 options: { compilerOptions: { lib: ["es6", "dom"], module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } 208 }); 209 210 transpilesCorrectly("Support options with types values", "const a = 10;", { 211 options: { compilerOptions: { types: ["jquery", "typescript"], module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } 212 }); 213 214 transpilesCorrectly("Supports setting 'allowJs'", "x;", { 215 options: { compilerOptions: { allowJs: true }, fileName: "input.js", reportDiagnostics: true } 216 }); 217 218 transpilesCorrectly("Supports setting 'allowSyntheticDefaultImports'", "x;", { 219 options: { compilerOptions: { allowSyntheticDefaultImports: true }, fileName: "input.js", reportDiagnostics: true } 220 }); 221 222 transpilesCorrectly("Supports setting 'allowUnreachableCode'", "x;", { 223 options: { compilerOptions: { allowUnreachableCode: true }, fileName: "input.js", reportDiagnostics: true } 224 }); 225 226 transpilesCorrectly("Supports setting 'allowUnusedLabels'", "x;", { 227 options: { compilerOptions: { allowUnusedLabels: true }, fileName: "input.js", reportDiagnostics: true } 228 }); 229 230 transpilesCorrectly("Supports setting 'alwaysStrict'", "x;", { 231 options: { compilerOptions: { alwaysStrict: true }, fileName: "input.js", reportDiagnostics: true } 232 }); 233 234 transpilesCorrectly("Supports setting 'baseUrl'", "x;", { 235 options: { compilerOptions: { baseUrl: "./folder/baseUrl" }, fileName: "input.js", reportDiagnostics: true } 236 }); 237 238 transpilesCorrectly("Supports setting 'charset'", "x;", { 239 options: { compilerOptions: { charset: "en-us" }, fileName: "input.js", reportDiagnostics: true } 240 }); 241 242 transpilesCorrectly("Supports setting 'declaration'", "x;", { 243 options: { compilerOptions: { declaration: true }, fileName: "input.js", reportDiagnostics: true } 244 }); 245 246 transpilesCorrectly("Supports setting 'declarationDir'", "x;", { 247 options: { compilerOptions: { declarationDir: "out/declarations" }, fileName: "input.js", reportDiagnostics: true } 248 }); 249 250 transpilesCorrectly("Supports setting 'emitBOM'", "x;", { 251 options: { compilerOptions: { emitBOM: true }, fileName: "input.js", reportDiagnostics: true } 252 }); 253 254 transpilesCorrectly("Supports setting 'emitDecoratorMetadata'", "x;", { 255 options: { compilerOptions: { emitDecoratorMetadata: true, experimentalDecorators: true }, fileName: "input.js", reportDiagnostics: true } 256 }); 257 258 transpilesCorrectly("Supports setting 'experimentalDecorators'", "x;", { 259 options: { compilerOptions: { experimentalDecorators: true }, fileName: "input.js", reportDiagnostics: true } 260 }); 261 262 transpilesCorrectly("Supports setting 'forceConsistentCasingInFileNames'", "x;", { 263 options: { compilerOptions: { forceConsistentCasingInFileNames: true }, fileName: "input.js", reportDiagnostics: true } 264 }); 265 266 transpilesCorrectly("Supports setting 'isolatedModules'", "x;", { 267 options: { compilerOptions: { isolatedModules: true }, fileName: "input.js", reportDiagnostics: true } 268 }); 269 270 transpilesCorrectly("Supports setting 'jsx'", "x;", { 271 options: { compilerOptions: { jsx: 1 }, fileName: "input.js", reportDiagnostics: true } 272 }); 273 274 transpilesCorrectly("Supports setting 'lib'", "x;", { 275 options: { compilerOptions: { lib: ["es2015", "dom"] }, fileName: "input.js", reportDiagnostics: true } 276 }); 277 278 transpilesCorrectly("Supports setting 'locale'", "x;", { 279 options: { compilerOptions: { locale: "en-us" }, fileName: "input.js", reportDiagnostics: true } 280 }); 281 282 transpilesCorrectly("Supports setting 'module'", "x;", { 283 options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } 284 }); 285 286 transpilesCorrectly("Supports setting 'moduleResolution'", "x;", { 287 options: { compilerOptions: { moduleResolution: ModuleResolutionKind.NodeJs }, fileName: "input.js", reportDiagnostics: true } 288 }); 289 290 transpilesCorrectly("Supports setting 'newLine'", "x;", { 291 options: { compilerOptions: { newLine: NewLineKind.CarriageReturnLineFeed }, fileName: "input.js", reportDiagnostics: true } 292 }); 293 294 transpilesCorrectly("Supports setting 'noEmit'", "x;", { 295 options: { compilerOptions: { noEmit: true }, fileName: "input.js", reportDiagnostics: true } 296 }); 297 298 transpilesCorrectly("Supports setting 'noEmitHelpers'", "x;", { 299 options: { compilerOptions: { noEmitHelpers: true }, fileName: "input.js", reportDiagnostics: true } 300 }); 301 302 transpilesCorrectly("Supports setting 'noEmitOnError'", "x;", { 303 options: { compilerOptions: { noEmitOnError: true }, fileName: "input.js", reportDiagnostics: true } 304 }); 305 306 transpilesCorrectly("Supports setting 'noErrorTruncation'", "x;", { 307 options: { compilerOptions: { noErrorTruncation: true }, fileName: "input.js", reportDiagnostics: true } 308 }); 309 310 transpilesCorrectly("Supports setting 'noFallthroughCasesInSwitch'", "x;", { 311 options: { compilerOptions: { noFallthroughCasesInSwitch: true }, fileName: "input.js", reportDiagnostics: true } 312 }); 313 314 transpilesCorrectly("Supports setting 'noImplicitAny'", "x;", { 315 options: { compilerOptions: { noImplicitAny: true }, fileName: "input.js", reportDiagnostics: true } 316 }); 317 318 transpilesCorrectly("Supports setting 'noImplicitReturns'", "x;", { 319 options: { compilerOptions: { noImplicitReturns: true }, fileName: "input.js", reportDiagnostics: true } 320 }); 321 322 transpilesCorrectly("Supports setting 'noImplicitThis'", "x;", { 323 options: { compilerOptions: { noImplicitThis: true }, fileName: "input.js", reportDiagnostics: true } 324 }); 325 326 transpilesCorrectly("Supports setting 'noImplicitUseStrict'", "x;", { 327 options: { compilerOptions: { noImplicitUseStrict: true }, fileName: "input.js", reportDiagnostics: true } 328 }); 329 330 transpilesCorrectly("Supports setting 'noLib'", "x;", { 331 options: { compilerOptions: { noLib: true }, fileName: "input.js", reportDiagnostics: true } 332 }); 333 334 transpilesCorrectly("Supports setting 'noResolve'", "x;", { 335 options: { compilerOptions: { noResolve: true }, fileName: "input.js", reportDiagnostics: true } 336 }); 337 338 transpilesCorrectly("Supports setting 'out'", "x;", { 339 options: { compilerOptions: { out: "./out" }, fileName: "input.js", reportDiagnostics: true } 340 }); 341 342 transpilesCorrectly("Supports setting 'outDir'", "x;", { 343 options: { compilerOptions: { outDir: "./outDir" }, fileName: "input.js", reportDiagnostics: true } 344 }); 345 346 transpilesCorrectly("Supports setting 'outFile'", "x;", { 347 options: { compilerOptions: { outFile: "./outFile" }, fileName: "input.js", reportDiagnostics: true } 348 }); 349 350 transpilesCorrectly("Supports setting 'paths'", "x;", { 351 options: { compilerOptions: { paths: { "*": ["./generated*"] } }, fileName: "input.js", reportDiagnostics: true } 352 }); 353 354 transpilesCorrectly("Supports setting 'preserveConstEnums'", "x;", { 355 options: { compilerOptions: { preserveConstEnums: true }, fileName: "input.js", reportDiagnostics: true } 356 }); 357 358 transpilesCorrectly("Supports setting 'reactNamespace'", "x;", { 359 options: { compilerOptions: { reactNamespace: "react" }, fileName: "input.js", reportDiagnostics: true } 360 }); 361 362 transpilesCorrectly("Supports setting 'jsxFactory'", "x;", { 363 options: { compilerOptions: { jsxFactory: "createElement" }, fileName: "input.js", reportDiagnostics: true } 364 }); 365 366 transpilesCorrectly("Supports setting 'jsxFragmentFactory'", "x;", { 367 options: { compilerOptions: { jsxFactory: "x", jsxFragmentFactory: "frag" }, fileName: "input.js", reportDiagnostics: true } 368 }); 369 370 transpilesCorrectly("Supports setting 'removeComments'", "x;", { 371 options: { compilerOptions: { removeComments: true }, fileName: "input.js", reportDiagnostics: true } 372 }); 373 374 transpilesCorrectly("Supports setting 'rootDir'", "x;", { 375 options: { compilerOptions: { rootDir: "./rootDir" }, fileName: "./rootDir/input.js", reportDiagnostics: true } 376 }); 377 378 transpilesCorrectly("Supports setting 'rootDirs'", "x;", { 379 options: { compilerOptions: { rootDirs: ["./a", "./b"] }, fileName: "input.js", reportDiagnostics: true } 380 }); 381 382 transpilesCorrectly("Supports setting 'skipLibCheck'", "x;", { 383 options: { compilerOptions: { skipLibCheck: true }, fileName: "input.js", reportDiagnostics: true } 384 }); 385 386 transpilesCorrectly("Supports setting 'skipDefaultLibCheck'", "x;", { 387 options: { compilerOptions: { skipDefaultLibCheck: true }, fileName: "input.js", reportDiagnostics: true } 388 }); 389 390 transpilesCorrectly("Supports setting 'strictNullChecks'", "x;", { 391 options: { compilerOptions: { strictNullChecks: true }, fileName: "input.js", reportDiagnostics: true } 392 }); 393 394 transpilesCorrectly("Supports setting 'stripInternal'", "x;", { 395 options: { compilerOptions: { stripInternal: true }, fileName: "input.js", reportDiagnostics: true } 396 }); 397 398 transpilesCorrectly("Supports setting 'suppressExcessPropertyErrors'", "x;", { 399 options: { compilerOptions: { suppressExcessPropertyErrors: true }, fileName: "input.js", reportDiagnostics: true } 400 }); 401 402 transpilesCorrectly("Supports setting 'suppressImplicitAnyIndexErrors'", "x;", { 403 options: { compilerOptions: { suppressImplicitAnyIndexErrors: true }, fileName: "input.js", reportDiagnostics: true } 404 }); 405 406 transpilesCorrectly("Supports setting 'target'", "x;", { 407 options: { compilerOptions: { target: 2 }, fileName: "input.js", reportDiagnostics: true } 408 }); 409 410 transpilesCorrectly("Supports setting 'types'", "x;", { 411 options: { compilerOptions: { types: ["jquery", "jasmine"] }, fileName: "input.js", reportDiagnostics: true } 412 }); 413 414 transpilesCorrectly("Supports setting 'typeRoots'", "x;", { 415 options: { compilerOptions: { typeRoots: ["./folder"] }, fileName: "input.js", reportDiagnostics: true } 416 }); 417 418 transpilesCorrectly("Supports setting 'incremental'", "x;", { 419 options: { compilerOptions: { incremental: true }, fileName: "input.js", reportDiagnostics: true } 420 }); 421 422 transpilesCorrectly("Supports setting 'composite'", "x;", { 423 options: { compilerOptions: { composite: true }, fileName: "input.js", reportDiagnostics: true } 424 }); 425 426 transpilesCorrectly("Supports setting 'tsbuildinfo'", "x;", { 427 options: { compilerOptions: { incremental: true, tsBuildInfoFile: "./folder/config.tsbuildinfo" }, fileName: "input.js", reportDiagnostics: true } 428 }); 429 430 transpilesCorrectly("Correctly serialize metadata when transpile with CommonJS option", 431 `import * as ng from "angular2/core";` + 432 `declare function foo(...args: any[]);` + 433 `@foo` + 434 `export class MyClass1 {` + 435 ` constructor(private _elementRef: ng.ElementRef){}` + 436 `}`, { 437 options: { 438 compilerOptions: { 439 target: ScriptTarget.ES5, 440 module: ModuleKind.CommonJS, 441 moduleResolution: ModuleResolutionKind.NodeJs, 442 emitDecoratorMetadata: true, 443 experimentalDecorators: true, 444 isolatedModules: true, 445 } 446 } 447 } 448 ); 449 450 transpilesCorrectly("Correctly serialize metadata when transpile with System option", 451 `import * as ng from "angular2/core";` + 452 `declare function foo(...args: any[]);` + 453 `@foo` + 454 `export class MyClass1 {` + 455 ` constructor(private _elementRef: ng.ElementRef){}` + 456 `}`, { 457 options: { 458 compilerOptions: { 459 target: ScriptTarget.ES5, 460 module: ModuleKind.System, 461 moduleResolution: ModuleResolutionKind.NodeJs, 462 emitDecoratorMetadata: true, 463 experimentalDecorators: true, 464 isolatedModules: true, 465 } 466 } 467 } 468 ); 469 470 transpilesCorrectly("Supports readonly keyword for arrays", "let x: readonly string[];", { 471 options: { compilerOptions: { module: ModuleKind.CommonJS } } 472 }); 473 474 transpilesCorrectly("Supports 'as const' arrays", `([] as const).forEach(k => console.log(k));`, { 475 options: { compilerOptions: { module: ModuleKind.CommonJS } } 476 }); 477 478 transpilesCorrectly("Infer correct file extension", `const fn = <T>(a: T) => a`, { 479 noSetFileName: true 480 }); 481 482 transpilesCorrectly("Export star as ns conflict does not crash", ` 483var a; 484export { a as alias }; 485export * as alias from './file';`, { 486 noSetFileName: true 487 }); 488 489 transpilesCorrectly("Elides import equals referenced only by export type", 490 `import IFoo = Namespace.IFoo;` + 491 `export type { IFoo };`, { 492 options: { compilerOptions: { module: ModuleKind.CommonJS } } 493 } 494 ); 495 496 transpilesCorrectly("Elides import equals referenced only by type only export specifier", 497 `import IFoo = Namespace.IFoo;` + 498 `export { type IFoo };`, { 499 options: { compilerOptions: { module: ModuleKind.CommonJS } } 500 } 501 ); 502 }); 503} 504