test_that("Render functions correctly handle quosures", { # Normally, quosures are not unwrapped at creation time. # However, using inject() will make it unwrap at creation time. a <- 1 r1 <- inject(renderText({ !!a })) r2 <- renderText({ eval_tidy(quo(!!a)) }) a <- 2 expect_identical(r1(), "1") expect_identical(r2(), "2") a <- 1 r1 <- inject(renderPrint({ !!a })) r2 <- renderPrint({ eval_tidy(quo(!!a)) }) a <- 2 expect_identical(r1(), "[1] 1") expect_identical(r2(), "[1] 2") a <- 1 r1 <- inject(renderUI({ tags$p(!!a) })) r2 <- renderUI({ eval_tidy(quo(tags$p(!!a))) }) a <- 2 res1 <- r1(shinysession = MockShinySession$new(), name = "foo") expect_identical(as.character(res1$html), "
1
") res2 <- r2(shinysession = MockShinySession$new(), name = "foo") expect_identical(as.character(res2$html), "2
") a <- 1 r1 <- inject(renderTable({ pressure[!!a, ] }, digits = 1)) r2 <- renderTable({ eval_tidy(quo(pressure[!!a, ])) }, digits = 1) a <- 2 expect_true(grepl("0\\.0", r1())) expect_true(grepl("20\\.0", r2())) }) test_that("functionLabel returns static value when the label can not be assigned to", { getFunc <- function(exprF, envF = parent.frame(), quotedF = FALSE) { quoToFunction(enquo0(exprF)) } expect_label <- function(func, labely) { expect_equal( as.character(body(func)[[2]][[1]]), labely ) } a <- 1 expect_label( getFunc({a + 1}), "getFunc" ) # multiline labels are not supported expect_label( (function(exprF) { quoToFunction(enquo0(exprF)) })(), "anonymous" ) # parents are not supported expect_label( (function(exprF) {quoToFunction(enquo0(exprF))})(), "anonymous" ) }) local({ # (must also copy logic into `lower - quoToFunction(enquo0(expr))` code) return_func <- function(func) { function() { value <- func() list(value, value) } } for (info in list( list( name = "exprToFunction(expr, env, quoted)", fn = function(exprF, envF = parent.frame(), quotedF = FALSE) { func <- exprToFunction(exprF, envF, quotedF) return_func(func) } ), list( name = "exprToFunction(expr, env, quoted = TRUE)", fn = function(exprF, envF = parent.frame(), quotedF = FALSE) { # `exprF` coudl be a raw quosure if `inject()`ed if (!quotedF) exprF <- substitute(exprF) func <- exprToFunction(exprF, envF, quoted = TRUE) return_func(func) } ), list( name = "exprToFunction(expr, env, quoted = TRUE) + force()", fn = function(exprF, envF = parent.frame(), quotedF = FALSE) { # Make `exprF` always language, even if `inject()`ed if (!quotedF) exprF <- substitute(force(exprF)) func <- exprToFunction(exprF, envF, quoted = TRUE) return_func(func) } ), list( name = "installExprFunction(expr, \"func\", env, quoted)", fn = function(exprF, envF = parent.frame(), quotedF = FALSE) { a <- 1000 installExprFunction(exprF, "func", envF, quotedF) return_func(func) } ), list( name = "installExprFunction(expr, \"func\", env, quoted = TRUE)", fn = function(exprF, envF = parent.frame(), quotedF = FALSE) { a <- 1000 # `exprF` coudl be a raw quosure if `inject()`ed if (!quotedF) exprF <- substitute(exprF) installExprFunction(exprF, "func", envF, quoted = TRUE) return_func(func) } ), list( name = "installExprFunction(expr, \"func\", env, quoted = TRUE)", fn = function(exprF, envF = parent.frame(), quotedF = FALSE) { a <- 1000 # Make `exprF` always language, even if `inject()`ed if (!quotedF) exprF <- substitute(force(exprF)) installExprFunction(exprF, "func", envF, quoted = TRUE) return_func(func) } ), list( name = "quoToFunction(enquo0(expr))", fn = function(expr) { func <- quoToFunction(enquo0(expr)) return_func(func) } ), list( name = "lower - quoToFunction(enquo0(expr))", fn = function(expr) { function() { func <- quoToFunction(enquo0(expr)) value <- func() list(value, value) } } ) )) { # Scope the local variables local({ renderH <- info$fn %||% stop("`info$fn` not found") # Different usages of env and quoted param a <- 1 e <- new.env() e$a <- 10 test_that(paste0("vanilla: ", info$name), { val <- renderH({a + 1})() expect_identical(val, list(2, 2)) }) # Test that no error is thrown when the function is created # This proves that the expression is not immediately evaluated test_that(paste0("stop('boom'): ", info$name), { expect_error( renderH(stop("boom")), NA ) }) if (length(formals(renderH)) > 1) { test_that(paste0("quoted = FALSE: ", info$name), { r <- renderH(a + 1, quotedF = FALSE) expect_identical(r(), list(2, 2)) }) test_that(paste0("quoted = TRUE: ", info$name), { r <- renderH(quote(a + 1), quotedF = TRUE) expect_identical(r(), list(2, 2)) }) test_that(paste0("env = e: ", info$name), { r <- renderH(a + 1, envF = e) expect_identical(r(), list(11, 11)) }) test_that(paste0("env = e, quoted = FALSE: ", info$name), { r <- renderH(a + 1, envF = e, quotedF = FALSE) expect_identical(r(), list(11, 11)) }) test_that(paste0("env = e, quoted = TRUE: ", info$name), { r <- renderH(quote(a + 1), envF = e, quotedF = TRUE) expect_identical(r(), list(11, 11)) }) test_that(paste0("Works with raw quosures, quoted = FALSE: ", info$name), { e <- list2env(list(a=10)) x <- new_quosure(quote({ a + 1 }) , env = e) r <- renderH(x, quotedF = FALSE) expect_identical(r(), list(x, x)) }) test_that(paste0( "Passing in a raw quosures, quoted = FALSE, env = otherenv", " is treated like an expression: ", info$name), { e <- list2env(list(a=10)) x <- new_quosure(quote({ a + 1 }) , env = e) other_env <- list2env(list(x=20)) r <- renderH(x, quotedF = FALSE, envF = e) expect_identical(r(), list(x, x)) }) test_that( paste0("Works with injected quosures, quoted = FALSE, env = otherenv: ", info$name), { e <- list2env(list(a=10)) x <- new_quosure(quote({ a + 1 }) , env = e) other_env <- new.env(parent = emptyenv()) r <- inject(renderH(!!x, quotedF = FALSE, envF = e)) expect_identical(r(), list(11, 11)) }) test_that(paste0("Works with raw quosures, quoted = TRUE: ", info$name), { e <- list2env(list(a=10)) x <- new_quosure(quote({ a + 1 }) , env = e) ans <- renderH(x, quotedF = TRUE)() expect_identical(ans, list(11, 11)) }) test_that(paste0("Works with injecting raw quosures: ", info$name), { e <- list2env(list(a=10)) x <- new_quosure(quote({ a + 1 }) , env = e) ans <- inject(renderH(!!x))() expect_identical(ans, list(11, 11)) }) test_that(paste0("Missing env with quosure, quoted = TRUE: ", info$name), { e <- list2env(list(a=10)) x <- new_quosure(quote({ a + 1 }) , env = e) ans <- renderH(x, envF = rlang::missing_arg(), quotedF = TRUE)() expect_identical(ans, list(11, 11)) }) } test_that(paste0("Works with inject / !!: ", info$name), { # Quosures a <- 1 r1 <- inject(renderH({ !!a })) r2 <- renderH({ eval_tidy(quo(!!a)) }) a <- 100 expect_identical(r1(), list(1,1)) expect_identical(r2(), list(100, 100)) }) }) } }) test_that("nested observe events work with exprToFunction", { val <- 0 local({ t1 <- reactiveVal(0) t2 <- reactiveVal(10) observeEvent( { # message("outer observeEvent trigger") val <<- val + 1 t1() }, { # message("outer observeEvent handler") val <<- val + 2 observeEvent( { # message("inner observeEvent trigger") val <<- val + 3 t2() }, { val <<- val + 4 # message("inner observeEvent handler") } ) } ) }) expect_equal(val, 0) flushReact() expect_equal(val, 1 + 2 + 3 + 4) })