UNPKG

19.5 kBJavaScriptView Raw
1#!/usr/bin/env node
2'use strict';
3Object.defineProperty(exports, "__esModule", { value: true });
4debugger;
5var process = require('suman-browser-polyfills/modules/process');
6var global = require('suman-browser-polyfills/modules/global');
7var callable = true;
8var logExit = function (code) {
9 if (callable) {
10 callable = false;
11 console.log('\n');
12 console.log(' => Suman cli exiting with code: ', code);
13 console.log('\n');
14 }
15};
16process.once('exit', function (code) {
17 if (!global.__suman || !global.__suman.isActualExitHandlerRegistered) {
18 logExit(code);
19 }
20});
21if (require.main !== module && process.env.SUMAN_EXTRANEOUS_EXECUTABLE !== 'yes') {
22 console.log('Warning: attempted to require Suman index.js but this cannot be.\n' +
23 'Set the SUMAN_EXTRANEOUS_EXECUTABLE env variable to "yes" to get around this.');
24 process.exit(1);
25}
26else {
27 delete process.env['SUMAN_EXTRANEOUS_EXECUTABLE'];
28}
29function handleExceptionsAndRejections() {
30 if (_suman && _suman.sumanOpts && (_suman.sumanOpts.ignore_uncaught_exceptions || _suman.sumanOpts.ignore_unhandled_rejections)) {
31 console.error('\n => uncaughtException occurred, but we are ignoring due to the ' +
32 '"--ignore-uncaught-exceptions" / "--ignore-unhandled-rejections" flag(s) you passed.');
33 }
34 else {
35 console.error('\n => Use "--ignore-uncaught-exceptions" / "--ignore-unhandled-rejections" ' +
36 'to force suman to continue despite the error.');
37 process.exit(59);
38 }
39}
40process.on('uncaughtException', function (err) {
41 debugger;
42 if (typeof err !== 'object') {
43 console.error(new Error("err passed to uncaughtException was not an object => " + err).stack);
44 err = new Error(typeof err === 'string' ? err : util.inspect(err));
45 }
46 if (String(err.stack || err).match(/Cannot find module/i) && _suman && _suman.sumanOpts && _suman.sumanOpts.transpile) {
47 console.log(' => If transpiling, you may need to transpile your entire test directory to the destination directory using the ' +
48 '--transpile and --all options together.');
49 }
50 setTimeout(function () {
51 if (err && !err._alreadyHandledBySuman) {
52 err._alreadyHandledBySuman = true;
53 console.error('\n => Suman "uncaughtException" event occurred =>\n', chalk.magenta(err.stack), '\n\n');
54 handleExceptionsAndRejections();
55 }
56 }, 500);
57});
58process.on('unhandledRejection', function (err, p) {
59 debugger;
60 if (typeof err !== 'object') {
61 console.error(new Error("err passed to unhandledRejection was not an object => '" + err + "'").stack);
62 err = new Error(typeof err === 'string' ? err : util.inspect(err));
63 }
64 setTimeout(function () {
65 if (err && !err._alreadyHandledBySuman) {
66 err._alreadyHandledBySuman = true;
67 console.error('\n\n => Suman "unhandledRejection" event occurred =>\n', (err.stack || err), '\n\n');
68 handleExceptionsAndRejections();
69 }
70 }, 500);
71});
72var path = require("path");
73var util = require("util");
74var assert = require("assert");
75var EE = require("events");
76var semver = require("semver");
77var dashdash = require('dashdash');
78var chalk = require("chalk");
79var su = require("suman-utils");
80var _ = require("lodash");
81var uniqBy = require('lodash.uniqby');
82var events = require('suman-events').events;
83var _suman = global.__suman = (global.__suman || {});
84require('./lib/helpers/add-suman-global-properties');
85require('./lib/patches/all');
86var load_reporters_1 = require("./lib/helpers/load-reporters");
87var suman_constants_1 = require("./config/suman-constants");
88var general_1 = require("./lib/helpers/general");
89if (su.weAreDebugging) {
90 _suman.log.info(' => Suman is in debug mode (we are debugging).');
91 _suman.log.info(' => Process PID => ', process.pid);
92}
93_suman.log.info(chalk.magenta(' => Suman started with the following command:'), chalk.bold(util.inspect(process.argv)));
94_suman.log.info(' => $NODE_PATH is as follows:', process.env['NODE_PATH']);
95_suman.log.info('Resolved path of Suman executable =>', '"' + __filename + '"');
96var nodeVersion = process.version;
97var oldestSupported = suman_constants_1.constants.OLDEST_SUPPORTED_NODE_VERSION;
98if (semver.lt(nodeVersion, oldestSupported)) {
99 _suman.log.error(chalk.red('warning => Suman is not well-tested against Node versions prior to ' +
100 oldestSupported + '; your Node version: ' + chalk.bold(nodeVersion)));
101 throw 'Please upgrade to a Node.js version newer than v6.0.0. Suman recommends usage of NVM.';
102}
103_suman.log.info('Node.js version:', chalk.bold(nodeVersion));
104var sumanLibRoot = _suman.sumanLibRoot = String(__dirname);
105var pkgJSON = require('./package.json');
106var sumanVersion = process.env.SUMAN_GLOBAL_VERSION = pkgJSON.version;
107_suman.log.info(chalk.italic('Suman ' + chalk.bold('v' + sumanVersion) + ' running...'));
108_suman.log.info('[process.pid] => ', process.pid);
109_suman.startTime = Date.now();
110var cwd = process.cwd();
111var sumanExecutablePath = _suman.sumanExecutablePath = process.env.SUMAN_EXECUTABLE_PATH = __filename;
112var projectRoot = _suman.projectRoot = process.env.SUMAN_PROJECT_ROOT = su.findProjectRoot(cwd);
113var cwdAsRoot = process.argv.indexOf('--cwd-is-root') > -1;
114if (!projectRoot) {
115 if (!cwdAsRoot) {
116 console.log(' => Warning => A NPM/Node.js project root could not be found given your current working directory.');
117 console.log(chalk.red.bold(' => cwd:', cwd, ' '));
118 console.log('\n', chalk.red.bold('=> Please execute the suman command from within the root of your project. '), '\n');
119 console.log('\n', chalk.blue.bold('=> (Perhaps you need to run "npm init" before running "suman --init", ' +
120 'which will create a package.json file for you at the root of your project.) ') + '\n');
121 process.exit(1);
122 }
123 else {
124 projectRoot = _suman.projectRoot = process.env.SUMAN_PROJECT_ROOT = cwd;
125 }
126}
127var sumanOpts = _suman.sumanOpts = require('./lib/parse-cmd-line-opts/parse-opts');
128_suman.sumanArgs = sumanOpts._args;
129if (su.vgt(7)) {
130 _suman.log.info('Project root:', projectRoot);
131}
132if (cwd !== projectRoot) {
133 if (su.vgt(1)) {
134 _suman.log.info('Note that your current working directory is not equal to the project root:');
135 _suman.log.info('cwd:', chalk.magenta(cwd));
136 _suman.log.info('Project root:', chalk.magenta(projectRoot));
137 }
138}
139else {
140 if (su.vgt(2)) {
141 if (cwd === projectRoot) {
142 _suman.log.info(chalk.gray('cwd:', cwd));
143 }
144 }
145 if (cwd !== projectRoot) {
146 _suman.log.info(chalk.magenta('cwd:', cwd));
147 }
148}
149var viaSuman = _suman.viaSuman = true;
150var resultBroadcaster = _suman.resultBroadcaster = (_suman.resultBroadcaster || new EE());
151var sumanConfig, pth;
152var configPath = sumanOpts.config;
153var serverName = sumanOpts.server_name;
154var convert = sumanOpts.convert_from_mocha;
155var src = sumanOpts.src;
156var dest = sumanOpts.dest;
157var init = sumanOpts.init;
158var uninstall = sumanOpts.uninstall;
159var force = sumanOpts.force;
160var fforce = sumanOpts.fforce;
161var s = sumanOpts.server;
162var tailRunner = sumanOpts.tail_runner;
163var tailTest = sumanOpts.tail_test;
164var useBabel = sumanOpts.use_babel;
165var useServer = sumanOpts.use_server;
166var tail = sumanOpts.tail;
167var removeBabel = sumanOpts.remove_babel;
168var create = sumanOpts.create;
169var watch = sumanOpts.watch;
170var useIstanbul = sumanOpts.use_istanbul;
171var interactive = sumanOpts.interactive;
172var appendMatchAny = sumanOpts.append_match_any;
173var appendMatchAll = sumanOpts.append_match_all;
174var appendMatchNone = sumanOpts.append_match_none;
175var matchAny = sumanOpts.match_any;
176var matchAll = sumanOpts.match_all;
177var matchNone = sumanOpts.match_none;
178var repair = sumanOpts.repair;
179var uninstallBabel = sumanOpts.uninstall_babel;
180var groups = sumanOpts.groups;
181var useTAPOutput = sumanOpts.use_tap_output;
182var fullStackTraces = sumanOpts.full_stack_traces;
183var coverage = sumanOpts.coverage;
184var diagnostics = sumanOpts.diagnostics;
185var installGlobals = sumanOpts.install_globals;
186var postinstall = sumanOpts.postinstall;
187var tscMultiWatch = sumanOpts.tsc_multi_watch;
188var sumanShell = sumanOpts.suman_shell;
189var watchPer = sumanOpts.watch_per;
190var singleProcess = sumanOpts.single_process;
191var script = sumanOpts.script;
192var browser = sumanOpts.browser;
193if (singleProcess) {
194 process.env.SUMAN_SINGLE_PROCESS = 'yes';
195}
196if (watch || watchPer) {
197 if (String(process.env.SUMAN_WATCH_TEST_RUN).trim() === 'yes') {
198 throw new Error('Suman watch process has launched a process which in turn will watch, this is not allowed.');
199 }
200}
201if (sumanOpts.user_args) {
202 _suman.log.info(chalk.magenta('raw user_args is'), sumanOpts.user_args);
203}
204var userArgs = sumanOpts.user_args = _.flatten([sumanOpts.user_args]).join(' ');
205if (coverage) {
206 _suman.log.info(chalk.magenta.bold('Coverage reports will be written out due to presence of --coverage flag.'));
207}
208var babelRegister = sumanOpts.babel_register;
209var noBabelRegister = sumanOpts.no_babel_register;
210var originalTranspileOption = sumanOpts.transpile = Boolean(sumanOpts.transpile);
211var sumanInstalledLocally = null;
212var sumanInstalledAtAll = null;
213var sumanServerInstalled = null;
214if (sumanOpts.version) {
215 console.log('\n');
216 _suman.log.info('Node.js version:', nodeVersion);
217 _suman.log.info('Suman version:', sumanVersion);
218 _suman.log.info('...And we\'re done here.', '\n');
219 process.exit(0);
220}
221function makeThrow(msg) {
222 console.log('\n');
223 console.error('\n');
224 throw msg;
225}
226if (sumanOpts.transpile && sumanOpts.no_transpile) {
227 makeThrow(' => Suman fatal problem => --transpile and --no-transpile options were both set,' +
228 ' please choose one only.');
229}
230if (sumanOpts.append_match_all && sumanOpts.match_all) {
231 makeThrow(' => Suman fatal problem => --match-all and --append-match-all options were both set,' +
232 ' please choose one only.');
233}
234if (sumanOpts.append_match_any && sumanOpts.match_any) {
235 makeThrow(' => Suman fatal problem => --match-any and --append-match-any options were both set,' +
236 ' please choose one only.');
237}
238if (sumanOpts.append_match_none && sumanOpts.match_none) {
239 makeThrow(' => Suman fatal problem => --match-none and --append-match-none options were both set,' +
240 ' please choose one only.');
241}
242if (sumanOpts.watch && sumanOpts.stop_watching) {
243 makeThrow('=> Suman fatal problem => --watch and --stop-watching options were both set, ' +
244 'please choose one only.');
245}
246if (sumanOpts.babel_register && sumanOpts.no_babel_register) {
247 makeThrow('=> Suman fatal problem => --babel-register and --no-babel-register command line options were both set,' +
248 ' please choose one only.');
249}
250try {
251 pth = path.resolve(configPath || (cwd + '/' + 'suman.conf.js'));
252 sumanConfig = _suman.sumanConfig = require(pth);
253 if (sumanOpts.verbosity > 8) {
254 _suman.log.info(' => Suman verbose message => Suman config used: ' + pth);
255 }
256}
257catch (err) {
258 _suman.log.error(err.stack);
259 if (!/Cannot find module/i.test(err.stack)) {
260 throw err;
261 }
262 if (!init) {
263 _suman.log.warning(chalk.bgBlack.yellow('warning => Could not load your config file ' +
264 'in your current working directory or given by --cfg at the command line...'));
265 _suman.log.warning(chalk.bgBlack.yellow(' => ...are you sure you issued the suman command in the right directory? ' +
266 '...now looking for a config file at the root of your project...'));
267 }
268 try {
269 pth = path.resolve(projectRoot + '/' + 'suman.conf.js');
270 sumanConfig = _suman.sumanConfig = require(pth);
271 if (sumanOpts.verbosity > 2) {
272 console.log(chalk.cyan(' => Suman config used: ' + pth + '\n'));
273 }
274 }
275 catch (err) {
276 _suman.usingDefaultConfig = true;
277 _suman.log.warning('warning => Using default configuration file, please create your suman.conf.js ' +
278 'file using "suman --init".');
279 sumanConfig = _suman.sumanConfig = require('./lib/default-conf-files/suman.default.conf.js');
280 }
281}
282if (init) {
283 console.log(chalk.magenta(' => "suman --init" is running.'));
284 sumanConfig = _suman.sumanConfig = _suman.sumanConfig || {};
285}
286else {
287 var vetLocalInstallations = require('./lib/cli-helpers/determine-if-suman-is-installed').vetLocalInstallations;
288 var installObj = vetLocalInstallations(sumanConfig, sumanOpts, projectRoot);
289 sumanInstalledAtAll = installObj.sumanInstalledAtAll;
290 sumanServerInstalled = installObj.sumanServerInstalled;
291 sumanInstalledLocally = installObj.sumanInstalledLocally;
292}
293var sumanPaths = general_1.resolveSharedDirs(sumanConfig, projectRoot, sumanOpts);
294var sumanObj = general_1.loadSharedObjects(sumanPaths, projectRoot, sumanOpts);
295if (sumanOpts.parallel && sumanOpts.series) {
296 throw chalk.red('suman usage error => "--series" and "--parallel" options were both used, ' +
297 'please choose one or neither...but not both!');
298}
299if ('concurrency' in sumanOpts) {
300 assert(Number.isInteger(sumanOpts.concurrency) && Number(sumanOpts.concurrency) > 0, chalk.red(' => Suman usage error => "--concurrency" option value should be an integer greater than 0.'));
301}
302_suman.maxProcs = sumanOpts.concurrency || sumanConfig.maxParallelProcesses || 15;
303sumanOpts.$useTAPOutput = _suman.useTAPOutput = sumanConfig.useTAPOutput || useTAPOutput;
304sumanOpts.$useTAPOutput && _suman.log.info('using TAP output => ', sumanOpts.$useTAPOutput);
305sumanOpts.$fullStackTraces = sumanConfig.fullStackTraces || sumanOpts.full_stack_traces;
306var sumanMatchesAny = (matchAny || (sumanConfig.matchAny || []).concat(appendMatchAny || []))
307 .map(function (item) { return (item instanceof RegExp) ? item : new RegExp(item); });
308if (sumanMatchesAny.length < 1) {
309 _suman.log.warning('no runnable file regexes available; using the default => /\.js$/');
310 sumanMatchesAny.push(/\.js$/);
311}
312var sumanMatchesNone = (matchNone || (sumanConfig.matchNone || []).concat(appendMatchNone || []))
313 .map(function (item) { return (item instanceof RegExp) ? item : new RegExp(item); });
314var sumanMatchesAll = (matchAll || (sumanConfig.matchAll || []).concat(appendMatchAll || []))
315 .map(function (item) { return (item instanceof RegExp) ? item : new RegExp(item); });
316_suman.sumanMatchesAny = uniqBy(sumanMatchesAny, function (item) { return item; });
317_suman.sumanMatchesNone = uniqBy(sumanMatchesNone, function (item) { return item; });
318_suman.sumanMatchesAll = uniqBy(sumanMatchesAll, function (item) { return item; });
319var preOptCheck = {
320 tscMultiWatch: tscMultiWatch, watch: watch, watchPer: watchPer,
321 create: create, useServer: useServer, useBabel: useBabel,
322 useIstanbul: useIstanbul, init: init, uninstall: uninstall,
323 convert: convert, groups: groups, s: s, interactive: interactive, uninstallBabel: uninstallBabel,
324 diagnostics: diagnostics, installGlobals: installGlobals, postinstall: postinstall,
325 repair: repair, sumanShell: sumanShell, script: script
326};
327var optCheck = Object.keys(preOptCheck).filter(function (key, index) {
328 return preOptCheck[key];
329})
330 .map(function (key) {
331 var value = preOptCheck[key];
332 var obj = {};
333 obj[key] = value;
334 return obj;
335});
336if (optCheck.length > 1) {
337 console.error('\t => Too many options, pick one from:\n', util.inspect(Object.keys(preOptCheck)));
338 console.error('\t => Current options used were:\n', util.inspect(optCheck));
339 console.error('\t => Use --help for more information.\n');
340 console.error('\t => Use --examples to see command line examples for using Suman in the intended manner.\n');
341 process.exit(suman_constants_1.constants.EXIT_CODES.BAD_COMMAND_LINE_OPTION);
342}
343load_reporters_1.loadReporters(sumanOpts, projectRoot, sumanConfig);
344resultBroadcaster.emit(String(events.NODE_VERSION), nodeVersion);
345resultBroadcaster.emit(String(events.SUMAN_VERSION), sumanVersion);
346var paths = _.flatten([sumanOpts._args]).slice(0);
347if (sumanOpts.test_paths_json) {
348 var jsonPaths = JSON.parse(String(sumanOpts.test_paths_json).trim());
349 jsonPaths.forEach(function (p) {
350 paths.push(p);
351 });
352}
353if (sumanOpts.replace_match && sumanOpts.replace_with) {
354 paths = paths.map(function (p) {
355 return String(p).replace(sumanOpts.replace_match, sumanOpts.replace_with);
356 });
357}
358if (sumanOpts.replace_ext_with) {
359 paths = paths.map(function (p) {
360 return String(p).substr(0, String(p).lastIndexOf('.')) + sumanOpts.replace_ext_with;
361 });
362}
363if (su.vgt(7)) {
364 console.log(' => Suman verbose message => arguments assumed to be test file paths to be run:', paths);
365 if (paths.length < 1) {
366 console.log(' => Suman verbose message => Since no paths were passed at the command line, we \n' +
367 'default to running tests from the "testSrc" directory (defined in your suman.conf.js file).');
368 }
369}
370if (sumanOpts.force_inherit_stdio) {
371 _suman.$forceInheritStdio = true;
372}
373var isTTY = process.stdout.isTTY;
374if (String(process.env.SUMAN_WATCH_TEST_RUN).trim() !== 'yes') {
375 if (!process.stdout.isTTY && !useTAPOutput) {
376 _suman.log.error(chalk.yellow.bold('you may need to turn on TAP output for test results to be captured in destination process.'));
377 }
378}
379if (diagnostics) {
380 require('./lib/cli-commands/run-diagnostics').run(sumanOpts);
381}
382else if (script) {
383 require('./lib/cli-commands/run-scripts').run(sumanConfig, sumanOpts);
384}
385else if (tscMultiWatch) {
386 require('./lib/cli-commands/run-tscmultiwatch').run(sumanOpts);
387}
388else if (repair) {
389 require('./lib/cli-commands/run-repair').run(sumanOpts);
390}
391else if (postinstall) {
392 require('./lib/cli-commands/postinstall').run(sumanOpts);
393}
394else if (installGlobals) {
395 require('./lib/cli-commands/install-global-deps')(paths);
396}
397else if (sumanShell) {
398 require('./lib/cli-commands/run-suman-shell').run(projectRoot, sumanLibRoot, sumanOpts.suman_d_opts);
399}
400else if (interactive) {
401 require('./lib/cli-commands/run-suman-interactive').run();
402}
403else if (uninstallBabel) {
404 require('./lib/use-babel/uninstall-babel')(null);
405}
406else if (useIstanbul) {
407 require('./lib/use-istanbul/use-istanbul')();
408}
409else if (create) {
410 require('./lib/cli-commands/create-opt').run(create);
411}
412else if (useServer) {
413 require('./lib/use-server/use-server')(null);
414}
415else if (useBabel) {
416 require('./lib/use-babel/use-babel')(null);
417}
418else if (init) {
419 require('./lib/cli-commands/init-opt').run(sumanOpts, projectRoot, cwd);
420}
421else if (uninstall) {
422 require('./lib/uninstall/uninstall-suman')({
423 force: force,
424 fforce: fforce,
425 removeBabel: removeBabel,
426 });
427}
428else if (convert) {
429 require('./lib/cli-commands/convert-mocha').run(projectRoot, src, dest, force);
430}
431else if (s) {
432 require('./lib/cli-commands/start-suman-server')(sumanServerInstalled, sumanConfig, serverName);
433}
434else if (!browser && (watch || watchPer)) {
435 require('./lib/cli-commands/watching').run(projectRoot, paths, sumanOpts, sumanConfig);
436}
437else if (groups) {
438 require('./lib/cli-commands/groups').run(paths);
439}
440else {
441 if (userArgs.length > 0 && sumanOpts.verbosity > 4) {
442 _suman.log.info('The following "--user-args" will be passed to child processes as process.argv:');
443 _suman.log.info(userArgs);
444 }
445 require('./lib/run').run(sumanOpts, sumanConfig, paths, sumanServerInstalled, sumanVersion);
446}