@testomatio/reporter 0.8.0-beta.30 → 0.8.0-beta.32
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 +16 -9
- package/lib/ArtifactStorage.js +131 -0
- package/lib/adapter/codecept.js +24 -18
- package/lib/adapter/cucumber/current.js +16 -12
- package/lib/adapter/cucumber/legacy.js +14 -10
- package/lib/adapter/cypress-plugin/index.js +7 -7
- package/lib/adapter/jasmine.js +5 -4
- package/lib/adapter/jest.js +7 -11
- package/lib/adapter/mocha.js +53 -18
- package/lib/adapter/playwright.js +7 -4
- package/lib/adapter/webdriver.js +2 -1
- package/lib/bin/reportXml.js +0 -1
- package/lib/bin/startTest.js +2 -2
- package/lib/client.js +61 -42
- package/lib/constants.js +9 -3
- package/lib/fileUploader.js +8 -4
- package/lib/junit-adapter/csharp.js +0 -1
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/csv.js +20 -6
- package/lib/pipe/github.js +19 -12
- package/lib/pipe/gitlab.js +24 -24
- package/lib/pipe/index.js +5 -3
- package/lib/pipe/testomatio.js +67 -42
- package/lib/reporter.js +2 -0
- package/lib/util.js +31 -13
- package/lib/xmlReader.js +19 -15
- package/package.json +8 -4
package/lib/pipe/gitlab.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:pipe:gitlab');
|
|
2
|
+
const { default: axios } = require('axios');
|
|
2
3
|
const chalk = require('chalk');
|
|
3
4
|
const humanizeDuration = require('humanize-duration');
|
|
4
5
|
const merge = require('lodash.merge');
|
|
@@ -9,6 +10,11 @@ const { ansiRegExp, isSameTest } = require('../util');
|
|
|
9
10
|
//! GITLAB_PAT environment variable is required for this functionality to work
|
|
10
11
|
//! and your pipeline trigger should be merge_request
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @class GitLabPipe
|
|
15
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
16
|
+
* @typedef {import('../../types').TestData} TestData
|
|
17
|
+
*/
|
|
12
18
|
class GitLabPipe {
|
|
13
19
|
constructor(params, store = {}) {
|
|
14
20
|
this.isEnabled = false;
|
|
@@ -18,32 +24,26 @@ class GitLabPipe {
|
|
|
18
24
|
// GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
|
|
19
25
|
this.token = params.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
20
26
|
this.hiddenCommentData = `<!--- testomat.io report ${process.env.CI_JOB_NAME || ''} -->`;
|
|
21
|
-
this.axios = axios.create();
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
`Project id: ${this.ENV.CI_PROJECT_ID}, MR id: ${this.ENV.CI_MERGE_REQUEST_IID}`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
28
|
+
debug(
|
|
29
|
+
chalk.yellow('GitLab Pipe:'),
|
|
30
|
+
this.token ? 'TOKEN passed' : '*no token*',
|
|
31
|
+
`Project id: ${this.ENV.CI_PROJECT_ID}, MR id: ${this.ENV.CI_MERGE_REQUEST_IID}`,
|
|
32
|
+
);
|
|
31
33
|
|
|
32
34
|
if (!this.ENV.CI_PROJECT_ID || !this.ENV.CI_MERGE_REQUEST_IID) {
|
|
33
|
-
|
|
35
|
+
debug(`CI pipeline should be run in Merge Request to have ability to add the report comment.`);
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
if (!this.token) {
|
|
38
|
-
|
|
40
|
+
debug(`Hint: GitLab CI variables are unavailable for unprotected branches by default.`);
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
this.isEnabled = true;
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
console.log(APP_PREFIX, chalk.yellow('GitLab Pipe: Enabled'));
|
|
46
|
-
}
|
|
46
|
+
debug('GitLab Pipe: Enabled');
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async createRun() {}
|
|
@@ -75,8 +75,7 @@ class GitLabPipe {
|
|
|
75
75
|
let summary = `${this.hiddenCommentData}
|
|
76
76
|
|
|
77
77
|
| [](https://testomat.io) | ${
|
|
78
|
-
statusEmoji(runParams.status
|
|
79
|
-
)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
78
|
+
statusEmoji(runParams.status)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
80
79
|
| --- | --- |
|
|
81
80
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
82
81
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
@@ -86,8 +85,8 @@ class GitLabPipe {
|
|
|
86
85
|
`;
|
|
87
86
|
|
|
88
87
|
if (this.ENV.CI_JOB_NAME && this.ENV.CI_JOB_ID) {
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
// eslint-disable-next-line max-len
|
|
89
|
+
summary += `| Job | 👷 [${this.ENV.CI_JOB_ID}](${this.ENV.CI_JOB_URL})<br>Name: **${this.ENV.CI_JOB_NAME}**<br>Stage: **${this.ENV.CI_JOB_STAGE}** | `;
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
const failures = this.tests
|
|
@@ -147,12 +146,13 @@ class GitLabPipe {
|
|
|
147
146
|
const commentsRequestURL = `https://gitlab.com/api/v4/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
|
|
148
147
|
|
|
149
148
|
// delete previous report
|
|
150
|
-
await deletePreviousReport(
|
|
149
|
+
await deletePreviousReport(axios, commentsRequestURL, this.hiddenCommentData, this.token);
|
|
151
150
|
|
|
152
151
|
// add current report
|
|
153
|
-
|
|
152
|
+
debug(`Adding comment via url: ${commentsRequestURL}`);
|
|
153
|
+
|
|
154
154
|
try {
|
|
155
|
-
const addCommentResponse = await
|
|
155
|
+
const addCommentResponse = await axios.post(`${commentsRequestURL}?access_token=${this.token}`, { body });
|
|
156
156
|
|
|
157
157
|
const commentID = addCommentResponse.data.id;
|
|
158
158
|
// eslint-disable-next-line max-len
|
|
@@ -160,7 +160,7 @@ class GitLabPipe {
|
|
|
160
160
|
|
|
161
161
|
console.log(APP_PREFIX, chalk.yellow('GitLab'), `Report created: ${chalk.magenta(commentURL)}`);
|
|
162
162
|
} catch (err) {
|
|
163
|
-
console.
|
|
163
|
+
console.error(
|
|
164
164
|
APP_PREFIX,
|
|
165
165
|
chalk.yellow('GitLab'),
|
|
166
166
|
`Couldn't create GitLab report\n${err}.
|
|
@@ -215,7 +215,7 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
|
|
|
215
215
|
const deleteCommentURL = `${commentsRequestURL}/${comment.id}?access_token=${token}`;
|
|
216
216
|
await axiosInstance.delete(deleteCommentURL);
|
|
217
217
|
} catch (e) {
|
|
218
|
-
console.
|
|
218
|
+
console.warn(`Can't delete previously added comment with testomat.io report. Ignore.`);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
// pass next env var if need to clear all previous reports;
|
package/lib/pipe/index.js
CHANGED
|
@@ -7,7 +7,7 @@ const GitHubPipe = require('./github');
|
|
|
7
7
|
const GitLabPipe = require('./gitlab');
|
|
8
8
|
const CsvPipe = require('./csv');
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
function PipeFactory(params, opts) {
|
|
11
11
|
const extraPipes = [];
|
|
12
12
|
|
|
13
13
|
// Add extra pipes into package.json file:
|
|
@@ -17,7 +17,7 @@ module.exports = function (params, opts) {
|
|
|
17
17
|
|
|
18
18
|
const packageJsonFile = path.join(process.cwd(), 'package.json');
|
|
19
19
|
if (fs.existsSync(packageJsonFile)) {
|
|
20
|
-
const packageJson = fs.readFileSync(packageJsonFile);
|
|
20
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonFile).toString());
|
|
21
21
|
const pipeDefs = packageJson?.testomatio?.pipes || [];
|
|
22
22
|
|
|
23
23
|
for (const pipeDef of pipeDefs) {
|
|
@@ -58,4 +58,6 @@ module.exports = function (params, opts) {
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
return pipes;
|
|
61
|
-
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = PipeFactory;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -1,94 +1,107 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:pipe:testomatio');
|
|
1
2
|
const chalk = require('chalk');
|
|
2
3
|
const axios = require('axios');
|
|
3
4
|
const JsonCycle = require('json-cycle');
|
|
4
|
-
|
|
5
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_RUN } = process.env;
|
|
6
|
-
const { APP_PREFIX } = require('../constants');
|
|
5
|
+
const { APP_PREFIX, STATUS } = require('../constants');
|
|
7
6
|
const { isValidUrl } = require('../util');
|
|
8
7
|
|
|
8
|
+
const { TESTOMATIO_RUN } = process.env;
|
|
9
9
|
if (TESTOMATIO_RUN) {
|
|
10
10
|
process.env.runId = TESTOMATIO_RUN;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import('../../types').Pipe} Pipe
|
|
15
|
+
* @typedef {import('../../types').TestData} TestData
|
|
16
|
+
* @class TestomatioPipe
|
|
17
|
+
* @implements {Pipe}
|
|
18
|
+
*/
|
|
13
19
|
class TestomatioPipe {
|
|
14
|
-
|
|
15
|
-
constructor(params, store = {}) {
|
|
20
|
+
constructor(params, store) {
|
|
16
21
|
this.isEnabled = false;
|
|
17
22
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
18
23
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
19
|
-
|
|
20
|
-
console.log(APP_PREFIX, 'Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
21
|
-
}
|
|
24
|
+
debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
22
25
|
if (!this.apiKey) {
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
this.store = store;
|
|
28
|
+
debug('Testomatio Pipe: Enabled');
|
|
29
|
+
this.store = store || {};
|
|
29
30
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
31
|
+
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
32
|
+
this.env = process.env.TESTOMATIO_ENV;
|
|
30
33
|
this.axios = axios.create();
|
|
31
34
|
this.isEnabled = true;
|
|
35
|
+
// do not finish this run (for parallel testing)
|
|
32
36
|
this.proceed = process.env.TESTOMATIO_PROCEED;
|
|
33
37
|
this.runId = params.runId || process.env.runId;
|
|
34
38
|
this.createNewTests = !!process.env.TESTOMATIO_CREATE;
|
|
35
39
|
|
|
36
40
|
if (!isValidUrl(this.url.trim())) {
|
|
37
41
|
this.isEnabled = false;
|
|
38
|
-
console.
|
|
39
|
-
APP_PREFIX,
|
|
40
|
-
chalk.red(`Error creating report on Testomat.io, report url '${this.url}' is invalid`),
|
|
41
|
-
);
|
|
42
|
-
|
|
42
|
+
console.error(APP_PREFIX, chalk.red(`Error creating report on Testomat.io, report url '${this.url}' is invalid`));
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* @returns Promise<void>
|
|
48
|
+
*/
|
|
49
|
+
async createRun() {
|
|
47
50
|
if (!this.isEnabled) return;
|
|
48
51
|
|
|
49
|
-
runParams
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const runParams = Object.fromEntries(
|
|
53
|
+
Object.entries({
|
|
54
|
+
api_key: this.apiKey.trim(),
|
|
55
|
+
group_title: this.groupTitle,
|
|
56
|
+
env: this.env,
|
|
57
|
+
title: this.title,
|
|
58
|
+
}).filter(([, value]) => !!value)
|
|
59
|
+
);
|
|
60
|
+
|
|
52
61
|
if (this.runId) {
|
|
53
|
-
return this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams)
|
|
62
|
+
return this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
|
|
54
63
|
}
|
|
55
|
-
|
|
64
|
+
|
|
56
65
|
try {
|
|
57
66
|
const resp = await this.axios.post(`${this.url.trim()}/api/reporter`, runParams, {
|
|
58
67
|
maxContentLength: Infinity,
|
|
59
68
|
maxBodyLength: Infinity,
|
|
60
|
-
})
|
|
69
|
+
});
|
|
61
70
|
this.runId = resp.data.uid;
|
|
62
71
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
63
72
|
this.store.runUrl = this.runUrl;
|
|
64
|
-
this.store.runId = this.runId;
|
|
73
|
+
this.store.runId = this.runId;
|
|
65
74
|
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
|
|
66
75
|
process.env.runId = this.runId;
|
|
67
76
|
} catch (err) {
|
|
68
|
-
console.
|
|
77
|
+
console.error(
|
|
69
78
|
APP_PREFIX,
|
|
70
79
|
'Error creating Testomat.io report, please check if your API key is valid. Skipping report',
|
|
71
80
|
);
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param testData data
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
addTest(data) {
|
|
76
90
|
if (!this.isEnabled) return;
|
|
77
91
|
if (!this.runId) return;
|
|
78
92
|
data.api_key = this.apiKey;
|
|
79
93
|
data.create = this.createNewTests;
|
|
80
94
|
const json = JsonCycle.stringify(data);
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} catch (err) {
|
|
96
|
+
return this.axios.post(`${this.url}/api/reporter/${this.runId}/testrun`, json, {
|
|
97
|
+
maxContentLength: Infinity,
|
|
98
|
+
maxBodyLength: Infinity,
|
|
99
|
+
headers: {
|
|
100
|
+
// Overwrite Axios's automatically set Content-Type
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
.catch((err) => {
|
|
92
105
|
if (err.response) {
|
|
93
106
|
if (err.response.status >= 400) {
|
|
94
107
|
const responseData = err.response.data || { message: '' };
|
|
@@ -103,18 +116,30 @@ class TestomatioPipe {
|
|
|
103
116
|
} else {
|
|
104
117
|
console.log(APP_PREFIX, chalk.blue(this.title), "Report couldn't be processed", err);
|
|
105
118
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
119
|
+
});
|
|
108
120
|
}
|
|
109
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {import('../../types').RunData} params
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
110
126
|
async finishRun(params) {
|
|
111
127
|
if (!this.isEnabled) return;
|
|
128
|
+
|
|
129
|
+
const { status, parallel } = params;
|
|
130
|
+
|
|
131
|
+
let status_event;
|
|
132
|
+
|
|
133
|
+
if (status === STATUS.FINISHED) status_event = 'finish';
|
|
134
|
+
if (status === STATUS.PASSED) status_event = 'pass';
|
|
135
|
+
if (status === STATUS.FAILED) status_event = 'fail';
|
|
136
|
+
if (parallel) status_event += '_parallel';
|
|
137
|
+
|
|
112
138
|
try {
|
|
113
139
|
if (this.runId && !this.proceed) {
|
|
114
140
|
await this.axios.put(`${this.url}/api/reporter/${this.runId}`, {
|
|
115
141
|
api_key: this.apiKey,
|
|
116
|
-
status_event
|
|
117
|
-
status: params.status,
|
|
142
|
+
status_event,
|
|
118
143
|
tests: params.tests,
|
|
119
144
|
});
|
|
120
145
|
if (this.runUrl) {
|
|
@@ -136,4 +161,4 @@ class TestomatioPipe {
|
|
|
136
161
|
}
|
|
137
162
|
}
|
|
138
163
|
|
|
139
|
-
module.exports = TestomatioPipe;
|
|
164
|
+
module.exports = TestomatioPipe;
|
package/lib/reporter.js
CHANGED
package/lib/util.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URL } = require('url');
|
|
2
|
-
const { sep } = require('path');
|
|
2
|
+
const { sep, basename } = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const isValid = require('is-valid-path');
|
|
@@ -7,7 +7,7 @@ const isValid = require('is-valid-path');
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {String} testTitle - Test title
|
|
9
9
|
*
|
|
10
|
-
* @returns {String} testId
|
|
10
|
+
* @returns {String|null} testId
|
|
11
11
|
*/
|
|
12
12
|
const parseTest = testTitle => {
|
|
13
13
|
const captures = testTitle.match(/@T([\w\d]+)/);
|
|
@@ -21,7 +21,7 @@ const parseTest = testTitle => {
|
|
|
21
21
|
/**
|
|
22
22
|
* @param {String} suiteTitle - suite title
|
|
23
23
|
*
|
|
24
|
-
* @returns {String} suiteId
|
|
24
|
+
* @returns {String|null} suiteId
|
|
25
25
|
*/
|
|
26
26
|
const parseSuite = suiteTitle => {
|
|
27
27
|
const captures = suiteTitle.match(/@S([\w\d]+)/);
|
|
@@ -63,12 +63,12 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
|
63
63
|
// .map(l => l.match(/\[(.*?)\]/)?.[1] || l) // minitest format
|
|
64
64
|
// .map(l => l.split(':')[0])
|
|
65
65
|
.map(l => l.trim())
|
|
66
|
-
.map(l => l.split(' ').find(p => p.includes(':')))
|
|
67
|
-
.filter(l => isValid(l
|
|
66
|
+
.map(l => l.split(' ').find(p => p.includes(':')) || '')
|
|
67
|
+
.filter(l => isValid(l?.split(':')[0]))
|
|
68
68
|
|
|
69
69
|
// // filter out 3rd party libs
|
|
70
|
-
.filter(l => !l
|
|
71
|
-
.filter(l => !l
|
|
70
|
+
.filter(l => !l?.includes(`vendor${ sep}`))
|
|
71
|
+
.filter(l => !l?.includes(`node_modules${ sep}`))
|
|
72
72
|
.filter(l => fs.existsSync(l.split(':')[0]))
|
|
73
73
|
.filter(l => fs.lstatSync(l.split(':')[0]).isFile())
|
|
74
74
|
|
|
@@ -153,14 +153,32 @@ const getCurrentDateTime = () => {
|
|
|
153
153
|
today.getHours() }_${ today.getMinutes() }_${ today.getSeconds()}`;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @param {Object} test - Test adapter object
|
|
158
|
+
*
|
|
159
|
+
* @returns {String|null} testInfo as one string
|
|
160
|
+
*/
|
|
161
|
+
const specificTestInfo = test => {
|
|
162
|
+
// TODO: afterEach has another context.... need to add specific handler, maybe...
|
|
163
|
+
if (test?.title && test?.file) {
|
|
164
|
+
|
|
165
|
+
return `${basename(test.file).split(".").join("#")
|
|
166
|
+
}#${
|
|
167
|
+
test.title.split(" ").join("#")}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return null;
|
|
171
|
+
};
|
|
172
|
+
|
|
156
173
|
module.exports = {
|
|
157
|
-
parseTest,
|
|
158
|
-
parseSuite,
|
|
159
|
-
ansiRegExp,
|
|
160
174
|
isSameTest,
|
|
161
|
-
isValidUrl,
|
|
162
175
|
fetchSourceCode,
|
|
163
176
|
fetchSourceCodeFromStackTrace,
|
|
164
177
|
fetchFilesFromStackTrace,
|
|
165
|
-
getCurrentDateTime
|
|
166
|
-
|
|
178
|
+
getCurrentDateTime,
|
|
179
|
+
specificTestInfo,
|
|
180
|
+
isValidUrl,
|
|
181
|
+
ansiRegExp,
|
|
182
|
+
parseTest,
|
|
183
|
+
parseSuite
|
|
184
|
+
}
|
package/lib/xmlReader.js
CHANGED
|
@@ -2,15 +2,14 @@ const debug = require('debug')('@testomatio/reporter:xml');
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const fs = require("fs");
|
|
5
|
-
|
|
6
|
-
// const util = require("util"); // you can see a result
|
|
7
5
|
const { XMLParser } = require("fast-xml-parser");
|
|
8
|
-
const { APP_PREFIX,
|
|
6
|
+
const { APP_PREFIX, STATUS } = require('./constants');
|
|
9
7
|
const { fetchFilesFromStackTrace, fetchSourceCode, fetchSourceCodeFromStackTrace } = require('./util');
|
|
10
8
|
const upload = require('./fileUploader');
|
|
11
9
|
const pipesFactory = require('./pipe');
|
|
12
10
|
const adapterFactory = require('./junit-adapter');
|
|
13
11
|
|
|
12
|
+
|
|
14
13
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || "https://app.testomat.io";
|
|
15
14
|
const TESTOMATIO = process.env.TESTOMATIO; // key?
|
|
16
15
|
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
@@ -37,7 +36,9 @@ class XmlReader {
|
|
|
37
36
|
};
|
|
38
37
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
39
38
|
this.adapter = adapterFactory(null, opts)
|
|
40
|
-
this.
|
|
39
|
+
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
40
|
+
|
|
41
|
+
this.opts = opts || {};
|
|
41
42
|
const store = {}
|
|
42
43
|
this.pipes = pipesFactory(opts, store);
|
|
43
44
|
|
|
@@ -93,7 +94,7 @@ class XmlReader {
|
|
|
93
94
|
create_tests: true,
|
|
94
95
|
name,
|
|
95
96
|
tests_count: parseInt(tests, 10),
|
|
96
|
-
passed_count: parseInt(tests - failures, 10),
|
|
97
|
+
passed_count: parseInt(tests, 10) - parseInt(failures, 10),
|
|
97
98
|
failed_count: parseInt(failures, 10),
|
|
98
99
|
skipped_count: 0,
|
|
99
100
|
tests: resultTests,
|
|
@@ -127,12 +128,13 @@ class XmlReader {
|
|
|
127
128
|
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
128
129
|
let example = td.name.match(/\((.*?)\)/);
|
|
129
130
|
if (example) example = { ...example[1].split(',')};
|
|
130
|
-
const suite = td.TestMethod.className.split('.');
|
|
131
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
131
132
|
const suite_title = suite.pop();
|
|
132
133
|
return {
|
|
133
134
|
title,
|
|
134
135
|
example,
|
|
135
136
|
file: suite.join('/'),
|
|
137
|
+
description: td.Description,
|
|
136
138
|
suite_title,
|
|
137
139
|
id: td.Execution.id,
|
|
138
140
|
}
|
|
@@ -154,12 +156,14 @@ class XmlReader {
|
|
|
154
156
|
const test = tests.find(t => t.id === r.id) || { };
|
|
155
157
|
r.suite_title = test.suite_title;
|
|
156
158
|
r.title = test.title?.trim();
|
|
159
|
+
if (test.code) r.code = test.code;
|
|
160
|
+
if (test.description) r.description = test.description;
|
|
157
161
|
if (test.example) r.example = test.example;
|
|
158
162
|
if (test.file) r.file = test.file;
|
|
159
163
|
r.create = true;
|
|
160
|
-
if (r.status === 'Passed') r.status = PASSED;
|
|
161
|
-
if (r.status === 'Failed') r.status = FAILED;
|
|
162
|
-
if (r.status === 'Skipped') r.status = SKIPPED;
|
|
164
|
+
if (r.status === 'Passed') r.status = STATUS.PASSED;
|
|
165
|
+
if (r.status === 'Failed') r.status = STATUS.FAILED;
|
|
166
|
+
if (r.status === 'Skipped') r.status = STATUS.SKIPPED;
|
|
163
167
|
delete r.id;
|
|
164
168
|
});
|
|
165
169
|
|
|
@@ -169,8 +173,8 @@ class XmlReader {
|
|
|
169
173
|
|
|
170
174
|
const failed_count = parseInt(counters.failed, 10) + parseInt(counters.error, 10);
|
|
171
175
|
|
|
172
|
-
let status = PASSED;
|
|
173
|
-
if (failed_count > 0) status = FAILED;
|
|
176
|
+
let status = STATUS.PASSED.toString();
|
|
177
|
+
if (failed_count > 0) status = STATUS.FAILED;
|
|
174
178
|
|
|
175
179
|
this.tests = results.filter(t => !!t.title);
|
|
176
180
|
|
|
@@ -310,7 +314,7 @@ class XmlReader {
|
|
|
310
314
|
const dataString = {
|
|
311
315
|
...this.stats,
|
|
312
316
|
api_key: this.requestParams.apiKey,
|
|
313
|
-
|
|
317
|
+
status: 'finished',
|
|
314
318
|
tests: this.tests,
|
|
315
319
|
};
|
|
316
320
|
|
|
@@ -345,9 +349,9 @@ function reduceTestCases(prev, item) {
|
|
|
345
349
|
// prepend system output
|
|
346
350
|
stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}`.trim()
|
|
347
351
|
|
|
348
|
-
let status = PASSED;
|
|
349
|
-
if ('failure' in testCaseItem || 'error' in testCaseItem) status = FAILED;
|
|
350
|
-
if ('skipped' in testCaseItem) status = SKIPPED;
|
|
352
|
+
let status = STATUS.PASSED.toString();
|
|
353
|
+
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
354
|
+
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
351
355
|
|
|
352
356
|
prev.push({
|
|
353
357
|
create: true,
|
package/package.json
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "0.8.0-beta.
|
|
3
|
+
"version": "0.8.0-beta.32",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
|
+
"typings": "typings/index.d.ts",
|
|
6
7
|
"repository": "git@github.com:testomatio/reporter.git",
|
|
7
8
|
"author": "Michael Bodnarchuk <davert@testomat.io>,Koushik Mohan <koushikmohan1996@gmail.com>",
|
|
8
9
|
"license": "MIT",
|
|
9
10
|
"dependencies": {
|
|
10
|
-
"@octokit/rest": "^19.0.5",
|
|
11
|
-
"aws-sdk": "^2.1072.0",
|
|
12
11
|
"@aws-sdk/client-s3": "^3.279.0",
|
|
13
12
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
13
|
+
"@octokit/rest": "^19.0.5",
|
|
14
|
+
"aws-sdk": "^2.1072.0",
|
|
14
15
|
"axios": "^0.25.0",
|
|
15
16
|
"callsite-record": "^4.1.4",
|
|
16
17
|
"chalk": "^4.1.0",
|
|
17
18
|
"commander": "^4.1.1",
|
|
19
|
+
"csv-writer": "^1.6.0",
|
|
20
|
+
"debug": "^4.3.4",
|
|
18
21
|
"dotenv": "^16.0.1",
|
|
19
22
|
"fast-xml-parser": "^4.0.8",
|
|
20
23
|
"glob": "^8.0.3",
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
"json-cycle": "^1.3.0",
|
|
25
28
|
"lodash.memoize": "^4.1.2",
|
|
26
29
|
"lodash.merge": "^4.6.2",
|
|
27
|
-
"
|
|
30
|
+
"uuid": "^9.0.0"
|
|
28
31
|
},
|
|
29
32
|
"files": [
|
|
30
33
|
"bin",
|
|
@@ -49,6 +52,7 @@
|
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
51
54
|
"@cucumber/cucumber": "^8.6.0",
|
|
55
|
+
"@redocly/cli": "^1.0.0-beta.125",
|
|
52
56
|
"@wdio/reporter": "^7.16.13",
|
|
53
57
|
"chai": "^4.3.6",
|
|
54
58
|
"codeceptjs": "^3.2.3",
|