@testomatio/reporter 1.1.0-beta → 1.2.1-beta

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.
@@ -7,9 +7,8 @@ const { TESTOMAT_ARTIFACT_SUFFIX } = require('./constants');
7
7
  const { specificTestInfo } = require('./util');
8
8
 
9
9
  class ArtifactStorage {
10
-
10
+
11
11
  constructor(params) {
12
- this._tmpPrefix = "tsmt_reporter";
13
12
  this.isFile = false;
14
13
  this.isMemory = true;
15
14
  this.storage = params?.toFile;
@@ -21,7 +20,7 @@ class ArtifactStorage {
21
20
  // so the solution is make it opposite to isMemory (which will be retrieved from global)
22
21
  this.isFile = true;
23
22
  this.isMemory = false;
24
- this.tmpDirFullpath = this.createTestomatTmpDir(this._tmpPrefix);
23
+ this.tmpDirFullpath = this.createTestomatTmpDir(ArtifactStorage._tmpPrefix);
25
24
  debug('SAVE to tmp folder mode enabled!');
26
25
  }
27
26
  }
@@ -113,7 +112,7 @@ class ArtifactStorage {
113
112
 
114
113
  // no need to use multiple dirs; we can implement it later if required
115
114
  static tmpTestomatDirNames() {
116
- const subname = this._tmpPrefix || "tsmt_reporter";
115
+ const subname = ArtifactStorage._tmpPrefix || "tsmt_reporter";
117
116
 
118
117
  return fs.readdirSync(os.tmpdir(), { withFileTypes: true })
119
118
  .filter((item) => item.isDirectory())
@@ -138,4 +137,6 @@ class ArtifactStorage {
138
137
  }
139
138
  }
140
139
 
140
+ ArtifactStorage._tmpPrefix = "tsmt_reporter";
141
+
141
142
  module.exports = ArtifactStorage;
@@ -12,6 +12,7 @@ class JestReporter {
12
12
  }
13
13
 
14
14
  static getIdOfCurrentlyRunningTest() {
15
+ if (!process.env.JEST_WORKER_ID) return null;
15
16
  // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
16
17
  return parseTest(expect?.getState()?.currentTestName) || null; // eslint-disable-line
17
18
  }
package/lib/client.js CHANGED
@@ -12,7 +12,7 @@ const logger = require('./logger');
12
12
  /**
13
13
  * @typedef {import('../types').TestData} TestData
14
14
  * @typedef {import('../types').RunStatus} RunStatus
15
- * @typedef {import('../types').PipeResult} PipeResult
15
+ * @typedef {import('../types').PipeResult} PipeResult
16
16
  */
17
17
 
18
18
  class Client {
@@ -105,6 +105,7 @@ class Client {
105
105
 
106
106
  const testLogs = logger.getLogs(test_id);
107
107
  debug(`Test logs for ${test_id}:\n`, testLogs);
108
+ if (stack) stack += '\n\n';
108
109
  stack += testLogs;
109
110
 
110
111
  if (Array.isArray(storeArtifacts) && storeArtifacts.length) {
@@ -29,8 +29,8 @@ class DataStorage {
29
29
  Thus, checking environment is only reasonable when you put data to starage
30
30
  and potentially useless when get data from storage
31
31
  */
32
- const runningEnvironment = this.getRunningEnviroment();
33
- if (runningEnvironment === 'jest') this.isFileStorage = true;
32
+ this.runningEnvironment = this.getRunningEnviroment();
33
+ if (this.runningEnvironment === 'jest') this.isFileStorage = true;
34
34
 
35
35
  if (this.isFileStorage) {
36
36
  this.dataDirPath = path.join(TESTOMAT_TMP_STORAGE.mainDir, this.dataDirName);
@@ -52,8 +52,12 @@ class DataStorage {
52
52
  */
53
53
  getRunningEnviroment() {
54
54
  if (process.env.JEST_WORKER_ID) return 'jest';
55
- // @ts-expect-error mocha could be undefined, its ok
56
- if (typeof mocha !== 'undefined') return 'mocha';
55
+ try {
56
+ // @ts-expect-error mocha is defined only in mocha environment
57
+ if (typeof mocha !== 'undefined') return 'mocha';
58
+ } catch (e) {
59
+ // ignore
60
+ }
57
61
  return undefined;
58
62
  }
59
63
 
@@ -64,6 +68,8 @@ class DataStorage {
64
68
  * @returns
65
69
  */
66
70
  putData(data, context = null) {
71
+ // this.isFileStorage = !global.testomatioDataStore;
72
+
67
73
  let testId = null;
68
74
  if (typeof context === 'string') {
69
75
  testId = context;
@@ -73,7 +79,7 @@ class DataStorage {
73
79
  }
74
80
 
75
81
  // try to get testId for Jest
76
- testId = testId ?? JestReporter.getIdOfCurrentlyRunningTest();
82
+ if (this.runningEnvironment === 'jest') testId = testId ?? JestReporter.getIdOfCurrentlyRunningTest();
77
83
 
78
84
  // if testId is not provided, data is be saved to `{dataType}_other` file;
79
85
  if (!testId) testId = 'other';
@@ -101,7 +107,9 @@ class DataStorage {
101
107
  // testId = context...
102
108
  }
103
109
 
104
- if (!testId) testId = 'other';
110
+ if (!testId) {
111
+ debug(`Cannot get test id from passed context:\n}`, context);
112
+ }
105
113
 
106
114
  if (this.isFileStorage) {
107
115
  return this._getDataFromFile(testId);
package/lib/logger.js CHANGED
@@ -1,7 +1,18 @@
1
+ const chalk = require('chalk');
1
2
  const debug = require('debug')('@testomatio/reporter:logger');
2
3
  const { DataStorage } = require('./dataStorage');
3
4
 
4
5
  const LOG_METHODS = ['assert', 'debug', 'error', 'info', 'log', 'trace', 'warn'];
6
+ const LEVELS = {
7
+ ALL: { severity: 1, color: '' },
8
+ VERBOSE: { severity: 3, color: 'grey' },
9
+ TRACE: { severity: 5, color: 'grey' },
10
+ DEBUG: { severity: 7, color: 'cyan' },
11
+ INFO: { severity: 9, color: 'black' },
12
+ LOG: { severity: 11, color: 'black' },
13
+ WARN: { severity: 13, color: 'yellow' },
14
+ ERROR: { severity: 15, color: 'red' },
15
+ };
5
16
 
6
17
  /**
7
18
  * Logger allows to:
@@ -14,7 +25,11 @@ class Logger {
14
25
  // _loggerToIntercept intercepted and reassigned immediately when added
15
26
 
16
27
  constructor(params = {}) {
28
+ // set default logger to be used in log, warn, error, etc methods
29
+ this._originalUserLogger = { ...console };
30
+
17
31
  this.dataStorage = new DataStorage('log', params);
32
+ this.logLevel = process?.env?.LOG_LEVEL?.toUpperCase() || 'ALL';
18
33
 
19
34
  // commented because prefer to use "intercept" method
20
35
  // if (params?.logger) this._loggerToIntercept = params.logger;
@@ -27,26 +42,6 @@ class Logger {
27
42
  }
28
43
  }
29
44
 
30
- /**
31
- * Returns the string (logs) and the context
32
- * This is required because loggers take multiple arguments
33
- * @param {...any} args
34
- */
35
- /*
36
- TODO: its difficult to distinguish context from log artument if its not a string,
37
- e.g. user can use something like console.log('some log', { key: 'value' });
38
- consider not to use this method at all and don't expect context from user inside default log methods;
39
- instead, use custom method like $`some log` and parse it
40
- */
41
- // _getLogStringAndContextFromArgs(...args) {
42
- // let context = '';
43
- // if (args.length > 1 && typeof args[args.length - 1] !== 'string') {
44
- // context = args.pop();
45
- // }
46
- // const logs = args.join(' ');
47
- // return { logs, context };
48
- // }
49
-
50
45
  /**
51
46
  * Tagget template literal. Allows to use different syntaxes:
52
47
  * 1. Tagget template: $`text ${someVar}`
@@ -65,22 +60,38 @@ class Logger {
65
60
  // it looks like: `string1 arg1 string2 arg2 string3`
66
61
  (args[index] !== undefined // eslint-disable-line no-nested-ternary
67
62
  ? typeof args[index] === 'string'
68
- ? // add arg as it is
69
- args[index]
70
- : // stringify arg
71
- this._strinfifyLogs(args[index])
72
- : // add space if no arg after string
73
- ' '),
63
+ ? args[index] // add arg as it is
64
+ : this._strinfifyLogs(args[index]) // stringify arg
65
+ : ' '), // add space if no arg after string
74
66
  // initial accumulator value
75
67
  '',
76
68
  );
77
69
  } else {
78
70
  // this block means arguments syntax is used (syntax like $('text', someVar))
71
+ // in this case strings represents just a first argument
79
72
  logs = this._strinfifyLogs(strings, ...args);
80
73
  }
81
74
  this.dataStorage.putData(logs);
82
75
  }
83
76
 
77
+ /**
78
+ * Allows you to define a step inside a test. Step name is attached to the report and
79
+ * helps to understand the test flow.
80
+ * @param {*} strings
81
+ * @param {...any} values
82
+ */
83
+ step(strings, ...values) {
84
+ let logs = '';
85
+ for (let i = 0; i < strings.length; i++) {
86
+ logs += strings[i];
87
+ if (i < values.length) {
88
+ logs += values[i];
89
+ }
90
+ }
91
+ logs = chalk.blue(`> ${logs}`);
92
+ this.dataStorage.putData(logs);
93
+ }
94
+
84
95
  /**
85
96
  *
86
97
  * @param {*} context testId or test context from test runner
@@ -97,13 +108,23 @@ class Logger {
97
108
  if (typeof arg === 'string') {
98
109
  logs.push(arg);
99
110
  } else {
100
- logs.push(JSON.stringify(arg));
111
+ try {
112
+ // eslint-disable-next-line no-unused-expressions
113
+ this.prettyObjects ? logs.push(JSON.stringify(arg, null, 2)) : logs.push(JSON.stringify(arg));
114
+ } catch (e) {
115
+ debug('Error while stringify object', e);
116
+ logs.push(arg);
117
+ }
101
118
  }
102
119
  }
103
120
  return logs.join(' ');
104
121
  }
105
122
 
106
123
  assert(...args) {
124
+ const level = 'ERROR';
125
+ const severity = LEVELS[level].severity;
126
+ if (severity < LEVELS[this.logLevel]?.severity) return;
127
+
107
128
  const logs = this._strinfifyLogs(...args);
108
129
  this.dataStorage.putData(logs);
109
130
  try {
@@ -114,8 +135,14 @@ class Logger {
114
135
  }
115
136
 
116
137
  debug(...args) {
138
+ const level = 'DEBUG';
139
+ const severity = LEVELS[level].severity;
140
+ if (severity < LEVELS[this.logLevel]?.severity) return;
141
+
142
+ if (this.logLevel === 'error' || this.logLevel === 'warn') return;
117
143
  const logs = this._strinfifyLogs(...args);
118
- this.dataStorage.putData(logs);
144
+ const colorizedLogs = chalk[LEVELS[level].color](logs);
145
+ this.dataStorage.putData(colorizedLogs);
119
146
  try {
120
147
  this._originalUserLogger.debug(...args);
121
148
  } catch (e) {
@@ -124,8 +151,13 @@ class Logger {
124
151
  }
125
152
 
126
153
  error(...args) {
154
+ const level = 'ERROR';
155
+ const severity = LEVELS[level].severity;
156
+ if (severity < LEVELS[this.logLevel]?.severity) return;
157
+
127
158
  const logs = this._strinfifyLogs(...args);
128
- this.dataStorage.putData(logs);
159
+ const colorizedLogs = chalk[LEVELS[level].color](logs);
160
+ this.dataStorage.putData(colorizedLogs);
129
161
  try {
130
162
  this._originalUserLogger.error(...args);
131
163
  } catch (e) {
@@ -134,6 +166,10 @@ class Logger {
134
166
  }
135
167
 
136
168
  info(...args) {
169
+ const level = 'INFO';
170
+ const severity = LEVELS[level].severity;
171
+ if (severity < LEVELS[this.logLevel]?.severity) return;
172
+
137
173
  const logs = this._strinfifyLogs(...args);
138
174
  this.dataStorage.putData(logs);
139
175
  try {
@@ -144,6 +180,10 @@ class Logger {
144
180
  }
145
181
 
146
182
  log(...args) {
183
+ const level = 'INFO';
184
+ const severity = LEVELS[level].severity;
185
+ if (severity < LEVELS[this.logLevel]?.severity) return;
186
+
147
187
  const logs = this._strinfifyLogs(...args);
148
188
  this.dataStorage.putData(logs);
149
189
  try {
@@ -154,8 +194,13 @@ class Logger {
154
194
  }
155
195
 
156
196
  trace(...args) {
197
+ const level = 'TRACE';
198
+ const severity = LEVELS[level].severity;
199
+ if (severity < LEVELS[this.logLevel]?.severity) return;
200
+
157
201
  const logs = this._strinfifyLogs(...args);
158
- this.dataStorage.putData(logs);
202
+ const colorizedLogs = chalk[LEVELS[level].color](logs);
203
+ this.dataStorage.putData(colorizedLogs);
159
204
  try {
160
205
  this._originalUserLogger.trace(...args);
161
206
  } catch (e) {
@@ -164,8 +209,13 @@ class Logger {
164
209
  }
165
210
 
166
211
  warn(...args) {
212
+ const level = 'WARN';
213
+ const severity = LEVELS[level].severity;
214
+ if (severity < LEVELS[this.logLevel]?.severity) return;
215
+
167
216
  const logs = this._strinfifyLogs(...args);
168
- this.dataStorage.putData(logs);
217
+ const colorizedLogs = chalk[LEVELS[level].color](logs);
218
+ this.dataStorage.putData(colorizedLogs);
169
219
  try {
170
220
  this._originalUserLogger.warn(...args);
171
221
  } catch (e) {
@@ -202,6 +252,20 @@ class Logger {
202
252
  this._loggerToIntercept[method] = (...args) => this[method](...args);
203
253
  }
204
254
  }
255
+
256
+ /**
257
+ * Allows to configure logger. Make sure you do it before the logger usage in your code.
258
+ *
259
+ * @param {Object} [config={}] - The configuration object.
260
+ * @param {string} [config.logLevel] - The desired log level. Valid values are 'DEBUG', 'INFO', 'WARN', and 'ERROR'.
261
+ * @param {boolean} [config.prettyObjects] - Specifies whether to enable pretty printing of objects.
262
+ * @returns {void}
263
+ */
264
+ configure(config = {}) {
265
+ if (!config) return;
266
+ if (config.prettyObjects) this.prettyObjects = config.prettyObjects;
267
+ if (config.logLevel) this.logLevel = config.logLevel.toUpperCase();
268
+ }
205
269
  }
206
270
 
207
271
  Logger.instance = null;
@@ -209,13 +273,6 @@ Logger.instance = null;
209
273
  // module.exports.Logger = Logger;
210
274
  module.exports = new Logger();
211
275
 
212
- // TODO: consider using fs promises instead of writeSync/appendFileSync to prevent blocking and improve performance
213
- // TODO: try to use method generator *
214
- // TODO: understand captureStackTrace in output (check on .error) (NOT SUCH IMPORTANT)
215
- // TODO: handle log levels to colorize logs later on UI
216
- // TODO: parse passed arguments as {level: 'str', message: 'str'} because some loggers use such syntax
217
- // TODO: add setLevel method - allows skip messages with the lower level
218
- // TODO: in case of unset _originalUserLogger, logger.{method} will not provide console output,
219
- // need to add some logger by default
276
+ // TODO: parse passed arguments as {level: 'str', message: 'str'} because some loggers use such syntax;
277
+ // upd: did not face such loggers, but still could be useful
220
278
 
221
- // TODO step method (blue color)
package/lib/reporter.js CHANGED
@@ -4,10 +4,12 @@ const TRConstants = require('./constants');
4
4
  const TRArtifacts = require('./_ArtifactStorageOld');
5
5
 
6
6
  const log = logger._log.bind(logger);
7
+ const step = logger.step.bind(logger);
7
8
 
8
9
  module.exports = {
9
10
  logger,
10
11
  log,
12
+ step,
11
13
  TestomatClient,
12
14
  TRConstants,
13
15
  TRArtifacts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.1.0-beta",
3
+ "version": "1.2.1-beta",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",