@testomatio/reporter 1.1.0-beta.mocha-create.9 → 1.1.1-beta-codecept-logger

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.
@@ -4,6 +4,7 @@ const TestomatClient = require('../client');
4
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
+ const { logger } = require('../storages/logger');
7
8
 
8
9
  if (!global.codeceptjs) {
9
10
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
@@ -58,14 +59,47 @@ function CodeceptReporter(config) {
58
59
  // clear tmp dir
59
60
  fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
60
61
 
61
- recorder.add('Creating new run', () => client.createRun());
62
+ // recorder.add('Creating new run', () => );
63
+ client.createRun();
62
64
  videos = [];
63
65
  traces = [];
64
66
 
65
67
  if (!global.testomatioDataStore) global.testomatioDataStore = {};
66
68
  });
67
69
 
68
- event.dispatcher.on(event.test.before, () => {
70
+ let hookSteps = [];
71
+ let suiteHookRunning = false;
72
+
73
+ event.dispatcher.on(event.suite.before, suite => {
74
+ suiteHookRunning = true;
75
+ hookSteps = [];
76
+ global.testomatioDataStore.steps = [];
77
+
78
+ logger.setContext(suite.fullTitle());
79
+ });
80
+
81
+ event.dispatcher.on(event.suite.after, () => {
82
+ logger.setContext(null);
83
+ });
84
+
85
+ event.dispatcher.on(event.hook.started, () => {
86
+ // global.testomatioDataStore.steps = [];
87
+ });
88
+
89
+ event.dispatcher.on(event.hook.passed, () => {
90
+ if (suiteHookRunning) hookSteps.push(...global.testomatioDataStore.steps);
91
+ if (suiteHookRunning) logger.setContext(null);
92
+ });
93
+
94
+ event.dispatcher.on(event.hook.failed, () => {
95
+ if (suiteHookRunning) hookSteps.push(...global.testomatioDataStore.steps);
96
+ if (suiteHookRunning) logger.setContext(null);
97
+ });
98
+
99
+ event.dispatcher.on(event.test.before, test => {
100
+ suiteHookRunning = false;
101
+ global.testomatioDataStore.steps = [];
102
+
69
103
  recorder.add(() => {
70
104
  currentMetaStep = [];
71
105
  // output.reset();
@@ -76,11 +110,15 @@ function CodeceptReporter(config) {
76
110
  if (!global.testomatioDataStore) global.testomatioDataStore = {};
77
111
  // reset steps
78
112
  global.testomatioDataStore.steps = [];
113
+
114
+ logger.setContext(test.fullTitle());
79
115
  });
80
116
 
81
117
  event.dispatcher.on(event.test.started, test => {
118
+ logger.setContext(test.fullTitle());
82
119
  testTimeMap[test.id] = Date.now();
83
120
  if (global.testomatioDataStore) global.testomatioDataStore.currentlyRunningTestId = getIdFromTestTitle(test.title);
121
+ // start logging
84
122
  });
85
123
 
86
124
  event.dispatcher.on(event.all.result, async () => {
@@ -104,6 +142,9 @@ function CodeceptReporter(config) {
104
142
  }
105
143
  const testId = parseTest(tags);
106
144
  const testObj = getTestAndMessage(title);
145
+
146
+ const logs = getTestLogs(test);
147
+
107
148
  client.addTestRun(STATUS.PASSED, {
108
149
  ...stripExampleFromTitle(title),
109
150
  suite_title: test.parent && test.parent.title,
@@ -111,6 +152,7 @@ function CodeceptReporter(config) {
111
152
  time: getDuration(test),
112
153
  steps: global.testomatioDataStore.steps.join('\n') || null,
113
154
  test_id: testId,
155
+ logs,
114
156
  });
115
157
  // output.stop();
116
158
  });
@@ -152,6 +194,9 @@ function CodeceptReporter(config) {
152
194
  if (artifacts.screenshot) files.push({ path: artifacts.screenshot, type: 'image/png' });
153
195
  // todo: video must be uploaded later....
154
196
 
197
+ const logs = getTestLogs(test);
198
+ logger.setContext(null);
199
+
155
200
  const reportTestPromise = client
156
201
  .addTestRun(STATUS.FAILED, {
157
202
  ...stripExampleFromTitle(title),
@@ -162,6 +207,7 @@ function CodeceptReporter(config) {
162
207
  time: getDuration(test),
163
208
  files,
164
209
  steps: global.testomatioDataStore?.steps?.join('\n') || null,
210
+ logs,
165
211
  })
166
212
  .then(pipes => {
167
213
  testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
@@ -230,7 +276,7 @@ function CodeceptReporter(config) {
230
276
  currentMetaStep = metaSteps;
231
277
  stepShift = 2 * shift;
232
278
 
233
- const durationMs = +new Date() - (+stepStart);
279
+ const durationMs = +new Date() - +stepStart;
234
280
  let duration = '';
235
281
  if (durationMs) {
236
282
  duration = repeat(1) + chalk.grey(`(${durationMs}ms)`);
@@ -255,7 +301,7 @@ async function uploadAttachments(client, attachments, messagePrefix, attachmentT
255
301
  if (attachments.length > 0) {
256
302
  console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType}/-s ...`);
257
303
 
258
- const promises = attachments.map(async (attachment) => {
304
+ const promises = attachments.map(async attachment => {
259
305
  const { testId, title, path, type } = attachment;
260
306
  const file = { path, type, title };
261
307
  return client.addTestRun(undefined, {
@@ -303,4 +349,21 @@ function repeat(num) {
303
349
  return ''.padStart(num, ' ');
304
350
  }
305
351
 
352
+ // TODO: think about moving to some common utils
353
+ function getTestLogs(test) {
354
+ const suiteLogsArr = logger.getLogs(test.parent.fullTitle());
355
+ const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
356
+ const testLogsArr = logger.getLogs(test.fullTitle());
357
+ const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
358
+
359
+ let logs = '';
360
+ if (suiteLogs) {
361
+ logs += `${chalk.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
362
+ }
363
+ if (testLogs) {
364
+ logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
365
+ }
366
+ return logs;
367
+ }
368
+
306
369
  module.exports = CodeceptReporter;
@@ -4,9 +4,19 @@ const chalk = require('chalk');
4
4
  const TestomatClient = require('../client');
5
5
  const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
6
6
  const { parseTest, fileSystem } = require('../utils/utils');
7
-
8
- const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
9
- Mocha.Runner.constants;
7
+ const { logger } = require('../storages/logger');
8
+
9
+ const {
10
+ EVENT_RUN_BEGIN,
11
+ EVENT_RUN_END,
12
+ EVENT_TEST_FAIL,
13
+ EVENT_TEST_PASS,
14
+ EVENT_TEST_PENDING,
15
+ EVENT_SUITE_BEGIN,
16
+ EVENT_SUITE_END,
17
+ EVENT_TEST_BEGIN,
18
+ EVENT_TEST_END,
19
+ } = Mocha.Runner.constants;
10
20
 
11
21
  function MochaReporter(runner, opts) {
12
22
  Mocha.reporters.Base.call(this, runner);
@@ -25,19 +35,34 @@ function MochaReporter(runner, opts) {
25
35
  fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
26
36
  });
27
37
 
38
+ runner.on(EVENT_SUITE_BEGIN, async suite => {
39
+ logger.setContext(suite.fullTitle());
40
+ });
41
+
42
+ runner.on(EVENT_SUITE_END, async () => {
43
+ logger.setContext(null);
44
+ });
45
+
46
+ runner.on(EVENT_TEST_BEGIN, async test => {
47
+ logger.setContext(test.fullTitle());
48
+ });
49
+
50
+ runner.on(EVENT_TEST_END, async () => {
51
+ logger.setContext(null);
52
+ });
53
+
28
54
  runner.on(EVENT_TEST_PASS, async test => {
29
55
  passes += 1;
30
-
31
56
  console.log(chalk.bold.green('✔'), test.fullTitle());
32
57
  const testId = parseTest(test.title);
33
58
 
59
+ const logs = getTestLogs(test);
60
+
34
61
  client.addTestRun(STATUS.PASSED, {
35
62
  test_id: testId,
36
- suite_title: getSuiteTitle(test),
37
- title: getTestName(test),
38
- code: test.body.toString(),
39
- file: getFile(test),
63
+ title: test.title,
40
64
  time: test.duration,
65
+ logs,
41
66
  });
42
67
  });
43
68
 
@@ -46,10 +71,7 @@ function MochaReporter(runner, opts) {
46
71
  console.log('skip: %s', test.fullTitle());
47
72
  const testId = parseTest(test.title);
48
73
  client.addTestRun(STATUS.SKIPPED, {
49
- title: getTestName(test),
50
- suite_title: getSuiteTitle(test),
51
- code: test.body.toString(),
52
- file: getFile(test),
74
+ title: test.title,
53
75
  test_id: testId,
54
76
  time: test.duration,
55
77
  });
@@ -60,14 +82,14 @@ function MochaReporter(runner, opts) {
60
82
  console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
61
83
  const testId = parseTest(test.title);
62
84
 
85
+ const logs = getTestLogs(test);
86
+
63
87
  client.addTestRun(STATUS.FAILED, {
64
88
  error: err,
65
- suite_title: getSuiteTitle(test),
66
- file: getFile(test),
67
89
  test_id: testId,
68
- title: getTestName(test),
69
- code: test.body.toString(),
90
+ title: test.title,
70
91
  time: test.duration,
92
+ logs,
71
93
  });
72
94
  });
73
95
 
@@ -78,26 +100,23 @@ function MochaReporter(runner, opts) {
78
100
  });
79
101
  }
80
102
 
103
+ function getTestLogs(test) {
104
+ const suiteLogsArr = logger.getLogs(test.parent.fullTitle());
105
+ const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
106
+ const testLogsArr = logger.getLogs(test.fullTitle());
107
+ const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
108
+
109
+ let logs = '';
110
+ if (suiteLogs) {
111
+ logs += `${chalk.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
112
+ }
113
+ if (testLogs) {
114
+ logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
115
+ }
116
+ return logs;
117
+ }
118
+
81
119
  // To have this reporter "extend" a built-in reporter uncomment the following line:
82
120
  Mocha.utils.inherits(MochaReporter, Mocha.reporters.Spec);
83
121
 
84
122
  module.exports = MochaReporter;
85
-
86
- function getSuiteTitle(test, pathArr = []) {
87
- let root = pathArr.length === 0;
88
-
89
- if (test.parent.parent) getSuiteTitle(test.parent, pathArr);
90
-
91
- pathArr.push(test.parent.title);
92
-
93
- return pathArr.filter(t => !!t)[0];
94
- }
95
-
96
- function getFile(test) {
97
- return test.parent.file?.replace(process.cwd(), '');
98
- }
99
-
100
- function getTestName(test) {
101
- if (process.env.TESTOMATIO_CREATE === 'fulltitle') return test.fullTitle();
102
- return test.title;
103
- }
package/lib/client.js CHANGED
@@ -1,14 +1,13 @@
1
1
  const debug = require('debug')('@testomatio/reporter:client');
2
2
  const createCallsiteRecord = require('callsite-record');
3
3
  const { sep, join } = require('path');
4
- const { minimatch } = require('minimatch')
4
+ const { minimatch } = require('minimatch');
5
5
  const fs = require('fs');
6
6
  const chalk = require('chalk');
7
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');
12
11
 
13
12
  /**
14
13
  * @typedef {import('../types').TestData} TestData
@@ -36,7 +35,7 @@ class Client {
36
35
 
37
36
  /**
38
37
  * Asynchronously prepares the execution list for running tests through various pipes.
39
- * Each pipe in the client is checked for enablement,
38
+ * Each pipe in the client is checked for enablement,
40
39
  * and if all pipes are disabled, the function returns a resolved Promise.
41
40
  * Otherwise, it executes the `prepareRun` method for each enabled pipe and collects the results.
42
41
  * The results are then filtered to remove any undefined values.
@@ -46,7 +45,7 @@ class Client {
46
45
  * @param {Object} params - The options for preparing the test execution list.
47
46
  * @param {string} params.pipe - Name of the executed pipe.
48
47
  * @param {string} params.pipeOptions - Filter option.
49
- * @returns {Promise<any>} - A Promise that resolves to an
48
+ * @returns {Promise<any>} - A Promise that resolves to an
50
49
  * array containing the prepared execution list,
51
50
  * or resolves to undefined if no valid results are found or if all pipes are disabled.
52
51
  */
@@ -63,22 +62,22 @@ class Client {
63
62
  if (!filterPipe.isEnabled) {
64
63
  // TODO:for the future for the another pipes
65
64
  console.warn(
66
- APP_PREFIX,
67
- `At the moment processing is available only for the "testomatio" key. Example: "testomatio:tag-name=xxx"`
65
+ APP_PREFIX,
66
+ `At the moment processing is available only for the "testomatio" key. Example: "testomatio:tag-name=xxx"`,
68
67
  );
69
68
  return;
70
69
  }
71
70
 
72
- const results = await Promise.all(this.pipes.map(async p =>
73
- ({ pipe: p.toString(), result: await p.prepareRun(pipeOptions) })
74
- ));
75
-
71
+ const results = await Promise.all(
72
+ this.pipes.map(async p => ({ pipe: p.toString(), result: await p.prepareRun(pipeOptions) })),
73
+ );
74
+
76
75
  const result = results.filter(p => p.pipe.includes('Testomatio'))[0]?.result;
77
76
 
78
77
  if (!result || result.length === 0) {
79
78
  return;
80
- }
81
-
79
+ }
80
+
82
81
  debug('Execution tests list', result);
83
82
 
84
83
  return result;
@@ -131,42 +130,31 @@ class Client {
131
130
  steps,
132
131
  code = null,
133
132
  title,
134
- file,
135
133
  suite_title,
136
134
  suite_id,
137
135
  test_id,
138
136
  } = testData;
139
137
  let { message = '' } = testData;
140
138
 
141
- const uploadedFiles = [];
142
-
143
- let stack = '';
144
139
 
140
+ let errorFormatted = '';
145
141
  if (error) {
146
- stack = this.formatError(error) || '';
142
+ errorFormatted += this.formatError(error) || '';
147
143
  message = error?.message;
148
144
  }
149
- if (steps) {
150
- stack = this.formatSteps(stack, steps);
151
- }
152
145
 
153
- stack += testData.stack || '';
154
-
155
- // ATTACH LOGS from storage
156
- // in some cases (e.g. using cucumber) logger instance becomes empty object, if import at the top of the file
157
- const logger = require('./storages/logger');
158
- const testLogs = logger.getLogs(test_id);
159
- // debug(`Test logs for ${test_id}:\n`, testLogs);
160
- if (stack) stack += '\n\n';
161
- stack += testLogs ? `${chalk.bold('Logs:')}\n${testLogs}` : '';
146
+ // Attach logs
147
+ const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
162
148
 
163
149
  // GET ARTIFACTS from storage
164
- const artifactFiles = artifactStorage.get(test_id);
165
- if (artifactFiles) files.push(...artifactFiles);
150
+ // const artifactFiles = artifactStorage.get(test_id);
151
+ // if (artifactFiles) files.push(...artifactFiles);
166
152
 
167
- // GET KEY-VALUEs from storage
168
- const keyValueStorage = require('./storages/key-value-storage');
169
- const keyValues = keyValueStorage.get(test_id);
153
+ // // GET KEY-VALUEs from storage
154
+ // const keyValueStorage = require('./storages/key-value-storage');
155
+ // const keyValues = keyValueStorage.get(test_id);
156
+
157
+ const uploadedFiles = [];
170
158
 
171
159
  for (const file of files) {
172
160
  uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
@@ -187,9 +175,8 @@ class Client {
187
175
  files,
188
176
  steps,
189
177
  status,
190
- stack,
178
+ stack: fullLogs,
191
179
  example,
192
- file,
193
180
  code,
194
181
  title,
195
182
  suite_title,
@@ -198,9 +185,11 @@ class Client {
198
185
  message,
199
186
  run_time: parseFloat(time),
200
187
  artifacts,
201
- meta: keyValues,
188
+ // meta: keyValues,
202
189
  };
203
190
 
191
+ debug('Adding test run...', data);
192
+
204
193
  this.queue = this.queue.then(() =>
205
194
  Promise.all(
206
195
  this.pipes.map(async p => {
@@ -252,15 +241,27 @@ class Client {
252
241
  return this.queue;
253
242
  }
254
243
 
255
- formatSteps(stack, steps) {
256
- return stack ? `${steps}\n\n${chalk.bold.red('################[ Failure ]################')}\n${stack}` : steps;
244
+ /**
245
+ * Returns the formatted stack including the stack trace, steps, and logs.
246
+ * @returns {string}
247
+ */
248
+ formatLogs({ error, steps, logs }) {
249
+ error = error?.trim();
250
+ steps = steps?.trim();
251
+ logs = logs?.trim();
252
+
253
+ let testLogs = '';
254
+ if (steps) testLogs += `${chalk.bold.blue('################[ Steps ]################')}\n${steps}`;
255
+ if (logs) testLogs += `\n\n${chalk.bold.gray('################[ Logs ]################')}\n${logs}`;
256
+ if (error) testLogs += `\n\n${chalk.bold.red('################[ Failure ]################')}\n${error}`;
257
+ return testLogs;
257
258
  }
258
259
 
259
260
  formatError(error, message) {
260
261
  if (!message) message = error.message;
261
262
  if (error.inspect) message = error.inspect() || '';
262
263
 
263
- let stack = `\n${chalk.bold(message)}\n`;
264
+ let stack = `${message}\n`;
264
265
 
265
266
  // diffs for mocha, cypress, codeceptjs style
266
267
  if (error.actual && error.expected) {
@@ -277,11 +278,11 @@ class Client {
277
278
  const record = createCallsiteRecord({
278
279
  forError: error,
279
280
  isCallsiteFrame: frame => {
280
- if (customFilter && minimatch(frame.getFileName(), customFilter)) return false;
281
+ if (customFilter && minimatch(frame.getFileName(), customFilter)) return false;
281
282
  if (hasFrame) return false;
282
283
  if (isNotInternalFrame(frame)) hasFrame = true;
283
284
  return hasFrame;
284
- }
285
+ },
285
286
  });
286
287
  if (record && !record.filename.startsWith('http')) {
287
288
  stack += record.renderSync({ stackFilter: isNotInternalFrame });
@@ -294,9 +295,11 @@ class Client {
294
295
  }
295
296
 
296
297
  function isNotInternalFrame(frame) {
297
- return frame.getFileName().includes(sep) &&
298
+ return (
299
+ frame.getFileName().includes(sep) &&
298
300
  !frame.getFileName().includes('node_modules') &&
299
301
  !frame.getFileName().includes('internal')
302
+ );
300
303
  }
301
304
 
302
305
  module.exports = Client;
@@ -175,14 +175,13 @@ class TestomatioPipe {
175
175
  * @returns
176
176
  */
177
177
  addTest(data) {
178
+ debug('Adding test...');
178
179
  if (!this.isEnabled) return;
179
180
  if (!this.runId) return;
180
181
  data.api_key = this.apiKey;
181
182
  data.create = this.createNewTests;
182
183
  const json = JsonCycle.stringify(data);
183
184
 
184
- debug('Adding test', json);
185
-
186
185
  return this.axios.post(`/api/reporter/${this.runId}/testrun`, json, {
187
186
  maxContentLength: Infinity,
188
187
  maxBodyLength: Infinity,
@@ -1,6 +1,6 @@
1
- const logger = require('./storages/logger');
2
- const artifactStorage = require('./storages/artifact-storage');
3
- const keyValueStorage = require('./storages/key-value-storage');
1
+ const { logger } = require('./storages/logger');
2
+ const { artifactStorage } = require('./storages/artifact-storage');
3
+ const { keyValueStorage } = require('./storages/key-value-storage');
4
4
 
5
5
  /**
6
6
  * Stores path to file as artifact and uploads it to the S3 storage
@@ -21,7 +21,7 @@ function logMessage(...args) {
21
21
 
22
22
  /**
23
23
  * Similar to "log" function but marks message in report as a step
24
- * @param {*} message
24
+ * @param {*} message
25
25
  */
26
26
  function addStep(message) {
27
27
  logger.step(message);
@@ -29,15 +29,26 @@ function addStep(message) {
29
29
 
30
30
  /**
31
31
  * Add key-value pair(s) to the test report
32
- * @param {*} keyValue
32
+ * @param {*} keyValue
33
33
  */
34
34
  function setKeyValue(keyValue) {
35
35
  keyValueStorage.put(keyValue);
36
36
  }
37
37
 
38
+ /**
39
+ *
40
+ * @param {string} context – test id or test title or suite title + test title
41
+ */
42
+ function _setContext(context) {
43
+ logger.setContext(context);
44
+ keyValueStorage.setContext(context);
45
+ artifactStorage.setContext(context);
46
+ }
47
+
38
48
  module.exports = {
39
49
  artifact: saveArtifact,
40
50
  log: logMessage,
41
51
  step: addStep,
42
52
  keyValue: setKeyValue,
53
+ _setContext,
43
54
  };
package/lib/reporter.js CHANGED
@@ -1,4 +1,4 @@
1
- const logger = require('./storages/logger');
1
+ const { logger } = require('./storages/logger');
2
2
  const TestomatClient = require('./client');
3
3
  const TRConstants = require('./constants');
4
4
 
@@ -20,4 +20,5 @@ module.exports.testomat = {
20
20
  log: reporterFunctions.log,
21
21
  step: reporterFunctions.step,
22
22
  meta: reporterFunctions.keyValue,
23
+ _setContext: reporterFunctions._setContext,
23
24
  };
@@ -5,24 +5,36 @@ const DataStorage = require('./data-storage');
5
5
  * Artifact storage is supposed to store file paths
6
6
  */
7
7
  class ArtifactStorage {
8
+ static #instance;
9
+
10
+ #context;
8
11
 
9
12
  // there is autocompletion for the class methods if implemented singleton this way
10
13
  constructor() {
11
14
  this.dataStorage = new DataStorage('artifact');
12
-
15
+
13
16
  // singleton
14
- if (!ArtifactStorage.instance) {
15
- ArtifactStorage.instance = this;
17
+ if (!ArtifactStorage.#instance) {
18
+ ArtifactStorage.#instance = this;
16
19
  }
17
20
  }
18
21
 
19
- // singleton
22
+ /**
23
+ * Singleton
24
+ * @returns {ArtifactStorage}
25
+ */
20
26
  static getInstance() {
21
- if (!ArtifactStorage.instance) {
22
- ArtifactStorage.instance = new ArtifactStorage();
27
+ if (!this.#instance) {
28
+ this.#instance = new ArtifactStorage();
23
29
  }
30
+ return this.#instance;
31
+ }
24
32
 
25
- return ArtifactStorage.instance;
33
+ /**
34
+ * @param {string} context - suite title + test title
35
+ */
36
+ setContext(context) {
37
+ this.#context = context;
26
38
  }
27
39
 
28
40
  /**
@@ -31,6 +43,7 @@ class ArtifactStorage {
31
43
  * @param {*} context testId or test title
32
44
  */
33
45
  put(data, context = null) {
46
+ context = context || this.#context;
34
47
  if (!data) return;
35
48
  debug('Save artifact:', data);
36
49
  this.dataStorage.putData(data, context);
@@ -63,8 +76,4 @@ class ArtifactStorage {
63
76
  }
64
77
  }
65
78
 
66
- ArtifactStorage.instance = null;
67
-
68
- // const artifactStorage = ArtifactStorage.getInstance();
69
- const artifactStorage = new ArtifactStorage();
70
- module.exports = artifactStorage;
79
+ module.exports.artifactStorage = ArtifactStorage.getInstance();