{"id":13736739,"url":"https://github.com/yonicd/reactor","last_synced_at":"2025-09-23T05:30:45.964Z","repository":{"id":48875338,"uuid":"219646466","full_name":"yonicd/reactor","owner":"yonicd","description":"unit testing for shiny reactivity","archived":false,"fork":false,"pushed_at":"2021-07-07T14:13:32.000Z","size":41537,"stargazers_count":59,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-03T20:43:21.698Z","etag":null,"topics":["r","shiny","testthat"],"latest_commit_sha":null,"homepage":"https://yonicd.github.io/reactor","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yonicd.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-05T03:06:54.000Z","updated_at":"2025-03-22T10:53:33.000Z","dependencies_parsed_at":"2022-08-19T14:52:10.178Z","dependency_job_id":null,"html_url":"https://github.com/yonicd/reactor","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/yonicd/reactor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonicd%2Freactor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonicd%2Freactor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonicd%2Freactor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonicd%2Freactor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yonicd","download_url":"https://codeload.github.com/yonicd/reactor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yonicd%2Freactor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276519124,"owners_count":25656556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-23T02:00:09.130Z","response_time":73,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["r","shiny","testthat"],"created_at":"2024-08-03T03:01:27.658Z","updated_at":"2025-09-23T05:30:45.617Z","avatar_url":"https://github.com/yonicd.png","language":"R","readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n\nlibrary(details)\n```\n\n# reactor \u003cimg src=\"https://github.com/yonicd/hex/raw/master/images/logos/reactor.png\" align=\"right\" class=\"logo\"/\u003e\n\n\u003c!-- badges: start --\u003e\n[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://www.tidyverse.org/lifecycle/#maturing)\n[![R-CMD-check](https://github.com/yonicd/reactor/workflows/R-CMD-check/badge.svg)](https://github.com/yonicd/reactor/actions)\n[![pkgdown](https://github.com/yonicd/reactor/workflows/pkgdown/badge.svg)](https://github.com/yonicd/reactor/actions)\n[![Reactor](https://github.com/yonicd/reactor/workflows/Reactor/badge.svg)](https://github.com/yonicd/reactor/actions)\n[![Covrpage Summary](https://img.shields.io/badge/covrpage-Last_Build_2020_12_21-brightgreen.svg)](https://tinyurl.com/yd2odf5g)\n\u003c!-- badges: end --\u003e\n\nWhen 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.\n\n## Installation\n\nAnd the development version from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"yonicd/reactor\")\n```\n\n## Usage\n\nReactor is a pipeline driven api where the user does not need to learn RSelenium in order to be able to drive their applications\n\n### Initializing Reactor\n\nStart by creating a reactor class object\n\n```{r}\nlibrary(reactor)\nobj \u003c- init_reactor()\nobj\n```\n\n### Populating Specifications\n\nYou can see it is expecting to be populated by two objects \n\n  - __application__: Specifications for the background process that will host the application\n  - __driver__: Specifications for the webdriver that will interact with the application in the background process\n  \nReactor comes with functions to help you create these specifications\n\n  - __application__: \n    - `set_runapp_args()` : Assumes that the application is located in a path on the machine and uses `shiny::runApp` as the function to launch the application\n    - `set_golem_args()`: Assumes that the application is a [golem](https://github.com/ThinkR-open/golem) package and uses the `golem` logic to launch the application.\n  - __driver__:\n    - `set_chrome_driver()`: Launches `RSelenium` with a chrome webdriver \n    - `set_firefox_driver()`: Launches `RSelenium` with a firefox (gecko) webdriver \n\n```{r}\nobj \u003c- obj%\u003e%\n  set_runapp_args(\n    appDir = system.file('examples/good_app.R',package = 'reactor')\n  )%\u003e%\n  set_chrome_driver()\n\n```\n\n \n```{details, echo = FALSE,details.lang = 'yml',details.summary = 'reactor object'}\nobj\n```\n\nIf you want turn off headless mode you can update the object\n\n```{r}\nobj \u003c- obj%\u003e%\n  set_chrome_driver(\n     opts = chrome_options(headless = FALSE)\n  )\n\n```\n\n```{details, echo = FALSE, details.lang = 'yml',details.summary = 'reactor object'}\nobj\n```\n\n### Starting Reactor\n\nOnce we have specifications in place we can start reactor using `start_reactor()`. \n\n```{r,eval = FALSE}\nobj%\u003e%\n  start_reactor()\n```\n\n### Interacting with the application\n\nNow that the app is running we can send to the webdriver to interact with the application\n\n- `set_id_value()`: \n  - expects an input id and the new value\n  - returns back the reactor object\n\n```{r,eval = FALSE}\nobj%\u003e%\n  set_id_value('n',500)\n```\n\nThe user can use the following utility functions to interact and query with an application\n\nInject:\n\n- Inputs\n  - `set_id_value()`: Sets a value for a shiny input object by id\n- JavaScript\n  - `execute()`: Executes a JavaScript call\n\nQuery:\n\n- Inputs \n  - `query_input_names()`: Returns names of the shiny input ids\n  - `query_input_id()`: Returns current values of a shiny input by id\n- Outputs\n  - `query_output_names()`: Returns names of the shiny output ids\n  - `query_output_id()`: Returns current values of a shiny output by id\n- JavaScript\n  - `query()`: Returns a value from JavaScript call\n\n### Closing Reactor\n\nTo safely close reactor and all the child processes use `kill_app()`:\n\n```{r,eval = FALSE}\nobj%\u003e%\n  kill_app()\n```\n\n### Pipeline Operations\n\nBecause each function is returning the reactor object it is simple to create reactor pipelines.\n\nReactor will wait for shiny to finish each action before proceeding to the next one.\n\n```{r,eval = FALSE}\ninit_reactor()%\u003e%\n  set_runapp_args(\n    appDir = system.file('examples/good_app.R',package = 'reactor')\n  )%\u003e%\n  set_chrome_driver()%\u003e%\n  start_reactor()%\u003e%\n  set_id_value('n',500)%\u003e%\n  set_id_value('n',300)%\u003e%\n  kill_app()\n```\n\n### Testing Expectations\n\nFinally reactor tests reactivity expectations in a `testthat` framework using the builtin `expect_reactivity()` function \n\n```{r,eval = FALSE}\ninit_reactor()%\u003e%\n  set_runapp_args(\n    appDir = system.file('examples/good_app.R',package = 'reactor')\n  )%\u003e%\n  set_chrome_driver()%\u003e%\n  start_reactor()%\u003e%\n  set_id_value('n',500)%\u003e%\n  expect_reactivity('hist',1)%\u003e%\n  set_id_value('n',200)%\u003e%\n  expect_reactivity('hist',2)%\u003e%\n  kill_app()\n```\n","funding_links":[],"categories":["R","Tools","Developer Tools"],"sub_categories":["Packages","Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyonicd%2Freactor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyonicd%2Freactor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyonicd%2Freactor/lists"}