Skip to content

Commit 3fdb187

Browse files
committed
Add documentation
1 parent 3752c87 commit 3fdb187

File tree

2 files changed

+327
-0
lines changed

2 files changed

+327
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The minor version might differ because Cucumber Scala may add Scala-related feat
2929
- [Basic usage](docs/usage.md)
3030
- [Step Definitions](docs/step_definitions.md)
3131
- [Hooks](docs/hooks.md)
32+
- [Transformers](docs/transformers.md)
3233
- [Example project](examples/README.md)
3334
- [Reference documentation for Java](https://docs.cucumber.io/docs/cucumber/)
3435
- [Changelog](CHANGELOG.md)

docs/transformers.md

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
# Transformers
2+
3+
Transformers are glue code like step definitions or hooks.
4+
You have to define them in your glue classes.
5+
6+
Cucumber allows the following specific transformers:
7+
- String to any type
8+
- DocString (multiline string) to any type
9+
- DataTable to any type
10+
- transform lines with named headers to any type
11+
- transform lines without headers to any type
12+
- transform tables to any type
13+
- transform cells content to any type
14+
15+
As well as default transformers for:
16+
- String
17+
- DataTable
18+
- lines with named headers
19+
- cells
20+
21+
## String to any
22+
23+
`ParameterType` allows to transform a String value from a Cucumber expression to a custom type.
24+
25+
It is defined by a name (used in the steps definitions) and a regex.
26+
Each group of the regex will map to a parameter of the transformation function.
27+
28+
For instance, the following transformer can be defined:
29+
```scala
30+
case class Point(x: Int, y: Int)
31+
32+
ParameterType("coordinates", "(.+),(.+)") { (x, y) =>
33+
Point(x.toInt, y.toInt)
34+
}
35+
```
36+
37+
And used like this:
38+
```gherkin
39+
Given balloon coordinates 123,456 in the game
40+
```
41+
42+
```scala
43+
Given("balloon coordinates {coordinates} in the game") { (coordinates: Point) =>
44+
// Do something with the coordinates
45+
}
46+
```
47+
48+
**Limitation:** there is a current limitation to 22 parameters in the `ParameterType` definition.
49+
50+
## DocString to any
51+
52+
`DocStringType` allows to transform DocString values (multiline string) to a custom type.
53+
54+
For instance, the following transformer can be defined:
55+
```scala
56+
case class JsonText(json: String)
57+
58+
DocStringType("json") { text =>
59+
JsonText(text)
60+
}
61+
```
62+
63+
And used like this:
64+
```gherkin
65+
Given the following json text
66+
"""json
67+
{
68+
"key": "value"
69+
}
70+
"""
71+
```
72+
73+
```scala
74+
Given("the following json text") { json: JsonText =>
75+
// Do something with JsonText
76+
}
77+
```
78+
79+
## DataTable to any
80+
81+
`DataTableType` allows to transform DataTable to a custom type.
82+
83+
This can be achieved in different ways:
84+
- transform lines with named headers to any type
85+
- transform lines without headers to any type
86+
- transform tables to any type
87+
- transform cells content to any type
88+
89+
Note that DataTables in Gherkin can not represent `null` or the empty string unambiguously.
90+
Cucumber will interpret empty cells as `null`.
91+
But you can use a replacement to represent empty strings.
92+
See below.
93+
94+
See also the [Datatable reference](https://github.com/cucumber/cucumber/tree/master/datatable).
95+
96+
### Lines with named headers
97+
98+
For instance, the following transformer can be defined:
99+
```scala
100+
case class Author(name: String, surname: String, famousBook: String)
101+
102+
DataTableType { entry: Map[String, String] =>
103+
Author(entry("name"), entry("surname"), entry("famousBook"))
104+
}
105+
```
106+
107+
And used like this:
108+
```gherkin
109+
Given the following authors
110+
| name | surname | famousBook |
111+
| Alan | Alou | The Lion King |
112+
| Robert | Bob | Le Petit Prince |
113+
```
114+
115+
```scala
116+
Given("the following authors") { (authors: java.util.List[Author]) =>
117+
// Do something
118+
}
119+
120+
// Or using DataTable
121+
Given("the following authors") { (table: DataTable) =>
122+
val authors = table.asList[Author](classOf[Author])
123+
}
124+
```
125+
126+
### Lines without headers
127+
128+
For instance, the following transformer can be defined:
129+
```scala
130+
case class Author(name: String, surname: String, famousBook: String)
131+
132+
DataTableType { row: Seq[String] =>
133+
Author(row(0), row(1), row(2))
134+
}
135+
```
136+
137+
And used like this:
138+
```gherkin
139+
Given the following authors
140+
| Alan | Alou | The Lion King |
141+
| Robert | Bob | Le Petit Prince |
142+
```
143+
144+
```scala
145+
Given("the following authors") { (authors: java.util.List[Author]) =>
146+
// Do something
147+
}
148+
149+
// Or using DataTable
150+
Given("the following authors") { (table: DataTable) =>
151+
val authors = table.asList[Author](classOf[Author])
152+
}
153+
```
154+
155+
### DataTable
156+
157+
For instance, the following transformer can be defined:
158+
```scala
159+
case class Author(name: String, surname: String, famousBook: String)
160+
case class GroupOfAuthor(authors: Seq[Author])
161+
162+
DataTableType { table: DataTable =>
163+
val authors = table.asMaps().asScala
164+
.map(_.asScala)
165+
.map(entry => Author(entry("name"), entry("surname"), entry("famousBook")))
166+
.toSeq
167+
GroupOfAuthor(authors)
168+
}
169+
```
170+
171+
_Please note that the same transformation could be done using a line transformer._
172+
_The purpose of this transformer is to show the syntax._
173+
174+
And used like this:
175+
```gherkin
176+
Given the following authors
177+
| name | surname | famousBook |
178+
| Alan | Alou | The Lion King |
179+
| Robert | Bob | Le Petit Prince |
180+
```
181+
182+
```scala
183+
Given("the following authors") { (table: DataTable) =>
184+
val authors = table.convert[GroupOfAuthor](classOf[GroupOfAuthor], false)
185+
}
186+
```
187+
188+
### Cell
189+
190+
For instance, the following transformer can be defined:
191+
```scala
192+
case class RichCell(content: String)
193+
194+
DataTableType { cell: String =>
195+
RichCell(cell)
196+
}
197+
```
198+
199+
And used like this:
200+
```gherkin
201+
Given the following authors
202+
| Alan | Alou | The Lion King |
203+
| Robert | Bob | Le Petit Prince |
204+
```
205+
206+
```scala
207+
Given("the following authors") { (authors: java.util.List[java.util.List[RichCell]]) =>
208+
// Do something
209+
}
210+
211+
// Or using DataTable
212+
Given("the following authors") { (table: DataTable) =>
213+
val authors = table.asLists[RichCell](classOf[RichCell]))
214+
}
215+
```
216+
217+
Or with headers like this:
218+
```gherkin
219+
Given the following authors
220+
| name | surname | famousBook |
221+
| Alan | Alou | The Lion King |
222+
| Robert | Bob | Le Petit Prince |
223+
```
224+
225+
```scala
226+
Given("the following authors") { (authors: java.util.List[java.util.Map[String, RichCell]]) =>
227+
// Do something
228+
}
229+
230+
// Or using DataTable
231+
Given("the following authors") { (table: DataTable) =>
232+
val authors = table.asMaps[String, RichCell](classOf[String], classOf[RichCell])
233+
}
234+
```
235+
236+
### Empty values
237+
238+
By default empty values in DataTable are treated as `null` by Cucumber.
239+
If you need to have empty values, you can define a replacement like `[empty]` that will be automatically replaced to empty when parsing DataTable.
240+
241+
To do so, you can add a parameter to a `DataTableType` definition.
242+
243+
For instance, with the following definition:
244+
```scala
245+
case class Author(name: String, surname: String, famousBook: String)
246+
247+
DataTableType("[empty]") { (entry: Map[String, String]) =>
248+
Author(entry("name"), entry("surname"), entry("famousBook"))
249+
}
250+
```
251+
252+
And the following step:
253+
```gherkin
254+
Given the following authors
255+
| name | surname | famousBook |
256+
| Alan | Alou | The Lion King |
257+
| [empty] | Bob | Le Petit Prince |
258+
```
259+
260+
You would actually get a list containing `Author("Alan", "Alou", "The Lion King")` and `Author("", "Bob", "Le Petit Prince")`.
261+
262+
## Default transformers
263+
264+
Default transformers are used when there is no specific transformer.
265+
266+
They can be used with object mappers like Jackson to easily convert from well known strings to objects.
267+
268+
### String
269+
270+
For instance, the following definition:
271+
```scala
272+
DefaultParameterTransformer { (fromValue: String, toValueType: java.lang.Type) =>
273+
// Apply logic to convert from String to toValueType
274+
}
275+
```
276+
277+
Will be used to convert with such step definitions:
278+
```scala
279+
Given("A step with a undefined {} string") { (param: SomeType) =>
280+
// The string between {} will be converted to SomeType
281+
}
282+
```
283+
284+
### DataTable
285+
286+
#### Lines with named headers
287+
288+
For instance the following definition:
289+
```scala
290+
DefaultDataTableEntryTransformer("[empty]") { (fromValue: Map[String, String], toValueType: java.lang.Type) =>
291+
// Apply some logic to convert from Map to toValueType
292+
}
293+
```
294+
295+
Will be used to convert with such step definitions:
296+
```scala
297+
Given("A step with a datatable") { (rows: java.util.List[SomeType]) =>
298+
// Do something
299+
}
300+
301+
// Or DataTable
302+
Given("A step with a datatable") { (dataTable: DataTable) =>
303+
val table = dataTable.asList[SomeType](classOf[SomeType])
304+
}
305+
```
306+
307+
#### Cells
308+
309+
For instance the following definition:
310+
```scala
311+
DefaultDataTableCellTransformer("[empty]") { (fromValue: String, toValueType: java.lang.Type) =>
312+
// Apply some logic to convert from String to toValueType
313+
}
314+
```
315+
316+
Will be used to convert with such step definitions:
317+
```scala
318+
Given("A step with a datatable") { (rows: java.util.List[java.util.List[SomeType]]) =>
319+
// Do something
320+
}
321+
322+
// Or DataTable
323+
Given("A step with a datatable") { (dataTable: DataTable) =>
324+
val table = dataTable.asLists[SomeType](classOf[SomeType])
325+
}
326+
```

0 commit comments

Comments
 (0)