From 75444610cc8bd98c0b027d5615c1a83108199f29 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 19 Mar 2024 15:54:08 -0700 Subject: [PATCH] Integration tests for the 'persistent progress' feature (#254) * Integration tests for the 'persistent progress' feature * Fix incorrect test * Add test folder --- inst/apps/227-persistent-progress/app.R | 68 +++++++++++++ inst/apps/227-persistent-progress/test.js | 96 +++++++++++++++++++ .../227-persistent-progress/tests/testthat.R | 1 + .../tests/testthat/setup.R | 2 + .../tests/testthat/test-shinyjster.R | 1 + 5 files changed, 168 insertions(+) create mode 100644 inst/apps/227-persistent-progress/app.R create mode 100644 inst/apps/227-persistent-progress/test.js create mode 100644 inst/apps/227-persistent-progress/tests/testthat.R create mode 100644 inst/apps/227-persistent-progress/tests/testthat/setup.R create mode 100644 inst/apps/227-persistent-progress/tests/testthat/test-shinyjster.R diff --git a/inst/apps/227-persistent-progress/app.R b/inst/apps/227-persistent-progress/app.R new file mode 100644 index 0000000000..2e7723284d --- /dev/null +++ b/inst/apps/227-persistent-progress/app.R @@ -0,0 +1,68 @@ +library(shiny) +library(bslib) +library(shinyjster) + +ui <- function(req) { + page_sidebar( + sidebar = sidebar( + actionButton("calc1", "Recalc one"), + hr(), + actionButton("calc2", "Recalc two"), + actionButton("calc2p", "Progress two"), + actionButton("calc2e", "Error two"), + actionButton("calc2a", "Abort two"), + actionButton("calc2c", "Cancel output two"), + ), + shinyjster_js(readLines("test.js")), + card( + card_header("One"), + plotOutput("plot1"), + ), + card( + card_header("Two"), + plotOutput("plot2") + ) + ) +} + +server <- function(input, output, session) { + shinyjster_server(input, output, session) + + output$plot1 <- renderPlot({ + input$calc1 + plot(runif(10), runif(10)) + }) + + plot2state = reactiveVal("value") + observeEvent(input$calc2, { + plot2state("value") + }) + observeEvent(input$calc2p, { + plot2state("progress") + }) + observeEvent(input$calc2e, { + plot2state("error") + }) + observeEvent(input$calc2a, { + plot2state("abort") + }) + observeEvent(input$calc2c, { + plot2state("cancel") + }) + + output$plot2 <- renderPlot({ + input$calc2; input$calc2p; input$calc2e; input$calc2a; input$calc2c + + switch(plot2state(), + value = NULL, + progress = req(FALSE, cancelOutput="progress"), + error = stop("boom"), + cancel = req(FALSE, cancelOutput=TRUE), + abort = req(FALSE), + ) + + plot(runif(10), runif(10)) + }) +} + +shinyApp(ui, server) diff --git a/inst/apps/227-persistent-progress/test.js b/inst/apps/227-persistent-progress/test.js new file mode 100644 index 0000000000..ad960504bf --- /dev/null +++ b/inst/apps/227-persistent-progress/test.js @@ -0,0 +1,96 @@ +function click(button_id) { + return () => { + $(`#${button_id}`).click(); + }; +} + +recalc = click("calc2"); +progress = click("calc2p"); +error = click("calc2e"); +abort = click("calc2a"); +cancel = click("calc2c"); + +function recalculating(id = "plot2") { + return document.getElementById(id).classList.contains("recalculating"); +} + +function assertWithTimeout(assertion, timeout = 5000, interval = 50) { + return new Promise((resolve, reject) => { + const start = Date.now(); + function attempt() { + try { + Jster.assert.isTrue(assertion()); + resolve(); + } catch (e) { + if (Date.now() - start > timeout) { + reject(e); + } else { + setTimeout(attempt, interval); + } + } + } + attempt(); + }); +} + +function testcase(jst, name, fns, selector) { + const plot2 = document.getElementById("plot2"); + + jst.add(() => console.log(`Running test: ${name}`)); + if (Array.isArray(fns)) { + for (const fn of fns) { + jst.add(fn); + } + } else { + jst.add(fns); + } + jst.add(Jster.shiny.waitUntilIdle); + jst.add(() => assertWithTimeout(() => plot2.matches(selector))); +} + +var jst = jster(); +jst.add(Jster.shiny.waitUntilIdle); + +jst.add(click("calc1")); +testcase(jst, "Recalculation is persistent", progress, ".recalculating"); +jst.add(() => Jster.assert.isTrue(!recalculating("plot1"))); + +testcase( + jst, + "Value stops recalculation", + [progress, recalc], + ":not(.recalculating)" +); + +testcase( + jst, + "Error stops recalculation", + [progress, error], + ":not(.recalculating)" +); + +testcase( + jst, + "Abort stops recalculation", + [progress, abort], + ":not(.recalculating)" +); + +testcase( + jst, + "Can stack recalculation", + [recalc, progress, (done) => setTimeout(done, 100), progress], + ".recalculating" +); + +const oldValue = document.querySelector("#plot1").innerHTML; +testcase( + jst, + "plot1 isn't blocked by plot2; plot2 doesn't stop recalculating because of plot1", + [progress, click("calc1")], + ".recalculating" +); +jst.add(() => + Jster.assert.isTrue(document.querySelector("#plot1").innerHTML !== oldValue) +); +jst.test(); diff --git a/inst/apps/227-persistent-progress/tests/testthat.R b/inst/apps/227-persistent-progress/tests/testthat.R new file mode 100644 index 0000000000..7d25b5b9e4 --- /dev/null +++ b/inst/apps/227-persistent-progress/tests/testthat.R @@ -0,0 +1 @@ +shinytest2::test_app() diff --git a/inst/apps/227-persistent-progress/tests/testthat/setup.R b/inst/apps/227-persistent-progress/tests/testthat/setup.R new file mode 100644 index 0000000000..be65b4f035 --- /dev/null +++ b/inst/apps/227-persistent-progress/tests/testthat/setup.R @@ -0,0 +1,2 @@ +# Load application support files into testing environment +shinytest2::load_app_env() diff --git a/inst/apps/227-persistent-progress/tests/testthat/test-shinyjster.R b/inst/apps/227-persistent-progress/tests/testthat/test-shinyjster.R new file mode 100644 index 0000000000..bea92a2e8c --- /dev/null +++ b/inst/apps/227-persistent-progress/tests/testthat/test-shinyjster.R @@ -0,0 +1 @@ +shinyjster::testthat_shinyjster()