test_that("can build article that uses html_vignette", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( output = "rmarkdown::html_vignette", pkgdown = list(as_is = TRUE) )) # theme is not set since html_vignette doesn't support it suppressMessages(expect_no_error(build_article("test", pkg))) }) test_that("can override html_document() options", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( output = list(html_document = list(number_sections = TRUE)), pkgdown = list(as_is = TRUE), "# Heading 1", "# Heading 2" )) suppressMessages(path <- build_article("test", pkg)) # Check that number_sections is respected html <- xml2::read_html(path) expect_equal(xpath_text(html, ".//h2//span"), c("1", "2")) # But title isn't affected expect_equal(xpath_text(html, ".//h1"), "title") # And no links or scripts are inlined expect_equal(xpath_length(html, ".//body//link"), 0) expect_equal(xpath_length(html, ".//body//script"), 0) }) test_that("can set width", { pkg <- local_pkgdown_site(meta = list(code = list(width = 50))) pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( r_code_block("getOption('width')") )) suppressMessages(path <- build_article("test", pkg)) html <- xml2::read_html(path) expect_equal(xpath_text(html, ".//pre")[[2]], "## [1] 50") }) test_that("bad width gives nice error", { pkg <- local_pkgdown_site(meta = list(code = list(width = "abc"))) expect_snapshot(rmarkdown_setup_pkgdown(pkg), error = TRUE) }) test_that("BS5 article laid out correctly with and without TOC", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/toc-true.Rmd", pkg_vignette( "## Heading 1", "## Heading 2" )) pkg <- pkg_add_file(pkg, "vignettes/toc-false.Rmd", pkg_vignette( toc = FALSE, "## Heading 1", "## Heading 2" )) suppressMessages(toc_true_path <- build_article("toc-true", pkg)) suppressMessages(toc_false_path <- build_article("toc-false", pkg)) toc_true <- xml2::read_html(toc_true_path) toc_false <- xml2::read_html(toc_false_path) # Always has class col-md-9 expect_equal(xpath_attr(toc_true, ".//main", "class"), "col-md-9") expect_equal(xpath_attr(toc_false, ".//main", "class"), "col-md-9") # The no sidebar without toc expect_equal(xpath_length(toc_true, ".//aside"), 1) expect_equal(xpath_length(toc_false, ".//aside"), 0) }) test_that("BS5 article gets correctly activated navbar", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/article.Rmd", pkg_vignette()) suppressMessages(article_path <- build_article("article", pkg)) html <- xml2::read_html(article_path) navbar <- xml2::xml_find_first(html, ".//div[contains(@class, 'navbar')]") expect_equal( xpath_text(navbar,".//li[contains(@class, 'active')]//button"), "Articles" ) }) test_that("titles are escaped when needed", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette(title = "a <-> b")) suppressMessages(path <- build_article("test", pkg)) html <- xml2::read_html(path) expect_equal(xpath_text(html, "//title", trim = TRUE), "a <-> b • testpackage") expect_equal(xpath_text(html, "//h1", trim = TRUE), "a <-> b") }) test_that("output is reproducible by default, i.e. 'seed' is respected", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( r_code_block("runif(5L)") )) suppressMessages(path <- build_article("test", pkg)) html <- xml2::read_html(path) output <- xpath_text(html, "//main//pre")[[2]] expect_snapshot(cat(output)) }) test_that("reports on bad open graph meta-data", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( opengraph = list(twitter = 1) )) expect_snapshot(build_article("test", pkg), error = TRUE) }) test_that("can control math mode", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/math.Rmd", "$1 + 1$") pkg$meta$template$`math-rendering` <- "mathml" suppressMessages(init_site(pkg)) suppressMessages(path <- build_article("math", pkg)) html <- xml2::read_html(path) expect_equal(xpath_length(html, ".//math"), 1) pkg$meta$template$`math-rendering` <- "mathjax" suppressMessages(init_site(pkg)) suppressMessages(path <- build_article("math", pkg)) html <- xml2::read_html(path) expect_equal(xpath_length(html, ".//span[contains(@class, 'math')]"), 1) pkg$meta$template$`math-rendering` <- "katex" suppressMessages(init_site(pkg)) suppressMessages(path <- build_article("math", pkg)) html <- xml2::read_html(path) expect_equal(xpath_length(html, ".//span[contains(@class, 'math')]"), 1) expect_contains( path_file(xpath_attr(html, ".//script", "src")), c("katex-auto.js", "katex.min.js") ) }) test_that("rmarkdown_template cleans up after itself", { pkg <- local_pkgdown_site() path <- NULL local({ path <<- rmarkdown_template(pkg) expect_true(file_exists(path)) }) expect_false(file_exists(path)) }) test_that("build_article styles ANSI escapes", { skip_if_no_pandoc() pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( r_code_block("cat(cli::col_red('X'), '\n')") )) suppressMessages(path <- build_article("test", pkg)) html <- xml2::read_html(path) expect_snapshot_output(xpath_xml(html, ".//code//span[@class='co']")) }) # Errors ----------------------------------------------------------------------- test_that("build_article yields useful error if pandoc fails", { skip_on_cran() # fragile due to pandoc dependency skip_if_no_pandoc("2.18") pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", "Hi") expect_snapshot( build_article("test", pkg, pandoc_args = "--fail-if-warnings"), error = TRUE ) }) test_that("build_article yields useful error if R fails", { skip_if_no_pandoc() pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", c( "---", "title: title", "---", "```{r}", "f <- function() g()", "g <- function() h()", "h <- function() rlang::abort('Error!')", "f()", "```" )) # check that error looks good expect_snapshot(build_article("test", pkg), error = TRUE) # check that traceback looks good - need extra work because rlang # avoids tracebacks in snapshots expect_snapshot(summary(expect_error(build_article("test", pkg)))) }) # Images ----------------------------------------------------------------------- test_that("build_article copies image files in subdirectories", { skip_if_no_pandoc() pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", c( "```{r}", "#| fig-alt: alt-text", "knitr::include_graphics('test/kitten.jpg')", "```" )) pkg <- pkg_add_kitten(pkg, "vignettes/test") expect_snapshot(build_article("test", pkg)) expect_equal(path_file(dir_ls(path(pkg$dst_path, "articles", "test"))), "kitten.jpg") }) test_that("finds external resources referenced by R code", { # weird path differences that I don't have the energy to dig into skip_on_cran() pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", c( "![external dependency](`r 'kitten.jpg'`)" )) pkg <- pkg_add_kitten(pkg, "vignettes") suppressMessages(path <- build_article("test", pkg)) # ensure that we the HTML references `` directly html <- xml2::read_html(path) expect_equal(xpath_attr(html, ".//img", "src"), "kitten.jpg") # expect that `external.png` was copied to the rendered article directory expect_true(file_exists(path(path_dir(path), "kitten.jpg"))) }) test_that("image links relative to output", { pkg <- local_pkgdown_site(test_path("assets/articles-images")) suppressMessages(init_site(pkg)) suppressMessages(copy_figures(pkg)) suppressMessages(build_article("kitten", pkg)) html <- xml2::read_html(path(pkg$dst_path, "articles", "kitten.html")) src <- xpath_attr(html, "//main//img", "src") expect_equal(src, c( # knitr::include_graphics() "../reference/figures/kitten.jpg", "another-kitten.jpg", # rmarkdown image "../reference/figures/kitten.jpg", "another-kitten.jpg", # magick::image_read() "kitten_files/figure-html/magick-1.png", # figure "kitten_files/figure-html/plot-1.jpg" )) # And files aren't copied expect_false(dir_exists(path(pkg$dst_path, "man"))) }) test_that("warns about missing images", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/kitten.Rmd", "![foo](kitten.jpg)") expect_snapshot(build_article("kitten", pkg)) }) test_that("spaces in sorce paths do work", { # create simulated package pkg0 <- local_pkgdown_site() pkg0 <- pkg_add_file(pkg0, "vignettes/kitten.Rmd", "![Kitten](kitten.jpg)") pkg0 <- pkg_add_kitten(pkg0, "vignettes") # copy simulated pkg to path that contains spaces pkg1 <- fs::dir_copy(pkg0$src_path, fs::file_temp(pattern = "beware of spaces-")) # check that pkgdown site builds anyways expect_no_error(suppressMessages( build_article("kitten", as_pkgdown(pkg1)) )) }) test_that("warns about missing alt-text", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/kitten.Rmd", "![](kitten.jpg)") pkg <- pkg_add_kitten(pkg, "vignettes") expect_snapshot(build_article("kitten", pkg)) }) # External dependencies -------------------------------------------------------- test_that("pkgdown deps are included only once in articles", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( # Some code that adds jquery/bootstrap r_code_block( 'htmltools::tagList( htmltools::p("hello"), rmarkdown::html_dependency_jquery(), rmarkdown::html_dependency_bootstrap("flatly") )' ) )) # Rely on default init_site() from local_pkgdown_site() setting all # the default includes to empty suppressMessages(path <- build_article("test", pkg)) html <- xml2::read_html(path) expect_equal(path_file(xpath_attr(html, ".//script", "src")), "pkgdown.js") expect_equal(path_file(xpath_attr(html, ".//link", "href")), character()) }) test_that("html widgets get needed css/js", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/test.Rmd", pkg_vignette( r_code_block(' path1 <- tempfile() writeLines(letters, path1) path2 <- tempfile() writeLines(letters[-(10:11)], path2) diffviewer::visual_diff(path1, path2) ') )) suppressMessages(path <- build_article("test", pkg)) html <- xml2::read_html(path) css <- xpath_attr(html, ".//body//link", "href") js <- xpath_attr(html, ".//body//script", "src") expect_true("diffviewer.css" %in% path_file(css)) expect_true("diffviewer.js" %in% path_file(js)) })