cypress-qase-reporter 2.1.0-beta.1 → 2.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,57 +2,70 @@
2
2
 
3
3
  Publish results simple and easy.
4
4
 
5
- To install the latest version, run:
5
+ ## Installation
6
+
7
+ To install the latest release version (2.0.x), run:
6
8
 
7
9
  ```sh
8
10
  npm install -D cypress-qase-reporter
9
11
  ```
10
12
 
13
+ <!-- if there's no current beta, comment the next block
14
+ -->
15
+
16
+ To install the latest beta version (2.1.x), run:
17
+
18
+ ```sh
19
+ npm install -D cypress-qase-reporter@beta
20
+ ```
21
+
11
22
  ## Updating from v1 to v2.1
12
23
 
13
- You can update a test project from using version 1 to version 2.1 in several steps:
24
+ To update an existing test project using Qase reporter from version 1 to version 2.1,
25
+ run the following steps:
14
26
 
15
- 1. Change import paths:
27
+ 1. Change import paths in your test files:
16
28
 
17
29
  ```diff
18
30
  - import { qase } from 'cypress-qase-reporter/dist/mocha'
19
31
  + import { qase } from 'cypress-qase-reporter/mocha'
20
- ```
32
+ ```
33
+
21
34
  2. Update reporter configuration in `cypress.config.js` and/or environment variables —
22
35
  see the [configuration reference](#configuration) below.
23
36
 
24
- 3. Set the hooks in the `e2e` section in `cypress.config.js`:
37
+ 3. Add a hook in the `e2e` section of `cypress.config.js`:
25
38
 
26
39
  ```diff
27
- ...
28
- e2e: {
29
- setupNodeEvents(on, config) {
30
- + require('cypress-qase-reporter/plugin')(on, config);
31
- }
32
- }
33
- ...
40
+ ...
41
+ e2e: {
42
+ + setupNodeEvents(on, config) {
43
+ + require('cypress-qase-reporter/plugin')(on, config)
44
+ }
45
+ }
46
+ ...
34
47
  ```
35
48
 
36
- If you are override before:run or after:run hooks, use this:
49
+ If you already override `before:run` or `after:run` hooks in your `cypress.config.js`, use this instead:
37
50
 
38
51
  ```diff
39
52
  const { beforeRunHook, afterRunHook } = require('cypress-qase-reporter/hooks');
40
53
 
41
54
  ...
