From c3529b687ace0d6e71eda6eb82d61a899775c1d9 Mon Sep 17 00:00:00 2001 From: Dmitrii Gridnev Date: Tue, 17 Sep 2024 14:20:28 +0200 Subject: [PATCH] feature: the ability to add steps in tests - `qase.step` - add a step to the test ```ts it('test', () => { qase.step('Step 1', () => { cy.visit('https://example.com'); }); }); ``` --- qase-cypress/README.md | 5 ++-- qase-cypress/changelog.md | 16 ++++++++++ qase-cypress/package.json | 2 +- qase-cypress/src/metadata.js | 14 +++++++++ qase-cypress/src/metadata/manager.ts | 39 +++++++++++++++++++++++- qase-cypress/src/metadata/models.ts | 16 ++++++++++ qase-cypress/src/mocha.ts | 22 ++++++++++++++ qase-cypress/src/reporter.ts | 45 +++++++++++++++++++++++++++- 8 files changed, 154 insertions(+), 5 deletions(-) diff --git a/qase-cypress/README.md b/qase-cypress/README.md index 46724ecb..03ddce7a 100644 --- a/qase-cypress/README.md +++ b/qase-cypress/README.md @@ -106,6 +106,7 @@ parameterize your tests. - `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. +- `qase.step` - create a step in the test case For example: @@ -206,8 +207,8 @@ module.exports = cypress.defineConfig({ video: false, e2e: { setupNodeEvents(on, config) { - require('cypress-qase-reporter/plugin')(on, config) - require('cypress-qase-reporter/metadata')(on) + 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 035a781f..eb3435cb 100644 --- a/qase-cypress/changelog.md +++ b/qase-cypress/changelog.md @@ -1,3 +1,19 @@ +# cypress-qase-reporter@2.2.0-beta.2 + +## What's new + +Added the ability to add steps in tests: + +- `qase.step` - add a step to the test + +```ts +it('test', () => { + qase.step('Step 1', () => { + cy.visit('https://example.com'); + }); +}); +``` + # cypress-qase-reporter@2.2.0-beta.1 ## What's new diff --git a/qase-cypress/package.json b/qase-cypress/package.json index 69d46daf..8478ec4f 100644 --- a/qase-cypress/package.json +++ b/qase-cypress/package.json @@ -1,6 +1,6 @@ { "name": "cypress-qase-reporter", - "version": "2.2.0-beta.1", + "version": "2.2.0-beta.2", "description": "Qase Cypress Reporter", "homepage": "https://github.com/qase-tms/qase-javascript", "sideEffects": false, diff --git a/qase-cypress/src/metadata.js b/qase-cypress/src/metadata.js index c94c62e8..40c09c04 100644 --- a/qase-cypress/src/metadata.js +++ b/qase-cypress/src/metadata.js @@ -49,4 +49,18 @@ module.exports = function(on) { return null; }, }); + + on('task', { + qaseStepStart(value) { + MetadataManager.addStepStart(value); + return null; + }, + }); + + on('task', { + qaseStepEnd(value) { + MetadataManager.addStepEnd(value); + return null; + }, + }); }; diff --git a/qase-cypress/src/metadata/manager.ts b/qase-cypress/src/metadata/manager.ts index 871ae3f4..3a56ef12 100644 --- a/qase-cypress/src/metadata/manager.ts +++ b/qase-cypress/src/metadata/manager.ts @@ -1,5 +1,6 @@ -import { Metadata } from './models'; +import { Metadata, StepStart } from './models'; import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'fs'; +import { v4 as uuidv4 } from 'uuid'; const metadataPath = 'qaseMetadata'; @@ -18,6 +19,9 @@ export class MetadataManager { ignore: false, suite: undefined, comment: undefined, + steps: [], + currentStepId: undefined, + firstStepName: undefined, }; try { @@ -38,6 +42,39 @@ export class MetadataManager { this.setMetadata(metadata); } + public static addStepStart(name: string): void { + const metadata = this.getMetadata() ?? {}; + + if (metadata.firstStepName === name) { + return; + } + + if (!metadata.steps) { + metadata.steps = []; + } + const id = uuidv4(); + const parentId = metadata.currentStepId ?? undefined; + metadata.steps.push({ timestamp: Date.now(), name, id: id, parentId: parentId }); + metadata.currentStepId = id; + + if (!metadata.firstStepName) { + metadata.firstStepName = name; + } + + this.setMetadata(metadata); + } + + public static addStepEnd(status: string): void { + const metadata = this.getMetadata() ?? {}; + if (!metadata.steps || !metadata.currentStepId) { + return; + } + const parentId = metadata.steps.reverse().find((step): step is StepStart => step.id === metadata.currentStepId)?.parentId; + metadata.steps.push({ timestamp: Date.now(), status, id: metadata.currentStepId }); + metadata.currentStepId = parentId; + this.setMetadata(metadata); + } + public static setSuite(suite: string): void { const metadata = this.getMetadata() ?? {}; metadata.suite = suite; diff --git a/qase-cypress/src/metadata/models.ts b/qase-cypress/src/metadata/models.ts index ec4c9f80..d4284b1a 100644 --- a/qase-cypress/src/metadata/models.ts +++ b/qase-cypress/src/metadata/models.ts @@ -6,4 +6,20 @@ export interface Metadata { ignore?: boolean; suite?: string | undefined; comment?: string | undefined; + steps?: (StepStart | StepEnd)[]; + currentStepId?: string | undefined; + firstStepName?: string | undefined; +} + +export interface StepStart { + id: string; + timestamp: number; + name: string; + parentId: string | undefined; +} + +export interface StepEnd { + id: string; + timestamp: number; + status: string; } diff --git a/qase-cypress/src/mocha.ts b/qase-cypress/src/mocha.ts index ab9a7141..4149e36b 100644 --- a/qase-cypress/src/mocha.ts +++ b/qase-cypress/src/mocha.ts @@ -127,3 +127,25 @@ qase.comment = ( // }); }; + +/** + * Add a step to the test case + * @param {string} name + * @param {() => T | PromiseLike} body + * @example + * it('test', () => { + * qase.step("Some step", () => { + * // some actions + * }); + * cy.visit('https://example.com'); + * }); + */ +qase.step = (name: string, body: () => T | PromiseLike) => { + return cy.task('qaseStepStart', name).then(() => { + return Cypress.Promise.resolve(body()); + }).then(() => { + cy.task('qaseStepEnd', 'passed').then(() => { + // + }); + }); +}; diff --git a/qase-cypress/src/reporter.ts b/qase-cypress/src/reporter.ts index dd65176c..001595b3 100644 --- a/qase-cypress/src/reporter.ts +++ b/qase-cypress/src/reporter.ts @@ -14,12 +14,15 @@ import { Attachment, FrameworkOptionsType, ConfigType, + TestStepType, + StepStatusEnum, } from 'qase-javascript-commons'; import { traverseDir } from './utils/traverse-dir'; import { configSchema } from './configSchema'; import { ReporterOptionsType } from './options'; import { MetadataManager } from './metadata/manager'; +import { StepEnd, StepStart } from './metadata/models'; const { EVENT_TEST_FAIL, @@ -232,7 +235,7 @@ export class CypressQaseReporter extends reporters.Base { relations: relations, run_id: null, signature: this.getSignature(test, ids), - steps: [], + steps: metadata?.steps ? this.getSteps(metadata.steps) : [], id: uuidv4(), execution: { status: test.state @@ -296,4 +299,44 @@ export class CypressQaseReporter extends reporters.Base { return undefined; } + + private getSteps(steps: (StepStart | StepEnd)[]): TestStepType[] { + const result: TestStepType[] = []; + const stepMap = new Map(); + + for (const step of steps.sort((a, b) => a.timestamp - b.timestamp)) { + if (!('status' in step)) { + const newStep = new TestStepType(); + newStep.id = step.id; + newStep.execution.status = StepStatusEnum.failed; + newStep.execution.start_time = step.timestamp; + newStep.execution.end_time = Date.now(); + newStep.data = { + action: step.name, + expected_result: null, + }; + + const parentId = step.parentId; + if (parentId) { + newStep.parent_id = parentId; + const parent = stepMap.get(parentId); + if (parent) { + parent.steps.push(newStep); + } + } else { + result.push(newStep); + } + + stepMap.set(step.id, newStep); + } else { + const stepType = stepMap.get(step.id); + if (stepType) { + stepType.execution.status = step.status as StepStatusEnum; + stepType.execution.end_time = step.timestamp; + } + } + } + + return result; + } }