qase-javascript-commons 2.0.0-beta.1 → 2.0.0-beta.10
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 +74 -97
- package/changelog.md +159 -0
- package/dist/config/config-validation-schema.js +36 -6
- package/dist/env/env-enum.d.ts +22 -8
- package/dist/env/env-enum.js +22 -6
- package/dist/env/env-to-config.js +10 -3
- package/dist/env/env-type.d.ts +9 -4
- package/dist/env/env-validation-schema.js +23 -4
- package/dist/formatter/index.d.ts +1 -0
- package/dist/formatter/index.js +3 -1
- package/dist/formatter/jsonp-formatter.d.ts +13 -0
- package/dist/formatter/jsonp-formatter.js +28 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/attachment.d.ts +9 -0
- package/dist/models/attachment.js +2 -0
- package/dist/models/execution-sum.d.ts +6 -0
- package/dist/models/execution-sum.js +2 -0
- package/dist/models/host-data.d.ts +11 -0
- package/dist/models/host-data.js +2 -0
- package/dist/models/index.d.ts +6 -2
- package/dist/models/index.js +6 -4
- package/dist/models/report.d.ts +14 -0
- package/dist/models/report.js +2 -0
- package/dist/models/short-result.d.ts +7 -0
- package/dist/models/short-result.js +2 -0
- package/dist/models/stats.d.ts +8 -0
- package/dist/models/stats.js +2 -0
- package/dist/models/step-data.d.ts +9 -0
- package/dist/models/step-data.js +2 -0
- package/dist/models/step-execution.d.ts +11 -0
- package/dist/models/step-execution.js +9 -0
- package/dist/models/test-execution.d.ts +19 -0
- package/dist/models/test-execution.js +15 -0
- package/dist/models/test-result.d.ts +24 -20
- package/dist/models/test-result.js +0 -13
- package/dist/models/test-step.d.ts +12 -10
- package/dist/models/test-step.js +6 -7
- package/dist/options/options-type.d.ts +2 -0
- package/dist/qase.d.ts +54 -10
- package/dist/qase.js +158 -37
- package/dist/reporters/abstract-reporter.d.ts +23 -40
- package/dist/reporters/abstract-reporter.js +33 -75
- package/dist/reporters/index.d.ts +1 -1
- package/dist/reporters/report-reporter.d.ts +28 -13
- package/dist/reporters/report-reporter.js +160 -15
- package/dist/reporters/testops-reporter.d.ts +80 -37
- package/dist/reporters/testops-reporter.js +246 -148
- package/dist/utils/logger.d.ts +29 -0
- package/dist/utils/logger.js +121 -0
- package/dist/utils/mimeTypes.d.ts +5 -0
- package/dist/utils/mimeTypes.js +41 -0
- package/dist/writer/driver-enum.d.ts +7 -0
- package/dist/writer/driver-enum.js +9 -1
- package/dist/writer/fs-writer.d.ts +22 -8
- package/dist/writer/fs-writer.js +75 -8
- package/dist/writer/index.d.ts +1 -1
- package/dist/writer/index.js +2 -1
- package/dist/writer/writer-interface.d.ts +5 -2
- package/package.json +6 -2
|
@@ -6,202 +6,293 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.TestOpsReporter = void 0;
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const strip_ansi_1 = __importDefault(require("strip-ansi"));
|
|
10
9
|
const qaseio_1 = require("qaseio");
|
|
11
10
|
const abstract_reporter_1 = require("./abstract-reporter");
|
|
12
11
|
const models_1 = require("../models");
|
|
13
12
|
const qase_error_1 = require("../utils/qase-error");
|
|
13
|
+
const defaultChunkSize = 200;
|
|
14
14
|
/**
|
|
15
15
|
* @class TestOpsReporter
|
|
16
16
|
* @extends AbstractReporter
|
|
17
17
|
*/
|
|
18
18
|
class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
19
19
|
/**
|
|
20
|
+
* @param {LoggerInterface} logger
|
|
20
21
|
* @param {ReporterOptionsType & TestOpsOptionsType} options
|
|
21
22
|
* @param {QaseApiInterface} api
|
|
22
|
-
* @param {
|
|
23
|
+
* @param {number} environment
|
|
23
24
|
*/
|
|
24
|
-
constructor(options, api,
|
|
25
|
-
const { project,
|
|
26
|
-
super(
|
|
25
|
+
constructor(logger, options, api, environment) {
|
|
26
|
+
const { project, uploadAttachments, run, } = options;
|
|
27
|
+
super(logger);
|
|
27
28
|
this.api = api;
|
|
28
29
|
/**
|
|
29
|
-
* @type {
|
|
30
|
-
* @private
|
|
31
|
-
*/
|
|
32
|
-
this.results = [];
|
|
33
|
-
/**
|
|
34
|
-
* @type {Record<string, string[]>}
|
|
30
|
+
* @type {number}
|
|
35
31
|
* @private
|
|
36
32
|
*/
|
|
37
|
-
this.
|
|
33
|
+
this.firstIndex = 0;
|
|
38
34
|
/**
|
|
39
|
-
* @type {
|
|
35
|
+
* @type {boolean}
|
|
40
36
|
* @private
|
|
41
37
|
*/
|
|
42
|
-
this.
|
|
38
|
+
this.isTestRunReady = false;
|
|
39
|
+
const baseUrl = 'https://app.qase.io';
|
|
43
40
|
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
44
41
|
this.projectCode = project;
|
|
45
|
-
this.
|
|
42
|
+
this.isUploadAttachments = uploadAttachments;
|
|
46
43
|
this.run = { complete: true, ...run };
|
|
44
|
+
this.environment = environment;
|
|
45
|
+
this.batchSize = options.batch?.size ?? defaultChunkSize;
|
|
46
|
+
this.useV2 = options.useV2 ?? false;
|
|
47
|
+
this.defect = options.defect ?? false;
|
|
47
48
|
}
|
|
48
49
|
/**
|
|
49
|
-
* @
|
|
50
|
+
* @returns {Promise<void>}
|
|
50
51
|
*/
|
|
51
|
-
|
|
52
|
-
this.
|
|
53
|
-
this.addAttachments(result);
|
|
52
|
+
async startTestRun() {
|
|
53
|
+
await this.checkOrCreateTestRun();
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
/**
|
|
56
|
+
* @param {TestResultType} result
|
|
57
|
+
* @returns {Promise<void>}
|
|
58
|
+
*/
|
|
59
|
+
async addTestResult(result) {
|
|
60
|
+
await super.addTestResult(result);
|
|
61
|
+
if (!this.isTestRunReady) {
|
|
62
|
+
return;
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
const countOfResults = this.batchSize + this.firstIndex;
|
|
65
|
+
if (this.results.length >= countOfResults) {
|
|
66
|
+
await this.publishResults(this.results.slice(this.firstIndex, countOfResults));
|
|
67
|
+
this.firstIndex = countOfResults;
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
/**
|
|
66
71
|
* @returns {Promise<void>}
|
|
67
72
|
*/
|
|
68
|
-
async
|
|
69
|
-
let runId;
|
|
73
|
+
async checkOrCreateTestRun() {
|
|
70
74
|
if (this.run.id !== undefined) {
|
|
75
|
+
this.logger.logDebug('Check test run');
|
|
71
76
|
await this.checkRun(this.run.id);
|
|
72
|
-
|
|
77
|
+
this.isTestRunReady = true;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.logger.logDebug('Create test run');
|
|
81
|
+
const { result } = await this.createRun(this.run.title, this.run.description, this.environment);
|
|
82
|
+
if (!result?.id) {
|
|
83
|
+
throw new Error('Cannot create run.');
|
|
84
|
+
}
|
|
85
|
+
this.logger.logDebug(`Test run created: ${result.id}`);
|
|
86
|
+
this.run.id = result.id;
|
|
87
|
+
process.env['QASE_TESTOPS_RUN_ID'] = String(result.id);
|
|
88
|
+
this.isTestRunReady = true;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* @returns {Promise<void>}
|
|
92
|
+
* @param testResults
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
async publishResults(testResults) {
|
|
96
|
+
if (this.useV2) {
|
|
97
|
+
const results = [];
|
|
98
|
+
for (const result of testResults) {
|
|
99
|
+
const resultCreateV2 = await this.transformTestResult(result);
|
|
100
|
+
results.push(resultCreateV2);
|
|
101
|
+
}
|
|
102
|
+
await this.api.result.createResultsV2(this.projectCode, this.run.id, {
|
|
103
|
+
results: results,
|
|
104
|
+
});
|
|
73
105
|
}
|
|
74
106
|
else {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
const results = [];
|
|
108
|
+
for (const result of testResults) {
|
|
109
|
+
const resultCreate = await this.transformTestResultV1(result);
|
|
110
|
+
results.push(resultCreate);
|
|
78
111
|
}
|
|
79
|
-
|
|
112
|
+
await this.api.results.createResultBulk(this.projectCode, this.run.id, {
|
|
113
|
+
results: results,
|
|
114
|
+
});
|
|
80
115
|
}
|
|
81
|
-
|
|
82
|
-
|
|
116
|
+
this.logger.logDebug(`Results sent to Qase: ${testResults.length}`);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
async publish() {
|
|
122
|
+
if (this.results.length === 0) {
|
|
123
|
+
this.logger.log((0, chalk_1.default) `{yellow No results to send to Qase}`);
|
|
83
124
|
return;
|
|
84
125
|
}
|
|
85
|
-
if (this.
|
|
86
|
-
await this.
|
|
126
|
+
if (this.firstIndex < this.results.length) {
|
|
127
|
+
await this.publishResults(this.results.slice(this.firstIndex));
|
|
87
128
|
}
|
|
88
|
-
await this.api.result.createResultsV2(this.projectCode, runId, {
|
|
89
|
-
results: this.results.map((result) => this.transformTestResult(result)),
|
|
90
|
-
});
|
|
91
|
-
this.log((0, chalk_1.default) `{green ${this.results.length} result(s) sent to Qase}`);
|
|
92
129
|
if (!this.run.complete) {
|
|
93
130
|
return;
|
|
94
131
|
}
|
|
95
132
|
try {
|
|
96
|
-
await this.api.runs.completeRun(this.projectCode,
|
|
97
|
-
this.log((0, chalk_1.default) `{green Run ${
|
|
133
|
+
await this.api.runs.completeRun(this.projectCode, this.run.id);
|
|
134
|
+
this.logger.log((0, chalk_1.default) `{green Run ${this.run.id} completed}`);
|
|
98
135
|
}
|
|
99
136
|
catch (error) {
|
|
100
137
|
throw new qase_error_1.QaseError('Error on completing run', { cause: error });
|
|
101
138
|
}
|
|
102
|
-
const runUrl = `${this.baseUrl}/run/${this.projectCode}/dashboard/${
|
|
103
|
-
this.log((0, chalk_1.default) `{blue Test run link: ${runUrl}}`);
|
|
139
|
+
const runUrl = `${this.baseUrl}/run/${this.projectCode}/dashboard/${this.run.id}`;
|
|
140
|
+
this.logger.log((0, chalk_1.default) `{blue Test run link: ${runUrl}}`);
|
|
104
141
|
}
|
|
105
142
|
/**
|
|
106
143
|
* @param {TestResultType} result
|
|
107
|
-
* @returns
|
|
144
|
+
* @returns Promise<ResultCreateV2>
|
|
108
145
|
* @private
|
|
109
146
|
*/
|
|
110
|
-
transformTestResult(result) {
|
|
111
|
-
const
|
|
147
|
+
async transformTestResult(result) {
|
|
148
|
+
const attachments = await this.uploadAttachments(result.attachments);
|
|
149
|
+
const steps = await this.transformSteps(result.steps);
|
|
150
|
+
const model = {
|
|
112
151
|
title: result.title,
|
|
113
|
-
|
|
114
|
-
|
|
152
|
+
execution: this.getExecution(result.execution),
|
|
153
|
+
testops_id: Array.isArray(result.testops_id) ? null : result.testops_id,
|
|
154
|
+
attachments: attachments,
|
|
155
|
+
steps: steps,
|
|
156
|
+
params: result.params,
|
|
157
|
+
relations: this.getRelation(result.relations),
|
|
158
|
+
message: result.message,
|
|
115
159
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
suite: {
|
|
119
|
-
data: this.getSuites(result.suiteTitle),
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
if (result.testOpsId[0]) {
|
|
124
|
-
resultObject.testops_id = result.testOpsId[0];
|
|
125
|
-
}
|
|
126
|
-
if (result.error) {
|
|
127
|
-
resultObject.message = (0, strip_ansi_1.default)(result.error.message);
|
|
128
|
-
}
|
|
129
|
-
if (result.steps) {
|
|
130
|
-
resultObject.steps = this.transformSteps(result.steps);
|
|
131
|
-
}
|
|
132
|
-
return resultObject;
|
|
160
|
+
this.logger.logDebug(`Transformed result: ${JSON.stringify(model)}`);
|
|
161
|
+
return model;
|
|
133
162
|
}
|
|
134
163
|
/**
|
|
135
|
-
* @param {
|
|
136
|
-
* @returns
|
|
164
|
+
* @param {TestResultType} result
|
|
165
|
+
* @returns Promise<ResultCreate>
|
|
137
166
|
* @private
|
|
138
167
|
*/
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
168
|
+
async transformTestResultV1(result) {
|
|
169
|
+
const attachments = await this.uploadAttachments(result.attachments);
|
|
170
|
+
const steps = await this.transformStepsV1(result.steps);
|
|
171
|
+
const resultCreate = {
|
|
172
|
+
attachments: attachments,
|
|
173
|
+
comment: result.message,
|
|
174
|
+
defect: this.defect,
|
|
175
|
+
param: result.params,
|
|
176
|
+
stacktrace: result.execution.stacktrace,
|
|
177
|
+
start_time: result.execution.start_time ? result.execution.start_time | 0 : null,
|
|
178
|
+
status: result.execution.status,
|
|
179
|
+
steps: steps,
|
|
180
|
+
time: result.execution.end_time,
|
|
181
|
+
time_ms: result.execution.duration,
|
|
182
|
+
};
|
|
183
|
+
const id = Array.isArray(result.testops_id) ? null : result.testops_id;
|
|
184
|
+
if (id) {
|
|
185
|
+
resultCreate.case_id = id;
|
|
186
|
+
return resultCreate;
|
|
187
|
+
}
|
|
188
|
+
resultCreate.case = {
|
|
189
|
+
title: result.title,
|
|
190
|
+
suite_title: result.relations?.suite ? result.relations?.suite?.data.map((suite) => suite.title).join('\t') : null,
|
|
191
|
+
};
|
|
192
|
+
this.logger.logDebug(`Transformed result: ${JSON.stringify(resultCreate)}`);
|
|
193
|
+
return resultCreate;
|
|
142
194
|
}
|
|
143
195
|
/**
|
|
144
|
-
* @param {TestResultType} result
|
|
145
196
|
* @returns {ResultExecution}
|
|
146
197
|
* @private
|
|
198
|
+
* @param {TestExecution} exec
|
|
147
199
|
*/
|
|
148
|
-
getExecution(
|
|
149
|
-
|
|
150
|
-
status: TestOpsReporter.statusMap[
|
|
200
|
+
getExecution(exec) {
|
|
201
|
+
return {
|
|
202
|
+
status: TestOpsReporter.statusMap[exec.status],
|
|
203
|
+
start_time: exec.start_time,
|
|
204
|
+
end_time: exec.end_time,
|
|
205
|
+
duration: exec.duration,
|
|
206
|
+
stacktrace: exec.stacktrace,
|
|
207
|
+
thread: exec.thread,
|
|
151
208
|
};
|
|
152
|
-
if (result.startTime) {
|
|
153
|
-
execution.start_time = result.startTime;
|
|
154
|
-
}
|
|
155
|
-
if (result.duration) {
|
|
156
|
-
execution.duration = result.duration;
|
|
157
|
-
}
|
|
158
|
-
if (result.endTime) {
|
|
159
|
-
execution.end_time = result.endTime;
|
|
160
|
-
}
|
|
161
|
-
if (result.error) {
|
|
162
|
-
execution.stacktrace = (0, strip_ansi_1.default)(String(result.error.stack));
|
|
163
|
-
}
|
|
164
|
-
return execution;
|
|
165
209
|
}
|
|
166
210
|
/**
|
|
167
|
-
* @param {
|
|
168
|
-
* @returns {
|
|
211
|
+
* @param {Relation | null} relation
|
|
212
|
+
* @returns {ResultRelations}
|
|
169
213
|
* @private
|
|
170
214
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return [];
|
|
215
|
+
getRelation(relation) {
|
|
216
|
+
if (!relation || !relation.suite) {
|
|
217
|
+
return {};
|
|
175
218
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
219
|
+
const suiteData = [];
|
|
220
|
+
for (const data of relation.suite.data) {
|
|
221
|
+
suiteData.push({
|
|
222
|
+
public_id: null,
|
|
223
|
+
title: data.title,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
suite: {
|
|
228
|
+
data: suiteData,
|
|
229
|
+
},
|
|
230
|
+
};
|
|
182
231
|
}
|
|
183
232
|
/**
|
|
184
233
|
* @param {TestStepType[]} steps
|
|
185
|
-
* @returns
|
|
234
|
+
* @returns Promise<ResultStep[]>
|
|
186
235
|
* @private
|
|
187
236
|
*/
|
|
188
|
-
transformSteps(steps) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
237
|
+
async transformSteps(steps) {
|
|
238
|
+
const resultsSteps = [];
|
|
239
|
+
for (const step of steps) {
|
|
240
|
+
const attachmentHashes = await this.uploadAttachments(step.attachments);
|
|
241
|
+
const resultStep = {
|
|
192
242
|
data: {
|
|
193
|
-
action:
|
|
243
|
+
action: '',
|
|
244
|
+
attachments: attachmentHashes,
|
|
194
245
|
},
|
|
195
246
|
execution: {
|
|
196
|
-
status: TestOpsReporter.stepStatusMap[status],
|
|
247
|
+
status: TestOpsReporter.stepStatusMap[step.execution.status],
|
|
197
248
|
},
|
|
198
|
-
attachments: this.getAttachmentsFor(id),
|
|
199
249
|
};
|
|
200
|
-
if (
|
|
201
|
-
step.
|
|
250
|
+
if (step.step_type === models_1.StepType.TEXT) {
|
|
251
|
+
if ('action' in step.data && resultStep.data != undefined) {
|
|
252
|
+
resultStep.data.action = step.data.action;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (step.step_type === models_1.StepType.GHERKIN) {
|
|
256
|
+
if ('keyword' in step.data && resultStep.data != undefined) {
|
|
257
|
+
resultStep.data.action = step.data.keyword;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (step.steps.length > 0) {
|
|
261
|
+
resultStep.steps = await this.transformSteps(step.steps);
|
|
262
|
+
}
|
|
263
|
+
resultsSteps.push(resultStep);
|
|
264
|
+
}
|
|
265
|
+
return resultsSteps;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* @param {TestStepType[]} steps
|
|
269
|
+
* @returns Promise<TestStepResultCreate[]>
|
|
270
|
+
* @private
|
|
271
|
+
*/
|
|
272
|
+
async transformStepsV1(steps) {
|
|
273
|
+
const resultsSteps = [];
|
|
274
|
+
for (const step of steps) {
|
|
275
|
+
const attachmentHashes = await this.uploadAttachments(step.attachments);
|
|
276
|
+
const resultStep = {
|
|
277
|
+
status: TestOpsReporter.stepStatusMapV1[step.execution.status],
|
|
278
|
+
attachments: attachmentHashes,
|
|
279
|
+
};
|
|
280
|
+
if (step.step_type === models_1.StepType.TEXT) {
|
|
281
|
+
if ('action' in step.data) {
|
|
282
|
+
resultStep.action = step.data.action;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (step.step_type === models_1.StepType.GHERKIN) {
|
|
286
|
+
if ('keyword' in step.data) {
|
|
287
|
+
resultStep.action = step.data.keyword;
|
|
288
|
+
}
|
|
202
289
|
}
|
|
203
|
-
|
|
204
|
-
|
|
290
|
+
if (step.steps.length > 0) {
|
|
291
|
+
resultStep.steps = await this.transformStepsV1(step.steps);
|
|
292
|
+
}
|
|
293
|
+
resultsSteps.push(resultStep);
|
|
294
|
+
}
|
|
295
|
+
return resultsSteps;
|
|
205
296
|
}
|
|
206
297
|
/**
|
|
207
298
|
* @param {number} runId
|
|
@@ -211,7 +302,7 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
211
302
|
async checkRun(runId) {
|
|
212
303
|
try {
|
|
213
304
|
const resp = await this.api.runs.getRun(this.projectCode, runId);
|
|
214
|
-
this.log(`Get run result on checking run "${String(resp.data.result?.id)}"`);
|
|
305
|
+
this.logger.log(`Get run result on checking run "${String(resp.data.result?.id)}"`);
|
|
215
306
|
}
|
|
216
307
|
catch (error) {
|
|
217
308
|
throw new qase_error_1.QaseError('Error on checking run', { cause: error });
|
|
@@ -221,7 +312,7 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
221
312
|
* @param {string} title
|
|
222
313
|
* @param {string} description
|
|
223
314
|
* @param {number | undefined} environment
|
|
224
|
-
* @returns {Promise<
|
|
315
|
+
* @returns {Promise<IdResponse>}
|
|
225
316
|
* @private
|
|
226
317
|
*/
|
|
227
318
|
async createRun(title, description, environment) {
|
|
@@ -246,54 +337,61 @@ class TestOpsReporter extends abstract_reporter_1.AbstractReporter {
|
|
|
246
337
|
* @returns {Promise<void>}
|
|
247
338
|
* @private
|
|
248
339
|
*/
|
|
249
|
-
async
|
|
250
|
-
this.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
return acc;
|
|
257
|
-
}, []);
|
|
258
|
-
this.attachmentsMap = Object.fromEntries(await Promise.all(uploads.map(async ([id, request]) => {
|
|
259
|
-
return [id, await request];
|
|
260
|
-
})));
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* @param {string[]} attachments
|
|
264
|
-
* @returns {Promise<AttachmentGet[]>}
|
|
265
|
-
* @private
|
|
266
|
-
*/
|
|
267
|
-
async doUploadAttachments(attachments) {
|
|
268
|
-
return (await Promise.all(attachments.map(async (attachment) => {
|
|
340
|
+
async uploadAttachments(attachments) {
|
|
341
|
+
if (!this.isUploadAttachments) {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
const acc = [];
|
|
345
|
+
for (const attachment of attachments) {
|
|
269
346
|
try {
|
|
270
|
-
|
|
347
|
+
let data;
|
|
348
|
+
if (attachment.file_path) {
|
|
349
|
+
data = { name: attachment.file_name, value: (0, fs_1.createReadStream)(attachment.file_path) };
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
if (typeof attachment.content === 'string') {
|
|
353
|
+
data = { name: attachment.file_name, value: Buffer.from(attachment.content) };
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
data = { name: attachment.file_name, value: attachment.content };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
271
359
|
const response = await this.api.attachments.uploadAttachment(this.projectCode, [data]);
|
|
272
|
-
|
|
360
|
+
if (response.data.result?.[0]?.hash != undefined) {
|
|
361
|
+
acc.push(response.data.result[0].hash);
|
|
362
|
+
}
|
|
273
363
|
}
|
|
274
364
|
catch (error) {
|
|
275
|
-
this.logError('Cannot upload attachment:', error);
|
|
276
|
-
return undefined;
|
|
365
|
+
this.logger.logError('Cannot upload attachment:', error);
|
|
277
366
|
}
|
|
278
|
-
}
|
|
367
|
+
}
|
|
368
|
+
return acc;
|
|
279
369
|
}
|
|
280
370
|
}
|
|
281
371
|
exports.TestOpsReporter = TestOpsReporter;
|
|
282
372
|
/**
|
|
283
|
-
* @type {Record<TestStatusEnum,
|
|
373
|
+
* @type {Record<TestStatusEnum, string>}
|
|
284
374
|
*/
|
|
285
375
|
TestOpsReporter.statusMap = {
|
|
286
|
-
[models_1.TestStatusEnum.passed]:
|
|
287
|
-
[models_1.TestStatusEnum.failed]:
|
|
288
|
-
[models_1.TestStatusEnum.skipped]:
|
|
289
|
-
[models_1.TestStatusEnum.disabled]:
|
|
290
|
-
[models_1.TestStatusEnum.blocked]:
|
|
291
|
-
[models_1.TestStatusEnum.invalid]:
|
|
376
|
+
[models_1.TestStatusEnum.passed]: 'passed',
|
|
377
|
+
[models_1.TestStatusEnum.failed]: 'failed',
|
|
378
|
+
[models_1.TestStatusEnum.skipped]: 'skipped',
|
|
379
|
+
[models_1.TestStatusEnum.disabled]: 'disabled',
|
|
380
|
+
[models_1.TestStatusEnum.blocked]: 'blocked',
|
|
381
|
+
[models_1.TestStatusEnum.invalid]: 'invalid',
|
|
292
382
|
};
|
|
293
383
|
/**
|
|
294
|
-
* @type {Record<StepStatusEnum,
|
|
384
|
+
* @type {Record<StepStatusEnum, ResultStepStatus>}
|
|
295
385
|
*/
|
|
296
386
|
TestOpsReporter.stepStatusMap = {
|
|
387
|
+
[models_1.StepStatusEnum.passed]: qaseio_1.ResultStepStatus.PASSED,
|
|
388
|
+
[models_1.StepStatusEnum.failed]: qaseio_1.ResultStepStatus.FAILED,
|
|
389
|
+
[models_1.StepStatusEnum.blocked]: qaseio_1.ResultStepStatus.BLOCKED,
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* @type {Record<StepStatusEnum, ResultStepStatus>}
|
|
393
|
+
*/
|
|
394
|
+
TestOpsReporter.stepStatusMapV1 = {
|
|
297
395
|
[models_1.StepStatusEnum.passed]: qaseio_1.TestStepResultCreateStatusEnum.PASSED,
|
|
298
396
|
[models_1.StepStatusEnum.failed]: qaseio_1.TestStepResultCreateStatusEnum.FAILED,
|
|
299
397
|
[models_1.StepStatusEnum.blocked]: qaseio_1.TestStepResultCreateStatusEnum.BLOCKED,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface LoggerInterface {
|
|
2
|
+
log(message: string): void;
|
|
3
|
+
logError(message: string, error?: unknown): void;
|
|
4
|
+
logDebug(message: string): void;
|
|
5
|
+
}
|
|
6
|
+
export declare class Logger implements LoggerInterface {
|
|
7
|
+
private readonly debug;
|
|
8
|
+
private readonly filePath;
|
|
9
|
+
constructor(options: {
|
|
10
|
+
debug?: boolean | undefined;
|
|
11
|
+
dir?: string;
|
|
12
|
+
});
|
|
13
|
+
log(message: string): void;
|
|
14
|
+
logError(message: string, error?: unknown): void;
|
|
15
|
+
logDebug(message: string): void;
|
|
16
|
+
private logToFile;
|
|
17
|
+
private doLogError;
|
|
18
|
+
/**
|
|
19
|
+
* @param {AxiosError} error
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
private logApiError;
|
|
23
|
+
/**
|
|
24
|
+
* @param errorFields
|
|
25
|
+
* @returns {string | undefined}
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
private formatErrorFields;
|
|
29
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.Logger = void 0;
|
|
30
|
+
const fs = __importStar(require("fs"));
|
|
31
|
+
const path = __importStar(require("path"));
|
|
32
|
+
const is_axios_error_1 = require("./is-axios-error");
|
|
33
|
+
const qase_error_1 = require("./qase-error");
|
|
34
|
+
const lodash_get_1 = __importDefault(require("lodash.get"));
|
|
35
|
+
class Logger {
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.debug = options.debug;
|
|
38
|
+
const dir = options.dir ?? './logs';
|
|
39
|
+
if (!fs.existsSync(dir)) {
|
|
40
|
+
fs.mkdirSync(dir);
|
|
41
|
+
}
|
|
42
|
+
this.filePath = path.join(dir, 'log.txt');
|
|
43
|
+
}
|
|
44
|
+
log(message) {
|
|
45
|
+
const logMessage = `[INFO] qase: ${message}`;
|
|
46
|
+
console.log(logMessage);
|
|
47
|
+
if (this.debug) {
|
|
48
|
+
this.logToFile(logMessage);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
logError(message, error) {
|
|
52
|
+
const logMessage = `[ERROR] qase: ${this.doLogError(message, error)}`;
|
|
53
|
+
console.error(logMessage);
|
|
54
|
+
if (this.debug) {
|
|
55
|
+
this.logToFile(logMessage);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
logDebug(message) {
|
|
59
|
+
if (this.debug) {
|
|
60
|
+
const logMessage = `[DEBUG] qase: ${message}`;
|
|
61
|
+
console.log(logMessage);
|
|
62
|
+
this.logToFile(logMessage);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
logToFile(message) {
|
|
66
|
+
const formattedMessage = `[${new Date().toISOString()}] ${message}\n`;
|
|
67
|
+
fs.appendFileSync(this.filePath, formattedMessage);
|
|
68
|
+
}
|
|
69
|
+
doLogError(message, error) {
|
|
70
|
+
let logMessage = message;
|
|
71
|
+
if (error instanceof Error) {
|
|
72
|
+
if ((0, is_axios_error_1.isAxiosError)(error)) {
|
|
73
|
+
logMessage += this.logApiError(error);
|
|
74
|
+
}
|
|
75
|
+
else if (error instanceof qase_error_1.QaseError && error.cause) {
|
|
76
|
+
logMessage += this.doLogError('\n Caused by:', error.cause);
|
|
77
|
+
}
|
|
78
|
+
logMessage += `\n ${error.stack || `${error.name}: ${error.message}`}`;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
logMessage += `\n ${String(error)}`;
|
|
82
|
+
}
|
|
83
|
+
return logMessage;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* @param {AxiosError} error
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
logApiError(error) {
|
|
90
|
+
let logMessage = '\n';
|
|
91
|
+
const errorMessage = (0, lodash_get_1.default)(error, 'response.data.errorMessage')
|
|
92
|
+
?? (0, lodash_get_1.default)(error, 'response.data.error')
|
|
93
|
+
?? (0, lodash_get_1.default)(error, 'response.statusText')
|
|
94
|
+
?? 'Unknown error';
|
|
95
|
+
const errorFields = this.formatErrorFields((0, lodash_get_1.default)(error, 'response.data.errorFields'));
|
|
96
|
+
logMessage += `Message: ${String(errorMessage)}`;
|
|
97
|
+
if (errorFields) {
|
|
98
|
+
logMessage += `\n ${errorFields}`;
|
|
99
|
+
}
|
|
100
|
+
return logMessage;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @param errorFields
|
|
104
|
+
* @returns {string | undefined}
|
|
105
|
+
* @private
|
|
106
|
+
*/
|
|
107
|
+
formatErrorFields(errorFields) {
|
|
108
|
+
if (Array.isArray(errorFields)) {
|
|
109
|
+
return errorFields.reduce((acc, item) => {
|
|
110
|
+
const field = (0, lodash_get_1.default)(item, 'field');
|
|
111
|
+
const error = (0, lodash_get_1.default)(item, 'error');
|
|
112
|
+
if (field && error) {
|
|
113
|
+
return acc + `${String(field)}: ${String(error)}\n`;
|
|
114
|
+
}
|
|
115
|
+
return acc;
|
|
116
|
+
}, '');
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.Logger = Logger;
|