Skip to content

Commit

Permalink
Merge pull request #5 from jakubsob/4-indentation
Browse files Browse the repository at this point in the history
✨ Allow setting feature file indentation
  • Loading branch information
jakubsob authored Feb 19, 2025
2 parents 463ddff + d5f4d85 commit 5feb20c
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 15 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
branches: [main, master, dev]
pull_request:
branches: [main, master]
branches: [main, master, dev]

name: R-CMD-check

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
branches: [main, master, dev]
pull_request:
branches: [main, master]
branches: [main, master, dev]

name: test-coverage

Expand Down
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: cucumber
Type: Package
Title: Behavior-Driven Development for R
Version: 1.1.0
Version: 1.1.1
Authors@R: person("Jakub", "Sobolewski", email = "[email protected]", role = c("aut", "cre"))
Description: Write executable specifications in a natural language that describes how your code should behave.
Write specifications in feature files using 'Gherkin' language and execute them using functions implemented in R.
Expand All @@ -24,6 +24,7 @@ Suggests:
Config/testthat/edition: 3
Imports:
checkmate,
cli,
dplyr,
fs,
glue,
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ importFrom(checkmate,assert_string)
importFrom(checkmate,assert_subset)
importFrom(checkmate,test_character)
importFrom(checkmate,test_subset)
importFrom(cli,cli_abort)
importFrom(dplyr,bind_cols)
importFrom(fs,dir_ls)
importFrom(glue,glue)
Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# cucumber 1.1.1

