• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env node
2/**
3 * Usage:
4 *   test-npm-package.js [--install] [--rebuild] <source> <test-arg>+
5 *
6 * Everything after the <source> directory gets passed to `npm run` to build
7 * the test command.
8 *
9 * If `--install` is passed, we'll run a full `npm install` before running the
10 * test suite. Same for `--rebuild` and `npm rebuild`.
11 *
12 * We always use the node used to spawn this script and the `npm` version
13 * bundled in `deps/npm`.
14 *
15 * If an additional `--logfile=<filename>` option is passed before `<source>`,
16 * the stdout output of the test script will be written to that file.
17 */
18'use strict';
19const { spawn, spawnSync } = require('child_process');
20const { createHash } = require('crypto');
21const { createWriteStream, mkdirSync, rmdirSync } = require('fs');
22const path = require('path');
23
24const common = require('../test/common');
25const tmpDir = require('../test/common/tmpdir');
26
27const projectDir = path.resolve(__dirname, '..');
28const npmBin = path.join(projectDir, 'deps', 'npm', 'bin', 'npm-cli.js');
29const nodePath = path.dirname(process.execPath);
30
31function spawnCopyDeepSync(source, destination) {
32  if (common.isWindows) {
33    mkdirSync(destination); // Prevent interactive prompt
34    return spawnSync('xcopy.exe', ['/E', source, destination]);
35  }
36  return spawnSync('cp', ['-r', `${source}/`, destination]);
37}
38
39function runNPMPackageTests({ srcDir, install, rebuild, testArgs, logfile }) {
40  // Make sure we don't conflict with concurrent test runs
41  const srcHash = createHash('md5').update(srcDir).digest('hex');
42  tmpDir.path = `${tmpDir.path}.npm.${srcHash}`;
43  tmpDir.refresh();
44
45  const npmCache = path.join(tmpDir.path, 'npm-cache');
46  const npmPrefix = path.join(tmpDir.path, 'npm-prefix');
47  const npmTmp = path.join(tmpDir.path, 'npm-tmp');
48  const npmUserconfig = path.join(tmpDir.path, 'npm-userconfig');
49  const pkgDir = path.join(tmpDir.path, 'pkg');
50
51  spawnCopyDeepSync(srcDir, pkgDir);
52
53  const npmOptions = {
54    cwd: pkgDir,
55    env: Object.assign({}, process.env, {
56      'npm_config_cache': npmCache,
57      'npm_config_prefix': npmPrefix,
58      'npm_config_tmp': npmTmp,
59      'npm_config_userconfig': npmUserconfig,
60    }),
61    stdio: 'inherit',
62  };
63
64  if (common.isWindows) {
65    npmOptions.env.home = tmpDir.path;
66    npmOptions.env.Path = `${nodePath};${process.env.Path}`;
67  } else {
68    npmOptions.env.HOME = tmpDir.path;
69    npmOptions.env.PATH = `${nodePath}:${process.env.PATH}`;
70  }
71
72  if (rebuild) {
73    spawnSync(process.execPath, [
74      npmBin,
75      'rebuild',
76    ], npmOptions);
77  }
78
79  if (install) {
80    spawnSync(process.execPath, [
81      npmBin,
82      'install',
83      '--ignore-scripts',
84      '--no-save',
85    ], npmOptions);
86  }
87
88  const testChild = spawn(process.execPath, [
89    npmBin,
90    '--silent',
91    'run',
92    ...testArgs,
93  ], Object.assign({}, npmOptions, { stdio: 'pipe' }));
94
95  testChild.stdout.pipe(process.stdout);
96  testChild.stderr.pipe(process.stderr);
97
98  if (logfile) {
99    const logStream = createWriteStream(logfile);
100    testChild.stdout.pipe(logStream);
101  }
102
103  testChild.on('exit', () => {
104    tmpDir.refresh();
105    rmdirSync(tmpDir.path);
106  });
107}
108
109function parseArgs(args) {
110  let srcDir;
111  let rebuild = false;
112  let install = false;
113  let logfile = null;
114  const testArgs = [];
115  args.forEach((arg) => {
116    if (srcDir) {
117      testArgs.push(arg);
118      return;
119    }
120
121    if (arg === '--install') {
122      install = true;
123    } else if (arg === '--rebuild') {
124      rebuild = true;
125    } else if (arg[0] !== '-') {
126      srcDir = path.resolve(projectDir, arg);
127    } else if (arg.startsWith('--logfile=')) {
128      logfile = path.resolve(projectDir, arg.slice('--logfile='.length));
129    } else {
130      throw new Error(`Unrecognized option ${arg}`);
131    }
132  });
133  if (!srcDir) {
134    throw new Error('Expected a source directory');
135  }
136  return { srcDir, install, rebuild, testArgs, logfile };
137}
138
139runNPMPackageTests(parseArgs(process.argv.slice(2)));
140