Skip to content

Commit 0cb7107

Browse files
authored
Merge pull request #7 from jakubsob/implement-scenario-outline
✨ Implement Scenario Outline
2 parents 53ab307 + 6046378 commit 0cb7107

File tree

9 files changed

+182
-11
lines changed

9 files changed

+182
-11
lines changed

R/map_keywords.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ map_keywords <- function(lines) {
44
lines,
55
c(
66
"Example:" = "Scenario:",
7-
"Examples:" = "Scenarios:"
7+
"Examples:" = "Scenarios:",
8+
"Scenario Template:" = "Scenario Outline:"
89
)
910
)
1011
}

R/parse_token.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,53 @@ parse_token <- function(
2626
get_hook(hooks, "after")(context, token$value)
2727
})
2828
},
29+
"Scenario Outline" = function() {
30+
test_that(glue("Scenario: {token$value}"), {
31+
# Find Examples section
32+
examples <- token$children |>
33+
keep(~ .x$type == "Scenarios") |>
34+
pluck(1)
35+
36+
# Parse examples table
37+
table_data <- parse_table(examples$data)
38+
39+
# Get scenario steps (excluding Examples)
40+
steps_tokens <- token$children |>
41+
keep(~ .x$type != "Scenarios")
42+
43+
# For each row in examples
44+
for (i in seq_len(nrow(table_data))) {
45+
row_data <- table_data[i,]
46+
47+
# Replace placeholders in steps
48+
processed_steps <- steps_tokens |>
49+
map(\(step) {
50+
new_step <- step
51+
for (col in names(row_data)) {
52+
placeholder <- glue("<{col}>")
53+
new_step$value <- gsub(
54+
placeholder,
55+
as.character(row_data[[col]]),
56+
new_step$value,
57+
fixed = TRUE
58+
)
59+
}
60+
new_step
61+
})
62+
63+
# Execute the steps
64+
context <- new.env()
65+
calls <- parse_token(processed_steps, steps, parameters) |>
66+
map(\(x) partial(x, context = context))
67+
68+
get_hook(hooks, "before")(context, token$value)
69+
for (call in calls) {
70+
call()
71+
}
72+
get_hook(hooks, "after")(context, token$value)
73+
}
74+
})
75+
},
2976
"Feature" = function(file_name = token$value) {
3077
context_start_file(glue("Feature: {file_name}"))
3178
calls <- parse_token(token$children, steps, parameters, hooks)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# https://cucumber.io/docs/gherkin/reference/#scenario-outline
2+
Feature: Eating
3+
Scenario Outline: eating
4+
Given there are <start> cucumbers
5+
When I eat <eat> cucumbers
6+
Then I should have <left> cucumbers
7+
8+
Examples:
9+
| start | eat | left |
10+
| 12 | 5 | 7 |
11+
| 20 | 5 | 15 |
12+
13+
Scenario Template: eating
14+
Given there are <start> cucumbers
15+
When I eat <eat> cucumbers
16+
Then I should have <left> cucumbers
17+
18+
Examples:
19+
| start | eat | left |
20+
| 12 | 5 | 7 |
21+
| 20 | 5 | 15 |
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
given("there are {int} cucumbers", function(int, context) {
2+
context$cucumbers <- int
3+
})
4+
5+
when("I eat {int} cucumbers", function(int, context) {
6+
context$cucumbers <- context$cucumbers - int
7+
})
8+
9+
then("I should have {int} cucumbers", function(int, context) {
10+
expect_equal(context$cucumbers, int)
11+
})
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cucumber::test(".", "steps/")

tests/testthat/_snaps/examples.md

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@
3232
== Results =====================================================================
3333
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 4 ]
3434

35+
# test: should run with shinytest2
36+
37+
Code
38+
testthat::test_dir(tests_path, reporter = testthat::ProgressReporter$new(
39+
show_praise = FALSE), stop_on_failure = FALSE)
40+
Output
41+
v | F W S OK | Context
42+
v | 2 | Feature: Formula display
43+
== Results =====================================================================
44+
45+
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 2 ]
46+
3547
# test: should run a Scenario with Given, When, Then, And, But keywords
3648