- ✨ Added option to set the indent of feature files. Useful when you use a different indent than the default 2 whitespaces. All user-facing options are documented in `?cucumber::opts`. (#5)
- ✨ Added validation of feature files to check if they have a consistent indentation. (#5)

# cucumber 1.1.0

- ✨ Added scenario `before` and `after` hooks.
Expand Down
26 changes: 26 additions & 0 deletions R/options.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#' {cucumber} Options
#'
#' Internally used, package-specific options.
#' They allow overriding the default behavior of the package.
#'
#' @details
#'
#' The following options are available:
#'
#' - `cucumber.indent`
#'
#' Regular expression for the indent of the feature files.
#'
#' default: `^\\s{2}`
#'
#' - `cucumber.interactive`
#'
#' Logical value indicating whether to ask which feature files to run.
#'
#' default: `FALSE`
#'
#' See [base::options()] and [base::getOption()] on how to work with options.
#'
#' @md
#' @name opts
NULL
1 change: 1 addition & 0 deletions R/test.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ test <- function(
} # nocov end
feature_files |>
map(readLines) |>
map(validate_feature) |>
map(normalize_feature) |>
walk(run, steps = steps, parameters = get_parameters())
}
3 changes: 1 addition & 2 deletions R/tokenize.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
INDENT <- "^\\s{2}"
NODE_REGEX <- paste0(
"^(?!\\s+)(",
paste0(
Expand Down Expand Up @@ -32,7 +31,7 @@ remove_trailing_colon <- function(x) {

#' @importFrom stringr str_remove_all
remove_indent <- function(x) {
str_remove_all(x, INDENT)
str_remove_all(x, getOption("cucumber.indent", default = "^\\s{2}"))
}

#' @importFrom stringr str_detect
Expand Down
25 changes: 25 additions & 0 deletions R/validate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#' Validate lines read from a feature file
#' @keywords internal
validate_feature <- function(lines) {
# Remove comments and empty lines for validation
clean_lines <- remove_comments(remove_empty_lines(lines))
clean_lines |>
validate_indentation()
invisible(lines)
}

#' @keywords internal
#' @importFrom stringr str_detect
#' @importFrom cli cli_abort
validate_indentation <- function(lines) {
indent <- getOption("cucumber.indent", default = "^\\s{2}")
test_lines <- lines[!str_detect(lines, "^Feature")] |>
remove_empty_lines()
if (any(!str_detect(test_lines, indent))) {
cli_abort(c(
"All lines must be indented with {indent}",
"i" = "Check the {.code getOption('cucumber.indent')} option if it is set to your feature file indent."
))
}
invisible(lines)
}
26 changes: 26 additions & 0 deletions man/opts.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions man/validate_feature.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions tests/testthat/_snaps/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,16 @@
--------------------------------------------------------------------------------
Failure ('test-cucumber.R:1:1'): Scenario: Adding integer and float
context$result (`actual`) not equal to `expected` (`expected`).
`actual`: 2
`expected`: 5
`actual`: 2.1
`expected`: 5.0
Backtrace:
x
1. \-global `<step>`(expected = 5L, context = `<env>`)
2. \-testthat::expect_equal(context$result, expected) at ./steps/addition.R:7:3
Failure ('test-cucumber.R:1:1'): Scenario: Adding float and float
context$result (`actual`) not equal to `expected` (`expected`).
`actual`: 2
`expected`: 5
`actual`: 2.2
`expected`: 5.0
Backtrace:
x
1. \-global `<step>`(expected = 5L, context = `<env>`)
Expand All @@ -207,16 +207,16 @@
-- Failed tests ----------------------------------------------------------------
Failure ('test-cucumber.R:1:1'): Scenario: Adding integer and float
context$result (`actual`) not equal to `expected` (`expected`).
`actual`: 2
`expected`: 5
`actual`: 2.1
`expected`: 5.0
Backtrace:
x
1. \-global `<step>`(expected = 5L, context = `<env>`)
2. \-testthat::expect_equal(context$result, expected) at ./steps/addition.R:7:3
Failure ('test-cucumber.R:1:1'): Scenario: Adding float and float
context$result (`actual`) not equal to `expected` (`expected`).
`actual`: 2
`expected`: 5
`actual`: 2.2
`expected`: 5.0
Backtrace:
x
1. \-global `<step>`(expected = 5L, context = `<env>`)
Expand Down
5 changes: 5 additions & 0 deletions tests/testthat/_snaps/validate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# validate_feature: should validate indent

All lines must be indented with ^\s{4}
i Check the `getOption('cucumber.indent')` option if it is set to your feature file indent.

5 changes: 5 additions & 0 deletions tests/testthat/test-examples.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ describe("test", {
})

it("should run with shinytest2", {
# nolint start
# Produces on r-devel:
# Superclass process has cloneable=FALSE, but subclass r_process has cloneable=TRUE. A subclass cannot be cloneable when its superclass is not cloneable, so cloning will be disabled for r_process.
# nolint end
skip_if(R.version$status == "Under development (unstable)")
test_example("examples/shinytest2")
})

Expand Down
49 changes: 49 additions & 0 deletions tests/testthat/test-tokenize.R
Original file line number Diff line number Diff line change
Expand Up @@ -454,4 +454,53 @@ describe("parse", {
)
)
})

it("should parse feature files with 4 spaces indent", {
# Arrange
lines <- c(
"Feature: Guess the word",
" Scenario: Maker starts a game",
" When the Maker starts a game",
" Then the Maker waits for a Breaker to join"
)
indent <- "^\\s{4}"

# Act
withr::with_options(list(cucumber.indent = indent), {
result <- tokenize(lines)
})

# Assert
expect_equal(
result,
list(
list(
type = "Feature",
value = "Guess the word",
children = list(
list(
type = "Scenario",
value = "Maker starts a game",
children = list(
list(
type = "When",
value = "the Maker starts a game",
children = NULL,
data = NULL
),
list(
type = "Then",
value = "the Maker waits for a Breaker to join",
children = NULL,
data = NULL
)
),
data = NULL
)
),
data = NULL
)
)
)
})
})
33 changes: 33 additions & 0 deletions tests/testthat/test-validate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
describe("validate_feature", {
it("shouldn't produce error feature with valid indentation", {
withr::with_options(list(cucumber.indent = "^\\s{2}"), {
expect_no_error(
validate_feature(
c(
"Feature: foo",
" Scenario: bar",
" Given foo",
" When foo",
" Then foo"
)
)
)
})
})

it("should validate indent", {
withr::with_options(list(cucumber.indent = "^\\s{4}"), {
expect_snapshot_error(
validate_feature(
c(
"Feature: foo",
" Scenario: bar",
" Given foo",
" When foo",
" Then foo"
)
)
)
})
})
})

0 comments on commit 5feb20c

Please sign in to comment.