@@ -55,3 +55,350 @@ Feature: Custom context objects
5555 Then it passes
5656 And the output contains "The state is 'initial value'"
5757 And the output contains "The state is 'step value'"
58+
59+ Scenario : Custom context objects can depend on other custom context objects two levels deep
60+ Given a file named "features/a.feature" with:
61+ """feature
62+ Feature: some feature
63+ Scenario: scenario a
64+ Given the state is "initial value"
65+ When I set the state to "step value"
66+ Then the state is "step value"
67+ """
68+ And a file named "support/level-one-state.ts" with:
69+ """ts
70+ import {binding} from 'cucumber-tsflow';
71+ import {LevelTwoState} from './level-two-state';
72+
73+ @binding([LevelTwoState])
74+ export class LevelOneState {
75+ constructor(public levelTwoState: LevelTwoState) {
76+ }
77+ }
78+ """
79+ And a file named "support/level-two-state.ts" with:
80+ """ts
81+ export class LevelTwoState {
82+ public value: string = "initial value";
83+ }
84+ """
85+ And a file named "step_definitions/one.ts" with:
86+ """ts
87+ import {LevelTwoState} from '../support/level-two-state';
88+ import {binding, when} from 'cucumber-tsflow';
89+
90+ @binding([LevelTwoState])
91+ class Steps {
92+ public constructor(private readonly levelTwoState: LevelTwoState) {
93+ }
94+
95+ @when("I set the state to {string}")
96+ public setState(newValue: string) {
97+ this.levelTwoState.value = newValue;
98+ }
99+ }
100+
101+ export = Steps;
102+ """
103+ And a file named "step_definitions/two.ts" with:
104+ """ts
105+ import {LevelOneState} from '../support/level-one-state';
106+ import {binding, then} from 'cucumber-tsflow';
107+ import * as assert from 'node:assert';
108+
109+ @binding([LevelOneState])
110+ class Steps {
111+ public constructor(private readonly levelOneState: LevelOneState) {}
112+
113+ @then("the state is {string}")
114+ public checkValue(value: string) {
115+ console.log(`The state is '${this.levelOneState.levelTwoState.value}'`);
116+ assert.equal(this.levelOneState.levelTwoState.value, value, "State value does not match");
117+ }
118+ }
119+
120+ export = Steps;
121+ """
122+ When I run cucumber-js
123+ Then it passes
124+ And the output contains "The state is 'initial value'"
125+ And the output contains "The state is 'step value'"
126+
127+ Scenario : Custom context objects can depend on other custom context objects three levels deep
128+ Given a file named "features/a.feature" with:
129+ """feature
130+ Feature: some feature
131+ Scenario: scenario a
132+ Given the state is "initial value"
133+ When I set the state to "step value"
134+ Then the state is "step value"
135+ """
136+ And a file named "support/level-one-state.ts" with:
137+ """ts
138+ import {binding} from 'cucumber-tsflow';
139+ import {LevelTwoState} from './level-two-state';
140+
141+ @binding([LevelTwoState])
142+ export class LevelOneState {
143+ constructor(public levelTwoState: LevelTwoState) {
144+ }
145+ }
146+ """
147+ And a file named "support/level-two-state.ts" with:
148+ """ts
149+ import {binding} from 'cucumber-tsflow';
150+ import {LevelThreeState} from './level-three-state';
151+
152+ @binding([LevelThreeState])
153+ export class LevelTwoState {
154+ constructor(public levelThreeState: LevelThreeState) {
155+ }
156+ }
157+ """
158+ And a file named "support/level-three-state.ts" with:
159+ """ts
160+ export class LevelThreeState {
161+ public value: string = "initial value";
162+ }
163+ """
164+ And a file named "step_definitions/one.ts" with:
165+ """ts
166+ import {LevelThreeState} from '../support/level-three-state';
167+ import {binding, when} from 'cucumber-tsflow';
168+
169+ @binding([LevelThreeState])
170+ class Steps {
171+ public constructor(private readonly levelThreeState: LevelThreeState) {
172+ }
173+
174+ @when("I set the state to {string}")
175+ public setState(newValue: string) {
176+ this.levelThreeState.value = newValue;
177+ }
178+ }
179+
180+ export = Steps;
181+ """
182+ And a file named "step_definitions/two.ts" with:
183+ """ts
184+ import {LevelOneState} from '../support/level-one-state';
185+ import {binding, then} from 'cucumber-tsflow';
186+ import * as assert from 'node:assert';
187+
188+ @binding([LevelOneState])
189+ class Steps {
190+ public constructor(private readonly levelOneState: LevelOneState) {}
191+
192+ @then("the state is {string}")
193+ public checkValue(value: string) {
194+ console.log(`The state is '${this.levelOneState.levelTwoState.levelThreeState.value}'`);
195+ assert.equal(this.levelOneState.levelTwoState.levelThreeState.value, value, "State value does not match");
196+ }
197+ }
198+
199+ export = Steps;
200+ """
201+ When I run cucumber-js
202+ Then it passes
203+ And the output contains "The state is 'initial value'"
204+ And the output contains "The state is 'step value'"
205+
206+ Scenario : Circular dependencies are explicitly communicated to the developer
207+ Given a file named "features/a.feature" with:
208+ """feature
209+ Feature: some feature
210+ Scenario: scenario a
211+ Given the state is "initial value"
212+ When I set the state to "step value"
213+ Then the state is "step value"
214+ """
215+ And a file named "support/state-one.ts" with:
216+ """ts
217+ import {binding} from 'cucumber-tsflow';
218+ import {StateTwo} from './state-two';
219+
220+ @binding([StateTwo])
221+ export class StateOne {
222+ constructor(public stateTwo: StateTwo) {
223+ }
224+ }
225+ """
226+ And a file named "support/state-two.ts" with:
227+ """ts
228+ import {StateOne} from './state-one';
229+ import {binding} from 'cucumber-tsflow';
230+
231+ @binding([StateOne])
232+ export class StateTwo {
233+ public value: string = "initial value";
234+ constructor(public stateOne: StateOne) {
235+ }
236+ }
237+ """
238+ And a file named "step_definitions/one.ts" with:
239+ """ts
240+ import {StateTwo} from '../support/state-two';
241+ import {binding, when} from 'cucumber-tsflow';
242+
243+ @binding([StateTwo])
244+ class Steps {
245+ public constructor(private readonly stateTwo: StateTwo) {
246+ }
247+
248+ @when("I set the state to {string}")
249+ public setState(newValue: string) {
250+ this.stateTwo.value = newValue;
251+ }
252+ }
253+
254+ export = Steps;
255+ """
256+ And a file named "step_definitions/two.ts" with:
257+ """ts
258+ import {StateOne} from '../support/state-one';
259+ import {binding, then} from 'cucumber-tsflow';
260+ import * as assert from 'node:assert';
261+
262+ @binding([StateOne])
263+ class Steps {
264+ public constructor(private readonly stateOne: StateOne) {}
265+
266+ @then("the state is {string}")
267+ public checkValue(value: string) {
268+ console.log(`The state is '${this.stateOne.stateTwo.value}'`);
269+ assert.equal(this.stateOne.stateTwo.value, value, "State value does not match");
270+ }
271+ }
272+
273+ export = Steps;
274+ """
275+ When I run cucumber-js
276+ Then it fails
277+ And the error output contains text:
278+ """
279+ Undefined context type at index 0 for StateOne, do you possibly have a circular dependency?
280+ """
281+
282+
283+ Scenario : Circular dependencies within the same file are vaguely communicated to the developer
284+ Given a file named "features/a.feature" with:
285+ """feature
286+ Feature: some feature
287+ Scenario: scenario a
288+ Given the state is "initial value"
289+ When I set the state to "step value"
290+ Then the state is "step value"
291+ """
292+ And a file named "support/state.ts" with:
293+ """ts
294+ import {binding} from 'cucumber-tsflow';
295+
296+ export class StateOne {
297+ constructor(public stateTwo: StateTwo) { }
298+ }
299+
300+ @binding([StateOne])
301+ export class StateTwo {
302+ public value: string = "initial value";
303+ constructor(public stateOne: StateOne) { }
304+ }
305+
306+ exports.StateOne = binding([StateTwo])(StateOne);
307+ """
308+ And a file named "step_definitions/one.ts" with:
309+ """ts
310+ import {StateTwo} from '../support/state';
311+ import {binding, when} from 'cucumber-tsflow';
312+
313+ @binding([StateTwo])
314+ class StepsOne {
315+ public constructor(private readonly stateTwo: StateTwo) {
316+ }
317+
318+ @when("I set the state to {string}")
319+ public setState(newValue: string) {
320+ this.stateTwo.value = newValue;
321+ }
322+ }
323+
324+ export = StepsOne;
325+ """
326+ And a file named "step_definitions/two.ts" with:
327+ """ts
328+ import {StateOne} from '../support/state';
329+ import {binding, then} from 'cucumber-tsflow';
330+ import * as assert from 'node:assert';
331+
332+ @binding([StateOne])
333+ class StepsTwo {
334+ public constructor(private readonly stateOne: StateOne) {}
335+
336+ @then("the state is {string}")
337+ public checkValue(value: string) {
338+ console.log(`The state is '${this.stateOne.stateTwo.value}'`);
339+ assert.equal(this.stateOne.stateTwo.value, value, "State value does not match");
340+ }
341+ }
342+
343+ export = StepsTwo;
344+ """
345+ When I run cucumber-js
346+ Then it fails
347+ And the error output contains text:
348+ """
349+ Undefined context type at index 0 for StepsTwo, do you possibly have a circular dependency?
350+ """
351+
352+ Scenario : In-file circular dependencies are thrown as maximum call stack exceeded errors
353+ Given a file named "features/a.feature" with:
354+ """feature
355+ Feature: some feature
356+ Scenario: scenario a
357+ Given the state is "initial value"
358+ When I set the state to "step value"
359+ Then the state is "step value"
360+ """
361+ And a file named "support/circular.ts" with:
362+ """ts
363+ import {binding} from 'cucumber-tsflow';
364+
365+ export class StateOne {
366+ constructor(public stateTwo: StateTwo) { }
367+ }
368+
369+ @binding([StateOne])
370+ export class StateTwo {
371+ public value: string = "initial value";
372+ constructor(public stateOne: StateOne) { }
373+ }
374+
375+ exports.StateOne = binding([StateTwo])(StateOne);
376+ """
377+ And a file named "step_definitions/one.ts" with:
378+ """ts
379+ import {StateTwo} from '../support/circular';
380+ import * as assert from 'node:assert';
381+ import {binding, when, then} from 'cucumber-tsflow';
382+
383+ @binding([StateTwo])
384+ class Steps {
385+ public constructor(private readonly stateTwo: StateTwo) {
386+ }
387+
388+ @when("I set the state to {string}")
389+ public setState(newValue: string) {
390+ this.stateTwo.value = newValue;
391+ }
392+
393+ @then("the state is {string}")
394+ public checkValue(value: string) {
395+ console.log(`The state is '${this.stateTwo.value}'`);
396+ assert.equal(this.stateTwo.value, value, "State value does not match");
397+ }
398+ }
399+
400+ export = Steps;
401+ """
402+ When I run cucumber-js
403+ Then it fails
404+ And the output contains "RangeError: Maximum call stack size exceeded"
0 commit comments