Skip to content

Commit 94fbb1a

Browse files
committed
Merge pull request #363 from sindresorhus/reporters
Internal reporters
2 parents 65ae07c + 2fab4b1 commit 94fbb1a

File tree

8 files changed

+470
-432
lines changed

8 files changed

+470
-432
lines changed

cli.js

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ if (debug.enabled) {
1717
require('time-require');
1818
}
1919

20+
var updateNotifier = require('update-notifier');
21+
var figures = require('figures');
2022
var arrify = require('arrify');
2123
var meow = require('meow');
22-
var updateNotifier = require('update-notifier');
2324
var chalk = require('chalk');
2425
var Promise = require('bluebird');
25-
var log = require('./lib/logger');
26-
var tap = require('./lib/tap');
26+
var verboseReporter = require('./lib/reporters/verbose');
27+
var tapReporter = require('./lib/reporters/tap');
28+
var Logger = require('./lib/logger');
2729
var Api = require('./api');
2830

2931
// Bluebird specific
@@ -68,75 +70,37 @@ if (cli.flags.init) {
6870
return;
6971
}
7072

71-
if (cli.flags.tap) {
72-
console.log(tap.start());
73-
} else {
74-
log.write();
75-
}
76-
7773
var api = new Api(cli.input, {
7874
failFast: cli.flags.failFast,
7975
serial: cli.flags.serial,
8076
require: arrify(cli.flags.require)
8177
});
8278

83-
api.on('test', function (test) {
84-
if (cli.flags.tap) {
85-
console.log(tap.test(test));
86-
return;
87-
}
88-
89-
if (test.error) {
90-
log.error(test.title, chalk.red(test.error.message));
91-
} else {
92-
// don't log it if there's only one file and one anonymous test
93-
if (api.fileCount === 1 && api.testCount === 1 && test.title === '[anonymous]') {
94-
return;
95-
}
79+
var logger = new Logger();
80+
logger.api = api;
9681

97-
log.test(test);
98-
}
99-
});
82+
if (cli.flags.tap) {
83+
logger.use(tapReporter());
84+
} else {
85+
logger.use(verboseReporter());
86+
}
10087

101-
api.on('error', function (data) {
102-
if (cli.flags.tap) {
103-
console.log(tap.unhandledError(data));
104-
return;
105-
}
88+
logger.start();
10689

107-
log.unhandledError(data.type, data.file, data);
108-
});
90+
api.on('test', logger.test);
91+
api.on('error', logger.unhandledError);
10992

