From b7650a76b5388335daa3d7e37cfaf9fbc824a4fb Mon Sep 17 00:00:00 2001 From: epszaw Date: Fri, 5 Jul 2024 14:30:36 +0200 Subject: [PATCH] allow to attach cypress videos only for failed or broken tests --- packages/allure-cypress/README.md | 22 ++ packages/allure-cypress/src/model.ts | 5 + packages/allure-cypress/src/reporter.ts | 18 +- .../allure-cypress/test/spec/video.test.ts | 220 +++++++++++++++--- 4 files changed, 232 insertions(+), 33 deletions(-) diff --git a/packages/allure-cypress/README.md b/packages/allure-cypress/README.md index 5e9353bdb..cbd41779d 100644 --- a/packages/allure-cypress/README.md +++ b/packages/allure-cypress/README.md @@ -185,6 +185,28 @@ module.exports = { }; ``` +## Write video attachments for failed tests only + +If you want to see Cypress videos only for failed or broken tests in your Allure report, you can use the `videoOnFailOnly` option: + +```diff +const { allureCypress } = require("allure-cypress/reporter"); + +module.exports = { + // ... + e2e: { ++ video: true, + setupNodeEvents: (on, config) => { + allureCypress(on, { ++ videoOnFailOnly: true, + }); + + return config; + }, + }, +}; +``` + ## Known issues ### Global hooks reporting diff --git a/packages/allure-cypress/src/model.ts b/packages/allure-cypress/src/model.ts index 1100e21be..5b0f79a5e 100644 --- a/packages/allure-cypress/src/model.ts +++ b/packages/allure-cypress/src/model.ts @@ -1,10 +1,15 @@ import type { Status, StatusDetails } from "allure-js-commons"; import type { RuntimeMessage } from "allure-js-commons/sdk"; +import type { Config } from "allure-js-commons/sdk/reporter"; export const ALLURE_REPORT_SYSTEM_HOOK = "__allure_report_system_hook__"; export const ALLURE_REPORT_STEP_COMMAND = "__allure_report_step_command__"; +export type AllureCypressConfig = Config & { + videoOnFailOnly?: boolean; +}; + export type CypressTest = Mocha.Test & { wallClockStartedAt?: Date; hookName?: string; diff --git a/packages/allure-cypress/src/reporter.ts b/packages/allure-cypress/src/reporter.ts index 1ff2c03a7..51cdd6035 100644 --- a/packages/allure-cypress/src/reporter.ts +++ b/packages/allure-cypress/src/reporter.ts @@ -9,8 +9,8 @@ import { getSuiteLabels, parseTestPlan, } from "allure-js-commons/sdk/reporter"; -import type { Config } from "allure-js-commons/sdk/reporter"; import type { + AllureCypressConfig, CypressHookEndMessage, CypressHookStartMessage, CypressMessage, @@ -23,10 +23,12 @@ export class AllureCypress { messagesByAbsolutePath = new Map(); runContextByAbsolutePath = new Map(); globalHooksMessages: CypressMessage[] = []; + videoOnFailOnly: boolean = false; - constructor(config?: Config) { - const { resultsDir = "./allure-results", ...rest } = config || {}; + constructor(config?: AllureCypressConfig) { + const { resultsDir = "./allure-results", videoOnFailOnly = false, ...rest } = config || {}; + this.videoOnFailOnly = videoOnFailOnly; this.allureRuntime = new ReporterRuntime({ writer: new FileSystemWriter({ resultsDir, @@ -76,6 +78,12 @@ export class AllureCypress { endSpec(spec: Cypress.Spec, cypressVideoPath?: string) { const specMessages = this.messagesByAbsolutePath.get(spec.absolute) ?? []; const runContext = this.runContextByAbsolutePath.get(spec.absolute)!; + const isSpecFailed = specMessages.some( + (message) => + message.type === "cypress_test_end" && + (message.data.status === Status.FAILED || message.data.status === Status.BROKEN), + ); + const shouldVideoBeAttached = (!this.videoOnFailOnly || isSpecFailed) && cypressVideoPath; specMessages.forEach((message, i) => { // we add cypressTestId to messages where it's possible because the field is very useful to glue data @@ -233,7 +241,7 @@ export class AllureCypress { this.allureRuntime.applyRuntimeMessages(last(runContext.executables)!, [message] as RuntimeMessage[]); }); - if (cypressVideoPath) { + if (shouldVideoBeAttached) { const fixtureUuid = this.allureRuntime.startFixture(runContext.scopes[0], "after", { name: "Cypress video", status: Status.PASSED, @@ -277,7 +285,7 @@ export class AllureCypress { } } -export const allureCypress = (on: Cypress.PluginEvents, allureConfig?: Config) => { +export const allureCypress = (on: Cypress.PluginEvents, allureConfig?: AllureCypressConfig) => { const allureCypressReporter = new AllureCypress(allureConfig); allureCypressReporter.attachToCypress(on); diff --git a/packages/allure-cypress/test/spec/video.test.ts b/packages/allure-cypress/test/spec/video.test.ts index 645f88c42..b9da1bb7d 100644 --- a/packages/allure-cypress/test/spec/video.test.ts +++ b/packages/allure-cypress/test/spec/video.test.ts @@ -1,16 +1,17 @@ -import { expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { ContentType } from "allure-js-commons"; import { runCypressInlineTest } from "../utils.js"; -it("attaches same video to each spec in a test", async () => { - const { tests, groups } = await runCypressInlineTest({ - "cypress/e2e/sample.cy.js": () => ` - it("foo", () => {}); +describe("write video for every test", () => { + it("attaches same video to each spec in a test", async () => { + const { tests, groups } = await runCypressInlineTest({ + "cypress/e2e/sample.cy.js": () => ` + it("foo", () => {}); - it("bar", () => {}); - `, - "cypress.config.js": () => - ` + it("bar", () => {}); + `, + "cypress.config.js": () => + ` const { allureCypress } = require("allure-cypress/reporter"); module.exports = { @@ -37,25 +38,188 @@ it("attaches same video to each spec in a test", async () => { }, }; `, + }); + + expect(tests).toHaveLength(2); + expect(groups).toHaveLength(1); + expect(groups[0]).toEqual( + expect.objectContaining({ + name: "Cypress video", + children: expect.arrayContaining([tests[0].uuid, tests[1].uuid]), + afters: [ + expect.objectContaining({ + name: "Cypress video", + attachments: [ + expect.objectContaining({ + name: "Cypress video", + type: ContentType.MP4, + }), + ], + }), + ], + }), + ); }); +}); + +describe("write video for failed tests only", () => { + it("doesn't attach video for passed tests", async () => { + const { tests, groups } = await runCypressInlineTest({ + "cypress/e2e/sample.cy.js": () => ` + it("foo", () => {}); + `, + "cypress.config.js": () => + ` + const { allureCypress } = require("allure-cypress/reporter"); + + module.exports = { + e2e: { + baseUrl: "https://allurereport.org", + viewportWidth: 1240, + video: true, + setupNodeEvents: (on, config) => { + allureCypress(on, { + videoOnFailOnly: true, + links: [ + { + type: "issue", + urlTemplate: "https://allurereport.org/issues/%s" + }, + { + type: "tms", + urlTemplate: "https://allurereport.org/tasks/%s" + }, + ] + }); + + return config; + }, + }, + }; + `, + }); + + expect(tests).toHaveLength(1); + expect(groups).toHaveLength(0); + }); + + it("attaches video for failed tests", async () => { + const { tests, groups } = await runCypressInlineTest({ + "cypress/e2e/sample.cy.js": () => ` + it("foo", () => { + cy.wrap(1).eq(2); + }); + `, + "cypress.config.js": () => + ` + const { allureCypress } = require("allure-cypress/reporter"); - expect(tests).toHaveLength(2); - expect(groups).toHaveLength(1); - expect(groups[0]).toEqual( - expect.objectContaining({ - name: "Cypress video", - children: expect.arrayContaining([tests[0].uuid, tests[1].uuid]), - afters: [ - expect.objectContaining({ - name: "Cypress video", - attachments: [ - expect.objectContaining({ - name: "Cypress video", - type: ContentType.MP4, - }), - ], - }), - ], - }), - ); + module.exports = { + e2e: { + baseUrl: "https://allurereport.org", + viewportWidth: 1240, + video: true, + testTimeout: 500, + setupNodeEvents: (on, config) => { + allureCypress(on, { + videoOnFailOnly: true, + links: [ + { + type: "issue", + urlTemplate: "https://allurereport.org/issues/%s" + }, + { + type: "tms", + urlTemplate: "https://allurereport.org/tasks/%s" + }, + ] + }); + + return config; + }, + }, + }; + `, + }); + + expect(tests).toHaveLength(1); + expect(groups).toHaveLength(1); + expect(groups[0]).toEqual( + expect.objectContaining({ + name: "Cypress video", + children: expect.arrayContaining([tests[0].uuid]), + afters: [ + expect.objectContaining({ + name: "Cypress video", + attachments: [ + expect.objectContaining({ + name: "Cypress video", + type: ContentType.MP4, + }), + ], + }), + ], + }), + ); + }); + + it("attaches video for broken tests", async () => { + const { tests, groups } = await runCypressInlineTest({ + "cypress/e2e/sample.cy.js": () => ` + it("foo", () => { + throw new Error("foo"); + }); + `, + "cypress.config.js": () => + ` + const { allureCypress } = require("allure-cypress/reporter"); + + module.exports = { + e2e: { + baseUrl: "https://allurereport.org", + viewportWidth: 1240, + video: true, + testTimeout: 500, + setupNodeEvents: (on, config) => { + allureCypress(on, { + videoOnFailOnly: true, + links: [ + { + type: "issue", + urlTemplate: "https://allurereport.org/issues/%s" + }, + { + type: "tms", + urlTemplate: "https://allurereport.org/tasks/%s" + }, + ] + }); + + return config; + }, + }, + }; + `, + }); + + expect(tests).toHaveLength(1); + expect(groups).toHaveLength(1); + expect(groups[0]).toEqual( + expect.objectContaining({ + name: "Cypress video", + children: expect.arrayContaining([tests[0].uuid]), + afters: [ + expect.objectContaining({ + name: "Cypress video", + attachments: [ + expect.objectContaining({ + name: "Cypress video", + type: ContentType.MP4, + }), + ], + }), + ], + }), + ); + }); });