diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 3083665..e54eb4b 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -41,7 +41,9 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::rcmdcheck + extra-packages: | + any::rcmdcheck + any::shinytest2 needs: check - uses: r-lib/actions/check-r-package@v2 diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index afa5a23..f9e3594 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -23,7 +23,9 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::covr + extra-packages: | + any::covr + any::shinytest2 needs: coverage - name: Test coverage diff --git a/DESCRIPTION b/DESCRIPTION index ac45253..4ec8f64 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: chattr Title: Integrates LLM's with the RStudio IDE -Version: 0.0.0.9009 +Version: 0.0.0.9010 Authors@R: c( person("Edgar", "Ruiz", , "edgar@posit.co", role = c("aut", "cre")), person(given = "Posit Software, PBC", role = c("cph", "fnd")) @@ -19,7 +19,7 @@ Imports: processx, jsonlite, config, - httr2 (>= 1.0.0.9000), + httr2 (>= 1.0.1), purrr, rlang, bslib, @@ -38,6 +38,7 @@ Suggests: knitr, rmarkdown, testthat (>= 3.0.0), + shinytest2, withr Config/testthat/edition: 3 VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index daf4470..6e7b18c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,7 +4,7 @@ S3method(app_init_message,ch_openai) S3method(app_init_message,default) S3method(ch_submit,ch_llamagpt) S3method(ch_submit,ch_openai) -S3method(ch_submit,test_backend) +S3method(ch_submit,ch_test_backend) S3method(ch_test,ch_llamagpt) S3method(ch_test,ch_openai_chat_completions) S3method(ch_test,ch_openai_github_copilot_chat) diff --git a/R/app_server.R b/R/app-server.R similarity index 93% rename from R/app_server.R rename to R/app-server.R index d409613..7f0563e 100644 --- a/R/app_server.R +++ b/R/app-server.R @@ -3,7 +3,14 @@ app_server <- function(input, output, session) { style <- app_theme_style() ch_env$stream_output <- "" app_add_history(input) - auto_invalidate <- reactiveTimer(100) + is_test <- unlist(options("chattr-shiny-test")) %||% FALSE + if (is_test) { + use_switch("apptest", path_ext_set("test", "yml")) + invalidate_time <- 1000 + } else { + invalidate_time <- 100 + } + auto_invalidate <- shiny::reactiveTimer(invalidate_time) session$sendCustomMessage(type = "refocus", message = list(NULL)) insertUI( @@ -24,7 +31,7 @@ app_server <- function(input, output, session) { }) output$provider <- renderText({ - defaults <- chattr_defaults() + defaults <- chattr_defaults(type = "chat") defaults$label }) diff --git a/R/app_theme.R b/R/app-theme.R similarity index 99% rename from R/app_theme.R rename to R/app-theme.R index 32a8e35..fe4873b 100644 --- a/R/app_theme.R +++ b/R/app-theme.R @@ -7,7 +7,7 @@ app_theme_style <- function(x = NULL) { } else { color_bg <- "#fff" color_fg <- "#000" - color_dark <- TRUE + color_dark <- FALSE } if (color_dark) { diff --git a/R/app_ui.R b/R/app-ui.R similarity index 100% rename from R/app_ui.R rename to R/app-ui.R diff --git a/R/chattr-app.R b/R/chattr-app.R index f022d6e..7dedaa1 100644 --- a/R/chattr-app.R +++ b/R/chattr-app.R @@ -33,7 +33,11 @@ chattr_app <- function(viewer = c("viewer", "dialog"), } if (!as_job) { app <- app_interactive(as_job = as_job) - runGadget(app$ui, app$server, viewer = viewer) + if (ide_is_rstudio()) { + runGadget(app$ui, app$server, viewer = viewer) + } else { + shinyApp(app$ui, app$server) + } } else { run_file <- tempfile() writeLines( diff --git a/R/chattr-defaults.R b/R/chattr-defaults.R index 2b47553..d4a92e8 100644 --- a/R/chattr-defaults.R +++ b/R/chattr-defaults.R @@ -75,7 +75,12 @@ chattr_defaults <- function(type = "default", env_model <- Sys.getenv("CHATTR_MODEL", unset = NA) check_files <- NULL if (!is.na(env_model)) { - check_files <- package_file("configs", path_ext_set(env_model, "yml")) + if (env_model == "test") { + env_folder <- "apptest" + } else { + env_folder <- "configs" + } + check_files <- package_file(env_folder, path_ext_set(env_model, "yml")) } # Overrides environment variable if YAML file is present diff --git a/R/chattr-test.R b/R/chattr-test.R index 15221f7..36eac60 100644 --- a/R/chattr-test.R +++ b/R/chattr-test.R @@ -101,12 +101,18 @@ ch_test.ch_openai_github_copilot_chat <- function(defaults = NULL) { } #' @export -ch_submit.test_backend <- function( +ch_submit.ch_test_backend <- function( defaults, prompt = NULL, stream = NULL, prompt_build = TRUE, preview = FALSE, ...) { - "test" + if (stream) { + for (i in seq_len(nchar(prompt))) { + cat(substr(prompt, i, i)) + Sys.sleep(0.1) + } + } + prompt } diff --git a/_pkgdown.yml b/_pkgdown.yml index 93af34f..a048edb 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -6,10 +6,12 @@ reference: contents: - chattr_app - chattr -- title: Defaults +- title: Session defaults contents: starts_with("chattr_defaults") - title: Utilities contents: - chattr_use - chattr_test - +- title: Integration + contents: + - ch_submit diff --git a/inst/apptest/test.yml b/inst/apptest/test.yml new file mode 100644 index 0000000..926aa6a --- /dev/null +++ b/inst/apptest/test.yml @@ -0,0 +1,13 @@ +default: + prompt: | + Use the R language, the tidyverse, and tidymodels + provider: test backend + path: "" + label: Test + model: Test model + max_data_files: 0 + max_data_frames: 0 + include_doc_contents: FALSE + include_history: FALSE + model_arguments: + n_predict: 1000 diff --git a/tests/testthat/_snaps/app_server.md b/tests/testthat/_snaps/app-server.md similarity index 100% rename from tests/testthat/_snaps/app_server.md rename to tests/testthat/_snaps/app-server.md diff --git a/tests/testthat/_snaps/app-server/001.json b/tests/testthat/_snaps/app-server/001.json new file mode 100644 index 0000000..473339a --- /dev/null +++ b/tests/testthat/_snaps/app-server/001.json @@ -0,0 +1,16 @@ +{ + "input": { + "close": 0, + "options": 0, + "prompt": "", + "submit": 0, + "tabs": null + }, + "output": { + "provider": "Test", + "stream": "" + }, + "export": { + + } +} diff --git a/tests/testthat/_snaps/app-server/002.json b/tests/testthat/_snaps/app-server/002.json new file mode 100644 index 0000000..ca7a7cd --- /dev/null +++ b/tests/testthat/_snaps/app-server/002.json @@ -0,0 +1,16 @@ +{ + "input": { + "close": 0, + "options": 0, + "prompt": "", + "submit": 1, + "tabs": null + }, + "output": { + "provider": "Test", + "stream": "" + }, + "export": { + + } +} diff --git a/tests/testthat/_snaps/app-server/003.json b/tests/testthat/_snaps/app-server/003.json new file mode 100644 index 0000000..dfa676d --- /dev/null +++ b/tests/testthat/_snaps/app-server/003.json @@ -0,0 +1,21 @@ +{ + "input": { + "close": 0, + "i_data": "0", + "i_files": "0", + "i_history": false, + "options": 1, + "prompt": "", + "prompt2": "Use the R language, the tidyverse, and tidymodels", + "saved": 0, + "submit": 1, + "tabs": null + }, + "output": { + "provider": "Test", + "stream": "" + }, + "export": { + + } +} diff --git a/tests/testthat/_snaps/app-server/004.json b/tests/testthat/_snaps/app-server/004.json new file mode 100644 index 0000000..6588cda --- /dev/null +++ b/tests/testthat/_snaps/app-server/004.json @@ -0,0 +1,21 @@ +{ + "input": { + "close": 0, + "i_data": "0", + "i_files": "0", + "i_history": false, + "options": 1, + "prompt": "", + "prompt2": "Use the R language, the tidyverse, and tidymodels", + "saved": 1, + "submit": 1, + "tabs": null + }, + "output": { + "provider": "Test", + "stream": "" + }, + "export": { + + } +} diff --git a/tests/testthat/_snaps/app_ui.md b/tests/testthat/_snaps/app-ui.md similarity index 98% rename from tests/testthat/_snaps/app_ui.md rename to tests/testthat/_snaps/app-ui.md index bb418eb..9c80a1f 100644 --- a/tests/testthat/_snaps/app_ui.md +++ b/tests/testthat/_snaps/app-ui.md @@ -17,8 +17,8 @@ Output [1] "
" [2] " " - [3] " " - [4] "
" + [3] " " + [4] "
" [5] "
" [6] "
" [7] "
" @@ -27,12 +27,12 @@ [10] "
" [11] "
" [12] "
" - [13] " " - [14] " " + [14] " " [17] "
" - [18] "
" + [18] "
" [19] "
" [20] "
" [21] "
" @@ -82,7 +82,7 @@ [31] " " [32] "
" [33] "
" - [34] " " + [34] " " [35] "
" [36] "
" [37] "
" @@ -100,7 +100,7 @@ Code capture.output(app_ui_entry("test", TRUE, 1)) Output - [1] "
" + [1] "
" [2] "
" [3] "
" [4] "
" @@ -108,19 +108,19 @@ [6] "
" [7] "
" [8] "
" - [9] " " [13] "
" [14] "
" - [15] " " [19] "
" [20] "
" - [21] " " @@ -139,7 +139,7 @@ Code capture.output(app_ui_entry("test", FALSE, 2)) Output - [1] "
" + [1] "
" [2] "
" [3] "
" [4] "
" diff --git a/tests/testthat/_snaps/ch_context.md b/tests/testthat/_snaps/ch_context.md index 14a1331..d30a072 100644 --- a/tests/testthat/_snaps/ch_context.md +++ b/tests/testthat/_snaps/ch_context.md @@ -20,7 +20,7 @@ Code ch_context_data_files(file_types = "R") Output - [1] "Data files available: \n|- helper-utils.R\n|- setup.R\n|- test-app_server.R\n|- test-app_ui.R\n|- test-backend-llamagpt.R\n|- test-backend-openai.R\n|- test-ch-defaults-save.R\n|- test-ch_context.R\n|- test-ch_defaults.R\n|- test-chattr-test.R\n|- test-chattr-use.R\n|- test-chattr.R\n|- test-ide.R\n|- test-utils.R" + [1] "Data files available: \n|- helper-utils.R\n|- setup.R\n|- test-app-server.R\n|- test-app-ui.R\n|- test-backend-llamagpt.R\n|- test-backend-openai.R\n|- test-ch-defaults-save.R\n|- test-ch_context.R\n|- test-ch_defaults.R\n|- test-chattr-test.R\n|- test-chattr-use.R\n|- test-chattr.R\n|- test-ide.R\n|- test-utils.R" --- diff --git a/tests/testthat/_snaps/chattr.md b/tests/testthat/_snaps/chattr.md new file mode 100644 index 0000000..8c5c210 --- /dev/null +++ b/tests/testthat/_snaps/chattr.md @@ -0,0 +1,22 @@ +# chattr() works + + Code + chattr("test", preview = TRUE) + Output + test + Message + + -- chattr ---------------------------------------------------------------------- + + -- Preview for: Console + * Provider: test backend + * Path/URL: https://api.openai.com/v1/chat/completions + * Model: gpt-4 + * Label: GPT 4 (OpenAI) + * temperature: 0.01 + * max_tokens: 1000 + * stream: TRUE + + -- Prompt: + test + diff --git a/tests/testthat/helper-utils.R b/tests/testthat/helper-utils.R index 58b54cf..1fd6e6d 100644 --- a/tests/testthat/helper-utils.R +++ b/tests/testthat/helper-utils.R @@ -3,3 +3,10 @@ test_simulate_model <- function(file, type = "console") { yaml::read_yaml() as_ch_model(defaults$default, type) } + +test_model_backend <- function() { + chattr_use("gpt4") + chattr_defaults("chat", provider = "test backend") + chattr_defaults("console", provider = "test backend") + chattr_defaults("script", provider = "test backend") +} diff --git a/tests/testthat/test-app_server.R b/tests/testthat/test-app-server.R similarity index 55% rename from tests/testthat/test-app_server.R rename to tests/testthat/test-app-server.R index 2c97246..bbfbd19 100644 --- a/tests/testthat/test-app_server.R +++ b/tests/testthat/test-app-server.R @@ -1,3 +1,25 @@ +test_that("chattr app initial values are consistent", { + skip_on_cran() + shiny_app <- shinyApp(app_ui(), app_server) + app <- shinytest2::AppDriver$new( + shiny_app, + options = list("chattr-shiny-test" = TRUE) + ) + Sys.sleep(1) + app$expect_values(screenshot_args = FALSE) + app$set_inputs(prompt = "hello", allow_no_input_binding_ = TRUE) + Sys.sleep(1) + app$click("submit") + Sys.sleep(1) + app$expect_values(screenshot_args = FALSE) + app$click("options") + Sys.sleep(1) + app$expect_values(screenshot_args = FALSE) + app$click("saved") + Sys.sleep(1) + app$expect_values(screenshot_args = FALSE) +}) + test_that("Split content function", { content <- readRDS(package_file("history", "raw.rds"))[[2]]$content expect_snapshot(app_split_content(content)) @@ -12,19 +34,6 @@ test_that("Cleanup", { expect_null(ch_history_set(NULL)) }) - -test_that("app_server() function runs", { - local_mocked_bindings( - insertUI = function(...) invisible(), - observeEvent = function(...) invisible() - ) - session <- list() - session$sendCustomMessage <- function(...) {} - expect_silent( - app_server(list(), list(), session = session) - ) -}) - test_that("Adding to history works", { local_mocked_bindings( ch_history = function(...) { @@ -35,7 +44,6 @@ test_that("Adding to history works", { expect_silent(app_add_history("test")) }) - test_that("app_add_assistant() function runs", { local_mocked_bindings( insertUI = function(...) invisible() diff --git a/tests/testthat/test-app_ui.R b/tests/testthat/test-app-ui.R similarity index 100% rename from tests/testthat/test-app_ui.R rename to tests/testthat/test-app-ui.R diff --git a/tests/testthat/test-chattr.R b/tests/testthat/test-chattr.R index a9a5371..6561e49 100644 --- a/tests/testthat/test-chattr.R +++ b/tests/testthat/test-chattr.R @@ -1,21 +1,29 @@ test_that("chattr() works", { - def <- test_simulate_model("gpt35.yml") - class(def) <- c("test_backend", "ch_model") + local_mocked_bindings( + ui_current = function(...) { + "" + } + ) + test_model_backend() + expect_output(chattr("hello", stream = TRUE), "hello") + expect_output(chattr("hello", stream = FALSE), "hello") + expect_snapshot(chattr("test", preview = TRUE)) +}) +test_that("External R submit works", { local_mocked_bindings( - chattr_defaults = function(...) def + ui_current = function(...) { + "script" + } ) + test_model_backend() expect_silent(chattr("hello")) - expect_output(chattr("hello", stream = FALSE)) }) -test_that("chattr() works", { - def <- test_simulate_model("gpt35.yml") - class(def) <- c("test_backend", "ch_model") - +test_that("Test for null output", { local_mocked_bindings( - chattr_defaults = function(...) def, - ui_current = function(...) "script" + ch_submit = function(...) NULL ) - expect_silent(chattr("hello")) + test_model_backend() + expect_null(chattr("hello")) })