From fc9c75056cdfdd741f4841ad12d3fc4e140003bd Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 7 Aug 2025 08:22:57 -0400 Subject: [PATCH] More informative `expect_match()` when object is a vector Fixes #2181 --- NEWS.md | 2 +- R/expect-match.R | 17 ++++++++++++++--- R/expect-output.R | 2 +- tests/testthat/_snaps/expect-match.md | 18 ++++++++++++------ tests/testthat/_snaps/expect-output.md | 2 +- tests/testthat/test-expect-match.R | 7 +++++-- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index c049ea22e..dd2080f0e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,7 +19,7 @@ * `vignette("custom-expectations)` has been overhauled to make it much clearer how to create high-quality expectations (#2113, #2132, #2072). * `expect_snapshot()` and friends will now fail when creating a new snapshot on CI. This is usually a signal that you've forgotten to run it locally before committing (#1461). * `expect_snapshot_value()` can now handle expressions that generate `-` (#1678) or zero length atomic vectors (#2042). -* `expect_matches()` failures should be a little easier to read (#2135). +* `expect_matches()` failures should be a little easier to read (#2135, #2181). * New `local_on_cran(TRUE)` allows you to simulate how your tests will run on CRAN (#2112). * `expect_no_*()` now executes the entire code block, rather than stopping at the first message or warning (#1991). * `expect_no_failures()` and `expect_no_successes()` are now deprecated as `expect_success()` now test for no failures and `expect_failure()` tests for no successes (#) diff --git a/R/expect-match.R b/R/expect-match.R index d20cde158..d612f1cf9 100644 --- a/R/expect-match.R +++ b/R/expect-match.R @@ -106,6 +106,7 @@ expect_match_ <- function( info = NULL, label = NULL, negate = FALSE, + title = "Text", trace_env = caller_env() ) { matches <- grepl(regexp, act$val, perl = perl, fixed = fixed, ...) @@ -118,14 +119,24 @@ expect_match_ <- function( text <- encodeString(act$val) if (length(act$val) == 1) { - values <- paste0('Text: "', text, '"') + values <- paste0(title, ': "', text, '"') + which <- "" } else { - values <- paste0("Text:\n", paste0("* ", text, collapse = "\n")) + bullet <- ifelse( + condition, + cli::col_green(cli::symbol$tick), + cli::col_red(cli::symbol$cross) + ) + values <- paste0(title, ":\n", paste0(bullet, " ", text, collapse = "\n")) + which <- if (all) "Every element of " else "Some element of " } + match <- if (negate) "matches" else "does not match" msg <- sprintf( - if (negate) "%s matches %s %s.\n%s" else "%s does not match %s %s.\n%s", + "%s%s %s %s %s.\n%s", + which, act$lab, + match, if (fixed) "string" else "regexp", encodeString(regexp, quote = '"'), values diff --git a/R/expect-output.R b/R/expect-output.R index 9605e54f2..65c16e950 100644 --- a/R/expect-output.R +++ b/R/expect-output.R @@ -48,6 +48,6 @@ expect_output <- function( pass(act$val) } else { act <- labelled_value(act$cap, act$lab) - expect_match_(act, enc2native(regexp), ...) + expect_match_(act, enc2native(regexp), ..., title = "Output") } } diff --git a/tests/testthat/_snaps/expect-match.md b/tests/testthat/_snaps/expect-match.md index f966e6ee9..6e148e3e1 100644 --- a/tests/testthat/_snaps/expect-match.md +++ b/tests/testthat/_snaps/expect-match.md @@ -9,13 +9,19 @@ --- - `many` does not match regexp "asdf". + Every element of `many` does not match regexp "a". Text: - * a - * b - * c - * d - * e + ✔ a + ✔ a + ✖ b + +--- + + Some element of `many` does not match regexp "c". + Text: + ✖ a + ✖ a + ✖ b # expect_match validates its inputs diff --git a/tests/testthat/_snaps/expect-output.md b/tests/testthat/_snaps/expect-output.md index 393572b7f..71af3d211 100644 --- a/tests/testthat/_snaps/expect-output.md +++ b/tests/testthat/_snaps/expect-output.md @@ -1,7 +1,7 @@ # expect = string checks for match `g()` does not match regexp "x". - Text: "!" + Output: "!" --- diff --git a/tests/testthat/test-expect-match.R b/tests/testthat/test-expect-match.R index 47b30ee80..9e417c30d 100644 --- a/tests/testthat/test-expect-match.R +++ b/tests/testthat/test-expect-match.R @@ -1,12 +1,15 @@ test_that("generates useful failure messages", { + local_reproducible_output(unicode = TRUE) + zero <- character(0) expect_snapshot_failure(expect_match(zero, 'asdf')) one <- "bcde" expect_snapshot_failure(expect_match(one, 'asdf')) - many <- letters[1:5] - expect_snapshot_failure(expect_match(many, 'asdf')) + many <- c("a", "a", "b") + expect_snapshot_failure(expect_match(many, "a")) + expect_snapshot_failure(expect_match(many, "c", all = FALSE)) }) test_that("expect_match validates its inputs", {