jest-html-reporter 2.8.1 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const dateformat_1 = __importDefault(require("dateformat"));
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const mkdirp_1 = __importDefault(require("mkdirp"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const strip_ansi_1 = __importDefault(require("strip-ansi"));
11
+ const xmlbuilder_1 = __importDefault(require("xmlbuilder"));
12
+ const sortingMethods_1 = require("./sortingMethods");
13
+ class HTMLReporter {
14
+ constructor(testData, options) {
15
+ this.testData = testData;
16
+ this.setupConfig(options);
17
+ }
18
+ async generate() {
19
+ const report = await this.renderTestReport();
20
+ const outputPath = this.getConfigValue("outputPath");
21
+ await mkdirp_1.default(path_1.default.dirname(outputPath));
22
+ await fs_1.default.writeFile(outputPath, report, writeFileError => {
23
+ if (writeFileError) {
24
+ throw new Error(`Something went wrong when creating the file: ${writeFileError}`);
25
+ }
26
+ });
27
+ this.logMessage("success", `Report generated (${outputPath})`);
28
+ }
29
+ async renderTestReport() {
30
+ try {
31
+ // Generate the content of the test report
32
+ const reportBody = await this.renderTestReportBody();
33
+ // --
34
+ // Boilerplate Option
35
+ if (!!this.getConfigValue("boilerplate")) {
36
+ const boilerplateContent = await this.getFileContent(this.getConfigValue("boilerplate"));
37
+ return boilerplateContent.replace("{jesthtmlreporter-content}", reportBody.toString());
38
+ }
39
+ // --
40
+ // Create HTML and apply reporter content
41
+ const HTMLBase = {
42
+ html: {
43
+ head: {
44
+ meta: { "@charset": "utf-8" },
45
+ title: { "#text": this.getConfigValue("pageTitle") },
46
+ style: undefined,
47
+ link: undefined
48
+ }
49
+ }
50
+ };
51
+ // Default to the currently set theme
52
+ let stylesheetFilePath = path_1.default.join(__dirname, `../style/${this.getConfigValue("theme")}.css`);
53
+ // Overriding stylesheet
54
+ if (this.getConfigValue("styleOverridePath")) {
55
+ stylesheetFilePath = this.getConfigValue("styleOverridePath");
56
+ }
57
+ // Decide whether to inline the CSS or not
58
+ const inlineCSS = !this.getConfigValue("useCssFile") &&
59
+ !!!this.getConfigValue("styleOverridePath");
60
+ if (inlineCSS) {
61
+ const stylesheetContent = await fs_1.default.readFileSync(stylesheetFilePath, "utf8");
62
+ HTMLBase.html.head.style = {
63
+ "@type": "text/css",
64
+ "#text": stylesheetContent
65
+ };
66
+ }
67
+ else {
68
+ HTMLBase.html.head.link = {
69
+ "@rel": "stylesheet",
70
+ "@type": "text/css",
71
+ "@href": stylesheetFilePath
72
+ };
73
+ }
74
+ const report = xmlbuilder_1.default.create(HTMLBase);
75
+ report.ele("body").raw(reportBody.toString());
76
+ return report;
77
+ }
78
+ catch (e) {
79
+ this.logMessage("error", e);
80
+ return;
81
+ }
82
+ }
83
+ async renderTestReportBody() {
84
+ try {
85
+ if (!this.testData) {
86
+ throw Error("No test data provided");
87
+ }
88
+ // HTML Body
89
+ const reportBody = xmlbuilder_1.default.begin().element("div", {
90
+ id: "jesthtml-content"
91
+ });
92
+ /**
93
+ * Page Header
94
+ */
95
+ const header = reportBody.ele("header");
96
+ // Page Title
97
+ header.ele("h1", { id: "title" }, this.getConfigValue("pageTitle"));
98
+ // Logo
99
+ const logo = this.getConfigValue("logo");
100
+ if (logo) {
101
+ header.ele("img", { id: "logo", src: logo });
102
+ }
103
+ /**
104
+ * Meta-Data
105
+ */
106
+ const metaDataContainer = reportBody.ele("div", {
107
+ id: "metadata-container"
108
+ });
109
+ // Timestamp
110
+ const timestamp = new Date(this.testData.startTime);
111
+ metaDataContainer.ele("div", { id: "timestamp" }, `Start: ${dateformat_1.default(timestamp.toDateString(), this.getConfigValue("dateFormat"))}`);
112
+ // Test Summary
113
+ metaDataContainer.ele("div", { id: "summary" }, `${this.testData.numTotalTests} tests -- ${this.testData.numPassedTests} passed / ${this.testData.numFailedTests} failed / ${this.testData.numPendingTests} pending`);
114
+ /**
115
+ * Apply any given sorting method to the test results
116
+ */
117
+ const sortedTestResults = sortingMethods_1.sortTestResults(this.testData.testResults, this.getConfigValue("sort"));
118
+ /**
119
+ * Setup ignored test result statuses
120
+ */
121
+ const statusIgnoreFilter = this.getConfigValue("statusIgnoreFilter");
122
+ let ignoredStatuses = [];
123
+ if (statusIgnoreFilter) {
124
+ ignoredStatuses = statusIgnoreFilter
125
+ .replace(/\s/g, "")
126
+ .toLowerCase()
127
+ .split(",");
128
+ }
129
+ /**
130
+ * Test Suites
131
+ */
132
+ sortedTestResults.map(suite => {
133
+ // Filter out the test results with statuses that equals the statusIgnoreFilter
134
+ for (const [i, result] of suite.testResults.entries()) {
135
+ if (ignoredStatuses.includes(result.status)) {
136
+ suite.testResults.splice(i, 1);
137
+ }
138
+ }
139
+ // Ignore this suite if there are no results
140
+ if (!suite.testResults || suite.testResults.length <= 0) {
141
+ return;
142
+ }
143
+ // Suite Information
144
+ const suiteInfo = reportBody.ele("div", { class: "suite-info" });
145
+ // Suite Path
146
+ suiteInfo.ele("div", { class: "suite-path" }, suite.testFilePath);
147
+ // Suite execution time
148
+ const executionTime = (suite.perfStats.end - suite.perfStats.start) / 1000;
149
+ suiteInfo.ele("div", { class: `suite-time${executionTime > 5 ? " warn" : ""}` }, `${executionTime}s`);
150
+ // Suite Test Table
151
+ const suiteTable = reportBody.ele("table", {
152
+ class: "suite-table",
153
+ cellspacing: "0",
154
+ cellpadding: "0"
155
+ });
156
+ // Test Results
157
+ suite.testResults.forEach(test => {
158
+ const testTr = suiteTable.ele("tr", { class: test.status });
159
+ // Suite Name(s)
160
+ testTr.ele("td", { class: "suite" }, test.ancestorTitles.join(" > "));
161
+ // Test name
162
+ const testTitleTd = testTr.ele("td", { class: "test" }, test.title);
163
+ // Test Failure Messages
164
+ if (test.failureMessages &&
165
+ this.getConfigValue("includeFailureMsg")) {
166
+ const failureMsgDiv = testTitleTd.ele("div", {
167
+ class: "failureMessages"
168
+ });
169
+ test.failureMessages.forEach(failureMsg => {
170
+ failureMsgDiv.ele("pre", { class: "failureMsg" }, strip_ansi_1.default(failureMsg));
171
+ });
172
+ }
173
+ // Append data to <tr>
174
+ testTr.ele("td", { class: "result" }, test.status === "passed"
175
+ ? `${test.status} in ${test.duration / 1000}s`
176
+ : test.status);
177
+ });
178
+ // All console.logs caught during the test run
179
+ if (this.consoleLogList &&
180
+ this.consoleLogList.length > 0 &&
181
+ this.getConfigValue("includeConsoleLog")) {
182
+ // Filter out the logs for this test file path
183
+ const filteredConsoleLogs = this.consoleLogList.find(logs => logs.filePath === suite.testFilePath);
184
+ if (filteredConsoleLogs && filteredConsoleLogs.logs.length > 0) {
185
+ // Console Log Container
186
+ const consoleLogContainer = reportBody.ele("div", {
187
+ class: "suite-consolelog"
188
+ });
189
+ // Console Log Header
190
+ consoleLogContainer.ele("div", { class: "suite-consolelog-header" }, "Console Log");
191
+ // Apply the logs to the body
192
+ filteredConsoleLogs.logs.forEach(log => {
193
+ const logElement = consoleLogContainer.ele("div", {
194
+ class: "suite-consolelog-item"
195
+ });
196
+ logElement.ele("pre", { class: "suite-consolelog-item-origin" }, strip_ansi_1.default(log.origin));
197
+ logElement.ele("pre", { class: "suite-consolelog-item-message" }, strip_ansi_1.default(log.message));
198
+ });
199
+ }
200
+ }
201
+ });
202
+ return reportBody;
203
+ }
204
+ catch (e) {
205
+ this.logMessage("error", e);
206
+ }
207
+ }
208
+ /**
209
+ * Fetch and setup configuration
210
+ */
211
+ setupConfig(options) {
212
+ this.config = {
213
+ append: {
214
+ defaultValue: false,
215
+ environmentVariable: "JEST_HTML_REPORTER_APPEND",
216
+ configValue: options.append
217
+ },
218
+ boilerplate: {
219
+ defaultValue: null,
220
+ environmentVariable: "JEST_HTML_REPORTER_BOILERPLATE",
221
+ configValue: options.boilerplate
222
+ },
223
+ customScriptPath: {
224
+ defaultValue: null,
225
+ environmentVariable: "JEST_HTML_REPORTER_CUSTOM_SCRIPT_PATH",
226
+ configValue: options.customScriptPath
227
+ },
228
+ dateFormat: {
229
+ defaultValue: "yyyy-mm-dd HH:MM:ss",
230
+ environmentVariable: "JEST_HTML_REPORTER_DATE_FORMAT",
231
+ configValue: options.dateFormat
232
+ },
233
+ executionTimeWarningThreshold: {
234
+ defaultValue: 5,
235
+ environmentVariable: "JEST_HTML_REPORTER_EXECUTION_TIME_WARNING_THRESHOLD",
236
+ configValue: options.executionTimeWarningThreshold
237
+ },
238
+ logo: {
239
+ defaultValue: null,
240
+ environmentVariable: "JEST_HTML_REPORTER_LOGO",
241
+ configValue: options.logo
242
+ },
243
+ includeFailureMsg: {
244
+ defaultValue: false,
245
+ environmentVariable: "JEST_HTML_REPORTER_INCLUDE_FAILURE_MSG",
246
+ configValue: options.includeFailureMsg
247
+ },
248
+ includeConsoleLog: {
249
+ defaultValue: false,
250
+ environmentVariable: "JEST_HTML_REPORTER_INCLUDE_CONSOLE_LOG",
251
+ configValue: options.includeConsoleLog
252
+ },
253
+ outputPath: {
254
+ defaultValue: path_1.default.join(process.cwd(), "test-report.html"),
255
+ environmentVariable: "JEST_HTML_REPORTER_OUTPUT_PATH",
256
+ configValue: options.outputPath
257
+ },
258
+ pageTitle: {
259
+ defaultValue: "Test Report",
260
+ environmentVariable: "JEST_HTML_REPORTER_PAGE_TITLE",
261
+ configValue: options.pageTitle
262
+ },
263
+ theme: {
264
+ defaultValue: "defaultTheme",
265
+ environmentVariable: "JEST_HTML_REPORTER_THEME",
266
+ configValue: options.theme
267
+ },
268
+ sort: {
269
+ defaultValue: null,
270
+ environmentVariable: "JEST_HTML_REPORTER_SORT",
271
+ configValue: options.sort
272
+ },
273
+ statusIgnoreFilter: {
274
+ defaultValue: null,
275
+ environmentVariable: "JEST_HTML_REPORTER_STATUS_FILTER",
276
+ configValue: options.statusIgnoreFilter
277
+ },
278
+ styleOverridePath: {
279
+ defaultValue: null,
280
+ environmentVariable: "JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH",
281
+ configValue: options.styleOverridePath
282
+ },
283
+ useCssFile: {
284
+ defaultValue: false,
285
+ environmentVariable: "JEST_HTML_REPORTER_USE_CSS_FILE",
286
+ configValue: options.useCssFile
287
+ }
288
+ };
289
+ // Attempt to collect and assign config settings from jesthtmlreporter.config.json
290
+ try {
291
+ const jesthtmlreporterconfig = fs_1.default.readFileSync(path_1.default.join(process.cwd(), "jesthtmlreporter.config.json"), "utf8");
292
+ if (jesthtmlreporterconfig) {
293
+ const parsedConfig = JSON.parse(jesthtmlreporterconfig);
294
+ for (const key of Object.keys(parsedConfig)) {
295
+ if (this.config[key]) {
296
+ this.config[key].configValue =
297
+ parsedConfig[key];
298
+ }
299
+ }
300
+ return;
301
+ }
302
+ }
303
+ catch (e) {
304
+ /** do nothing */
305
+ }
306
+ // If above method did not work we attempt to check package.json
307
+ try {
308
+ const packageJson = fs_1.default.readFileSync(path_1.default.join(process.cwd(), "package.json"), "utf8");
309
+ if (packageJson) {
310
+ const parsedConfig = JSON.parse(packageJson)["jest-html-reporter"];
311
+ for (const key of Object.keys(parsedConfig)) {
312
+ if (this.config[key]) {
313
+ this.config[key].configValue =
314
+ parsedConfig[key];
315
+ }
316
+ }
317
+ }
318
+ }
319
+ catch (e) {
320
+ /** do nothing */
321
+ }
322
+ }
323
+ /**
324
+ * Returns the configurated value from the config in the following priority order:
325
+ * Environment Variable > JSON configured value > Default value
326
+ * @param key
327
+ */
328
+ getConfigValue(key) {
329
+ const option = this.config[key];
330
+ if (!option) {
331
+ return;
332
+ }
333
+ if (process.env[option.environmentVariable]) {
334
+ return process.env[option.environmentVariable];
335
+ }
336
+ return option.configValue || option.defaultValue;
337
+ }
338
+ async getFileContent(filePath) {
339
+ try {
340
+ fs_1.default.readFile(filePath, "utf8", (err, content) => {
341
+ if (err) {
342
+ throw Error(`Could not locate file: '${filePath}': ${err}`);
343
+ }
344
+ return content;
345
+ });
346
+ }
347
+ catch (e) {
348
+ this.logMessage("error", e);
349
+ return;
350
+ }
351
+ }
352
+ /**
353
+ * Method for logging to the terminal
354
+ * @param type
355
+ * @param message
356
+ * @param ignoreConsole
357
+ */
358
+ logMessage(type = "default", message, ignoreConsole) {
359
+ const logTypes = {
360
+ default: "\x1b[37m%s\x1b[0m",
361
+ success: "\x1b[32m%s\x1b[0m",
362
+ error: "\x1b[31m%s\x1b[0m"
363
+ };
364
+ const logColor = !logTypes[type] ? logTypes.default : logTypes[type];
365
+ const logMsg = `jest-html-reporter >> ${message}`;
366
+ if (!ignoreConsole) {
367
+ console.log(logColor, logMsg);
368
+ }
369
+ return { logColor, logMsg }; // Return for testing purposes
370
+ }
371
+ }
372
+ exports.default = HTMLReporter;
373
+ //# sourceMappingURL=htmlreporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlreporter.js","sourceRoot":"","sources":["../src/htmlreporter.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,602 @@
1
+ 'use strict';
2
+
3
+ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
4
+
5
+ var dateformat = _interopDefault(require('dateformat'));
6
+ var fs = _interopDefault(require('fs'));
7
+ var mkdirp = _interopDefault(require('mkdirp'));
8
+ var path = _interopDefault(require('path'));
9
+ var stripAnsi = _interopDefault(require('strip-ansi'));
10
+ var xmlbuilder = _interopDefault(require('xmlbuilder'));
11
+
12
+ var sorting = ((testResults, sortType) => {
13
+ const sortTypeLowercase = sortType && sortType.toLowerCase();
14
+
15
+ switch (sortTypeLowercase) {
16
+ case "status":
17
+ return sortByStatus(testResults);
18
+
19
+ case "executiondesc":
20
+ return sortByExecutionDesc(testResults);
21
+
22
+ case "executionasc":
23
+ return sortByExecutionAsc(testResults);
24
+
25
+ case "titledesc":
26
+ return sortByTitleDesc(testResults);
27
+
28
+ case "titleasc":
29
+ return sortByTitleAsc(testResults);
30
+
31
+ default:
32
+ return testResults;
33
+ }
34
+ });
35
+ /**
36
+ * Splits test suites apart based on individual test status and sorts by that status:
37
+ * 1. Pending
38
+ * 2. Failed
39
+ * 3. Passed
40
+ */
41
+
42
+ const sortByStatus = testResults => {
43
+ const pendingSuites = [];
44
+ const failingSuites = [];
45
+ const passingSuites = [];
46
+ testResults.forEach(result => {
47
+ const pending = [];
48
+ const failed = [];
49
+ const passed = [];
50
+ result.testResults.forEach(x => {
51
+ if (x.status === "pending") {
52
+ pending.push(x);
53
+ } else if (x.status === "failed") {
54
+ failed.push(x);
55
+ } else {
56
+ passed.push(x);
57
+ }
58
+ });
59
+
60
+ if (pending.length > 0) {
61
+ pendingSuites.push({ ...result,
62
+ testResults: pending
63
+ });
64
+ }
65
+
66
+ if (failed.length > 0) {
67
+ failingSuites.push({ ...result,
68
+ testResults: failed
69
+ });
70
+ }
71
+
72
+ if (passed.length > 0) {
73
+ passingSuites.push({ ...result,
74
+ testResults: passed
75
+ });
76
+ }
77
+ });
78
+ return [].concat(pendingSuites, failingSuites, passingSuites);
79
+ };
80
+ /**
81
+ * Sorts by Execution Time | Descending
82
+ */
83
+
84
+
85
+ const sortByExecutionDesc = testResults => {
86
+ if (testResults) {
87
+ testResults.sort((a, b) => b.perfStats.end - b.perfStats.start - (a.perfStats.end - a.perfStats.start));
88
+ }
89
+
90
+ return testResults;
91
+ };
92
+ /**
93
+ * Sorts by Execution Time | Ascending
94
+ */
95
+
96
+
97
+ const sortByExecutionAsc = testResults => {
98
+ if (testResults) {
99
+ testResults.sort((a, b) => a.perfStats.end - a.perfStats.start - (b.perfStats.end - b.perfStats.start));
100
+ }
101
+
102
+ return testResults;
103
+ };
104
+ /**
105
+ * Sorts by Suite filename and Test name | Descending
106
+ */
107
+
108
+
109
+ const sortByTitleDesc = testResults => {
110
+ if (testResults) {
111
+ // Sort Suites
112
+ const sorted = testResults.sort((a, b) => sortAlphabetically(a.testFilePath, b.testFilePath, true)); // Sort Suite testResults
113
+
114
+ sorted.forEach(suite => {
115
+ suite.testResults.sort((a, b) => sortAlphabetically(a.ancestorTitles.join(" "), b.ancestorTitles.join(" "), true));
116
+ });
117
+ return sorted;
118
+ }
119
+
120
+ return testResults;
121
+ };
122
+ /**
123
+ * Sorts by Suite filename and Test name | Ascending
124
+ */
125
+
126
+
127
+ const sortByTitleAsc = testResults => {
128
+ if (testResults) {
129
+ // Sort Suites
130
+ const sorted = testResults.sort((a, b) => sortAlphabetically(a.testFilePath, b.testFilePath)); // Sort Suite testResults
131
+
132
+ sorted.forEach(suite => {
133
+ suite.testResults.sort((a, b) => sortAlphabetically(a.ancestorTitles.join(" "), b.ancestorTitles.join(" ")));
134
+ });
135
+ return sorted;
136
+ }
137
+
138
+ return testResults;
139
+ };
140
+ /**
141
+ * Helper sorting method
142
+ */
143
+
144
+
145
+ const sortAlphabetically = (a, b, reversed = false) => {
146
+ if (!reversed && a < b || reversed && a > b) {
147
+ return -1;
148
+ } else if (!reversed && a > b || reversed && a < b) {
149
+ return 1;
150
+ }
151
+
152
+ return 0;
153
+ };
154
+
155
+ class HTMLReporter {
156
+ constructor(testData, options, consoleLogs) {
157
+ this.testData = testData;
158
+ this.consoleLogList = consoleLogs;
159
+ this.setupConfig(options);
160
+ }
161
+
162
+ async generate() {
163
+ try {
164
+ const report = await this.renderTestReport();
165
+ const outputPath = this.getConfigValue("outputPath");
166
+ await mkdirp(path.dirname(outputPath));
167
+
168
+ if (this.getConfigValue("append")) {
169
+ await fs.appendFileSync(outputPath, report);
170
+ } else {
171
+ await fs.writeFileSync(outputPath, report);
172
+ }
173
+
174
+ this.logMessage("success", `Report generated (${outputPath})`);
175
+ return report;
176
+ } catch (e) {
177
+ this.logMessage("error", e);
178
+ }
179
+ }
180
+
181
+ async renderTestReport() {
182
+ // Generate the content of the test report
183
+ const reportBody = await this.renderTestReportBody(); // --
184
+ // Boilerplate Option
185
+
186
+ if (!!this.getConfigValue("boilerplate")) {
187
+ const boilerplateContent = await fs.readFileSync(this.getConfigValue("boilerplate"), "utf8");
188
+ return boilerplateContent.replace("{jesthtmlreporter-content}", reportBody.toString());
189
+ } // --
190
+ // Create HTML and apply reporter content
191
+
192
+
193
+ const HTMLBase = {
194
+ html: {
195
+ head: {
196
+ meta: {
197
+ "@charset": "utf-8"
198
+ },
199
+ title: {
200
+ "#text": this.getConfigValue("pageTitle")
201
+ },
202
+ style: undefined,
203
+ link: undefined
204
+ }
205
+ }
206
+ }; // Default to the currently set theme
207
+
208
+ let stylesheetFilePath = path.join(__dirname, `../style/${this.getConfigValue("theme")}.css`); // Overriding stylesheet
209
+
210
+ if (this.getConfigValue("styleOverridePath")) {
211
+ stylesheetFilePath = this.getConfigValue("styleOverridePath");
212
+ } // Decide whether to inline the CSS or not
213
+
214
+
215
+ const inlineCSS = !this.getConfigValue("useCssFile") && !!!this.getConfigValue("styleOverridePath");
216
+
217
+ if (inlineCSS) {
218
+ const stylesheetContent = await fs.readFileSync(stylesheetFilePath, "utf8");
219
+ HTMLBase.html.head.style = {
220
+ "@type": "text/css",
221
+ "#text": stylesheetContent
222
+ };
223
+ } else {
224
+ HTMLBase.html.head.link = {
225
+ "@rel": "stylesheet",
226
+ "@type": "text/css",
227
+ "@href": stylesheetFilePath
228
+ };
229
+ }
230
+
231
+ const report = xmlbuilder.create(HTMLBase);
232
+ report.ele("body").raw(reportBody.toString());
233
+ return report;
234
+ }
235
+
236
+ async renderTestReportBody() {
237
+ try {
238
+ if (!this.testData || Object.entries(this.testData).length === 0) {
239
+ throw Error("No test data provided");
240
+ } // HTML Body
241
+
242
+
243
+ const reportBody = xmlbuilder.begin().element("div", {
244
+ id: "jesthtml-content"
245
+ });
246
+ /**
247
+ * Page Header
248
+ */
249
+
250
+ const header = reportBody.ele("header"); // Page Title
251
+
252
+ header.ele("h1", {
253
+ id: "title"
254
+ }, this.getConfigValue("pageTitle")); // Logo
255
+
256
+ const logo = this.getConfigValue("logo");
257
+
258
+ if (logo) {
259
+ header.ele("img", {
260
+ id: "logo",
261
+ src: logo
262
+ });
263
+ }
264
+ /**
265
+ * Meta-Data
266
+ */
267
+
268
+
269
+ const metaDataContainer = reportBody.ele("div", {
270
+ id: "metadata-container"
271
+ }); // Timestamp
272
+
273
+ const timestamp = new Date(this.testData.startTime);
274
+ metaDataContainer.ele("div", {
275
+ id: "timestamp"
276
+ }, `Start: ${dateformat(timestamp.toDateString(), this.getConfigValue("dateFormat"))}`); // Test Summary
277
+
278
+ metaDataContainer.ele("div", {
279
+ id: "summary"
280
+ }, `${this.testData.numTotalTests} tests -- ${this.testData.numPassedTests} passed / ${this.testData.numFailedTests} failed / ${this.testData.numPendingTests} pending`);
281
+ /**
282
+ * Apply any given sorting method to the test results
283
+ */
284
+
285
+ const sortedTestResults = sorting(this.testData.testResults, this.getConfigValue("sort"));
286
+ /**
287
+ * Setup ignored test result statuses
288
+ */
289
+
290
+ const statusIgnoreFilter = this.getConfigValue("statusIgnoreFilter");
291
+ let ignoredStatuses = [];
292
+
293
+ if (statusIgnoreFilter) {
294
+ ignoredStatuses = statusIgnoreFilter.replace(/\s/g, "").toLowerCase().split(",");
295
+ }
296
+ /**
297
+ * Test Suites
298
+ */
299
+
300
+
301
+ sortedTestResults.map(suite => {
302
+ // Ignore this suite if there are no results
303
+ if (!suite.testResults || suite.testResults.length <= 0) {
304
+ return;
305
+ } // Suite Information
306
+
307
+
308
+ const suiteInfo = reportBody.ele("div", {
309
+ class: "suite-info"
310
+ }); // Suite Path
311
+
312
+ suiteInfo.ele("div", {
313
+ class: "suite-path"
314
+ }, suite.testFilePath); // Suite execution time
315
+
316
+ const executionTime = (suite.perfStats.end - suite.perfStats.start) / 1000;
317
+ suiteInfo.ele("div", {
318
+ class: `suite-time${executionTime > 5 ? " warn" : ""}`
319
+ }, `${executionTime}s`); // Suite Test Table
320
+
321
+ const suiteTable = reportBody.ele("table", {
322
+ class: "suite-table",
323
+ cellspacing: "0",
324
+ cellpadding: "0"
325
+ }); // Test Results
326
+
327
+ suite.testResults // Filter out the test results with statuses that equals the statusIgnoreFilter
328
+ .filter(s => !ignoredStatuses.includes(s.status)).forEach(test => {
329
+ const testTr = suiteTable.ele("tr", {
330
+ class: test.status
331
+ }); // Suite Name(s)
332
+
333
+ testTr.ele("td", {
334
+ class: "suite"
335
+ }, test.ancestorTitles.join(" > ")); // Test name
336
+
337
+ const testTitleTd = testTr.ele("td", {
338
+ class: "test"
339
+ }, test.title); // Test Failure Messages
340
+
341
+ if (test.failureMessages && this.getConfigValue("includeFailureMsg")) {
342
+ const failureMsgDiv = testTitleTd.ele("div", {
343
+ class: "failureMessages"
344
+ });
345
+ test.failureMessages.forEach(failureMsg => {
346
+ failureMsgDiv.ele("pre", {
347
+ class: "failureMsg"
348
+ }, stripAnsi(failureMsg));
349
+ });
350
+ } // Append data to <tr>
351
+
352
+
353
+ testTr.ele("td", {
354
+ class: "result"
355
+ }, test.status === "passed" ? `${test.status} in ${test.duration / 1000}s` : test.status);
356
+ }); // All console.logs caught during the test run
357
+
358
+ if (this.consoleLogList && this.consoleLogList.length > 0 && this.getConfigValue("includeConsoleLog")) {
359
+ // Filter out the logs for this test file path
360
+ const filteredConsoleLogs = this.consoleLogList.find(logs => logs.filePath === suite.testFilePath);
361
+
362
+ if (filteredConsoleLogs && filteredConsoleLogs.logs.length > 0) {
363
+ // Console Log Container
364
+ const consoleLogContainer = reportBody.ele("div", {
365
+ class: "suite-consolelog"
366
+ }); // Console Log Header
367
+
368
+ consoleLogContainer.ele("div", {
369
+ class: "suite-consolelog-header"
370
+ }, "Console Log"); // Apply the logs to the body
371
+
372
+ filteredConsoleLogs.logs.forEach(log => {
373
+ const logElement = consoleLogContainer.ele("div", {
374
+ class: "suite-consolelog-item"
375
+ });
376
+ logElement.ele("pre", {
377
+ class: "suite-consolelog-item-origin"
378
+ }, stripAnsi(log.origin));
379
+ logElement.ele("pre", {
380
+ class: "suite-consolelog-item-message"
381
+ }, stripAnsi(log.message));
382
+ });
383
+ }
384
+ }
385
+ });
386
+ return reportBody;
387
+ } catch (e) {
388
+ this.logMessage("error", e);
389
+ }
390
+ }
391
+ /**
392
+ * Fetch and setup configuration
393
+ */
394
+
395
+
396
+ setupConfig(options) {
397
+ this.config = {
398
+ append: {
399
+ defaultValue: false,
400
+ environmentVariable: "JEST_HTML_REPORTER_APPEND",
401
+ configValue: options.append
402
+ },
403
+ boilerplate: {
404
+ defaultValue: null,
405
+ environmentVariable: "JEST_HTML_REPORTER_BOILERPLATE",
406
+ configValue: options.boilerplate
407
+ },
408
+ customScriptPath: {
409
+ defaultValue: null,
410
+ environmentVariable: "JEST_HTML_REPORTER_CUSTOM_SCRIPT_PATH",
411
+ configValue: options.customScriptPath
412
+ },
413
+ dateFormat: {
414
+ defaultValue: "yyyy-mm-dd HH:MM:ss",
415
+ environmentVariable: "JEST_HTML_REPORTER_DATE_FORMAT",
416
+ configValue: options.dateFormat
417
+ },
418
+ executionTimeWarningThreshold: {
419
+ defaultValue: 5,
420
+ environmentVariable: "JEST_HTML_REPORTER_EXECUTION_TIME_WARNING_THRESHOLD",
421
+ configValue: options.executionTimeWarningThreshold
422
+ },
423
+ logo: {
424
+ defaultValue: null,
425
+ environmentVariable: "JEST_HTML_REPORTER_LOGO",
426
+ configValue: options.logo
427
+ },
428
+ includeFailureMsg: {
429
+ defaultValue: false,
430
+ environmentVariable: "JEST_HTML_REPORTER_INCLUDE_FAILURE_MSG",
431
+ configValue: options.includeFailureMsg
432
+ },
433
+ includeConsoleLog: {
434
+ defaultValue: false,
435
+ environmentVariable: "JEST_HTML_REPORTER_INCLUDE_CONSOLE_LOG",
436
+ configValue: options.includeConsoleLog
437
+ },
438
+ outputPath: {
439
+ defaultValue: path.join(process.cwd(), "test-report.html"),
440
+ environmentVariable: "JEST_HTML_REPORTER_OUTPUT_PATH",
441
+ configValue: options.outputPath
442
+ },
443
+ pageTitle: {
444
+ defaultValue: "Test Report",
445
+ environmentVariable: "JEST_HTML_REPORTER_PAGE_TITLE",
446
+ configValue: options.pageTitle
447
+ },
448
+ theme: {
449
+ defaultValue: "defaultTheme",
450
+ environmentVariable: "JEST_HTML_REPORTER_THEME",
451
+ configValue: options.theme
452
+ },
453
+ sort: {
454
+ defaultValue: null,
455
+ environmentVariable: "JEST_HTML_REPORTER_SORT",
456
+ configValue: options.sort
457
+ },
458
+ statusIgnoreFilter: {
459
+ defaultValue: null,
460
+ environmentVariable: "JEST_HTML_REPORTER_STATUS_FILTER",
461
+ configValue: options.statusIgnoreFilter
462
+ },
463
+ styleOverridePath: {
464
+ defaultValue: null,
465
+ environmentVariable: "JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH",
466
+ configValue: options.styleOverridePath
467
+ },
468
+ useCssFile: {
469
+ defaultValue: false,
470
+ environmentVariable: "JEST_HTML_REPORTER_USE_CSS_FILE",
471
+ configValue: options.useCssFile
472
+ }
473
+ }; // Attempt to collect and assign config settings from jesthtmlreporter.config.json
474
+
475
+ try {
476
+ const jesthtmlreporterconfig = fs.readFileSync(path.join(process.cwd(), "jesthtmlreporter.config.json"), "utf8");
477
+
478
+ if (jesthtmlreporterconfig) {
479
+ const parsedConfig = JSON.parse(jesthtmlreporterconfig);
480
+
481
+ for (const key of Object.keys(parsedConfig)) {
482
+ if (this.config[key]) {
483
+ this.config[key].configValue = parsedConfig[key];
484
+ }
485
+ }
486
+
487
+ return;
488
+ }
489
+ } catch (e) {}
490
+ /** do nothing */
491
+ // If above method did not work we attempt to check package.json
492
+
493
+
494
+ try {
495
+ const packageJson = fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8");
496
+
497
+ if (packageJson) {
498
+ const parsedConfig = JSON.parse(packageJson)["jest-html-reporter"];
499
+
500
+ for (const key of Object.keys(parsedConfig)) {
501
+ if (this.config[key]) {
502
+ this.config[key].configValue = parsedConfig[key];
503
+ }
504
+ }
505
+ }
506
+ } catch (e) {
507
+ /** do nothing */
508
+ }
509
+ }
510
+ /**
511
+ * Returns the configurated value from the config in the following priority order:
512
+ * Environment Variable > JSON configured value > Default value
513
+ * @param key
514
+ */
515
+
516
+
517
+ getConfigValue(key) {
518
+ const option = this.config[key];
519
+
520
+ if (!option) {
521
+ return;
522
+ }
523
+
524
+ if (process.env[option.environmentVariable]) {
525
+ return process.env[option.environmentVariable];
526
+ }
527
+
528
+ return option.configValue || option.defaultValue;
529
+ }
530
+ /**
531
+ * Method for logging to the terminal
532
+ * @param type
533
+ * @param message
534
+ * @param ignoreConsole
535
+ */
536
+
537
+
538
+ logMessage(type = "default", message, ignoreConsole) {
539
+ const logTypes = {
540
+ default: "\x1b[37m%s\x1b[0m",
541
+ success: "\x1b[32m%s\x1b[0m",
542
+ error: "\x1b[31m%s\x1b[0m"
543
+ };
544
+ const logColor = !logTypes[type] ? logTypes.default : logTypes[type];
545
+ const logMsg = `jest-html-reporter >> ${message}`;
546
+
547
+ if (!ignoreConsole) {
548
+ console.log(logColor, logMsg);
549
+ }
550
+
551
+ return {
552
+ logColor,
553
+ logMsg
554
+ }; // Return for testing purposes
555
+ }
556
+
557
+ }
558
+
559
+ function JestHtmlReporter(globalConfig, options) {
560
+ const consoleLogs = [];
561
+ /**
562
+ * If the first parameter has a property named 'testResults',
563
+ * the script is being run as a 'testResultsProcessor'.
564
+ * We then need to return the test results as they were received from Jest
565
+ * https://facebook.github.io/jest/docs/en/configuration.html#testresultsprocessor-string
566
+ */
567
+
568
+ if (Object.prototype.hasOwnProperty.call(globalConfig, "testResults")) {
569
+ // @ts-ignore
570
+ setupAndRun(globalConfig.testResults, options); // Return the results as required by Jest
571
+
572
+ return globalConfig;
573
+ }
574
+ /**
575
+ * The default behaviour - run as Custom Reporter, in parallel with Jest.
576
+ * https://facebook.github.io/jest/docs/en/configuration.html#reporters-array-modulename-modulename-options
577
+ */
578
+
579
+
580
+ this.onTestResult = (data, result) => {
581
+ // Catch console logs per test
582
+ if (result.console) {
583
+ consoleLogs.push({
584
+ filePath: result.testFilePath,
585
+ logs: result.console
586
+ });
587
+ }
588
+ };
589
+
590
+ this.onRunComplete = (contexts, testResult) => setupAndRun(testResult, options, consoleLogs);
591
+ }
592
+ /**
593
+ * Setup Jest HTML Reporter and generate a report with the given data
594
+ */
595
+
596
+
597
+ const setupAndRun = (testResults, options, logs) => {
598
+ const reporter = new HTMLReporter(testResults, options, logs);
599
+ return reporter.generate();
600
+ };
601
+
602
+ module.exports = JestHtmlReporter;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAGA,kEAA0C;AAG1C,SAAS,gBAAgB,CACvB,YAAiC,EACjC,OAA8B;IAE9B,oDAAoD;IACpD,MAAM,WAAW,GAA+B,EAAE,CAAC;IAEnD;;;;;OAKG;IACH,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE;QACrE,kBAAkB;QAClB,MAAM,QAAQ,GAAG,IAAI,sBAAY;QAC/B,aAAa;QACb,YAAY,CAAC,WAAW,EACxB,OAAmC,CACpC,CAAC;QACF,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACpB,yCAAyC;QACzC,OAAO,YAAY,CAAC;KACrB;IAED;;;;OAIG;IACH,IAAI,CAAC,UAAU,GAAG,YAAY,CAAC;IAC/B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;IAE3B,IAAI,CAAC,YAAY,GAAG,CAAC,IAAS,EAAE,MAAkB,EAAE,EAAE;QACpD,8BAA8B;QAC9B,IAAI,MAAM,CAAC,OAAO,EAAE;YAClB,WAAW,CAAC,IAAI,CAAC;gBACf,QAAQ,EAAE,MAAM,CAAC,YAAY;gBAC7B,IAAI,EAAE,MAAM,CAAC,OAAO;aACrB,CAAC,CAAC;SACJ;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,aAAa,GAAG,CAAC,QAAa,EAAE,UAA4B,EAAE,EAAE;QACnE,MAAM,QAAQ,GAAG,IAAI,sBAAY,CAC/B,UAAU,EACV,OAAmC,CACpC,CAAC;QACF,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACpB,2CAA2C;IAC7C,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe,gBAAgB,CAAC"}
package/dist/main.js CHANGED
@@ -531,18 +531,8 @@ class ReportGenerator {
531
531
  const consoleLogContainer = reportBody.ele('div', { class: 'suite-consolelog' });
532
532
  // Console Log Header
533
533
  consoleLogContainer.ele('div', { class: 'suite-consolelog-header' }, 'Console Log');
534
- // Sort the order by the path
535
- const sortedConsoleLogs = filteredConsoleLogs.logs.sort((a, b) => {
536
- if (a.origin < b.origin) {
537
- return -1;
538
- }
539
- if (a.origin > b.origin) {
540
- return 1;
541
- }
542
- return 0;
543
- });
544
534
  // Apply the logs to the body
545
- sortedConsoleLogs.forEach((log) => {
535
+ filteredConsoleLogs.logs.forEach((log) => {
546
536
  const logElement = consoleLogContainer.ele('div', { class: 'suite-consolelog-item' });
547
537
  logElement.ele('pre', { class: 'suite-consolelog-item-origin' }, stripAnsi(log.origin));
548
538
  logElement.ele('pre', { class: 'suite-consolelog-item-message' }, stripAnsi(log.message));
package/dist/main.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var fs=_interopDefault(require("fs")),path=_interopDefault(require("path")),mkdirp=_interopDefault(require("mkdirp")),xmlbuilder=_interopDefault(require("xmlbuilder")),dateformat=_interopDefault(require("dateformat")),stripAnsi=_interopDefault(require("strip-ansi"));function createCommonjsModule(e,t){return e(t={exports:{}},t.exports),t.exports}var utils=createCommonjsModule(function(e){e.exports={logMessage:({type:e,msg:t,ignoreConsole:s})=>{const o={default:"%s",success:"%s",error:"%s"},n=o[e]?o[e]:o.default,i=`jest-html-reporter >> ${t}`;return s||console.log(n,i),{logColor:n,logMsg:i}},writeFile:({filePath:e,content:t})=>new Promise((s,o)=>{mkdirp(path.dirname(e),n=>n?o(new Error(`Something went wrong when creating the folder: ${n}`)):fs.writeFile(e,t,t=>t?o(new Error(`Something went wrong when creating the file: ${t}`)):s(e)))}),appendFile:({filePath:e,content:t})=>new Promise((s,o)=>{mkdirp(path.dirname(e),n=>n?o(new Error(`Something went wrong when creating the folder: ${n}`)):fs.readFile(e,"utf8",(n,i)=>{let r=t;if(!n){const n=/<body>(.*?)<\/body>/gm.exec(t);if(n){const[e]=n;r=e}if(i){let t=i;const n=/<\/body>/gm.exec(i),l=n?n.index:0;return t=[i.slice(0,l),r,i.slice(l)].join(""),fs.writeFile(e,t,t=>t?o(new Error(`Something went wrong when creating the file: ${t}`)):s(e))}}return fs.appendFile(e,r,t=>t?o(new Error(`Something went wrong when appending the file: ${t}`)):s(e))}))}),getFileContent:({filePath:e})=>new Promise((t,s)=>{fs.readFile(e,"utf8",(o,n)=>o?s(new Error(`Could not locate file: '${e}': ${o}`)):t(n))}),createHtmlBase:({pageTitle:e,stylesheet:t,stylesheetPath:s})=>{const o={html:{head:{meta:{"@charset":"utf-8"},title:{"#text":e}}}};return s?o.html.head.link={"@rel":"stylesheet","@type":"text/css","@href":s}:o.html.head.style={"@type":"text/css","#text":t},xmlbuilder.create(o)},sortAlphabetically:({a:e,b:t,reversed:s})=>!s&&e<t||s&&e>t?-1:!s&&e>t||s&&e<t?1:0}}),utils_1=utils.logMessage,utils_2=utils.writeFile,utils_3=utils.appendFile,utils_4=utils.getFileContent,utils_5=utils.createHtmlBase,utils_6=utils.sortAlphabetically,sorting=createCommonjsModule(function(e){e.exports={sortSuiteResults:({testData:e,sortMethod:t})=>{if(t)switch(t.toLowerCase()){case"status":return(e=>{const t=[],s=[],o=[];return e.forEach(e=>{const n=[],i=[],r=[];e.testResults.forEach(e=>{"pending"===e.status?n.push(e):"failed"===e.status?i.push(e):r.push(e)}),n.length&&t.push(Object.assign({},e,{testResults:n})),i.length&&s.push(Object.assign({},e,{testResults:i})),r.length&&o.push(Object.assign({},e,{testResults:r}))}),[].concat(t,s,o)})(e);case"executiondesc":return(e=>(e&&e.sort((e,t)=>t.perfStats.end-t.perfStats.start-(e.perfStats.end-e.perfStats.start)),e))(e);case"executionasc":return(e=>(e&&e.sort((e,t)=>e.perfStats.end-e.perfStats.start-(t.perfStats.end-t.perfStats.start)),e))(e);case"titledesc":return(e=>{if(e){const t=e.sort((e,t)=>utils.sortAlphabetically({a:e.testFilePath,b:t.testFilePath,reversed:!0}));return t.forEach(e=>{e.testResults.sort((e,t)=>utils.sortAlphabetically({a:e.ancestorTitles.join(" "),b:t.ancestorTitles.join(" "),reversed:!0}))}),t}return e})(e);case"titleasc":return(e=>{if(e){const t=e.sort((e,t)=>utils.sortAlphabetically({a:e.testFilePath,b:t.testFilePath}));return t.forEach(e=>{e.testResults.sort((e,t)=>utils.sortAlphabetically({a:e.ancestorTitles.join(" "),b:t.ancestorTitles.join(" ")}))}),t}return e})(e);default:return e}return e}}}),sorting_1=sorting.sortSuiteResults;class ReportGenerator{constructor(e){this.config=e,this.consoleLogs=null}generate({data:e,ignoreConsole:t}){const s=this.config.getOutputFilepath(),o=this.config.shouldUseCssFile(),n=this.config.shouldGetStylesheetContent(),i=this.config.getAppend();let r=null,l=null;return o&&(r=this.config.getStylesheetFilepath()),(l=n?()=>this.getStylesheetContent():()=>Promise.resolve())().then(t=>this.renderHtmlReport({data:e,stylesheet:t,stylesheetPath:r})).then(e=>i?utils.appendFile({filePath:s,content:e}):utils.writeFile({filePath:s,content:e})).then(()=>utils.logMessage({type:"success",msg:`Report generated (${s})`,ignoreConsole:t})).catch(e=>utils.logMessage({type:"error",msg:e,ignoreConsole:t}))}integrateContentIntoBoilerplate({content:e}){const t=this.config.getBoilerplatePath();return new Promise((s,o)=>utils.getFileContent({filePath:t}).then(t=>s(t.replace("{jesthtmlreporter-content}",e))).catch(e=>o(e)))}getStylesheetContent(){const e=this.config.getStylesheetFilepath();return utils.getFileContent({filePath:e})}renderHtmlReport({data:e,stylesheet:t,stylesheetPath:s}){return new Promise((o,n)=>{if(!e)return n(new Error("Test data missing or malformed"));const i=this.config.getPageTitle(),r=this.getReportBody({data:e,pageTitle:i});if(this.config.getBoilerplatePath())return this.integrateContentIntoBoilerplate({content:r}).then(e=>o(e));const l=utils.createHtmlBase({pageTitle:i,stylesheet:t,stylesheetPath:s}),a=l.ele("body");a.raw(r);const c=this.config.getCustomScriptFilepath();return c&&a.raw(`<script src="${c}"><\/script>`),o(l)})}getReportBody({data:e,pageTitle:t}){const s=xmlbuilder.begin().element("div",{id:"jesthtml-content"}),o=s.ele("header");o.ele("h1",{id:"title"},t);const n=this.config.getLogo();n&&o.ele("img",{id:"logo",src:n});const i=s.ele("div",{id:"metadata-container"}),r=new Date(e.startTime);i.ele("div",{id:"timestamp"},`Start: ${dateformat(r,this.config.getDateFormat())}`),i.ele("div",{id:"summary"},`${e.numTotalTests} tests -- ${e.numPassedTests} passed / ${e.numFailedTests} failed / ${e.numPendingTests} pending`);const l=sorting.sortSuiteResults({testData:e.testResults,sortMethod:this.config.getSort()}),a=this.config.getStatusIgnoreFilter();let c=[];return a&&(c=a.replace(/\s/g,"").toLowerCase().split(",")),l.forEach(e=>{for(let t=e.testResults.length-1;t>=0;t-=1)c.includes(e.testResults[t].status)&&e.testResults.splice(t,1);if(!e.testResults||e.testResults.length<=0)return;const t=s.ele("div",{class:"suite-info"});t.ele("div",{class:"suite-path"},e.testFilePath);const o=(e.perfStats.end-e.perfStats.start)/1e3;t.ele("div",{class:`suite-time${o>5?" warn":""}`},`${o}s`);const n=s.ele("table",{class:"suite-table",cellspacing:"0",cellpadding:"0"});if(e.testResults.forEach(e=>{const t=n.ele("tr",{class:e.status});t.ele("td",{class:"suite"},e.ancestorTitles.join(" > "));const s=t.ele("td",{class:"test"},e.title);if(e.failureMessages&&this.config.shouldIncludeFailureMessages()){const t=s.ele("div",{class:"failureMessages"});e.failureMessages.forEach(e=>{t.ele("pre",{class:"failureMsg"},stripAnsi(e))})}t.ele("td",{class:"result"},"passed"===e.status?`${e.status} in ${e.duration/1e3}s`:e.status)}),this.consoleLogs&&this.consoleLogs.length>0&&this.config.shouldIncludeConsoleLog()){const t=this.consoleLogs.find(t=>t.testFilePath===e.testFilePath);if(t&&t.logs.length>0){const e=s.ele("div",{class:"suite-consolelog"});e.ele("div",{class:"suite-consolelog-header"},"Console Log"),t.logs.sort((e,t)=>e.origin<t.origin?-1:e.origin>t.origin?1:0).forEach(t=>{const s=e.ele("div",{class:"suite-consolelog-item"});s.ele("pre",{class:"suite-consolelog-item-origin"},stripAnsi(t.origin)),s.ele("pre",{class:"suite-consolelog-item-message"},stripAnsi(t.message))})}}}),s}}var reportGenerator=ReportGenerator,config_1=createCommonjsModule(function(e){const t={},s=e=>Object.assign(t,e),o=()=>process.env.JEST_HTML_REPORTER_THEME||t.theme||"defaultTheme",n=()=>Boolean(process.env.JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH)||Boolean(t.styleOverridePath),i=()=>process.env.JEST_HTML_REPORTER_USE_CSS_FILE||t.useCssFile||!1;e.exports={config:t,setup:()=>{try{const e=fs.readFileSync(path.join(process.cwd(),"jesthtmlreporter.config.json"),"utf8");if(e)return s(JSON.parse(e))}catch(e){}try{const e=fs.readFileSync(path.join(process.cwd(),"package.json"),"utf8");if(e)return s(JSON.parse(e)["jest-html-reporter"])}catch(e){}return t},setConfigData:s,getOutputFilepath:()=>{return(process.env.JEST_HTML_REPORTER_OUTPUT_PATH||t.outputPath||path.join(process.cwd(),"test-report.html")).replace(/<rootdir>/gi,".")},getStylesheetFilepath:()=>process.env.JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH||t.styleOverridePath||path.join(__dirname,`../style/${o()}.css`),getHasStyleOverridePath:n,getCustomScriptFilepath:()=>process.env.JEST_HTML_REPORTER_CUSTOM_SCRIPT_PATH||t.customScriptPath||null,getPageTitle:()=>process.env.JEST_HTML_REPORTER_PAGE_TITLE||t.pageTitle||"Test report",getLogo:()=>process.env.JEST_HTML_REPORTER_LOGO||t.logo||null,shouldIncludeFailureMessages:()=>process.env.JEST_HTML_REPORTER_INCLUDE_FAILURE_MSG||t.includeFailureMsg||!1,shouldIncludeConsoleLog:()=>process.env.JEST_HTML_REPORTER_INCLUDE_CONSOLE_LOG||t.includeConsoleLog||!1,shouldUseCssFile:i,shouldGetStylesheetContent:()=>!(n()&&i()),getExecutionTimeWarningThreshold:()=>process.env.JEST_HTML_REPORTER_EXECUTION_TIME_WARNING_THRESHOLD||t.executionTimeWarningThreshold||5,getBoilerplatePath:()=>process.env.JEST_HTML_REPORTER_BOILERPLATE||t.boilerplate||null,getTheme:o,getDateFormat:()=>process.env.JEST_HTML_REPORTER_DATE_FORMAT||t.dateFormat||"yyyy-mm-dd HH:MM:ss",getSort:()=>process.env.JEST_HTML_REPORTER_SORT||t.sort||"default",getStatusIgnoreFilter:()=>process.env.JEST_HTML_REPORTER_STATUS_FILTER||t.statusIgnoreFilter||null,getAppend:()=>process.env.JEST_HTML_REPORTER_APPEND||t.append||!1}}),config_2=config_1.config,config_3=config_1.setup,config_4=config_1.setConfigData,config_5=config_1.getOutputFilepath,config_6=config_1.getStylesheetFilepath,config_7=config_1.getHasStyleOverridePath,config_8=config_1.getCustomScriptFilepath,config_9=config_1.getPageTitle,config_10=config_1.getLogo,config_11=config_1.shouldIncludeFailureMessages,config_12=config_1.shouldIncludeConsoleLog,config_13=config_1.shouldUseCssFile,config_14=config_1.shouldGetStylesheetContent,config_15=config_1.getExecutionTimeWarningThreshold,config_16=config_1.getBoilerplatePath,config_17=config_1.getTheme,config_18=config_1.getDateFormat,config_19=config_1.getSort,config_20=config_1.getStatusIgnoreFilter,config_21=config_1.getAppend;function JestHtmlReporter(e,t){config_1.setup();const s=new reportGenerator(config_1);if(Object.prototype.hasOwnProperty.call(e,"testResults"))return s.generate({data:e}),e;this.jestConfig=e,this.jestOptions=t,this.consoleLogs=[],this.onTestResult=((e,t)=>{t.console&&this.consoleLogs.push({testFilePath:t.testFilePath,logs:t.console})}),this.onRunComplete=((e,t)=>(config_1.setConfigData(this.jestOptions),s.config=config_1,s.consoleLogs=this.consoleLogs,s.generate({data:t})))}var src=JestHtmlReporter;module.exports=src;
1
+ "use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var fs=_interopDefault(require("fs")),path=_interopDefault(require("path")),mkdirp=_interopDefault(require("mkdirp")),xmlbuilder=_interopDefault(require("xmlbuilder")),dateformat=_interopDefault(require("dateformat")),stripAnsi=_interopDefault(require("strip-ansi"));function createCommonjsModule(e,t){return e(t={exports:{}},t.exports),t.exports}var utils=createCommonjsModule(function(e){e.exports={logMessage:({type:e,msg:t,ignoreConsole:s})=>{const o={default:"%s",success:"%s",error:"%s"},n=o[e]?o[e]:o.default,i=`jest-html-reporter >> ${t}`;return s||console.log(n,i),{logColor:n,logMsg:i}},writeFile:({filePath:e,content:t})=>new Promise((s,o)=>{mkdirp(path.dirname(e),n=>n?o(new Error(`Something went wrong when creating the folder: ${n}`)):fs.writeFile(e,t,t=>t?o(new Error(`Something went wrong when creating the file: ${t}`)):s(e)))}),appendFile:({filePath:e,content:t})=>new Promise((s,o)=>{mkdirp(path.dirname(e),n=>n?o(new Error(`Something went wrong when creating the folder: ${n}`)):fs.readFile(e,"utf8",(n,i)=>{let r=t;if(!n){const n=/<body>(.*?)<\/body>/gm.exec(t);if(n){const[e]=n;r=e}if(i){let t=i;const n=/<\/body>/gm.exec(i),l=n?n.index:0;return t=[i.slice(0,l),r,i.slice(l)].join(""),fs.writeFile(e,t,t=>t?o(new Error(`Something went wrong when creating the file: ${t}`)):s(e))}}return fs.appendFile(e,r,t=>t?o(new Error(`Something went wrong when appending the file: ${t}`)):s(e))}))}),getFileContent:({filePath:e})=>new Promise((t,s)=>{fs.readFile(e,"utf8",(o,n)=>o?s(new Error(`Could not locate file: '${e}': ${o}`)):t(n))}),createHtmlBase:({pageTitle:e,stylesheet:t,stylesheetPath:s})=>{const o={html:{head:{meta:{"@charset":"utf-8"},title:{"#text":e}}}};return s?o.html.head.link={"@rel":"stylesheet","@type":"text/css","@href":s}:o.html.head.style={"@type":"text/css","#text":t},xmlbuilder.create(o)},sortAlphabetically:({a:e,b:t,reversed:s})=>!s&&e<t||s&&e>t?-1:!s&&e>t||s&&e<t?1:0}}),utils_1=utils.logMessage,utils_2=utils.writeFile,utils_3=utils.appendFile,utils_4=utils.getFileContent,utils_5=utils.createHtmlBase,utils_6=utils.sortAlphabetically,sorting=createCommonjsModule(function(e){e.exports={sortSuiteResults:({testData:e,sortMethod:t})=>{if(t)switch(t.toLowerCase()){case"status":return(e=>{const t=[],s=[],o=[];return e.forEach(e=>{const n=[],i=[],r=[];e.testResults.forEach(e=>{"pending"===e.status?n.push(e):"failed"===e.status?i.push(e):r.push(e)}),n.length&&t.push(Object.assign({},e,{testResults:n})),i.length&&s.push(Object.assign({},e,{testResults:i})),r.length&&o.push(Object.assign({},e,{testResults:r}))}),[].concat(t,s,o)})(e);case"executiondesc":return(e=>(e&&e.sort((e,t)=>t.perfStats.end-t.perfStats.start-(e.perfStats.end-e.perfStats.start)),e))(e);case"executionasc":return(e=>(e&&e.sort((e,t)=>e.perfStats.end-e.perfStats.start-(t.perfStats.end-t.perfStats.start)),e))(e);case"titledesc":return(e=>{if(e){const t=e.sort((e,t)=>utils.sortAlphabetically({a:e.testFilePath,b:t.testFilePath,reversed:!0}));return t.forEach(e=>{e.testResults.sort((e,t)=>utils.sortAlphabetically({a:e.ancestorTitles.join(" "),b:t.ancestorTitles.join(" "),reversed:!0}))}),t}return e})(e);case"titleasc":return(e=>{if(e){const t=e.sort((e,t)=>utils.sortAlphabetically({a:e.testFilePath,b:t.testFilePath}));return t.forEach(e=>{e.testResults.sort((e,t)=>utils.sortAlphabetically({a:e.ancestorTitles.join(" "),b:t.ancestorTitles.join(" ")}))}),t}return e})(e);default:return e}return e}}}),sorting_1=sorting.sortSuiteResults;class ReportGenerator{constructor(e){this.config=e,this.consoleLogs=null}generate({data:e,ignoreConsole:t}){const s=this.config.getOutputFilepath(),o=this.config.shouldUseCssFile(),n=this.config.shouldGetStylesheetContent(),i=this.config.getAppend();let r=null,l=null;return o&&(r=this.config.getStylesheetFilepath()),(l=n?()=>this.getStylesheetContent():()=>Promise.resolve())().then(t=>this.renderHtmlReport({data:e,stylesheet:t,stylesheetPath:r})).then(e=>i?utils.appendFile({filePath:s,content:e}):utils.writeFile({filePath:s,content:e})).then(()=>utils.logMessage({type:"success",msg:`Report generated (${s})`,ignoreConsole:t})).catch(e=>utils.logMessage({type:"error",msg:e,ignoreConsole:t}))}integrateContentIntoBoilerplate({content:e}){const t=this.config.getBoilerplatePath();return new Promise((s,o)=>utils.getFileContent({filePath:t}).then(t=>s(t.replace("{jesthtmlreporter-content}",e))).catch(e=>o(e)))}getStylesheetContent(){const e=this.config.getStylesheetFilepath();return utils.getFileContent({filePath:e})}renderHtmlReport({data:e,stylesheet:t,stylesheetPath:s}){return new Promise((o,n)=>{if(!e)return n(new Error("Test data missing or malformed"));const i=this.config.getPageTitle(),r=this.getReportBody({data:e,pageTitle:i});if(this.config.getBoilerplatePath())return this.integrateContentIntoBoilerplate({content:r}).then(e=>o(e));const l=utils.createHtmlBase({pageTitle:i,stylesheet:t,stylesheetPath:s}),a=l.ele("body");a.raw(r);const c=this.config.getCustomScriptFilepath();return c&&a.raw(`<script src="${c}"><\/script>`),o(l)})}getReportBody({data:e,pageTitle:t}){const s=xmlbuilder.begin().element("div",{id:"jesthtml-content"}),o=s.ele("header");o.ele("h1",{id:"title"},t);const n=this.config.getLogo();n&&o.ele("img",{id:"logo",src:n});const i=s.ele("div",{id:"metadata-container"}),r=new Date(e.startTime);i.ele("div",{id:"timestamp"},`Start: ${dateformat(r,this.config.getDateFormat())}`),i.ele("div",{id:"summary"},`${e.numTotalTests} tests -- ${e.numPassedTests} passed / ${e.numFailedTests} failed / ${e.numPendingTests} pending`);const l=sorting.sortSuiteResults({testData:e.testResults,sortMethod:this.config.getSort()}),a=this.config.getStatusIgnoreFilter();let c=[];return a&&(c=a.replace(/\s/g,"").toLowerCase().split(",")),l.forEach(e=>{for(let t=e.testResults.length-1;t>=0;t-=1)c.includes(e.testResults[t].status)&&e.testResults.splice(t,1);if(!e.testResults||e.testResults.length<=0)return;const t=s.ele("div",{class:"suite-info"});t.ele("div",{class:"suite-path"},e.testFilePath);const o=(e.perfStats.end-e.perfStats.start)/1e3;t.ele("div",{class:`suite-time${o>5?" warn":""}`},`${o}s`);const n=s.ele("table",{class:"suite-table",cellspacing:"0",cellpadding:"0"});if(e.testResults.forEach(e=>{const t=n.ele("tr",{class:e.status});t.ele("td",{class:"suite"},e.ancestorTitles.join(" > "));const s=t.ele("td",{class:"test"},e.title);if(e.failureMessages&&this.config.shouldIncludeFailureMessages()){const t=s.ele("div",{class:"failureMessages"});e.failureMessages.forEach(e=>{t.ele("pre",{class:"failureMsg"},stripAnsi(e))})}t.ele("td",{class:"result"},"passed"===e.status?`${e.status} in ${e.duration/1e3}s`:e.status)}),this.consoleLogs&&this.consoleLogs.length>0&&this.config.shouldIncludeConsoleLog()){const t=this.consoleLogs.find(t=>t.testFilePath===e.testFilePath);if(t&&t.logs.length>0){const e=s.ele("div",{class:"suite-consolelog"});e.ele("div",{class:"suite-consolelog-header"},"Console Log"),t.logs.forEach(t=>{const s=e.ele("div",{class:"suite-consolelog-item"});s.ele("pre",{class:"suite-consolelog-item-origin"},stripAnsi(t.origin)),s.ele("pre",{class:"suite-consolelog-item-message"},stripAnsi(t.message))})}}}),s}}var reportGenerator=ReportGenerator,config_1=createCommonjsModule(function(e){const t={},s=e=>Object.assign(t,e),o=()=>process.env.JEST_HTML_REPORTER_THEME||t.theme||"defaultTheme",n=()=>Boolean(process.env.JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH)||Boolean(t.styleOverridePath),i=()=>process.env.JEST_HTML_REPORTER_USE_CSS_FILE||t.useCssFile||!1;e.exports={config:t,setup:()=>{try{const e=fs.readFileSync(path.join(process.cwd(),"jesthtmlreporter.config.json"),"utf8");if(e)return s(JSON.parse(e))}catch(e){}try{const e=fs.readFileSync(path.join(process.cwd(),"package.json"),"utf8");if(e)return s(JSON.parse(e)["jest-html-reporter"])}catch(e){}return t},setConfigData:s,getOutputFilepath:()=>{return(process.env.JEST_HTML_REPORTER_OUTPUT_PATH||t.outputPath||path.join(process.cwd(),"test-report.html")).replace(/<rootdir>/gi,".")},getStylesheetFilepath:()=>process.env.JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH||t.styleOverridePath||path.join(__dirname,`../style/${o()}.css`),getHasStyleOverridePath:n,getCustomScriptFilepath:()=>process.env.JEST_HTML_REPORTER_CUSTOM_SCRIPT_PATH||t.customScriptPath||null,getPageTitle:()=>process.env.JEST_HTML_REPORTER_PAGE_TITLE||t.pageTitle||"Test report",getLogo:()=>process.env.JEST_HTML_REPORTER_LOGO||t.logo||null,shouldIncludeFailureMessages:()=>process.env.JEST_HTML_REPORTER_INCLUDE_FAILURE_MSG||t.includeFailureMsg||!1,shouldIncludeConsoleLog:()=>process.env.JEST_HTML_REPORTER_INCLUDE_CONSOLE_LOG||t.includeConsoleLog||!1,shouldUseCssFile:i,shouldGetStylesheetContent:()=>!(n()&&i()),getExecutionTimeWarningThreshold:()=>process.env.JEST_HTML_REPORTER_EXECUTION_TIME_WARNING_THRESHOLD||t.executionTimeWarningThreshold||5,getBoilerplatePath:()=>process.env.JEST_HTML_REPORTER_BOILERPLATE||t.boilerplate||null,getTheme:o,getDateFormat:()=>process.env.JEST_HTML_REPORTER_DATE_FORMAT||t.dateFormat||"yyyy-mm-dd HH:MM:ss",getSort:()=>process.env.JEST_HTML_REPORTER_SORT||t.sort||"default",getStatusIgnoreFilter:()=>process.env.JEST_HTML_REPORTER_STATUS_FILTER||t.statusIgnoreFilter||null,getAppend:()=>process.env.JEST_HTML_REPORTER_APPEND||t.append||!1}}),config_2=config_1.config,config_3=config_1.setup,config_4=config_1.setConfigData,config_5=config_1.getOutputFilepath,config_6=config_1.getStylesheetFilepath,config_7=config_1.getHasStyleOverridePath,config_8=config_1.getCustomScriptFilepath,config_9=config_1.getPageTitle,config_10=config_1.getLogo,config_11=config_1.shouldIncludeFailureMessages,config_12=config_1.shouldIncludeConsoleLog,config_13=config_1.shouldUseCssFile,config_14=config_1.shouldGetStylesheetContent,config_15=config_1.getExecutionTimeWarningThreshold,config_16=config_1.getBoilerplatePath,config_17=config_1.getTheme,config_18=config_1.getDateFormat,config_19=config_1.getSort,config_20=config_1.getStatusIgnoreFilter,config_21=config_1.getAppend;function JestHtmlReporter(e,t){config_1.setup();const s=new reportGenerator(config_1);if(Object.prototype.hasOwnProperty.call(e,"testResults"))return s.generate({data:e}),e;this.jestConfig=e,this.jestOptions=t,this.consoleLogs=[],this.onTestResult=((e,t)=>{t.console&&this.consoleLogs.push({testFilePath:t.testFilePath,logs:t.console})}),this.onRunComplete=((e,t)=>(config_1.setConfigData(this.jestOptions),s.config=config_1,s.consoleLogs=this.consoleLogs,s.generate({data:t})))}var src=JestHtmlReporter;module.exports=src;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sortTestResults = (testResults, sortType) => {
4
+ switch (sortType) {
5
+ case "status":
6
+ return sortTestResultsByStatus(testResults);
7
+ default:
8
+ return testResults;
9
+ }
10
+ };
11
+ /**
12
+ * Splits test suites apart based on individual test status and sorts by that status:
13
+ * 1. Pending
14
+ * 2. Failed
15
+ * 3. Passed
16
+ */
17
+ const sortTestResultsByStatus = (testResults) => {
18
+ const pendingSuites = [];
19
+ const failingSuites = [];
20
+ const passingSuites = [];
21
+ testResults.forEach(result => {
22
+ const pending = [];
23
+ const failed = [];
24
+ const passed = [];
25
+ result.testResults.forEach(x => {
26
+ if (x.status === "pending") {
27
+ pending.push(x);
28
+ }
29
+ else if (x.status === "failed") {
30
+ failed.push(x);
31
+ }
32
+ else {
33
+ passed.push(x);
34
+ }
35
+ });
36
+ if (pending.length) {
37
+ pendingSuites.push({
38
+ ...result,
39
+ testResults: pending
40
+ });
41
+ }
42
+ if (failed.length) {
43
+ failingSuites.push({
44
+ ...result,
45
+ testResults: failed
46
+ });
47
+ }
48
+ if (passed.length) {
49
+ pendingSuites.push({
50
+ ...result,
51
+ testResults: failed
52
+ });
53
+ }
54
+ });
55
+ return [].concat(pendingSuites, failingSuites, passingSuites);
56
+ };
57
+ //# sourceMappingURL=sortingMethods.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sortingMethods.js","sourceRoot":"","sources":["../src/sortingMethods.ts"],"names":[],"mappings":";;AAGa,QAAA,eAAe,GAAG,CAC7B,WAA4C,EAC5C,QAAkC,EACD,EAAE;IACnC,QAAQ,QAAQ,EAAE;QAChB,KAAK,QAAQ;YACX,OAAO,uBAAuB,CAAC,WAAW,CAAC,CAAC;QAC9C;YACE,OAAO,WAAW,CAAC;KACtB;AACH,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,CAC9B,WAA4C,EAC5C,EAAE;IACF,MAAM,aAAa,GAAoC,EAAE,CAAC;IAC1D,MAAM,aAAa,GAAoC,EAAE,CAAC;IAC1D,MAAM,aAAa,GAAoC,EAAE,CAAC;IAE1D,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QAC3B,MAAM,OAAO,GAAsB,EAAE,CAAC;QACtC,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,MAAM,MAAM,GAAsB,EAAE,CAAC;QAErC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YAC7B,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE;gBAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACjB;iBAAM,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE;gBAChC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAChB;iBAAM;gBACL,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAChB;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,MAAM,EAAE;YAClB,aAAa,CAAC,IAAI,CAAC;gBACjB,GAAG,MAAM;gBACT,WAAW,EAAE,OAAO;aACrB,CAAC,CAAC;SACJ;QACD,IAAI,MAAM,CAAC,MAAM,EAAE;YACjB,aAAa,CAAC,IAAI,CAAC;gBACjB,GAAG,MAAM;gBACT,WAAW,EAAE,MAAM;aACpB,CAAC,CAAC;SACJ;QACD,IAAI,MAAM,CAAC,MAAM,EAAE;YACjB,aAAa,CAAC,IAAI,CAAC;gBACjB,GAAG,MAAM;gBACT,WAAW,EAAE,MAAM;aACpB,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;AAChE,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jest-html-reporter",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
4
4
  "description": "Jest test results processor for generating a summary in HTML",
5
5
  "main": "dist/main",
6
6
  "unpkg": "dist/main.min.js",
@@ -44,15 +44,15 @@
44
44
  "xmlbuilder": "13.0.2"
45
45
  },
46
46
  "peerDependencies": {
47
- "jest": "19.x - 24.x"
47
+ "jest": "19.x - 25.x"
48
48
  },
49
49
  "devDependencies": {
50
50
  "eslint": "^4.19.1",
51
51
  "eslint-config-airbnb-base": "^12.1.0",
52
- "eslint-plugin-import": "^2.18.2",
52
+ "eslint-plugin-import": "^2.20.1",
53
53
  "istanbul-api": "1.2.2",
54
54
  "istanbul-reports": "1.1.4",
55
- "jest": "^24.9.0",
55
+ "jest": "^25.1.0",
56
56
  "rollup": "^0.55.5",
57
57
  "rollup-plugin-commonjs": "^8.4.1",
58
58
  "rollup-plugin-node-resolve": "^3.4.0",