@testomatio/reporter 1.4.1-beta-1 → 1.4.1

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.
Files changed (45) hide show
  1. package/README.md +60 -57
  2. package/lib/adapter/codecept.js +86 -27
  3. package/lib/adapter/cucumber/current.js +17 -10
  4. package/lib/adapter/cucumber/legacy.js +5 -4
  5. package/lib/adapter/cucumber.js +2 -2
  6. package/lib/adapter/cypress-plugin/index.js +53 -26
  7. package/lib/adapter/jasmine.js +2 -2
  8. package/lib/adapter/jest.js +48 -10
  9. package/lib/adapter/mocha.js +90 -10
  10. package/lib/adapter/playwright.js +68 -18
  11. package/lib/adapter/webdriver.js +47 -2
  12. package/lib/bin/reportXml.js +21 -16
  13. package/lib/bin/startTest.js +12 -12
  14. package/lib/client.js +112 -52
  15. package/lib/config.js +34 -0
  16. package/lib/constants.js +28 -1
  17. package/lib/data-storage.js +203 -0
  18. package/lib/fileUploader.js +66 -53
  19. package/lib/junit-adapter/adapter.js +0 -2
  20. package/lib/junit-adapter/csharp.js +3 -4
  21. package/lib/junit-adapter/index.js +3 -3
  22. package/lib/junit-adapter/java.js +11 -11
  23. package/lib/junit-adapter/javascript.js +1 -2
  24. package/lib/junit-adapter/python.js +12 -14
  25. package/lib/junit-adapter/ruby.js +1 -2
  26. package/lib/pipe/csv.js +35 -32
  27. package/lib/pipe/github.js +4 -3
  28. package/lib/pipe/gitlab.js +17 -10
  29. package/lib/pipe/html.js +361 -0
  30. package/lib/pipe/index.js +3 -1
  31. package/lib/pipe/testomatio.js +230 -51
  32. package/lib/reporter-functions.js +12 -9
  33. package/lib/reporter.js +8 -12
  34. package/lib/services/artifacts.js +57 -0
  35. package/lib/services/index.js +13 -0
  36. package/lib/{storages/key-value-storage.js → services/key-values.js} +19 -19
  37. package/lib/{storages → services}/logger.js +58 -37
  38. package/lib/template/emptyData.svg +23 -0
  39. package/lib/template/testomatio.hbs +1421 -0
  40. package/lib/utils/pipe_utils.js +73 -79
  41. package/lib/utils/utils.js +53 -40
  42. package/lib/xmlReader.js +113 -105
  43. package/package.json +13 -7
  44. package/lib/storages/artifact-storage.js +0 -70
  45. package/lib/storages/data-storage.js +0 -307
package/lib/xmlReader.js CHANGED
@@ -1,57 +1,59 @@
1
1
  const debug = require('debug')('@testomatio/reporter:xml');
2
- const path = require("path");
2
+ const path = require('path');
3
3
  const chalk = require('chalk');
4
- const fs = require("fs");
5
- const { XMLParser } = require("fast-xml-parser");
4
+ const fs = require('fs');
5
+ const { XMLParser } = require('fast-xml-parser');
6
6
  const { APP_PREFIX, STATUS } = require('./constants');
7
- const { fetchFilesFromStackTrace,
8
- fetchIdFromOutput,
9
- fetchSourceCode,
10
- fetchSourceCodeFromStackTrace,
11
- fetchIdFromCode,
12
- humanize
13
- } = require('./utils/utils');
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 || "https://app.testomat.io";
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
+ // batch uploading is implemented for xml already
42
+ isBatchEnabled: false,
41
43
  };
42
44
  this.runId = opts.runId || TESTOMATIO_RUN;
43
- this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
45
+ this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
44
46
  if (!this.adapter) throw new Error('XML adapter for this format not found');
45
47
 
46
48
  this.opts = opts || {};
47
- this.store = {}
49
+ this.store = {};
48
50
  this.pipes = pipesFactory(opts, this.store);
49
51
 
50
52
  this.parser = new XMLParser(options);
51
- this.tests = []
52
- this.stats = {}
53
+ this.tests = [];
54
+ this.stats = {};
53
55
  this.stats.language = opts.lang?.toLowerCase();
