diff --git a/CHANGELOG.md b/CHANGELOG.md index 6366cc4e..26ba95dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. - Allow generation of JSON reports with hooks (After / Before) even if `baseUrl` is undefined, fixes [#1017](https://github.com/badeball/cypress-cucumber-preprocessor/issues/1017). +- Correctly filter test cases in HTML reports when using `omitFiltered`, fixes [#1018](https://github.com/badeball/cypress-cucumber-preprocessor/issues/1018). + ## v17.1.0 - Add support for (testing) type-specific configuration, fixes [#700](https://github.com/badeball/cypress-cucumber-preprocessor/issues/700). diff --git a/features/reporters/html.feature b/features/reporters/html.feature index 9404f058..857b764f 100644 --- a/features/reporters/html.feature +++ b/features/reporters/html.feature @@ -40,3 +40,87 @@ Feature: html report When I run cypress Then it passes And the report should display when last run + + Rule: it should obey `omitFiltered` + Background: + Given additional preprocessor configuration + """ + { + "omitFiltered": true + } + """ + + Scenario: without tags + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a step + Scenario: another scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() {}) + """ + When I run cypress + Then it should appear as if both tests ran + And the HTML should display 2 executed scenarios + + Scenario: with user-provided tags + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + @foobar + Scenario: a scenario + Given a step + Scenario: another scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() {}) + """ + When I run cypress with "--env tags=@foobar" + Then it should appear as if only a single test ran + And the HTML should display 1 executed scenario + + Scenario: @focus + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + @focus + Scenario: a scenario + Given a step + Scenario: another scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() {}) + """ + When I run cypress + Then it should appear as if only a single test ran + And the HTML should display 1 executed scenario + + Scenario: @skip + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a step + @skip + Scenario: another scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() {}) + """ + When I run cypress + Then it should appear as if only a single test ran + And the HTML should display 1 executed scenario diff --git a/features/step_definitions/html_steps.ts b/features/step_definitions/html_steps.ts index 1e0b9bb7..63c66321 100644 --- a/features/step_definitions/html_steps.ts +++ b/features/step_definitions/html_steps.ts @@ -22,23 +22,48 @@ Then("the report should display when last run", async function () { { runScripts: "dangerously" } ); - const dt = Array.from(dom.window.document.querySelectorAll("dt")).find( - (el) => el.textContent === "last run" + const dt = assertAndReturn( + Array.from(dom.window.document.querySelectorAll("dt")).find( + (el) => el.textContent === "last run" + ), + "Expected to find a 'last run' dt" ); - assert(dt, "Expected to find a 'last run' dt"); - - const dd = dt.parentElement?.querySelector("dd"); - - assert(dd, "Expected to find a 'last run' dt's dd"); - - const lastRunText = dd.textContent; + const dd = assertAndReturn( + dt.parentElement?.querySelector("dd"), + "Expected to find a 'last run' dt's dd" + ); - assert(lastRunText, "Expected to find 'XX seconds ago'"); + const lastRunText = assertAndReturn( + dd.textContent, + "Expected to find 'XX seconds ago'" + ); assert.match(lastRunText, /\d+ seconds? ago/); }); +Then( + "the HTML should display {int} executed scenario(s)", + async function (n: number) { + const dom = await JSDOM.fromFile( + path.join(this.tmpDir, "cucumber-report.html"), + { runScripts: "dangerously" } + ); + + const dt = assertAndReturn( + Array.from(dom.window.document.querySelectorAll("dt")).find( + (el) => el.textContent && /\d+ executed/.test(el.textContent) + ), + "Expected to find a 'XX executed' dt" + ); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const actual = parseInt(dt.textContent!, 10); + + assert.equal(actual, n); + } +); + Then("the report should have an image attachment", async function () { const dom = await JSDOM.fromFile( path.join(this.tmpDir, "cucumber-report.html"), diff --git a/lib/browser-runtime.ts b/lib/browser-runtime.ts index 073c68e2..d0568365 100644 --- a/lib/browser-runtime.ts +++ b/lib/browser-runtime.ts @@ -297,7 +297,7 @@ function createPickle(context: CompositionContext, pickle: messages.Pickle) { ...afterHooks.map((hook) => ({ hook })), ]; - if (!testFilter.evaluate(tags) || tags.includes("@skip")) { + if (shouldSkipPickle(testFilter, pickle)) { if (!context.omitFiltered) { it.skip(scenarioName); } @@ -565,6 +565,12 @@ function createTestFilter( } } +function shouldSkipPickle(testFilter: Node, pickle: messages.Pickle) { + const tags = collectTagNames(pickle.tags); + + return !testFilter.evaluate(tags) || tags.includes("@skip"); +} + function beforeHandler(context: CompositionContext) { if (!retrieveInternalSuiteProperties()?.isEventHandlersAttached) { fail( @@ -749,6 +755,8 @@ export default function createTests( registry.finalize(newId); + const testFilter = createTestFilter(gherkinDocument, Cypress.env()); + const stepDefinitions: messages.StepDefinition[] = registry.stepDefinitions.map((stepDefinition) => { const type: messages.StepDefinitionPatternType = @@ -766,42 +774,46 @@ export default function createTests( }; }); - const testCases: messages.TestCase[] = pickles.map((pickle) => { - const tags = collectTagNames(pickle.tags); - const beforeHooks = registry.resolveBeforeHooks(tags); - const afterHooks = registry.resolveAfterHooks(tags); - - const hooksToStep = (hook: IHook): messages.TestStep => { - return { - id: hook.id, - hookId: hook.id, + const testCases: messages.TestCase[] = pickles + .filter((pickle) => { + return !omitFiltered || !shouldSkipPickle(testFilter, pickle); + }) + .map((pickle) => { + const tags = collectTagNames(pickle.tags); + const beforeHooks = registry.resolveBeforeHooks(tags); + const afterHooks = registry.resolveAfterHooks(tags); + + const hooksToStep = (hook: IHook): messages.TestStep => { + return { + id: hook.id, + hookId: hook.id, + }; }; - }; - const pickleStepToTestStep = ( - pickleStep: messages.PickleStep - ): messages.TestStep => { - const stepDefinitionIds = registry - .getMatchingStepDefinitions(pickleStep.text) - .map((stepDefinition) => stepDefinition.id); + const pickleStepToTestStep = ( + pickleStep: messages.PickleStep + ): messages.TestStep => { + const stepDefinitionIds = registry + .getMatchingStepDefinitions(pickleStep.text) + .map((stepDefinition) => stepDefinition.id); + + return { + id: pickleStep.id, + pickleStepId: pickleStep.id, + stepDefinitionIds, + }; + }; return { - id: pickleStep.id, - pickleStepId: pickleStep.id, - stepDefinitionIds, + id: pickle.id, + pickleId: pickle.id, + testSteps: [ + ...beforeHooks.map(hooksToStep), + ...pickle.steps.map(pickleStepToTestStep), + ...afterHooks.map(hooksToStep), + ], }; - }; - - return { - id: pickle.id, - pickleId: pickle.id, - testSteps: [ - ...beforeHooks.map(hooksToStep), - ...pickle.steps.map(pickleStepToTestStep), - ...afterHooks.map(hooksToStep), - ], - }; - }); + }); const specEnvelopes: messages.Envelope[] = []; @@ -848,8 +860,6 @@ export default function createTests( }); } - const testFilter = createTestFilter(gherkinDocument, Cypress.env()); - const context: CompositionContext = { registry, newId,