insightsengineering / insightsengineering/teal.picks
[Feature Request]: Add `shinytest2` utils to package
- Dominant language
- R
- Stars
- 4
- Forks
- 0
- Avg merge
- 6d 11h
- Merged PRs (30d)
- 3
Description
### Feature description
Goal: Add utilities that modify picks in `shinytest2::AppDriver` to reuse on end-to-end tests and avoid having multiple definitions in other packages from frameworks
#### Details
Custom functions that make changes in `teal.picks` are being used and re-used in `teal.modules.clinical`, `teal.modules.general` and other packages
This may fit better in this package as the workflow is very specific to `teal.picks` and not general.
The workflow is implemented as:
- Modifications to input are only applied after a non-binding input changes from `TRUE` to `FALSE`
- For instance `var1-variables-selected` only changes when ``var1-variables-selected_open` is toggled
- The same for `var1-datasets-selected` and `var1-values-selected`
- This is part of `shinyWidgets::pickerInput` implementation, but the logic in `teal.picks` also uses this
- The idea is to only process changes to picks once the picker popup closes
A proposal from `teal.modules.general` that needs a slight modification to no longer rely on `TealAppDriver` (by using `id` as a fully formed namespace path, instead of the `app_driver$set_active_module_input()` and `app_driver$get_active_module_input()`)
```r
#' Set the selected value of a teal.picks slot in a teal app using shinytest2.
#'
#' It sets the value of picks of the currently active module.
#'
#' @param app_driver (`TealAppDriver`).
#' @param id (`character(1)`) `picks`` id.
#' @param slot_name (`character(1)`) The name of the slot. One of "variables", "datasets", or "values".
#' @param value The value to set using `AppDriver$set_input`
#' @param ... arguments passed to `AppDriver$set_input`.
#' @return `TRUE` if the value was set successfully, `FALSE` if the value was already set,
#' and a warning if the resolved value does not match the expected value.
.set_picks_slot_selected <- function(app_driver, id, value, slot_name = c("variables", "datasets", "values"), ...) {
slot_name <- match.arg(slot_name)
selected_open_id <- sprintf("%s-%s-selected_open", id, slot_name) # teal.picks implementation detail
selected_id <- sprintf("%s-%s-selected", id, slot_name)
if (isTRUE(app_driver$get_value(input = selected_open_id))) {
stop("Cannot set picks slot while the picker input is open. Please close the picker input first.")
}
if (isTRUE(all.equal(app_driver$get_value(input = selected_id), value, tolerance = 1e-15))) {
return(FALSE)
}
# Mock the opening of the picker input
app_driver$set_input(selected_open_id, TRUE, allow_no_input_binding_ = TRUE)
# Set the value
tryCatch(
.change_selectpicker(app_driver, selected_id, value),
error = function(e) warning(e)
)
# Mock the closing of the picker input
app_driver$set_input(selected_open_id, FALSE, allow_no_input_binding_ = TRUE)
# Validate change in picks
picks_export_id <- sprintf("%s-picks_resolved", id)
resolved_value <- app_driver$get_values(export = picks_export_id)$export[[picks_export_id]][[slot_name]]$selected
if (!isTRUE(all.equal(resolved_value, value, tolerance = 1e-15))) {
warning("Setting picks slot did not result in expected resolved value.")
}
TRUE
}
```
Additionally, the `shinyWidgets::pickerInput` does not allow consistent modification for its value using `shinytest2` api _(`AppDriver$set_inputs()`)_
And a custom Javascript needs to be used (code below is from `teal.modules.general`)
```r
change_selectpicker <- function(app_driver, id, value, wait_ = TRUE) {
app_driver$run_js(sprintf("$('select#%s').selectpicker('val', %s);", id, jsonlite::toJSON(value, auto_unbox = TRUE)))
if (wait_) {
app_driver$wait_for_idle()
}
return(all.equal(app_driver$get_values()$input[[id]], value, tolerance = 1e-15))
}
```
### Code of Conduct
- [x] I agree to follow this project's Code of Conduct.
### Contribution Guidelines
- [x] I agree to follow this project's Contribution Guidelines.
### Security Policy
- [x] I agree to follow this project's Security Policy.
Contributor guide
Research direction
Start by reading the proposed .set_picks_slot_selected and change_selectpicker entry points, then inspect the shinytest2::AppDriver calls and teal.picks picker IDs described in the issue. Done means reusable utilities can update variables, datasets, and values selections, handle the picker open/close state, and validate the resolved picks value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, r
- Domain
- testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100