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
Copy file name to clipboardExpand all lines: docs/step-definitions.md
+112-9Lines changed: 112 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,30 +12,133 @@ Step definitions are resolved using search paths that are configurable through t
12
12
}
13
13
```
14
14
15
-
This means that if you have a file `cypress/e2e/duckduckgo.feature`, it will match step definitions found in
15
+
The default configuration means that if you have a file `cypress/e2e/duckduckgo.feature`, it will match step definitions found in
16
16
17
17
*`cypress/e2e/duckduckgo/steps.ts`
18
18
*`cypress/e2e/duckduckgo.ts`
19
19
*`cypress/support/step_definitions/duckduckgo.ts`
20
20
21
-
## Hierarchy
21
+
## Pairing explained
22
22
23
-
There's also a `[filepart]` option available. Given a configuration shown below
23
+
From Cucumber you might be familiar with the fact that step definitons *aren't* linked to particular feature files, as per their [docs](https://cucumber.io/docs/cucumber/step-definitions):
24
+
25
+
> Step definitions aren’t linked to a particular feature file or scenario. The file, class or package name of a step definition does not affect what Gherkin steps it will match. The only thing that matters is the step definition’s expression.
26
+
27
+
This is *not* true for the preprocessor and represents the only place where the two experiences (Cucumber vs. Cypress + this preprocessor) deviates significantly. However, the preprocessor don't magically understand your intention in regards to this.
28
+
29
+
**Pairing** dictates which step definitions will be available in which feature files. Furthermore, hooks specified in paired files are the hooks which will take effect. Pairing is entirely configured through the `stepDefintions` property. Below are some examples to further explain this concept.
30
+
31
+
A prerequisite for understanding this process is some knowledge of glob / search patterns. Explaining this however is out of the scope is this page. Internally, [glob](https://github.com/isaacs/node-glob) is used.
32
+
33
+
### Example 1: Templating introduced
34
+
35
+
Let's consider the following directory structure.
36
+
37
+
```
38
+
/home/john.doe/my-project
39
+
└── cypress
40
+
└── e2e
41
+
├── bar
42
+
│ ├── a.feature
43
+
│ └── a.js
44
+
└── foo
45
+
├── b.feature
46
+
└── b.js
47
+
```
48
+
49
+
.. and a configuration `{ "stepDefinitions": ["cypress/e2e/[filepath].js"] }`. The following is what happens when you run `a.feature`:
50
+
51
+
1. The project root (`"/home/john.doe/my-project"`) is subtracted from the full path of the feature file, leaving us with with `"cypress/e2e/foo/a.feature"`.
52
+
2. The *integration directory* (`"cypress/e2e"`) is subtracted from above result, resulting in `"foo/a.feature"`.
53
+
- For Cypress v9 users and below, the *integration directory* is an explicitly configured path.
54
+
- For Cypress v10 users and higher, this is implicitly calculated and is the *common ancestor path* of all feature files found. For the example above, this is `"cypress/e2e"`. Thus we're left with `"foo/a.feature"`.
55
+
3. The file extension is removed, leaving us with `"foo/a"`.
56
+
57
+
The last value is used to replace `[filepath]` in each member of the configured `stepDefinitions`. For our example above this would yield `["cypress/e2e/foo/a.js"]`. This resulting array is used as search patterns for files containing step definitions (internally using [glob](https://github.com/isaacs/node-glob)).
58
+
59
+
When `a.feature` is run, the preprocessor will only looks for step definitions in `cypress/e2e/foo/a.js`. Furthermore, only hooks defined in said file will take effect. The feature file is now said to be *paired* with that file containing step definitions.
60
+
61
+
The observant reader will now understand that the `[filepath]` template value allows you to create a hierarchy of step definitions that matches the structure of your feature files. It also allows to you put step definitions in an entirely separate directory.
62
+
63
+
### Example 2: Directory with common step definitions
64
+
65
+
Let's now consider the following directory structure.
66
+
67
+
```
68
+
/home/john.doe/my-project
69
+
└── cypress
70
+
└── e2e
71
+
├── foo
72
+
│ ├── a.feature
73
+
│ └── a.js
74
+
├── bar
75
+
│ ├── b.feature
76
+
│ └── b.js
77
+
└── common-step-definitions
78
+
├── 1.js
79
+
└── 2.js
80
+
```
81
+
82
+
Using the *same* value `stepDefinitions` as before, the preprocessor would *not* be able to resolve any files in `cypress/e2e/common-step-definitions`. This is likely not what you want, but we can add a member to the configuration value, as shown below.
83
+
84
+
```
85
+
{
86
+
"stepDefinitions": [
87
+
"cypress/e2e/[filepath].js",
88
+
"cypress/e2e/common-step-definitions/*.js"
89
+
]
90
+
}
91
+
```
92
+
93
+
When running `a.feature`, we know from the previous example that the first member of the array would yield the search pattern `cypress/e2e/foo/a.js`, which correctly matches one of our files.
94
+
95
+
The least member however, obeys to the same rules and goes to the same string replacement process described previously. It doesn't contain `[filepath]`, but that doesn't matter. The resulting search pattern is `"cypress/e2e/common-step-definitions/*.js"`, which is provided to [glob](https://github.com/isaacs/node-glob) and would yield `1.js` and `2.js`.
96
+
97
+
All of these three files are now said to be *paired* with `a.feature`. Hooks in `b.js` will not be available when running `a.feature` and its hooks will not take effect.
98
+
99
+
### Example 3: A common mistake
100
+
101
+
Now let's consider the *same* file & directory structure as in the previous example, but the following configuration.
102
+
103
+
```
104
+
{
105
+
"stepDefinitions": [
106
+
"cypress/e2e/**/*.js"
107
+
]
108
+
}
109
+
```
110
+
111
+
The single value will go through the string replacement process and yield itself, because it does not contain the string `[filepath]`. The value will then be propagated to [glob](https://github.com/isaacs/node-glob), which will find *every*`.js` file in the hierarchy.
112
+
113
+
You might initially be tempted to believe that you have configured the processor correctly, because your test might run at this point. However, step definitions from `b.js` will be available in `a.feature` and vice versa. Similarly, hooks in either file will always take effect.
114
+
115
+
### Example 4: Hierarchy
116
+
117
+
There's also a `[filepart]` option available (notice the subtle difference from `[filepath]`). Given a configuration shown below
... and a feature file `cypress/e2e/foo/bar/baz.feature`, the preprocessor would look for step definitions in
127
+
... and a feature file `cypress/e2e/foo/bar/baz.feature`. When running said feature file, the preprocessor would go through a process and replace `[filepath]` with `"foo/bar/baz"` in each member of the configuration (here a single value).
128
+
129
+
The `[filepart]` options works similarly, except that the previous mentioned value (`"foo/bar/baz"`) is further split into
130
+
131
+
-`"foo/bar/baz"`
132
+
-`"foo/bar"`
133
+
-`"foo"`
134
+
-`"."`
135
+
136
+
All of these will be used to replace `[filepart]`. Thus, a single configuration member would yield *four* values and use [glob](https://github.com/isaacs/node-glob) to search in
0 commit comments