diff --git a/DESCRIPTION b/DESCRIPTION index 7771968..fe958dc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: quarto Title: R Interface to 'Quarto' Markdown Publishing System -Version: 1.4.4.9007 +Version: 1.4.4.9008 Authors@R: c( person("JJ", "Allaire", , "jj@posit.co", role = "aut", comment = c(ORCID = "0000-0003-0174-9868")), diff --git a/NAMESPACE b/NAMESPACE index 7ec0931..7fe0658 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,14 +5,17 @@ export(quarto_add_extension) export(quarto_binary_sitrep) export(quarto_create_project) export(quarto_inspect) +export(quarto_list_extensions) export(quarto_path) export(quarto_preview) export(quarto_preview_stop) export(quarto_publish_app) export(quarto_publish_doc) export(quarto_publish_site) +export(quarto_remove_extension) export(quarto_render) export(quarto_serve) +export(quarto_update_extension) export(quarto_use_template) export(quarto_version) export(theme_brand_flextable) diff --git a/NEWS.md b/NEWS.md index ab4177d..3da1439 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ # quarto (development version) +- Add several new wrapper function (thanks, @parmsam, #192): + - `quarto_list_extensions()` to list installed extensions using `quarto list extensions` + - `quarto_remove_extension()` to remove an installed extension using `quarto remove extensions` + - `quarto_update_extension()` to update an installed extension using `quarto update extensions` + - `quarto_create_project()` offers better user experience now (thanks, @jennybc, #206, #153). - `quarto_preview()` gains a `quiet` argument to suppress any output from R or Quarto CLI (thanks, @cwickham, #232.) diff --git a/R/list.R b/R/list.R new file mode 100644 index 0000000..597ef8e --- /dev/null +++ b/R/list.R @@ -0,0 +1,38 @@ +#' List Installed Quarto extensions +#' +#' List Quarto Extensions in this folder or project by running `quarto list` +#' +#' @return A data frame with the installed extensions or NULL (invisibly) if no extensions are installed. +#' +#' @examples +#' \dontrun{ +#' # List Quarto Extensions in this folder or project +#' quarto_list_extensions() +#' } +#' +#' @export +quarto_list_extensions <- function() { + quarto_bin <- find_quarto() + + # quarto list extensions --quiet will return nothing so we need to prevent that. + args <- c("extensions") + x <- quarto_list(args, quarto_bin = quarto_bin, echo = FALSE) + # Clean the stderr output to remove extra spaces and ensure consistent formatting + stderr_cleaned <- gsub("\\s+$", "", x$stderr) + if (grepl("No extensions are installed", stderr_cleaned)) { + invisible() + } else { + df <- utils::read.table( + text = stderr_cleaned, + header = TRUE, + fill = TRUE, + sep = "", + stringsAsFactors = FALSE + ) + df[order(df$Id), ] + } +} + +quarto_list <- function(args = character(), ...) { + quarto_run_what("list", args = args, ...) +} diff --git a/R/remove.R b/R/remove.R new file mode 100644 index 0000000..7dd68a0 --- /dev/null +++ b/R/remove.R @@ -0,0 +1,62 @@ +#' Remove a Quarto extensions +#' +#' Remove an extension in this folder or project by running `quarto remove` +#' +#' @inheritParams quarto_render +#' +#' @param extension The extension name to remove, as in `quarto remove `. +#' +#' @param no_prompt Do not prompt to confirm approval to download external extension. +#' +#' +#' @return Returns invisibly `TRUE` if the extension was removed, `FALSE` otherwise. +#' +#' @seealso `quarto_add_extension()` and [Quarto Website](https://quarto.org/docs/extensions/managing.html). +#' +#' @examples +#' \dontrun{ +#' # Remove an already installed extension +#' quarto_remove_extension("quarto-ext/fontawesome") +#' } +#' @export +quarto_remove_extension <- function( + extension = NULL, + no_prompt = FALSE, + quiet = FALSE, + quarto_args = NULL +) { + rlang::check_required(extension) + + installed_extensions <- quarto_list_extensions() + if (is.null(installed_extensions)) { + if (!quiet) { + cli::cli_alert_warning("No extensions installed.") + } + return(invisible(FALSE)) + } + + quarto_bin <- find_quarto() + + # This will ask for approval or stop installation + approval <- check_removal_approval( + no_prompt, + extension, + "https://quarto.org/docs/extensions/managing.html" + ) + + if (approval) { + args <- c(extension, "--no-prompt", if (quiet) cli_arg_quiet(), quarto_args) + quarto_remove(args, quarto_bin = quarto_bin, echo = FALSE) + if (!quiet) { + cli::cli_alert_success( + "Extension {.code {extension}} successfully removed." + ) + } + } + + invisible(TRUE) +} + +quarto_remove <- function(args = character(), ...) { + quarto_run_what("remove", args = args, ...) +} diff --git a/R/theme.R b/R/theme.R index 9fd5e17..d58d54c 100644 --- a/R/theme.R +++ b/R/theme.R @@ -10,7 +10,6 @@ #' @param fg The foreground color #' @param brand_yml The path to a brand.yml file - #' @rdname theme_helpers #' #' @export @@ -41,7 +40,8 @@ theme_colors_ggplot <- function(bg, fg) { if (!requireNamespace("ggplot2", quietly = TRUE)) { return(NULL) } - ggplot2::`%+%`(ggplot2::theme_minimal(base_size = 11), + ggplot2::`%+%`( + ggplot2::theme_minimal(base_size = 11), ggplot2::theme( panel.border = ggplot2::element_blank(), panel.grid.major.y = ggplot2::element_blank(), @@ -54,7 +54,8 @@ theme_colors_ggplot <- function(bg, fg) { plot.background = ggplot2::element_rect(fill = bg, colour = NA), axis.line = ggplot2::element_line(colour = fg), axis.ticks = ggplot2::element_line(colour = fg) - )) + ) + ) } #' @rdname theme_helpers @@ -92,10 +93,12 @@ theme_brand_gt <- function(brand_yml) { #' @export theme_colors_plotly <- function(bg, fg) { (function(plot) { - plot |> plotly::layout(paper_bgcolor = bg, - plot_bgcolor = bg, - font = list(color = fg) - ) + plot |> + plotly::layout( + paper_bgcolor = bg, + plot_bgcolor = bg, + font = list(color = fg) + ) }) } @@ -116,7 +119,8 @@ theme_colors_thematic <- function(bg, fg) { thematic::thematic_rmd( bg = bg, fg = fg, - )}) + ) + }) } #' @rdname theme_helpers diff --git a/R/update.R b/R/update.R new file mode 100644 index 0000000..3d5e207 --- /dev/null +++ b/R/update.R @@ -0,0 +1,64 @@ +#' Update a Quarto extensions +#' +#' Update an extension to this folder or project by running `quarto update` +#' +#' # Extension Trust +#' +#' Quarto extensions may execute code when documents are rendered. Therefore, if +#' you do not trust the author of an extension, we recommend that you do not +#' install or use the extension. +#' By default `no_prompt = FALSE` which means that +#' the function will ask for explicit approval when used interactively, or +#' disallow installation. +#' +#' @inheritParams quarto_render +#' +#' @param extension The extension to update, either by its name (i.e ` quarto update extension /`), an archive (` quarto update extension `) or a url (`quarto update extension `). +#' +#' @param no_prompt Do not prompt to confirm approval to download external extension. Setting `no_prompt = FALSE` means [Extension Trust](#extension-trust) is accepted. +#' +#' @seealso [quarto_add_extension()], [quarto_remove_extension()], and [Quarto website](https://quarto.org/docs/extensions/managing.html). +#' +#' @return Returns invisibly `TRUE` if the extension was updated, `FALSE` otherwise. +#' +#' @examples +#' \dontrun{ +#' # Update a template and set up a draft document from a GitHub repository +#' quarto_update_extension("quarto-ext/fontawesome") +#' +#' # Update a template and set up a draft document from a ZIP archive +#' quarto_update_extension("https://github.com/quarto-ext/fontawesome/archive/refs/heads/main.zip") +#' } +#' @export +quarto_update_extension <- function( + extension = NULL, + no_prompt = FALSE, + quiet = FALSE, + quarto_args = NULL +) { + rlang::check_required(extension) + + quarto_bin <- find_quarto() + + # This will ask for approval or stop installation + approval <- check_extension_approval( + no_prompt, + "Quarto extension", + "https://quarto.org/docs/extensions/managing.html" + ) + + if (!approval) { + return(invisible(FALSE)) + } + + args <- c(extension, "--no-prompt", if (quiet) cli_arg_quiet(), quarto_args) + quarto_update(args, quarto_bin = quarto_bin, echo = TRUE) + if (!quiet) { + cli::cli_inform("Extension {.code {extension}} updated.") + } + invisible(TRUE) +} + +quarto_update <- function(args = character(), ...) { + quarto_run_what("update", args = args, ...) +} diff --git a/R/utils-prompt.R b/R/utils-prompt.R index be4f576..1340598 100644 --- a/R/utils-prompt.R +++ b/R/utils-prompt.R @@ -1,32 +1,82 @@ -check_extension_approval <- function( +check_approval <- function( no_prompt = FALSE, what = "Something", - see_more_at = NULL + not_action = "approved", + see_more_at = NULL, + prompt_message = NULL, + interactive_info = NULL, # could use `{ what }` as used in `cli_inform()` + .call = rlang::caller_env() ) { if (no_prompt) return(TRUE) - if (!rlang::is_interactive()) { - cli::cli_abort(c( - "{ what } requires explicit approval.", - ">" = "Set {.arg no_prompt = TRUE} if you agree.", - if (!is.null(see_more_at)) { - c(i = "See more at {.url {see_more_at}}") - } - )) + if (!is_interactive()) { + cli::cli_abort( + c( + "{ what } requires explicit approval.", + ">" = "Set {.arg no_prompt = TRUE} if you agree.", + if (!is.null(see_more_at)) { + c(i = "See more at {.url {see_more_at}}") + } + ), + call = .call + ) } else { - cli::cli_inform(c( - "{what} may execute code when documents are rendered. ", - "*" = "If you do not trust the author(s) of this {what}, we recommend that you do not install or use this {what}." - )) - prompt_value <- tolower(readline(sprintf( - "Do you trust the authors of this %s (Y/n)? ", - what - ))) - if (!prompt_value %in% "y") { - cli::cli_inform("{what} not installed.") + if (!is.null(interactive_info)) { + cli::cli_inform(interactive_info) + } + prompt_value <- tolower(readline(prompt_message)) + if (!prompt_value %in% c("", "y")) { + cli::cli_alert_info(paste0(what, " not {not_action}")) return(invisible(FALSE)) - } else { - return(invisible(TRUE)) } } + return(invisible(TRUE)) +} + +check_extension_approval <- function( + no_prompt = FALSE, + what = "Something", + see_more_at = NULL +) { + interactive_info <- c( + "{what} may execute code when documents are rendered. ", + "*" = "If you do not trust the author(s) of this {what}, we recommend that you do not install or use this {what}." + ) + + prompt_message <- sprintf( + "Do you trust the authors of this %s (Y/n)? ", + what + ) + + check_approval( + no_prompt = no_prompt, + what = what, + not_action = "installed", + see_more_at = see_more_at, + prompt_message = prompt_message, + interactive_info = interactive_info + ) +} + +check_removal_approval <- function( + no_prompt = FALSE, + what = "Something", + see_more_at = NULL +) { + prompt_message <- sprintf( + "Are you sure you'd like to remove %s (Y/n)? ", + what + ) + + check_approval( + no_prompt = no_prompt, + what = what, + not_action = "removed", + see_more_at = see_more_at, + prompt_message = prompt_message, + interactive_info = NULL + ) } + +# Needed for testthat to mock base function +readline <- NULL diff --git a/_pkgdown.yml b/_pkgdown.yml index 916b2b9..9436a26 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -21,11 +21,12 @@ reference: - title: "Extensions" desc: > - This functions enable you to install extensions and use template from extensions in your folder and projects. + These functions enable you to manage Quarto extensions and use template from extensions in your folder and projects. More about Quarto extensions at contents: - - starts_with("quarto_add") - - starts_with("quarto_use") + - ends_with("_extension") + - ends_with("_extensions") + - quarto_use_template - title: "Projects" desc: > diff --git a/man/quarto_list_extensions.Rd b/man/quarto_list_extensions.Rd new file mode 100644 index 0000000..ffbff6a --- /dev/null +++ b/man/quarto_list_extensions.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/list.R +\name{quarto_list_extensions} +\alias{quarto_list_extensions} +\title{List Installed Quarto extensions} +\usage{ +quarto_list_extensions() +} +\value{ +A data frame with the installed extensions or NULL (invisibly) if no extensions are installed. +} +\description{ +List Quarto Extensions in this folder or project by running \verb{quarto list} +} +\examples{ +\dontrun{ +# List Quarto Extensions in this folder or project +quarto_list_extensions() +} + +} diff --git a/man/quarto_publish_doc.Rd b/man/quarto_publish_doc.Rd index df6c011..8e6ae6f 100644 --- a/man/quarto_publish_doc.Rd +++ b/man/quarto_publish_doc.Rd @@ -50,11 +50,12 @@ Defaults to the name of the \code{input}.} supplied, will often be displayed in favor of the name. When deploying a new document, you may supply only the title to receive an auto-generated name} -\item{account, server}{Uniquely identify a remote server with either your -user \code{account}, the \code{server} name, or both. If neither are supplied, and -there are multiple options, you'll be prompted to pick one. +\item{server}{Server name. Required only if you use the same account name on +multiple servers.} -Use \code{\link[rsconnect:accounts]{accounts()}} to see the full list of available options.} +\item{account}{Account to deploy application to. This parameter is only +required for the initial deployment of an application when there are +multiple accounts configured on the system (see \link[rsconnect]{accounts}).} \item{render}{\code{local} to render locally before publishing; \code{server} to render on the server; \code{none} to use whatever rendered content currently @@ -62,10 +63,7 @@ exists locally. (defaults to \code{local})} \item{metadata}{Additional metadata fields to save with the deployment record. These fields will be returned on subsequent calls to -\code{\link[rsconnect:deployments]{deployments()}}. - -Multi-value fields are recorded as comma-separated values and returned in -that form. Custom value serialization is the responsibility of the caller.} +\code{\link[rsconnect:deployments]{deployments()}}.} \item{...}{Named parameters to pass along to \code{rsconnect::deployApp()}} } diff --git a/man/quarto_remove_extension.Rd b/man/quarto_remove_extension.Rd new file mode 100644 index 0000000..707d7ef --- /dev/null +++ b/man/quarto_remove_extension.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/remove.R +\name{quarto_remove_extension} +\alias{quarto_remove_extension} +\title{Remove a Quarto extensions} +\usage{ +quarto_remove_extension( + extension = NULL, + no_prompt = FALSE, + quiet = FALSE, + quarto_args = NULL +) +} +\arguments{ +\item{extension}{The extension name to remove, as in \verb{quarto remove }.} + +\item{no_prompt}{Do not prompt to confirm approval to download external extension.} + +\item{quiet}{Suppress warning and other messages.} + +\item{quarto_args}{Character vector of other \code{quarto} CLI arguments to append +to the Quarto command executed by this function. This is mainly intended for +advanced usage and useful for CLI arguments which are not yet mirrored in a +dedicated parameter of this \R function. See \verb{quarto render --help} for options.} +} +\value{ +Returns invisibly \code{TRUE} if the extension was removed, \code{FALSE} otherwise. +} +\description{ +Remove an extension in this folder or project by running \verb{quarto remove} +} +\examples{ +\dontrun{ +# Remove an already installed extension +quarto_remove_extension("quarto-ext/fontawesome") +} +} +\seealso{ +\code{quarto_add_extension()} and \href{https://quarto.org/docs/extensions/managing.html}{Quarto Website}. +} diff --git a/man/quarto_update_extension.Rd b/man/quarto_update_extension.Rd new file mode 100644 index 0000000..5e6d8fe --- /dev/null +++ b/man/quarto_update_extension.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/update.R +\name{quarto_update_extension} +\alias{quarto_update_extension} +\title{Update a Quarto extensions} +\usage{ +quarto_update_extension( + extension = NULL, + no_prompt = FALSE, + quiet = FALSE, + quarto_args = NULL +) +} +\arguments{ +\item{extension}{The extension to update, either by its name (i.e \verb{ quarto update extension /}), an archive (\verb{ quarto update extension }) or a url (\verb{quarto update extension }).} + +\item{no_prompt}{Do not prompt to confirm approval to download external extension. Setting \code{no_prompt = FALSE} means \href{#extension-trust}{Extension Trust} is accepted.} + +\item{quiet}{Suppress warning and other messages.} + +\item{quarto_args}{Character vector of other \code{quarto} CLI arguments to append +to the Quarto command executed by this function. This is mainly intended for +advanced usage and useful for CLI arguments which are not yet mirrored in a +dedicated parameter of this \R function. See \verb{quarto render --help} for options.} +} +\value{ +Returns invisibly \code{TRUE} if the extension was updated, \code{FALSE} otherwise. +} +\description{ +Update an extension to this folder or project by running \verb{quarto update} +} +\section{Extension Trust}{ +Quarto extensions may execute code when documents are rendered. Therefore, if +you do not trust the author of an extension, we recommend that you do not +install or use the extension. +By default \code{no_prompt = FALSE} which means that +the function will ask for explicit approval when used interactively, or +disallow installation. +} + +\examples{ +\dontrun{ +# Update a template and set up a draft document from a GitHub repository +quarto_update_extension("quarto-ext/fontawesome") + +# Update a template and set up a draft document from a ZIP archive +quarto_update_extension("https://github.com/quarto-ext/fontawesome/archive/refs/heads/main.zip") +} +} +\seealso{ +\code{\link[=quarto_add_extension]{quarto_add_extension()}}, \code{\link[=quarto_remove_extension]{quarto_remove_extension()}}, and \href{https://quarto.org/docs/extensions/managing.html}{Quarto website}. +} diff --git a/tests/testthat/_snaps/remove.md b/tests/testthat/_snaps/remove.md new file mode 100644 index 0000000..52b7768 --- /dev/null +++ b/tests/testthat/_snaps/remove.md @@ -0,0 +1,14 @@ +# Removing an extension + + Code + expect_false(quarto_remove_extension("quarto-ext/fontawesome", no_prompt = TRUE)) + Message + ! No extensions installed. + +--- + + Code + expect_true(quarto_remove_extension("quarto-ext/fontawesome", no_prompt = TRUE)) + Message + v Extension `quarto-ext/fontawesome` successfully removed. + diff --git a/tests/testthat/_snaps/utils-prompt.md b/tests/testthat/_snaps/utils-prompt.md new file mode 100644 index 0000000..9d39c47 --- /dev/null +++ b/tests/testthat/_snaps/utils-prompt.md @@ -0,0 +1,118 @@ +# Checking non interactive approval + + Code + expect_true(check_approval(TRUE, "My thing")) + +--- + + Code + check_approval(FALSE, "My thing") + Condition + Error: + ! My thing requires explicit approval. + > Set `no_prompt = TRUE` if you agree. + +--- + + Code + check_approval(FALSE, "My thing", see_more_at = "https://example.com") + Condition + Error: + ! My thing requires explicit approval. + > Set `no_prompt = TRUE` if you agree. + i See more at + +# Checking interactive approval with prompt mocked n + + Code + expect_false({ + check_approval(FALSE, "my-thing", see_more_at = "https://example.com") + }) + Message + i my-thing not approved + +# Checking non interactive extension approval + + Code + expect_true(check_extension_approval(TRUE, "My thing")) + +--- + + Code + check_extension_approval(FALSE, "My thing") + Condition + Error in `check_extension_approval()`: + ! My thing requires explicit approval. + > Set `no_prompt = TRUE` if you agree. + +--- + + Code + check_extension_approval(FALSE, "My thing", see_more_at = "https://example.com") + Condition + Error in `check_extension_approval()`: + ! My thing requires explicit approval. + > Set `no_prompt = TRUE` if you agree. + i See more at + +# Checking interactive extension approval with prompt mocked y + + Code + expect_true({ + check_extension_approval(FALSE, "my-thing") + }) + Message + my-thing may execute code when documents are rendered. + * If you do not trust the author(s) of this my-thing, we recommend that you do not install or use this my-thing. + +# Checking interactive extension approval with prompt mocked n + + Code + expect_false({ + check_extension_approval(FALSE, "my-thing") + }) + Message + my-thing may execute code when documents are rendered. + * If you do not trust the author(s) of this my-thing, we recommend that you do not install or use this my-thing. + i my-thing not installed + +# Checking non interactive removal approval + + Code + expect_true(check_removal_approval(TRUE, "My thing")) + +--- + + Code + check_removal_approval(FALSE, "My thing") + Condition + Error in `check_removal_approval()`: + ! My thing requires explicit approval. + > Set `no_prompt = TRUE` if you agree. + +--- + + Code + check_removal_approval(FALSE, "My thing", see_more_at = "https://example.com") + Condition + Error in `check_removal_approval()`: + ! My thing requires explicit approval. + > Set `no_prompt = TRUE` if you agree. + i See more at + +# Checking interactive removal approval with prompt mocked y + + Code + expect_true({ + check_removal_approval(FALSE, "my-thing") + }) + +# Checking interactive removal approval with prompt mocked n + + Code + expect_false({ + check_removal_approval(FALSE, "my-thing") + }) + Message + i my-thing not removed + diff --git a/tests/testthat/test-add.R b/tests/testthat/test-add.R index 96fa785..5c6f05e 100644 --- a/tests/testthat/test-add.R +++ b/tests/testthat/test-add.R @@ -3,6 +3,7 @@ test_that("Installing an extension", { skip_if_offline("github.com") qmd <- local_qmd_file(c("content")) withr::local_dir(dirname(qmd)) + rlang::local_interactive(FALSE) expect_error( quarto_add_extension("quarto-ext/fontawesome"), "explicit approval" diff --git a/tests/testthat/test-list.R b/tests/testthat/test-list.R new file mode 100644 index 0000000..12a8d82 --- /dev/null +++ b/tests/testthat/test-list.R @@ -0,0 +1,21 @@ +test_that("Listing extensions", { + # don't try to install extensions on CRAN + skip_on_cran() + skip_if_no_quarto() + skip_if_offline("github.com") + qmd <- local_qmd_file(c("content")) + withr::local_dir(dirname(qmd)) + expect_null(quarto_list_extensions()) + quarto_add_extension("quarto-ext/fontawesome", no_prompt = TRUE, quiet = TRUE) + expect_true(dir.exists("_extensions/quarto-ext/fontawesome")) + expect_identical( + quarto_list_extensions()$Id, + c("quarto-ext/fontawesome") + ) + quarto_add_extension("quarto-ext/lightbox", no_prompt = TRUE, quiet = TRUE) + expect_true(dir.exists("_extensions/quarto-ext/lightbox")) + expect_identical( + quarto_list_extensions()$Id, + c("quarto-ext/fontawesome", "quarto-ext/lightbox") + ) +}) diff --git a/tests/testthat/test-remove.R b/tests/testthat/test-remove.R new file mode 100644 index 0000000..5ccb8bc --- /dev/null +++ b/tests/testthat/test-remove.R @@ -0,0 +1,17 @@ +test_that("Removing an extension", { + skip_if_no_quarto() + skip_if_offline("github.com") + qmd <- local_qmd_file(c("content")) + withr::local_dir(dirname(qmd)) + expect_snapshot(expect_false(quarto_remove_extension( + "quarto-ext/fontawesome", + no_prompt = TRUE + ))) + quarto_add_extension("quarto-ext/fontawesome", no_prompt = TRUE, quiet = TRUE) + expect_true(dir.exists("_extensions/quarto-ext/fontawesome")) + expect_snapshot(expect_true(quarto_remove_extension( + "quarto-ext/fontawesome", + no_prompt = TRUE + ))) + expect_false(dir.exists("_extensions")) +}) diff --git a/tests/testthat/test-update.R b/tests/testthat/test-update.R new file mode 100644 index 0000000..25263ec --- /dev/null +++ b/tests/testthat/test-update.R @@ -0,0 +1,23 @@ +test_that("Updating an extension", { + skip_if_no_quarto() + skip_if_offline("github.com") + qmd <- local_qmd_file(c("content")) + withr::local_dir(dirname(qmd)) + quarto_add_extension( + "quarto-ext/fontawesome@v0.0.1", + no_prompt = TRUE, + quiet = TRUE + ) + expect_equal(quarto_list_extensions()$Version, "0.0.1") + quarto_update_extension( + "quarto-ext/fontawesome", + no_prompt = TRUE, + quiet = TRUE + ) + expect_true(dir.exists("_extensions/quarto-ext/fontawesome")) + expect_true( + as.numeric_version(current_version <- quarto_list_extensions()$Version) > + "0.0.1" + ) + expect_false(identical(current_version, "0.0.1")) +}) diff --git a/tests/testthat/test-utils-prompt.R b/tests/testthat/test-utils-prompt.R new file mode 100644 index 0000000..5b3a0e9 --- /dev/null +++ b/tests/testthat/test-utils-prompt.R @@ -0,0 +1,101 @@ +test_that("Checking non interactive approval", { + rlang::local_interactive(FALSE) + expect_snapshot(expect_true(check_approval(TRUE, "My thing"))) + expect_snapshot(error = TRUE, { + check_approval(FALSE, "My thing") + }) + expect_snapshot(error = TRUE, { + check_approval(FALSE, "My thing", see_more_at = "https://example.com") + }) +}) + + +test_that("Checking interactive approval with prompt mocked y", { + local_mocked_bindings( + readline = function(...) "y" + ) + rlang::local_interactive(TRUE) + expect_true({ + check_approval(FALSE, "my-thing") + }) +}) + +test_that("Checking interactive approval with prompt mocked n", { + local_mocked_bindings( + readline = function(...) "n" + ) + rlang::local_interactive(TRUE) + expect_snapshot(expect_false({ + check_approval(FALSE, "my-thing", see_more_at = "https://example.com") + })) +}) + +test_that("Checking non interactive extension approval", { + rlang::local_interactive(FALSE) + expect_snapshot(expect_true(check_extension_approval(TRUE, "My thing"))) + expect_snapshot(error = TRUE, { + check_extension_approval(FALSE, "My thing") + }) + expect_snapshot(error = TRUE, { + check_extension_approval( + FALSE, + "My thing", + see_more_at = "https://example.com" + ) + }) +}) + +test_that("Checking interactive extension approval with prompt mocked y", { + local_mocked_bindings( + readline = function(...) "y" + ) + rlang::local_interactive(TRUE) + expect_snapshot(expect_true({ + check_extension_approval(FALSE, "my-thing") + })) +}) + +test_that("Checking interactive extension approval with prompt mocked n", { + local_mocked_bindings( + readline = function(...) "n" + ) + rlang::local_interactive(TRUE) + expect_snapshot(expect_false({ + check_extension_approval(FALSE, "my-thing") + })) +}) + +test_that("Checking non interactive removal approval", { + rlang::local_interactive(FALSE) + expect_snapshot(expect_true(check_removal_approval(TRUE, "My thing"))) + expect_snapshot(error = TRUE, { + check_removal_approval(FALSE, "My thing") + }) + expect_snapshot(error = TRUE, { + check_removal_approval( + FALSE, + "My thing", + see_more_at = "https://example.com" + ) + }) +}) + +test_that("Checking interactive removal approval with prompt mocked y", { + local_mocked_bindings( + readline = function(...) "y" + ) + rlang::local_interactive(TRUE) + expect_snapshot(expect_true({ + check_removal_approval(FALSE, "my-thing") + })) +}) + +test_that("Checking interactive removal approval with prompt mocked n", { + local_mocked_bindings( + readline = function(...) "n" + ) + rlang::local_interactive(TRUE) + expect_snapshot(expect_false({ + check_removal_approval(FALSE, "my-thing") + })) +})