AppDriver can't run apps with the dev copy of shiny itself

Open
#463 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
53/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
r
Domain
testing

Research direction

Start in R/app-driver-start.R at app_start_shiny() and trace how the background process sets up the library() / require() shim and .seed block before runApp(). Reproduce with a directory app while developing shiny itself, then check whether loading the dev package first keeps it active throughout startup. Done means the directory app starts with the dev copy of shiny; the function app behavior under devtools::test() is described separately as an unresolved limitation.

Written by the indexing model from the issue text.

Description

ai-generated-issue bug

When the package in development is shiny itself, AppDriver can't run an app with the dev copy of shiny. For a directory app, the app never starts.

Cause

In app_start_shiny() (R/app-driver-start.R, v0.5.1), the background process installs the library() / require() shim, then runs the app:

do.call(shiny::runApp, c(list(appDir = .app_dir), .shiny_args))

shiny::runApp loads the installed shiny before app.R is sourced. When app.R then calls library(shiny), the shim runs pkgload::load_all() on the dev shiny, replacing the namespace under the runApp() that is already running. The page never becomes ready (window.shinytest2.ready never turns true). The .seed block has the same problem: getNamespace("shiny") loads installed shiny before the shim is set up.

Reprex

Run from a shiny source checkout. The installed shiny is 1.14.0, the checkout is 1.14.0.9000. app.R logs which shiny is loaded before and after its own library(shiny):

# app.R
probe <- function(when) message(sprintf("PROBE %s: path=%s", when,
  if (isNamespaceLoaded("shiny")) getNamespaceInfo("shiny", "path") else "not loaded"))
probe("before library(shiny)")
library(shiny)
probe("after library(shiny)")
shinyApp(fluidPage("hi"), function(input, output) {})
# from the shiny checkout
devtools::load_all()
app <- shinytest2::AppDriver$new("path/to/app-dir")
#> Error: ... window.shinytest2 && window.shinytest2.ready === true
# app logs:
#> PROBE before library(shiny): path=<R library>/shiny        (installed 1.14.0)
#> PROBE after library(shiny):  path=<shiny checkout>         (dev 1.14.0.9000)

Results

App How tests run shiny used Works?
directory load_all() + test_file() installed, then dev reload mid-run no
directory devtools::test() installed, then dev reload mid-run no
function load_all() + test_file() dev yes
function devtools::test() installed yes, but not dev

Proposed fix

When the dev package is shiny, attach it first thing in the background process (before the .seed block), and mark it loaded so the shim only calls base::library():

if (identical(.package_name, "shiny")) {
  pkgload::load_all(
    .package_path,
    attach = TRUE,
    export_all = FALSE,
    export_imports = TRUE,
    attach_testthat = FALSE,
    quiet = TRUE
  )
  has_loaded$value <- TRUE
}

I tried this by patching app_start_shiny() in memory. Both directory-app cases then run dev shiny the whole time and the app starts. Loading only the namespace (attach = FALSE) did not work: the shim's later base::library(shiny) brought the installed copy back.

Not fixed by this: function apps under devtools::test()

devtools::test() evaluates test code in an environment whose parent is shiny's namespace. callr serializes the app function with that environment, so the background process loads installed shiny while deserializing the arguments, before any shinytest2 code runs. Reloading dev shiny afterwards leaves the function's closure pointing at the old namespace, and the app never becomes stable. Callers can work around it with environment(app_fn) <- globalenv() before passing the function to AppDriver; I checked that the function app then runs dev shiny under devtools::test(). shinytest2 could do this itself, or document it.

Found while working on rstudio/shiny#4455, which runs shiny's own shinytest2 browser tests.

Dominant language
R
Stars
123
Forks
23
Avg merge
3h 13m
Merged PRs (30d)
12

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from rstudio/shinytest2

All issues in rstudio/shinytest2

Similar issues

More R issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.