11093
api.run()
11194
.then(function () {
112-
if (cli.flags.tap) {
113-
console.log(tap.finish(api.passCount, api.failCount, api.rejectionCount, api.exceptionCount));
95+
logger.finish();
96+
logger.exit(api.failCount > 0 || api.rejectionCount > 0 || api.exceptionCount > 0 ? 1 : 0);
97+
})
98+
.catch(function (err) {
99+
if (err instanceof Error) {
100+
console.log(' ' + chalk.red(figures.cross) + ' ' + err.message);
114101
} else {
115-
log.write();
116-
log.report(api.passCount, api.failCount, api.rejectionCount, api.exceptionCount);
117-
log.write();
118-
119-
if (api.failCount > 0) {
120-
log.errors(api.errors);
121-
}
102+
console.error(err.stack);
122103
}
123104

124-
process.stdout.write('');
125-
flushIoAndExit(api.failCount > 0 || api.rejectionCount > 0 || api.exceptionCount > 0 ? 1 : 0);
126-
})
127-
.catch(function (err) {
128-
log.error(err.message);
129-
flushIoAndExit(1);
105+
logger.exit(1);
130106
});
131-
132-
function flushIoAndExit(code) {
133-
// TODO: figure out why this needs to be here to
134-
// correctly flush the output when multiple test files
135-
process.stdout.write('');
136-
process.stderr.write('');
137-
138-
// timeout required to correctly flush IO on Node.js 0.10 on Windows
139-
setTimeout(function () {
140-
process.exit(code);
141-
}, process.env.AVA_APPVEYOR ? 500 : 0);
142-
}

lib/logger.js

Lines changed: 41 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,66 @@
11
'use strict';
2-
var prettyMs = require('pretty-ms');
3-
var figures = require('figures');
4-
var Squeak = require('squeak');
5-
var chalk = require('chalk');
6-
var plur = require('plur');
7-
var log = new Squeak({separator: ' '});
8-
var x = module.exports;
92

10-
function beautifyStack(stack) {
11-
var re = /(?:^(?! {4}at\b).{6})|(?:\((?:[A-Z]:)?(?:[\\\/](?:(?!node_modules[\\\/]ava[\\\/])[^:\\\/])+)+:\d+:\d+\))/;
12-
var found = false;
3+
function Logger() {
4+
if (!(this instanceof Logger)) {
5+
return new Logger();
6+
}
137

14-
return stack.split('\n').filter(function (line) {
15-
var relevant = re.test(line);
16-
found = found || relevant;
17-
return !found || relevant;
18-
}).join('\n');
8+
Object.keys(Logger.prototype).forEach(function (key) {
9+
this[key] = this[key].bind(this);
10+
}, this);
1911
}
2012

21-
x._beautifyStack = beautifyStack;
22-
23-
log.type('success', {
24-
color: 'green',
25-
prefix: figures.tick
26-
});
27-
28-
log.type('error', {
29-
color: 'red',
30-
prefix: figures.cross
31-
});
13+
module.exports = Logger;
3214

33-
x.write = log.write.bind(log);
34-
x.writelpad = log.writelpad.bind(log);
35-
x.success = log.success.bind(log);
36-
x.error = log.error.bind(log);
37-
38-
x.test = function (props) {
39-
if (props.err) {
40-
log.error(props.title, chalk.red(props.err.message));
41-
return;
42-
}
15+
Logger.prototype.use = function (reporter) {
16+
this.reporter = reporter;
17+
this.reporter.api = this.api;
18+
};
4319

44-
if (props.skip) {
45-
log.write(' ' + chalk.cyan('- ' + props.title));
20+
Logger.prototype.start = function () {
21+
if (!this.reporter.start) {
4622
return;
4723
}
4824

49-
// display duration only over a threshold
50-
var threshold = 100;
51-
var dur = props.duration > threshold ? chalk.gray.dim(' (' + prettyMs(props.duration) + ')') : '';
52-
log.success(props.title + dur);
25+
this.write(this.reporter.start());
5326
};
5427

55-
x.errors = function (tests) {
56-
var i = 0;
28+
Logger.prototype.test = function (test) {
29+
this.write(this.reporter.test(test));
30+
};
5731

58-
tests.forEach(function (test) {
59-
i++;
32+
Logger.prototype.unhandledError = function (err) {
33+
if (!this.reporter.unhandledError) {
34+
return;
35+
}
6036

61-
log.writelpad(chalk.red(i + '.', test.title));
62-
log.writelpad(chalk.red(beautifyStack(test.error.stack)));
63-
log.write();
64-
});
37+
this.write(this.reporter.unhandledError(err));
6538
};
6639

67-
x.report = function (passed, failed, unhandled, uncaught) {
68-
if (failed > 0) {
69-
log.writelpad(chalk.red(failed, plur('test', failed), 'failed'));
70-
} else {
71-
log.writelpad(chalk.green(passed, plur('test', passed), 'passed'));
40+
Logger.prototype.finish = function () {
41+
if (!this.reporter.finish) {
42+
return;
7243
}
7344

74-
if (unhandled > 0) {
75-
log.writelpad(chalk.red(unhandled, 'unhandled', plur('rejection', unhandled)));
76-
}
45+
this.write(this.reporter.finish());
46+
};
7747

78-
if (uncaught > 0) {
79-
log.writelpad(chalk.red(uncaught, 'uncaught', plur('exception', uncaught)));
48+
Logger.prototype.write = function (str) {
49+
if (typeof str === 'undefined') {
50+
return;
8051
}
81-
};
8252

83-
var types = {
84-
rejection: 'Unhandled Rejection',
85-
exception: 'Uncaught Exception'
53+
this.reporter.write(str);
8654
};
8755

88-
x.unhandledError = function (type, file, error) {
89-
log.write(chalk.red(types[type] + ':', file));
90-
91-
if (error.stack) {
92-
log.writelpad(chalk.red(beautifyStack(error.stack)));
93-
} else {
94-
log.writelpad(chalk.red(JSON.stringify(error)));
95-
}
56+
Logger.prototype.exit = function (code) {
57+
// TODO: figure out why this needs to be here to
58+
// correctly flush the output when multiple test files
59+
process.stdout.write('');
60+
process.stderr.write('');
9661

97-
log.write();
62+
// timeout required to correctly flush IO on Node.js 0.10 on Windows
63+
setTimeout(function () {
64+
process.exit(code);
65+
}, process.env.AVA_APPVEYOR ? 500 : 0);
9866
};

lib/tap.js renamed to lib/reporters/tap.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,27 @@ function getSourceFromStack(stack, index) {
1010
.replace(/^\s+at /, '');
1111
}
1212

13-
exports.start = function () {
13+
function TapReporter() {
14+
if (!(this instanceof TapReporter)) {
15+
return new TapReporter();
16+
}
17+
18+
this.i = 0;
19+
}
20+
21+
module.exports = TapReporter;
22+
23+
TapReporter.prototype.start = function () {
1424
return 'TAP version 13';
1525
};
1626

17-
var i = 0;
18-
19-
exports.test = function (test) {
27+
TapReporter.prototype.test = function (test) {
2028
var output;
2129

2230
if (test.error) {
2331
output = [
2432
'# ' + test.title,
25-
format('not ok %d - %s', ++i, test.error.message),
33+
format('not ok %d - %s', ++this.i, test.error.message),
2634
' ---',
2735
' operator: ' + test.error.operator,
2836
' expected: ' + test.error.expected,
@@ -33,17 +41,17 @@ exports.test = function (test) {
3341
} else {
3442
output = [
3543
'# ' + test.title,
36-
format('ok %d - %s', ++i, test.title)
44+
format('ok %d - %s', ++this.i, test.title)
3745
];
3846
}
3947

4048
return output.join('\n');
4149
};
4250

43-
exports.unhandledError = function (err) {
51+
TapReporter.prototype.unhandledError = function (err) {
4452
var output = [
4553
'# ' + err.message,
46-
format('not ok %d - %s', ++i, err.message),
54+
format('not ok %d - %s', ++this.i, err.message),
4755
' ---',
4856
' name: ' + err.name,
4957
' at: ' + getSourceFromStack(err.stack, 1),
@@ -53,15 +61,19 @@ exports.unhandledError = function (err) {
5361
return output.join('\n');
5462
};
5563

56-
exports.finish = function (passCount, failCount, rejectionCount, exceptionCount) {
64+
TapReporter.prototype.finish = function () {
5765
var output = [
5866
'',
59-
'1..' + (passCount + failCount),
60-
'# tests ' + (passCount + failCount),
61-
'# pass ' + passCount,
62-
'# fail ' + (failCount + rejectionCount + exceptionCount),
67+
'1..' + (this.api.passCount + this.api.failCount),
68+
'# tests ' + (this.api.passCount + this.api.failCount),
69+
'# pass ' + this.api.passCount,
70+
'# fail ' + (this.api.failCount + this.api.rejectionCount + this.api.exceptionCount),
6371
''
6472
];
6573

6674
return output.join('\n');
6775
};
76+
77+
TapReporter.prototype.write = function (str) {
78+
console.log(str);
79+
};

0 commit comments

Comments
 (0)