diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 23c7458b..62897db0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: needs: prepare-versions runs-on: ubuntu-20.04 container: - image: cypress/base:20.5.0 + image: cypress/browsers:node-20.5.0-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1 strategy: matrix: cypress-version: ${{fromJson(needs.prepare-versions.outputs.matrix)}} diff --git a/features/browser_crash.feature b/features/browser_crash.feature new file mode 100644 index 00000000..1efa79f5 --- /dev/null +++ b/features/browser_crash.feature @@ -0,0 +1,70 @@ +@cypress>=12 +Feature: browser crash + + # Crash-behavior is a mess, ref. https://github.com/cypress-io/cypress/issues/22631. + # Pre-12.17.0, enabling video recording actually made a difference, ref. https://github.com/cypress-io/cypress/pull/27167. + # Post-12.17.0 behavior seems to be consistent enough that it's testable. + + Background: + Given additional preprocessor configuration + """ + { + "json": { + "enabled": true + } + } + """ + + Rule: report generation should fail gracefully in the event of a browser crash + + Scenario: Chromium process crash + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given, attach } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() { + new Cypress.Promise(() => { + Cypress.automation("remote:debugger:protocol", { + command: "Browser.crash", + }); + }); + }); + """ + When I run cypress with a chromium-family browser + Then it fails + And the output should contain + """ + Due to browser crash, no reports are created for cypress/e2e/a.feature. + """ + And the JSON report shouldn't contain any specs + + Scenario: Renderer process crash + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given, attach } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() { + new Cypress.Promise(() => { + Cypress.automation("remote:debugger:protocol", { + command: "Page.crash", + }); + }); + }); + """ + When I run cypress with a chromium-family browser + Then it fails + And the output should contain + """ + Due to browser crash, no reports are created for cypress/e2e/a.feature. + """ + And the JSON report shouldn't contain any specs diff --git a/features/step_definitions/cli_steps.ts b/features/step_definitions/cli_steps.ts index 184bbd4c..3db747e8 100644 --- a/features/step_definitions/cli_steps.ts +++ b/features/step_definitions/cli_steps.ts @@ -9,6 +9,8 @@ import * as glob from "glob"; import ICustomWorld from "../support/ICustomWorld"; import { assertAndReturn } from "../support/helpers"; +const isCI = process.env.CI === "true"; + function execAsync( command: string ): Promise<{ stdout: string; stderr: string }> { @@ -74,6 +76,19 @@ When( } ); +When( + "I run cypress with a chromium-family browser", + { timeout: 60 * 1000 }, + async function (this: ICustomWorld) { + /** + * Chrome is installed in CI, Chromium is installed in my (maintainer) environment. + */ + await this.runCypress({ + extraArgs: ["--browser", isCI ? "chrome" : "chromium"], + }); + } +); + When( "I run diagnostics", { timeout: 60 * 1000 }, diff --git a/lib/plugin-event-handlers.ts b/lib/plugin-event-handlers.ts index 45da5601..619de44d 100644 --- a/lib/plugin-event-handlers.ts +++ b/lib/plugin-event-handlers.ts @@ -496,6 +496,40 @@ export async function afterSpecHandler( return; } + /** + * This pretty much can't happen and the check is merely to satisfy TypeScript in the next block. + */ + switch (state.state) { + case "uninitialized": + case "after-run": + throw createError("Unexpected state in afterSpecHandler: " + state.state); + } + + const browserCrashExprCol = [ + /We detected that the .+ process just crashed/, + /We detected that the .+ Renderer process just crashed/, + ]; + + const error = results.error; + + if (error != null && browserCrashExprCol.some((expr) => expr.test(error))) { + console.log( + chalk.yellow( + ` Due to browser crash, no reports are created for ${spec.relative}.` + ) + ); + + state = { + state: "after-spec", + pretty: state.pretty, + messages: { + accumulation: state.messages.accumulation, + }, + }; + + return; + } + switch (state.state) { case "test-finished": // This is the normal case. case "before-spec": // This can happen if a spec doesn't contain any tests.