@testomatio/reporter 2.0.0-beta.2-xml → 2.0.0-beta.3-xml
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/nightwatch.js +5 -5
- package/lib/adapter/webdriver.d.ts +1 -1
- package/lib/bin/cli.js +7 -6
- package/lib/bin/reportXml.js +4 -2
- package/lib/bin/startTest.js +3 -2
- package/lib/bin/uploadArtifacts.js +5 -4
- package/lib/data-storage.d.ts +1 -1
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +11 -1
- package/lib/pipe/bitbucket.d.ts +0 -2
- package/lib/pipe/bitbucket.js +19 -21
- package/lib/pipe/gitlab.d.ts +0 -2
- package/lib/pipe/gitlab.js +8 -27
- package/lib/pipe/testomatio.d.ts +1 -2
- package/lib/pipe/testomatio.js +65 -75
- package/lib/reporter.d.ts +12 -12
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/logger.d.ts +1 -1
- package/lib/utils/utils.d.ts +2 -0
- package/lib/utils/utils.js +20 -4
- package/lib/xmlReader.js +38 -11
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const client_js_1 = __importDefault(require("../client.js"));
|
|
7
7
|
const config_js_1 = require("../config.js");
|
|
8
|
-
const
|
|
8
|
+
const constants_js_1 = require("../constants.js");
|
|
9
9
|
const utils_js_1 = require("../utils/utils.js");
|
|
10
10
|
const apiKey = config_js_1.config.TESTOMATIO;
|
|
11
11
|
const client = new client_js_1.default({ apiKey });
|
|
@@ -29,14 +29,14 @@ module.exports = {
|
|
|
29
29
|
let status;
|
|
30
30
|
switch (test.status) {
|
|
31
31
|
case 'pass':
|
|
32
|
-
status =
|
|
32
|
+
status = constants_js_1.STATUS.PASSED;
|
|
33
33
|
break;
|
|
34
34
|
case 'fail':
|
|
35
|
-
status =
|
|
35
|
+
status = constants_js_1.STATUS.FAILED;
|
|
36
36
|
break;
|
|
37
37
|
// probably not required (because skipped tests are in separate array), but just in case
|
|
38
38
|
case 'skip':
|
|
39
|
-
status =
|
|
39
|
+
status = constants_js_1.STATUS.SKIPPED;
|
|
40
40
|
console.info('Skipped test is in completed tests array:', test, 'Not expected behavior.');
|
|
41
41
|
break;
|
|
42
42
|
default:
|
|
@@ -58,7 +58,7 @@ module.exports = {
|
|
|
58
58
|
}
|
|
59
59
|
// just array with skipped tests titles, no any other info
|
|
60
60
|
for (const testTitle of skippedTests) {
|
|
61
|
-
client.addTestRun(
|
|
61
|
+
client.addTestRun(constants_js_1.STATUS.SKIPPED, {
|
|
62
62
|
suite_title: suiteTitle,
|
|
63
63
|
tags,
|
|
64
64
|
rid: `${testModule.uuid || ''}_${testTitle || ''}`,
|
|
@@ -19,6 +19,6 @@ declare class WebdriverReporter extends WDIOReporter {
|
|
|
19
19
|
*/
|
|
20
20
|
addBddScenario(scenario: import("../../types/types.js").WebdriverIOScenario): Promise<import("../../types/types.js").PipeResult[]>;
|
|
21
21
|
}
|
|
22
|
-
import WDIOReporter from '@wdio/reporter';
|
|
22
|
+
import { default as WDIOReporter } from '@wdio/reporter';
|
|
23
23
|
import TestomatClient from '../client.js';
|
|
24
24
|
import { RunnerStats } from '@wdio/reporter';
|
package/lib/bin/cli.js
CHANGED
|
@@ -11,17 +11,18 @@ const debug_1 = __importDefault(require("debug"));
|
|
|
11
11
|
const client_js_1 = __importDefault(require("../client.js"));
|
|
12
12
|
const xmlReader_js_1 = __importDefault(require("../xmlReader.js"));
|
|
13
13
|
const constants_js_1 = require("../constants.js");
|
|
14
|
-
const package_json_1 = require("../../package.json");
|
|
15
|
-
const config_js_1 = require("../config.js");
|
|
16
14
|
const utils_js_1 = require("../utils/utils.js");
|
|
15
|
+
const config_js_1 = require("../config.js");
|
|
16
|
+
const utils_js_2 = require("../utils/utils.js");
|
|
17
17
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
18
18
|
const filesize_1 = require("filesize");
|
|
19
19
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
20
20
|
const debug = (0, debug_1.default)('@testomatio/reporter:xml-cli');
|
|
21
|
-
|
|
21
|
+
const version = (0, utils_js_1.getPackageVersion)();
|
|
22
|
+
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
|
|
22
23
|
const program = new commander_1.Command();
|
|
23
24
|
program
|
|
24
|
-
.version(
|
|
25
|
+
.version(version)
|
|
25
26
|
.option('--env-file <envfile>', 'Load environment variables from env file')
|
|
26
27
|
.hook('preAction', thisCommand => {
|
|
27
28
|
const opts = thisCommand.opts();
|
|
@@ -48,7 +49,7 @@ program
|
|
|
48
49
|
.command('finish')
|
|
49
50
|
.description('Finish Run by its ID')
|
|
50
51
|
.action(async () => {
|
|
51
|
-
process.env.TESTOMATIO_RUN ||= (0,
|
|
52
|
+
process.env.TESTOMATIO_RUN ||= (0, utils_js_2.readLatestRunId)();
|
|
52
53
|
if (!process.env.TESTOMATIO_RUN) {
|
|
53
54
|
console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
|
|
54
55
|
return process.exit(1);
|
|
@@ -180,7 +181,7 @@ program
|
|
|
180
181
|
.action(async (opts) => {
|
|
181
182
|
const apiKey = config_js_1.config.TESTOMATIO;
|
|
182
183
|
process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
|
|
183
|
-
const runId = process.env.TESTOMATIO_RUN || process.env.runId || (0,
|
|
184
|
+
const runId = process.env.TESTOMATIO_RUN || process.env.runId || (0, utils_js_2.readLatestRunId)();
|
|
184
185
|
if (!runId) {
|
|
185
186
|
console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
|
|
186
187
|
return process.exit(1);
|
package/lib/bin/reportXml.js
CHANGED
|
@@ -10,10 +10,12 @@ const glob_1 = require("glob");
|
|
|
10
10
|
const debug_1 = __importDefault(require("debug"));
|
|
11
11
|
const constants_js_1 = require("../constants.js");
|
|
12
12
|
const xmlReader_js_1 = __importDefault(require("../xmlReader.js"));
|
|
13
|
-
const
|
|
13
|
+
const utils_js_1 = require("../utils/utils.js");
|
|
14
14
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const version = (0, utils_js_1.getPackageVersion)();
|
|
15
17
|
const debug = (0, debug_1.default)('@testomatio/reporter:xml-cli');
|
|
16
|
-
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io XML Reporter v${
|
|
18
|
+
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io XML Reporter v${version}`)));
|
|
17
19
|
const program = new commander_1.Command();
|
|
18
20
|
program
|
|
19
21
|
.arguments('<pattern>')
|
package/lib/bin/startTest.js
CHANGED
|
@@ -9,10 +9,11 @@ const commander_1 = require("commander");
|
|
|
9
9
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
10
10
|
const client_js_1 = __importDefault(require("../client.js"));
|
|
11
11
|
const constants_js_1 = require("../constants.js");
|
|
12
|
-
const
|
|
12
|
+
const utils_js_1 = require("../utils/utils.js");
|
|
13
13
|
const config_js_1 = require("../config.js");
|
|
14
14
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
15
|
-
|
|
15
|
+
const version = (0, utils_js_1.getPackageVersion)();
|
|
16
|
+
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
|
|
16
17
|
const program = new commander_1.Command();
|
|
17
18
|
program
|
|
18
19
|
.option('-c, --command <cmd>', 'Test runner command')
|
|
@@ -9,12 +9,13 @@ const picocolors_1 = __importDefault(require("picocolors"));
|
|
|
9
9
|
const debug_1 = __importDefault(require("debug"));
|
|
10
10
|
const client_js_1 = __importDefault(require("../client.js"));
|
|
11
11
|
const constants_js_1 = require("../constants.js");
|
|
12
|
-
const package_json_1 = require("../../package.json");
|
|
13
|
-
const config_js_1 = require("../config.js");
|
|
14
12
|
const utils_js_1 = require("../utils/utils.js");
|
|
13
|
+
const config_js_1 = require("../config.js");
|
|
14
|
+
const utils_js_2 = require("../utils/utils.js");
|
|
15
15
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
16
16
|
const debug = (0, debug_1.default)('@testomatio/reporter:upload-cli');
|
|
17
|
-
|
|
17
|
+
const version = (0, utils_js_1.getPackageVersion)();
|
|
18
|
+
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
|
|
18
19
|
const program = new commander_1.Command();
|
|
19
20
|
program
|
|
20
21
|
.option('--env-file <envfile>', 'Load environment variables from env file')
|
|
@@ -28,7 +29,7 @@ program
|
|
|
28
29
|
}
|
|
29
30
|
const apiKey = config_js_1.config.TESTOMATIO;
|
|
30
31
|
process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
|
|
31
|
-
const runId = process.env.TESTOMATIO_RUN || process.env.runId || (0,
|
|
32
|
+
const runId = process.env.TESTOMATIO_RUN || process.env.runId || (0, utils_js_2.readLatestRunId)();
|
|
32
33
|
if (!runId) {
|
|
33
34
|
console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
|
|
34
35
|
return process.exit(1);
|
package/lib/data-storage.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
6
7
|
const adapter_js_1 = __importDefault(require("./adapter.js"));
|
|
7
8
|
class CSharpAdapter extends adapter_js_1.default {
|
|
8
9
|
formatTest(t) {
|
|
@@ -12,9 +13,18 @@ class CSharpAdapter extends adapter_js_1.default {
|
|
|
12
13
|
t.example = { ...example[1].split(',') };
|
|
13
14
|
const suite = t.suite_title.split('.');
|
|
14
15
|
t.suite_title = suite.pop();
|
|
15
|
-
t.file =
|
|
16
|
+
t.file = namespaceToFileName(t.file);
|
|
16
17
|
t.title = title.trim();
|
|
17
18
|
return t;
|
|
18
19
|
}
|
|
20
|
+
getFilePath(t) {
|
|
21
|
+
const fileName = namespaceToFileName(t.file);
|
|
22
|
+
return fileName;
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
module.exports = CSharpAdapter;
|
|
26
|
+
function namespaceToFileName(fileName) {
|
|
27
|
+
const fileParts = fileName.split('.');
|
|
28
|
+
fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
|
|
29
|
+
return `${fileParts.join(path_1.default.sep)}.cs`;
|
|
30
|
+
}
|
package/lib/pipe/bitbucket.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ export class BitbucketPipe {
|
|
|
11
11
|
tests: any[];
|
|
12
12
|
token: any;
|
|
13
13
|
hiddenCommentData: string;
|
|
14
|
-
client: Gaxios;
|
|
15
14
|
cleanLog(log: any): Promise<string>;
|
|
16
15
|
prepareRun(): Promise<void>;
|
|
17
16
|
createRun(): Promise<void>;
|
|
@@ -22,4 +21,3 @@ export class BitbucketPipe {
|
|
|
22
21
|
}
|
|
23
22
|
export type Pipe = import("../../types/types.js").Pipe;
|
|
24
23
|
export type TestData = import("../../types/types.js").TestData;
|
|
25
|
-
import { Gaxios } from 'gaxios';
|
package/lib/pipe/bitbucket.js
CHANGED
|
@@ -40,7 +40,7 @@ exports.BitbucketPipe = void 0;
|
|
|
40
40
|
const constants_js_1 = require("../constants.js");
|
|
41
41
|
const utils_js_1 = require("../utils/utils.js");
|
|
42
42
|
const pipe_utils_js_1 = require("../utils/pipe_utils.js");
|
|
43
|
-
const
|
|
43
|
+
const axios_1 = __importDefault(require("axios"));
|
|
44
44
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
45
45
|
const humanize_duration_1 = __importDefault(require("humanize-duration"));
|
|
46
46
|
const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
@@ -69,13 +69,6 @@ class BitbucketPipe {
|
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
this.isEnabled = true;
|
|
72
|
-
this.client = new gaxios_1.Gaxios({
|
|
73
|
-
baseURL: 'https://api.bitbucket.org/2.0',
|
|
74
|
-
headers: {
|
|
75
|
-
'Content-Type': 'application/json',
|
|
76
|
-
'Authorization': `Bearer ${this.token}`
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
72
|
debug('Bitbucket Pipe: Enabled');
|
|
80
73
|
}
|
|
81
74
|
async cleanLog(log) {
|
|
@@ -175,17 +168,18 @@ class BitbucketPipe {
|
|
|
175
168
|
}
|
|
176
169
|
// Construct Bitbucket API URL for comments
|
|
177
170
|
// eslint-disable-next-line max-len
|
|
178
|
-
const commentsRequestURL =
|
|
171
|
+
const commentsRequestURL = `https://api.bitbucket.org/2.0/repositories/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pullrequests/${this.ENV.BITBUCKET_PR_ID}/comments`;
|
|
179
172
|
// Delete previous report
|
|
180
|
-
await deletePreviousReport(
|
|
173
|
+
await deletePreviousReport(axios_1.default, commentsRequestURL, this.hiddenCommentData, this.token);
|
|
181
174
|
// Add current report
|
|
182
175
|
debug(`Adding comment via URL: ${commentsRequestURL}`);
|
|
183
176
|
debug(`Final Bitbucket API call body: ${body}`);
|
|
184
177
|
try {
|
|
185
|
-
const addCommentResponse = await
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
178
|
+
const addCommentResponse = await axios_1.default.post(commentsRequestURL, { content: { raw: body } }, {
|
|
179
|
+
headers: {
|
|
180
|
+
Authorization: `Bearer ${this.token}`,
|
|
181
|
+
'Content-Type': 'application/json',
|
|
182
|
+
},
|
|
189
183
|
});
|
|
190
184
|
const commentID = addCommentResponse.data.id;
|
|
191
185
|
// eslint-disable-next-line max-len
|
|
@@ -204,15 +198,17 @@ class BitbucketPipe {
|
|
|
204
198
|
updateRun() { }
|
|
205
199
|
}
|
|
206
200
|
exports.BitbucketPipe = BitbucketPipe;
|
|
207
|
-
async function deletePreviousReport(
|
|
201
|
+
async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
|
|
208
202
|
if (process.env.BITBUCKET_KEEP_OUTDATED_REPORTS)
|
|
209
203
|
return;
|
|
210
204
|
// Get comments
|
|
211
205
|
let comments = [];
|
|
212
206
|
try {
|
|
213
|
-
const response = await
|
|
214
|
-
|
|
215
|
-
|
|
207
|
+
const response = await axiosInstance.get(commentsRequestURL, {
|
|
208
|
+
headers: {
|
|
209
|
+
Authorization: `Bearer ${token}`,
|
|
210
|
+
'Content-Type': 'application/json',
|
|
211
|
+
},
|
|
216
212
|
});
|
|
217
213
|
comments = response.data.values;
|
|
218
214
|
}
|
|
@@ -227,9 +223,11 @@ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentDat
|
|
|
227
223
|
try {
|
|
228
224
|
// Delete previous comment
|
|
229
225
|
const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
|
|
230
|
-
await
|
|
231
|
-
|
|
232
|
-
|
|
226
|
+
await axiosInstance.delete(deleteCommentURL, {
|
|
227
|
+
headers: {
|
|
228
|
+
Authorization: `Bearer ${token}`,
|
|
229
|
+
'Content-Type': 'application/json',
|
|
230
|
+
},
|
|
233
231
|
});
|
|
234
232
|
}
|
|
235
233
|
catch (e) {
|
package/lib/pipe/gitlab.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ declare class GitLabPipe {
|
|
|
14
14
|
tests: any[];
|
|
15
15
|
token: any;
|
|
16
16
|
hiddenCommentData: string;
|
|
17
|
-
client: Gaxios;
|
|
18
17
|
prepareRun(): Promise<void>;
|
|
19
18
|
createRun(): Promise<void>;
|
|
20
19
|
addTest(test: any): void;
|
|
@@ -22,4 +21,3 @@ declare class GitLabPipe {
|
|
|
22
21
|
toString(): string;
|
|
23
22
|
updateRun(): void;
|
|
24
23
|
}
|
|
25
|
-
import { Gaxios } from 'gaxios';
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const debug_1 = __importDefault(require("debug"));
|
|
7
|
-
const
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
9
|
const humanize_duration_1 = __importDefault(require("humanize-duration"));
|
|
10
10
|
const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
@@ -39,12 +39,6 @@ class GitLabPipe {
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
this.isEnabled = true;
|
|
42
|
-
this.client = new gaxios_1.Gaxios({
|
|
43
|
-
baseURL: 'https://gitlab.com/api/v4',
|
|
44
|
-
headers: {
|
|
45
|
-
'Content-Type': 'application/json',
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
42
|
debug('GitLab Pipe: Enabled');
|
|
49
43
|
}
|
|
50
44
|
// TODO: to using SET opts as argument => prepareRun(opts)
|
|
@@ -132,18 +126,13 @@ class GitLabPipe {
|
|
|
132
126
|
body += '\n</details>';
|
|
133
127
|
}
|
|
134
128
|
// eslint-disable-next-line max-len
|
|
135
|
-
const commentsRequestURL =
|
|
129
|
+
const commentsRequestURL = `https://gitlab.com/api/v4/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
|
|
136
130
|
// delete previous report
|
|
137
|
-
await deletePreviousReport(
|
|
131
|
+
await deletePreviousReport(axios_1.default, commentsRequestURL, this.hiddenCommentData, this.token);
|
|
138
132
|
// add current report
|
|
139
133
|
debug(`Adding comment via url: ${commentsRequestURL}`);
|
|
140
134
|
try {
|
|
141
|
-
const addCommentResponse = await
|
|
142
|
-
method: 'POST',
|
|
143
|
-
url: commentsRequestURL,
|
|
144
|
-
params: { access_token: this.token },
|
|
145
|
-
data: { body }
|
|
146
|
-
});
|
|
135
|
+
const addCommentResponse = await axios_1.default.post(`${commentsRequestURL}?access_token=${this.token}`, { body });
|
|
147
136
|
const commentID = addCommentResponse.data.id;
|
|
148
137
|
// eslint-disable-next-line max-len
|
|
149
138
|
const commentURL = `${this.ENV.CI_PROJECT_URL}/-/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}#note_${commentID}`;
|
|
@@ -160,17 +149,13 @@ class GitLabPipe {
|
|
|
160
149
|
}
|
|
161
150
|
updateRun() { }
|
|
162
151
|
}
|
|
163
|
-
async function deletePreviousReport(
|
|
152
|
+
async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
|
|
164
153
|
if (process.env.GITLAB_KEEP_OUTDATED_REPORTS)
|
|
165
154
|
return;
|
|
166
155
|
// get comments
|
|
167
156
|
let comments = [];
|
|
168
157
|
try {
|
|
169
|
-
const response = await
|
|
170
|
-
method: 'GET',
|
|
171
|
-
url: commentsRequestURL,
|
|
172
|
-
params: { access_token: token }
|
|
173
|
-
});
|
|
158
|
+
const response = await axiosInstance.get(`${commentsRequestURL}?access_token=${token}`);
|
|
174
159
|
comments = response.data;
|
|
175
160
|
}
|
|
176
161
|
catch (e) {
|
|
@@ -183,12 +168,8 @@ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentDat
|
|
|
183
168
|
if (comment.body.includes(hiddenCommentData)) {
|
|
184
169
|
try {
|
|
185
170
|
// delete previous comment
|
|
186
|
-
const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
|
|
187
|
-
await
|
|
188
|
-
method: 'DELETE',
|
|
189
|
-
url: deleteCommentURL,
|
|
190
|
-
params: { access_token: token }
|
|
191
|
-
});
|
|
171
|
+
const deleteCommentURL = `${commentsRequestURL}/${comment.id}?access_token=${token}`;
|
|
172
|
+
await axiosInstance.delete(deleteCommentURL);
|
|
192
173
|
}
|
|
193
174
|
catch (e) {
|
|
194
175
|
console.warn(`Can't delete previously added comment with testomat.io report. Ignore.`);
|
package/lib/pipe/testomatio.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ declare class TestomatioPipe implements Pipe {
|
|
|
31
31
|
groupTitle: any;
|
|
32
32
|
env: string;
|
|
33
33
|
label: string;
|
|
34
|
-
|
|
34
|
+
axios: import("axios").AxiosInstance;
|
|
35
35
|
proceed: string;
|
|
36
36
|
jiraId: string;
|
|
37
37
|
runId: any;
|
|
@@ -68,4 +68,3 @@ declare class TestomatioPipe implements Pipe {
|
|
|
68
68
|
toString(): string;
|
|
69
69
|
#private;
|
|
70
70
|
}
|
|
71
|
-
import { Gaxios } from 'gaxios';
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -5,7 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const debug_1 = __importDefault(require("debug"));
|
|
7
7
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
|
-
|
|
8
|
+
// Retry interceptor function
|
|
9
|
+
const axios_retry_1 = __importDefault(require("axios-retry"));
|
|
10
|
+
// Default axios instance
|
|
11
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
12
|
const json_cycle_1 = __importDefault(require("json-cycle"));
|
|
10
13
|
const constants_js_1 = require("../constants.js");
|
|
11
14
|
const utils_js_1 = require("../utils/utils.js");
|
|
@@ -51,31 +54,42 @@ class TestomatioPipe {
|
|
|
51
54
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
52
55
|
this.env = process.env.TESTOMATIO_ENV;
|
|
53
56
|
this.label = process.env.TESTOMATIO_LABEL;
|
|
54
|
-
// Create a new instance of
|
|
55
|
-
this.
|
|
57
|
+
// Create a new instance of axios with a custom config
|
|
58
|
+
this.axios = axios_1.default.create({
|
|
56
59
|
baseURL: `${this.url.trim()}`,
|
|
57
60
|
timeout: constants_js_1.AXIOS_TIMEOUT,
|
|
58
|
-
proxy: proxy
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
proxy: proxy
|
|
62
|
+
? {
|
|
63
|
+
host: proxy.hostname,
|
|
64
|
+
port: parseInt(proxy.port, 10),
|
|
65
|
+
protocol: proxy.protocol,
|
|
66
|
+
}
|
|
67
|
+
: false,
|
|
68
|
+
});
|
|
69
|
+
// Pass the axios instance to the retry function
|
|
70
|
+
(0, axios_retry_1.default)(this.axios, {
|
|
71
|
+
// do not use retries for unit tests
|
|
72
|
+
retries: constants_js_1.REPORTER_REQUEST_RETRIES.retriesPerRequest, // Number of retries
|
|
73
|
+
shouldResetTimeout: true,
|
|
74
|
+
retryCondition: error => {
|
|
75
|
+
if (!error.response)
|
|
76
|
+
return false;
|
|
77
|
+
switch (error.response?.status) {
|
|
78
|
+
case 400: // Bad request (probably wrong API key)
|
|
79
|
+
case 404: // Test not matched
|
|
80
|
+
case 429: // Rate limit exceeded
|
|
81
|
+
case 500: // Internal server error
|
|
66
82
|
return false;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case 404: // Test not matched
|
|
70
|
-
case 429: // Rate limit exceeded
|
|
71
|
-
case 500: // Internal server error
|
|
72
|
-
return false;
|
|
73
|
-
default:
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
83
|
+
default:
|
|
84
|
+
break;
|
|
77
85
|
}
|
|
78
|
-
|
|
86
|
+
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
87
|
+
},
|
|
88
|
+
retryDelay: () => constants_js_1.REPORTER_REQUEST_RETRIES.retryTimeout, // sum = 15sec
|
|
89
|
+
onRetry: async (retryCount, error) => {
|
|
90
|
+
this.retriesTimestamps.push(Date.now());
|
|
91
|
+
debug(`${error.message || `Request failed ${error.status}`}. Retry #${retryCount} ...`);
|
|
92
|
+
},
|
|
79
93
|
});
|
|
80
94
|
this.isEnabled = true;
|
|
81
95
|
// do not finish this run (for parallel testing)
|
|
@@ -110,14 +124,11 @@ class TestomatioPipe {
|
|
|
110
124
|
if (!q) {
|
|
111
125
|
return;
|
|
112
126
|
}
|
|
113
|
-
const resp = await this.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
|
|
119
|
-
(0, utils_js_1.foundedTestLog)(constants_js_1.APP_PREFIX, resp.data.tests);
|
|
120
|
-
return resp.data.tests;
|
|
127
|
+
const resp = await this.axios.get('/api/test_grep', q);
|
|
128
|
+
const { data } = resp;
|
|
129
|
+
if (Array.isArray(data?.tests) && data?.tests?.length > 0) {
|
|
130
|
+
(0, utils_js_1.foundedTestLog)(constants_js_1.APP_PREFIX, data.tests);
|
|
131
|
+
return data.tests;
|
|
121
132
|
}
|
|
122
133
|
console.log(constants_js_1.APP_PREFIX, `⛔ No tests found for your --filter --> ${type}=${id}`);
|
|
123
134
|
}
|
|
@@ -139,6 +150,7 @@ class TestomatioPipe {
|
|
|
139
150
|
let buildUrl = process.env.BUILD_URL || process.env.CI_JOB_URL || process.env.CIRCLE_BUILD_URL;
|
|
140
151
|
// GitHub Actions Url
|
|
141
152
|
if (!buildUrl && process.env.GITHUB_RUN_ID) {
|
|
153
|
+
// eslint-disable-next-line max-len
|
|
142
154
|
buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
|
143
155
|
}
|
|
144
156
|
// Azure DevOps Url
|
|
@@ -168,23 +180,16 @@ class TestomatioPipe {
|
|
|
168
180
|
if (this.runId) {
|
|
169
181
|
this.store.runId = this.runId;
|
|
170
182
|
debug(`Run with id ${this.runId} already created, updating...`);
|
|
171
|
-
const resp = await this.
|
|
172
|
-
method: 'PUT',
|
|
173
|
-
url: `/api/reporter/${this.runId}`,
|
|
174
|
-
data: runParams
|
|
175
|
-
});
|
|
183
|
+
const resp = await this.axios.put(`/api/reporter/${this.runId}`, runParams);
|
|
176
184
|
if (resp.data.artifacts)
|
|
177
185
|
(0, pipe_utils_js_1.setS3Credentials)(resp.data.artifacts);
|
|
178
186
|
return;
|
|
179
187
|
}
|
|
180
188
|
debug('Creating run...');
|
|
181
189
|
try {
|
|
182
|
-
const resp = await this.
|
|
183
|
-
method: 'POST',
|
|
184
|
-
url: '/api/reporter',
|
|
185
|
-
data: runParams,
|
|
190
|
+
const resp = await this.axios.post(`/api/reporter`, runParams, {
|
|
186
191
|
maxContentLength: Infinity,
|
|
187
|
-
|
|
192
|
+
maxBodyLength: Infinity,
|
|
188
193
|
});
|
|
189
194
|
this.runId = resp.data.uid;
|
|
190
195
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
@@ -200,7 +205,6 @@ class TestomatioPipe {
|
|
|
200
205
|
}
|
|
201
206
|
catch (err) {
|
|
202
207
|
const errorText = err.response?.data?.message || err.message;
|
|
203
|
-
debug('Error creating run', err);
|
|
204
208
|
console.log(errorText || err);
|
|
205
209
|
if (!this.apiKey)
|
|
206
210
|
console.error('Testomat.io API key is not set');
|
|
@@ -241,15 +245,7 @@ class TestomatioPipe {
|
|
|
241
245
|
}
|
|
242
246
|
const json = json_cycle_1.default.stringify(data);
|
|
243
247
|
debug('Adding test', json);
|
|
244
|
-
return this.
|
|
245
|
-
method: 'POST',
|
|
246
|
-
url: `/api/reporter/${this.runId}/testrun`,
|
|
247
|
-
data: json,
|
|
248
|
-
headers: {
|
|
249
|
-
'Content-Type': 'application/json',
|
|
250
|
-
},
|
|
251
|
-
maxContentLength: Infinity
|
|
252
|
-
}).catch(err => {
|
|
248
|
+
return this.axios.post(`/api/reporter/${this.runId}/testrun`, json, axiosAddTestrunRequestConfig).catch(err => {
|
|
253
249
|
this.requestFailures++;
|
|
254
250
|
this.notReportedTestsCount++;
|
|
255
251
|
if (err.response) {
|
|
@@ -294,19 +290,9 @@ class TestomatioPipe {
|
|
|
294
290
|
// get tests from batch and clear batch
|
|
295
291
|
const testsToSend = this.batch.tests.splice(0);
|
|
296
292
|
debug('📨 Batch upload', testsToSend.length, 'tests');
|
|
297
|
-
return this.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
data: {
|
|
301
|
-
api_key: this.apiKey,
|
|
302
|
-
tests: testsToSend,
|
|
303
|
-
batch_index: this.batch.batchIndex
|
|
304
|
-
},
|
|
305
|
-
headers: {
|
|
306
|
-
'Content-Type': 'application/json',
|
|
307
|
-
},
|
|
308
|
-
maxContentLength: Infinity
|
|
309
|
-
}).catch(err => {
|
|
293
|
+
return this.axios
|
|
294
|
+
.post(`/api/reporter/${this.runId}/testrun`, { api_key: this.apiKey, tests: testsToSend, batch_index: this.batch.batchIndex }, axiosAddTestrunRequestConfig)
|
|
295
|
+
.catch(err => {
|
|
310
296
|
this.requestFailures++;
|
|
311
297
|
this.notReportedTestsCount += testsToSend.length;
|
|
312
298
|
if (err.response) {
|
|
@@ -380,16 +366,12 @@ class TestomatioPipe {
|
|
|
380
366
|
status_event += '_parallel';
|
|
381
367
|
try {
|
|
382
368
|
if (this.runId && !this.proceed) {
|
|
383
|
-
await this.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
status_event,
|
|
390
|
-
detach: params.detach,
|
|
391
|
-
tests: params.tests,
|
|
392
|
-
}
|
|
369
|
+
await this.axios.put(`/api/reporter/${this.runId}`, {
|
|
370
|
+
api_key: this.apiKey,
|
|
371
|
+
duration: params.duration,
|
|
372
|
+
status_event,
|
|
373
|
+
detach: params.detach,
|
|
374
|
+
tests: params.tests,
|
|
393
375
|
});
|
|
394
376
|
if (this.runUrl) {
|
|
395
377
|
console.log(constants_js_1.APP_PREFIX, '📊 Report Saved. Report URL:', picocolors_1.default.magenta(this.runUrl));
|
|
@@ -439,10 +421,18 @@ function printCreateIssue(err) {
|
|
|
439
421
|
if (!err.config)
|
|
440
422
|
return;
|
|
441
423
|
const time = new Date().toUTCString();
|
|
442
|
-
const {
|
|
424
|
+
const { data, url, baseURL, method } = err?.config || {};
|
|
443
425
|
console.log('```js');
|
|
444
|
-
console.log({
|
|
426
|
+
console.log({ data: data?.replace(/"(tstmt_[^"]+)"/g, 'tstmt_*'), url, baseURL, method, time });
|
|
445
427
|
console.log('```');
|
|
446
428
|
});
|
|
447
429
|
}
|
|
430
|
+
const axiosAddTestrunRequestConfig = {
|
|
431
|
+
maxContentLength: Infinity,
|
|
432
|
+
maxBodyLength: Infinity,
|
|
433
|
+
headers: {
|
|
434
|
+
// Overwrite Axios's automatically set Content-Type
|
|
435
|
+
'Content-Type': 'application/json',
|
|
436
|
+
},
|
|
437
|
+
};
|
|
448
438
|
module.exports = TestomatioPipe;
|
package/lib/reporter.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type log = typeof import("./reporter-functions.js");
|
|
|
8
8
|
export const log: (...args: any[]) => void;
|
|
9
9
|
export type logger = typeof import("./services/index.js");
|
|
10
10
|
export const logger: {
|
|
11
|
-
"__#
|
|
11
|
+
"__#12@#originalUserLogger": {
|
|
12
12
|
assert(condition?: boolean, ...data: any[]): void;
|
|
13
13
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
14
14
|
clear(): void;
|
|
@@ -53,13 +53,13 @@ export const logger: {
|
|
|
53
53
|
profile(label?: string): void;
|
|
54
54
|
profileEnd(label?: string): void;
|
|
55
55
|
};
|
|
56
|
-
"__#
|
|
56
|
+
"__#12@#userLoggerWithOverridenMethods": any;
|
|
57
57
|
logLevel: string;
|
|
58
58
|
step(strings: any, ...values: any[]): void;
|
|
59
59
|
getLogs(context: string): string[];
|
|
60
|
-
"__#
|
|
60
|
+
"__#12@#stringifyLogs"(...args: any[]): string;
|
|
61
61
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
62
|
-
"__#
|
|
62
|
+
"__#12@#logWrapper"(argsArray: any, level: any): void;
|
|
63
63
|
assert(...args: any[]): void;
|
|
64
64
|
debug(...args: any[]): void;
|
|
65
65
|
error(...args: any[]): void;
|
|
@@ -83,7 +83,7 @@ export type step = typeof import("./reporter-functions.js");
|
|
|
83
83
|
export const step: (message: string) => void;
|
|
84
84
|
declare namespace _default {
|
|
85
85
|
let testomatioLogger: {
|
|
86
|
-
"__#
|
|
86
|
+
"__#12@#originalUserLogger": {
|
|
87
87
|
assert(condition?: boolean, ...data: any[]): void;
|
|
88
88
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
89
89
|
clear(): void;
|
|
@@ -128,13 +128,13 @@ declare namespace _default {
|
|
|
128
128
|
profile(label?: string): void;
|
|
129
129
|
profileEnd(label?: string): void;
|
|
130
130
|
};
|
|
131
|
-
"__#
|
|
131
|
+
"__#12@#userLoggerWithOverridenMethods": any;
|
|
132
132
|
logLevel: string;
|
|
133
133
|
step(strings: any, ...values: any[]): void;
|
|
134
134
|
getLogs(context: string): string[];
|
|
135
|
-
"__#
|
|
135
|
+
"__#12@#stringifyLogs"(...args: any[]): string;
|
|
136
136
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
137
|
-
"__#
|
|
137
|
+
"__#12@#logWrapper"(argsArray: any, level: any): void;
|
|
138
138
|
assert(...args: any[]): void;
|
|
139
139
|
debug(...args: any[]): void;
|
|
140
140
|
error(...args: any[]): void;
|
|
@@ -157,7 +157,7 @@ declare namespace _default {
|
|
|
157
157
|
}, context?: any) => void;
|
|
158
158
|
let log: (...args: any[]) => void;
|
|
159
159
|
let logger: {
|
|
160
|
-
"__#
|
|
160
|
+
"__#12@#originalUserLogger": {
|
|
161
161
|
assert(condition?: boolean, ...data: any[]): void;
|
|
162
162
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
163
163
|
clear(): void;
|
|
@@ -202,13 +202,13 @@ declare namespace _default {
|
|
|
202
202
|
profile(label?: string): void;
|
|
203
203
|
profileEnd(label?: string): void;
|
|
204
204
|
};
|
|
205
|
-
"__#
|
|
205
|
+
"__#12@#userLoggerWithOverridenMethods": any;
|
|
206
206
|
logLevel: string;
|
|
207
207
|
step(strings: any, ...values: any[]): void;
|
|
208
208
|
getLogs(context: string): string[];
|
|
209
|
-
"__#
|
|
209
|
+
"__#12@#stringifyLogs"(...args: any[]): string;
|
|
210
210
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
211
|
-
"__#
|
|
211
|
+
"__#12@#logWrapper"(argsArray: any, level: any): void;
|
|
212
212
|
assert(...args: any[]): void;
|
|
213
213
|
debug(...args: any[]): void;
|
|
214
214
|
error(...args: any[]): void;
|
package/lib/services/logger.d.ts
CHANGED
package/lib/utils/utils.d.ts
CHANGED
package/lib/utils/utils.js
CHANGED
|
@@ -36,7 +36,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.testRunnerHelper = exports.specificTestInfo = exports.parseSuite = exports.isValidUrl = exports.humanize = exports.getTestomatIdFromTestTitle = exports.getCurrentDateTime = exports.foundedTestLog = exports.fileSystem = exports.fetchFilesFromStackTrace = exports.fetchIdFromOutput = exports.fetchIdFromCode = exports.fetchSourceCodeFromStackTrace = exports.fetchSourceCode = exports.isSameTest = exports.ansiRegExp = void 0;
|
|
39
|
+
exports.testRunnerHelper = exports.specificTestInfo = exports.parseSuite = exports.isValidUrl = exports.humanize = exports.getTestomatIdFromTestTitle = exports.getCurrentDateTime = exports.foundedTestLog = exports.fileSystem = exports.fetchFilesFromStackTrace = exports.fetchIdFromOutput = exports.fetchIdFromCode = exports.fetchSourceCodeFromStackTrace = exports.fetchSourceCode = exports.isSameTest = exports.ansiRegExp = exports.TEST_ID_REGEX = void 0;
|
|
40
|
+
exports.getPackageVersion = getPackageVersion;
|
|
40
41
|
exports.formatStep = formatStep;
|
|
41
42
|
exports.readLatestRunId = readLatestRunId;
|
|
42
43
|
exports.removeColorCodes = removeColorCodes;
|
|
@@ -48,7 +49,9 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
48
49
|
const is_valid_path_1 = __importDefault(require("is-valid-path"));
|
|
49
50
|
const debug_1 = __importDefault(require("debug"));
|
|
50
51
|
const os_1 = __importDefault(require("os"));
|
|
52
|
+
const url_2 = require("url");
|
|
51
53
|
const debug = (0, debug_1.default)('@testomatio/reporter:util');
|
|
54
|
+
// Use __dirname directly since we're compiling to CommonJS
|
|
52
55
|
/**
|
|
53
56
|
* @param {String} testTitle - Test title
|
|
54
57
|
*
|
|
@@ -140,7 +143,7 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
|
140
143
|
.join('\n');
|
|
141
144
|
};
|
|
142
145
|
exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
143
|
-
|
|
146
|
+
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
144
147
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
145
148
|
const comments = code
|
|
146
149
|
.split('\n')
|
|
@@ -154,11 +157,11 @@ const fetchIdFromCode = (code, opts = {}) => {
|
|
|
154
157
|
return l.startsWith('// ');
|
|
155
158
|
}
|
|
156
159
|
});
|
|
157
|
-
return comments.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
|
|
160
|
+
return comments.find(c => c.match(exports.TEST_ID_REGEX))?.match(exports.TEST_ID_REGEX)?.[1];
|
|
158
161
|
};
|
|
159
162
|
exports.fetchIdFromCode = fetchIdFromCode;
|
|
160
163
|
const fetchIdFromOutput = output => {
|
|
161
|
-
const TID_FULL_PATTERN = new RegExp(`tid:\\/\\/.*?(${TEST_ID_REGEX.source})`);
|
|
164
|
+
const TID_FULL_PATTERN = new RegExp(`tid:\\/\\/.*?(${exports.TEST_ID_REGEX.source})`);
|
|
162
165
|
return output.match(TID_FULL_PATTERN)?.[2];
|
|
163
166
|
};
|
|
164
167
|
exports.fetchIdFromOutput = fetchIdFromOutput;
|
|
@@ -183,6 +186,12 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
183
186
|
if (lineIndex === -1)
|
|
184
187
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
185
188
|
}
|
|
189
|
+
else if (opts.lang === 'csharp') {
|
|
190
|
+
if (lineIndex === -1)
|
|
191
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
192
|
+
if (lineIndex === -1)
|
|
193
|
+
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
194
|
+
}
|
|
186
195
|
else {
|
|
187
196
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
188
197
|
}
|
|
@@ -384,6 +393,13 @@ function formatStep(step, shift = 0) {
|
|
|
384
393
|
}
|
|
385
394
|
return lines;
|
|
386
395
|
}
|
|
396
|
+
function getPackageVersion() {
|
|
397
|
+
const packageJsonPath = path_1.default.resolve(__dirname, '../../package.json');
|
|
398
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
|
|
399
|
+
return packageJson.version;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
module.exports.getPackageVersion = getPackageVersion;
|
|
387
403
|
|
|
388
404
|
module.exports.formatStep = formatStep;
|
|
389
405
|
|
package/lib/xmlReader.js
CHANGED
|
@@ -20,7 +20,7 @@ const uploader_js_1 = require("./uploader.js");
|
|
|
20
20
|
const debug = (0, debug_1.default)('@testomatio/reporter:xml');
|
|
21
21
|
const ridRunId = (0, crypto_1.randomUUID)();
|
|
22
22
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
23
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } = process.env;
|
|
23
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } = process.env;
|
|
24
24
|
const options = {
|
|
25
25
|
ignoreDeclaration: true,
|
|
26
26
|
ignoreAttributes: false,
|
|
@@ -28,6 +28,7 @@ const options = {
|
|
|
28
28
|
attributeNamePrefix: '',
|
|
29
29
|
parseTagValue: true,
|
|
30
30
|
};
|
|
31
|
+
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
31
32
|
const reduceOptions = {};
|
|
32
33
|
class XmlReader {
|
|
33
34
|
constructor(opts = {}) {
|
|
@@ -75,7 +76,7 @@ class XmlReader {
|
|
|
75
76
|
/(<system-out><!\[CDATA\[)([\s\S]*?)(\]\]><\/system-out>)/g,
|
|
76
77
|
];
|
|
77
78
|
for (const regex of cutRegexes) {
|
|
78
|
-
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0,
|
|
79
|
+
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
|
|
79
80
|
}
|
|
80
81
|
const jsonResult = this.parser.parse(xmlData);
|
|
81
82
|
let jsonSuite;
|
|
@@ -313,6 +314,8 @@ class XmlReader {
|
|
|
313
314
|
this.stats.language = 'js';
|
|
314
315
|
if (file.endsWith('.ts'))
|
|
315
316
|
this.stats.language = 'ts';
|
|
317
|
+
if (file.endsWith('.cs'))
|
|
318
|
+
this.stats.language = 'csharp';
|
|
316
319
|
}
|
|
317
320
|
if (!fs_1.default.existsSync(file)) {
|
|
318
321
|
debug('Failed to open file with the source code', file);
|
|
@@ -359,13 +362,13 @@ class XmlReader {
|
|
|
359
362
|
async uploadArtifacts() {
|
|
360
363
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
361
364
|
let files = [];
|
|
362
|
-
if (test.files?.length)
|
|
363
|
-
|
|
364
|
-
files =
|
|
365
|
+
if (!test.files?.length)
|
|
366
|
+
continue;
|
|
367
|
+
files = test.files.map(f => path_1.default.isAbsolute(f) ? f : path_1.default.join(process.cwd(), f));
|
|
365
368
|
if (!files.length)
|
|
366
369
|
continue;
|
|
367
370
|
const runId = this.runId || this.store.runId || Date.now().toString();
|
|
368
|
-
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId])));
|
|
371
|
+
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId, path_1.default.basename(f)])));
|
|
369
372
|
console.log(constants_js_1.APP_PREFIX, `🗄️ Uploaded ${picocolors_1.default.bold(`${files.length} artifacts`)} for test ${test.title}`);
|
|
370
373
|
}
|
|
371
374
|
}
|
|
@@ -425,7 +428,7 @@ function reduceTestCases(prev, item) {
|
|
|
425
428
|
testCases
|
|
426
429
|
.filter(t => !!t)
|
|
427
430
|
.forEach(testCaseItem => {
|
|
428
|
-
const file = testCaseItem.file || item.filepath || '';
|
|
431
|
+
const file = testCaseItem.file || item.filepath || item.fullname || '';
|
|
429
432
|
let stack = '';
|
|
430
433
|
let message = '';
|
|
431
434
|
if (testCaseItem.error)
|
|
@@ -458,15 +461,34 @@ function reduceTestCases(prev, item) {
|
|
|
458
461
|
title = title.replace(/\(.*?\)/, '').trim();
|
|
459
462
|
}
|
|
460
463
|
stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
461
|
-
|
|
464
|
+
let testId = (0, utils_js_1.fetchIdFromOutput)(stack);
|
|
465
|
+
if (tags?.length && !testId) {
|
|
466
|
+
testId = tags.filter(t => t.startsWith('T')).map(t => `@${t}`).find(t => t.match(utils_js_1.TEST_ID_REGEX))?.slice(2);
|
|
467
|
+
}
|
|
462
468
|
let status = constants_js_1.STATUS.PASSED.toString();
|
|
463
469
|
if ('failure' in testCaseItem || 'error' in testCaseItem)
|
|
464
470
|
status = constants_js_1.STATUS.FAILED;
|
|
465
471
|
if ('skipped' in testCaseItem)
|
|
466
472
|
status = constants_js_1.STATUS.SKIPPED;
|
|
473
|
+
if (testCaseItem.result && Object.values(constants_js_1.STATUS).includes(testCaseItem.result.toLowerCase())) {
|
|
474
|
+
status = testCaseItem.result.toLowerCase();
|
|
475
|
+
}
|
|
467
476
|
let rid = null;
|
|
468
477
|
if (testCaseItem.id)
|
|
469
478
|
rid = `${ridRunId}-${testCaseItem.id}`;
|
|
479
|
+
// Extract attachments
|
|
480
|
+
let files = [];
|
|
481
|
+
if (testCaseItem.attachments) {
|
|
482
|
+
const attachments = Array.isArray(testCaseItem.attachments.attachment)
|
|
483
|
+
? testCaseItem.attachments.attachment
|
|
484
|
+
: [testCaseItem.attachments.attachment];
|
|
485
|
+
files = attachments
|
|
486
|
+
.filter(a => a && a.filePath)
|
|
487
|
+
.map(a => a.filePath);
|
|
488
|
+
}
|
|
489
|
+
// Extract files from stack trace using existing utility
|
|
490
|
+
const stackFiles = (0, utils_js_1.fetchFilesFromStackTrace)(stack);
|
|
491
|
+
files = [...new Set([...files, ...stackFiles])]; // Remove duplicates
|
|
470
492
|
prev.push({
|
|
471
493
|
rid,
|
|
472
494
|
file,
|
|
@@ -481,7 +503,9 @@ function reduceTestCases(prev, item) {
|
|
|
481
503
|
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
482
504
|
status,
|
|
483
505
|
title,
|
|
506
|
+
root_suite_id: TESTOMATIO_SUITE,
|
|
484
507
|
suite_title: suiteTitle,
|
|
508
|
+
files,
|
|
485
509
|
});
|
|
486
510
|
});
|
|
487
511
|
return prev;
|
|
@@ -505,11 +529,14 @@ function fetchProperties(item) {
|
|
|
505
529
|
let title = '';
|
|
506
530
|
if (!item.properties)
|
|
507
531
|
return {};
|
|
508
|
-
|
|
532
|
+
// Handle both single property and array of properties
|
|
533
|
+
const properties = Array.isArray(item.properties.property)
|
|
534
|
+
? item.properties.property
|
|
535
|
+
: [item.properties.property].filter(Boolean);
|
|
536
|
+
const prop = properties.find(p => p.name === 'Description');
|
|
509
537
|
if (prop)
|
|
510
538
|
title = prop.value;
|
|
511
|
-
|
|
512
|
-
.flat()
|
|
539
|
+
properties
|
|
513
540
|
.filter(p => p.name === 'Category')
|
|
514
541
|
.forEach(p => tags.push(p.value));
|
|
515
542
|
return { title, tags };
|