You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
world: extend docs around classes and functions, support generics for parameters (#2002)
* add generics for world params
* some tweaks to existing world doc
* document ctor as fn
* update world.md documentation
* Update CHANGELOG.md
Co-authored-by: Aurélien Reeves <[email protected]>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,7 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO
10
10
## [Unreleased]
11
11
### Added
12
12
- Add support for named hooks (see [documentation](./docs/support_files/hooks.md#named-hooks)) ([#1994](https://github.com/cucumber/cucumber-js/pull/1994))
13
+
- Add generics support for world parameters type in world-related interfaces and classes (see [documentation](./docs/support_files/world.md#typescript)) ([#1968](https://github.com/cucumber/cucumber-js/issues/1968)[#2002](https://github.com/cucumber/cucumber-js/pull/2002))
13
14
14
15
### Changed
15
16
- Rename the `cucumber-js` binary's underlying file to be `cucumber.js`, so it doesn't fall foul of Node.js module conventions and plays nicely with ESM loaders (see [documentation](./docs/esm.md#transpiling)) ([#1993](https://github.com/cucumber/cucumber-js/pull/1993))
Copy file name to clipboardExpand all lines: docs/support_files/world.md
+98-11
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,11 @@
1
1
# World
2
2
3
-
*World*, or sometimes *context*, is an isolated scope for each scenario, exposed to the steps and most hooks as `this`. It allows you to set variables in one step and recall them in a later step. All variables set this way are discarded when the scenario concludes. It is managed by a world class, either the default one or one you create. Each scenario is given an new instance of the class when the test starts, even if it is a [retry run](../retry.md).
3
+
*World*, is an isolated scope for each scenario, exposed to the steps and most hooks as `this`. It allows you to set variables in one step and recall them in a later step. All variables set this way are discarded when the scenario concludes. It is managed by a world class, either the default one or one you create. Each scenario is given an new instance of the class when the test starts, even if it is a [retry run](../retry.md).
4
4
5
5
The world is not available to the hooks `BeforeAll` or `AfterAll` as each of these executes outside any particular scenario.
6
6
7
-
##### Basic Example
7
+
Here's some simple usage of the world to retain state between steps:
8
+
8
9
```javascript
9
10
const { Given, Then } =require('@cucumber/cucumber')
10
11
@@ -18,7 +19,8 @@ Then("my color should not be red", function() {
18
19
}
19
20
});
20
21
```
21
-
With those step definitions in place
22
+
23
+
With those step definitions in place:
22
24
23
25
```gherkin
24
26
Scenario: Will pass
@@ -41,17 +43,17 @@ Then("my color should not be blue", () => {
41
43
});
42
44
```
43
45
44
-
## Cucumber World
46
+
## Built-in world
45
47
46
-
Cucumberprovides a number of formatting helpers that are passed into the constructor of the World. The default world binds these helpers as follows:
48
+
By default, the world is an instance of Cucumber's built-in `World` class. Cucumber provides a number of formatting helpers that are passed into the constructor as an options object. The default world binds these helpers as follows:
47
49
48
50
*`this.attach`: a method for adding [attachments](./attachments.md) to hooks/steps
49
51
*`this.log`: a method for [logging](./attachments.md#logging) information from hooks/steps
50
-
*`this.parameters`: an object of parameters passed in via the [CLI](../cli.md#world-parameters)
52
+
*`this.parameters`: an object of parameters passed in via configuration (see below)
51
53
52
54
Your custom world will also receive these arguments, but it's up to you to decide what to do with them and they can be safely ignored.
53
55
54
-
### World Parameters
56
+
### World parameters
55
57
56
58
Tests often require configuration and environment information. One of the most frequent cases is web page tests that are using a browser driver; things like viewport, browser to use, application URL and so on.
57
59
@@ -62,14 +64,53 @@ The `worldParameters` configuration option allows you to provide this informatio
62
64
63
65
This option is repeatable, so you can use it multiple times and the objects will be merged with the later ones taking precedence.
64
66
65
-
## Custom Worlds
67
+
## Custom worlds
68
+
69
+
You might also want to have methods on your world that hooks and steps can access to keep their own code simple. To do this, you can write your own world implementation with its own properties and methods that help with your instrumentation, and then call `setWorldConstructor` to tell Cucumber about it:
70
+
71
+
```javascript
72
+
const { setWorldConstructor, World, When } =require('@cucumber/cucumber')
73
+
74
+
classCustomWorldextendsWorld {
75
+
count =0
76
+
77
+
constructor(options) {
78
+
super(options)
79
+
}
80
+
81
+
eat(count) {
82
+
this.count+= count
83
+
}
84
+
}
85
+
86
+
setWorldConstructor(CustomWorld)
87
+
88
+
When('I eat {int} cucumbers', function(count) {
89
+
this.eat(count)
90
+
})
91
+
```
92
+
93
+
In the example above we've extended the built-in `World` class, which is recommended. You can also use a plain function as your world constructor:
94
+
95
+
```javascript
96
+
const { setWorldConstructor, When } =require('@cucumber/cucumber')
97
+
98
+
setWorldConstructor(function(options) {
99
+
this.count=0
100
+
this.eat= (count) =>this.count+= count
101
+
})
102
+
103
+
When('I eat {int} cucumbers', function(count) {
104
+
this.eat(count)
105
+
})
106
+
```
66
107
67
-
You might also want to have methods on your World that hooks and steps can access to keep their own code simple. To do this, you can provide your own World class with its own properties and methods that help with your instrumentation, and then call `setWorldConstructor` to tell Cucumber about it.
108
+
### Real-world example
68
109
69
110
Let's walk through a typical scenario, setting up world that manages a browser context. We'll use the ES6 module syntax for this example. First, let's set up our custom world. Class files should not be loaded as steps - they should be imported. So in this example we'll presume it is in a classes folder next to the steps folder.
70
111
71
-
###### CustomWorld.js
72
112
```javascript
113
+
// CustomWorld.js
73
114
import { World } from'@cucumber/cucumber';
74
115
importseleniumWebdriverfrom"selenium-webdriver";
75
116
@@ -117,8 +158,8 @@ export default class extends World {
117
158
118
159
Now we'll use a step file to setup this custom world and declare the before hook.
This pattern allows for cleaner feature files. Remember that, ideally, scenarios should be between 3-5 lines and communicate **what** the user is doing clearly to the whole team without going into the details of **how** it will be done. While steps can be reused that should not come at the expense of feature clarity.
144
185
186
+
## TypeScript
187
+
188
+
If you're using TypeScript, you can get optimum type safety and completion based on your custom world and parameters.
189
+
190
+
### Hooks and steps
191
+
192
+
If you have a custom world, you'll need to tell TypeScript about the type of `this` in your hook and step functions:
If you're using world parameters (see above), Cucumber's world-related interfaces and classes support generics to easily specify their interface:
205
+
206
+
```typescript
207
+
interfaceCustomParameters {
208
+
cukeLimit:number
209
+
}
210
+
211
+
classCustomWorldextendsWorld<CustomParameters> {
212
+
// etc
213
+
}
214
+
```
215
+
216
+
### Plain functions
217
+
218
+
If you're using a plain function as your world constructor, you'll need to define an interface for your world and type that as `this` for your function:
0 commit comments