UNPKG

5.4 kBJavaScriptView Raw
1const Base = require('mocha/lib/reporters/base');
2const mochaPkg = require('mocha/package.json');
3const uuid = require('uuid');
4const marge = require('mochawesome-report-generator');
5const margePkg = require('mochawesome-report-generator/package.json');
6const conf = require('./config');
7const utils = require('./utils');
8const pkg = require('../package.json');
9
10// Import the utility functions
11const {
12 log,
13 mapSuites
14} = utils;
15
16// Track the total number of tests registered
17const totalTestsRegistered = { total: 0 };
18
19/**
20 * Done function gets called before mocha exits
21 *
22 * Creates and saves the report HTML and JSON files
23 *
24 * @param {Object} output Final report object
25 * @param {Object} options Options to pass to report generator
26 * @param {Object} config Reporter config object
27 * @param {Number} failures Number of reported failures
28 * @param {Function} exit
29 *
30 * @return {Promise} Resolves with successful report creation
31 */
32function done(output, options, config, failures, exit) {
33 return marge.create(output, options)
34 .then(([ htmlFile, jsonFile ]) => {
35 if (!htmlFile && !jsonFile) {
36 log('No files were generated', 'warn', config);
37 } else {
38 jsonFile && log(`Report JSON saved to ${jsonFile}`, null, config);
39 htmlFile && log(`Report HTML saved to ${htmlFile}`, null, config);
40 }
41 })
42 .catch(err => {
43 log(err, 'error', config);
44 })
45 .then(() => {
46 exit && exit(failures > 0 ? 1 : 0);
47 });
48}
49
50/**
51 * Get the class of the configured console reporter. This reporter outputs
52 * test results to the console while mocha is running, and before
53 * mochawesome generates its own report.
54 *
55 * Defaults to 'spec'.
56 *
57 * @param {String} reporter Name of reporter to use for console output
58 *
59 * @return {Object} Reporter class object
60 */
61function consoleReporter(reporter) {
62 if (reporter) {
63 try {
64 // eslint-disable-next-line import/no-dynamic-require
65 return require(`mocha/lib/reporters/${reporter}`);
66 } catch (e) {
67 log(`Unknown console reporter '${reporter}', defaulting to spec`);
68 }
69 }
70
71 return require('mocha/lib/reporters/spec');
72}
73
74/**
75 * Initialize a new reporter.
76 *
77 * @param {Runner} runner
78 * @api public
79 */
80function Mochawesome(runner, options) {
81 // Set the config options
82 this.config = conf(options);
83
84 // Ensure stats collector has been initialized
85 if (!runner.stats) {
86 const createStatsCollector = require('mocha/lib/stats-collector');
87 createStatsCollector(runner);
88 }
89
90 // Reporter options
91 const reporterOptions = {
92 ...options.reporterOptions,
93 reportFilename: this.config.reportFilename,
94 saveHtml: this.config.saveHtml,
95 saveJson: this.config.saveJson
96 };
97
98 // Done function will be called before mocha exits
99 // This is where we will save JSON and generate the HTML report
100 this.done = (failures, exit) => done(
101 this.output,
102 reporterOptions,
103 this.config,
104 failures,
105 exit
106 );
107
108 // Reset total tests counter
109 totalTestsRegistered.total = 0;
110
111 // Call the Base mocha reporter
112 Base.call(this, runner);
113
114 const reporterName = reporterOptions.consoleReporter;
115 if (reporterName !== 'none') {
116 const ConsoleReporter = consoleReporter(reporterName);
117 new ConsoleReporter(runner); // eslint-disable-line
118 }
119
120 let endCalled = false;
121
122 // Add a unique identifier to each suite/test/hook
123 [ 'suite', 'test', 'hook', 'pending' ].forEach(type => {
124 runner.on(type, item => {
125 item.uuid = uuid.v4();
126 });
127 });
128
129 // Process the full suite
130 runner.on('end', () => {
131 try {
132 /* istanbul ignore else */
133 if (!endCalled) {
134 // end gets called more than once for some reason
135 // so we ensure the suite is processed only once
136 endCalled = true;
137
138 const rootSuite = mapSuites(this.runner.suite, totalTestsRegistered, this.config);
139
140 const obj = {
141 stats: this.stats,
142 results: [ rootSuite ],
143 meta: {
144 mocha: {
145 version: mochaPkg.version
146 },
147 mochawesome: {
148 options: this.config,
149 version: pkg.version
150 },
151 marge: {
152 options: options.reporterOptions,
153 version: margePkg.version
154 }
155 }
156 };
157
158 obj.stats.testsRegistered = totalTestsRegistered.total;
159
160 const { passes, failures, pending, tests, testsRegistered } = obj.stats;
161 const passPercentage = (passes / (testsRegistered - pending)) * 100;
162 const pendingPercentage = (pending / testsRegistered) * 100;
163
164 obj.stats.passPercent = passPercentage;
165 obj.stats.pendingPercent = pendingPercentage;
166 obj.stats.other = (passes + failures + pending) - tests; // Failed hooks
167 obj.stats.hasOther = obj.stats.other > 0;
168 obj.stats.skipped = testsRegistered - tests;
169 obj.stats.hasSkipped = obj.stats.skipped > 0;
170 obj.stats.failures -= obj.stats.other;
171
172 // Save the final output to be used in the done function
173 this.output = obj;
174 }
175 } catch (e) {
176 // required because thrown errors are not handled directly in the
177 // event emitter pattern and mocha does not have an "on error"
178 /* istanbul ignore next */
179 log(`Problem with mochawesome: ${e.stack}`, 'error');
180 }
181 });
182}
183
184module.exports = Mochawesome;