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.
Files changed (60) hide show
  1. package/README.md +74 -97
  2. package/changelog.md +159 -0
  3. package/dist/config/config-validation-schema.js +36 -6
  4. package/dist/env/env-enum.d.ts +22 -8
  5. package/dist/env/env-enum.js +22 -6
  6. package/dist/env/env-to-config.js +10 -3
  7. package/dist/env/env-type.d.ts +9 -4
  8. package/dist/env/env-validation-schema.js +23 -4
  9. package/dist/formatter/index.d.ts +1 -0
  10. package/dist/formatter/index.js +3 -1
  11. package/dist/formatter/jsonp-formatter.d.ts +13 -0
  12. package/dist/formatter/jsonp-formatter.js +28 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/models/attachment.d.ts +9 -0
  16. package/dist/models/attachment.js +2 -0
  17. package/dist/models/execution-sum.d.ts +6 -0
  18. package/dist/models/execution-sum.js +2 -0
  19. package/dist/models/host-data.d.ts +11 -0
  20. package/dist/models/host-data.js +2 -0
  21. package/dist/models/index.d.ts +6 -2
  22. package/dist/models/index.js +6 -4
  23. package/dist/models/report.d.ts +14 -0
  24. package/dist/models/report.js +2 -0
  25. package/dist/models/short-result.d.ts +7 -0
  26. package/dist/models/short-result.js +2 -0
  27. package/dist/models/stats.d.ts +8 -0
  28. package/dist/models/stats.js +2 -0
  29. package/dist/models/step-data.d.ts +9 -0
  30. package/dist/models/step-data.js +2 -0
  31. package/dist/models/step-execution.d.ts +11 -0
  32. package/dist/models/step-execution.js +9 -0
  33. package/dist/models/test-execution.d.ts +19 -0
  34. package/dist/models/test-execution.js +15 -0
  35. package/dist/models/test-result.d.ts +24 -20
  36. package/dist/models/test-result.js +0 -13
  37. package/dist/models/test-step.d.ts +12 -10
  38. package/dist/models/test-step.js +6 -7
  39. package/dist/options/options-type.d.ts +2 -0
  40. package/dist/qase.d.ts +54 -10
  41. package/dist/qase.js +158 -37
  42. package/dist/reporters/abstract-reporter.d.ts +23 -40
  43. package/dist/reporters/abstract-reporter.js +33 -75
  44. package/dist/reporters/index.d.ts +1 -1
  45. package/dist/reporters/report-reporter.d.ts +28 -13
  46. package/dist/reporters/report-reporter.js +160 -15
  47. package/dist/reporters/testops-reporter.d.ts +80 -37
  48. package/dist/reporters/testops-reporter.js +246 -148
  49. package/dist/utils/logger.d.ts +29 -0
  50. package/dist/utils/logger.js +121 -0
  51. package/dist/utils/mimeTypes.d.ts +5 -0
  52. package/dist/utils/mimeTypes.js +41 -0
  53. package/dist/writer/driver-enum.d.ts +7 -0
  54. package/dist/writer/driver-enum.js +9 -1
  55. package/dist/writer/fs-writer.d.ts +22 -8
  56. package/dist/writer/fs-writer.js +75 -8
  57. package/dist/writer/index.d.ts +1 -1
  58. package/dist/writer/index.js +2 -1
  59. package/dist/writer/writer-interface.d.ts +5 -2
  60. 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 {LoggerInterface} logger
23
+ * @param {number} environment
23
24
  */
