mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
The View Engine ngc tests currently rely on the `npm_package` output that is built locally. This becomes problematic with the new Angular Package Format v13 where no metadata files are shipped. To continue being able to test View Engine compilation, we will use the v12.x framework packages for running the View Engine test. Note: This means that we no longer test metadata extraction directly for our framework packages, but given that any change to View Engine will still land in patch, where the VE packaging still occurs, we should be covered here. PR Close #43431
197 lines
6.8 KiB
TypeScript
197 lines
6.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC 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
|
|
*/
|
|
/// <reference types="node" />
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as ts from 'typescript';
|
|
|
|
import * as ng from '../index';
|
|
import {NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system';
|
|
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '../src/ngtsc/testing';
|
|
|
|
// TEST_TMPDIR is always set by Bazel.
|
|
const tmpdir = process.env.TEST_TMPDIR!;
|
|
|
|
export function makeTempDir(): string {
|
|
let dir: string;
|
|
while (true) {
|
|
const id = (Math.random() * 1000000).toFixed(0);
|
|
dir = path.posix.join(tmpdir, `tmp.${id}`);
|
|
if (!fs.existsSync(dir)) break;
|
|
}
|
|
fs.mkdirSync(dir);
|
|
return dir;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
function createTestSupportFor(basePath: string) {
|
|
// 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,
|
|
'newLine': ts.NewLineKind.LineFeed,
|
|
'module': ts.ModuleKind.ES2015,
|
|
'moduleResolution': ts.ModuleResolutionKind.NodeJs,
|
|
'enableIvy': false,
|
|
'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
|
|
};
|
|
|
|
|
|
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
|
|
};
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
function write(fileName: string, content: string) {
|
|
const absolutePathToFile = path.resolve(basePath, fileName);
|
|
ensureDirExists(path.dirname(absolutePathToFile));
|
|
fs.writeFileSync(absolutePathToFile, content);
|
|
}
|
|
|
|
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 {
|
|
return {...defaultCompilerOptions, ...overrideOptions};
|
|
}
|
|
|
|
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})`);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function setupBazelTo(tmpDirPath: string) {
|
|
const nodeModulesPath = path.join(tmpDirPath, 'node_modules');
|
|
const angularDirectory = path.join(nodeModulesPath, '@angular');
|
|
|
|
fs.mkdirSync(nodeModulesPath);
|
|
fs.mkdirSync(angularDirectory);
|
|
|
|
function linkNpmArtifact(artifact: string, target: string = artifact) {
|
|
try {
|
|
const source = resolveNpmTreeArtifact(`npm/node_modules/${artifact}`);
|
|
const dest = path.join(nodeModulesPath, target);
|
|
fs.symlinkSync(source, dest, 'junction');
|
|
} catch (e) {
|
|
// Allow a module to be missing as some Bazel targets do not need all artifacts.
|
|
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
}
|
|
}
|
|
|
|
// Link @angular packages. These reference the v12 packages from NPM as the ViewEngine tests
|
|
// can no longer depend on `npm_package` Bazel targets, given that the `ng_module` to produce
|
|
// them is no longer capable of producing ViewEngine packages. As a result, the `npm_package`
|
|
// outputs end up being compiled using Ivy partial compilation format which is not suitable to be
|
|
// used in ViewEngine ngc tests.
|
|
linkNpmArtifact('@angular/core-12', '@angular/core');
|
|
linkNpmArtifact('@angular/common-12', '@angular/common');
|
|
linkNpmArtifact('@angular/router-12', '@angular/router');
|
|
linkNpmArtifact('@angular/platform-browser-12', '@angular/platform-browser');
|
|
|
|
// Link typescript
|
|
linkNpmArtifact('typescript');
|
|
|
|
// 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, 'junction');
|
|
} catch (e) {
|
|
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
}
|
|
}
|
|
|
|
export function setup(): TestSupport {
|
|
// // `TestSupport` provides its own file-system abstraction so we just use
|
|
// // the native `NodeJSFileSystem` under the hood.
|
|
setFileSystem(new NodeJSFileSystem());
|
|
const tmpDirPath = makeTempDir();
|
|
setupBazelTo(tmpDirPath);
|
|
return createTestSupportFor(tmpDirPath);
|
|
}
|
|
|
|
export function expectNoDiagnostics(options: ng.CompilerOptions, diags: ng.Diagnostics) {
|
|
const errorDiags = diags.filter(d => d.category !== ts.DiagnosticCategory.Message);
|
|
if (errorDiags.length) {
|
|
throw new Error(`Expected no diagnostics: ${ng.formatDiagnostics(errorDiags)}`);
|
|
}
|
|
}
|
|
|
|
export function expectNoDiagnosticsInProgram(options: ng.CompilerOptions, p: ng.Program) {
|
|
expectNoDiagnostics(options, [
|
|
...p.getNgStructuralDiagnostics(), ...p.getTsSemanticDiagnostics(),
|
|
...p.getNgSemanticDiagnostics()
|
|
]);
|
|
}
|
|
|
|
export function normalizeSeparators(path: string): string {
|
|
return path.replace(/\\/g, '/');
|
|
}
|
|
|
|
const STRIP_ANSI = /\x1B\x5B\d+m/g;
|
|
|
|
export function stripAnsi(diags: string): string {
|
|
return diags.replace(STRIP_ANSI, '');
|
|
}
|