Skip to content

Commit d6f5dba

Browse files
committed
fix --inspect and its ilk; closes #3681
- upgrade `node-environment-flags` - add some tests - use `mocha:cli:mocha` in `bin/mocha` as unique `DEBUG` identifier (`DEBUG=mocha:cli:mocha bin/mocha ...`) - add some handiness to the integration test helpers - update docstrings in integration test helpers
1 parent c91b325 commit d6f5dba

File tree

7 files changed

+191
-27
lines changed

7 files changed

+191
-27
lines changed

bin/mocha

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
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');
1923

2024
const mochaPath = require.resolve('./_mocha');
@@ -45,13 +49,12 @@ const disableTimeouts = value => {
4549
* @ignore
4650
*/
4751
const trimV8Option = value =>
48-
value !== 'v8-options' && /^v8-/.test(value) ? value.slice(2) : value;
52+
value !== 'v8-options' && /^v8-/.test(value) ? value.slice(3) : value;
4953

5054
// sort options into "node" and "mocha" buckets
5155
Object.keys(opts).forEach(opt => {
52-
opt = trimV8Option(opt);
5356
if (isNodeFlag(opt)) {
54-
nodeArgs[opt] = opts[opt];
57+
nodeArgs[trimV8Option(opt)] = opts[opt];
5558
disableTimeouts(opt);
5659
} else {
5760
mochaArgs[opt] = opts[opt];
@@ -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

Lines changed: 20 additions & 0 deletions
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.
@@ -46,3 +47,22 @@ exports.isNodeFlag = flag =>
4647
* @private
4748
*/
4849
exports.impliesNoTimeouts = flag => debugFlags.has(flag);
50+
51+
/**
52+
* All non-strictly-boolean arguments to node--those with values--must specify those values using `=`, e.g., `--inspect=0.0.0.0`.
53+
* Unparse these arguments using `yargs-unparser` (which would result in `--inspect 0.0.0.0`), then supply `=` where we have values.
54+
* There's probably an easier or more robust way to do this; fixes welcome
55+
* @param {Object} opts - Arguments object
56+
* @returns {string[]} Unparsed arguments using `=` to specify values
57+
* @private
58+
*/
59+
exports.unparseNodeFlags = opts => {
60+
var args = unparse(opts);
61+
return args.length
62+
? args
63+
.join(' ')
64+
.split(/\b/)
65+
.map(arg => (arg === ' ' ? '=' : arg))
66+
.join('')
67+
: [];
68+
};

lib/cli/options.js

Lines changed: 37 additions & 12 deletions
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

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
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

Lines changed: 9 additions & 3 deletions
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, depending
1313
Object.keys(v).length === 2
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/options/debug.spec.js

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ 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);
@@ -25,8 +25,116 @@ describe('--debug', function() {
2525
);
2626
done();
2727
},
28-
{stdio: 'pipe'}
28+
'pipe'
2929
);
3030
});
31+
32+
it('should invoke --inspect-brk', function(done) {
33+
var proc = invokeMocha(
34+
['--debug-brk', DEFAULT_FIXTURE],
35+
function(err, res) {
36+
if (err) {
37+
return done(err);
38+
}
39+
expect(res, 'to have passed').and(
40+
'to contain output',
41+
/Debugger listening/i
42+
);
43+
done();
44+
},
45+
'pipe'
46+
);
47+
48+
// debugger must be manually killed
49+
setTimeout(function() {
50+
proc.kill('SIGINT');
51+
}, 1000);
52+
});
53+
54+
it('should respect custom host/port', function(done) {
55+
invokeMocha(
56+
['--debug=127.0.0.1:9229', DEFAULT_FIXTURE],
57+
function(err, res) {
58+
if (err) {
59+
return done(err);
60+
}
61+
expect(res, 'to have passed').and(
62+
'to contain output',
63+
/Debugger listening on .*127.0.0.1:9229/i
64+
);
65+
done();
66+
},
67+
'pipe'
68+
);
69+
});
70+
71+
it('should warn about incorrect usage for version', function(done) {
72+
invokeMocha(
73+
['--debug=127.0.0.1:9229', DEFAULT_FIXTURE],
74+
function(err, res) {
75+
if (err) {
76+
return done(err);
77+
}
78+
expect(res, 'to have passed').and(
79+
'to contain output',
80+
/"--debug" is not available/i
81+
);
82+
done();
83+
},
84+
'pipe'
85+
);
86+
});
87+
});
88+
89+
describe('Node.js v6', function() {
90+
// note that v6.3.0 and newer supports --inspect but still supports --debug.
91+
before(function() {
92+
if (process.version.substring(0, 2) !== 'v6') {
93+
this.skip();
94+
}
95+
});
96+
97+
it('should start debugger', function(done) {
98+
var proc = invokeMocha(
99+
['--debug', DEFAULT_FIXTURE],
100+
function(err, res) {
101+
if (err) {
102+
return done(err);
103+
}
104+
expect(res, 'to have passed').and(
105+
'to contain output',
106+
/Debugger listening/i
107+
);
108+
done();
109+
},
110+
'pipe'
111+
);
112+
113+
// debugger must be manually killed
114+
setTimeout(function() {
115+
proc.kill('SIGINT');
116+
}, 1000);
117+
});
118+
119+
it('should respect custom host/port', function(done) {
120+
var proc = invokeMocha(
121+
['--debug=127.0.0.1:9229', DEFAULT_FIXTURE],
122+
function(err, res) {
123+
if (err) {
124+
return done(err);
125+
}
126+
expect(res, 'to have passed').and(
127+
'to contain output',
128+
/Debugger listening on .*127.0.0.1:9229/i
129+
);
130+
done();
131+
},
132+
'pipe'
133+
);
134+
135+
setTimeout(function() {
136+
proc.kill('SIGINT');
137+
}, 1000);
138+
});
31139
});
32140
});

0 commit comments

Comments
 (0)