@testomatio/reporter 1.3.6-beta → 1.4.0-beta-csv-enable
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/README.md +60 -57
- package/lib/adapter/codecept.js +96 -35
- package/lib/adapter/cucumber/current.js +19 -12
- package/lib/adapter/cucumber/legacy.js +7 -6
- package/lib/adapter/cucumber.js +2 -2
- package/lib/adapter/cypress-plugin/index.js +53 -26
- package/lib/adapter/jasmine.js +2 -2
- package/lib/adapter/jest.js +50 -17
- package/lib/adapter/mocha.js +110 -58
- package/lib/adapter/playwright.js +95 -33
- package/lib/adapter/webdriver.js +2 -2
- package/lib/bin/reportXml.js +22 -16
- package/lib/bin/startTest.js +27 -6
- package/lib/client.js +179 -53
- package/lib/config.js +34 -0
- package/lib/constants.js +32 -7
- package/lib/data-storage.js +203 -0
- package/lib/fileUploader.js +135 -54
- package/lib/junit-adapter/adapter.js +0 -2
- package/lib/junit-adapter/csharp.js +3 -4
- package/lib/junit-adapter/index.js +3 -3
- package/lib/junit-adapter/java.js +35 -17
- package/lib/junit-adapter/javascript.js +1 -2
- package/lib/junit-adapter/python.js +12 -14
- package/lib/junit-adapter/ruby.js +1 -2
- package/lib/pipe/csv.js +37 -31
- package/lib/pipe/github.js +9 -19
- package/lib/pipe/gitlab.js +22 -26
- package/lib/pipe/html.js +361 -0
- package/lib/pipe/index.js +3 -1
- package/lib/pipe/testomatio.js +256 -53
- package/lib/reporter-functions.js +46 -0
- package/lib/reporter.js +11 -9
- package/lib/services/artifacts.js +57 -0
- package/lib/services/index.js +13 -0
- package/lib/services/key-values.js +58 -0
- package/lib/services/logger.js +311 -0
- package/lib/template/emptyData.svg +23 -0
- package/lib/template/testomatio.hbs +1421 -0
- package/lib/utils/pipe_utils.js +129 -0
- package/lib/{util.js → utils/utils.js} +147 -17
- package/lib/xmlReader.js +211 -122
- package/package.json +17 -9
- package/lib/_ArtifactStorageOld.js +0 -142
- package/lib/artifactStorage.js +0 -25
- package/lib/dataStorage.js +0 -244
- package/lib/logger.js +0 -301
package/lib/logger.js
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const debug = require('debug')('@testomatio/reporter:logger');
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const { DataStorage } = require('./dataStorage');
|
|
5
|
-
|
|
6
|
-
const LOG_METHODS = ['assert', 'debug', 'error', 'info', 'log', 'trace', 'warn'];
|
|
7
|
-
const LEVELS = {
|
|
8
|
-
ALL: { severity: 1, color: '' },
|
|
9
|
-
VERBOSE: { severity: 3, color: 'grey' },
|
|
10
|
-
TRACE: { severity: 5, color: 'grey' },
|
|
11
|
-
DEBUG: { severity: 7, color: 'cyan' },
|
|
12
|
-
INFO: { severity: 9, color: 'black' },
|
|
13
|
-
LOG: { severity: 11, color: 'black' },
|
|
14
|
-
WARN: { severity: 13, color: 'yellow' },
|
|
15
|
-
ERROR: { severity: 15, color: 'red' },
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Logger allows to:
|
|
20
|
-
* 1. Intercept logs from user logger (console.log, etc) and store them.
|
|
21
|
-
* 2. Output logs to console (actually, logger functionality).
|
|
22
|
-
* 3. Varied syntax.
|
|
23
|
-
*/
|
|
24
|
-
class Logger {
|
|
25
|
-
// _originalUserLogger used to output logs to console by the user logger
|
|
26
|
-
// _loggerToIntercept intercepted and reassigned immediately when added
|
|
27
|
-
|
|
28
|
-
constructor() {
|
|
29
|
-
// set default logger to be used in log, warn, error, etc methods
|
|
30
|
-
this._originalUserLogger = { ...console };
|
|
31
|
-
|
|
32
|
-
this.dataStorage = new DataStorage('log');
|
|
33
|
-
this.logLevel = process?.env?.LOG_LEVEL?.toUpperCase() || 'ALL';
|
|
34
|
-
|
|
35
|
-
// commented because prefer to use "intercept" method
|
|
36
|
-
// if (params?.logger) this._loggerToIntercept = params.logger;
|
|
37
|
-
|
|
38
|
-
// intercept console by default
|
|
39
|
-
this.intercept(console);
|
|
40
|
-
|
|
41
|
-
// singleton
|
|
42
|
-
if (!Logger.instance) {
|
|
43
|
-
Logger.instance = this;
|
|
44
|
-
}
|
|
45
|
-
|
|
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
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Allows you to define a step inside a test. Step name is attached to the report and
|
|
60
|
-
* helps to understand the test flow.
|
|
61
|
-
* @param {*} strings
|
|
62
|
-
* @param {...any} values
|
|
63
|
-
*/
|
|
64
|
-
step(strings, ...values) {
|
|
65
|
-
let logs = '';
|
|
66
|
-
for (let i = 0; i < strings.length; i++) {
|
|
67
|
-
logs += strings[i];
|
|
68
|
-
if (i < values.length) {
|
|
69
|
-
logs += values[i];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
logs = chalk.blue(`> ${logs}`);
|
|
73
|
-
this.dataStorage.putData(logs);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
*
|
|
78
|
-
* @param {*} context testId or test context from test runner
|
|
79
|
-
* @returns
|
|
80
|
-
*/
|
|
81
|
-
getLogs(context) {
|
|
82
|
-
const logs = this.dataStorage.getData(context);
|
|
83
|
-
return logs || '';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
_stringifyLogs(...args) {
|
|
87
|
-
const logs = [];
|
|
88
|
-
// stringify everything except strings
|
|
89
|
-
for (const arg of args) {
|
|
90
|
-
// ignore empty strings
|
|
91
|
-
if (arg === '') continue;
|
|
92
|
-
if (typeof arg === 'string') {
|
|
93
|
-
logs.push(arg);
|
|
94
|
-
} else if (Array.isArray(arg)) {
|
|
95
|
-
logs.push(arg.join(' '));
|
|
96
|
-
} else {
|
|
97
|
-
try {
|
|
98
|
-
// eslint-disable-next-line no-unused-expressions
|
|
99
|
-
this.prettyObjects ? logs.push(JSON.stringify(arg, null, 2)) : logs.push(JSON.stringify(arg));
|
|
100
|
-
} catch (e) {
|
|
101
|
-
debug('Error while stringify object', e);
|
|
102
|
-
logs.push(arg);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return logs.join(' ');
|
|
107
|
-
}
|
|
108
|
-
|
|
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
|
-
}
|
|
124
|
-
|
|
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);
|
|
149
|
-
}
|
|
150
|
-
this._originalUserLogger.log(logs);
|
|
151
|
-
this.dataStorage.putData(logs, context);
|
|
152
|
-
}
|
|
153
|
-
|
|
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
|
-
|
|
172
|
-
const severity = LEVELS[level].severity;
|
|
173
|
-
if (severity < LEVELS[this.logLevel]?.severity) return;
|
|
174
|
-
|
|
175
|
-
const logs = this._stringifyLogs(...argsArray);
|
|
176
|
-
const colorizedLogs = chalk[LEVELS[level].color](logs);
|
|
177
|
-
this.dataStorage.putData(colorizedLogs, context);
|
|
178
|
-
try {
|
|
179
|
-
// level.toLowerCase() represents method name (log, warn, error, etc)
|
|
180
|
-
this._originalUserLogger[level.toLowerCase()](colorizedLogs);
|
|
181
|
-
} catch (e) {
|
|
182
|
-
// method could be unexisting, ignore error
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
assert(...args) {
|
|
187
|
-
// sometimes user invokes logger without any arguments passed
|
|
188
|
-
if (!args.length) return;
|
|
189
|
-
|
|
190
|
-
const level = 'ERROR';
|
|
191
|
-
const severity = LEVELS[level].severity;
|
|
192
|
-
if (severity < LEVELS[this.logLevel]?.severity) return;
|
|
193
|
-
|
|
194
|
-
const logs = this._stringifyLogs(...args);
|
|
195
|
-
this.dataStorage.putData(logs);
|
|
196
|
-
try {
|
|
197
|
-
this._originalUserLogger.log(`Assertion result: `, ...args);
|
|
198
|
-
} catch (e) {
|
|
199
|
-
// method could be unexisting, ignore error
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
debug(...args) {
|
|
204
|
-
this._logWrapper(args, 'DEBUG');
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
error(...args) {
|
|
208
|
-
this._logWrapper(args, 'ERROR');
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
info(...args) {
|
|
212
|
-
this._logWrapper(args, 'INFO');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
log(...args) {
|
|
216
|
-
this._logWrapper(args, 'LOG');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
trace(...args) {
|
|
220
|
-
this._logWrapper(args, 'TRACE');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
warn(...args) {
|
|
224
|
-
this._logWrapper(args, 'WARN');
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Intercepts user logger messages.
|
|
229
|
-
* When call this method, Logger start to control the user logger,
|
|
230
|
-
* but almost nothing is changed for user regarding the console output (like log level set by user)
|
|
231
|
-
* (until multiple loggers are intercepted,
|
|
232
|
-
* in this case only the last intercepted logger will be used as user console output).
|
|
233
|
-
* @param {*} userLogger
|
|
234
|
-
*/
|
|
235
|
-
intercept(userLogger) {
|
|
236
|
-
if (!userLogger) return;
|
|
237
|
-
|
|
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
|
-
}
|
|
262
|
-
this._loggerToIntercept = userLogger;
|
|
263
|
-
|
|
264
|
-
/*
|
|
265
|
-
override user logger (any, e.g. console) methods to intercept log messages
|
|
266
|
-
this._loggerToIntercept = this; could be used, but decided to override only output methods
|
|
267
|
-
*/
|
|
268
|
-
for (const method of LOG_METHODS) {
|
|
269
|
-
/*
|
|
270
|
-
decided to comment next code line because its better to create method even if it does not exist in user logger;
|
|
271
|
-
on method invocation, we will store the data anyway and catch block will prevent potential errors
|
|
272
|
-
*/
|
|
273
|
-
// if (!this._loggerToIntercept[method]) continue;
|
|
274
|
-
this._loggerToIntercept[method] = (...args) => this[method](...args);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Allows to configure logger. Make sure you do it before the logger usage in your code.
|
|
280
|
-
*
|
|
281
|
-
* @param {Object} [config={}] - The configuration object.
|
|
282
|
-
* @param {string} [config.logLevel] - The desired log level. Valid values are 'DEBUG', 'INFO', 'WARN', and 'ERROR'.
|
|
283
|
-
* @param {boolean} [config.prettyObjects] - Specifies whether to enable pretty printing of objects.
|
|
284
|
-
* @returns {void}
|
|
285
|
-
*/
|
|
286
|
-
configure(config = {}) {
|
|
287
|
-
if (!config) return;
|
|
288
|
-
if (config.prettyObjects) this.prettyObjects = config.prettyObjects;
|
|
289
|
-
if (config.logLevel) this.logLevel = config.logLevel.toUpperCase();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
Logger.instance = null;
|
|
294
|
-
|
|
295
|
-
// module.exports.Logger = Logger;
|
|
296
|
-
const logger = new Logger();
|
|
297
|
-
|
|
298
|
-
module.exports = logger;
|
|
299
|
-
|
|
300
|
-
// TODO: parse passed arguments as {level: 'str', message: 'str'} because some loggers use such syntax;
|
|
301
|
-
// upd: did not face such loggers, but still could be useful
|