This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
shinytest2 is an R package that provides automated testing for Shiny applications using a headless Chromium browser via the chromote package. It integrates with testthat for snapshot-based testing of Shiny apps.
-
AppDriver (R6 class): The central testing interface defined in R/app-driver.R. This R6 class manages:
- Launching Shiny apps in background R processes (via callr)
- Controlling a headless Chrome browser (via chromote)
- Capturing app state (inputs, outputs, exports)
- Taking screenshots and executing JavaScript
- Waiting for reactivity to stabilize
-
Test Recording: R/record-test.R implements an interactive test recorder that launches a special Shiny app to record user interactions and generate test code automatically.
-
Expectations: The package provides several expectation methods split across files like R/app-driver-expect-values.R, R/app-driver-expect-screenshot.R, etc. These wrap testthat's snapshot functions.
-
Split R6 Methods: The AppDriver R6 class is split across multiple files (app-driver-*.R) for maintainability. Each file handles a specific aspect (expectations, waiting, JavaScript execution, etc.).
-
Background Processes: Shiny apps run in separate R processes via callr to isolate them from the testing environment.
-
Chromote Integration: The package heavily uses chromote to control a headless Chrome browser for realistic user interaction simulation.
-
Snapshot Testing: Integrates with testthat's snapshot testing infrastructure. Snapshots are stored in tests/testthat/_snaps/.
-
Test Mode: Shiny apps must run with
test.mode = TRUEto expose internal values viashiny::exportTestValues().
# Run all package tests
devtools::test()
# Run specific test file
testthat::test_file("tests/testthat/test-app-driver.R")
# Run tests with filter
devtools::test(filter = "screenshot")# Load package for development
devtools::load_all()
# Check package
devtools::check()
# Build documentation
devtools::document()
# Build website
pkgdown::build_site()# Create an AppDriver for interactive testing
library(shinytest2)
app <- AppDriver$new("path/to/app")
app$view() # Opens browser for visual inspection
app$get_values()
app$set_inputs(input_name = value)
app$stop()The package includes C++ code (in src/) compiled via cpp11. After modifying C++ code:
# Rebuild and reload package
devtools::load_all()-
$new()initializes the driver:- Starts background R process with Shiny app
- Launches ChromoteSession
- Injects shiny-tracer.js for monitoring
- Optionally waits for app to stabilize
-
$stop()cleans up:- Stops Shiny process (SIGINT → SIGTERM → SIGKILL)
- Closes ChromoteSession
- Cleans up logs if requested
$wait_for_idle(): Waits for Shiny to be idle (no reactivity) for a specified duration$wait_for_value(): Waits for a specific input/output/export to have a non-ignored value$wait_for_js(): Waits for JavaScript expression to evaluate to true- All set_inputs() calls wait by default until an output updates
- Default selector changed in v0.3.0 from
"html"to"scrollable_area" compare_screenshot_threshold()in R/compare-screenshot-threshold.R provides threshold-based comparison using convolution to handle minor pixel differences- Screenshots are platform-dependent; use
variant = platform_variant()for cross-platform testing
- Package follows standard R package structure with tests in tests/testthat/
- Recorded tests typically saved as test-app-.R
- Support files (helper functions, data) should go in tests/testthat/ and are loaded via
load_app_support()
This package replaces the deprecated shinytest package. Key function: migrate_from_shinytest() in R/migrate.R converts old shinytest tests to shinytest2 format.
-
$get_logs()returns a data.frame with timestamped logs from three sources:- "shinytest2": AppDriver logging via
$log_message() - "shiny": stdout/stderr from the background Shiny process
- "chromote": Browser console, exceptions, and WebSocket traffic (if
options(shiny.trace = TRUE))
- "shinytest2": AppDriver logging via
-
Set
options(shiny.trace = TRUE)in the app's options to capture all WebSocket messages
The package uses rstudio/shiny-workflows GitHub Actions:
- R-CMD-check runs on multiple platforms
- Website deployment via pkgdown
- Test apps can be tested in CI using the custom action at actions/test-app
- Prefer
$expect_values()over$expect_screenshot()for robustness - Prefer
$expect_text()over$expect_html()to avoid brittleness from internal DOM changes - Use
$expect_screenshot()sparingly as screenshots are very brittle to:- R version changes
- OS differences
- Font differences
- Package version updates
Critical dependencies to be aware of:
- chromote (≥ 0.5.0): Browser automation
- testthat (≥ 3.3.1): Testing framework
- callr: Background R processes
- shiny: The framework being tested
- cpp11: C++ integration for performance-critical operations