cypress-qase-reporter 3.1.2 → 3.2.0
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 +6 -0
- package/changelog.md +6 -0
- package/dist/mocha.d.ts +3 -0
- package/dist/mocha.js +19 -0
- package/dist/reporter.d.ts +1 -12
- package/dist/reporter.js +31 -38
- package/docs/MULTI_PROJECT.md +107 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -259,6 +259,12 @@ module.exports = cypress.defineConfig({
|
|
|
259
259
|
Check out the example of configuration for multiple reporters in the
|
|
260
260
|
[demo project](../examples/cypress/cypress.config.js).
|
|
261
261
|
|
|
262
|
+
### Multi-Project Support
|
|
263
|
+
|
|
264
|
+
Qase Cypress Reporter supports sending test results to multiple Qase projects simultaneously. You can specify different test case IDs for each project using `qase.projects(mapping, it(...))`.
|
|
265
|
+
|
|
266
|
+
For detailed information, configuration, and examples, see the [Multi-Project Support Guide](docs/MULTI_PROJECT.md).
|
|
267
|
+
|
|
262
268
|
## Cucumber/Gherkin Integration
|
|
263
269
|
|
|
264
270
|
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`.
|
package/changelog.md
CHANGED
package/dist/mocha.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Test } from 'mocha';
|
|
2
|
+
/** Project code → test case IDs for multi-project (testops_multi) mode. */
|
|
3
|
+
export type ProjectMapping = Record<string, number[]>;
|
|
2
4
|
export declare const qase: {
|
|
3
5
|
(caseId: number | string | number[] | string[], test: Test): Test;
|
|
6
|
+
projects(mapping: ProjectMapping, nameOrTest: string | Test): string | Test;
|
|
4
7
|
title(value: string): Cypress.Chainable<JQuery<void>>;
|
|
5
8
|
fields(values: Record<string, string>): Cypress.Chainable<JQuery<void>>;
|
|
6
9
|
ignore(): Cypress.Chainable<JQuery<void>>;
|
package/dist/mocha.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.qase = void 0;
|
|
4
|
+
const qase_javascript_commons_1 = require("qase-javascript-commons");
|
|
4
5
|
const qase = (caseId, test) => {
|
|
5
6
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
6
7
|
if (!test?.title) {
|
|
@@ -11,6 +12,24 @@ const qase = (caseId, test) => {
|
|
|
11
12
|
return test;
|
|
12
13
|
};
|
|
13
14
|
exports.qase = qase;
|
|
15
|
+
/**
|
|
16
|
+
* Build test title with multi-project markers (for testops_multi mode).
|
|
17
|
+
* Supports two usages:
|
|
18
|
+
* - it(qase.projects({ PROJ1: [100], PROJ2: [200] }, 'Login flow'), () => { ... }) — returns formatted title string.
|
|
19
|
+
* - qase.projects({ PROJ1: [100], PROJ2: [200] }, it('Login flow', () => { ... })) — mutates test.title and returns the test.
|
|
20
|
+
* @param mapping — e.g. { PROJ1: [1, 2], PROJ2: [3] }
|
|
21
|
+
* @param nameOrTest — test title string or Mocha Test (when passing it(...)); the test title is updated with markers so the reporter can parse testops_project_mapping.
|
|
22
|
+
*/
|
|
23
|
+
function isTestObject(nameOrTest) {
|
|
24
|
+
return Boolean(nameOrTest && typeof nameOrTest === 'object' && 'title' in nameOrTest && typeof nameOrTest.title === 'string');
|
|
25
|
+
}
|
|
26
|
+
exports.qase.projects = (mapping, nameOrTest) => {
|
|
27
|
+
if (isTestObject(nameOrTest)) {
|
|
28
|
+
nameOrTest.title = (0, qase_javascript_commons_1.formatTitleWithProjectMapping)(nameOrTest.title, mapping);
|
|
29
|
+
return nameOrTest;
|
|
30
|
+
}
|
|
31
|
+
return (0, qase_javascript_commons_1.formatTitleWithProjectMapping)(String(nameOrTest), mapping);
|
|
32
|
+
};
|
|
14
33
|
/**
|
|
15
34
|
* Set a title for the test case
|
|
16
35
|
* @param {string} value
|
package/dist/reporter.d.ts
CHANGED
|
@@ -13,17 +13,12 @@ export declare class CypressQaseReporter extends reporters.Base {
|
|
|
13
13
|
/**
|
|
14
14
|
* @type {RegExp}
|
|
15
15
|
*/
|
|
16
|
+
/** @deprecated Use parseProjectMappingFromTitle from qase-javascript-commons for multi-project support. */
|
|
16
17
|
static qaseIdRegExp: RegExp;
|
|
17
18
|
/**
|
|
18
19
|
* @type {Record<CypressState, TestStatusEnum>}
|
|
19
20
|
*/
|
|
20
21
|
static statusMap: Record<CypressState, TestStatusEnum>;
|
|
21
|
-
/**
|
|
22
|
-
* @param {string} title
|
|
23
|
-
* @returns {number[]}
|
|
24
|
-
* @private
|
|
25
|
-
*/
|
|
26
|
-
private static getCaseId;
|
|
27
22
|
/**
|
|
28
23
|
* @type {string | undefined}
|
|
29
24
|
* @private
|
|
@@ -114,12 +109,6 @@ export declare class CypressQaseReporter extends reporters.Base {
|
|
|
114
109
|
* @private
|
|
115
110
|
*/
|
|
116
111
|
private removeQaseIdsFromTitle;
|
|
117
|
-
/**
|
|
118
|
-
* Extracts numbers from @qaseid tags, regardless of case.
|
|
119
|
-
* @param tags - An array of tags to process.
|
|
120
|
-
* @returns An array of numbers extracted from the tags.
|
|
121
|
-
*/
|
|
122
|
-
private extractQaseIds;
|
|
123
112
|
private convertCypressMessages;
|
|
124
113
|
private getSteps;
|
|
125
114
|
}
|
package/dist/reporter.js
CHANGED
|
@@ -23,6 +23,7 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
23
23
|
/**
|
|
24
24
|
* @type {RegExp}
|
|
25
25
|
*/
|
|
26
|
+
/** @deprecated Use parseProjectMappingFromTitle from qase-javascript-commons for multi-project support. */
|
|
26
27
|
static qaseIdRegExp = /\(Qase ID:? ([\d,]+)\)/;
|
|
27
28
|
/**
|
|
28
29
|
* @type {Record<CypressState, TestStatusEnum>}
|
|
@@ -32,15 +33,6 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
32
33
|
passed: qase_javascript_commons_1.TestStatusEnum.passed,
|
|
33
34
|
pending: qase_javascript_commons_1.TestStatusEnum.skipped,
|
|
34
35
|
};
|
|
35
|
-
/**
|
|
36
|
-
* @param {string} title
|
|
37
|
-
* @returns {number[]}
|
|
38
|
-
* @private
|
|
39
|
-
*/
|
|
40
|
-
static getCaseId(title) {
|
|
41
|
-
const [, ids] = title.match(CypressQaseReporter.qaseIdRegExp) ?? [];
|
|
42
|
-
return ids ? ids.split(',').map((id) => Number(id)) : [];
|
|
43
|
-
}
|
|
44
36
|
/**
|
|
45
37
|
* @type {string | undefined}
|
|
46
38
|
* @private
|
|
@@ -196,7 +188,9 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
196
188
|
const end_time = Date.now();
|
|
197
189
|
const duration = 0; // Skipped tests have no duration
|
|
198
190
|
const start_time = this.testBeginTime || Date.now();
|
|
199
|
-
const
|
|
191
|
+
const fromTitle = (0, qase_javascript_commons_1.parseProjectMappingFromTitle)(test.title);
|
|
192
|
+
const legacyIds = [...fromTitle.legacyIds];
|
|
193
|
+
const projectMapping = { ...fromTitle.projectMapping };
|
|
200
194
|
const testFileName = this.getTestFileName(test);
|
|
201
195
|
const files = this.screenshotsFolder ?
|
|
202
196
|
fileSearcher_1.FileSearcher.findFilesBeforeTime(this.screenshotsFolder, testFileName, new Date(start_time))
|
|
@@ -230,9 +224,14 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
230
224
|
const file = this.getFile(test.parent);
|
|
231
225
|
if (file) {
|
|
232
226
|
const tags = (0, tagParser_1.extractTags)(file, test.title);
|
|
233
|
-
|
|
227
|
+
const fromTags = (0, qase_javascript_commons_1.parseProjectMappingFromTags)(tags);
|
|
228
|
+
legacyIds.push(...fromTags.legacyIds);
|
|
229
|
+
for (const [code, idsFromTag] of Object.entries(fromTags.projectMapping)) {
|
|
230
|
+
projectMapping[code] = [...(projectMapping[code] ?? []), ...idsFromTag];
|
|
231
|
+
}
|
|
234
232
|
}
|
|
235
233
|
}
|
|
234
|
+
const hasProjectMapping = Object.keys(projectMapping).length > 0;
|
|
236
235
|
const result = {
|
|
237
236
|
attachments: attachments,
|
|
238
237
|
author: null,
|
|
@@ -243,7 +242,7 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
243
242
|
group_params: {},
|
|
244
243
|
relations: relations,
|
|
245
244
|
run_id: null,
|
|
246
|
-
signature: this.getSignature(test,
|
|
245
|
+
signature: this.getSignature(test, hasProjectMapping ? [] : legacyIds, {}),
|
|
247
246
|
steps: [],
|
|
248
247
|
id: (0, uuid_1.v4)(),
|
|
249
248
|
execution: {
|
|
@@ -254,8 +253,11 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
254
253
|
stacktrace: null,
|
|
255
254
|
thread: null,
|
|
256
255
|
},
|
|
257
|
-
testops_id:
|
|
258
|
-
|
|
256
|
+
testops_id: !hasProjectMapping && legacyIds.length > 0
|
|
257
|
+
? (legacyIds.length === 1 ? legacyIds[0] : legacyIds)
|
|
258
|
+
: null,
|
|
259
|
+
testops_project_mapping: hasProjectMapping ? projectMapping : null,
|
|
260
|
+
title: fromTitle.cleanedTitle || this.removeQaseIdsFromTitle(test.title),
|
|
259
261
|
preparedAttachments: [],
|
|
260
262
|
};
|
|
261
263
|
void this.reporter.addTestResult(result);
|
|
@@ -276,7 +278,9 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
276
278
|
manager_1.MetadataManager.clear();
|
|
277
279
|
return;
|
|
278
280
|
}
|
|
279
|
-
const
|
|
281
|
+
const fromTitle = (0, qase_javascript_commons_1.parseProjectMappingFromTitle)(test.title);
|
|
282
|
+
const legacyIds = [...fromTitle.legacyIds];
|
|
283
|
+
const projectMapping = { ...fromTitle.projectMapping };
|
|
280
284
|
const testFileName = this.getTestFileName(test);
|
|
281
285
|
const files = this.screenshotsFolder ?
|
|
282
286
|
fileSearcher_1.FileSearcher.findFilesBeforeTime(this.screenshotsFolder, testFileName, new Date(this.testBeginTime))
|
|
@@ -340,10 +344,15 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
340
344
|
const file = this.getFile(test.parent);
|
|
341
345
|
if (file) {
|
|
342
346
|
const tags = (0, tagParser_1.extractTags)(file, test.title);
|
|
343
|
-
|
|
347
|
+
const fromTags = (0, qase_javascript_commons_1.parseProjectMappingFromTags)(tags);
|
|
348
|
+
legacyIds.push(...fromTags.legacyIds);
|
|
349
|
+
for (const [code, idsFromTag] of Object.entries(fromTags.projectMapping)) {
|
|
350
|
+
projectMapping[code] = [...(projectMapping[code] ?? []), ...idsFromTag];
|
|
351
|
+
}
|
|
344
352
|
}
|
|
345
353
|
}
|
|
346
354
|
}
|
|
355
|
+
const hasProjectMapping = Object.keys(projectMapping).length > 0;
|
|
347
356
|
const result = {
|
|
348
357
|
attachments: attachments,
|
|
349
358
|
author: null,
|
|
@@ -354,7 +363,7 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
354
363
|
group_params: metadata?.groupParams ?? {},
|
|
355
364
|
relations: relations,
|
|
356
365
|
run_id: null,
|
|
357
|
-
signature: this.getSignature(test,
|
|
366
|
+
signature: this.getSignature(test, hasProjectMapping ? [] : legacyIds, metadata?.parameters ?? {}),
|
|
358
367
|
steps: steps,
|
|
359
368
|
id: (0, uuid_1.v4)(),
|
|
360
369
|
execution: {
|
|
@@ -365,8 +374,11 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
365
374
|
stacktrace: test.err?.stack ?? null,
|
|
366
375
|
thread: null,
|
|
367
376
|
},
|
|
368
|
-
testops_id:
|
|
369
|
-
|
|
377
|
+
testops_id: !hasProjectMapping && legacyIds.length > 0
|
|
378
|
+
? (legacyIds.length === 1 ? legacyIds[0] : legacyIds)
|
|
379
|
+
: null,
|
|
380
|
+
testops_project_mapping: hasProjectMapping ? projectMapping : null,
|
|
381
|
+
title: metadata?.title ?? (fromTitle.cleanedTitle || this.removeQaseIdsFromTitle(test.title)),
|
|
370
382
|
preparedAttachments: [],
|
|
371
383
|
};
|
|
372
384
|
void this.reporter.addTestResult(result);
|
|
@@ -428,25 +440,6 @@ class CypressQaseReporter extends mocha_1.reporters.Base {
|
|
|
428
440
|
}
|
|
429
441
|
return title;
|
|
430
442
|
}
|
|
431
|
-
/**
|
|
432
|
-
* Extracts numbers from @qaseid tags, regardless of case.
|
|
433
|
-
* @param tags - An array of tags to process.
|
|
434
|
-
* @returns An array of numbers extracted from the tags.
|
|
435
|
-
*/
|
|
436
|
-
extractQaseIds(tags) {
|
|
437
|
-
const qaseIdRegex = /@qaseid\((\d+(?:,\d+)*)\)/i;
|
|
438
|
-
const qaseIds = [];
|
|
439
|
-
for (const tag of tags) {
|
|
440
|
-
const match = qaseIdRegex.exec(tag);
|
|
441
|
-
if (match) {
|
|
442
|
-
const ids = match[1]?.split(',').map(id => parseInt(id, 10));
|
|
443
|
-
if (ids) {
|
|
444
|
-
qaseIds.push(...ids);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
return qaseIds;
|
|
449
|
-
}
|
|
450
443
|
convertCypressMessages(messages, testStatus) {
|
|
451
444
|
const result = [];
|
|
452
445
|
const lastIndex = messages.length - 1;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Multi-Project Support in Cypress
|
|
2
|
+
|
|
3
|
+
Qase Cypress Reporter supports sending test results to multiple Qase projects simultaneously. This feature allows you to report the same test execution to different projects with different test case IDs, which is useful when:
|
|
4
|
+
|
|
5
|
+
* You need to report the same test to different projects
|
|
6
|
+
* Different projects track the same functionality with different test case IDs
|
|
7
|
+
* You want to maintain separate test runs for different environments or teams
|
|
8
|
+
|
|
9
|
+
## Configuration
|
|
10
|
+
|
|
11
|
+
For detailed configuration options, refer to the [qase-javascript-commons README](../../qase-javascript-commons/README.md#multi-project-support).
|
|
12
|
+
|
|
13
|
+
### Basic Multi-Project Configuration
|
|
14
|
+
|
|
15
|
+
To enable multi-project support, set the mode to `testops_multi` in your Cypress reporter options (e.g. in `cypress.config.js` or `qase.config.json`):
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mode": "testops_multi",
|
|
20
|
+
"testops": {
|
|
21
|
+
"api": { "token": "<token>", "host": "qase.io" },
|
|
22
|
+
"batch": { "size": 100 }
|
|
23
|
+
},
|
|
24
|
+
"testops_multi": {
|
|
25
|
+
"default_project": "PROJ1",
|
|
26
|
+
"projects": [
|
|
27
|
+
{
|
|
28
|
+
"code": "PROJ1",
|
|
29
|
+
"run": { "title": "PROJ1 Cypress Run", "complete": true }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"code": "PROJ2",
|
|
33
|
+
"run": { "title": "PROJ2 Cypress Run", "complete": true }
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Using `qase.projects()`
|
|
41
|
+
|
|
42
|
+
The `qase.projects(mapping, nameOrTest)` helper lets you map a test to one or more projects and case IDs.
|
|
43
|
+
|
|
44
|
+
### Pass the test (recommended)
|
|
45
|
+
|
|
46
|
+
Pass the result of `it()` as the second argument so the test title is updated with markers; the reporter will parse them and set `testops_project_mapping`:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const { qase } = require('cypress-qase-reporter');
|
|
50
|
+
|
|
51
|
+
describe('Suite', () => {
|
|
52
|
+
qase.projects({ PROJ1: [1], PROJ2: [2] }, it('A test reported to two projects', () => {
|
|
53
|
+
cy.visit('https://example.cypress.io');
|
|
54
|
+
cy.contains('type').click();
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
qase.projects(
|
|
58
|
+
{ PROJ1: [10, 11], PROJ2: [20] },
|
|
59
|
+
it('Multiple cases per project', () => {
|
|
60
|
+
cy.visit('https://example.cypress.io');
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Pass the title string
|
|
67
|
+
|
|
68
|
+
You can also pass a title string; the helper returns the formatted title for use as the first argument to `it()`:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
it(qase.projects({ PROJ1: [100], PROJ2: [200] }, 'Login flow'), () => {
|
|
72
|
+
cy.visit('/login');
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Combining with other Qase methods
|
|
77
|
+
|
|
78
|
+
Use `qase.projects()` together with other Qase methods (e.g. `qase.title()`, `qase.attach()`) inside the test.
|
|
79
|
+
|
|
80
|
+
## Tests Without Project Mapping
|
|
81
|
+
|
|
82
|
+
If a test does not use `qase.projects()` and has no `(Qase PROJ: ids)` markers in the title, it is sent to the `default_project` from your configuration. If the test also has no `(Qase ID: …)` legacy ID, the result is sent to the default project without linking to a test case.
|
|
83
|
+
|
|
84
|
+
## Important Notes
|
|
85
|
+
|
|
86
|
+
1. **Project codes must match**: The project codes in `qase.projects({ PROJ1: [1], ... })` must exactly match the codes in `testops_multi.projects[].code`.
|
|
87
|
+
|
|
88
|
+
2. **Mode requirement**: Set `mode` to `testops_multi` in your reporter config. Single-project mode (`testops`) does not use project mapping.
|
|
89
|
+
|
|
90
|
+
3. **Cucumber/BDD**: When using Cypress with Cucumber (e.g. `@badeball/cypress-cucumber-preprocessor`), use tags in feature files: `@qaseid.PROJ1(1) @qaseid.PROJ2(2)`. See [Cucumber documentation](cucumber.md).
|
|
91
|
+
|
|
92
|
+
## Examples
|
|
93
|
+
|
|
94
|
+
See the [multi-project Cypress example](../../examples/multiProject/cypress/) for a complete runnable setup.
|
|
95
|
+
|
|
96
|
+
## Troubleshooting
|
|
97
|
+
|
|
98
|
+
### Results not appearing in projects
|
|
99
|
+
|
|
100
|
+
* Ensure `mode` is `testops_multi` in reporter options.
|
|
101
|
+
* Verify project codes in `qase.projects()` match `testops_multi.projects[].code`.
|
|
102
|
+
* For Cypress, ensure you pass the test to `qase.projects(mapping, it(...))` so the title is updated with markers, or use a title that already contains `(Qase PROJ: ids)`.
|
|
103
|
+
|
|
104
|
+
### Results sent to wrong project
|
|
105
|
+
|
|
106
|
+
* Check the `default_project` setting for tests without explicit mapping.
|
|
107
|
+
* Project codes are case-sensitive and must match exactly.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cypress-qase-reporter",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Qase Cypress Reporter",
|
|
5
5
|
"homepage": "https://github.com/qase-tms/qase-javascript",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"author": "Qase Team <support@qase.io>",
|
|
52
52
|
"license": "Apache-2.0",
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"qase-javascript-commons": "~2.
|
|
54
|
+
"qase-javascript-commons": "~2.5.0",
|
|
55
55
|
"uuid": "^9.0.1"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|