Compare commits

...

8 Commits

Author SHA1 Message Date
Alan Dipert
9884895b34 document 2020-04-22 23:00:14 +00:00
Alan Dipert
a9f2511374 $setInputs(): make dots dynamic 2020-04-22 22:59:22 +00:00
Alan Dipert
6f8f2611b7 $click, $setInputs: add \\dontrun 2020-04-22 20:34:52 +00:00
Alan Dipert
f4e793be78 $click() examples 2020-04-22 20:18:15 +00:00
Alan Dipert
d49ff29fa7 session$click() test w/ observeEvent 2020-04-22 20:15:34 +00:00
Alan Dipert
f2a1e59995 session$click() test w/ observeEvent 2020-04-22 20:13:40 +00:00
Alan Dipert
d1239b454a Fix return value of MockShinySession$click() 2020-04-22 20:02:47 +00:00
Alan Dipert
65d64e01e8 MockShinySession: add $click() 2020-04-22 20:01:04 +00:00
3 changed files with 116 additions and 20 deletions

View File

@@ -70,8 +70,7 @@ extract <- function(promise) {
}
#' @noRd
mapNames <- function(func, ...) {
vals <- list(...)
mapNames <- function(func, vals) {
names(vals) <- vapply(names(vals), func, character(1))
vals
}
@@ -190,20 +189,35 @@ MockShinySession <- R6Class(
return(paste('data:', contentType, ';base64,', b64, sep=''))
},
#' @description Sets reactive values associated with the `session$inputs` object
#' and flushes the reactives.
#' @param ... The inputs to set.
#' @description Sets reactive values associated with the `session$inputs`
#' object and flushes the reactives.
#' @param ... The inputs to set. These arguments are processed with
#' [rlang::list2()] and so are _[dynamic][rlang::dyn-dots]_. Input names
#' may not be duplicated.
#' @examples
#' s <- MockShinySession$new()
#' s$setInputs(x=1, y=2)
#' \dontrun{
#' session$setInputs(x=1, y=2)
#' }
setInputs = function(...) {
vals <- list(...)
vals <- rlang::dots_list(..., .homonyms = "error")
mapply(names(vals), vals, FUN = function(name, value) {
private$.input$set(name, value)
})
private$flush()
},
#' @description Simulates clicking an action button.
#' @param id The id of the button to click.
#' @examples
#' \dontrun{
#' session$click("button1")
#' }
click = function(id) {
val <- (private$.input$get(id) %OR% 0L) + 1L
self$setInputs(!!id := val)
val
},
#' @description An internal method which shouldn't be used by others.
#' @param millis The number of milliseconds on which to schedule a callback
#' @param callback The function to schedule
@@ -399,7 +413,10 @@ MockShinySession <- R6Class(
output = structure(.createOutputWriter(self, ns = ns), class = "shinyoutput"),
makeScope = function(namespace) self$makeScope(ns(namespace)),
ns = function(namespace) ns(namespace),
setInputs = function(...) do.call(self$setInputs, mapNames(ns, ...))
click = function(id) self$click(ns(id)),
setInputs = function(...) {
self$setInputs(!!!mapNames(ns, rlang::dots_list(..., .homonyms = "error")))
}
)
},
#' @description Set the environment associated with a testServer() call, but

View File

