qase-javascript-commons 2.0.12 → 2.1.0-beta.1

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 CHANGED
@@ -1,3 +1,17 @@
1
+ # qase-javascript-commons@2.1.0-beta.1
2
+
3
+ ## What's new
4
+
5
+ - update a `InternalReporterInterface`. Added a new methods `sendResults` and `complete` to send the results and complete the test run.
6
+ - add `StateManager` class to manage and share the state of the reporter between the different instances of the reporter.
7
+
8
+ # qase-javascript-commons@2.0.13
9
+
10
+ ## What's new
11
+
12
+ - If a plan ID is specified then when creating a test run it also specifies the plan ID.
13
+ - If a test run ID is specified then the reporter won't check for the existence of a test run.
14
+
1
15
  # qase-javascript-commons@2.0.12
2
16
 
3
17
  ## What's new
package/dist/qase.d.ts CHANGED
@@ -5,6 +5,9 @@ export interface ReporterInterface {
5
5
  publish(): Promise<void>;
6
6
  startTestRun(): void;
7
7
  isCaptureLogs(): boolean;
8
+ getResults(): TestResultType[];
9
+ sendResults(): Promise<void>;
10
+ complete(): Promise<void>;
8
11
  }
9
12
  /**
10
13
  * @class QaseReporter
@@ -47,14 +50,20 @@ export declare class QaseReporter implements ReporterInterface {
47
50
  private useFallback;
48
51
  private readonly logger;
49
52
  private startTestRunOperation?;
53
+ private options;
50
54
  /**
51
55
  * @param {OptionsType} options
52
56
  */
53
57
  private constructor();
58
+ getResults(): TestResultType[];
59
+ setTestResults(results: TestResultType[]): void;
60
+ sendResults(): Promise<void>;
61
+ complete(): Promise<void>;
54
62
  /**
55
63
  * @returns {void}
56
64
  */
57
65
  startTestRun(): void;
66
+ startTestRunAsync(): Promise<void>;
58
67
  /**
59
68
  * @param {OptionsType} options
60
69
  * @returns {QaseReporter}
package/dist/qase.js CHANGED
@@ -17,6 +17,7 @@ const get_package_version_1 = require("./utils/get-package-version");
17
17
  const custom_boundary_1 = require("./utils/custom-boundary");
18
18
  const disabled_exception_1 = require("./utils/disabled-exception");
19
19
  const logger_1 = require("./utils/logger");
20
+ const state_1 = require("./state/state");
20
21
  /**
21
22
  * @type {Record<TestStatusEnum, (test: TestResultType) => string>}
22
23
  */
@@ -79,8 +80,18 @@ class QaseReporter {
79
80
  * @private
80
81
  */
81
82
  this.useFallback = false;
83
+ if (state_1.StateManager.isStateExists()) {
84
+ const state = state_1.StateManager.getState();
85
+ if (state.IsModeChanged && state.Mode) {
86
+ process.env[env_1.EnvEnum.mode] = state.Mode.toString();
87
+ }
88
+ if (state.RunId) {
89
+ process.env[env_1.EnvRunEnum.id] = state.RunId.toString();
90
+ }
91
+ }
82
92
  const env = (0, env_1.envToConfig)((0, env_schema_1.default)({ schema: env_1.envValidationSchema }));
83
93
  const composedOptions = (0, options_1.composeOptions)(options, env);
94
+ this.options = composedOptions;
84
95
  this.logger = new logger_1.Logger({ debug: composedOptions.debug });
85
96
  this.logger.logDebug(`Config: ${JSON.stringify(composedOptions)}`);
86
97
  this.captureLogs = composedOptions.captureLogs;
@@ -120,6 +131,89 @@ class QaseReporter {
120
131
  }
121
132
  }
122
133
  }
