Skip to content

Commit 21ba5ce

Browse files
authored
fix --inspect and its ilk; closes #3681 (#3699)
This seems to make `--inspect` work properly with or without a parameter (`--inspect=0.0.0.0:12345`). The `=` is required, as it's required by `node`. In v6 of Node, `--debug --port=12345` should also work as expected. Due to ignorance, skip Windows for now
1 parent 52b9a5f commit 21ba5ce

File tree

9 files changed

+194
-39
lines changed

9 files changed

+194
-39
lines changed

.mocharc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ global:
44
- okGlobalA,okGlobalB
55
- okGlobalC
66
- callback*
7-
timeout: 200
7+
timeout: 300

bin/mocha

+16-11
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
const {deprecate, warn} = require('../lib/utils');
1313
const {spawn} = require('child_process');
1414
const {loadOptions} = require('../lib/cli/options');
15-
const {isNodeFlag, impliesNoTimeouts} = require('../lib/cli/node-flags');
15+
const {
16+
unparseNodeFlags,
17+
isNodeFlag,
18+
impliesNoTimeouts
19+
} = require('../lib/cli/node-flags');
1620
const unparse = require('yargs-unparser');
17-
const debug = require('debug')('mocha:cli');
21+
const debug = require('debug')('mocha:cli:mocha');
1822
const {aliases} = require('../lib/cli/run-option-metadata');
23+
const nodeEnv = require('node-environment-flags');
1924

