cucumberjs-qase-reporter 2.3.0 → 2.4.1
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 +21 -0
- package/dist/modules/eventStorage.d.ts +30 -0
- package/dist/modules/eventStorage.js +121 -0
- package/dist/modules/profilerTracker.d.ts +11 -0
- package/dist/modules/profilerTracker.js +28 -0
- package/dist/modules/resultBuilder.d.ts +22 -0
- package/dist/modules/resultBuilder.js +63 -0
- package/dist/modules/statusMaps.d.ts +5 -0
- package/dist/modules/statusMaps.js +23 -0
- package/dist/modules/statusTracker.d.ts +10 -0
- package/dist/modules/statusTracker.js +51 -0
- package/dist/modules/stepConverter.d.ts +11 -0
- package/dist/modules/stepConverter.js +49 -0
- package/dist/modules/tagParser.d.ts +6 -0
- package/dist/modules/tagParser.js +99 -0
- package/dist/reporter.d.ts +0 -5
- package/dist/reporter.js +8 -9
- package/dist/storage.d.ts +9 -117
- package/dist/storage.js +60 -464
- package/package.json +1 -1
package/changelog.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# cucumberjs-qase-reporter@2.4.1
|
|
2
|
+
|
|
3
|
+
## Fixed
|
|
4
|
+
|
|
5
|
+
- Gherkin step `execution.start_time` and `execution.end_time` are now populated from the Cucumber `testStepStarted` / `testStepFinished` envelope timestamps (Unix seconds with fractional ms). Previously both fields were hardcoded to `null`, breaking timeline rendering on the Qase side.
|
|
6
|
+
- Step `execution.duration` now includes the `nanos` part of the Cucumber duration. Previously only `seconds * 1000` was emitted, so any sub-second step was reported as 0 ms.
|
|
7
|
+
|
|
8
|
+
# cucumberjs-qase-reporter@2.4.0
|
|
9
|
+
|
|
10
|
+
## Changed
|
|
11
|
+
|
|
12
|
+
- Decomposed `storage.ts` (595 LOC) into a thin facade plus 6 focused modules under `src/modules/`: `TagParser`, `EventStorage`, `StatusTracker`, `StepConverter`, `ProfilerTracker`, `ResultBuilder`, plus `STATUS_MAP` / `STEP_STATUS_MAP` constants. Public contract preserved: `Storage` named export, constructor signature, all 7 instance methods, static `statusMap` and `stepStatusMap`, all 9 user-facing tag patterns.
|
|
13
|
+
|
|
14
|
+
## Bug fixes
|
|
15
|
+
|
|
16
|
+
- Cross-reporter consistency: `testops_id` is now `null` when project mapping (`@qaseid.PROJ(ids)` tag) is set, matching the behavior of mocha / cypress / jest reporters. Previously cucumberjs emitted both `testops_id: ids` and `testops_project_mapping: mapping`, which could lead to ambiguous results in multi-project runs.
|
|
17
|
+
|
|
18
|
+
## Internal
|
|
19
|
+
|
|
20
|
+
- Added 38 per-module unit tests on top of the existing 44 facade integration tests (82 total).
|
|
21
|
+
|
|
1
22
|
# cucumberjs-qase-reporter@2.3.0
|
|
2
23
|
|
|
3
24
|
## What's new
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Attachment as Attach, GherkinDocument, Pickle, TestCaseStarted, TestStepFinished, TestStepStarted } from '@cucumber/messages';
|
|
2
|
+
import { TestCase } from '@cucumber/messages/dist/esm/src/messages';
|
|
3
|
+
import { Attachment } from 'qase-javascript-commons';
|
|
4
|
+
import { ScenarioData } from '../models';
|
|
5
|
+
export declare class EventStorage {
|
|
6
|
+
private pickles;
|
|
7
|
+
private testCaseStarts;
|
|
8
|
+
private testStepStarted;
|
|
9
|
+
private testStepFinished;
|
|
10
|
+
private testCases;
|
|
11
|
+
private scenarios;
|
|
12
|
+
private attachments;
|
|
13
|
+
addPickle(pickle: Pickle): void;
|
|
14
|
+
addScenario(document: GherkinDocument): void;
|
|
15
|
+
addAttachment(attachment: Attach): void;
|
|
16
|
+
addTestCase(testCase: TestCase): void;
|
|
17
|
+
addTestCaseStarted(testCaseStarted: TestCaseStarted): void;
|
|
18
|
+
addTestStepStarted(step: TestStepStarted): void;
|
|
19
|
+
addTestStepFinished(step: TestStepFinished): void;
|
|
20
|
+
getPickle(id: string): Pickle | undefined;
|
|
21
|
+
getTestCase(id: string): TestCase | undefined;
|
|
22
|
+
getTestCaseStarted(id: string): TestCaseStarted | undefined;
|
|
23
|
+
getScenario(id: string): ScenarioData | undefined;
|
|
24
|
+
getAttachments(key: string): Attachment[];
|
|
25
|
+
getTestStepFinished(id: string): TestStepFinished | undefined;
|
|
26
|
+
getAllTestStepFinished(): Map<string, TestStepFinished>;
|
|
27
|
+
getAllTestStepStarted(): Map<string, TestStepStarted>;
|
|
28
|
+
private appendAttachment;
|
|
29
|
+
static getFileNameFromMediaType(mediaType: string): string;
|
|
30
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventStorage = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
class EventStorage {
|
|
6
|
+
pickles = {};
|
|
7
|
+
testCaseStarts = {};
|
|
8
|
+
testStepStarted = {};
|
|
9
|
+
testStepFinished = {};
|
|
10
|
+
testCases = {};
|
|
11
|
+
scenarios = {};
|
|
12
|
+
attachments = {};
|
|
13
|
+
addPickle(pickle) {
|
|
14
|
+
this.pickles[pickle.id] = pickle;
|
|
15
|
+
}
|
|
16
|
+
addScenario(document) {
|
|
17
|
+
if (!document.feature)
|
|
18
|
+
return;
|
|
19
|
+
const { children, name } = document.feature;
|
|
20
|
+
for (const { scenario } of children) {
|
|
21
|
+
if (!scenario)
|
|
22
|
+
continue;
|
|
23
|
+
const parameters = {};
|
|
24
|
+
scenario.examples?.forEach((example) => {
|
|
25
|
+
if (example.tableHeader) {
|
|
26
|
+
const columnNames = example.tableHeader.cells.map((cell) => cell.value);
|
|
27
|
+
example.tableBody.forEach((row) => {
|
|
28
|
+
const rowParams = {};
|
|
29
|
+
row.cells.forEach((cell, index) => {
|
|
30
|
+
const columnName = columnNames[index];
|
|
31
|
+
if (columnName) {
|
|
32
|
+
rowParams[columnName] = cell.value;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
parameters[row.id] = rowParams;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
this.scenarios[scenario.id] = {
|
|
40
|
+
name,
|
|
41
|
+
parameters,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
addAttachment(attachment) {
|
|
46
|
+
if (attachment.testStepId) {
|
|
47
|
+
this.appendAttachment(attachment.testStepId, attachment);
|
|
48
|
+
}
|
|
49
|
+
if (attachment.testCaseStartedId) {
|
|
50
|
+
this.appendAttachment(attachment.testCaseStartedId, attachment);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
addTestCase(testCase) {
|
|
54
|
+
this.testCases[testCase.id] = testCase;
|
|
55
|
+
}
|
|
56
|
+
addTestCaseStarted(testCaseStarted) {
|
|
57
|
+
this.testCaseStarts[testCaseStarted.id] = testCaseStarted;
|
|
58
|
+
}
|
|
59
|
+
addTestStepStarted(step) {
|
|
60
|
+
this.testStepStarted[step.testStepId] = step;
|
|
61
|
+
}
|
|
62
|
+
addTestStepFinished(step) {
|
|
63
|
+
this.testStepFinished[step.testStepId] = step;
|
|
64
|
+
}
|
|
65
|
+
getPickle(id) {
|
|
66
|
+
return this.pickles[id];
|
|
67
|
+
}
|
|
68
|
+
getTestCase(id) {
|
|
69
|
+
return this.testCases[id];
|
|
70
|
+
}
|
|
71
|
+
getTestCaseStarted(id) {
|
|
72
|
+
return this.testCaseStarts[id];
|
|
73
|
+
}
|
|
74
|
+
getScenario(id) {
|
|
75
|
+
return this.scenarios[id];
|
|
76
|
+
}
|
|
77
|
+
getAttachments(key) {
|
|
78
|
+
return this.attachments[key] ?? [];
|
|
79
|
+
}
|
|
80
|
+
getTestStepFinished(id) {
|
|
81
|
+
return this.testStepFinished[id];
|
|
82
|
+
}
|
|
83
|
+
getAllTestStepFinished() {
|
|
84
|
+
return new Map(Object.entries(this.testStepFinished));
|
|
85
|
+
}
|
|
86
|
+
getAllTestStepStarted() {
|
|
87
|
+
return new Map(Object.entries(this.testStepStarted));
|
|
88
|
+
}
|
|
89
|
+
appendAttachment(key, attachment) {
|
|
90
|
+
const list = this.attachments[key] ?? [];
|
|
91
|
+
this.attachments[key] = list;
|
|
92
|
+
list.push({
|
|
93
|
+
file_name: EventStorage.getFileNameFromMediaType(attachment.mediaType),
|
|
94
|
+
mime_type: attachment.mediaType,
|
|
95
|
+
file_path: null,
|
|
96
|
+
content: attachment.body,
|
|
97
|
+
size: 0,
|
|
98
|
+
id: (0, uuid_1.v4)(),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
static getFileNameFromMediaType(mediaType) {
|
|
102
|
+
const extensions = {
|
|
103
|
+
'text/plain': 'txt',
|
|
104
|
+
'application/json': 'json',
|
|
105
|
+
'image/png': 'png',
|
|
106
|
+
'image/jpeg': 'jpg',
|
|
107
|
+
'image/gif': 'gif',
|
|
108
|
+
'text/html': 'html',
|
|
109
|
+
'application/pdf': 'pdf',
|
|
110
|
+
'application/xml': 'xml',
|
|
111
|
+
'application/zip': 'zip',
|
|
112
|
+
'application/msword': 'doc',
|
|
113
|
+
'application/vnd.ms-excel': 'xls',
|
|
114
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
|
115
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
|
116
|
+
};
|
|
117
|
+
const extension = extensions[mediaType];
|
|
118
|
+
return extension ? `file.${extension}` : 'file';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.EventStorage = EventStorage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TestStepType } from 'qase-javascript-commons';
|
|
2
|
+
import { NetworkProfiler } from 'qase-javascript-commons/profilers';
|
|
3
|
+
export declare class ProfilerTracker {
|
|
4
|
+
private readonly profiler;
|
|
5
|
+
private snapshots;
|
|
6
|
+
constructor(profiler: NetworkProfiler | null);
|
|
7
|
+
onTestStart(testCaseStartedId: string): void;
|
|
8
|
+
getEvents(testCaseStartedId: string): TestStepType[];
|
|
9
|
+
reset(testCaseStartedId: string): void;
|
|
10
|
+
restore(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProfilerTracker = void 0;
|
|
4
|
+
class ProfilerTracker {
|
|
5
|
+
profiler;
|
|
6
|
+
snapshots = {};
|
|
7
|
+
constructor(profiler) {
|
|
8
|
+
this.profiler = profiler;
|
|
9
|
+
}
|
|
10
|
+
onTestStart(testCaseStartedId) {
|
|
11
|
+
if (this.profiler) {
|
|
12
|
+
this.snapshots[testCaseStartedId] = this.profiler.getAllSteps().length;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
getEvents(testCaseStartedId) {
|
|
16
|
+
if (!this.profiler)
|
|
17
|
+
return [];
|
|
18
|
+
const snapshot = this.snapshots[testCaseStartedId] ?? 0;
|
|
19
|
+
return this.profiler.getAllSteps().slice(snapshot);
|
|
20
|
+
}
|
|
21
|
+
reset(testCaseStartedId) {
|
|
22
|
+
delete this.snapshots[testCaseStartedId];
|
|
23
|
+
}
|
|
24
|
+
restore() {
|
|
25
|
+
this.profiler?.restore();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.ProfilerTracker = ProfilerTracker;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Pickle, TestCaseFinished, TestCaseStarted } from '@cucumber/messages';
|
|
2
|
+
import { TestCase } from '@cucumber/messages/dist/esm/src/messages';
|
|
3
|
+
import { Attachment, CompoundError, TestResultType, TestStatusEnum, TestStepType } from 'qase-javascript-commons';
|
|
4
|
+
import { TestMetadata } from '../models';
|
|
5
|
+
export interface BuildArgs {
|
|
6
|
+
testCaseFinished: TestCaseFinished;
|
|
7
|
+
testCaseStarted: TestCaseStarted;
|
|
8
|
+
testCase: TestCase;
|
|
9
|
+
pickle: Pickle;
|
|
10
|
+
metadata: TestMetadata;
|
|
11
|
+
status: TestStatusEnum;
|
|
12
|
+
error: CompoundError | undefined;
|
|
13
|
+
steps: TestStepType[];
|
|
14
|
+
profilerSteps: TestStepType[];
|
|
15
|
+
attachments: Attachment[];
|
|
16
|
+
scenarioName: string | undefined;
|
|
17
|
+
scenarioParameters: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export declare class ResultBuilder {
|
|
20
|
+
static build(args: BuildArgs): TestResultType;
|
|
21
|
+
static getSignature(pickle: Pickle, ids: number[], parameters?: Record<string, string>): string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResultBuilder = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
5
|
+
class ResultBuilder {
|
|
6
|
+
static build(args) {
|
|
7
|
+
const { testCaseFinished, testCaseStarted, pickle, metadata, status, error, steps, profilerSteps, attachments, scenarioName, scenarioParameters, } = args;
|
|
8
|
+
let relations = null;
|
|
9
|
+
if (metadata.suite) {
|
|
10
|
+
const suiteParts = metadata.suite.split('\t').filter((part) => part.trim().length > 0);
|
|
11
|
+
relations = {
|
|
12
|
+
suite: {
|
|
13
|
+
data: suiteParts.map((suite) => ({
|
|
14
|
+
title: suite.trim(),
|
|
15
|
+
public_id: null,
|
|
16
|
+
})),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
else if (scenarioName !== undefined) {
|
|
21
|
+
relations = {
|
|
22
|
+
suite: {
|
|
23
|
+
data: [
|
|
24
|
+
{ title: scenarioName, public_id: null },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Merge scenario params with metadata parameters (metadata wins)
|
|
30
|
+
const params = { ...scenarioParameters, ...metadata.parameters };
|
|
31
|
+
const hasProjectMapping = Object.keys(metadata.projectMapping).length > 0;
|
|
32
|
+
return {
|
|
33
|
+
attachments,
|
|
34
|
+
author: null,
|
|
35
|
+
execution: {
|
|
36
|
+
status,
|
|
37
|
+
start_time: testCaseStarted.timestamp.seconds,
|
|
38
|
+
end_time: testCaseFinished.timestamp.seconds,
|
|
39
|
+
duration: Math.abs(testCaseFinished.timestamp.seconds - testCaseStarted.timestamp.seconds) * 1000,
|
|
40
|
+
stacktrace: error?.stacktrace ?? null,
|
|
41
|
+
thread: null,
|
|
42
|
+
},
|
|
43
|
+
fields: metadata.fields,
|
|
44
|
+
message: error?.message ?? null,
|
|
45
|
+
muted: false,
|
|
46
|
+
params,
|
|
47
|
+
group_params: metadata.group_params,
|
|
48
|
+
relations,
|
|
49
|
+
run_id: null,
|
|
50
|
+
signature: ResultBuilder.getSignature(pickle, metadata.ids, params),
|
|
51
|
+
steps: [...steps, ...profilerSteps],
|
|
52
|
+
testops_id: hasProjectMapping ? null : (metadata.ids.length > 0 ? metadata.ids : null),
|
|
53
|
+
testops_project_mapping: hasProjectMapping ? metadata.projectMapping : null,
|
|
54
|
+
id: testCaseStarted.id,
|
|
55
|
+
title: metadata.title ?? pickle.name,
|
|
56
|
+
tags: metadata.tags,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
static getSignature(pickle, ids, parameters = {}) {
|
|
60
|
+
return (0, qase_javascript_commons_1.generateSignature)(ids, [...pickle.uri.split('/'), pickle.name], parameters);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.ResultBuilder = ResultBuilder;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Status } from '@cucumber/cucumber';
|
|
2
|
+
import { StepStatusEnum, TestStatusEnum } from 'qase-javascript-commons';
|
|
3
|
+
export type TestStepResultStatus = (typeof Status)[keyof typeof Status];
|
|
4
|
+
export declare const STATUS_MAP: Record<TestStepResultStatus, TestStatusEnum>;
|
|
5
|
+
export declare const STEP_STATUS_MAP: Record<TestStepResultStatus, StepStatusEnum>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.STEP_STATUS_MAP = exports.STATUS_MAP = void 0;
|
|
4
|
+
const cucumber_1 = require("@cucumber/cucumber");
|
|
5
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
6
|
+
exports.STATUS_MAP = {
|
|
7
|
+
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.TestStatusEnum.passed,
|
|
8
|
+
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.TestStatusEnum.failed,
|
|
9
|
+
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
10
|
+
[cucumber_1.Status.AMBIGUOUS]: qase_javascript_commons_1.TestStatusEnum.invalid,
|
|
11
|
+
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
12
|
+
[cucumber_1.Status.UNDEFINED]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
13
|
+
[cucumber_1.Status.UNKNOWN]: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
14
|
+
};
|
|
15
|
+
exports.STEP_STATUS_MAP = {
|
|
16
|
+
[cucumber_1.Status.PASSED]: qase_javascript_commons_1.StepStatusEnum.passed,
|
|
17
|
+
[cucumber_1.Status.FAILED]: qase_javascript_commons_1.StepStatusEnum.failed,
|
|
18
|
+
[cucumber_1.Status.SKIPPED]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
19
|
+
[cucumber_1.Status.AMBIGUOUS]: qase_javascript_commons_1.StepStatusEnum.failed,
|
|
20
|
+
[cucumber_1.Status.PENDING]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
21
|
+
[cucumber_1.Status.UNDEFINED]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
22
|
+
[cucumber_1.Status.UNKNOWN]: qase_javascript_commons_1.StepStatusEnum.skipped,
|
|
23
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TestStepFinished } from '@cucumber/messages';
|
|
2
|
+
import { CompoundError, TestStatusEnum } from 'qase-javascript-commons';
|
|
3
|
+
export declare class StatusTracker {
|
|
4
|
+
private results;
|
|
5
|
+
private errors;
|
|
6
|
+
onTestStarted(testCaseStartedId: string): void;
|
|
7
|
+
applyStep(step: TestStepFinished): void;
|
|
8
|
+
getStatus(testCaseStartedId: string): TestStatusEnum;
|
|
9
|
+
getErrors(testCaseStartedId: string): CompoundError | undefined;
|
|
10
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StatusTracker = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
5
|
+
const statusMaps_1 = require("./statusMaps");
|
|
6
|
+
class StatusTracker {
|
|
7
|
+
results = {};
|
|
8
|
+
errors = {};
|
|
9
|
+
onTestStarted(testCaseStartedId) {
|
|
10
|
+
this.results[testCaseStartedId] = qase_javascript_commons_1.TestStatusEnum.passed;
|
|
11
|
+
}
|
|
12
|
+
applyStep(step) {
|
|
13
|
+
const oldStatus = this.results[step.testCaseStartedId];
|
|
14
|
+
let error = null;
|
|
15
|
+
if (step.testStepResult.message) {
|
|
16
|
+
error = new Error(step.testStepResult.message);
|
|
17
|
+
}
|
|
18
|
+
const newStatus = (0, qase_javascript_commons_1.determineTestStatus)(error, statusMaps_1.STATUS_MAP[step.testStepResult.status]);
|
|
19
|
+
if (newStatus !== qase_javascript_commons_1.TestStatusEnum.passed) {
|
|
20
|
+
if (step.testStepResult.message) {
|
|
21
|
+
if (!this.errors[step.testCaseStartedId]) {
|
|
22
|
+
this.errors[step.testCaseStartedId] = [];
|
|
23
|
+
}
|
|
24
|
+
this.errors[step.testCaseStartedId]?.push(step.testStepResult.message);
|
|
25
|
+
}
|
|
26
|
+
if (oldStatus) {
|
|
27
|
+
if (oldStatus !== qase_javascript_commons_1.TestStatusEnum.failed && oldStatus !== qase_javascript_commons_1.TestStatusEnum.invalid) {
|
|
28
|
+
this.results[step.testCaseStartedId] = newStatus;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.results[step.testCaseStartedId] = newStatus;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
getStatus(testCaseStartedId) {
|
|
37
|
+
return this.results[testCaseStartedId] ?? qase_javascript_commons_1.TestStatusEnum.passed;
|
|
38
|
+
}
|
|
39
|
+
getErrors(testCaseStartedId) {
|
|
40
|
+
const messages = this.errors[testCaseStartedId];
|
|
41
|
+
if (!messages)
|
|
42
|
+
return undefined;
|
|
43
|
+
const err = new qase_javascript_commons_1.CompoundError();
|
|
44
|
+
messages.forEach((message) => {
|
|
45
|
+
err.addMessage(message);
|
|
46
|
+
err.addStacktrace(message);
|
|
47
|
+
});
|
|
48
|
+
return err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.StatusTracker = StatusTracker;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PickleStep, TestStepFinished, TestStepStarted } from '@cucumber/messages';
|
|
2
|
+
import { TestCase } from '@cucumber/messages/dist/esm/src/messages';
|
|
3
|
+
import { Attachment, TestStepType } from 'qase-javascript-commons';
|
|
4
|
+
export declare class StepConverter {
|
|
5
|
+
static convert(pickleSteps: readonly PickleStep[], testCase: TestCase, startedSteps: Map<string, TestStepStarted>, finishedSteps: Map<string, TestStepFinished>, getAttachments: (stepId: string) => Attachment[]): TestStepType[];
|
|
6
|
+
/**
|
|
7
|
+
* Cucumber `Timestamp` is `{ seconds, nanos }`. Convert to a Unix-seconds
|
|
8
|
+
* number with fractional milliseconds.
|
|
9
|
+
*/
|
|
10
|
+
private static toSeconds;
|
|
11
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StepConverter = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
5
|
+
const statusMaps_1 = require("./statusMaps");
|
|
6
|
+
class StepConverter {
|
|
7
|
+
static convert(pickleSteps, testCase, startedSteps, finishedSteps, getAttachments) {
|
|
8
|
+
const results = [];
|
|
9
|
+
for (const s of testCase.testSteps) {
|
|
10
|
+
const finished = finishedSteps.get(s.id);
|
|
11
|
+
if (!finished)
|
|
12
|
+
continue;
|
|
13
|
+
const step = pickleSteps.find((ps) => ps.id === s.pickleStepId);
|
|
14
|
+
if (!step)
|
|
15
|
+
continue;
|
|
16
|
+
const started = startedSteps.get(s.id);
|
|
17
|
+
const startTimeSec = started ? StepConverter.toSeconds(started.timestamp) : null;
|
|
18
|
+
const endTimeSec = StepConverter.toSeconds(finished.timestamp);
|
|
19
|
+
results.push({
|
|
20
|
+
id: s.id,
|
|
21
|
+
step_type: qase_javascript_commons_1.StepType.GHERKIN,
|
|
22
|
+
data: {
|
|
23
|
+
keyword: step.text,
|
|
24
|
+
name: step.text,
|
|
25
|
+
line: 0,
|
|
26
|
+
},
|
|
27
|
+
execution: {
|
|
28
|
+
status: statusMaps_1.STEP_STATUS_MAP[finished.testStepResult.status],
|
|
29
|
+
start_time: startTimeSec,
|
|
30
|
+
end_time: endTimeSec,
|
|
31
|
+
duration: finished.testStepResult.duration.seconds * 1000
|
|
32
|
+
+ Math.round(finished.testStepResult.duration.nanos / 1e6),
|
|
33
|
+
},
|
|
34
|
+
attachments: getAttachments(s.id),
|
|
35
|
+
steps: [],
|
|
36
|
+
parent_id: null,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Cucumber `Timestamp` is `{ seconds, nanos }`. Convert to a Unix-seconds
|
|
43
|
+
* number with fractional milliseconds.
|
|
44
|
+
*/
|
|
45
|
+
static toSeconds(ts) {
|
|
46
|
+
return ts.seconds + ts.nanos / 1e9;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.StepConverter = StepConverter;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TagParser = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
5
|
+
const qaseIdRegExp = /^@[Qq]-?(\d+)$/;
|
|
6
|
+
const newQaseIdRegExp = /^@[Qq]ase[Ii][Dd]=(\d+(?:,\s*\d+)*)$/;
|
|
7
|
+
const qaseTitleRegExp = /^@[Qq]ase[Tt]itle=(.+)$/;
|
|
8
|
+
const qaseFieldsRegExp = /^@[Qq]ase[Ff]ields=(.+)$/;
|
|
9
|
+
const qaseParametersRegExp = /^@[Qq]ase[Pp]arameters=(.+)$/;
|
|
10
|
+
const qaseGroupParametersRegExp = /^@[Qq]ase[Gg]roup[Pp]arameters=(.+)$/;
|
|
11
|
+
const qaseSuiteRegExp = /^@[Qq]ase[Ss]uite=(.+)$/;
|
|
12
|
+
const qaseIgnoreRegExp = /^@[Qq]ase[Ii][Gg][Nn][Oo][Rr][Ee]$/;
|
|
13
|
+
const qaseTagsRegExp = /^@[Qq]ase[Tt]ags=(.+)$/;
|
|
14
|
+
class TagParser {
|
|
15
|
+
static parse(tags) {
|
|
16
|
+
const tagNames = tags.map((t) => t.name);
|
|
17
|
+
const { legacyIds, projectMapping } = (0, qase_javascript_commons_1.parseProjectMappingFromTags)(tagNames);
|
|
18
|
+
const metadata = {
|
|
19
|
+
ids: [...legacyIds],
|
|
20
|
+
projectMapping: { ...projectMapping },
|
|
21
|
+
fields: {},
|
|
22
|
+
title: null,
|
|
23
|
+
isIgnore: false,
|
|
24
|
+
parameters: {},
|
|
25
|
+
group_params: {},
|
|
26
|
+
suite: null,
|
|
27
|
+
tags: [],
|
|
28
|
+
};
|
|
29
|
+
for (const tag of tags) {
|
|
30
|
+
if (qaseIdRegExp.test(tag.name)) {
|
|
31
|
+
metadata.ids.push(Number(tag.name.replace(/^@[Qq]-?/, '')));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (newQaseIdRegExp.test(tag.name)) {
|
|
35
|
+
const idsStr = tag.name.replace(/^@[Qq]ase[Ii][Dd]=/, '');
|
|
36
|
+
metadata.ids.push(...idsStr.split(',').map((s) => Number(s.trim())));
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (qaseTitleRegExp.test(tag.name)) {
|
|
40
|
+
metadata.title = tag.name.replace(/^@[Qq]ase[Tt]itle=/, '');
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (qaseFieldsRegExp.test(tag.name)) {
|
|
44
|
+
const value = tag.name.replace(/^@[Qq]ase[Ff]ields=/, '');
|
|
45
|
+
try {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
47
|
+
const record = JSON.parse(TagParser.normalizeJsonString(value));
|
|
48
|
+
metadata.fields = { ...metadata.fields, ...record };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// do nothing
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (qaseParametersRegExp.test(tag.name)) {
|
|
55
|
+
const value = tag.name.replace(/^@[Qq]ase[Pp]arameters=/, '');
|
|
56
|
+
try {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
58
|
+
const record = JSON.parse(TagParser.normalizeJsonString(value));
|
|
59
|
+
metadata.parameters = { ...metadata.parameters, ...record };
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// do nothing
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (qaseGroupParametersRegExp.test(tag.name)) {
|
|
66
|
+
const value = tag.name.replace(/^@[Qq]ase[Gg]roup[Pp]arameters=/, '');
|
|
67
|
+
try {
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
69
|
+
const record = JSON.parse(TagParser.normalizeJsonString(value));
|
|
70
|
+
metadata.group_params = { ...metadata.group_params, ...record };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// do nothing
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (qaseSuiteRegExp.test(tag.name)) {
|
|
77
|
+
metadata.suite = tag.name.replace(/^@[Qq]ase[Ss]uite=/, '');
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (qaseTagsRegExp.test(tag.name)) {
|
|
81
|
+
const value = tag.name.replace(/^@[Qq]ase[Tt]ags=/, '');
|
|
82
|
+
const parsedTags = value.split(',').map((t) => t.trim()).filter((t) => t.length > 0);
|
|
83
|
+
metadata.tags.push(...parsedTags);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (qaseIgnoreRegExp.test(tag.name)) {
|
|
87
|
+
metadata.isIgnore = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return metadata;
|
|
91
|
+
}
|
|
92
|
+
static normalizeJsonString(jsonString) {
|
|
93
|
+
if (jsonString.includes("'")) {
|
|
94
|
+
return jsonString.replace(/'/g, '"');
|
|
95
|
+
}
|
|
96
|
+
return jsonString;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.TagParser = TagParser;
|
package/dist/reporter.d.ts
CHANGED
package/dist/reporter.js
CHANGED
|
@@ -20,11 +20,6 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
20
20
|
* @private
|
|
21
21
|
*/
|
|
22
22
|
reporter;
|
|
23
|
-
/**
|
|
24
|
-
* @type {NetworkProfiler | null}
|
|
25
|
-
* @private
|
|
26
|
-
*/
|
|
27
|
-
profiler = null;
|
|
28
23
|
/**
|
|
29
24
|
* @type {EventEmitter}
|
|
30
25
|
* @private
|
|
@@ -45,15 +40,16 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
45
40
|
frameworkName: 'cucumberjs',
|
|
46
41
|
reporterName: 'cucumberjs-qase-reporter',
|
|
47
42
|
});
|
|
43
|
+
let profiler = null;
|
|
48
44
|
if (composedOptions.profilers?.includes('network')) {
|
|
49
|
-
|
|
45
|
+
profiler = new profilers_1.NetworkProfiler({
|
|
50
46
|
skipDomains: composedOptions.networkProfiler?.skip_domains,
|
|
51
47
|
trackOnFail: composedOptions.networkProfiler?.track_on_fail,
|
|
52
48
|
});
|
|
53
|
-
|
|
49
|
+
profiler.enable();
|
|
54
50
|
}
|
|
55
51
|
this.eventBroadcaster = formatterOptions.eventBroadcaster;
|
|
56
|
-
this.storage = new storage_1.Storage(
|
|
52
|
+
this.storage = new storage_1.Storage(profiler);
|
|
57
53
|
this.bindEventListeners();
|
|
58
54
|
}
|
|
59
55
|
/**
|
|
@@ -82,6 +78,9 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
82
78
|
else if (envelope.testCaseStarted) {
|
|
83
79
|
this.storage.addTestCaseStarted(envelope.testCaseStarted);
|
|
84
80
|
}
|
|
81
|
+
else if (envelope.testStepStarted) {
|
|
82
|
+
this.storage.addTestCaseStepStarted(envelope.testStepStarted);
|
|
83
|
+
}
|
|
85
84
|
else if (envelope.testStepFinished) {
|
|
86
85
|
this.storage.addTestCaseStep(envelope.testStepFinished);
|
|
87
86
|
}
|
|
@@ -99,7 +98,7 @@ class CucumberQaseReporter extends cucumber_1.Formatter {
|
|
|
99
98
|
* @private
|
|
100
99
|
*/
|
|
101
100
|
async publishResults() {
|
|
102
|
-
this.
|
|
101
|
+
this.storage.restore();
|
|
103
102
|
await this.reporter.publish();
|
|
104
103
|
}
|
|
105
104
|
/**
|