diff --git a/.gitignore b/.gitignore index 42e5245..dbd7608 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ docs/ man/*.Rd !man/figures/ +man/figures/niffler_screenshots ~*.xlsx ~*.xlsm # {shinytest2}: Ignore new debug snapshots for `$expect_values()` diff --git a/DESCRIPTION b/DESCRIPTION index ed82381..2c07e68 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -17,7 +17,7 @@ Authors@R: ) Description: Helpers for shiny development. -URL: https://github.com/dataheld/niffler +URL: https://github.com/dataheld/niffler, https://niffler.maxheld.de BugReports: https://github.com/dataheld/niffler/issues License: file LICENSE Encoding: UTF-8 @@ -26,20 +26,21 @@ Language: en Roxygen: list(markdown = TRUE) Imports: checkmate, + fs, glue, purrr, rlang, shiny, + testthat Suggests: bslib, brio, elf, - fs, roxygen2, shinytest2, showimage, - testthat, - withr + withr, + magick Remotes: dataheld/elf Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 068dc07..62629e8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,21 +1,31 @@ # Generated by roxygen2: do not edit by hand +S3method(format,rd_section_nifflerInsertSnaps) S3method(roxygen2::roxy_tag_parse,roxy_tag_nifflerExamplesShiny) +S3method(roxygen2::roxy_tag_parse,roxy_tag_nifflerInsertSnaps) S3method(roxygen2::roxy_tag_rd,roxy_tag_nifflerExamplesShiny) +S3method(roxygen2::roxy_tag_rd,roxy_tag_nifflerInsertSnaps) export(abort_if_not_reactive) export(abort_if_reactive) export(counter_button_app) export(counter_button_server) export(counter_button_ui) +export(dir_ls_snaps) export(examples_app) export(get_screenshot_args_attr) export(get_screenshot_from_app) +export(glue_regexp_snaps) +export(image_animate_snaps) +export(image_write_snaps) export(mixed_react_tree_app) export(mixed_react_tree_server) export(mixed_react_tree_ui) export(module2app) export(module2app_server) export(module2app_ui) +export(snaps2fig) +export(snaps2md) +export(snaps2rd) export(x_counter_button_app) export(x_counter_button_server) export(x_counter_button_ui) diff --git a/R/helpers.R b/R/helpers.R index 2380538..6814204 100644 --- a/R/helpers.R +++ b/R/helpers.R @@ -23,3 +23,12 @@ examples_app <- function(example = "01_hello") { skip_if_load_all2 <- function() { elf::skip_if_pkg_installed_but_not_via_loadall("niffler") } + +skip_example_screenshots <- function() { + testthat::skip( + message = paste( + "Setting up example screenshots.", + "These need not be tested themselves, but are used in other tests." + ) + ) +} diff --git a/R/shiny2screenshot.R b/R/shiny2screenshot.R index 3d3c25a..221dd4d 100644 --- a/R/shiny2screenshot.R +++ b/R/shiny2screenshot.R @@ -18,14 +18,14 @@ NULL #' @rdname tag_shiny #' @details -#' - `@nifflerExamplesShiny${1:# example code}` +#' - `@nifflerExamplesShiny$ {1:# example code}` #' R code which returns a shiny app. #' A screenshot of the shiny app is added to the documentation, #' along with the code required to create the screenshot and #' launch the app interactively. #' Wraps @examples. #' @usage -#' # @nifflerExamplesShiny${1:# example code} +#' # @nifflerExamplesShiny ${1:# example code} #' @name nifflerExamplesShiny NULL @@ -68,8 +68,6 @@ check_installed_shinytest2 <- function() { ) } -# TODO add link to snapshot-reuse function -# https://github.com/dataheld/niffler/issues/13. #' Get screenshot from shiny app #' #' Wrapper around [shinytest2::AppDriver()]. @@ -125,10 +123,10 @@ get_screenshot_from_app <- function(appDir, glue::glue( "The screenshot could not be generated.", "Please check the logs for errors.", - sep = " " + .sep = " " ) }, - quiet = FALSE + quiet = TRUE ) } f_screenshot() @@ -149,3 +147,246 @@ get_screenshot_from_app_strictly <- function(appDir, withr::defer(driver$stop()) driver$get_screenshot(file = file) } + +# nifflerInsertSnaps tag ==== + +#' @rdname tag_shiny +#' @details +#' - `@nifflerInsertSnaps +#' ${1:test_file} +#' ${2:name} +#' ${3:auto_numbered} +#' ${4:variant} +#' ${5:fps}` +#' Instead of re-creating screenshots, +#' insert reused screenshots created by +#' [shinytest2](https://rstudio.github.io/shinytest2/) snapshot testing. +#' For arguments and defaults, see [snaps2fig()]. +#' You can also use [snaps2md()] directly, without a custom tag. +#' @usage +#' # @nifflerInsertSnaps +#' # ${1:test_file} +#' # ${2:name} +#' # ${3:auto_numbered} +#' # ${4:variant} +#' # ${5:fps} +#' @nifflerInsertSnaps +#' helpers +#' bins +#' FALSE +#' linux +#' @name nifflerInsertSnaps +NULL + +#' @exportS3Method roxygen2::roxy_tag_parse +roxy_tag_parse.roxy_tag_nifflerInsertSnaps <- function(x) { + check_installed_roxygen2() + roxygen2::tag_words(x, min = 1, max = 5) +} + +#' @exportS3Method roxygen2::roxy_tag_rd +roxy_tag_rd.roxy_tag_nifflerInsertSnaps <- function(x, base_path, env) { + args <- as.list(x[["val"]]) + if (length(args) >= 2) args[[3]] <- as.logical(args[[3]]) + roxygen2::rd_section( + type = "nifflerInsertSnaps", + value = rlang::exec(snaps2rd, !!!args) + ) +} + +#' @export +format.rd_section_nifflerInsertSnaps <- function(x, ...) { + paste0( + "\\section{Screenshots from Tests}{\n", + "\\if{html}", + x$value, + "\\if{latex}{Screenshots cannot be shown in this output format.}", + "}\n" + ) +} + +#' Get screenshots from snapshots +#' +#' Retrieves screenshots from +#' [testthat](https://testthat.r-lib.org)'s `_snaps/` directory. +#' If several files match `dir_ls_snaps()`, +#' they are merged into an animated gif. +#' @family documentation +#' @name get_screenshot_from_snaps +NULL + +#' @describeIn get_screenshot_from_snaps +#' Save screenshots to `man/figures` and return *relative* path from there. +#' @inheritParams glue_regexp_snaps +#' @export +snaps2fig <- function(test_file = character(), + name = NULL, + auto_numbered = TRUE, + variant = shinytest2::platform_variant(), + fps = 5, + ...) { + snaps_paths <- dir_ls_snaps( + test_file = test_file, + regexp = glue_regexp_snaps( + name = name, + auto_numbered = auto_numbered + ), + variant = variant + ) + if (length(snaps_paths) == 0) { + rlang::abort( + "No images were found." + ) + } + snaps_img <- image_animate_snaps(snaps = snaps_paths, fps = fps, ...) + path_for_results <- fs::path( + "man", + "figures", + "niffler_screenshots", + test_file, + if (!is.null(name)) name, + ext = unique(magick::image_info(snaps_img)$format) + ) + fs::dir_create(path = fs::path_dir(path_for_results)) + # side effect happens here + res <- image_write_snaps(snaps_img, path = path_for_results) + # roxygen2/man markdown expects relative paths from here + fs::path_rel(res, start = "man/figures") +} + +snap_alt_text <- function() "Screenshot from App" + +#' @describeIn get_screenshot_from_snaps +#' Save screenshots to `man/figures` and return markdown image markup, +#' to be inserted in roxygen2 documentation. +#' @param ... Arguments passed on to other functions. +#' @export +snaps2md <- function(...) { + path <- snaps2fig(...) + paste0("![", snap_alt_text(), "](", path, ")", collapse = "") +} + +#' @describeIn get_screenshot_from_snaps +#' Save screenshots to `man/figures` and return R documentation image markup, +#' to be inserted in R documentation. +#' For a custom roxygen2 tag with equivalent funcionality, +#' see [nifflerInsertSnaps()]. +#' @export +snaps2rd <- function(...) { + path <- snaps2fig(...) + paste0( + "{\\figure{", + path, + "}{options: width='100\\%' alt=", + snap_alt_text(), + "}}", + collapse = "" + ) +} + +#' @describeIn get_screenshot_from_snaps +#' List all testthat `_snaps/` screenshots +#' Finds all files for a variant, file and name. +#' +#' @section Matching several screenshots: +#' You can deposit several screenshots of a shiny app using +#' [shinytest2::AppDriver] in testing. +#' Use [dir_ls_snaps()] to identify all the resulting images. +#' Typically used for *consecutive* screenshots. +#' @param test_file +#' Name of the test file, in which the snapshots are generated, +#' *without*: +#' - the extension +#' - the `test-` prefix. +#' If you're using testthat convention, +#' this will be the name of the file in `R/`, +#' which you are currently testing. +#' @inheritParams shinytest2::AppDriver +#' @inheritParams testthat::expect_snapshot_file +#' @inheritParams fs::dir_ls +#' @export +dir_ls_snaps <- function(test_file = character(), + regexp = glue_regexp_snaps(), + variant = shinytest2::platform_variant()) { + checkmate::assert_string(test_file) + test_path <- testthat::test_path("_snaps", variant, test_file) + fs::dir_ls( + test_path, + all = FALSE, + recurse = FALSE, + type = "file", + regexp = regexp + ) +} + +#' Build the regular expression to match consecutive screenshots +#' +#' [shinytest2::AppDriver] uses several schemes to +#' name consecutive screenshot files. +#' Use this regex to capture paths of screenshots. +#' @param name +#' The `name` passed to [shinytest2::AppDriver] to be used for screenshots. +#' Can be `NULL`, for no filtering by name. +#' @param auto_numbered +#' If `TRUE`, filter for snapshot files automatically numbered +#' according to the scheme used by [shinytest2::AppDriver]. +#' If you pass a `name` only to `shinytest2::AppDriver$new()` (recommended), +#' and then invoke several `shinytest2::AppDriver$expect_snapshot()`, +#' they resulting snapshots will all have the same name, +#' appended by a counter from `000` to `999`. +#' If `FALSE`, any filename `{name}*.png` will be selected. +#' You may need to set `FALSE` +#' if you pass a name to`shinytest2::AppDriver$expect_snapshot()` +#' directly. +#' @family documentation +#' @export +glue_regexp_snaps <- function(name = NULL, auto_numbered = TRUE) { + checkmate::assert_string(name, null.ok = TRUE) + checkmate::assert_flag(auto_numbered) + glue::glue( + "^.*[\\\\/]", # path + if (is.null(name)) "" else "{name}", + if (!is.null(name) && auto_numbered) "-" else "", + if (auto_numbered) "\\d{{3}}" else ".*", + # shinytest2 only defaults to png + "\\.png$" + ) +} + +#' @describeIn get_screenshot_from_snaps +#' Read in screenshot. +#' If several, animate into a gif. +#' @param snaps +#' Vector of file names, as returned by [dir_ls_snaps()] +#' @inheritParams magick::image_animate +#' @inheritDotParams magick::image_animate +#' @return For [image_animate_snaps()] A `magick-image`. +#' @export +image_animate_snaps <- function(snaps = fs::path(), fps = 5, ...) { + if (any(!fs::file_exists(snaps))) rlang::abort("File could not be found.") + names(snaps) <- fs::path_file(snaps) + check_installed_magick() + # stripping helps to avoid spurious diffs + res <- magick::image_read(snaps, strip = TRUE) + if (length(snaps) == 1) { + res + } else { + magick::image_animate(res, fps = fps, ...) + } +} + +#' @describeIn get_screenshot_from_snaps +#' Write out (merged) screenshots to new path. +#' @inheritParams magick::image_write +#' @return For [image_write_snaps()], path to the (merged) screenshots. +#' @export +image_write_snaps <- function(image, path = withr::local_tempfile()) { + magick::image_write(image = image, path = path) +} + +check_installed_magick <- function() { + rlang::check_installed( + "magick", + reason = "magick is needed show `snaps`." + ) +} diff --git a/inst/examples/snaps2fig/example.R b/inst/examples/snaps2fig/example.R new file mode 100644 index 0000000..3953ab6 --- /dev/null +++ b/inst/examples/snaps2fig/example.R @@ -0,0 +1,7 @@ +#' An example documentation with inserted snaps +#' @nifflerInsertSnaps +#' helpers +#' bins +#' FALSE +#' linux +bins_app <- function() examples_app() diff --git a/tests/testthat/_snaps/linux/helpers/001.png b/tests/testthat/_snaps/linux/helpers/001.png new file mode 100644 index 0000000..da6e01a Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/001.png differ diff --git a/tests/testthat/_snaps/linux/helpers/002.png b/tests/testthat/_snaps/linux/helpers/002.png new file mode 100644 index 0000000..da6e01a Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/002.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bar.png b/tests/testthat/_snaps/linux/helpers/bar.png new file mode 100644 index 0000000..ce2647f Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bar.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-20.png b/tests/testthat/_snaps/linux/helpers/bins-20.png new file mode 100644 index 0000000..8eb3c32 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-20.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-21.png b/tests/testthat/_snaps/linux/helpers/bins-21.png new file mode 100644 index 0000000..3c80ac6 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-21.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-22.png b/tests/testthat/_snaps/linux/helpers/bins-22.png new file mode 100644 index 0000000..e355fee Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-22.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-23.png b/tests/testthat/_snaps/linux/helpers/bins-23.png new file mode 100644 index 0000000..1db1f32 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-23.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-24.png b/tests/testthat/_snaps/linux/helpers/bins-24.png new file mode 100644 index 0000000..612373a Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-24.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-25.png b/tests/testthat/_snaps/linux/helpers/bins-25.png new file mode 100644 index 0000000..28dead8 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-25.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-26.png b/tests/testthat/_snaps/linux/helpers/bins-26.png new file mode 100644 index 0000000..ae19eeb Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-26.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-27.png b/tests/testthat/_snaps/linux/helpers/bins-27.png new file mode 100644 index 0000000..85d330d Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-27.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-28.png b/tests/testthat/_snaps/linux/helpers/bins-28.png new file mode 100644 index 0000000..0cc6743 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-28.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-29.png b/tests/testthat/_snaps/linux/helpers/bins-29.png new file mode 100644 index 0000000..b121ec6 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-29.png differ diff --git a/tests/testthat/_snaps/linux/helpers/bins-30.png b/tests/testthat/_snaps/linux/helpers/bins-30.png new file mode 100644 index 0000000..1f86bdc Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/bins-30.png differ diff --git a/tests/testthat/_snaps/linux/helpers/foo.png b/tests/testthat/_snaps/linux/helpers/foo.png new file mode 100644 index 0000000..ce2647f Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/foo.png differ diff --git a/tests/testthat/_snaps/linux/helpers/mpg-001.png b/tests/testthat/_snaps/linux/helpers/mpg-001.png new file mode 100644 index 0000000..c4b9556 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/mpg-001.png differ diff --git a/tests/testthat/_snaps/linux/helpers/mpg-002.png b/tests/testthat/_snaps/linux/helpers/mpg-002.png new file mode 100644 index 0000000..c4b9556 Binary files /dev/null and b/tests/testthat/_snaps/linux/helpers/mpg-002.png differ diff --git a/tests/testthat/_snaps/linux/shiny2screenshot.md b/tests/testthat/_snaps/linux/shiny2screenshot.md new file mode 100644 index 0000000..c65a9ba --- /dev/null +++ b/tests/testthat/_snaps/linux/shiny2screenshot.md @@ -0,0 +1,33 @@ +# dir_ls_snaps: finds manually numbered, named screenshots + + Code + snaps + Output + _snaps/linux/helpers/bins-20.png _snaps/linux/helpers/bins-21.png + _snaps/linux/helpers/bins-22.png _snaps/linux/helpers/bins-23.png + _snaps/linux/helpers/bins-24.png _snaps/linux/helpers/bins-25.png + _snaps/linux/helpers/bins-26.png _snaps/linux/helpers/bins-27.png + _snaps/linux/helpers/bins-28.png _snaps/linux/helpers/bins-29.png + _snaps/linux/helpers/bins-30.png + +# dir_ls_snaps: finds automatically numbered, named screenshots + + Code + snaps + Output + _snaps/linux/helpers/mpg-001.png _snaps/linux/helpers/mpg-002.png + +# dir_ls_snaps: finds automatically numbered, unnamed screenshots + + Code + snaps + Output + _snaps/linux/helpers/001.png _snaps/linux/helpers/002.png + +# dir_ls_snaps: finds non-numbered, named screenshots + + Code + snaps + Output + _snaps/linux/helpers/foo.png + diff --git a/tests/testthat/_snaps/linux/shiny2screenshot/multiple.gif b/tests/testthat/_snaps/linux/shiny2screenshot/multiple.gif new file mode 100644 index 0000000..8edcbdc Binary files /dev/null and b/tests/testthat/_snaps/linux/shiny2screenshot/multiple.gif differ diff --git a/tests/testthat/_snaps/linux/shiny2screenshot/single.png b/tests/testthat/_snaps/linux/shiny2screenshot/single.png new file mode 100644 index 0000000..fbf8472 Binary files /dev/null and b/tests/testthat/_snaps/linux/shiny2screenshot/single.png differ diff --git a/tests/testthat/_snaps/mac/helpers/001.png b/tests/testthat/_snaps/mac/helpers/001.png new file mode 100644 index 0000000..a0a4cd8 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/001.png differ diff --git a/tests/testthat/_snaps/mac/helpers/002.png b/tests/testthat/_snaps/mac/helpers/002.png new file mode 100644 index 0000000..a0a4cd8 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/002.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bar.png b/tests/testthat/_snaps/mac/helpers/bar.png new file mode 100644 index 0000000..434fc1f Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bar.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-20.png b/tests/testthat/_snaps/mac/helpers/bins-20.png new file mode 100644 index 0000000..8c78e94 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-20.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-21.png b/tests/testthat/_snaps/mac/helpers/bins-21.png new file mode 100644 index 0000000..8a7fc0e Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-21.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-22.png b/tests/testthat/_snaps/mac/helpers/bins-22.png new file mode 100644 index 0000000..44e8fd0 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-22.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-23.png b/tests/testthat/_snaps/mac/helpers/bins-23.png new file mode 100644 index 0000000..ebd0cb3 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-23.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-24.png b/tests/testthat/_snaps/mac/helpers/bins-24.png new file mode 100644 index 0000000..5703399 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-24.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-25.png b/tests/testthat/_snaps/mac/helpers/bins-25.png new file mode 100644 index 0000000..53acdc6 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-25.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-26.png b/tests/testthat/_snaps/mac/helpers/bins-26.png new file mode 100644 index 0000000..d5566de Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-26.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-27.png b/tests/testthat/_snaps/mac/helpers/bins-27.png new file mode 100644 index 0000000..ef60620 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-27.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-28.png b/tests/testthat/_snaps/mac/helpers/bins-28.png new file mode 100644 index 0000000..140d2d1 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-28.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-29.png b/tests/testthat/_snaps/mac/helpers/bins-29.png new file mode 100644 index 0000000..944edb2 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-29.png differ diff --git a/tests/testthat/_snaps/mac/helpers/bins-30.png b/tests/testthat/_snaps/mac/helpers/bins-30.png new file mode 100644 index 0000000..969c768 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/bins-30.png differ diff --git a/tests/testthat/_snaps/mac/helpers/foo.png b/tests/testthat/_snaps/mac/helpers/foo.png new file mode 100644 index 0000000..434fc1f Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/foo.png differ diff --git a/tests/testthat/_snaps/mac/helpers/mpg-001.png b/tests/testthat/_snaps/mac/helpers/mpg-001.png new file mode 100644 index 0000000..95fd701 Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/mpg-001.png differ diff --git a/tests/testthat/_snaps/mac/helpers/mpg-002.png b/tests/testthat/_snaps/mac/helpers/mpg-002.png new file mode 100644 index 0000000..8eeb2cf Binary files /dev/null and b/tests/testthat/_snaps/mac/helpers/mpg-002.png differ diff --git a/tests/testthat/_snaps/mac/shiny2screenshot.md b/tests/testthat/_snaps/mac/shiny2screenshot.md new file mode 100644 index 0000000..1819392 --- /dev/null +++ b/tests/testthat/_snaps/mac/shiny2screenshot.md @@ -0,0 +1,33 @@ +# dir_ls_snaps: finds manually numbered, named screenshots + + Code + snaps + Output + _snaps/mac/helpers/bins-20.png _snaps/mac/helpers/bins-21.png + _snaps/mac/helpers/bins-22.png _snaps/mac/helpers/bins-23.png + _snaps/mac/helpers/bins-24.png _snaps/mac/helpers/bins-25.png + _snaps/mac/helpers/bins-26.png _snaps/mac/helpers/bins-27.png + _snaps/mac/helpers/bins-28.png _snaps/mac/helpers/bins-29.png + _snaps/mac/helpers/bins-30.png + +# dir_ls_snaps: finds automatically numbered, named screenshots + + Code + snaps + Output + _snaps/mac/helpers/mpg-001.png _snaps/mac/helpers/mpg-002.png + +# dir_ls_snaps: finds automatically numbered, unnamed screenshots + + Code + snaps + Output + _snaps/mac/helpers/001.png _snaps/mac/helpers/002.png + +# dir_ls_snaps: finds non-numbered, named screenshots + + Code + snaps + Output + _snaps/mac/helpers/foo.png + diff --git a/tests/testthat/_snaps/mac/shiny2screenshot/multiple.gif b/tests/testthat/_snaps/mac/shiny2screenshot/multiple.gif new file mode 100644 index 0000000..094d4e3 Binary files /dev/null and b/tests/testthat/_snaps/mac/shiny2screenshot/multiple.gif differ diff --git a/tests/testthat/_snaps/mac/shiny2screenshot/single.png b/tests/testthat/_snaps/mac/shiny2screenshot/single.png new file mode 100644 index 0000000..a86e8f5 Binary files /dev/null and b/tests/testthat/_snaps/mac/shiny2screenshot/single.png differ diff --git a/tests/testthat/_snaps/shiny2screenshot.md b/tests/testthat/_snaps/shiny2screenshot.md index 6e302dc..07afbdb 100644 --- a/tests/testthat/_snaps/shiny2screenshot.md +++ b/tests/testthat/_snaps/shiny2screenshot.md @@ -37,10 +37,40 @@ } } -# screenshots fail according to `strict` setting +# roxy_tag_nifflerInsertSnaps: can be parsed Code - suppressMessages(get_screenshot_from_app(counter_button_app(), name = "does_not_exist")) + roxygen2::parse_text(example)[[1]]$tags + Output + [[1]] + [: 1] @title 'An example documentation with inserted snaps' {parsed} + + [[2]] + [: 2] @nifflerInsertSnaps '...' {parsed} + + [[3]] + [: 7] @usage '' {parsed} + + [[4]] + [: 7] @.formals '' {unparsed} + + [[5]] + [: 7] @backref '' {parsed} + + +# roxy_tag_nifflerInsertSnaps: can be formatted + + Code + topic$get_section("nifflerInsertSnaps") + Output + \section{Screenshots from Tests}{ + \if{html}{\figure{niffler_screenshots/helpers/bins.gif}{options: width='100\%' alt=Screenshot from App}}\if{latex}{Screenshots cannot be shown in this output format.}} + + +# snaps2fig and friends work: writes out markdown syntax + + Code + res Output - The screenshot could not be generated.Please check the logs for errors. + [1] "![Screenshot from App](niffler_screenshots/helpers/bins.gif)" diff --git a/tests/testthat/test-helpers.R b/tests/testthat/test-helpers.R new file mode 100644 index 0000000..ff2778c --- /dev/null +++ b/tests/testthat/test-helpers.R @@ -0,0 +1,59 @@ +test_that("example app for manually numbered, named screenshots", { + bins <- 20:30 + purrr::walk( + bins, + function(bin) testthat::announce_snapshot_file(glue::glue("bins-{bin}.png")) + ) + skip_example_screenshots() + skip_if_load_all2() + driver <- shinytest2::AppDriver$new( + examples_app(), + variant = shinytest2::platform_variant(r_version = FALSE) + ) + purrr::walk( + bins, + function(bin) { + driver$set_inputs(bins = bin) + driver$expect_screenshot(name = glue::glue("bins-{bin}.png")) + } + ) +}) +test_that("example app for automatically numbered, named screenshots", { + announce_snapshot_file("mpg-001.png") + announce_snapshot_file("mpg-002.png") + skip_example_screenshots() + skip_if_load_all2() + driver <- shinytest2::AppDriver$new( + examples_app("04_mpg"), + name = "mpg", + variant = shinytest2::platform_variant(r_version = FALSE) + ) + driver$expect_screenshot() + driver$expect_screenshot() +}) +test_that("example app for automatically numbered, unnamed screenshots", { + announce_snapshot_file("001.png") + announce_snapshot_file("002.png") + skip_example_screenshots() + skip_if_load_all2() + driver <- shinytest2::AppDriver$new( + examples_app("05_sliders"), + variant = shinytest2::platform_variant(r_version = FALSE) + ) + driver$expect_screenshot() + driver$expect_screenshot() +}) +test_that("example app for non-numbered, named screenshots", { + announce_snapshot_file("foo.png") + announce_snapshot_file("bar.png") + skip_example_screenshots() + skip_if_load_all2() + driver <- shinytest2::AppDriver$new( + # 06_tabsets example was bad to use, + # because it uses rnorm and thus keeps changing + examples_app("07_widgets"), + variant = shinytest2::platform_variant(r_version = FALSE) + ) + driver$expect_screenshot(name = "foo") + driver$expect_screenshot(name = "bar") +}) diff --git a/tests/testthat/test-shiny2screenshot.R b/tests/testthat/test-shiny2screenshot.R index b9943d4..7f4589f 100644 --- a/tests/testthat/test-shiny2screenshot.R +++ b/tests/testthat/test-shiny2screenshot.R @@ -1,3 +1,5 @@ +variant <- shinytest2::platform_variant(r_version = FALSE) + describe("roxy_tag_nifflerExamplesShiny", { example <- brio::read_file( fs::path_package( @@ -17,33 +19,44 @@ describe("roxy_tag_nifflerExamplesShiny", { } ) }) -# these should use boring counter_button_app, not examples_app, -# because they're in this package and don't change -# upstream change from shiny examples could break screenshots -describe("get_screenshot_from_app", { - name <- "counter.png" - announce_snapshot_file(name = name) - skip_if_load_all2() - path <- withr::local_tempfile(fileext = ".png") - it( - "can record a screenshot", - { - get_screenshot_from_app(counter_button_app(), file = path) - expect_snapshot_file( - path = path, - name = name, - variant = shinytest2::platform_variant(r_version = FALSE) - ) - } - ) +# unnecessary wrapper, but otherwise skip has global scope, +# see #32 +test_that("get_screenshot_works", { + # these should use boring counter_button_app, not examples_app, + # because they're in this package and don't change + # upstream change from shiny examples could break screenshots + describe("get_screenshot_from_app", { + name <- "counter.png" + announce_snapshot_file(name = name) + skip_if_load_all2() + path <- withr::local_tempfile(fileext = ".png") + it( + "can record a screenshot", + { + get_screenshot_from_app(counter_button_app(), file = path) + expect_snapshot_file( + path = path, + name = name, + variant = variant + ) + } + ) + }) }) test_that("screenshots fail according to `strict` setting", { - expect_snapshot( + expect_equal( # messages must be supressed, # otherwise snapshot gets polluted with timestamps suppressMessages( get_screenshot_from_app(counter_button_app(), name = "does_not_exist") + ), + # oddly, a snapshot doesn't work here, + # but keeps getting deleted/re-added + glue::glue( + "The screenshot could not be generated.", + "Please check the logs for errors.", + .sep = " " ) ) expect_error( @@ -54,3 +67,135 @@ test_that("screenshots fail according to `strict` setting", { ) ) }) + +describe("roxy_tag_nifflerInsertSnaps", { + example <- brio::read_file( + fs::path_package( + package = "niffler", + "examples", "snaps2fig", "example", ext = "R" + ) + ) + it( + "can be parsed", + expect_snapshot(roxygen2::parse_text(example)[[1]]$tags) + ) + it( + "can be formatted", + { + topic <- roxygen2::roc_proc_text(roxygen2::rd_roclet(), example)[[1]] + expect_snapshot(topic$get_section("nifflerInsertSnaps")) + } + ) +}) + +describe("dir_ls_snaps", { + it("finds manually numbered, named screenshots", { + snaps <- dir_ls_snaps( + test_file = "helpers", + regexp = glue_regexp_snaps( + name = "bins", + auto_numbered = FALSE + ), + variant = variant + ) + expect_snapshot(snaps, variant = variant) + }) + it("finds automatically numbered, named screenshots", { + snaps <- dir_ls_snaps( + test_file = "helpers", + regexp = glue_regexp_snaps( + name = "mpg", + auto_numbered = FALSE + ), + variant = variant + ) + expect_snapshot(snaps, variant = variant) + }) + it("finds automatically numbered, unnamed screenshots", { + snaps <- dir_ls_snaps( + test_file = "helpers", + regexp = glue_regexp_snaps(), + variant = variant + ) + expect_snapshot(snaps, variant = variant) + }) + it("finds non-numbered, named screenshots", { + snaps <- dir_ls_snaps( + test_file = "helpers", + regexp = glue_regexp_snaps( + name = "foo", + auto_numbered = FALSE + ), + variant = variant + ) + expect_snapshot(snaps, variant = variant) + }) +}) + +describe("map_snaps_animate", { + it("fails if file is missing", { + expect_error(map_snaps_animate("i-do-not-exist")) + expect_error(map_snaps_animate(c("i-do-not-exist", "me-neither"))) + }) + it("reads in single screenshot", { + snaps <- dir_ls_snaps( + test_file = "helpers", + regexp = glue_regexp_snaps( + name = "foo", + auto_numbered = FALSE + ), + variant = variant + ) + path <- withr::local_tempfile() + testthat::expect_snapshot_file( + path = image_animate_snaps(snaps) |> image_write_snaps(path = path), + name = "single.png", + variant = variant + ) + }) + it("reads in multiple screenshots", { + snaps <- dir_ls_snaps( + test_file = "helpers", + regexp = glue_regexp_snaps( + name = "bins", + auto_numbered = FALSE + ), + variant = variant + ) + path <- withr::local_tempfile() + testthat::expect_snapshot_file( + path = image_animate_snaps(snaps) |> image_write_snaps(path = path), + name = "multiple.gif", + variant = variant + ) + }) +}) + +describe("snaps2fig and friends work", { + output_path <- "man/figures/niffler_screenshots/helpers/bins.gif" + withr::defer(fs::file_delete(output_path)) + it("writes out snapshots to man folder", { + res <- snaps2fig( + test_file = "helpers", + name = "bins", + auto_numbered = FALSE, + variant = variant + ) + checkmate::expect_file_exists( + "man/figures/niffler_screenshots/helpers/bins.gif" + ) + expect_equal( + res, + fs::path("niffler_screenshots", "helpers", "bins", ext = "gif") + ) + }) + it("writes out markdown syntax", { + res <- snaps2md( + test_file = "helpers", + name = "bins", + auto_numbered = FALSE, + variant = variant + ) + expect_snapshot(res) + }) +})