134
+ if (!state_1.StateManager.isStateExists()) {
135
+ const state = {
136
+ RunId: undefined,
137
+ Mode: this.useFallback ? composedOptions.fallback : composedOptions.mode,
138
+ IsModeChanged: undefined,
139
+ };
140
+ if (this.disabled) {
141
+ state.Mode = options_1.ModeEnum.off;
142
+ }
143
+ state_1.StateManager.setState(state);
144
+ }
145
+ }
146
+ getResults() {
147
+ if (this.disabled) {
148
+ return [];
149
+ }
150
+ if (this.useFallback) {
151
+ return this.fallbackReporter?.getTestResults() ?? [];
152
+ }
153
+ return this.upstreamReporter?.getTestResults() ?? [];
154
+ }
155
+ setTestResults(results) {
156
+ if (this.disabled) {
157
+ return;
158
+ }
159
+ if (this.useFallback) {
160
+ this.fallbackReporter?.setTestResults(results);
161
+ }
162
+ else {
163
+ this.upstreamReporter?.setTestResults(results);
164
+ }
165
+ }
166
+ async sendResults() {
167
+ if (this.disabled) {
168
+ return;
169
+ }
170
+ try {
171
+ await this.upstreamReporter?.sendResults();
172
+ }
173
+ catch (error) {
174
+ this.logger.logError('Unable to send the results to the upstream reporter:', error);
175
+ if (this.fallbackReporter == undefined) {
176
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
177
+ return;
178
+ }
179
+ if (!this.useFallback) {
180
+ this.fallbackReporter.setTestResults(this.upstreamReporter?.getTestResults() ?? []);
181
+ this.useFallback = true;
182
+ }
183
+ try {
184
+ await this.fallbackReporter?.sendResults();
185
+ state_1.StateManager.setMode(this.options.fallback);
186
+ }
187
+ catch (error) {
188
+ this.logger.logError('Unable to send the results to the fallback reporter:', error);
189
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
190
+ }
191
+ }
192
+ }
193
+ async complete() {
194
+ state_1.StateManager.clearState();
195
+ if (this.disabled) {
196
+ return;
197
+ }
198
+ try {
199
+ await this.upstreamReporter?.complete();
200
+ }
201
+ catch (error) {
202
+ this.logger.logError('Unable to complete the run in the upstream reporter:', error);
203
+ if (this.fallbackReporter == undefined) {
204
+ return;
205
+ }
206
+ if (!this.useFallback) {
207
+ this.fallbackReporter.setTestResults(this.upstreamReporter?.getTestResults() ?? []);
208
+ this.useFallback = true;
209
+ }
210
+ try {
211
+ await this.fallbackReporter?.complete();
212
+ }
213
+ catch (error) {
214
+ this.logger.logError('Unable to complete the run in the fallback reporter:', error);
215
+ }
216
+ }
123
217
  }
124
218
  /**
125
219
  * @returns {void}
@@ -134,18 +228,25 @@ class QaseReporter {
134
228
  this.logger.logError('Unable to start test run in the upstream reporter: ', error);
135
229
  if (this.fallbackReporter == undefined) {
136
230
  this.disabled = true;
231
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
137
232
  return;
138
233
  }
139
234
  try {
140
235
  this.startTestRunOperation = this.fallbackReporter?.startTestRun();
236
+ state_1.StateManager.setMode(this.options.fallback);
141
237
  }
142
238
  catch (error) {
143
239
  this.logger.logError('Unable to start test run in the fallback reporter: ', error);
144
240
  this.disabled = true;
241
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
145
242
  }
146
243
  }
147
244
  }
148
245
  }
246
+ async startTestRunAsync() {
247
+ this.startTestRun();
248
+ await this.startTestRunOperation;
249
+ }
149
250
  /**
150
251
  * @param {OptionsType} options
151
252
  * @returns {QaseReporter}
@@ -174,6 +275,7 @@ class QaseReporter {
174
275
  this.logger.logError('Unable to add the result to the upstream reporter:', error);
175
276
  if (this.fallbackReporter == undefined) {
176
277
  this.disabled = true;
278
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
177
279
  return;
178
280
  }
179
281
  if (!this.useFallback) {
@@ -191,10 +293,12 @@ class QaseReporter {
191
293
  async addTestResultToFallback(result) {
192
294
  try {
193
295
  await this.fallbackReporter?.addTestResult(result);
296
+ state_1.StateManager.setMode(this.options.fallback);
194
297
  }
195
298
  catch (error) {
196
299
  this.logger.logError('Unable to add the result to the fallback reporter:', error);
197
300
  this.disabled = true;
301
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
198
302
  }
199
303
  }
200
304
  /**
@@ -220,6 +324,7 @@ class QaseReporter {
220
324
  this.logger.logError('Unable to publish the run results to the upstream reporter:', error);
221
325
  if (this.fallbackReporter == undefined) {
222
326
  this.disabled = true;
327
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
223
328
  return;
224
329
  }
225
330
  if (!this.useFallback) {
@@ -236,8 +341,10 @@ class QaseReporter {
236
341
  async publishFallback() {
237
342
  try {
238
343
  await this.fallbackReporter?.publish();
344
+ state_1.StateManager.setMode(this.options.fallback);
239
345
  }
240
346
  catch (error) {
347
+ state_1.StateManager.setMode(options_1.ModeEnum.off);
241
348
  this.logger.logError('Unable to publish the run results to the fallback reporter:', error);
242
349
  this.disabled = true;
243
350
  }
@@ -6,6 +6,8 @@ export interface InternalReporterInterface {
6
6
  startTestRun(): Promise<void>;
7
7
  getTestResults(): TestResultType[];
8
8
  setTestResults(results: TestResultType[]): void;
9
+ sendResults(): Promise<void>;
10
+ complete(): Promise<void>;
9
11
  }
10
12
  /**
11
13
  * @abstract
@@ -31,6 +33,14 @@ export declare abstract class AbstractReporter implements InternalReporterInterf
31
33
  * @returns {Promise<void>}
32
34
  */
