UNPKG

3.22 kBJavaScriptView Raw
1import _ from 'babel/polyfill';
2import { diffWords, diffJson } from 'diff';
3import chalk from 'chalk';
4import duplexer from 'duplexer';
5import figures from 'figures';
6import through2 from 'through2';
7import parser from 'tap-parser';
8import prettyMs from 'pretty-ms';
9
10const INDENT = ' ';
11const FIG_TICK = figures.tick;
12const FIG_CROSS = figures.cross;
13
14const createReporter = () => {
15 const output = through2()
16 const p = parser();
17 const stream = duplexer(p, output);
18 const startedAt = Date.now();
19
20 const write = (str, indentLevel = 0) => {
21 let indent = '';
22
23 for (let i = 0; i < indentLevel; ++i) {
24 indent += INDENT;
25 }
26
27 output.push(
28 str.split('\n')
29 .map(part => part.length > 0 ? `${indent}${part}` : part)
30 .join('\n')
31 );
32 };
33
34 const handleTest = name => {
35 write('\n');
36 write(`${chalk.blue(name)}\n`, 1);
37 };
38
39 const handleAssertSuccess = assert => {
40 const name = assert.name;
41
42 write(`${chalk.green(FIG_TICK)} ${chalk.dim(name)}\n`, 2);
43 };
44
45 const handleAssertFailure = assert => {
46 const name = assert.name;
47 const diag = assert.diag;
48 const writeDiff = ({ value, added, removed }) => {
49 let style = chalk.white;
50
51 if (added) style = chalk.green.inverse;
52 if (removed) style = chalk.red.inverse;
53
54 return value.split('\n')
55 .map(str => str.length > 0 ? style(str) : str)
56 .join('\n')
57 };
58
59 write(`${chalk.red(FIG_CROSS)} ${chalk.red(name)} `, 2);
60 write(`at ${chalk.magenta(diag.at)}\n`);
61
62 if (typeof diag.expected === 'object' && diag.expected !== null) {
63 const compared = diffJson(diag.actual, diag.expected)
64 .map(writeDiff)
65 .join('');
66
67 write(`${compared}\n`, 4);
68 } else if (typeof diag.expected === 'string') {
69 const compared = diffWords(diag.actual, diag.expected)
70 .map(writeDiff)
71 .join('');
72
73 write(`${compared}\n`, 4);
74 } else {
75 write(' ' +
76 chalk.red.inverse(diag.actual) +
77 chalk.green.inverse(diag.expected) + '\n'
78 );
79 }
80
81
82 };
83
84 const handleComplete = results => {
85 const finishedAt = Date.now();
86
87 write('\n');
88 write(chalk.green(`passed: ${results.pass} `));
89 write(chalk.red(`failed: ${results.fail || 0} `));
90 write(chalk.white(`of ${results.count} tests `));
91 write(chalk.dim(`(${prettyMs(finishedAt - startedAt)})\n\n`));
92
93 if (results.ok) {
94 write(chalk.green(`All of ${results.count} tests passed!`));
95 } else {
96 write(chalk.red(
97 `${results.fail} of ${results.count} tests failed.`
98 ));
99 stream.isFailed = true;
100 }
101
102 write('\n\n');
103 };
104
105 p.on('comment', (comment) => {
106 const trimmed = comment.replace('# ', '').trim();
107
108 if (/^tests\s+[0-9]+$/.test(trimmed)) return;
109 if (/^pass\s+[0-9]+$/.test(trimmed)) return;
110 if (/^fail\s+[0-9]+$/.test(trimmed)) return;
111 if (/^ok$/.test(trimmed)) return;
112
113 handleTest(trimmed);
114 });
115
116 p.on('assert', (assert) => {
117 if (assert.ok) return handleAssertSuccess(assert);
118
119 handleAssertFailure(assert);
120 });
121
122 p.on('complete', handleComplete);
123
124 p.on('child', (child) => {
125 ;
126 });
127
128 return stream;
129};
130
131export default createReporter;