164 lines
7.5 KiB
JavaScript
164 lines
7.5 KiB
JavaScript
/**
|
|
* @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
|
|
*/
|
|
|
|
const child_process = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const shx = require('shelljs');
|
|
|
|
/** Manifest path that refers to the Bazel package that contains all test sources. */
|
|
const baseManifestPath = 'angular/packages/compiler-cli/integrationtest';
|
|
|
|
/**
|
|
* Temporary directory which will be used to build and run the integration tests. Note that
|
|
* this environment variable is automatically set by Bazel for such test actions.
|
|
*/
|
|
const tmpDir = process.env.TEST_TMPDIR;
|
|
|
|
/** Fine grained node modules which are required in order to run the integration test. */
|
|
const requiredNodeModules = {
|
|
'@angular/animations': resolveNpmTreeArtifact('angular/packages/animations/npm_package'),
|
|
'@angular/common': resolveNpmTreeArtifact('angular/packages/common/npm_package'),
|
|
'@angular/compiler': resolveNpmTreeArtifact('angular/packages/compiler/npm_package'),
|
|
'@angular/compiler-cli': resolveNpmTreeArtifact('angular/packages/compiler-cli/npm_package'),
|
|
'@angular/core': resolveNpmTreeArtifact('angular/packages/core/npm_package'),
|
|
'@angular/forms': resolveNpmTreeArtifact('angular/packages/forms/npm_package'),
|
|
'@angular/platform-browser':
|
|
resolveNpmTreeArtifact('angular/packages/platform-browser/npm_package'),
|
|
'@angular/platform-browser-dynamic':
|
|
resolveNpmTreeArtifact('angular/packages/platform-browser-dynamic/npm_package'),
|
|
'@angular/platform-server':
|
|
resolveNpmTreeArtifact('angular/packages/platform-server/npm_package'),
|
|
'@angular/router': resolveNpmTreeArtifact('angular/packages/router/npm_package'),
|
|
// Note, @bazel/typescript does not appear here because it's not listed as a dependency of
|
|
// @angular/compiler-cli
|
|
'@types/jasmine': resolveNpmTreeArtifact('npm/node_modules/@types/jasmine'),
|
|
'@types/node': resolveNpmTreeArtifact('npm/node_modules/@types/node'),
|
|
|
|
// Transitive dependencies which need to be specified because the Angular NPM packages
|
|
// depend on these without the Angular NPM packages being part of the Bazel managed deps.
|
|
// This means that transitive dependencies need to be manually declared as required.
|
|
'tslib': resolveNpmTreeArtifact('npm/node_modules/tslib'),
|
|
'domino': resolveNpmTreeArtifact('npm/node_modules/domino'),
|
|
'xhr2': resolveNpmTreeArtifact('npm/node_modules/xhr2'),
|
|
'fs-extra': resolveNpmTreeArtifact('npm/node_modules/fs-extra'),
|
|
|
|
// Fine grained dependencies which are used by the integration test Angular modules, and
|
|
// need to be symlinked so that they can be resolved by NodeJS or NGC.
|
|
'buffer-from': resolveNpmTreeArtifact('npm/node_modules/buffer-from'),
|
|
'reflect-metadata': resolveNpmTreeArtifact('npm/node_modules/reflect-metadata'),
|
|
'rxjs': resolveNpmTreeArtifact('npm/node_modules/rxjs'),
|
|
'source-map': resolveNpmTreeArtifact('npm/node_modules/source-map'),
|
|
'source-map-support': resolveNpmTreeArtifact('npm/node_modules/source-map-support'),
|
|
'typescript': resolveNpmTreeArtifact('npm/node_modules/typescript'),
|
|
'zone.js': resolveNpmTreeArtifact('angular/packages/zone.js/npm_package'),
|
|
};
|
|
|
|
/** Sets up the temporary test directory and returns the path to the directory. */
|
|
exports.setupTestDirectory = function() {
|
|
copySourceFilesToTempDir();
|
|
symlinkNodeModules();
|
|
return tmpDir;
|
|
};
|
|
|
|
/**
|
|
* Runs a given binary with the specified command line arguments. The working directory for
|
|
* the spawned process will be the temporary directory.
|
|
*/
|
|
exports.runCommand = function runCommand(binary, args = []) {
|
|
const ngcProcess = child_process.spawnSync(binary, args, {
|
|
stdio: 'inherit',
|
|
cwd: tmpDir,
|
|
env: {
|
|
...process.env,
|
|
// We need to set the "NODE_PATH" here because the built Angular NPM packages are symlinks
|
|
// which NodeJS resolves into the output location. This is problematic because the output
|
|
// location does not have the required dependencies of these NPM packages installed. This
|
|
// could be fixed by setting the NodeJS "--preserve-symlinks" option, but this would mean
|
|
// that transitive dependencies of fine-grained dependencies cannot be resolved either.
|
|
NODE_PATH: path.join(tmpDir, 'node_modules/')
|
|
}
|
|
});
|
|
|
|
if (ngcProcess.status !== 0) {
|
|
console.error(`Command ${binary} failed with arguments: "${args.join(' ')}". See error above.`);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Symlinks the specified node modules within the temporary directory. This is necessary because
|
|
* this test is an integration test and we don't want to rely on any path-mapped module resolution.
|
|
* Additionally, NGC expects types and imported packages to be within the project's root dir.
|
|
*/
|
|
function symlinkNodeModules() {
|
|
Object.keys(requiredNodeModules).forEach(importName => {
|
|
const outputPath = path.join(tmpDir, 'node_modules', importName);
|
|
const moduleDir = requiredNodeModules[importName];
|
|
shx.mkdir('-p', path.dirname(outputPath));
|
|
fs.symlinkSync(moduleDir, outputPath, 'junction');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Copies all source files for the integration test to a temporary directory. This
|
|
* is necessary because runfiles resolve on Windows to the original source location,
|
|
* and we don't want to pollute the workspace sources. This breaks hermeticity.
|
|
*/
|
|
function copySourceFilesToTempDir() {
|
|
getSourceFilesFromRunfiles().forEach(({realPath, relativeFilePath}) => {
|
|
const tmpFilePath = path.join(tmpDir, relativeFilePath);
|
|
|
|
shx.mkdir('-p', path.dirname(tmpFilePath));
|
|
shx.cp(realPath, tmpFilePath);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets all source files for the integration test by querying the Bazel runfiles.
|
|
* In case there is a runfiles manifest (e.g. on Windows), the source files are resolved
|
|
* through the manifest because on these platforms the runfiles are not symlinked and
|
|
* cannot be searched within the real filesystem.
|
|
*/
|
|
function getSourceFilesFromRunfiles() {
|
|
// Path to the Bazel runfiles manifest if present. This file is present if runfiles are
|
|
// not symlinked into the runfiles directory.
|
|
const runfilesManifestPath = process.env.RUNFILES_MANIFEST_FILE;
|
|
|
|
if (!runfilesManifestPath) {
|
|
const packageRunfilesDir = path.join(process.env.RUNFILES, baseManifestPath);
|
|
return findFilesWithinDirectory(packageRunfilesDir).map(filePath => ({
|
|
realPath: filePath,
|
|
relativeFilePath: path.relative(
|
|
packageRunfilesDir, filePath)
|
|
}));
|
|
}
|
|
|
|
return fs.readFileSync(runfilesManifestPath, 'utf8')
|
|
.split('\n')
|
|
.map(mapping => mapping.split(' '))
|
|
.filter(([runfilePath]) => runfilePath.startsWith(baseManifestPath))
|
|
.map(
|
|
([runfilePath, realPath]) =>
|
|
({realPath, relativeFilePath: path.relative(baseManifestPath, runfilePath)}));
|
|
}
|
|
|
|
/**
|
|
* Resolves a NPM package from the Bazel runfiles. We need to resolve the Bazel tree
|
|
* artifacts using a "resolve file" because the NodeJS module resolution does not allow
|
|
* resolving to directory paths.
|
|
*/
|
|
function resolveNpmTreeArtifact(manifestPath, resolveFile = 'package.json') {
|
|
return path.dirname(require.resolve(path.posix.join(manifestPath, resolveFile)));
|
|
}
|
|
|
|
/** Finds all files within a specified directory. */
|
|
function findFilesWithinDirectory(directoryPath) {
|
|
return shx.find(directoryPath).filter(filePath => !fs.lstatSync(filePath).isDirectory());
|
|
}
|