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

Warnings coming from inside of server are not captured as a test warning #378

Open
pawelru opened this issue Mar 25, 2024 · 0 comments
Open

Comments

@pawelru
Copy link

pawelru commented Mar 25, 2024

Consider a following example:

testthat::test_that("dummy description", {
    app <- shiny::shinyApp(
        ui = shiny::fluidPage({
            shiny::tags$p(id = "hello", "hello")
        }),
        server = function(input, output) {
            warning(1)
            NULL
        }
    )
    app_drier <- shinytest2::AppDriver$new(app)
    app_drier$expect_unique_names()
})
#> Test passed 😀

Created on 2024-03-25 with reprex v2.1.0

This is a false positive. I would expect it to throw a test warning. Similar to what it does in case of warning is in UI function:

testthat::test_that("dummy description", {
    app <- shiny::shinyApp(
        ui = shiny::fluidPage({
            warning(1)
            shiny::tags$p(id = "hello", "hello")
        }),
        server = function(input, output) {
            NULL
        }
    )
    app_drier <- shinytest2::AppDriver$new(app)
    app_drier$expect_unique_names()
})
#> ── Warning: dummy description ──────────────────────────────────────────────────
#> 1
#> Backtrace:
#>     ▆
#>  1. ├─shiny::shinyApp(...)
#>  2. │ └─shiny:::uiHttpHandler(ui, uiPattern)
#>  3. │   └─base::force(ui)
#>  4. └─shiny::fluidPage(...)
#>  5.   ├─shiny::bootstrapPage(...)
#>  6.   │ └─rlang::list2(...)
#>  7.   └─htmltools::div(class = "container-fluid", ...)
#>  8.     └─rlang::dots_list(...)

Created on 2024-03-25 with reprex v2.1.0

averissimo added a commit to insightsengineering/teal.modules.general that referenced this issue Apr 22, 2024
# Pull Request

<!--- Replace `#nnn` with your issue link for reference. -->

Part of #712 

#### Changes description

- Adds tests that iterate on each documentation file and runs the
examples apps by mocking `interactive` and
`shinyApp` functions.
- Checks if there are no errors nor validation errors (with exceptions)
- Implements
insightsengineering/teal.modules.clinical#983 on
this repository

#### Changes from
insightsengineering/teal.modules.clinical#983

- Adds:
  - Regex rules define "accepted" validation errors
- Fixes:
- Reverts to use `library` instead of `pkgload::load_all` due to
problems with `system.file` call that cannot find package files.

```diff
diff -u teal.modules.clinical/tests/testthat/test-examples.R teal.modules.general/tests/testthat/test-examples.R 
--- teal.modules.clinical/tests/testthat/test-examples.R	2024-04-12 10:32:33.100707738 +0200
+++ teal.modules.general/tests/testthat/test-examples.R	2024-04-12 10:26:27.645642183 +0200
@@ -38,12 +38,7 @@ with_mocked_app_bindings <- function(code) {
   # change to `print(shiny__shinyApp(...))` and remove allow warning once fixed
   mocked_shinyApp <- function(ui, server, ...) { # nolint object_name_linter.
     functionBody(server) <- bquote({
-      pkgload::load_all(
-        .(normalizePath(file.path(testthat::test_path(), "..", ".."))),
-        export_all = FALSE,
-        attach_testthat = FALSE,
-        warn_conflicts = FALSE
-      )
+      library(.(testthat::testing_package()), character.only = TRUE)
       .(functionBody(server))
     })
     print(do.call(shiny__shinyApp, append(x = list(ui = ui, server = server), list(...))))
@@ -56,16 +51,34 @@ with_mocked_app_bindings <- function(code) {
     app_driver <- shinytest2::AppDriver$new(
       x,
       shiny_args = args,
+      timeout = 20 * 1000,
+      load_timeout = 30 * 1000,
       check_names = FALSE, # explicit check below
       options = options() # rstudio/shinytest2#377
     )
     on.exit(app_driver$stop(), add = TRUE)
-    app_driver$wait_for_idle(timeout = 20000)
+    app_driver$wait_for_idle()
 
     # Simple testing
     ## warning in the app does not invoke a warning in the test
     ## rstudio/shinytest2#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(
@@ -79,9 +92,17 @@ with_mocked_app_bindings <- function(code) {
     ## 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 <- app_driver$get_html(".shiny-output-error"))) {
+    if (!is.null(err_el) && length(err_el) > 0) {
       stop(sprintf("Module error is observed:\n%s", err_el))
     }
 
@@ -110,11 +131,14 @@ with_mocked_app_bindings <- function(code) {
 
 strict_exceptions <- c(
   # r-lib/gtable#94
-  "tm_g_barchart_simple.Rd",
-  "tm_g_ci.Rd",
-  "tm_g_ipp.Rd",
-  "tm_g_pp_adverse_events.Rd",
-  "tm_g_pp_vitals.Rd"
+  "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()) {
@@ -122,11 +146,18 @@ for (i in rd_files()) {
     paste0("example-", basename(i)),
     {
       testthat::skip_on_cran()
+      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 insightsengineering/teal.code#194
         suppress_warnings(```

---------

Signed-off-by: André Veríssimo <[email protected]>
Co-authored-by: kartikeya kirar <[email protected]>
Co-authored-by: Vedha Viyash <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant