Nothing
context("props")
library(shiny)
# Recursively crawl a list and replace any environments with a special
# environment. This is a workaround for a change in behavior in R 3.2.0
# for all.equal when given two environments.
blank_envs <- function(x) {
replacement_env <- new.env(parent = emptyenv())
# Use `x[]<-` to preserve any attributes on x
x[] <- lapply(x, function(val) {
if (is.environment(val)) replacement_env
else if (is.list(val)) blank_envs(val)
else val
})
x
}
test_prop <- function(p, property, value, scale, event = "update") {
expect_identical(p$property, property)
expect_identical(p$value, value)
expect_identical(p$scale, scale)
expect_identical(p$event, event)
}
test_that("creating prop objects with prop()", {
expect_error(prop("y")) # Need value
expect_error(prop(x = 1)) # Need scale
# Unscaled, constant
test_prop(prop("x", 1, scale = FALSE), "x", 1, NULL)
test_prop(prop("x", 1), "x", 1, NULL) # Use default scale
test_prop(prop("x", ~1), "x", 1, "x")
test_prop(prop("x2", 1), "x2", 1, NULL)
# Unscaled, variable, with quote() and ~
test_prop(prop("x", quote(cyl), scale = FALSE), "x", quote(cyl), NULL)
test_prop(prop("x2", ~cyl, scale = FALSE), "x2", quote(cyl), NULL)
# Scaled, constant
test_prop(prop("x", 1, scale = "x"), "x", 1, "x")
test_prop(prop("x2", 1, scale = "x"), "x2", 1, "x")
test_prop(prop("fill", 1, scale = "foo"), "fill", 1, "foo")
# Scaled, variable
test_prop(prop("x", quote(cyl)), "x", quote(cyl), "x") # Use default scale
test_prop(prop("x2", ~cyl), "x2", quote(cyl), "x")
test_prop(prop("x", quote(cyl), scale = "x"), "x", quote(cyl), "x")
test_prop(prop("x", ~cyl, scale = "foo"), "x", quote(cyl), "foo")
# Event is used
test_prop(prop("x", 1, event = "update"), "x", 1, NULL, "update")
test_prop(prop("x", 1, event = "enter"), "x", 1, NULL, "enter")
# key doesn't get an event
test_prop(prop("key", ~id, scale = FALSE), "key", quote(id), NULL, NULL)
expect_error(prop("key", ~id, event = "update"))
# scale must be FALSE
expect_error(prop("key", ~id))
expect_error(prop("key", ~id, scale = TRUE))
# can't be constant
expect_error(prop("key", 1:10))
})
test_that("property names for variables", {
# pname(wt) is equivalent to prop_label(prop(quote(wt)))
pname <- function(x) prop_label(prop("x", substitute(x)))
# Regular variable names are simply converted to string
expect_identical(pname(wt), "wt")
# Variable names with special characters are made vega-safe
expect_identical(pname(`wt/mpg`), "wt/mpg")
expect_identical(pname(`wt.mpg`), "wt.mpg")
# safe_vega_var escapes "."
expect_identical(safe_vega_var("wt.mpg"), "wt\\.mpg")
# Expressions are kept as is
expect_identical(pname(wt/mpg), "wt/mpg")
# Expressions and weird column names will produce same name
# (but this is very unlikely)
expect_true(pname(wt/mpg) == pname(`wt/mpg`))
# Constants have no variable name
expect_identical(pname(10), "")
expect_identical(pname("wt"), "")
})
test_that("property values for variables", {
# Create a data frame with columns 'a', 'b', and 'b-a'
dat <- data.frame(a = 1:3, b = 4:6, c = 7:9)
names(dat)[3] <- "b-a"
# Column named `b-a`
expect_identical(prop_value(prop("x", quote(`b-a`)), dat), 7:9)
# Expression b-a
expect_equal(prop_value(prop("x", quote(b-a)), dat), c(3, 3, 3))
# Constant prop
expect_equal(prop_value(prop("x", 10), dat), rep(10, 3))
# Expression which returns a single value; value should be replicated
expect_equal(prop_value(prop("x", quote(min(a))), dat), c(1, 1, 1))
# Expression which returns # of values that doesn't divide into # of rows
expect_error(prop_value(prop("x", quote(range(a))), dat))
})
test_that("prop captures environment for evaluation", {
dat <- data.frame(a = 1:2, b = 3:4, c = 5:6)
b <- 11:12
d <- 13:14
# Column from data
expect_identical(prop_value(prop("x", quote(a)), dat), 1:2)
# Column from data, even though a var in the calling env has same name
expect_identical(prop_value(prop("x", quote(b)), dat), 3:4)
# Variable in calling environment
expect_identical(prop_value(prop("x", quote(d)), dat), 13:14)
# Column from data with variable in calling environment
expect_identical(prop_value(prop("x", quote(b+d)), dat), 3:4 + 13:14)
# Column from data with variable in calling environment
expect_identical(prop_value(prop("x", quote(b+d)), dat), 3:4 + 13:14)
# Create new environment for storing variables
env <- new.env()
env$d <- 23:24
# Variable in env
expect_identical(prop_value(prop("x", quote(d), env = env), dat), 23:24)
# Column from data, even though a var in env has same name
expect_identical(prop_value(prop("x", quote(b), env = env), dat), 3:4)
})
test_that("props() creates correct prop objects", {
# 6 combinations of := and constant/variable/reactive
p <- props(x := 1, y := ~mpg, x2 := reactive(cyl),
stroke = "foo", fill = ~wt, size = reactive(am))
expect_equal(sort(names(p)),
sort(c("x.update", "y.update", "x2.update",
"stroke.update", "fill.update", "size.update"))
)
test_prop(p$x.update, "x", 1, NULL)
test_prop(p$y.update, "y", quote(mpg), NULL)
# Reactives are hard to test with identical, so test the components
expect_identical(p$x2.update$property, "x2")
expect_true(is.reactive(p$x2.update$value))
expect_identical(p$x2.update$scale, NULL)
test_prop(p$stroke.update, "stroke", "foo", "stroke")
test_prop(p$fill.update, "fill", quote(wt), "fill")
expect_identical(p$size.update$property, "size")
expect_true(is.reactive(p$size.update$value))
expect_identical(p$size.update$scale, "size")
# Properties get default scales (e.g. x2 gets scale x)
p <- props(x2 = ~cyl)
test_prop(p$x2.update, "x2", quote(cyl), "x")
# Explicitly-created prop objects
p <- props(prop("fill", "foo"), prop("x2", ~cyl))
test_prop(p$fill.update, "fill", "foo", NULL)
test_prop(p$x2.update, "x2", quote(cyl), "x")
# Unnamed arguments
expect_error(props(~wt, ~mpg, 1)) # Too many
p <- props(~wt, 1)
test_prop(p$x.update, "x", quote(wt), "x")
test_prop(p$y.update, "y", 1, "y")
# Unnamed arguments, out of order
p <- props(~mpg, prop("fill", ~cyl), x = ~wt)
test_prop(p$x.update, "x", quote(wt), "x")
test_prop(p$y.update, "y", quote(mpg), "y")
test_prop(p$fill.update, "fill", quote(cyl), "fill")
# Prop objects, stored in variables
p1 <- prop("fill", ~cyl)
p2 <- 1
p <- props(p1, p2)
test_prop(p$x.update, "x", 1, "x")
test_prop(p$fill.update, "fill", quote(cyl), "fill")
# Prop objects with event (.enter, .exit, .update, .hover)
p <- props(x.exit := 1, prop("fill", ~cyl, event = "enter"))
test_prop(p$x.exit, "x", 1, NULL, "exit")
test_prop(p$fill.enter, "fill", quote(cyl), "fill", "enter")
# x.enter doesn't block automatic naming for x.update
p <- props(x.enter := 1, ~mpg)
test_prop(p$x.enter, "x", 1, NULL, "enter")
test_prop(p$x.update, "x", quote(mpg), "x", "update")
# key is special
p <- props(key := ~id)
test_prop(p$key, "key", quote(id), NULL, NULL)
expect_error(props(key = ~id)) # Must be unscaled
expect_error(props(key := 1:10)) # Can't be constant
})
test_that("props evaluates arguments in correct environment", {
p <- 1
f <- function() {
p <- 2
props(p)
}
expect_identical(f()$x.update$value, 2)
})
test_that("props uses environment in formulas", {
dat <- data.frame(a = 1:2, b = 3:4)
val <- 11:12
# Create a formula that captures the function environment
gen_formula <- function() {
val <- 21:22
~ val
}
p <- props(w = ~val, x := gen_formula(), y := 5, z := 6)
# Should get val from this environment, where w~val was defined
expect_identical(prop_value(p$w, dat), 11:12)
# Should get val from gen_formula environment, where x~val was defined
expect_identical(prop_value(p$x, dat), 21:22)
})
test_that("merging props", {
# Utility function: sort props by name
sortp <- function(p) p[sort(names(p))]
p_i <- blank_envs(props(x=~a, z:="red"))
q_i <- blank_envs(props(y=~b, z:="blue"))
q_ni <- blank_envs(props(y=~b, z:="blue", inherit=FALSE))
expect_equal(sortp(merge_props(p_i, q_i)), blank_envs(props(x=~a, y=~b, z:="blue")))
expect_equal(sortp(merge_props(p_i, q_ni)), q_ni)
expect_equal(
sortp(merge_props(q_ni, p_i)),
blank_envs(props(x=~a, y=~b, z:="red", inherit = FALSE))
)
})
test_that("prop_event_sets splits up props properly", {
p <- props(x.update = ~wt, x.enter = 1, y = ~mpg, fill.hover := "red")
ps <- prop_event_sets(p)
expect_equal(names(ps), c("enter", "update", "hover"))
expect_equal(names(ps$enter), "x")
expect_equal(names(ps$update), c("x", "y"))
expect_equal(names(ps$hover), "fill")
expect_identical(ps$enter$x, prop("x", 1, scale = TRUE, event = "enter"))
expect_identical(ps$update$x, prop("x", quote(wt), event = "update"))
expect_identical(ps$update$y, prop("y", quote(mpg), event = "update"))
expect_identical(ps$hover$fill, prop("fill", "red", event = "hover"))
expect_true(attr(ps$enter, "inherit"))
expect_true(attr(ps$update, "inherit"))
expect_true(attr(ps$hover, "inherit"))
# value of inherit is passed along
p <- props(x = ~wt, x.enter = 1, inherit = FALSE)
ps <- prop_event_sets(p)
expect_false(attr(ps$enter, "inherit"))
expect_false(attr(ps$update, "inherit"))
})
test_that("drop_props", {
p <- props(x = ~wt, x.enter = 0, stroke.enter := "black", stroke.hover := "red")
expect_identical(
drop_props(p, c("stroke", "strokeOpacity")),
props(x = ~wt, x.enter = 0)
)
# Use unname() because it drop_props returns an named empty list as opposed
# to an unnamed empty list.
expect_identical(
unname(drop_props(p, c("x", "stroke"))),
props()
)
})
test_that("band() is created properly", {
# Automatic setting of scale, event
test_prop(prop("width", band()), "width", NULL, "x", "update")
test_prop(prop("height", band()), "height", NULL, "y", "update")
# Explicit settings of scale, event
test_prop(prop("width", band(), scale = "x"), "width", NULL, "x")
test_prop(prop("width", band(), event = "enter"), "width", NULL, "x", "enter")
test_prop(prop("width", band(), scale = "foo"), "width", NULL, "foo")
# Create with props()
test_prop(props(width = band())$width.update, "width", NULL, "x", "update")
test_prop(props(height.enter = band())$height.enter, "height", NULL, "y", "enter")
# Error if property isn't width or height
expect_error(prop("x", band()))
expect_error(props(x = band()))
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.