Skip to content

Commit 6ae0964

Browse files
authored
Expose scenario information and advanced tags (#122)
1 parent 8ea9108 commit 6ae0964

File tree

9 files changed

+333
-17
lines changed

9 files changed

+333
-17
lines changed

cucumber-tsflow-specs/features/provided-context-objects.feature renamed to cucumber-tsflow-specs/features/cucumber-context-objects.feature

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,39 @@ Feature: Cucumber context objects
153153

154154
When I run cucumber-js
155155
Then it passes
156+
157+
Scenario: Reading the scenario information
158+
Given a file named "features/a.feature" with:
159+
"""feature
160+
@foo
161+
Feature: Feature
162+
@bar
163+
Scenario: example
164+
Then the scenario title is "example"
165+
And the tags are [ "@foo", "@bar" ]
166+
"""
167+
And a file named "step_definitions/steps.ts" with:
168+
"""ts
169+
import {binding, then, ScenarioInfo} from 'cucumber-tsflow';
170+
import * as assert from 'node:assert';
171+
172+
@binding([ScenarioInfo])
173+
class Steps {
174+
public constructor(private readonly scenario: ScenarioInfo) {}
175+
176+
@then("the scenario title is {string}")
177+
public checkScenarioName(name: string) {
178+
assert.strictEqual(this.scenario.scenarioTitle, name);
179+
}
180+
181+
@then("the tags are {}")
182+
public checkTags(tags: string) {
183+
assert.deepStrictEqual(this.scenario.tags, JSON.parse(tags));
184+
}
185+
}
186+
187+
export = Steps;
188+
"""
189+
190+
When I run cucumber-js
191+
Then it passes
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
Feature: Tag parameters
2+
3+
Background:
4+
Given a file named "step_definitions/steps.ts" with:
5+
"""ts
6+
import * as assert from 'assert';
7+
import {binding, then, ScenarioInfo} from 'cucumber-tsflow';
8+
9+
@binding([ScenarioInfo])
10+
class Steps {
11+
public constructor(private readonly scenario: ScenarioInfo) {}
12+
13+
@then("the flag {string} is enabled")
14+
public checkEnabled(name: string) {
15+
assert.ok(this.scenario.getFlag(name))
16+
}
17+
18+
@then("the flag {string} is disabled")
19+
public checkDisabled(name: string) {
20+
assert.ok(!this.scenario.getFlag(name))
21+
}
22+
23+
@then("the option tag {string} is set to {string}")
24+
public checkOption(name: string, value: string) {
25+
assert.strictEqual(this.scenario.getOptionTag(name), value);
26+
}
27+
28+
@then("the option tag {string} is unset")
29+
public checkOptionUnset(name: string) {
30+
assert.strictEqual(this.scenario.getOptionTag(name), undefined);
31+
}
32+
33+
@then("the attribute tag {string} is set to {}")
34+
@then("the attribute tag {string} is set to:")
35+
public checkAttributes(name: string, values: string) {
36+
assert.deepStrictEqual(this.scenario.getAttributeTag(name), JSON.parse(values));
37+
}
38+
39+
@then("the attribute tag {string} is unset")
40+
public checkAttributesUnset(name: string) {
41+
assert.deepStrictEqual(this.scenario.getAttributeTag(name), undefined);
42+
}
43+
}
44+
45+
export = Steps;
46+
"""
47+
48+
Scenario: Checking for an absent flag
49+
Given a file named "features/a.feature" with:
50+
"""feature
51+
Feature: Feature
52+
Scenario: example
53+
Then the flag "enableFoo" is disabled
54+
"""
55+
When I run cucumber-js
56+
Then it passes
57+
58+
Scenario: Checking for a flag on the feature
59+
Given a file named "features/a.feature" with:
60+
"""feature
61+
@enableFoo
62+
Feature: Feature
63+
Scenario: One
64+
Then the flag "enableFoo" is enabled
65+
Scenario: Two
66+
Then the flag "enableFoo" is enabled
67+
"""
68+
When I run cucumber-js
69+
Then it passes
70+
71+
Scenario: Checking for a flag on the scenario
72+
Given a file named "features/a.feature" with:
73+
"""feature
74+
Feature: Feature
75+
@enableFoo
76+
Scenario: One
77+
Then the flag "enableFoo" is enabled
78+
Then the flag "enableBar" is disabled
79+
@enableBar
80+
Scenario: Two
81+
Then the flag "enableFoo" is disabled
82+
Then the flag "enableBar" is enabled
83+
"""
84+
When I run cucumber-js
85+
Then it passes
86+
87+
Scenario: Checking for an absent option
88+
Given a file named "features/a.feature" with:
89+
"""feature
90+
Feature: Feature
91+
Scenario: example
92+
Then the option tag "foo" is unset
93+
"""
94+
When I run cucumber-js
95+
Then it passes
96+
97+
Scenario: Checking for an option on the feature
98+
Given a file named "features/a.feature" with:
99+
"""feature
100+
@foo(bar)
101+
Feature: Feature
102+
Scenario: One
103+
Then the option tag "foo" is set to "bar"
104+
Scenario: Two
105+
Then the option tag "foo" is set to "bar"
106+
"""
107+
When I run cucumber-js
108+
Then it passes
109+
110+
Scenario: Checking for an option on the scenario
111+
Given a file named "features/a.feature" with:
112+
"""feature
113+
Feature: Feature
114+
@foo(bar)
115+
Scenario: One
116+
Then the option tag "foo" is set to "bar"
117+
@foo(baz)
118+
Scenario: Two
119+
Then the option tag "foo" is set to "baz"
120+
"""
121+
When I run cucumber-js
122+
Then it passes
123+
124+
Scenario: Checking for an option on the scenario overriding one on the feature
125+
Given a file named "features/a.feature" with:
126+
"""feature
127+
@foo(bar)
128+
Feature: Feature
129+
Scenario: One
130+
Then the option tag "foo" is set to "bar"
131+
@foo(baz)
132+
Scenario: Two
133+
Then the option tag "foo" is set to "baz"
134+
"""
135+
When I run cucumber-js
136+
Then it passes
137+
138+
Scenario: Checking for an absent attribute tag
139+
Given a file named "features/a.feature" with:
140+
"""feature
141+
Feature: Feature
142+
Scenario: example
143+
Then the attribute tag "foo" is unset
144+
"""
145+
When I run cucumber-js
146+
Then it passes
147+
148+
Scenario: Checking for an attribute tag on the feature
149+
Given a file named "features/a.feature" with:
150+
"""feature
151+
@foo({"bar":1})
152+
Feature: Feature
153+
Scenario: One
154+
Then the attribute tag "foo" is set to { "bar": 1 }
155+
Scenario: Two
156+
Then the attribute tag "foo" is set to { "bar": 1 }
157+
"""
158+
When I run cucumber-js
159+
Then it passes
160+
161+
Scenario: Checking for an attribute tag on the scenario
162+
Given a file named "features/a.feature" with:
163+
"""feature
164+
Feature: Feature
165+
@foo({"bar":1})
166+
Scenario: One
167+
Then the attribute tag "foo" is set to { "bar": 1 }
168+
@foo({"bar":2})
169+
Scenario: Two
170+
Then the attribute tag "foo" is set to { "bar": 2 }
171+
"""
172+
When I run cucumber-js
173+
Then it passes
174+
175+
Scenario: Checking for an attribute tag on the scenario overriding one on the feature
176+
Given a file named "features/a.feature" with:
177+
"""feature
178+
@foo({"bar":1})
179+
Feature: Feature
180+
Scenario: One
181+
Then the attribute tag "foo" is set to { "bar": 1 }
182+
@foo({"not-bar":2})
183+
Scenario: Two
184+
Then the attribute tag "foo" is set to { "not-bar": 2 }
185+
"""
186+
When I run cucumber-js
187+
Then it passes

cucumber-tsflow-specs/src/step_definitions/cucumber_steps.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ class CucumberSteps {
2626

2727
@then("it passes")
2828
public checkPassed() {
29-
expect(this.runner.lastRun.error).toBeNull();
29+
const { lastRun } = this.runner;
30+
31+
if (lastRun?.error != null) {
32+
throw new Error(
33+
`Last run errored unexpectedly. Output:\n\n${lastRun.output}\n\n` +
34+
`Error Output:\n\n${lastRun.errorOutput}`
35+
);
36+
}
3037
}
3138

3239
@then("it fails")

cucumber-tsflow/src/binding-decorator.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { PickleTag } from "@cucumber/messages";
77
import * as _ from "underscore";
88
import { BindingRegistry, DEFAULT_TAG } from "./binding-registry";
99
import logger from "./logger";
10-
import { ManagedScenarioContext } from "./managed-scenario-context";
10+
import { ManagedScenarioContext, ScenarioInfo } from "./managed-scenario-context";
1111
import { CucumberAttachments, CucumberLog, WorldParameters } from "./provided-context";
1212
import { StepBinding, StepBindingFlags } from "./step-binding";
1313
import { ContextType, StepPattern, TypeDecorator } from "./types";
@@ -101,13 +101,18 @@ const ensureSystemBindings = _.once(() => {
101101
JSON.stringify(scenario)
102102
);
103103

104-
const scenarioContext = new ManagedScenarioContext(
104+
const scenarioInfo = new ScenarioInfo(
105105
scenario.pickle.name!,
106106
_.map(scenario.pickle.tags!, (tag: PickleTag) => tag.name!)
107107
);
108108

109+
const scenarioContext = new ManagedScenarioContext(
110+
scenarioInfo
111+
);
112+
109113
this[SCENARIO_CONTEXT_SLOTNAME] = scenarioContext;
110114

115+
scenarioContext.addExternalObject(scenarioInfo);
111116
scenarioContext.addExternalObject(new WorldParameters(this.parameters));
112117
scenarioContext.addExternalObject(new CucumberLog(this.log.bind(this)));
113118
scenarioContext.addExternalObject(new CucumberAttachments(this.attach.bind(this)));

cucumber-tsflow/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ export * from "./binding-decorator";
22
export * from "./hook-decorators";
33
export * from "./step-definition-decorators";
44
export { ScenarioContext, ScenarioInfo } from "./scenario-context";
5-
export * from './provided-context';
5+
export * from "./provided-context";

cucumber-tsflow/src/managed-scenario-context.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
import * as _ from "underscore";
2-
3-
import { ScenarioContext, ScenarioInfo } from "./scenario-context";
2+
import { ScenarioContext } from "./scenario-context";
3+
import { ScenarioInfo } from "./scenario-info";
44
import { ContextType, isProvidedContextType } from "./types";
55

66
/**
77
* Represents a [[ScenarioContext]] implementation that manages a collection of context objects that
88
* are created and used by binding classes during a running Cucumber scenario.
99
*/
1010
export class ManagedScenarioContext implements ScenarioContext {
11-
private readonly _scenarioInfo: ScenarioInfo;
12-
1311
private _activeObjects = new Map<any, any>();
1412

15-
constructor(scenarioTitle: string, tags: string[]) {
16-
this._scenarioInfo = new ScenarioInfo(scenarioTitle, tags);
17-
}
13+
constructor(private readonly _scenarioInfo: ScenarioInfo) {}
1814

1915
/**
2016
* Gets information about the scenario.
21-
*
2217
*/
2318
public get scenarioInfo(): ScenarioInfo {
2419
return this._scenarioInfo;

cucumber-tsflow/src/scenario-context.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import * as _ from "underscore";
2-
31
import { ScenarioInfo } from "./scenario-info";
42

53
/**

0 commit comments

Comments
 (0)