From d9043da90f9eebd84338f9eaceefada0b1796019 Mon Sep 17 00:00:00 2001 From: Jonas Amundsen Date: Mon, 10 Jul 2023 14:02:23 +0200 Subject: [PATCH] Add support for skipped steps This fixes #1053 [1]. [1] https://github.com/badeball/cypress-cucumber-preprocessor/issues/1053 --- CHANGELOG.md | 4 ++ docs/cucumber-basics.md | 24 +++++++++- features/fixtures/skipped-steps.json | 63 ++++++++++++++++++++++++++ features/fixtures/skipped-steps.ndjson | 17 +++++++ features/reporters/json.feature | 22 +++++++++ features/reporters/messages.feature | 22 +++++++++ features/skipped_steps.feature | 52 +++++++++++++++++++++ lib/browser-runtime.ts | 32 +++++++++---- 8 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 features/fixtures/skipped-steps.json create mode 100644 features/fixtures/skipped-steps.ndjson create mode 100644 features/skipped_steps.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index 1be82232..f91782c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## Unreleased + +- Add support for skipped steps, fixes [#1053](https://github.com/badeball/cypress-cucumber-preprocessor/issues/1053). + ## v18.0.1 - Give each TestStep (from `@cucumber/messages`) a unique ID, fixes [#1034](https://github.com/badeball/cypress-cucumber-preprocessor/issues/1034). diff --git a/docs/cucumber-basics.md b/docs/cucumber-basics.md index 5a3cb0c6..4d3021e2 100644 --- a/docs/cucumber-basics.md +++ b/docs/cucumber-basics.md @@ -46,7 +46,7 @@ Custom parameter types can be registered using `defineParameterType()`. They sha # Pending steps -You can return `"pending"` from a step defintion or a chain to mark a step as pending. This will halt the execution and Cypress will report the test as "skipped". +You can return `"pending"` from a step defintion or a chain to mark a step as pending. This will halt the execution and Cypress will report the test as "skipped". This is generally used for marking steps as "unimplemented" and allows you to commit unfinished work without breaking the test suite. ```ts import { When } from "@badeball/cypress-cucumber-preprocessor"; @@ -66,6 +66,28 @@ When("a step", () => { }); ``` +# Skipped steps + +You can return `"skipped"` from a step defintion or a chain to mark a step as pending. This will halt the execution and Cypress will report the test as "skipped". This however is generally used for conditionally short circuiting a test. + +```ts +import { When } from "@badeball/cypress-cucumber-preprocessor"; + +When("a step", () => { + return "skipped"; +}); +``` + +```ts +import { When } from "@badeball/cypress-cucumber-preprocessor"; + +When("a step", () => { + cy.then(() => { + return "skipped"; + }); +}); +``` + # Nested steps You can invoke other steps from a step using `Step()`, as shown below. diff --git a/features/fixtures/skipped-steps.json b/features/fixtures/skipped-steps.json new file mode 100644 index 00000000..d8060a78 --- /dev/null +++ b/features/fixtures/skipped-steps.json @@ -0,0 +1,63 @@ +[ + { + "description": "", + "elements": [ + { + "description": "", + "id": "a-feature;a-scenario", + "keyword": "Scenario", + "line": 2, + "name": "a scenario", + "steps": [ + { + "arguments": [], + "keyword": "Given ", + "line": 3, + "name": "a preceding step", + "match": { + "location": "not available:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "arguments": [], + "keyword": "And ", + "line": 4, + "name": "a skipped step", + "match": { + "location": "not available:0" + }, + "result": { + "status": "skipped", + "duration": 0 + } + }, + { + "arguments": [], + "keyword": "And ", + "line": 5, + "name": "a succeeding step", + "match": { + "location": "not available:0" + }, + "result": { + "status": "skipped", + "duration": 0 + } + } + ], + "tags": [], + "type": "scenario" + } + ], + "id": "a-feature", + "line": 1, + "keyword": "Feature", + "name": "a feature", + "tags": [], + "uri": "cypress/e2e/a.feature" + } +] diff --git a/features/fixtures/skipped-steps.ndjson b/features/fixtures/skipped-steps.ndjson new file mode 100644 index 00000000..86a4fc0f --- /dev/null +++ b/features/fixtures/skipped-steps.ndjson @@ -0,0 +1,17 @@ +{"testRunStarted":{"timestamp":{"seconds":0,"nanos":0}}} +{"source":{"data":"Feature: a feature\n Scenario: a scenario\n Given a preceding step\n And a skipped step\n And a succeeding step","uri":"cypress/e2e/a.feature","mediaType":"text/x.cucumber.gherkin+plain"}} +{"gherkinDocument":{"feature":{"tags":[],"location":{"line":1,"column":1},"language":"en","keyword":"Feature","name":"a feature","description":"","children":[{"scenario":{"id":"id","tags":[],"location":{"line":2,"column":3},"keyword":"Scenario","name":"a scenario","description":"","steps":[{"id":"id","location":{"line":3,"column":5},"keyword":"Given ","keywordType":"Context","text":"a preceding step"},{"id":"id","location":{"line":4,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"a skipped step"},{"id":"id","location":{"line":5,"column":5},"keyword":"And ","keywordType":"Conjunction","text":"a succeeding step"}],"examples":[]}}]},"comments":[],"uri":"cypress/e2e/a.feature"}} +{"pickle":{"id":"id","uri":"cypress/e2e/a.feature","astNodeIds":["id"],"tags":[],"name":"a scenario","language":"en","steps":[{"id":"id","text":"a preceding step","type":"Context","astNodeIds":["id"]},{"id":"id","text":"a skipped step","type":"Context","astNodeIds":["id"]},{"id":"id","text":"a succeeding step","type":"Context","astNodeIds":["id"]}]}} +{"stepDefinition":{"id":"id","pattern":{"type":"CUCUMBER_EXPRESSION","source":"a preceding step"},"sourceReference":{"uri":"not available","location":{"line":0}}}} +{"stepDefinition":{"id":"id","pattern":{"type":"CUCUMBER_EXPRESSION","source":"a skipped step"},"sourceReference":{"uri":"not available","location":{"line":0}}}} +{"stepDefinition":{"id":"id","pattern":{"type":"CUCUMBER_EXPRESSION","source":"a succeeding step"},"sourceReference":{"uri":"not available","location":{"line":0}}}} +{"testCase":{"id":"id","pickleId":"id","testSteps":[{"id":"id","pickleStepId":"id","stepDefinitionIds":["id"]},{"id":"id","pickleStepId":"id","stepDefinitionIds":["id"]},{"id":"id","pickleStepId":"id","stepDefinitionIds":["id"]}]}} +{"testCaseStarted":{"id":"id","testCaseId":"id","attempt":0,"timestamp":{"seconds":0,"nanos":0}}} +{"testStepStarted":{"testStepId":"id","testCaseStartedId":"id","timestamp":{"seconds":0,"nanos":0}}} +{"testStepFinished":{"testStepId":"id","testCaseStartedId":"id","testStepResult":{"status":"PASSED","duration":0},"timestamp":{"seconds":0,"nanos":0}}} +{"testStepStarted":{"testStepId":"id","testCaseStartedId":"id","timestamp":{"seconds":0,"nanos":0}}} +{"testStepFinished":{"testStepId":"id","testCaseStartedId":"id","testStepResult":{"status":"SKIPPED","duration":0},"timestamp":{"seconds":0,"nanos":0}}} +{"testStepStarted":{"testStepId":"id","testCaseStartedId":"id","timestamp":{"seconds":0,"nanos":0}}} +{"testStepFinished":{"testStepId":"id","testCaseStartedId":"id","testStepResult":{"status":"SKIPPED","duration":0},"timestamp":{"seconds":0,"nanos":0}}} +{"testCaseFinished":{"testCaseStartedId":"id","timestamp":{"seconds":0,"nanos":0},"willBeRetried":false}} +{"testRunFinished":{"timestamp":{"seconds":0,"nanos":0}}} diff --git a/features/reporters/json.feature b/features/reporters/json.feature index c82cb138..83fe40d5 100644 --- a/features/reporters/json.feature +++ b/features/reporters/json.feature @@ -140,6 +140,28 @@ Feature: JSON formatter Then it passes And there should be a JSON output similar to "fixtures/pending-steps.json" + Scenario: skipped step + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a preceding step + And a skipped step + And a succeeding step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a preceding step", function () {}) + Given("a skipped step", function () { + return "skipped"; + }); + Given("a succeeding step", function () {}) + """ + When I run cypress + Then it passes + And there should be a JSON output similar to "fixtures/skipped-steps.json" + Scenario: retried Given additional Cypress configuration """ diff --git a/features/reporters/messages.feature b/features/reporters/messages.feature index 473ef4ee..02fb361d 100644 --- a/features/reporters/messages.feature +++ b/features/reporters/messages.feature @@ -140,6 +140,28 @@ Feature: messages report Then it passes And there should be a messages similar to "fixtures/pending-steps.ndjson" + Scenario: skipped step + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a preceding step + And a skipped step + And a succeeding step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a preceding step", function () {}) + Given("a skipped step", function () { + return "skipped"; + }); + Given("a succeeding step", function () {}) + """ + When I run cypress + Then it passes + And there should be a messages similar to "fixtures/skipped-steps.ndjson" + Scenario: retried Given additional Cypress configuration """ diff --git a/features/skipped_steps.feature b/features/skipped_steps.feature new file mode 100644 index 00000000..e64f04d4 --- /dev/null +++ b/features/skipped_steps.feature @@ -0,0 +1,52 @@ +Feature: skipped steps + Background: + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a skipped step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor") + Given("a skipped step", function() { + return "skipped" + }) + """ + + Rule: skipped steps make the test skipped + + Scenario: basic skipped step + When I run cypress + Then it passes + And it should appear to have skipped the scenario "a scenario" + + Scenario: with step hooks returning strings + Given a file named "cypress/support/step_definitions/hooks.js" with: + """ + const { BeforeStep, AfterStep } = require("@badeball/cypress-cucumber-preprocessor") + BeforeStep(function() { + return "foobar" + }) + AfterStep(function() { + return "foobar" + }) + """ + When I run cypress + Then it passes + And it should appear to have skipped the scenario "a scenario" + + Scenario: with step hooks returning chains + Given a file named "cypress/support/step_definitions/hooks.js" with: + """ + const { BeforeStep, AfterStep } = require("@badeball/cypress-cucumber-preprocessor") + BeforeStep(function() { + return cy.wrap("foobar") + }) + AfterStep(function() { + return cy.wrap("foobar") + }) + """ + When I run cypress + Then it passes + And it should appear to have skipped the scenario "a scenario" diff --git a/lib/browser-runtime.ts b/lib/browser-runtime.ts index 1f0115a7..d99f135d 100644 --- a/lib/browser-runtime.ts +++ b/lib/browser-runtime.ts @@ -589,16 +589,28 @@ function createPickle(context: CompositionContext, pickle: messages.Pickle) { .then(({ start, result }) => { const end = createTimestamp(); - if (result === "pending") { - taskTestStepFinished(context, { - testStepId, - testCaseStartedId, - testStepResult: { - status: messages.TestStepResultStatus.PENDING, - duration: duration(start, end), - }, - timestamp: end, - }); + if (result === "pending" || result === "skipped") { + if (result === "pending") { + taskTestStepFinished(context, { + testStepId, + testCaseStartedId, + testStepResult: { + status: messages.TestStepResultStatus.PENDING, + duration: duration(start, end), + }, + timestamp: end, + }); + } else { + taskTestStepFinished(context, { + testStepId, + testCaseStartedId, + testStepResult: { + status: messages.TestStepResultStatus.SKIPPED, + duration: duration(start, end), + }, + timestamp: end, + }); + } remainingSteps.shift();