@@ -13,8 +13,17 @@ provided to Shiny server functions or modules.
## Method `MockShinySession$setInputs`
## ------------------------------------------------
s <- MockShinySession$new()
s$setInputs(x=1, y=2)
\dontrun{
session$setInputs(x=1, y=2)
}
## ------------------------------------------------
## Method `MockShinySession$click`
## ------------------------------------------------
\dontrun{
session$click("button1")
}
}
\section{Public fields}{
\if{html}{\out{<div class="r6-fields">}}
@@ -59,6 +68,7 @@ s$setInputs(x=1, y=2)
\item \href{#method-cycleStartAction}{\code{MockShinySession$cycleStartAction()}}
\item \href{#method-fileUrl}{\code{MockShinySession$fileUrl()}}
\item \href{#method-setInputs}{\code{MockShinySession$setInputs()}}
\item \href{#method-click}{\code{MockShinySession$click()}}
\item \href{#method-.scheduleTask}{\code{MockShinySession$.scheduleTask()}}
\item \href{#method-elapse}{\code{MockShinySession$elapse()}}
\item \href{#method-.now}{\code{MockShinySession$.now()}}
@@ -255,8 +265,8 @@ Base64-encode the given file. Needed for image rendering.
\if{html}{\out{<a id="method-setInputs"></a>}}
\if{latex}{\out{\hypertarget{method-setInputs}{}}}
\subsection{Method \code{setInputs()}}{
Sets reactive values associated with the \code{session$inputs} object
and flushes the reactives.
Sets reactive values associated with the \code{session$inputs}
object and flushes the reactives.
\subsection{Usage}{
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setInputs(...)}\if{html}{\out{</div>}}
}
@@ -264,14 +274,44 @@ and flushes the reactives.
\subsection{Arguments}{
\if{html}{\out{<div class="arguments">}}
\describe{
\item{\code{...}}{The inputs to set.}
\item{\code{...}}{The inputs to set. These arguments are processed with
\code{\link[rlang:list2]{rlang::list2()}} and so are \emph{\link[rlang:dyn-dots]{dynamic}}. Input names
may not be duplicated.}
}
\if{html}{\out{</div>}}
}
\subsection{Examples}{
\if{html}{\out{<div class="r example copy">}}
\preformatted{s <- MockShinySession$new()
s$setInputs(x=1, y=2)
\preformatted{\dontrun{
session$setInputs(x=1, y=2)
}
}
\if{html}{\out{</div>}}
}
}
\if{html}{\out{<hr>}}
\if{html}{\out{<a id="method-click"></a>}}
\if{latex}{\out{\hypertarget{method-click}{}}}
\subsection{Method \code{click()}}{
Simulates clicking an action button.
\subsection{Usage}{
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$click(id)}\if{html}{\out{</div>}}
}
\subsection{Arguments}{
\if{html}{\out{<div class="arguments">}}
\describe{
\item{\code{id}}{The id of the button to click.}
}
\if{html}{\out{</div>}}
}
\subsection{Examples}{
\if{html}{\out{<div class="r example copy">}}
\preformatted{\dontrun{
session$click("button1")
}
}
\if{html}{\out{</div>}}

View File

@@ -61,10 +61,7 @@ test_that("testServer handles observers", {
})
test_that("inputs aren't directly assignable", {
module <- function(id) {
moduleServer(id, function(input, output, session) {
})
}
module <- function(id) moduleServer(id, function(input, output, session) {})
testServer(module, {
session$setInputs(x = 0)
@@ -73,6 +70,48 @@ test_that("inputs aren't directly assignable", {
})
})
test_that("inputs can be incremented like actionButtons with session$click", {
module <- function(id) {
moduleServer(id, function(input, output, session) {
num_clicks <- reactiveVal(0)
})
}
testServer(module, {
expect_null(input$button1)
expect_equal(num_clicks(), 0)
observeEvent(input$button1, num_clicks(num_clicks() + 1))
newv <- session$click("button1")
expect_equal(newv, 1)
expect_equal(input$button1, 1)
expect_equal(num_clicks(), 1)
for (i in 1:10) session$click("button1")
expect_equal(input$button1, 11)
expect_equal(num_clicks(), 11)
})
})
test_that("setInputs dots are dynamic", {
module <- function(id) moduleServer(id, function(input, output, session) {})
inputs_initial <- list(x=1, y=2)
input_y <- "y"
testServer(module, {
session$setInputs(!!!inputs_initial)
expect_equal(input$x, 1)
expect_equal(input$y, 2)
session$setInputs(!!input_y := 3)
expect_equal(input$y, 3)
# Duplicate names are an error
expect_error(session$setInputs(x = 1, x = 2))
})
})
test_that("testServer handles more complex expressions", {
module <- function(id) {
moduleServer(id, function(input, output, session){