tests/testthat/test-interactive-input.R

box::use(
  testthat[
    expect_equal,
    expect_error,
    expect_false,
    expect_null,
    expect_true,
    test_that
  ]
)

box::use(artma / interactive / input[ask_checkbox, ask_select, ask_text, ask_yes_no])

test_that("ask_text returns the trimmed answer", {
  answer <- ask_text("Name", read_input = function(prompt) "  hello  ")
  expect_equal(answer, "hello")
})

test_that("ask_text falls back to the default on Enter and shows it in the input row", {
  seen_prompt <- NULL
  answer <- ask_text(
    "Name",
    default = "my_analysis",
    read_input = function(prompt) {
      seen_prompt <<- prompt
      ""
    }
  )
  expect_equal(answer, "my_analysis")
  expect_true(grepl("my_analysis", seen_prompt, fixed = TRUE))
})

test_that("ask_text prefers a typed answer over the default", {
  answer <- ask_text("Name", default = "my_analysis", read_input = function(prompt) "custom")
  expect_equal(answer, "custom")
})

test_that("ask_text re-asks on empty input until a value arrives", {
  answers <- c("", "", "value")
  attempt <- 0
  answer <- ask_text("Name", read_input = function(prompt) {
    attempt <<- attempt + 1
    answers[[attempt]]
  })
  expect_equal(answer, "value")
  expect_equal(attempt, 3)
})

test_that("ask_text gives up with an empty string after max_retries attempts", {
  attempt <- 0
  answer <- ask_text("Name", max_retries = 2, read_input = function(prompt) {
    attempt <<- attempt + 1
    ""
  })
  expect_equal(answer, "")
  expect_equal(attempt, 2)
})

test_that("ask_text accepts an empty answer when allow_empty is set", {
  attempt <- 0
  answer <- ask_text("Name", allow_empty = TRUE, read_input = function(prompt) {
    attempt <<- attempt + 1
    ""
  })
  expect_equal(answer, "")
  expect_equal(attempt, 1)
})

test_that("ask_text aborts in non-interactive sessions with the default reader", {
  expect_error(ask_text("Name"), "interactive")
})

test_that("ask_text applies sanitize before the default substitution", {
  # A sanitizer that strips an answer down to nothing must fall through to the
  # default, matching the quoted-path behavior of option prompts.
  answer <- ask_text(
    "Path",
    default = "fallback",
    sanitize = function(x) gsub("\"", "", x),
    read_input = function(prompt) "\"\""
  )
  expect_equal(answer, "fallback")
})

test_that("ask_text passes the sanitized answer to validate", {
  seen <- NULL
  answer <- ask_text(
    "Name",
    sanitize = toupper,
    validate = function(x) {
      seen <<- x
      NULL
    },
    read_input = function(prompt) "abc"
  )
  expect_equal(answer, "ABC")
  expect_equal(seen, "ABC")
})

test_that("ask_text re-asks on a validation failure and accepts a later answer", {
  answers <- c("bad", "good")
  attempt <- 0
  answer <- ask_text(
    "Name",
    validate = function(x) if (x == "bad") "Not a valid name." else NULL,
    read_input = function(prompt) {
      attempt <<- attempt + 1
      answers[[attempt]]
    }
  )
  expect_equal(answer, "good")
  expect_equal(attempt, 2)
})

test_that("ask_text gives up with an empty string after repeated validation failures", {
  attempt <- 0
  answer <- ask_text(
    "Name",
    max_retries = 2,
    validate = function(x) "Never valid.",
    read_input = function(prompt) {
      attempt <<- attempt + 1
      "value"
    }
  )
  expect_equal(answer, "")
  expect_equal(attempt, 2)
})

test_that("ask_text does not validate the default or an allowed empty answer", {
  validate_calls <- 0
  counting_validate <- function(x) {
    validate_calls <<- validate_calls + 1
    "Rejected."
  }

  answer <- ask_text(
    "Name",
    default = "my_default",
    validate = counting_validate,
    read_input = function(prompt) ""
  )
  expect_equal(answer, "my_default")

  answer <- ask_text(
    "Name",
    allow_empty = TRUE,
    validate = counting_validate,
    read_input = function(prompt) ""
  )
  expect_equal(answer, "")
  expect_equal(validate_calls, 0)
})

test_that("ask_select hands the named vector to the backend and returns its value", {
  choices <- c("1% (default)" = "0.01", "5%" = "0.05")
  seen <- NULL
  answer <- ask_select(
    "Winsorization level",
    choices = choices,
    default = "0.01",
    select_fn = function(choices, prompt, selected, descriptions = NULL) {
      seen <<- list(choices = choices, prompt = prompt, selected = selected)
      "0.05"
    }
  )
  expect_equal(answer, "0.05")
  expect_equal(seen$choices, c("1% (default)" = "0.01", "5%" = "0.05"))
  expect_equal(seen$prompt, "Winsorization level")
  expect_equal(seen$selected, 1)
})

test_that("ask_select forwards the descriptions column to the backend", {
  seen <- NULL
  ask_select(
    "Preview data",
    choices = c("Studies" = "studies", "Back" = "back"),
    descriptions = c("per-study estimate counts", ""),
    select_fn = function(choices, prompt, selected, descriptions = NULL) {
      seen <<- descriptions
      "back"
    }
  )
  expect_equal(seen, c("per-study estimate counts", ""))
})

test_that("ask_select passes plain vectors through unchanged", {
  answer <- ask_select(
    "Theme",
    choices = c("light", "dark"),
    select_fn = function(choices, prompt, selected, descriptions = NULL) {
      expect_null(selected)
      "dark"
    }
  )
  expect_equal(answer, "dark")
})

