Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

712 - shinytest2 Run all example apps #721

Merged
merged 23 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3993408
feat: adds shinytest2 tests on example apps
averissimo Apr 10, 2024
b8091a6
merge: from {tmc} upstream
averissimo Apr 10, 2024
71d9c94
chore: update from {tmc}
averissimo Apr 11, 2024
6781876
feat: adds regex exclusion for specific validation errors
averissimo Apr 12, 2024
26d657d
chore: remove pkgload as it no longer is needed
averissimo Apr 12, 2024
409dc16
chore: adds skip if too deep
averissimo Apr 12, 2024
4f2084e
chore: reinstates pkgload
averissimo Apr 12, 2024
677cd6e
chore: use print instead of mocked_runapp (which is equivalent)
averissimo Apr 12, 2024
6d7e8e6
fix: remove skip_on_cran in favor of testing depth
averissimo Apr 12, 2024
e94bd4d
pr: test with verbose mode that prints main active pane
averissimo Apr 12, 2024
ef14516
pr: inspects TESTING_DEPT on all examples
averissimo Apr 12, 2024
f6d1c5e
Merge branch 'main' into 477_shinytest2_examples@main
averissimo Apr 17, 2024
a6c801e
feat: use hint instead library/pkgload
averissimo Apr 17, 2024
a5a49d0
cleanup: remove debugging screenshot
averissimo Apr 17, 2024
ff4c9eb
fix: use binding
averissimo Apr 18, 2024
62e3e39
Merge branch 'main' into 477_shinytest2_examples@main
averissimo Apr 18, 2024
c670db5
Merge branch 'main' into 477_shinytest2_examples@main
averissimo Apr 19, 2024
845183b
fix: replace shiny::runApp with mocked_run_app
averissimo Apr 19, 2024
c8477fa
Merge branch 'main' into 477_shinytest2_examples@main
averissimo Apr 19, 2024
aa667c4
Merge branch 'main' into 477_shinytest2_examples@main
kartikeyakirar Apr 22, 2024
a43b0ce
Merge branch 'main' into 477_shinytest2_examples@main
kartikeyakirar Apr 22, 2024
422e1b3
Merge branch 'main' into 477_shinytest2_examples@main
kartikeyakirar Apr 22, 2024
b13ce16
Merge branch 'main' into 477_shinytest2_examples@main
vedhav Apr 22, 2024
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
5 changes: 4 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ Suggests:
nestcolor (>= 0.1.0),
rlang (>= 1.0.0),
rtables (>= 0.6.6),
rvest,
shinytest2,
pkgload,
sparkline,
testthat (>= 3.0.4),
withr (>= 2.0.0)
Expand All @@ -90,7 +92,8 @@ Config/Needs/verdepcheck: haleyjeppson/ggmosaic, tidyverse/ggplot2,
gridExtra, ramnathv/htmlwidgets, jeroen/jsonlite, yihui/knitr,
deepayan/lattice, daroczig/logger, MASS,
insightsengineering/nestcolor, r-lib/rlang,
insightsengineering/rtables, sparkline, rstudio/shinytest2,
insightsengineering/rtables, tidyverse/rvest, sparkline, rstudio/shinytest2,
r-lib/pkgload,
insightsengineering/teal.data, r-lib/testthat, r-lib/withr
Config/Needs/website: insightsengineering/nesttemplate
Encoding: UTF-8
Expand Down
2 changes: 1 addition & 1 deletion R/tm_a_pca.R
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
all_cols <- teal.data::col_labels(isolate(data())[[response[[i]]$dataname]])
ignore_cols <- unlist(teal.data::join_keys(isolate(data()))[[response[[i]]$dataname]])
color_cols <- all_cols[!names(all_cols) %in% ignore_cols]
response[[i]]$select$choices <- choices_labeled(names(color_cols), color_cols)
response[[i]]$select$choices <- teal.transform::choices_labeled(names(color_cols), color_cols)
}

