aboutsummaryrefslogtreecommitdiff
path: root/node_modules/ava/lib/reporters
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2017-05-28 00:38:50 +0200
committerFlorian Dold <florian.dold@gmail.com>2017-05-28 00:40:43 +0200
commit7fff4499fd915bcea3fa93b1aa8b35f4fe7a6027 (patch)
tree6de9a1aebd150a23b7f8c273ec657a5d0a18fe3e /node_modules/ava/lib/reporters
parent963b7a41feb29cc4be090a2446bdfe0c1f1bcd81 (diff)
add linting (and some initial fixes)
Diffstat (limited to 'node_modules/ava/lib/reporters')
-rw-r--r--node_modules/ava/lib/reporters/improper-usage-messages.js21
-rw-r--r--node_modules/ava/lib/reporters/mini.js318
-rw-r--r--node_modules/ava/lib/reporters/tap.js119
-rw-r--r--node_modules/ava/lib/reporters/verbose.js165
4 files changed, 623 insertions, 0 deletions
diff --git a/node_modules/ava/lib/reporters/improper-usage-messages.js b/node_modules/ava/lib/reporters/improper-usage-messages.js
new file mode 100644
index 000000000..0a2626638
--- /dev/null
+++ b/node_modules/ava/lib/reporters/improper-usage-messages.js
@@ -0,0 +1,21 @@
+'use strict';
+const chalk = require('chalk');
+
+exports.forError = error => {
+ if (!error.improperUsage) {
+ return null;
+ }
+
+ const assertion = error.assertion;
+ if (assertion !== 'throws' || !assertion === 'notThrows') {
+ return null;
+ }
+
+ return `Try wrapping the first argument to \`t.${assertion}()\` in a function:
+
+ ${chalk.cyan(`t.${assertion}(() => { `)}${chalk.grey('/* your code here */')}${chalk.cyan(' })')}
+
+Visit the following URL for more details:
+
+ ${chalk.blue.underline('https://github.com/avajs/ava#throwsfunctionpromise-error-message')}`;
+};
diff --git a/node_modules/ava/lib/reporters/mini.js b/node_modules/ava/lib/reporters/mini.js
new file mode 100644
index 000000000..df481a76a
--- /dev/null
+++ b/node_modules/ava/lib/reporters/mini.js
@@ -0,0 +1,318 @@
+'use strict';
+const StringDecoder = require('string_decoder').StringDecoder;
+const cliCursor = require('cli-cursor');
+const lastLineTracker = require('last-line-stream/tracker');
+const plur = require('plur');
+const spinners = require('cli-spinners');
+const chalk = require('chalk');
+const cliTruncate = require('cli-truncate');
+const cross = require('figures').cross;
+const indentString = require('indent-string');
+const formatAssertError = require('../format-assert-error');
+const extractStack = require('../extract-stack');
+const codeExcerpt = require('../code-excerpt');
+const colors = require('../colors');
+const improperUsageMessages = require('./improper-usage-messages');
+
+// TODO(@jamestalamge): This should be fixed in log-update and ansi-escapes once we are confident it's a good solution.
+const CSI = '\u001B[';
+const ERASE_LINE = CSI + '2K';
+const CURSOR_TO_COLUMN_0 = CSI + '0G';
+const CURSOR_UP = CSI + '1A';
+
+// Returns a string that will erase `count` lines from the end of the terminal.
+function eraseLines(count) {
+ let clear = '';
+
+ for (let i = 0; i < count; i++) {
+ clear += ERASE_LINE + (i < count - 1 ? CURSOR_UP : '');
+ }
+
+ if (count) {
+ clear += CURSOR_TO_COLUMN_0;
+ }
+
+ return clear;
+}
+
+class MiniReporter {
+ constructor(options) {
+ this.options = Object.assign({}, options);
+
+ chalk.enabled = this.options.color;
+ for (const key of Object.keys(colors)) {
+ colors[key].enabled = this.options.color;
+ }
+
+ const spinnerDef = spinners[process.platform === 'win32' ? 'line' : 'dots'];
+ this.spinnerFrames = spinnerDef.frames.map(c => chalk.gray.dim(c));
+ this.spinnerInterval = spinnerDef.interval;
+
+ this.reset();
+ this.stream = process.stderr;
+ this.stringDecoder = new StringDecoder();
+ }
+ start() {
+ this.interval = setInterval(() => {
+ this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
+ this.write(this.prefix());
+ }, this.spinnerInterval);
+
+ return this.prefix('');
+ }
+ reset() {
+ this.clearInterval();
+ this.passCount = 0;
+ this.knownFailureCount = 0;
+ this.failCount = 0;
+ this.skipCount = 0;
+ this.todoCount = 0;
+ this.rejectionCount = 0;
+ this.exceptionCount = 0;
+ this.currentStatus = '';
+ this.currentTest = '';
+ this.statusLineCount = 0;
+ this.spinnerIndex = 0;
+ this.lastLineTracker = lastLineTracker();
+ }
+ spinnerChar() {
+ return this.spinnerFrames[this.spinnerIndex];
+ }
+ clearInterval() {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ test(test) {
+ if (test.todo) {
+ this.todoCount++;
+ } else if (test.skip) {
+ this.skipCount++;
+ } else if (test.error) {
+ this.failCount++;
+ } else {
+ this.passCount++;
+ if (test.failing) {
+ this.knownFailureCount++;
+ }
+ }
+
+ if (test.todo || test.skip) {
+ return;
+ }
+
+ return this.prefix(this._test(test));
+ }
+ prefix(str) {
+ str = str || this.currentTest;
+ this.currentTest = str;
+
+ // The space before the newline is required for proper formatting
+ // TODO(jamestalmage): Figure out why it's needed and document it here
+ return ` \n ${this.spinnerChar()} ${str}`;
+ }
+ _test(test) {
+ const SPINNER_WIDTH = 3;
+ const PADDING = 1;
+ let title = cliTruncate(test.title, process.stdout.columns - SPINNER_WIDTH - PADDING);
+
+ if (test.error || test.failing) {
+ title = colors.error(test.title);
+ }
+
+ return title + '\n' + this.reportCounts();
+ }
+ unhandledError(err) {
+ if (err.type === 'exception') {
+ this.exceptionCount++;
+ } else {
+ this.rejectionCount++;
+ }
+ }
+ reportCounts(time) {
+ const lines = [
+ this.passCount > 0 ? '\n ' + colors.pass(this.passCount, 'passed') : '',
+ this.knownFailureCount > 0 ? '\n ' + colors.error(this.knownFailureCount, plur('known failure', this.knownFailureCount)) : '',
+ this.failCount > 0 ? '\n ' + colors.error(this.failCount, 'failed') : '',
+ this.skipCount > 0 ? '\n ' + colors.skip(this.skipCount, 'skipped') : '',
+ this.todoCount > 0 ? '\n ' + colors.todo(this.todoCount, 'todo') : ''
+ ].filter(Boolean);
+
+ if (time && lines.length > 0) {
+ lines[0] += ' ' + time;
+ }
+
+ return lines.join('');
+ }
+ finish(runStatus) {
+ this.clearInterval();
+ let time;
+
+ if (this.options.watching) {
+ time = chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']');
+ }
+
+ let status = this.reportCounts(time);
+
+ if (this.rejectionCount > 0) {
+ status += '\n ' + colors.error(this.rejectionCount, plur('rejection', this.rejectionCount));
+ }
+
+ if (this.exceptionCount > 0) {
+ status += '\n ' + colors.error(this.exceptionCount, plur('exception', this.exceptionCount));
+ }
+
+ if (runStatus.previousFailCount > 0) {
+ status += '\n ' + colors.error(runStatus.previousFailCount, 'previous', plur('failure', runStatus.previousFailCount), 'in test files that were not rerun');
+ }
+
+ if (this.knownFailureCount > 0) {
+ for (const test of runStatus.knownFailures) {
+ const title = test.title;
+ status += '\n\n ' + colors.title(title);
+ // TODO: Output description with link
+ // status += colors.stack(description);
+ }
+ }
+
+ if (this.failCount > 0) {
+ runStatus.errors.forEach((test, index) => {
+ if (!test.error) {
+ return;
+ }
+
+ const beforeSpacing = index === 0 ? '\n\n' : '\n\n\n\n';
+
+ status += beforeSpacing + ' ' + colors.title(test.title) + '\n';
+ if (test.error.source) {
+ status += ' ' + colors.errorSource(test.error.source.file + ':' + test.error.source.line) + '\n';
+
+ const excerpt = codeExcerpt(test.error.source, {maxWidth: process.stdout.columns});
+ if (excerpt) {
+ status += '\n' + indentString(excerpt, 2) + '\n';
+ }
+ }
+
+ if (test.error.message) {
+ status += '\n' + indentString(test.error.message, 2) + '\n';
+ }
+
+ if (test.error.avaAssertionError) {
+ const formatted = formatAssertError.formatSerializedError(test.error);
+ if (formatted) {
+ status += '\n' + indentString(formatted, 2);
+ }
+
+ const message = improperUsageMessages.forError(test.error);
+ if (message) {
+ status += '\n' + indentString(message, 2) + '\n';
+ }
+ }
+
+ if (test.error.stack) {
+ const extracted = extractStack(test.error.stack);
+ if (extracted.includes('\n')) {
+ status += '\n' + indentString(colors.errorStack(extracted), 2);
+ }
+ }
+ });
+ }
+
+ if (this.rejectionCount > 0 || this.exceptionCount > 0) {
+ // TODO(sindresorhus): Figure out why this causes a test failure when switched to a for-of loop
+ runStatus.errors.forEach(err => {
+ if (err.title) {
+ return;
+ }
+
+ if (err.type === 'exception' && err.name === 'AvaError') {
+ status += '\n\n ' + colors.error(cross + ' ' + err.message);
+ } else {
+ const title = err.type === 'rejection' ? 'Unhandled Rejection' : 'Uncaught Exception';
+ let description = err.stack ? err.stack.trimRight() : JSON.stringify(err);
+ description = description.split('\n');
+ const errorTitle = err.name ? description[0] : 'Threw non-error: ' + description[0];
+ const errorStack = description.slice(1).join('\n');
+
+ status += '\n\n ' + colors.title(title) + '\n';
+ status += ' ' + colors.stack(errorTitle) + '\n';
+ status += colors.errorStack(errorStack);
+ }
+ });
+ }
+
+ if (runStatus.failFastEnabled === true && runStatus.remainingCount > 0 && runStatus.failCount > 0) {
+ const remaining = 'At least ' + runStatus.remainingCount + ' ' + plur('test was', 'tests were', runStatus.remainingCount) + ' skipped.';
+ status += '\n\n ' + colors.information('`--fail-fast` is on. ' + remaining);
+ }
+
+ if (runStatus.hasExclusive === true && runStatus.remainingCount > 0) {
+ status += '\n\n ' + colors.information('The .only() modifier is used in some tests.', runStatus.remainingCount, plur('test', runStatus.remainingCount), plur('was', 'were', runStatus.remainingCount), 'not run');
+ }
+
+ return status + '\n\n';
+ }
+ section() {
+ return '\n' + chalk.gray.dim('\u2500'.repeat(process.stdout.columns || 80));
+ }
+ clear() {
+ return '';
+ }
+ write(str) {
+ cliCursor.hide();
+ this.currentStatus = str;
+ this._update();
+ this.statusLineCount = this.currentStatus.split('\n').length;
+ }
+ stdout(data) {
+ this._update(data);
+ }
+ stderr(data) {
+ this._update(data);
+ }
+ _update(data) {
+ let str = '';
+ let ct = this.statusLineCount;
+ const columns = process.stdout.columns;
+ let lastLine = this.lastLineTracker.lastLine();
+
+ // Terminals automatically wrap text. We only need the last log line as seen on the screen.
+ lastLine = lastLine.substring(lastLine.length - (lastLine.length % columns));
+
+ // Don't delete the last log line if it's completely empty.
+ if (lastLine.length > 0) {
+ ct++;
+ }
+
+ // Erase the existing status message, plus the last log line.
+ str += eraseLines(ct);
+
+ // Rewrite the last log line.
+ str += lastLine;
+
+ if (str.length > 0) {
+ this.stream.write(str);
+ }
+
+ if (data) {
+ // Send new log data to the terminal, and update the last line status.
+ this.lastLineTracker.update(this.stringDecoder.write(data));
+ this.stream.write(data);
+ }
+
+ let currentStatus = this.currentStatus;
+
+ if (currentStatus.length > 0) {
+ lastLine = this.lastLineTracker.lastLine();
+ // We need a newline at the end of the last log line, before the status message.
+ // However, if the last log line is the exact width of the terminal a newline is implied,
+ // and adding a second will cause problems.
+ if (lastLine.length % columns) {
+ currentStatus = '\n' + currentStatus;
+ }
+ // Rewrite the status message.
+ this.stream.write(currentStatus);
+ }
+ }
+}
+
+module.exports = MiniReporter;
diff --git a/node_modules/ava/lib/reporters/tap.js b/node_modules/ava/lib/reporters/tap.js
new file mode 100644
index 000000000..37c2cfd95
--- /dev/null
+++ b/node_modules/ava/lib/reporters/tap.js
@@ -0,0 +1,119 @@
+'use strict';
+const format = require('util').format;
+const indentString = require('indent-string');
+const stripAnsi = require('strip-ansi');
+const yaml = require('js-yaml');
+const extractStack = require('../extract-stack');
+
+// Parses stack trace and extracts original function name, file name and line
+function getSourceFromStack(stack) {
+ return extractStack(stack).split('\n')[0];
+}
+
+function dumpError(error, includeMessage) {
+ const obj = Object.assign({}, error.object);
+ if (error.name) {
+ obj.name = error.name;
+ }
+ if (includeMessage && error.message) {
+ obj.message = error.message;
+ }
+
+ if (error.avaAssertionError) {
+ if (error.assertion) {
+ obj.assertion = error.assertion;
+ }
+ if (error.operator) {
+ obj.operator = error.operator;
+ }
+ if (error.values.length > 0) {
+ obj.values = error.values.reduce((acc, value) => {
+ acc[value.label] = stripAnsi(value.formatted);
+ return acc;
+ }, {});
+ }
+ }
+
+ if (error.stack) {
+ obj.at = getSourceFromStack(error.stack);
+ }
+
+ return ` ---\n${indentString(yaml.safeDump(obj).trim(), 4)}\n ...`;
+}
+
+class TapReporter {
+ constructor() {
+ this.i = 0;
+ }
+ start() {
+ return 'TAP version 13';
+ }
+ test(test) {
+ let output;
+
+ let directive = '';
+ const passed = test.todo ? 'not ok' : 'ok';
+
+ if (test.todo) {
+ directive = '# TODO';
+ } else if (test.skip) {
+ directive = '# SKIP';
+ }
+
+ const title = stripAnsi(test.title);
+
+ if (test.error) {
+ output = [
+ '# ' + title,
+ format('not ok %d - %s', ++this.i, title),
+ dumpError(test.error, true)
+ ];
+ } else {
+ output = [
+ `# ${title}`,
+ format('%s %d - %s %s', passed, ++this.i, title, directive).trim()
+ ];
+ }
+
+ return output.join('\n');
+ }
+ unhandledError(err) {
+ const output = [
+ `# ${err.message}`,
+ format('not ok %d - %s', ++this.i, err.message)
+ ];
+ // AvaErrors don't have stack traces
+ if (err.type !== 'exception' || err.name !== 'AvaError') {
+ output.push(dumpError(err, false));
+ }
+
+ return output.join('\n');
+ }
+ finish(runStatus) {
+ const output = [
+ '',
+ '1..' + (runStatus.passCount + runStatus.failCount + runStatus.skipCount),
+ '# tests ' + (runStatus.passCount + runStatus.failCount + runStatus.skipCount),
+ '# pass ' + runStatus.passCount
+ ];
+
+ if (runStatus.skipCount > 0) {
+ output.push(`# skip ${runStatus.skipCount}`);
+ }
+
+ output.push('# fail ' + (runStatus.failCount + runStatus.rejectionCount + runStatus.exceptionCount), '');
+
+ return output.join('\n');
+ }
+ write(str) {
+ console.log(str);
+ }
+ stdout(data) {
+ process.stderr.write(data);
+ }
+ stderr(data) {
+ this.stdout(data);
+ }
+}
+
+module.exports = TapReporter;
diff --git a/node_modules/ava/lib/reporters/verbose.js b/node_modules/ava/lib/reporters/verbose.js
new file mode 100644
index 000000000..1be43ce5e
--- /dev/null
+++ b/node_modules/ava/lib/reporters/verbose.js
@@ -0,0 +1,165 @@
+'use strict';
+const indentString = require('indent-string');
+const prettyMs = require('pretty-ms');
+const figures = require('figures');
+const chalk = require('chalk');
+const plur = require('plur');
+const formatAssertError = require('../format-assert-error');
+const extractStack = require('../extract-stack');
+const codeExcerpt = require('../code-excerpt');
+const colors = require('../colors');
+const improperUsageMessages = require('./improper-usage-messages');
+
+class VerboseReporter {
+ constructor(options) {
+ this.options = Object.assign({}, options);
+
+ chalk.enabled = this.options.color;
+ for (const key of Object.keys(colors)) {
+ colors[key].enabled = this.options.color;
+ }
+ }
+ start() {
+ return '';
+ }
+ test(test, runStatus) {
+ if (test.error) {
+ return ' ' + colors.error(figures.cross) + ' ' + test.title + ' ' + colors.error(test.error.message);
+ }
+
+ if (test.todo) {
+ return ' ' + colors.todo('- ' + test.title);
+ } else if (test.skip) {
+ return ' ' + colors.skip('- ' + test.title);
+ }
+
+ if (test.failing) {
+ return ' ' + colors.error(figures.tick) + ' ' + colors.error(test.title);
+ }
+
+ if (runStatus.fileCount === 1 && runStatus.testCount === 1 && test.title === '[anonymous]') {
+ return undefined;
+ }
+
+ // Display duration only over a threshold
+ const threshold = 100;
+ const duration = test.duration > threshold ? colors.duration(' (' + prettyMs(test.duration) + ')') : '';
+
+ return ' ' + colors.pass(figures.tick) + ' ' + test.title + duration;
+ }
+ unhandledError(err) {
+ if (err.type === 'exception' && err.name === 'AvaError') {
+ return colors.error(' ' + figures.cross + ' ' + err.message);
+ }
+
+ const types = {
+ rejection: 'Unhandled Rejection',
+ exception: 'Uncaught Exception'
+ };
+
+ let output = colors.error(types[err.type] + ':', err.file) + '\n';
+
+ if (err.stack) {
+ output += ' ' + colors.stack(err.stack) + '\n';
+ } else {
+ output += ' ' + colors.stack(JSON.stringify(err)) + '\n';
+ }
+
+ output += '\n';
+
+ return output;
+ }
+ finish(runStatus) {
+ let output = '\n';
+
+ const lines = [
+ runStatus.failCount > 0 ?
+ ' ' + colors.error(runStatus.failCount, plur('test', runStatus.failCount), 'failed') :
+ ' ' + colors.pass(runStatus.passCount, plur('test', runStatus.passCount), 'passed'),
+ runStatus.knownFailureCount > 0 ? ' ' + colors.error(runStatus.knownFailureCount, plur('known failure', runStatus.knownFailureCount)) : '',
+ runStatus.skipCount > 0 ? ' ' + colors.skip(runStatus.skipCount, plur('test', runStatus.skipCount), 'skipped') : '',
+ runStatus.todoCount > 0 ? ' ' + colors.todo(runStatus.todoCount, plur('test', runStatus.todoCount), 'todo') : '',
+ runStatus.rejectionCount > 0 ? ' ' + colors.error(runStatus.rejectionCount, 'unhandled', plur('rejection', runStatus.rejectionCount)) : '',
+ runStatus.exceptionCount > 0 ? ' ' + colors.error(runStatus.exceptionCount, 'uncaught', plur('exception', runStatus.exceptionCount)) : '',
+ runStatus.previousFailCount > 0 ? ' ' + colors.error(runStatus.previousFailCount, 'previous', plur('failure', runStatus.previousFailCount), 'in test files that were not rerun') : ''
+ ].filter(Boolean);
+
+ if (lines.length > 0) {
+ lines[0] += ' ' + chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']');
+ output += lines.join('\n');
+ }
+
+ if (runStatus.knownFailureCount > 0) {
+ runStatus.knownFailures.forEach(test => {
+ output += '\n\n\n ' + colors.error(test.title);
+ });
+ }
+
+ if (runStatus.failCount > 0) {
+ runStatus.tests.forEach((test, index) => {
+ if (!test.error) {
+ return;
+ }
+
+ const beforeSpacing = index === 0 ? '\n\n' : '\n\n\n\n';
+ output += beforeSpacing + ' ' + colors.title(test.title) + '\n';
+ if (test.error.source) {
+ output += ' ' + colors.errorSource(test.error.source.file + ':' + test.error.source.line) + '\n';
+
+ const excerpt = codeExcerpt(test.error.source, {maxWidth: process.stdout.columns});
+ if (excerpt) {
+ output += '\n' + indentString(excerpt, 2) + '\n';
+ }
+ }
+
+ if (test.error.message) {
+ output += '\n' + indentString(test.error.message, 2) + '\n';
+ }
+
+ if (test.error.avaAssertionError) {
+ const formatted = formatAssertError.formatSerializedError(test.error);
+ if (formatted) {
+ output += '\n' + indentString(formatted, 2);
+ }
+
+ const message = improperUsageMessages.forError(test.error);
+ if (message) {
+ output += '\n' + indentString(message, 2) + '\n';
+ }
+ }
+
+ if (test.error.stack) {
+ const extracted = extractStack(test.error.stack);
+ if (extracted.includes('\n')) {
+ output += '\n' + indentString(colors.errorStack(extracted), 2);
+ }
+ }
+ });
+ }
+
+ if (runStatus.failFastEnabled === true && runStatus.remainingCount > 0 && runStatus.failCount > 0) {
+ const remaining = 'At least ' + runStatus.remainingCount + ' ' + plur('test was', 'tests were', runStatus.remainingCount) + ' skipped.';
+ output += '\n\n\n ' + colors.information('`--fail-fast` is on. ' + remaining);
+ }
+
+ if (runStatus.hasExclusive === true && runStatus.remainingCount > 0) {
+ output += '\n\n\n ' + colors.information('The .only() modifier is used in some tests.', runStatus.remainingCount, plur('test', runStatus.remainingCount), plur('was', 'were', runStatus.remainingCount), 'not run');
+ }
+
+ return output + '\n';
+ }
+ section() {
+ return chalk.gray.dim('\u2500'.repeat(process.stdout.columns || 80));
+ }
+ write(str) {
+ console.error(str);
+ }
+ stdout(data) {
+ process.stderr.write(data);
+ }
+ stderr(data) {
+ process.stderr.write(data);
+ }
+}
+
+module.exports = VerboseReporter;