Skip to content

Commit 065429c

Browse files
committed
Allow step hooks to return skipped / pending
This is similar to how steps themselves can also return the above mentioned literals. This is in line with how cucumber-js behaves.
1 parent 15eb687 commit 065429c

File tree

4 files changed

+230
-16
lines changed

4 files changed

+230
-16
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ All notable changes to this project will be documented in this file.
66

77
- Run hooks (BeforeAll/AfterAll) may now be optionally named. This is in line with how cucumber-js behaves.
88

9+
- Allow step hooks to return skipped / pending.
10+
11+
- This is similar to how steps themselves can also return the above mentioned literals. This is in line with how cucumber-js behaves.
12+
913
## v23.2.1
1014

1115
- Determine interactive mode correctly, fixes [#1323](https://github.com/badeball/cypress-cucumber-preprocessor/issues/1323).

features/step_hooks.feature

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
Feature: step hooks
2+
3+
Background:
4+
Given additional preprocessor configuration
5+
"""
6+
{
7+
"messages": {
8+
"enabled": true
9+
}
10+
}
11+
"""
12+
13+
Rule: skipped step hooks should affect the step result
14+
15+
Background:
16+
Given a file named "cypress/e2e/a.feature" with:
17+
"""
18+
Feature: a feature
19+
Scenario: a scenario
20+
Given a preceding step
21+
And a skipped step
22+
And a succeeding step
23+
"""
24+
And a file named "cypress/support/step_definitions/steps.js" with:
25+
"""
26+
const { Given } = require("@badeball/cypress-cucumber-preprocessor");
27+
Given("a preceding step", function () {})
28+
Given("a skipped step", function() {})
29+
Given("a succeeding step", function () {})
30+
"""
31+
32+
Scenario: returning literal 'skipped' in BeforeStep
33+
Given a file named "cypress/support/step_definitions/hooks.js" with:
34+
"""
35+
const { BeforeStep } = require("@badeball/cypress-cucumber-preprocessor");
36+
BeforeStep(function ({ pickleStep }) {
37+
if (pickleStep.text === "a skipped step") {
38+
return "skipped";
39+
}
40+
});
41+
"""
42+
When I run cypress
43+
Then it passes
44+
And there should be a messages similar to "fixtures/skipped-steps.ndjson"
45+
46+
Scenario: returning wrapped 'skipped' in BeforeStep
47+
Given a file named "cypress/support/step_definitions/hooks.js" with:
48+
"""
49+
const { BeforeStep } = require("@badeball/cypress-cucumber-preprocessor");
50+
BeforeStep(function ({ pickleStep }) {
51+
if (pickleStep.text === "a skipped step") {
52+
return cy.wrap("skipped");
53+
}
54+
});
55+
"""
56+
When I run cypress
57+
Then it passes
58+
And there should be a messages similar to "fixtures/skipped-steps.ndjson"
59+
60+
Scenario: returning literal 'skipped' in AfterStep
61+
Given a file named "cypress/support/step_definitions/hooks.js" with:
62+
"""
63+
const { AfterStep } = require("@badeball/cypress-cucumber-preprocessor");
64+
AfterStep(function ({ pickleStep }) {
65+
if (pickleStep.text === "a skipped step") {
66+
return "skipped";
67+
}
68+
});
69+
"""
70+
When I run cypress
71+
Then it passes
72+
And there should be a messages similar to "fixtures/skipped-steps.ndjson"
73+
74+
Scenario: returning wrapped 'skipped' in AfterStep
75+
Given a file named "cypress/support/step_definitions/hooks.js" with:
76+
"""
77+
const { AfterStep } = require("@badeball/cypress-cucumber-preprocessor");
78+
AfterStep(function ({ pickleStep }) {
79+
if (pickleStep.text === "a skipped step") {
80+
return cy.wrap("skipped");
81+
}
82+
});
83+
"""
84+
When I run cypress
85+
Then it passes
86+
And there should be a messages similar to "fixtures/skipped-steps.ndjson"
87+
88+
Rule: pending step hooks should affect the step result
89+
90+
Background:
91+
Given a file named "cypress/e2e/a.feature" with:
92+
"""
93+
Feature: a feature
94+
Scenario: a scenario
95+
Given a preceding step
96+
And a pending step
97+
And a succeeding step
98+
"""
99+
And a file named "cypress/support/step_definitions/steps.js" with:
100+
"""
101+
const { Given } = require("@badeball/cypress-cucumber-preprocessor");
102+
Given("a preceding step", function () {})
103+
Given("a pending step", function() {})
104+
Given("a succeeding step", function () {})
105+
"""
106+
107+
Scenario: returning literal 'pending' in BeforeStep
108+
Given a file named "cypress/support/step_definitions/hooks.js" with:
109+
"""
110+
const { BeforeStep } = require("@badeball/cypress-cucumber-preprocessor");
111+
BeforeStep(function ({ pickleStep }) {
112+
if (pickleStep.text === "a pending step") {
113+
return "pending";
114+
}
115+
});
116+
"""
117+
When I run cypress
118+
Then it passes
119+
And there should be a messages similar to "fixtures/pending-steps.ndjson"
120+
121+
Scenario: returning wrapped 'pending' in BeforeStep
122+
Given a file named "cypress/support/step_definitions/hooks.js" with:
123+
"""
124+
const { BeforeStep } = require("@badeball/cypress-cucumber-preprocessor");
125+
BeforeStep(function ({ pickleStep }) {
126+
if (pickleStep.text === "a pending step") {
127+
return cy.wrap("pending");
128+
}
129+
});
130+
"""
131+
When I run cypress
132+
Then it passes
133+
And there should be a messages similar to "fixtures/pending-steps.ndjson"
134+
135+
Scenario: returning literal 'pending' in AfterStep
136+
Given a file named "cypress/support/step_definitions/hooks.js" with:
137+
"""
138+
const { AfterStep } = require("@badeball/cypress-cucumber-preprocessor");
139+
AfterStep(function ({ pickleStep }) {
140+
if (pickleStep.text === "a pending step") {
141+
return "pending";
142+
}
143+
});
144+
"""
145+
When I run cypress
146+
Then it passes
147+
And there should be a messages similar to "fixtures/pending-steps.ndjson"
148+
149+
Scenario: returning wrapped 'pending' in AfterStep
150+
Given a file named "cypress/support/step_definitions/hooks.js" with:
151+
"""
152+
const { AfterStep } = require("@badeball/cypress-cucumber-preprocessor");
153+
AfterStep(function ({ pickleStep }) {
154+
if (pickleStep.text === "a pending step") {
155+
return cy.wrap("pending");
156+
}
157+
});
158+
"""
159+
When I run cypress
160+
Then it passes
161+
And there should be a messages similar to "fixtures/pending-steps.ndjson"

lib/browser-runtime.ts

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { runStepWithLogGroup } from "./helpers/cypress";
4444
import { getTags } from "./helpers/environment";
4545
import { createTimestamp, duration, StrictTimestamp } from "./helpers/messages";
4646
import {
47+
getWorstTestStepResult,
4748
HookType,
4849
SourceMediaType,
4950
StepDefinitionPatternType,
@@ -109,6 +110,18 @@ function getSourceReferenceFromPosition(
109110
}
110111
}
111112

113+
function convertReturnValueToTestStepResultStatus(
114+
retval: any,
115+
): TestStepResultStatus {
116+
if (retval === "skipped") {
117+
return TestStepResultStatus.SKIPPED;
118+
} else if (retval === "pending") {
119+
return TestStepResultStatus.PENDING;
120+
} else {
121+
return TestStepResultStatus.PASSED;
122+
}
123+
}
124+
112125
interface IStep {
113126
hook?: ICaseHook<Mocha.Context>;
114127
pickleStep?: messages.PickleStep;
@@ -588,8 +601,11 @@ function createPickle(context: CompositionContext, pickle: messages.Pickle) {
588601

589602
const end = createTimestamp();
590603

591-
if (result === "pending" || result === "skipped") {
592-
if (result === "pending") {
604+
if (
605+
result === TestStepResultStatus.PENDING ||
606+
result === TestStepResultStatus.SKIPPED
607+
) {
608+
if (result === TestStepResultStatus.PENDING) {
593609
taskTestStepFinished(context, {
594610
testStepId,
595611
testCaseStartedId,
@@ -770,21 +786,24 @@ function createPickle(context: CompositionContext, pickle: messages.Pickle) {
770786

771787
const beforeHooksChain = beforeStepHooks.reduce(
772788
(chain, beforeStepHook) => {
773-
return chain.then(() =>
789+
return chain.then((results) =>
774790
runStepWithLogGroup({
775791
keyword: "BeforeStep",
776792
text: createStepDescription(beforeStepHook),
777793
fn: dryRun
778794
? noopFn
779795
: () =>
780796
registry.runStepHook(this, beforeStepHook, options),
781-
}),
797+
}).then((result) => [
798+
...results,
799+
convertReturnValueToTestStepResultStatus(result),
800+
]),
782801
);
783802
},
784-
cy.wrap({} as unknown, { log: false }),
803+
cy.wrap([] as any, { log: false }),
785804
);
786805

787-
return beforeHooksChain.then(() => {
806+
return beforeHooksChain.then((beforeStepHookResults) => {
788807
try {
789808
return runStepWithLogGroup({
790809
keyword: ensure(
@@ -795,11 +814,12 @@ function createPickle(context: CompositionContext, pickle: messages.Pickle) {
795814
text,
796815
fn: () =>
797816
registry.runStepDefinition(this, text, dryRun, argument),
798-
}).then((result) => {
799-
return afterStepHooks
800-
.reduce(
817+
})
818+
.then(convertReturnValueToTestStepResultStatus)
819+
.then((stepResult) => {
820+
const afterStepHooksChain = afterStepHooks.reduce(
801821
(chain, afterStepHook) => {
802-
return chain.then(() =>
822+
return chain.then((results) =>
803823
runStepWithLogGroup({
804824
keyword: "AfterStep",
805825
text: createStepDescription(afterStepHook),
@@ -811,15 +831,26 @@ function createPickle(context: CompositionContext, pickle: messages.Pickle) {
811831
afterStepHook,
812832
options,
813833
),
814-
}),
834+
}).then((result) => [
835+
...results,
836+
convertReturnValueToTestStepResultStatus(result),
837+
]),
815838
);
816839
},
817-
cy.wrap({} as unknown, { log: false }),
818-
)
819-
.then(() => {
820-
return { start, result };
840+
cy.wrap([] as any, { log: false }),
841+
);
842+
843+
return afterStepHooksChain.then((afterStepHookResults) => {
844+
return {
845+
start,
846+
result: getWorstTestStepResult([
847+
...beforeStepHookResults,
848+
stepResult,
849+
...afterStepHookResults,
850+
]),
851+
};
821852
});
822-
});
853+
});
823854
} catch (e) {
824855
if (e instanceof MissingDefinitionError) {
825856
throw new Error(

lib/helpers/messages-enums.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,21 @@ export enum HookType {
3838
BEFORE_TEST_STEP = "BEFORE_TEST_STEP",
3939
AFTER_TEST_STEP = "AFTER_TEST_STEP",
4040
}
41+
42+
export function getWorstTestStepResult(
43+
testStepResults: readonly TestStepResultStatus[],
44+
): TestStepResultStatus {
45+
return testStepResults.slice().sort((r1, r2) => ordinal(r2) - ordinal(r1))[0];
46+
}
47+
48+
function ordinal(status: TestStepResultStatus) {
49+
return [
50+
TestStepResultStatus.UNKNOWN,
51+
TestStepResultStatus.PASSED,
52+
TestStepResultStatus.SKIPPED,
53+
TestStepResultStatus.PENDING,
54+
TestStepResultStatus.UNDEFINED,
55+
TestStepResultStatus.AMBIGUOUS,
56+
TestStepResultStatus.FAILED,
57+
].indexOf(status);
58+
}

0 commit comments

Comments
 (0)