qase-javascript-commons 2.2.10 → 2.2.12
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/changelog.md +14 -0
- package/dist/qase.d.ts +4 -0
- package/dist/qase.js +100 -31
- package/dist/reporters/testops-reporter.d.ts +4 -1
- package/dist/reporters/testops-reporter.js +30 -11
- package/dist/state/state.js +7 -1
- package/package.json +3 -2
package/changelog.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# qase-javascript-commons@2.2.12
|
|
2
|
+
|
|
3
|
+
## What's new
|
|
4
|
+
|
|
5
|
+
Added a mutex to ensure correct result submission when running tests in multiple threads, preventing potential
|
|
6
|
+
duplication.
|
|
7
|
+
|
|
8
|
+
# qase-javascript-commons@2.2.11
|
|
9
|
+
|
|
10
|
+
## What's new
|
|
11
|
+
|
|
12
|
+
- Improved state manager behavior to ensure correct handling.
|
|
13
|
+
- Masked token in logs to enhance security.
|
|
14
|
+
|
|
1
15
|
# qase-javascript-commons@2.2.10
|
|
2
16
|
|
|
3
17
|
## What's new
|
package/dist/qase.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare class QaseReporter implements ReporterInterface {
|
|
|
51
51
|
private readonly logger;
|
|
52
52
|
private startTestRunOperation?;
|
|
53
53
|
private options;
|
|
54
|
+
private withState;
|
|
54
55
|
/**
|
|
55
56
|
* @param {OptionsType} options
|
|
56
57
|
*/
|
|
@@ -103,4 +104,7 @@ export declare class QaseReporter implements ReporterInterface {
|
|
|
103
104
|
* @private
|
|
104
105
|
*/
|
|
105
106
|
private logTestItem;
|
|
107
|
+
private setWithState;
|
|
108
|
+
private maskToken;
|
|
109
|
+
private sanitizeOptions;
|
|
106
110
|
}
|
package/dist/qase.js
CHANGED
|
@@ -95,24 +95,28 @@ class QaseReporter {
|
|
|
95
95
|
logger;
|
|
96
96
|
startTestRunOperation;
|
|
97
97
|
options;
|
|
98
|
+
withState;
|
|
98
99
|
/**
|
|
99
100
|
* @param {OptionsType} options
|
|
100
101
|
*/
|
|
101
102
|
constructor(options) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
this.withState = this.setWithState(options);
|
|
104
|
+
if (this.withState) {
|
|
105
|
+
if (state_1.StateManager.isStateExists()) {
|
|
106
|
+
const state = state_1.StateManager.getState();
|
|
107
|
+
if (state.IsModeChanged && state.Mode) {
|
|
108
|
+
process.env[env_1.EnvEnum.mode] = state.Mode.toString();
|
|
109
|
+
}
|
|
110
|
+
if (state.RunId) {
|
|
111
|
+
process.env[env_1.EnvRunEnum.id] = state.RunId.toString();
|
|
112
|
+
}
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
const env = (0, env_1.envToConfig)((0, env_schema_1.default)({ schema: env_1.envValidationSchema }));
|
|
112
116
|
const composedOptions = (0, options_1.composeOptions)(options, env);
|
|
113
117
|
this.options = composedOptions;
|
|
114
118
|
this.logger = new logger_1.Logger({ debug: composedOptions.debug });
|
|
115
|
-
this.logger.logDebug(`Config: ${JSON.stringify(composedOptions)}`);
|
|
119
|
+
this.logger.logDebug(`Config: ${JSON.stringify(this.sanitizeOptions(composedOptions))}`);
|
|
116
120
|
this.captureLogs = composedOptions.captureLogs;
|
|
117
121
|
try {
|
|
118
122
|
this.upstreamReporter = this.createReporter(
|
|
@@ -150,16 +154,18 @@ class QaseReporter {
|
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
if (this.withState) {
|
|
158
|
+
if (!state_1.StateManager.isStateExists()) {
|
|
159
|
+
const state = {
|
|
160
|
+
RunId: undefined,
|
|
161
|
+
Mode: this.useFallback ? composedOptions.fallback : composedOptions.mode,
|
|
162
|
+
IsModeChanged: undefined,
|
|
163
|
+
};
|
|
164
|
+
if (this.disabled) {
|
|
165
|
+
state.Mode = options_1.ModeEnum.off;
|
|
166
|
+
}
|
|
167
|
+
state_1.StateManager.setState(state);
|
|
161
168
|
}
|
|
162
|
-
state_1.StateManager.setState(state);
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
171
|
getResults() {
|
|
@@ -192,7 +198,9 @@ class QaseReporter {
|
|
|
192
198
|
catch (error) {
|
|
193
199
|
this.logger.logError('Unable to send the results to the upstream reporter:', error);
|
|
194
200
|
if (this.fallbackReporter == undefined) {
|
|
195
|
-
|
|
201
|
+
if (this.withState) {
|
|
202
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
203
|
+
}
|
|
196
204
|
return;
|
|
197
205
|
}
|
|
198
206
|
if (!this.useFallback) {
|
|
@@ -201,16 +209,22 @@ class QaseReporter {
|
|
|
201
209
|
}
|
|
202
210
|
try {
|
|
203
211
|
await this.fallbackReporter?.sendResults();
|
|
204
|
-
|
|
212
|
+
if (this.withState) {
|
|
213
|
+
state_1.StateManager.setMode(this.options.fallback);
|
|
214
|
+
}
|
|
205
215
|
}
|
|
206
216
|
catch (error) {
|
|
207
217
|
this.logger.logError('Unable to send the results to the fallback reporter:', error);
|
|
208
|
-
|
|
218
|
+
if (this.withState) {
|
|
219
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
220
|
+
}
|
|
209
221
|
}
|
|
210
222
|
}
|
|
211
223
|
}
|
|
212
224
|
async complete() {
|
|
213
|
-
|
|
225
|
+
if (this.withState) {
|
|
226
|
+
state_1.StateManager.clearState();
|
|
227
|
+
}
|
|
214
228
|
if (this.disabled) {
|
|
215
229
|
return;
|
|
216
230
|
}
|
|
@@ -238,6 +252,10 @@ class QaseReporter {
|
|
|
238
252
|
* @returns {void}
|
|
239
253
|
*/
|
|
240
254
|
startTestRun() {
|
|
255
|
+
if (this.withState) {
|
|
256
|
+
console.log("Clean state");
|
|
257
|
+
state_1.StateManager.clearState();
|
|
258
|
+
}
|
|
241
259
|
if (!this.disabled) {
|
|
242
260
|
this.logger.logDebug('Starting test run');
|
|
243
261
|
try {
|
|
@@ -247,17 +265,23 @@ class QaseReporter {
|
|
|
247
265
|
this.logger.logError('Unable to start test run in the upstream reporter: ', error);
|
|
248
266
|
if (this.fallbackReporter == undefined) {
|
|
249
267
|
this.disabled = true;
|
|
250
|
-
|
|
268
|
+
if (this.withState) {
|
|
269
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
270
|
+
}
|
|
251
271
|
return;
|
|
252
272
|
}
|
|
253
273
|
try {
|
|
254
274
|
this.startTestRunOperation = this.fallbackReporter?.startTestRun();
|
|
255
|
-
|
|
275
|
+
if (this.withState) {
|
|
276
|
+
state_1.StateManager.setMode(this.options.fallback);
|
|
277
|
+
}
|
|
256
278
|
}
|
|
257
279
|
catch (error) {
|
|
258
280
|
this.logger.logError('Unable to start test run in the fallback reporter: ', error);
|
|
259
281
|
this.disabled = true;
|
|
260
|
-
|
|
282
|
+
if (this.withState) {
|
|
283
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
284
|
+
}
|
|
261
285
|
}
|
|
262
286
|
}
|
|
263
287
|
}
|
|
@@ -294,7 +318,9 @@ class QaseReporter {
|
|
|
294
318
|
this.logger.logError('Unable to add the result to the upstream reporter:', error);
|
|
295
319
|
if (this.fallbackReporter == undefined) {
|
|
296
320
|
this.disabled = true;
|
|
297
|
-
|
|
321
|
+
if (this.withState) {
|
|
322
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
323
|
+
}
|
|
298
324
|
return;
|
|
299
325
|
}
|
|
300
326
|
if (!this.useFallback) {
|
|
@@ -312,7 +338,9 @@ class QaseReporter {
|
|
|
312
338
|
async addTestResultToFallback(result) {
|
|
313
339
|
try {
|
|
314
340
|
await this.fallbackReporter?.addTestResult(result);
|
|
315
|
-
|
|
341
|
+
if (this.withState) {
|
|
342
|
+
state_1.StateManager.setMode(this.options.fallback);
|
|
343
|
+
}
|
|
316
344
|
}
|
|
317
345
|
catch (error) {
|
|
318
346
|
this.logger.logError('Unable to add the result to the fallback reporter:', error);
|
|
@@ -343,7 +371,9 @@ class QaseReporter {
|
|
|
343
371
|
this.logger.logError('Unable to publish the run results to the upstream reporter:', error);
|
|
344
372
|
if (this.fallbackReporter == undefined) {
|
|
345
373
|
this.disabled = true;
|
|
346
|
-
|
|
374
|
+
if (this.withState) {
|
|
375
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
376
|
+
}
|
|
347
377
|
return;
|
|
348
378
|
}
|
|
349
379
|
if (!this.useFallback) {
|
|
@@ -353,7 +383,9 @@ class QaseReporter {
|
|
|
353
383
|
await this.publishFallback();
|
|
354
384
|
}
|
|
355
385
|
}
|
|
356
|
-
|
|
386
|
+
if (this.withState) {
|
|
387
|
+
state_1.StateManager.clearState();
|
|
388
|
+
}
|
|
357
389
|
}
|
|
358
390
|
/**
|
|
359
391
|
* @returns {Promise<void>}
|
|
@@ -361,10 +393,14 @@ class QaseReporter {
|
|
|
361
393
|
async publishFallback() {
|
|
362
394
|
try {
|
|
363
395
|
await this.fallbackReporter?.publish();
|
|
364
|
-
|
|
396
|
+
if (this.withState) {
|
|
397
|
+
state_1.StateManager.setMode(this.options.fallback);
|
|
398
|
+
}
|
|
365
399
|
}
|
|
366
400
|
catch (error) {
|
|
367
|
-
|
|
401
|
+
if (this.withState) {
|
|
402
|
+
state_1.StateManager.setMode(options_1.ModeEnum.off);
|
|
403
|
+
}
|
|
368
404
|
this.logger.logError('Unable to publish the run results to the fallback reporter:', error);
|
|
369
405
|
this.disabled = true;
|
|
370
406
|
}
|
|
@@ -407,7 +443,7 @@ class QaseReporter {
|
|
|
407
443
|
batch,
|
|
408
444
|
useV2,
|
|
409
445
|
defect,
|
|
410
|
-
}, apiClient, environment, rootSuite, api.host);
|
|
446
|
+
}, apiClient, this.withState, environment, rootSuite, api.host);
|
|
411
447
|
}
|
|
412
448
|
case options_1.ModeEnum.report: {
|
|
413
449
|
const localOptions = report.connections?.[writer_1.DriverEnum.local];
|
|
@@ -427,5 +463,38 @@ class QaseReporter {
|
|
|
427
463
|
logTestItem(test) {
|
|
428
464
|
this.logger.log(resultLogMap[test.execution.status](test));
|
|
429
465
|
}
|
|
466
|
+
setWithState(options) {
|
|
467
|
+
return options.frameworkName == 'cypress'
|
|
468
|
+
|| options.frameworkName == ''
|
|
469
|
+
|| options.frameworkName == null
|
|
470
|
+
|| options.frameworkName == undefined;
|
|
471
|
+
}
|
|
472
|
+
maskToken(token) {
|
|
473
|
+
if (token.length <= 7) {
|
|
474
|
+
return '*'.repeat(token.length);
|
|
475
|
+
}
|
|
476
|
+
return `${token.slice(0, 3)}****${token.slice(-4)}`;
|
|
477
|
+
}
|
|
478
|
+
sanitizeOptions(options) {
|
|
479
|
+
if (typeof options !== 'object' || options === null) {
|
|
480
|
+
return options;
|
|
481
|
+
}
|
|
482
|
+
const sanitizedObject = {};
|
|
483
|
+
for (const key in options) {
|
|
484
|
+
if (Object.prototype.hasOwnProperty.call(options, key)) {
|
|
485
|
+
const value = options[key];
|
|
486
|
+
if (key === 'token' && typeof value === 'string') {
|
|
487
|
+
sanitizedObject[key] = this.maskToken(value);
|
|
488
|
+
}
|
|
489
|
+
else if (typeof value === 'object' && value !== null) {
|
|
490
|
+
sanitizedObject[key] = this.sanitizeOptions(value);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
sanitizedObject[key] = value;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return sanitizedObject;
|
|
498
|
+
}
|
|
430
499
|
}
|
|
431
500
|
exports.QaseReporter = QaseReporter;
|
|
@@ -29,6 +29,7 @@ export interface TestOpsOptionsType {
|
|
|
29
29
|
*/
|
|
30
30
|
export declare class TestOpsReporter extends AbstractReporter {
|
|
31
31
|
private api;
|
|
32
|
+
private withState;
|
|
32
33
|
/**
|
|
33
34
|
* @type {Record<TestStatusEnum, string>}
|
|
34
35
|
*/
|
|
@@ -101,15 +102,17 @@ export declare class TestOpsReporter extends AbstractReporter {
|
|
|
101
102
|
* @private
|
|
102
103
|
*/
|
|
103
104
|
private isTestRunReady;
|
|
105
|
+
private mutex;
|
|
104
106
|
/**
|
|
105
107
|
* @param {LoggerInterface} logger
|
|
106
108
|
* @param {ReporterOptionsType & TestOpsOptionsType} options
|
|
107
109
|
* @param {QaseApiInterface} api
|
|
110
|
+
* @param {boolean} withState
|
|
108
111
|
* @param {string | undefined} environment
|
|
109
112
|
* @param {string | undefined} rootSuite
|
|
110
113
|
* @param {string | undefined} baseUrl
|
|
111
114
|
*/
|
|
112
|
-
constructor(logger: LoggerInterface, options: TestOpsOptionsType, api: QaseApiInterface, environment?: string, rootSuite?: string, baseUrl?: string);
|
|
115
|
+
constructor(logger: LoggerInterface, options: TestOpsOptionsType, api: QaseApiInterface, withState: boolean, environment?: string, rootSuite?: string, baseUrl?: string);
|
|
113
116
|
/**
|
|
114
117
|
* @returns {Promise<void>}
|
|
115
118
|
*/
|
|
@@ -12,6 +12,7 @@ const models_1 = require("../models");
|
|
|
12
12
|
const qase_error_1 = require("../utils/qase-error");
|
|
13
13
|
const axios_1 = __importDefault(require("axios"));
|
|
14
14
|
const state_1 = require("../state/state");
|
|
15
|
+
const async_mutex_1 = require("async-mutex");
|
|
15
16
|
const defaultChunkSize = 200;
|
|
16
17
|
/**
|
|
17
18
|
* @class TestOpsReporter
|
|
@@ -19,6 +20,7 @@ const defaultChunkSize = 200;
|
|
|
19
20
|
*/
|
|
20
21
|
class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
21
22
|
api;
|
|
23
|
+
withState;
|
|
22
24
|
/**
|
|
23
25
|
* @type {Record<TestStatusEnum, string>}
|
|
24
26
|
*/
|
|
@@ -108,18 +110,21 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
108
110
|
* @private
|
|
109
111
|
*/
|
|
110
112
|
isTestRunReady = false;
|
|
113
|
+
mutex = new async_mutex_1.Mutex();
|
|
111
114
|
/**
|
|
112
115
|
* @param {LoggerInterface} logger
|
|
113
116
|
* @param {ReporterOptionsType & TestOpsOptionsType} options
|
|
114
117
|
* @param {QaseApiInterface} api
|
|
118
|
+
* @param {boolean} withState
|
|
115
119
|
* @param {string | undefined} environment
|
|
116
120
|
* @param {string | undefined} rootSuite
|
|
117
121
|
* @param {string | undefined} baseUrl
|
|
118
122
|
*/
|
|
119
|
-
constructor(logger, options, api, environment, rootSuite, baseUrl) {
|
|
123
|
+
constructor(logger, options, api, withState, environment, rootSuite, baseUrl) {
|
|
120
124
|
const { project, uploadAttachments, run, plan, } = options;
|
|
121
125
|
super(logger);
|
|
122
126
|
this.api = api;
|
|
127
|
+
this.withState = withState;
|
|
123
128
|
this.baseUrl = this.getBaseUrl(baseUrl);
|
|
124
129
|
this.projectCode = project;
|
|
125
130
|
this.isUploadAttachments = uploadAttachments;
|
|
@@ -148,15 +153,21 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
148
153
|
this.showLink(id, result.title);
|
|
149
154
|
}
|
|
150
155
|
}
|
|
151
|
-
await
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
const release = await this.mutex.acquire();
|
|
157
|
+
try {
|
|
158
|
+
await super.addTestResult(result);
|
|
159
|
+
if (!this.isTestRunReady) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const countOfResults = this.batchSize + this.firstIndex;
|
|
163
|
+
if (this.results.length >= countOfResults) {
|
|
164
|
+
const firstIndex = this.firstIndex;
|
|
165
|
+
this.firstIndex = countOfResults;
|
|
166
|
+
await this.publishResults(this.results.slice(firstIndex, countOfResults));
|
|
167
|
+
}
|
|
154
168
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const firstIndex = this.firstIndex;
|
|
158
|
-
this.firstIndex = countOfResults;
|
|
159
|
-
await this.publishResults(this.results.slice(firstIndex, countOfResults));
|
|
169
|
+
finally {
|
|
170
|
+
release();
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
173
|
/**
|
|
@@ -188,7 +199,9 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
188
199
|
this.logger.logDebug(`Test run created: ${result.id}`);
|
|
189
200
|
this.run.id = result.id;
|
|
190
201
|
process.env['QASE_TESTOPS_RUN_ID'] = String(result.id);
|
|
191
|
-
|
|
202
|
+
if (this.withState) {
|
|
203
|
+
state_1.StateManager.setRunId(result.id);
|
|
204
|
+
}
|
|
192
205
|
this.isTestRunReady = true;
|
|
193
206
|
}
|
|
194
207
|
/**
|
|
@@ -233,7 +246,13 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
233
246
|
* @returns {Promise<void>}
|
|
234
247
|
*/
|
|
235
248
|
async publish() {
|
|
236
|
-
await this.
|
|
249
|
+
const release = await this.mutex.acquire();
|
|
250
|
+
try {
|
|
251
|
+
await this.sendResults();
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
release();
|
|
255
|
+
}
|
|
237
256
|
await this.complete();
|
|
238
257
|
}
|
|
239
258
|
/**
|
package/dist/state/state.js
CHANGED
|
@@ -7,13 +7,16 @@ exports.StateManager = void 0;
|
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
class StateManager {
|
|
10
|
-
static statePath = path_1.default.resolve(
|
|
10
|
+
static statePath = path_1.default.resolve(__dirname, 'reporterState.json');
|
|
11
11
|
static getState() {
|
|
12
12
|
let state = {
|
|
13
13
|
RunId: undefined,
|
|
14
14
|
Mode: undefined,
|
|
15
15
|
IsModeChanged: undefined,
|
|
16
16
|
};
|
|
17
|
+
if (!this.isStateExists()) {
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
17
20
|
try {
|
|
18
21
|
const data = (0, fs_1.readFileSync)(this.statePath, 'utf8');
|
|
19
22
|
state = JSON.parse(data);
|
|
@@ -49,6 +52,9 @@ class StateManager {
|
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
static clearState() {
|
|
55
|
+
if (!this.isStateExists()) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
52
58
|
try {
|
|
53
59
|
(0, fs_1.unlinkSync)(this.statePath);
|
|
54
60
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qase-javascript-commons",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.12",
|
|
4
4
|
"description": "Qase JS Reporters",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"mime-types": "^2.1.33",
|
|
35
35
|
"qaseio": "~2.4.0",
|
|
36
36
|
"strip-ansi": "^6.0.1",
|
|
37
|
-
"uuid": "^9.0.0"
|
|
37
|
+
"uuid": "^9.0.0",
|
|
38
|
+
"async-mutex": "~0.5.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@jest/globals": "^29.5.0",
|