context("templates") # Searches for an html dependency of format name[version], as in "d3[3.5.10]", # within the html-dependencies script tag findDep <- function(x, name, version) { deps <- sub( '.*.*', "\\1", x ) grepl(paste0(name, "[", version, "]"), deps, fixed = TRUE) } test_that("Code blocks are evaluated and rendered correctly", { template <- htmlTemplate(test_path("template-document.html"), x = div(class = "foo", "bar") ) html <- renderDocument(template) expect_true(grepl('
bar
', html)) # With text_ argument template <- htmlTemplate(text_ = "a {{ foo + 1 }} b", foo = 10) expect_identical(as.character(as.character(template)), "a 11 b") # Make char vectors are pasted together template <- htmlTemplate(text_ = c("a", "{{ foo + 1 }} b"), foo = 10) expect_identical(as.character(as.character(template)), "a\n11 b") }) test_template <- function(){ template <- htmlTemplate(test_path("template-document.html"), x = "") html <- renderDocument(template) expect_identical(Encoding(html), "UTF-8") # Create the string 'Δ★😎', making sure it's UTF-8 encoded on all platforms. # These characters are 2, 3, and 4 bytes long, respectively. pat <- rawToChar(as.raw(c(0xce, 0x94, 0xe2, 0x98, 0x85, 0xf0, 0x9f, 0x98, 0x8e))) Encoding(pat) <- "UTF-8" expect_true(grepl(pat, html)) # If template is passed text_ argument, make sure it's converted from native # to UTF-8. latin1_str <- rawToChar(as.raw(0xFF)) Encoding(latin1_str) <- "latin1" text <- as.character(htmlTemplate(text_ = latin1_str)) expect_identical(charToRaw(text), as.raw(c(0xc3, 0xbf))) } test_that("UTF-8 characters in templates with default locale", { # The default locale loc <- "" withr::with_locale(c(LC_COLLATE=loc, LC_CTYPE=loc, LC_MONETARY=loc, LC_TIME=loc), test_template()) }) test_that("UTF-8 characters in templates with Chinese locale", { # Chinese locale loc <- "Chinese" testthat::skip_if_not(is_locale_available(loc), "Chinese locale not available") withr::with_locale(c(LC_COLLATE=loc, LC_CTYPE=loc, LC_MONETARY=loc, LC_TIME=loc), test_template()) }) test_that("UTF-8 characters in template head but not body", { # On Windows, a string with "中文" will automatically be marked as UTF-8. ui <- tagList( tags$head(tags$script("alert('中文')")), "test" ) html <- htmlTemplate(test_path("template-basic.html"), body = ui) res <- renderDocument(html) expect_identical(Encoding(res), "UTF-8") expect_true(grepl("中文", res, fixed = TRUE)) # On Windows, a string with "á" will automatically be marked as latin1. ui <- tagList( tags$head(tags$script("alert('á')")), "test" ) html <- htmlTemplate(test_path("template-basic.html"), body = ui) res <- renderDocument(html) expect_identical(Encoding(res), "UTF-8") expect_true(grepl("á", res, fixed = TRUE)) }) test_that("Dependencies are added properly", { dep <- htmlDependency("d3", "3.5.10", c(href="shared"), script = "d3.js") # Add dependency by inserting a tag with a dependency template <- htmlTemplate(test_path("template-document.html"), x = attachDependencies(div(), dep) ) html <- renderDocument(template) expect_true(findDep(html, "d3", "3.5.10")) expect_true(grepl('', html, fixed = TRUE)) # Add dependency via a renderDocument template <- htmlTemplate(test_path("template-document.html"), x = "") html <- renderDocument(template, dep) expect_true(findDep(html, "d3", "3.5.10")) expect_true(grepl('', html, fixed = TRUE)) }) test_that("Dependencies can be suppressed", { # The template includes suppressDependencies("jquery"), so we shouldn't see # this dependency in the final output. dep <- htmlDependency("jquery", "1.11.3", c(href="shared"), script = "jquery.js") # Add dependency by inserting a tag with a dependency template <- htmlTemplate(test_path("template-document.html"), x = attachDependencies(div(), dep) ) html <- renderDocument(template) expect_true(findDep(html, "jquery", "9999")) expect_false(grepl(']+jquery[^>]+>', html)) # Add dependency via a renderDocument template <- htmlTemplate(test_path("template-document.html"), x = "") html <- renderDocument(template, dep) expect_true(findDep(html, "jquery", "9999")) expect_false(grepl(']+jquery[^>]+>', html)) }) test_that("Errors for mismatched brackets", { # Error if unmatched opening brackets expect_error(htmlTemplate(text_ = "text {{ code")) # No error if we didn't open a code block expect_identical( as.character(htmlTemplate(text_ = "code }} text")), "code }} text" ) # Error if unmatched brackets, when no leading or trailing space expect_error(htmlTemplate(text_ = "{{ code")) # No error if we didn't open a code block expect_identical( as.character(htmlTemplate(text_ = "code }}")), "code }}" ) }) test_that("Brackets at start or end of text", { # Code and text expect_identical( as.character(htmlTemplate(text_ = "text {{ code }} text", code = 1)), "text 1 text" ) expect_identical( as.character(htmlTemplate(text_ = "text{{code}}text", code = 1)), "text1text" ) # No brackets expect_identical( as.character(htmlTemplate(text_ = "text", code = 1)), "text" ) # No leading or trailing text expect_identical( as.character(htmlTemplate(text_ = "{{ code }}", code = 1)), "1" ) expect_identical( as.character(htmlTemplate(text_ = " {{ code }}", code = 1)), " 1" ) expect_identical( as.character(htmlTemplate(text_ = "{{ code }} ", code = 1)), "1 " ) # Edge cases expect_identical(as.character(htmlTemplate(text_ = "")), "") expect_identical(as.character(htmlTemplate(text_ = "X")), "X") expect_identical(as.character(htmlTemplate(text_ = " ")), " ") expect_identical(as.character(htmlTemplate(text_ = "{{}}")), "") expect_identical(as.character(htmlTemplate(text_ = " {{}} ")), " ") expect_identical(as.character(htmlTemplate(text_ = "{{ }}")), "") expect_identical(as.character(htmlTemplate(text_ = "{{}}{{}}")), "") expect_identical(as.character(htmlTemplate(text_ = "{{1}}{{2}}")), "12") expect_error(as.character(htmlTemplate(text_ = "{{"))) expect_error(as.character(htmlTemplate(text_ = " {{"))) expect_error(as.character(htmlTemplate(text_ = "{{ "))) expect_identical(as.character(htmlTemplate(text_ = "}}")), "}}") expect_identical(as.character(htmlTemplate(text_ = " }}")), " }}") expect_identical(as.character(htmlTemplate(text_ = "}} ")), "}} ") }) test_that("Template DFA edge cases", { # Single quotes expect_identical(as.character(htmlTemplate(text_ = "{{ '' }}")), "") expect_identical(as.character(htmlTemplate(text_ = " {{ '' }} ")), " ") expect_identical(as.character(htmlTemplate(text_ = "{{ '\\'' }}")), "'") expect_identical(as.character(htmlTemplate(text_ = "{{ '\\\\' }}")), "\\") expect_identical(as.character(htmlTemplate(text_ = "{{ '}}' }}")), "}}") # Double quotes expect_identical(as.character(htmlTemplate(text_ = '{{ "" }}')), '') expect_identical(as.character(htmlTemplate(text_ = ' {{ "" }} ')), ' ') expect_identical(as.character(htmlTemplate(text_ = '{{ "\\"" }}')), '"') expect_identical(as.character(htmlTemplate(text_ = '{{ "\\\\" }}')), '\\') expect_identical(as.character(htmlTemplate(text_ = '{{ "}}" }}')), '}}') # Backticks in code expect_identical(as.character(htmlTemplate(text_ = "{{ `}}`<-1 }}")), "1") expect_identical(as.character(htmlTemplate(text_ = "{{ `x\\`x`<-1 }}")), "1") # Percent operator - various delimiters in percent operator expect_identical( as.character(htmlTemplate(text_ = "a{{ `%'%` <- function(x, y) 1; 2 %'% 3 }}b")), "a1b" ) expect_identical( as.character(htmlTemplate(text_ = "a{{ `%}}%` <- function(x, y) 1; 2 %}}% 3 }}b")), "a1b" ) # Comments expect_identical( as.character(htmlTemplate(text_ = "a{{ 1 #2 }}b")), "a1b" ) expect_identical( as.character(htmlTemplate(text_ = "a{{ 1 #2\n3 }}b")), "a3b" ) expect_identical( as.character(htmlTemplate(text_ = "a{{ 1 #2'3 }}b")), "a1b" ) expect_identical( as.character(htmlTemplate(text_ = "a{{ 1 #2}3 }}b")), "a1b" ) })