@testomatio/reporter 2.7.5 → 2.7.8
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/constants.d.ts +2 -1
- package/lib/constants.js +10 -4
- package/lib/pipe/coverage.js +1 -1
- package/lib/pipe/testomatio.js +20 -17
- package/lib/xmlReader.js +25 -3
- package/package.json +1 -2
- package/src/constants.js +7 -2
- package/src/pipe/coverage.js +2 -2
- package/src/pipe/testomatio.js +28 -14
- package/src/xmlReader.js +27 -3
package/lib/constants.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export namespace HTML_REPORT {
|
|
|
15
15
|
let REPORT_DEFAULT_NAME: string;
|
|
16
16
|
let TEMPLATE_NAME: string;
|
|
17
17
|
}
|
|
18
|
-
export const
|
|
18
|
+
export const REQUEST_TIMEOUT: number;
|
|
19
|
+
export function getCreateRunRequestTimeout(): number;
|
|
19
20
|
export const testomatLogoURL: "https://avatars.githubusercontent.com/u/59105116?s=36&v=4";
|
|
20
21
|
export namespace REPORTER_REQUEST_RETRIES {
|
|
21
22
|
let retryTimeout: number;
|
package/lib/constants.js
CHANGED
|
@@ -3,7 +3,8 @@ 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
|
-
exports.SCREENSHOTS_ON_STEPS = exports.REPORTER_REQUEST_RETRIES = exports.testomatLogoURL = exports.
|
|
6
|
+
exports.SCREENSHOTS_ON_STEPS = exports.REPORTER_REQUEST_RETRIES = exports.testomatLogoURL = exports.REQUEST_TIMEOUT = exports.HTML_REPORT = exports.STATUS = exports.CSV_HEADERS = exports.TESTOMAT_TMP_STORAGE_DIR = exports.APP_PREFIX = void 0;
|
|
7
|
+
exports.getCreateRunRequestTimeout = getCreateRunRequestTimeout;
|
|
7
8
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
9
|
const os_1 = __importDefault(require("os"));
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
@@ -14,8 +15,8 @@ const TESTOMATIO_REQUEST_TIMEOUT = parseInt(process.env.TESTOMATIO_REQUEST_TIMEO
|
|
|
14
15
|
if (TESTOMATIO_REQUEST_TIMEOUT) {
|
|
15
16
|
console.log(`${APP_PREFIX} Request timeout is set to ${TESTOMATIO_REQUEST_TIMEOUT / 1000}s`);
|
|
16
17
|
}
|
|
17
|
-
const
|
|
18
|
-
exports.
|
|
18
|
+
const REQUEST_TIMEOUT = TESTOMATIO_REQUEST_TIMEOUT || 20 * 1000;
|
|
19
|
+
exports.REQUEST_TIMEOUT = REQUEST_TIMEOUT;
|
|
19
20
|
const SCREENSHOTS_ON_STEPS = process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS == null
|
|
20
21
|
|| (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS);
|
|
21
22
|
exports.SCREENSHOTS_ON_STEPS = SCREENSHOTS_ON_STEPS;
|
|
@@ -52,10 +53,15 @@ const REPORTER_REQUEST_RETRIES = {
|
|
|
52
53
|
withinTimeSeconds: Number(process.env.TESTOMATIO_MAX_REQUEST_RETRIES_WITHIN_TIME_SECONDS) || 60,
|
|
53
54
|
};
|
|
54
55
|
exports.REPORTER_REQUEST_RETRIES = REPORTER_REQUEST_RETRIES;
|
|
56
|
+
function getCreateRunRequestTimeout() {
|
|
57
|
+
return Math.max(REQUEST_TIMEOUT, 80 * 1000);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports.getCreateRunRequestTimeout = getCreateRunRequestTimeout;
|
|
55
61
|
|
|
56
62
|
module.exports.APP_PREFIX = APP_PREFIX;
|
|
57
63
|
|
|
58
|
-
module.exports.
|
|
64
|
+
module.exports.REQUEST_TIMEOUT = REQUEST_TIMEOUT;
|
|
59
65
|
|
|
60
66
|
module.exports.SCREENSHOTS_ON_STEPS = SCREENSHOTS_ON_STEPS;
|
|
61
67
|
|
package/lib/pipe/coverage.js
CHANGED
|
@@ -67,7 +67,7 @@ class CoveragePipe {
|
|
|
67
67
|
// Create a new instance of gaxios with a custom config
|
|
68
68
|
this.client = new gaxios_1.Gaxios({
|
|
69
69
|
baseURL: `${this.url.trim()}`,
|
|
70
|
-
timeout: constants_js_1.
|
|
70
|
+
timeout: constants_js_1.REQUEST_TIMEOUT,
|
|
71
71
|
proxy: proxy ? proxy.toString() : undefined,
|
|
72
72
|
retry: true,
|
|
73
73
|
retryConfig: {
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -70,7 +70,7 @@ class TestomatioPipe {
|
|
|
70
70
|
// Create a new instance of gaxios with a custom config
|
|
71
71
|
this.client = new gaxios_1.Gaxios({
|
|
72
72
|
baseURL: `${this.url.trim()}`,
|
|
73
|
-
timeout: constants_js_1.
|
|
73
|
+
timeout: constants_js_1.REQUEST_TIMEOUT,
|
|
74
74
|
proxy: proxy ? proxy.toString() : undefined,
|
|
75
75
|
retry: true,
|
|
76
76
|
retryConfig: {
|
|
@@ -225,6 +225,7 @@ class TestomatioPipe {
|
|
|
225
225
|
method: 'PUT',
|
|
226
226
|
url: `/api/reporter/${this.runId}`,
|
|
227
227
|
data: runParams,
|
|
228
|
+
timeout: (0, constants_js_1.getCreateRunRequestTimeout)(),
|
|
228
229
|
responseType: 'json',
|
|
229
230
|
});
|
|
230
231
|
if (resp.data.artifacts)
|
|
@@ -246,6 +247,7 @@ class TestomatioPipe {
|
|
|
246
247
|
method: 'POST',
|
|
247
248
|
url: '/api/reporter',
|
|
248
249
|
data: runParams,
|
|
250
|
+
timeout: (0, constants_js_1.getCreateRunRequestTimeout)(),
|
|
249
251
|
maxContentLength: Infinity,
|
|
250
252
|
responseType: 'json',
|
|
251
253
|
});
|
|
@@ -262,17 +264,14 @@ class TestomatioPipe {
|
|
|
262
264
|
debug('Run created', this.runId);
|
|
263
265
|
}
|
|
264
266
|
catch (err) {
|
|
267
|
+
if (!this.apiKey)
|
|
268
|
+
console.error('Testomat.io API key is not set');
|
|
265
269
|
const errorText = err.response?.data?.message || err.message;
|
|
266
270
|
debug('Error creating run', err);
|
|
267
|
-
console.log(errorText || err);
|
|
271
|
+
console.log(constants_js_1.APP_PREFIX, errorText || err);
|
|
268
272
|
if (err.response?.status === 403)
|
|
269
273
|
this.#disablePipe();
|
|
270
|
-
|
|
271
|
-
console.error('Testomat.io API key is not set');
|
|
272
|
-
if (!this.apiKey?.startsWith('tstmt'))
|
|
273
|
-
console.error('Testomat.io API key is invalid');
|
|
274
|
-
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG)
|
|
275
|
-
this.#logFailedResponse(err);
|
|
274
|
+
this.#logFailedResponse(err);
|
|
276
275
|
console.error(constants_js_1.APP_PREFIX, 'Error creating Testomat.io report (see details above), please check if your API key is valid. Skipping report');
|
|
277
276
|
printCreateIssue();
|
|
278
277
|
}
|
|
@@ -477,9 +476,8 @@ class TestomatioPipe {
|
|
|
477
476
|
}
|
|
478
477
|
}
|
|
479
478
|
catch (err) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.#logFailedResponse(err);
|
|
479
|
+
console.log(constants_js_1.APP_PREFIX, 'Error updating status, skipping...', err);
|
|
480
|
+
this.#logFailedResponse(err);
|
|
483
481
|
printCreateIssue();
|
|
484
482
|
}
|
|
485
483
|
debug('Run finished');
|
|
@@ -501,18 +499,23 @@ class TestomatioPipe {
|
|
|
501
499
|
responseBody = '<empty>';
|
|
502
500
|
responseBody = hideTestomatioToken(responseBody);
|
|
503
501
|
const statusCode = error.status || error.code || error.response?.status || '<unknown status code>';
|
|
504
|
-
const method = error.response?.config
|
|
505
|
-
const url = error.response?.config
|
|
506
|
-
let message = picocolors_1.default.yellow('
|
|
507
|
-
message += picocolors_1.default.bold(`${picocolors_1.default.red(statusCode)} ${method} ${url}\n`);
|
|
502
|
+
const method = error.response?.config?.method || '<unknown method>';
|
|
503
|
+
const url = String(error.response?.config?.url || '<unknown url>');
|
|
504
|
+
let message = picocolors_1.default.yellow('⚠️ Request to Testomat.io failed:\n');
|
|
505
|
+
message += picocolors_1.default.bold(`${picocolors_1.default.red(statusCode)} ${method} ${picocolors_1.default.gray(url)}\n`);
|
|
506
|
+
if (statusCode === 403) {
|
|
507
|
+
message += `\t${picocolors_1.default.red('Please check your API token. It might be invalid or expired.')}\n`;
|
|
508
|
+
}
|
|
508
509
|
message += `\t${picocolors_1.default.bold('response: ')}${picocolors_1.default.gray(responseBody)}\n`;
|
|
509
510
|
const requestBody = hideTestomatioToken(stringify(error.response?.config?.data));
|
|
510
|
-
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG) {
|
|
511
|
+
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG || requestBody.length < 1000) {
|
|
512
|
+
// full body
|
|
511
513
|
message += `\t${picocolors_1.default.bold('request: ')}${picocolors_1.default.gray(requestBody)}\n`;
|
|
512
514
|
}
|
|
513
515
|
else {
|
|
516
|
+
// cut body
|
|
514
517
|
const requestBodyCut = requestBody.slice(0, 1000);
|
|
515
|
-
message += `\t${picocolors_1.default.bold('request: ')}${picocolors_1.default.gray(`${requestBodyCut}
|
|
518
|
+
message += `\t${picocolors_1.default.bold('request: ')}${picocolors_1.default.gray(`${requestBodyCut}...`)}\n`;
|
|
516
519
|
message += '\trequest body is cut, run with TESTOMATIO_DEBUG=1 to see full body\n';
|
|
517
520
|
}
|
|
518
521
|
console.log(message);
|
package/lib/xmlReader.js
CHANGED
|
@@ -22,15 +22,25 @@ const log_js_1 = require("./utils/log.js");
|
|
|
22
22
|
const debug = (0, debug_1.default)('@testomatio/reporter:xml');
|
|
23
23
|
const ridRunId = (0, crypto_1.randomUUID)();
|
|
24
24
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
25
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED, TESTOMATIO_LEGACY_NUNIT, } = process.env;
|
|
25
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED, TESTOMATIO_LEGACY_NUNIT, TESTOMATIO_MAX_ENTITY_EXPANSIONS, } = process.env;
|
|
26
|
+
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
27
|
+
const MAX_ENTITY_EXPANSIONS = parseInt(TESTOMATIO_MAX_ENTITY_EXPANSIONS, 10) || 10000;
|
|
28
|
+
const ENTITY_EXPANSION_LIMIT_REGEXP = /Entity expansion limit exceeded/i;
|
|
26
29
|
const options = {
|
|
27
30
|
ignoreDeclaration: true,
|
|
28
31
|
ignoreAttributes: false,
|
|
29
32
|
alwaysCreateTextNode: false,
|
|
30
33
|
attributeNamePrefix: '',
|
|
31
34
|
parseTagValue: true,
|
|
35
|
+
processEntities: {
|
|
36
|
+
enabled: true,
|
|
37
|
+
maxEntitySize: 10000,
|
|
38
|
+
maxExpansionDepth: 10,
|
|
39
|
+
maxTotalExpansions: MAX_ENTITY_EXPANSIONS,
|
|
40
|
+
maxExpandedLength: 100000,
|
|
41
|
+
maxEntityCount: 10000,
|
|
42
|
+
},
|
|
32
43
|
};
|
|
33
|
-
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
34
44
|
const reduceOptions = {};
|
|
35
45
|
class XmlReader {
|
|
36
46
|
constructor(opts = {}) {
|
|
@@ -84,7 +94,19 @@ class XmlReader {
|
|
|
84
94
|
for (const regex of cutRegexes) {
|
|
85
95
|
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
|
|
86
96
|
}
|
|
87
|
-
|
|
97
|
+
let jsonResult;
|
|
98
|
+
try {
|
|
99
|
+
jsonResult = this.parser.parse(xmlData);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (ENTITY_EXPANSION_LIMIT_REGEXP.test(error.message)) {
|
|
103
|
+
throw new Error(`${error.message}\n\n` +
|
|
104
|
+
`XML report contains more entity references than the current limit (${MAX_ENTITY_EXPANSIONS}). ` +
|
|
105
|
+
'If this XML report is trusted, increase the limit with TESTOMATIO_MAX_ENTITY_EXPANSIONS, for example:\n' +
|
|
106
|
+
`TESTOMATIO_MAX_ENTITY_EXPANSIONS=${MAX_ENTITY_EXPANSIONS * 2} npx report-xml "{pattern}" --lang={lang}`);
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
88
110
|
let jsonSuite;
|
|
89
111
|
if (jsonResult.testsuites) {
|
|
90
112
|
jsonSuite = jsonResult.testsuites;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.8",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18"
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
18
18
|
"@cucumber/cucumber": "^10.9.0",
|
|
19
19
|
"@octokit/rest": "^21.1.1",
|
|
20
|
-
"aws-sdk": "^2.1072.0",
|
|
21
20
|
"callsite-record": "^4.1.4",
|
|
22
21
|
"commander": "^12",
|
|
23
22
|
"cross-spawn": "^7.0.3",
|
package/src/constants.js
CHANGED
|
@@ -8,7 +8,7 @@ const TESTOMATIO_REQUEST_TIMEOUT = parseInt(process.env.TESTOMATIO_REQUEST_TIMEO
|
|
|
8
8
|
if (TESTOMATIO_REQUEST_TIMEOUT) {
|
|
9
9
|
console.log(`${APP_PREFIX} Request timeout is set to ${TESTOMATIO_REQUEST_TIMEOUT / 1000}s`);
|
|
10
10
|
}
|
|
11
|
-
const
|
|
11
|
+
const REQUEST_TIMEOUT = TESTOMATIO_REQUEST_TIMEOUT || 20 * 1000;
|
|
12
12
|
const SCREENSHOTS_ON_STEPS = process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS == null
|
|
13
13
|
|| transformEnvVarToBoolean(process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS);
|
|
14
14
|
|
|
@@ -44,13 +44,18 @@ const REPORTER_REQUEST_RETRIES = {
|
|
|
44
44
|
withinTimeSeconds: Number(process.env.TESTOMATIO_MAX_REQUEST_RETRIES_WITHIN_TIME_SECONDS) || 60,
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
function getCreateRunRequestTimeout() {
|
|
48
|
+
return Math.max(REQUEST_TIMEOUT, 80 * 1000);
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
export {
|
|
48
52
|
APP_PREFIX,
|
|
49
53
|
TESTOMAT_TMP_STORAGE_DIR,
|
|
50
54
|
CSV_HEADERS,
|
|
51
55
|
STATUS,
|
|
52
56
|
HTML_REPORT,
|
|
53
|
-
|
|
57
|
+
REQUEST_TIMEOUT,
|
|
58
|
+
getCreateRunRequestTimeout,
|
|
54
59
|
testomatLogoURL,
|
|
55
60
|
REPORTER_REQUEST_RETRIES,
|
|
56
61
|
SCREENSHOTS_ON_STEPS,
|
package/src/pipe/coverage.js
CHANGED
|
@@ -4,7 +4,7 @@ import yaml from 'js-yaml';
|
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
5
|
import { Gaxios } from 'gaxios';
|
|
6
6
|
import { minimatch } from 'minimatch';
|
|
7
|
-
import { APP_PREFIX,
|
|
7
|
+
import { APP_PREFIX, REQUEST_TIMEOUT, REPORTER_REQUEST_RETRIES } from '../constants.js';
|
|
8
8
|
import { generateFilterRequestParams } from '../utils/pipe_utils.js';
|
|
9
9
|
import { parsePipeOptions } from '../utils/pipe_utils.js';
|
|
10
10
|
import { config } from '../config.js';
|
|
@@ -76,7 +76,7 @@ class CoveragePipe { // or Changes for the future???
|
|
|
76
76
|
// Create a new instance of gaxios with a custom config
|
|
77
77
|
this.client = new Gaxios({
|
|
78
78
|
baseURL: `${this.url.trim()}`,
|
|
79
|
-
timeout:
|
|
79
|
+
timeout: REQUEST_TIMEOUT,
|
|
80
80
|
proxy: proxy ? proxy.toString() : undefined,
|
|
81
81
|
retry: true,
|
|
82
82
|
retryConfig: {
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -2,7 +2,13 @@ import createDebugMessages from 'debug';
|
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { Gaxios } from 'gaxios';
|
|
4
4
|
import JsonCycle from 'json-cycle';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
APP_PREFIX,
|
|
7
|
+
STATUS,
|
|
8
|
+
REQUEST_TIMEOUT,
|
|
9
|
+
getCreateRunRequestTimeout,
|
|
10
|
+
REPORTER_REQUEST_RETRIES,
|
|
11
|
+
} from '../constants.js';
|
|
6
12
|
import {
|
|
7
13
|
isValidUrl,
|
|
8
14
|
foundedTestLog,
|
|
@@ -81,7 +87,7 @@ class TestomatioPipe {
|
|
|
81
87
|
// Create a new instance of gaxios with a custom config
|
|
82
88
|
this.client = new Gaxios({
|
|
83
89
|
baseURL: `${this.url.trim()}`,
|
|
84
|
-
timeout:
|
|
90
|
+
timeout: REQUEST_TIMEOUT,
|
|
85
91
|
proxy: proxy ? proxy.toString() : undefined,
|
|
86
92
|
retry: true,
|
|
87
93
|
retryConfig: {
|
|
@@ -256,6 +262,7 @@ class TestomatioPipe {
|
|
|
256
262
|
method: 'PUT',
|
|
257
263
|
url: `/api/reporter/${this.runId}`,
|
|
258
264
|
data: runParams,
|
|
265
|
+
timeout: getCreateRunRequestTimeout(),
|
|
259
266
|
responseType: 'json',
|
|
260
267
|
});
|
|
261
268
|
if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
|
|
@@ -277,6 +284,7 @@ class TestomatioPipe {
|
|
|
277
284
|
method: 'POST',
|
|
278
285
|
url: '/api/reporter',
|
|
279
286
|
data: runParams,
|
|
287
|
+
timeout: getCreateRunRequestTimeout(),
|
|
280
288
|
maxContentLength: Infinity,
|
|
281
289
|
responseType: 'json',
|
|
282
290
|
});
|
|
@@ -294,14 +302,13 @@ class TestomatioPipe {
|
|
|
294
302
|
process.env.runId = this.runId;
|
|
295
303
|
debug('Run created', this.runId);
|
|
296
304
|
} catch (err) {
|
|
305
|
+
if (!this.apiKey) console.error('Testomat.io API key is not set');
|
|
297
306
|
const errorText = err.response?.data?.message || err.message;
|
|
298
307
|
debug('Error creating run', err);
|
|
299
|
-
console.log(errorText || err);
|
|
308
|
+
console.log(APP_PREFIX, errorText || err);
|
|
300
309
|
if (err.response?.status === 403) this.#disablePipe();
|
|
301
|
-
if (!this.apiKey) console.error('Testomat.io API key is not set');
|
|
302
|
-
if (!this.apiKey?.startsWith('tstmt')) console.error('Testomat.io API key is invalid');
|
|
303
310
|
|
|
304
|
-
|
|
311
|
+
this.#logFailedResponse(err);
|
|
305
312
|
|
|
306
313
|
console.error(
|
|
307
314
|
APP_PREFIX,
|
|
@@ -532,8 +539,8 @@ class TestomatioPipe {
|
|
|
532
539
|
);
|
|
533
540
|
}
|
|
534
541
|
} catch (err) {
|
|
535
|
-
log
|
|
536
|
-
|
|
542
|
+
console.log(APP_PREFIX, 'Error updating status, skipping...', err);
|
|
543
|
+
this.#logFailedResponse(err);
|
|
537
544
|
printCreateIssue();
|
|
538
545
|
}
|
|
539
546
|
debug('Run finished');
|
|
@@ -558,19 +565,26 @@ class TestomatioPipe {
|
|
|
558
565
|
responseBody = hideTestomatioToken(responseBody);
|
|
559
566
|
|
|
560
567
|
const statusCode = error.status || error.code || error.response?.status || '<unknown status code>';
|
|
561
|
-
const method = error.response?.config
|
|
562
|
-
const url = error.response?.config
|
|
568
|
+
const method = error.response?.config?.method || '<unknown method>';
|
|
569
|
+
const url = String(error.response?.config?.url || '<unknown url>');
|
|
570
|
+
|
|
571
|
+
let message = pc.yellow('⚠️ Request to Testomat.io failed:\n');
|
|
572
|
+
message += pc.bold(`${pc.red(statusCode)} ${method} ${pc.gray(url)}\n`);
|
|
573
|
+
|
|
574
|
+
if (statusCode === 403) {
|
|
575
|
+
message += `\t${pc.red('Please check your API token. It might be invalid or expired.')}\n`;
|
|
576
|
+
}
|
|
563
577
|
|
|
564
|
-
let message = pc.yellow('\n⚠️ Request to Testomat.io failed:\n');
|
|
565
|
-
message += pc.bold(`${pc.red(statusCode)} ${method} ${url}\n`);
|
|
566
578
|
message += `\t${pc.bold('response: ')}${pc.gray(responseBody)}\n`;
|
|
567
579
|
|
|
568
580
|
const requestBody = hideTestomatioToken(stringify(error.response?.config?.data));
|
|
569
|
-
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG) {
|
|
581
|
+
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG || requestBody.length < 1000) {
|
|
582
|
+
// full body
|
|
570
583
|
message += `\t${pc.bold('request: ')}${pc.gray(requestBody)}\n`;
|
|
571
584
|
} else {
|
|
585
|
+
// cut body
|
|
572
586
|
const requestBodyCut = requestBody.slice(0, 1000);
|
|
573
|
-
message += `\t${pc.bold('request: ')}${pc.gray(`${requestBodyCut}
|
|
587
|
+
message += `\t${pc.bold('request: ')}${pc.gray(`${requestBodyCut}...`)}\n`;
|
|
574
588
|
message += '\trequest body is cut, run with TESTOMATIO_DEBUG=1 to see full body\n';
|
|
575
589
|
}
|
|
576
590
|
|
package/src/xmlReader.js
CHANGED
|
@@ -39,18 +39,29 @@ const {
|
|
|
39
39
|
TESTOMATIO_RUN,
|
|
40
40
|
TESTOMATIO_MARK_DETACHED,
|
|
41
41
|
TESTOMATIO_LEGACY_NUNIT,
|
|
42
|
+
TESTOMATIO_MAX_ENTITY_EXPANSIONS,
|
|
42
43
|
} = process.env;
|
|
43
44
|
|
|
45
|
+
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
46
|
+
const MAX_ENTITY_EXPANSIONS = parseInt(TESTOMATIO_MAX_ENTITY_EXPANSIONS, 10) || 10000;
|
|
47
|
+
const ENTITY_EXPANSION_LIMIT_REGEXP = /Entity expansion limit exceeded/i;
|
|
48
|
+
|
|
44
49
|
const options = {
|
|
45
50
|
ignoreDeclaration: true,
|
|
46
51
|
ignoreAttributes: false,
|
|
47
52
|
alwaysCreateTextNode: false,
|
|
48
53
|
attributeNamePrefix: '',
|
|
49
54
|
parseTagValue: true,
|
|
55
|
+
processEntities: {
|
|
56
|
+
enabled: true,
|
|
57
|
+
maxEntitySize: 10000,
|
|
58
|
+
maxExpansionDepth: 10,
|
|
59
|
+
maxTotalExpansions: MAX_ENTITY_EXPANSIONS,
|
|
60
|
+
maxExpandedLength: 100000,
|
|
61
|
+
maxEntityCount: 10000,
|
|
62
|
+
},
|
|
50
63
|
};
|
|
51
64
|
|
|
52
|
-
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
53
|
-
|
|
54
65
|
const reduceOptions = {};
|
|
55
66
|
|
|
56
67
|
class XmlReader {
|
|
@@ -113,7 +124,20 @@ class XmlReader {
|
|
|
113
124
|
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
|
|
114
125
|
}
|
|
115
126
|
|
|
116
|
-
|
|
127
|
+
let jsonResult;
|
|
128
|
+
try {
|
|
129
|
+
jsonResult = this.parser.parse(xmlData);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (ENTITY_EXPANSION_LIMIT_REGEXP.test(error.message)) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`${error.message}\n\n` +
|
|
134
|
+
`XML report contains more entity references than the current limit (${MAX_ENTITY_EXPANSIONS}). ` +
|
|
135
|
+
'If this XML report is trusted, increase the limit with TESTOMATIO_MAX_ENTITY_EXPANSIONS, for example:\n' +
|
|
136
|
+
`TESTOMATIO_MAX_ENTITY_EXPANSIONS=${MAX_ENTITY_EXPANSIONS * 2} npx report-xml "{pattern}" --lang={lang}`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
117
141
|
let jsonSuite;
|
|
118
142
|
|
|
119
143
|
if (jsonResult.testsuites) {
|