54
- this.filesToUpload = {}
56
+ this.filesToUpload = {};
55
57
 
56
58
  this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
57
59
  console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
@@ -82,8 +84,8 @@ class XmlReader {
82
84
  } else if (jsonResult.assemblies) {
83
85
  return this.processXUnit(jsonResult.assemblies);
84
86
  } else {
85
- console.log(jsonResult)
86
- throw new Error("Format can't be parsed")
87
+ console.log(jsonResult);
88
+ throw new Error("Format can't be parsed");
87
89
  }
88
90
 
89
91
  return this.processJUnit(jsonSuite);
@@ -96,7 +98,7 @@ class XmlReader {
96
98
  const resultTests = processTestSuite(testsuite);
97
99
 
98
100
  const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
99
- const status = (failures > 0 || errors > 0 || hasFailures) ? 'failed' : 'passed';
101
+ const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
100
102
 
101
103
  this.tests = this.tests.concat(resultTests);
102
104
 
@@ -135,21 +137,22 @@ class XmlReader {
135
137
  let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
136
138
  if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
137
139
 
138
- const tests = defs.map(td => {
139
- const title = td.name.replace(/\(.*?\)/, '').trim();
140
- let example = td.name.match(/\((.*?)\)/);
141
- if (example) example = { ...example[1].split(',') };
142
- const suite = td.TestMethod.className.split(', ')[0].split('.');
143
- const suite_title = suite.pop();
144
- return {
145
- title,
146
- example,
147
- file: suite.join('/'),
148
- description: td.Description,
149
- suite_title,
150
- id: td.Execution.id,
151
- }
152
- }) || [];
140
+ const tests =
141
+ defs.map(td => {
142
+ const title = td.name.replace(/\(.*?\)/, '').trim();
143
+ let example = td.name.match(/\((.*?)\)/);
144
+ if (example) example = { ...example[1].split(',') };
145
+ const suite = td.TestMethod.className.split(', ')[0].split('.');
146
+ const suite_title = suite.pop();
147
+ return {
148
+ title,
149
+ example,
150
+ file: suite.join('/'),
151
+ description: td.Description,
152
+ suite_title,
153
+ id: td.Execution.id,
154
+ };
155
+ }) || [];
153
156
 
154
157
  let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
155
158
  if (!Array.isArray(result)) result = [result].filter(d => !!d);
@@ -160,10 +163,9 @@ class XmlReader {
160
163
  run_time: parseFloat(td.duration) * 1000,
161
164
  status: td.outcome,
162
165
  stack: td.Output.StdOut,
163
- files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
166
+ files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
164
167
  }));
165
168
 
166
-
167
169
  results.forEach(r => {
168
170
  const test = tests.find(t => t.id === r.id) || {};
169
171
  r.suite_title = test.suite_title;
@@ -223,7 +225,7 @@ class XmlReader {
223
225
 
224
226
  if (testCase.failure) {
225
227
  message = testCase.failure.message;
226
- stack = testCase.failure['stack-trace']
228
+ stack = testCase.failure['stack-trace'];
227
229
  }
228
230
  if (testCase.reason) {
229
231
  message = testCase.reason.message;
@@ -250,7 +252,6 @@ class XmlReader {
250
252
  suite_title,
251
253
  run_time,
252
254
  });
253
-
254
255
  });
255
256
  });
256
257
  });
@@ -283,12 +284,12 @@ class XmlReader {
283
284
  passed_count: 0,
284
285
  failed_count: 0,
285
286
  skipped_count: 0,
286
- }
287
+ };
287
288
  this.tests.forEach(t => {
288
289
  this.stats.tests_count++;
289
290
  if (t.status === 'passed') this.stats.passed_count++;
290
291
  if (t.status === 'failed') this.stats.failed_count++;
291
- })
292
+ });
292
293
  if (this.stats.failed_count) this.stats.status = 'failed';
293
294
 
294
295
  return this.stats;
