@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.
- package/lib/_ArtifactStorageOld.js +5 -4
- package/lib/adapter/jest.js +1 -0
- package/lib/client.js +2 -1
- package/lib/dataStorage.js +14 -6
- package/lib/logger.js +97 -40
- package/lib/reporter.js +2 -0
- package/package.json +1 -1
|
@@ -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(
|
|
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 =
|
|
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;
|
package/lib/adapter/jest.js
CHANGED
|
@@ -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) {
|
package/lib/dataStorage.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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)
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
213
|
-
//
|
|
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,
|