From 700462af1d70c632be31266dfcd8dbdcc174589f Mon Sep 17 00:00:00 2001 From: LDSamson Date: Fri, 19 Jan 2024 18:15:49 +0100 Subject: [PATCH 01/22] Add functionality to show local reverse dependencies of a selected package --- DESCRIPTION | 3 +- R/mod_packageDependencies.R | 18 ++++++- tests/testthat/test-packageDependencies.R | 57 ++++++++++++++++++++++- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f7e361533..639ca7c0e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -78,7 +78,8 @@ Suggests: shinytest2, spelling, testthat (>= 3.0.0), - tinytex + tinytex, + withr Config/testthat/edition: 3 Language: en-US Depends: diff --git a/R/mod_packageDependencies.R b/R/mod_packageDependencies.R index dd1b0fc4c..2ab3cde2d 100644 --- a/R/mod_packageDependencies.R +++ b/R/mod_packageDependencies.R @@ -40,6 +40,7 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { depends <- reactiveVal(value = NULL) suggests <- reactiveVal(value = NULL) revdeps <- reactiveVal(value = NULL) + revdeps_local <- reactiveVal(value = NULL) rev_pkg <- reactiveVal(value = 0L) toggled <- reactiveVal(value = 0L) pkg_updates <- reactiveValues() @@ -93,6 +94,8 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { if (rlang::is_empty(pkgref()$dependencies[[1]])) depends(dplyr::tibble(package = character(0), type = character(0))) revdeps(pkgref()$reverse_dependencies[[1]] %>% as.vector()) + revdeps_local(with(loaded2_db(), name[name %in% revdeps()])) + # send either depends() or both to build_dep_cards(), depending on toggled() if (toggled() == 0L) { cards(build_dep_cards(data = depends(), loaded = loaded2_db()$name, toggled = 0L)) @@ -263,8 +266,19 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { }) ) # column ), # fluidRow - br(), - h4(glue::glue("Reverse Dependencies: {length(revdeps())}"), style = "text-align: left;"), + br(), + h4(glue::glue("Reverse Dependencies available in database: {length(revdeps_local())}"), style = "text-align: left;"), + br(), br(), + fluidRow( + column( + width = 8, + wellPanel( + renderText(revdeps_local() %>% sort()), + style = "max-height: 500px; overflow: auto" + ) + ) + ), + h4(glue::glue("All reverse Dependencies: {length(revdeps())}"), style = "text-align: left;"), br(), br(), fluidRow( column( diff --git a/tests/testthat/test-packageDependencies.R b/tests/testthat/test-packageDependencies.R index 483340eb3..83d3001a1 100644 --- a/tests/testthat/test-packageDependencies.R +++ b/tests/testthat/test-packageDependencies.R @@ -41,4 +41,59 @@ test_that("module can produce a table of package dependencies", { unlink("app_db_loc") rm(out_htm, id_strng, json, actual, expected, app_db_loc) -}) \ No newline at end of file +}) + +test_that( + "Feature 1. module packageDependencies selects all reverse dependencies + in the database. + Scenario 1. Given the selected package is 'dplyr', + and the packages names in the package database are 'dplyr' and 'dbplyr', + I expect that the package names 'plotly', 'admiral', 'dbplyr' and 'glue' are found in [revdeps], + and that [revdeps_local] is equal to 'dbplyr'.", + { + testargs <- list( + selected_pkg = list( + name = reactiveVal("dplyr") + ), + user = "test_user", + parent = reactiveValues( + input = reactiveValues( + tabs = "Package Metrics", + metric_type = "dep" + ) + ) + ) + + test_db_loc <- system.file("testdata", "upload_format.database", + package = "riskassessment") + temp_db_loc <- withr::local_tempfile(fileext = ".database") + file.copy(test_db_loc, temp_db_loc) + con <- withr::local_db_connection( + DBI::dbConnect(RSQLite::SQLite(), temp_db_loc) + ) + # because only the dplyr package is in the test dataset, we add one of its + # known reverse dependencies: + DBI::dbAppendTable( + con, + "package", + data.frame( + name = "dbplyr", + version = "1.0.0", + score = "0.32" + ) + ) + # add test db location to the app session: + app_session <- MockShinySession$new() + app_session$options$golem_options <- list( + assessment_db_name = temp_db_loc + ) + + testServer(packageDependenciesServer, args = testargs, { + session$flushReact() + expect_true(all(c("plotly", "admiral", "dbplyr", "glue") %in% revdeps())) + expect_equal(revdeps_local(), "dbplyr") + }, + session = app_session) + } +) + From 4b5da3b92a4447c64cb5c98f2086e47ede34271c Mon Sep 17 00:00:00 2001 From: LDSamson Date: Mon, 22 Jan 2024 11:34:10 +0100 Subject: [PATCH 02/22] slight update for consistent line breaks --- R/mod_packageDependencies.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/R/mod_packageDependencies.R b/R/mod_packageDependencies.R index 2ab3cde2d..bb889e949 100644 --- a/R/mod_packageDependencies.R +++ b/R/mod_packageDependencies.R @@ -266,9 +266,9 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { }) ) # column ), # fluidRow - br(), + br(), br(), h4(glue::glue("Reverse Dependencies available in database: {length(revdeps_local())}"), style = "text-align: left;"), - br(), br(), + br(), fluidRow( column( width = 8, @@ -278,8 +278,9 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { ) ) ), - h4(glue::glue("All reverse Dependencies: {length(revdeps())}"), style = "text-align: left;"), br(), br(), + h4(glue::glue("All reverse Dependencies: {length(revdeps())}"), style = "text-align: left;"), + br(), fluidRow( column( width = 8, From 5c5b6ad630a0a01df91bfbff14da81d59835af33 Mon Sep 17 00:00:00 2001 From: LDSamson Date: Tue, 23 Jan 2024 11:41:06 +0100 Subject: [PATCH 03/22] improve temp db connection in test --- tests/testthat/test-packageDependencies.R | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/testthat/test-packageDependencies.R b/tests/testthat/test-packageDependencies.R index 83d3001a1..f7e0ad990 100644 --- a/tests/testthat/test-packageDependencies.R +++ b/tests/testthat/test-packageDependencies.R @@ -68,19 +68,22 @@ test_that( package = "riskassessment") temp_db_loc <- withr::local_tempfile(fileext = ".database") file.copy(test_db_loc, temp_db_loc) - con <- withr::local_db_connection( - DBI::dbConnect(RSQLite::SQLite(), temp_db_loc) - ) + # because only the dplyr package is in the test dataset, we add one of its # known reverse dependencies: - DBI::dbAppendTable( - con, - "package", - data.frame( - name = "dbplyr", - version = "1.0.0", - score = "0.32" - ) + withr::with_db_connection( + list(con = DBI::dbConnect(RSQLite::SQLite(), temp_db_loc)), + { + DBI::dbAppendTable( + con, + "package", + data.frame( + name = "dbplyr", + version = "1.0.0", + score = "0.32" + ) + ) + } ) # add test db location to the app session: app_session <- MockShinySession$new() From 1da48a7e402d089260866f983cb394aa21eaa8e7 Mon Sep 17 00:00:00 2001 From: Jeff Thompson Date: Wed, 31 Jan 2024 14:33:53 -0500 Subject: [PATCH 04/22] Repair test --- R/mod_packageDependencies.R | 2 +- tests/testthat/test-packageDependencies.R | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/R/mod_packageDependencies.R b/R/mod_packageDependencies.R index b80b7487e..181ff70c7 100644 --- a/R/mod_packageDependencies.R +++ b/R/mod_packageDependencies.R @@ -93,7 +93,7 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { if (rlang::is_empty(pkgref()$dependencies[[1]])) depends(dplyr::tibble(package = character(0), type = character(0), name = character(0))) revdeps(pkgref()$reverse_dependencies[[1]] %>% as.vector()) - revdeps_local(with(loaded2_db(), name[name %in% revdeps()])) + revdeps_local(with(session$userData$loaded2_db(), name[name %in% revdeps()])) # send either depends() or both to build_dep_cards(), depending on toggled() if (toggled() == 0L) { diff --git a/tests/testthat/test-packageDependencies.R b/tests/testthat/test-packageDependencies.R index f7e0ad990..5c20d2f51 100644 --- a/tests/testthat/test-packageDependencies.R +++ b/tests/testthat/test-packageDependencies.R @@ -90,6 +90,7 @@ test_that( app_session$options$golem_options <- list( assessment_db_name = temp_db_loc ) + app_session$userData$loaded2_db <- reactiveVal(dbSelect("SELECT name, version, score FROM package", temp_db_loc)) testServer(packageDependenciesServer, args = testargs, { session$flushReact() From 25f0c4c705537082f14c059d91a4e1a17623e705 Mon Sep 17 00:00:00 2001 From: LDSamson Date: Sun, 4 Feb 2024 10:16:03 +0100 Subject: [PATCH 05/22] Implement local revdeps table --- NAMESPACE | 1 + R/mod_packageDependencies.R | 131 ++++++++++++------------------------ R/utils.R | 118 +++++++++++++++++++++++++++++++- man/add_buttons_to_table.Rd | 33 +++++++++ man/datatable_custom.Rd | 34 ++++++++++ 5 files changed, 227 insertions(+), 90 deletions(-) create mode 100644 man/add_buttons_to_table.Rd create mode 100644 man/datatable_custom.Rd diff --git a/NAMESPACE b/NAMESPACE index a73fff296..3d978267f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(add_buttons_to_table) export(app_theme) export(build_comm_plotly) export(generate_comm_data) diff --git a/R/mod_packageDependencies.R b/R/mod_packageDependencies.R index 181ff70c7..b4734a422 100644 --- a/R/mod_packageDependencies.R +++ b/R/mod_packageDependencies.R @@ -35,7 +35,6 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { depends <- reactiveVal(value = NULL) suggests <- reactiveVal(value = NULL) revdeps <- reactiveVal(value = NULL) - revdeps_local <- reactiveVal(value = NULL) rev_pkg <- reactiveVal(value = 0L) toggled <- reactiveVal(value = 0L) pkg_updates <- reactiveValues() @@ -93,7 +92,6 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { if (rlang::is_empty(pkgref()$dependencies[[1]])) depends(dplyr::tibble(package = character(0), type = character(0), name = character(0))) revdeps(pkgref()$reverse_dependencies[[1]] %>% as.vector()) - revdeps_local(with(session$userData$loaded2_db(), name[name %in% revdeps()])) # send either depends() or both to build_dep_cards(), depending on toggled() if (toggled() == 0L) { @@ -150,24 +148,20 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { }, ignoreInit = TRUE) data_table <- eventReactive(pkg_df(), { - cbind( - pkg_df(), - data.frame( - Actions = shinyInput( - actionButton, nrow(pkg_df()), - "button_", - size = "xs", - style = "height:24px; padding-top:1px;", - label = icon("arrow-right", class = "fa-regular", lib = "font-awesome"), - onclick = paste0('Shiny.setInputValue(\"', ns("select_button"), '\", this.id, {priority: "event"})') - ) - ) - ) %>% # remove action button if there is nothing to review + add_buttons_to_table(pkg_df(), ns("select_button")) %>% + # remove action button if there is nothing to review mutate(Actions = if_else(identical(package, character(0)) | name %in% c(rownames(installed.packages(priority = "base"))), "", Actions)) %>% # if package name not yet loaded, switch the actionbutton to fa-upload mutate(Actions = if_else(!name %in% session$userData$loaded2_db()$name, gsub("fas fa-arrow-right fa-regular", "fas fa-upload fa-solid", Actions), Actions)) }) + table_revdeps_local <- reactive({ + df <- session$userData$loaded2_db() + req(df, df$name) + with(df, df[name %in% revdeps(),]) |> + add_buttons_to_table(ns("go_to_revdep")) + }) + # Create metric grid card. metricGridServer(id = 'metricGrid', metrics = cards) @@ -221,70 +215,20 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { column( width = 8, DT::renderDataTable(server = FALSE, { - # Hiding name from DT table. target contains index for "name" - # The - 1 is because js uses 0 index instead of 1 like R - target <- which(names(data_table()) %in% c("name")) - 1 - - formattable::as.datatable( - formattable::formattable( - data_table(), - list( - score = formattable::formatter( - "span", - style = x ~ formattable::style( - display = "block", - "border-radius" = "4px", - "padding-right" = "4px", - "color" = "#000000", - "order" = x, - "background-color" = formattable::csscolor( - setColorPalette(100)[round(as.numeric(x)*100)] - ) - ) - ), - decision = formattable::formatter( - "span", - style = x ~ formattable::style( - display = "block", - "border-radius" = "4px", - "padding-right" = "4px", - "font-weight" = "bold", - "color" = ifelse(x %in% decision_lst, "white", "inherit"), - "background-color" = - ifelse(x %in% decision_lst, - color_lst[x], - "transparent" - ) - ) - ) - ) - ), - selection = "none", - colnames = c("Package", "Type", "Name", "Version", "Score", "Review Package"), - rownames = FALSE, - options = list( - lengthMenu = list(c(15, -1), c("15", "All")), - columnDefs = list(list(visible = FALSE, targets = target)), - searchable = FALSE - ), - style = "default" - ) %>% - DT::formatStyle(names(data_table()), textAlign = "center") - }) - ) # column - ), # fluidRow - br(), br(), - h4(glue::glue("Reverse Dependencies available in database: {length(revdeps_local())}"), style = "text-align: left;"), - br(), - fluidRow( - column( - width = 8, - wellPanel( - renderText(revdeps_local() %>% sort()), - style = "max-height: 500px; overflow: auto" - ) + datatable_custom(data_table()) + }), + br(), br(), + h4(glue::glue("Reverse Dependencies available in database: {nrow(table_revdeps_local()) %||% 0}"), style = "text-align: left;"), + br(), + DT::renderDataTable({ + datatable_custom( + table_revdeps_local(), + colnames = c("Package", "Version", "Score", "Review Package"), + hide_names = NULL + ) + }) ) - ), + ), br(), br(), h4(glue::glue("All reverse Dependencies: {length(revdeps())}"), style = "text-align: left;"), br(), @@ -302,21 +246,30 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { } }) # renderUI + # the package selected in the table to either browser to or to upload: + selected_package <- reactiveVal() pkgname <- reactiveVal() + observeEvent(input$select_button, { + req(pkg_df()) + selectedRow <- as.numeric(strsplit(input$select_button, "_")[[1]][2]) + selected_package(with(pkg_df(), name[selectedRow])) + pkgname("-") + }) + observeEvent(input$go_to_revdep, { + req(table_revdeps_local()) + selectedRow <- as.numeric(strsplit(input$go_to_revdep, "_")[[1]][2]) + selected_package(with(table_revdeps_local(), name[selectedRow])) + }) - observeEvent(input$select_button, + observeEvent(selected_package(), { - req(pkg_df()) rev_pkg(0L) - - selectedRow <- as.numeric(strsplit(input$select_button, "_")[[1]][2]) - - # grab the package name - pkg_name <- pkg_df()[selectedRow, 3] %>% pull() pkgname("-") - - if (!pkg_name %in% session$userData$loaded2_db()$name) { - pkgname(pkg_name) + # to ensure that if the same package is clicked on, this observeEvent will + # run again: + on.exit(selected_package(NULL)) + if (!selected_package() %in% session$userData$loaded2_db()$name) { + pkgname(selected_package()) shiny::showModal(modalDialog( size = "l", easyClose = TRUE, @@ -341,7 +294,7 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { session = parent, inputId = "sidebar-select_pkg", choices = c("-", session$userData$loaded2_db()$name), - selected = pkg_name + selected = selected_package() ) } }, diff --git a/R/utils.R b/R/utils.R index b9f22454e..38f2769e1 100644 --- a/R/utils.R +++ b/R/utils.R @@ -540,4 +540,120 @@ shinyInput <- function(FUN, len, id, ...) { inputs[i] <- as.character(FUN(paste0(id, i), ...)) } inputs -} \ No newline at end of file +} + +#' Custom datatable +#' +#' Small helper function to create a `DT::datatable()` object in a consistent +#' style. +#' +#' @param data A data frame as input. +#' @param colnames +#' @param hide_names Character vector. Whether to hide columns in the data +#' frame. +#' @param ... Other options. Currently not in use. +#' +#' @return a DT::datatable object. +#' +#' @examples datatable_custom(mtcars, colnames = paste0("custom_", names(mtcars))) +#' +datatable_custom <- function( + data, + colnames = c("Package", "Type", "Name", "Version", "Score", "Review Package"), + hide_names = "name", + ... +){ + colnames <- colnames %||% character(0) + hide_names <- hide_names %||% character(0) + data <- data %||% as.data.frame(matrix(nrow = 0, ncol = pmax(length(colnames), 1) )) + stopifnot(is.data.frame(data)) + stopifnot(is.character(hide_names)) + stopifnot(is.character(colnames)) + # Hiding name from DT table. + # The - 1 is because js uses 0 index instead of 1 like R + target <- which(names(data) %in% hide_names) - 1 + + formattable::as.datatable( + formattable::formattable( + data, + list( + score = formattable::formatter( + "span", + style = x ~ formattable::style( + display = "block", + "border-radius" = "4px", + "padding-right" = "4px", + "color" = "#000000", + "order" = x, + "background-color" = formattable::csscolor( + setColorPalette(100)[round(as.numeric(x)*100)] + ) + ) + ), + decision = formattable::formatter( + "span", + style = x ~ formattable::style( + display = "block", + "border-radius" = "4px", + "padding-right" = "4px", + "font-weight" = "bold", + "color" = ifelse(x %in% decision_lst, "white", "inherit"), + "background-color" = + ifelse(x %in% decision_lst, + color_lst[x], + "transparent" + ) + ) + ) + ) + ), + selection = "none", + colnames = if(length(colnames) == 0) names(data) else colnames, + rownames = FALSE, + options = list( + lengthMenu = list(c(15, -1), c("15", "All")), + columnDefs = list(list(visible = FALSE, targets = target)), + searchable = FALSE + ), + style = "default" + ) %>% + DT::formatStyle(names(data), textAlign = "center") +} + +#' Add buttons to data frame +#' +#' Small helper function to add Shiny action buttons to a data frame. +#' +#' @param data A data frame. +#' @param id Character vector. the main id of the buttons. +#' @param label Label to use for the button. +#' @param ... For future expansions. Currently not in use. +#' +#' @return A data frame with a button in each table row. +#' @export +#' +#' @examples +#' add_buttons_to_table(mtcars[, 1:5], "button_id", "click me") |> +#' datatable_custom() +#' +add_buttons_to_table <- function( + data, + id, + label = icon("arrow-right", class = "fa-regular", lib = "font-awesome"), + ... +){ + stopifnot(is.data.frame(data)) + cbind( + data, + data.frame( + Actions = shinyInput( + actionButton, nrow(data), + "button_", + size = "xs", + style = "height:24px; padding-top:1px;", + label = label, + onclick = paste0('Shiny.setInputValue(\"', id, '\", this.id, {priority: "event"})') + ) + ) + ) +} diff --git a/man/add_buttons_to_table.Rd b/man/add_buttons_to_table.Rd new file mode 100644 index 000000000..ec5a97858 --- /dev/null +++ b/man/add_buttons_to_table.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{add_buttons_to_table} +\alias{add_buttons_to_table} +\title{Add buttons to data frame} +\usage{ +add_buttons_to_table( + data, + id, + label = icon("arrow-right", class = "fa-regular", lib = "font-awesome"), + ... +) +} +\arguments{ +\item{data}{A data frame.} + +\item{id}{Character vector. the main id of the buttons.} + +\item{label}{Label to use for the button.} + +\item{...}{For future expansions. Currently not in use.} +} +\value{ +A data frame with a button in each table row. +} +\description{ +Small helper function to add Shiny action buttons to a data frame. +} +\examples{ +add_buttons_to_table(mtcars[, 1:5], "button_id", "click me") |> + datatable_custom() + +} diff --git a/man/datatable_custom.Rd b/man/datatable_custom.Rd new file mode 100644 index 000000000..646607de2 --- /dev/null +++ b/man/datatable_custom.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{datatable_custom} +\alias{datatable_custom} +\title{Custom datatable} +\usage{ +datatable_custom( + data, + colnames = c("Package", "Type", "Name", "Version", "Score", "Review Package"), + hide_names = "name", + ... +) +} +\arguments{ +\item{data}{A data frame as input.} + +\item{colnames}{} + +\item{hide_names}{Character vector. Whether to hide columns in the data +frame.} + +\item{...}{Other options. Currently not in use.} +} +\value{ +a DT::datatable object. +} +\description{ +Small helper function to create a \code{DT::datatable()} object in a consistent +style. +} +\examples{ +datatable_custom(mtcars, colnames = paste0("custom_", names(mtcars))) + +} From 2abf267bfadabd4cde0c3d9a67d79c9458cbc1c7 Mon Sep 17 00:00:00 2001 From: LDSamson Date: Sun, 4 Feb 2024 11:26:16 +0100 Subject: [PATCH 06/22] Fix selected package name not showing in 'confirm to upload package' modal --- R/mod_packageDependencies.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/mod_packageDependencies.R b/R/mod_packageDependencies.R index b4734a422..4f2991343 100644 --- a/R/mod_packageDependencies.R +++ b/R/mod_packageDependencies.R @@ -279,7 +279,7 @@ packageDependenciesServer <- function(id, selected_pkg, user, parent) { fluidRow( column( width = 12, - "Please confirm to load this package: ", span(class = "text-info", input$decision), + "Please confirm to load this package: ", span(class = "text-info", selected_package()), ) ), br(), From 177e900a91c0b2bfafc7c4644b34afb6743feb7a Mon Sep 17 00:00:00 2001 From: LDSamson Date: Sun, 4 Feb 2024 11:41:27 +0100 Subject: [PATCH 07/22] Add utils tests and fix module test --- tests/testthat/test-packageDependencies.R | 7 ++-- tests/testthat/test-utils.R | 42 +++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-packageDependencies.R b/tests/testthat/test-packageDependencies.R index 5c20d2f51..df99f56c1 100644 --- a/tests/testthat/test-packageDependencies.R +++ b/tests/testthat/test-packageDependencies.R @@ -49,7 +49,8 @@ test_that( Scenario 1. Given the selected package is 'dplyr', and the packages names in the package database are 'dplyr' and 'dbplyr', I expect that the package names 'plotly', 'admiral', 'dbplyr' and 'glue' are found in [revdeps], - and that [revdeps_local] is equal to 'dbplyr'.", + and that [table_revdeps_local] contains the package name 'dbplyr', + and that [table_revdeps_local] contains an action button", { testargs <- list( selected_pkg = list( @@ -95,7 +96,9 @@ test_that( testServer(packageDependenciesServer, args = testargs, { session$flushReact() expect_true(all(c("plotly", "admiral", "dbplyr", "glue") %in% revdeps())) - expect_equal(revdeps_local(), "dbplyr") + expect_equal(table_revdeps_local()$name, "dbplyr") + # the table contains an action button: + expect_true(grepl('button id=\"button_1\"',table_revdeps_local()$Actions)) }, session = app_session) } diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index a755a0cc0..354d7641e 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -165,3 +165,45 @@ test_that("Test that get_time() works", { test_that("Test that build_dep_cards() works", { skip("Placeholder for build_dep_cards()") }) + +test_that("add_buttons_to_table works", { + expect_no_error(add_buttons_to_table(mtcars, "testid", "click me")) +}) +test_that("add_buttons_to_table adds unique buttons to each table row", { + output <- add_buttons_to_table(mtcars, "testid", "click me") + # individual buttons are added to each row: + expected_button_ids <- paste0('