Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ Imports:
vdiffr (>= 1.0.7)
Suggests:
ggplot2
RoxygenNote: 7.3.3
Roxygen: list(markdown = TRUE)
Config/roxygen2/version: 8.0.0
29 changes: 23 additions & 6 deletions R/testthat-helper-plots.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
#' This function compares a stored .svg of a plot, to the plot that is created when the tests are run.
#' If no visual reference (.svg) exists yet, \pkg{vdiffr} handles it like other visual snapshots.
#'
#' For \pkg{ggplot2} objects, a structural fallback snapshot is also maintained.
#' Plot recipes are materialized before they are passed to \pkg{vdiffr}. For
#' rendered \pkg{ggplot2} objects, a structural fallback snapshot is also maintained.
#' In interactive test runs, if that structural snapshot is missing, it is created automatically
#' (even when the visual comparison passes).
#'
#' To accept changed structural snapshots, use \code{testthat::snapshot_accept()} from the
#' package root after running tests.
#'
#'
#' @param test The plot object you wish to test (does not work well for non-ggplot2 objects).
#' @param test The plot object or plot recipe you wish to test.
#' @param name The name of the reference plot (a .svg stored in /tests/testthat/_snaps).
#' @param dir `r lifecycle::badge('deprecated')`
#'
Expand All @@ -36,6 +37,9 @@ expect_equal_plots <- function(test, name, dir = lifecycle::deprecated(), tolera
return()
}

originalTest <- test
test <- materialize_plot_for_vdiffr(test)

skip_if_grob(test)
skip_if_recordedPlot(test)

Expand All @@ -49,22 +53,35 @@ expect_equal_plots <- function(test, name, dir = lifecycle::deprecated(), tolera
if (inherits(test, "qgraph")) {
qq <- test
test <- function() plot(qq)
originalTest <- test
}
expect_plot_with_fallback(name, test, tolerance = tolerance)
fallbackTest <- if (is_ggplot(test)) test else originalTest
expect_plot_with_fallback(name, test, fallback_test = fallbackTest, tolerance = tolerance)
}
}

expect_plot_with_fallback <- function(name, test, tolerance = NULL) {
materialize_plot_for_vdiffr <- function(test) {
if (!("jaspPlotRecipe" %in% sub("^.*::", "", class(test))))
return(test)

if (!requireNamespace("jaspGraphs", quietly = TRUE))
stop("Package 'jaspGraphs' is required to render a jaspPlotRecipe.", call. = FALSE)

materialize <- getExportedValue("jaspGraphs", "materializeJaspPlotRecipe")
materialize(test)
}

expect_plot_with_fallback <- function(name, test, fallback_test = test, tolerance = NULL) {
result <- capture_vdiffr_expectation(name, test)
if (isTRUE(result$passed)) {
maybe_seed_ggplot_structure_snapshot(test, name)
maybe_seed_ggplot_structure_snapshot(fallback_test, name)
return(invisible(TRUE))
}

# Save the CI-generated SVG so it can be uploaded as an artifact for comparison
save_failed_plot_svg(test, name)

fallbackResult <- expect_doppelganger_fallback(test, name, vdiffr_result = result, tolerance = tolerance)
fallbackResult <- expect_doppelganger_fallback(fallback_test, name, vdiffr_result = result, tolerance = tolerance)
if (isTRUE(fallbackResult$passed)) {
warning("vdiffr mismatch for '", name, "' accepted by structural fallback.", call. = FALSE)
testthat::succeed(paste0("vdiffr mismatch for '", name, "' accepted by fallback."))
Expand Down
17 changes: 14 additions & 3 deletions man/expect_equal_plots.Rd

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

68 changes: 68 additions & 0 deletions tests/testthat/test-expect-equal-plots-fallback.R
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,71 @@ test_that("compare_ggplot_structure_snapshot returns TRUE only for equal structu
expect_true(jaspTools:::compare_ggplot_structure_snapshot(f1, f2))
expect_false(jaspTools:::compare_ggplot_structure_snapshot(f1, f3))
})

test_that("vdiffr and fallback can use different plot objects", {
rendered <- structure(list(), class = "rendered-plot")
original <- structure(list(), class = "original-plot")
seen <- new.env(parent = emptyenv())

testthat::local_mocked_bindings(
capture_vdiffr_expectation = function(name, test) {
seen$vdiffr <- test
list(passed = FALSE, exception = simpleError("forced mismatch"))
},
save_failed_plot_svg = function(...) invisible(NULL),
expect_doppelganger_fallback = function(test, ...) {
seen$fallback <- test
list(passed = TRUE, has_fallback = TRUE, exception = NULL, message = NULL)
},
.package = "jaspTools"
)

expect_warning(
jaspTools:::expect_plot_with_fallback("separate-targets", rendered, fallback_test = original),
"accepted by structural fallback"
)
expect_identical(seen$vdiffr, rendered)
expect_identical(seen$fallback, original)
})

test_that("materialized ggplot is used for vdiffr and structural fallback", {
skip_if_not_installed("ggplot2")

recipe <- structure(list(fun = "example"), class = "jaspPlotRecipe")
rendered <- ggplot2::ggplot(mtcars, ggplot2::aes(wt, mpg)) + ggplot2::geom_point()
seen <- new.env(parent = emptyenv())

testthat::local_mocked_bindings(
materialize_plot_for_vdiffr = function(test) rendered,
expect_plot_with_fallback = function(name, test, fallback_test, ...) {
seen$vdiffr <- test
seen$fallback <- fallback_test
invisible(TRUE)
},
.package = "jaspTools"
)

jaspTools::expect_equal_plots(recipe, "recipe-plot")
expect_identical(seen$vdiffr, rendered)
expect_identical(seen$fallback, rendered)
})

test_that("non-ggplot recipe rendering keeps the recipe as fallback", {
recipe <- structure(list(fun = "example"), class = "jaspPlotRecipe")
rendered <- function() graphics::plot(1:3)
seen <- new.env(parent = emptyenv())

testthat::local_mocked_bindings(
materialize_plot_for_vdiffr = function(test) rendered,
expect_plot_with_fallback = function(name, test, fallback_test, ...) {
seen$vdiffr <- test
seen$fallback <- fallback_test
invisible(TRUE)
},
.package = "jaspTools"
)

jaspTools::expect_equal_plots(recipe, "recipe-function-plot")
expect_identical(seen$vdiffr, rendered)
expect_identical(seen$fallback, recipe)
})
Loading