42
- e2e: {
55
+ e2e: {
43
56
  setupNodeEvents(on, config) {
44
- + on('before:run', async () => {
45
- + console.log('override before:run');
46
- + await beforeRunHook(config);
47
- + });
48
-
49
- + on('after:run', async () => {
50
- + console.log('override after:run');
51
- + await afterRunHook(config);
52
- + });
57
+ + on('before:run', async () => {
58
+ + console.log('override before:run');
59
+ + await beforeRunHook(config);
60
+ + });
61
+
62
+ + on('after:run', async () => {
63
+ + console.log('override after:run');
64
+ + await afterRunHook(config);
65
+ + });
53
66
  },
54
- },
55
- ...
67
+ },
68
+ ...
56
69
  ```
57
70
 
58
71
  ## Getting started
package/changelog.md ADDED
@@ -0,0 +1,68 @@
1
+ # cypress-qase-reporter@2.1.0-beta.3
2
+
3
+ ## What's new
4
+
5
+ Fixed an issue with the reporter completing the test run after each file with tests.
6
+
7
+ # cypress-qase-reporter@2.1.0-beta.1
8
+
9
+ ## What's new
10
+
11
+ - fixed an issue with the reporter completing the test run before all results are sent to Qase
12
+ - fixed an issue with the reporter not sending all results to Qase
13
+
14
+ Need to add the following to the `cypress.config.js` file:
15
+
16
+ ```diff
17
+ ...
18
+ e2e: {
19
+ setupNodeEvents(on, config) {
20
+ + require('cypress-qase-reporter/plugin')(on, config);
21
+ }
22
+ }
23
+ ...
24
+ ```
25
+
26
+ # cypress-qase-reporter@2.0.3
27
+
28
+ ## What's new
29
+
30
+ Cypress doesn't finish the process after the last test.
31
+ The reporter will wait for all results to be sent to Qase and will not block the process after sending.
32
+
33
+ # cypress-qase-reporter@2.0.2
34
+
35
+ ## What's new
36
+
37
+ 1. Cypress kills the process after the last tests.
38
+ The reporter will wait for all results to be sent to Qase and will not block the process after sending.
39
+
40
+ 2. The reporter will collect suites and add them to results.
41
+
42
+ # cypress-qase-reporter@2.0.1
43
+
44
+ ## What's new
45
+
46
+ The reporter would mark the test as blocked if the test was skipped in Cypress.
47
+ Now, the reporter will mark the test as skipped.
48
+
49
+ # cypress-qase-reporter@2.0.0
50
+
51
+ ## What's new
52
+
53
+ This is the first release in the 2.x series of the Cypress reporter.
54
+ It brings new and more flexible configs, uploading results in parallel with running tests,
55
+ and other powerful features.
56
+
57
+ This changelog entry will be updated soon.
58
+ For more information about the new features and a guide for migration from v1, refer to the
59
+ [reporter documentation](https://github.com/qase-tms/qase-javascript/tree/main/qase-cypress#readme)
60
+
61
+ # cypress-qase-reporter@2.0.0-beta.3
62
+
63
+ Fixed an issue with multiple test runs created when Cypress is running
64
+ multiple tests in parallel.
65
+
66
+ # cypress-qase-reporter@2.0.0-beta.2
67
+
68
+ First major beta release for the version 2 series of the Qase Cypress reporter.
@@ -0,0 +1 @@
1
+ export {};
package/dist/child.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const qase_javascript_commons_1 = require("qase-javascript-commons");
4
+ const options = JSON.parse(process.env?.reporterConfig);
5
+ const results = JSON.parse(process.env?.results);
6
+ const runChild = async () => {
7
+ const reporter = qase_javascript_commons_1.QaseReporter.getInstance(options);
8
+ reporter.setTestResults(results);
9
+ await reporter.sendResults();
10
+ };
11
+ runChild();
@@ -0,0 +1,4 @@
1
+ import { JSONSchemaType } from 'ajv';
2
+ import { FrameworkOptionsType } from 'qase-javascript-commons';
3
+ import { ReporterOptionsType } from './options';
4
+ export declare const configSchema: JSONSchemaType<FrameworkOptionsType<'cypress', ReporterOptionsType>>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configSchema = void 0;
4
+ exports.configSchema = {
5
+ type: 'object',
6
+ nullable: true,
7
+ properties: {
8
+ framework: {
9
+ type: 'object',
10
+ nullable: true,
11
+ properties: {
12
+ cypress: {
13
+ type: 'object',
14
+ nullable: true,
15
+ properties: {
16
+ screenshotsFolder: {
17
+ type: 'string',
18
+ nullable: true,
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/hooks.js ADDED
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /// <reference types="cypress" />
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const qase_javascript_commons_1 = require("qase-javascript-commons");
5
+ const configSchema_1 = require("./configSchema");
6
+ async function beforeRunHook(options) {
7
+ const configLoader = new qase_javascript_commons_1.ConfigLoader(configSchema_1.configSchema);
8
+ const config = configLoader.load();
9
+ const { reporterOptions } = options;
10
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11
+ const { ...composedOptions } = (0, qase_javascript_commons_1.composeOptions)(reporterOptions['cypressQaseReporterReporterOptions'], config);
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
13
+ const reporter = qase_javascript_commons_1.QaseReporter.getInstance({
14
+ ...composedOptions,
15
+ frameworkPackage: 'cypress',
16
+ frameworkName: 'cypress',
17
+ reporterName: 'cypress-qase-reporter',
18
+ });
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
20
+ await reporter.startTestRunAsync();
21
+ }
22
+ async function afterRunHook(options) {
23
+ const configLoader = new qase_javascript_commons_1.ConfigLoader(configSchema_1.configSchema);
24
+ const config = configLoader.load();
25
+ const { reporterOptions } = options;
26
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
27
+ const { ...composedOptions } = (0, qase_javascript_commons_1.composeOptions)(reporterOptions['cypressQaseReporterReporterOptions'], config);
28
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
29
+ const reporter = qase_javascript_commons_1.QaseReporter.getInstance({
30
+ ...composedOptions,
31
+ frameworkPackage: 'cypress',
32
+ frameworkName: 'cypress',
33
+ reporterName: 'cypress-qase-reporter',
34
+ });
35
+ await reporter.complete();
36
+ }
37
+ module.exports = {
38
+ beforeRunHook,
39
+ afterRunHook,
40
+ };
@@ -0,0 +1 @@
1
+ export { qase } from './mocha';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.qase = void 0;
4
+ var mocha_1 = require("./mocha");
5
+ Object.defineProperty(exports, "qase", { enumerable: true, get: function () { return mocha_1.qase; } });
@@ -0,0 +1,2 @@
1
+ import { CypressQaseReporter } from './reporter';
2
+ export = CypressQaseReporter;
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ const reporter_1 = require("./reporter");
3
+ module.exports = reporter_1.CypressQaseReporter;
@@ -0,0 +1,3 @@
1
+ /// <reference types="cypress" />
2
+ import { Test } from 'mocha';
3
+ export declare const qase: (caseId: number | string | number[] | string[], test: Test) => Test;
package/dist/mocha.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.qase = void 0;
4
+ const qase = (caseId, test) => {
5
+ const caseIds = Array.isArray(caseId) ? caseId : [caseId];
6
+ test.title = `${test.title} (Qase ID: ${caseIds.join(',')})`;
7
+ return test;
8
+ };
9
+ exports.qase = qase;
@@ -0,0 +1,3 @@
1
+ export type ReporterOptionsType = {
2
+ screenshotsFolder?: string;
3
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export {};
package/dist/plugin.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const hooks_1 = require("./hooks");
4
+ module.exports = function (on, config) {
5
+ on('before:run', async () => {
6
+ await (0, hooks_1.beforeRunHook)(config);
7
+ });
8
+ on('after:run', async () => {
9
+ await (0, hooks_1.afterRunHook)(config);
10
+ });
11
+ };
@@ -0,0 +1,74 @@
1
+ /// <reference types="cypress" />
2
+ import { MochaOptions, reporters, Runner } from 'mocha';
3
+ import { ConfigLoader, TestStatusEnum, FrameworkOptionsType } from 'qase-javascript-commons';
4
+ import { ReporterOptionsType } from './options';
5
+ type CypressState = 'failed' | 'passed' | 'pending';
6
+ export type CypressQaseOptionsType = Omit<MochaOptions, 'reporterOptions'> & {
7
+ reporterOptions: ReporterOptionsType;
8
+ };
9
+ /**
10
+ * @class CypressQaseReporter
11
+ * @extends reporters.Base
12
+ */
13
+ export declare class CypressQaseReporter extends reporters.Base {
14
+ /**
15
+ * @type {RegExp}
16
+ */
17
+ static qaseIdRegExp: RegExp;
18
+ /**
19
+ * @type {Record<CypressState, TestStatusEnum>}
20
+ */
21
+ static statusMap: Record<CypressState, TestStatusEnum>;
22
+ /**
23
+ * @param {string} title
24
+ * @returns {number[]}
25
+ * @private
26
+ */
27
+ private static getCaseId;
28
+ /**
29
+ * @param {number[]} ids
30
+ * @param {string} dir
31
+ * @returns {Attachment[]}
32
+ * @private
33
+ */
34
+ private static findAttachments;
35
+ /**
36
+ * @type {string | undefined}
37
+ * @private
38
+ */
39
+ private screenshotsFolder;
40
+ /**
41
+ * @type {ReporterInterface}
42
+ * @private
43
+ */
44
+ private reporter;
45
+ private options;
46
+ /**
47
+ * @param {Runner} runner
48
+ * @param {CypressQaseOptionsType} options
49
+ * @param {ConfigLoaderInterface} configLoader
50
+ */
51
+ constructor(runner: Runner, options: CypressQaseOptionsType, configLoader?: ConfigLoader<FrameworkOptionsType<"cypress", ReporterOptionsType>>);
52
+ /**
53
+ * @param {Runner} runner
54
+ * @private
55
+ */
56
+ private addRunnerListeners;
57
+ /**
58
+ * @param {Test} test
59
+ * @private
60
+ */
61
+ private addTestResult;
62
+ /**
63
+ * @param {Test} test
64
+ * @param {number[]} ids
65
+ * @private
66
+ */
67
+ private getSignature;
68
+ /**
69
+ * @param {Suite} suite
70
+ * @private
71
+ */
72
+ private getFile;
73
+ }
74
+ export {};
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CypressQaseReporter = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const uuid_1 = require("uuid");
9
+ const child_process_1 = require("child_process");
10
+ const mocha_1 = require("mocha");
11
+ const qase_javascript_commons_1 = require("qase-javascript-commons");
12
+ const traverse_dir_1 = require("./utils/traverse-dir");
13
+ const configSchema_1 = require("./configSchema");
14
+ const { EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING, EVENT_RUN_END, } = mocha_1.Runner.constants;
15
+ /**
16
+ * @class CypressQaseReporter
17
+ * @extends reporters.Base
18
+ */
19
+ class CypressQaseReporter extends mocha_1.reporters.Base {
20
+ /**
21
+ * @param {string} title
22
+ * @returns {number[]}
23
+ * @private
24
+ */
25
+ static getCaseId(title) {
26
+ const [, ids] = title.match(CypressQaseReporter.qaseIdRegExp) ?? [];
27
+ return ids ? ids.split(',').map((id) => Number(id)) : [];
28
+ }
29
+ /**
30
+ * @param {number[]} ids
31
+ * @param {string} dir
32
+ * @returns {Attachment[]}
33
+ * @private
34
+ */
35
+ static findAttachments(ids, dir) {
36
+ const idSet = new Set(ids);
37
+ const attachments = [];
38
+ try {
39
+ (0, traverse_dir_1.traverseDir)(path_1.default.join(process.cwd(), dir), (filePath) => {
40
+ if (CypressQaseReporter.getCaseId(filePath).some((item) => idSet.has(item))) {
41
+ attachments.push({
42
+ content: '',
43
+ id: (0, uuid_1.v4)(),
44
+ mime_type: '', size: 0,
45
+ file_name: path_1.default.basename(filePath),
46
+ file_path: filePath,
47
+ });
48
+ }
49
+ });
50
+ }
51
+ catch (error) { /* ignore */
52
+ }
53
+ return attachments;
54
+ }
55
+ /**
56
+ * @param {Runner} runner
57
+ * @param {CypressQaseOptionsType} options
58
+ * @param {ConfigLoaderInterface} configLoader
59
+ */
60
+ constructor(runner, options, configLoader = new qase_javascript_commons_1.ConfigLoader(configSchema_1.configSchema)) {
61
+ super(runner, options);
62
+ const { reporterOptions } = options;
63
+ const config = configLoader.load();
64
+ const { framework, ...composedOptions } = (0, qase_javascript_commons_1.composeOptions)(reporterOptions, config);
65
+ this.screenshotsFolder = framework?.cypress?.screenshotsFolder;
66
+ this.options = composedOptions;
67
+ this.reporter = qase_javascript_commons_1.QaseReporter.getInstance({
68
+ ...composedOptions,
69
+ frameworkPackage: 'cypress',
70
+ frameworkName: 'cypress',
71
+ reporterName: 'cypress-qase-reporter',
72
+ });
73
+ this.addRunnerListeners(runner);
74
+ }
75
+ /**
76
+ * @param {Runner} runner
77
+ * @private
78
+ */
79
+ addRunnerListeners(runner) {
80
+ runner.on(EVENT_TEST_PASS, (test) => this.addTestResult(test));
81
+ runner.on(EVENT_TEST_PENDING, (test) => this.addTestResult(test));
82
+ runner.on(EVENT_TEST_FAIL, (test) => this.addTestResult(test));
83
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
84
+ runner.once(EVENT_RUN_END, () => {
85
+ const results = this.reporter.getResults();
86
+ (0, child_process_1.spawnSync)('node', [`${__dirname}/child.js`], {
87
+ stdio: 'inherit',
88
+ env: Object.assign(process.env, {
89
+ reporterConfig: JSON.stringify(this.options),
90
+ results: JSON.stringify(results),
91
+ }),
92
+ });
93
+ });
94
+ }
95
+ /**
96
+ * @param {Test} test
97
+ * @private
98
+ */
99
+ addTestResult(test) {
100
+ const ids = CypressQaseReporter.getCaseId(test.title);
101
+ const attachments = this.screenshotsFolder
102
+ ? CypressQaseReporter.findAttachments(ids, this.screenshotsFolder)
103
+ : undefined;
104
+ let relations = {};
105
+ if (test.parent !== undefined) {
106
+ const data = [];
107
+ for (const suite of test.parent.titlePath()) {
108
+ data.push({
109
+ title: suite,
110
+ public_id: null,
111
+ });
112
+ }
113
+ relations = {
114
+ suite: {
115
+ data: data,
116
+ },
117
+ };
118
+ }
119
+ const result = {
120
+ attachments: attachments ?? [],
121
+ author: null,
122
+ fields: {},
123
+ message: test.err?.message ?? null,
124
+ muted: false,
125
+ params: {},
126
+ relations: relations,
127
+ run_id: null,
128
+ signature: this.getSignature(test, ids),
129
+ steps: [],
130
+ id: (0, uuid_1.v4)(),
131
+ execution: {
132
+ status: test.state
133
+ ? CypressQaseReporter.statusMap[test.state]
134
+ : qase_javascript_commons_1.TestStatusEnum.invalid,
135
+ start_time: null,
136
+ end_time: null,
137
+ duration: test.duration ?? 0,
138
+ stacktrace: test.err?.stack ?? null,
139
+ thread: null,
140
+ },
141
+ testops_id: ids.length > 0 ? ids : null,
142
+ title: test.title,
143
+ };
144
+ void this.reporter.addTestResult(result);
145
+ }
146
+ /**
147
+ * @param {Test} test
148
+ * @param {number[]} ids
149
+ * @private
150
+ */
151
+ getSignature(test, ids) {
152
+ let signature = '';
153
+ const file = test.parent ? this.getFile(test.parent) : undefined;
154
+ if (file) {
155
+ signature = file.split('/').join('::');
156
+ }
157
+ if (test.parent) {
158
+ for (const suite of test.parent.titlePath()) {
159
+ signature += '::' + suite.toLowerCase().replace(/\s/g, '_');
160
+ }
161
+ }
162
+ signature += '::' + test.title.toLowerCase().replace(/\s/g, '_');
163
+ if (ids.length > 0) {
164
+ signature += '::' + ids.join('::');
165
+ }
166
+ return signature;
167
+ }
168
+ /**
169
+ * @param {Suite} suite
170
+ * @private
171
+ */
172
+ getFile(suite) {
173
+ if (suite.file) {
174
+ return suite.file;
175
+ }
176
+ if (suite.parent) {
177
+ return this.getFile(suite.parent);
178
+ }
179
+ return undefined;
180
+ }
181
+ }
182
+ exports.CypressQaseReporter = CypressQaseReporter;
183
+ /**
184
+ * @type {RegExp}
185
+ */
186
+ CypressQaseReporter.qaseIdRegExp = /\(Qase ID:? ([\d,]+)\)/;
187
+ /**
188
+ * @type {Record<CypressState, TestStatusEnum>}
189
+ */
190
+ CypressQaseReporter.statusMap = {
191
+ failed: qase_javascript_commons_1.TestStatusEnum.failed,
192
+ passed: qase_javascript_commons_1.TestStatusEnum.passed,
193
+ pending: qase_javascript_commons_1.TestStatusEnum.skipped,
194
+ };
@@ -0,0 +1 @@
1
+ export declare const traverseDir: (dirPath: string, callback: (filePath: string) => void) => void;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.traverseDir = void 0;
7
+ const fs_1 = require("fs");
8
+ const path_1 = __importDefault(require("path"));
9
+ // TODO: get rid of recursion
10
+ const traverseDir = (dirPath, callback) => {
11
+ const items = (0, fs_1.readdirSync)(dirPath, { withFileTypes: true });
12
+ items.forEach((item) => {
13
+ const itemPath = path_1.default.join(dirPath, item.name);
14
+ if (item.isFile()) {
15
+ callback(itemPath);
16
+ }
17
+ else if (item.isDirectory()) {
18
+ try {
19
+ (0, exports.traverseDir)(itemPath, callback);
20
+ }
21
+ catch (error) { /* ignore */ }
22
+ }
23
+ });
24
+ };
25
+ exports.traverseDir = traverseDir;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cypress-qase-reporter",
3
- "version": "2.1.0-beta.1",
3
+ "version": "2.1.0-beta.3",
4
4
  "description": "Qase Cypress Reporter",
5
5
  "homepage": "https://github.com/qase-tms/qase-javascript",
6
6
  "sideEffects": false,
@@ -11,7 +11,8 @@
11
11
  "./mocha": "./dist/mocha.js",
12
12
  "./reporter": "./dist/reporter.js",
13
13
  "./package.json": "./package.json",
14
- "./plugin": "./dist/plugin.js"
14
+ "./plugin": "./dist/plugin.js",
15
+ "./hooks": "./dist/hooks.js"
15
16
  },
16
17
  "typesVersions": {
17
18
  "*": {
@@ -59,8 +60,5 @@
59
60
  "jest": "^29.5.0",
60
61
  "mocha": "^10.2.0",
61
62
  "ts-jest": "^29.1.0"
62
- },
63
- "files": [
64
- "plugin.js"
65
- ]
63
+ }
66
64
  }
Binary file
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+
4
+ "compilerOptions": {
5
+ "noEmit": false
6
+ },
7
+
8
+ "include": ["./src/**/*.ts", "./src/*.js"],
9
+ }