3749
Code
@@ -171,7 +183,7 @@
171183
x
172184
1. \-cucumber (local) call() at cucumber/R/parse_token.R:24:13
173185
2. \-cucumber (local) x(context = context, ...)
174-
3. \-cucumber (local) step(expected = 5L, ...)
186+
3. \-global step(expected = 5L, ...)
175187
4. \-testthat::expect_equal(context$result, expected) at ./steps/addition.R:7:3
176188
Failure ('test-cucumber.R:1:1'): Scenario: Adding float and float
177189
context$result (`actual`) not equal to `expected` (`expected`).
@@ -181,7 +193,7 @@
181193
x
182194
1. \-cucumber (local) call() at cucumber/R/parse_token.R:24:13
183195
2. \-cucumber (local) x(context = context, ...)
184-
3. \-cucumber (local) step(expected = 5L, ...)
196+
3. \-global step(expected = 5L, ...)
185197
4. \-testthat::expect_equal(context$result, expected) at ./steps/addition.R:7:3
186198
--------------------------------------------------------------------------------
187199
x | 1 1 | Feature: Guess the word
@@ -194,7 +206,7 @@
194206
x
195207
1. \-cucumber (local) call() at cucumber/R/parse_token.R:24:13
196208
2. \-cucumber (local) x(context = context, ...)
197-
3. \-cucumber (local) step(n = 6L, ...)
209+
3. \-global step(n = 6L, ...)
198210
4. \-testthat::expect_equal(nchar(context$word), n) at ./steps/guess_the_word.R:18:3
199211
--------------------------------------------------------------------------------
200212
== Results =====================================================================
@@ -207,7 +219,7 @@
207219
x
208220
1. \-cucumber (local) call() at cucumber/R/parse_token.R:24:13
209221
2. \-cucumber (local) x(context = context, ...)
210-
3. \-cucumber (local) step(expected = 5L, ...)
222+
3. \-global step(expected = 5L, ...)
211223
4. \-testthat::expect_equal(context$result, expected) at ./steps/addition.R:7:3
212224
Failure ('test-cucumber.R:1:1'): Scenario: Adding float and float
213225
context$result (`actual`) not equal to `expected` (`expected`).
@@ -217,7 +229,7 @@
217229
x
218230
1. \-cucumber (local) call() at cucumber/R/parse_token.R:24:13
219231
2. \-cucumber (local) x(context = context, ...)
220-
3. \-cucumber (local) step(expected = 5L, ...)
232+
3. \-global step(expected = 5L, ...)
221233
4. \-testthat::expect_equal(context$result, expected) at ./steps/addition.R:7:3
222234
Failure ('test-cucumber.R:1:1'): Scenario: Breaker joins a game
223235
nchar(context$word) (`actual`) not equal to `n` (`expected`).
@@ -227,7 +239,7 @@
227239
x
228240
1. \-cucumber (local) call() at cucumber/R/parse_token.R:24:13
229241
2. \-cucumber (local) x(context = context, ...)
230-
3. \-cucumber (local) step(n = 6L, ...)
242+
3. \-global step(n = 6L, ...)
231243
4. \-testthat::expect_equal(nchar(context$word), n) at ./steps/guess_the_word.R:18:3
232244
[ FAIL 3 | WARN 0 | SKIP 0 | PASS 2 ]
233245

@@ -242,3 +254,14 @@
242254
== Results =====================================================================
243255
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 1 ]
244256

257+
# test: should work with Scenario Outline
258+
259+
Code
260+
testthat::test_dir(tests_path, reporter = testthat::ProgressReporter$new(
261+
show_praise = FALSE), stop_on_failure = FALSE)
262+
Output
263+
v | F W S OK | Context
264+
v | 4 | Feature: Eating
265+
== Results =====================================================================
266+
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 4 ]
267+

tests/testthat/test-examples.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,8 @@ describe("test", {
104104
it("should work with loading steps from setup files", {
105105
test_example("examples/testthat_setup_files")
106106
})
107+
108+
it("should work with Scenario Outline", {
109+
test_example("examples/scenario_outline")
110+
})
107111
})

tests/testthat/test-parse_token.R

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,68 @@ describe("parse_token", {
297297
mockery::expect_called(spies[[1]], 1)
298298
mockery::expect_called(spies[[2]], 1)
299299
})
300+
301+
it("should parse a Scenario Outline to a call list", {
302+
# Arrange
303+
token <- list(
304+
list(
305+
type = "Scenario Outline",
306+
value = "eating",
307+
children = list(
308+
list(
309+
type = "Given",
310+
value = "there are <start> cucumbers",
311+
children = NULL,
312+
data = NULL
313+
),
314+
list(
315+
type = "When",
316+
value = "I eat <eat> cucumbers",
317+
children = NULL,
318+
data = NULL
319+
),
320+
list(
321+
type = "Then",
322+
value = "I should have <left> cucumbers",
323+
children = NULL,
324+
data = NULL
325+
),
326+
list(
327+
type = "Scenarios",
328+
value = "",
329+
children = NULL,
330+
data = c("| start | eat | left |", "| 12 | 5 | 7 |", "| 20 | 5 | 15 |")
331+
)
332+
),
333+
data = NULL
334+
)
335+
)
336+
spies <- list(mockery::mock(), mockery::mock(), mockery::mock())
337+
steps <- list(
338+
given("there are {int} cucumbers", function(start, context) {
339+
spies[[1]]()
340+
testthat::succeed()
341+
}),
342+
when("I eat {int} cucumbers", function(eat, context) {
343+
spies[[2]]()
344+
testthat::succeed()
345+
}),
346+
then("I should have {int} cucumbers", function(left, context) {
347+
spies[[3]]()
348+
testthat::succeed()
349+
})
350+
)
351+
parameters <- .parameters(
352+
get_parameters()$int
353+
)
354+
355+
# Act
356+
callable <- parse_token(token, steps, parameters)
357+
purrr::walk(callable, \(x) x())
358+
359+
# Assert
360+
mockery::expect_called(spies[[1]], 2)
361+
mockery::expect_called(spies[[2]], 2)
362+
mockery::expect_called(spies[[3]], 2)
363+
})
300364
})

tests/testthat/test-tokenize.R

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,10 @@ describe("parse", {
262262
" Given there are <start> cucumbers",
263263
" When I eat <eat> cucumbers",
264264
" Then I should have <left> cucumbers",
265-
"",
266265
" Examples:",
267-
" | start | eat | left |",
268-
" | 12 | 5 | 7 |",
269-
" | 20 | 5 | 15 |"
266+
" | start | eat | left |",
267+
" | 12 | 5 | 7 |",
268+
" | 20 | 5 | 15 |"
270269
)
271270

272271
# Act

0 commit comments

Comments
 (0)