qase-javascript-commons 2.6.2 → 2.6.4
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 +47 -0
- package/dist/client/clientV1.d.ts +6 -83
- package/dist/client/clientV1.js +15 -549
- package/dist/client/clientV2.d.ts +6 -24
- package/dist/client/clientV2.js +8 -255
- package/dist/client/services/api-error-handler.d.ts +10 -0
- package/dist/client/services/api-error-handler.js +44 -0
- package/dist/client/services/attachment-service.d.ts +16 -0
- package/dist/client/services/attachment-service.js +209 -0
- package/dist/client/services/configuration-service.d.ts +12 -0
- package/dist/client/services/configuration-service.js +110 -0
- package/dist/client/services/result-transformer.d.ts +23 -0
- package/dist/client/services/result-transformer.js +188 -0
- package/dist/client/services/run-service.d.ts +17 -0
- package/dist/client/services/run-service.js +114 -0
- package/dist/client/transport/api-config-builder.d.ts +8 -0
- package/dist/client/transport/api-config-builder.js +96 -0
- package/dist/formatter/index.d.ts +1 -0
- package/dist/formatter/index.js +3 -1
- package/dist/formatter/report-serializer.d.ts +20 -0
- package/dist/formatter/report-serializer.js +89 -0
- package/dist/qase/options-resolver.d.ts +19 -0
- package/dist/qase/options-resolver.js +47 -0
- package/dist/qase/reporter-factory.d.ts +19 -0
- package/dist/qase/reporter-factory.js +67 -0
- package/dist/qase/status-processor.d.ts +17 -0
- package/dist/qase/status-processor.js +48 -0
- package/dist/qase.d.ts +17 -85
- package/dist/qase.js +133 -415
- package/dist/reporters/report-reporter.d.ts +4 -35
- package/dist/reporters/report-reporter.js +6 -130
- package/dist/reporters/shared/fallback-coordinator.d.ts +47 -0
- package/dist/reporters/shared/fallback-coordinator.js +119 -0
- package/dist/reporters/shared/testops-constants.d.ts +5 -0
- package/dist/reporters/shared/testops-constants.js +8 -0
- package/dist/reporters/shared/testops-url.d.ts +9 -0
- package/dist/reporters/shared/testops-url.js +17 -0
- package/dist/reporters/testops-multi-reporter.d.ts +0 -1
- package/dist/reporters/testops-multi-reporter.js +4 -9
- package/dist/reporters/testops-reporter.d.ts +0 -6
- package/dist/reporters/testops-reporter.js +7 -17
- package/dist/utils/token-masker.d.ts +11 -0
- package/dist/utils/token-masker.js +26 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { Attachment } from '../models';
|
|
|
3
3
|
import { WriterInterface } from '../writer';
|
|
4
4
|
import { LoggerInterface } from '../utils/logger';
|
|
5
5
|
import { HostData } from '../models/host-data';
|
|
6
|
+
import { ReportSerializerInterface } from '../formatter/report-serializer';
|
|
6
7
|
/**
|
|
7
8
|
* @class ReportReporter
|
|
8
9
|
* @extends AbstractReporter
|
|
@@ -15,6 +16,7 @@ export declare class ReportReporter extends AbstractReporter {
|
|
|
15
16
|
private readonly runId;
|
|
16
17
|
private readonly rootSuite;
|
|
17
18
|
private readonly hostData;
|
|
19
|
+
private readonly serializer;
|
|
18
20
|
private startTime;
|
|
19
21
|
/**
|
|
20
22
|
* @param {LoggerInterface} logger
|
|
@@ -25,8 +27,9 @@ export declare class ReportReporter extends AbstractReporter {
|
|
|
25
27
|
* @param {string | undefined} rootSuite
|
|
26
28
|
* @param {number | undefined} runId
|
|
27
29
|
* @param {HostData | undefined} hostData
|
|
30
|
+
* @param {ReportSerializerInterface} serializer
|
|
28
31
|
*/
|
|
29
|
-
constructor(logger: LoggerInterface, writer: WriterInterface, frameworkName: string, reporterName: string, environment?: string, rootSuite?: string, runId?: number, hostData?: HostData);
|
|
32
|
+
constructor(logger: LoggerInterface, writer: WriterInterface, frameworkName: string, reporterName: string, environment?: string, rootSuite?: string, runId?: number, hostData?: HostData, serializer?: ReportSerializerInterface);
|
|
30
33
|
/**
|
|
31
34
|
* @returns {Promise<void>}
|
|
32
35
|
*/
|
|
@@ -44,38 +47,4 @@ export declare class ReportReporter extends AbstractReporter {
|
|
|
44
47
|
* @returns {TestStepType[]}
|
|
45
48
|
*/
|
|
46
49
|
private copyStepAttachments;
|
|
47
|
-
/**
|
|
48
|
-
* Serialize a test result to spec-compliant JSON format.
|
|
49
|
-
* Transforms internal model fields to match the Qase Report specification.
|
|
50
|
-
* @private
|
|
51
|
-
*/
|
|
52
|
-
private serializeResultForReport;
|
|
53
|
-
/**
|
|
54
|
-
* Transform group_params Record to param_groups array of arrays.
|
|
55
|
-
* Same logic as clientV2.ts transformGroupParams.
|
|
56
|
-
* @private
|
|
57
|
-
*/
|
|
58
|
-
private transformGroupParams;
|
|
59
|
-
/**
|
|
60
|
-
* Serialize attachment for report output (exclude size and content fields).
|
|
61
|
-
* @private
|
|
62
|
-
*/
|
|
63
|
-
private serializeAttachment;
|
|
64
|
-
/**
|
|
65
|
-
* Serialize steps recursively, transforming:
|
|
66
|
-
* - data.data -> data.input_data (STEP-01)
|
|
67
|
-
* - attachments -> execution.attachments (STEP-02)
|
|
68
|
-
* @private
|
|
69
|
-
*/
|
|
70
|
-
private serializeSteps;
|
|
71
|
-
/**
|
|
72
|
-
* Serialize a single step to spec-compliant format.
|
|
73
|
-
* @private
|
|
74
|
-
*/
|
|
75
|
-
private serializeStep;
|
|
76
|
-
/**
|
|
77
|
-
* Serialize step data, transforming data.data -> data.input_data for text steps.
|
|
78
|
-
* @private
|
|
79
|
-
*/
|
|
80
|
-
private serializeStepData;
|
|
81
50
|
}
|
|
@@ -4,6 +4,7 @@ exports.ReportReporter = void 0;
|
|
|
4
4
|
const abstract_reporter_1 = require("./abstract-reporter");
|
|
5
5
|
const models_1 = require("../models");
|
|
6
6
|
const hostData_1 = require("../utils/hostData");
|
|
7
|
+
const report_serializer_1 = require("../formatter/report-serializer");
|
|
7
8
|
/**
|
|
8
9
|
* @class ReportReporter
|
|
9
10
|
* @extends AbstractReporter
|
|
@@ -16,6 +17,7 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
16
17
|
runId;
|
|
17
18
|
rootSuite;
|
|
18
19
|
hostData;
|
|
20
|
+
serializer;
|
|
19
21
|
startTime = Date.now();
|
|
20
22
|
/**
|
|
21
23
|
* @param {LoggerInterface} logger
|
|
@@ -26,8 +28,9 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
26
28
|
* @param {string | undefined} rootSuite
|
|
27
29
|
* @param {number | undefined} runId
|
|
28
30
|
* @param {HostData | undefined} hostData
|
|
31
|
+
* @param {ReportSerializerInterface} serializer
|
|
29
32
|
*/
|
|
30
|
-
constructor(logger, writer, frameworkName, reporterName, environment, rootSuite, runId, hostData) {
|
|
33
|
+
constructor(logger, writer, frameworkName, reporterName, environment, rootSuite, runId, hostData, serializer = new report_serializer_1.ReportSerializer()) {
|
|
31
34
|
super(logger);
|
|
32
35
|
this.writer = writer;
|
|
33
36
|
this.frameworkName = frameworkName;
|
|
@@ -36,6 +39,7 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
36
39
|
this.runId = runId;
|
|
37
40
|
this.rootSuite = rootSuite;
|
|
38
41
|
this.hostData = hostData;
|
|
42
|
+
this.serializer = serializer;
|
|
39
43
|
}
|
|
40
44
|
/**
|
|
41
45
|
* @returns {Promise<void>}
|
|
@@ -80,7 +84,7 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
// Serialize to spec-compliant format before writing
|
|
83
|
-
const serialized = this.
|
|
87
|
+
const serialized = this.serializer.serializeResult(result);
|
|
84
88
|
await this.writer.writeTestResult(serialized);
|
|
85
89
|
}
|
|
86
90
|
}
|
|
@@ -158,133 +162,5 @@ class ReportReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
158
162
|
}
|
|
159
163
|
return steps;
|
|
160
164
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Serialize a test result to spec-compliant JSON format.
|
|
163
|
-
* Transforms internal model fields to match the Qase Report specification.
|
|
164
|
-
* @private
|
|
165
|
-
*/
|
|
166
|
-
serializeResultForReport(result) {
|
|
167
|
-
// Transform testops_id -> testops_ids (RSLT-01)
|
|
168
|
-
let testopsIds = null;
|
|
169
|
-
if (result.testops_id !== null) {
|
|
170
|
-
testopsIds = Array.isArray(result.testops_id)
|
|
171
|
-
? result.testops_id
|
|
172
|
-
: [result.testops_id];
|
|
173
|
-
}
|
|
174
|
-
// Transform group_params -> param_groups (RSLT-02)
|
|
175
|
-
const paramGroups = this.transformGroupParams(result.group_params);
|
|
176
|
-
// Serialize attachments (exclude size and content fields)
|
|
177
|
-
const attachments = result.attachments.map(att => this.serializeAttachment(att));
|
|
178
|
-
// Serialize steps (handle data.data -> data.input_data and attachments -> execution.attachments)
|
|
179
|
-
const steps = this.serializeSteps(result.steps);
|
|
180
|
-
// Build spec-compliant result object
|
|
181
|
-
const serialized = {
|
|
182
|
-
id: result.id,
|
|
183
|
-
title: result.title,
|
|
184
|
-
signature: result.signature,
|
|
185
|
-
execution: result.execution,
|
|
186
|
-
fields: result.fields,
|
|
187
|
-
attachments: attachments,
|
|
188
|
-
steps: steps,
|
|
189
|
-
params: result.params,
|
|
190
|
-
param_groups: paramGroups,
|
|
191
|
-
testops_ids: testopsIds,
|
|
192
|
-
relations: result.relations,
|
|
193
|
-
muted: result.muted,
|
|
194
|
-
message: result.message,
|
|
195
|
-
tags: result.tags,
|
|
196
|
-
};
|
|
197
|
-
// Internal-only fields are excluded: testops_id, group_params, run_id, author, testops_project_mapping, preparedAttachments
|
|
198
|
-
return serialized;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Transform group_params Record to param_groups array of arrays.
|
|
202
|
-
* Same logic as clientV2.ts transformGroupParams.
|
|
203
|
-
* @private
|
|
204
|
-
*/
|
|
205
|
-
transformGroupParams(groupParams) {
|
|
206
|
-
const keys = Object.keys(groupParams);
|
|
207
|
-
if (keys.length === 0) {
|
|
208
|
-
return [];
|
|
209
|
-
}
|
|
210
|
-
return [keys];
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Serialize attachment for report output (exclude size and content fields).
|
|
214
|
-
* @private
|
|
215
|
-
*/
|
|
216
|
-
serializeAttachment(att) {
|
|
217
|
-
return {
|
|
218
|
-
id: att.id,
|
|
219
|
-
file_name: att.file_name,
|
|
220
|
-
mime_type: att.mime_type,
|
|
221
|
-
file_path: att.file_path,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Serialize steps recursively, transforming:
|
|
226
|
-
* - data.data -> data.input_data (STEP-01)
|
|
227
|
-
* - attachments -> execution.attachments (STEP-02)
|
|
228
|
-
* @private
|
|
229
|
-
*/
|
|
230
|
-
serializeSteps(steps) {
|
|
231
|
-
return steps.map(step => this.serializeStep(step));
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Serialize a single step to spec-compliant format.
|
|
235
|
-
* @private
|
|
236
|
-
*/
|
|
237
|
-
serializeStep(step) {
|
|
238
|
-
// Transform step data (handle data.data -> data.input_data for text steps)
|
|
239
|
-
const data = this.serializeStepData(step);
|
|
240
|
-
// Serialize step attachments
|
|
241
|
-
const attachments = step.attachments.map(att => this.serializeAttachment(att));
|
|
242
|
-
// Move attachments into execution.attachments (STEP-02)
|
|
243
|
-
const execution = {
|
|
244
|
-
...step.execution,
|
|
245
|
-
attachments: attachments,
|
|
246
|
-
};
|
|
247
|
-
// Recursively serialize nested steps
|
|
248
|
-
const nestedSteps = this.serializeSteps(step.steps);
|
|
249
|
-
return {
|
|
250
|
-
id: step.id,
|
|
251
|
-
step_type: step.step_type,
|
|
252
|
-
data: data,
|
|
253
|
-
parent_id: step.parent_id,
|
|
254
|
-
execution: execution,
|
|
255
|
-
steps: nestedSteps,
|
|
256
|
-
// Note: attachments field is NOT at top-level in serialized output
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Serialize step data, transforming data.data -> data.input_data for text steps.
|
|
261
|
-
* @private
|
|
262
|
-
*/
|
|
263
|
-
serializeStepData(step) {
|
|
264
|
-
const data = { ...step.data };
|
|
265
|
-
// For text steps, rename data.data to data.input_data (STEP-01)
|
|
266
|
-
if (step.step_type === models_1.StepType.TEXT && 'data' in data) {
|
|
267
|
-
const textData = data;
|
|
268
|
-
return {
|
|
269
|
-
action: textData.action,
|
|
270
|
-
expected_result: textData.expected_result,
|
|
271
|
-
input_data: textData.data, // Rename: data -> input_data
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
// For gherkin steps, convert to text format (STEP-03)
|
|
275
|
-
if (step.step_type === models_1.StepType.GHERKIN && 'keyword' in data && 'name' in data) {
|
|
276
|
-
const gherkinData = data;
|
|
277
|
-
return {
|
|
278
|
-
action: `${gherkinData.keyword} ${gherkinData.name}`,
|
|
279
|
-
expected_result: null,
|
|
280
|
-
input_data: null, // JS GherkinData has no data field
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
// For request steps, pass raw fields through (all 7 StepRequestData fields are preserved)
|
|
284
|
-
if (step.step_type === models_1.StepType.REQUEST && 'request_method' in data) {
|
|
285
|
-
return data;
|
|
286
|
-
}
|
|
287
|
-
return data;
|
|
288
|
-
}
|
|
289
165
|
}
|
|
290
166
|
exports.ReportReporter = ReportReporter;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { InternalReporterInterface } from '../abstract-reporter';
|
|
2
|
+
import { LoggerInterface } from '../../utils/logger';
|
|
3
|
+
export interface FallbackCoordinatorCallbacks {
|
|
4
|
+
onUpstreamFailure?: () => void;
|
|
5
|
+
onFallbackFailure?: () => void;
|
|
6
|
+
onFallbackActivated?: () => void;
|
|
7
|
+
onDisabled?: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Encapsulates the upstream → fallback → disabled cascade used across all
|
|
11
|
+
* `QaseReporter` lifecycle methods. Keeps both reporters' state (useFallback
|
|
12
|
+
* / disabled) in one place instead of spreading try/catch blocks everywhere.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FallbackCoordinator {
|
|
15
|
+
private readonly logger;
|
|
16
|
+
private readonly upstream;
|
|
17
|
+
private readonly fallback;
|
|
18
|
+
private readonly callbacks;
|
|
19
|
+
private useFallback;
|
|
20
|
+
private disabled;
|
|
21
|
+
private onDisabledFired;
|
|
22
|
+
constructor(logger: LoggerInterface, upstream: InternalReporterInterface | undefined, fallback: InternalReporterInterface | undefined, callbacks?: FallbackCoordinatorCallbacks);
|
|
23
|
+
isDisabled(): boolean;
|
|
24
|
+
isUsingFallback(): boolean;
|
|
25
|
+
setDisabled(value: boolean): void;
|
|
26
|
+
setUseFallback(value: boolean): void;
|
|
27
|
+
getUpstream(): InternalReporterInterface | undefined;
|
|
28
|
+
getFallback(): InternalReporterInterface | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Clear the disabled / useFallback flags so the coordinator can be reused
|
|
31
|
+
* for a fresh test run. Does not re-arm the `onDisabled` callback — it
|
|
32
|
+
* only fires once per coordinator lifetime regardless of resets.
|
|
33
|
+
*/
|
|
34
|
+
reset(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Run `op` on the currently active reporter. On upstream failure, switch to
|
|
37
|
+
* the fallback (copying accumulated results over once) and retry. On
|
|
38
|
+
* fallback failure, disable the coordinator.
|
|
39
|
+
*
|
|
40
|
+
* Returns the operation's result, or `undefined` if the coordinator is
|
|
41
|
+
* disabled or the operation produced no value.
|
|
42
|
+
*/
|
|
43
|
+
run<T>(op: (reporter: InternalReporterInterface) => Promise<T>, opName: string): Promise<T | undefined>;
|
|
44
|
+
private disable;
|
|
45
|
+
private activateFallback;
|
|
46
|
+
private runOnFallback;
|
|
47
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FallbackCoordinator = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Encapsulates the upstream → fallback → disabled cascade used across all
|
|
6
|
+
* `QaseReporter` lifecycle methods. Keeps both reporters' state (useFallback
|
|
7
|
+
* / disabled) in one place instead of spreading try/catch blocks everywhere.
|
|
8
|
+
*/
|
|
9
|
+
class FallbackCoordinator {
|
|
10
|
+
logger;
|
|
11
|
+
upstream;
|
|
12
|
+
fallback;
|
|
13
|
+
callbacks;
|
|
14
|
+
useFallback = false;
|
|
15
|
+
disabled = false;
|
|
16
|
+
onDisabledFired = false;
|
|
17
|
+
constructor(logger, upstream, fallback, callbacks = {}) {
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
this.upstream = upstream;
|
|
20
|
+
this.fallback = fallback;
|
|
21
|
+
this.callbacks = callbacks;
|
|
22
|
+
}
|
|
23
|
+
isDisabled() {
|
|
24
|
+
return this.disabled;
|
|
25
|
+
}
|
|
26
|
+
isUsingFallback() {
|
|
27
|
+
return this.useFallback;
|
|
28
|
+
}
|
|
29
|
+
setDisabled(value) {
|
|
30
|
+
this.disabled = value;
|
|
31
|
+
}
|
|
32
|
+
setUseFallback(value) {
|
|
33
|
+
this.useFallback = value;
|
|
34
|
+
}
|
|
35
|
+
getUpstream() {
|
|
36
|
+
return this.upstream;
|
|
37
|
+
}
|
|
38
|
+
getFallback() {
|
|
39
|
+
return this.fallback;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clear the disabled / useFallback flags so the coordinator can be reused
|
|
43
|
+
* for a fresh test run. Does not re-arm the `onDisabled` callback — it
|
|
44
|
+
* only fires once per coordinator lifetime regardless of resets.
|
|
45
|
+
*/
|
|
46
|
+
reset() {
|
|
47
|
+
this.disabled = false;
|
|
48
|
+
this.useFallback = false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Run `op` on the currently active reporter. On upstream failure, switch to
|
|
52
|
+
* the fallback (copying accumulated results over once) and retry. On
|
|
53
|
+
* fallback failure, disable the coordinator.
|
|
54
|
+
*
|
|
55
|
+
* Returns the operation's result, or `undefined` if the coordinator is
|
|
56
|
+
* disabled or the operation produced no value.
|
|
57
|
+
*/
|
|
58
|
+
async run(op, opName) {
|
|
59
|
+
if (this.disabled)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (this.useFallback) {
|
|
62
|
+
return this.runOnFallback(op, opName);
|
|
63
|
+
}
|
|
64
|
+
if (!this.upstream) {
|
|
65
|
+
// No upstream configured — route directly to fallback without
|
|
66
|
+
// copying results (there are none to copy).
|
|
67
|
+
if (!this.fallback)
|
|
68
|
+
return undefined;
|
|
69
|
+
this.useFallback = true;
|
|
70
|
+
return this.runOnFallback(op, opName);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
return await op(this.upstream);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.logger.logError(`Unable to ${opName} in the upstream reporter:`, error);
|
|
77
|
+
this.callbacks.onUpstreamFailure?.();
|
|
78
|
+
if (!this.fallback) {
|
|
79
|
+
this.disable();
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
this.activateFallback();
|
|
83
|
+
return this.runOnFallback(op, opName);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
disable() {
|
|
87
|
+
if (this.disabled)
|
|
88
|
+
return;
|
|
89
|
+
this.disabled = true;
|
|
90
|
+
if (!this.onDisabledFired) {
|
|
91
|
+
this.onDisabledFired = true;
|
|
92
|
+
this.callbacks.onDisabled?.();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
activateFallback() {
|
|
96
|
+
if (this.useFallback)
|
|
97
|
+
return;
|
|
98
|
+
const results = this.upstream?.getTestResults() ?? [];
|
|
99
|
+
this.fallback?.setTestResults(results);
|
|
100
|
+
this.useFallback = true;
|
|
101
|
+
this.callbacks.onFallbackActivated?.();
|
|
102
|
+
}
|
|
103
|
+
async runOnFallback(op, opName) {
|
|
104
|
+
if (!this.fallback) {
|
|
105
|
+
this.disable();
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
return await op(this.fallback);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
this.logger.logError(`Unable to ${opName} in the fallback reporter:`, error);
|
|
113
|
+
this.callbacks.onFallbackFailure?.();
|
|
114
|
+
this.disable();
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.FallbackCoordinator = FallbackCoordinator;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_BATCH_SIZE = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Default number of test results per upload batch.
|
|
6
|
+
* Used by TestOpsReporter and TestOpsMultiReporter when no explicit batch size is configured.
|
|
7
|
+
*/
|
|
8
|
+
exports.DEFAULT_BATCH_SIZE = 200;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the TestOps app URL from an API host.
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* - undefined / empty / "qase.io" → "https://app.qase.io"
|
|
6
|
+
* - "api.domain" → "https://app.domain"
|
|
7
|
+
* - anything else → "https://<host>"
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveTestOpsBaseUrl(host: string | undefined): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTestOpsBaseUrl = resolveTestOpsBaseUrl;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the TestOps app URL from an API host.
|
|
6
|
+
*
|
|
7
|
+
* Rules:
|
|
8
|
+
* - undefined / empty / "qase.io" → "https://app.qase.io"
|
|
9
|
+
* - "api.domain" → "https://app.domain"
|
|
10
|
+
* - anything else → "https://<host>"
|
|
11
|
+
*/
|
|
12
|
+
function resolveTestOpsBaseUrl(host) {
|
|
13
|
+
if (!host || host === 'qase.io') {
|
|
14
|
+
return 'https://app.qase.io';
|
|
15
|
+
}
|
|
16
|
+
return `https://${host.replace('api', 'app')}`;
|
|
17
|
+
}
|
|
@@ -10,7 +10,8 @@ const abstract_reporter_1 = require("./abstract-reporter");
|
|
|
10
10
|
const models_1 = require("../models");
|
|
11
11
|
const async_mutex_1 = require("async-mutex");
|
|
12
12
|
const clientV2_1 = require("../client/clientV2");
|
|
13
|
-
const
|
|
13
|
+
const testops_constants_1 = require("./shared/testops-constants");
|
|
14
|
+
const testops_url_1 = require("./shared/testops-url");
|
|
14
15
|
/**
|
|
15
16
|
* Multi-project TestOps reporter. Sends test results to multiple Qase projects
|
|
16
17
|
* with different test case IDs per project. Each project gets its own run.
|
|
@@ -33,8 +34,8 @@ class TestOpsMultiReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
33
34
|
showPublicReportLink;
|
|
34
35
|
constructor(logger, testopsOptions, multiConfig, withState, hostData, reporterName, frameworkPackage, environment, baseUrl, batchSize, showPublicReportLink) {
|
|
35
36
|
super(logger);
|
|
36
|
-
this.baseUrl =
|
|
37
|
-
this.batchSize = batchSize ?? testopsOptions.batch?.size ??
|
|
37
|
+
this.baseUrl = (0, testops_url_1.resolveTestOpsBaseUrl)(baseUrl ?? testopsOptions.api?.host);
|
|
38
|
+
this.batchSize = batchSize ?? testopsOptions.batch?.size ?? testops_constants_1.DEFAULT_BATCH_SIZE;
|
|
38
39
|
this.showPublicReportLink = showPublicReportLink ?? testopsOptions.showPublicReportLink;
|
|
39
40
|
this.defaultProject =
|
|
40
41
|
multiConfig.default_project ??
|
|
@@ -288,12 +289,6 @@ class TestOpsMultiReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
288
289
|
}
|
|
289
290
|
return '';
|
|
290
291
|
}
|
|
291
|
-
getBaseUrl(url) {
|
|
292
|
-
if (!url || url === 'qase.io') {
|
|
293
|
-
return 'https://app.qase.io';
|
|
294
|
-
}
|
|
295
|
-
return `https://${url.replace('api', 'app')}`;
|
|
296
|
-
}
|
|
297
292
|
showLink(projectCode, id, title) {
|
|
298
293
|
const runId = this.runIds.get(projectCode);
|
|
299
294
|
if (runId === undefined)
|
|
@@ -62,12 +62,6 @@ export declare class TestOpsReporter extends AbstractReporter {
|
|
|
62
62
|
* @returns {Promise<void>}
|
|
63
63
|
*/
|
|
64
64
|
complete(): Promise<void>;
|
|
65
|
-
/**
|
|
66
|
-
* @param {string | undefined} url
|
|
67
|
-
* @return string
|
|
68
|
-
* @private
|
|
69
|
-
*/
|
|
70
|
-
private getBaseUrl;
|
|
71
65
|
/**
|
|
72
66
|
* @param {number | null} id
|
|
73
67
|
* @param {string} title
|
|
@@ -9,7 +9,8 @@ const abstract_reporter_1 = require("./abstract-reporter");
|
|
|
9
9
|
const models_1 = require("../models");
|
|
10
10
|
const state_1 = require("../state/state");
|
|
11
11
|
const async_mutex_1 = require("async-mutex");
|
|
12
|
-
const
|
|
12
|
+
const testops_constants_1 = require("./shared/testops-constants");
|
|
13
|
+
const testops_url_1 = require("./shared/testops-url");
|
|
13
14
|
/**
|
|
14
15
|
* @class TestOpsReporter
|
|
15
16
|
* @extends AbstractReporter
|
|
@@ -39,8 +40,8 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
39
40
|
this.withState = withState;
|
|
40
41
|
this.projectCode = projectCode;
|
|
41
42
|
this.showPublicReportLink = showPublicReportLink;
|
|
42
|
-
this.baseUrl =
|
|
43
|
-
this.batchSize = batchSize ??
|
|
43
|
+
this.baseUrl = (0, testops_url_1.resolveTestOpsBaseUrl)(baseUrl);
|
|
44
|
+
this.batchSize = batchSize ?? testops_constants_1.DEFAULT_BATCH_SIZE;
|
|
44
45
|
this.runId = runId;
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
@@ -128,12 +129,12 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
128
129
|
}
|
|
129
130
|
const remainingResults = this.results.slice(this.firstIndex);
|
|
130
131
|
if (this.firstIndex < this.results.length) {
|
|
131
|
-
if (remainingResults.length <=
|
|
132
|
+
if (remainingResults.length <= testops_constants_1.DEFAULT_BATCH_SIZE) {
|
|
132
133
|
await this.publishResults(remainingResults);
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
|
-
for (let i = 0; i < remainingResults.length; i +=
|
|
136
|
-
await this.publishResults(remainingResults.slice(i, i +
|
|
136
|
+
for (let i = 0; i < remainingResults.length; i += testops_constants_1.DEFAULT_BATCH_SIZE) {
|
|
137
|
+
await this.publishResults(remainingResults.slice(i, i + testops_constants_1.DEFAULT_BATCH_SIZE));
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
// Clear results because we don't need to send them again then we use Cypress reporter
|
|
@@ -164,17 +165,6 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
164
165
|
}
|
|
165
166
|
this.logger.log((0, chalk_1.default) `{green Run ${this.runId} completed}`);
|
|
166
167
|
}
|
|
167
|
-
/**
|
|
168
|
-
* @param {string | undefined} url
|
|
169
|
-
* @return string
|
|
170
|
-
* @private
|
|
171
|
-
*/
|
|
172
|
-
getBaseUrl(url) {
|
|
173
|
-
if (!url || url === 'qase.io') {
|
|
174
|
-
return 'https://app.qase.io';
|
|
175
|
-
}
|
|
176
|
-
return `https://${url.replace('api', 'app')}`;
|
|
177
|
-
}
|
|
178
168
|
/**
|
|
179
169
|
* @param {number | null} id
|
|
180
170
|
* @param {string} title
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mask a token for safe logging.
|
|
3
|
+
* Tokens with 7 chars or fewer are fully masked. Longer tokens expose only
|
|
4
|
+
* the first 3 and last 4 characters.
|
|
5
|
+
*/
|
|
6
|
+
export declare function maskToken(token: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Produce a deep-cloned copy of options with `testops.api.token` masked.
|
|
9
|
+
* Safe to log — never mutates the input.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sanitizeOptionsForLog<T>(options: T): T;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.maskToken = maskToken;
|
|
4
|
+
exports.sanitizeOptionsForLog = sanitizeOptionsForLog;
|
|
5
|
+
/**
|
|
6
|
+
* Mask a token for safe logging.
|
|
7
|
+
* Tokens with 7 chars or fewer are fully masked. Longer tokens expose only
|
|
8
|
+
* the first 3 and last 4 characters.
|
|
9
|
+
*/
|
|
10
|
+
function maskToken(token) {
|
|
11
|
+
if (token.length <= 7) {
|
|
12
|
+
return '*'.repeat(token.length);
|
|
13
|
+
}
|
|
14
|
+
return `${token.slice(0, 3)}****${token.slice(-4)}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Produce a deep-cloned copy of options with `testops.api.token` masked.
|
|
18
|
+
* Safe to log — never mutates the input.
|
|
19
|
+
*/
|
|
20
|
+
function sanitizeOptionsForLog(options) {
|
|
21
|
+
const sanitized = JSON.parse(JSON.stringify(options));
|
|
22
|
+
if (sanitized.testops?.api?.token) {
|
|
23
|
+
sanitized.testops.api.token = maskToken(sanitized.testops.api.token);
|
|
24
|
+
}
|
|
25
|
+
return sanitized;
|
|
26
|
+
}
|