@testomatio/reporter 1.0.2 → 1.0.3

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.
@@ -6,6 +6,7 @@ const { join } = require('path');
6
6
  const JestReporter = require('./adapter/jest');
7
7
  const { TESTOMAT_TMP_STORAGE } = require('./constants');
8
8
  const { fileSystem } = require('./util');
9
+ const getTestIdFromTestTitle = require('./util').parseTest;
9
10
 
10
11
  class DataStorage {
11
12
  /**
@@ -13,52 +14,48 @@ class DataStorage {
13
14
  * Stores data to global variable or to file depending on what is applicable for current test runner.
14
15
  * dataType: 'log' | 'artifact' | ...
15
16
  * @param {*} dataType storage type (like 'log', 'artifact'); could be any string, used only to define file path
16
- * @param {*} params
17
17
  */
18
- constructor(dataType, params = {}) {
19
- if (!dataType) throw new Error('Data type is required when creating data storage');
18
+ constructor(dataType) {
19
+ // if (!dataType) throw new Error('Data type is required when creating data storage');
20
20
  this.dataType = dataType || 'data';
21
- this.dataDirName = `${dataType}s`;
22
- this.isFileStorage = params?.isFileStorage ?? !global.testomatioDataStore;
21
+ this.dataDirName = this.dataType;
23
22
 
24
- /*
25
- FYI:
26
- If this storage instance is used within test runner, it works fine.
27
- But if, for example, we create storage instance inside the testomatio client (to get stored data),
28
- the environment could be different (not already a test runner env).
29
- Thus, checking environment is only reasonable when you put data to starage
30
- and potentially useless when get data from storage
31
- */
32
- this.runningEnvironment = this.getRunningEnviroment();
33
- if (this.runningEnvironment === 'jest') this.isFileStorage = true;
23
+ this._refreshStorageType();
34
24
 
35
- if (this.isFileStorage) {
36
- this.dataDirPath = path.join(TESTOMAT_TMP_STORAGE.mainDir, this.dataDirName);
37
- fileSystem.createDir(this.dataDirPath);
38
- }
25
+ // dir is created in any case (not to recheck its existence every time)
26
+ this.dataDirPath = path.join(TESTOMAT_TMP_STORAGE.mainDir, this.dataDirName);
27
+ fileSystem.createDir(this.dataDirPath);
39
28
 
40
29
  debug(`Data storage mode: ${this.isFileStorage ? 'file' : 'memory'}`);
41
30
  }
42
31
 
43
- // TODO: implement
44
- getTestIdFromContext(context) { // eslint-disable-line
45
- // eslint-disable-line
46
- // return testId;
47
- }
48
-
49
32
  /**
50
33
  * Try to define the running environment. Not 100% reliable and used as additional check
51
34
  * @returns jest | mocha | ...
52
35
  */
53
36
  getRunningEnviroment() {
37
+ // jest
54
38
  if (process.env.JEST_WORKER_ID) return 'jest';
39
+
40
+ // mocha
55
41
  try {
56
42
  // @ts-expect-error mocha is defined only in mocha environment
57
43
  if (typeof mocha !== 'undefined') return 'mocha';
58
44
  } catch (e) {
59
45
  // ignore
60
46
  }
61
- return undefined;
47
+
48
+ // codeceptjs
49
+ // @ts-expect-error codeceptjs is defined only in codeceptjs environment
50
+ if (global.codeceptjs) return 'codeceptjs';
51
+
52
+ // others
53
+ // 'cucumber:current', 'cucumber:legacy'
54
+ if (global.testomatioRunningEnvironment) return global.testomatioRunningEnvironment;
55
+
56
+ if (process.env.PLAYWRIGHT_TEST_BASE_URL) return 'playwright';
57
+
58
+ return null;
62
59
  }
63
60
 
64
61
  /**
@@ -68,21 +65,24 @@ class DataStorage {
68
65
  * @returns
69
66
  */
70
67
  putData(data, context = null) {
71
- // this.isFileStorage = !global.testomatioDataStore;
68
+ this._refreshStorageType();
72
69
 
73
- let testId = null;
74
- if (typeof context === 'string') {
75
- testId = context;
76
- } else {
77
- // TODO: derive testId from context
78
- // testId = context...
79
- }
70
+ let testId = this._tryToRetrieveTestId(context) || null;
80
71
 
81
- // try to get testId for Jest
72
+ if (this.runningEnvironment === 'codeceptjs') testId = testId ?? global.testomatioDataStore?.currentlyRunningTestId;
82
73
  if (this.runningEnvironment === 'jest') testId = testId ?? JestReporter.getIdOfCurrentlyRunningTest();
74
+ // logs in playwright are gathered by pw framwork itself
75
+ if (this.runningEnvironment === 'playwright' && this.dataType === 'log') return;
76
+
77
+ // get id from global store
78
+ if (global.testomatioDataStore && global.testomatioDataStore.currentlyRunningTestId)
79
+ testId = testId ?? global.testomatioDataStore?.currentlyRunningTestId;
83
80
 
84
81
  // if testId is not provided, data is be saved to `{dataType}_other` file;
85
- if (!testId) testId = 'other';
82
+ if (!testId) {
83
+ debug(`No test id provided for ${this.dataType} data: ${data}`);
84
+ return;
85
+ }
86
86
 
87
87
  if (this.isFileStorage) {
88
88
  this._putDataToFile(data, testId);
@@ -92,13 +92,17 @@ class DataStorage {
92
92
  }
93
93
 
94
94
  /**
95
- * Returns data, stored for specific testId (or data which was stored without test id specified)
95
+ * Returns data, stored for specific testId (or data which was stored without test id specified).
96
+ * This method will get data from global variable. But if it is not available, it will try to get data from file.
97
+ * Thus, good approach is to remove file storage folder before each test run (and after, for sure).
96
98
  *
97
- * (Don't try to guess the execution environment (e.g. test runner) inside this method, it could be any)
99
+ * Defining the execution environment is not guaranteed! Is used only as additional check.
98
100
  * @param {*} context could be testId or any context (test, suite, etc) which is used to define testId
99
101
  * @returns
100
102
  */
101
103
  getData(context) {
104
+ this._refreshStorageType();
105
+
102
106
  let testId = null;
103
107
  if (typeof context === 'string') {
104
108
  testId = context;
@@ -109,18 +113,71 @@ class DataStorage {
109
113
 
110
114
  if (!testId) {
111
115
  debug(`Cannot get test id from passed context:\n}`, context);
116
+ return null;
112
117
  }
113
118
 
114
- if (this.isFileStorage) {
115
- return this._getDataFromFile(testId);
116
- }
119
+ let testData = null;
120
+
117
121
  if (global?.testomatioDataStore) {
118
- return this._getDataFromGlobalVar(testId);
122
+ testData = this._getDataFromGlobalVar(testId);
123
+ if (testData) return testData;
119
124
  }
125
+
126
+ if (this.isFileStorage || !testData) {
127
+ testData = this._getDataFromFile(testId);
128
+ if (testData) return testData;
129
+ }
130
+
120
131
  debug(`No ${this.dataType} data for test id ${testId} in both file and global variable`);
132
+ return testData;
133
+ }
134
+
135
+ /**
136
+ * This method is named as "try" because it does not guarantee that test id could be retrieved.
137
+ * Context could be anything (test, suite, string, etc) which is used to define testId.
138
+ * Or it could represent any other entity (which does not contain test id).
139
+ * @param {*} context
140
+ */
141
+ _tryToRetrieveTestId(context) {
142
+ if (!context) return null;
143
+ this._refreshStorageType();
144
+
145
+ if (this.runningEnvironment === 'playwright' || context?.title) {
146
+ // context is testInfo
147
+ const testId = getTestIdFromTestTitle(context.title);
148
+ if (testId) return testId;
149
+ }
150
+
151
+ if (typeof context === 'string') {
152
+ const testId = getTestIdFromTestTitle(context);
153
+ if (testId) return testId;
154
+ }
155
+
121
156
  return null;
122
157
  }
123
158
 
159
+ /**
160
+ * Refreshes storage type (file or global variable) depending on current environment
161
+ * This method should be run on each attempt to put or get data from/to storage
162
+ * Because storage instance is created before the test runner is started. And storage type could be changed
163
+ */
164
+ _refreshStorageType() {
165
+ this.isFileStorage = !global.testomatioDataStore;
166
+ /*
167
+ FYI:
168
+ If this storage instance is used within test runner, it works fine.
169
+ But if, for example, we create storage instance inside the testomatio client (to get stored data),
170
+ the environment could differs (not already a test runner environment).
171
+ Thus, checking environment is only reasonable when you put data to starage
172
+ (to define where to put data - to global variable or to file)
173
+ and potentially useless when getting data from storage
174
+ */
175
+ this.runningEnvironment = this.getRunningEnviroment();
176
+
177
+ // some test frameworks do not persist global variables, thus file storage is used for them
178
+ if (this.runningEnvironment === 'jest') this.isFileStorage = true;
179
+ }
180
+
124
181
  _getDataFromGlobalVar(testId) {
125
182
  try {
126
183
  if (global?.testomatioDataStore[this.dataDirName]) {
@@ -128,7 +185,7 @@ class DataStorage {
128
185
  debug(`Data for test id ${testId}:\n${testData}`);
129
186
  return testData;
130
187
  }
131
- debug(`No ${this.dataType} data for test id ${testId}`);
188
+ debug(`No ${this.dataType} data for test id ${testId} in <global> storage`);
132
189
  return null;
133
190
  } catch (e) {
134
191
  // there could be no data, ignore
@@ -137,13 +194,13 @@ class DataStorage {
137
194
 
138
195
  _getDataFromFile(testId) {
139
196
  try {
140
- const filepath = join(TESTOMAT_TMP_STORAGE.mainDir, `${this.dataType}_${testId}`);
197
+ const filepath = join(this.dataDirPath, `${this.dataType}_${testId}`);
141
198
  if (fs.existsSync(filepath)) {
142
199
  const testData = fs.readFileSync(filepath, 'utf-8');
143
200
  debug(`Data for test id ${testId}:\n${testData}`);
144
201
  return testData;
145
202
  }
146
- debug(`No ${this.dataType} data for test id ${testId}`);
203
+ debug(`No ${this.dataType} data for test id ${testId} in <file> storage`);
147
204
  return null;
148
205
  } catch (e) {
149
206
  // there could be no data, ignore
@@ -153,14 +210,16 @@ class DataStorage {
153
210
 
154
211
  _putDataToGlobalVar(data, testId) {
155
212
  debug('Saving data to global variable for test', testId, ':\n', data, '\n');
156
- global.testomatioDataStore[this.dataDirName] = {};
157
- global.testomatioDataStore[this.dataDirName][testId] = data;
213
+ if (!global.testomatioDataStore[this.dataDirName]) global.testomatioDataStore[this.dataDirName] = {};
214
+ global.testomatioDataStore[this.dataDirName][testId] // eslint-disable-line no-unused-expressions
215
+ ? (global.testomatioDataStore[this.dataDirName][testId] += `\n${data}`)
216
+ : (global.testomatioDataStore[this.dataDirName][testId] = data);
158
217
  }
159
218
 
160
219
  _putDataToFile(data, testId) {
161
220
  if (typeof data !== 'string') data = JSON.stringify(data);
162
221
  const filename = `${this.dataType}_${testId}`;
163
- const filepath = join(TESTOMAT_TMP_STORAGE.mainDir, filename);
222
+ const filepath = join(this.dataDirPath, filename);
164
223
  debug('Saving data to file for test', testId, 'to', filepath, ':\n', data, '\n');
165
224
 
166
225
  // TODO: handle multiple invocations of JSON.stringify.
@@ -177,4 +236,9 @@ module.exports.DataStorage = DataStorage;
177
236
  // TODO: rewrite client - everything regarding storing artifacts
178
237
  // TODO: try to define adapter inside client
179
238
  // TODO: use .env
180
- // TODO: ability to intercept multiple loggers
239
+ // TODO: ability to intercept multiple loggers (upd: no need)
240
+
241
+
242
+ /* Cypress
243
+ Parallelization is only available if using Cypress Dashboard??
244
+ */
package/lib/logger.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const chalk = require('chalk');
2
2
  const debug = require('debug')('@testomatio/reporter:logger');
3
+ const _ = require('lodash');
3
4
  const { DataStorage } = require('./dataStorage');
4
5
 
5
6
  const LOG_METHODS = ['assert', 'debug', 'error', 'info', 'log', 'trace', 'warn'];
@@ -24,61 +25,41 @@ class Logger {
24
25
  // _originalUserLogger used to output logs to console by the user logger
25
26
  // _loggerToIntercept intercepted and reassigned immediately when added
26
27
 
27
- constructor(params = {}) {
28
+ constructor() {
28
29
  // set default logger to be used in log, warn, error, etc methods
29
30
  this._originalUserLogger = { ...console };
30
31
 
31
- this.dataStorage = new DataStorage('log', params);
32
+ this.dataStorage = new DataStorage('log');
32
33
  this.logLevel = process?.env?.LOG_LEVEL?.toUpperCase() || 'ALL';
33
34
 
34
35
  // commented because prefer to use "intercept" method
35
36
  // if (params?.logger) this._loggerToIntercept = params.logger;
36
37
 
37
- this.intercept(this._loggerToIntercept);
38
+ // intercept console by default
39
+ this.intercept(console);
38
40
 
39
41
  // singleton
40
42
  if (!Logger.instance) {
41
43
  Logger.instance = this;
42
44
  }
43
- }
44
45
 
45
- /**
46
- * Tagget template literal. Allows to use different syntaxes:
47
- * 1. Tagget template: $`text ${someVar}`
48
- * 2. Standard: $(`text ${someVar}`)
49
- * 3. Standard with multiple arguments: $('text', someVar)
50
- */
51
- _log(strings, ...args) {
52
- let logs;
53
- // this block means tagged template is used (syntax like $`text ${someVar}`)
54
- if (Array.isArray(strings) && strings.length === args.length + 1) {
55
- logs = strings.reduce(
56
- (result, current, index) =>
57
- result +
58
- current +
59
- // strings are splitted by args when use tagged template, thus we add arg after each string
60
- // it looks like: `string1 arg1 string2 arg2 string3`
61
- (args[index] !== undefined // eslint-disable-line no-nested-ternary
62
- ? typeof args[index] === 'string'
63
- ? args[index] // add arg as it is
64
- : this._strinfifyLogs(args[index]) // stringify arg
65
- : ' '), // add space if no arg after string
66
- // initial accumulator value
67
- '',
68
- );
69
- } else {
70
- // this block means arguments syntax is used (syntax like $('text', someVar))
71
- // in this case strings represents just a first argument
72
- logs = this._strinfifyLogs(strings, ...args);
73
- }
74
- this.dataStorage.putData(logs);
46
+ this._helpers = {
47
+ parseLastArgToGetTestId: (...args) => {
48
+ try {
49
+ return this.dataStorage._tryToRetrieveTestId(args.at(-1));
50
+ } catch (e) {
51
+ // node 14 support
52
+ return this.dataStorage._tryToRetrieveTestId(args[args.length - 1]);
53
+ }
54
+ },
55
+ };
75
56
  }
76
57
 
77
58
  /**
78
59
  * Allows you to define a step inside a test. Step name is attached to the report and
79
60
  * helps to understand the test flow.
80
- * @param {*} strings
81
- * @param {...any} values
61
+ * @param {*} strings
62
+ * @param {...any} values
82
63
  */
83
64
  step(strings, ...values) {
84
65
  let logs = '';
@@ -98,15 +79,20 @@ class Logger {
98
79
  * @returns
99
80
  */
100
81
  getLogs(context) {
101
- return this.dataStorage.getData(context);
82
+ const logs = this.dataStorage.getData(context);
83
+ return logs || '';
102
84
  }
103
85
 
104
- _strinfifyLogs(...args) {
86
+ _stringifyLogs(...args) {
105
87
  const logs = [];
106
88
  // stringify everything except strings
107
89
  for (const arg of args) {
90
+ // ignore empty strings
91
+ if (arg === '') continue;
108
92
  if (typeof arg === 'string') {
109
93
  logs.push(arg);
94
+ } else if (Array.isArray(arg)) {
95
+ logs.push(arg.join(' '));
110
96
  } else {
111
97
  try {
112
98
  // eslint-disable-next-line no-unused-expressions
@@ -120,123 +106,159 @@ class Logger {
120
106
  return logs.join(' ');
121
107
  }
122
108
 
123
- assert(...args) {
124
- const level = 'ERROR';
125
- const severity = LEVELS[level].severity;
126
- if (severity < LEVELS[this.logLevel]?.severity) return;
109
+ /**
110
+ * Tagget template literal. Allows to use different syntaxes:
111
+ * 1. Tagget template: $`text ${someVar}`
112
+ * 2. Standard: $(`text ${someVar}`)
113
+ * 3. Standard with multiple arguments: $('text', someVar)
114
+ */
115
+ _log(strings, ...args) {
116
+ // entity which is used to define testId
117
+ let context = null;
118
+ // last argument could contain testId
119
+ const testId = this._helpers.parseLastArgToGetTestId(...args);
120
+ if (testId) {
121
+ // last arg is test id, do not log it
122
+ context = args.pop();
123
+ }
127
124
 
128
- const logs = this._strinfifyLogs(...args);
129
- this.dataStorage.putData(logs);
130
- try {
131
- this._originalUserLogger.log(`Assertion result: `, ...args);
132
- } catch (e) {
133
- // method could be unexisting, ignore error
125
+ // filter empty strings
126
+ strings = strings.filter(item => item !== '');
127
+
128
+ let logs;
129
+ // this block means tagged template is used (syntax like $`text ${someVar}`)
130
+ if (Array.isArray(strings) && strings.length === args.length + 1) {
131
+ logs = strings.reduce(
132
+ (result, current, index) =>
133
+ result +
134
+ current +
135
+ // strings are splitted by args when use tagged template, thus we add arg after each string
136
+ // it looks like: `string1 arg1 string2 arg2 string3`
137
+ (args[index] !== undefined // eslint-disable-line no-nested-ternary
138
+ ? typeof args[index] === 'string'
139
+ ? args[index] // add arg as it is
140
+ : this._stringifyLogs(args[index]) // stringify arg
141
+ : ' '), // add space if no arg after string
142
+ // initial accumulator value
143
+ '',
144
+ );
145
+ } else {
146
+ // this block means arguments syntax is used (syntax like $('text', someVar))
147
+ // in this case strings represents just a first argument
148
+ logs = this._stringifyLogs(strings, ...args);
134
149
  }
150
+ this._originalUserLogger.log(logs);
151
+ this.dataStorage.putData(logs, context);
135
152
  }
136
153
 
137
- debug(...args) {
138
- const level = 'DEBUG';
154
+ /**
155
+ * This function is a wrapper for all logging methods (not to repeat the same code)
156
+ * @param {*} argsArray
157
+ * @param {*} level
158
+ * @returns
159
+ */
160
+ _logWrapper(argsArray, level) {
161
+ if (!argsArray.length) return;
162
+
163
+ // entity which is used to define testId
164
+ let context = null;
165
+ // last argument could contain testId
166
+ const testId = this._helpers.parseLastArgToGetTestId(...argsArray);
167
+ if (testId) {
168
+ // last arg is test id, do not log it
169
+ context = argsArray.pop();
170
+ }
171
+
139
172
  const severity = LEVELS[level].severity;
140
173
  if (severity < LEVELS[this.logLevel]?.severity) return;
141
174
 
142
- if (this.logLevel === 'error' || this.logLevel === 'warn') return;
143
- const logs = this._strinfifyLogs(...args);
175
+ const logs = this._stringifyLogs(...argsArray);
144
176
  const colorizedLogs = chalk[LEVELS[level].color](logs);
145
- this.dataStorage.putData(colorizedLogs);
177
+ this.dataStorage.putData(colorizedLogs, context);
146
178
  try {
147
- this._originalUserLogger.debug(...args);
179
+ // level.toLowerCase() represents method name (log, warn, error, etc)
180
+ this._originalUserLogger[level.toLowerCase()](colorizedLogs);
148
181
  } catch (e) {
149
182
  // method could be unexisting, ignore error
150
183
  }
151
184
  }
152
185
 
153
- error(...args) {
186
+ assert(...args) {
187
+ // sometimes user invokes logger without any arguments passed
188
+ if (!args.length) return;
189
+
154
190
  const level = 'ERROR';
155
191
  const severity = LEVELS[level].severity;
156
192
  if (severity < LEVELS[this.logLevel]?.severity) return;
157
193
 
158
- const logs = this._strinfifyLogs(...args);
159
- const colorizedLogs = chalk[LEVELS[level].color](logs);
160
- this.dataStorage.putData(colorizedLogs);
194
+ const logs = this._stringifyLogs(...args);
195
+ this.dataStorage.putData(logs);
161
196
  try {
162
- this._originalUserLogger.error(...args);
197
+ this._originalUserLogger.log(`Assertion result: `, ...args);
163
198
  } catch (e) {
164
199
  // method could be unexisting, ignore error
165
200
  }
166
201
  }
167
202
 
168
- info(...args) {
169
- const level = 'INFO';
170
- const severity = LEVELS[level].severity;
171
- if (severity < LEVELS[this.logLevel]?.severity) return;
203
+ debug(...args) {
204
+ this._logWrapper(args, 'DEBUG');
205
+ }
172
206
 
173
- const logs = this._strinfifyLogs(...args);
174
- this.dataStorage.putData(logs);
175
- try {
176
- this._originalUserLogger.info(...args);
177
- } catch (e) {
178
- // method could be unexisting, ignore error
179
- }
207
+ error(...args) {
208
+ this._logWrapper(args, 'ERROR');
180
209
  }
181
210
 
182
- log(...args) {
183
- const level = 'INFO';
184
- const severity = LEVELS[level].severity;
185
- if (severity < LEVELS[this.logLevel]?.severity) return;
211
+ info(...args) {
212
+ this._logWrapper(args, 'INFO');
213
+ }
186
214
 
187
- const logs = this._strinfifyLogs(...args);
188
- this.dataStorage.putData(logs);
189
- try {
190
- this._originalUserLogger.log(...args);
191
- } catch (e) {
192
- // method could be unexisting, ignore error
193
- }
215
+ log(...args) {
216
+ this._logWrapper(args, 'LOG');
194
217
  }
195
218
 
196
219
  trace(...args) {
197
- const level = 'TRACE';
198
- const severity = LEVELS[level].severity;
199
- if (severity < LEVELS[this.logLevel]?.severity) return;
200
-
201
- const logs = this._strinfifyLogs(...args);
202
- const colorizedLogs = chalk[LEVELS[level].color](logs);
203
- this.dataStorage.putData(colorizedLogs);
204
- try {
205
- this._originalUserLogger.trace(...args);
206
- } catch (e) {
207
- // method could be unexisting, ignore error
208
- }
220
+ this._logWrapper(args, 'TRACE');
209
221
  }
210
222
 
211
223
  warn(...args) {
212
- const level = 'WARN';
213
- const severity = LEVELS[level].severity;
214
- if (severity < LEVELS[this.logLevel]?.severity) return;
215
-
216
- const logs = this._strinfifyLogs(...args);
217
- const colorizedLogs = chalk[LEVELS[level].color](logs);
218
- this.dataStorage.putData(colorizedLogs);
219
- try {
220
- this._originalUserLogger.warn(...args);
221
- } catch (e) {
222
- // method could be unexisting, ignore error
223
- }
224
+ this._logWrapper(args, 'WARN');
224
225
  }
225
226
 
226
227
  /**
227
228
  * Intercepts user logger messages.
228
229
  * When call this method, Logger start to control the user logger,
229
- * but almost nothing is changed for user ragarding the console output (like log level set by user)
230
+ * but almost nothing is changed for user regarding the console output (like log level set by user)
230
231
  * (until multiple loggers are intercepted,
231
232
  * in this case only the last intercepted logger will be used as user console output).
232
233
  * @param {*} userLogger
233
234
  */
234
235
  intercept(userLogger) {
235
236
  if (!userLogger) return;
236
- debug(`Intercepting user logger`);
237
237
 
238
- // save original user logger to use it for logging (last intercepted will be used for console output)
239
- this._originalUserLogger = { ...userLogger };
238
+ /* prevent multiple console interceptions (cause infinite loop)
239
+ actual only for "console", because its used as default output and is intercepted by default */
240
+ const isUserLoggerConsole = userLogger.toString?.().toLowerCase() === '[object console]';
241
+ if (isUserLoggerConsole && process.env.TESTOMATIO_LOGGER_CONSOLE_INTERCEPTED) {
242
+ debug(`Try to intercept console, but it is already intercepted`);
243
+ return;
244
+ }
245
+ // prevent other loggers multiple interceptions
246
+ if (_.isEqual(userLogger, this._originalUserLogger)) {
247
+ debug(`Try to intercept user logger, but it is already intercepted`);
248
+ return;
249
+ }
250
+
251
+ process.env.TESTOMATIO_LOGGER_CONSOLE_INTERCEPTED = 'true';
252
+ debug(isUserLoggerConsole ? 'console intercepted' : 'User logger intercepted');
253
+
254
+ /* logs could be intercepted for playwright only if console provides output, thus adding this exception.
255
+ it means default _originalUserLogger (console by default) will be used to provide output
256
+ (for other frameworks user logger will be passed for output) */
257
+ if (this.dataStorage.runningEnvironment !== 'playwright') {
258
+ // save original user logger to use it for logging (last intercepted will be used for console output)
259
+ //! TODO: temporary don't oeverride user logger to prevent recursion; console will be used for output
260
+ // this._originalUserLogger = { ...userLogger };
261
+ }
240
262
  this._loggerToIntercept = userLogger;
241
263
 
242
264
  /*
@@ -271,8 +293,9 @@ class Logger {
271
293
  Logger.instance = null;
272
294
 
273
295
  // module.exports.Logger = Logger;
274
- module.exports = new Logger();
296
+ const logger = new Logger();
297
+
298
+ module.exports = logger;
275
299
 
276
300
  // TODO: parse passed arguments as {level: 'str', message: 'str'} because some loggers use such syntax;
277
301
  // upd: did not face such loggers, but still could be useful
278
-
@@ -27,6 +27,7 @@ class TestomatioPipe {
27
27
  return;
28
28
  }
29
29
  debug('Testomatio Pipe: Enabled');
30
+ this.parallel = params.parallel;
30
31
  this.store = store || {};
31
32
  this.title = params.title || process.env.TESTOMATIO_TITLE;
32
33
  this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
@@ -53,6 +54,7 @@ class TestomatioPipe {
53
54
 
54
55
  const runParams = Object.fromEntries(
55
56
  Object.entries({
57
+ parallel: this.parallel,
56
58
  api_key: this.apiKey.trim(),
57
59
  group_title: this.groupTitle,
58
60
  env: this.env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",
@@ -25,6 +25,7 @@
25
25
  "humanize-duration": "^3.27.3",
26
26
  "is-valid-path": "^0.1.1",
27
27
  "json-cycle": "^1.3.0",
28
+ "lodash": "^4.17.21",
28
29
  "lodash.memoize": "^4.1.2",
29
30
  "lodash.merge": "^4.6.2",
30
31
  "uuid": "^9.0.0"
@@ -51,11 +52,12 @@
51
52
  "test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js"
52
53
  },
53
54
  "devDependencies": {
54
- "@cucumber/cucumber": "^8.6.0",
55
+ "@cucumber/cucumber": "^9.3.0",
55
56
  "@redocly/cli": "^1.0.0-beta.125",
56
57
  "@wdio/reporter": "^7.16.13",
57
58
  "chai": "^4.3.6",
58
59
  "codeceptjs": "^3.2.3",
60
+ "cucumber": "^6.0.7",
59
61
  "eslint": "^8.7.0",
60
62
  "eslint-config-airbnb-base": "^15.0.0",
61
63
  "eslint-config-prettier": "^8.3.0",