24
- constructor(options, api, logger) {
25
- const { project, baseUrl = 'https://app.qase.io', uploadAttachments, run, ...restOptions } = options;
26
- super(restOptions, logger);
25
+ constructor(logger, options, api, environment) {
26
+ const { project, uploadAttachments, run, } = options;
27
+ super(logger);
27
28
  this.api = api;
28
29
  /**
29
- * @type {TestResultType[]}
30
- * @private
31
- */
32
- this.results = [];
33
- /**
34
- * @type {Record<string, string[]>}
30
+ * @type {number}
35
31
  * @private
36
32
  */
37
- this.attachments = {};
33
+ this.firstIndex = 0;
38
34
  /**
39
- * @type {Record<string, string[]>}
35
+ * @type {boolean}
40
36
  * @private
41
37
  */
42
- this.attachmentsMap = {};
38
+ this.isTestRunReady = false;
39
+ const baseUrl = 'https://app.qase.io';
43
40
  this.baseUrl = baseUrl.replace(/\/$/, '');
44
41
  this.projectCode = project;
45
- this.uploadAttachments = uploadAttachments;
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
- * @param {TestResultType} result
50
+ * @returns {Promise<void>}
50
51
  */
51
- addTestResult(result) {
52
- this.results.push(result);
53
- this.addAttachments(result);
52
+ async startTestRun() {
53
+ await this.checkOrCreateTestRun();
54
54
  }
55
- addAttachments(resultOrStep) {
56
- if (resultOrStep.attachments) {
57
- this.attachments[resultOrStep.id] = resultOrStep.attachments;
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
- if (resultOrStep.steps) {
60
- for (const step of resultOrStep.steps) {
61
- this.addAttachments(step);
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 publish() {
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
- runId = this.run.id;
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 { result } = await this.createRun(this.run.title, this.run.description, this.run.environment);
76
- if (!result?.id) {
77
- throw new Error('Cannot create run.');
107
+ const results = [];
108
+ for (const result of testResults) {
109
+ const resultCreate = await this.transformTestResultV1(result);
110
+ results.push(resultCreate);
78
111
  }
79
- runId = result.id;
112
+ await this.api.results.createResultBulk(this.projectCode, this.run.id, {
113
+ results: results,
114
+ });
80
115
  }
81
- if (!this.results.length) {
82
- this.log('No test cases were matched. Ensure that your tests are declared correctly.');
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.uploadAttachments && Object.keys(this.attachments).length) {
86
- await this.prepareAttachments();
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, runId);
97
- this.log((0, chalk_1.default) `{green Run ${runId} completed}`);
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/${runId}`;
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 {ResultCreate}
144
+ * @returns Promise<ResultCreateV2>
108
145
  * @private
109
146
  */
110
- transformTestResult(result) {
111
- const resultObject = {
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
- attachments: this.getAttachmentsFor(result.id),
114
- execution: this.getExecution(result),
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
- if (result.suiteTitle) {
117
- resultObject.relations = {
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 {string | string[]} suiteTitle
136
- * @returns {RelationSuiteItem[]}
164
+ * @param {TestResultType} result
165
+ * @returns Promise<ResultCreate>
137
166
  * @private
138
167
  */
139
- getSuites(suiteTitle) {
140
- const suiteTitles = Array.isArray(suiteTitle) ? suiteTitle : suiteTitle.split('\t');
141
- return suiteTitles.map((title) => ({ title }));
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(result) {
149
- const execution = {
150
- status: TestOpsReporter.statusMap[result.status],
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 {string} id
168
- * @returns {ResultAttachment[]}
211
+ * @param {Relation | null} relation
212
+ * @returns {ResultRelations}
169
213
  * @private
170
214
  */
171
- getAttachmentsFor(id) {
172
- const attachments = this.attachmentsMap[id];
173
- if (!attachments) {
174
- return [];
215
+ getRelation(relation) {
216
+ if (!relation || !relation.suite) {
217
+ return {};
175
218
  }
176
- return attachments.reduce((acc, attachment) => {
177
- if (attachment.hash) {
178
- acc.push({ id: attachment.hash });
179
- }
180
- return acc;
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 {ResultStep[]}
234
+ * @returns Promise<ResultStep[]>
186
235
  * @private
187
236
  */
188
- transformSteps(steps) {
189
- return steps.map(({ id, title, status, steps, }) => {
190
- const step = {
191
- step_type: 'text',
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: title,
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 (steps) {
201
- step.steps = this.transformSteps(steps);
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
- return step;
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<any>}
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 prepareAttachments() {
250
- this.log((0, chalk_1.default) `{yellow Uploading attachments to Qase}`);
251
- const uploads = Object.keys(this.attachments).reduce((acc, id) => {
252
- const attachment = this.attachments[id];
253
- if (attachment) {
254
- acc.push([id, this.doUploadAttachments(attachment)]);
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
- const data = (0, fs_1.createReadStream)(attachment);
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
- return response.data.result?.[0];
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
- }))).filter((attachments) => Boolean(attachments));
367
+ }
368
+ return acc;
279
369
  }
280
370
  }
281
371
  exports.TestOpsReporter = TestOpsReporter;
282
372
  /**
283
- * @type {Record<TestStatusEnum, ResultExecutionStatusEnum>}
373
+ * @type {Record<TestStatusEnum, string>}
284
374
  */
285
375
  TestOpsReporter.statusMap = {
286
- [models_1.TestStatusEnum.passed]: qaseio_1.ResultExecutionStatusEnum.PASSED,
287
- [models_1.TestStatusEnum.failed]: qaseio_1.ResultExecutionStatusEnum.FAILED,
288
- [models_1.TestStatusEnum.skipped]: qaseio_1.ResultExecutionStatusEnum.SKIPPED,
289
- [models_1.TestStatusEnum.disabled]: qaseio_1.ResultExecutionStatusEnum.SKIPPED,
290
- [models_1.TestStatusEnum.blocked]: qaseio_1.ResultExecutionStatusEnum.BLOCKED,
291
- [models_1.TestStatusEnum.invalid]: qaseio_1.ResultExecutionStatusEnum.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, TestStepResultCreateStatusEnum>}
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;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Get mime type of the file
3
+ * @param {string} filePath
4
+ */
5
+ export declare function getMimeTypes(filePath: string): string;