@testomatio/reporter 1.2.0-beta-4 → 1.2.0
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 +27 -18
- package/lib/adapter/codecept.js +83 -11
- package/lib/adapter/cucumber/current.js +17 -10
- package/lib/adapter/cucumber/legacy.js +4 -3
- package/lib/adapter/cypress-plugin/index.js +51 -24
- package/lib/adapter/jest.js +47 -23
- package/lib/adapter/mocha.js +99 -45
- package/lib/adapter/playwright.js +94 -32
- package/lib/bin/startTest.js +8 -7
- package/lib/client.js +119 -62
- package/lib/config.js +34 -0
- package/lib/constants.js +9 -7
- package/lib/data-storage.js +203 -0
- package/lib/fileUploader.js +125 -46
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/gitlab.js +1 -1
- package/lib/pipe/html.js +248 -246
- package/lib/pipe/testomatio.js +73 -50
- package/lib/reporter-functions.js +46 -0
- package/lib/reporter.js +11 -10
- package/lib/services/artifacts.js +57 -0
- package/lib/services/index.js +13 -0
- package/lib/services/key-values.js +58 -0
- package/lib/{logger.js → services/logger.js} +58 -66
- package/lib/utils/utils.js +28 -2
- package/lib/xmlReader.js +106 -105
- package/package.json +5 -3
- package/lib/_ArtifactStorageOld.js +0 -142
- package/lib/artifactStorage.js +0 -25
- package/lib/dataStorage.js +0 -232
package/lib/xmlReader.js
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:xml');
|
|
2
|
-
const path = require(
|
|
2
|
+
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const fs = require(
|
|
5
|
-
const { XMLParser } = require(
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('./constants');
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const {
|
|
8
|
+
fetchFilesFromStackTrace,
|
|
9
|
+
fetchIdFromOutput,
|
|
10
|
+
fetchSourceCode,
|
|
11
|
+
fetchSourceCodeFromStackTrace,
|
|
12
|
+
fetchIdFromCode,
|
|
13
|
+
humanize,
|
|
14
|
+
} = require('./utils/utils');
|
|
14
15
|
const upload = require('./fileUploader');
|
|
15
16
|
const pipesFactory = require('./pipe');
|
|
16
17
|
const adapterFactory = require('./junit-adapter');
|
|
18
|
+
const config = require('./config');
|
|
17
19
|
|
|
18
|
-
const TESTOMATIO_URL = process.env.TESTOMATIO_URL ||
|
|
19
|
-
const TESTOMATIO = process.env.TESTOMATIO; // key?
|
|
20
|
+
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
20
21
|
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
21
22
|
|
|
22
23
|
const options = {
|
|
23
24
|
ignoreDeclaration: true,
|
|
24
25
|
ignoreAttributes: false,
|
|
25
26
|
alwaysCreateTextNode: false,
|
|
26
|
-
attributeNamePrefix:
|
|
27
|
+
attributeNamePrefix: '',
|
|
27
28
|
parseTagValue: true,
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
const reduceOptions = {};
|
|
31
32
|
|
|
32
33
|
class XmlReader {
|
|
33
|
-
|
|
34
34
|
constructor(opts = {}) {
|
|
35
35
|
this.requestParams = {
|
|
36
|
-
apiKey: opts.apiKey || TESTOMATIO,
|
|
36
|
+
apiKey: opts.apiKey || config.TESTOMATIO,
|
|
37
37
|
url: opts.url || TESTOMATIO_URL,
|
|
38
38
|
title: TESTOMATIO_TITLE,
|
|
39
39
|
env: TESTOMATIO_ENV,
|
|
40
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
41
41
|
};
|
|
42
42
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
43
|
-
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
|
|
43
|
+
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
44
44
|
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
45
45
|
|
|
46
46
|
this.opts = opts || {};
|
|
47
|
-
this.store = {}
|
|
47
|
+
this.store = {};
|
|
48
48
|
this.pipes = pipesFactory(opts, this.store);
|
|
49
49
|
|
|
50
50
|
this.parser = new XMLParser(options);
|
|
51
|
-
this.tests = []
|
|
52
|
-
this.stats = {}
|
|
51
|
+
this.tests = [];
|
|
52
|
+
this.stats = {};
|
|
53
53
|
this.stats.language = opts.lang?.toLowerCase();
|
|
54
|
-
this.filesToUpload = {}
|
|
54
|
+
this.filesToUpload = {};
|
|
55
55
|
|
|
56
56
|
this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
|
|
57
57
|
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
@@ -82,8 +82,8 @@ class XmlReader {
|
|
|
82
82
|
} else if (jsonResult.assemblies) {
|
|
83
83
|
return this.processXUnit(jsonResult.assemblies);
|
|
84
84
|
} else {
|
|
85
|
-
console.log(jsonResult)
|
|
86
|
-
throw new Error("Format can't be parsed")
|
|
85
|
+
console.log(jsonResult);
|
|
86
|
+
throw new Error("Format can't be parsed");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
return this.processJUnit(jsonSuite);
|
|
@@ -96,7 +96,7 @@ class XmlReader {
|
|
|
96
96
|
const resultTests = processTestSuite(testsuite);
|
|
97
97
|
|
|
98
98
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
99
|
-
const status =
|
|
99
|
+
const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
|
|
100
100
|
|
|
101
101
|
this.tests = this.tests.concat(resultTests);
|
|
102
102
|
|
|
@@ -135,21 +135,22 @@ class XmlReader {
|
|
|
135
135
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
136
136
|
if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
|
|
137
137
|
|
|
138
|
-
const tests =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
const tests =
|
|
139
|
+
defs.map(td => {
|
|
140
|
+
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
141
|
+
let example = td.name.match(/\((.*?)\)/);
|
|
142
|
+
if (example) example = { ...example[1].split(',') };
|
|
143
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
144
|
+
const suite_title = suite.pop();
|
|
145
|
+
return {
|
|
146
|
+
title,
|
|
147
|
+
example,
|
|
148
|
+
file: suite.join('/'),
|
|
149
|
+
description: td.Description,
|
|
150
|
+
suite_title,
|
|
151
|
+
id: td.Execution.id,
|
|
152
|
+
};
|
|
153
|
+
}) || [];
|
|
153
154
|
|
|
154
155
|
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
155
156
|
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
@@ -160,10 +161,9 @@ class XmlReader {
|
|
|
160
161
|
run_time: parseFloat(td.duration) * 1000,
|
|
161
162
|
status: td.outcome,
|
|
162
163
|
stack: td.Output.StdOut,
|
|
163
|
-
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
164
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
164
165
|
}));
|
|
165
166
|
|
|
166
|
-
|
|
167
167
|
results.forEach(r => {
|
|
168
168
|
const test = tests.find(t => t.id === r.id) || {};
|
|
169
169
|
r.suite_title = test.suite_title;
|
|
@@ -223,7 +223,7 @@ class XmlReader {
|
|
|
223
223
|
|
|
224
224
|
if (testCase.failure) {
|
|
225
225
|
message = testCase.failure.message;
|
|
226
|
-
stack = testCase.failure['stack-trace']
|
|
226
|
+
stack = testCase.failure['stack-trace'];
|
|
227
227
|
}
|
|
228
228
|
if (testCase.reason) {
|
|
229
229
|
message = testCase.reason.message;
|
|
@@ -250,7 +250,6 @@ class XmlReader {
|
|
|
250
250
|
suite_title,
|
|
251
251
|
run_time,
|
|
252
252
|
});
|
|
253
|
-
|
|
254
253
|
});
|
|
255
254
|
});
|
|
256
255
|
});
|
|
@@ -283,12 +282,12 @@ class XmlReader {
|
|
|
283
282
|
passed_count: 0,
|
|
284
283
|
failed_count: 0,
|
|
285
284
|
skipped_count: 0,
|
|
286
|
-
}
|
|
285
|
+
};
|
|
287
286
|
this.tests.forEach(t => {
|
|
288
287
|
this.stats.tests_count++;
|
|
289
288
|
if (t.status === 'passed') this.stats.passed_count++;
|
|
290
289
|
if (t.status === 'failed') this.stats.failed_count++;
|
|
291
|
-
})
|
|
290
|
+
});
|
|
292
291
|
if (this.stats.failed_count) this.stats.status = 'failed';
|
|
293
292
|
|
|
294
293
|
return this.stats;
|
|
@@ -297,7 +296,7 @@ class XmlReader {
|
|
|
297
296
|
fetchSourceCode() {
|
|
298
297
|
this.tests.forEach(t => {
|
|
299
298
|
try {
|
|
300
|
-
const file = this.adapter.getFilePath(t)
|
|
299
|
+
const file = this.adapter.getFilePath(t);
|
|
301
300
|
if (!file) return;
|
|
302
301
|
|
|
303
302
|
if (!this.stats.language) {
|
|
@@ -310,16 +309,16 @@ class XmlReader {
|
|
|
310
309
|
}
|
|
311
310
|
|
|
312
311
|
if (!fs.existsSync(file)) {
|
|
313
|
-
debug('Failed to open file with the source code', file)
|
|
312
|
+
debug('Failed to open file with the source code', file);
|
|
314
313
|
return;
|
|
315
314
|
}
|
|
316
315
|
const contents = fs.readFileSync(file).toString();
|
|
317
|
-
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
|
|
316
|
+
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language });
|
|
318
317
|
if (t.code) debug('Fetched code for test %s', t.title);
|
|
319
|
-
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language })
|
|
318
|
+
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language });
|
|
320
319
|
if (t.test_id) debug('Fetched test id %s for test %s', t.test_id, t.title);
|
|
321
320
|
} catch (err) {
|
|
322
|
-
debug(err)
|
|
321
|
+
debug(err);
|
|
323
322
|
}
|
|
324
323
|
});
|
|
325
324
|
}
|
|
@@ -327,21 +326,22 @@ class XmlReader {
|
|
|
327
326
|
formatTests() {
|
|
328
327
|
this.tests.forEach(t => {
|
|
329
328
|
if (t.file) {
|
|
330
|
-
t.file = t.file.replace(process.cwd() + path.sep, '')
|
|
329
|
+
t.file = t.file.replace(process.cwd() + path.sep, '');
|
|
331
330
|
}
|
|
332
331
|
|
|
333
|
-
this.adapter.formatTest(t)
|
|
332
|
+
this.adapter.formatTest(t);
|
|
334
333
|
|
|
335
334
|
t.title = humanize(t.title);
|
|
336
335
|
});
|
|
337
336
|
}
|
|
338
337
|
|
|
339
338
|
formatErrors() {
|
|
340
|
-
this.tests
|
|
341
|
-
t
|
|
342
|
-
t
|
|
343
|
-
|
|
344
|
-
|
|
339
|
+
this.tests
|
|
340
|
+
.filter(t => !!t.stack)
|
|
341
|
+
.forEach(t => {
|
|
342
|
+
t.stack = this.formatStack(t);
|
|
343
|
+
t.message = this.adapter.formatMessage(t);
|
|
344
|
+
});
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
formatStack(t) {
|
|
@@ -359,7 +359,7 @@ class XmlReader {
|
|
|
359
359
|
async uploadArtifacts() {
|
|
360
360
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
361
361
|
let files = [];
|
|
362
|
-
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f))
|
|
362
|
+
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f));
|
|
363
363
|
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
364
364
|
|
|
365
365
|
if (!files.length) continue;
|
|
@@ -378,7 +378,7 @@ class XmlReader {
|
|
|
378
378
|
group_title: this.requestParams.group_title,
|
|
379
379
|
};
|
|
380
380
|
|
|
381
|
-
debug(
|
|
381
|
+
debug('Run', runParams);
|
|
382
382
|
|
|
383
383
|
return Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
384
384
|
}
|
|
@@ -391,12 +391,10 @@ class XmlReader {
|
|
|
391
391
|
this.formatErrors();
|
|
392
392
|
this.formatTests();
|
|
393
393
|
|
|
394
|
-
debug(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
tests: this.tests,
|
|
399
|
-
})
|
|
394
|
+
debug('Uploading data', {
|
|
395
|
+
...this.stats,
|
|
396
|
+
tests: this.tests,
|
|
397
|
+
});
|
|
400
398
|
|
|
401
399
|
const dataString = {
|
|
402
400
|
...this.stats,
|
|
@@ -411,59 +409,62 @@ class XmlReader {
|
|
|
411
409
|
|
|
412
410
|
module.exports = XmlReader;
|
|
413
411
|
|
|
414
|
-
|
|
415
412
|
function reduceTestCases(prev, item) {
|
|
416
413
|
let testCases = item.testcase;
|
|
417
414
|
if (!testCases) testCases = item['test-case'];
|
|
418
415
|
if (!Array.isArray(testCases)) {
|
|
419
|
-
testCases = [testCases]
|
|
416
|
+
testCases = [testCases];
|
|
420
417
|
}
|
|
421
418
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
422
419
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
423
|
-
testCases
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
420
|
+
testCases
|
|
421
|
+
.filter(t => !!t)
|
|
422
|
+
.forEach(testCaseItem => {
|
|
423
|
+
const file = testCaseItem.file || item.filepath || '';
|
|
424
|
+
|
|
425
|
+
let stack = '';
|
|
426
|
+
let message = '';
|
|
427
|
+
if (testCaseItem.error) stack = testCaseItem.error;
|
|
428
|
+
if (testCaseItem.failure) stack = testCaseItem.failure;
|
|
429
|
+
if (testCaseItem?.failure?.['stack-trace']) stack = testCaseItem.failure['stack-trace'];
|
|
430
|
+
if (testCaseItem?.failure?.message) message = testCaseItem.failure.message;
|
|
431
|
+
if (testCaseItem?.error?.message) message = testCaseItem.error.message;
|
|
432
|
+
|
|
433
|
+
if (testCaseItem.failure && testCaseItem.failure['#text']) stack = testCaseItem.failure['#text'];
|
|
434
|
+
if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
|
|
435
|
+
if (!message) message = stack.trim().split('\n')[0];
|
|
436
|
+
|
|
437
|
+
// eslint-disable-next-line
|
|
438
|
+
stack = `${
|
|
439
|
+
testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
|
|
440
|
+
}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
441
|
+
const testId = fetchIdFromOutput(stack);
|
|
442
|
+
|
|
443
|
+
let status = STATUS.PASSED.toString();
|
|
444
|
+
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
445
|
+
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
446
|
+
|
|
447
|
+
prev.push({
|
|
448
|
+
create: true,
|
|
449
|
+
file,
|
|
450
|
+
stack,
|
|
451
|
+
test_id: testId,
|
|
452
|
+
message,
|
|
453
|
+
line: testCaseItem.lineno,
|
|
454
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
455
|
+
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
456
|
+
status,
|
|
457
|
+
title: testCaseItem.name,
|
|
458
|
+
suite_title: reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname,
|
|
459
|
+
});
|
|
460
|
+
});
|
|
460
461
|
return prev;
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
function processTestSuite(testsuite) {
|
|
464
465
|
if (!testsuite) return [];
|
|
465
|
-
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite)
|
|
466
|
-
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite'])
|
|
466
|
+
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite);
|
|
467
|
+
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite']);
|
|
467
468
|
|
|
468
469
|
let suites = testsuite;
|
|
469
470
|
if (!Array.isArray(testsuite)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "1.2.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"typings": "typings/index.d.ts",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
13
13
|
"@octokit/rest": "^19.0.5",
|
|
14
14
|
"aws-sdk": "^2.1072.0",
|
|
15
|
-
"axios": "^
|
|
15
|
+
"axios": "^1.6.2",
|
|
16
|
+
"axios-retry": "^3.9.1",
|
|
16
17
|
"callsite-record": "^4.1.4",
|
|
17
18
|
"chalk": "^4.1.0",
|
|
18
19
|
"commander": "^4.1.1",
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"lodash.memoize": "^4.1.2",
|
|
31
32
|
"lodash.merge": "^4.6.2",
|
|
32
33
|
"minimatch": "^9.0.3",
|
|
34
|
+
"promise-retry": "^2.0.1",
|
|
33
35
|
"uuid": "^9.0.0"
|
|
34
36
|
},
|
|
35
37
|
"files": [
|
|
@@ -51,7 +53,7 @@
|
|
|
51
53
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
52
54
|
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
53
55
|
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js",
|
|
54
|
-
"test:storage": "npx mocha
|
|
56
|
+
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js"
|
|
55
57
|
},
|
|
56
58
|
"devDependencies": {
|
|
57
59
|
"@cucumber/cucumber": "^9.3.0",
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
const debug = require('debug')('@testomatio/reporter:storage');
|
|
2
|
-
const { join, resolve } = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const os = require('os');
|
|
5
|
-
const uuid = require('uuid');
|
|
6
|
-
const { TESTOMAT_ARTIFACT_SUFFIX } = require('./constants');
|
|
7
|
-
const { specificTestInfo } = require('./utils/utils');
|
|
8
|
-
|
|
9
|
-
class ArtifactStorage {
|
|
10
|
-
|
|
11
|
-
constructor(params) {
|
|
12
|
-
this.isFile = false;
|
|
13
|
-
this.isMemory = true;
|
|
14
|
-
this.storage = params?.toFile;
|
|
15
|
-
|
|
16
|
-
if (this.storage) {
|
|
17
|
-
// this is fine to use when you start saving artifacts;
|
|
18
|
-
// but when you need to retrieve them, you will not know if there is "isFile" param,
|
|
19
|
-
// because you need to create a new instance of ArtifactStorage inside client
|
|
20
|
-
// so the solution is make it opposite to isMemory (which will be retrieved from global)
|
|
21
|
-
this.isFile = true;
|
|
22
|
-
this.isMemory = false;
|
|
23
|
-
this.tmpDirFullpath = this.createTestomatTmpDir(ArtifactStorage._tmpPrefix);
|
|
24
|
-
debug('SAVE to tmp folder mode enabled!');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
static async storeToFile(tmpDirName, artifact, testSuffix = "test") {
|
|
29
|
-
// this assumes to use multiple files for each test. do we really want this?
|
|
30
|
-
const suffix = TESTOMAT_ARTIFACT_SUFFIX + uuid.v4() + testSuffix;
|
|
31
|
-
const dirpath = join(os.tmpdir(), tmpDirName);
|
|
32
|
-
// why json?
|
|
33
|
-
const filepath = resolve(dirpath, `${suffix}.json`);
|
|
34
|
-
|
|
35
|
-
return fs.promises.appendFile(filepath, JSON.stringify(artifact));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static async artifact(artifact, context) {
|
|
39
|
-
// TODO: mode for different pre-hooks. As variant - "all-tests" || "only_fail"
|
|
40
|
-
// const mode = process.env.TESTOMAT_ARTIFACTS || "only_fail";
|
|
41
|
-
|
|
42
|
-
// you save the artifact without specifying its source - test id
|
|
43
|
-
if (Array.isArray(global.testomatioArtifacts)) {
|
|
44
|
-
debug("Saving artifacts to global storage");
|
|
45
|
-
|
|
46
|
-
global.testomatioArtifacts.push(artifact);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (global?.testomatioArtifacts === undefined) {
|
|
50
|
-
const tmpDirNames = ArtifactStorage.tmpTestomatDirNames();
|
|
51
|
-
|
|
52
|
-
const testSuffix = specificTestInfo(context.test);
|
|
53
|
-
|
|
54
|
-
if (Array.isArray(tmpDirNames) && tmpDirNames.length) {
|
|
55
|
-
debug("Saving artifacts to memory tmp folder");
|
|
56
|
-
|
|
57
|
-
return ArtifactStorage.storeToFile(tmpDirNames[tmpDirNames.length - 1], artifact, testSuffix);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async artifactByTestName(test) {
|
|
63
|
-
const list = [];
|
|
64
|
-
|
|
65
|
-
if (this.isFile && this.tmpDirFullpath) {
|
|
66
|
-
const files = fs.readdirSync(this.tmpDirFullpath);
|
|
67
|
-
|
|
68
|
-
for (const file of files) {
|
|
69
|
-
if (file.includes(test)) {
|
|
70
|
-
const buff = await fs.promises.readFile(`${this.tmpDirFullpath }/${ file}`);
|
|
71
|
-
|
|
72
|
-
list.push(JSON.parse(buff.toString()));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return list;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// returns all the content from all files; do we really need it?
|
|
81
|
-
async tmpContents() {
|
|
82
|
-
const contents = [];
|
|
83
|
-
|
|
84
|
-
if (this.isFile && this.tmpDirFullpath) {
|
|
85
|
-
const files = fs.readdirSync(this.tmpDirFullpath);
|
|
86
|
-
|
|
87
|
-
for (const file of files) {
|
|
88
|
-
const buff = await fs.promises.readFile(`${this.tmpDirFullpath }/${ file}`);
|
|
89
|
-
const content = buff.toString();
|
|
90
|
-
|
|
91
|
-
contents.push(content);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return contents;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
createTestomatTmpDir(clientPrefix) {
|
|
99
|
-
return fs.mkdtempSync(join(os.tmpdir(), clientPrefix));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
clearTmpDirByName(name) {
|
|
103
|
-
const tmpDirPath = join(os.tmpdir(), name);
|
|
104
|
-
|
|
105
|
-
if (fs.existsSync(tmpDirPath)) {
|
|
106
|
-
fs.rmSync(tmpDirPath, { recursive: true });
|
|
107
|
-
debug(` Testomat tmpDir = ${tmpDirPath} was deleted successfully!`);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// no need to use multiple dirs; we can implement it later if required
|
|
114
|
-
static tmpTestomatDirNames() {
|
|
115
|
-
const subname = ArtifactStorage._tmpPrefix || "tsmt_reporter";
|
|
116
|
-
|
|
117
|
-
return fs.readdirSync(os.tmpdir(), { withFileTypes: true })
|
|
118
|
-
.filter((item) => item.isDirectory())
|
|
119
|
-
.map((item) => item.name)
|
|
120
|
-
.filter((name) => name.includes(subname));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
cleanup() {
|
|
124
|
-
// when you do a cleanup, I know nothing about isFile param
|
|
125
|
-
if (this.isFile && this.tmpDirFullpath) {
|
|
126
|
-
const tmpDirNames = ArtifactStorage.tmpTestomatDirNames();
|
|
127
|
-
|
|
128
|
-
if (Array.isArray(tmpDirNames) && tmpDirNames.length) {
|
|
129
|
-
for (const name of tmpDirNames) {
|
|
130
|
-
this.clearTmpDirByName(name);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
debug("The tmp folder has not been created! Nothing to delete");
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
ArtifactStorage._tmpPrefix = "tsmt_reporter";
|
|
141
|
-
|
|
142
|
-
module.exports = ArtifactStorage;
|
package/lib/artifactStorage.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// const debug = require('debug')('@testomatio/reporter:logger');
|
|
2
|
-
const { DataStorage } = require('./dataStorage');
|
|
3
|
-
|
|
4
|
-
class ArtifactStorage {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.dataStorage = new DataStorage('artifact');
|
|
7
|
-
|
|
8
|
-
// singleton
|
|
9
|
-
if (!ArtifactStorage.instance) {
|
|
10
|
-
ArtifactStorage.instance = this;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
save(data, context = null) {
|
|
15
|
-
this.dataStorage.putData(data, context);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
get(context) {
|
|
19
|
-
this.dataStorage.getData(context);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
ArtifactStorage.instance = null;
|
|
24
|
-
|
|
25
|
-
module.exports = new ArtifactStorage();
|