UNPKG

7.75 kBJavaScriptView Raw
1"use strict";
2/**
3 * @module Core
4 */
5var __importDefault = (this && this.__importDefault) || function (mod) {
6 return (mod && mod.__esModule) ? mod : { "default": mod };
7};
8Object.defineProperty(exports, "__esModule", { value: true });
9/*
10 * japa
11 *
12 * (c) Harminder Virk <virk@adonisjs.com>
13 *
14 * For the full copyright and license information, please view the LICENSE
15 * file that was distributed with this source code.
16*/
17const ms_1 = __importDefault(require("ms"));
18const chalk_1 = __importDefault(require("chalk"));
19const jest_diff_1 = __importDefault(require("jest-diff"));
20const utils_1 = require("../utils");
21const Contracts_1 = require("../Contracts");
22/**
23 * Icons to be used for different test and group
24 * statuses
25 */
26const icons = {
27 passed: chalk_1.default.green('✓'),
28 failed: chalk_1.default.red('✖'),
29 skipped: chalk_1.default.yellow('.'),
30 todo: chalk_1.default.cyan('!'),
31 regression: '',
32};
33/**
34 * Colors to be used for different test and group
35 * statuses
36 */
37const colors = {
38 passed: 'grey',
39 failed: 'red',
40 skipped: 'yellow',
41 todo: 'cyan',
42 regression: 'magenta',
43};
44/**
45 * List reporter to show the tests progress on stdout in
46 * a list format
47 */
48class ListReporter {
49 constructor(emitter) {
50 this._store = new utils_1.TestsStore();
51 emitter.on(Contracts_1.IEvents.STARTED, this._onStart.bind(this));
52 emitter.on(Contracts_1.IEvents.COMPLETED, this._onEnd.bind(this));
53 emitter.on(Contracts_1.IEvents.GROUPSTARTED, this._onGroupStart.bind(this));
54 emitter.on(Contracts_1.IEvents.GROUPCOMPLETED, this._onGroupEnd.bind(this));
55 emitter.on(Contracts_1.IEvents.TESTCOMPLETED, this._onTestEnd.bind(this));
56 }
57 get _indent() {
58 return this._store.activeGroup.title === 'root' ? '' : ' ';
59 }
60 /**
61 * When test runner has started. We just need to initiate
62 * the store on this event
63 */
64 _onStart() {
65 console.log('');
66 this._store.open();
67 }
68 /**
69 * Everytime a new group starts
70 */
71 _onGroupStart(group) {
72 this._store.recordGroup(group);
73 /**
74 * Log group title when it's not root
75 */
76 if (group.title !== 'root') {
77 console.log(`\n${group.title}`);
78 }
79 }
80 /**
81 * Everytime a group has completed running all tests
82 */
83 _onGroupEnd(group) {
84 this._store.endGroup(group);
85 }
86 /**
87 * Print count for a label
88 */
89 _printCount(label, count) {
90 if (count) {
91 console.log(chalk_1.default.dim(`${label.padEnd(13)} : ${count}`));
92 }
93 }
94 /**
95 * Prints the error for the test. If error is an assertion error, then
96 * there is no need to print the stack.
97 */
98 _printTestError(error) {
99 if ((0, utils_1.isCoreException)(error)) {
100 console.log(chalk_1.default.red(` ${error.message}`));
101 return;
102 }
103 const { actual, expected } = error;
104 if (actual && expected) {
105 const diff = (0, jest_diff_1.default)(expected, actual);
106 const mainLine = this._cleanupErrorStack(error.stack)[1];
107 if (mainLine) {
108 console.log(chalk_1.default.dim(` source => ${mainLine.trim().replace(/at\s+/, '')}`));
109 }
110 if (diff) {
111 console.log(diff);
112 }
113 else {
114 console.log(chalk_1.default.red(` Assertion Error: ${error.message}`));
115 }
116 return;
117 }
118 console.log(` ${this._formatErrorStack(error.stack)}`);
119 }
120 /**
121 * Everytime tests ends
122 */
123 _onTestEnd(test) {
124 this._store.recordTest(test);
125 const icon = icons[test.status];
126 const message = chalk_1.default[colors[test.status]](test.title);
127 const duration = chalk_1.default.dim(`(${(0, ms_1.default)(test.duration)})`);
128 const regressionMessage = test.regressionMessage
129 ? `\n${this._indent} ${chalk_1.default.magenta(test.regressionMessage)}`
130 : '';
131 console.log(`${this._indent}${icon} ${message} ${duration}${regressionMessage}`);
132 }
133 /**
134 * Returns a boolean if the error stack fine part of the
135 * japa core
136 */
137 _isNativeStackLine(line) {
138 return ['Callable'].some((keyword) => line.includes(keyword));
139 }
140 /**
141 * Returns a boolean telling if error stack is part
142 * of japa core by finding the sorroundings.
143 */
144 _isNativeSorroundedLine(line) {
145 return ['Generator.next', 'new Promise'].some((keyword) => line.includes(keyword));
146 }
147 /**
148 * Returns the title for the failing test
149 */
150 _getFailingTitle(title) {
151 return chalk_1.default.red(`${icons.failed} ${title}`);
152 }
153 /**
154 * Cleans up the error stack
155 */
156 _cleanupErrorStack(errorStack) {
157 let prevIsNative = false;
158 if (!errorStack) {
159 return [];
160 }
161 return errorStack
162 .split('\n')
163 .filter((line) => {
164 if (prevIsNative && this._isNativeSorroundedLine(line)) {
165 return false;
166 }
167 prevIsNative = this._isNativeStackLine(line);
168 return !prevIsNative;
169 });
170 }
171 /**
172 * Returns the error stack by filtering the japa core
173 * lines from it.
174 */
175 _formatErrorStack(errorStack) {
176 if (!errorStack) {
177 return;
178 }
179 return this._cleanupErrorStack(errorStack).map((line, index) => {
180 if (index === 0) {
181 return chalk_1.default.red(line);
182 }
183 return chalk_1.default.dim(line);
184 }).join('\n');
185 }
186 /**
187 * When test runner stops
188 */
189 _onEnd() {
190 this._store.close();
191 const report = this._store.getReport();
192 /**
193 * Show zero executed tests when no tests were ran
194 */
195 if (report.total === 0 && report.groups.length === 0) {
196 console.log(chalk_1.default.bgMagenta.white(' ZERO TESTS EXECUTED '));
197 return;
198 }
199 const failedGroups = report.groups.filter((group) => {
200 return group.failedTests.length || group.failedHooks.length;
201 });
202 console.log('');
203 if (failedGroups.length) {
204 console.log(chalk_1.default.bgRed.white(' FAILED '));
205 }
206 else {
207 console.log(chalk_1.default.bgGreen.white(' PASSED '));
208 }
209 console.log('');
210 this._printCount('total', report.total);
211 this._printCount('failed', report.failedCount);
212 this._printCount('passed', report.passedCount);
213 this._printCount('todo', report.todoCount);
214 this._printCount('skipped', report.skippedCount);
215 this._printCount('regression', report.regressionCount);
216 this._printCount('duration', (0, ms_1.default)(report.duration));
217 failedGroups.forEach(({ title, failedHooks, failedTests }) => {
218 console.log('');
219 console.log(failedHooks.length ? this._getFailingTitle(title) : title);
220 if (failedHooks.length) {
221 const failedHook = failedHooks[0];
222 console.log(`${chalk_1.default.red(` (${failedHook.title})`)} ${this._formatErrorStack(failedHook.error.stack)}`);
223 }
224 if (failedTests.length) {
225 failedTests.forEach((test) => {
226 console.log('');
227 console.log(` ${this._getFailingTitle(test.title)}`);
228 this._printTestError(test.error);
229 });
230 }
231 });
232 }
233}
234function listReporter(emitter) {
235 return new ListReporter(emitter);
236}
237exports.default = listReporter;