33
35
  abstract startTestRun(): Promise<void>;
36
+ /**
37
+ * @returns {Promise<void>}
38
+ */
39
+ abstract complete(): Promise<void>;
40
+ /**
41
+ * @returns {Promise<void>}
42
+ */
43
+ abstract sendResults(): Promise<void>;
34
44
  /**
35
45
  * @protected
36
46
  * @param {LoggerInterface} logger
@@ -24,7 +24,9 @@ class AbstractReporter {
24
24
  * @returns {TestResultType[]}
25
25
  */
26
26
  getTestResults() {
27
- return this.results;
27
+ const results = this.results;
28
+ this.results = [];
29
+ return results;
28
30
  }
29
31
  /**
30
32
  * @param {TestResultType} result
@@ -28,6 +28,8 @@ export declare class ReportReporter extends AbstractReporter {
28
28
  *
29
29
  */
30
30
  publish(): Promise<void>;
31
+ sendResults(): Promise<void>;
32
+ complete(): Promise<void>;
31
33
  /**
32
34
  * @param {TestStepType[]} steps
33
35
  * @returns {TestStepType[]}
@@ -61,6 +61,40 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
61
61
  *
62
62
  */
63
63
  async publish() {
64
+ await this.sendResults();
65
+ await this.complete();
66
+ }
67
+ async sendResults() {
68
+ this.writer.clearPreviousResults();
69
+ for (const result of this.results) {
70
+ if (result.attachments.length > 0) {
71
+ result.attachments = this.writer.writeAttachment(result.attachments);
72
+ }
73
+ result.steps = this.copyStepAttachments(result.steps);
74
+ result.run_id = this.runId ?? null;
75
+ if (result.relations != null && this.rootSuite != null) {
76
+ const data = {
77
+ title: this.rootSuite,
78
+ public_id: null,
79
+ };
80
+ result.relations.suite?.data.unshift(data);
81
+ }
82
+ else if (this.rootSuite != null) {
83
+ result.relations = {
84
+ suite: {
85
+ data: [
86
+ {
87
+ title: this.rootSuite,
88
+ public_id: null,
89
+ },
90
+ ],
91
+ },
92
+ };
93
+ }
94
+ await this.writer.writeTestResult(result);
95
+ }
96
+ }
97
+ async complete() {
64
98
  this.writer.clearPreviousResults();
65
99
  const report = {
66
100
  title: 'Test report',
@@ -103,9 +137,6 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
103
137
  report.stats.muted++;
104
138
  break;
105
139
  }
106
- if (result.execution.thread && !report.threads.includes(result.execution.thread)) {
107
- report.threads.push(result.execution.thread);
108
- }
109
140
  report.execution.cumulative_duration += result.execution.duration ?? 0;
110
141
  report.results.push({
111
142
  duration: result.execution.duration ?? 0,
@@ -114,31 +145,6 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
114
145
  thread: result.execution.thread,
115
146
  title: result.title,
116
147
  });
117
- if (result.attachments.length > 0) {
118
- result.attachments = this.writer.writeAttachment(result.attachments);
119
- }
120
- result.steps = this.copyStepAttachments(result.steps);
121
- result.run_id = this.runId ?? null;
122
- if (result.relations != null && this.rootSuite != null) {
123
- const data = {
124
- title: this.rootSuite,
125
- public_id: null,
126
- };
127
- result.relations.suite?.data.unshift(data);
128
- }
129
- else if (this.rootSuite != null) {
130
- result.relations = {
131
- suite: {
132
- data: [
133
- {
134
- title: this.rootSuite,
135
- public_id: null,
136
- },
137
- ],
138
- },
139
- };
140
- }
141
- await this.writer.writeTestResult(result);
142
148
  }
