cucumberjs-qase-reporter 2.0.0-beta.1 → 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 +8 -49
- package/dist/reporter.js +22 -138
- package/dist/storage.d.ts +101 -0
- package/dist/storage.js +294 -0
- package/package.json +2 -2
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 {
|
|
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,55 +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 {RegExp}
|
|
18
|
-
*/
|
|
19
|
-
static qaseIdRegExp: RegExp;
|
|
20
|
-
/**
|
|
21
|
-
* @param {readonly PickleTag[]} tagsList
|
|
22
|
-
* @returns {number[]}
|
|
23
|
-
* @private
|
|
24
|
-
*/
|
|
25
|
-
private static getCaseIds;
|
|
26
|
-
/**
|
|
27
|
-
* @type {Record<string, PickleInfoType>}
|
|
28
|
-
* @private
|
|
29
|
-
*/
|
|
30
|
-
private pickleInfo;
|
|
31
|
-
/**
|
|
32
|
-
* @type {Record<string, TestCaseStarted>}
|
|
33
|
-
* @private
|
|
34
|
-
*/
|
|
35
|
-
private testCaseStarts;
|
|
36
|
-
/**
|
|
37
|
-
* @type {Record<string, >}
|
|
38
|
-
* @private
|
|
39
|
-
*/
|
|
40
|
-
private testCaseStartedResult;
|
|
41
|
-
/**
|
|
42
|
-
* @type {Record<string, string[]>}
|
|
43
|
-
* @private
|
|
44
|
-
*/
|
|
45
|
-
private testCaseStartedErrors;
|
|
46
|
-
/**
|
|
47
|
-
* @type {Record<string, string>}
|
|
48
|
-
* @private
|
|
49
|
-
*/
|
|
50
|
-
private testCaseScenarioId;
|
|
51
11
|
/**
|
|
52
12
|
* @type {Record<string, string>}
|
|
53
13
|
* @private
|
|
54
14
|
*/
|
|
55
|
-
private
|
|
56
|
-
/**
|
|
57
|
-
* @type {Record<string, string>}
|
|
58
|
-
* @private
|
|
59
|
-
*/
|
|
60
|
-
private attachments;
|
|
15
|
+
private storage;
|
|
61
16
|
/**
|
|
62
17
|
* @type {ReporterInterface}
|
|
63
18
|
* @private
|
|
@@ -82,5 +37,9 @@ export declare class CucumberQaseReporter extends Formatter {
|
|
|
82
37
|
* @private
|
|
83
38
|
*/
|
|
84
39
|
private publishResults;
|
|
40
|
+
/**
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
private startTestRun;
|
|
85
45
|
}
|
|
86
|
-
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,48 +17,14 @@ 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
|
-
this.testCaseStartedResult = {};
|
|
48
|
-
/**
|
|
49
|
-
* @type {Record<string, string[]>}
|
|
50
|
-
* @private
|
|
51
|
-
*/
|
|
52
|
-
this.testCaseStartedErrors = {};
|
|
53
|
-
/**
|
|
54
|
-
* @type {Record<string, string>}
|
|
55
|
-
* @private
|
|
56
|
-
*/
|
|
57
|
-
this.testCaseScenarioId = {};
|
|
58
|
-
/**
|
|
59
|
-
* @type {Record<string, string>}
|
|
60
|
-
* @private
|
|
61
|
-
*/
|
|
62
|
-
this.scenarios = {};
|
|
63
|
-
/**
|
|
64
|
-
* @type {Record<string, string>}
|
|
65
|
-
* @private
|
|
66
|
-
*/
|
|
67
|
-
this.attachments = {};
|
|
68
|
-
this.reporter = new qase_javascript_commons_1.QaseReporter({
|
|
20
|
+
this.reporter = qase_javascript_commons_1.QaseReporter.getInstance({
|
|
69
21
|
...(0, qase_javascript_commons_1.composeOptions)(qase, config),
|
|
70
22
|
frameworkPackage: '@cucumber/cucumber',
|
|
71
23
|
frameworkName: 'cucumberjs',
|
|
72
24
|
reporterName: 'cucumberjs-qase-reporter',
|
|
73
25
|
});
|
|
74
26
|
this.eventBroadcaster = formatterOptions.eventBroadcaster;
|
|
27
|
+
this.storage = new storage_1.Storage();
|
|
75
28
|
this.bindEventListeners();
|
|
76
29
|
}
|
|
77
30
|
/**
|
|
@@ -80,95 +33,35 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
80
33
|
bindEventListeners() {
|
|
81
34
|
this.eventBroadcaster.on('envelope', (envelope) => {
|
|
82
35
|
if (envelope.gherkinDocument) {
|
|
83
|
-
|
|
84
|
-
const { children, name } = envelope.gherkinDocument.feature;
|
|
85
|
-
children.forEach(({ scenario }) => {
|
|
86
|
-
if (scenario) {
|
|
87
|
-
this.scenarios[scenario.id] = name;
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
36
|
+
this.storage.addScenario(envelope.gherkinDocument);
|
|
91
37
|
}
|
|
92
38
|
else if (envelope.pickle) {
|
|
93
|
-
this.
|
|
94
|
-
caseIds: CucumberQaseReporter.getCaseIds(envelope.pickle.tags),
|
|
95
|
-
name: envelope.pickle.name,
|
|
96
|
-
lastAstNodeId: envelope.pickle.astNodeIds[envelope.pickle.astNodeIds.length - 1],
|
|
97
|
-
};
|
|
39
|
+
this.storage.addPickle(envelope.pickle);
|
|
98
40
|
}
|
|
99
41
|
else if (envelope.attachment) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
42
|
+
this.storage.addAttachment(envelope.attachment);
|
|
43
|
+
}
|
|
44
|
+
else if (envelope.testRunStarted) {
|
|
45
|
+
this.startTestRun();
|
|
105
46
|
}
|
|
106
47
|
else if (envelope.testRunFinished) {
|
|
107
48
|
void this.publishResults();
|
|
108
49
|
}
|
|
109
50
|
else if (envelope.testCase) {
|
|
110
|
-
this.
|
|
111
|
-
envelope.testCase.pickleId;
|
|
51
|
+
this.storage.addTestCase(envelope.testCase);
|
|
112
52
|
}
|
|
113
53
|
else if (envelope.testCaseStarted) {
|
|
114
|
-
this.
|
|
115
|
-
envelope.testCaseStarted;
|
|
116
|
-
this.testCaseStartedResult[envelope.testCaseStarted.id] =
|
|
117
|
-
qase_javascript_commons_1.TestStatusEnum.passed;
|
|
54
|
+
this.storage.addTestCaseStarted(envelope.testCaseStarted);
|
|
118
55
|
}
|
|
119
56
|
else if (envelope.testStepFinished) {
|
|
120
|
-
|
|
121
|
-
const oldStatus = this.testCaseStartedResult[stepFin.testCaseStartedId];
|
|
122
|
-
const newStatus = CucumberQaseReporter.statusMap[stepFin.testStepResult.status];
|
|
123
|
-
if (newStatus === null) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
if (newStatus !== qase_javascript_commons_1.TestStatusEnum.passed) {
|
|
127
|
-
if (stepFin.testStepResult.message) {
|
|
128
|
-
const errors = this.testCaseStartedErrors[stepFin.testCaseStartedId] ?? [];
|
|
129
|
-
if (!this.testCaseStartedErrors[stepFin.testCaseStartedId]) {
|
|
130
|
-
this.testCaseStartedErrors[stepFin.testCaseStartedId] = errors;
|
|
131
|
-
}
|
|
132
|
-
errors.push(stepFin.testStepResult.message);
|
|
133
|
-
}
|
|
134
|
-
if (oldStatus) {
|
|
135
|
-
if (oldStatus !== qase_javascript_commons_1.TestStatusEnum.failed) {
|
|
136
|
-
this.testCaseStartedResult[stepFin.testCaseStartedId] = newStatus;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
this.testCaseStartedResult[stepFin.testCaseStartedId] = newStatus;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
57
|
+
this.storage.addTestCaseStep(envelope.testStepFinished);
|
|
143
58
|
}
|
|
144
59
|
else if (envelope.testCaseFinished) {
|
|
145
|
-
const
|
|
146
|
-
if (!
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
const pickleId = this.testCaseScenarioId[tcs.testCaseId];
|
|
150
|
-
if (!pickleId) {
|
|
60
|
+
const result = this.storage.convertTestCase(envelope.testCaseFinished);
|
|
61
|
+
if (!result) {
|
|
151
62
|
return;
|
|
152
63
|
}
|
|
153
|
-
|
|
154
|
-
if (!info) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
let error;
|
|
158
|
-
if (this.testCaseStartedErrors[tcs.id]?.length) {
|
|
159
|
-
error = new Error(this.testCaseStartedErrors[tcs.id]?.join('\n\n'));
|
|
160
|
-
}
|
|
161
|
-
this.reporter.addTestResult({
|
|
162
|
-
id: tcs.id,
|
|
163
|
-
testOpsId: info.caseIds,
|
|
164
|
-
title: info.name,
|
|
165
|
-
suiteTitle: info.lastAstNodeId && this.scenarios[info.lastAstNodeId],
|
|
166
|
-
status: this.testCaseStartedResult[envelope.testCaseFinished.testCaseStartedId] ?? qase_javascript_commons_1.TestStatusEnum.passed,
|
|
167
|
-
startTime: tcs.timestamp.seconds,
|
|
168
|
-
endTime: envelope.testCaseFinished.timestamp.seconds,
|
|
169
|
-
duration: Math.abs(envelope.testCaseFinished.timestamp.seconds - tcs.timestamp.seconds),
|
|
170
|
-
error,
|
|
171
|
-
});
|
|
64
|
+
void this.reporter.addTestResult(result);
|
|
172
65
|
}
|
|
173
66
|
});
|
|
174
67
|
}
|
|
@@ -179,21 +72,12 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
179
72
|
async publishResults() {
|
|
180
73
|
await this.reporter.publish();
|
|
181
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
startTestRun() {
|
|
80
|
+
this.reporter.startTestRun();
|
|
81
|
+
}
|
|
182
82
|
}
|
|
183
83
|
exports.CucumberQaseReporter = CucumberQaseReporter;
|
|
184
|
-
/**
|
|
185
|
-
* @type {Record<TestStepResultStatus, TestStatusEnum | null>}
|
|
186
|
-
*/
|
|
187
|
-
CucumberQaseReporter.statusMap = {
|
|
188
|
-
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.TestStatusEnum.passed,
|
|
189
|
-
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.TestStatusEnum.failed,
|
|
190
|
-
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
191
|
-
[cucumber_1.Status.AMBIGUOUS]: null,
|
|
192
|
-
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.TestStatusEnum.blocked,
|
|
193
|
-
[cucumber_1.Status.UNDEFINED]: null,
|
|
194
|
-
[cucumber_1.Status.UNKNOWN]: null,
|
|
195
|
-
};
|
|
196
|
-
/**
|
|
197
|
-
* @type {RegExp}
|
|
198
|
-
*/
|
|
199
|
-
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
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cucumberjs-qase-reporter",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.3",
|
|
4
4
|
"description": "Qase TMS CucumberJS Reporter",
|
|
5
5
|
"homepage": "https://github.com/qase-tms/qase-javascript",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"license": "Apache-2.0",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@cucumber/messages": "^22.0.0",
|
|
43
|
-
"qase-javascript-commons": "^2.0.0-beta.
|
|
43
|
+
"qase-javascript-commons": "^2.0.0-beta.8"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@cucumber/cucumber": ">=7.0.0"
|