@testomatio/reporter 1.0.17 → 1.0.18-beta.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.
package/Changelog.md CHANGED
@@ -1,4 +1,8 @@
1
1
  <!-- pending release updates -->
2
+ # 1.0.18
3
+
4
+ * Fixed stack traces for CodeceptJS
5
+
2
6
  # 1.0.17
3
7
 
4
8
  Renamed `TESTOMATIO_STACK_FILTER` to `TESTOMATIO_STACK_IGNORE`
@@ -1,7 +1,7 @@
1
1
  const debug = require('debug')('@testomatio/reporter:adapter:codeceptjs');
2
2
  const chalk = require('chalk');
3
3
  const TestomatClient = require('../client');
4
- const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE } = require('../constants');
4
+ const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
5
5
  const upload = require('../fileUploader');
6
6
  const { parseTest: getIdFromTestTitle, fileSystem } = require('../utils/utils');
7
7
 
@@ -56,7 +56,7 @@ function CodeceptReporter(config) {
56
56
  // Listening to events
57
57
  event.dispatcher.on(event.all.before, () => {
58
58
  // clear tmp dir
59
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
59
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
60
60
 
61
61
  recorder.add('Creating new run', () => client.createRun());
62
62
  videos = [];
@@ -147,9 +147,6 @@ function CodeceptReporter(config) {
147
147
  failedTests.push(id || title);
148
148
  let testId = parseTest(tags);
149
149
  const testObj = getTestAndMessage(title);
150
- if (error && error.stack && test.steps && test.steps.length) {
151
- error.stack = test.steps[test.steps.length - 1].line();
152
- }
153
150
 
154
151
  const files = [];
155
152
  if (artifacts.screenshot) files.push({ path: artifacts.screenshot, type: 'image/png' });
@@ -2,9 +2,9 @@
2
2
  const { Formatter, formatterHelpers } = require('@cucumber/cucumber');
3
3
  const chalk = require('chalk');
4
4
  const fs = require('fs');
5
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
- const logger = require('../../logger');
7
+ const logger = require('../../storages/logger');
8
8
  const { parseTest, fileSystem } = require('../../utils/utils');
9
9
 
10
10
  const { GherkinDocumentParser, PickleParser } = formatterHelpers;
@@ -37,7 +37,7 @@ class CucumberReporter extends Formatter {
37
37
 
38
38
  parseEnvelope(envelope) {
39
39
  if (envelope.testRunStarted) {
40
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
40
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
41
41
  }
42
42
  if (envelope.testCaseStarted && this.client) {
43
43
  this.client.createRun();
@@ -2,7 +2,7 @@
2
2
  const { Formatter } = require('cucumber');
3
3
  const chalk = require('chalk');
4
4
  const { parseTest, fileSystem } = require('../../utils/utils');
5
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
7
 
8
8
  const createTestomatFormatter = apiKey => {
@@ -97,7 +97,7 @@ const createTestomatFormatter = apiKey => {
97
97
 
98
98
  options.eventBroadcaster.on('gherkin-document', addDocument);
99
99
  options.eventBroadcaster.on('test-run-started', () => {
100
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
100
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
101
101
  this?.client?.createRun();
102
102
  });
103
103
  options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
@@ -1,5 +1,5 @@
1
1
  const TestomatClient = require('../client');
2
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
2
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
3
3
  const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
4
4
 
5
5
  class JestReporter {
@@ -11,20 +11,9 @@ class JestReporter {
11
11
  this.client.createRun();
12
12
  }
13
13
 
14
- static getIdOfCurrentlyRunningTest() {
15
- if (!process.env.JEST_WORKER_ID) return null;
16
- try {
17
- // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
18
- // eslint-disable-next-line no-undef
19
- if (expect && expect?.getState()?.currentTestName) return parseTest(expect?.getState()?.currentTestName);
20
- } catch (e) {
21
- return null;
22
- }
23
- }
24
-
25
14
  onRunStart() {
26
15
  // clear tmp dir
27
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
16
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
28
17
  }
29
18
 
30
19
  onTestResult(test, testResult) {
@@ -72,9 +61,6 @@ class JestReporter {
72
61
  const { numFailedTests } = results;
73
62
  const status = numFailedTests === 0 ? STATUS.PASSED : STATUS.FAILED;
74
63
  this.client.updateRunStatus(status);
75
-
76
- // clear tmp dir
77
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
78
64
  }
79
65
  }
80
66
 
@@ -1,11 +1,9 @@
1
1
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
2
2
  const Mocha = require('mocha');
3
- const debug = require('debug')('@testomatio/reporter:adapter:mocha');
4
3
  const chalk = require('chalk');
5
4
  const TestomatClient = require('../client');
6
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
7
- const { parseTest, specificTestInfo, fileSystem } = require('../utils/utils');
8
- const ArtifactStorage = require('../_ArtifactStorageOld');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
6
+ const { parseTest, fileSystem } = require('../utils/utils');
9
7
 
10
8
  const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
11
9
  Mocha.Runner.constants;
@@ -15,7 +13,7 @@ function MochaReporter(runner, opts) {
15
13
  let passes = 0;
16
14
  let failures = 0;
17
15
  let skipped = 0;
18
- let artifactStore;
16
+ // let artifactStore;
19
17
 
20
18
  const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
21
19
 
@@ -24,13 +22,7 @@ function MochaReporter(runner, opts) {
24
22
  runner.on(EVENT_RUN_BEGIN, () => {
25
23
  client.createRun();
26
24
 
27
- const params = {
28
- toFile: true,
29
- };
30
-
31
- artifactStore = runner._workerReporter !== undefined ? new ArtifactStorage(params) : new ArtifactStorage();
32
-
33
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
25
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
34
26
  });
35
27
 
36
28
  runner.on(EVENT_TEST_PASS, async test => {
@@ -38,20 +30,11 @@ function MochaReporter(runner, opts) {
38
30
  console.log(chalk.bold.green('✔'), test.fullTitle());
39
31
  const testId = parseTest(test.title);
40
32
 
41
- const specificTest = specificTestInfo(test);
42
- const content = await artifactStore.artifactByTestName(specificTest);
43
-
44
- debug(`test=${specificTest} content = `, content);
45
-
46
- client.addTestRun(
47
- STATUS.PASSED,
48
- {
49
- test_id: testId,
50
- title: test.title,
51
- time: test.duration,
52
- },
53
- content,
54
- );
33
+ client.addTestRun(STATUS.PASSED, {
34
+ test_id: testId,
35
+ title: test.title,
36
+ time: test.duration,
37
+ });
55
38
  });
56
39
 
57
40
  runner.on(EVENT_TEST_PENDING, test => {
@@ -70,29 +53,18 @@ function MochaReporter(runner, opts) {
70
53
  console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
71
54
  const testId = parseTest(test.title);
72
55
 
73
- const specificTest = specificTestInfo(test);
74
- const content = await artifactStore.artifactByTestName(specificTest);
75
-
76
- debug(`fail test=${specificTest} content = `, content);
77
-
78
- client.addTestRun(
79
- STATUS.FAILED,
80
- {
81
- error: err,
82
- test_id: testId,
83
- title: test.title,
84
- time: test.duration,
85
- },
86
- content,
87
- );
56
+ client.addTestRun(STATUS.FAILED, {
57
+ error: err,
58
+ test_id: testId,
59
+ title: test.title,
60
+ time: test.duration,
61
+ });
88
62
  });
89
63
 
90
64
  runner.on(EVENT_RUN_END, () => {
91
65
  const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
92
66
  console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
93
67
  client.updateRunStatus(status);
94
-
95
- artifactStore.cleanup();
96
68
  });
97
69
  }
98
70
 
@@ -3,7 +3,7 @@ const crypto = require('crypto');
3
3
  const os = require('os');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE } = require('../constants');
6
+ const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
7
7
  const TestomatioClient = require('../client');
8
8
  const { isArtifactsEnabled } = require('../fileUploader');
9
9
  const { parseTest, fileSystem } = require('../utils/utils');
@@ -19,15 +19,17 @@ class PlaywrightReporter {
19
19
 
20
20
  onBegin(_config, suite) {
21
21
  // clean data storage
22
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
22
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
23
23
  if (!this.client) return;
24
24
  this.suite = suite;
25
25
  this.client.createRun();
26
26
  }
27
27
 
28
- // onTestBegin(test) {
29
- // const testId = parseTest(test.title);
30
- // }
28
+ /*
29
+ onTestBegin(test) {
30
+ // does not work; value is not to the storage context
31
+ global.testTitle = test.title;
32
+ } */
31
33
 
32
34
  onTestEnd(test, result) {
33
35
  if (!this.client) return;
@@ -44,23 +46,33 @@ class PlaywrightReporter {
44
46
  appendStep(step, steps);
45
47
  }
46
48
 
47
- const logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
48
-
49
- const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
50
- error,
51
- test_id: testId,
52
- suite_title,
53
- title,
54
- steps: steps.join('\n'),
55
- time: duration,
56
- stack: logs,
57
- }).then(pipes => {
58
- testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
59
-
60
- this.uploads.push({
61
- testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
49
+ let logs = '';
50
+ if (result.stderr.length || result.stdout.length) {
51
+ logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
52
+ }
53
+
54
+ const reportTestPromise = this.client
55
+ .addTestRun(checkStatus(result.status), {
56
+ error,
57
+ test_id: testId,
58
+ suite_title,
59
+ title,
60
+ steps: steps.join('\n'),
61
+ time: duration,
62
+ stack: logs,
63
+ })
64
+ .then(pipes => {
65
+ testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
66
+
67
+ this.uploads.push({
68
+ testId,
69
+ title,
70
+ suite_title,
71
+ files: result.attachments.filter(a => a.body || a.path),
72
+ });
73
+ // remove empty uploads
74
+ this.uploads = this.uploads.filter(upload => upload.files.length);
62
75
  });
63
- });
64
76
 
65
77
  reportTestPromises.push(reportTestPromise);
66
78
  }
@@ -53,6 +53,12 @@ program
53
53
 
54
54
  let exitCode = 0;
55
55
 
56
+ if (!command.split) {
57
+ process.exitCode = 255;
58
+ console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
59
+ return;
60
+ }
61
+
56
62
  const client = new TestomatClient({ apiKey, title, parallel: true });
57
63
 
58
64
  if(filter) {
@@ -74,12 +80,6 @@ program
74
80
  }
75
81
  }
76
82
 
77
- if (!command.split) {
78
- process.exitCode = 255;
79
- console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
80
- return;
81
- }
82
-
83
83
  const testCmds = command.split(' ');
84
84
  console.log(APP_PREFIX, `🚀 Running`, chalk.green(command));
85
85
 
package/lib/client.js CHANGED
@@ -8,6 +8,7 @@ const { randomUUID } = require('crypto');
8
8
  const upload = require('./fileUploader');
9
9
  const { APP_PREFIX } = require('./constants');
10
10
  const pipesFactory = require('./pipe');
11
+ const artifactStorage = require('./storages/artifact-storage');
11
12
 
12
13
  /**
13
14
  * @typedef {import('../types').TestData} TestData
@@ -96,9 +97,6 @@ class Client {
96
97
  // all pipes disabled, skipping
97
98
  if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
98
99
 
99
- global.testomatioArtifacts = [];
100
- if (!global.testomatioDataStore) global.testomatioDataStore = {};
101
-
102
100
  this.queue = this.queue
103
101
  .then(() => Promise.all(this.pipes.map(p => p.createRun())))
104
102
  .catch(err => console.log(APP_PREFIX, err))
@@ -112,11 +110,9 @@ class Client {
112
110
  *
113
111
  * @param {string|undefined} status
114
112
  * @param {TestData} [testData]
115
- * @param {string[]} [storeArtifacts]
116
113
  * @returns {Promise<PipeResult[]>}
117
114
  */
118
- async addTestRun(status, testData, storeArtifacts = []) {
119
- debug('Adding test run for test', testData?.test_id || 'unknown test');
115
+ async addTestRun(status, testData) {
120
116
  // all pipes disabled, skipping
121
117
  if (!this.pipes?.filter(p => p.isEnabled).length) return [];
122
118
 
@@ -155,23 +151,21 @@ class Client {
155
151
 
156
152
  stack += testData.stack || '';
157
153
 
154
+ // ATTACH LOGS from storage
158
155
  // in some cases (e.g. using cucumber) logger instance becomes empty object, if import at the top of the file
159
- const logger = require('./logger');
156
+ const logger = require('./storages/logger');
160
157
  const testLogs = logger.getLogs(test_id);
161
158
  // debug(`Test logs for ${test_id}:\n`, testLogs);
162
159
  if (stack) stack += '\n\n';
163
160
  stack += testLogs ? `${chalk.bold('Logs:')}\n${testLogs}` : '';
164
161
 
165
- if (Array.isArray(storeArtifacts) && storeArtifacts.length) {
166
- debug('CLIENT storeArtifact', storeArtifacts);
167
- files.push(...storeArtifacts);
168
- }
162
+ // GET ARTIFACTS from storage
163
+ const artifactFiles = artifactStorage.get(test_id);
164
+ if (artifactFiles) files.push(...artifactFiles);
169
165
 
170
- if (Array.isArray(global.testomatioArtifacts)) {
171
- debug('CLIENT global[testomatioArtifacts]', global.testomatioArtifacts);
172
- files.push(...global.testomatioArtifacts);
173
- global.testomatioArtifacts = [];
174
- }
166
+ // GET KEY-VALUEs from storage
167
+ const keyValueStorage = require('./storages/key-value-storage');
168
+ const keyValues = keyValueStorage.get(test_id);
175
169
 
176
170
  for (const file of files) {
177
171
  uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
@@ -184,7 +178,7 @@ class Client {
184
178
 
185
179
  const artifacts = await Promise.all(uploadedFiles);
186
180
 
187
- global.testomatioArtifacts = [];
181
+ // global.testomatioArtifacts = [];
188
182
 
189
183
  this.totalUploaded += uploadedFiles.filter(n => n).length;
190
184
 
@@ -202,6 +196,7 @@ class Client {
202
196
  message,
203
197
  run_time: parseFloat(time),
204
198
  artifacts,
199
+ meta: keyValues,
205
200
  };
206
201
 
207
202
  this.queue = this.queue.then(() =>
@@ -237,14 +232,16 @@ class Client {
237
232
  this.queue = this.queue
238
233
  .then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
239
234
  .then(() => {
240
- debug('TOTAL uploaded files', this.totalUploaded);
235
+ debug('TOTAL artifacts', this.totalUploaded);
236
+ if (this.totalUploaded && !upload.isArtifactsEnabled())
237
+ debug(`${this.totalUploaded} artifacts are not uploaded, because artifacts uploading is not enabled`);
241
238
 
242
- if (upload.isArtifactsEnabled() && this.totalUploaded > 0) {
239
+ if (this.totalUploaded && upload.isArtifactsEnabled()) {
243
240
  console.log(
244
241
  APP_PREFIX,
245
- `🗄️ Total ${this.totalUploaded} artifacts ${
242
+ `🗄️ ${this.totalUploaded} artifacts ${
246
243
  process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
247
- } uploaded to S3 bucket `,
244
+ } uploaded to S3 bucket`,
248
245
  );
249
246
  }
250
247
  })
package/lib/constants.js CHANGED
@@ -1,11 +1,10 @@
1
1
  const chalk = require('chalk');
2
+ const os = require('os');
3
+ const path = require('path');
2
4
 
3
5
  const APP_PREFIX = chalk.gray('[TESTOMATIO]');
4
- const TESTOMAT_ARTIFACT_SUFFIX = "testomatio_artifact_";
5
6
 
6
- const TESTOMAT_TMP_STORAGE = {
7
- mainDir: "testomatio_tmp",
8
- }
7
+ const TESTOMAT_TMP_STORAGE_DIR = path.join(os.tmpdir(), 'testomatio_tmp');
9
8
 
10
9
  const CSV_HEADERS = [
11
10
  { id: 'suite_title', title: 'Suite_title' },
@@ -24,8 +23,7 @@ const STATUS = {
24
23
 
25
24
  module.exports = {
26
25
  APP_PREFIX,
27
- TESTOMAT_ARTIFACT_SUFFIX,
28
- TESTOMAT_TMP_STORAGE,
26
+ TESTOMAT_TMP_STORAGE_DIR,
29
27
  CSV_HEADERS,
30
28
  STATUS,
31
29
  }
@@ -0,0 +1,43 @@
1
+ const logger = require('./storages/logger');
2
+ const artifactStorage = require('./storages/artifact-storage');
3
+ const keyValueStorage = require('./storages/key-value-storage');
4
+
5
+ /**
6
+ * Stores path to file as artifact and uploads it to the S3 storage
7
+ * @param {string | {path: string, type: string, name: string}} data - path to file or object with path, type and name
8
+ */
9
+ function saveArtifact(data, context = null) {
10
+ if (!data) return;
11
+ artifactStorage.put(data, context);
12
+ }
13
+
14
+ /**
15
+ * Attach log message(s) to the test report
16
+ * @param {...any} args
17
+ */
18
+ function logMessage(...args) {
19
+ logger.log(...args);
20
+ }
21
+
22
+ /**
23
+ * Similar to "log" function but marks message in report as a step
24
+ * @param {*} message
25
+ */
26
+ function addStep(message) {
27
+ logger.step(message);
28
+ }
29
+
30
+ /**
31
+ * Add key-value pair(s) to the test report
32
+ * @param {*} keyValue
33
+ */
34
+ function setKeyValue(keyValue) {
35
+ keyValueStorage.put(keyValue);
36
+ }
37
+
38
+ module.exports = {
39
+ artifact: saveArtifact,
40
+ log: logMessage,
41
+ step: addStep,
42
+ keyValue: setKeyValue,
43
+ };
package/lib/reporter.js CHANGED
@@ -1,10 +1,10 @@
1
- const logger = require('./logger');
1
+ const logger = require('./storages/logger');
2
2
  const TestomatClient = require('./client');
3
3
  const TRConstants = require('./constants');
4
- const TRArtifacts = require('./_ArtifactStorageOld');
5
4
 
6
5
  const log = logger.templateLiteralLog.bind(logger);
7
6
  const step = logger.step.bind(logger);
7
+ const reporterFunctions = require('./reporter-functions');
8
8
 
9
9
  module.exports = {
10
10
  logger,
@@ -13,6 +13,11 @@ module.exports = {
13
13
  step,
14
14
  TestomatClient,
15
15
  TRConstants,
16
- TRArtifacts,
17
- addArtifact: TRArtifacts.artifact,
16
+ };
17
+
18
+ module.exports.testomat = {
19
+ artifact: reporterFunctions.artifact,
20
+ log: reporterFunctions.log,
21
+ step: reporterFunctions.step,
22
+ meta: reporterFunctions.keyValue,
18
23
  };
@@ -0,0 +1,70 @@
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;
@@ -3,16 +3,18 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
5
  const { join } = require('path');
6
- const JestReporter = require('./adapter/jest');
7
- const { TESTOMAT_TMP_STORAGE } = require('./constants');
8
- const { fileSystem } = require('./utils/utils');
9
- const getTestIdFromTestTitle = require('./utils/utils').parseTest;
6
+ const { TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
7
+ const { fileSystem, jestHelpers, parseTest } = require('../utils/utils');
8
+
9
+ const getTestIdFromTestTitle = parseTest;
10
10
 
11
11
  class DataStorage {
12
12
  /**
13
13
  * Creates data storage instance for specific data type.
14
14
  * Stores data to global variable or to file depending on what is applicable for current test runner
15
15
  * (running environment).
16
+ * Recommend to use composition while using this class (instead of inheritance).
17
+ * ! Also the class which will use data storage should be singleton (to avoid data loss).
16
18
  * @param {*} dataType storage type (like 'log', 'artifact'); could be any string, used only to define file path
17
19
  */
18
20
  constructor(dataType) {
@@ -20,8 +22,35 @@ class DataStorage {
20
22
  this.isFileStorage = true;
21
23
  this.#refreshStorageType();
22
24
 
23
- this.dataDirPath = path.join(TESTOMAT_TMP_STORAGE.mainDir, this.dataType);
24
- fileSystem.createDir(this.dataDirPath);
25
+ this.dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, this.dataType);
26
+
27
+ // set test title for mocha (could not be moved to adapter)
28
+ // it does not override existing hook, just add new one
29
+ try {
30
+ // @ts-ignore
31
+ if (!beforeEach) return;
32
+ // @ts-ignore-next-line
33
+ beforeEach(function () {
34
+ if (this.currentTest?.__mocha_id__) {
35
+ global.testTitle = this.currentTest.fullTitle();
36
+ }
37
+ });
38
+ } catch (e) {
39
+ // ignore
40
+ }
41
+
42
+ // set test title for playwright
43
+ try {
44
+ // eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
45
+ const { test } = require('@playwright/test');
46
+ // eslint-disable-next-line no-empty-pattern
47
+ test.beforeEach(async ({}, testInfo) => {
48
+ global.testTitle = testInfo.title;
49
+ global.testomatioRunningEnvironment = 'playwright';
50
+ });
51
+ } catch (e) {
52
+ // ignore
53
+ }
25
54
  }
26
55
 
27
56
  /**
@@ -44,15 +73,18 @@ class DataStorage {
44
73
  }
45
74
 
46
75
  /**
47
- * Puts any data to storage (file or global variable)
48
- * @param {*} data anything you want to store
76
+ * Puts any data to storage (file or global variable).
77
+ * If file: stores data as text, if global variable – stores as array of data.
78
+ * @param {*} data anything you want to store (string, object, array, etc)
49
79
  * @param {*} context could be testId or any context (test, suite, etc) which is used to define testId
50
80
  * @returns
51
81
  */
52
82
  putData(data, context = null) {
53
- this.#refreshStorageType();
83
+ fileSystem.createDir(this.dataDirPath);
54
84
 
55
- let testId = this.#tryToRetrieveTestId(context) || null;
85
+ this.#refreshStorageType();
86
+ context = context ?? global.testTitle ?? null;
87
+ let testId = this.#tryToRetrieveTestId(context) || context || null;
56
88
 
57
89
  if (this.runningEnvironment === 'codeceptjs') {
58
90
  this.isFileStorage = false;
@@ -60,7 +92,7 @@ class DataStorage {
60
92
  }
61
93
 
62
94
  if (this.runningEnvironment === 'jest') {
63
- testId = testId ?? JestReporter.getIdOfCurrentlyRunningTest();
95
+ testId = testId ?? jestHelpers.getIdOfCurrentlyRunningTest();
64
96
  }
65
97
 
66
98
  // logs in playwright are gathered by pw framework itself
@@ -74,7 +106,7 @@ class DataStorage {
74
106
  testId = this.#tryToRetrieveTestId(global.currentlyRunningTestTitle);
75
107
 
76
108
  if (!testId) {
77
- debug(`No test id provided for ${this.dataType} data: ${data}`);
109
+ debug(`No test id provided for "${this.dataType}" data:`, data);
78
110
  return;
79
111
  }
80
112
 
@@ -87,12 +119,10 @@ class DataStorage {
87
119
 
88
120
  /**
89
121
  * Returns data, stored for specific testId (or data which was stored without test id specified).
90
- * This method will get data from global variable. But if it is not available, it will try to get data from file.
91
- * Thus, good approach is to remove file storage folder before each test run (and after, for sure).
122
+ * This method will get data from global variable and/or from from file (previosly saved with put method).
92
123
  *
93
- * Defining the execution environment is not guaranteed! Is used only as additional check.
94
- * @param {*} context could be testId or any context (test, suite, etc) which is used to define testId
95
- * @returns
124
+ * @param {*} context could be testId or any context (test title, suite, etc) which is used to define testId
125
+ * @returns array of data (any type), null (if no data found for test) or string (if data type is log)
96
126
  */
97
127
  getData(context) {
98
128
  this.#refreshStorageType();
@@ -100,9 +130,6 @@ class DataStorage {
100
130
  let testId = null;
101
131
  if (typeof context === 'string') {
102
132
  testId = this.#tryToRetrieveTestId(context) || context;
103
- } else {
104
- // TODO: derive testId from context
105
- // testId = context...
106
133
  }
107
134
 
108
135
  if (!testId) {
@@ -110,12 +137,20 @@ class DataStorage {
110
137
  return null;
111
138
  }
112
139
 
113
- let testData = '';
140
+ let testDataFromFile = [];
141
+ let testDataFromGlobalVar = [];
114
142
 
115
143
  if (global?.testomatioDataStore) {
116
- testData = this.#getDataFromGlobalVar(testId);
144
+ testDataFromGlobalVar = this.#getDataFromGlobalVar(testId);
117
145
  // these frameworks use global variable storage
118
- if (testData && ['cucumber', 'codeceptjs'].includes(this.runningEnvironment)) return testData;
146
+ // if (testDataFromGlobalVar && ['cucumber', 'codeceptjs'].includes(this.runningEnvironment)) {
147
+ if (testDataFromGlobalVar) {
148
+ // return as string if its log data
149
+ if (this.dataType === 'log' && testDataFromGlobalVar.length) return testDataFromGlobalVar.join('\n');
150
+ // return as array for other data types
151
+ if (testDataFromGlobalVar.length) return testDataFromGlobalVar;
152
+ }
153
+ // don't return nothing if no data in global variable
119
154
  }
120
155
 
121
156
  /* condition is removed for mocha
@@ -126,11 +161,16 @@ class DataStorage {
126
161
  // if (testData) return testData;
127
162
  // }
128
163
 
129
- const testDataFromFile = this.#getDataFromFile(testId);
130
- testData += testDataFromFile || '';
164
+ testDataFromFile = this.#getDataFromFile(testId);
131
165
 
132
- debug(`No ${this.dataType} data for test id ${testId} in both file and global variable`);
133
- return testData || '';
166
+ if (testDataFromFile.length) {
167
+ if (this.dataType === 'log') return testDataFromFile.join('\n');
168
+ return testDataFromFile;
169
+ }
170
+ debug(`No "${this.dataType}" data for test id ${testId} in both file and global variable`);
171
+
172
+ // in case no data found for test
173
+ return null;
134
174
  }
135
175
 
136
176
  /**
@@ -166,46 +206,63 @@ class DataStorage {
166
206
  this.runningEnvironment = this.getRunningEnviroment();
167
207
 
168
208
  // some test frameworks do not persist global variables, thus file storage is used for them (by default)
169
- if (['playwright', 'codeceptjs'].includes(this.runningEnvironment)) this.isFileStorage = false;
209
+ if (this.runningEnvironment === 'codeceptjs') this.isFileStorage = false;
210
+ // playwrigth stores logs by itself; but file storage is used for other data types for playwright
211
+ if (this.runningEnvironment === 'playwright' && this.dataType === 'log') this.isFileStorage = false;
170
212
  }
171
213
 
214
+ /**
215
+ * @param {*} testId
216
+ * @returns aray of data (any type)
217
+ */
172
218
  #getDataFromGlobalVar(testId) {
173
219
  try {
174
220
  if (global?.testomatioDataStore[this.dataType]) {
175
221
  const testData = global.testomatioDataStore[this.dataType][testId];
176
- debug(`Data for test id ${testId}:\n${testData}`);
177
- return testData || '';
222
+ if (testData) debug(`"${this.dataType}" data for test id ${testId}:`, testData.join(', '));
223
+ return testData || [];
178
224
  }
179
- debug(`No ${this.dataType} data for test id ${testId} in <global> storage`);
180
- return '';
225
+ // debug(`No ${this.dataType} data for test id ${testId} in <global> storage`);
226
+ return [];
181
227
  } catch (e) {
182
228
  // there could be no data, ignore
183
229
  }
184
230
  }
185
231
 
232
+ /**
233
+ *
234
+ * @param {*} testId
235
+ * @returns array of data (any type)
236
+ */
186
237
  #getDataFromFile(testId) {
187
238
  try {
188
239
  const filepath = join(this.dataDirPath, `${this.dataType}_${testId}`);
189
240
  if (fs.existsSync(filepath)) {
190
- const testData = fs.readFileSync(filepath, 'utf-8');
191
- debug(`Data for test id ${testId}:\n${testData}`);
192
- return testData || '';
241
+ const testDataAsText = fs.readFileSync(filepath, 'utf-8');
242
+ if (testDataAsText) debug(`"${this.dataType}" data for test id ${testId}:`, testDataAsText);
243
+ const testDataArr = testDataAsText?.split(os.EOL) || [];
244
+ return testDataArr;
193
245
  }
194
- debug(`No ${this.dataType} data for test id ${testId} in <file> storage`);
195
- return '';
246
+ // debug(`No ${this.dataType} data for test id ${testId} in <file> storage`);
247
+ return [];
196
248
  } catch (e) {
197
249
  // there could be no data, ignore
198
250
  }
199
- return '';
251
+ return [];
200
252
  }
201
253
 
254
+ /**
255
+ * Puts data to global variable. Unlike the file storage, stores data in array (file storage just append as string).
256
+ * @param {*} data
257
+ * @param {*} testId
258
+ */
202
259
  #putDataToGlobalVar(data, testId) {
203
- debug('Saving data to global variable for test', testId, ':\n', data, '\n');
260
+ debug(`Saving data to global variable for test ${testId}:`, data);
204
261
  if (!global.testomatioDataStore) global.testomatioDataStore = {};
205
262
  if (!global.testomatioDataStore?.[this.dataType]) global.testomatioDataStore[this.dataType] = {};
206
- global.testomatioDataStore?.[this.dataType][testId] // eslint-disable-line no-unused-expressions
207
- ? (global.testomatioDataStore[this.dataType][testId] += `\n${data}`)
208
- : (global.testomatioDataStore[this.dataType][testId] = data);
263
+
264
+ if (!global.testomatioDataStore?.[this.dataType][testId]) global.testomatioDataStore[this.dataType][testId] = [];
265
+ global.testomatioDataStore[this.dataType][testId].push(data);
209
266
  }
210
267
 
211
268
  #putDataToFile(data, testId) {
@@ -215,18 +272,36 @@ class DataStorage {
215
272
  if (!fs.existsSync(this.dataDirPath)) fileSystem.createDir(this.dataDirPath);
216
273
  debug('Saving data to file for test', testId, 'to', filepath, ':\n', data, '\n');
217
274
 
218
- // TODO: handle multiple invocations of JSON.stringify.
219
- // UPD: not actual because decided not to use it - it created extra wrapping quotes "" and // (escaped slashes)
220
- fs.appendFileSync(filepath, data + os.EOL, 'utf-8');
275
+ // append new line if file already exists (in this case its definitely includes some data)
276
+ if (fs.existsSync(filepath)) {
277
+ fs.appendFileSync(filepath, os.EOL + data, 'utf-8');
278
+ } else {
279
+ fs.writeFileSync(filepath, data, 'utf-8');
280
+ }
221
281
  }
222
282
  }
223
283
 
224
- module.exports.DataStorage = DataStorage;
284
+ module.exports = DataStorage;
225
285
 
226
286
  // TODO: consider using fs promises instead of writeSync/appendFileSync to
227
287
  // prevent blocking and improve performance (probably queue usage will be required)
228
288
 
229
289
  // TODO: rewrite client - everything regarding storing artifacts
230
290
  // TODO: try to define adapter inside client
231
- // TODO: use .env
232
291
  // TODO: ability to intercept multiple loggers (upd: no need)
292
+
293
+ /* does not work for Playwright
294
+ try {
295
+ const { test } = require('@playwright/test');
296
+ test.beforeEach(async ({}, testInfo) => {
297
+ global.testTitle = testInfo.title;
298
+ global.testomatioRunningEnvironment = 'playwright';
299
+ });
300
+ } catch (e) {
301
+ // ignore
302
+ }
303
+ // Playwright storage works fine only when running tests from a single file;
304
+ // otherwise it is not possible to define test id/title, it is overwritten
305
+ TODO: the only way implementation for Pw for now is to pass testInfo inside test
306
+ // (and process this testInfo inside "put" function)
307
+ */
@@ -0,0 +1,58 @@
1
+ const debug = require('debug')('@testomatio/reporter:key-value-storage');
2
+ const DataStorage = require('./data-storage');
3
+
4
+ class KeyValueStorage {
5
+ constructor() {
6
+ this.dataStorage = new DataStorage('keyvalue');
7
+
8
+ // singleton
9
+ if (!KeyValueStorage.instance) {
10
+ KeyValueStorage.instance = this;
11
+ }
12
+ }
13
+
14
+ /**
15
+ * Stores key-value pair and passes it to reporter
16
+ * @param {{key: string}} keyValue - key-value pair(s) as object
17
+ * @param {*} context testId or test title
18
+ */
19
+ put(keyValue, context = null) {
20
+ if (!keyValue) return;
21
+ this.dataStorage.putData(keyValue, context);
22
+ }
23
+
24
+ #isKeyValueObject(smth) {
25
+ return smth && typeof smth === 'object' && !Array.isArray(smth) && smth !== null;
26
+ }
27
+
28
+ /**
29
+ * Returns key-values pairs for the test as object
30
+ * @param {*} context testId or test context from test runner
31
+ * @returns {Object} key-values pairs as object, e.g. {priority: 'high', browser: 'chrome'}
32
+ */
33
+ get(context) {
34
+ if (!context) return null;
35
+
36
+ const keyValuesList = this.dataStorage.getData(context);
37
+ if (!keyValuesList || !keyValuesList?.length) return null;
38
+
39
+ const keyValues = {};
40
+ for (const keyValue of keyValuesList) {
41
+ if (this.#isKeyValueObject(keyValue)) {
42
+ Object.assign(keyValues, keyValue);
43
+ } else if (typeof keyValue === 'string') {
44
+ try {
45
+ Object.assign(keyValues, JSON.parse(keyValue));
46
+ } catch (e) {
47
+ debug(`Error parsing key-values for test ${context}`, keyValue);
48
+ }
49
+ }
50
+ }
51
+
52
+ return Object.keys(keyValues).length ? keyValues : null;
53
+ }
54
+ }
55
+
56
+ KeyValueStorage.instance = null;
57
+
58
+ module.exports = new KeyValueStorage();
@@ -1,6 +1,6 @@
1
1
  const chalk = require('chalk');
2
2
  const debug = require('debug')('@testomatio/reporter:logger');
3
- const { DataStorage } = require('./dataStorage');
3
+ const DataStorage = require('./data-storage');
4
4
 
5
5
  const LOG_METHODS = ['assert', 'debug', 'error', 'info', 'log', 'trace', 'warn'];
6
6
  const LEVELS = {
@@ -38,20 +38,6 @@ class Logger {
38
38
  if (!Logger.instance) {
39
39
  Logger.instance = this;
40
40
  }
41
-
42
- // add beforeEach hook for mocha. it does not override existing hook, just add new one
43
- try {
44
- // @ts-ignore
45
- if (!beforeEach) return;
46
- // @ts-ignore-next-line
47
- beforeEach(function () {
48
- if (this.currentTest?.__mocha_id__) {
49
- global.testTitle = this.currentTest.fullTitle();
50
- }
51
- });
52
- } catch (e) {
53
- // ignore
54
- }
55
41
  }
56
42
 
57
43
  /**
@@ -61,9 +47,6 @@ class Logger {
61
47
  * @param {...any} values
62
48
  */
63
49
  step(strings, ...values) {
64
- // get testId for mocha
65
- const context = global.testTitle ?? null;
66
-
67
50
  let logs = '';
68
51
  for (let i = 0; i < strings.length; i++) {
69
52
  logs += strings[i];
@@ -72,7 +55,7 @@ class Logger {
72
55
  }
73
56
  }
74
57
  logs = chalk.blue(`> ${logs}`);
75
- this.#dataStorage.putData(logs, context);
58
+ this.#dataStorage.putData(logs);
76
59
  }
77
60
 
78
61
  /**
@@ -117,11 +100,6 @@ class Logger {
117
100
  templateLiteralLog(strings, ...args) {
118
101
  if (Array.isArray(strings)) strings = strings.filter(item => item !== '').map(item => item.trim());
119
102
  if (Array.isArray(args)) args = args.filter(item => item !== '');
120
- // entity which is used to define testId
121
- let context = null;
122
-
123
- // get testId for mocha
124
- context = global.testTitle ?? null;
125
103
 
126
104
  let logs;
127
105
  // this block means tagged template is used (syntax like $`text ${someVar}`)
@@ -146,7 +124,7 @@ class Logger {
146
124
  logs = this.#stringifyLogs(strings, ...args);
147
125
  }
148
126
  this.#originalUserLogger.log(logs);
149
- this.#dataStorage.putData(logs, context);
127
+ this.#dataStorage.putData(logs);
150
128
  }
151
129
 
152
130
  /**
@@ -158,18 +136,12 @@ class Logger {
158
136
  #logWrapper(argsArray, level) {
159
137
  if (!argsArray.length) return;
160
138
 
161
- // entity which is used to define testId
162
- let context = null;
163
-
164
- // get context for mocha
165
- context = global.testTitle ?? null;
166
-
167
139
  const severity = LEVELS[level].severity;
168
140
  if (severity < LEVELS[this.logLevel]?.severity) return;
169
141
 
170
142
  const logs = this.#stringifyLogs(...argsArray);
171
143
  const colorizedLogs = chalk[LEVELS[level].color](logs);
172
- this.#dataStorage.putData(colorizedLogs, context);
144
+ this.#dataStorage.putData(colorizedLogs);
173
145
  try {
174
146
  // level.toLowerCase() represents method name (log, warn, error, etc)
175
147
  this.#originalUserLogger[level.toLowerCase()](colorizedLogs);
@@ -269,7 +241,6 @@ class Logger {
269
241
 
270
242
  Logger.instance = null;
271
243
 
272
- // module.exports.Logger = Logger;
273
244
  const logger = new Logger();
274
245
 
275
246
  module.exports = logger;
@@ -290,6 +290,19 @@ function removeColorCodes(input) {
290
290
  return input.replace(/\x1b\[[0-9;]*m/g, '');
291
291
  }
292
292
 
293
+ const jestHelpers = {
294
+ getIdOfCurrentlyRunningTest: () => {
295
+ if (!process.env.JEST_WORKER_ID) return null;
296
+ try {
297
+ // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
298
+ // eslint-disable-next-line no-undef
299
+ if (expect && expect?.getState()?.currentTestName) return parseTest(expect?.getState()?.currentTestName);
300
+ } catch (e) {
301
+ return null;
302
+ }
303
+ },
304
+ };
305
+
293
306
  module.exports = {
294
307
  isSameTest,
295
308
  fetchSourceCode,
@@ -299,6 +312,7 @@ module.exports = {
299
312
  fetchFilesFromStackTrace,
300
313
  fileSystem,
301
314
  getCurrentDateTime,
315
+ jestHelpers,
302
316
  specificTestInfo,
303
317
  isValidUrl,
304
318
  ansiRegExp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.0.17",
3
+ "version": "1.0.18-beta.1",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",
@@ -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;
@@ -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();