@@ -297,7 +298,7 @@ class XmlReader {
297
298
  fetchSourceCode() {
298
299
  this.tests.forEach(t => {
299
300
  try {
300
- const file = this.adapter.getFilePath(t)
301
+ const file = this.adapter.getFilePath(t);
301
302
  if (!file) return;
302
303
 
303
304
  if (!this.stats.language) {
@@ -310,16 +311,16 @@ class XmlReader {
310
311
  }
311
312
 
312
313
  if (!fs.existsSync(file)) {
313
- debug('Failed to open file with the source code', file)
314
+ debug('Failed to open file with the source code', file);
314
315
  return;
315
316
  }
316
317
  const contents = fs.readFileSync(file).toString();
317
- t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
318
+ t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language });
318
319
  if (t.code) debug('Fetched code for test %s', t.title);
319
- t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language })
320
+ t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language });
320
321
  if (t.test_id) debug('Fetched test id %s for test %s', t.test_id, t.title);
321
322
  } catch (err) {
322
- debug(err)
323
+ debug(err);
323
324
  }
324
325
  });
325
326
  }
@@ -327,21 +328,22 @@ class XmlReader {
327
328
  formatTests() {
328
329
  this.tests.forEach(t => {
329
330
  if (t.file) {
330
- t.file = t.file.replace(process.cwd() + path.sep, '')
331
+ t.file = t.file.replace(process.cwd() + path.sep, '');
331
332
  }
332
333
 
333
- this.adapter.formatTest(t)
334
+ this.adapter.formatTest(t);
334
335
 
335
336
  t.title = humanize(t.title);
336
337
  });
337
338
  }
338
339
 
339
340
  formatErrors() {
340
- this.tests.filter(t => !!t.stack).forEach(t => {
341
- t.stack = this.formatStack(t)
342
- t.message = this.adapter.formatMessage(t);
343
- });
344
-
341
+ this.tests
342
+ .filter(t => !!t.stack)
343
+ .forEach(t => {
344
+ t.stack = this.formatStack(t);
345
+ t.message = this.adapter.formatMessage(t);
346
+ });
345
347
  }
346
348
 
347
349
  formatStack(t) {
@@ -359,7 +361,7 @@ class XmlReader {
359
361
  async uploadArtifacts() {
360
362
  for (const test of this.tests.filter(t => !!t.stack)) {
361
363
  let files = [];
362
- if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f))
364
+ if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f));
363
365
  files = [...files, ...fetchFilesFromStackTrace(test.stack)];
364
366
 
365
367
  if (!files.length) continue;
@@ -376,9 +378,10 @@ class XmlReader {
376
378
  title: this.requestParams.title,
377
379
  env: this.requestParams.env,
378
380
  group_title: this.requestParams.group_title,
381
+ isBatchEnabled: this.requestParams.isBatchEnabled,
379
382
  };
380
383
 
381
- debug("Run", runParams);
384
+ debug('Run', runParams);
382
385
 
383
386
  return Promise.all(this.pipes.map(p => p.createRun(runParams)));
384
387
  }
