@testomatio/reporter 1.6.13 → 2.0.1-beta-esm
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.
- package/lib/adapter/codecept.d.ts +2 -0
- package/lib/adapter/codecept.js +295 -335
- package/lib/adapter/cucumber/current.d.ts +14 -0
- package/lib/adapter/cucumber/current.js +195 -203
- package/lib/adapter/cucumber/legacy.d.ts +0 -0
- package/lib/adapter/cucumber/legacy.js +130 -155
- package/lib/adapter/cucumber.d.ts +2 -0
- package/lib/adapter/cucumber.js +5 -16
- package/lib/adapter/cypress-plugin/index.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.js +93 -105
- package/lib/adapter/jasmine.d.ts +11 -0
- package/lib/adapter/jasmine.js +54 -53
- package/lib/adapter/jest.d.ts +13 -0
- package/lib/adapter/jest.js +97 -99
- package/lib/adapter/mocha.d.ts +2 -0
- package/lib/adapter/mocha.js +112 -140
- package/lib/adapter/playwright.d.ts +14 -0
- package/lib/adapter/playwright.js +195 -231
- package/lib/adapter/vitest.d.ts +35 -0
- package/lib/adapter/vitest.js +150 -149
- package/lib/adapter/webdriver.d.ts +24 -0
- package/lib/adapter/webdriver.js +134 -119
- package/lib/bin/cli.d.ts +2 -0
- package/lib/bin/cli.js +164 -211
- package/lib/bin/reportXml.d.ts +2 -0
- package/lib/bin/reportXml.js +49 -52
- package/lib/bin/startTest.d.ts +2 -0
- package/lib/bin/startTest.js +82 -95
- package/lib/bin/uploadArtifacts.d.ts +2 -0
- package/lib/bin/uploadArtifacts.js +55 -61
- package/lib/client.d.ts +76 -0
- package/lib/client.js +411 -465
- package/lib/config.d.ts +1 -0
- package/lib/config.js +16 -21
- package/lib/constants.d.ts +25 -0
- package/lib/constants.js +50 -44
- package/lib/data-storage.d.ts +34 -0
- package/lib/data-storage.js +206 -188
- package/lib/junit-adapter/adapter.d.ts +9 -0
- package/lib/junit-adapter/adapter.js +17 -20
- package/lib/junit-adapter/csharp.d.ts +4 -0
- package/lib/junit-adapter/csharp.js +18 -14
- package/lib/junit-adapter/index.d.ts +3 -0
- package/lib/junit-adapter/index.js +27 -25
- package/lib/junit-adapter/java.d.ts +5 -0
- package/lib/junit-adapter/java.js +41 -53
- package/lib/junit-adapter/javascript.d.ts +4 -0
- package/lib/junit-adapter/javascript.js +30 -27
- package/lib/junit-adapter/python.d.ts +5 -0
- package/lib/junit-adapter/python.js +38 -37
- package/lib/junit-adapter/ruby.d.ts +4 -0
- package/lib/junit-adapter/ruby.js +11 -8
- package/lib/output.d.ts +11 -0
- package/lib/output.js +44 -52
- package/lib/package.json +3 -0
- package/lib/pipe/bitbucket.d.ts +23 -0
- package/lib/pipe/bitbucket.js +210 -229
- package/lib/pipe/csv.d.ts +47 -0
- package/lib/pipe/csv.js +113 -126
- package/lib/pipe/debug.d.ts +29 -0
- package/lib/pipe/debug.js +104 -99
- package/lib/pipe/github.d.ts +30 -0
- package/lib/pipe/github.js +186 -213
- package/lib/pipe/gitlab.d.ts +23 -0
- package/lib/pipe/gitlab.js +166 -207
- package/lib/pipe/html.d.ts +34 -0
- package/lib/pipe/html.js +260 -319
- package/lib/pipe/index.d.ts +1 -0
- package/lib/pipe/index.js +84 -66
- package/lib/pipe/testomatio.d.ts +70 -0
- package/lib/pipe/testomatio.js +413 -462
- package/lib/reporter-functions.d.ts +34 -0
- package/lib/reporter-functions.js +28 -26
- package/lib/reporter.d.ts +232 -0
- package/lib/reporter.js +34 -29
- package/lib/services/artifacts.d.ts +33 -0
- package/lib/services/artifacts.js +55 -51
- package/lib/services/index.d.ts +9 -0
- package/lib/services/index.js +14 -12
- package/lib/services/key-values.d.ts +27 -0
- package/lib/services/key-values.js +56 -53
- package/lib/services/logger.d.ts +64 -0
- package/lib/services/logger.js +227 -245
- package/lib/template/testomatio.hbs +651 -1366
- package/lib/uploader.d.ts +60 -0
- package/lib/uploader.js +291 -360
- package/lib/utils/pipe_utils.d.ts +41 -0
- package/lib/utils/pipe_utils.js +89 -85
- package/lib/utils/utils.d.ts +45 -0
- package/lib/utils/utils.js +347 -307
- package/lib/xmlReader.d.ts +92 -0
- package/lib/xmlReader.js +490 -529
- package/package.json +57 -15
- package/src/adapter/codecept.js +375 -0
- package/src/adapter/cucumber/current.js +228 -0
- package/src/adapter/cucumber/legacy.js +158 -0
- package/src/adapter/cucumber.js +4 -0
- package/src/adapter/cypress-plugin/index.js +112 -0
- package/src/adapter/jasmine.js +60 -0
- package/src/adapter/jest.js +107 -0
- package/src/adapter/mocha.cjs +2 -0
- package/src/adapter/mocha.js +157 -0
- package/src/adapter/playwright.js +250 -0
- package/src/adapter/vitest.js +183 -0
- package/src/adapter/webdriver.js +142 -0
- package/src/bin/cli.js +280 -0
- package/src/bin/reportXml.js +74 -0
- package/src/bin/startTest.js +123 -0
- package/src/bin/uploadArtifacts.js +90 -0
- package/src/client.js +504 -0
- package/src/config.js +30 -0
- package/src/constants.js +53 -0
- package/src/data-storage.js +204 -0
- package/src/junit-adapter/adapter.js +23 -0
- package/src/junit-adapter/csharp.js +16 -0
- package/src/junit-adapter/index.js +28 -0
- package/src/junit-adapter/java.js +58 -0
- package/src/junit-adapter/javascript.js +31 -0
- package/src/junit-adapter/python.js +42 -0
- package/src/junit-adapter/ruby.js +10 -0
- package/src/output.js +57 -0
- package/src/pipe/bitbucket.js +254 -0
- package/src/pipe/csv.js +140 -0
- package/src/pipe/debug.js +104 -0
- package/src/pipe/github.js +233 -0
- package/src/pipe/gitlab.js +229 -0
- package/src/pipe/html.js +374 -0
- package/src/pipe/index.js +71 -0
- package/src/pipe/testomatio.js +503 -0
- package/src/reporter-functions.js +55 -0
- package/src/reporter.cjs_decprecated +21 -0
- package/src/reporter.js +33 -0
- package/src/services/artifacts.js +59 -0
- package/src/services/index.js +13 -0
- package/src/services/key-values.js +59 -0
- package/src/services/logger.js +316 -0
- package/src/template/emptyData.svg +23 -0
- package/src/template/testomatio.hbs +706 -0
- package/src/uploader.js +371 -0
- package/src/utils/pipe_utils.js +119 -0
- package/src/utils/utils.js +383 -0
- package/src/xmlReader.js +562 -0
package/lib/pipe/html.js
CHANGED
|
@@ -1,241 +1,202 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
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 debug_1 = __importDefault(require("debug"));
|
|
7
|
+
const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
11
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
12
|
+
const file_url_1 = __importDefault(require("file-url"));
|
|
13
|
+
const utils_js_1 = require("../utils/utils.js");
|
|
14
|
+
const constants_js_1 = require("../constants.js");
|
|
15
|
+
const node_url_1 = require("node:url");
|
|
16
|
+
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:html');
|
|
17
|
+
// @ts-ignore – this line will be removed in compiled code (already defined in the global scope of commonjs)
|
|
11
18
|
class HtmlPipe {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.templateFolderPath = path.resolve(__dirname, '..', 'template');
|
|
46
|
-
this.templateHtmlPath = path.resolve(this.templateFolderPath, HTML_REPORT.TEMPLATE_NAME);
|
|
47
|
-
this.htmlOutputPath = path.join(this.htmlReportDir, this.htmlReportName);
|
|
48
|
-
// create a new folder for the HTML reports
|
|
49
|
-
fileSystem.createDir(this.htmlReportDir);
|
|
50
|
-
|
|
51
|
-
debug(
|
|
52
|
-
chalk.yellow('HTML Pipe:'),
|
|
53
|
-
`Save HTML report: ${this.isEnabled}`,
|
|
54
|
-
`HTML report folder: ${this.htmlReportDir}, report name: ${this.htmlReportName}`,
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async createRun() {
|
|
60
|
-
// empty
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
updateRun() {
|
|
64
|
-
// empty
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Add test data to the result array for saving. As a result of this function, we get a result object to save.
|
|
69
|
-
* @param {import('../../types').RunData} test - object which includes each test entry.
|
|
70
|
-
*/
|
|
71
|
-
addTest(test) {
|
|
72
|
-
if (!this.isEnabled) return;
|
|
73
|
-
|
|
74
|
-
if (!test.status) return;
|
|
75
|
-
|
|
76
|
-
const index = this.tests.findIndex(t => isSameTest(t, test));
|
|
77
|
-
// update if they were already added
|
|
78
|
-
if (index >= 0) {
|
|
79
|
-
this.tests[index] = merge(this.tests[index], test);
|
|
80
|
-
return;
|
|
19
|
+
constructor(params, store = {}) {
|
|
20
|
+
this.store = store || {};
|
|
21
|
+
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
22
|
+
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
23
|
+
this.isHtml = process.env.TESTOMATIO_HTML_REPORT_SAVE;
|
|
24
|
+
debug('HTML Pipe: ', this.apiKey ? 'API KEY' : '*no api key provided*');
|
|
25
|
+
this.isEnabled = false;
|
|
26
|
+
this.htmlOutputPath = '';
|
|
27
|
+
this.fullHtmlOutputPath = '';
|
|
28
|
+
this.filenameMsg = '';
|
|
29
|
+
this.tests = [];
|
|
30
|
+
if (this.isHtml) {
|
|
31
|
+
this.isEnabled = true;
|
|
32
|
+
this.htmlReportDir = process.env.TESTOMATIO_HTML_REPORT_FOLDER || constants_js_1.HTML_REPORT.FOLDER;
|
|
33
|
+
if (process.env.TESTOMATIO_HTML_FILENAME && process.env.TESTOMATIO_HTML_FILENAME.endsWith('.html')) {
|
|
34
|
+
this.htmlReportName = process.env.TESTOMATIO_HTML_FILENAME;
|
|
35
|
+
}
|
|
36
|
+
if (process.env.TESTOMATIO_HTML_FILENAME && !process.env.TESTOMATIO_HTML_FILENAME.endsWith('.html')) {
|
|
37
|
+
this.htmlReportName = constants_js_1.HTML_REPORT.REPORT_DEFAULT_NAME;
|
|
38
|
+
this.filenameMsg =
|
|
39
|
+
'HTML filename must include the extension ".html".' +
|
|
40
|
+
` The default report name "${this.htmlReportDir}/${constants_js_1.HTML_REPORT.REPORT_DEFAULT_NAME}" is used!`;
|
|
41
|
+
}
|
|
42
|
+
if (!process.env.TESTOMATIO_HTML_FILENAME) {
|
|
43
|
+
this.htmlReportName = constants_js_1.HTML_REPORT.REPORT_DEFAULT_NAME;
|
|
44
|
+
}
|
|
45
|
+
this.templateFolderPath = path_1.default.resolve(__dirname, '..', 'template');
|
|
46
|
+
this.templateHtmlPath = path_1.default.resolve(this.templateFolderPath, constants_js_1.HTML_REPORT.TEMPLATE_NAME);
|
|
47
|
+
this.htmlOutputPath = path_1.default.join(this.htmlReportDir, this.htmlReportName);
|
|
48
|
+
// create a new folder for the HTML reports
|
|
49
|
+
utils_js_1.fileSystem.createDir(this.htmlReportDir);
|
|
50
|
+
debug(picocolors_1.default.yellow('HTML Pipe:'), `Save HTML report: ${this.isEnabled}`, `HTML report folder: ${this.htmlReportDir}, report name: ${this.htmlReportName}`);
|
|
51
|
+
}
|
|
81
52
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async finishRun(runParams) {
|
|
87
|
-
if (!this.isEnabled) return;
|
|
88
|
-
|
|
89
|
-
if (this.isHtml) {
|
|
90
|
-
// GENERATE HTML reports based on the results data
|
|
91
|
-
this.buildReport({
|
|
92
|
-
runParams,
|
|
93
|
-
// TODO: this.tests=[] in case of Mocha, need retest by Vitalii
|
|
94
|
-
tests: this.tests,
|
|
95
|
-
outputPath: this.htmlOutputPath,
|
|
96
|
-
templatePath: this.templateHtmlPath,
|
|
97
|
-
warningMsg: this.filenameMsg,
|
|
98
|
-
});
|
|
53
|
+
async createRun() {
|
|
54
|
+
// empty
|
|
99
55
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
* Generates an HTML report based on provided test data and a template.
|
|
103
|
-
* @param {object} opts - Test options used to generate the HTML report:
|
|
104
|
-
* runParams, tests, outputPath, templatePath
|
|
105
|
-
* @returns {void} - This function does not return anything.
|
|
106
|
-
*/
|
|
107
|
-
|
|
108
|
-
buildReport(opts) {
|
|
109
|
-
const { runParams, tests, outputPath, templatePath, warningMsg: msg } = opts;
|
|
110
|
-
|
|
111
|
-
debug('HTML tests data:', tests);
|
|
112
|
-
|
|
113
|
-
if (!outputPath) {
|
|
114
|
-
console.log(chalk.yellow(`🚨 HTML export path is not set, ignoring...`));
|
|
115
|
-
return;
|
|
56
|
+
updateRun() {
|
|
57
|
+
// empty
|
|
116
58
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Add test data to the result array for saving. As a result of this function, we get a result object to save.
|
|
61
|
+
* @param {import('../../types/types.js').RunData} test - object which includes each test entry.
|
|
62
|
+
*/
|
|
63
|
+
addTest(test) {
|
|
64
|
+
if (!this.isEnabled)
|
|
65
|
+
return;
|
|
66
|
+
if (!test.status)
|
|
67
|
+
return;
|
|
68
|
+
const index = this.tests.findIndex(t => (0, utils_js_1.isSameTest)(t, test));
|
|
69
|
+
// update if they were already added
|
|
70
|
+
if (index >= 0) {
|
|
71
|
+
this.tests[index] = (0, lodash_merge_1.default)(this.tests[index], test);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.tests.push(test);
|
|
122
75
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (!test.suite_title?.trim()) {
|
|
138
|
-
test.suite_title = 'Unknown suite';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (!test.title?.trim()) {
|
|
142
|
-
test.title = 'Unknown test title';
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!test.files?.length) {
|
|
146
|
-
test.files = 'This test has no files';
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (!test.steps?.trim()) {
|
|
150
|
-
test.steps = "This test has no 'steps' code";
|
|
151
|
-
} else {
|
|
152
|
-
test.steps = removeAnsiColorCodes(test.steps);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// TODO: future-proof: currently there is no need to display Artifacts and Metadata in HTML
|
|
156
|
-
test.artifacts = test.artifacts || [];
|
|
157
|
-
test.meta = test.meta || {};
|
|
158
|
-
// TODO: u can added an additional test values to this checks in the future
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const data = {
|
|
162
|
-
runId: this.store.runId || '',
|
|
163
|
-
status: runParams.status || 'No status info',
|
|
164
|
-
parallel: runParams.isParallel || 'No parallel info',
|
|
165
|
-
runUrl: this.store.runUrl || '',
|
|
166
|
-
executionTime: testExecutionSumTime(tests),
|
|
167
|
-
executionDate: getCurrentDateTimeFormatted(),
|
|
168
|
-
tests,
|
|
169
|
-
};
|
|
170
|
-
// generate output HTML based on the template
|
|
171
|
-
const html = this.#generateHTMLReport(data, templatePath);
|
|
172
|
-
|
|
173
|
-
if (!html) return;
|
|
174
|
-
|
|
175
|
-
fs.writeFileSync(outputPath, html, 'utf-8');
|
|
176
|
-
// Check if the file exists
|
|
177
|
-
if (fs.existsSync(outputPath)) {
|
|
178
|
-
// Get the absolute path of the file
|
|
179
|
-
const absolutePath = path.resolve(outputPath);
|
|
180
|
-
// Convert the file path to a file URL
|
|
181
|
-
const fileUrlPath = fileUrl(absolutePath, { resolve: true });
|
|
182
|
-
|
|
183
|
-
debug('HTML tests data:', fileUrlPath);
|
|
184
|
-
|
|
185
|
-
console.log(chalk.green(`📊 The HTML report was successfully generated. Full filepath: ${fileUrlPath}`));
|
|
186
|
-
} else {
|
|
187
|
-
console.log(chalk.red(`🚨 Failed to generate the HTML report.`));
|
|
76
|
+
async finishRun(runParams) {
|
|
77
|
+
if (!this.isEnabled)
|
|
78
|
+
return;
|
|
79
|
+
if (this.isHtml) {
|
|
80
|
+
// GENERATE HTML reports based on the results data
|
|
81
|
+
this.buildReport({
|
|
82
|
+
runParams,
|
|
83
|
+
// TODO: this.tests=[] in case of Mocha, need retest by Vitalii
|
|
84
|
+
tests: this.tests,
|
|
85
|
+
outputPath: this.htmlOutputPath,
|
|
86
|
+
templatePath: this.templateHtmlPath,
|
|
87
|
+
warningMsg: this.filenameMsg,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
188
90
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Generates an HTML report based on provided test data and a template.
|
|
93
|
+
* @param {object} opts - Test options used to generate the HTML report:
|
|
94
|
+
* runParams, tests, outputPath, templatePath
|
|
95
|
+
* @returns {void} - This function does not return anything.
|
|
96
|
+
*/
|
|
97
|
+
buildReport(opts) {
|
|
98
|
+
const { runParams, tests, outputPath, templatePath, warningMsg: msg } = opts;
|
|
99
|
+
debug('HTML tests data:', tests);
|
|
100
|
+
if (!outputPath) {
|
|
101
|
+
console.log(picocolors_1.default.yellow(`🚨 HTML export path is not set, ignoring...`));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
console.log(picocolors_1.default.yellow(`⏳ The test results will be added to the HTML report. It will take some time...`));
|
|
105
|
+
if (msg) {
|
|
106
|
+
console.log(picocolors_1.default.blue(msg));
|
|
107
|
+
}
|
|
108
|
+
tests.forEach(test => {
|
|
109
|
+
// steps could be an array or a string
|
|
110
|
+
test.steps = Array.isArray(test.steps)
|
|
111
|
+
? (test.steps = test.steps
|
|
112
|
+
.map(step => (0, utils_js_1.formatStep)(step))
|
|
113
|
+
.flat()
|
|
114
|
+
.join('\n'))
|
|
115
|
+
: test.steps;
|
|
116
|
+
if (!test.message?.trim()) {
|
|
117
|
+
test.message = "This test has no 'message' code";
|
|
118
|
+
}
|
|
119
|
+
if (!test.suite_title?.trim()) {
|
|
120
|
+
test.suite_title = 'Unknown suite';
|
|
121
|
+
}
|
|
122
|
+
if (!test.title?.trim()) {
|
|
123
|
+
test.title = 'Unknown test title';
|
|
124
|
+
}
|
|
125
|
+
if (!test.files?.length) {
|
|
126
|
+
test.files = 'This test has no files';
|
|
127
|
+
}
|
|
128
|
+
if (!test.steps?.trim()) {
|
|
129
|
+
test.steps = "This test has no 'steps' code";
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
test.steps = removeAnsiColorCodes(test.steps);
|
|
133
|
+
}
|
|
134
|
+
// TODO: future-proof: currently there is no need to display Artifacts and Metadata in HTML
|
|
135
|
+
test.artifacts = test.artifacts || [];
|
|
136
|
+
test.meta = test.meta || {};
|
|
137
|
+
// TODO: u can added an additional test values to this checks in the future
|
|
138
|
+
});
|
|
139
|
+
const data = {
|
|
140
|
+
runId: this.store.runId || '',
|
|
141
|
+
status: runParams.status || 'No status info',
|
|
142
|
+
parallel: runParams.isParallel || 'No parallel info',
|
|
143
|
+
runUrl: this.store.runUrl || '',
|
|
144
|
+
executionTime: testExecutionSumTime(tests),
|
|
145
|
+
executionDate: getCurrentDateTimeFormatted(),
|
|
146
|
+
tests,
|
|
147
|
+
};
|
|
148
|
+
// generate output HTML based on the template
|
|
149
|
+
const html = this.#generateHTMLReport(data, templatePath);
|
|
150
|
+
if (!html)
|
|
151
|
+
return;
|
|
152
|
+
fs_1.default.writeFileSync(outputPath, html, 'utf-8');
|
|
153
|
+
// Check if the file exists
|
|
154
|
+
if (fs_1.default.existsSync(outputPath)) {
|
|
155
|
+
// Get the absolute path of the file
|
|
156
|
+
const absolutePath = path_1.default.resolve(outputPath);
|
|
157
|
+
// Convert the file path to a file URL
|
|
158
|
+
const fileUrlPath = (0, file_url_1.default)(absolutePath, { resolve: true });
|
|
159
|
+
debug('HTML tests data:', fileUrlPath);
|
|
160
|
+
console.log(picocolors_1.default.green(`📊 The HTML report was successfully generated. Full filepath: ${fileUrlPath}`));
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log(picocolors_1.default.red(`🚨 Failed to generate the HTML report.`));
|
|
164
|
+
}
|
|
201
165
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Generates an HTML report based on provided test data and a template path.
|
|
168
|
+
* @param {any} data - Test data used to generate the HTML report.
|
|
169
|
+
* @param {string} [templatePath=""] - The path to the HTML template used for generating the report.
|
|
170
|
+
* @returns {string | void} - The generated HTML report as a string or void if templatePath is not provided.
|
|
171
|
+
*/
|
|
172
|
+
#generateHTMLReport(data, templatePath = '') {
|
|
173
|
+
if (!templatePath) {
|
|
174
|
+
console.log(picocolors_1.default.red(`🚨 HTML template not found. Report generation is impossible!`));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const templateSource = fs_1.default.readFileSync(templatePath, 'utf8');
|
|
178
|
+
this.#loadReportHelpers();
|
|
179
|
+
try {
|
|
180
|
+
const template = handlebars_1.default.compile(templateSource);
|
|
181
|
+
return template(data);
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
console.log(picocolors_1.default.red('❌ Oops! An unknown error occurred when generating an HTML report'));
|
|
185
|
+
console.log(picocolors_1.default.red(e));
|
|
186
|
+
}
|
|
212
187
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
handlebars.registerHelper(
|
|
217
|
-
'getTestsByStatus',
|
|
218
|
-
(tests, status) => tests.filter(test => test.status.toLowerCase() === status.toLowerCase()).length,
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
handlebars.registerHelper(
|
|
222
|
-
'selectComponent',
|
|
223
|
-
() =>
|
|
224
|
-
new handlebars.SafeString(
|
|
225
|
-
`<select style="width: 70px;height: 38px;" class="form-select" aria-label="Tests counter on page">
|
|
188
|
+
#loadReportHelpers() {
|
|
189
|
+
handlebars_1.default.registerHelper('getTestsByStatus', (tests, status) => tests.filter(test => test.status.toLowerCase() === status.toLowerCase()).length);
|
|
190
|
+
handlebars_1.default.registerHelper('selectComponent', () => new handlebars_1.default.SafeString(`<select style="width: 70px;height: 38px;" class="form-select" aria-label="Tests counter on page">
|
|
226
191
|
<option value="0">10</option>
|
|
227
192
|
<option value="1">25</option>
|
|
228
193
|
<option value="2">50</option>
|
|
229
|
-
</select
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const svgContent = fs.readFileSync(svgFilePath, 'utf8');
|
|
236
|
-
|
|
237
|
-
return new handlebars.SafeString(
|
|
238
|
-
`
|
|
194
|
+
</select>`));
|
|
195
|
+
/* eslint-disable */
|
|
196
|
+
handlebars_1.default.registerHelper('emptyDataComponent', () => {
|
|
197
|
+
const svgFilePath = path_1.default.join(__dirname, '..', 'template', 'emptyData.svg');
|
|
198
|
+
const svgContent = fs_1.default.readFileSync(svgFilePath, 'utf8');
|
|
199
|
+
return new handlebars_1.default.SafeString(`
|
|
239
200
|
<div class="noData">
|
|
240
201
|
<div class="noDataSvg">
|
|
241
202
|
${svgContent}
|
|
@@ -243,67 +204,55 @@ class HtmlPipe {
|
|
|
243
204
|
<div class="noDataText">
|
|
244
205
|
NO MATCHING TESTS
|
|
245
206
|
</div>
|
|
246
|
-
<div
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const paginatedItems = [];
|
|
274
|
-
for (let i = 0; i < items.length; i += pageSize) {
|
|
275
|
-
paginatedItems.push(items.slice(i, i + pageSize));
|
|
276
|
-
}
|
|
277
|
-
return paginatedItems;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
statuses.forEach(status => {
|
|
281
|
-
for (const option in paginationOptions) {
|
|
282
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
283
|
-
if (paginationOptions.hasOwnProperty(option)) {
|
|
284
|
-
const pageSize = paginationOptions[option];
|
|
285
|
-
let filteredItems = totalTests;
|
|
286
|
-
|
|
287
|
-
if (status !== 'all') {
|
|
288
|
-
filteredItems = totalTests.filter(item => item.status === status);
|
|
207
|
+
<div>`);
|
|
208
|
+
});
|
|
209
|
+
/* eslint-enable */
|
|
210
|
+
handlebars_1.default.registerHelper('pageDispleyElements', tests => {
|
|
211
|
+
// We wrapp the lines to the HTML format we need
|
|
212
|
+
const totalTests = JSON.parse(JSON.stringify(tests)
|
|
213
|
+
.replace(/<script>/g, '<script>')
|
|
214
|
+
.replace(/<\/script>/g, '</script>'));
|
|
215
|
+
const paginationOptions = {
|
|
216
|
+
0: 10,
|
|
217
|
+
1: 25,
|
|
218
|
+
2: 50,
|
|
219
|
+
};
|
|
220
|
+
const statuses = ['all', 'passed', 'failed', 'skipped'];
|
|
221
|
+
const pageItemGroups = {
|
|
222
|
+
all: {},
|
|
223
|
+
passed: {},
|
|
224
|
+
failed: {},
|
|
225
|
+
skipped: {},
|
|
226
|
+
totalTests,
|
|
227
|
+
};
|
|
228
|
+
function paginateItems(items, pageSize) {
|
|
229
|
+
const paginatedItems = [];
|
|
230
|
+
for (let i = 0; i < items.length; i += pageSize) {
|
|
231
|
+
paginatedItems.push(items.slice(i, i + pageSize));
|
|
232
|
+
}
|
|
233
|
+
return paginatedItems;
|
|
289
234
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
235
|
+
statuses.forEach(status => {
|
|
236
|
+
for (const option in paginationOptions) {
|
|
237
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
238
|
+
if (paginationOptions.hasOwnProperty(option)) {
|
|
239
|
+
const pageSize = paginationOptions[option];
|
|
240
|
+
let filteredItems = totalTests;
|
|
241
|
+
if (status !== 'all') {
|
|
242
|
+
filteredItems = totalTests.filter(item => item.status === status);
|
|
243
|
+
}
|
|
244
|
+
pageItemGroups[status][option] = paginateItems(filteredItems, pageSize);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
pageItemGroups.totalTests = totalTests;
|
|
249
|
+
return JSON.stringify(pageItemGroups);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
toString() {
|
|
253
|
+
return 'HTML Reporter';
|
|
254
|
+
}
|
|
305
255
|
}
|
|
306
|
-
|
|
307
256
|
/**
|
|
308
257
|
* Calculates the total execution time for an array of tests.
|
|
309
258
|
* @param {Object[]} tests - An array of test objects.
|
|
@@ -311,58 +260,50 @@ class HtmlPipe {
|
|
|
311
260
|
* @returns {string} - The total execution time in a formatted duration string.
|
|
312
261
|
*/
|
|
313
262
|
function testExecutionSumTime(tests) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return formatDuration(totalMilliseconds);
|
|
263
|
+
const totalMilliseconds = tests.reduce((sum, test) => {
|
|
264
|
+
if (typeof test.run_time === 'number' && !Number.isNaN(test.run_time)) {
|
|
265
|
+
return sum + test.run_time;
|
|
266
|
+
}
|
|
267
|
+
return sum;
|
|
268
|
+
}, 0);
|
|
269
|
+
return formatDuration(totalMilliseconds);
|
|
322
270
|
}
|
|
323
|
-
|
|
324
271
|
/**
|
|
325
272
|
* Removes ANSI color codes and converts newline characters to HTML line breaks in a given string.
|
|
326
273
|
* @param {string} str - The input string containing ANSI color codes.
|
|
327
274
|
* @returns {string} - The updated string with removed ANSI color codes and replaced newline characters.
|
|
328
275
|
*/
|
|
329
276
|
function removeAnsiColorCodes(str) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
return updatedStr;
|
|
277
|
+
let updatedStr = str.replace((0, utils_js_1.ansiRegExp)(), '');
|
|
278
|
+
updatedStr = updatedStr.replace(/\n/g, '<br>');
|
|
279
|
+
return updatedStr;
|
|
334
280
|
}
|
|
335
|
-
|
|
336
281
|
/**
|
|
337
282
|
* Formats duration in milliseconds into a human-readable string representation.
|
|
338
283
|
* @param {number} duration - The duration in milliseconds.
|
|
339
284
|
* @returns {string} - The formatted duration string (e.g., "2h 30m 15s 500ms").
|
|
340
285
|
*/
|
|
341
286
|
function formatDuration(duration) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
return `${hours}h ${minutes}m ${seconds}s ${milliseconds}ms`;
|
|
287
|
+
const milliseconds = duration % 1000;
|
|
288
|
+
duration = (duration - milliseconds) / 1000;
|
|
289
|
+
const seconds = duration % 60;
|
|
290
|
+
duration = (duration - seconds) / 60;
|
|
291
|
+
const minutes = duration % 60;
|
|
292
|
+
const hours = (duration - minutes) / 60;
|
|
293
|
+
return `${hours}h ${minutes}m ${seconds}s ${milliseconds}ms`;
|
|
350
294
|
}
|
|
351
|
-
|
|
352
295
|
/**
|
|
353
296
|
* Retrieves the current date and time in a formatted string.
|
|
354
297
|
* @returns {string} - The formatted date and time string (e.g., "(01/01/2023 12:00:00)").
|
|
355
298
|
*/
|
|
356
299
|
function getCurrentDateTimeFormatted() {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
return `(${day}/${month}/${year} ${hours}:${minutes}:${seconds})`;
|
|
300
|
+
const currentDate = new Date();
|
|
301
|
+
const day = currentDate.getDate().toString().padStart(2, '0');
|
|
302
|
+
const month = (currentDate.getMonth() + 1).toString().padStart(2, '0');
|
|
303
|
+
const year = currentDate.getFullYear();
|
|
304
|
+
const hours = currentDate.getHours().toString().padStart(2, '0');
|
|
305
|
+
const minutes = currentDate.getMinutes().toString().padStart(2, '0');
|
|
306
|
+
const seconds = currentDate.getSeconds().toString().padStart(2, '0');
|
|
307
|
+
return `(${day}/${month}/${year} ${hours}:${minutes}:${seconds})`;
|
|
366
308
|
}
|
|
367
|
-
|
|
368
309
|
module.exports = HtmlPipe;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function pipesFactory(params: any, opts: any): Promise<any[]>;
|