From d7f4a7e1e9d1261db72821475458b9f8d964c665 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:44:43 +0200 Subject: [PATCH 1/2] shinytest2 tests (#240) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of [#503](https://github.com/insightsengineering/coredev-tasks/issues/503) There is 4 functions that I am testing. There is a couple more, but we can do them in a separate PR. --------- Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Dawid Kałędkowski <6959016+gogonzo@users.noreply.github.com> --- .github/workflows/check.yaml | 1 + DESCRIPTION | 1 + .../pws-download_menu.png | Bin 22093 -> 0 bytes .../plot_with_settings_ui/pws-hidden.png | Bin 6343 -> 0 bytes .../plot_with_settings_ui/pws-resize_menu.png | Bin 26306 -> 0 bytes .../plot_with_settings_ui/pws-visible.png | Bin 11531 -> 0 bytes .../tws-final_values.json | 36 -- tests/testthat/helpers-testing-depth.R | 51 ++ tests/testthat/helpers-utils.R | 30 + tests/testthat/helpers-with-settings.R | 97 ---- tests/testthat/test-plot_with_settings_ui.R | 511 +++++++++++++++++- tests/testthat/test-table_with_settings_ui.R | 401 +++++++++++++- tests/testthat/test-verbatim_popup_ui.R | 87 +++ 13 files changed, 1038 insertions(+), 177 deletions(-) delete mode 100644 tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-download_menu.png delete mode 100644 tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-hidden.png delete mode 100644 tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-resize_menu.png delete mode 100644 tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-visible.png delete mode 100644 tests/testthat/_snaps/app_tws_ui/table_with_settings_ui/tws-final_values.json create mode 100644 tests/testthat/helpers-testing-depth.R create mode 100644 tests/testthat/helpers-utils.R delete mode 100644 tests/testthat/helpers-with-settings.R create mode 100644 tests/testthat/test-verbatim_popup_ui.R diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index dcca1057..e712e189 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -28,6 +28,7 @@ jobs: additional-env-vars: | _R_CHECK_CRAN_INCOMING_REMOTE_=false NOT_CRAN=true + TESTING_DEPTH=5 additional-r-cmd-check-params: --as-cran enforce-note-blocklist: true note-blocklist: | diff --git a/DESCRIPTION b/DESCRIPTION index f7c22229..77bad83f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -41,6 +41,7 @@ Suggests: lattice (>= 0.18-4), magrittr (>= 1.5), png, + rvest, shinytest2 (>= 0.2.0), shinyvalidate, testthat (>= 3.1.5), diff --git a/tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-download_menu.png b/tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-download_menu.png deleted file mode 100644 index 505d0f8940b03c335fa68e9eeb66ddcd58e545ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22093 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 oKj7?|XxBu$CjP%^qG{!z^yMhzfIGPIsgCw diff --git a/tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-resize_menu.png b/tests/testthat/_snaps/app_pws_ui/plot_with_settings_ui/pws-resize_menu.png deleted file mode 100644 index b7664369157be302f3f2b1a4a16a5e3711eca06b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26306 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\n
\n
\n

<\/p>\n <\/div>\n

<\/div>\n <\/div>\n \n \n \n \n \n \n \n
<\/th>\n B: Placebo<\/th>\n C: Combination<\/th>\n <\/tr>\n
SEX<\/td>\n <\/td>\n <\/td>\n <\/tr>\n
F<\/td>\n 0<\/td>\n 1<\/td>\n <\/tr>\n
M<\/td>\n 1<\/td>\n 0<\/td>\n <\/tr>\n
AGE<\/td>\n <\/td>\n <\/td>\n <\/tr>\n
Mean<\/td>\n 35.00<\/td>\n 41.00<\/td>\n <\/tr>\n
<\/caption>\n <\/table>\n
<\/div>\n<\/div>", - "deps": [ - - ] - }, - "table_with_settings-table_out_modal": { - "html": "
\n
\n
\n

<\/p>\n <\/div>\n

<\/div>\n <\/div>\n \n \n \n \n \n \n \n
<\/th>\n B: Placebo<\/th>\n C: Combination<\/th>\n <\/tr>\n
SEX<\/td>\n <\/td>\n <\/td>\n <\/tr>\n
F<\/td>\n 0<\/td>\n 1<\/td>\n <\/tr>\n
M<\/td>\n 1<\/td>\n 0<\/td>\n <\/tr>\n
AGE<\/td>\n <\/td>\n <\/td>\n <\/tr>\n
Mean<\/td>\n 35.00<\/td>\n 41.00<\/td>\n <\/tr>\n
<\/caption>\n <\/table>\n
<\/div>\n<\/div>", - "deps": [ - - ] - } - }, - "export": { - - } -} diff --git a/tests/testthat/helpers-testing-depth.R b/tests/testthat/helpers-testing-depth.R new file mode 100644 index 00000000..99e12959 --- /dev/null +++ b/tests/testthat/helpers-testing-depth.R @@ -0,0 +1,51 @@ +#' Returns testing depth set by session option or by environmental variable. +#' +#' @details Looks for the session option `TESTING_DEPTH` first. +#' If not set, takes the system environmental variable `TESTING_DEPTH`. +#' If neither is set, then returns 3 by default. +#' If the value of `TESTING_DEPTH` is not a numeric of length 1, then returns 3. +#' +#' @return `numeric(1)` the testing depth. +#' +get_testing_depth <- function() { + default_depth <- 3 + depth <- getOption("TESTING_DEPTH", Sys.getenv("TESTING_DEPTH", default_depth)) + depth <- tryCatch( + as.numeric(depth), + error = function(error) default_depth, + warning = function(warning) default_depth + ) + if (length(depth) != 1) depth <- default_depth + depth +} + +#' Skipping tests in the testthat pipeline under specific scope +#' @description This function should be used per each `testthat::test_that` call. +#' Each of the call should specify an appropriate depth value. +#' The depth value will set the appropriate scope so more/less time consuming tests could be recognized. +#' The environment variable `TESTING_DEPTH` is used for changing the scope of `testthat` pipeline. +#' `TESTING_DEPTH` interpretation for each possible value: +#' \itemize{ +#' \item{0}{no tests at all} +#' \item{1}{fast - small scope - executed on every commit} +#' \item{3}{medium - medium scope - daily integration pipeline} +#' \item{5}{slow - all tests - daily package tests} +#' } +#' @param depth `numeric` the depth of the testing evaluation, +#' has opposite interpretation to environment variable `TESTING_DEPTH`. +#' So e.g. `0` means run it always and `5` means a heavy test which should be run rarely. +#' If the `depth` argument is larger than `TESTING_DEPTH` then the test is skipped. +#' @importFrom testthat skip +#' @return `NULL` or invoke an error produced by `testthat::skip` +#' @note By default `TESTING_DEPTH` is equal to 3 if there is no environment variable for it. +#' By default `depth` argument lower or equal to 3 will not be skipped because by default `TESTING_DEPTH` +#' is equal to 3. To skip <= 3 depth tests then the environment variable has to be lower than 3 respectively. +skip_if_too_deep <- function(depth) { # nolintr + checkmate::assert_numeric(depth, len = 1, lower = 0, upper = 5) + testing_depth <- get_testing_depth() # by default 3 if there are no env variable + if (testing_depth < depth) { + testthat::skip(paste("testing depth", testing_depth, "is below current testing specification", depth)) + } +} + +default_idle_timeout <- 20000 diff --git a/tests/testthat/helpers-utils.R b/tests/testthat/helpers-utils.R new file mode 100644 index 00000000..71e3af76 --- /dev/null +++ b/tests/testthat/helpers-utils.R @@ -0,0 +1,30 @@ +#' Function to check if a function has a side effect of drawing something +#' @param `function` function which possibly draws something. +#' @return `logical(1)` whether the function has a side effect of drawing a plot. +#' @note reference to https://stackoverflow.com/questions/74615694/check-if-a-function-draw-plot-something +#' @keywords internal +is_draw <- function(plot_fun) { + checkmate::assert_function(plot_fun) + grDevices::graphics.off() # close any current graphics devices + cdev <- grDevices::dev.cur() + plot_fun() + if (cdev != grDevices::dev.cur()) { + on.exit(grDevices::dev.off()) + return(TRUE) + } + return(FALSE) +} + + +is_visible <- function(element, app_driver) { + any( + unlist( + app_driver$get_js( + sprintf( + "Array.from(document.querySelectorAll('%s')).map(el => el.checkVisibility())", + element + ) + ) + ) + ) +} diff --git a/tests/testthat/helpers-with-settings.R b/tests/testthat/helpers-with-settings.R deleted file mode 100644 index 6def17aa..00000000 --- a/tests/testthat/helpers-with-settings.R +++ /dev/null @@ -1,97 +0,0 @@ -#' Table with settings app -#' -#' @description Example table with setting app for testing using \code{shinytest2} -#' -#' @keywords internal -#' -app_tws <- function() { - shiny::shinyApp( - ui = shiny::fluidPage( - table_with_settings_ui( - id = "table_with_settings" - ) - ), - server = function(input, output, session) { - df1 <- data.frame( - AGE = c(35, 41), - SEX = factor(c("M", "F")), - ARM = c("B: Placebo", "C: Combination") - ) - - table_r <- shiny::reactive({ - l1 <- rtables::basic_table() - l2 <- rtables::split_cols_by(l1, "ARM") - l3 <- rtables::analyze(l2, c("SEX", "AGE")) - tbl <- rtables::build_table(l3, df1) - tbl - }) - table_with_settings_srv(id = "table_with_settings", table_r = table_r) - } - ) -} - -#' Plot with settings app -#' -#' @description Example plot with setting app for testing using \code{shinytest2} -#' -#' @keywords internal -#' -app_pws <- function() { - shiny::shinyApp( - ui = shiny::fluidPage( - shinyjs::useShinyjs(), - shiny::actionButton("button", "Show/Hide"), - plot_with_settings_ui( - id = "plot_with_settings" - ) - ), - server = function(input, output, session) { - plot_r <- shiny::reactive({ - ggplot2::ggplot(data.frame(x = 1:5, y = 1:5)) + - ggplot2::geom_point(ggplot2::aes(x = 1:5, y = 1:5)) - }) - - show_hide_signal <- shiny::reactiveVal(TRUE) - - shiny::observeEvent(input$button, { - show_hide_signal( - !show_hide_signal() - ) - }) - - plot_data <- plot_with_settings_srv( - id = "plot_with_settings", - plot_r = plot_r, - height = c(400, 100, 1200), - width = c(500, 250, 750), - brushing = TRUE, - clicking = TRUE, - dblclicking = TRUE, - hovering = TRUE, - show_hide_signal = show_hide_signal - ) - - shiny::exportTestValues( - plot_r = plot_r, - plot_data = plot_data - ) - } - ) -} - -#' Function to check if a function has a side effect of drawing something -#' @param `function` function which possibly draws something. -#' @return `logical(1)` whether the function has a side effect of drawing a plot. -#' @note reference to https://stackoverflow.com/questions/74615694/check-if-a-function-draw-plot-something -#' @keywords internal -is_draw <- function(plot_fun) { - checkmate::assert_function(plot_fun) - grDevices::graphics.off() # close any current graphics devices - cdev <- grDevices::dev.cur() - plot_fun() - if (cdev != grDevices::dev.cur()) { - on.exit(grDevices::dev.off()) - return(TRUE) - } - return(FALSE) -} diff --git a/tests/testthat/test-plot_with_settings_ui.R b/tests/testthat/test-plot_with_settings_ui.R index 5cd4dc61..cb4d48d0 100644 --- a/tests/testthat/test-plot_with_settings_ui.R +++ b/tests/testthat/test-plot_with_settings_ui.R @@ -1,3 +1,59 @@ +#' Plot with settings app +#' +#' @description Example plot with setting app for testing using \code{shinytest2} +#' +#' @keywords internal +#' +app_driver_pws <- function() { + shiny::shinyApp( + ui = shiny::fluidPage( + shinyjs::useShinyjs(), + shiny::actionButton("button", "Show/Hide"), + plot_with_settings_ui( + id = "plot_with_settings" + ) + ), + server = function(input, output, session) { + plot_r <- shiny::reactive({ + ggplot2::ggplot(data.frame(x = 1:5, y = 1:5)) + + ggplot2::geom_point(ggplot2::aes(x = 1:5, y = 1:5)) + }) + + show_hide_signal <- shiny::reactiveVal(TRUE) + + shiny::observeEvent(input$button, { + show_hide_signal( + !show_hide_signal() + ) + }) + + plot_data <- plot_with_settings_srv( + id = "plot_with_settings", + plot_r = plot_r, + height = c(400, 100, 1200), + width = c(500, 250, 750), + brushing = TRUE, + clicking = TRUE, + dblclicking = TRUE, + hovering = TRUE, + show_hide_signal = show_hide_signal + ) + + shiny::exportTestValues( + plot_r = plot_r, + plot_data = plot_data + ) + } + ) +} + +get_active_module_pws_output <- function(app_driver, pws, attr) { + app_driver$get_html("html") %>% + rvest::read_html() %>% + rvest::html_nodes(sprintf("#plot_with_settings-%s > img", pws)) %>% + rvest::html_attr(attr) +} + testthat::test_that("type_download_ui returns `shiny.tag`", { testthat::expect_s3_class(type_download_ui("STH"), "shiny.tag") }) @@ -6,39 +62,446 @@ testthat::test_that("plot_with_settings_ui returns `shiny.tag.list`", { testthat::expect_s3_class(plot_with_settings_ui("STH"), "shiny.tag.list") }) -testthat::test_that("Plot with settings: UI screenshots", { - app <- shinytest2::AppDriver$new( - app_pws(), +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: initializes with a plot and toggle, download and resize buttons", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + # Check if there is an image. + testthat::expect_true(is_visible("#plot_with_settings-plot_main > img", app_driver)) + + # Check if there are three buttons above the table. + testthat::expect_true(is_visible("#plot_with_settings-downbutton-downl", app_driver)) + testthat::expect_true(is_visible("#plot_with_settings-expand", app_driver)) + testthat::expect_true(is_visible("#plot_with_settings-expbut", app_driver)) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: buttons have proper FA icons and two of them are dropdowns", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-downbutton-downl") %>% + rvest::read_html() %>% + rvest::html_element("button") %>% + rvest::html_attr("data-toggle"), + "dropdown" + ) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-downbutton-downl") %>% + rvest::read_html() %>% + rvest::html_element("button") %>% + rvest::html_attr("aria-expanded"), + "false" + ) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-downbutton-downl") %>% + rvest::read_html() %>% + rvest::html_element("i") %>% + rvest::html_attr("class"), + "fas fa-download" + ) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-expand") %>% + rvest::read_html() %>% + rvest::html_element("i") %>% + rvest::html_attr("class"), + "fas fa-up-right-and-down-left-from-center" + ) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-expbut") %>% + rvest::read_html() %>% + rvest::html_element("i") %>% + rvest::html_attr("class"), + "fas fa-maximize" + ) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-expbut") %>% + rvest::read_html() %>% + rvest::html_element("button") %>% + rvest::html_attr("data-toggle"), + "dropdown" + ) + + testthat::expect_equal( + app_driver$get_html("#plot_with_settings-expbut") %>% + rvest::read_html() %>% + rvest::html_element("button") %>% + rvest::html_attr("aria-expanded"), + "false" + ) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: the click on the download button opens a download menu + with file type, file name and download button", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible("#plot_with_settings-downbutton-data_download", app_driver)) + testthat::expect_false(is_visible("#plot_with_settings-downbutton-file_format", app_driver)) + testthat::expect_false(is_visible("#plot_with_settings-downbutton-file_name", app_driver)) + + app_driver$click(selector = "#plot_with_settings-downbutton-downl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_equal( + app_driver$get_text("#plot_with_settings-downbutton-file_format-label"), + "File type" + ) + testthat::expect_identical( + app_driver$get_value(input = "plot_with_settings-downbutton-file_format"), + "png" + ) + + file_format_text <- app_driver$get_text("#plot_with_settings-downbutton-file_format > div") + testthat::expect_match(file_format_text, "svg\n", fixed = TRUE) + testthat::expect_match(file_format_text, "pdf\n", fixed = TRUE) + + testthat::expect_equal( + app_driver$get_text("#plot_with_settings-downbutton-file_name-label"), + "File name (without extension)" + ) + testthat::expect_match( + app_driver$get_value(input = "plot_with_settings-downbutton-file_name"), + sprintf("plot_%s", gsub("-", "", Sys.Date())) + ) + + testthat::expect_true(is_visible("#plot_with_settings-downbutton-data_download", app_driver)) + + download_button <- + app_driver$get_html("#plot_with_settings-downbutton-data_download > i") %>% + rvest::read_html() + + testthat::expect_equal( + download_button %>% + rvest::html_node("i") %>% + rvest::html_attr("class"), + "fas fa-download" + ) + testthat::expect_equal( + download_button %>% + rvest::html_node("i") %>% + rvest::html_attr("aria-label"), + "download icon" + ) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: the click on the expand button opens a modal + plot height, plot width, plot, download dropdown and dismiss button", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible("#plot_with_settings-height_in_modal", app_driver)) + testthat::expect_false(is_visible("#plot_with_settings-width_in_modal", app_driver)) + testthat::expect_false(is_visible("#plot_with_settings-modal_downbutton-downl", app_driver)) + + app_driver$click(selector = "#plot_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_true(is_visible("#plot_with_settings-height_in_modal", app_driver)) + testthat::expect_true(is_visible("#plot_with_settings-width_in_modal", app_driver)) + testthat::expect_true(is_visible("#plot_with_settings-modal_downbutton-downl", app_driver)) + + testthat::expect_identical( + app_driver$get_value(input = "plot_with_settings-height_in_modal"), + 400L + ) + testthat::expect_identical( + app_driver$get_text("#plot_with_settings-height_in_modal-label"), + "Plot height" + ) + testthat::expect_identical( + app_driver$get_value(input = "plot_with_settings-width_in_modal"), + 500L + ) + testthat::expect_identical( + app_driver$get_text("#plot_with_settings-width_in_modal-label"), + "Plot width" + ) + + testthat::expect_true(is_visible("#plot_with_settings-plot_main > img", app_driver)) + + app_driver$stop() + } +) +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: the click on the download button in expand modal opens a download dropdown", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible("#plot_with_settings-modal_downbutton-file_format", app_driver)) + testthat::expect_false(is_visible("#plot_with_settings-modal_downbutton-file_name", app_driver)) + + app_driver$click(selector = "#plot_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#plot_with_settings-modal_downbutton-downl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_true(is_visible("#plot_with_settings-modal_downbutton-file_format", app_driver)) + testthat::expect_true(is_visible("#plot_with_settings-modal_downbutton-file_name", app_driver)) + + testthat::expect_equal( + app_driver$get_text("#plot_with_settings-modal_downbutton-file_format-label"), + "File type" + ) + testthat::expect_identical( + app_driver$get_value(input = "plot_with_settings-modal_downbutton-file_format"), + "png" + ) + + testthat::expect_equal( + app_driver$get_text("#plot_with_settings-modal_downbutton-file_name-label"), + "File name (without extension)" + ) + testthat::expect_match( + app_driver$get_value(input = "plot_with_settings-modal_downbutton-file_name"), + sprintf("plot_%s", gsub("-", "", Sys.Date())) + ) + + testthat::expect_true(is_visible("#plot_with_settings-modal_downbutton-data_download", app_driver)) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: the click on the resize button opens a dropdown menu + plot height, plot width, plot, download dropdown and dismiss button", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible("#plot_with_settings-slider_ui", app_driver)) + + app_driver$click(selector = "#plot_with_settings-expbut") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + + testthat::expect_identical( + app_driver$get_value(input = "plot_with_settings-height"), + 400L + ) + testthat::expect_identical( + app_driver$get_text("#plot_with_settings-height-label"), + "Plot height" + ) + testthat::expect_identical( + app_driver$get_value(input = "plot_with_settings-width"), + 500L + ) + + testthat::expect_identical( + app_driver$get_text("span.bootstrap-switch-handle-off.bootstrap-switch-default"), + "OFF" + ) + + app_driver$stop() + } +) + + +testthat::test_that( + "e2e: teal.widgets::plot_with_settings: it is possible to set height and width for the plot + on the third button dropdown menu without errors", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$set_inputs(`plot_with_settings-height_in_modal` = 1000) + app_driver$set_inputs(`plot_with_settings-width_in_modal` = 350) + testthat::expect_null( + app_driver$get_html(".shiny-output-error-validation"), + info = "No validation error is observed" + ) + app_driver$stop() + } +) + +testthat::test_that( + "e2e teal.widgets::plot_with_settings: clicking download+download button downloads image in a specified format", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#plot_with_settings-downbutton-downl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + app_driver$expect_download("plot_with_settings-downbutton-data_download") + + filename <- app_driver$get_download("plot_with_settings-downbutton-data_download") + testthat::expect_match(filename, "png$", fixed = FALSE) + + app_driver$stop() + } +) +testthat::test_that("e2e teal.widgets::plot_with_settings: expanded image can be resized", { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#plot_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + plot_before <- get_active_module_pws_output(app_driver, pws = "plot_modal", attr = "src") + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_modal", attr = "width"), + "500" + ) + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_modal", attr = "height"), + "400" + ) + + app_driver$set_inputs(`plot_with_settings-height_in_modal` = 1000) + app_driver$set_inputs(`plot_with_settings-width_in_modal` = 350) + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_modal", attr = "width"), + "350" + ) + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_modal", attr = "height"), + "1000" + ) + + testthat::expect_false( + identical(plot_before, get_active_module_pws_output(app_driver, pws = "plot_modal", attr = "src")) + ) + + app_driver$stop() +}) +testthat::test_that("e2e teal.widgets::plot_with_settings: expanded image can be downloaded", { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), + name = "pws", + variant = "app_driver_pws_ui" + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#plot_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#plot_with_settings-modal_downbutton-downl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$expect_download("plot_with_settings-modal_downbutton-data_download") + + filename <- app_driver$get_download("plot_with_settings-modal_downbutton-data_download") + testthat::expect_match(filename, "png$", fixed = FALSE) + + app_driver$stop() +}) + +testthat::test_that("e2e teal.widgets::plot_with_settings: main image can be resized", { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_pws(), name = "pws", - variant = "app_pws_ui" + variant = "app_driver_pws_ui" ) - threshold <- 75 - kernel_size <- 5 - delay <- 0.1 + app_driver$wait_for_idle(timeout = default_idle_timeout) - app$set_window_size(width = 1000, height = 700) + app_driver$click(selector = "#plot_with_settings-expbut") + app_driver$wait_for_idle(timeout = default_idle_timeout) - # click on hide/show button - app$click("button") - app$expect_screenshot( - threshold = threshold, kernel_size = kernel_size, delay = delay, name = "hidden" + plot_before <- get_active_module_pws_output(app_driver, pws = "plot_main", attr = "src") + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_main", attr = "width"), + "500" ) - app$click("button") - app$expect_screenshot( - threshold = threshold, kernel_size = kernel_size, delay = delay, name = "visible" + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_main", attr = "height"), + "400" ) - app$set_inputs(`plot_with_settings-downbutton-file_name` = "plot1") - app$set_inputs(`plot_with_settings-modal_downbutton-file_name` = "plot2") + app_driver$set_inputs(`plot_with_settings-height` = 1000) + app_driver$set_inputs(`plot_with_settings-width` = 350) - app$click("plot_with_settings-downbutton-downl") - app$expect_screenshot( - threshold = threshold, kernel_size = kernel_size, delay = delay, name = "download_menu" + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_main", attr = "width"), + "350" ) - app$click("plot_with_settings-expbut") - app$expect_screenshot( - threshold = threshold, kernel_size = kernel_size, delay = delay, name = "resize_menu" + + testthat::expect_equal( + get_active_module_pws_output(app_driver, pws = "plot_main", attr = "height"), + "1000" + ) + + testthat::expect_false( + identical(plot_before, get_active_module_pws_output(app_driver, pws = "plot_main", attr = "src")) ) - app$stop() + app_driver$stop() }) diff --git a/tests/testthat/test-table_with_settings_ui.R b/tests/testthat/test-table_with_settings_ui.R index 16731142..d6cdca53 100644 --- a/tests/testthat/test-table_with_settings_ui.R +++ b/tests/testthat/test-table_with_settings_ui.R @@ -1,23 +1,384 @@ -testthat::test_that("Table with settings: UI screenshots", { - app <- shinytest2::AppDriver$new( - app_tws(), +#' Table with settings app +#' +#' @description Example table with setting app for testing using \code{shinytest2} +#' +#' @keywords internal +#' +app_driver_tws <- function() { + shiny::shinyApp( + ui = shiny::fluidPage( + table_with_settings_ui( + id = "table_with_settings" + ) + ), + server = function(input, output, session) { + df1 <- data.frame( + AGE = c(35, 41), + SEX = factor(c("M", "F")), + ARM = c("B: Placebo", "C: Combination") + ) + + table_r <- shiny::reactive({ + l1 <- rtables::basic_table() + l2 <- rtables::split_cols_by(l1, "ARM") + l3 <- rtables::analyze(l2, c("SEX", "AGE")) + tbl <- rtables::build_table(l3, df1) + tbl + }) + table_with_settings_srv(id = "table_with_settings", table_r = table_r) + } + ) +} + +check_table <- function(content) { + testthat::expect_match(content, "B: Placebo", fixed = TRUE, all = FALSE) + testthat::expect_match(content, "C: Combination", fixed = TRUE, all = FALSE) + testthat::expect_match(content, "SEX", fixed = TRUE, all = FALSE) + testthat::expect_match(content, "35.00", fixed = TRUE, all = FALSE) + testthat::expect_match(content, "41.00", fixed = TRUE, all = FALSE) +} + +testthat::test_that("table_with_settings_ui returns `shiny.tag.list`", { + testthat::expect_s3_class(table_with_settings_ui("STH"), "shiny.tag.list") +}) + +testthat::test_that( + "e2e: teal.widgets::table_with_settings is initialized with 2 buttons and a table", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + # Check if there is an table. + testthat::expect_true(is_visible("#table_with_settings-table_out_main", app_driver)) + + testthat::expect_true(is_visible("#table_with_settings-downbutton-dwnl", app_driver)) + testthat::expect_true(is_visible("#table_with_settings-expand", app_driver)) + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::table_with_settings: buttons have proper FA icons and one of them is dropdown", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_equal( + app_driver$get_html("#table_with_settings-downbutton-dwnl") %>% + rvest::read_html() %>% + rvest::html_element("button") %>% + rvest::html_attr("data-toggle"), + "dropdown" + ) + + testthat::expect_equal( + app_driver$get_html("#table_with_settings-downbutton-dwnl") %>% + rvest::read_html() %>% + rvest::html_element("button") %>% + rvest::html_attr("aria-expanded"), + "false" + ) + + testthat::expect_equal( + app_driver$get_html("#table_with_settings-downbutton-dwnl") %>% + rvest::read_html() %>% + rvest::html_element("i") %>% + rvest::html_attr("class"), + "fas fa-download" + ) + + testthat::expect_equal( + app_driver$get_html("#table_with_settings-expand") %>% + rvest::read_html() %>% + rvest::html_element("i") %>% + rvest::html_attr("class"), + "fas fa-up-right-and-down-left-from-center" + ) + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::table_with_settings: the click on the download button opens a download menu + with file type, file name and download button", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible("#table_with_settings-downbutton-data_download", app_driver)) + testthat::expect_false(is_visible("#table_with_settings-downbutton-file_format", app_driver)) + testthat::expect_false(is_visible("#table_with_settings-downbutton-file_name", app_driver)) + + app_driver$click(selector = "#table_with_settings-downbutton-dwnl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_equal( + app_driver$get_text("#table_with_settings-downbutton-file_format-label"), + "File type" + ) + testthat::expect_identical( + app_driver$get_value(input = "table_with_settings-downbutton-file_format"), + ".txt" + ) + + file_format_text <- app_driver$get_text("#table_with_settings-downbutton-file_format > div") + testthat::expect_match(file_format_text, "formatted txt\n", fixed = TRUE) + testthat::expect_match(file_format_text, "csv\n", fixed = TRUE) + testthat::expect_match(file_format_text, "pdf\n", fixed = TRUE) + + testthat::expect_equal( + app_driver$get_text("#table_with_settings-downbutton-file_name-label"), + "File name (without extension)" + ) + + testthat::expect_match( + app_driver$get_value(input = "table_with_settings-downbutton-file_name"), + sprintf("table_%s", gsub("-", "", Sys.Date())) + ) + + testthat::expect_true(is_visible("#table_with_settings-downbutton-data_download", app_driver)) + + download_button <- + app_driver$get_html("#table_with_settings-downbutton-data_download > i") %>% + rvest::read_html() + + testthat::expect_equal( + download_button %>% + rvest::html_node("i") %>% + rvest::html_attr("class"), + "fas fa-download" + ) + testthat::expect_equal( + download_button %>% + rvest::html_node("i") %>% + rvest::html_attr("aria-label"), + "download icon" + ) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::table_with_settings: check pagination appearance for .txt and disappearance for .csv + for the first button", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#table_with_settings-downbutton-dwnl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + pagination <- "#dropdown-menu-table_with_settings-downbutton-dwnl .paginate-ui .form-group.shiny-input-container" + pagination_text <- app_driver$get_text(pagination) + testthat::expect_match(pagination_text, "Paginate table:\n", fixed = TRUE) + testthat::expect_match(pagination_text, "lines / page\n", fixed = TRUE) + + app_driver$click(selector = "input[value='.csv']") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible(pagination, app_driver)) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::table_with_settings: the click on expand button opens a modal with a table", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_false(is_visible("#table_with_settings-table_out_modal", app_driver)) + + app_driver$click(selector = "#table_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + table_content <- app_driver$get_text("#table_with_settings-table_out_modal") + + check_table(table_content) + + # Close modal. + app_driver$click(selector = "#shiny-modal-wrapper .modal-footer > button") + app_driver$wait_for_idle(timeout = default_idle_timeout) + testthat::expect_null(app_driver$get_html("#table_with_settings-table_out_modal")) + + # Review the main table content. + main_table_content <- app_driver$get_text("#table_with_settings-table_out_main") + check_table(main_table_content) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e: teal.widgets::table_with_settings: clicking download in an expand modal opens dropdown menu with dwnl settings, + such as: file type, file name, pagination", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + testthat::expect_false(is_visible("#table_with_settings-modal_downbutton-dwnl", app_driver)) + + app_driver$click(selector = "#table_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + app_driver$click(selector = "#table_with_settings-modal_downbutton-dwnl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + testthat::expect_equal( + app_driver$get_text("#table_with_settings-modal_downbutton-file_format-label"), + "File type" + ) + testthat::expect_identical( + app_driver$get_value(input = "table_with_settings-modal_downbutton-file_format"), + ".txt" + ) + + file_format_text <- app_driver$get_text("#table_with_settings-modal_downbutton-file_format > div") + testthat::expect_match(file_format_text, "formatted txt\n", fixed = TRUE) + testthat::expect_match(file_format_text, "csv\n", fixed = TRUE) + testthat::expect_match(file_format_text, "pdf\n", fixed = TRUE) + + testthat::expect_equal( + app_driver$get_text("#table_with_settings-modal_downbutton-file_name-label"), + "File name (without extension)" + ) + + testthat::expect_match( + app_driver$get_value(input = "table_with_settings-modal_downbutton-file_name"), + sprintf("table_%s", gsub("-", "", Sys.Date())) + ) + + testthat::expect_true(is_visible("#table_with_settings-modal_downbutton-data_download", app_driver)) + + download_button <- + app_driver$get_html("#table_with_settings-modal_downbutton-data_download > i") %>% + rvest::read_html() + + testthat::expect_equal( + download_button %>% + rvest::html_node("i") %>% + rvest::html_attr("class"), + "fas fa-download" + ) + testthat::expect_equal( + download_button %>% + rvest::html_node("i") %>% + rvest::html_attr("aria-label"), + "download icon" + ) + + app_driver$stop() + } +) + + +testthat::test_that( + "e2e: teal.widgets::table_with_settings: check pagination appearance for .txt and disappearance for .csv + for the modal on the second button", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + testthat::expect_false(is_visible("#table_with_settings-modal_downbutton-dwnl", app_driver)) + + app_driver$click(selector = "#table_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + app_driver$click(selector = "#table_with_settings-modal_downbutton-dwnl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + pagination <- + "#dropdown-menu-table_with_settings-modal_downbutton-dwnl .paginate-ui .form-group.shiny-input-container" + pagination_text <- app_driver$get_text(pagination) + testthat::expect_match(pagination_text, "Paginate table:\n", fixed = TRUE) + testthat::expect_match(pagination_text, "lines / page\n", fixed = TRUE) + + app_driver$stop() + } +) + +testthat::test_that( + "e2e teal.widgets::table_with_settings: clicking download+download button downloads table in a specified format", + { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), + name = "tws", + variant = "app_driver_tws_ui", + ) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#table_with_settings-downbutton-dwnl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + app_driver$expect_download("table_with_settings-downbutton-data_download") + + filename <- app_driver$get_download("table_with_settings-downbutton-data_download") + testthat::expect_match(filename, "txt$", fixed = FALSE) + + content <- readLines(filename) + + check_table(content) + + app_driver$stop() + } +) + +testthat::test_that("e2e teal.widgets::table_with_settings: expanded table can be downloaded", { + skip_if_too_deep(5) + app_driver <- shinytest2::AppDriver$new( + app_driver_tws(), name = "tws", - variant = "app_tws_ui", + variant = "app_driver_tws_ui", ) - app$set_inputs(`table_with_settings-downbutton-file_name` = "table") - - # click on download button - app$click("table_with_settings-downbutton-dwnl") - - # test clicking on modal - app$click("table_with_settings-expand") - # wait for the expand to happen - Sys.sleep(0.1) - app$set_inputs(`table_with_settings-modal_downbutton-lpp` = 70) - app$click("table_with_settings-modal_downbutton-dwnl") - app$set_inputs(`table_with_settings-modal_downbutton-file_name` = "table") - - # now test values in json - app$expect_values(screenshot_args = FALSE, name = "final_values") - app$stop() + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#table_with_settings-expand") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$click(selector = "#table_with_settings-modal_downbutton-dwnl") + app_driver$wait_for_idle(timeout = default_idle_timeout) + + app_driver$expect_download("table_with_settings-modal_downbutton-data_download") + + filename <- app_driver$get_download("table_with_settings-modal_downbutton-data_download") + testthat::expect_match(filename, "txt$", fixed = FALSE) + + content <- readLines(filename) + + check_table(content) + + app_driver$stop() }) diff --git a/tests/testthat/test-verbatim_popup_ui.R b/tests/testthat/test-verbatim_popup_ui.R new file mode 100644 index 00000000..5849678c --- /dev/null +++ b/tests/testthat/test-verbatim_popup_ui.R @@ -0,0 +1,87 @@ +#' Verbatim popup app +#' +#' @description Example table with setting app for testing using \code{shinytest2} +#' +#' @keywords internal +#' +app_driver_vpu <- function(button_label, verbatim_content, title) { + shiny::shinyApp( + ui = shiny::fluidPage( + verbatim_popup_ui( + id = "verbatim_popup", + button_label = button_label + ) + ), + server = function(input, output, session) { + verbatim_popup_srv( + id = "verbatim_popup", + verbatim_content = verbatim_content, + title = title, + style = FALSE + ) + } + ) +} + +testthat::test_that("verbatim_popup_ui returns `shiny.tag.list`", { + testthat::expect_s3_class(verbatim_popup_ui("STH", "STH2"), "shiny.tag.list") +}) + +testthat::test_that( + "e2e: teal.widgets::verbatim_popup is initialized with a button that opens a modal with a verbatim text", + { + skip_if_too_deep(5) + ui_popup_button_label <- "Open me" + modal_title <- "Verbatim popup title" + verbatim_content_text <- "if (TRUE) { print('Popups are the best') }" + + app_driver <- shinytest2::AppDriver$new( + app_driver_vpu( + button_label = ui_popup_button_label, + verbatim_content = verbatim_content_text, + title = modal_title + ), + name = "vpu", + variant = "app_driver_vpu_ui" + ) + + app_driver$wait_for_idle(timeout = default_idle_timeout) + + popup_button_element <- "#verbatim_popup-button" + testthat::expect_equal( + app_driver$get_text(popup_button_element), + ui_popup_button_label + ) + + # Click the button. + app_driver$click(selector = popup_button_element) + app_driver$wait_for_idle(timeout = default_idle_timeout) + + # Verify the content of the popped modal is as expected. + testthat::expect_equal( + app_driver$get_text(".modal-title"), + modal_title + ) + + testthat::expect_equal( + app_driver$get_text("#verbatim_popup-copy_button1"), + "Copy to Clipboard" + ) + testthat::expect_equal( + app_driver$get_text("#shiny-modal > div > div > div.modal-body > div > button:nth-child(2)"), + "Dismiss" + ) + + testthat::expect_equal( + app_driver$get_text("#verbatim_popup-verbatim_content"), + verbatim_content_text + ) + + # Modal is closed, once the button is clicked. + app_driver$click(selector = ".modal-body button[data-dismiss='modal']") + app_driver$wait_for_idle(timeout = default_idle_timeout) + testthat::expect_null(app_driver$get_html("#shiny-modal-wrapper")) + + app_driver$stop() + } +) From 570fae4950e87e0115a36f963c615d50fad2d51d Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 26 Apr 2024 10:45:39 +0000 Subject: [PATCH 2/2] [skip actions] Bump version to 0.4.2.9010 --- .pre-commit-config.yaml | 2 +- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 85b7138f..06e1278c 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.4.0 + rev: v0.4.2 hooks: - id: style-files name: Style code with `styler` diff --git a/DESCRIPTION b/DESCRIPTION index 77bad83f..19a99500 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: teal.widgets Title: 'shiny' Widgets for 'teal' Applications -Version: 0.4.2.9009 -Date: 2024-03-18 +Version: 0.4.2.9010 +Date: 2024-04-26 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre")), person("Pawel", "Rucki", , "pawel.rucki@roche.com", role = "aut"), diff --git a/NEWS.md b/NEWS.md index 86640e70..dca58280 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal.widgets 0.4.2.9009 +# teal.widgets 0.4.2.9010 # teal.widgets 0.4.2