mocha 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/bin/_mocha +21 -7
- package/bin/mocha +7 -0
- package/lib/browser/diff.js +369 -0
- package/lib/mocha.js +20 -8
- package/lib/reporters/base.js +11 -12
- package/lib/reporters/html.js +2 -2
- package/lib/reporters/markdown.js +14 -5
- package/lib/reporters/nyan.js +13 -13
- package/lib/reporters/xunit.js +41 -9
- package/lib/runnable.js +4 -3
- package/lib/runner.js +4 -3
- package/lib/suite.js +2 -2
- package/lib/utils.js +190 -30
- package/mocha.js +317 -88
- package/package.json +2 -3
- package/Readme.md +0 -203
package/lib/mocha.js
CHANGED
|
@@ -80,7 +80,7 @@ function Mocha(options) {
|
|
|
80
80
|
this.suite = new exports.Suite('', new exports.Context);
|
|
81
81
|
this.ui(options.ui);
|
|
82
82
|
this.bail(options.bail);
|
|
83
|
-
this.reporter(options.reporter);
|
|
83
|
+
this.reporter(options.reporter, options.reporterOptions);
|
|
84
84
|
if (null != options.timeout) this.timeout(options.timeout);
|
|
85
85
|
this.useColors(options.useColors)
|
|
86
86
|
if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
|
|
@@ -131,10 +131,10 @@ Mocha.prototype.addFile = function(file){
|
|
|
131
131
|
* Set reporter to `reporter`, defaults to "spec".
|
|
132
132
|
*
|
|
133
133
|
* @param {String|Function} reporter name or constructor
|
|
134
|
+
* @param {Object} reporterOptions optional options
|
|
134
135
|
* @api public
|
|
135
136
|
*/
|
|
136
|
-
|
|
137
|
-
Mocha.prototype.reporter = function(reporter){
|
|
137
|
+
Mocha.prototype.reporter = function(reporter, reporterOptions){
|
|
138
138
|
if ('function' == typeof reporter) {
|
|
139
139
|
this._reporter = reporter;
|
|
140
140
|
} else {
|
|
@@ -149,6 +149,7 @@ Mocha.prototype.reporter = function(reporter){
|
|
|
149
149
|
if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
|
|
150
150
|
this._reporter = _reporter;
|
|
151
151
|
}
|
|
152
|
+
this.options.reporterOptions = reporterOptions;
|
|
152
153
|
return this;
|
|
153
154
|
};
|
|
154
155
|
|
|
@@ -297,9 +298,9 @@ Mocha.prototype.globals = function(globals){
|
|
|
297
298
|
*/
|
|
298
299
|
|
|
299
300
|
Mocha.prototype.useColors = function(colors){
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
if (colors !== undefined) {
|
|
302
|
+
this.options.useColors = colors;
|
|
303
|
+
}
|
|
303
304
|
return this;
|
|
304
305
|
};
|
|
305
306
|
|
|
@@ -401,7 +402,18 @@ Mocha.prototype.run = function(fn){
|
|
|
401
402
|
if (options.grep) runner.grep(options.grep, options.invert);
|
|
402
403
|
if (options.globals) runner.globals(options.globals);
|
|
403
404
|
if (options.growl) this._growl(runner, reporter);
|
|
404
|
-
|
|
405
|
+
if (options.useColors !== undefined) {
|
|
406
|
+
exports.reporters.Base.useColors = options.useColors;
|
|
407
|
+
}
|
|
405
408
|
exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
|
|
406
|
-
|
|
409
|
+
|
|
410
|
+
function done(failures) {
|
|
411
|
+
if (reporter.done) {
|
|
412
|
+
reporter.done(failures, fn);
|
|
413
|
+
} else {
|
|
414
|
+
fn(failures);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return runner.run(done);
|
|
407
419
|
};
|
package/lib/reporters/base.js
CHANGED
|
@@ -97,7 +97,7 @@ if ('win32' == process.platform) {
|
|
|
97
97
|
*/
|
|
98
98
|
|
|
99
99
|
var color = exports.color = function(type, str) {
|
|
100
|
-
if (!exports.useColors) return str;
|
|
100
|
+
if (!exports.useColors) return String(str);
|
|
101
101
|
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
|
|
102
102
|
};
|
|
103
103
|
|
|
@@ -154,7 +154,7 @@ exports.cursor = {
|
|
|
154
154
|
*/
|
|
155
155
|
|
|
156
156
|
exports.list = function(failures){
|
|
157
|
-
console.
|
|
157
|
+
console.log();
|
|
158
158
|
failures.forEach(function(test, i){
|
|
159
159
|
// format
|
|
160
160
|
var fmt = color('error title', ' %s) %s:\n')
|
|
@@ -178,13 +178,13 @@ exports.list = function(failures){
|
|
|
178
178
|
|
|
179
179
|
// explicitly show diff
|
|
180
180
|
if (err.showDiff && sameType(actual, expected)) {
|
|
181
|
-
escape = false;
|
|
182
|
-
err.actual = actual = utils.stringify(actual);
|
|
183
|
-
err.expected = expected = utils.stringify(expected);
|
|
184
|
-
}
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
if ('string' !== typeof actual) {
|
|
183
|
+
escape = false;
|
|
184
|
+
err.actual = actual = utils.stringify(actual);
|
|
185
|
+
err.expected = expected = utils.stringify(expected);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
188
|
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
|
|
189
189
|
var match = message.match(/^([^:]+): expected/);
|
|
190
190
|
msg = '\n ' + color('error message', match ? match[1] : msg);
|
|
@@ -200,7 +200,7 @@ exports.list = function(failures){
|
|
|
200
200
|
stack = stack.slice(index ? index + 1 : index)
|
|
201
201
|
.replace(/^/gm, ' ');
|
|
202
202
|
|
|
203
|
-
console.
|
|
203
|
+
console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
|
|
204
204
|
});
|
|
205
205
|
};
|
|
206
206
|
|
|
@@ -305,11 +305,10 @@ Base.prototype.epilogue = function(){
|
|
|
305
305
|
if (stats.failures) {
|
|
306
306
|
fmt = color('fail', ' %d failing');
|
|
307
307
|
|
|
308
|
-
console.
|
|
309
|
-
stats.failures);
|
|
308
|
+
console.log(fmt, stats.failures);
|
|
310
309
|
|
|
311
310
|
Base.list(this.failures);
|
|
312
|
-
console.
|
|
311
|
+
console.log();
|
|
313
312
|
}
|
|
314
313
|
|
|
315
314
|
console.log();
|
package/lib/reporters/html.js
CHANGED
|
@@ -135,7 +135,7 @@ function HTML(runner) {
|
|
|
135
135
|
} else if (test.pending) {
|
|
136
136
|
var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
|
|
137
137
|
} else {
|
|
138
|
-
var el = fragment('<li class="test fail"><h2>%e <a href="
|
|
138
|
+
var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">‣</a></h2></li>', test.title, self.testURL(test));
|
|
139
139
|
var str = test.err.stack || test.err.toString();
|
|
140
140
|
|
|
141
141
|
// FF / Opera do not add the message
|
|
@@ -183,7 +183,7 @@ function HTML(runner) {
|
|
|
183
183
|
*/
|
|
184
184
|
var makeUrl = function makeUrl(s) {
|
|
185
185
|
var search = window.location.search;
|
|
186
|
-
return (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
|
|
186
|
+
return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
|
|
187
187
|
};
|
|
188
188
|
|
|
189
189
|
/**
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
var Base = require('./base')
|
|
6
6
|
, utils = require('../utils');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Constants
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
var SUITE_PREFIX = '$';
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* Expose `Markdown`.
|
|
10
16
|
*/
|
|
@@ -35,8 +41,9 @@ function Markdown(runner) {
|
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
function mapTOC(suite, obj) {
|
|
38
|
-
var ret = obj
|
|
39
|
-
|
|
44
|
+
var ret = obj,
|
|
45
|
+
key = SUITE_PREFIX + suite.title;
|
|
46
|
+
obj = obj[key] = obj[key] || { suite: suite };
|
|
40
47
|
suite.suites.forEach(function(suite){
|
|
41
48
|
mapTOC(suite, obj);
|
|
42
49
|
});
|
|
@@ -49,11 +56,13 @@ function Markdown(runner) {
|
|
|
49
56
|
var link;
|
|
50
57
|
for (var key in obj) {
|
|
51
58
|
if ('suite' == key) continue;
|
|
52
|
-
if (key
|
|
53
|
-
|
|
59
|
+
if (key !== SUITE_PREFIX) {
|
|
60
|
+
link = ' - [' + key.substring(1) + ']';
|
|
61
|
+
link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
|
|
62
|
+
buf += Array(level).join(' ') + link;
|
|
63
|
+
}
|
|
54
64
|
buf += stringifyTOC(obj[key], level);
|
|
55
65
|
}
|
|
56
|
-
--level;
|
|
57
66
|
return buf;
|
|
58
67
|
}
|
|
59
68
|
|
package/lib/reporters/nyan.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Module dependencies.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
var Base = require('./base')
|
|
6
|
-
, color = Base.color;
|
|
5
|
+
var Base = require('./base');
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Expose `Dot`.
|
|
@@ -80,17 +79,16 @@ NyanCat.prototype.draw = function(){
|
|
|
80
79
|
|
|
81
80
|
NyanCat.prototype.drawScoreboard = function(){
|
|
82
81
|
var stats = this.stats;
|
|
83
|
-
var colors = Base.colors;
|
|
84
82
|
|
|
85
|
-
function draw(
|
|
83
|
+
function draw(type, n) {
|
|
86
84
|
write(' ');
|
|
87
|
-
write(
|
|
85
|
+
write(Base.color(type, n));
|
|
88
86
|
write('\n');
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
draw(
|
|
92
|
-
draw(
|
|
93
|
-
draw(
|
|
89
|
+
draw('green', stats.passes);
|
|
90
|
+
draw('fail', stats.failures);
|
|
91
|
+
draw('pending', stats.pending);
|
|
94
92
|
write('\n');
|
|
95
93
|
|
|
96
94
|
this.cursorUp(this.numberOfLines);
|
|
@@ -140,26 +138,26 @@ NyanCat.prototype.drawRainbow = function(){
|
|
|
140
138
|
NyanCat.prototype.drawNyanCat = function() {
|
|
141
139
|
var self = this;
|
|
142
140
|
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
|
|
143
|
-
var
|
|
141
|
+
var dist = '\u001b[' + startWidth + 'C';
|
|
144
142
|
var padding = '';
|
|
145
143
|
|
|
146
|
-
write(
|
|
144
|
+
write(dist);
|
|
147
145
|
write('_,------,');
|
|
148
146
|
write('\n');
|
|
149
147
|
|
|
150
|
-
write(
|
|
148
|
+
write(dist);
|
|
151
149
|
padding = self.tick ? ' ' : ' ';
|
|
152
150
|
write('_|' + padding + '/\\_/\\ ');
|
|
153
151
|
write('\n');
|
|
154
152
|
|
|
155
|
-
write(
|
|
153
|
+
write(dist);
|
|
156
154
|
padding = self.tick ? '_' : '__';
|
|
157
155
|
var tail = self.tick ? '~' : '^';
|
|
158
156
|
var face;
|
|
159
157
|
write(tail + '|' + padding + this.face() + ' ');
|
|
160
158
|
write('\n');
|
|
161
159
|
|
|
162
|
-
write(
|
|
160
|
+
write(dist);
|
|
163
161
|
padding = self.tick ? ' ' : ' ';
|
|
164
162
|
write(padding + '"" "" ');
|
|
165
163
|
write('\n');
|
|
@@ -240,6 +238,8 @@ NyanCat.prototype.generateColors = function(){
|
|
|
240
238
|
*/
|
|
241
239
|
|
|
242
240
|
NyanCat.prototype.rainbowify = function(str){
|
|
241
|
+
if (!Base.useColors)
|
|
242
|
+
return str;
|
|
243
243
|
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
|
|
244
244
|
this.colorIndex += 1;
|
|
245
245
|
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
|
package/lib/reporters/xunit.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
var Base = require('./base')
|
|
6
6
|
, utils = require('../utils')
|
|
7
|
+
, fs = require('fs')
|
|
7
8
|
, escape = utils.escape;
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -29,12 +30,19 @@ exports = module.exports = XUnit;
|
|
|
29
30
|
* @api public
|
|
30
31
|
*/
|
|
31
32
|
|
|
32
|
-
function XUnit(runner) {
|
|
33
|
+
function XUnit(runner, options) {
|
|
33
34
|
Base.call(this, runner);
|
|
34
35
|
var stats = this.stats
|
|
35
36
|
, tests = []
|
|
36
37
|
, self = this;
|
|
37
38
|
|
|
39
|
+
if (options.reporterOptions && options.reporterOptions.output) {
|
|
40
|
+
if (! fs.createWriteStream) {
|
|
41
|
+
throw new Error('file output not supported in browser');
|
|
42
|
+
}
|
|
43
|
+
self.fileStream = fs.createWriteStream(options.reporterOptions.output);
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
runner.on('pending', function(test){
|
|
39
47
|
tests.push(test);
|
|
40
48
|
});
|
|
@@ -48,7 +56,7 @@ function XUnit(runner) {
|
|
|
48
56
|
});
|
|
49
57
|
|
|
50
58
|
runner.on('end', function(){
|
|
51
|
-
|
|
59
|
+
self.write(tag('testsuite', {
|
|
52
60
|
name: 'Mocha Tests'
|
|
53
61
|
, tests: stats.tests
|
|
54
62
|
, failures: stats.failures
|
|
@@ -58,22 +66,46 @@ function XUnit(runner) {
|
|
|
58
66
|
, time: (stats.duration / 1000) || 0
|
|
59
67
|
}, false));
|
|
60
68
|
|
|
61
|
-
tests.forEach(test);
|
|
62
|
-
|
|
69
|
+
tests.forEach(function(t) { self.test(t); });
|
|
70
|
+
self.write('</testsuite>');
|
|
63
71
|
});
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Override done to close the stream (if it's a file).
|
|
76
|
+
*/
|
|
77
|
+
XUnit.prototype.done = function(failures, fn) {
|
|
78
|
+
if (this.fileStream) {
|
|
79
|
+
this.fileStream.end(function() {
|
|
80
|
+
fn(failures);
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
fn(failures);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
66
87
|
/**
|
|
67
88
|
* Inherit from `Base.prototype`.
|
|
68
89
|
*/
|
|
69
90
|
|
|
70
91
|
XUnit.prototype.__proto__ = Base.prototype;
|
|
71
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Write out the given line
|
|
95
|
+
*/
|
|
96
|
+
XUnit.prototype.write = function(line) {
|
|
97
|
+
if (this.fileStream) {
|
|
98
|
+
this.fileStream.write(line + '\n');
|
|
99
|
+
} else {
|
|
100
|
+
console.log(line);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
72
104
|
/**
|
|
73
105
|
* Output tag for the given `test.`
|
|
74
106
|
*/
|
|
75
107
|
|
|
76
|
-
function
|
|
108
|
+
XUnit.prototype.test = function(test, ostream) {
|
|
77
109
|
var attrs = {
|
|
78
110
|
classname: test.parent.fullTitle()
|
|
79
111
|
, name: test.title
|
|
@@ -82,13 +114,13 @@ function test(test) {
|
|
|
82
114
|
|
|
83
115
|
if ('failed' == test.state) {
|
|
84
116
|
var err = test.err;
|
|
85
|
-
|
|
117
|
+
this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
|
|
86
118
|
} else if (test.pending) {
|
|
87
|
-
|
|
119
|
+
this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
|
|
88
120
|
} else {
|
|
89
|
-
|
|
121
|
+
this.write(tag('testcase', attrs, true) );
|
|
90
122
|
}
|
|
91
|
-
}
|
|
123
|
+
};
|
|
92
124
|
|
|
93
125
|
/**
|
|
94
126
|
* HTML tag helper.
|
package/lib/runnable.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
var EventEmitter = require('events').EventEmitter
|
|
6
6
|
, debug = require('debug')('mocha:runnable')
|
|
7
|
-
, milliseconds = require('./ms')
|
|
7
|
+
, milliseconds = require('./ms')
|
|
8
|
+
, utils = require('./utils');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Save timer references to avoid Sinon interfering (see GH-237).
|
|
@@ -226,7 +227,7 @@ Runnable.prototype.run = function(fn){
|
|
|
226
227
|
done();
|
|
227
228
|
});
|
|
228
229
|
} catch (err) {
|
|
229
|
-
done(err);
|
|
230
|
+
done(utils.getError(err));
|
|
230
231
|
}
|
|
231
232
|
return;
|
|
232
233
|
}
|
|
@@ -243,7 +244,7 @@ Runnable.prototype.run = function(fn){
|
|
|
243
244
|
callFn(this.fn);
|
|
244
245
|
}
|
|
245
246
|
} catch (err) {
|
|
246
|
-
done(err);
|
|
247
|
+
done(utils.getError(err));
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
function callFn(fn) {
|
package/lib/runner.js
CHANGED
|
@@ -19,7 +19,9 @@ var globals = [
|
|
|
19
19
|
'setInterval',
|
|
20
20
|
'clearInterval',
|
|
21
21
|
'XMLHttpRequest',
|
|
22
|
-
'Date'
|
|
22
|
+
'Date',
|
|
23
|
+
'setImmediate',
|
|
24
|
+
'clearImmediate'
|
|
23
25
|
];
|
|
24
26
|
|
|
25
27
|
/**
|
|
@@ -244,7 +246,6 @@ Runner.prototype.hook = function(name, fn){
|
|
|
244
246
|
function next(i) {
|
|
245
247
|
var hook = hooks[i];
|
|
246
248
|
if (!hook) return fn();
|
|
247
|
-
if (self.failures && suite.bail()) return fn();
|
|
248
249
|
self.currentRunnable = hook;
|
|
249
250
|
|
|
250
251
|
hook.ctx.currentTest = self.test;
|
|
@@ -538,7 +539,7 @@ Runner.prototype.uncaught = function(err){
|
|
|
538
539
|
}.call(err) ? err : ( err.message || err ));
|
|
539
540
|
} else {
|
|
540
541
|
debug('uncaught undefined exception');
|
|
541
|
-
err =
|
|
542
|
+
err = utils.undefinedError();
|
|
542
543
|
}
|
|
543
544
|
err.uncaught = true;
|
|
544
545
|
|
package/lib/suite.js
CHANGED
|
@@ -98,7 +98,7 @@ Suite.prototype.clone = function(){
|
|
|
98
98
|
|
|
99
99
|
Suite.prototype.timeout = function(ms){
|
|
100
100
|
if (0 == arguments.length) return this._timeout;
|
|
101
|
-
if (ms === 0) this._enableTimeouts = false;
|
|
101
|
+
if (ms.toString() === '0') this._enableTimeouts = false;
|
|
102
102
|
if ('string' == typeof ms) ms = milliseconds(ms);
|
|
103
103
|
debug('timeout %d', ms);
|
|
104
104
|
this._timeout = parseInt(ms, 10);
|
|
@@ -139,7 +139,7 @@ Suite.prototype.slow = function(ms){
|
|
|
139
139
|
/**
|
|
140
140
|
* Sets whether to bail after first error.
|
|
141
141
|
*
|
|
142
|
-
* @
|
|
142
|
+
* @param {Boolean} bail
|
|
143
143
|
* @return {Suite|Number} for chaining
|
|
144
144
|
* @api private
|
|
145
145
|
*/
|
package/lib/utils.js
CHANGED
|
@@ -292,54 +292,192 @@ exports.highlightTags = function(name) {
|
|
|
292
292
|
}
|
|
293
293
|
};
|
|
294
294
|
|
|
295
|
+
/**
|
|
296
|
+
* If a value could have properties, and has none, this function is called, which returns
|
|
297
|
+
* a string representation of the empty value.
|
|
298
|
+
*
|
|
299
|
+
* Functions w/ no properties return `'[Function]'`
|
|
300
|
+
* Arrays w/ length === 0 return `'[]'`
|
|
301
|
+
* Objects w/ no properties return `'{}'`
|
|
302
|
+
* All else: return result of `value.toString()`
|
|
303
|
+
*
|
|
304
|
+
* @param {*} value Value to inspect
|
|
305
|
+
* @param {string} [type] The type of the value, if known.
|
|
306
|
+
* @returns {string}
|
|
307
|
+
*/
|
|
308
|
+
var emptyRepresentation = function emptyRepresentation(value, type) {
|
|
309
|
+
type = type || exports.type(value);
|
|
310
|
+
|
|
311
|
+
switch(type) {
|
|
312
|
+
case 'function':
|
|
313
|
+
return '[Function]';
|
|
314
|
+
case 'object':
|
|
315
|
+
return '{}';
|
|
316
|
+
case 'array':
|
|
317
|
+
return '[]';
|
|
318
|
+
default:
|
|
319
|
+
return value.toString();
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Takes some variable and asks `{}.toString()` what it thinks it is.
|
|
325
|
+
* @param {*} value Anything
|
|
326
|
+
* @example
|
|
327
|
+
* type({}) // 'object'
|
|
328
|
+
* type([]) // 'array'
|
|
329
|
+
* type(1) // 'number'
|
|
330
|
+
* type(false) // 'boolean'
|
|
331
|
+
* type(Infinity) // 'number'
|
|
332
|
+
* type(null) // 'null'
|
|
333
|
+
* type(new Date()) // 'date'
|
|
334
|
+
* type(/foo/) // 'regexp'
|
|
335
|
+
* type('type') // 'string'
|
|
336
|
+
* type(global) // 'global'
|
|
337
|
+
* @api private
|
|
338
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
|
|
339
|
+
* @returns {string}
|
|
340
|
+
*/
|
|
341
|
+
exports.type = function type(value) {
|
|
342
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
|
343
|
+
return 'buffer';
|
|
344
|
+
}
|
|
345
|
+
return Object.prototype.toString.call(value)
|
|
346
|
+
.replace(/^\[.+\s(.+?)\]$/, '$1')
|
|
347
|
+
.toLowerCase();
|
|
348
|
+
};
|
|
295
349
|
|
|
296
350
|
/**
|
|
297
|
-
* Stringify `
|
|
351
|
+
* @summary Stringify `value`.
|
|
352
|
+
* @description Different behavior depending on type of value.
|
|
353
|
+
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
|
|
354
|
+
* - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
|
|
355
|
+
* - If `value` is an *empty* object, function, or array, return result of function
|
|
356
|
+
* {@link emptyRepresentation}.
|
|
357
|
+
* - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
|
|
358
|
+
* JSON.stringify().
|
|
298
359
|
*
|
|
299
|
-
* @
|
|
300
|
-
* @
|
|
360
|
+
* @see exports.type
|
|
361
|
+
* @param {*} value
|
|
362
|
+
* @return {string}
|
|
301
363
|
* @api private
|
|
302
364
|
*/
|
|
303
365
|
|
|
304
|
-
exports.stringify = function(
|
|
305
|
-
|
|
306
|
-
|
|
366
|
+
exports.stringify = function(value) {
|
|
367
|
+
var prop,
|
|
368
|
+
type = exports.type(value);
|
|
369
|
+
|
|
370
|
+
if (type === 'null' || type === 'undefined') {
|
|
371
|
+
return '[' + type + ']';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (type === 'date') {
|
|
375
|
+
return '[Date: ' + value.toISOString() + ']';
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!~exports.indexOf(['object', 'array', 'function'], type)) {
|
|
379
|
+
return value.toString();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
for (prop in value) {
|
|
383
|
+
if (value.hasOwnProperty(prop)) {
|
|
384
|
+
return JSON.stringify(exports.canonicalize(value), null, 2).replace(/,(\n|$)/g, '$1');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return emptyRepresentation(value, type);
|
|
307
389
|
};
|
|
308
390
|
|
|
309
391
|
/**
|
|
310
|
-
* Return
|
|
311
|
-
* @param {Object}
|
|
312
|
-
* @
|
|
313
|
-
* @return {Object}
|
|
392
|
+
* Return if obj is a Buffer
|
|
393
|
+
* @param {Object} arg
|
|
394
|
+
* @return {Boolean}
|
|
314
395
|
* @api private
|
|
315
396
|
*/
|
|
397
|
+
exports.isBuffer = function (arg) {
|
|
398
|
+
return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
|
|
399
|
+
};
|
|
316
400
|
|
|
317
|
-
|
|
318
|
-
|
|
401
|
+
/**
|
|
402
|
+
* @summary Return a new Thing that has the keys in sorted order. Recursive.
|
|
403
|
+
* @description If the Thing...
|
|
404
|
+
* - has already been seen, return string `'[Circular]'`
|
|
405
|
+
* - is `undefined`, return string `'[undefined]'`
|
|
406
|
+
* - is `null`, return value `null`
|
|
407
|
+
* - is some other primitive, return the value
|
|
408
|
+
* - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
|
|
409
|
+
* - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
|
|
410
|
+
* - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
|
|
411
|
+
*
|
|
412
|
+
* @param {*} value Thing to inspect. May or may not have properties.
|
|
413
|
+
* @param {Array} [stack=[]] Stack of seen values
|
|
414
|
+
* @return {(Object|Array|Function|string|undefined)}
|
|
415
|
+
* @see {@link exports.stringify}
|
|
416
|
+
* @api private
|
|
417
|
+
*/
|
|
319
418
|
|
|
320
|
-
|
|
419
|
+
exports.canonicalize = function(value, stack) {
|
|
420
|
+
var canonicalizedObj,
|
|
421
|
+
type = exports.type(value),
|
|
422
|
+
prop,
|
|
423
|
+
withStack = function withStack(value, fn) {
|
|
424
|
+
stack.push(value);
|
|
425
|
+
fn();
|
|
426
|
+
stack.pop();
|
|
427
|
+
};
|
|
321
428
|
|
|
322
|
-
|
|
429
|
+
stack = stack || [];
|
|
323
430
|
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
canonicalizedObj
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
431
|
+
if (exports.indexOf(stack, value) !== -1) {
|
|
432
|
+
return '[Circular]';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
switch(type) {
|
|
436
|
+
case 'undefined':
|
|
437
|
+
canonicalizedObj = '[undefined]';
|
|
438
|
+
break;
|
|
439
|
+
case 'buffer':
|
|
440
|
+
case 'null':
|
|
441
|
+
canonicalizedObj = value;
|
|
442
|
+
break;
|
|
443
|
+
case 'array':
|
|
444
|
+
withStack(value, function () {
|
|
445
|
+
canonicalizedObj = exports.map(value, function (item) {
|
|
446
|
+
return exports.canonicalize(item, stack);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
break;
|
|
450
|
+
case 'date':
|
|
451
|
+
canonicalizedObj = '[Date: ' + value.toISOString() + ']';
|
|
452
|
+
break;
|
|
453
|
+
case 'function':
|
|
454
|
+
for (prop in value) {
|
|
455
|
+
canonicalizedObj = {};
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
if (!canonicalizedObj) {
|
|
459
|
+
canonicalizedObj = emptyRepresentation(value, type);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
/* falls through */
|
|
463
|
+
case 'object':
|
|
464
|
+
canonicalizedObj = canonicalizedObj || {};
|
|
465
|
+
withStack(value, function () {
|
|
466
|
+
exports.forEach(exports.keys(value).sort(), function (key) {
|
|
467
|
+
canonicalizedObj[key] = exports.canonicalize(value[key], stack);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
break;
|
|
471
|
+
case 'number':
|
|
472
|
+
case 'boolean':
|
|
473
|
+
canonicalizedObj = value;
|
|
474
|
+
break;
|
|
475
|
+
default:
|
|
476
|
+
canonicalizedObj = value.toString();
|
|
339
477
|
}
|
|
340
478
|
|
|
341
479
|
return canonicalizedObj;
|
|
342
|
-
|
|
480
|
+
};
|
|
343
481
|
|
|
344
482
|
/**
|
|
345
483
|
* Lookup file names at the given `path`.
|
|
@@ -386,3 +524,25 @@ exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
|
|
|
386
524
|
|
|
387
525
|
return files;
|
|
388
526
|
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Generate an undefined error with a message warning the user.
|
|
530
|
+
*
|
|
531
|
+
* @return {Error}
|
|
532
|
+
*/
|
|
533
|
+
|
|
534
|
+
exports.undefinedError = function(){
|
|
535
|
+
return new Error('Caught undefined error, did you throw without specifying what?');
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Generate an undefined error if `err` is not defined.
|
|
540
|
+
*
|
|
541
|
+
* @param {Error} err
|
|
542
|
+
* @return {Error}
|
|
543
|
+
*/
|
|
544
|
+
|
|
545
|
+
exports.getError = function(err){
|
|
546
|
+
return err || exports.undefinedError();
|
|
547
|
+
};
|
|
548
|
+
|