cucumberjs-qase-reporter 2.0.0-beta.2 → 2.0.0-beta.3
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 +31 -0
- package/dist/models.d.ts +5 -0
- package/dist/models.js +1 -0
- package/dist/reporter.d.ts +3 -59
- package/dist/reporter.js +11 -215
- package/dist/storage.d.ts +101 -0
- package/dist/storage.js +294 -0
- package/package.json +1 -1
package/changelog.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# qase-cucumberjs@2.0.0-beta.3
|
|
2
|
+
|
|
3
|
+
## What's new
|
|
4
|
+
|
|
5
|
+
Added support new Qase tags.
|
|
6
|
+
|
|
7
|
+
```diff
|
|
8
|
+
- @Q-1
|
|
9
|
+
+ @QaseID=2
|
|
10
|
+
+ @QaseTitle=Scenario_with_Qase_title_tag
|
|
11
|
+
+ @QaseFields={"description":"Description","severity":"high"}
|
|
12
|
+
Scenario: simple test
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
QaseID - is a unique identifier of the test case in the Qase TMS.
|
|
16
|
+
QaseTitle - is a title of the test case in the Qase TMS.
|
|
17
|
+
QaseFields - is a JSON object with additional fields for the test case in the Qase TMS.
|
|
18
|
+
|
|
19
|
+
# qase-cucumberjs@2.0.0-beta.2
|
|
20
|
+
|
|
21
|
+
## What's new
|
|
22
|
+
|
|
23
|
+
Added support gherkin steps.
|
|
24
|
+
Before this version, the reporter was not able to parse the steps from the feature files.
|
|
25
|
+
Now, the reporter can parse the steps and send them to the Qase TMS.
|
|
26
|
+
|
|
27
|
+
# qase-cucumberjs@2.0.0-beta.1
|
|
28
|
+
|
|
29
|
+
## What's new
|
|
30
|
+
|
|
31
|
+
First major beta release for the version 2 series of the Qase Cypress reporter.
|
package/dist/models.d.ts
ADDED
package/dist/models.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/dist/reporter.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { Formatter, IFormatterOptions
|
|
2
|
-
import { ConfigLoader, ConfigType
|
|
3
|
-
type TestStepResultStatus = (typeof Status)[keyof typeof Status];
|
|
1
|
+
import { Formatter, IFormatterOptions } from '@cucumber/cucumber';
|
|
2
|
+
import { ConfigLoader, ConfigType } from 'qase-javascript-commons';
|
|
4
3
|
export type CucumberQaseOptionsType = IFormatterOptions & {
|
|
5
4
|
qase?: ConfigType;
|
|
6
5
|
};
|
|
@@ -9,64 +8,11 @@ export type CucumberQaseOptionsType = IFormatterOptions & {
|
|
|
9
8
|
* @extends Formatter
|
|
10
9
|
*/
|
|
11
10
|
export declare class CucumberQaseReporter extends Formatter {
|
|
12
|
-
/**
|
|
13
|
-
* @type {Record<TestStepResultStatus, TestStatusEnum | null>}
|
|
14
|
-
*/
|
|
15
|
-
static statusMap: Record<TestStepResultStatus, TestStatusEnum | null>;
|
|
16
|
-
/**
|
|
17
|
-
* @type {Record<TestStepResultStatus, StepStatusEnum>}
|
|
18
|
-
*/
|
|
19
|
-
static stepStatusMap: Record<TestStepResultStatus, StepStatusEnum>;
|
|
20
|
-
/**
|
|
21
|
-
* @type {RegExp}
|
|
22
|
-
*/
|
|
23
|
-
static qaseIdRegExp: RegExp;
|
|
24
|
-
/**
|
|
25
|
-
* @param {readonly PickleTag[]} tagsList
|
|
26
|
-
* @returns {number[]}
|
|
27
|
-
* @private
|
|
28
|
-
*/
|
|
29
|
-
private static getCaseIds;
|
|
30
|
-
/**
|
|
31
|
-
* @type {Record<string, PickleInfoType>}
|
|
32
|
-
* @private
|
|
33
|
-
*/
|
|
34
|
-
private pickleInfo;
|
|
35
|
-
/**
|
|
36
|
-
* @type {Record<string, TestCaseStarted>}
|
|
37
|
-
* @private
|
|
38
|
-
*/
|
|
39
|
-
private testCaseStarts;
|
|
40
|
-
/**
|
|
41
|
-
* @type {Record<string, >}
|
|
42
|
-
* @private
|
|
43
|
-
*/
|
|
44
|
-
/**
|
|
45
|
-
* @type {Record<string, TestStepFinished>}
|
|
46
|
-
* @private
|
|
47
|
-
*/
|
|
48
|
-
private testCaseStepsFinished;
|
|
49
|
-
private testCaseStartedResult;
|
|
50
|
-
/**
|
|
51
|
-
* @type {Record<string, string[]>}
|
|
52
|
-
* @private
|
|
53
|
-
*/
|
|
54
|
-
private testCaseStartedErrors;
|
|
55
|
-
/**
|
|
56
|
-
* @type {Record<string, string>}
|
|
57
|
-
* @private
|
|
58
|
-
*/
|
|
59
|
-
private testCaseScenarioId;
|
|
60
|
-
/**
|
|
61
|
-
* @type {Record<string, string>}
|
|
62
|
-
* @private
|
|
63
|
-
*/
|
|
64
|
-
private scenarios;
|
|
65
11
|
/**
|
|
66
12
|
* @type {Record<string, string>}
|
|
67
13
|
* @private
|
|
68
14
|
*/
|
|
69
|
-
private
|
|
15
|
+
private storage;
|
|
70
16
|
/**
|
|
71
17
|
* @type {ReporterInterface}
|
|
72
18
|
* @private
|
|
@@ -86,7 +32,6 @@ export declare class CucumberQaseReporter extends Formatter {
|
|
|
86
32
|
* @private
|
|
87
33
|
*/
|
|
88
34
|
private bindEventListeners;
|
|
89
|
-
private convertSteps;
|
|
90
35
|
/**
|
|
91
36
|
* @returns {Promise<void>}
|
|
92
37
|
* @private
|
|
@@ -98,4 +43,3 @@ export declare class CucumberQaseReporter extends Formatter {
|
|
|
98
43
|
*/
|
|
99
44
|
private startTestRun;
|
|
100
45
|
}
|
|
101
|
-
export {};
|
package/dist/reporter.js
CHANGED
|
@@ -3,25 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CucumberQaseReporter = void 0;
|
|
4
4
|
const cucumber_1 = require("@cucumber/cucumber");
|
|
5
5
|
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
6
|
+
const storage_1 = require("./storage");
|
|
6
7
|
/**
|
|
7
8
|
* @class CucumberQaseReporter
|
|
8
9
|
* @extends Formatter
|
|
9
10
|
*/
|
|
10
11
|
class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
11
|
-
/**
|
|
12
|
-
* @param {readonly PickleTag[]} tagsList
|
|
13
|
-
* @returns {number[]}
|
|
14
|
-
* @private
|
|
15
|
-
*/
|
|
16
|
-
static getCaseIds(tagsList) {
|
|
17
|
-
return tagsList.reduce((acc, tagInfo) => {
|
|
18
|
-
const ids = Array.from(tagInfo.name.matchAll(CucumberQaseReporter.qaseIdRegExp))
|
|
19
|
-
.map(([, id]) => Number(id))
|
|
20
|
-
.filter((id) => id !== undefined);
|
|
21
|
-
acc.push(...ids);
|
|
22
|
-
return acc;
|
|
23
|
-
}, []);
|
|
24
|
-
}
|
|
25
12
|
/**
|
|
26
13
|
* @param {CucumberQaseOptionsType} options
|
|
27
14
|
* @param {ConfigLoaderInterface} configLoader
|
|
@@ -30,46 +17,6 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
30
17
|
const { qase, ...formatterOptions } = options;
|
|
31
18
|
const config = configLoader.load();
|
|
32
19
|
super(formatterOptions);
|
|
33
|
-
/**
|
|
34
|
-
* @type {Record<string, PickleInfoType>}
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
this.pickleInfo = {};
|
|
38
|
-
/**
|
|
39
|
-
* @type {Record<string, TestCaseStarted>}
|
|
40
|
-
* @private
|
|
41
|
-
*/
|
|
42
|
-
this.testCaseStarts = {};
|
|
43
|
-
/**
|
|
44
|
-
* @type {Record<string, >}
|
|
45
|
-
* @private
|
|
46
|
-
*/
|
|
47
|
-
/**
|
|
48
|
-
* @type {Record<string, TestStepFinished>}
|
|
49
|
-
* @private
|
|
50
|
-
*/
|
|
51
|
-
this.testCaseStepsFinished = {};
|
|
52
|
-
this.testCaseStartedResult = {};
|
|
53
|
-
/**
|
|
54
|
-
* @type {Record<string, string[]>}
|
|
55
|
-
* @private
|
|
56
|
-
*/
|
|
57
|
-
this.testCaseStartedErrors = {};
|
|
58
|
-
/**
|
|
59
|
-
* @type {Record<string, string>}
|
|
60
|
-
* @private
|
|
61
|
-
*/
|
|
62
|
-
this.testCaseScenarioId = {};
|
|
63
|
-
/**
|
|
64
|
-
* @type {Record<string, string>}
|
|
65
|
-
* @private
|
|
66
|
-
*/
|
|
67
|
-
this.scenarios = {};
|
|
68
|
-
/**
|
|
69
|
-
* @type {Record<string, string>}
|
|
70
|
-
* @private
|
|
71
|
-
*/
|
|
72
|
-
this.attachments = {};
|
|
73
20
|
this.reporter = qase_javascript_commons_1.QaseReporter.getInstance({
|
|
74
21
|
...(0, qase_javascript_commons_1.composeOptions)(qase, config),
|
|
75
22
|
frameworkPackage: '@cucumber/cucumber',
|
|
@@ -77,6 +24,7 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
77
24
|
reporterName: 'cucumberjs-qase-reporter',
|
|
78
25
|
});
|
|
79
26
|
this.eventBroadcaster = formatterOptions.eventBroadcaster;
|
|
27
|
+
this.storage = new storage_1.Storage();
|
|
80
28
|
this.bindEventListeners();
|
|
81
29
|
}
|
|
82
30
|
/**
|
|
@@ -85,29 +33,13 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
85
33
|
bindEventListeners() {
|
|
86
34
|
this.eventBroadcaster.on('envelope', (envelope) => {
|
|
87
35
|
if (envelope.gherkinDocument) {
|
|
88
|
-
|
|
89
|
-
const { children, name } = envelope.gherkinDocument.feature;
|
|
90
|
-
children.forEach(({ scenario }) => {
|
|
91
|
-
if (scenario) {
|
|
92
|
-
this.scenarios[scenario.id] = name;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
36
|
+
this.storage.addScenario(envelope.gherkinDocument);
|
|
96
37
|
}
|
|
97
38
|
else if (envelope.pickle) {
|
|
98
|
-
this.
|
|
99
|
-
caseIds: CucumberQaseReporter.getCaseIds(envelope.pickle.tags),
|
|
100
|
-
name: envelope.pickle.name,
|
|
101
|
-
lastAstNodeId: envelope.pickle.astNodeIds[envelope.pickle.astNodeIds.length - 1],
|
|
102
|
-
steps: envelope.pickle.steps,
|
|
103
|
-
};
|
|
39
|
+
this.storage.addPickle(envelope.pickle);
|
|
104
40
|
}
|
|
105
41
|
else if (envelope.attachment) {
|
|
106
|
-
|
|
107
|
-
envelope.attachment.fileName) {
|
|
108
|
-
this.attachments[envelope.attachment.testCaseStartedId] =
|
|
109
|
-
envelope.attachment.fileName;
|
|
110
|
-
}
|
|
42
|
+
this.storage.addAttachment(envelope.attachment);
|
|
111
43
|
}
|
|
112
44
|
else if (envelope.testRunStarted) {
|
|
113
45
|
this.startTestRun();
|
|
@@ -116,131 +48,23 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
116
48
|
void this.publishResults();
|
|
117
49
|
}
|
|
118
50
|
else if (envelope.testCase) {
|
|
119
|
-
this.
|
|
120
|
-
envelope.testCase;
|
|
51
|
+
this.storage.addTestCase(envelope.testCase);
|
|
121
52
|
}
|
|
122
53
|
else if (envelope.testCaseStarted) {
|
|
123
|
-
this.
|
|
124
|
-
envelope.testCaseStarted;
|
|
125
|
-
this.testCaseStartedResult[envelope.testCaseStarted.id] =
|
|
126
|
-
qase_javascript_commons_1.TestStatusEnum.passed;
|
|
54
|
+
this.storage.addTestCaseStarted(envelope.testCaseStarted);
|
|
127
55
|
}
|
|
128
56
|
else if (envelope.testStepFinished) {
|
|
129
|
-
|
|
130
|
-
const oldStatus = this.testCaseStartedResult[stepFin.testCaseStartedId];
|
|
131
|
-
const newStatus = CucumberQaseReporter.statusMap[stepFin.testStepResult.status];
|
|
132
|
-
if (newStatus === null) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
this.testCaseStepsFinished[stepFin.testStepId] = stepFin;
|
|
136
|
-
if (newStatus !== qase_javascript_commons_1.TestStatusEnum.passed) {
|
|
137
|
-
if (stepFin.testStepResult.message) {
|
|
138
|
-
const errors = this.testCaseStartedErrors[stepFin.testCaseStartedId] ?? [];
|
|
139
|
-
if (!this.testCaseStartedErrors[stepFin.testCaseStartedId]) {
|
|
140
|
-
this.testCaseStartedErrors[stepFin.testCaseStartedId] = errors;
|
|
141
|
-
}
|
|
142
|
-
errors.push(stepFin.testStepResult.message);
|
|
143
|
-
}
|
|
144
|
-
if (oldStatus) {
|
|
145
|
-
if (oldStatus !== qase_javascript_commons_1.TestStatusEnum.failed) {
|
|
146
|
-
this.testCaseStartedResult[stepFin.testCaseStartedId] = newStatus;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
this.testCaseStartedResult[stepFin.testCaseStartedId] = newStatus;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
57
|
+
this.storage.addTestCaseStep(envelope.testStepFinished);
|
|
153
58
|
}
|
|
154
59
|
else if (envelope.testCaseFinished) {
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
const testCase = this.testCaseScenarioId[tcs.testCaseId];
|
|
160
|
-
if (!testCase) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const info = this.pickleInfo[testCase.pickleId];
|
|
164
|
-
if (!info) {
|
|
60
|
+
const result = this.storage.convertTestCase(envelope.testCaseFinished);
|
|
61
|
+
if (!result) {
|
|
165
62
|
return;
|
|
166
63
|
}
|
|
167
|
-
|
|
168
|
-
if (this.testCaseStartedErrors[tcs.id]?.length) {
|
|
169
|
-
error = new Error(this.testCaseStartedErrors[tcs.id]?.join('\n\n'));
|
|
170
|
-
}
|
|
171
|
-
let relations = null;
|
|
172
|
-
if (info.lastAstNodeId != undefined && this.scenarios[info.lastAstNodeId] != undefined) {
|
|
173
|
-
relations = {
|
|
174
|
-
suite: {
|
|
175
|
-
data: [
|
|
176
|
-
{
|
|
177
|
-
title: this.scenarios[info.lastAstNodeId] ?? '',
|
|
178
|
-
public_id: null,
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
void this.reporter.addTestResult({
|
|
185
|
-
attachments: [],
|
|
186
|
-
author: null,
|
|
187
|
-
execution: {
|
|
188
|
-
status: this.testCaseStartedResult[envelope.testCaseFinished.testCaseStartedId] ?? qase_javascript_commons_1.TestStatusEnum.passed,
|
|
189
|
-
start_time: null,
|
|
190
|
-
end_time: null,
|
|
191
|
-
duration: Math.abs(envelope.testCaseFinished.timestamp.seconds - tcs.timestamp.seconds),
|
|
192
|
-
stacktrace: error?.stack ?? null,
|
|
193
|
-
thread: null,
|
|
194
|
-
},
|
|
195
|
-
fields: {},
|
|
196
|
-
message: null,
|
|
197
|
-
muted: false,
|
|
198
|
-
params: {},
|
|
199
|
-
relations: relations,
|
|
200
|
-
run_id: null,
|
|
201
|
-
signature: '',
|
|
202
|
-
steps: this.convertSteps(info.steps, testCase),
|
|
203
|
-
testops_id: info.caseIds.length > 0 ? info.caseIds : null,
|
|
204
|
-
id: tcs.id,
|
|
205
|
-
title: info.name,
|
|
206
|
-
// suiteTitle: info.lastAstNodeId && this.scenarios[info.lastAstNodeId],
|
|
207
|
-
});
|
|
64
|
+
void this.reporter.addTestResult(result);
|
|
208
65
|
}
|
|
209
66
|
});
|
|
210
67
|
}
|
|
211
|
-
convertSteps(steps, testCase) {
|
|
212
|
-
const results = [];
|
|
213
|
-
for (const s of testCase.testSteps) {
|
|
214
|
-
const finished = this.testCaseStepsFinished[s.id];
|
|
215
|
-
if (!finished) {
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
const step = steps.find((step) => step.id === s.pickleStepId);
|
|
219
|
-
if (!step) {
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
const result = {
|
|
223
|
-
id: s.id,
|
|
224
|
-
step_type: qase_javascript_commons_1.StepType.GHERKIN,
|
|
225
|
-
data: {
|
|
226
|
-
keyword: step.text,
|
|
227
|
-
name: step.text,
|
|
228
|
-
line: 0,
|
|
229
|
-
},
|
|
230
|
-
execution: {
|
|
231
|
-
status: CucumberQaseReporter.stepStatusMap[finished.testStepResult.status],
|
|
232
|
-
start_time: null,
|
|
233
|
-
end_time: null,
|
|
234
|
-
duration: finished.testStepResult.duration.seconds,
|
|
235
|
-
},
|
|
236
|
-
attachments: [],
|
|
237
|
-
steps: [],
|
|
238
|
-
parent_id: null,
|
|
239
|
-
};
|
|
240
|
-
results.push(result);
|
|
241
|
-
}
|
|
242
|
-
return results;
|
|
243
|
-
}
|
|
244
68
|
/**
|
|
245
69
|
* @returns {Promise<void>}
|
|
246
70
|
* @private
|
|
@@ -257,31 +81,3 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
257
81
|
}
|
|
258
82
|
}
|
|
259
83
|
exports.CucumberQaseReporter = CucumberQaseReporter;
|
|
260
|
-
/**
|
|
261
|
-
* @type {Record<TestStepResultStatus, TestStatusEnum | null>}
|
|
262
|
-
*/
|
|
263
|
-
CucumberQaseReporter.statusMap = {
|
|
264
|
-
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.TestStatusEnum.passed,
|
|
265
|
-
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.TestStatusEnum.failed,
|
|
266
|
-
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
267
|
-
[cucumber_1.Status.AMBIGUOUS]: null,
|
|
268
|
-
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.TestStatusEnum.blocked,
|
|
269
|
-
[cucumber_1.Status.UNDEFINED]: null,
|
|
270
|
-
[cucumber_1.Status.UNKNOWN]: null,
|
|
271
|
-
};
|
|
272
|
-
/**
|
|
273
|
-
* @type {Record<TestStepResultStatus, StepStatusEnum>}
|
|
274
|
-
*/
|
|
275
|
-
CucumberQaseReporter.stepStatusMap = {
|
|
276
|
-
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.StepStatusEnum.passed,
|
|
277
|
-
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.StepStatusEnum.failed,
|
|
278
|
-
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.StepStatusEnum.blocked,
|
|
279
|
-
[cucumber_1.Status.AMBIGUOUS]: qase_javascript_commons_1.StepStatusEnum.blocked,
|
|
280
|
-
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.StepStatusEnum.blocked,
|
|
281
|
-
[cucumber_1.Status.UNDEFINED]: qase_javascript_commons_1.StepStatusEnum.blocked,
|
|
282
|
-
[cucumber_1.Status.UNKNOWN]: qase_javascript_commons_1.StepStatusEnum.blocked,
|
|
283
|
-
};
|
|
284
|
-
/**
|
|
285
|
-
* @type {RegExp}
|
|
286
|
-
*/
|
|
287
|
-
CucumberQaseReporter.qaseIdRegExp = /^@[Qq]-?(\d+)$/g;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Attachment as Attach, GherkinDocument, Pickle, TestCaseFinished, TestCaseStarted, TestStepFinished } from '@cucumber/messages';
|
|
2
|
+
import { StepStatusEnum, TestResultType, TestStatusEnum } from 'qase-javascript-commons';
|
|
3
|
+
import { TestCase } from '@cucumber/messages/dist/esm/src/messages';
|
|
4
|
+
import { Status } from '@cucumber/cucumber';
|
|
5
|
+
type TestStepResultStatus = (typeof Status)[keyof typeof Status];
|
|
6
|
+
export declare class Storage {
|
|
7
|
+
/**
|
|
8
|
+
* @type {Record<string, Pickle>}
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
private pickles;
|
|
12
|
+
/**
|
|
13
|
+
* @type {Record<string, TestCaseStarted>}
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
private testCaseStarts;
|
|
17
|
+
/**
|
|
18
|
+
* @type {Record<string, TestStepFinished>}
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
private testCaseSteps;
|
|
22
|
+
/**
|
|
23
|
+
* @type {Record<string, TestStatusEnum>}
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
private testCaseStartedResult;
|
|
27
|
+
/**
|
|
28
|
+
* @type {Record<string, string[]>}
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
private testCaseStartedErrors;
|
|
32
|
+
/**
|
|
33
|
+
* @type {Record<string, string>}
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
private testCases;
|
|
37
|
+
/**
|
|
38
|
+
* @type {Record<string, string>}
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
private scenarios;
|
|
42
|
+
/**
|
|
43
|
+
* @type {Record<string, string>}
|
|
44
|
+
* @private
|
|
45
|
+
*/
|
|
46
|
+
private attachments;
|
|
47
|
+
/**
|
|
48
|
+
* Add pickle to storage
|
|
49
|
+
* @param {Pickle} pickle
|
|
50
|
+
*/
|
|
51
|
+
addPickle(pickle: Pickle): void;
|
|
52
|
+
/**
|
|
53
|
+
* Add scenario to storage
|
|
54
|
+
* @param {GherkinDocument} document
|
|
55
|
+
*/
|
|
56
|
+
addScenario(document: GherkinDocument): void;
|
|
57
|
+
/**
|
|
58
|
+
* Add attachment to storage
|
|
59
|
+
* @param {Attach} attachment
|
|
60
|
+
*/
|
|
61
|
+
addAttachment(attachment: Attach): void;
|
|
62
|
+
/**
|
|
63
|
+
* Add test case to storage
|
|
64
|
+
* @param {TestCase} testCase
|
|
65
|
+
*/
|
|
66
|
+
addTestCase(testCase: TestCase): void;
|
|
67
|
+
/**
|
|
68
|
+
* Get all test cases
|
|
69
|
+
* @param {TestCaseStarted} testCaseStarted
|
|
70
|
+
*/
|
|
71
|
+
addTestCaseStarted(testCaseStarted: TestCaseStarted): void;
|
|
72
|
+
/**
|
|
73
|
+
* Add test case step to storage
|
|
74
|
+
* @param {TestStepFinished} testCaseStep
|
|
75
|
+
*/
|
|
76
|
+
addTestCaseStep(testCaseStep: TestStepFinished): void;
|
|
77
|
+
/**
|
|
78
|
+
* Convert test case to test result
|
|
79
|
+
* @param {TestCaseFinished} testCase
|
|
80
|
+
* @returns {undefined | TestResultType}
|
|
81
|
+
*/
|
|
82
|
+
convertTestCase(testCase: TestCaseFinished): undefined | TestResultType;
|
|
83
|
+
/**
|
|
84
|
+
* Convert test steps to test result steps
|
|
85
|
+
* @param {readonly PickleStep[]} steps
|
|
86
|
+
* @param {TestCase} testCase
|
|
87
|
+
* @returns {TestStepType[]}
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
private convertSteps;
|
|
91
|
+
/**
|
|
92
|
+
* @type {Record<TestStepResultStatus, TestStatusEnum>}
|
|
93
|
+
*/
|
|
94
|
+
static statusMap: Record<TestStepResultStatus, TestStatusEnum>;
|
|
95
|
+
/**
|
|
96
|
+
* @type {Record<TestStepResultStatus, StepStatusEnum>}
|
|
97
|
+
*/
|
|
98
|
+
static stepStatusMap: Record<TestStepResultStatus, StepStatusEnum>;
|
|
99
|
+
private parseTags;
|
|
100
|
+
}
|
|
101
|
+
export {};
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Storage = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
5
|
+
const cucumber_1 = require("@cucumber/cucumber");
|
|
6
|
+
const qaseIdRegExp = /^@[Qq]-?(\d+)$/g;
|
|
7
|
+
const newQaseIdRegExp = /^@[Qq]ase[Ii][Dd]=(\d+)$/g;
|
|
8
|
+
const qaseTitleRegExp = /^@[Qq]ase[Tt]itle=(.+)$/g;
|
|
9
|
+
const qaseFieldsRegExp = /^@[Qq]ase[Ff]ields:(.+?)=(.+)$/g;
|
|
10
|
+
class Storage {
|
|
11
|
+
constructor() {
|
|
12
|
+
/**
|
|
13
|
+
* @type {Record<string, Pickle>}
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
this.pickles = {};
|
|
17
|
+
/**
|
|
18
|
+
* @type {Record<string, TestCaseStarted>}
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
this.testCaseStarts = {};
|
|
22
|
+
/**
|
|
23
|
+
* @type {Record<string, TestStepFinished>}
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
this.testCaseSteps = {};
|
|
27
|
+
/**
|
|
28
|
+
* @type {Record<string, TestStatusEnum>}
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
this.testCaseStartedResult = {};
|
|
32
|
+
/**
|
|
33
|
+
* @type {Record<string, string[]>}
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
this.testCaseStartedErrors = {};
|
|
37
|
+
/**
|
|
38
|
+
* @type {Record<string, string>}
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
this.testCases = {};
|
|
42
|
+
/**
|
|
43
|
+
* @type {Record<string, string>}
|
|
44
|
+
* @private
|
|
45
|
+
*/
|
|
46
|
+
this.scenarios = {};
|
|
47
|
+
/**
|
|
48
|
+
* @type {Record<string, string>}
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
this.attachments = {};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Add pickle to storage
|
|
55
|
+
* @param {Pickle} pickle
|
|
56
|
+
*/
|
|
57
|
+
addPickle(pickle) {
|
|
58
|
+
this.pickles[pickle.id] = pickle;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Add scenario to storage
|
|
62
|
+
* @param {GherkinDocument} document
|
|
63
|
+
*/
|
|
64
|
+
addScenario(document) {
|
|
65
|
+
if (document.feature) {
|
|
66
|
+
const { children, name } = document.feature;
|
|
67
|
+
children.forEach(({ scenario }) => {
|
|
68
|
+
if (scenario) {
|
|
69
|
+
this.scenarios[scenario.id] = name;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Add attachment to storage
|
|
76
|
+
* @param {Attach} attachment
|
|
77
|
+
*/
|
|
78
|
+
addAttachment(attachment) {
|
|
79
|
+
if (attachment.testCaseStartedId && attachment.fileName) {
|
|
80
|
+
if (!this.attachments[attachment.testCaseStartedId]) {
|
|
81
|
+
this.attachments[attachment.testCaseStartedId] = [];
|
|
82
|
+
}
|
|
83
|
+
this.attachments[attachment.testCaseStartedId]?.push({
|
|
84
|
+
file_name: attachment.fileName,
|
|
85
|
+
mime_type: attachment.mediaType,
|
|
86
|
+
file_path: null,
|
|
87
|
+
content: attachment.body,
|
|
88
|
+
size: 0,
|
|
89
|
+
id: attachment.fileName,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Add test case to storage
|
|
95
|
+
* @param {TestCase} testCase
|
|
96
|
+
*/
|
|
97
|
+
addTestCase(testCase) {
|
|
98
|
+
this.testCases[testCase.id] = testCase;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all test cases
|
|
102
|
+
* @param {TestCaseStarted} testCaseStarted
|
|
103
|
+
*/
|
|
104
|
+
addTestCaseStarted(testCaseStarted) {
|
|
105
|
+
this.testCaseStarts[testCaseStarted.id] =
|
|
106
|
+
testCaseStarted;
|
|
107
|
+
this.testCaseStartedResult[testCaseStarted.id] =
|
|
108
|
+
qase_javascript_commons_1.TestStatusEnum.passed;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Add test case step to storage
|
|
112
|
+
* @param {TestStepFinished} testCaseStep
|
|
113
|
+
*/
|
|
114
|
+
addTestCaseStep(testCaseStep) {
|
|
115
|
+
const oldStatus = this.testCaseStartedResult[testCaseStep.testCaseStartedId];
|
|
116
|
+
const newStatus = Storage.statusMap[testCaseStep.testStepResult.status];
|
|
117
|
+
this.testCaseSteps[testCaseStep.testStepId] = testCaseStep;
|
|
118
|
+
if (newStatus !== qase_javascript_commons_1.TestStatusEnum.passed) {
|
|
119
|
+
if (testCaseStep.testStepResult.message) {
|
|
120
|
+
if (!this.testCaseStartedErrors[testCaseStep.testCaseStartedId]) {
|
|
121
|
+
this.testCaseStartedErrors[testCaseStep.testCaseStartedId] = [];
|
|
122
|
+
}
|
|
123
|
+
this.testCaseStartedErrors[testCaseStep.testCaseStartedId]?.push(testCaseStep.testStepResult.message);
|
|
124
|
+
}
|
|
125
|
+
if (oldStatus) {
|
|
126
|
+
if (oldStatus !== qase_javascript_commons_1.TestStatusEnum.failed) {
|
|
127
|
+
this.testCaseStartedResult[testCaseStep.testCaseStartedId] = newStatus;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
this.testCaseStartedResult[testCaseStep.testCaseStartedId] = newStatus;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Convert test case to test result
|
|
137
|
+
* @param {TestCaseFinished} testCase
|
|
138
|
+
* @returns {undefined | TestResultType}
|
|
139
|
+
*/
|
|
140
|
+
convertTestCase(testCase) {
|
|
141
|
+
const tcs = this.testCaseStarts[testCase.testCaseStartedId];
|
|
142
|
+
if (!tcs) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
const tc = this.testCases[tcs.testCaseId];
|
|
146
|
+
if (!tc) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const pickle = this.pickles[tc.pickleId];
|
|
150
|
+
if (!pickle) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
let error;
|
|
154
|
+
if (this.testCaseStartedErrors[tcs.id]?.length) {
|
|
155
|
+
error = new Error(this.testCaseStartedErrors[tcs.id]?.join('\n\n'));
|
|
156
|
+
}
|
|
157
|
+
let relations = null;
|
|
158
|
+
const nodeId = pickle.astNodeIds[pickle.astNodeIds.length - 1];
|
|
159
|
+
if (nodeId != undefined && this.scenarios[nodeId] != undefined) {
|
|
160
|
+
relations = {
|
|
161
|
+
suite: {
|
|
162
|
+
data: [
|
|
163
|
+
{
|
|
164
|
+
title: this.scenarios[nodeId] ?? '',
|
|
165
|
+
public_id: null,
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const metadata = this.parseTags(pickle.tags);
|
|
172
|
+
return {
|
|
173
|
+
attachments: [],
|
|
174
|
+
author: null,
|
|
175
|
+
execution: {
|
|
176
|
+
status: this.testCaseStartedResult[testCase.testCaseStartedId] ?? qase_javascript_commons_1.TestStatusEnum.passed,
|
|
177
|
+
start_time: null,
|
|
178
|
+
end_time: null,
|
|
179
|
+
duration: Math.abs(testCase.timestamp.seconds - tcs.timestamp.seconds),
|
|
180
|
+
stacktrace: error?.stack ?? null,
|
|
181
|
+
thread: null,
|
|
182
|
+
},
|
|
183
|
+
fields: metadata.fields,
|
|
184
|
+
message: null,
|
|
185
|
+
muted: false,
|
|
186
|
+
params: {},
|
|
187
|
+
relations: relations,
|
|
188
|
+
run_id: null,
|
|
189
|
+
signature: '',
|
|
190
|
+
steps: this.convertSteps(pickle.steps, tc),
|
|
191
|
+
testops_id: metadata.ids.length > 0 ? metadata.ids : null,
|
|
192
|
+
id: tcs.id,
|
|
193
|
+
title: metadata.title ?? pickle.name,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Convert test steps to test result steps
|
|
198
|
+
* @param {readonly PickleStep[]} steps
|
|
199
|
+
* @param {TestCase} testCase
|
|
200
|
+
* @returns {TestStepType[]}
|
|
201
|
+
* @private
|
|
202
|
+
*/
|
|
203
|
+
convertSteps(steps, testCase) {
|
|
204
|
+
const results = [];
|
|
205
|
+
for (const s of testCase.testSteps) {
|
|
206
|
+
const finished = this.testCaseSteps[s.id];
|
|
207
|
+
if (!finished) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const step = steps.find((step) => step.id === s.pickleStepId);
|
|
211
|
+
if (!step) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const result = {
|
|
215
|
+
id: s.id,
|
|
216
|
+
step_type: qase_javascript_commons_1.StepType.GHERKIN,
|
|
217
|
+
data: {
|
|
218
|
+
keyword: step.text,
|
|
219
|
+
name: step.text,
|
|
220
|
+
line: 0,
|
|
221
|
+
},
|
|
222
|
+
execution: {
|
|
223
|
+
status: Storage.stepStatusMap[finished.testStepResult.status],
|
|
224
|
+
start_time: null,
|
|
225
|
+
end_time: null,
|
|
226
|
+
duration: finished.testStepResult.duration.seconds,
|
|
227
|
+
},
|
|
228
|
+
attachments: [],
|
|
229
|
+
steps: [],
|
|
230
|
+
parent_id: null,
|
|
231
|
+
};
|
|
232
|
+
results.push(result);
|
|
233
|
+
}
|
|
234
|
+
return results;
|
|
235
|
+
}
|
|
236
|
+
parseTags(tags) {
|
|
237
|
+
const metadata = {
|
|
238
|
+
ids: [],
|
|
239
|
+
fields: {},
|
|
240
|
+
title: null,
|
|
241
|
+
};
|
|
242
|
+
for (const tag of tags) {
|
|
243
|
+
if (qaseIdRegExp.test(tag.name)) {
|
|
244
|
+
metadata.ids.push(Number(tag.name.replace(/^@[Qq]-?/, '')));
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (newQaseIdRegExp.test(tag.name)) {
|
|
248
|
+
metadata.ids.push(Number(tag.name.replace(/^@[Qq]ase[Ii][Dd]=/, '')));
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (qaseTitleRegExp.test(tag.name)) {
|
|
252
|
+
metadata.title = tag.name.replace(/^@[Qq]ase[Tt]itle=/, '');
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (qaseFieldsRegExp.test(tag.name)) {
|
|
256
|
+
const value = tag.name.replace(/^@[Qq]ase[Ff]ields:/, '');
|
|
257
|
+
try {
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
259
|
+
const record = JSON.parse(value);
|
|
260
|
+
metadata.fields = { ...metadata.fields, ...record };
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
// do nothing
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return metadata;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
exports.Storage = Storage;
|
|
271
|
+
/**
|
|
272
|
+
* @type {Record<TestStepResultStatus, TestStatusEnum>}
|
|
273
|
+
*/
|
|
274
|
+
Storage.statusMap = {
|
|
275
|
+
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.TestStatusEnum.passed,
|
|
276
|
+
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.TestStatusEnum.failed,
|
|
277
|
+
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
278
|
+
[cucumber_1.Status.AMBIGUOUS]: qase_javascript_commons_1.TestStatusEnum.failed,
|
|
279
|
+
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
280
|
+
[cucumber_1.Status.UNDEFINED]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
281
|
+
[cucumber_1.Status.UNKNOWN]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* @type {Record<TestStepResultStatus, StepStatusEnum>}
|
|
285
|
+
*/
|
|
286
|
+
Storage.stepStatusMap = {
|
|
287
|
+
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.StepStatusEnum.passed,
|
|
288
|
+
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.StepStatusEnum.failed,
|
|
289
|
+
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
290
|
+
[cucumber_1.Status.AMBIGUOUS]: qase_javascript_commons_1.StepStatusEnum.failed,
|
|
291
|
+
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
292
|
+
[cucumber_1.Status.UNDEFINED]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
293
|
+
[cucumber_1.Status.UNKNOWN]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
294
|
+
};
|