2025
const mochaPath = require.resolve('./_mocha');
2126
const mochaArgs = {};
@@ -32,7 +37,7 @@ debug('loaded opts', opts);
3237
const disableTimeouts = value => {
3338
if (impliesNoTimeouts(value)) {
3439
debug(`option "${value}" disabled timeouts`);
35-
mochaArgs.timeout = false;
40+
mochaArgs.timeout = 0;
3641
delete mochaArgs.timeouts;
3742
delete mochaArgs.t;
3843
}
@@ -45,13 +50,12 @@ const disableTimeouts = value => {
4550
* @ignore
4651
*/
4752
const trimV8Option = value =>
48-
value !== 'v8-options' && /^v8-/.test(value) ? value.slice(2) : value;
53+
value !== 'v8-options' && /^v8-/.test(value) ? value.slice(3) : value;
4954

5055
// sort options into "node" and "mocha" buckets
5156
Object.keys(opts).forEach(opt => {
52-
opt = trimV8Option(opt);
5357
if (isNodeFlag(opt)) {
54-
nodeArgs[opt] = opts[opt];
58+
nodeArgs[trimV8Option(opt)] = opts[opt];
5559
disableTimeouts(opt);
5660
} else {
5761
mochaArgs[opt] = opts[opt];
@@ -90,20 +94,19 @@ if (/^(debug|inspect)$/.test(mochaArgs._[0])) {
9094
}
9195

9296
// allow --debug to invoke --inspect on Node.js v8 or newer.
93-
// these show up in childOpts because they are not recognized as valid node flags in this version of node.
9497
['debug', 'debug-brk']
95-
.filter(opt => opt in mochaArgs)
98+
.filter(opt => opt in nodeArgs && !nodeEnv.has(opt))
9699
.forEach(opt => {
97100
const newOpt = opt === 'debug' ? 'inspect' : 'inspect-brk';
98101
warn(
99102
`"--${opt}" is not available in Node.js ${
100103
process.version
101104
}; use "--${newOpt}" instead.`
102105
);
103-
nodeArgs[newOpt] = mochaArgs[opt];
106+
nodeArgs[newOpt] = nodeArgs[opt];
104107
mochaArgs.timeout = false;
105108
debug(`--${opt} -> ${newOpt}`);
106-
delete mochaArgs[opt];
109+
delete nodeArgs[opt];
107110
});
108111

109112
// historical
@@ -115,8 +118,10 @@ if (nodeArgs.gc) {
115118
delete nodeArgs.gc;
116119
}
117120

121+
debug('final node args', nodeArgs);
122+
118123
const args = [].concat(
119-
unparse(nodeArgs),
124+
unparseNodeFlags(nodeArgs),
120125
mochaPath,
121126
unparse(mochaArgs, {alias: aliases})
122127
);

lib/cli/node-flags.js

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
const nodeFlags = require('node-environment-flags');
10+
const unparse = require('yargs-unparser');
1011

1112
/**
1213
* These flags are considered "debug" flags.
@@ -34,6 +35,7 @@ const debugFlags = new Set(['debug', 'debug-brk', 'inspect', 'inspect-brk']);
3435
exports.isNodeFlag = flag =>
3536
!/^(?:require|r)$/.test(flag) &&
3637
(nodeFlags.has(flag) ||
38+
debugFlags.has(flag) ||
3739
/(?:preserve-symlinks(?:-main)?|harmony(?:[_-]|$)|(?:trace[_-].+$)|gc(?:[_-]global)?$|es[_-]staging$|use[_-]strict$|v8[_-](?!options).+?$)/.test(
3840
flag
3941
));
@@ -46,3 +48,22 @@ exports.isNodeFlag = flag =>
4648
* @private
4749
*/
4850
exports.impliesNoTimeouts = flag => debugFlags.has(flag);
51+
52+
/**
53+
* All non-strictly-boolean arguments to node--those with values--must specify those values using `=`, e.g., `--inspect=0.0.0.0`.
54+
* Unparse these arguments using `yargs-unparser` (which would result in `--inspect 0.0.0.0`), then supply `=` where we have values.
55+
* There's probably an easier or more robust way to do this; fixes welcome
56+
* @param {Object} opts - Arguments object
57+
* @returns {string[]} Unparsed arguments using `=` to specify values
58+
* @private
59+
*/
60+
exports.unparseNodeFlags = opts => {
61+
var args = unparse(opts);
62+
return args.length
63+
? args
64+
.join(' ')
65+
.split(/\b/)
66+
.map(arg => (arg === ' ' ? '=' : arg))
67+
.join('')
68+
: [];
69+
};

lib/cli/options.js

+37-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const findup = require('findup-sync');
1818
const {deprecate} = require('../utils');
1919
const debug = require('debug')('mocha:cli:options');
2020
const {createMissingArgumentError} = require('../errors');
21+
const {isNodeFlag} = require('./node-flags');
2122

2223
/**
2324
* The `yargs-parser` namespace
@@ -75,22 +76,46 @@ const nargOpts = types.array
7576
* @ignore
7677
*/
7778
const parse = (args = [], ...configObjects) => {
78-
const result = yargsParser.detailed(
79-
args,
80-
Object.assign(
81-
{
82-
configuration,
83-
configObjects,
84-
coerce: coerceOpts,
85-
narg: nargOpts,
86-
alias: aliases
87-
},
88-
types
89-
)
79+
// save node-specific args for special handling.
80+
// 1. when these args have a "=" they should be considered to have values
81+
// 2. if they don't, they just boolean flags
82+
// 3. to avoid explicitly defining the set of them, we tell yargs-parser they
83+
// are ALL boolean flags.
84+
// 4. we can then reapply the values after yargs-parser is done.
85+
const nodeArgs = (Array.isArray(args) ? args : args.split(' ')).reduce(
86+
(acc, arg) => {
87+
const pair = arg.split('=');
88+
const flag = pair[0].replace(/^--?/, '');
89+
if (isNodeFlag(flag)) {
90+
return arg.includes('=')
91+
? acc.concat([[flag, pair[1]]])
92+
: acc.concat([[flag, true]]);
93+
}
94+
return acc;
95+
},
96+
[]
9097
);
98+
99+
const result = yargsParser.detailed(args, {
100+
configuration,
101+
configObjects,
102+
coerce: coerceOpts,
103+
narg: nargOpts,
104+
alias: aliases,
105+
string: types.string,
106+
array: types.array,
107+
number: types.number,
108+
boolean: types.boolean.concat(nodeArgs.map(pair => pair[0]))
109+
});
91110
if (result.error) {
92111
throw createMissingArgumentError(result.error.message);
93112
}
113+
114+
// reapply "=" arg values from above
115+
nodeArgs.forEach(([key, value]) => {
116+
result.argv[key] = value;
117+
});
118+
94119
return result.argv;
95120
};
96121

package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@
498498
"minimatch": "3.0.4",
499499
"mkdirp": "0.5.1",
500500
"ms": "2.1.1",
501-
"node-environment-flags": "1.0.2",
501+
"node-environment-flags": "1.0.4",
502502
"object.assign": "4.1.0",
503503
"strip-json-comments": "2.0.1",
504504
"supports-color": "6.0.0",

test/assertions.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ exports.mixinMochaAssertions = function(expect) {
99
return (
1010
Object.prototype.toString.call(v) === '[object Object]' &&
1111
typeof v.output === 'string' &&
12-
typeof v.code === 'number' &&
12+
'code' in v && // may be null
1313
Array.isArray(v.args)
1414
);
1515
}
@@ -59,9 +59,9 @@ exports.mixinMochaAssertions = function(expect) {
5959
}
6060
)
6161
.addAssertion(
62-
'<RawResult|RawRunResult|JSONRunResult> [not] to have [completed with] [exit] code <number>',
62+
'<RawRunResult|JSONRunResult> [not] to have completed with [exit] code <number>',
6363
function(expect, result, code) {
64-
expect(result, '[not] to have property', 'code', code);
64+
expect(result.code, '[not] to be', code);
6565
}
6666
)
6767
.addAssertion(
@@ -295,5 +295,11 @@ exports.mixinMochaAssertions = function(expect) {
295295
function(expect, result, output) {
296296
expect(result.output, '[not] to satisfy', output);
297297
}
298+
)
299+
.addAssertion(
300+
'<RawRunResult|JSONRunResult> to have [exit] code <number>',
301+
function(expect, result, code) {
302+
expect(result.code, 'to be', code);
303+
}
298304
);
299305
};

test/integration/helpers.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ module.exports = {
8181
var path;
8282

8383
path = resolveFixturePath(fixturePath);
84-
args = args || [];
84+
args = (args || []).concat('--reporter', 'json', path);
8585

8686
return invokeMocha(
87-
args.concat(['--reporter', 'json', path]),
87+
args,
8888
function(err, res) {
8989
if (err) return fn(err);
9090

@@ -95,8 +95,8 @@ module.exports = {
9595
fn(
9696
new Error(
9797
format(
98-
'Failed to parse JSON reporter output.\nArgs: %O\nResult:\n\n%O',
99-
args,
98+
'Failed to parse JSON reporter output. Error:\n%O\nResponse:\n%O',
99+
err,
100100
res
101101
)
102102
)

test/integration/options/debug.spec.js

+102-4
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,117 @@ describe('--debug', function() {
1414

1515
it('should invoke --inspect', function(done) {
1616
invokeMocha(
17-
['--debug', '--file', DEFAULT_FIXTURE],
17+
['--debug', DEFAULT_FIXTURE],
1818
function(err, res) {
1919
if (err) {
2020
return done(err);
2121
}
22-
expect(res, 'to have passed').and(
22+
expect(res, 'to contain output', /Debugger listening/i);
23+
done();
24+
},
25+
'pipe'
26+
);
27+
});
28+
29+
it('should invoke --inspect-brk', function(done) {
30+
var proc = invokeMocha(
31+
['--debug-brk', DEFAULT_FIXTURE],
32+
function(err, res) {
33+
if (err) {
34+
return done(err);
35+
}
36+
expect(res, 'to contain output', /Debugger listening/i);
37+
done();
38+
},
39+
'pipe'
40+
);
41+
42+
// debugger must be manually killed
43+
setTimeout(function() {
44+
process.kill(proc.pid, 'SIGINT');
45+
}, 2000);
46+
});
47+
48+
it('should respect custom host/port', function(done) {
49+
invokeMocha(
50+
['--debug=127.0.0.1:9229', DEFAULT_FIXTURE],
51+
function(err, res) {
52+
if (err) {
53+
return done(err);
54+
}
55+
expect(
56+
res,
57+
'to contain output',
58+
/Debugger listening on .*127.0.0.1:9229/i
59+
);
60+
done();
61+
},
62+
'pipe'
63+
);
64+
});
65+
66+
it('should warn about incorrect usage for version', function(done) {
67+
invokeMocha(
68+
['--debug=127.0.0.1:9229', DEFAULT_FIXTURE],
69+
function(err, res) {
70+
if (err) {
71+
return done(err);
72+
}
73+
expect(res, 'to contain output', /"--debug" is not available/i);
74+
done();
75+
},
76+
'pipe'
77+
);
78+
});
79+
});
80+
81+
describe('Node.js v6', function() {
82+
// note that v6.3.0 and newer supports --inspect but still supports --debug.
83+
before(function() {
84+
if (process.version.substring(0, 2) !== 'v6') {
85+
this.skip();
86+
}
87+
});
88+
89+
it('should start debugger', function(done) {
90+
var proc = invokeMocha(
91+
['--debug', DEFAULT_FIXTURE],
92+
function(err, res) {
93+
if (err) {
94+
return done(err);
95+
}
96+
expect(res, 'to contain output', /Debugger listening/i);
97+
done();
98+
},
99+
'pipe'
100+
);
101+
102+
// debugger must be manually killed
103+
setTimeout(function() {
104+
process.kill(proc.pid, 'SIGINT');
105+
}, 2000);
106+
});
107+
108+
it('should respect custom host/port', function(done) {
109+
var proc = invokeMocha(
110+
['--debug=127.0.0.1:9229', DEFAULT_FIXTURE],
111+
function(err, res) {
112+
if (err) {
113+
return done(err);
114+
}
115+
expect(
116+
res,
23117
'to contain output',
24-
/Debugger listening/i
118+
/Debugger listening on .*127.0.0.1:9229/i
25119
);
26120
done();
27121
},
28-
{stdio: 'pipe'}
122+
'pipe'
29123
);
124+
125+
setTimeout(function() {
126+
process.kill(proc.pid, 'SIGINT');
127+
}, 2000);
30128
});
31129
});
32130
});

0 commit comments

Comments
 (0)