@testomatio/reporter 1.1.0-beta.mocha-create.9 → 1.1.0-rc.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/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;
@@ -135,41 +134,28 @@ class Client {
135
134
  suite_title,
136
135
  suite_id,
137
136
  test_id,
137
+ manuallyAttachedArtifacts,
138
+ meta,
138
139
  } = testData;
139
140
  let { message = '' } = testData;
140
141
 
141
- const uploadedFiles = [];
142
-
143
- let stack = '';
144
142
 
143
+ let errorFormatted = '';
145
144
  if (error) {
146
- stack = this.formatError(error) || '';
145
+ errorFormatted += this.formatError(error) || '';
147
146
  message = error?.message;
148
147
  }
149
- if (steps) {
150
- stack = this.formatSteps(stack, steps);
151
- }
152
148
 
153
- stack += testData.stack || '';
149
+ // Attach logs
150
+ const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
154
151
 
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}` : '';
152
+ // add artifacts
153
+ if (manuallyAttachedArtifacts?.length) files.push(...manuallyAttachedArtifacts);
162
154
 
163
- // GET ARTIFACTS from storage
164
- const artifactFiles = artifactStorage.get(test_id);
165
- if (artifactFiles) files.push(...artifactFiles);
166
-
167
- // GET KEY-VALUEs from storage
168
- const keyValueStorage = require('./storages/key-value-storage');
169
- const keyValues = keyValueStorage.get(test_id);
155
+ const uploadedFiles = [];
170
156
 
171
- for (const file of files) {
172
- uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
157
+ for (const f of files) {
158
+ uploadedFiles.push(upload.uploadFileByPath(f, this.uuid));
173
159
  }
174
160
 
175
161
  for (const [idx, buffer] of filesBuffers.entries()) {
@@ -187,7 +173,7 @@ class Client {
187
173
  files,
188
174
  steps,
189
175
  status,
190
- stack,
176
+ stack: fullLogs,
191
177
  example,
192
178
  file,
193
179
  code,
@@ -198,9 +184,11 @@ class Client {
198
184
  message,
199
185
  run_time: parseFloat(time),
200
186
  artifacts,
201
- meta: keyValues,
187
+ meta,
202
188
  };
203
189
 
190
+ debug('Adding test run...', data);
191
+
204
192
  this.queue = this.queue.then(() =>
205
193
  Promise.all(
206
194
  this.pipes.map(async p => {
@@ -252,15 +240,27 @@ class Client {
252
240
  return this.queue;
253
241
  }
254
242
 
255
- formatSteps(stack, steps) {
256
- return stack ? `${steps}\n\n${chalk.bold.red('################[ Failure ]################')}\n${stack}` : steps;
243
+ /**
244
+ * Returns the formatted stack including the stack trace, steps, and logs.
245
+ * @returns {string}
246
+ */
247
+ formatLogs({ error, steps, logs }) {
248
+ error = error?.trim();
249
+ steps = steps?.trim();
250
+ logs = logs?.trim();
251
+
252
+ let testLogs = '';
253
+ if (steps) testLogs += `${chalk.bold.blue('################[ Steps ]################')}\n${steps}\n\n`;
254
+ if (logs) testLogs += `${chalk.bold.gray('################[ Logs ]################')}\n${logs}\n\n`;
255
+ if (error) testLogs += `${chalk.bold.red('################[ Failure ]################')}\n${error}`;
256
+ return testLogs;
257
257
  }
258
258
 
259
259
  formatError(error, message) {
260
260
  if (!message) message = error.message;
261
261
  if (error.inspect) message = error.inspect() || '';
262
262
 
263
- let stack = `\n${chalk.bold(message)}\n`;
263
+ let stack = `${message}\n`;
264
264
 
265
265
  // diffs for mocha, cypress, codeceptjs style
266
266
  if (error.actual && error.expected) {
@@ -277,11 +277,11 @@ class Client {
277
277
  const record = createCallsiteRecord({
278
278
  forError: error,
279
279
  isCallsiteFrame: frame => {
280
- if (customFilter && minimatch(frame.getFileName(), customFilter)) return false;
280
+ if (customFilter && minimatch(frame.getFileName(), customFilter)) return false;
281
281
  if (hasFrame) return false;
282
282
  if (isNotInternalFrame(frame)) hasFrame = true;
283
283
  return hasFrame;
284
- }
284
+ },
285
285
  });
286
286
  if (record && !record.filename.startsWith('http')) {
287
287
  stack += record.renderSync({ stackFilter: isNotInternalFrame });
@@ -294,9 +294,11 @@ class Client {
294
294
  }
295
295
 
296
296
  function isNotInternalFrame(frame) {
297
- return frame.getFileName().includes(sep) &&
297
+ return (
298
+ frame.getFileName().includes(sep) &&
298
299
  !frame.getFileName().includes('node_modules') &&
299
300
  !frame.getFileName().includes('internal')
301
+ );
300
302
  }
301
303
 
302
304
  module.exports = Client;
package/lib/config.js CHANGED
@@ -8,6 +8,10 @@ const debug = require('debug')('@testomatio/reporter:config');
8
8
  const dotenv = require('dotenv');
9
9
  const envFileVars = dotenv.config({ path: '.env' }).parsed; */
10
10
 
11
+ const TESTOMATIO = process.env.TESTOMATIO || process.env.TESTOMATIO_API_KEY || process.env.TESTOMATIO_TOKEN || '';
12
+ if (TESTOMATIO === 'undefined') console.error('TESTOMATIO is "undefined". Something went wrong. Contact dev team.');
13
+ process.env.TESTOMATIO = TESTOMATIO;
14
+
11
15
  // select only TESTOMATIO related variables (only to print them in debug)
12
16
  const testomatioEnvVars =
13
17
  Object.keys(process.env)
@@ -22,3 +26,4 @@ debug('TESTOMATIO variables:', testomatioEnvVars);
22
26
  const config = process.env;
23
27
 
24
28
  module.exports = config;
29
+ module.exports.TESTOMATIO = TESTOMATIO;
@@ -0,0 +1,203 @@
1
+ const debug = require('debug')('@testomatio/reporter:storage');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { join } = require('path');
6
+ const { TESTOMAT_TMP_STORAGE_DIR } = require('./constants');
7
+ const { fileSystem, jestHelpers } = require('./utils/utils');
8
+
9
+ class DataStorage {
10
+ static #instance;
11
+
12
+ context;
13
+
14
+ /**
15
+ *
16
+ * @returns {DataStorage}
17
+ */
18
+ static getInstance() {
19
+ if (!this.#instance) {
20
+ this.#instance = new DataStorage();
21
+ }
22
+ return this.#instance;
23
+ }
24
+
25
+ setContext(context) {
26
+ this.context = context;
27
+ }
28
+
29
+ /**
30
+ * Creates data storage instance as singleton
31
+ * Stores data to global variable or to file depending on what is applicable for current test runner (adapter)
32
+ * Recommend to use composition while using this class (instead of inheritance).
33
+ * ! Also the class which will use data storage should be singleton (to avoid data loss).
34
+ */
35
+ constructor() {
36
+ // some frameworks use global variable to store data, some use file storage
37
+ this.isFileStorage = true;
38
+ }
39
+
40
+ #stringToFilename(str) {
41
+ // TODO: use md5 hash later
42
+ const validFilenameRegex = /[^a-zA-Z0-9_.-]/g;
43
+ // replace all characters not in the regex above with underscore, then duplicate underscores removed
44
+ let filename = str.replace(validFilenameRegex, '_').replace(/_{2,}/g, '_').substring(0, 255); // max filename length
45
+ // remove leading and trailing underscores
46
+ filename = filename.replace(/^_+|_+$/g, '');
47
+ return filename;
48
+ }
49
+
50
+ /**
51
+ * Puts any data to storage (file or global variable).
52
+ * If file: stores data as text, if global variable – stores as array of data.
53
+ * @param {'log' | 'artifact' | 'keyvalue'} dataType
54
+ * @param {*} data anything you want to store (string, object, array, etc)
55
+ * @param {*} context could be testId or any context (test name, suite name, including their IDs etc)
56
+ * suite name + test name is used by default
57
+ * @returns
58
+ */
59
+ putData(dataType, data, context = null) {
60
+ if (!dataType || !data) return;
61
+ const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
62
+ fileSystem.createDir(dataDirPath);
63
+
64
+ context = context || this.context || global.testomatioTestTitle || jestHelpers.getIdOfCurrentlyRunningTest();
65
+ if (!context) {
66
+ debug(`No context provided for "${dataType}" data:`, data);
67
+ return;
68
+ }
69
+ context = this.#stringToFilename(context);
70
+
71
+ if (this.isFileStorage) {
72
+ this.#putDataToFile(dataType, data, context);
73
+ } else {
74
+ this.#putDataToGlobalVar(dataType, data, context);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Returns data, stored for specific test/context (or data which was stored without test id specified).
80
+ * This method will get data from global variable and/or from from file (previosly saved with put method).
81
+ *
82
+ * @param {'log' | 'artifact' | 'keyvalue'} dataType
83
+ * @param {string} context
84
+ * @returns {any []} array of data (any type), null (if no data found for context) or string (if data type is log)
85
+ */
86
+ getData(dataType, context) {
87
+ // TODO: think if it could be useful
88
+ // context = context || this.context || global.testomatioTestTitle || jestHelpers.getIdOfCurrentlyRunningTest();
89
+
90
+ if (!context) {
91
+ debug(`Trying to get "${dataType}" data without context`);
92
+ return null;
93
+ }
94
+
95
+ context = this.#stringToFilename(context);
96
+
97
+ let testDataFromFile = [];
98
+ let testDataFromGlobalVar = [];
99
+
100
+ if (global?.testomatioDataStore) {
101
+ testDataFromGlobalVar = this.#getDataFromGlobalVar(dataType, context);
102
+ if (testDataFromGlobalVar) {
103
+ if (testDataFromGlobalVar.length) return testDataFromGlobalVar;
104
+ }
105
+ // don't return nothing if no data in global variable
106
+ }
107
+
108
+ testDataFromFile = this.#getDataFromFile(dataType, context);
109
+
110
+ if (testDataFromFile.length) {
111
+ return testDataFromFile;
112
+ }
113
+ debug(`No "${dataType}" data for context "${context}" in both file and global variable`);
114
+
115
+ // in case no data found for context
116
+ return null;
117
+ }
118
+
119
+ /**
120
+ * @param {'log' | 'artifact' | 'keyvalue'} dataType
121
+ * @param {string} context
122
+ * @returns aray of data (any type)
123
+ */
124
+ #getDataFromGlobalVar(dataType, context) {
125
+ try {
126
+ if (global?.testomatioDataStore[dataType]) {
127
+ const testData = global.testomatioDataStore[dataType][context];
128
+ if (testData) debug(`"${dataType}" data for constext "${context}":`, testData.join(', '));
129
+ return testData || [];
130
+ }
131
+ // debug(`No ${this.dataType} data for context ${context} in <global> storage`);
132
+ return [];
133
+ } catch (e) {
134
+ // there could be no data, ignore
135
+ }
136
+ }
137
+
138
+ /**
139
+ * @param {'log' | 'artifact' | 'keyvalue'} dataType
140
+ * @param {*} context
141
+ * @returns array of data (any type)
142
+ */
143
+ #getDataFromFile(dataType, context) {
144
+ const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
145
+ try {
146
+ const filepath = join(dataDirPath, `${dataType}_${context}`);
147
+ if (fs.existsSync(filepath)) {
148
+ const testDataAsText = fs.readFileSync(filepath, 'utf-8');
149
+ if (testDataAsText) debug(`"${dataType}" data for context "${context}":`, testDataAsText);
150
+ const testDataArr = testDataAsText?.split(os.EOL) || [];
151
+ return testDataArr;
152
+ }
153
+ // debug(`No ${this.dataType} data for ${context} in <file> storage`);
154
+ return [];
155
+ } catch (e) {
156
+ // there could be no data, ignore
157
+ }
158
+ return [];
159
+ }
160
+
161
+ /**
162
+ * Puts data to global variable. Unlike the file storage, stores data in array (file storage just append as string).
163
+ * @param {'log' | 'artifact' | 'keyvalue'} dataType
164
+ * @param {*} data
165
+ * @param {*} context
166
+ */
167
+ #putDataToGlobalVar(dataType, data, context) {
168
+ debug('Saving data to global variable for ', context, ':', data);
169
+ if (!global.testomatioDataStore) global.testomatioDataStore = {};
170
+ if (!global.testomatioDataStore?.[dataType]) global.testomatioDataStore[dataType] = {};
171
+
172
+ if (!global.testomatioDataStore?.[dataType][context]) global.testomatioDataStore[dataType][context] = [];
173
+ global.testomatioDataStore[dataType][context].push(data);
174
+ }
175
+
176
+ /**
177
+ * Puts data to file. Unlike the global variable storage, stores data as string
178
+ * @param {'log' | 'artifact' | 'keyvalue'} dataType
179
+ * @param {*} data
180
+ * @param {string} context
181
+ * @returns
182
+ */
183
+ #putDataToFile(dataType, data, context) {
184
+ const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
185
+ if (typeof data !== 'string') data = JSON.stringify(data);
186
+ const filename = `${dataType}_${context}`;
187
+ const filepath = join(dataDirPath, filename);
188
+ if (!fs.existsSync(dataDirPath)) fileSystem.createDir(dataDirPath);
189
+ debug(`Saving data to file for context "${context}" to ${filepath}. Data: ${JSON.stringify(data)}`);
190
+
191
+ // append new line if file already exists (in this case its definitely includes some data)
192
+ if (fs.existsSync(filepath)) {
193
+ fs.appendFileSync(filepath, os.EOL + data, 'utf-8');
194
+ } else {
195
+ fs.writeFileSync(filepath, data, 'utf-8');
196
+ }
197
+ }
198
+ }
199
+
200
+ module.exports.dataStorage = DataStorage.getInstance();
201
+
202
+ // TODO: consider using fs promises instead of writeSync/appendFileSync to
203
+ // prevent blocking and improve performance (probably queue usage will be required)
@@ -6,8 +6,7 @@ const RubyAdapter = require('./ruby');
6
6
  const CSharpAdapter = require('./csharp');
7
7
 
8
8
  function AdapterFactory(lang, opts) {
9
- if (!lang) return new Adapter(opts);
10
-
9
+
11
10
  if (lang === 'java') {
12
11
  return new JavaAdapter(opts);
13
12
  }
@@ -23,6 +22,8 @@ function AdapterFactory(lang, opts) {
23
22
  if (lang === 'c#' || lang === 'csharp') {
24
23
  return new CSharpAdapter(opts);
25
24
  }
25
+
26
+ return new Adapter(opts);
26
27
  }
27
28
 
28
29
  module.exports = AdapterFactory;
@@ -5,6 +5,7 @@ const JsonCycle = require('json-cycle');
5
5
  const { APP_PREFIX, STATUS } = require('../constants');
6
6
  const { isValidUrl, foundedTestLog } = require('../utils/utils');
7
7
  const { parseFilterParams, generateFilterRequestParams, setS3Credentials, } = require('../utils/pipe_utils');
8
+ const { TESTOMATIO } = require('../config');
8
9
 
9
10
  if (process.env.TESTOMATIO_RUN) {
10
11
  process.env.runId = process.env.TESTOMATIO_RUN;
@@ -20,7 +21,7 @@ class TestomatioPipe {
20
21
  constructor(params, store) {
21
22
  this.isEnabled = false;
22
23
  this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
23
- this.apiKey = params.apiKey || process.env.TESTOMATIO;
24
+ this.apiKey = params.apiKey || TESTOMATIO;
24
25
  debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
25
26
  if (!this.apiKey) {
26
27
  return;
@@ -1,6 +1,9 @@
1
- const logger = require('./storages/logger');
2
- const artifactStorage = require('./storages/artifact-storage');
3
- const keyValueStorage = require('./storages/key-value-storage');
1
+ const { services } = require('./services');
2
+ const { initPlaywrightForStorage } = require('./adapter/playwright');
3
+
4
+ if (process.env.PLAYWRIGHT_TEST_BASE_URL) {
5
+ initPlaywrightForStorage();
6
+ }
4
7
 
5
8
  /**
6
9
  * Stores path to file as artifact and uploads it to the S3 storage
@@ -8,7 +11,7 @@ const keyValueStorage = require('./storages/key-value-storage');
8
11
  */
9
12
  function saveArtifact(data, context = null) {
10
13
  if (!data) return;
11
- artifactStorage.put(data, context);
14
+ services.artifacts.put(data, context);
12
15
  }
13
16
 
14
17
  /**
@@ -16,23 +19,31 @@ function saveArtifact(data, context = null) {
16
19
  * @param {...any} args
17
20
  */
18
21
  function logMessage(...args) {
19
- logger.log(...args);
22
+ services.logger.log(...args);
20
23
  }
21
24
 
22
25
  /**
23
26
  * Similar to "log" function but marks message in report as a step
24
- * @param {*} message
27
+ * @param {*} message
25
28
  */
26
29
  function addStep(message) {
27
- logger.step(message);
30
+ services.logger.step(message);
28
31
  }
29
32
 
30
33
  /**
31
34
  * Add key-value pair(s) to the test report
32
- * @param {*} keyValue
35
+ * @param {*} keyValue
33
36
  */
34
37
  function setKeyValue(keyValue) {
35
- keyValueStorage.put(keyValue);
38
+ services.keyValues.put(keyValue);
39
+ }
40
+
41
+ /**
42
+ *
43
+ * @param {string} context – test id or test title or suite title + test title
44
+ */
45
+ function _setStorageContext(context) {
46
+ services.setContext(context);
36
47
  }
37
48
 
38
49
  module.exports = {
@@ -40,4 +51,5 @@ module.exports = {
40
51
  log: logMessage,
41
52
  step: addStep,
42
53
  keyValue: setKeyValue,
54
+ _setStorageContext,
43
55
  };
package/lib/reporter.js CHANGED
@@ -1,14 +1,20 @@
1
- const logger = require('./storages/logger');
1
+ const { services } = require('./services');
2
2
  const TestomatClient = require('./client');
3
3
  const TRConstants = require('./constants');
4
4
 
5
+ const logger = services.logger;
6
+
7
+ // TODO: should be deprecated, reporter-functions should be used instead
5
8
  const log = logger.templateLiteralLog.bind(logger);
6
9
  const step = logger.step.bind(logger);
10
+
7
11
  const reporterFunctions = require('./reporter-functions');
8
12
 
9
13
  module.exports = {
14
+ // duplications; no difference
10
15
  logger,
11
16
  testomatioLogger: logger,
17
+
12
18
  log,
13
19
  step,
14
20
  TestomatClient,
@@ -20,4 +26,5 @@ module.exports.testomat = {
20
26
  log: reporterFunctions.log,
21
27
  step: reporterFunctions.step,
22
28
  meta: reporterFunctions.keyValue,
29
+ _setStorageContext: reporterFunctions._setStorageContext,
23
30
  };
@@ -1,28 +1,21 @@
1
1
  const debug = require('debug')('@testomatio/reporter:artifact-storage');
2
- const DataStorage = require('./data-storage');
2
+ const { dataStorage } = require('../data-storage');
3
3
 
4
4
  /**
5
5
  * Artifact storage is supposed to store file paths
6
6
  */
7
7
  class ArtifactStorage {
8
+ static #instance;
8
9
 
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
10
+ /**
11
+ * Singleton
12
+ * @returns {ArtifactStorage}
13
+ */
20
14
  static getInstance() {
21
- if (!ArtifactStorage.instance) {
22
- ArtifactStorage.instance = new ArtifactStorage();
15
+ if (!this.#instance) {
16
+ this.#instance = new ArtifactStorage();
23
17
  }
24
-
25
- return ArtifactStorage.instance;
18
+ return this.#instance;
26
19
  }
27
20
 
28
21
  /**
@@ -33,23 +26,20 @@ class ArtifactStorage {
33
26
  put(data, context = null) {
34
27
  if (!data) return;
35
28
  debug('Save artifact:', data);
36
- this.dataStorage.putData(data, context);
29
+ dataStorage.putData('artifact', data, context);
37
30
  }
38
31
 
39
32
  /**
40
33
  * Returns list of artifacts to upload
41
34
  * @param {*} context testId or test context from test runner
42
- * @returns {string | {path: string, type: string, name: string}[]}
35
+ * @returns {(string | {path: string, type: string, name: string})[]}
43
36
  */
44
37
  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;
38
+ let artifacts = dataStorage.getData('artifact', context);
39
+ if (!artifacts || !artifacts.length) return [];
50
40
 
51
41
  artifacts = artifacts.map(artifactData => {
52
- // artifact could be an object ({type, path, name} props) or string
42
+ // artifact could be an object ({type, path, name} props) or string (just path)
53
43
  let artifact;
54
44
  try {
55
45
  artifact = JSON.parse(artifactData);
@@ -59,12 +49,9 @@ class ArtifactStorage {
59
49
  return artifact;
60
50
  });
61
51
  artifacts = artifacts.filter(artifact => !!artifact);
62
- return artifacts.length ? artifacts : null;
52
+ debug(`Artifacts for test ${context}:`, artifacts);
53
+ return artifacts.length ? artifacts : [];
63
54
  }
64
55
  }
65
56
 
66
- ArtifactStorage.instance = null;
67
-
68
- // const artifactStorage = ArtifactStorage.getInstance();
69
- const artifactStorage = new ArtifactStorage();
70
- module.exports = artifactStorage;
57
+ module.exports.artifactStorage = ArtifactStorage.getInstance();
@@ -0,0 +1,13 @@
1
+ const { logger } = require('./logger');
2
+ const { artifactStorage } = require('./artifacts');
3
+ const { keyValueStorage } = require('./key-values');
4
+ const { dataStorage } = require('../data-storage');
5
+
6
+ module.exports.services = {
7
+ logger,
8
+ artifacts: artifactStorage,
9
+ keyValues: keyValueStorage,
10
+ setContext: context => {
11
+ dataStorage.setContext(context);
12
+ },
13
+ };