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