diff --git a/bin/mocha b/bin/mocha index bc61a54c02..24c89116a8 100755 --- a/bin/mocha +++ b/bin/mocha @@ -12,9 +12,13 @@ const {deprecate} = require('../lib/utils'); const {spawn} = require('child_process'); const {loadOptions} = require('../lib/cli/options'); -const {isNodeFlag, impliesNoTimeouts} = require('../lib/cli/node-flags'); +const { + unparseNodeFlags, + isNodeFlag, + impliesNoTimeouts +} = require('../lib/cli/node-flags'); const unparse = require('yargs-unparser'); -const debug = require('debug')('mocha:cli'); +const debug = require('debug')('mocha:cli:mocha'); const {aliases} = require('../lib/cli/run-option-metadata'); const mochaPath = require.resolve('./_mocha'); @@ -87,8 +91,10 @@ if (/^(debug|inspect)$/.test(childOpts._[0])) { ]; } +debug('node opts', nodeOpts); + const args = [].concat( - unparse(nodeOpts), + unparseNodeFlags(nodeOpts), mochaPath, unparse(childOpts, {alias: aliases}) ); diff --git a/lib/cli/node-flags.js b/lib/cli/node-flags.js index 9b4fd09d3c..e89825776c 100644 --- a/lib/cli/node-flags.js +++ b/lib/cli/node-flags.js @@ -7,6 +7,7 @@ */ const nodeFlags = require('node-environment-flags'); +const unparse = require('yargs-unparser'); /** * These flags are considered "debug" flags. @@ -46,3 +47,22 @@ exports.isNodeFlag = flag => * @private */ exports.impliesNoTimeouts = flag => debugFlags.has(flag); + +/** + * All non-strictly-boolean arguments to node--those with values--must specify those values using `=`, e.g., `--inspect=0.0.0.0`. + * Unparse these arguments using `yargs-unparser` (which would result in `--inspect 0.0.0.0`), then supply `=` where we have values. + * There's probably an easier or more robust way to do this; fixes welcome + * @param {Object} opts - Arguments object + * @returns {string[]} Unparsed arguments using `=` to specify values + * @private + */ +exports.unparseNodeFlags = opts => { + var args = unparse(opts); + return args.length + ? args + .join(' ') + .split(/\b/) + .map(arg => (arg === ' ' ? '=' : arg)) + .join('') + : []; +}; diff --git a/lib/cli/options.js b/lib/cli/options.js index 854120418f..336edd3a53 100644 --- a/lib/cli/options.js +++ b/lib/cli/options.js @@ -259,6 +259,13 @@ module.exports.loadPkgRc = loadPkgRc; * @returns {external:yargsParser.Arguments} Parsed args from everything */ const loadOptions = (argv = []) => { + // save node-specific args having + const nodeArgOptionalValues = new Map( + argv + .filter(arg => arg.includes('=')) + .map(arg => arg.substring(2).split('=')) + ); + let args = parse(argv); // short-circuit: look for a flag that would abort loading of mocha.opts if ( @@ -299,6 +306,10 @@ const loadOptions = (argv = []) => { delete args.spec; } + nodeArgOptionalValues.forEach((value, key) => { + args[key] = value; + }); + return args; }; diff --git a/lib/cli/run-option-metadata.js b/lib/cli/run-option-metadata.js index 0838e6050c..b205b55713 100644 --- a/lib/cli/run-option-metadata.js +++ b/lib/cli/run-option-metadata.js @@ -42,7 +42,15 @@ exports.types = { 'recursive', 'reporters', 'sort', - 'watch' + 'watch', + + // these are special-cased args for node which have optional values. + // if we don't treat them as boolean, they get greedy and might eat subsequent positional arguments. + // instead, we will save their "real" values before parsing via yargs-parser. + 'debug', + 'debug-brk', + 'inspect', + 'inspect-brk' ], number: ['retries', 'slow', 'timeout'], string: ['fgrep', 'grep', 'package', 'reporter', 'ui'] diff --git a/test/integration/options/debug.spec.js b/test/integration/options/debug.spec.js index 5c073156da..594c8188d9 100644 --- a/test/integration/options/debug.spec.js +++ b/test/integration/options/debug.spec.js @@ -14,7 +14,7 @@ describe('--debug', function() { it('should invoke --inspect', function(done) { invokeMocha( - ['--debug', '--file', DEFAULT_FIXTURE], + ['--debug', DEFAULT_FIXTURE], function(err, res) { if (err) { return done(err); @@ -29,4 +29,34 @@ describe('--debug', function() { ); }); }); + + describe('Node.js v6', function() { + before(function() { + if (process.version.substring(0, 2) !== 'v6') { + this.skip(); + } + }); + + it('should start native debugger', function(done) { + var proc = invokeMocha( + ['--debug', DEFAULT_FIXTURE], + function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have passed').and( + 'to contain output', + /Debugger listening/i + ); + done(); + }, + {stdio: 'pipe'} + ); + + // native debugger must be manually killed + setTimeout(function() { + proc.kill('SIGINT'); + }, 500); + }); + }); });