selector_list <- teal.transform::data_extract_multiple_srv(
Expand Down
2 changes: 2 additions & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@

### global variables
ggplot_themes <- c("gray", "bw", "linedraw", "light", "dark", "minimal", "classic", "void")

interactive <- NULL
averissimo marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions tests/testthat/man
171 changes: 171 additions & 0 deletions tests/testthat/test-examples.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# this test requires a `man` directory in the `tests/testthat` directory
# (presumably symlinked to the package root `man` directory to avoid duplication)
# this also requires `devtools::document()` to be run before running the tests

rd_files <- function() {
man_path <- if (testthat::is_checking()) {
testthat::test_path("..", "..", "00_pkg_src", testthat::testing_package(), "man")
} else {
testthat::test_path("..", "..", "man")
}

if (!dir.exists(man_path)) {
stop("Cannot find path to `man` directory.")
}

list.files(
man_path,
pattern = "\\.[Rr]d$",
full.names = TRUE
)
}

suppress_warnings <- function(expr, pattern = "*", ...) {
withCallingHandlers(
expr,
warning = function(w) {
if (grepl(pattern, conditionMessage(w))) {
invokeRestart("muffleWarning")
}
}
)
}

with_mocked_app_bindings <- function(code) {
shiny__shinyApp <- shiny::shinyApp # nolint object_name.
shiny__runApp <- shiny::runApp # nolint object_name.
averissimo marked this conversation as resolved.
Show resolved Hide resolved
# workaround of https://github.com/rstudio/shinytest2/issues/381
# change to `print(shiny__shinyApp(...))` and remove allow warning once fixed
mocked_shinyApp <- function(ui, server, ...) { # nolint object_linter.
functionBody(server) <- bquote({
.hint_to_load_package <- add_facet_labels # Hint to shinytest2 when looking for packages in globals
.(functionBody(server))
})
shiny::runApp(do.call(shiny__shinyApp, append(x = list(ui = ui, server = server), list(...))))
}

mocked_runApp <- function(x, ...) { # nolint object_name_linter.
args <- list(...)
args[["launch.browser"]] <- FALSE # needed for RStudio

app_driver <- shinytest2::AppDriver$new(
x,
shiny_args = args,
timeout = 20 * 1000,
load_timeout = 30 * 1000,
check_names = FALSE, # explicit check below
options = options() # https://github.com/rstudio/shinytest2/issues/377
)
on.exit(app_driver$stop(), add = TRUE)
app_driver$wait_for_idle()

# Simple testing
## warning in the app does not invoke a warning in the test
## https://github.com/rstudio/shinytest2/issues/378
app_logs <- subset(app_driver$get_logs(), location == "shiny")[["message"]]

# Check if the teal app has content (indicator of a Shiny App fatal error)
if (identical(trimws(app_driver$get_text("#teal-main_ui_container")), "")) {
tryCatch(
app_driver$wait_for_idle(duration = 2000), # wait 2 seconds for session to disconnect
error = function(err) {
stop(
sprintf(
"Teal Application is empty. An Error may have occured:\n%s",
paste0(subset(app_driver$get_logs(), location == "shiny")[["message"]], collapse = "\n")
)
)
}
)
}

# allow `Warning in file(con, "r")` warning coming from pkgload::load_all()
if (any(grepl("Warning in.*", app_logs) & !grepl("Warning in file\\(con, \"r\"\\)", app_logs))) {
warning(
sprintf(
"Detected a warning in the application logs:\n%s",
paste0(app_logs, collapse = "\n")
)
)
}

## Throw an error instead of a warning (default `AppDriver$new(..., check_names = TRUE)` throws a warning)
app_driver$expect_unique_names()

err_el <- Filter(
function(x) {
allowed_errors <- getOption("test_examples.discard_error_regex", "")
identical(allowed_errors, "") || !grepl(allowed_errors, x)
},
app_driver$get_html(".shiny-output-error")
)

## shinytest2 captures app crash but teal continues on error inside the module
## we need to use a different way to check if there are errors
if (!is.null(err_el) && length(err_el) > 0) {
stop(sprintf("Module error is observed:\n%s", err_el))
}

## validation errors from shinyvalidate - added by default to assure the examples are "clean"
if (!is.null(err_el <- app_driver$get_html(".shiny-input-container.has-error:not(.shiny-output-error-validation)"))) { # nolint line_length_linter.
stop(sprintf("shinyvalidate error is observed:\n%s", err_el))
}
}

# support both `shinyApp(...)` as well as prefixed `shiny::shinyApp(...)` calls
# mock `shinyApp` to `shiny::shinyApp` and `shiny::shinyApp` to custom function
# same for `runApp(...)` and `shiny::runApp`
# additionally mock `interactive()`
testthat::with_mocked_bindings(
testthat::with_mocked_bindings(
code,
shinyApp = shiny::shinyApp,
runApp = shiny::runApp,
interactive = function() TRUE
),
shinyApp = mocked_shinyApp,
runApp = mocked_runApp,
.package = "shiny"
)
}

strict_exceptions <- c(
# https://github.com/r-lib/gtable/pull/94
"tm_outliers.Rd",
"tm_g_response.Rd",
"tm_a_pca.Rd"
)

discard_validation_regex <- list(
"tm_file_viewer.Rd" = "Please select a file\\.",
"tm_g_distribution.Rd" = "Please select a test"
)

for (i in rd_files()) {
testthat::test_that(
paste0("example-", basename(i)),
{
skip_if_too_deep(5)
if (basename(i) %in% strict_exceptions) {
op <- options()
withr::local_options(opts_partial_match_old)
withr::defer(options(op))
}
# Allow for specific validation errors for individual examples
withr::local_options(
list(
"test_examples.discard_error_regex" = discard_validation_regex[[basename(i)]]
)
)
with_mocked_app_bindings(
# suppress warnings coming from saving qenv https://github.com/insightsengineering/teal.code/issues/194
suppress_warnings(
testthat::expect_no_error(
pkgload::run_example(i, run_donttest = TRUE, run_dontrun = FALSE, quiet = TRUE)
),
"may not be available when loading"
)
)
}
)
}
Loading