2017-09-13 19:55:42 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
import * as fs from 'fs';
|
|
|
|
import * as path from 'path';
|
2017-09-19 14:43:34 -04:00
|
|
|
import * as ts from 'typescript';
|
|
|
|
import * as ng from '../index';
|
2019-01-25 13:48:27 -05:00
|
|
|
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from './runfile_helpers';
|
2017-09-13 19:55:42 -04:00
|
|
|
|
2019-01-25 13:44:49 -05:00
|
|
|
// TEST_TMPDIR is always set by Bazel.
|
2019-01-25 13:48:27 -05:00
|
|
|
const tmpdir = process.env.TEST_TMPDIR !;
|
2017-09-13 19:55:42 -04:00
|
|
|
|
|
|
|
export function makeTempDir(): string {
|
2017-12-11 11:50:46 -05:00
|
|
|
let dir: string;
|
|
|
|
while (true) {
|
|
|
|
const id = (Math.random() * 1000000).toFixed(0);
|
|
|
|
dir = path.join(tmpdir, `tmp.${id}`);
|
|
|
|
if (!fs.existsSync(dir)) break;
|
|
|
|
}
|
2017-09-13 19:55:42 -04:00
|
|
|
fs.mkdirSync(dir);
|
|
|
|
return dir;
|
2017-09-19 14:43:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface TestSupport {
|
|
|
|
basePath: string;
|
|
|
|
write(fileName: string, content: string): void;
|
|
|
|
writeFiles(...mockDirs: {[fileName: string]: string}[]): void;
|
|
|
|
createCompilerOptions(overrideOptions?: ng.CompilerOptions): ng.CompilerOptions;
|
|
|
|
shouldExist(fileName: string): void;
|
|
|
|
shouldNotExist(fileName: string): void;
|
|
|
|
}
|
|
|
|
|
2018-03-21 17:22:06 -04:00
|
|
|
function createTestSupportFor(basePath: string) {
|
2018-08-21 19:27:44 -04:00
|
|
|
// Typescript uses identity comparison on `paths` and other arrays in order to determine
|
|
|
|
// if program structure can be reused for incremental compilation, so we reuse the default
|
|
|
|
// values unless overriden, and freeze them so that they can't be accidentaly changed somewhere
|
|
|
|
// in tests.
|
|
|
|
const defaultCompilerOptions = {
|
|
|
|
basePath,
|
|
|
|
'experimentalDecorators': true,
|
|
|
|
'skipLibCheck': true,
|
|
|
|
'strict': true,
|
|
|
|
'strictPropertyInitialization': false,
|
|
|
|
'types': Object.freeze<string>([]) as string[],
|
|
|
|
'outDir': path.resolve(basePath, 'built'),
|
|
|
|
'rootDir': basePath,
|
|
|
|
'baseUrl': basePath,
|
|
|
|
'declaration': true,
|
|
|
|
'target': ts.ScriptTarget.ES5,
|
2019-01-25 13:48:27 -05:00
|
|
|
'newLine': ts.NewLineKind.LineFeed,
|
2018-08-21 19:27:44 -04:00
|
|
|
'module': ts.ModuleKind.ES2015,
|
|
|
|
'moduleResolution': ts.ModuleResolutionKind.NodeJs,
|
|
|
|
'lib': Object.freeze([
|
|
|
|
path.resolve(basePath, 'node_modules/typescript/lib/lib.es6.d.ts'),
|
|
|
|
]) as string[],
|
|
|
|
// clang-format off
|
|
|
|
'paths': Object.freeze({'@angular/*': ['./node_modules/@angular/*']}) as {[index: string]: string[]}
|
|
|
|
// clang-format on
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-01-25 13:48:27 -05:00
|
|
|
return {
|
|
|
|
// We normalize the basePath into a posix path, so that multiple assertions which compare
|
|
|
|
// paths don't need to normalize the path separators each time.
|
|
|
|
basePath: normalizeSeparators(basePath),
|
|
|
|
write,
|
|
|
|
writeFiles,
|
|
|
|
createCompilerOptions,
|
|
|
|
shouldExist,
|
|
|
|
shouldNotExist
|
|
|
|
};
|
2017-09-19 14:43:34 -04:00
|
|
|
|
2019-02-11 06:01:01 -05:00
|
|
|
function ensureDirExists(absolutePathToDir: string) {
|
|
|
|
if (fs.existsSync(absolutePathToDir)) {
|
|
|
|
if (!fs.statSync(absolutePathToDir).isDirectory()) {
|
|
|
|
throw new Error(`'${absolutePathToDir}' exists and is not a directory.`);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const parentDir = path.dirname(absolutePathToDir);
|
|
|
|
ensureDirExists(parentDir);
|
|
|
|
fs.mkdirSync(absolutePathToDir);
|
2017-09-19 14:43:34 -04:00
|
|
|
}
|
2019-02-11 06:01:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function write(fileName: string, content: string) {
|
|
|
|
const absolutePathToFile = path.resolve(basePath, fileName);
|
|
|
|
ensureDirExists(path.dirname(absolutePathToFile));
|
|
|
|
fs.writeFileSync(absolutePathToFile, content);
|
2017-09-19 14:43:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function writeFiles(...mockDirs: {[fileName: string]: string}[]) {
|
|
|
|
mockDirs.forEach(
|
|
|
|
(dir) => { Object.keys(dir).forEach((fileName) => { write(fileName, dir[fileName]); }); });
|
|
|
|
}
|
|
|
|
|
|
|
|
function createCompilerOptions(overrideOptions: ng.CompilerOptions = {}): ng.CompilerOptions {
|
2018-08-21 19:27:44 -04:00
|
|
|
return {...defaultCompilerOptions, ...overrideOptions};
|
2017-09-19 14:43:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function shouldExist(fileName: string) {
|
|
|
|
if (!fs.existsSync(path.resolve(basePath, fileName))) {
|
|
|
|
throw new Error(`Expected ${fileName} to be emitted (basePath: ${basePath})`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function shouldNotExist(fileName: string) {
|
|
|
|
if (fs.existsSync(path.resolve(basePath, fileName))) {
|
|
|
|
throw new Error(`Did not expect ${fileName} to be emitted (basePath: ${basePath})`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 13:48:27 -05:00
|
|
|
export function setupBazelTo(tmpDirPath: string) {
|
|
|
|
const nodeModulesPath = path.join(tmpDirPath, 'node_modules');
|
2018-03-21 17:22:06 -04:00
|
|
|
const angularDirectory = path.join(nodeModulesPath, '@angular');
|
|
|
|
|
2019-01-25 13:48:27 -05:00
|
|
|
fs.mkdirSync(nodeModulesPath);
|
2018-03-21 17:22:06 -04:00
|
|
|
fs.mkdirSync(angularDirectory);
|
|
|
|
|
2019-03-06 03:06:34 -05:00
|
|
|
getAngularPackagesFromRunfiles().forEach(({pkgPath, name}) => {
|
|
|
|
fs.symlinkSync(pkgPath, path.join(angularDirectory, name), 'dir');
|
|
|
|
|
|
|
|
// todo: check why we always need an index.d.ts
|
|
|
|
if (!fs.existsSync(path.join(angularDirectory, name, 'index.d.ts'))) {
|
|
|
|
fs.symlinkSync(
|
|
|
|
path.join(pkgPath, `${name}.d.ts`), path.join(angularDirectory, name, 'index.d.ts'));
|
|
|
|
}
|
|
|
|
});
|
2018-03-21 17:22:06 -04:00
|
|
|
|
|
|
|
// Link typescript
|
2019-02-20 12:54:42 -05:00
|
|
|
const typeScriptSource = resolveNpmTreeArtifact('npm/node_modules/typescript');
|
2018-03-21 17:22:06 -04:00
|
|
|
const typescriptDest = path.join(nodeModulesPath, 'typescript');
|
2019-01-25 13:48:27 -05:00
|
|
|
fs.symlinkSync(typeScriptSource, typescriptDest, 'dir');
|
|
|
|
|
|
|
|
// Link "rxjs" if it has been set up as a runfile. "rxjs" is linked optionally because
|
|
|
|
// not all compiler-cli tests need "rxjs" set up.
|
|
|
|
try {
|
|
|
|
const rxjsSource = resolveNpmTreeArtifact('rxjs', 'index.js');
|
|
|
|
const rxjsDest = path.join(nodeModulesPath, 'rxjs');
|
|
|
|
fs.symlinkSync(rxjsSource, rxjsDest, 'dir');
|
|
|
|
} catch (e) {
|
|
|
|
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
2018-03-21 17:22:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setup(): TestSupport {
|
2019-01-25 13:44:49 -05:00
|
|
|
const tmpDirPath = makeTempDir();
|
|
|
|
setupBazelTo(tmpDirPath);
|
|
|
|
return createTestSupportFor(tmpDirPath);
|
2018-03-21 17:22:06 -04:00
|
|
|
}
|
|
|
|
|
2017-09-19 14:43:34 -04:00
|
|
|
export function expectNoDiagnostics(options: ng.CompilerOptions, diags: ng.Diagnostics) {
|
2017-09-29 18:02:11 -04:00
|
|
|
const errorDiags = diags.filter(d => d.category !== ts.DiagnosticCategory.Message);
|
|
|
|
if (errorDiags.length) {
|
2017-10-16 12:31:25 -04:00
|
|
|
throw new Error(`Expected no diagnostics: ${ng.formatDiagnostics(errorDiags)}`);
|
2017-09-19 14:43:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function expectNoDiagnosticsInProgram(options: ng.CompilerOptions, p: ng.Program) {
|
|
|
|
expectNoDiagnostics(options, [
|
|
|
|
...p.getNgStructuralDiagnostics(), ...p.getTsSemanticDiagnostics(),
|
|
|
|
...p.getNgSemanticDiagnostics()
|
|
|
|
]);
|
|
|
|
}
|
2019-01-25 13:48:27 -05:00
|
|
|
|
|
|
|
export function normalizeSeparators(path: string): string {
|
|
|
|
return path.replace(/\\/g, '/');
|
|
|
|
}
|