tests/testthat/test-add-image.R

test_that("add_image() accumulates image parts via parameter_fn", {
  tp <- tidyprompt("Describe the picture")
  tp <- add_image(tp, image = "data:image/png;base64,AAAA")
  tp <- add_image(tp, image = "https://example.com/cat.jpg")

  wraps <- get_prompt_wraps(tp, order = "default")
  expect_true(length(wraps) >= 1)

  prov <- llm_provider_fake()
  # Apply parameter_fns as send_prompt would
  for (pw in wraps) {
    if (!is.null(pw$parameter_fn)) {
      prov$set_parameters(pw$parameter_fn(prov))
    }
  }

  parts <- prov$parameters$.add_image_parts
  expect_true(is.list(parts))
  expect_equal(length(parts), 2)
  expect_setequal(
    unique(vapply(parts, function(p) p$source, character(1))),
    c("b64", "url")
  )
})

test_that("add_image() converts recordedplot objects to images", {
  skip_on_cran()
  skip_on_ci()

  tmp_img <- tempfile(fileext = ".png")
  on.exit(unlink(tmp_img), add = TRUE)

  grDevices::png(tmp_img)
  recorded <- tryCatch(
    {
      plot(1:5, 1:5)
      grDevices::recordPlot()
    },
    error = function(e) e
  )
  grDevices::dev.off()

  skip_if(inherits(recorded, "error"), "Current device cannot record plots")
  skip_if_not(inherits(recorded, "recordedplot"))
  # In headless CI environments, png() might produce empty files or fail to record properly
  if (file.exists(tmp_img) && file.size(tmp_img) == 0) {
    skip("png() device produced empty file; skipping plot test")
  }

  tp <- tidyprompt("Describe the chart")
  tp <- add_image(tp, image = recorded)

  wraps <- get_prompt_wraps(tp, order = "default")
  prov <- llm_provider_fake()
  for (pw in wraps) {
    if (!is.null(pw$parameter_fn)) {
      prov$set_parameters(pw$parameter_fn(prov))
    }
  }

  parts <- prov$parameters$.add_image_parts
  expect_true(length(parts) >= 1)
  last_part <- parts[[length(parts)]]
  expect_identical(last_part$mime, "image/png")
  expect_identical(last_part$source, "b64")
  expect_true(nchar(last_part$data) > 100)
})

test_that("add_image() accepts ggplot objects when available", {
  skip_if_not_installed("ggplot2")
  skip_on_ci()
  skip_on_cran()

  plt <- ggplot2::ggplot(mtcars, ggplot2::aes(mpg, disp)) +
    ggplot2::geom_point()

  skip_if_not(
    inherits(plt, "ggplot"),
    "ggplot object creation failed to produce 'ggplot' class"
  )

  # Check if png device is working (required for rasterizing ggplot)
  tmp_check <- tempfile()
  png_works <- tryCatch(
    {
      grDevices::png(tmp_check)
      grDevices::dev.off()
      file.exists(tmp_check)
    },
    error = function(e) FALSE
  )
  unlink(tmp_check)
  skip_if_not(png_works, "png() device is not available or working")

  tp <- tidyprompt("Describe the scatterplot")
  tp <- add_image(tp, image = plt)

  wraps <- get_prompt_wraps(tp, order = "default")
  prov <- llm_provider_fake()
  for (pw in wraps) {
    if (!is.null(pw$parameter_fn)) {
      prov$set_parameters(pw$parameter_fn(prov))
    }
  }

  parts <- prov$parameters$.add_image_parts
  expect_true(length(parts) >= 1)
  last_part <- parts[[length(parts)]]
  expect_identical(last_part$mime, "image/png")
  expect_identical(last_part$source, "b64")
  expect_true(nchar(last_part$data) > 100)
})

test_that("ggplot objects are not misclassified as ellmer content", {
  skip_if_not_installed("ggplot2")

  plt <- ggplot2::ggplot(mtcars, ggplot2::aes(mpg, disp)) +
    ggplot2::geom_point()

  expect_false(tidyprompt:::.tp_is_ellmer_content(plt))
  expect_true(tidyprompt:::.tp_is_plot_object(plt))
})

Try the tidyprompt package in your browser

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

tidyprompt documentation built on April 21, 2026, 9:07 a.m.