From c55fcda2bfbce4fdcb7cd1a60aea38d5f4aaed8b Mon Sep 17 00:00:00 2001 From: Dmitrii Gridnev Date: Thu, 19 Sep 2024 12:45:00 +0200 Subject: [PATCH] feature: ability to add attachments to tests or steps - `qase.attach` - add an attachment to test or step ```ts it('test', () => { qase.attach({ paths: '/path/to/file' }); qase.step('Step 1', () => { cy.visit('https://example.com'); qase.attach({ name: 'attachment.txt', content: 'Hello, world!', contentType: 'text/plain' }); }); }); ``` --- qase-cypress/changelog.md | 18 +++++++ qase-cypress/package.json | 2 +- qase-cypress/src/metadata.js | 7 +++ qase-cypress/src/metadata/manager.ts | 73 +++++++++++++++++++++++++++- qase-cypress/src/metadata/models.ts | 12 +++++ qase-cypress/src/mocha.ts | 36 +++++++++++--- qase-cypress/src/reporter.ts | 10 +++- 7 files changed, 147 insertions(+), 11 deletions(-) diff --git a/qase-cypress/changelog.md b/qase-cypress/changelog.md index eb3435cb..5ae978e3 100644 --- a/qase-cypress/changelog.md +++ b/qase-cypress/changelog.md @@ -1,3 +1,21 @@ +# cypress-qase-reporter@2.2.0-beta.3 + +## What's new + +Added the ability to add attachments to tests or steps: + +- `qase.attach` - add an attachment to test or step + +```ts +it('test', () => { + qase.attach({ paths: '/path/to/file' }); + qase.step('Step 1', () => { + cy.visit('https://example.com'); + qase.attach({ name: 'attachment.txt', content: 'Hello, world!', contentType: 'text/plain' }); + }); +}); +``` + # cypress-qase-reporter@2.2.0-beta.2 ## What's new diff --git a/qase-cypress/package.json b/qase-cypress/package.json index 8478ec4f..15c2e2f4 100644 --- a/qase-cypress/package.json +++ b/qase-cypress/package.json @@ -1,6 +1,6 @@ { "name": "cypress-qase-reporter", - "version": "2.2.0-beta.2", + "version": "2.2.0-beta.3", "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 40c09c04..984bb96d 100644 --- a/qase-cypress/src/metadata.js +++ b/qase-cypress/src/metadata.js @@ -63,4 +63,11 @@ module.exports = function(on) { return null; }, }); + + on('task', { + qaseAttach(value) { + MetadataManager.addAttach(value); + return null; + }, + }); }; diff --git a/qase-cypress/src/metadata/manager.ts b/qase-cypress/src/metadata/manager.ts index 3a56ef12..7f81ad55 100644 --- a/qase-cypress/src/metadata/manager.ts +++ b/qase-cypress/src/metadata/manager.ts @@ -1,6 +1,8 @@ -import { Metadata, StepStart } from './models'; +import { Attach, Metadata, StepStart } from './models'; import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'fs'; import { v4 as uuidv4 } from 'uuid'; +import path from 'path'; +import { Attachment, getMimeTypes } from 'qase-javascript-commons'; const metadataPath = 'qaseMetadata'; @@ -22,6 +24,8 @@ export class MetadataManager { steps: [], currentStepId: undefined, firstStepName: undefined, + attachments: [], + stepAttachments: {}, }; try { @@ -75,6 +79,31 @@ export class MetadataManager { this.setMetadata(metadata); } + public static addAttach(attach: Attach) { + const metadata = this.getMetadata() ?? {}; + + if (!metadata.attachments) { + metadata.attachments = []; + } + if (!metadata.stepAttachments) { + metadata.stepAttachments = {}; + } + + const attachments = this.prepareAttach(attach); + + if (metadata.currentStepId) { + if (metadata.stepAttachments[metadata.currentStepId] === undefined) { + metadata.stepAttachments[metadata.currentStepId] = attachments; + } else { + metadata.stepAttachments[metadata.currentStepId]?.push(...attachments); + } + } else { + metadata.attachments.push(...attachments); + } + + this.setMetadata(metadata); + } + public static setSuite(suite: string): void { const metadata = this.getMetadata() ?? {}; metadata.suite = suite; @@ -135,4 +164,46 @@ export class MetadataManager { static isExists(): boolean { return existsSync(metadataPath); } + + static prepareAttach(attach: Attach): Attachment[] { + const attachments: Attachment[] = []; + if (attach.paths) { + if (Array.isArray(attach.paths)) { + attach.paths.forEach((file) => { + const attachmentName = path.basename(file); + const contentType: string = getMimeTypes(file); + attachments.push({ + file_name: attachmentName, + mime_type: contentType, + file_path: file, + content: '', + size: 0, + id: uuidv4(), + }); + }); + } else { + const attachmentName = path.basename(attach.paths); + const contentType: string = getMimeTypes(attach.paths); + attachments.push({ + file_name: attachmentName, + mime_type: contentType, + file_path: attach.paths, + content: '', + size: 0, + id: uuidv4(), + }); + } + } else if (attach.content) { + attachments.push({ + file_name: attach.name ?? 'attachment', + mime_type: attach.contentType ?? 'application/octet-stream', + file_path: null, + content: attach.content, + size: 0, + id: uuidv4(), + }); + } + + return attachments; + } } diff --git a/qase-cypress/src/metadata/models.ts b/qase-cypress/src/metadata/models.ts index d4284b1a..e7184a18 100644 --- a/qase-cypress/src/metadata/models.ts +++ b/qase-cypress/src/metadata/models.ts @@ -1,3 +1,5 @@ +import { Attachment } from 'qase-javascript-commons'; + export interface Metadata { title?: string | undefined; fields?: Record; @@ -9,6 +11,8 @@ export interface Metadata { steps?: (StepStart | StepEnd)[]; currentStepId?: string | undefined; firstStepName?: string | undefined; + attachments?: Attachment[]; + stepAttachments?: Record; } export interface StepStart { @@ -23,3 +27,11 @@ export interface StepEnd { timestamp: number; status: string; } + + +export interface Attach { + name?: string; + paths?: string | string[]; + content?: Buffer | string; + contentType?: string; +} diff --git a/qase-cypress/src/mocha.ts b/qase-cypress/src/mocha.ts index 4149e36b..a60b73d8 100644 --- a/qase-cypress/src/mocha.ts +++ b/qase-cypress/src/mocha.ts @@ -23,7 +23,7 @@ export const qase = ( qase.title = ( value: string, ) => { - cy.task('qaseTitle', value).then(() => { + return cy.task('qaseTitle', value).then(() => { // }); }; @@ -41,7 +41,7 @@ qase.title = ( qase.fields = ( values: Record, ) => { - cy.task('qaseFields', values).then(() => { + return cy.task('qaseFields', values).then(() => { // }); }; @@ -55,7 +55,7 @@ qase.fields = ( * }); */ qase.ignore = () => { - cy.task('qaseIgnore').then(() => { + return cy.task('qaseIgnore').then(() => { // }); }; @@ -72,7 +72,7 @@ qase.ignore = () => { qase.parameters = ( values: Record, ) => { - cy.task('qaseParameters', values).then(() => { + return cy.task('qaseParameters', values).then(() => { // }); }; @@ -89,7 +89,7 @@ qase.parameters = ( qase.groupParameters = ( values: Record, ) => { - cy.task('qaseGroupParameters', values).then(() => { + return cy.task('qaseGroupParameters', values).then(() => { // }); }; @@ -106,7 +106,7 @@ qase.groupParameters = ( qase.suite = ( value: string, ) => { - cy.task('qaseSuite', value).then(() => { + return cy.task('qaseSuite', value).then(() => { // }); }; @@ -123,7 +123,7 @@ qase.suite = ( qase.comment = ( value: string, ) => { - cy.task('qaseComment', value).then(() => { + return cy.task('qaseComment', value).then(() => { // }); }; @@ -149,3 +149,25 @@ qase.step = (name: string, body: () => T | PromiseLike) => { }); }); }; + +/** + * Attach a file to the test case or the step + * @param attach + * @example + * it('test', () => { + * qase.attach({ name: 'attachment.txt', content: 'Hello, world!', contentType: 'text/plain' }); + * qase.attach({ paths: '/path/to/file'}); + * qase.attach({ paths: ['/path/to/file', '/path/to/another/file']}); + * cy.visit('https://example.com'); + * }); + */ +qase.attach = (attach: { + name?: string, + paths?: string | string[], + content?: Buffer | string, + contentType?: string, +}) => { + return cy.task('qaseAttach', attach).then(() => { + // + }); +}; diff --git a/qase-cypress/src/reporter.ts b/qase-cypress/src/reporter.ts index 001595b3..33a4173e 100644 --- a/qase-cypress/src/reporter.ts +++ b/qase-cypress/src/reporter.ts @@ -183,6 +183,8 @@ export class CypressQaseReporter extends reporters.Base { ? CypressQaseReporter.findAttachments(ids, this.screenshotsFolder) : undefined; + attachments?.push(...(metadata?.attachments ?? [])); + let relations = {}; if (test.parent !== undefined) { const data = []; @@ -235,7 +237,7 @@ export class CypressQaseReporter extends reporters.Base { relations: relations, run_id: null, signature: this.getSignature(test, ids), - steps: metadata?.steps ? this.getSteps(metadata.steps) : [], + steps: metadata?.steps ? this.getSteps(metadata.steps, metadata.stepAttachments ?? {}) : [], id: uuidv4(), execution: { status: test.state @@ -300,7 +302,7 @@ export class CypressQaseReporter extends reporters.Base { return undefined; } - private getSteps(steps: (StepStart | StepEnd)[]): TestStepType[] { + private getSteps(steps: (StepStart | StepEnd)[], attachments: Record): TestStepType[] { const result: TestStepType[] = []; const stepMap = new Map(); @@ -316,6 +318,10 @@ export class CypressQaseReporter extends reporters.Base { expected_result: null, }; + if (attachments[step.id]) { + newStep.attachments = attachments[step.id] ?? []; + } + const parentId = step.parentId; if (parentId) { newStep.parent_id = parentId;