@@ -391,12 +394,10 @@ class XmlReader {
391
394
  this.formatErrors();
392
395
  this.formatTests();
393
396
 
394
- debug(
395
- 'Uploading data',
396
- {
397
- ...this.stats,
398
- tests: this.tests,
399
- })
397
+ debug('Uploading data', {
398
+ ...this.stats,
399
+ tests: this.tests,
400
+ });
400
401
 
401
402
  const dataString = {
402
403
  ...this.stats,
@@ -407,63 +408,70 @@ class XmlReader {
407
408
 
408
409
  return Promise.all(this.pipes.map(p => p.finishRun(dataString)));
409
410
  }
411
+
412
+ async _finishRun() {
413
+ return Promise.all(this.pipes.map(p => p.finishRun({ status: 'finished' })));
414
+ }
410
415
  }
411
416
 
412
417
  module.exports = XmlReader;
413
418
 
414
-
415
419
  function reduceTestCases(prev, item) {
416
420
  let testCases = item.testcase;
417
421
  if (!testCases) testCases = item['test-case'];
418
422
  if (!Array.isArray(testCases)) {
419
- testCases = [testCases]
423
+ testCases = [testCases];
420
424
  }
421
425
  const suiteOutput = item['system-out'] || item.output || item.log || '';
422
426
  const suiteErr = item['system-err'] || item.output || item.log || '';
423
- testCases.filter(t => !!t).forEach(testCaseItem => {
424
- const file = testCaseItem.file || item.filepath || '';
425
-
426
- let stack = '';
427
- let message = '';
428
- if (testCaseItem.error) stack = testCaseItem.error;
429
- if (testCaseItem.failure) stack = testCaseItem.failure;
430
- if (testCaseItem?.failure?.['stack-trace']) stack = testCaseItem.failure['stack-trace'];
431
- if (testCaseItem?.failure?.message) message = testCaseItem.failure.message;
432
- if (testCaseItem?.error?.message) message = testCaseItem.error.message;
433
-
434
- if (testCaseItem.failure && testCaseItem.failure['#text']) stack = testCaseItem.failure['#text'];
435
- if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
436
- if (!message) message = stack.trim().split('\n')[0];
437
-
438
- // eslint-disable-next-line
439
- stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim()
440
- const testId = fetchIdFromOutput(stack);
441
-
442
- let status = STATUS.PASSED.toString();
443
- if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
444
- if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
445
-
446
- prev.push({
447
- create: true,
448
- file,
449
- stack,
450
- test_id: testId,
451
- message,
452
- line: testCaseItem.lineno,
453
- // seconds are used in junit reports, but ms are used by testomatio
454
- run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
455
- status,
456
- title: testCaseItem.name,
457
- suite_title: reduceOptions.preferClassname ? testCaseItem.classname : (item.name || testCaseItem.classname),
458
- })
459
- });
427
+ testCases
428
+ .filter(t => !!t)
429
+ .forEach(testCaseItem => {
430
+ const file = testCaseItem.file || item.filepath || '';
431
+
432
+ let stack = '';
433
+ let message = '';
434
+ if (testCaseItem.error) stack = testCaseItem.error;
435
+ if (testCaseItem.failure) stack = testCaseItem.failure;
436
+ if (testCaseItem?.failure?.['stack-trace']) stack = testCaseItem.failure['stack-trace'];
437
+ if (testCaseItem?.failure?.message) message = testCaseItem.failure.message;
438
+ if (testCaseItem?.error?.message) message = testCaseItem.error.message;
439
+
440
+ if (testCaseItem.failure && testCaseItem.failure['#text']) stack = testCaseItem.failure['#text'];
441
+ if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
442
+ if (!message) message = stack.trim().split('\n')[0];
443
+
444
+ // eslint-disable-next-line
445
+ stack = `${
446
+ testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
447
+ }\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
448
+ const testId = fetchIdFromOutput(stack);
449
+
450
+ let status = STATUS.PASSED.toString();
451
+ if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
452
+ if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
453
+
454
+ prev.push({
455
+ create: true,
456
+ file,
457
+ stack,
458
+ test_id: testId,
459
+ message,
460
+ line: testCaseItem.lineno,
461
+ // seconds are used in junit reports, but ms are used by testomatio
462
+ run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
463
+ status,
464
+ title: testCaseItem.name,
465
+ suite_title: reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname,
466
+ });
467
+ });
460
468
  return prev;
461
469
  }
462
470
 
463
471
  function processTestSuite(testsuite) {
464
472
  if (!testsuite) return [];
465
- if (testsuite.testsuite) return processTestSuite(testsuite.testsuite)
466
- if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite'])
473
+ if (testsuite.testsuite) return processTestSuite(testsuite.testsuite);
474
+ if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite']);
467
475
 
468
476
  let suites = testsuite;
469
477
  if (!Array.isArray(testsuite)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.4.1-beta-1",
3
+ "version": "1.4.1",
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": "^0.25.0",
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",
@@ -20,7 +21,9 @@
20
21
  "debug": "^4.3.4",
21
22
  "dotenv": "^16.0.1",
22
23
  "fast-xml-parser": "^4.0.8",
24
+ "file-url": "3.0.0",
23
25
  "glob": "^10.3",
26
+ "handlebars": "^4.7.8",
24
27
  "has-flag": "^5.0.1",
25
28
  "humanize-duration": "^3.27.3",
26
29
  "is-valid-path": "^0.1.1",
@@ -28,8 +31,8 @@
28
31
  "lodash.memoize": "^4.1.2",
29
32
  "lodash.merge": "^4.6.2",
30
33
  "minimatch": "^9.0.3",
31
- "uuid": "^9.0.0",
32
- "promise-retry": "^2.0.1"
34
+ "promise-retry": "^2.0.1",
35
+ "uuid": "^9.0.0"
33
36
  },
34
37
  "files": [
35
38
  "bin",
@@ -38,9 +41,11 @@
38
41
  ],
39
42
  "scripts": {
40
43
  "clear-exportdir": "rm -rf export/",
41
- "pretty": "prettier --write .",
44
+ "pretty": "npx prettier --debug-check --check .",
45
+ "pretty:fix": "prettier --write .",
42
46
  "lint": "eslint lib",
43
47
  "lint:fix": "eslint lib --fix",
48
+ "format": "npm run lint:fix && npm run pretty:fix",
44
49
  "test": "mocha tests/**",
45
50
  "init": "cd ./tests/adapter/examples/cucumber && npm i",
46
51
  "test:adapter": "mocha './tests/adapter/index.test.js'",
@@ -50,7 +55,7 @@
50
55
  "test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
51
56
  "test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
52
57
  "test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js",
53
- "test:storage": "npx mocha ./tests-storage/**"
58
+ "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"
54
59
  },
55
60
  "devDependencies": {
56
61
  "@cucumber/cucumber": "^9.3.0",
@@ -65,10 +70,11 @@
65
70
  "eslint-plugin-import": "^2.25.4",
66
71
  "jasmine": "^3.10.0",
67
72
  "jest": "^27.4.7",
73
+ "jsdom": "^22.1.0",
68
74
  "mocha": "^9.2.0",
69
75
  "mock-http-server": "^1.4.5",
70
76
  "pino": "^8.15.0",
71
- "prettier": "2.5.1",
77
+ "prettier": "^3.2.5",
72
78
  "puppeteer": "^13.1.2"
73
79
  },
74
80
  "bin": {
@@ -1,70 +0,0 @@
1
- const debug = require('debug')('@testomatio/reporter:artifact-storage');
2
- const DataStorage = require('./data-storage');
3
-
4
- /**
5
- * Artifact storage is supposed to store file paths
6
- */
7
- class ArtifactStorage {
8
-
9
- // there is autocompletion for the class methods if implemented singleton this way
10
- constructor() {
11
- this.dataStorage = new DataStorage('artifact');
12
-
13
- // singleton
14
- if (!ArtifactStorage.instance) {
15
- ArtifactStorage.instance = this;
16
- }
17
- }
18
-
19
- // singleton
20
- static getInstance() {
21
- if (!ArtifactStorage.instance) {
22
- ArtifactStorage.instance = new ArtifactStorage();
23
- }
24
-
25
- return ArtifactStorage.instance;
26
- }
27
-
28
- /**
29
- * Stores path to file as artifact and uploads it to the S3 storage
30
- * @param {string | {path: string, type: string, name: string}} data - path to file or object with path, type and name
31
- * @param {*} context testId or test title
32
- */
33
- put(data, context = null) {
34
- if (!data) return;
35
- debug('Save artifact:', data);
36
- this.dataStorage.putData(data, context);
37
- }
38
-
39
- /**
40
- * Returns list of artifacts to upload
41
- * @param {*} context testId or test context from test runner
42
- * @returns {string | {path: string, type: string, name: string}[]}
43
- */
44
- get(context) {
45
- if (!context) return null;
46
-
47
- // array of any data
48
- let artifacts = this.dataStorage.getData(context);
49
- if (!artifacts || !artifacts.length) return null;
50
-
51
- artifacts = artifacts.map(artifactData => {
52
- // artifact could be an object ({type, path, name} props) or string
53
- let artifact;
54
- try {
55
- artifact = JSON.parse(artifactData);
56
- } catch (e) {
57
- artifact = artifactData;
58
- }
59
- return artifact;
60
- });
61
- artifacts = artifacts.filter(artifact => !!artifact);
62
- return artifacts.length ? artifacts : null;
63
- }
64
- }
65
-
66
- ArtifactStorage.instance = null;
67
-
68
- // const artifactStorage = ArtifactStorage.getInstance();
69
- const artifactStorage = new ArtifactStorage();
70
- module.exports = artifactStorage;