@testomatio/reporter 2.3.7-beta.1-xml-import β 2.3.7-beta.11-stack-artifacts
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/adapter/codecept.js +22 -2
- package/lib/bin/cli.js +0 -0
- package/lib/bin/reportXml.js +1 -4
- package/lib/bin/startTest.js +0 -0
- package/lib/bin/uploadArtifacts.js +0 -0
- package/lib/client.js +30 -21
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +7 -36
- package/lib/pipe/debug.js +1 -1
- package/lib/pipe/testomatio.js +14 -19
- package/lib/reporter.d.ts +19 -9
- package/lib/reporter.js +40 -5
- package/lib/uploader.js +0 -4
- package/lib/utils/utils.d.ts +1 -0
- package/lib/utils/utils.js +23 -35
- package/lib/xmlReader.d.ts +26 -11
- package/lib/xmlReader.js +1 -50
- package/package.json +1 -1
- package/src/adapter/codecept.js +27 -3
- package/src/bin/reportXml.js +1 -4
- package/src/client.js +55 -27
- package/src/junit-adapter/csharp.js +6 -40
- package/src/pipe/debug.js +3 -2
- package/src/pipe/testomatio.js +80 -76
- package/src/reporter.js +7 -4
- package/src/uploader.js +0 -5
- package/src/utils/utils.js +21 -35
- package/src/xmlReader.js +1 -62
- package/lib/junit-adapter/nunit-parser.d.ts +0 -82
- package/lib/junit-adapter/nunit-parser.js +0 -357
- package/src/junit-adapter/nunit-parser.js +0 -391
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Testomat.io Reporter (this npm package) supports:
|
|
|
13
13
|
- π [Stack traces](./docs/stacktrace.md) and error messages
|
|
14
14
|
- π [GitHub](./docs/pipes/github.md), [GitLab](./docs/pipes/gitlab.md) & [Bitbucket](./docs/pipes/bitbucket.md) integration
|
|
15
15
|
- π
Realtime reports
|
|
16
|
-
- ποΈ Other test frameworks supported via [
|
|
16
|
+
- ποΈ Other test frameworks supported via [JUNit XML](./docs/junit.md)
|
|
17
17
|
- πΆββοΈ Steps _(work in progress)_
|
|
18
18
|
- π [Logger](./docs/logger.md) _(work in progress, supports Jest for now)_
|
|
19
19
|
- βοΈ Custom properties and metadata _(work in progress)_
|
package/lib/adapter/codecept.js
CHANGED
|
@@ -110,6 +110,25 @@ function CodeceptReporter(config) {
|
|
|
110
110
|
output.stepShift = 2;
|
|
111
111
|
index_js_1.services.setContext(null);
|
|
112
112
|
});
|
|
113
|
+
// mark as failed all tests inside the failed hook
|
|
114
|
+
event.dispatcher.on(event.hook.failed, hook => {
|
|
115
|
+
if (hook.name !== 'BeforeSuiteHook')
|
|
116
|
+
return;
|
|
117
|
+
const suite = hook.runnable.parent;
|
|
118
|
+
if (!suite)
|
|
119
|
+
return;
|
|
120
|
+
const error = hook?.ctx?.currentTest?.err;
|
|
121
|
+
for (const test of suite.tests) {
|
|
122
|
+
client.addTestRun('failed', {
|
|
123
|
+
...stripExampleFromTitle(test.title),
|
|
124
|
+
rid: test.uid,
|
|
125
|
+
test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(test.title),
|
|
126
|
+
suite_title: stripTagsFromTitle(suite.title),
|
|
127
|
+
error,
|
|
128
|
+
time: hook?.runnable?.duration,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
113
132
|
event.dispatcher.on(event.suite.before, suite => {
|
|
114
133
|
data_storage_js_1.dataStorage.setContext(suite.fullTitle());
|
|
115
134
|
});
|
|
@@ -379,7 +398,7 @@ function formatCodeceptStep(step) {
|
|
|
379
398
|
if (!step)
|
|
380
399
|
return null;
|
|
381
400
|
const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
|
|
382
|
-
const title =
|
|
401
|
+
const title = (0, utils_js_1.truncate)(step); // Use built-in toString
|
|
383
402
|
const duration = step.duration || 0; // Use built-in duration
|
|
384
403
|
const formattedStep = {
|
|
385
404
|
category,
|
|
@@ -403,10 +422,11 @@ function formatHookStep(step) {
|
|
|
403
422
|
if (step.actor && step.name) {
|
|
404
423
|
title = `${step.actor} ${step.name}`;
|
|
405
424
|
if (step.args && step.args.length > 0) {
|
|
406
|
-
const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
|
|
425
|
+
const argsStr = step.args.map(arg => (0, utils_js_1.truncate)(JSON.stringify(arg))).join(', ');
|
|
407
426
|
title += ` ${argsStr}`;
|
|
408
427
|
}
|
|
409
428
|
}
|
|
429
|
+
title = (0, utils_js_1.truncate)(title);
|
|
410
430
|
return {
|
|
411
431
|
category: 'hook',
|
|
412
432
|
title,
|
package/lib/bin/cli.js
CHANGED
|
File without changes
|
package/lib/bin/reportXml.js
CHANGED
|
@@ -37,10 +37,7 @@ program
|
|
|
37
37
|
lang = lang?.toLowerCase();
|
|
38
38
|
if (javaTests === true || (lang === 'java' && !javaTests))
|
|
39
39
|
javaTests = 'src/test/java';
|
|
40
|
-
const runReader = new xmlReader_js_1.default({
|
|
41
|
-
javaTests,
|
|
42
|
-
lang,
|
|
43
|
-
});
|
|
40
|
+
const runReader = new xmlReader_js_1.default({ javaTests, lang });
|
|
44
41
|
const files = glob_1.glob.sync(pattern, { cwd: opts.dir || process.cwd() });
|
|
45
42
|
if (!files.length) {
|
|
46
43
|
console.log(constants_js_1.APP_PREFIX, `Report can't be created. No XML files found π₯`);
|
package/lib/bin/startTest.js
CHANGED
|
File without changes
|
|
File without changes
|
package/lib/client.js
CHANGED
|
@@ -51,7 +51,9 @@ const node_url_1 = require("node:url");
|
|
|
51
51
|
const uploader_js_1 = require("./uploader.js");
|
|
52
52
|
const utils_js_1 = require("./utils/utils.js");
|
|
53
53
|
const filesize_1 = require("filesize");
|
|
54
|
+
const util_1 = require("util");
|
|
54
55
|
const debug = (0, debug_1.default)('@testomatio/reporter:client');
|
|
56
|
+
const stripColors = util_1.stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
|
|
55
57
|
// removed __dirname usage, because:
|
|
56
58
|
// 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
|
|
57
59
|
// 2. got error "__dirname already defined" in compiles js code (cjs dir)
|
|
@@ -158,17 +160,6 @@ class Client {
|
|
|
158
160
|
* @returns {Promise<PipeResult[]>}
|
|
159
161
|
*/
|
|
160
162
|
async addTestRun(status, testData) {
|
|
161
|
-
if (!this.pipes || !this.pipes.length)
|
|
162
|
-
this.pipes = await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
163
|
-
// all pipes disabled, skipping
|
|
164
|
-
if (!this.pipes?.filter(p => p.isEnabled).length)
|
|
165
|
-
return [];
|
|
166
|
-
if (isTestShouldBeExculedFromReport(testData))
|
|
167
|
-
return [];
|
|
168
|
-
if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
169
|
-
debug('Skipping test from report', testData?.title);
|
|
170
|
-
return []; // do not log skipped tests
|
|
171
|
-
}
|
|
172
163
|
if (!testData)
|
|
173
164
|
testData = {
|
|
174
165
|
title: 'Unknown test',
|
|
@@ -181,9 +172,12 @@ class Client {
|
|
|
181
172
|
/**
|
|
182
173
|
* @type {TestData}
|
|
183
174
|
*/
|
|
184
|
-
const { rid, error = null,
|
|
175
|
+
const { rid, error = null, steps: originalSteps, title, suite_title, } = testData;
|
|
176
|
+
let steps = originalSteps;
|
|
177
|
+
const uploadedFiles = [];
|
|
178
|
+
const stackArtifactsEnabled = (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_STACK_ARTIFACTS);
|
|
179
|
+
const { time = 0, example = null, files = [], filesBuffers = [], code = null, file, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, tags, } = testData;
|
|
185
180
|
let { message = '', meta = {} } = testData;
|
|
186
|
-
// stringify meta values and limit keys and values length to 255
|
|
187
181
|
meta = Object.entries(meta)
|
|
188
182
|
.filter(([, value]) => value !== null && value !== undefined)
|
|
189
183
|
.reduce((acc, [key, value]) => {
|
|
@@ -191,19 +185,34 @@ class Client {
|
|
|
191
185
|
acc[key] = value;
|
|
192
186
|
return acc;
|
|
193
187
|
}, {});
|
|
194
|
-
// Get links from storage using the test context
|
|
195
188
|
const testContext = suite_title ? `${suite_title} ${title}` : title;
|
|
196
189
|
let errorFormatted = '';
|
|
197
190
|
if (error) {
|
|
198
191
|
errorFormatted += this.formatError(error) || '';
|
|
199
192
|
message = error?.message;
|
|
200
193
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
let fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
|
|
195
|
+
if (stackArtifactsEnabled && fullLogs?.trim()?.length > 0) {
|
|
196
|
+
uploadedFiles.push(this.uploader.uploadFileAsBuffer(Buffer.from(stripColors(fullLogs), 'utf8'), [this.runId, rid, `logs_${+new Date}.log`]));
|
|
197
|
+
fullLogs = '';
|
|
198
|
+
steps = null;
|
|
199
|
+
}
|
|
200
|
+
if (!this.pipes || !this.pipes.length)
|
|
201
|
+
this.pipes = await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
202
|
+
if (!this.pipes?.filter(p => p.isEnabled).length) {
|
|
203
|
+
if (uploadedFiles.length > 0) {
|
|
204
|
+
await Promise.all(uploadedFiles);
|
|
205
|
+
}
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
if (isTestShouldBeExculedFromReport(testData))
|
|
209
|
+
return [];
|
|
210
|
+
if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
211
|
+
debug('Skipping test from report', testData?.title);
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
204
214
|
if (manuallyAttachedArtifacts?.length)
|
|
205
215
|
files.push(...manuallyAttachedArtifacts);
|
|
206
|
-
const uploadedFiles = [];
|
|
207
216
|
for (let f of files) {
|
|
208
217
|
if (!f)
|
|
209
218
|
continue; // f === null
|
|
@@ -285,7 +294,7 @@ class Client {
|
|
|
285
294
|
const uploadedArtifacts = this.uploader.successfulUploads.map(file => ({
|
|
286
295
|
relativePath: file.path.replace(process.cwd(), ''),
|
|
287
296
|
link: file.link,
|
|
288
|
-
sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
297
|
+
sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
289
298
|
}));
|
|
290
299
|
uploadedArtifacts.forEach(upload => {
|
|
291
300
|
debug(`π’Uploaded artifact`, `${upload.relativePath},`, 'size:', `${upload.sizePretty},`, 'link:', `${upload.link}`);
|
|
@@ -295,7 +304,7 @@ class Client {
|
|
|
295
304
|
console.log(constants_js_1.APP_PREFIX, `ποΈ ${this.uploader.failedUploads.length} artifacts π΄${picocolors_1.default.bold('failed')} to upload`);
|
|
296
305
|
const failedUploads = this.uploader.failedUploads.map(file => ({
|
|
297
306
|
relativePath: file.path.replace(process.cwd(), ''),
|
|
298
|
-
sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
307
|
+
sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
299
308
|
}));
|
|
300
309
|
const pathPadding = Math.max(...failedUploads.map(upload => upload.relativePath.length)) + 1;
|
|
301
310
|
failedUploads.forEach(upload => {
|
|
@@ -329,7 +338,7 @@ class Client {
|
|
|
329
338
|
*/
|
|
330
339
|
formatLogs({ error, steps, logs }) {
|
|
331
340
|
error = error?.trim();
|
|
332
|
-
logs = logs?.trim();
|
|
341
|
+
logs = logs?.trim().split('\n').map(l => (0, utils_js_1.truncate)(l)).join('\n');
|
|
333
342
|
if (Array.isArray(steps)) {
|
|
334
343
|
steps = steps
|
|
335
344
|
.map(step => (0, utils_js_1.formatStep)(step))
|
|
@@ -7,53 +7,24 @@ const path_1 = __importDefault(require("path"));
|
|
|
7
7
|
const adapter_js_1 = __importDefault(require("./adapter.js"));
|
|
8
8
|
class CSharpAdapter extends adapter_js_1.default {
|
|
9
9
|
formatTest(t) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
const exampleMatch = t.title.match(/\((.*?)\)/);
|
|
15
|
-
if (exampleMatch) {
|
|
16
|
-
// Keep as array for consistency with NUnit XML processing
|
|
17
|
-
t.example = exampleMatch[1].split(',').map(param => param.trim());
|
|
18
|
-
}
|
|
19
|
-
t.title = title.trim();
|
|
20
|
-
}
|
|
10
|
+
const title = t.title.replace(/\(.*?\)/, '').trim();
|
|
11
|
+
const example = t.title.match(/\((.*?)\)/);
|
|
12
|
+
if (example)
|
|
13
|
+
t.example = { ...example[1].split(',') };
|
|
21
14
|
const suite = t.suite_title.split('.');
|
|
22
15
|
t.suite_title = suite.pop();
|
|
23
16
|
t.file = namespaceToFileName(t.file);
|
|
17
|
+
t.title = title.trim();
|
|
24
18
|
return t;
|
|
25
19
|
}
|
|
26
20
|
getFilePath(t) {
|
|
27
|
-
|
|
28
|
-
return null;
|
|
29
|
-
// Normalize path separators for cross-platform compatibility
|
|
30
|
-
let filePath = t.file.replace(/\\/g, '/');
|
|
31
|
-
// If file already has .cs extension, use it directly
|
|
32
|
-
if (filePath.endsWith('.cs')) {
|
|
33
|
-
// Make relative path if it's absolute
|
|
34
|
-
if (path_1.default.isAbsolute(filePath)) {
|
|
35
|
-
// Try to find project-relative path
|
|
36
|
-
const cwd = process.cwd().replace(/\\/g, '/');
|
|
37
|
-
if (filePath.startsWith(cwd)) {
|
|
38
|
-
filePath = path_1.default.relative(cwd, filePath).replace(/\\/g, '/');
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return filePath;
|
|
42
|
-
}
|
|
43
|
-
// Convert namespace path to file path
|
|
44
|
-
const fileName = namespaceToFileName(filePath);
|
|
21
|
+
const fileName = namespaceToFileName(t.file);
|
|
45
22
|
return fileName;
|
|
46
23
|
}
|
|
47
24
|
}
|
|
48
25
|
module.exports = CSharpAdapter;
|
|
49
26
|
function namespaceToFileName(fileName) {
|
|
50
|
-
if (!fileName)
|
|
51
|
-
return '';
|
|
52
|
-
// If already a .cs file path, clean it up
|
|
53
|
-
if (fileName.endsWith('.cs')) {
|
|
54
|
-
return fileName.replace(/\\/g, '/');
|
|
55
|
-
}
|
|
56
27
|
const fileParts = fileName.split('.');
|
|
57
28
|
fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
|
|
58
|
-
return `${fileParts.join(
|
|
29
|
+
return `${fileParts.join(path_1.default.sep)}.cs`;
|
|
59
30
|
}
|
package/lib/pipe/debug.js
CHANGED
|
@@ -18,7 +18,7 @@ class DebugPipe {
|
|
|
18
18
|
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
19
19
|
if (this.isEnabled) {
|
|
20
20
|
this.batch = {
|
|
21
|
-
isEnabled: this.params.isBatchEnabled ??
|
|
21
|
+
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
22
22
|
intervalFunction: null,
|
|
23
23
|
intervalTime: 5000,
|
|
24
24
|
tests: [],
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -23,7 +23,7 @@ if (process.env.TESTOMATIO_RUN)
|
|
|
23
23
|
class TestomatioPipe {
|
|
24
24
|
constructor(params, store) {
|
|
25
25
|
this.batch = {
|
|
26
|
-
isEnabled: params?.isBatchEnabled ??
|
|
26
|
+
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
27
27
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
28
28
|
intervalTime: 5000, // how often tests are sent
|
|
29
29
|
tests: [], // array of tests in batch
|
|
@@ -60,7 +60,7 @@ class TestomatioPipe {
|
|
|
60
60
|
retry: constants_js_1.REPORTER_REQUEST_RETRIES.retriesPerRequest,
|
|
61
61
|
retryDelay: constants_js_1.REPORTER_REQUEST_RETRIES.retryTimeout,
|
|
62
62
|
httpMethodsToRetry: ['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE', 'POST'],
|
|
63
|
-
shouldRetry: error => {
|
|
63
|
+
shouldRetry: (error) => {
|
|
64
64
|
if (!error.response)
|
|
65
65
|
return false;
|
|
66
66
|
switch (error.response?.status) {
|
|
@@ -73,8 +73,8 @@ class TestomatioPipe {
|
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
76
|
-
}
|
|
77
|
-
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
78
|
});
|
|
79
79
|
this.isEnabled = true;
|
|
80
80
|
// do not finish this run (for parallel testing)
|
|
@@ -193,7 +193,7 @@ class TestomatioPipe {
|
|
|
193
193
|
method: 'PUT',
|
|
194
194
|
url: `/api/reporter/${this.runId}`,
|
|
195
195
|
data: runParams,
|
|
196
|
-
responseType: 'json'
|
|
196
|
+
responseType: 'json'
|
|
197
197
|
});
|
|
198
198
|
if (resp.data.artifacts)
|
|
199
199
|
(0, pipe_utils_js_1.setS3Credentials)(resp.data.artifacts);
|
|
@@ -206,7 +206,7 @@ class TestomatioPipe {
|
|
|
206
206
|
url: '/api/reporter',
|
|
207
207
|
data: runParams,
|
|
208
208
|
maxContentLength: Infinity,
|
|
209
|
-
responseType: 'json'
|
|
209
|
+
responseType: 'json'
|
|
210
210
|
});
|
|
211
211
|
this.runId = resp.data.uid;
|
|
212
212
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
@@ -259,17 +259,15 @@ class TestomatioPipe {
|
|
|
259
259
|
this.#formatData(data);
|
|
260
260
|
const json = json_cycle_1.default.stringify(data);
|
|
261
261
|
debug('Adding test', json);
|
|
262
|
-
return this.client
|
|
263
|
-
.request({
|
|
262
|
+
return this.client.request({
|
|
264
263
|
method: 'POST',
|
|
265
264
|
url: `/api/reporter/${this.runId}/testrun`,
|
|
266
265
|
data: json,
|
|
267
266
|
headers: {
|
|
268
267
|
'Content-Type': 'application/json',
|
|
269
268
|
},
|
|
270
|
-
maxContentLength: Infinity
|
|
271
|
-
})
|
|
272
|
-
.catch(err => {
|
|
269
|
+
maxContentLength: Infinity
|
|
270
|
+
}).catch(err => {
|
|
273
271
|
this.requestFailures++;
|
|
274
272
|
this.notReportedTestsCount++;
|
|
275
273
|
if (err.response) {
|
|
@@ -314,21 +312,19 @@ class TestomatioPipe {
|
|
|
314
312
|
// get tests from batch and clear batch
|
|
315
313
|
const testsToSend = this.batch.tests.splice(0);
|
|
316
314
|
debug('π¨ Batch upload', testsToSend.length, 'tests');
|
|
317
|
-
return this.client
|
|
318
|
-
.request({
|
|
315
|
+
return this.client.request({
|
|
319
316
|
method: 'POST',
|
|
320
317
|
url: `/api/reporter/${this.runId}/testrun`,
|
|
321
318
|
data: {
|
|
322
319
|
api_key: this.apiKey,
|
|
323
320
|
tests: testsToSend,
|
|
324
|
-
batch_index: this.batch.batchIndex
|
|
321
|
+
batch_index: this.batch.batchIndex
|
|
325
322
|
},
|
|
326
323
|
headers: {
|
|
327
324
|
'Content-Type': 'application/json',
|
|
328
325
|
},
|
|
329
|
-
maxContentLength: Infinity
|
|
330
|
-
})
|
|
331
|
-
.catch(err => {
|
|
326
|
+
maxContentLength: Infinity
|
|
327
|
+
}).catch(err => {
|
|
332
328
|
this.requestFailures++;
|
|
333
329
|
this.notReportedTestsCount += testsToSend.length;
|
|
334
330
|
if (err.response) {
|
|
@@ -412,9 +408,8 @@ class TestomatioPipe {
|
|
|
412
408
|
status_event,
|
|
413
409
|
detach: params.detach,
|
|
414
410
|
tests: params.tests,
|
|
415
|
-
}
|
|
411
|
+
}
|
|
416
412
|
});
|
|
417
|
-
debug(constants_js_1.APP_PREFIX, 'β
Testrun finished');
|
|
418
413
|
if (this.runUrl) {
|
|
419
414
|
console.log(constants_js_1.APP_PREFIX, 'π Report Saved. Report URL:', picocolors_1.default.magenta(this.runUrl));
|
|
420
415
|
}
|
package/lib/reporter.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export { Client };
|
|
2
|
+
export const STATUS: {
|
|
3
|
+
PASSED: string;
|
|
4
|
+
FAILED: string;
|
|
5
|
+
SKIPPED: string;
|
|
6
|
+
FINISHED: string;
|
|
7
|
+
};
|
|
1
8
|
export const artifact: (data: string | {
|
|
2
9
|
path: string;
|
|
3
10
|
type: string;
|
|
@@ -80,7 +87,7 @@ export const label: (key: string, value?: string | null) => void;
|
|
|
80
87
|
export const linkTest: (...testIds: string[]) => void;
|
|
81
88
|
export const linkJira: (...jiraIds: string[]) => void;
|
|
82
89
|
declare namespace _default {
|
|
83
|
-
let testomatioLogger: {
|
|
90
|
+
export let testomatioLogger: {
|
|
84
91
|
"__#13@#originalUserLogger": {
|
|
85
92
|
assert(condition?: boolean, ...data: any[]): void;
|
|
86
93
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
@@ -148,13 +155,13 @@ declare namespace _default {
|
|
|
148
155
|
}): void;
|
|
149
156
|
prettyObjects: boolean;
|
|
150
157
|
};
|
|
151
|
-
let artifact: (data: string | {
|
|
158
|
+
export let artifact: (data: string | {
|
|
152
159
|
path: string;
|
|
153
160
|
type: string;
|
|
154
161
|
name: string;
|
|
155
162
|
}, context?: any) => void;
|
|
156
|
-
let log: (...args: any[]) => void;
|
|
157
|
-
let logger: {
|
|
163
|
+
export let log: (...args: any[]) => void;
|
|
164
|
+
export let logger: {
|
|
158
165
|
"__#13@#originalUserLogger": {
|
|
159
166
|
assert(condition?: boolean, ...data: any[]): void;
|
|
160
167
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
@@ -222,13 +229,15 @@ declare namespace _default {
|
|
|
222
229
|
}): void;
|
|
223
230
|
prettyObjects: boolean;
|
|
224
231
|
};
|
|
225
|
-
let meta: (keyValue: {
|
|
232
|
+
export let meta: (keyValue: {
|
|
226
233
|
[key: string]: string;
|
|
227
234
|
} | string, value?: string | null) => void;
|
|
228
|
-
let step: (message: string) => void;
|
|
229
|
-
let label: (key: string, value?: string | null) => void;
|
|
230
|
-
let linkTest: (...testIds: string[]) => void;
|
|
231
|
-
let linkJira: (...jiraIds: string[]) => void;
|
|
235
|
+
export let step: (message: string) => void;
|
|
236
|
+
export let label: (key: string, value?: string | null) => void;
|
|
237
|
+
export let linkTest: (...testIds: string[]) => void;
|
|
238
|
+
export let linkJira: (...jiraIds: string[]) => void;
|
|
239
|
+
export { Client as TestomatioClient };
|
|
240
|
+
export { STATUS };
|
|
232
241
|
}
|
|
233
242
|
export default _default;
|
|
234
243
|
export type ArtifactFunction = typeof import("./reporter-functions.js").default.artifact;
|
|
@@ -237,3 +246,4 @@ export type LoggerService = typeof import("./services/index.js").services.logger
|
|
|
237
246
|
export type MetaFunction = typeof import("./reporter-functions.js").default.keyValue;
|
|
238
247
|
export type StepFunction = typeof import("./reporter-functions.js").default.step;
|
|
239
248
|
export type LabelFunction = typeof import("./reporter-functions.js").default.label;
|
|
249
|
+
import Client from './client.js';
|
package/lib/reporter.js
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.linkJira = exports.linkTest = exports.label = exports.step = exports.meta = exports.logger = exports.log = exports.artifact = void 0;
|
|
7
|
-
|
|
8
|
-
|
|
39
|
+
exports.linkJira = exports.linkTest = exports.label = exports.step = exports.meta = exports.logger = exports.log = exports.artifact = exports.STATUS = exports.Client = void 0;
|
|
40
|
+
const client_js_1 = __importDefault(require("./client.js"));
|
|
41
|
+
exports.Client = client_js_1.default;
|
|
42
|
+
const TestomatioConstants = __importStar(require("./constants.js"));
|
|
9
43
|
const index_js_1 = require("./services/index.js");
|
|
10
44
|
const reporter_functions_js_1 = __importDefault(require("./reporter-functions.js"));
|
|
45
|
+
exports.STATUS = TestomatioConstants.STATUS;
|
|
11
46
|
exports.artifact = reporter_functions_js_1.default.artifact;
|
|
12
47
|
exports.log = reporter_functions_js_1.default.log;
|
|
13
48
|
exports.logger = index_js_1.services.logger;
|
|
@@ -37,6 +72,6 @@ module.exports = {
|
|
|
37
72
|
label: reporter_functions_js_1.default.label,
|
|
38
73
|
linkTest: reporter_functions_js_1.default.linkTest,
|
|
39
74
|
linkJira: reporter_functions_js_1.default.linkJira,
|
|
40
|
-
|
|
41
|
-
|
|
75
|
+
TestomatioClient: client_js_1.default,
|
|
76
|
+
STATUS: exports.STATUS,
|
|
42
77
|
};
|
package/lib/uploader.js
CHANGED
|
@@ -170,10 +170,6 @@ class S3Uploader {
|
|
|
170
170
|
if (typeof filePath === 'string' && !path_1.default.isAbsolute(filePath)) {
|
|
171
171
|
filePath = path_1.default.join(process.cwd(), filePath);
|
|
172
172
|
}
|
|
173
|
-
// Normalize path separators for cross-platform compatibility
|
|
174
|
-
if (typeof filePath === 'string') {
|
|
175
|
-
filePath = filePath.replace(/\\/g, '/');
|
|
176
|
-
}
|
|
177
173
|
const data = { rid, file: filePath, uploaded };
|
|
178
174
|
const jsonLine = `${JSON.stringify(data)}\n`;
|
|
179
175
|
fs_1.default.appendFileSync(tempFilePath, jsonLine);
|
package/lib/utils/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export function getPackageVersion(): any;
|
|
|
2
2
|
export const TEST_ID_REGEX: RegExp;
|
|
3
3
|
export const SUITE_ID_REGEX: RegExp;
|
|
4
4
|
export function ansiRegExp(): RegExp;
|
|
5
|
+
export function truncate(s: any, size?: number): any;
|
|
5
6
|
export function cleanLatestRunId(): any;
|
|
6
7
|
export function isSameTest(test: any, t: any): boolean;
|
|
7
8
|
export function fetchSourceCode(contents: any, opts?: {}): string;
|
package/lib/utils/utils.js
CHANGED
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.validateSuiteId = 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.SUITE_ID_REGEX = exports.TEST_ID_REGEX = void 0;
|
|
40
40
|
exports.getPackageVersion = getPackageVersion;
|
|
41
|
+
exports.truncate = truncate;
|
|
41
42
|
exports.cleanLatestRunId = cleanLatestRunId;
|
|
42
43
|
exports.formatStep = formatStep;
|
|
43
44
|
exports.readLatestRunId = readLatestRunId;
|
|
@@ -121,8 +122,12 @@ const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
|
|
|
121
122
|
.map(f => f[1].trim())
|
|
122
123
|
.map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
|
|
123
124
|
.map(f => {
|
|
124
|
-
//
|
|
125
|
-
|
|
125
|
+
// Convert Windows paths to Linux paths for testing purposes
|
|
126
|
+
if (f.match(/^[A-Za-z]:[\\\/]/)) {
|
|
127
|
+
// Convert Windows path to Linux equivalent for test scenarios
|
|
128
|
+
return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
|
|
129
|
+
}
|
|
130
|
+
return f;
|
|
126
131
|
});
|
|
127
132
|
debug('Found files in stack trace: ', files);
|
|
128
133
|
return files.filter(f => {
|
|
@@ -169,8 +174,6 @@ exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
|
169
174
|
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
170
175
|
exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
171
176
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
172
|
-
if (!code)
|
|
173
|
-
return null;
|
|
174
177
|
const comments = code
|
|
175
178
|
.split('\n')
|
|
176
179
|
.map(l => l.trim())
|
|
@@ -213,29 +216,10 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
213
216
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
214
217
|
}
|
|
215
218
|
else if (opts.lang === 'csharp') {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (lineIndex === -1)
|
|
219
|
-
lineIndex = lines.findIndex(l => l.includes(`public async Task ${title}(`));
|
|
220
|
-
}
|
|
221
|
-
if (lineIndex === -1) {
|
|
219
|
+
if (lineIndex === -1)
|
|
220
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
221
|
+
if (lineIndex === -1)
|
|
222
222
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
223
|
-
}
|
|
224
|
-
// Look for TestCase or Test attributes above the method
|
|
225
|
-
if (lineIndex === -1) {
|
|
226
|
-
const testAttributeIndex = lines.findIndex((l, index) => {
|
|
227
|
-
if (l.includes('[TestCase') || l.includes('[Test')) {
|
|
228
|
-
// Check next few lines for the method
|
|
229
|
-
const nextLines = lines.slice(index, Math.min(lines.length, index + 5));
|
|
230
|
-
const hasMethod = nextLines.some(nextLine => nextLine.includes(`${title}(`));
|
|
231
|
-
return hasMethod;
|
|
232
|
-
}
|
|
233
|
-
return false;
|
|
234
|
-
});
|
|
235
|
-
if (testAttributeIndex !== -1) {
|
|
236
|
-
lineIndex = testAttributeIndex;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
223
|
}
|
|
240
224
|
else {
|
|
241
225
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
@@ -244,7 +228,7 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
244
228
|
if (opts.prepend) {
|
|
245
229
|
lineIndex -= opts.prepend;
|
|
246
230
|
}
|
|
247
|
-
if (lineIndex
|
|
231
|
+
if (lineIndex) {
|
|
248
232
|
const result = [];
|
|
249
233
|
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
250
234
|
if (lines[i] === undefined)
|
|
@@ -287,14 +271,6 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
287
271
|
break;
|
|
288
272
|
if (opts.lang === 'java' && lines[i].includes(' class '))
|
|
289
273
|
break;
|
|
290
|
-
if (opts.lang === 'csharp' && lines[i].trim().match(/^\[Test/))
|
|
291
|
-
break;
|
|
292
|
-
if (opts.lang === 'csharp' && lines[i].includes(' public void '))
|
|
293
|
-
break;
|
|
294
|
-
if (opts.lang === 'csharp' && lines[i].includes(' public async Task '))
|
|
295
|
-
break;
|
|
296
|
-
if (opts.lang === 'csharp' && lines[i].includes(' class ') && lines[i].includes('public'))
|
|
297
|
-
break;
|
|
298
274
|
}
|
|
299
275
|
result.push(lines[i]);
|
|
300
276
|
}
|
|
@@ -494,9 +470,21 @@ function transformEnvVarToBoolean(value) {
|
|
|
494
470
|
// if not recognized, return truthy if any value is set
|
|
495
471
|
return Boolean(value);
|
|
496
472
|
}
|
|
473
|
+
function truncate(s, size = 255) {
|
|
474
|
+
if (s === undefined || s === null) {
|
|
475
|
+
return '';
|
|
476
|
+
}
|
|
477
|
+
const str = s.toString();
|
|
478
|
+
if (str.trim().length < size) {
|
|
479
|
+
return str;
|
|
480
|
+
}
|
|
481
|
+
return `${str.substring(0, size)}...`;
|
|
482
|
+
}
|
|
497
483
|
|
|
498
484
|
module.exports.getPackageVersion = getPackageVersion;
|
|
499
485
|
|
|
486
|
+
module.exports.truncate = truncate;
|
|
487
|
+
|
|
500
488
|
module.exports.cleanLatestRunId = cleanLatestRunId;
|
|
501
489
|
|
|
502
490
|
module.exports.formatStep = formatStep;
|