diff --git a/qase-cypress/README.md b/qase-cypress/README.md index 62cc6387..46724ecb 100644 --- a/qase-cypress/README.md +++ b/qase-cypress/README.md @@ -4,7 +4,7 @@ Publish results simple and easy. ## Installation -To install the latest release version (2.0.x), run: +To install the latest release version (2.1.x), run: ```sh npm install -D cypress-qase-reporter @@ -13,7 +13,7 @@ npm install -D cypress-qase-reporter -To install the latest beta version (2.1.x), run: +To install the latest beta version (2.2.x), run: ```sh npm install -D cypress-qase-reporter@beta @@ -30,7 +30,7 @@ run the following steps: - import { qase } from 'cypress-qase-reporter/dist/mocha' + import { qase } from 'cypress-qase-reporter/mocha' ``` - + 2. Update reporter configuration in `cypress.config.js` and/or environment variables — see the [configuration reference](#configuration) below. @@ -68,6 +68,23 @@ run the following steps: ... ``` +## Updating from v2.1 to v2.2 + +To update an existing test project using Qase reporter from version 2.1 to version 2.2, +run the following steps: + +1. Add a metadata in the `e2e` section of `cypress.config.js` + + ```diff + ... + e2e: { + setupNodeEvents(on, config) { + require('cypress-qase-reporter/plugin')(on, config) + + require('cypress-qase-reporter/metadata')(on) + } + } + ... + ## Getting started The Cypress reporter can auto-generate test cases @@ -80,6 +97,16 @@ from Qase.io before executing tests. It's a more reliable way to bind autotests to test cases, that persists when you rename, move, or parameterize your tests. +### Metadata + +- `qase.title` - set the title of the test case +- `qase.fields` - set the fields of the test case +- `qase.suite` - set the suite of the test case +- `qase.comment` - set the comment of the test case +- `qase.parameters` - set the parameters of the test case +- `qase.groupParameters` - set the group parameters of the test case +- `qase.ignore` - ignore the test case in Qase. The test will be executed, but the results will not be sent to Qase. + For example: ```typescript @@ -88,6 +115,7 @@ import { qase } from 'cypress-qase-reporter/mocha'; describe('My First Test', () => { qase(1, it('Several ids', () => { + qase.title('My title'); expect(true).to.equal(true); }) ); @@ -145,8 +173,6 @@ Example `cypress.config.js` config: ```js import cypress from 'cypress'; -import plugins from './cypress/plugins/index.js'; - module.exports = cypress.defineConfig({ reporter: 'cypress-multi-reporters', reporterOptions: { @@ -180,7 +206,8 @@ module.exports = cypress.defineConfig({ video: false, e2e: { setupNodeEvents(on, config) { - return plugins(on, config); + require('cypress-qase-reporter/plugin')(on, config) + require('cypress-qase-reporter/metadata')(on) }, }, }); diff --git a/qase-cypress/changelog.md b/qase-cypress/changelog.md index 65e960f2..035a781f 100644 --- a/qase-cypress/changelog.md +++ b/qase-cypress/changelog.md @@ -1,3 +1,31 @@ +# cypress-qase-reporter@2.2.0-beta.1 + +## What's new + +Added the ability to specify a test metadata in tests: + +- `qase.title` - set the test title +- `qase.fields` - set the test fields +- `qase.suite` - set the test suite +- `qase.comment` - set the test comment +- `qase.parameters` - set the test parameters +- `qase.groupParameters` - set the test group parameters +- `qase.ignore` - ignore the test in Qase + +```ts +it('test', () => { + qase.title('Title'); + qase.fields({ field: 'value' }); + qase.suite('Suite'); + qase.comment('Comment'); + qase.parameters({ param: 'value' }); + qase.groupParameters({ param: 'value' }); + qase.ignore(); + + cy.visit('https://example.com'); +}); +``` + # cypress-qase-reporter@2.1.0 ## What's new @@ -40,8 +68,8 @@ The reporter will wait for all results to be sent to Qase and will not block the ## What's new -1. Cypress kills the process after the last tests. -The reporter will wait for all results to be sent to Qase and will not block the process after sending. +1. Cypress kills the process after the last tests. + The reporter will wait for all results to be sent to Qase and will not block the process after sending. 2. The reporter will collect suites and add them to results. @@ -66,7 +94,7 @@ For more information about the new features and a guide for migration from v1, r # cypress-qase-reporter@2.0.0-beta.3 -Fixed an issue with multiple test runs created when Cypress is running +Fixed an issue with multiple test runs created when Cypress is running multiple tests in parallel. # cypress-qase-reporter@2.0.0-beta.2 diff --git a/qase-cypress/package.json b/qase-cypress/package.json index 7bf505c1..69d46daf 100644 --- a/qase-cypress/package.json +++ b/qase-cypress/package.json @@ -1,6 +1,6 @@ { "name": "cypress-qase-reporter", - "version": "2.1.1", + "version": "2.2.0-beta.1", "description": "Qase Cypress Reporter", "homepage": "https://github.com/qase-tms/qase-javascript", "sideEffects": false, @@ -12,6 +12,7 @@ "./reporter": "./dist/reporter.js", "./package.json": "./package.json", "./plugin": "./dist/plugin.js", + "./metadata": "./dist/metadata.js", "./hooks": "./dist/hooks.js" }, "typesVersions": { diff --git a/qase-cypress/src/metadata.js b/qase-cypress/src/metadata.js new file mode 100644 index 00000000..c94c62e8 --- /dev/null +++ b/qase-cypress/src/metadata.js @@ -0,0 +1,52 @@ +import { MetadataManager } from './metadata/manager'; + +module.exports = function(on) { + on('task', { + qaseTitle(value) { + MetadataManager.setTitle(value); + return null; + }, + }); + + on('task', { + qaseFields(value) { + MetadataManager.setFields(value); + return null; + }, + }); + + on('task', { + qaseIgnore() { + MetadataManager.setIgnore(); + return null; + }, + }); + + on('task', { + qaseParameters(value) { + MetadataManager.setParameters(value); + return null; + }, + }); + + on('task', { + qaseGroupParameters(value) { + MetadataManager.setGroupParams(value); + return null; + }, + }); + + on('task', { + qaseSuite(value) { + MetadataManager.setSuite(value); + return null; + }, + }); + + on('task', { + qaseComment(value) { + MetadataManager.setComment(value); + return null; + }, + }); +}; diff --git a/qase-cypress/src/metadata/manager.ts b/qase-cypress/src/metadata/manager.ts new file mode 100644 index 00000000..871ae3f4 --- /dev/null +++ b/qase-cypress/src/metadata/manager.ts @@ -0,0 +1,101 @@ +import { Metadata } from './models'; +import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'fs'; + +const metadataPath = 'qaseMetadata'; + +// eslint-disable-next-line @typescript-eslint/no-extraneous-class +export class MetadataManager { + public static getMetadata(): Metadata | undefined { + if (!this.isExists()) { + return undefined; + } + + let metadata: Metadata = { + title: undefined, + fields: {}, + parameters: {}, + groupParams: {}, + ignore: false, + suite: undefined, + comment: undefined, + }; + + try { + const data = readFileSync(metadataPath, 'utf8'); + metadata = JSON.parse(data) as Metadata; + + return metadata; + } catch (err) { + console.error('Error reading metadata file:', err); + } + + return undefined; + } + + public static setIgnore(): void { + const metadata = this.getMetadata() ?? {}; + metadata.ignore = true; + this.setMetadata(metadata); + } + + public static setSuite(suite: string): void { + const metadata = this.getMetadata() ?? {}; + metadata.suite = suite; + this.setMetadata(metadata); + } + + public static setComment(comment: string): void { + const metadata = this.getMetadata() ?? {}; + metadata.comment = comment; + this.setMetadata(metadata); + } + + public static setTitle(title: string): void { + const metadata = this.getMetadata() ?? {}; + metadata.title = title; + this.setMetadata(metadata); + } + + public static setFields(fields: Record): void { + const metadata = this.getMetadata() ?? {}; + metadata.fields = fields; + this.setMetadata(metadata); + } + + public static setParameters(parameters: Record): void { + const metadata = this.getMetadata() ?? {}; + metadata.parameters = parameters; + this.setMetadata(metadata); + } + + public static setGroupParams(groupParams: Record): void { + const metadata = this.getMetadata() ?? {}; + metadata.groupParams = groupParams; + this.setMetadata(metadata); + } + + private static setMetadata(metadata: Metadata): void { + try { + const data = JSON.stringify(metadata); + writeFileSync(metadataPath, data); + } catch (err) { + console.error('Error writing metadata file:', err); + } + } + + public static clear() { + if (!this.isExists()) { + return; + } + + try { + unlinkSync(metadataPath); + } catch (err) { + console.error('Error clearing state file:', err); + } + } + + static isExists(): boolean { + return existsSync(metadataPath); + } +} diff --git a/qase-cypress/src/metadata/models.ts b/qase-cypress/src/metadata/models.ts new file mode 100644 index 00000000..ec4c9f80 --- /dev/null +++ b/qase-cypress/src/metadata/models.ts @@ -0,0 +1,9 @@ +export interface Metadata { + title?: string | undefined; + fields?: Record; + parameters?: Record; + groupParams?: Record; + ignore?: boolean; + suite?: string | undefined; + comment?: string | undefined; +} diff --git a/qase-cypress/src/mocha.ts b/qase-cypress/src/mocha.ts index cab30740..ab9a7141 100644 --- a/qase-cypress/src/mocha.ts +++ b/qase-cypress/src/mocha.ts @@ -10,3 +10,120 @@ export const qase = ( return test; }; + +/** + * Set a title for the test case + * @param {string} value + * @example + * it('test', () => { + * qase.title("Title"); + * cy.visit('https://example.com'); + * }); + */ +qase.title = ( + value: string, +) => { + cy.task('qaseTitle', value).then(() => { + // + }); +}; + + +/** + * Set fields for the test case + * @param {Record} values + * @example + * it('test', () => { + * qase.fields({description: "Description"}); + * cy.visit('https://example.com'); + * }); + */ +qase.fields = ( + values: Record, +) => { + cy.task('qaseFields', values).then(() => { + // + }); +}; + +/** + * Ignore the test case result in Qase + * @example + * it('test', () => { + * qase.ignore(); + * cy.visit('https://example.com'); + * }); + */ +qase.ignore = () => { + cy.task('qaseIgnore').then(() => { + // + }); +}; + +/** + * Set parameters for the test case + * @param {Record} values + * @example + * it('test', () => { + * qase.parameters({param01: "value01"}); + * cy.visit('https://example.com'); + * }); + */ +qase.parameters = ( + values: Record, +) => { + cy.task('qaseParameters', values).then(() => { + // + }); +}; + +/** + * Set group parameters for the test case + * @param {Record} values + * @example + * it('test', () => { + * qase.groupParameters({param01: "value01"}); + * cy.visit('https://example.com'); + * }); + */ +qase.groupParameters = ( + values: Record, +) => { + cy.task('qaseGroupParameters', values).then(() => { + // + }); +}; + +/** + * Set a suite for the test case + * @param {string} value + * @example + * it('test', () => { + * qase.suite("Suite 01"); + * cy.visit('https://example.com'); + * }); + */ +qase.suite = ( + value: string, +) => { + cy.task('qaseSuite', value).then(() => { + // + }); +}; + +/** + * Set a comment for the test case + * @param {string} value + * @example + * it('test', () => { + * qase.comment("Some comment"); + * cy.visit('https://example.com'); + * }); + */ +qase.comment = ( + value: string, +) => { + cy.task('qaseComment', value).then(() => { + // + }); +}; diff --git a/qase-cypress/src/reporter.ts b/qase-cypress/src/reporter.ts index 18b5657a..dd65176c 100644 --- a/qase-cypress/src/reporter.ts +++ b/qase-cypress/src/reporter.ts @@ -19,6 +19,7 @@ import { import { traverseDir } from './utils/traverse-dir'; import { configSchema } from './configSchema'; import { ReporterOptionsType } from './options'; +import { MetadataManager } from './metadata/manager'; const { EVENT_TEST_FAIL, @@ -166,6 +167,13 @@ export class CypressQaseReporter extends reporters.Base { * @private */ private addTestResult(test: Test) { + const metadata = MetadataManager.getMetadata(); + + if (metadata?.ignore) { + MetadataManager.clear(); + return; + } + const ids = CypressQaseReporter.getCaseId(test.title); const attachments = this.screenshotsFolder @@ -189,14 +197,38 @@ export class CypressQaseReporter extends reporters.Base { }; } + if (metadata?.suite) { + relations = { + suite: { + data: [ + { + title: metadata.suite, + public_id: null, + }, + ], + }, + }; + } + + let message: string | null = null; + if (metadata?.comment) { + message = metadata.comment; + } + if (test.err?.message) { + if (message) { + message += '\n\n'; + } + message += test.err.message; + } + const result: TestResultType = { attachments: attachments ?? [], author: null, - fields: {}, - message: test.err?.message ?? null, + fields: metadata?.fields ?? {}, + message: message, muted: false, - params: {}, - group_params: {}, + params: metadata?.parameters ?? {}, + group_params: metadata?.groupParams ?? {}, relations: relations, run_id: null, signature: this.getSignature(test, ids), @@ -213,10 +245,12 @@ export class CypressQaseReporter extends reporters.Base { thread: null, }, testops_id: ids.length > 0 ? ids : null, - title: test.title, + title: metadata?.title ?? test.title, }; void this.reporter.addTestResult(result); + + MetadataManager.clear(); } /**