@testomatio/reporter 1.3.1-beta → 1.3.1-beta.1-exclude-skipped-tests
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 +61 -54
- package/lib/adapter/codecept.js +96 -35
- package/lib/adapter/cucumber/current.js +19 -12
- package/lib/adapter/cucumber/legacy.js +7 -6
- package/lib/adapter/cucumber.js +2 -2
- package/lib/adapter/cypress-plugin/index.js +53 -26
- package/lib/adapter/jasmine.js +2 -2
- package/lib/adapter/jest.js +50 -17
- package/lib/adapter/mocha.js +110 -58
- package/lib/adapter/playwright.js +95 -33
- package/lib/adapter/webdriver.js +47 -2
- package/lib/bin/reportXml.js +22 -16
- package/lib/bin/startTest.js +27 -6
- package/lib/client.js +185 -62
- package/lib/config.js +34 -0
- package/lib/constants.js +32 -7
- package/lib/data-storage.js +203 -0
- package/lib/fileUploader.js +135 -54
- package/lib/junit-adapter/adapter.js +0 -2
- package/lib/junit-adapter/csharp.js +3 -4
- package/lib/junit-adapter/index.js +3 -3
- package/lib/junit-adapter/java.js +35 -17
- package/lib/junit-adapter/javascript.js +1 -2
- package/lib/junit-adapter/python.js +12 -14
- package/lib/junit-adapter/ruby.js +1 -2
- package/lib/pipe/csv.js +37 -31
- package/lib/pipe/github.js +27 -39
- package/lib/pipe/gitlab.js +22 -26
- package/lib/pipe/html.js +361 -0
- package/lib/pipe/index.js +3 -1
- package/lib/pipe/testomatio.js +337 -54
- package/lib/reporter-functions.js +46 -0
- package/lib/reporter.js +11 -9
- package/lib/services/artifacts.js +57 -0
- package/lib/services/index.js +13 -0
- package/lib/services/key-values.js +58 -0
- package/lib/services/logger.js +311 -0
- package/lib/template/emptyData.svg +23 -0
- package/lib/template/testomatio.hbs +1421 -0
- package/lib/utils/pipe_utils.js +129 -0
- package/lib/{util.js → utils/utils.js} +147 -17
- package/lib/xmlReader.js +218 -122
- package/package.json +17 -9
- package/lib/_ArtifactStorageOld.js +0 -142
- package/lib/artifactStorage.js +0 -25
- package/lib/dataStorage.js +0 -244
- package/lib/logger.js +0 -301
|
@@ -3,12 +3,11 @@ const path = require('path');
|
|
|
3
3
|
const Adapter = require('./adapter');
|
|
4
4
|
|
|
5
5
|
class JavaScriptAdapter extends Adapter {
|
|
6
|
-
|
|
7
6
|
formatStack(t) {
|
|
8
7
|
let stack = super.formatStack(t);
|
|
9
8
|
|
|
10
9
|
try {
|
|
11
|
-
const error = new Error(stack.split('\n')[0])
|
|
10
|
+
const error = new Error(stack.split('\n')[0]);
|
|
12
11
|
error.stack = stack;
|
|
13
12
|
const record = createCallsiteRecord({
|
|
14
13
|
forError: error,
|
|
@@ -3,7 +3,6 @@ const fs = require('fs');
|
|
|
3
3
|
const Adapter = require('./adapter');
|
|
4
4
|
|
|
5
5
|
class PythonAdapter extends Adapter {
|
|
6
|
-
|
|
7
6
|
getFilePath(t) {
|
|
8
7
|
let fileName = namespaceToFileName(t.suite_title, { checkFile: true });
|
|
9
8
|
if (!fileName) fileName = namespaceToFileName(t.suite_title, { checkFile: false });
|
|
@@ -11,34 +10,33 @@ class PythonAdapter extends Adapter {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
formatTest(t) {
|
|
14
|
-
const fileParts = t.suite_title.split('.')
|
|
13
|
+
const fileParts = t.suite_title.split('.');
|
|
15
14
|
const example = t.title.match(/\[(.*)\]/)?.[1];
|
|
16
|
-
if (example) t.example = {
|
|
15
|
+
if (example) t.example = { '#': example };
|
|
17
16
|
|
|
18
17
|
t.file = namespaceToFileName(t.suite_title);
|
|
19
18
|
t.title = t.title.split('[')[0];
|
|
20
|
-
t.suite_title = fileParts[fileParts.length - 1].replace(/\$/g, ' | ')
|
|
19
|
+
t.suite_title = fileParts[fileParts.length - 1].replace(/\$/g, ' | ');
|
|
21
20
|
return t;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
formatMessage(t) {
|
|
25
24
|
return t.message.split(' ')[0];
|
|
26
25
|
}
|
|
27
|
-
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
function namespaceToFileName(fileName, opts = {}) {
|
|
31
|
-
|
|
29
|
+
const fileParts = fileName.split('.');
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
fileParts.pop();
|
|
31
|
+
while (fileParts.length > 0) {
|
|
32
|
+
const file = `${fileParts.join(path.sep)}.py`;
|
|
33
|
+
if (!opts.checkFile) return file;
|
|
34
|
+
if (fs.existsSync(`${fileParts.join(path.sep)}.py`)) {
|
|
35
|
+
return file;
|
|
40
36
|
}
|
|
41
|
-
|
|
37
|
+
fileParts.pop();
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
module.exports = PythonAdapter;
|
package/lib/pipe/csv.js
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const csvWriter = require('csv-writer');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
const merge = require('lodash.merge');
|
|
7
|
-
const { isSameTest, getCurrentDateTime } = require('../
|
|
7
|
+
const { isSameTest, getCurrentDateTime, ansiRegExp } = require('../utils/utils');
|
|
8
8
|
const { CSV_HEADERS } = require('../constants');
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -14,32 +14,37 @@ const { CSV_HEADERS } = require('../constants');
|
|
|
14
14
|
* @implements {Pipe}
|
|
15
15
|
*/
|
|
16
16
|
class CsvPipe {
|
|
17
|
-
|
|
18
17
|
constructor(params, store) {
|
|
19
18
|
this.store = store || {};
|
|
20
19
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
21
20
|
this.results = [];
|
|
22
21
|
|
|
23
22
|
this.outputDir = 'export';
|
|
23
|
+
this.defaultReportName = 'report.csv';
|
|
24
24
|
this.csvFilename = process.env.TESTOMATIO_CSV_FILENAME;
|
|
25
|
-
this.isEnabled =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
this.isEnabled = false;
|
|
26
|
+
|
|
27
|
+
if (this.csvFilename) {
|
|
28
|
+
const filenameParts = this.csvFilename.split('.');
|
|
29
|
+
|
|
30
|
+
if (filenameParts.length > 0) {
|
|
31
|
+
this.isEnabled = true;
|
|
32
|
+
const baseFilename = filenameParts[0];
|
|
33
|
+
const defaultOutputFile = path.resolve(process.cwd(), this.outputDir, this.defaultReportName);
|
|
34
|
+
|
|
35
|
+
const outputFile =
|
|
36
|
+
baseFilename === this.defaultReportName.split('.')[0] // = 'report'
|
|
37
|
+
? defaultOutputFile
|
|
38
|
+
: path.resolve(process.cwd(), this.outputDir, `${getCurrentDateTime()}_${baseFilename}.csv`);
|
|
39
|
+
|
|
40
|
+
this.outputFile = outputFile;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
// TODO: to using SET opts as argument => prepareRun(opts)
|
|
46
|
+
async prepareRun() {}
|
|
47
|
+
|
|
43
48
|
async createRun() {
|
|
44
49
|
// empty
|
|
45
50
|
}
|
|
@@ -67,22 +72,22 @@ class CsvPipe {
|
|
|
67
72
|
this.checkExportDir();
|
|
68
73
|
|
|
69
74
|
if (!this.outputFile) {
|
|
70
|
-
console.log(
|
|
75
|
+
console.log(chalk.yellow(`⚠️ CSV file is not set, ignoring`));
|
|
71
76
|
return;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
console.log(
|
|
75
|
-
|
|
76
|
-
const writer = csvWriter.createObjectCsvWriter({
|
|
77
|
-
path: this.outputFile,
|
|
78
|
-
header: headers,
|
|
79
|
-
});
|
|
80
|
-
// Save csv file based on the current data
|
|
79
|
+
console.log(chalk.yellow(`⏳ The test results will be added to the csv. It will take some time...`));
|
|
80
|
+
|
|
81
81
|
try {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
// Create csv writer object
|
|
83
|
+
const writer = csvWriter.createObjectCsvWriter({
|
|
84
|
+
path: this.outputFile,
|
|
85
|
+
header: headers,
|
|
86
|
+
});
|
|
87
|
+
// Save csv file based on the current data
|
|
88
|
+
return await writer.writeRecords(data);
|
|
84
89
|
} catch (e) {
|
|
85
|
-
console.log('Unknown error: ', e);
|
|
90
|
+
console.log('Unknown csv error: ', e);
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
|
|
@@ -106,8 +111,8 @@ class CsvPipe {
|
|
|
106
111
|
suite_title,
|
|
107
112
|
title,
|
|
108
113
|
status,
|
|
109
|
-
message,
|
|
110
|
-
stack,
|
|
114
|
+
message: message.replace(ansiRegExp(), ''),
|
|
115
|
+
stack: stack.replace(ansiRegExp(), ''),
|
|
111
116
|
});
|
|
112
117
|
}
|
|
113
118
|
|
|
@@ -120,8 +125,9 @@ class CsvPipe {
|
|
|
120
125
|
|
|
121
126
|
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
122
127
|
// Save results based on the default headers
|
|
123
|
-
if (this.
|
|
124
|
-
this.saveToCsv(this.results, CSV_HEADERS);
|
|
128
|
+
if (this.isEnabled) {
|
|
129
|
+
await this.saveToCsv(this.results, CSV_HEADERS);
|
|
130
|
+
console.log(chalk.green(`🗃️ Recording completed! You can check the result in file = ${this.outputFile}`));
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
|
package/lib/pipe/github.js
CHANGED
|
@@ -4,8 +4,9 @@ const chalk = require('chalk');
|
|
|
4
4
|
const humanizeDuration = require('humanize-duration');
|
|
5
5
|
const merge = require('lodash.merge');
|
|
6
6
|
const { Octokit } = require('@octokit/rest');
|
|
7
|
-
const { APP_PREFIX } = require('../constants');
|
|
8
|
-
const { ansiRegExp, isSameTest } = require('../
|
|
7
|
+
const { APP_PREFIX, testomatLogoURL } = require('../constants');
|
|
8
|
+
const { ansiRegExp, isSameTest } = require('../utils/utils');
|
|
9
|
+
const { statusEmoji, fullName } = require('../utils/pipe_utils');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {import('../../types').Pipe} Pipe
|
|
@@ -21,7 +22,8 @@ class GitHubPipe {
|
|
|
21
22
|
this.token = params.GH_PAT || process.env.GH_PAT;
|
|
22
23
|
this.ref = process.env.GITHUB_REF;
|
|
23
24
|
this.repo = process.env.GITHUB_REPOSITORY;
|
|
24
|
-
this.
|
|
25
|
+
this.jobKey = `${process.env.GITHUB_WORKFLOW || ''} / ${process.env.GITHUB_JOB || ''}`;
|
|
26
|
+
this.hiddenCommentData = `<!--- testomat.io report ${this.jobKey} -->`;
|
|
25
27
|
|
|
26
28
|
debug('GitHub Pipe: ', this.token ? 'TOKEN' : '*no token*', 'Ref:', this.ref, 'Repo:', this.repo);
|
|
27
29
|
|
|
@@ -36,6 +38,9 @@ class GitHubPipe {
|
|
|
36
38
|
debug('GitHub Pipe: Enabled');
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// TODO: to using SET opts as argument => prepareRun(opts)
|
|
42
|
+
async prepareRun() {}
|
|
43
|
+
|
|
39
44
|
async createRun() {}
|
|
40
45
|
|
|
41
46
|
addTest(test) {
|
|
@@ -72,36 +77,34 @@ class GitHubPipe {
|
|
|
72
77
|
|
|
73
78
|
let summary = `${this.hiddenCommentData}
|
|
74
79
|
|
|
75
|
-
| [](https://testomat.io) | ${statusEmoji(
|
|
81
|
+
runParams.status,
|
|
82
|
+
)} ${`${process.env.GITHUB_JOB} ${runParams.status}`.toUpperCase()} |
|
|
77
83
|
| --- | --- |
|
|
78
84
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
79
|
-
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
85
|
+
| Summary | ${failedCount ? `${statusEmoji('failed')} **${failedCount}** failed; ` : ''} ${statusEmoji(
|
|
80
86
|
'passed',
|
|
81
87
|
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
82
|
-
| Duration | 🕐 **${humanizeDuration(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
| Duration | 🕐 **${humanizeDuration(
|
|
89
|
+
parseInt(
|
|
90
|
+
this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
|
|
91
|
+
10,
|
|
92
|
+
),
|
|
93
|
+
{
|
|
94
|
+
maxDecimalPoints: 0,
|
|
95
|
+
},
|
|
96
|
+
)}** |`;
|
|
97
|
+
|
|
86
98
|
if (this.store.runUrl) {
|
|
87
|
-
summary +=
|
|
99
|
+
summary += `\n| Testomat.io Report | 📊 [Run #${this.store.runId}](${this.store.runUrl}) | `;
|
|
88
100
|
}
|
|
89
101
|
if (process.env.GITHUB_WORKFLOW) {
|
|
90
|
-
summary +=
|
|
102
|
+
summary += `\n| Job | 🗂️ [${this.jobKey}](${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${
|
|
103
|
+
this.repo
|
|
104
|
+
}/actions/runs/${process.env.GITHUB_RUN_ID}) | `;
|
|
91
105
|
}
|
|
92
106
|
if (process.env.RUNNER_OS) {
|
|
93
|
-
summary +=
|
|
94
|
-
}
|
|
95
|
-
if (process.env.GITHUB_HEAD_REF) {
|
|
96
|
-
summary += `| Branch | 🌳 \`${process.env.GITHUB_HEAD_REF}\` | `;
|
|
97
|
-
}
|
|
98
|
-
if (process.env.GITHUB_RUN_ATTEMPT) {
|
|
99
|
-
summary += `| Run Attempt | 🌒 \`${process.env.GITHUB_RUN_ATTEMPT}\` | `;
|
|
100
|
-
}
|
|
101
|
-
if (process.env.GITHUB_RUN_ID) {
|
|
102
|
-
summary += `| Build Log | ✒️ ${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${
|
|
103
|
-
this.repo
|
|
104
|
-
}/actions/runs/${process.env.GITHUB_RUN_ID} | `;
|
|
107
|
+
summary += `\n| Operating System | 🖥️ \`${process.env.RUNNER_OS}\` ${process.env.RUNNER_ARCH || ''} | `;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
const failures = this.tests
|
|
@@ -184,21 +187,6 @@ class GitHubPipe {
|
|
|
184
187
|
}
|
|
185
188
|
}
|
|
186
189
|
|
|
187
|
-
function statusEmoji(status) {
|
|
188
|
-
if (status === 'passed') return '🟢';
|
|
189
|
-
if (status === 'failed') return '🔴';
|
|
190
|
-
if (status === 'skipped') return '🟡';
|
|
191
|
-
return '';
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function fullName(t) {
|
|
195
|
-
let line = '';
|
|
196
|
-
if (t.suite_title) line = `${t.suite_title}: `;
|
|
197
|
-
line += `**${t.title}**`;
|
|
198
|
-
if (t.example) line += ` \`[${Object.values(t.example)}]\``;
|
|
199
|
-
return line;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
190
|
async function deletePreviousReport(octokit, owner, repo, issue, hiddenCommentData) {
|
|
203
191
|
if (process.env.GH_KEEP_OUTDATED_REPORTS) return;
|
|
204
192
|
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -4,8 +4,9 @@ const chalk = require('chalk');
|
|
|
4
4
|
const humanizeDuration = require('humanize-duration');
|
|
5
5
|
const merge = require('lodash.merge');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const { APP_PREFIX } = require('../constants');
|
|
8
|
-
const { ansiRegExp, isSameTest } = require('../
|
|
7
|
+
const { APP_PREFIX, testomatLogoURL } = require('../constants');
|
|
8
|
+
const { ansiRegExp, isSameTest } = require('../utils/utils');
|
|
9
|
+
const { statusEmoji, fullName } = require('../utils/pipe_utils');
|
|
9
10
|
|
|
10
11
|
//! GITLAB_PAT environment variable is required for this functionality to work
|
|
11
12
|
//! and your pipeline trigger should be merge_request
|
|
@@ -22,7 +23,7 @@ class GitLabPipe {
|
|
|
22
23
|
this.store = store;
|
|
23
24
|
this.tests = [];
|
|
24
25
|
// GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
|
|
25
|
-
this.token = params.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
26
|
+
this.token = params.GITLAB_PAT || process.env.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
26
27
|
this.hiddenCommentData = `<!--- testomat.io report ${process.env.CI_JOB_NAME || ''} -->`;
|
|
27
28
|
|
|
28
29
|
debug(
|
|
@@ -46,6 +47,9 @@ class GitLabPipe {
|
|
|
46
47
|
debug('GitLab Pipe: Enabled');
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
// TODO: to using SET opts as argument => prepareRun(opts)
|
|
51
|
+
async prepareRun() {}
|
|
52
|
+
|
|
49
53
|
async createRun() {}
|
|
50
54
|
|
|
51
55
|
addTest(test) {
|
|
@@ -73,17 +77,24 @@ class GitLabPipe {
|
|
|
73
77
|
|
|
74
78
|
// constructing the table
|
|
75
79
|
let summary = `${this.hiddenCommentData}
|
|
76
|
-
|
|
77
|
-
| [](https://testomat.io) | ${statusEmoji(
|
|
82
|
+
runParams.status,
|
|
83
|
+
)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
79
84
|
| --- | --- |
|
|
80
85
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
81
86
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Duration | 🕐 **${humanizeDuration(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
'passed',
|
|
88
|
+
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
89
|
+
| Duration | 🕐 **${humanizeDuration(
|
|
90
|
+
parseInt(
|
|
91
|
+
this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
|
|
92
|
+
10,
|
|
93
|
+
),
|
|
94
|
+
{
|
|
95
|
+
maxDecimalPoints: 0,
|
|
96
|
+
},
|
|
97
|
+
)}** |
|
|
87
98
|
`;
|
|
88
99
|
|
|
89
100
|
if (this.ENV.CI_JOB_NAME && this.ENV.CI_JOB_ID) {
|
|
@@ -179,21 +190,6 @@ class GitLabPipe {
|
|
|
179
190
|
updateRun() {}
|
|
180
191
|
}
|
|
181
192
|
|
|
182
|
-
function statusEmoji(status) {
|
|
183
|
-
if (status === 'passed') return '🟢';
|
|
184
|
-
if (status === 'failed') return '🔴';
|
|
185
|
-
if (status === 'skipped') return '🟡';
|
|
186
|
-
return '';
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function fullName(t) {
|
|
190
|
-
let line = '';
|
|
191
|
-
if (t.suite_title) line = `${t.suite_title}: `;
|
|
192
|
-
line += `**${t.title}**`;
|
|
193
|
-
if (t.example) line += ` \`[${Object.values(t.example)}]\``;
|
|
194
|
-
return line;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
193
|
async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
|
|
198
194
|
if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
|
|
199
195
|
|