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

run examples using shinytest2 #983

Merged
merged 29 commits into from
Apr 11, 2024
Merged

run examples using shinytest2 #983

merged 29 commits into from
Apr 11, 2024

Conversation

pawelru
Copy link
Contributor

@pawelru pawelru commented Jan 26, 2024

UPDATE: This is meant to be an example implementation copy&pasted in other repos.

This is what I came up with when testing examples against strict argument match (a separate PR; let's not discuss it here) and then I realize this could be an alternative solution to #917. This is a draft and remain draft for a time being. The aim is to compare and discuss alternative approaches.

CC @vedhav

After additional silencing logger (a separate PR not to mix it here) this is the output:

r$> devtools::test(filter = "examples")
ℹ Testing teal.modules.clinical
✔ | F W  S  OK | Context
✖ | 1      114 | examples [3.2s]                                   
───────────────────────────────────────────────────────────────────
Failure (test-examples.R:19:9): example-template_binary_outcome.Rd
Expected `suppress_warnings(...)` to run without any errors.
i Actually got a <simpleError> with text:
  object 'adrs' not found
───────────────────────────────────────────────────────────────────

══ Results ════════════════════════════════════════════════════════
Duration: 3.2 s

── Failed tests ───────────────────────────────────────────────────
Failure (test-examples.R:19:9): example-template_binary_outcome.Rd
Expected `suppress_warnings(...)` to run without any errors.
i Actually got a <simpleError> with text:
  object 'adrs' not found

[ FAIL 1 | WARN 0 | SKIP 0 | PASS 114 ]

Locally, it took ~5secs to get all the examples tested

library(tictoc)

tic()
capture.output(
    devtools::test("/Users/ruckip/Documents/repo/gh/insightsengineering/teal.modules.clinical", filter = "examples"),
    file = nullfile()
)
#> ℹ Testing teal.modules.clinical
#> Registered S3 method overwritten by 'teal':
#>   method        from      
#>   c.teal_slices teal.slice
#> 
#> Registered S3 method overwritten by 'tern':
#>   method   from 
#>   tidy.glm broom
#> 
#> mmrm() registered as emmeans extension
toc()
#> 4.778 sec elapsed

Created on 2024-01-26 with reprex v2.1.0

@donyunardi
Copy link
Contributor

Hi @pawelru

I thought we have some reservation with using shinytest2 when discussion this with @cicdguy in the past.
Is that no longer the case?

@cicdguy
Copy link
Contributor

cicdguy commented Jan 30, 2024

Hi @pawelru

I thought we have some reservation with using shinytest2 when discussion this with @cicdguy in the past.

Is that no longer the case?

It is no longer the case. Unleash the shinytest2 tests at will!

@m7pr
Copy link
Contributor

m7pr commented Feb 9, 2024

Hey, using shinytest2 brings less overhead than using cypress. Unless for this example. We do not have any configuration files, nor directories in tests/ that only cypress developer understands. All is written in R, not JavaScript, and this does not require any extra software (like node). If we agree that JavaScript is a common language across insightsengineering group then I guess this should be fine, but for now I see we all talk mainly R?

@cicdguy
Copy link
Contributor

cicdguy commented Feb 9, 2024

I agree with @m7pr. There are many tools and technologies for app testing, but we should keep our codebase homogeneous and use shinytest2.

@vedhav
Copy link
Contributor

vedhav commented Feb 9, 2024

but for now I see we all talk mainly R

True, But JS + Shiny unlocks so much potential for the apps. The fact that we can offload some tasks outside server, increases the scalability and performance of the apps just to name one advantage. I truly believe that we will implement some CSS and JS features in teal apps moving forward. I see these tests as a one-time thing that we set up like our CI and don't make drastic changes to it until we add new features that need testing and, just adding a test case should not be that complicated to learn in my opinion.

@m7pr
Copy link
Contributor

m7pr commented Feb 9, 2024

just adding a test case should not be that complicated to learn in my opinion.

Depending on who is going to write and read those tests. I personally do not understand tests written in cypress/JavaScript so I will not be able to help. Does it go any further? Should we allow writing statistical modules in python or Java for statistical computing, if they can be covered in teal module?

@vedhav
Copy link
Contributor

vedhav commented Feb 9, 2024

Does it go any further? Should we allow writing statistical modules in python or Java for statistical computing, if they can be covered in teal module?

Okay, I get your point. It's valid. We think so deeply about just adding an R package dependency to our packages. Adding a whole new programming language increases the complexity of maintaining it. The question is: Does this justify the benefits of cypress over shinytest2. I don't know yet. Let's refine our test specs to understand how much effort it tasks to implement and maintain them in both these testing frameworks.

tests/testthat/man Outdated Show resolved Hide resolved
@pawelru pawelru marked this pull request as ready for review March 11, 2024 10:39
Copy link
Contributor

github-actions bot commented Mar 11, 2024

Unit Tests Summary

  1 files   34 suites   4s ⏱️
260 tests 150 ✅ 110 💤 0 ❌
392 runs  170 ✅ 222 💤 0 ❌

Results for commit f6d93a2.

♻️ This comment has been updated with latest results.

Copy link
Contributor

github-actions bot commented Mar 11, 2024

Unit Test Performance Difference

Test Suite $Status$ Time on main $±Time$ $±Tests$ $±Skipped$ $±Failures$ $±Errors$
examples 👶 $+1.52$ $+110$ $+110$ $0$ $0$
Additional test case details
Test Suite $Status$ Time on main $±Time$ Test Case
examples 👶 $+0.01$ example_add_expr.Rd
examples 👶 $+0.06$ example_arm_ref_comp_observer.Rd
examples 👶 $+0.04$ example_as_num.Rd
examples 👶 $+0.02$ example_bracket_expr.Rd
examples 👶 $+0.01$ example_call_concatenate.Rd
examples 👶 $+0.02$ example_check_arm_ref_comp.Rd
examples 👶 $+0.02$ example_clean_description.Rd
examples 👶 $+0.02$ example_color_lab_values.Rd
examples 👶 $+0.02$ example_column_annotation_label.Rd
examples 👶 $+0.01$ example_control_tte.Rd
examples 👶 $+0.02$ example_cs_to_des_filter.Rd
examples 👶 $+0.01$ example_cs_to_des_select.Rd
examples 👶 $+0.02$ example_cs_to_filter_spec.Rd
examples 👶 $+0.01$ example_cs_to_select_spec.Rd
examples 👶 $+0.02$ example_default_total_label.Rd
examples 👶 $+0.02$ example_ex_data.Rd
examples 👶 $+0.01$ example_extract_input.Rd
examples 👶 $+0.01$ example_facet_grid_formula.Rd
examples 👶 $+0.03$ example_get_g_forest_obj_var_name.Rd
examples 👶 $+0.01$ example_get_paramcd_label.Rd
examples 👶 $+0.01$ example_get_var_labels.Rd
examples 👶 $+0.01$ example_h_concat_expr.Rd
examples 👶 $+0.01$ example_is.cs_or_des.Rd
examples 👶 $+0.01$ example_make_barchart_simple_call.Rd
examples 👶 $+0.01$ example_module_arguments.Rd
examples 👶 $+0.01$ example_pipe_expr.Rd
examples 👶 $+0.01$ example_prepare_arm.Rd
examples 👶 $+0.01$ example_prepare_arm_levels.Rd
examples 👶 $+0.01$ example_split_choices.Rd
examples 👶 $+0.01$ example_split_col_expr.Rd
examples 👶 $+0.01$ example_split_interactions.Rd
examples 👶 $+0.01$ example_substitute_names.Rd
examples 👶 $+0.01$ example_substitute_q.Rd
examples 👶 $+0.01$ example_teal.modules.clinical_package.Rd
examples 👶 $+0.01$ example_template_a_gee.Rd
examples 👶 $+0.01$ example_template_abnormality.Rd
examples 👶 $+0.01$ example_template_abnormality_by_worst_grade.Rd
examples 👶 $+0.01$ example_template_adverse_events.Rd
examples 👶 $+0.01$ example_template_ancova.Rd
examples 👶 $+0.01$ example_template_arguments.Rd
examples 👶 $+0.01$ example_template_basic_info.Rd
examples 👶 $+0.02$ example_template_binary_outcome.Rd
examples 👶 $+0.01$ example_template_coxreg_m.Rd
examples 👶 $+0.01$ example_template_coxreg_u.Rd
examples 👶 $+0.01$ example_template_events.Rd
examples 👶 $+0.01$ example_template_events_by_grade.Rd
examples 👶 $+0.01$ example_template_events_col_by_grade.Rd
examples 👶 $+0.01$ example_template_events_patyear.Rd
examples 👶 $+0.01$ example_template_events_summary.Rd
examples 👶 $+0.01$ example_template_exposure.Rd
examples 👶 $+0.01$ example_template_fit_mmrm.Rd
examples 👶 $+0.01$ example_template_forest_rsp.Rd
examples 👶 $+0.01$ example_template_forest_tte.Rd
examples 👶 $+0.01$ example_template_g_ci.Rd
examples 👶 $+0.01$ example_template_g_ipp.Rd
examples 👶 $+0.01$ example_template_g_km.Rd
examples 👶 $+0.01$ example_template_g_lineplot.Rd
examples 👶 $+0.01$ example_template_laboratory.Rd
examples 👶 $+0.01$ example_template_logistic.Rd
examples 👶 $+0.01$ example_template_medical_history.Rd
examples 👶 $+0.01$ example_template_mult_events.Rd
examples 👶 $+0.01$ example_template_patient_timeline.Rd
examples 👶 $+0.01$ example_template_prior_medication.Rd
examples 👶 $+0.01$ example_template_shift_by_arm.Rd
examples 👶 $+0.02$ example_template_shift_by_arm_by_worst.Rd
examples 👶 $+0.01$ example_template_shift_by_grade.Rd
examples 👶 $+0.01$ example_template_smq.Rd
examples 👶 $+0.01$ example_template_summary.Rd
examples 👶 $+0.01$ example_template_summary_by.Rd
examples 👶 $+0.01$ example_template_therapy.Rd
examples 👶 $+0.01$ example_template_tte.Rd
examples 👶 $+0.01$ example_template_vitals.Rd
examples 👶 $+0.01$ example_tm_a_gee.Rd
examples 👶 $+0.01$ example_tm_a_mmrm.Rd
examples 👶 $+0.01$ example_tm_g_barchart_simple.Rd
examples 👶 $+0.01$ example_tm_g_ci.Rd
examples 👶 $+0.01$ example_tm_g_forest_rsp.Rd
examples 👶 $+0.01$ example_tm_g_forest_tte.Rd
examples 👶 $+0.01$ example_tm_g_ipp.Rd
examples 👶 $+0.01$ example_tm_g_km.Rd
examples 👶 $+0.01$ example_tm_g_lineplot.Rd
examples 👶 $+0.01$ example_tm_g_pp_adverse_events.Rd
examples 👶 $+0.01$ example_tm_g_pp_patient_timeline.Rd
examples 👶 $+0.01$ example_tm_g_pp_therapy.Rd
examples 👶 $+0.01$ example_tm_g_pp_vitals.Rd
examples 👶 $+0.01$ example_tm_t_abnormality.Rd
examples 👶 $+0.02$ example_tm_t_abnormality_by_worst_grade.Rd
examples 👶 $+0.01$ example_tm_t_ancova.Rd
examples 👶 $+0.01$ example_tm_t_binary_outcome.Rd
examples 👶 $+0.01$ example_tm_t_coxreg.Rd
examples 👶 $+0.01$ example_tm_t_events.Rd
examples 👶 $+0.01$ example_tm_t_events_by_grade.Rd
examples 👶 $+0.01$ example_tm_t_events_patyear.Rd
examples 👶 $+0.01$ example_tm_t_events_summary.Rd
examples 👶 $+0.01$ example_tm_t_exposure.Rd
examples 👶 $+0.01$ example_tm_t_logistic.Rd
examples 👶 $+0.01$ example_tm_t_mult_events.Rd
examples 👶 $+0.01$ example_tm_t_pp_basic_info.Rd
examples 👶 $+0.01$ example_tm_t_pp_laboratory.Rd
examples 👶 $+0.01$ example_tm_t_pp_medical_history.Rd
examples 👶 $+0.01$ example_tm_t_pp_prior_medication.Rd
examples 👶 $+0.01$ example_tm_t_shift_by_arm.Rd
examples 👶 $+0.01$ example_tm_t_shift_by_arm_by_worst.Rd
examples 👶 $+0.01$ example_tm_t_shift_by_grade.Rd
examples 👶 $+0.01$ example_tm_t_smq.Rd
examples 👶 $+0.01$ example_tm_t_summary.Rd
examples 👶 $+0.01$ example_tm_t_summary_by.Rd
examples 👶 $+0.01$ example_tm_t_tte.Rd
examples 👶 $+0.01$ example_validate_arm.Rd
examples 👶 $+0.02$ example_validate_standard_inputs.Rd

Results for commit fcfa119

♻️ This comment has been updated with latest results.

@pawelru
Copy link
Contributor Author

pawelru commented Mar 11, 2024

Testing bot is reporting +110 tests and all of them are skipped 🙃 This is because of --as-cran flag in our workflow. It will be resolved here: https://github.com/insightsengineering/idr-tasks/issues/715

@pawelru pawelru added the core label Mar 14, 2024
@donyunardi donyunardi self-assigned this Mar 15, 2024
Copy link
Contributor

@donyunardi donyunardi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a valuable addition to ensure that the module example app can be executed based on the current state of the package. This will be very helpful during module development.

I also think this will complement the ongoing work on shinytest2, as outlined here. The latter has a broader scope, considering that examples in our modules tend to be simple and straightforward

tests/testthat/test-examples.R Outdated Show resolved Hide resolved
tests/testthat/test-examples.R Outdated Show resolved Hide resolved
@vedhav
Copy link
Contributor

vedhav commented Mar 21, 2024

I like the tests, Since they don't have any interactions after the app is initialized, they run quickly. For the short term adding them is a quick win and allows us to catch any module-related errors.
I think eventually we will need the flexibility to interact with the encoding panel and add extensive tests along with testing input data for the modules with the help of TealAppDriver.
One more reason to test the modules explicitly using R code is that the test coverage is not calculated when we take the R code from the manual pages.

@pawelru
Copy link
Contributor Author

pawelru commented Mar 21, 2024

I realised that I made a mistake with run_examples(..., quiet = TRUE). This essentially does not invoke applications at all and silently exit with test success (because no error and explicit expect_no_error). My best guess is that in the previous approach we still do eval but not print. I'm still thinking if there is a better way to do this to avoid such mistake in the future.

So I switched this arg to FALSE and introduced capture.output() to keep the console clean. The timings I mentioned above is not true anymore - this is muuuuch slower. Currently it is ~135 secs on my machine and most of it is due to shiny tests. I guess there is little we can do with that...

When testing locally I got a meaningful warning: #1044. That's a sign of increased coverage.

Other than that I improved the way we detect errors - added snippet provided by @vedhav as well as validation errors. This is needed because teal actually continues on error inside its child module. shinytest2 seems to capture only hard crash type of event.

@vedhav
Copy link
Contributor

vedhav commented Mar 21, 2024

Ah! At least the super-fast test runs have an explanation now.

@averissimo
Copy link
Contributor

averissimo commented Apr 9, 2024

I can't confirm @donyunardi results either, but as @pawelru said, it might be something with the installed packages.

I get 2 errors locally when running this with all packages installed (included {tmc}).

[ FAIL 2 | WARN 3 | SKIP 0 | PASS 145]

Fails (confirmed with running manual examples):

Warnings (can't confirm when running example app manually)

  • example-tm_t_summary_by.Rd
    • Warning in merged$anl_input_r()$columns_source$summarize_var : partial match of 'summarize_var' to 'summarize_vars'
  • example-tm_t_shift_by_arm.Rd
    • Warning in merged$anl_input_r()$filter : partial match of 'filter' to 'filter_info'
  • example-tm_t_events_summary.Rd
    • 'package:teal.modules.clinical' may not be available when loading

@vedhav
Copy link
Contributor

vedhav commented Apr 9, 2024

I can confirm that I too get the two errors mentioned here. I had all the dev versions of the packages installed before I tested it.
I think after adding the testing depth we should be good, perhaps a skip if cran should be enough for this.
One thing we can decide is when we want to start using the teal:::TealAppDriver class instead of shinytest2::AppDriver class. I think we can merge this and migrate the class after we've implemented the features used in this PR and exported the class from teal.

@donyunardi
Copy link
Contributor

donyunardi commented Apr 9, 2024

I installed everything, including tmc, and I got similar result with the rest of you when performing R CMD Check:

══ Results ═════════════════════════════════════════════════════════════════════════════════════════════════════════════
Duration: 369.7 s

── Failed tests ────────────────────────────────────────────────────────────────────────────────────────────────────────
Failure (test-examples.R:107:7): example-tm_t_logistic.Rd
Expected `pkgload::run_example(...)` to run without any errors.
i Actually got a <expectation_failure> with text:
  `app_check_unique_names(self, private)` threw an unexpected warning.
  Message: ! Shiny inputs and outputs should have unique HTML id values.
  i The following HTML id values are not unique:
  * teal-main_ui-root-logistic_regression-module-interaction_var
  i Do you have a Shiny input and output value with the same name?
  Class:   rlang_warning/warning/condition
Backtrace:1. ├─teal.modules.clinical (local) with_mocked_app_bindings(...) at test-examples.R:107:7
 2. │ ├─testthat::with_mocked_bindings(...) at test-examples.R:75:3
 3. │ └─testthat::with_mocked_bindings(...) at test-examples.R:75:3
 4. ├─teal.modules.clinical (local) suppress_warnings(...) at test-examples.R:75:3
 5. │ └─base::withCallingHandlers(...) at test-examples.R:10:3
 6. └─testthat::expect_no_error(...)

[ FAIL 1 | WARN 2 | SKIP 0 | PASS 428 ]

Here's another curve-ball, I got a different behavior when running devtools::test(filter = "examples"), the code that @pawelru was using, interactively in VSCode and RStudio.

If I run the the statement in VSCode R Console, I got consistent result with R CMD Check.
However, if I run the statement in RStudio R Console, I got lots of failures that the job got terminated early:

Backtrace:1. ├─teal.modules.clinical (local) with_mocked_app_bindings(...) at test-examples.R:107:7
 2. │ ├─testthat::with_mocked_bindings(...) at test-examples.R:75:3
 3. │ └─testthat::with_mocked_bindings(...) at test-examples.R:75:3
 4. ├─teal.modules.clinical (local) suppress_warnings(...) at test-examples.R:75:3
 5. │ └─base::withCallingHandlers(...) at test-examples.R:10:3
 6. └─testthat::expect_no_error(...)

[ FAIL 36 | WARN 0 | SKIP 0 | PASS 110 ]
══ Terminated early ═════════════════════════════════════════════════════════════════════════════

@pawelru @vedhav @averissimo
Can you give is a try and see if you can reproduce this?

@pawelru
Copy link
Contributor Author

pawelru commented Apr 10, 2024

This one I can reproduce. I'm investigating this now

@averissimo
Copy link
Contributor

averissimo commented Apr 10, 2024

I witnessed the problem with rstudio as well. @kartikeyakirar also confirmed

#>   Error in .Call("rs_shinyviewer", url, getwd(), "window", NULL, PACKAGE = "(embedding)") : "rs_shinyviewer" not available for .Call() for package "(embedding)"

I guess that it tries to detect the environment to launch a browser window and fails when it doesn't find a binding to rs_shinyviewer.

@pawelru
Copy link
Contributor Author

pawelru commented Apr 10, 2024

It looks like this issue is an RStudio IDE issue. This piece of code does not belong to any package source code but it's set by RStudio as a shiny.launch.browser option which is then used by shiny::runApp. If we provide one argument to that function directly we won't be using this option anymore. This should fix the issue you identified.

Tested in both IDEs and I got the same results.

@pawelru
Copy link
Contributor Author

pawelru commented Apr 10, 2024

I have pushed one more change - replace library() with load_all() within a workaround. As a result, we relax the requirement of having a given package pre-installed before running tests.

Copy link
Contributor

@averissimo averissimo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks ready to be merged 🤩. Is there anything else you want to add / fix on this PR?

I left a minor comment that may allow us not to symlink man folder.

tests/testthat/test-examples.R Outdated Show resolved Hide resolved
tests/testthat/test-examples.R Outdated Show resolved Hide resolved
Co-authored-by: André Veríssimo <[email protected]>
Signed-off-by: Pawel Rucki <[email protected]>
@pawelru
Copy link
Contributor Author

pawelru commented Apr 10, 2024

Is there anything else you want to add / fix on this PR?

No. From my side it's complete. Most probably we would need to implement allow-list mechanism for some warnings, validation errors that are expected (something you have noticed & proposed in tmb) but I'm happy to do this gradually during copy&pasting this functionality to other repos. I guess we would not foresee all the edge-cases and I have done my best to cover all I detected here.

@pawelru
Copy link
Contributor Author

pawelru commented Apr 10, 2024

Actually there is one thing that I am not super sure about - code comments. I tried to be super verbose what I'm doing here to help you understand what's going on. If you think that anything is too much then please raise it and I'm happy to remove it.

@averissimo
Copy link
Contributor

Let's keep them as this code is being propagated across packages.

The comments give proper context on the (non-obvious) methods used to achieve example testing.

The only one I find that is not necessary is # additionally mock `interactive()`

Copy link
Contributor

@donyunardi donyunardi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pawelru for investigating the findings that I raised.

Overall, I believe this PR is ready for merging.

I'm aware we had a small discussion regarding whether to use a symlink or not, and I've made some suggestions as well.

Once that's resolved, feel free to merge this.

Copy link
Contributor

@averissimo averissimo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job on this one 🥇

@pawelru
Copy link
Contributor Author

pawelru commented Apr 11, 2024

OK, I'm merging this guy as this might unblock some efforts elsewhere. If needed feel free to modify it!

@pawelru pawelru merged commit a6974fa into main Apr 11, 2024
24 checks passed
@pawelru pawelru deleted the 477_shinytest2_examples branch April 11, 2024 13:59
@donyunardi donyunardi mentioned this pull request Apr 12, 2024
3 tasks
averissimo added a commit to insightsengineering/teal.modules.general that referenced this pull request 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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants