cypress-qase-reporter 3.0.3 → 3.1.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/README.md +15 -59
- package/changelog.md +13 -0
- package/dist/cucumber.d.ts +30 -0
- package/dist/cucumber.js +61 -7
- package/dist/fileSearcher.js +17 -7
- package/dist/hooks.d.ts +0 -1
- package/dist/hooks.js +3 -4
- package/dist/index.cjs.d.ts +1 -1
- package/dist/index.cjs.js +2 -1
- package/dist/metadata/models.d.ts +0 -1
- package/dist/metadata.js +6 -0
- package/dist/mocha.d.ts +0 -90
- package/dist/reporter.d.ts +38 -1
- package/dist/reporter.js +155 -12
- package/dist/utils/tagParser.js +18 -9
- package/docs/cucumber.md +329 -0
- package/docs/usage.md +62 -0
- package/package.json +9 -9
- package/tsconfig.build.json +3 -1
package/README.md
CHANGED
|
@@ -138,6 +138,10 @@ parameterize your tests.
|
|
|
138
138
|
- `qase.step` - create a step in the test case
|
|
139
139
|
- `qase.attach` - attach a file or content to the test case
|
|
140
140
|
|
|
141
|
+
#### Cucumber-specific
|
|
142
|
+
|
|
143
|
+
- `addCucumberStep(stepName)` - manually add a Cucumber step to Qase report (useful for `@badeball/cypress-cucumber-preprocessor`)
|
|
144
|
+
|
|
141
145
|
For detailed instructions on using annotations and methods, refer to [Usage](docs/usage.md).
|
|
142
146
|
|
|
143
147
|
For example:
|
|
@@ -255,71 +259,23 @@ module.exports = cypress.defineConfig({
|
|
|
255
259
|
Check out the example of configuration for multiple reporters in the
|
|
256
260
|
[demo project](../examples/cypress/cypress.config.js).
|
|
257
261
|
|
|
258
|
-
|
|
262
|
+
## Cucumber/Gherkin Integration
|
|
259
263
|
|
|
260
|
-
|
|
261
|
-
import cypress from 'cypress';
|
|
262
|
-
import { afterSpecHook } from 'cypress-qase-reporter/hooks';
|
|
263
|
-
|
|
264
|
-
const cucumber = require('cypress-cucumber-preprocessor').default;
|
|
264
|
+
If you use Cucumber with Gherkin feature files in your Cypress tests, Qase reporter provides full support for both the legacy `cypress-cucumber-preprocessor` and the modern `@badeball/cypress-cucumber-preprocessor`.
|
|
265
265
|
|
|
266
|
-
|
|
267
|
-
reporter: 'cypress-multi-reporters',
|
|
268
|
-
reporterOptions: {
|
|
269
|
-
reporterEnabled: 'cypress-mochawesome-reporter, cypress-qase-reporter',
|
|
270
|
-
cypressMochawesomeReporterReporterOptions: {
|
|
271
|
-
charts: true,
|
|
272
|
-
},
|
|
273
|
-
cypressQaseReporterReporterOptions: {
|
|
274
|
-
debug: true,
|
|
266
|
+
The reporter can automatically:
|
|
275
267
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
268
|
+
- Extract test cases from feature files
|
|
269
|
+
- Report individual Gherkin steps (Given/When/Then) with their execution status
|
|
270
|
+
- Link test results to Qase test cases using `@QaseID` tags
|
|
271
|
+
- Attach screenshots and videos to test results
|
|
280
272
|
|
|
281
|
-
|
|
282
|
-
uploadAttachments: true,
|
|
273
|
+
**📚 For detailed instructions, configuration examples, and troubleshooting, see the [Cucumber Integration Guide](docs/cucumber.md).**
|
|
283
274
|
|
|
284
|
-
|
|
285
|
-
complete: true,
|
|
286
|
-
},
|
|
287
|
-
},
|
|
275
|
+
**Quick links:**
|
|
288
276
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
screenshotsFolder: 'cypress/screenshots',
|
|
292
|
-
videosFolder: 'cypress/videos',
|
|
293
|
-
uploadDelay: 10, // Delay in seconds before uploading video files (default: 10)
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
video: false,
|
|
299
|
-
e2e: {
|
|
300
|
-
setupNodeEvents(on, config) {
|
|
301
|
-
on('file:preprocessor', cucumber());
|
|
302
|
-
require('cypress-qase-reporter/plugin')(on, config);
|
|
303
|
-
require('cypress-qase-reporter/metadata')(on);
|
|
304
|
-
on('after:spec', async (spec, results) => {
|
|
305
|
-
await afterSpecHook(spec, config);
|
|
306
|
-
});
|
|
307
|
-
},
|
|
308
|
-
specPattern: 'cypress/e2e/*.feature',
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
And add the following lines in `support/e2e.js` file:
|
|
314
|
-
|
|
315
|
-
```javascript
|
|
316
|
-
import { enableCucumberSupport } from "cypress-qase-reporter";
|
|
317
|
-
|
|
318
|
-
enableCucumberSupport();
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
Check out the example of configuration for multiple reporters in the
|
|
322
|
-
[demo project](../examples/cypressCucumber/cypress.config.js).
|
|
277
|
+
- [Example project for @badeball/cypress-cucumber-preprocessor](../examples/cypressBadeballCucumber/)
|
|
278
|
+
- [Example project for cypress-cucumber-preprocessor (legacy)](../examples/cypressCucumber/)
|
|
323
279
|
|
|
324
280
|
## Requirements
|
|
325
281
|
|
package/changelog.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# cypress-qase-reporter@3.1.1
|
|
2
|
+
|
|
3
|
+
## What's new
|
|
4
|
+
|
|
5
|
+
- Fixed an issue where the reporter was not reporting skipped tests due to failing beforeEach hooks.
|
|
6
|
+
|
|
7
|
+
# cypress-qase-reporter@3.1.0
|
|
8
|
+
|
|
9
|
+
## What's new
|
|
10
|
+
|
|
11
|
+
- Added support for Cucumber steps reporting for `@badeball/cypress-cucumber-preprocessor`.
|
|
12
|
+
- Added `addCucumberStep` function to manually add a Cucumber step to Qase report.
|
|
13
|
+
|
|
1
14
|
# cypress-qase-reporter@3.0.3
|
|
2
15
|
|
|
3
16
|
## What's new
|
package/dist/cucumber.d.ts
CHANGED
|
@@ -1 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enable automatic Cucumber step reporting for cypress-cucumber-preprocessor (legacy).
|
|
3
|
+
* This function automatically captures Gherkin steps and reports them to Qase.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```javascript
|
|
7
|
+
* // In cypress/support/e2e.js
|
|
8
|
+
* import { enableCucumberSupport } from 'cypress-qase-reporter/cucumber';
|
|
9
|
+
*
|
|
10
|
+
* enableCucumberSupport();
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
1
13
|
export declare const enableCucumberSupport: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Manually add a cucumber step to Qase report.
|
|
16
|
+
* Use this function in Before/After hooks or directly in step definitions
|
|
17
|
+
* when using @badeball/cypress-cucumber-preprocessor.
|
|
18
|
+
*
|
|
19
|
+
* @param stepName - The name of the step (e.g., "Given I am on the homepage")
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { Before } from '@badeball/cypress-cucumber-preprocessor';
|
|
24
|
+
* import { addCucumberStep } from 'cypress-qase-reporter/cucumber';
|
|
25
|
+
*
|
|
26
|
+
* Before(function() {
|
|
27
|
+
* addCucumberStep(this.pickle.name);
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const addCucumberStep: (stepName: string) => void;
|
package/dist/cucumber.js
CHANGED
|
@@ -1,25 +1,79 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/// <reference types="cypress" />
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.enableCucumberSupport = void 0;
|
|
4
|
+
exports.addCucumberStep = exports.enableCucumberSupport = void 0;
|
|
4
5
|
const CUCUMBER_TASK_NAME = 'qaseCucumberStepStart';
|
|
6
|
+
/**
|
|
7
|
+
* Enable automatic Cucumber step reporting for cypress-cucumber-preprocessor (legacy).
|
|
8
|
+
* This function automatically captures Gherkin steps and reports them to Qase.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```javascript
|
|
12
|
+
* // In cypress/support/e2e.js
|
|
13
|
+
* import { enableCucumberSupport } from 'cypress-qase-reporter/cucumber';
|
|
14
|
+
*
|
|
15
|
+
* enableCucumberSupport();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
5
18
|
const enableCucumberSupport = () => {
|
|
6
|
-
registerCypressEventListeners();
|
|
7
|
-
};
|
|
8
|
-
exports.enableCucumberSupport = enableCucumberSupport;
|
|
9
|
-
const registerCypressEventListeners = () => {
|
|
10
19
|
Cypress.on('log:added', handleLogAdded);
|
|
11
20
|
};
|
|
21
|
+
exports.enableCucumberSupport = enableCucumberSupport;
|
|
12
22
|
const handleLogAdded = (_, entry) => {
|
|
13
23
|
if (isCucumberStep(entry)) {
|
|
14
24
|
processCucumberStep(entry);
|
|
15
25
|
}
|
|
16
26
|
};
|
|
17
|
-
const isCucumberStep = (
|
|
27
|
+
const isCucumberStep = (entry) => {
|
|
28
|
+
const { attributes: { name, event, instrument } } = entry;
|
|
18
29
|
return instrument === 'command' && !event && name === 'step';
|
|
19
30
|
};
|
|
20
|
-
const processCucumberStep = (
|
|
31
|
+
const processCucumberStep = (entry) => {
|
|
32
|
+
const { attributes: { displayName, message } } = entry;
|
|
21
33
|
sendTaskMessage(`${displayName ?? ''} ${message}`);
|
|
22
34
|
};
|
|
23
35
|
const sendTaskMessage = (message) => {
|
|
24
36
|
cy.task(CUCUMBER_TASK_NAME, message, { log: false });
|
|
25
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Manually add a cucumber step to Qase report.
|
|
40
|
+
* Use this function in Before/After hooks or directly in step definitions
|
|
41
|
+
* when using @badeball/cypress-cucumber-preprocessor.
|
|
42
|
+
*
|
|
43
|
+
* @param stepName - The name of the step (e.g., "Given I am on the homepage")
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { Before } from '@badeball/cypress-cucumber-preprocessor';
|
|
48
|
+
* import { addCucumberStep } from 'cypress-qase-reporter/cucumber';
|
|
49
|
+
*
|
|
50
|
+
* Before(function() {
|
|
51
|
+
* addCucumberStep(this.pickle.name);
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
const addCucumberStep = (stepName) => {
|
|
56
|
+
// Use Cypress.log to avoid command queue issues
|
|
57
|
+
// The step will be collected by MetadataManager
|
|
58
|
+
try {
|
|
59
|
+
if (stepName && stepName.trim()) {
|
|
60
|
+
Cypress.log({
|
|
61
|
+
name: 'qase:step',
|
|
62
|
+
message: stepName,
|
|
63
|
+
consoleProps: () => ({
|
|
64
|
+
Step: stepName,
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
// Also send to task for backend collection
|
|
68
|
+
// We need to wrap this in a way that doesn't interfere with command queue
|
|
69
|
+
cy.then(() => {
|
|
70
|
+
cy.task(CUCUMBER_TASK_NAME, stepName, { log: false });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
// Silently fail if Cypress is not available
|
|
76
|
+
console.debug('Failed to add cucumber step:', e);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.addCucumberStep = addCucumberStep;
|
package/dist/fileSearcher.js
CHANGED
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
36
|
exports.FileSearcher = void 0;
|
|
27
37
|
const fs = __importStar(require("fs"));
|
package/dist/hooks.d.ts
CHANGED
package/dist/hooks.js
CHANGED
|
@@ -4,7 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
7
|
+
exports.beforeRunHook = beforeRunHook;
|
|
8
|
+
exports.afterRunHook = afterRunHook;
|
|
9
|
+
exports.afterSpecHook = afterSpecHook;
|
|
8
10
|
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
9
11
|
const configSchema_1 = require("./configSchema");
|
|
10
12
|
const resultsManager_1 = require("./metadata/resultsManager");
|
|
@@ -28,7 +30,6 @@ async function beforeRunHook(options) {
|
|
|
28
30
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
29
31
|
await reporter.startTestRunAsync();
|
|
30
32
|
}
|
|
31
|
-
exports.beforeRunHook = beforeRunHook;
|
|
32
33
|
async function afterRunHook(options) {
|
|
33
34
|
const configLoader = new qase_javascript_commons_1.ConfigLoader(configSchema_1.configSchema);
|
|
34
35
|
const config = configLoader.load();
|
|
@@ -44,7 +45,6 @@ async function afterRunHook(options) {
|
|
|
44
45
|
});
|
|
45
46
|
await reporter.complete();
|
|
46
47
|
}
|
|
47
|
-
exports.afterRunHook = afterRunHook;
|
|
48
48
|
async function afterSpecHook(spec, options) {
|
|
49
49
|
const results = resultsManager_1.ResultsManager.getResults();
|
|
50
50
|
if (results) {
|
|
@@ -131,4 +131,3 @@ async function afterSpecHook(spec, options) {
|
|
|
131
131
|
}
|
|
132
132
|
resultsManager_1.ResultsManager.clear();
|
|
133
133
|
}
|
|
134
|
-
exports.afterSpecHook = afterSpecHook;
|
package/dist/index.cjs.d.ts
CHANGED
package/dist/index.cjs.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ResultsManager = exports.enableCucumberSupport = exports.qase = void 0;
|
|
3
|
+
exports.ResultsManager = exports.addCucumberStep = exports.enableCucumberSupport = exports.qase = void 0;
|
|
4
4
|
var mocha_1 = require("./mocha");
|
|
5
5
|
Object.defineProperty(exports, "qase", { enumerable: true, get: function () { return mocha_1.qase; } });
|
|
6
6
|
var cucumber_1 = require("./cucumber");
|
|
7
7
|
Object.defineProperty(exports, "enableCucumberSupport", { enumerable: true, get: function () { return cucumber_1.enableCucumberSupport; } });
|
|
8
|
+
Object.defineProperty(exports, "addCucumberStep", { enumerable: true, get: function () { return cucumber_1.addCucumberStep; } });
|
|
8
9
|
var resultsManager_1 = require("./metadata/resultsManager");
|
|
9
10
|
Object.defineProperty(exports, "ResultsManager", { enumerable: true, get: function () { return resultsManager_1.ResultsManager; } });
|
package/dist/metadata.js
CHANGED
package/dist/mocha.d.ts
CHANGED
|
@@ -1,104 +1,14 @@
|
|
|
1
|
-
/// <reference types="cypress" />
|
|
2
|
-
/// <reference types="cypress" />
|
|
3
|
-
/// <reference types="cypress" />
|
|
4
|
-
/// <reference types="cypress" />
|
|
5
|
-
/// <reference types="node" />
|
|
6
1
|
import { Test } from 'mocha';
|
|
7
2
|
export declare const qase: {
|
|
8
3
|
(caseId: number | string | number[] | string[], test: Test): Test;
|
|
9
|
-
/**
|
|
10
|
-
* Set a title for the test case
|
|
11
|
-
* @param {string} value
|
|
12
|
-
* @example
|
|
13
|
-
* it('test', () => {
|
|
14
|
-
* qase.title("Title");
|
|
15
|
-
* cy.visit('https://example.com');
|
|
16
|
-
* });
|
|
17
|
-
*/
|
|
18
4
|
title(value: string): Cypress.Chainable<JQuery<void>>;
|
|
19
|
-
/**
|
|
20
|
-
* Set fields for the test case
|
|
21
|
-
* @param {Record<string, string>} values
|
|
22
|
-
* @example
|
|
23
|
-
* it('test', () => {
|
|
24
|
-
* qase.fields({description: "Description"});
|
|
25
|
-
* cy.visit('https://example.com');
|
|
26
|
-
* });
|
|
27
|
-
*/
|
|
28
5
|
fields(values: Record<string, string>): Cypress.Chainable<JQuery<void>>;
|
|
29
|
-
/**
|
|
30
|
-
* Ignore the test case result in Qase
|
|
31
|
-
* @example
|
|
32
|
-
* it('test', () => {
|
|
33
|
-
* qase.ignore();
|
|
34
|
-
* cy.visit('https://example.com');
|
|
35
|
-
* });
|
|
36
|
-
*/
|
|
37
6
|
ignore(): Cypress.Chainable<JQuery<void>>;
|
|
38
|
-
/**
|
|
39
|
-
* Set parameters for the test case
|
|
40
|
-
* @param {Record<string, string>} values
|
|
41
|
-
* @example
|
|
42
|
-
* it('test', () => {
|
|
43
|
-
* qase.parameters({param01: "value01"});
|
|
44
|
-
* cy.visit('https://example.com');
|
|
45
|
-
* });
|
|
46
|
-
*/
|
|
47
7
|
parameters(values: Record<string, string>): Cypress.Chainable<JQuery<void>>;
|
|
48
|
-
/**
|
|
49
|
-
* Set group parameters for the test case
|
|
50
|
-
* @param {Record<string, string>} values
|
|
51
|
-
* @example
|
|
52
|
-
* it('test', () => {
|
|
53
|
-
* qase.groupParameters({param01: "value01"});
|
|
54
|
-
* cy.visit('https://example.com');
|
|
55
|
-
* });
|
|
56
|
-
*/
|
|
57
8
|
groupParameters(values: Record<string, string>): Cypress.Chainable<JQuery<void>>;
|
|
58
|
-
/**
|
|
59
|
-
* Set a suite for the test case
|
|
60
|
-
* @param {string} value
|
|
61
|
-
* @example
|
|
62
|
-
* it('test', () => {
|
|
63
|
-
* qase.suite("Suite 01");
|
|
64
|
-
* cy.visit('https://example.com');
|
|
65
|
-
* });
|
|
66
|
-
*/
|
|
67
9
|
suite(value: string): Cypress.Chainable<JQuery<void>>;
|
|
68
|
-
/**
|
|
69
|
-
* Set a comment for the test case
|
|
70
|
-
* @param {string} value
|
|
71
|
-
* @example
|
|
72
|
-
* it('test', () => {
|
|
73
|
-
* qase.comment("Some comment");
|
|
74
|
-
* cy.visit('https://example.com');
|
|
75
|
-
* });
|
|
76
|
-
*/
|
|
77
10
|
comment(value: string): Cypress.Chainable<JQuery<void>>;
|
|
78
|
-
/**
|
|
79
|
-
* Add a step to the test case
|
|
80
|
-
* @param {string} name
|
|
81
|
-
* @param {() => T | PromiseLike<T>} body
|
|
82
|
-
* @example
|
|
83
|
-
* it('test', () => {
|
|
84
|
-
* qase.step("Some step", () => {
|
|
85
|
-
* // some actions
|
|
86
|
-
* });
|
|
87
|
-
* cy.visit('https://example.com');
|
|
88
|
-
* });
|
|
89
|
-
*/
|
|
90
11
|
step<T = void>(name: string, body: () => T | PromiseLike<T>): Cypress.Chainable<JQuery<void>>;
|
|
91
|
-
/**
|
|
92
|
-
* Attach a file to the test case or the step
|
|
93
|
-
* @param attach
|
|
94
|
-
* @example
|
|
95
|
-
* it('test', () => {
|
|
96
|
-
* qase.attach({ name: 'attachment.txt', content: 'Hello, world!', contentType: 'text/plain' });
|
|
97
|
-
* qase.attach({ paths: '/path/to/file'});
|
|
98
|
-
* qase.attach({ paths: ['/path/to/file', '/path/to/another/file']});
|
|
99
|
-
* cy.visit('https://example.com');
|
|
100
|
-
* });
|
|
101
|
-
*/
|
|
102
12
|
attach(attach: {
|
|
103
13
|
name?: string;
|
|
104
14
|
paths?: string | string[];
|
package/dist/reporter.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="cypress" />
|
|
2
1
|
import { MochaOptions, reporters, Runner } from 'mocha';
|
|
3
2
|
import { ConfigLoader, TestStatusEnum } from 'qase-javascript-commons';
|
|
4
3
|
import { ReporterOptionsType } from './options';
|
|
@@ -36,6 +35,12 @@ export declare class CypressQaseReporter extends reporters.Base {
|
|
|
36
35
|
*/
|
|
37
36
|
private reporter;
|
|
38
37
|
private testBeginTime;
|
|
38
|
+
/**
|
|
39
|
+
* Set to track processed tests to identify skipped tests when beforeEach fails
|
|
40
|
+
* @type {Set<string>}
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
private processedTests;
|
|
39
44
|
/**
|
|
40
45
|
* @param {Runner} runner
|
|
41
46
|
* @param {CypressQaseOptionsType} options
|
|
@@ -47,6 +52,38 @@ export declare class CypressQaseReporter extends reporters.Base {
|
|
|
47
52
|
* @private
|
|
48
53
|
*/
|
|
49
54
|
private addRunnerListeners;
|
|
55
|
+
/**
|
|
56
|
+
* Generate a unique identifier for a test
|
|
57
|
+
* @param {Test} test
|
|
58
|
+
* @returns {string}
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private getTestIdentifier;
|
|
62
|
+
/**
|
|
63
|
+
* Mark a test as processed
|
|
64
|
+
* @param {Test} test
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
private markTestAsProcessed;
|
|
68
|
+
/**
|
|
69
|
+
* Check if a test was processed
|
|
70
|
+
* @param {Test} test
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
private isTestProcessed;
|
|
75
|
+
/**
|
|
76
|
+
* Handle skipped tests in a suite when beforeEach hook fails
|
|
77
|
+
* @param {Suite} suite
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
private handleSkippedTestsInSuite;
|
|
81
|
+
/**
|
|
82
|
+
* Add a test result for a skipped test (due to beforeEach failure)
|
|
83
|
+
* @param {Test} test
|
|
84
|
+
* @private
|
|
85
|
+
*/
|
|
86
|
+
private addSkippedTestResult;
|
|
50
87
|
/**
|
|
51
88
|
* @param {Test} test
|
|
52
89
|
* @private
|
package/dist/reporter.js
CHANGED
|
@@ -14,7 +14,7 @@ const manager_1 = require("./metadata/manager");
|
|
|
14
14
|
const fileSearcher_1 = require("./fileSearcher");
|
|
15
15
|
const tagParser_1 = require("./utils/tagParser");
|
|
16
16
|
const resultsManager_1 = require("./metadata/resultsManager");
|
|
17
|
-
const { EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING, EVENT_RUN_END, EVENT_TEST_BEGIN, } = mocha_1.Runner.constants;
|
|
17
|
+
const { EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING, EVENT_RUN_END, EVENT_TEST_BEGIN, EVENT_SUITE_END } = mocha_1.Runner.constants;
|
|
18
18
|
/**
|
|
19
19
|
* @class CypressQaseReporter
|
|
20
20
|
* @extends reporters.Base
|
|
@@ -57,6 +57,12 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
57
57
|
*/
|
|
58
58
|
reporter;
|
|
59
59
|
testBeginTime = Date.now();
|
|
60
|
+
/**
|
|
61
|
+
* Set to track processed tests to identify skipped tests when beforeEach fails
|
|
62
|
+
* @type {Set<string>}
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
processedTests = new Set();
|
|
60
66
|
// private options: Omit<(FrameworkOptionsType<'cypress', ReporterOptionsType> & ConfigType & ReporterOptionsType & NonNullable<unknown>) | (null & ReporterOptionsType & NonNullable<unknown>), 'framework'>;
|
|
61
67
|
/**
|
|
62
68
|
* @param {Runner} runner
|
|
@@ -84,26 +90,161 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
84
90
|
* @private
|
|
85
91
|
*/
|
|
86
92
|
addRunnerListeners(runner) {
|
|
87
|
-
runner.on(EVENT_TEST_PASS, (test) =>
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
runner.on(EVENT_TEST_PASS, (test) => {
|
|
94
|
+
this.markTestAsProcessed(test);
|
|
95
|
+
this.addTestResult(test);
|
|
96
|
+
});
|
|
97
|
+
runner.on(EVENT_TEST_PENDING, (test) => {
|
|
98
|
+
this.markTestAsProcessed(test);
|
|
99
|
+
this.addTestResult(test);
|
|
100
|
+
});
|
|
101
|
+
runner.on(EVENT_TEST_FAIL, (test) => {
|
|
102
|
+
this.markTestAsProcessed(test);
|
|
103
|
+
this.addTestResult(test);
|
|
104
|
+
});
|
|
90
105
|
runner.on(EVENT_TEST_BEGIN, () => {
|
|
91
106
|
this.testBeginTime = Date.now();
|
|
92
107
|
manager_1.MetadataManager.clear();
|
|
93
108
|
});
|
|
109
|
+
runner.on(EVENT_SUITE_END, (suite) => {
|
|
110
|
+
this.handleSkippedTestsInSuite(suite);
|
|
111
|
+
});
|
|
94
112
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
95
113
|
runner.once(EVENT_RUN_END, () => {
|
|
96
114
|
const results = this.reporter.getResults();
|
|
97
115
|
resultsManager_1.ResultsManager.setResults(results);
|
|
98
|
-
|
|
99
|
-
// stdio: 'inherit',
|
|
100
|
-
// env: Object.assign(process.env, {
|
|
101
|
-
// reporterConfig: JSON.stringify(this.options),
|
|
102
|
-
// results: JSON.stringify(results),
|
|
103
|
-
// }),
|
|
104
|
-
// });
|
|
116
|
+
this.processedTests.clear();
|
|
105
117
|
});
|
|
106
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Generate a unique identifier for a test
|
|
121
|
+
* @param {Test} test
|
|
122
|
+
* @returns {string}
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
getTestIdentifier(test) {
|
|
126
|
+
const file = test.parent ? this.getFile(test.parent) ?? '' : '';
|
|
127
|
+
const suitePath = test.parent ? test.parent.titlePath().join(' > ') : '';
|
|
128
|
+
let testTitle = test.fullTitle();
|
|
129
|
+
// Remove "before each" hook prefix and quotes if present (can be anywhere in the string)
|
|
130
|
+
testTitle = testTitle.replace(/"before each" hook for "/g, '');
|
|
131
|
+
// Remove trailing quote if present
|
|
132
|
+
if (testTitle.endsWith('"')) {
|
|
133
|
+
testTitle = testTitle.slice(0, -1);
|
|
134
|
+
}
|
|
135
|
+
return `${file}::${suitePath}::${testTitle}`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Mark a test as processed
|
|
139
|
+
* @param {Test} test
|
|
140
|
+
* @private
|
|
141
|
+
*/
|
|
142
|
+
markTestAsProcessed(test) {
|
|
143
|
+
const identifier = this.getTestIdentifier(test);
|
|
144
|
+
this.processedTests.add(identifier);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if a test was processed
|
|
148
|
+
* @param {Test} test
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
* @private
|
|
151
|
+
*/
|
|
152
|
+
isTestProcessed(test) {
|
|
153
|
+
const identifier = this.getTestIdentifier(test);
|
|
154
|
+
return this.processedTests.has(identifier);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Handle skipped tests in a suite when beforeEach hook fails
|
|
158
|
+
* @param {Suite} suite
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
handleSkippedTestsInSuite(suite) {
|
|
162
|
+
// Get tests only from the current suite (not nested suites, they will be processed separately)
|
|
163
|
+
const tests = suite.tests ?? [];
|
|
164
|
+
// Find tests that were not processed (skipped due to beforeEach failure)
|
|
165
|
+
for (const test of tests) {
|
|
166
|
+
// Skip if test was already processed (e.g., first test that got EVENT_TEST_FAIL)
|
|
167
|
+
if (!this.isTestProcessed(test)) {
|
|
168
|
+
// Test was skipped due to beforeEach failure, report it as skipped
|
|
169
|
+
this.addSkippedTestResult(test);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Add a test result for a skipped test (due to beforeEach failure)
|
|
175
|
+
* @param {Test} test
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
addSkippedTestResult(test) {
|
|
179
|
+
const end_time = Date.now();
|
|
180
|
+
const duration = 0; // Skipped tests have no duration
|
|
181
|
+
const start_time = this.testBeginTime || Date.now();
|
|
182
|
+
const ids = CypressQaseReporter.getCaseId(test.title);
|
|
183
|
+
const testFileName = this.getTestFileName(test);
|
|
184
|
+
const files = this.screenshotsFolder ?
|
|
185
|
+
fileSearcher_1.FileSearcher.findFilesBeforeTime(this.screenshotsFolder, testFileName, new Date(start_time))
|
|
186
|
+
: [];
|
|
187
|
+
const attachments = files.map((file) => ({
|
|
188
|
+
content: '',
|
|
189
|
+
id: (0, uuid_1.v4)(),
|
|
190
|
+
mime_type: 'image/png',
|
|
191
|
+
size: 0,
|
|
192
|
+
file_name: path_1.default.basename(file),
|
|
193
|
+
file_path: file,
|
|
194
|
+
}));
|
|
195
|
+
let relations = {};
|
|
196
|
+
if (test.parent !== undefined) {
|
|
197
|
+
const data = [];
|
|
198
|
+
for (const suite of test.parent.titlePath()) {
|
|
199
|
+
data.push({
|
|
200
|
+
title: suite,
|
|
201
|
+
public_id: null,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
relations = {
|
|
205
|
+
suite: {
|
|
206
|
+
data: data,
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// For skipped tests, we don't have metadata since the test never ran
|
|
211
|
+
// But we can still check for cucumber tags if the test has a parent with a file
|
|
212
|
+
if (test.parent) {
|
|
213
|
+
const file = this.getFile(test.parent);
|
|
214
|
+
if (file) {
|
|
215
|
+
const tags = (0, tagParser_1.extractTags)(file, test.title);
|
|
216
|
+
ids.push(...this.extractQaseIds(tags));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const result = {
|
|
220
|
+
attachments: attachments,
|
|
221
|
+
author: null,
|
|
222
|
+
fields: {},
|
|
223
|
+
message: null,
|
|
224
|
+
muted: false,
|
|
225
|
+
params: {},
|
|
226
|
+
group_params: {},
|
|
227
|
+
relations: relations,
|
|
228
|
+
run_id: null,
|
|
229
|
+
signature: this.getSignature(test, ids, {}),
|
|
230
|
+
steps: [],
|
|
231
|
+
id: (0, uuid_1.v4)(),
|
|
232
|
+
execution: {
|
|
233
|
+
status: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
234
|
+
start_time: this.testBeginTime / 1000,
|
|
235
|
+
end_time: end_time / 1000,
|
|
236
|
+
duration: duration,
|
|
237
|
+
stacktrace: null,
|
|
238
|
+
thread: null,
|
|
239
|
+
},
|
|
240
|
+
testops_id: ids.length > 0 ? ids : null,
|
|
241
|
+
title: this.removeQaseIdsFromTitle(test.title),
|
|
242
|
+
preparedAttachments: [],
|
|
243
|
+
};
|
|
244
|
+
void this.reporter.addTestResult(result);
|
|
245
|
+
// Mark as processed to avoid duplicate reporting
|
|
246
|
+
this.markTestAsProcessed(test);
|
|
247
|
+
}
|
|
107
248
|
/**
|
|
108
249
|
* @param {Test} test
|
|
109
250
|
* @private
|
|
@@ -113,6 +254,8 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
113
254
|
const duration = end_time - this.testBeginTime;
|
|
114
255
|
const metadata = manager_1.MetadataManager.getMetadata();
|
|
115
256
|
if (metadata?.ignore) {
|
|
257
|
+
// Mark as processed even if ignored to avoid duplicate reporting
|
|
258
|
+
this.markTestAsProcessed(test);
|
|
116
259
|
manager_1.MetadataManager.clear();
|
|
117
260
|
return;
|
|
118
261
|
}
|
|
@@ -198,7 +341,7 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
198
341
|
steps: steps,
|
|
199
342
|
id: (0, uuid_1.v4)(),
|
|
200
343
|
execution: {
|
|
201
|
-
status: (0, qase_javascript_commons_1.determineTestStatus)(test.err
|
|
344
|
+
status: (0, qase_javascript_commons_1.determineTestStatus)(test.err ?? null, test.state ?? 'failed'),
|
|
202
345
|
start_time: this.testBeginTime / 1000,
|
|
203
346
|
end_time: end_time / 1000,
|
|
204
347
|
duration: duration,
|
package/dist/utils/tagParser.js
CHANGED
|
@@ -15,15 +15,25 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.extractTags =
|
|
36
|
+
exports.extractTags = extractTags;
|
|
27
37
|
const fs = __importStar(require("fs"));
|
|
28
38
|
/**
|
|
29
39
|
* Extracts tags for a given scenario name from a Gherkin feature file.
|
|
@@ -58,4 +68,3 @@ function extractTags(filePath, scenarioName) {
|
|
|
58
68
|
}
|
|
59
69
|
return tags;
|
|
60
70
|
}
|
|
61
|
-
exports.extractTags = extractTags;
|
package/docs/cucumber.md
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# Cucumber/Gherkin Integration with Qase Reporter
|
|
2
|
+
|
|
3
|
+
This guide explains how to integrate Qase reporter with Cucumber/Gherkin tests in Cypress using either the legacy `cypress-cucumber-preprocessor` or the newer `@badeball/cypress-cucumber-preprocessor`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Choosing a Preprocessor](#choosing-a-preprocessor)
|
|
10
|
+
- [Configuration for @badeball/cypress-cucumber-preprocessor (Recommended)](#configuration-for-badeballcypress-cucumber-preprocessor-recommended)
|
|
11
|
+
- [Configuration for cypress-cucumber-preprocessor (Legacy)](#configuration-for-cypress-cucumber-preprocessor-legacy)
|
|
12
|
+
- [Cucumber Steps Reporting](#cucumber-steps-reporting)
|
|
13
|
+
- [Examples](#examples)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Choosing a Preprocessor
|
|
18
|
+
|
|
19
|
+
There are two Cucumber preprocessors available for Cypress:
|
|
20
|
+
|
|
21
|
+
- **`@badeball/cypress-cucumber-preprocessor`** (Recommended) - Actively maintained, modern implementation
|
|
22
|
+
- **`cypress-cucumber-preprocessor`** (Legacy) - Original implementation, now deprecated
|
|
23
|
+
|
|
24
|
+
This guide covers both, but we recommend using `@badeball/cypress-cucumber-preprocessor` for new projects.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Configuration for @badeball/cypress-cucumber-preprocessor (Recommended)
|
|
29
|
+
|
|
30
|
+
### Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -D @badeball/cypress-cucumber-preprocessor
|
|
34
|
+
npm install -D @bahmutov/cypress-esbuild-preprocessor
|
|
35
|
+
npm install -D cypress-qase-reporter
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Cypress Configuration
|
|
39
|
+
|
|
40
|
+
Update your `cypress.config.js`:
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
import cypress from 'cypress';
|
|
44
|
+
import { afterSpecHook } from 'cypress-qase-reporter/hooks';
|
|
45
|
+
const createBundler = require('@bahmutov/cypress-esbuild-preprocessor');
|
|
46
|
+
const addCucumberPreprocessorPlugin = require('@badeball/cypress-cucumber-preprocessor').addCucumberPreprocessorPlugin;
|
|
47
|
+
const createEsbuildPlugin = require('@badeball/cypress-cucumber-preprocessor/esbuild').createEsbuildPlugin;
|
|
48
|
+
|
|
49
|
+
module.exports = cypress.defineConfig({
|
|
50
|
+
reporter: 'cypress-multi-reporters',
|
|
51
|
+
reporterOptions: {
|
|
52
|
+
reporterEnabled: 'cypress-qase-reporter',
|
|
53
|
+
cypressQaseReporterReporterOptions: {
|
|
54
|
+
debug: true,
|
|
55
|
+
testops: {
|
|
56
|
+
api: {
|
|
57
|
+
token: 'api_token',
|
|
58
|
+
},
|
|
59
|
+
project: 'project_code',
|
|
60
|
+
uploadAttachments: true,
|
|
61
|
+
run: {
|
|
62
|
+
complete: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
framework: {
|
|
66
|
+
cypress: {
|
|
67
|
+
screenshotsFolder: 'cypress/screenshots',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
video: false,
|
|
73
|
+
e2e: {
|
|
74
|
+
specPattern: 'cypress/e2e/**/*.feature',
|
|
75
|
+
async setupNodeEvents(on, config) {
|
|
76
|
+
// 1. Set up the Cucumber preprocessor FIRST
|
|
77
|
+
await addCucumberPreprocessorPlugin(on, config);
|
|
78
|
+
on(
|
|
79
|
+
'file:preprocessor',
|
|
80
|
+
createBundler({
|
|
81
|
+
plugins: [createEsbuildPlugin(config)],
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// 2. Set up the Qase reporter plugin
|
|
86
|
+
require('cypress-qase-reporter/plugin')(on, config);
|
|
87
|
+
require('cypress-qase-reporter/metadata')(on);
|
|
88
|
+
|
|
89
|
+
// 3. Register the after:spec hook
|
|
90
|
+
on('after:spec', async (spec, results) => {
|
|
91
|
+
await afterSpecHook(spec, config);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return config;
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Cucumber Steps Reporting
|
|
101
|
+
|
|
102
|
+
To automatically capture and report Gherkin steps to Qase, create a hooks file.
|
|
103
|
+
|
|
104
|
+
Create `cypress/support/step_definitions/hooks.js`:
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
import { BeforeStep } from '@badeball/cypress-cucumber-preprocessor';
|
|
108
|
+
import { addCucumberStep } from 'cypress-qase-reporter/cucumber';
|
|
109
|
+
|
|
110
|
+
// Automatically report each Gherkin step to Qase
|
|
111
|
+
BeforeStep(function({ pickleStep }) {
|
|
112
|
+
const keyword = pickleStep.keyword || '';
|
|
113
|
+
const stepText = `${keyword}${pickleStep.text}`;
|
|
114
|
+
addCucumberStep(stepText);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This hook will automatically:
|
|
119
|
+
- Capture each Gherkin step (Given/When/Then/And/But)
|
|
120
|
+
- Report it to Qase with the correct execution status (passed/failed)
|
|
121
|
+
- Include step details in your test results
|
|
122
|
+
|
|
123
|
+
**Note:** Step reporting is optional. If you only need test-level results without individual step details, you can skip this configuration.
|
|
124
|
+
|
|
125
|
+
### Manual Step Reporting (Alternative)
|
|
126
|
+
|
|
127
|
+
If you prefer more control, you can manually add steps in your step definitions:
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor';
|
|
131
|
+
import { addCucumberStep } from 'cypress-qase-reporter/cucumber';
|
|
132
|
+
|
|
133
|
+
Given('I am on the homepage', () => {
|
|
134
|
+
addCucumberStep('Given I am on the homepage');
|
|
135
|
+
cy.visit('https://example.com');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
When('I click the button', () => {
|
|
139
|
+
addCucumberStep('When I click the button');
|
|
140
|
+
cy.get('button').click();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
Then('I should see the result', () => {
|
|
144
|
+
addCucumberStep('Then I should see the result');
|
|
145
|
+
cy.get('.result').should('be.visible');
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Configuration for cypress-cucumber-preprocessor (Legacy)
|
|
152
|
+
|
|
153
|
+
### Installation
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
npm install -D cypress-cucumber-preprocessor
|
|
157
|
+
npm install -D cypress-qase-reporter
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Cypress Configuration
|
|
161
|
+
|
|
162
|
+
Update your `cypress.config.js`:
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
import cypress from 'cypress';
|
|
166
|
+
import { afterSpecHook } from 'cypress-qase-reporter/hooks';
|
|
167
|
+
|
|
168
|
+
module.exports = cypress.defineConfig({
|
|
169
|
+
reporter: 'cypress-multi-reporters',
|
|
170
|
+
reporterOptions: {
|
|
171
|
+
reporterEnabled: 'cypress-qase-reporter',
|
|
172
|
+
cypressQaseReporterReporterOptions: {
|
|
173
|
+
debug: true,
|
|
174
|
+
testops: {
|
|
175
|
+
api: {
|
|
176
|
+
token: 'api_token',
|
|
177
|
+
},
|
|
178
|
+
project: 'project_code',
|
|
179
|
+
uploadAttachments: true,
|
|
180
|
+
run: {
|
|
181
|
+
complete: true,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
framework: {
|
|
185
|
+
cypress: {
|
|
186
|
+
screenshotsFolder: 'cypress/screenshots',
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
video: false,
|
|
192
|
+
e2e: {
|
|
193
|
+
specPattern: '**/*.feature',
|
|
194
|
+
async setupNodeEvents(on, config) {
|
|
195
|
+
require('cypress-qase-reporter/plugin')(on, config);
|
|
196
|
+
require('cypress-qase-reporter/metadata')(on);
|
|
197
|
+
|
|
198
|
+
on('after:spec', async (spec, results) => {
|
|
199
|
+
await afterSpecHook(spec, config);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return config;
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Cucumber Steps Reporting
|
|
209
|
+
|
|
210
|
+
For the legacy preprocessor, add the following to your `cypress/support/e2e.js` file:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import { enableCucumberSupport } from 'cypress-qase-reporter/cucumber';
|
|
214
|
+
|
|
215
|
+
enableCucumberSupport();
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
This will automatically capture all Gherkin steps and report them to Qase. No additional configuration is needed.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Cucumber Steps Reporting
|
|
223
|
+
|
|
224
|
+
### What Gets Reported
|
|
225
|
+
|
|
226
|
+
When you enable Cucumber steps reporting, the following information is sent to Qase for each step:
|
|
227
|
+
|
|
228
|
+
- **Step text** - The full Gherkin step (e.g., "Given I am on the homepage")
|
|
229
|
+
- **Step status** - Whether the step passed or failed
|
|
230
|
+
- **Step timing** - When the step started execution
|
|
231
|
+
- **Step attachments** - Any screenshots or files attached during the step
|
|
232
|
+
|
|
233
|
+
### Example Feature File
|
|
234
|
+
|
|
235
|
+
```gherkin
|
|
236
|
+
Feature: User Login
|
|
237
|
+
|
|
238
|
+
@QaseID(1)
|
|
239
|
+
Scenario: Successful login
|
|
240
|
+
Given I am on the login page
|
|
241
|
+
When I enter valid credentials
|
|
242
|
+
And I click the login button
|
|
243
|
+
Then I should see the dashboard
|
|
244
|
+
And I should see my username
|
|
245
|
+
|
|
246
|
+
@QaseID(2)
|
|
247
|
+
Scenario: Failed login
|
|
248
|
+
Given I am on the login page
|
|
249
|
+
When I enter invalid credentials
|
|
250
|
+
And I click the login button
|
|
251
|
+
Then I should see an error message
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Adding Qase IDs
|
|
255
|
+
|
|
256
|
+
You can associate Qase test case IDs with your scenarios using tags:
|
|
257
|
+
|
|
258
|
+
- Single ID: `@QaseID(123)`
|
|
259
|
+
- Multiple IDs: `@QaseID(123,456)`
|
|
260
|
+
|
|
261
|
+
The Qase reporter will automatically extract these IDs and link the test results to the corresponding test cases in Qase.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Examples
|
|
266
|
+
|
|
267
|
+
### Complete Working Examples
|
|
268
|
+
|
|
269
|
+
Check out our example projects:
|
|
270
|
+
|
|
271
|
+
- **[@badeball/cypress-cucumber-preprocessor Example](../../examples/cypressBadeballCucumber/)** - Modern implementation with automatic step reporting
|
|
272
|
+
- **[cypress-cucumber-preprocessor Example](../../examples/cypressCucumber/)** - Legacy implementation
|
|
273
|
+
|
|
274
|
+
Each example includes:
|
|
275
|
+
- Complete Cypress configuration
|
|
276
|
+
- Feature files with Gherkin scenarios
|
|
277
|
+
- Step definitions
|
|
278
|
+
- Qase reporter integration
|
|
279
|
+
- Automatic step reporting setup
|
|
280
|
+
|
|
281
|
+
### Running the Examples
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Navigate to the example directory
|
|
285
|
+
cd examples/cypressBadeballCucumber
|
|
286
|
+
|
|
287
|
+
# Install dependencies
|
|
288
|
+
npm install
|
|
289
|
+
|
|
290
|
+
# Set your Qase credentials in cypress.config.js
|
|
291
|
+
|
|
292
|
+
# Run tests
|
|
293
|
+
npm test
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Troubleshooting
|
|
299
|
+
|
|
300
|
+
### Steps are not being reported
|
|
301
|
+
|
|
302
|
+
**For @badeball/cypress-cucumber-preprocessor:**
|
|
303
|
+
- Ensure you've created the `hooks.js` file with the `BeforeStep` hook
|
|
304
|
+
- Verify the file is in `cypress/support/step_definitions/` directory
|
|
305
|
+
- Check that you're importing `addCucumberStep` from `'cypress-qase-reporter/cucumber'`
|
|
306
|
+
|
|
307
|
+
**For cypress-cucumber-preprocessor (legacy):**
|
|
308
|
+
- Verify you've called `enableCucumberSupport()` in `cypress/support/e2e.js`
|
|
309
|
+
- Ensure you're importing from `'cypress-qase-reporter/cucumber'`
|
|
310
|
+
|
|
311
|
+
### Cypress command queue errors
|
|
312
|
+
|
|
313
|
+
If you see errors like "Cypress detected that you returned a promise from a command", make sure you're using the provided `addCucumberStep` function, which properly handles the Cypress command queue.
|
|
314
|
+
|
|
315
|
+
### Test results not appearing in Qase
|
|
316
|
+
|
|
317
|
+
- Verify your API token is correct in `cypress.config.js`
|
|
318
|
+
- Check that your project code matches your Qase project
|
|
319
|
+
- Enable `debug: true` in reporter options to see detailed logs
|
|
320
|
+
- Ensure `mode: 'testops'` is set in your configuration
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Additional Resources
|
|
325
|
+
|
|
326
|
+
- [Qase Reporter Configuration](../README.md#configuration)
|
|
327
|
+
- [Qase Reporter Usage Guide](./usage.md)
|
|
328
|
+
- [@badeball/cypress-cucumber-preprocessor Documentation](https://github.com/badeball/cypress-cucumber-preprocessor)
|
|
329
|
+
- [Cypress Documentation](https://docs.cypress.io/)
|
package/docs/usage.md
CHANGED
|
@@ -194,3 +194,65 @@ it('test', () => {
|
|
|
194
194
|
cy.visit('https://example.com');
|
|
195
195
|
});
|
|
196
196
|
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Cucumber Steps Reporting
|
|
201
|
+
|
|
202
|
+
### For @badeball/cypress-cucumber-preprocessor
|
|
203
|
+
|
|
204
|
+
When using `@badeball/cypress-cucumber-preprocessor`, you can automatically report Cucumber/Gherkin steps to Qase using the `addCucumberStep` function in a `BeforeStep` hook.
|
|
205
|
+
|
|
206
|
+
#### Automatic Step Reporting
|
|
207
|
+
|
|
208
|
+
Create a file `cypress/support/step_definitions/hooks.js`:
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
import { BeforeStep } from '@badeball/cypress-cucumber-preprocessor';
|
|
212
|
+
import { addCucumberStep } from 'cypress-qase-reporter/cucumber';
|
|
213
|
+
|
|
214
|
+
// Automatically report each Gherkin step to Qase
|
|
215
|
+
BeforeStep(function({ pickleStep }) {
|
|
216
|
+
const keyword = pickleStep.keyword || '';
|
|
217
|
+
const stepText = `${keyword}${pickleStep.text}`;
|
|
218
|
+
addCucumberStep(stepText);
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
This will automatically capture each step (Given/When/Then/And/But) and report it to Qase with its execution status.
|
|
223
|
+
|
|
224
|
+
#### Manual Step Reporting
|
|
225
|
+
|
|
226
|
+
Alternatively, you can manually add steps in your step definitions:
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor';
|
|
230
|
+
import { addCucumberStep } from 'cypress-qase-reporter/cucumber';
|
|
231
|
+
|
|
232
|
+
Given('I am on the homepage', () => {
|
|
233
|
+
addCucumberStep('Given I am on the homepage');
|
|
234
|
+
cy.visit('https://example.com');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
When('I click the button', () => {
|
|
238
|
+
addCucumberStep('When I click the button');
|
|
239
|
+
cy.get('button').click();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
Then('I should see the result', () => {
|
|
243
|
+
addCucumberStep('Then I should see the result');
|
|
244
|
+
cy.get('.result').should('be.visible');
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### For cypress-cucumber-preprocessor (legacy)
|
|
249
|
+
|
|
250
|
+
For the legacy `cypress-cucumber-preprocessor`, steps are automatically captured. Add the following to your `support/e2e.js` file:
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
import { enableCucumberSupport } from 'cypress-qase-reporter/cucumber';
|
|
254
|
+
|
|
255
|
+
enableCucumberSupport();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
No additional configuration is needed - all Gherkin steps will be automatically reported to Qase.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cypress-qase-reporter",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Qase Cypress Reporter",
|
|
5
5
|
"homepage": "https://github.com/qase-tms/qase-javascript",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -51,19 +51,19 @@
|
|
|
51
51
|
"author": "Qase Team <support@qase.io>",
|
|
52
52
|
"license": "Apache-2.0",
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"qase-javascript-commons": "~2.4.
|
|
54
|
+
"qase-javascript-commons": "~2.4.13",
|
|
55
55
|
"uuid": "^9.0.1"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"cypress": ">=8.0.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@jest/globals": "^29.
|
|
62
|
-
"@types/jest": "^29.5.
|
|
63
|
-
"@types/mocha": "^10.0.
|
|
64
|
-
"ajv": "^8.
|
|
65
|
-
"jest": "^29.
|
|
66
|
-
"mocha": "^10.2
|
|
67
|
-
"ts-jest": "^29.
|
|
61
|
+
"@jest/globals": "^29.7.0",
|
|
62
|
+
"@types/jest": "^29.5.14",
|
|
63
|
+
"@types/mocha": "^10.0.10",
|
|
64
|
+
"ajv": "^8.17.1",
|
|
65
|
+
"jest": "^29.7.0",
|
|
66
|
+
"mocha": "^10.8.2",
|
|
67
|
+
"ts-jest": "^29.4.5"
|
|
68
68
|
}
|
|
69
69
|
}
|