143
149
  const path = await this.writer.writeReport(report);
144
150
  this.logger.log(`Report saved to ${path}`);
@@ -62,10 +62,15 @@ export declare class TestOpsReporter extends AbstractReporter {
62
62
  */
63
63
  private run;
64
64
  /**
65
- * @type { number | undefined}
65
+ * @type { string | undefined}
66
66
  * @private
67
67
  */
68
68
  private readonly environment;
69
+ /**
70
+ * @type { number | undefined}
71
+ * @private
72
+ */
73
+ private readonly planId;
69
74
  /**
70
75
  * @type {TestResultType[]}
71
76
  * @private
@@ -128,6 +133,14 @@ export declare class TestOpsReporter extends AbstractReporter {
128
133
  * @returns {Promise<void>}
129
134
  */
130
135
  publish(): Promise<void>;
136
+ /**
137
+ * @returns {Promise<void>}
138
+ */
139
+ sendResults(): Promise<void>;
140
+ /**
141
+ * @returns {Promise<void>}
142
+ */
143
+ complete(): Promise<void>;
131
144
  /**
132
145
  * @param {TestResultType} result
133
146
  * @returns Promise<ResultCreateV2>
@@ -167,12 +180,6 @@ export declare class TestOpsReporter extends AbstractReporter {
167
180
  */
168
181
  private transformStepsV1;
169
182
  private logEmptyStep;
170
- /**
171
- * @param {number} runId
172
- * @returns {Promise<void>}
173
- * @private
174
- */
175
- private checkRun;
176
183
  /**
177
184
  * @param {string} title
178
185
  * @param {string} description
@@ -11,6 +11,7 @@ const abstract_reporter_1 = require("./abstract-reporter");
11
11
  const models_1 = require("../models");
12
12
  const qase_error_1 = require("../utils/qase-error");
13
13
  const axios_1 = __importDefault(require("axios"));
14
+ const state_1 = require("../state/state");
14
15
  const defaultChunkSize = 200;
15
16
  /**
16
17
  * @class TestOpsReporter
@@ -26,7 +27,7 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
26
27
  * @param {string | undefined} baseUrl
27
28
  */
28
29
  constructor(logger, options, api, environment, rootSuite, baseUrl) {
29
- const { project, uploadAttachments, run, } = options;
30
+ const { project, uploadAttachments, run, plan, } = options;
30
31
  super(logger);
31
32
  this.api = api;
32
33
  /**
@@ -44,6 +45,7 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
44
45
  this.isUploadAttachments = uploadAttachments;
45
46
  this.run = { complete: true, ...run };
46
47
  this.environment = environment;
48
+ this.planId = plan.id;
47
49
  this.batchSize = options.batch?.size ?? defaultChunkSize;
48
50
  this.useV2 = options.useV2 ?? false;
49
51
  this.defect = options.defect ?? false;
@@ -86,8 +88,6 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
86
88
  */
87
89
  async checkOrCreateTestRun() {
88
90
  if (this.run.id !== undefined) {
89
- this.logger.logDebug('Check test run');
90
- await this.checkRun(this.run.id);
91
91
  this.isTestRunReady = true;
92
92
  return;
93
93
  }
@@ -112,6 +112,7 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
112
112
  this.logger.logDebug(`Test run created: ${result.id}`);
113
113
  this.run.id = result.id;
114
114
  process.env['QASE_TESTOPS_RUN_ID'] = String(result.id);
115
+ state_1.StateManager.setRunId(result.id);
115
116
  this.isTestRunReady = true;
116
117
  }
117
118
  /**
@@ -156,6 +157,13 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
156
157
  * @returns {Promise<void>}
157
158
  */
158
159
  async publish() {
160
+ await this.sendResults();
161
+ await this.complete();
162
+ }
163
+ /**
164
+ * @returns {Promise<void>}
165
+ */
166
+ async sendResults() {
159
167
  if (this.results.length === 0) {
160
168
  this.logger.log((0, chalk_1.default) `{yellow No results to send to Qase}`);
161
169
  return;
@@ -165,6 +173,11 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
165
173
  }
166
174
  // Clear results because we don't need to send them again then we use Cypress reporter
167
175
  this.results.length = 0;
176
+ }
177
+ /**
178
+ * @returns {Promise<void>}
179
+ */
180
+ async complete() {
168
181
  if (!this.run.complete) {
169
182
  return;
170
183
  }
@@ -369,20 +382,6 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
369
382
  logEmptyStep(testTitle) {
370
383
  this.logger.log((0, chalk_1.default) `{magenta Test '${testTitle}' has empty action in step. The reporter will mark this step as unnamed step.}`);
371
384
  }
372
- /**
373
- * @param {number} runId
374
- * @returns {Promise<void>}
375
- * @private
376
- */
377
- async checkRun(runId) {
378
- try {
379
- const resp = await this.api.runs.getRun(this.projectCode, runId);
380
- this.logger.log(`Get run result on checking run "${String(resp.data.result?.id)}"`);
381
- }
382
- catch (error) {
383
- throw this.processError(error, 'Error on checking run');
384
- }
385
- }
386
385
  /**
387
386
  * @param {string} title
388
387
  * @param {string} description
@@ -402,6 +401,9 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
402
401
  if (environment !== undefined) {
403
402
  runObject.environment_id = environment;
404
403
  }
404
+ if (this.planId) {
405
+ runObject.plan_id = this.planId;
406
+ }
405
407
  const { data } = await this.api.runs.createRun(this.projectCode, runObject);
406
408
  return data;
407
409
  }
@@ -0,0 +1,15 @@
1
+ import { ModeEnum } from '../options';
2
+ export interface StateModel {
3
+ RunId: number | undefined;
4
+ Mode: ModeEnum | undefined;
5
+ IsModeChanged: boolean | undefined;
6
+ }
7
+ export declare class StateManager {
8
+ static getState(): StateModel;
9
+ static setRunId(runId: number): void;
10
+ static setMode(mode: ModeEnum): void;
11
+ static setIsModeChanged(isModeChanged: boolean): void;
12
+ static setState(state: StateModel): void;
13
+ static clearState(): void;
14
+ static isStateExists(): boolean;
15
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StateManager = void 0;
4
+ const fs_1 = require("fs");
5
+ const statePath = 'reporterState.json';
6
+ class StateManager {
7
+ static getState() {
8
+ let state = {
9
+ RunId: undefined,
10
+ Mode: undefined,
11
+ IsModeChanged: undefined,
12
+ };
13
+ try {
14
+ const data = (0, fs_1.readFileSync)(statePath, 'utf8');
15
+ state = JSON.parse(data);
16
+ }
17
+ catch (err) {
18
+ console.error('Error reading state file:', err);
19
+ }
20
+ return state;
21
+ }
22
+ static setRunId(runId) {
23
+ const state = this.getState();
24
+ state.RunId = runId;
25
+ this.setState(state);
26
+ }
27
+ static setMode(mode) {
28
+ const state = this.getState();
29
+ state.Mode = mode;
30
+ this.setState(state);
31
+ }
32
+ static setIsModeChanged(isModeChanged) {
33
+ const state = this.getState();
34
+ state.IsModeChanged = isModeChanged;
35
+ this.setState(state);
36
+ }
37
+ static setState(state) {
38
+ try {
39
+ const data = JSON.stringify(state);
40
+ (0, fs_1.writeFileSync)(statePath, data);
41
+ }
42
+ catch (err) {
43
+ console.error('Error writing state file:', err);
44
+ }
45
+ }
46
+ static clearState() {
47
+ try {
48
+ (0, fs_1.unlinkSync)(statePath);
49
+ }
50
+ catch (err) {
51
+ console.error('Error clearing state file:', err);
52
+ }
53
+ }
54
+ static isStateExists() {
55
+ return (0, fs_1.existsSync)(statePath);
56
+ }
57
+ }
58
+ exports.StateManager = StateManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qase-javascript-commons",
3
- "version": "2.0.12",
3
+ "version": "2.1.0-beta.1",
4
4
  "description": "Qase JS Reporters",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",