Awesome
<!-- README.md is generated from README.Rmd. Please edit that file -->reactor <img src="https://github.com/yonicd/hex/raw/master/images/logos/reactor.png" align="right" class="logo"/>
<!-- badges: start --> <!-- badges: end -->When developing Shiny apps there is a lot of reactivity problems that
can arise when one reactive
or observe
element triggers other
elements. In some cases these can create cascading reactivity (the
horror). The goal of reactor
is to diagnose these reactivity problems
and then plan unit tests to avert them during development to make
development less painful.
Installation
And the development version from GitHub with:
# install.packages("remotes")
remotes::install_github("yonicd/reactor")
Usage
Reactor is a pipeline driven api where the user does not need to learn RSelenium in order to be able to drive their applications
Initializing Reactor
Start by creating a reactor class object
library(reactor)
obj <- init_reactor()
obj
#> reactor:
#> application: ~
#> driver: ~
Populating Specifications
You can see it is expecting to be populated by two objects
- application: Specifications for the background process that will host the application
- driver: Specifications for the webdriver that will interact with the application in the background process
Reactor comes with functions to help you create these specifications
- application:
set_runapp_args()
: Assumes that the application is located in a path on the machine and usesshiny::runApp
as the function to launch the applicationset_golem_args()
: Assumes that the application is a golem package and uses thegolem
logic to launch the application.
- driver:
set_chrome_driver()
: LaunchesRSelenium
with a chrome webdriverset_firefox_driver()
: LaunchesRSelenium
with a firefox (gecko) webdriver
obj <- obj%>%
set_runapp_args(
appDir = system.file('examples/good_app.R',package = 'reactor')
)%>%
set_chrome_driver()
#> Adding runApp Settings
#> Adding chrome Settings
<details closed>
<summary>
<span title="Click to Open"> reactor object </span>
</summary>
reactor:
application:
runApp:
test_port: 41896
test_path: /var/folders/kx/t4h_mm1910sb7vhm_gnfnx2c0000gn/T//Rtmp2f3Exn
test_ip: 127.0.0.1
appDir: /Library/Frameworks/R.framework/Versions/3.6/Resources/library/reactor/examples/good_app.R
driver:
chrome:
test_path: /var/folders/kx/t4h_mm1910sb7vhm_gnfnx2c0000gn/T//Rtmp2f3Exn
verbose: no
port: 11136
opts:
args:
- --headless
- --disable-gpu
- --window-size=1280,800
prefs:
profile.default_content_settings.popups: 0
download.prompt_for_download: no
download.directory_upgrade: yes
safebrowsing.enabled: yes
download.default_directory: /var/folders/kx/t4h_mm1910sb7vhm_gnfnx2c0000gn/T//Rtmp2f3Exn
</details>
<br>
If you want turn off headless mode you can update the object
obj <- obj%>%
set_chrome_driver(
opts = chrome_options(headless = FALSE)
)
#> Updating chrome Settings
<details closed>
<summary>
<span title="Click to Open"> reactor object </span>
</summary>
reactor:
application:
runApp:
test_port: 41896
test_path: /var/folders/kx/t4h_mm1910sb7vhm_gnfnx2c0000gn/T//Rtmp2f3Exn
test_ip: 127.0.0.1
appDir: /Library/Frameworks/R.framework/Versions/3.6/Resources/library/reactor/examples/good_app.R
driver:
chrome:
test_path: /var/folders/kx/t4h_mm1910sb7vhm_gnfnx2c0000gn/T//Rtmp2f3Exn
verbose: no
port: 14336
opts:
args:
- --disable-gpu
- --window-size=1280,800
prefs:
profile.default_content_settings.popups: 0
download.prompt_for_download: no
download.directory_upgrade: yes
safebrowsing.enabled: yes
download.default_directory: /var/folders/kx/t4h_mm1910sb7vhm_gnfnx2c0000gn/T//Rtmp2f3Exn
</details>
<br>
Starting Reactor
Once we have specifications in place we can start reactor using
start_reactor()
.
obj%>%
start_reactor()
Interacting with the application
Now that the app is running we can send to the webdriver to interact with the application
set_id_value()
:- expects an input id and the new value
- returns back the reactor object
obj%>%
set_id_value('n',500)
The user can use the following utility functions to interact and query with an application
Inject:
- Inputs
set_id_value()
: Sets a value for a shiny input object by id
- JavaScript
execute()
: Executes a JavaScript call
Query:
- Inputs
query_input_names()
: Returns names of the shiny input idsquery_input_id()
: Returns current values of a shiny input by id
- Outputs
query_output_names()
: Returns names of the shiny output idsquery_output_id()
: Returns current values of a shiny output by id
- JavaScript
query()
: Returns a value from JavaScript call
Closing Reactor
To safely close reactor and all the child processes use kill_app()
:
obj%>%
kill_app()
Pipeline Operations
Because each function is returning the reactor object it is simple to create reactor pipelines.
Reactor will wait for shiny to finish each action before proceeding to the next one.
init_reactor()%>%
set_runapp_args(
appDir = system.file('examples/good_app.R',package = 'reactor')
)%>%
set_chrome_driver()%>%
start_reactor()%>%
set_id_value('n',500)%>%
set_id_value('n',300)%>%
kill_app()
Testing Expectations
Finally reactor tests reactivity expectations in a testthat
framework
using the builtin expect_reactivity()
function
init_reactor()%>%
set_runapp_args(
appDir = system.file('examples/good_app.R',package = 'reactor')
)%>%
set_chrome_driver()%>%
start_reactor()%>%
set_id_value('n',500)%>%
expect_reactivity('hist',1)%>%
set_id_value('n',200)%>%
expect_reactivity('hist',2)%>%
kill_app()