test_that("ask_select falls back to the default on an empty selection", {
  for (empty in list(NULL, character(0))) {
    answer <- ask_select(
      "Strategy",
      choices = c("Stop" = "stop", "Remove" = "remove"),
      default = "stop",
      select_fn = function(choices, prompt, selected, descriptions = NULL) empty
    )
    expect_equal(answer, "stop")
  }
})

test_that("ask_select strips a named value returned by the backend", {
  answer <- ask_select(
    "Strategy",
    choices = c("Stop" = "stop", "Remove" = "remove"),
    select_fn = function(choices, prompt, selected, descriptions = NULL) choices[1]
  )
  expect_equal(answer, "stop")
})

test_that("ask_select returns character(0) on an empty selection without a default", {
  answer <- ask_select(
    "Strategy",
    choices = c("stop", "remove"),
    select_fn = function(choices, prompt, selected, descriptions = NULL) NULL
  )
  expect_equal(answer, character(0))
})

test_that("ask_select rejects a default that is not one of the choices", {
  expect_error(
    ask_select("Q", choices = c("a", "b"), default = "c", select_fn = function(...) "a")
  )
})

test_that("ask_select aborts in non-interactive sessions with the default backend", {
  expect_error(ask_select("Q", choices = c("a", "b")), "interactive")
})

test_that("ask_checkbox hands the named vector to the backend and returns its values", {
  choices <- c("Bayesian model averaging" = "bma", "Linear tests" = "linear_tests", "MAIVE" = "maive")
  seen <- NULL
  answer <- ask_checkbox(
    "Pick methods",
    choices = choices,
    checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) {
      seen <<- list(choices = choices, prompt = prompt, selected = selected, allow_select_all = allow_select_all)
      c("bma", "maive")
    }
  )
  expect_equal(answer, c("bma", "maive"))
  expect_equal(seen$choices, choices)
  expect_equal(seen$prompt, "Pick methods")
  expect_null(seen$selected)
  expect_false(seen$allow_select_all)
})

test_that("ask_checkbox forwards the descriptions column to the backend", {
  seen <- NULL
  ask_checkbox(
    "Pick methods",
    choices = c("BMA" = "bma", "MAIVE" = "maive"),
    descriptions = c("Bayesian model averaging", "spurious-precision robust"),
    checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) {
      seen <<- descriptions
      character(0)
    }
  )
  expect_equal(seen, c("Bayesian model averaging", "spurious-precision robust"))
})

test_that("ask_checkbox passes plain vectors through unchanged", {
  answer <- ask_checkbox(
    "Pick methods",
    choices = c("bma", "maive"),
    allow_select_all = TRUE,
    checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) {
      expect_true(allow_select_all)
      choices
    }
  )
  expect_equal(answer, c("bma", "maive"))
})

test_that("ask_checkbox preselects the defaults by value", {
  seen_selected <- NULL
  ask_checkbox(
    "Pick methods",
    choices = c("BMA" = "bma", "Linear tests" = "linear_tests", "MAIVE" = "maive"),
    default = c("bma", "maive"),
    checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) {
      seen_selected <<- selected
      character(0)
    }
  )
  expect_equal(seen_selected, c(1, 3))
})

test_that("ask_checkbox returns character(0) on a cancelled or empty confirmation", {
  for (empty in list(NULL, character(0))) {
    answer <- ask_checkbox(
      "Pick methods",
      choices = c("BMA" = "bma", "MAIVE" = "maive"),
      default = "bma",
      checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) empty
    )
    expect_equal(answer, character(0))
  }
})

test_that("ask_checkbox rejects defaults that are not among the choices", {
  expect_error(
    ask_checkbox(
      "Pick methods",
      choices = c("a", "b"),
      default = c("a", "c"),
      checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) "a"
    )
  )
})

test_that("ask_checkbox rejects choices with duplicate labels", {
  expect_error(
    ask_checkbox(
      "Pick methods",
      choices = c("Same" = "a", "Same" = "b"),
      checkbox_fn = function(choices, prompt, selected, allow_select_all, descriptions = NULL) "a"
    )
  )
})

test_that("ask_checkbox aborts in non-interactive sessions with the default backend", {
  expect_error(ask_checkbox("Pick methods", choices = c("a", "b")), "interactive")
})

test_that("ask_yes_no returns a logical and honors the default", {
  expect_true(ask_yes_no("Proceed?", select_fn = function(choices, prompt, selected, descriptions = NULL) "yes"))
  expect_false(ask_yes_no("Proceed?", select_fn = function(choices, prompt, selected, descriptions = NULL) "no"))

  # Empty selection falls back to the default.
  expect_false(ask_yes_no("Proceed?", select_fn = function(choices, prompt, selected, descriptions = NULL) NULL))
  expect_true(ask_yes_no("Proceed?", default = TRUE, select_fn = function(choices, prompt, selected, descriptions = NULL) NULL))
})

test_that("ask_yes_no preselects the default choice", {
  seen_selected <- NULL
  ask_yes_no("Proceed?", default = TRUE, select_fn = function(choices, prompt, selected, descriptions = NULL) {
    seen_selected <<- selected
    "no"
  })
  expect_equal(seen_selected, 1)

  ask_yes_no("Proceed?", default = FALSE, select_fn = function(choices, prompt, selected, descriptions = NULL) {
    seen_selected <<- selected
    "yes"
  })
  expect_equal(seen_selected, 2)
})

Try the artma package in your browser

Any scripts or data that you put into this service are public.

artma documentation built on Oct. 27, 2026, 9:06 a.m.