diff --git a/NEWS.md b/NEWS.md index 43dd3b98d..310ebf2bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # pkgdown (development version) +* Custom navbars that specify `icon` but not `aria-label` will now generate a message reminding you to provide one for to improve accessibility (#2533). +* `init_site()` will no longer automatically build favicons on CI systems (e.g. GHA). This is an expensive operation that uses an external service so it should only be run locally (#2553). +* `build_home_index()` now reports when rendering the home page (#2544). * Bootstrap 3 has been deprecated. It was superseded in December 2021, and now we're starting to more directly encourage folks to move away from it. * Improve HTML5 compliance (#2369): * No longer support IE9 or earlier diff --git a/R/build-articles.R b/R/build-articles.R index 1b56b752e..6ce95d7d3 100644 --- a/R/build-articles.R +++ b/R/build-articles.R @@ -88,9 +88,9 @@ #' #' ## Missing topics #' -#' pkgdown will warn if there are (non-internal) articles that aren't listed -#' in the articles index. You can suppress such warnings by listing the -#' affected articles in a section with `title: internal` (case sensitive); +#' pkgdown will warn if there are (non-internal) articles that aren't listed +#' in the articles index. You can suppress such warnings by listing the +#' affected articles in a section with `title: internal` (case sensitive); #' this section will not be displayed on the index page. #' #' # External files @@ -248,10 +248,8 @@ build_article <- function(name, front <- rmarkdown::yaml_front_matter(input_path) # Take opengraph from article's yaml front matter - front_opengraph <- check_open_graph(front$opengraph %||% list()) - data$opengraph <- utils::modifyList( - data$opengraph %||% list(), front_opengraph - ) + front_opengraph <- check_open_graph(front$opengraph, input) + data$opengraph <- modify_list(data$opengraph, front_opengraph) # Allow users to opt-in to their own template ext <- purrr::pluck(front, "pkgdown", "extension", .default = "html") diff --git a/R/build-favicons.R b/R/build-favicons.R index 8bdca8f5b..0200268c9 100644 --- a/R/build-favicons.R +++ b/R/build-favicons.R @@ -84,11 +84,11 @@ build_favicons <- function(pkg = ".", overwrite = FALSE) { cli::cli_abort("API request failed.", .internal = TRUE) } - tmp <- withr::local_tempdir() + tmp <- withr::local_tempfile() result <- httr::RETRY( "GET", result$favicon$package_url, - httr::write_disk(tmp), + httr::write_disk(tmp, overwrite = TRUE), quiet = TRUE ) diff --git a/R/build-home-authors.R b/R/build-home-authors.R index 75af5b16b..fd48e90c8 100644 --- a/R/build-home-authors.R +++ b/R/build-home-authors.R @@ -292,7 +292,7 @@ citation_auto <- function(pkg) { ) } -# helpers ----------------------------------------------------------------- +# helpers ------------------------------------------------------------------------- # Not strictly necessary, but produces a simpler data structure testing remove_orcid <- function(x) { @@ -301,4 +301,4 @@ remove_orcid <- function(x) { names(out) <- NULL } out -} \ No newline at end of file +} diff --git a/R/build-home-index.R b/R/build-home-index.R index 2a75c9d9f..3ed407bdb 100644 --- a/R/build-home-index.R +++ b/R/build-home-index.R @@ -8,11 +8,15 @@ build_home_index <- function(pkg = ".", quiet = TRUE) { data <- data_home(pkg) if (is.null(src_path)) { + cli::cli_inform("Reading {.file DESCRIPTION}") data$index <- linkify(pkg$desc$get_field("Description", "")) } else { + cli::cli_inform("Reading {src_path(path_rel(src_path, pkg$src_path))}") local_options_link(pkg, depth = 0L) data$index <- markdown_body(src_path) } + + cur_digest <- file_digest(dst_path) render_page(pkg, "home", data, "index.html", quiet = quiet) strip_header <- isTRUE(pkg$meta$home$strip_header) @@ -28,6 +32,11 @@ build_home_index <- function(pkg = ".", quiet = TRUE) { logo = logo_path(pkg, depth = 0) ) + new_digest <- file_digest(dst_path) + if (cur_digest != new_digest) { + writing_file(path_rel(dst_path, pkg$dst_path), "index.html") + } + invisible() } diff --git a/R/build-home.R b/R/build-home.R index 8c085e59c..29e885970 100644 --- a/R/build-home.R +++ b/R/build-home.R @@ -15,40 +15,40 @@ #' iterating when experimenting with site styles. #' #' # Home page -#' +#' #' The main content of the home page (`index.html`) is generated from #' `pkgdown/index.md`, `index.md`, or `README.md`, in that order. -#' Most packages will use `README.md` because that's also displayed by GitHub -#' and CRAN. Use `index.md` if you want your package website to look -#' different to your README, and use `pkgdown/index.md` if you don't want that +#' Most packages will use `README.md` because that's also displayed by GitHub +#' and CRAN. Use `index.md` if you want your package website to look +#' different to your README, and use `pkgdown/index.md` if you don't want that #' file to live in your package root directory. -#' -#' If you use `index.Rmd` or `README.Rmd` it's your responsibility to knit -#' the document to create the corresponding `.md`. pkgdown does not do this +#' +#' If you use `index.Rmd` or `README.Rmd` it's your responsibility to knit +#' the document to create the corresponding `.md`. pkgdown does not do this #' for you because it only touches files in the `doc/` directory. -#' -#' Extra markdown files in the base directory (e.g. `ROADMAP.md`) or in +#' +#' Extra markdown files in the base directory (e.g. `ROADMAP.md`) or in #' `.github/` (e.g. `CODE_OF_CONDUCT.md`) are copied by `build_home()` to `docs/` and converted to HTML. -#' -#' The home page also features a sidebar with information extracted from the -#' package. You can tweak it via the configuration file, to help make the home +#' +#' The home page also features a sidebar with information extracted from the +#' package. You can tweak it via the configuration file, to help make the home #' page an as informative as possible landing page. -#' +#' #' ## Images and figures -#' -#' If you want to include images in your `README.md`, they must be stored +#' +#' If you want to include images in your `README.md`, they must be stored #' somewhere in the package so that they can be displayed on the CRAN website. -#' The best place to put them is `man/figures`. If you are generating figures +#' The best place to put them is `man/figures`. If you are generating figures #' with R Markdown, make sure you set up `fig.path` as followed: -#' +#' #' ``` r #' knitr::opts_chunk$set( #' fig.path = "man/figures/" #' ) #' ``` -#' +#' #' This should usually go in a chunk with `include = FALSE`. -#' +#' #' ```` markdown #' ```{r chunk-name, include=FALSE}`r ''` #' knitr::opts_chunk$set( @@ -56,27 +56,27 @@ #' ) #' ``` #' ```` -#' +#' #' ## Package logo -#' +#' #' If you have a package logo, you can include it at the top of your README #' in a level-one heading: -#' +#' #' ``` markdown #' # pkgdown #' ``` -#' +#' #' [init_site()] will also automatically create a favicon set from your package logo. -#' +#' #' ## YAML config - title and description -#' +#' #' By default, the page title and description are extracted automatically from #' the `Title` and `Description` fields `DESCRIPTION` (stripping single quotes -#' off quoted words). CRAN ensures that these fields don't contain phrases -#' like "R package" because that's obvious on CRAN. To make your package more -#' findable on search engines, it's good practice to override the `title` and +#' off quoted words). CRAN ensures that these fields don't contain phrases +#' like "R package" because that's obvious on CRAN. To make your package more +#' findable on search engines, it's good practice to override the `title` and #' `description`, thinking about what people might search for: -#' +#' #' ```yaml #' home: #' title: An R package for pool-noodle discovery @@ -85,45 +85,45 @@ #' using this package to automatically discover and add pool-noodles #' to your growing collection. #' ``` -#' -#' (Note the use of YAML's `>` i.e. "YAML pipes"; this is a convenient way of +#' +#' (Note the use of YAML's `>` i.e. "YAML pipes"; this is a convenient way of #' writing paragraphs of text.) -#' +#' #' ## Dev badges -#' +#' #' pkgdown identifies badges in three ways: -#' -#' - Any image-containing links between `` and -#' ``, as e.g. created by `usethis::use_readme_md()` -#' or `usethis::use_readme_rmd()`. There should always be an empty line after -#' the `` line. If you divide badges into paragraphs, +#' +#' - Any image-containing links between `` and +#' ``, as e.g. created by `usethis::use_readme_md()` +#' or `usethis::use_readme_rmd()`. There should always be an empty line after +#' the `` line. If you divide badges into paragraphs, #' make sure to add an empty line before the `` line. -#' +#' #' - Any image-containing links within `
`. -#' +#' #' - Within the first paragraph, if it only contains image-containing links. -#' +#' #' Identified badges are **removed** from the _main content_. #' They are shown or not in the _sidebar_ depending on the development mode and #' sidebar customization, see the sidebar section. -#' +#' #' # Authors -#' +#' #' By default, pkgdown will display author information in three places: -#' +#' #' * the sidebar, #' * the left part side of the footer, #' * the author page. -#' -#' This documentation describes how to customise the overall author display. -#' See `?build_home` and `?build_site` for details about changing the location +#' +#' This documentation describes how to customise the overall author display. +#' See `?build_home` and `?build_site` for details about changing the location #' of the authors information within the home sidebar and the site footer. -#' +#' #' ## Authors ORCID and bio -#' -#' Author ORCID identification numbers in the `DESCRIPTION` are linked using +#' +#' Author ORCID identification numbers in the `DESCRIPTION` are linked using #' the ORCID logo: -#' +#' #' ```r #' Authors@R: c( #' person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre"), @@ -134,11 +134,11 @@ #' ) #' ) #' ``` -#' -#' If you want to add more details about authors or their involvement with the -#' package, you can use the comment field, which will be rendered on the +#' +#' If you want to add more details about authors or their involvement with the +#' package, you can use the comment field, which will be rendered on the #' authors page. -#' +#' #' ```r #' Authors@R: c( #' person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre"), @@ -149,49 +149,49 @@ #' ) #' ) #' ``` -#' +#' #' ## Additional control via YAML -#' -#' You can control additinal aspects of the authors display via the `authors` +#' +#' You can control additinal aspects of the authors display via the `authors` #' YAML field: -#' +#' #' * display of each author in the footer, sidebar and authors page, #' * which authors (by role) are displayed in the sidebar and footer, -#' * text before authors in the footer, +#' * text before authors in the footer, #' * text before and after authors in the sidebar, #' * text before and after authors on the authors page. -#' -#' You can modify how each author's name is displayed by adding a subsection -#' for `authors`. Each entry in `authors` should be named the author's name +#' +#' You can modify how each author's name is displayed by adding a subsection +#' for `authors`. Each entry in `authors` should be named the author's name #' (matching `DESCRIPTION`) and can contain `href` and/or `html` fields: -#' +#' #' * If `href` is provided, the author's name will be linked to this URL. #' * If `html` is provided, it will be shown instead of the author's name. #' This is particularly useful if you want to display the logo of a corporate -#' sponsor. Use an absolute URL to an image, not a relative link. Use an empty -#' alternative text rather than no alternative text so a screen-reader would +#' sponsor. Use an absolute URL to an image, not a relative link. Use an empty +#' alternative text rather than no alternative text so a screen-reader would #' skip over it. -#' +#' #' ```yaml #' authors: #' firstname lastname: #' href: "http://name-website.com" #' html: "" #' ``` -#' -#' -#' By default, the "developers" list shown in the sidebar and footer is -#' populated by the maintainer ("cre"), authors ("aut"), and funder ("fnd") +#' +#' +#' By default, the "developers" list shown in the sidebar and footer is +#' populated by the maintainer ("cre"), authors ("aut"), and funder ("fnd") #' from the `DESCRIPTION`. You could choose other roles for filtering. #' With the configuration below: -#' -#' * only the maintainer and funder(s) appear in the footer, after the text +#' +#' * only the maintainer and funder(s) appear in the footer, after the text #' "Crafted by", #' * all authors and contributors appear in the sidebar, #' * the authors list on the sidebar is preceded and followed by some text, #' * the authors list on the authors page is preceded and followed by some text. -#' -#' +#' +#' #' ```yaml #' authors: #' footer: @@ -204,32 +204,32 @@ #' before: "This package is proudly brought to you by:" #' after: "See the [changelog](news/index.html) for other contributors. :pray:" #' ``` -#' -#' If you want to filter authors based on something else than their roles, -#' consider using a custom sidebar/footer component +#' +#' If you want to filter authors based on something else than their roles, +#' consider using a custom sidebar/footer component #' (see `?build_home`/`?build_site`, respectively). -#' +#' #' # Sidebar -#' +#' #' You can customise the homepage sidebar with the `home.sidebar` field. -#' It's made up of two pieces: `structure`, which defines the overall layout, -#' and `components`, which defines what each piece looks like. This organisation -#' makes it easy to mix and match the pkgdown defaults with your own +#' It's made up of two pieces: `structure`, which defines the overall layout, +#' and `components`, which defines what each piece looks like. This organisation +#' makes it easy to mix and match the pkgdown defaults with your own #' customisations. -#' +#' #' This is the default structure: -#' +#' #' ``` yaml #' home: #' sidebar: #' structure: [links, license, community, citation, authors, dev] #' ``` -#' +#' #' These are drawn from seven built-in components: -#' -#' - `links`: automated links generated from `URL` and `BugReports` fields +#' +#' - `links`: automated links generated from `URL` and `BugReports` fields #' from `DESCRIPTION` plus manual links from the `home.links` field: -#' +#' #' ``` yaml #' home: #' links: @@ -238,26 +238,26 @@ #' - text: Roadmap #' href: /roadmap.html #' ``` -#' -#' - `license`: Licensing information if `LICENSE`/`LICENCE` or +#' +#' - `license`: Licensing information if `LICENSE`/`LICENCE` or #' `LICENSE.md`/`LICENCE.md` files are present. -#' -#' - `community`: links to to `.github/CONTRIBUTING.md`, +#' +#' - `community`: links to to `.github/CONTRIBUTING.md`, #' `.github/CODE_OF_CONDUCT.md`, etc. -#' -#' - `citation`: link to package citation information. Uses either +#' +#' - `citation`: link to package citation information. Uses either #' `inst/CITATION` or, if absent, information from the `DESCRIPTION`. -#' +#' #' - `authors`: selected authors from the `DESCRIPTION`. -#' -#' - `dev`: development status badges extracted from `README.md`/`index.md`. -#' This is only shown for "development" versions of websites; see -#' "Development mode" in `?build_site` for details. -#' +#' +#' - `dev`: development status badges extracted from `README.md`/`index.md`. +#' This is only shown for "development" versions of websites; see +#' "Development mode" in `?build_site` for details. +#' #' - `toc`: a table of contents for the README (not shown by default). -#' +#' #' You can also add your own components, where `text` is markdown text: -#' +#' #' ``` yaml #' home: #' sidebar: @@ -267,17 +267,17 @@ #' title: Funding #' text: We are *grateful* for funding! #' ``` -#' +#' #' Alternatively, you can provide a ready-made sidebar HTML: -#' +#' #' ``` yaml #' home: #' sidebar: #' html: path-to-sidebar.html #' ``` -#' +#' #' Or completely remove it: -#' +#' #' ``` yaml #' home: #' sidebar: FALSE diff --git a/R/build-news.R b/R/build-news.R index 6ef5c0514..f50bb302f 100644 --- a/R/build-news.R +++ b/R/build-news.R @@ -182,12 +182,12 @@ data_news <- function(pkg = list()) { sections <- sections[!is.na(versions)] if (length(sections) == 0) { - cli::cli_warn(c( + cli::cli_warn(c( "No version headings found in {src_path('NEWS.md')}", i = "See {.help pkgdown::build_news} for expected structure." )) } - + versions <- versions[!is.na(versions)] show_dates <- purrr::pluck(pkg, "meta", "news", "cran_dates", .default = !is_testing()) diff --git a/R/build-redirects.R b/R/build-redirects.R index 2486afd54..d73e32dbe 100644 --- a/R/build-redirects.R +++ b/R/build-redirects.R @@ -1,24 +1,24 @@ #' Build redirects -#' +#' #' @description -#' If you change the structure of your documentation (by renaming vignettes or +#' If you change the structure of your documentation (by renaming vignettes or #' help topics) you can setup redirects from the old content to the new content. -#' One or several now-absent pages can be redirected to a new page (or to a new -#' section of a new page). This works by creating a html page that performs a -#' "meta refresh", which isn't the best way of doing a redirect but works +#' One or several now-absent pages can be redirected to a new page (or to a new +#' section of a new page). This works by creating a html page that performs a +#' "meta refresh", which isn't the best way of doing a redirect but works #' everywhere that you might deploy your site. -#' -#' The syntax is the following, with old paths on the left, and new paths or +#' +#' The syntax is the following, with old paths on the left, and new paths or #' URLs on the right. -#' +#' #' ```yaml #' redirects: #' - ["articles/old-vignette-name.html", "articles/new-vignette-name.html"] #' - ["articles/another-old-vignette-name.html", "articles/new-vignette-name.html"] #' - ["articles/yet-another-old-vignette-name.html", "https://pkgdown.r-lib.org/dev"] #' ``` -#' -#' If for some reason you choose to redirect an existing page make sure to +#' +#' If for some reason you choose to redirect an existing page make sure to #' exclude it from the search index, see `?build_search`. #' #' @inheritParams as_pkgdown diff --git a/R/build-reference-index.R b/R/build-reference-index.R index 8e2ef7500..67e257ba5 100644 --- a/R/build-reference-index.R +++ b/R/build-reference-index.R @@ -137,7 +137,7 @@ check_missing_topics <- function(rows, pkg, error_call = caller_env()) { if (any(missing)) { config_abort( - pkg, + pkg, c( "{sum(missing)} topic{?s} missing from index: {.val {pkg$topics$name[missing]}}.", i = "Either use {.code @keywords internal} to drop from index, or" diff --git a/R/build-reference.R b/R/build-reference.R index d0baf52ce..3e34118da 100644 --- a/R/build-reference.R +++ b/R/build-reference.R @@ -200,7 +200,7 @@ build_reference <- function(pkg = ".", lazy = lazy, examples_env = examples_env, run_dont_run = run_dont_run - )) + )) preview_site(pkg, "reference", preview = preview) } @@ -295,7 +295,7 @@ build_reference_topic <- function(topic, ), error = function(err) { cli::cli_abort( - "Failed to parse Rd in {.file {topic$file_in}}", + "Failed to parse Rd in {.file {topic$file_in}}", parent = err, call = quote(build_reference()) ) diff --git a/R/build-search-docs.R b/R/build-search-docs.R index 6711edcef..2b97a3a7c 100644 --- a/R/build-search-docs.R +++ b/R/build-search-docs.R @@ -39,7 +39,7 @@ build_sitemap <- function(pkg = ".") { doc <- xml2::read_xml( paste0("") ) - + url_nodes <- unwrap_purrr_error(purrr::map(urls, url_node)) for (url in url_nodes) { xml2::xml_add_child(doc, url) diff --git a/R/build.R b/R/build.R index 2c5f5e9cf..e7995c0ee 100644 --- a/R/build.R +++ b/R/build.R @@ -37,7 +37,7 @@ #' # Navbar and footer #' #' The `navbar` and `footer` fields control the appearance of the navbar -#' footer which appear on every page. Learn more about these fields in +#' footer which appear on every page. Learn more about these fields in #' `vignette("customise")`. #' #' # Search @@ -58,8 +58,8 @@ #' ### Setting development mode #' #' The development `mode` of a site controls where the built site is placed -#' and how it is styled (i.e. the colour of the package version in the -#' navbar, the version tooltip), and whether or not the site is indexed by +#' and how it is styled (i.e. the colour of the package version in the +#' navbar, the version tooltip), and whether or not the site is indexed by #' search engines. There are four possible modes: #' #' * **automatic** (`mode: auto`): determines the mode based on the version: @@ -69,9 +69,9 @@ #' * everything else -> release. #' #' * **release** (`mode: release`), the default. Site is written to `docs/` -#' and styled like a released package, even if the content is for an -#' unreleased or development version. Version in navbar gets the default -#' colouring. Development badges are not shown in the sidebar +#' and styled like a released package, even if the content is for an +#' unreleased or development version. Version in navbar gets the default +#' colouring. Development badges are not shown in the sidebar #' (see `?build_home`). #' #' * **development** (`mode: devel`). Site is written to `docs/dev/`. @@ -85,8 +85,8 @@ #' package is not yet on CRAN. #' Development badges are shown in the sidebar (see `?build_home`). #' -#' Use `mode: auto` if you want both a released and a dev site, and -#' `mode: release` if you just want a single site. It is very rare that you +#' Use `mode: auto` if you want both a released and a dev site, and +#' `mode: release` if you just want a single site. It is very rare that you #' will need either devel or unreleased modes. #' #' You can override the mode specified in the `_pkgdown.yml` by setting @@ -178,12 +178,12 @@ #' #' To capture usage of your site with a web analytics tool, you can make #' use of the `includes` field to add the special HTML they need. This HTML -#' is typically placed `in_header` (actually in the ``), `before_body`, -#' or `after_body`. +#' is typically placed `in_header` (actually in the ``), `before_body`, +#' or `after_body`. #' You can learn more about how includes work in pkgdown at #' . #' -#' I include a few examples of popular analytics platforms below, but we +#' I include a few examples of popular analytics platforms below, but we #' recommend getting the HTML directly from the tool: #' #' * [plausible.io](https://plausible.io): @@ -470,7 +470,7 @@ build_site_local <- function(pkg = ".", } check_built_site(pkg) - + cli::cli_rule("Finished building pkgdown site for package {.pkg {pkg$package}}") preview_site(pkg, preview = preview) } diff --git a/R/check.R b/R/check.R index 69761135c..881566fc0 100644 --- a/R/check.R +++ b/R/check.R @@ -1,10 +1,10 @@ #' Check `_pkgdown.yml` #' #' @description -#' This pair of functions checks that your `_pkgdown.yml` is valid without +#' This pair of functions checks that your `_pkgdown.yml` is valid without #' building the whole site. `check_pkgdown()` errors at the first problem; #' `pkgdown_sitrep()` reports the status of all checks. -#' +#' #' Currently they check that: #' #' * There's a `url` in the pkgdown configuration, which is also recorded @@ -65,7 +65,7 @@ error_to_sitrep <- function(title, code) { check_urls <- function(pkg = ".", call = caller_env()) { pkg <- as_pkgdown(pkg) details <- c(i = "See details in {.vignette pkgdown::metadata}.") - + if (identical(pkg$meta, list())) { cli::cli_abort( c("No {.path _pkgdown.yml} found.", details), @@ -83,12 +83,12 @@ check_urls <- function(pkg = ".", call = caller_env()) { } else { desc_urls <- pkg$desc$get_urls() desc_urls <- sub("/$", "", desc_urls) - + if (!pkg$meta[["url"]] %in% desc_urls) { cli::cli_abort( c("{.file DESCRIPTION} {.field URL} lacks package url ({url}).", details), call = call ) - } + } } } diff --git a/R/compat-friendly-type.R b/R/compat-friendly-type.R deleted file mode 100644 index 848387f65..000000000 --- a/R/compat-friendly-type.R +++ /dev/null @@ -1,108 +0,0 @@ -# nocov start --- r-lib/rlang compat-friendly-type -# -# Changelog -# ========= -# -# 2021-04-19: -# - Added support for matrices and arrays (#141). -# - Added documentation. -# - Added changelog. - - -#' Return English-friendly type -#' @param x Any R object. -#' @param length Whether to mention the length of vectors and lists. -#' @return A string describing the type. Starts with an indefinite -#' article, e.g. "an integer vector". -#' @noRd -friendly_type_of <- function(x, length = FALSE) { - if (is.object(x)) { - if (inherits(x, "quosure")) { - type <- "quosure" - } else { - type <- paste(class(x), collapse = "/") - } - return(sprintf("a <%s> object", type)) - } - - if (!rlang::is_vector(x)) { - return(.rlang_as_friendly_type(typeof(x))) - } - - n_dim <- length(dim(x)) - type <- .rlang_as_friendly_vector_type(typeof(x), n_dim) - - if (length && !n_dim) { - type <- paste0(type, sprintf(" of length %s", length(x))) - } - - type -} - -.rlang_as_friendly_vector_type <- function(type, n_dim) { - if (type == "list") { - if (n_dim < 2) { - return("a list") - } else if (n_dim == 2) { - return("a list matrix") - } else { - return("a list array") - } - } - - type <- switch( - type, - logical = "a logical %s", - integer = "an integer %s", - numeric = , - double = "a double %s", - complex = "a complex %s", - character = "a character %s", - raw = "a raw %s", - type = paste0("a ", type, " %s") - ) - - if (n_dim < 2) { - kind <- "vector" - } else if (n_dim == 2) { - kind <- "matrix" - } else { - kind <- "array" - } - sprintf(type, kind) -} - -.rlang_as_friendly_type <- function(type) { - switch( - type, - - list = "a list", - - NULL = "NULL", - environment = "an environment", - externalptr = "a pointer", - weakref = "a weak reference", - S4 = "an S4 object", - - name = , - symbol = "a symbol", - language = "a call", - pairlist = "a pairlist node", - expression = "an expression vector", - - char = "an internal string", - promise = "an internal promise", - ... = "an internal dots object", - any = "an internal `any` object", - bytecode = "an internal bytecode object", - - primitive = , - builtin = , - special = "a primitive function", - closure = "a function", - - type - ) -} - -# nocov end diff --git a/R/config.R b/R/config.R index d167ecb7d..84ff9c635 100644 --- a/R/config.R +++ b/R/config.R @@ -129,6 +129,6 @@ config_abort <- function(pkg, config_path <- function(pkg) { # Not all projects necessary have a _pkgdown.yml (#2542) - config <- pkgdown_config_path(pkg$src_path) %||% "_pkgdown.yml" + config <- pkgdown_config_path(pkg) %||% "_pkgdown.yml" cli::style_hyperlink(path_file(config), paste0("file://", config)) } diff --git a/R/init.R b/R/init.R index 4597fc310..4f32f1267 100644 --- a/R/init.R +++ b/R/init.R @@ -40,8 +40,8 @@ init_site <- function(pkg = ".") { build_bslib(pkg) } - if (has_logo(pkg) && !has_favicons(pkg)) { - # Building favicons is expensive, so we hopefully only do it once. + # Building favicons is expensive, so we hopefully only do it once, locally + if (has_logo(pkg) && !has_favicons(pkg) && !on_ci()) { build_favicons(pkg) } copy_favicons(pkg) @@ -59,8 +59,8 @@ copy_assets <- function(pkg = ".") { # pkgdown assets if (!identical(template$default_assets, FALSE)) { copy_asset_dir( - pkg, - path_pkgdown(paste0("BS", pkg$bs_version, "/", "assets")), + pkg, + path_pkgdown(paste0("BS", pkg$bs_version, "/", "assets")), src_root = path_pkgdown(), src_label = "/" ) @@ -69,7 +69,7 @@ copy_assets <- function(pkg = ".") { # package assets if (!is.null(template$package)) { copy_asset_dir( - pkg, + pkg, path_package_pkgdown("assets", template$package, pkg$bs_version), src_root = system_file(package = template$package), src_label = paste0("<", template$package, ">/") diff --git a/R/navbar-menu.R b/R/navbar-menu.R index 7f500d0cc..3c54e54ef 100644 --- a/R/navbar-menu.R +++ b/R/navbar-menu.R @@ -59,7 +59,7 @@ navbar_html <- function(x, path_depth = 0L, menu_depth = 0L, side = c("left", "r side <- arg_match(side) type <- menu_type(x, menu_depth = menu_depth) - text <- switch(type, + text <- switch(type, menu = navbar_html_menu(x, menu_depth = menu_depth, path_depth = path_depth, side = side), heading = navbar_html_heading(x), link = navbar_html_link(x, menu_depth = menu_depth), @@ -168,7 +168,7 @@ html_tag <- function(tag, ..., class = NULL) { attr <- purrr::compact(c(list(class = class), dots_attr)) if (length(attr) > 0) { html_attr <- ifelse( - is.na(attr), + is.na(attr), names(attr), paste0(names(attr), '="', attr, '"') ) @@ -176,7 +176,7 @@ html_tag <- function(tag, ..., class = NULL) { } else { html_attr <- "" } - + html_child <- paste0(purrr::compact(dots_child), collapse = " ") needs_close <- !tag %in% "input" @@ -195,8 +195,20 @@ navbar_html_text <- function(x) { classes <- strsplit(x$icon, " ")[[1]] icon_classes <- classes[grepl("-", classes)] iconset <- purrr::map_chr(strsplit(icon_classes, "-"), 1) - + icon <- html_tag("span", class = unique(c(iconset, classes))) + + if (is.null(x$`aria-label`)) { + cli::cli_inform( + c( + x = "Icon {.str {x$icon}} lacks an {.var aria-label}.", + i = "Specify {.var aria-label} to make the icon accessible to screen readers.", + i = "Learn more in {.vignette accessibility}." + ), + .frequency = "regularly", + .frequency_id = "icon-aria-label" + ) + } } paste0( @@ -208,4 +220,4 @@ navbar_html_text <- function(x) { indent <- function(x, indent) { paste0(indent, gsub("\n", paste0("\n", indent), x)) -} \ No newline at end of file +} diff --git a/R/navbar.R b/R/navbar.R index 7383ccc43..b311dfa86 100644 --- a/R/navbar.R +++ b/R/navbar.R @@ -124,7 +124,7 @@ navbar_components <- function(pkg = ".") { if (pkg$bs_version == 5) { menu$search <- menu_search() } - + if (!is.null(pkg$tutorials)) { menu$tutorials <- menu_submenu( tr_("Tutorials"), diff --git a/R/package.R b/R/package.R index 8c550cd89..f06242b4c 100644 --- a/R/package.R +++ b/R/package.R @@ -168,6 +168,10 @@ check_bootstrap_version <- function(version, pkg) { # Metadata ---------------------------------------------------------------- pkgdown_config_path <- function(path) { + if (is_pkgdown(path)) { + path <- path$src_path + } + path_first_existing( path, c( diff --git a/R/pkgdown-package.R b/R/pkgdown-package.R new file mode 100644 index 000000000..596c61735 --- /dev/null +++ b/R/pkgdown-package.R @@ -0,0 +1,16 @@ +#' @keywords internal +"_PACKAGE" + +## usethis namespace: start +#' @importFrom magrittr %>% +#' @importFrom utils installed.packages +#' @import rlang +#' @import fs +## usethis namespace: end +NULL + +release_bullets <- function() { + c( + "Check that [test/widget.html](https://pkgdown.r-lib.org/dev/articles/) responds to mouse clicks on 5/10/50" + ) +} diff --git a/R/pkgdown.R b/R/pkgdown.R index 45f4f0000..61f1d4733 100644 --- a/R/pkgdown.R +++ b/R/pkgdown.R @@ -1,16 +1,3 @@ -#' @importFrom magrittr %>% -#' @importFrom utils installed.packages -#' @import rlang -#' @import fs -#' @keywords internal -"_PACKAGE" - -release_bullets <- function() { - c( - "Check that [test/widget.html](https://pkgdown.r-lib.org/dev/articles/) responds to mouse clicks on 5/10/50" - ) -} - #' Determine if code is executed by pkgdown #' #' This is occasionally useful when you need different behaviour by @@ -40,7 +27,7 @@ local_pkgdown_site <- function(path = NULL, meta = NULL, clone = FALSE, env = pa desc$set("Package", "testpackage") desc$set("Title", "A test package") desc$write(file = path(path, "DESCRIPTION")) - } + } if (clone) { if (is.null(path)) { @@ -90,4 +77,3 @@ local_pkgdown_template_pkg <- function(path = NULL, meta = NULL, env = parent.fr path } - diff --git a/R/rd-html.R b/R/rd-html.R index e55ea1416..1e73ceb03 100644 --- a/R/rd-html.R +++ b/R/rd-html.R @@ -81,7 +81,7 @@ as_html.character <- function(x, ..., escape = TRUE) { } } #' @export -as_html.TEXT <- function(x, ..., escape = TRUE) { +as_html.TEXT <- function(x, ..., escape = TRUE) { # tools:::htmlify x <- gsub("---", "\u2014", x) x <- gsub("--", "\u2013", x) @@ -399,7 +399,7 @@ parse_descriptions <- function(rd, ..., id_prefix = NULL) { if (inherits(x, "tag_item")) { term <- flatten_text(x[[1]], ...) def <- flatten_para(x[[2]], ...) - + if (!is.null(id_prefix)) { id <- paste0(id_prefix, make_slug(term)) id_attr <- paste0(" id='", id, "'") diff --git a/R/render.R b/R/render.R index 3799a730b..4dfafaa52 100644 --- a/R/render.R +++ b/R/render.R @@ -65,7 +65,7 @@ render_page_html <- function(pkg, name, data = list(), depth = 0L) { # Strip trailing whitespace rendered <- gsub("\\s+\n", "\n", rendered, perl = TRUE) - + xml2::read_html(rendered, encoding = "UTF-8") } @@ -132,10 +132,10 @@ data_template <- function(pkg = ".", depth = 0L) { print_yaml(out) } -data_open_graph <- function(pkg = ".") { +data_open_graph <- function(pkg = ".", call = caller_env()) { pkg <- as_pkgdown(pkg) - og <- pkg$meta$template$opengraph %||% list() - og <- check_open_graph(og) + og <- pkg$meta$template$opengraph + og <- check_open_graph(og, pkgdown_config_path(pkg), call = call) if (is.null(og$image) && !is.null(find_logo(pkg$src_path))) { og$image <- list(src = path_file(find_logo(pkg$src_path))) } @@ -153,55 +153,47 @@ data_open_graph <- function(pkg = ".") { og } -check_open_graph <- function(og) { - if (!is.list(og)) { - fog <- friendly_type_of(og) - cli::cli_abort( - "{.var opengraph} must be a list, not {.val fog}.", - call = caller_env() - ) +check_open_graph <- function(og, path, call = caller_env()) { + if (is.null(og)) { + return() } + + is_yaml <- path_ext(path) %in% c("yml", "yaml") + base_path <- if (is_yaml) "template.opengraph" else "opengraph" + + check_open_graph_list( + og, + file_path = path, + error_path = base_path, + error_call = call + ) + supported_fields <- c("image", "twitter") unsupported_fields <- setdiff(names(og), supported_fields) if (length(unsupported_fields)) { cli::cli_warn( - "Unsupported {.var opengraph} field{?s}: {.val unsupported_fields}." + "{.file {path}}: Unsupported {.field {base_path}} {cli::qty(unsupported_fields)} field{?s}: {.val {unsupported_fields}}.", + call = call ) } - if ("twitter" %in% names(og)) { - if (is.character(og$twitter) && length(og$twitter) == 1 && grepl("^@", og$twitter)) { - cli::cli_abort( - "The {.var opengraph: twitter} option must be a list.", - call = caller_env() - ) - } - if (!is.list(og$twitter)) { - cli::cli_abort( - "The {.var opengraph: twitter} option must be a list.", - call = caller_env() - ) - } - if (is.null(og$twitter$creator) && is.null(og$twitter$site)) { - cli::cli_abort( - "{.var opengraph: twitter} must include either {.val creator} or {.val site}.", - call = caller_env() - ) - } - } - if ("image" %in% names(og)) { - if (is.character(og$image) && length(og$image) == 1) { - cli::cli_abort( - "The {.var opengraph: image} option must be a list.", - call = caller_env() - ) - } - if (!is.list(og$image)) { - cli::cli_abort( - "The {.var opengraph: image} option must be a list.", - call = caller_env() - ) - } + check_open_graph_list( + og$twitter, + file_path = path, + error_path = paste0(base_path, ".twitter"), + error_call = call + ) + if (!is.null(og$twitter) && is.null(og$twitter$creator) && is.null(og$twitter$site)) { + cli::cli_abort( + "{.file {path}}: {.field opengraph.twitter} must include either {.field creator} or {.field site}.", + call = call + ) } + check_open_graph_list( + og$image, + file_path = path, + error_path = paste0(base_path, ".image"), + error_call = call + ) og[intersect(supported_fields, names(og))] } @@ -213,6 +205,20 @@ render_template <- function(path, data) { whisker::whisker.render(template, data) } +check_open_graph_list <- function(x, + file_path, + error_path, + error_call = caller_env()) { + if (is.list(x) || is.null(x)) { + return() + } + not <- friendly_type_of(x) + cli::cli_abort( + "{.file {file_path}}: {.field {error_path}} must be a list, not {not}.", + call = error_call + ) +} + write_if_different <- function(pkg, contents, path, quiet = FALSE, check = TRUE) { # Almost all uses are relative to destination, except for rmarkdown templates full_path <- path_abs(path, start = pkg$dst_path) diff --git a/R/topics.R b/R/topics.R index abe5cf4cf..a26631240 100644 --- a/R/topics.R +++ b/R/topics.R @@ -107,25 +107,25 @@ match_env <- function(topics) { fns$starts_with <- function(x, internal = FALSE) { check_string(x) check_bool(internal) - + any_alias(~ grepl(paste0("^", x), .), .internal = internal) } fns$ends_with <- function(x, internal = FALSE) { check_string(x) check_bool(internal) - + any_alias(~ grepl(paste0(x, "$"), .), .internal = internal) } fns$matches <- function(x, internal = FALSE) { check_string(x) check_bool(internal) - + any_alias(~ grepl(x, .), .internal = internal) } fns$contains <- function(x, internal = FALSE) { check_string(x) check_bool(internal) - + any_alias(~ grepl(x, ., fixed = TRUE), .internal = internal) } fns$has_keyword <- function(x) { @@ -145,7 +145,7 @@ match_env <- function(topics) { fns$lacks_concepts <- function(x, internal = FALSE) { check_character(x) check_bool(internal) - + nomatch <- topics$concepts %>% purrr::map(~ match(str_trim(.), x, nomatch = FALSE)) %>% purrr::map_lgl(~ length(.) == 0L | all(. == 0L)) @@ -192,7 +192,7 @@ match_eval <- function(string, env) { eval(expr, env), error = function(e) { cli::cli_abort( - "Failed to evaluate topic selector {.val {string}}.", + "Failed to evaluate topic selector {.val {string}}.", parent = e, call = NULL ) diff --git a/R/usage.R b/R/usage.R index 29af585cf..49d179f2f 100644 --- a/R/usage.R +++ b/R/usage.R @@ -61,7 +61,7 @@ parse_usage <- function(x) { cli::cli_warn("Failed to parse usage: {.code {r}}") list() } - ) + ) purrr::map(exprs, usage_type) } diff --git a/R/utils-fs.R b/R/utils-fs.R index ea1a12bc5..60525a3db 100644 --- a/R/utils-fs.R +++ b/R/utils-fs.R @@ -1,5 +1,5 @@ -dir_copy_to <- function(src_dir, - dst_dir, +dir_copy_to <- function(src_dir, + dst_dir, src_root, dst_root, src_label = "", @@ -13,7 +13,7 @@ dir_copy_to <- function(src_dir, src_paths <- dir_ls(src_dir, recurse = TRUE) is_dir <- is_dir(src_paths) - + dst_paths <- path(dst_dir, path_rel(src_paths, src_dir)) # First create directories @@ -127,10 +127,3 @@ path_package_pkgdown <- function(path, path_pkgdown <- function(...) { system_file(..., package = "pkgdown") } - -pkgdown_config_relpath <- function(pkg) { - pkg <- as_pkgdown(pkg) - config_path <- pkgdown_config_path(pkg$src_path) - - path_rel(config_path, pkg$src_path) -} diff --git a/R/utils-io.R b/R/utils-io.R index 97c7dc2f8..90d078e9c 100644 --- a/R/utils-io.R +++ b/R/utils-io.R @@ -5,7 +5,7 @@ read_file <- function(path) { paste0(lines, "\n", collapse = "") } -# Inspired by roxygen2 utils-io.R (https://github.com/klutometis/roxygen/) ----------- +# Inspired by roxygen2 utils-io.R (https://github.com/klutometis/roxygen/) -------- read_lines <- function(path, n = -1L) { base::readLines(path, n = n, encoding = "UTF-8", warn = FALSE) # nolint diff --git a/R/utils.R b/R/utils.R index a656b3f4c..cc59dcee1 100644 --- a/R/utils.R +++ b/R/utils.R @@ -181,6 +181,10 @@ section_id <- function(section) { xml2::xml_attr(h, "id") } +on_ci <- function() { + isTRUE(as.logical(Sys.getenv("CI", "false"))) +} + # yaml ------------------------------------------------------------ print_yaml <- function(x) { diff --git a/man/pkgdown-package.Rd b/man/pkgdown-package.Rd index 0a833e0db..d6df52195 100644 --- a/man/pkgdown-package.Rd +++ b/man/pkgdown-package.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/pkgdown.R +% Please edit documentation in R/pkgdown-package.R \docType{package} \name{pkgdown-package} \alias{pkgdown} @@ -13,7 +13,7 @@ Generate an attractive and useful website from a source package. 'pkgdown' conve \seealso{ Useful links: \itemize{ - \item \url{https://pkgdown.r-lib.org} + \item \url{https://pkgdown.r-lib.org/} \item \url{https://github.com/r-lib/pkgdown} \item Report bugs at \url{https://github.com/r-lib/pkgdown/issues} } diff --git a/pkgdown/favicon/apple-touch-icon-120x120.png b/pkgdown/favicon/apple-touch-icon-120x120.png index 9386a565a..abd2b173a 100644 Binary files a/pkgdown/favicon/apple-touch-icon-120x120.png and b/pkgdown/favicon/apple-touch-icon-120x120.png differ diff --git a/pkgdown/favicon/apple-touch-icon-152x152.png b/pkgdown/favicon/apple-touch-icon-152x152.png index 7c41c0738..802d9ee04 100644 Binary files a/pkgdown/favicon/apple-touch-icon-152x152.png and b/pkgdown/favicon/apple-touch-icon-152x152.png differ diff --git a/pkgdown/favicon/apple-touch-icon-180x180.png b/pkgdown/favicon/apple-touch-icon-180x180.png index 4d75fa735..36438604a 100644 Binary files a/pkgdown/favicon/apple-touch-icon-180x180.png and b/pkgdown/favicon/apple-touch-icon-180x180.png differ diff --git a/pkgdown/favicon/apple-touch-icon-60x60.png b/pkgdown/favicon/apple-touch-icon-60x60.png index 18049b161..7f48aa8cc 100644 Binary files a/pkgdown/favicon/apple-touch-icon-60x60.png and b/pkgdown/favicon/apple-touch-icon-60x60.png differ diff --git a/pkgdown/favicon/apple-touch-icon-76x76.png b/pkgdown/favicon/apple-touch-icon-76x76.png index 755d5f03e..2c6da4735 100644 Binary files a/pkgdown/favicon/apple-touch-icon-76x76.png and b/pkgdown/favicon/apple-touch-icon-76x76.png differ diff --git a/pkgdown/favicon/apple-touch-icon.png b/pkgdown/favicon/apple-touch-icon.png index f1d46ea80..6006af40b 100644 Binary files a/pkgdown/favicon/apple-touch-icon.png and b/pkgdown/favicon/apple-touch-icon.png differ diff --git a/pkgdown/favicon/favicon-16x16.png b/pkgdown/favicon/favicon-16x16.png index 58c7cba9a..11252fbbd 100644 Binary files a/pkgdown/favicon/favicon-16x16.png and b/pkgdown/favicon/favicon-16x16.png differ diff --git a/pkgdown/favicon/favicon-32x32.png b/pkgdown/favicon/favicon-32x32.png index 94135aae8..9e0df4806 100644 Binary files a/pkgdown/favicon/favicon-32x32.png and b/pkgdown/favicon/favicon-32x32.png differ diff --git a/tests/testthat/_snaps/build-articles.md b/tests/testthat/_snaps/build-articles.md index d0ea7fcdb..0669621eb 100644 --- a/tests/testthat/_snaps/build-articles.md +++ b/tests/testthat/_snaps/build-articles.md @@ -54,3 +54,11 @@ Output ## [1] 0.080750138 0.834333037 0.600760886 0.157208442 0.007399441 +# reports on bad open graph meta-data + + Code + build_article(pkg = pkg, name = "bad-opengraph") + Condition + Error in `build_article()`: + ! 'vignettes/bad-opengraph.Rmd': opengraph.twitter must be a list, not an integer vector. + diff --git a/tests/testthat/_snaps/build-home-index.md b/tests/testthat/_snaps/build-home-index.md index 833f84e7a..510a7c5b3 100644 --- a/tests/testthat/_snaps/build-home-index.md +++ b/tests/testthat/_snaps/build-home-index.md @@ -1,3 +1,15 @@ +# messages about reading and writing + + Code + build_home_index(pkg) + Message + Reading 'DESCRIPTION' + Writing `index.html` + Code + build_home_index(pkg) + Message + Reading 'DESCRIPTION' + # data_home_sidebar() works by default Code diff --git a/tests/testthat/_snaps/navbar-menu.md b/tests/testthat/_snaps/navbar-menu.md index 9a01cbfc5..486696581 100644 --- a/tests/testthat/_snaps/navbar-menu.md +++ b/tests/testthat/_snaps/navbar-menu.md @@ -45,6 +45,16 @@ Output +# icons warn if no aria-label + + Code + . <- navbar_html(menu_icon("fa-question", "https://example.com", NULL)) + Message + x Icon "fa-question" lacks an `aria-label`. + i Specify `aria-label` to make the icon accessible to screen readers. + i Learn more in `vignette(accessibility)`. + This message is displayed once every 8 hours. + # simple components don't change without warning Code diff --git a/tests/testthat/_snaps/render.md b/tests/testthat/_snaps/render.md index 11d04eb82..7e4715fd0 100644 --- a/tests/testthat/_snaps/render.md +++ b/tests/testthat/_snaps/render.md @@ -39,7 +39,6 @@ toc: Table of contents site_nav: Site navigation has_favicons: no - opengraph: [] extra: css: ~ js: ~ @@ -63,3 +62,35 @@ right:

Site built with pkgdown {version}.

+# check_opengraph validates inputs + + Code + check_open_graph_(list(foo = list()), ) + Condition + Warning in `check_open_graph_()`: + '_pkgdown.yml': Unsupported template.opengraph field: "foo". + Output + named list() + Code + check_open_graph_(list(foo = list(), bar = list())) + Condition + Warning in `check_open_graph_()`: + '_pkgdown.yml': Unsupported template.opengraph fields: "foo" and "bar". + Output + named list() + Code + check_open_graph_(list(twitter = 1)) + Condition + Error in `check_open_graph_()`: + ! '_pkgdown.yml': template.opengraph.twitter must be a list, not a double vector. + Code + check_open_graph_(list(twitter = list())) + Condition + Error in `check_open_graph_()`: + ! '_pkgdown.yml': opengraph.twitter must include either creator or site. + Code + check_open_graph_(list(image = 1)) + Condition + Error in `check_open_graph_()`: + ! '_pkgdown.yml': template.opengraph.image must be a list, not a double vector. + diff --git a/tests/testthat/assets/articles/vignettes/bad-opengraph.Rmd b/tests/testthat/assets/articles/vignettes/bad-opengraph.Rmd new file mode 100644 index 000000000..f6f31e13f --- /dev/null +++ b/tests/testthat/assets/articles/vignettes/bad-opengraph.Rmd @@ -0,0 +1,7 @@ +--- +title: "Introduction to poolnoodlr" +description: "A brief introduction to pool noodles in R." +author: "Mara Averick" +opengraph: + twitter: 1 +--- \ No newline at end of file diff --git a/tests/testthat/test-build-articles.R b/tests/testthat/test-build-articles.R index 198382568..6d6e112d4 100644 --- a/tests/testthat/test-build-articles.R +++ b/tests/testthat/test-build-articles.R @@ -21,7 +21,7 @@ test_that("image links relative to output", { # knitr::include_graphics() "../reference/figures/kitten.jpg", "another-kitten.jpg", - # rmarkdown image + # rmarkdown image "../reference/figures/kitten.jpg", "another-kitten.jpg", # magick::image_read() @@ -292,3 +292,9 @@ test_that("output is reproducible by default, i.e. 'seed' is respected", { expect_snapshot(cat(output)) }) + +test_that("reports on bad open graph meta-data", { + pkg <- local_pkgdown_site(test_path("assets/articles")) + suppressMessages(init_site(pkg)) + expect_snapshot(build_article(pkg = pkg, name = "bad-opengraph"), error = TRUE) +}) diff --git a/tests/testthat/test-build-home-authors.R b/tests/testthat/test-build-home-authors.R index 0782e3190..a13d95322 100644 --- a/tests/testthat/test-build-home-authors.R +++ b/tests/testthat/test-build-home-authors.R @@ -4,7 +4,7 @@ test_that("authors page includes inst/AUTHORS", { suppressMessages(build_citation_authors(pkg)) lines <- read_lines(path(pkg$dst_path, "authors.html")) - expect_true(any(grepl("
Hello
", lines))) + expect_match(lines, "
Hello
", all = FALSE) }) # authors -------------------------------------------------------------------- @@ -127,10 +127,10 @@ test_that("bibtex is escaped", { ' textVersion = ""', ')' )) - + suppressMessages(init_site(pkg)) suppressMessages(build_citation_authors(pkg)) html <- xml2::read_html(path(pkg$dst_path, "authors.html")) - + expect_match(xpath_text(html, "//pre"), "<&>", fixed = TRUE) }) diff --git a/tests/testthat/test-build-home-index.R b/tests/testthat/test-build-home-index.R index 858e8b556..15ba3f433 100644 --- a/tests/testthat/test-build-home-index.R +++ b/tests/testthat/test-build-home-index.R @@ -1,3 +1,12 @@ +test_that("messages about reading and writing", { + pkg <- local_pkgdown_site(test_path("assets/home-index-rmd")) + + expect_snapshot({ + build_home_index(pkg) + build_home_index(pkg) + }) +}) + test_that("title and description come from DESCRIPTION by default", { pkg <- as_pkgdown(test_path("assets/home-index-rmd")) expect_equal(data_home(pkg)$pagetitle, "A test package") @@ -11,7 +20,7 @@ test_that("title and description come from DESCRIPTION by default", { test_that("math is handled", { pkg <- local_pkgdown_site(test_path("assets/home-readme-rmd"), clone = TRUE) write_lines(c("$1 + 1$"), path(pkg$src_path, "README.md")) - + suppressMessages(init_site(pkg)) suppressMessages(build_home_index(pkg)) @@ -24,7 +33,7 @@ test_that("version formatting in preserved", { expect_equal(pkg$version, "1.0.0-9000") suppressMessages(init_site(pkg)) - build_home_index(pkg, quiet = TRUE) + suppressMessages(build_home_index(pkg)) index <- read_lines(path(pkg$dst_path, "index.html")) expect_true(any(grepl("1.0.0-9000", index, fixed = TRUE))) }) @@ -47,7 +56,7 @@ test_that("data_home_sidebar() can be removed", { # nor later -- so probably not to be tested here?! dir_create(path(pkg$dst_path)) - build_home_index(pkg) + suppressMessages(build_home_index(pkg)) html <- xml2::read_html(path(pkg$dst_path, "index.html")) expect_equal(xpath_length(html, ".//aside/*"), 0) }) diff --git a/tests/testthat/test-build-reference-index.R b/tests/testthat/test-build-reference-index.R index 1f7c8ace9..8bd73f716 100644 --- a/tests/testthat/test-build-reference-index.R +++ b/tests/testthat/test-build-reference-index.R @@ -71,10 +71,10 @@ test_that("errors well when a content entry is empty", { - title: bla contents: - aname - - + - ") suppressMessages(init_site(pkg)) - + expect_snapshot(build_reference_index(pkg), error = TRUE) }) diff --git a/tests/testthat/test-build-reference.R b/tests/testthat/test-build-reference.R index 24d376919..7efb599cf 100644 --- a/tests/testthat/test-build-reference.R +++ b/tests/testthat/test-build-reference.R @@ -53,7 +53,7 @@ test_that(".Rd without usage doesn't get Usage section", { pkg <- local_pkgdown_site(test_path("assets/reference")) suppressMessages(init_site(pkg)) suppressMessages(build_reference(pkg, topics = "e")) - + html <- xml2::read_html(path(pkg$dst_path, "reference", "e.html")) expect_equal(xpath_length(html, "//div[@id='ref-usage']"), 0) clean_site(pkg, quiet = TRUE) @@ -122,7 +122,7 @@ test_that("title and page title escapes html", { pkg <- local_pkgdown_site(test_path("assets/reference")) suppressMessages(init_site(pkg)) suppressMessages(build_reference(pkg, topics = "g")) - + html <- xml2::read_html(path(pkg$dst_path, "reference", "g.html")) expect_equal(xpath_text(html, "//title", trim = TRUE), "g <-> h — g • testpackage") expect_equal(xpath_text(html, "//h1", trim = TRUE), "g <-> h") diff --git a/tests/testthat/test-build-search-docs.R b/tests/testthat/test-build-search-docs.R index e8205f4cc..6bec99bbe 100644 --- a/tests/testthat/test-build-search-docs.R +++ b/tests/testthat/test-build-search-docs.R @@ -53,4 +53,4 @@ test_that("build_search() builds the expected search.json with no URL", { test_that("url_node gives informative error", { expect_snapshot(url_node("<"), error = TRUE) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-check.R b/tests/testthat/test-check.R index 7a09611d4..a0b3a7e3c 100644 --- a/tests/testthat/test-check.R +++ b/tests/testthat/test-check.R @@ -1,6 +1,6 @@ test_that("sitrep complains about BS3", { pkg <- local_pkgdown_site(test_path("assets/open-graph"), meta = " - template: + template: bootstrap: 3 ") expect_snapshot(pkgdown_sitrep(pkg)) diff --git a/tests/testthat/test-config.R b/tests/testthat/test-config.R index 1e38b5961..2a2e1742a 100644 --- a/tests/testthat/test-config.R +++ b/tests/testthat/test-config.R @@ -30,10 +30,10 @@ test_that("config_check_list gives informative errors", { pkg <- local_pkgdown_site() config_check_list_ <- function(...) { config_check_list(..., error_pkg = pkg, error_path = "path") - } + } expect_snapshot(error = TRUE, { config_check_list_(1, has_names = "x") config_check_list_(list(x = 1, y = 1), has_names = c("y", "z")) }) -}) +}) diff --git a/tests/testthat/test-init.R b/tests/testthat/test-init.R index 5316b2258..1f8aa314f 100644 --- a/tests/testthat/test-init.R +++ b/tests/testthat/test-init.R @@ -57,11 +57,11 @@ test_that("site meta doesn't break unexpectedly", { test_that("site meta includes vignette subdirectories", { pkg <- local_pkgdown_site() - vig_path <- path(pkg$src_path, "vignettes") + vig_path <- path(pkg$src_path, "vignettes") dir_create(path(vig_path, "a")) file_create(path(vig_path, "a", c("a.Rmd", "b.Rmd"))) pkg <- as_pkgdown(pkg$src_path) meta <- site_meta(pkg) expect_equal(meta$articles, list("a/a" = "a/a.html", "a/b" = "a/b.html")) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-markdown.R b/tests/testthat/test-markdown.R index af42a8c2b..3b79607ca 100644 --- a/tests/testthat/test-markdown.R +++ b/tests/testthat/test-markdown.R @@ -1,13 +1,12 @@ -test_that("handles empty inputs", { - expect_equal(markdown_text_inline(""), NULL) - expect_equal(markdown_text_inline(NULL), NULL) - - expect_equal(markdown_text_block(NULL), NULL) - expect_equal(markdown_text_block(""), NULL) +test_that("handles empty inputs (returns NULL)", { + expect_null(markdown_text_inline("")) + expect_null(markdown_text_inline(NULL)) + expect_null(markdown_text_block(NULL)) + expect_null(markdown_text_block("")) path <- withr::local_tempfile() file_create(path) - expect_equal(markdown_body(path), NULL) + expect_null(markdown_body(path)) }) test_that("header attributes are parsed", { diff --git a/tests/testthat/test-navbar-menu.R b/tests/testthat/test-navbar-menu.R index bfeb4a635..5152b2f37 100644 --- a/tests/testthat/test-navbar-menu.R +++ b/tests/testthat/test-navbar-menu.R @@ -44,6 +44,14 @@ test_that("bullet class varies based on depth", { ) }) +test_that("icons warn if no aria-label", { + reset_message_verbosity("icon-aria-label") + + expect_snapshot({ + . <- navbar_html(menu_icon("fa-question", "https://example.com", NULL)) + }) +}) + test_that("icons extract base iconset class automatically", { expect_match( navbar_html(menu_icon("fa-question", "https://example.com", "label")), @@ -80,11 +88,11 @@ test_that("simple components don't change without warning", { test_that("navbar_html_text() combines icons and text", { expect_equal(navbar_html_text(list(text = "a")), 'a') expect_equal( - navbar_html_text(list(icon = "fas-github")), + navbar_html_text(list(icon = "fas-github", `aria-label` = "github")), '' ) expect_equal( - navbar_html_text(list(text = "a", icon = "fas-github")), + navbar_html_text(list(text = "a", icon = "fas-github", `aria-label` = "github")), ' a' ) }) diff --git a/tests/testthat/test-navbar.R b/tests/testthat/test-navbar.R index 8b2c524b6..4ced204ab 100644 --- a/tests/testthat/test-navbar.R +++ b/tests/testthat/test-navbar.R @@ -82,7 +82,7 @@ test_that("data_navbar() works by default", { test_that("data_navbar() can re-order default elements", { pkg <- local_pkgdown_site(meta = " - template: + template: bootstrap: 5 repo: url: @@ -187,7 +187,7 @@ test_that("dropdowns on right are right-aligned", { articles = menu_submenu("Articles", list(menu_heading("A"), menu_heading("B"))) ) pkg <- list(bs_version = 5) - + right <- xml2::read_html(render_navbar_links(x, pkg = pkg, side = "right")) left <- xml2::read_html(render_navbar_links(x, pkg = pkg, side = "left")) diff --git a/tests/testthat/test-render.R b/tests/testthat/test-render.R index 95cf03f4f..1b3cfcd7f 100644 --- a/tests/testthat/test-render.R +++ b/tests/testthat/test-render.R @@ -69,3 +69,17 @@ test_that("can include text in header, before body, and after body", { c("in header", "before body", "after body") ) }) + +test_that("check_opengraph validates inputs", { + check_open_graph_ <- function(...) { + check_open_graph(..., path = "_pkgdown.yml") + } + + expect_snapshot(error = TRUE, { + check_open_graph_(list(foo = list()), ) + check_open_graph_(list(foo = list(), bar = list())) + check_open_graph_(list(twitter = 1)) + check_open_graph_(list(twitter = list())) + check_open_graph_(list(image = 1)) + }) +}) diff --git a/tests/testthat/test-repo.R b/tests/testthat/test-repo.R index 8ac2a0f40..45f68a720 100644 --- a/tests/testthat/test-repo.R +++ b/tests/testthat/test-repo.R @@ -61,7 +61,7 @@ test_that("repo_source() is robust to trailing slash", { meta <- function(x) { list(repo = list(url = list(source = x))) - } + } source <- "Source: a" expect_equal(repo_source(meta("http://example.com"), "a"), source) expect_equal(repo_source(meta("http://example.com/"), "a"), source) diff --git a/tests/testthat/test-usage.R b/tests/testthat/test-usage.R index 2f9b01258..dea67d31a 100644 --- a/tests/testthat/test-usage.R +++ b/tests/testthat/test-usage.R @@ -5,7 +5,7 @@ test_that("usage escapes special characters", { usage2html <- function(x) { rd <- rd_text(paste0("\\usage{", x, "}"), FALSE)[[1]] - as_data(rd) + as_data(rd) } expect_snapshot({ @@ -13,7 +13,7 @@ test_that("usage escapes special characters", { cat(strip_html_tags(usage2html("# <>\nx"))) "Unparseable" cat(strip_html_tags(usage2html("# <>\n<"))) - }) + }) }) test_that("S4 methods gets comment", {