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 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,6 @@
1
+ import { PickleTag } from '@cucumber/messages';
2
+ import { TestMetadata } from '../models';
3
+ export declare class TagParser {
4
+ static parse(tags: readonly PickleTag[]): TestMetadata;
5
+ static normalizeJsonString(jsonString: string): string;
6
+ }
@@ -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;
@@ -18,11 +18,6 @@ export declare class CucumberQaseReporter extends Formatter {
18
18
  * @private
19
19
  */
20
20
  private reporter;
21
- /**
22
- * @type {NetworkProfiler | null}
23
- * @private
24
- */
25
- private profiler;
26
21
  /**
27
22
  * @type {EventEmitter}
28
23
  * @private
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
- this.profiler = new profilers_1.NetworkProfiler({
45
+ profiler = new profilers_1.NetworkProfiler({
50
46
  skipDomains: composedOptions.networkProfiler?.skip_domains,
51
47
  trackOnFail: composedOptions.networkProfiler?.track_on_fail,
52
48
  });
53
- this.profiler.enable();
49
+ profiler.enable();
54
50
  }
55
51
  this.eventBroadcaster = formatterOptions.eventBroadcaster;
56
- this.storage = new storage_1.Storage(this.profiler);
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.profiler?.restore();
101
+ this.storage.restore();
103
102
  await this.reporter.publish();
104
103
  }
105
104
  /**