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/http': resolveNpmTreeArtifact('angular/packages/http/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'),
 | 
						|
 | 
						|
  // 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('npm/node_modules/zone.js'),
 | 
						|
};
 | 
						|
 | 
						|
/** 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());
 | 
						|
}
 |