diff --git a/.Rbuildignore b/.Rbuildignore
index c1d8c7e..8358911 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -12,3 +12,4 @@
^README\.md$
^rsconnect$
^tpma-explorer\.Rproj$
+^.cache$
\ No newline at end of file
diff --git a/.lintr b/.lintr
index 52b33f6..e0a6781 100644
--- a/.lintr
+++ b/.lintr
@@ -1,3 +1,7 @@
+linters: linters_with_defaults(
+ line_length_linter(120),
+ object_length_linter(50)
+ )
exclusions: list(
"data-raw/la-lookups.R"
)
diff --git a/DESCRIPTION b/DESCRIPTION
index 9e568d1..b82371f 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: tpma.explorer
Title: Explore TPMA Data
-Version: 0.2.0.9000
+Version: 0.3.4
Authors@R: c(
person("Matt", "Dray", , "matt.dray@nhs.net", role = c("aut", "cre")),
person("Tom", "Jemmett", , "thomas.jemmett@nhs.net", role = "aut")
@@ -18,6 +18,7 @@ Imports:
AzureStor,
bsicons,
bslib,
+ cachem,
config,
dplyr,
forcats,
@@ -26,6 +27,7 @@ Imports:
ggrepel,
glue,
gt,
+ httr2,
jsonlite,
markdown,
purrr,
@@ -36,11 +38,16 @@ Imports:
shinycssloaders,
stringr,
tibble,
- tidyr,
- tidyselect,
- withr
+ tidyr
Remotes:
The-Strategy-Unit/azkit
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.3
+Suggests:
+ mockery,
+ testthat (>= 3.0.0),
+ tidyselect,
+ vdiffr,
+ withr
+Config/testthat/edition: 3
diff --git a/NAMESPACE b/NAMESPACE
index 8a933e6..3e4a64c 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,15 +1,18 @@
# Generated by roxygen2: do not edit by hand
-export(convert_md_to_html)
export(entable_encounters)
export(fetch_strategy_text)
export(generate_rates_baseline_data)
export(get_all_geo_data)
export(get_container)
export(get_golem_config)
+export(get_peers_lookup)
+export(get_rates_data)
+export(get_rates_trend_data)
export(isolate_provider_peers)
export(make_strategy_group_lookup)
export(md_file_to_html)
+export(md_string_to_html)
export(plot_age_sex_pyramid)
export(plot_nee)
export(plot_rates_box)
diff --git a/R/add_external_resources.R b/R/add_external_resources.R
index 5003741..aea7814 100644
--- a/R/add_external_resources.R
+++ b/R/add_external_resources.R
@@ -1,17 +1,3 @@
-#' Add External Resources to the Application
-#' This function is internally used to add external resources inside the Shiny
-#' application.
-#' @noRd
-add_external_resources <- function() {
- shiny::addResourcePath(
- "www",
- app_sys("app/www")
- )
- shiny::singleton(
- shiny::tags$head()
- )
-}
-
#' Access Files in the Current App
#' @param ... Character vectors, specifying subdirectory and file(s)
#' within your package. The default, none, returns the root of the app.
diff --git a/R/fct_azure.R b/R/fct_azure.R
deleted file mode 100644
index 63286b1..0000000
--- a/R/fct_azure.R
+++ /dev/null
@@ -1,32 +0,0 @@
-#' Get Azure Container
-#' @param tenant Character. Tenant ID.
-#' @param app_id Character. App ID.
-#' @param ep_uri Character. Endpoint URI.
-#' @param container_name Character. The name of the blob/storage container that
-#' hosts files you want to read.
-#' @return A blob_container/storage_container list.
-#' @export
-get_container <- function(
- ep_uri = Sys.getenv("AZ_STORAGE_EP"),
- container_name = Sys.getenv("AZ_CONTAINER_INPUTS")
-) {
- # if the app_id variable is empty, we assume that this is running on an Azure
- # VM, and then we will use Managed Identities for authentication.
-
- token <- tryCatch(
- {
- AzureAuth::get_managed_token("https://storage.azure.com/")
- },
- error = function(...) {
- AzureAuth::get_azure_token(
- resource = "https://storage.azure.com",
- tenant = "common",
- app = "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
- use_cache = TRUE # avoid browser-authorisation prompt
- )
- }
- )
- ep_uri |>
- AzureStor::blob_endpoint(token = token) |>
- AzureStor::storage_container(container_name)
-}
diff --git a/R/mod_plot_rates.R b/R/mod_plot_rates.R
index ecf5641..2336963 100644
--- a/R/mod_plot_rates.R
+++ b/R/mod_plot_rates.R
@@ -35,77 +35,45 @@ mod_plot_rates_server <- function(
) {
# load static data items
strategies_config <- get_golem_config("mitigators_config")
+ strategy_group_lookup <- make_strategy_group_lookup(strategies_config)
# return the shiny module
shiny::moduleServer(id, function(input, output, session) {
peers_lookup <- shiny::reactive({
- filename <- switch(
- selected_geography(),
- "nhp" = "nhp-peers.csv",
- "la" = "la-peers.csv"
- )
-
- shiny::req(filename)
-
- readr::read_csv(
- app_sys("app", "data", filename),
- col_types = "c"
- )
+ get_peers_lookup(selected_geography())
}) |>
shiny::bindCache(selected_geography())
# Prepare data ----
rates_data <- shiny::reactive({
- df <- inputs_data()[["rates"]]
-
- national <- df |>
- dplyr::filter(.data$provider == "national") |>
- dplyr::select(
- "strategy",
- "fyear",
- national_rate = "std_rate"
- )
-
- df |>
- dplyr::filter(!.data$provider %in% c("national", "unknown")) |>
- dplyr::inner_join(
- national,
- by = c("strategy", "fyear")
- ) |>
- dplyr::rename(rate = "std_rate") |>
- dplyr::select(-"crude_rate")
+ strategy <- shiny::req(selected_strategy())
+
+ get_rates_data(
+ inputs_data()[["rates"]],
+ strategy
+ )
})
rates_trend_data <- shiny::reactive({
- shiny::req(rates_data())
- shiny::req(selected_provider())
- shiny::req(selected_strategy())
+ df <- shiny::req(rates_data())
+ provider <- shiny::req(selected_provider())
- rates_data() |>
- dplyr::filter(
- .data$provider == selected_provider(),
- .data$strategy == selected_strategy()
- ) |>
- dplyr::arrange(.data$fyear)
+ get_rates_trend_data(df, provider)
})
rates_baseline_data <- shiny::reactive({
- shiny::req(rates_data())
- shiny::req(peers_lookup())
- shiny::req(selected_provider())
- shiny::req(selected_strategy())
-
- provider_peers <- isolate_provider_peers(
- selected_provider(),
- peers_lookup()
+ df <- shiny::req(rates_data())
+ peers_lookup <- shiny::req(peers_lookup())
+ provider <- shiny::req(selected_provider())
+ strategy <- shiny::req(selected_strategy())
+ year <- shiny::req(selected_year())
+
+ generate_rates_baseline_data(
+ df,
+ provider,
+ peers_lookup,
+ year
)
- rates_data() |>
- generate_rates_baseline_data(
- selected_provider(),
- provider_peers,
- selected_strategy(),
- selected_year()
- )
})
rates_funnel_calculations <- shiny::reactive({
@@ -133,15 +101,9 @@ mod_plot_rates_server <- function(
})
strategy_config <- shiny::reactive({
- shiny::req(strategies_config)
- shiny::req(selected_strategy())
-
- strategy_group_lookup <- strategies_config |> make_strategy_group_lookup()
-
- strategy_group <- strategy_group_lookup |>
- dplyr::filter(.data$strategy == selected_strategy()) |>
- dplyr::pull("group")
+ strategy <- shiny::req(selected_strategy())
+ strategy_group <- strategy_group_lookup[[strategy]]
strategies_config[[strategy_group]]
})
diff --git a/R/mod_select_geography.R b/R/mod_select_geography.R
index 2223aee..4784ded 100644
--- a/R/mod_select_geography.R
+++ b/R/mod_select_geography.R
@@ -6,7 +6,10 @@ mod_select_geography_ui <- function(id) {
shiny::selectInput(
ns("geography_select"),
"Filter by geography:",
- choices = NULL
+ choices = c(
+ "NHS provider trusts" = "nhp",
+ "Local authorities (LAs)" = "la"
+ )
)
}
@@ -14,20 +17,7 @@ mod_select_geography_ui <- function(id) {
#' @param id Internal parameter for `shiny`.
#' @noRd
mod_select_geography_server <- function(id) {
- geographies <- c(
- "NHS provider trusts" = "nhp",
- "Local authorities (LAs)" = "la"
- )
-
shiny::moduleServer(id, function(input, output, session) {
- shiny::observe({
- shiny::req(geographies)
- shiny::updateSelectInput(
- session,
- "geography_select",
- choices = geographies
- )
- })
shiny::reactive(input$geography_select)
})
}
diff --git a/R/mod_select_strategy.R b/R/mod_select_strategy.R
index 3e7b4f6..2a40777 100644
--- a/R/mod_select_strategy.R
+++ b/R/mod_select_strategy.R
@@ -21,47 +21,53 @@ mod_select_strategy_ui <- function(id) {
)
}
-#' Select Strategy Server
-#' @param id Internal parameter for `shiny`.
+#' Get TPMAs for the drop down menu
+#'
+#' Reads the mitigators.json file and extracts the name and category (IP/OP/AE).
+#'
+#' @return A named list of data frames, where the names are the categories (IP/OP/AE)
#' @noRd
-mod_select_strategy_server <- function(id) {
- # load static data items
+mod_select_strategy_get_strategies <- function() {
strategies <- jsonlite::read_json(
app_sys("app", "data", "mitigators.json"),
simplify_vector = TRUE
)
+ strategies |>
+ unlist() |>
+ tibble::enframe("strategy", "name") |>
+ dplyr::mutate(
+ category = stringr::str_extract(
+ .data$name,
+ "(?<= \\()(IP|OP|AE)(?=-(AA|EF))" # e.g. 'IP' in 'IP-AA-001'
+ ) |>
+ stringr::str_to_lower()
+ ) |>
+ dplyr::nest_by(.data$category) |>
+ tibble::deframe()
+}
+
+#' Select Strategy Server
+#' @param id Internal parameter for `shiny`.
+#' @noRd
+mod_select_strategy_server <- function(id) {
+ # load static data items
+ strategies <- mod_select_strategy_get_strategies()
+
# return the shiny module
shiny::moduleServer(id, function(input, output, session) {
- shiny::req(strategies)
-
- select_category <- shiny::reactive({
+ selected_category <- shiny::reactive({
shiny::req(input$strategy_category_select)
input$strategy_category_select
})
shiny::observe({
- shiny::req(select_category())
+ category <- shiny::req(selected_category())
- category_strategies <- strategies |>
- unlist() |>
- tibble::enframe("strategy", "name") |>
- dplyr::mutate(
- category = stringr::str_extract(
- .data$name,
- "(?<= \\()(IP|OP|AE)(?=-(AA|EF))" # e.g. 'IP' in 'IP-AA-001'
- ) |>
- stringr::str_to_lower()
- ) |>
- dplyr::filter(.data$category == select_category()) |>
- dplyr::select("strategy", "name") |>
+ strategy_choices <- strategies[[category]] |>
+ dplyr::select("name", "strategy") |>
tibble::deframe()
- strategy_choices <- purrr::set_names(
- names(category_strategies),
- category_strategies
- )
-
shiny::updateSelectInput(
session,
"strategy_select",
diff --git a/R/mod_show_strategy_text.R b/R/mod_show_strategy_text.R
index b0d76fe..a177711 100644
--- a/R/mod_show_strategy_text.R
+++ b/R/mod_show_strategy_text.R
@@ -13,6 +13,16 @@ mod_show_strategy_text_ui <- function(id) {
)
}
+#' Get Strategy descriptions Lookup
+#' @return a character vector of the strategy stubs.
+#' @noRd
+mod_show_strategy_text_get_descriptions_lookup <- function() {
+ jsonlite::read_json(
+ app_sys("app", "data", "descriptions.json"),
+ simplifyVector = TRUE
+ )
+}
+
#' Show Strategy Description Server
#' @param id Internal parameter for `shiny`.
#' @noRd
@@ -21,20 +31,26 @@ mod_show_strategy_text_server <- function(
selected_strategy
) {
# load static data items
- descriptions_lookup <- jsonlite::read_json(
- app_sys("app", "data", "descriptions.json"),
- simplifyVector = TRUE
- )
+ descriptions_lookup <- mod_show_strategy_text_get_descriptions_lookup()
# return the shiny module
shiny::moduleServer(id, function(input, output, session) {
- output$strategy_text <- shiny::renderText({
- shiny::req(selected_strategy())
- shiny::req(descriptions_lookup)
- selected_strategy() |>
- fetch_strategy_text(descriptions_lookup) |>
- convert_md_to_html()
+ strategy_stub <- shiny::reactive({
+ strategy <- shiny::req(selected_strategy())
+
+ is_stub <- stringr::str_detect(strategy, descriptions_lookup)
+ descriptions_lookup[is_stub]
+ })
+
+ strategy_text <- shiny::reactive({
+ s <- shiny::req(strategy_stub())
+ fetch_strategy_text(s)
}) |>
- shiny::bindCache(selected_strategy())
+ shiny::bindCache(strategy_stub())
+
+ output$strategy_text <- shiny::renderText({
+ t <- shiny::req(strategy_text())
+ md_string_to_html(t)
+ })
})
}
diff --git a/R/utils_data.R b/R/utils_data.R
index 95a9e3a..b5c39f7 100644
--- a/R/utils_data.R
+++ b/R/utils_data.R
@@ -69,21 +69,23 @@ get_golem_config <- function(
#' @param strategies_config List. Configuration for strategies from the
#' `"mitigators_config"` element of `golem-config.yml`, read in with
#' [get_golem_config].
-#' @return A data.frame.
+#' @return A lookup from strategy name to strategy group name.
#' @export
make_strategy_group_lookup <- function(strategies_config) {
strategies_config |>
purrr::map(\(strategy_group) {
- strategy_group |> purrr::pluck("strategy_subset") |> names()
+ strategy_group |>
+ purrr::pluck("strategy_subset") |>
+ names()
}) |>
- tibble::enframe(name = "group", value = "strategy") |>
- tidyr::unnest_longer("strategy")
+ purrr::imap(\(.x, .y) purrr::set_names(rep(.y, length(.x)), .x)) |>
+ purrr::flatten()
}
#' Read an Markdown File and Convert to HTML
#' @param ... Character vectors. Construct a path to a Markdown file (like
#' [file.path]).
-#' @return A data.frame.
+#' @return A shiny HTML object.
#' @export
md_file_to_html <- function(...) {
file <- app_sys(...)
@@ -92,3 +94,11 @@ md_file_to_html <- function(...) {
}
shiny::HTML(markdown::mark_html(file, output = FALSE, template = FALSE))
}
+
+#' Convert a Markdown String to HTML
+#' @param text Character string. Markdown text to convert to HTML.
+#' @return A shiny HTML object.
+#' @export
+md_string_to_html <- function(text) {
+ shiny::HTML(markdown::mark_html(text, output = FALSE, template = FALSE))
+}
diff --git a/R/utils_plot.R b/R/utils_plot.R
index be485a2..cf4b441 100644
--- a/R/utils_plot.R
+++ b/R/utils_plot.R
@@ -14,24 +14,20 @@ isolate_provider_peers <- function(provider, peers) {
#' Generate Rates Baseline Data
#' @param rates A data.frame. Rates data read from Azure.
#' @param provider Character. Provider code, e.g. `"RCF"`.
-#' @param peers Character. A vector of peers for given `provider`.
-#' @param strategy Character. Strategy variable name, e.g.
-#' `"alcohol_partially_attributable_acute"`.
+#' @param peers_lookup Dataframe. A lookup from a provider to its peers.
#' @param selected_year Integer. Baseline year in the form `202324`.
#' @return A data.frame.
#' @export
generate_rates_baseline_data <- function(
rates,
provider,
- peers,
- strategy,
+ peers_lookup,
selected_year
) {
+ peers <- isolate_provider_peers(provider, peers_lookup)
+
rates |>
- dplyr::filter(
- .data$strategy == .env$strategy,
- .data$fyear == .env$selected_year
- ) |>
+ dplyr::filter(.data$fyear == .env$selected_year) |>
dplyr::mutate(
is_peer = dplyr::case_when(
.data$provider == .env$provider ~ FALSE,
diff --git a/R/utils_plot_rates.R b/R/utils_plot_rates.R
new file mode 100644
index 0000000..55ddcfc
--- /dev/null
+++ b/R/utils_plot_rates.R
@@ -0,0 +1,73 @@
+#' Get Peers Lookup
+#'
+#' Reads the appropriate peers lookup file based on the selected geography.
+#'
+#' @param selected_geography Character. The selected geography, e.g. "nhp" or "la".
+#'
+#' @return A data.frame with provider codes and their corresponding peers.
+#'
+#' @export
+get_peers_lookup <- function(selected_geography) {
+ filename <- switch(
+ selected_geography,
+ "nhp" = "nhp-peers.csv",
+ "la" = "la-peers.csv"
+ )
+
+ shiny::req(filename)
+
+ readr::read_csv(
+ app_sys("app", "data", filename),
+ col_types = "c"
+ )
+}
+
+#' Get Rates Data
+#'
+#' This function prepares the data for the rates_data() reactive. It moves the
+#' national row to a separate column.
+#'
+#' @param df the rates data from inputs_data
+#' @param strategy Character. Strategy variable name to filter rows to
+#'
+#' @return Dataframe with national rates in a separate column
+#'
+#' @export
+get_rates_data <- function(df, strategy) {
+ df <- df |>
+ dplyr::filter(.data$strategy == .env$strategy)
+
+ national <- df |>
+ dplyr::filter(.data$provider == "national") |>
+ dplyr::select(
+ "strategy",
+ "fyear",
+ national_rate = "std_rate"
+ )
+
+ df |>
+ dplyr::filter(!.data$provider %in% c("national", "unknown")) |>
+ dplyr::inner_join(
+ national,
+ by = c("strategy", "fyear")
+ ) |>
+ dplyr::rename(rate = "std_rate") |>
+ dplyr::select(-"crude_rate")
+}
+
+#' Get Rates Trend Data
+#'
+#' Extracts the trend for a specific provider from the rates data
+#' (see [get_rates_data]).
+#'
+#' @param df A data.frame. Prepared by get_rates_data
+#' @param provider Character. Provider code, e.g. `"RCF"`.
+#'
+#' @return Dataframe filtered to the specific provider
+#'
+#' @export
+get_rates_trend_data <- function(df, provider) {
+ df |>
+ dplyr::filter(.data$provider == .env$provider) |>
+ dplyr::arrange(.data$fyear)
+}
diff --git a/R/utils_server.R b/R/utils_server.R
index 70487e3..7dbe51d 100644
--- a/R/utils_server.R
+++ b/R/utils_server.R
@@ -1,12 +1,41 @@
+#' Get Azure Container
+#' @param ep_uri Character. Endpoint URI.
+#' @param container_name Character. The name of the blob/storage container that
+#' hosts files you want to read.
+#' @return A blob_container/storage_container list.
+#' @export
+get_container <- function(
+ ep_uri = Sys.getenv("AZ_STORAGE_EP"),
+ container_name = Sys.getenv("AZ_CONTAINER_INPUTS")
+) {
+ # if the app_id variable is empty, we assume that this is running on an Azure
+ # VM, and then we will use Managed Identities for authentication.
+
+ token <- tryCatch(
+ {
+ AzureAuth::get_managed_token("https://storage.azure.com/")
+ },
+ error = function(...) {
+ AzureAuth::get_azure_token(
+ resource = "https://storage.azure.com",
+ tenant = "common",
+ app = "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
+ use_cache = TRUE # avoid browser-authorisation prompt
+ )
+ }
+ )
+ ep_uri |>
+ AzureStor::blob_endpoint(token = token) |>
+ AzureStor::storage_container(container_name)
+}
+
#' Read Inputs Datasets for All Geographies
#' @param geography Character. The geography level for which the user wants to
#' select a provider. Either "nhp" or "la".
#' @return A list. One element for each dataframes of data.
#' @export
get_all_geo_data <- function(geography) {
- inputs_container <- get_container(
- container_name = Sys.getenv("AZ_CONTAINER_INPUTS")
- )
+ inputs_container <- get_container()
data_types <- purrr::set_names(c(
"age_sex",
diff --git a/R/utils_show_strategy_text.R b/R/utils_show_strategy_text.R
index 2b1980e..0bf3835 100644
--- a/R/utils_show_strategy_text.R
+++ b/R/utils_show_strategy_text.R
@@ -1,36 +1,23 @@
#' Fetch Strategy Text from NHP Inputs
-#' @param strategy Character. Strategy variable name, e.g.
-#' `"alcohol_partially_attributable_acute"`.
-#' @param descriptions_lookup Character. The names of the available Markdown
-#' description files.
+#' @param strategy_stub Character. Strategy variable name stub, e.g.
+#' `"alcohol_partially_attributable"`.
#' @details Markdown files containing strategy descriptions are read from
#' [NHP Inputs](https://github.com/The-Strategy-Unit/nhp_inputs/).
#' @return Character.
#' @export
-fetch_strategy_text <- function(strategy, descriptions_lookup) {
- is_stub <- stringr::str_detect(strategy, descriptions_lookup)
- strategy_stub <- descriptions_lookup[is_stub]
-
- withr::with_tempfile("temp", {
- # nolint start object_usage_linter.
- utils::download.file(
- glue::glue(
- "https://raw.githubusercontent.com/The-Strategy-Unit/nhp_inputs/",
- "refs/heads/main/inst/app/strategy_text/{strategy_stub}.md"
- ),
- temp
- )
- cat("\n", file = temp, append = TRUE) # stop 'incomplete final line' warning
- paste(readLines(temp), collapse = "\n")
- # nolint end
- })
-}
-
-#' Convert Strategy Text from Markdown to HTML
-#' @param text Character. The Markdown text description for a strategy, as read
-#' by [fetch_strategy_text].
-#' @return HTML/character.
-#' @export
-convert_md_to_html <- function(text) {
- shiny::HTML(markdown::mark_html(text, output = FALSE, template = FALSE))
+fetch_strategy_text <- function(strategy_stub) {
+ httr2::request("https://raw.githubusercontent.com") |>
+ httr2::req_url_path(
+ "The-Strategy-Unit",
+ "nhp_inputs",
+ "refs",
+ "heads",
+ "main",
+ "inst",
+ "app",
+ "strategy_text",
+ glue::glue("{strategy_stub}.md")
+ ) |>
+ httr2::req_perform() |>
+ httr2::resp_body_string()
}
diff --git a/R/utils_table.R b/R/utils_table.R
index 38a1c06..5d6a1a0 100644
--- a/R/utils_table.R
+++ b/R/utils_table.R
@@ -80,12 +80,12 @@ prepare_diagnoses_data <- function(
}
diagnoses_prepared <- diagnoses_filtered |>
- dplyr::inner_join(
+ dplyr::left_join(
diagnoses_lookup,
by = c("diagnosis" = "diagnosis_code")
) |>
tidyr::replace_na(list(
- description = "Unknown/Invalid Diagnosis Code"
+ diagnosis_description = "Unknown/Invalid Diagnosis Code"
)) |>
dplyr::select("diagnosis_description", "n", "pcnt")
diff --git a/air.toml b/air.toml
new file mode 100644
index 0000000..e637396
--- /dev/null
+++ b/air.toml
@@ -0,0 +1,3 @@
+[format]
+line-width = 120
+skip = ["tribble"]
\ No newline at end of file
diff --git a/man/convert_md_to_html.Rd b/man/convert_md_to_html.Rd
deleted file mode 100644
index 2947999..0000000
--- a/man/convert_md_to_html.Rd
+++ /dev/null
@@ -1,18 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/utils_show_strategy_text.R
-\name{convert_md_to_html}
-\alias{convert_md_to_html}
-\title{Convert Strategy Text from Markdown to HTML}
-\usage{
-convert_md_to_html(text)
-}
-\arguments{
-\item{text}{Character. The Markdown text description for a strategy, as read
-by \link{fetch_strategy_text}.}
-}
-\value{
-HTML/character.
-}
-\description{
-Convert Strategy Text from Markdown to HTML
-}
diff --git a/man/fetch_strategy_text.Rd b/man/fetch_strategy_text.Rd
index 8d5e1f1..69e188a 100644
--- a/man/fetch_strategy_text.Rd
+++ b/man/fetch_strategy_text.Rd
@@ -4,14 +4,11 @@
\alias{fetch_strategy_text}
\title{Fetch Strategy Text from NHP Inputs}
\usage{
-fetch_strategy_text(strategy, descriptions_lookup)
+fetch_strategy_text(strategy_stub)
}
\arguments{
-\item{strategy}{Character. Strategy variable name, e.g.
-\code{"alcohol_partially_attributable_acute"}.}
-
-\item{descriptions_lookup}{Character. The names of the available Markdown
-description files.}
+\item{strategy_stub}{Character. Strategy variable name stub, e.g.
+\code{"alcohol_partially_attributable"}.}
}
\value{
Character.
diff --git a/man/generate_rates_baseline_data.Rd b/man/generate_rates_baseline_data.Rd
index 03b56e3..0a76d03 100644
--- a/man/generate_rates_baseline_data.Rd
+++ b/man/generate_rates_baseline_data.Rd
@@ -4,17 +4,14 @@
\alias{generate_rates_baseline_data}
\title{Generate Rates Baseline Data}
\usage{
-generate_rates_baseline_data(rates, provider, peers, strategy, selected_year)
+generate_rates_baseline_data(rates, provider, peers_lookup, selected_year)
}
\arguments{
\item{rates}{A data.frame. Rates data read from Azure.}
\item{provider}{Character. Provider code, e.g. \code{"RCF"}.}
-\item{peers}{Character. A vector of peers for given \code{provider}.}
-
-\item{strategy}{Character. Strategy variable name, e.g.
-\code{"alcohol_partially_attributable_acute"}.}
+\item{peers_lookup}{Dataframe. A lookup from a provider to its peers.}
\item{selected_year}{Integer. Baseline year in the form \code{202324}.}
}
diff --git a/man/get_container.Rd b/man/get_container.Rd
index fc470c7..8d98d38 100644
--- a/man/get_container.Rd
+++ b/man/get_container.Rd
@@ -1,5 +1,5 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/fct_azure.R
+% Please edit documentation in R/utils_server.R
\name{get_container}
\alias{get_container}
\title{Get Azure Container}
@@ -14,10 +14,6 @@ get_container(
\item{container_name}{Character. The name of the blob/storage container that
hosts files you want to read.}
-
-\item{tenant}{Character. Tenant ID.}
-
-\item{app_id}{Character. App ID.}
}
\value{
A blob_container/storage_container list.
diff --git a/man/get_peers_lookup.Rd b/man/get_peers_lookup.Rd
new file mode 100644
index 0000000..f74df54
--- /dev/null
+++ b/man/get_peers_lookup.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils_plot_rates.R
+\name{get_peers_lookup}
+\alias{get_peers_lookup}
+\title{Get Peers Lookup}
+\usage{
+get_peers_lookup(selected_geography)
+}
+\arguments{
+\item{selected_geography}{Character. The selected geography, e.g. "nhp" or "la".}
+}
+\value{
+A data.frame with provider codes and their corresponding peers.
+}
+\description{
+Reads the appropriate peers lookup file based on the selected geography.
+}
diff --git a/man/get_rates_data.Rd b/man/get_rates_data.Rd
new file mode 100644
index 0000000..4d9e127
--- /dev/null
+++ b/man/get_rates_data.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils_plot_rates.R
+\name{get_rates_data}
+\alias{get_rates_data}
+\title{Get Rates Data}
+\usage{
+get_rates_data(df, strategy)
+}
+\arguments{
+\item{df}{the rates data from inputs_data}
+
+\item{strategy}{Character. Strategy variable name to filter rows to}
+}
+\value{
+Dataframe with national rates in a separate column
+}
+\description{
+This function prepares the data for the rates_data() reactive. It moves the
+national row to a separate column.
+}
diff --git a/man/get_rates_trend_data.Rd b/man/get_rates_trend_data.Rd
new file mode 100644
index 0000000..b7cf153
--- /dev/null
+++ b/man/get_rates_trend_data.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils_plot_rates.R
+\name{get_rates_trend_data}
+\alias{get_rates_trend_data}
+\title{Get Rates Trend Data}
+\usage{
+get_rates_trend_data(df, provider)
+}
+\arguments{
+\item{df}{A data.frame. Prepared by get_rates_data}
+
+\item{provider}{Character. Provider code, e.g. \code{"RCF"}.}
+}
+\value{
+Dataframe filtered to the specific provider
+}
+\description{
+Extracts the trend for a specific provider from the rates data
+(see \link{get_rates_data}).
+}
diff --git a/man/make_strategy_group_lookup.Rd b/man/make_strategy_group_lookup.Rd
index 5edb755..111cfa2 100644
--- a/man/make_strategy_group_lookup.Rd
+++ b/man/make_strategy_group_lookup.Rd
@@ -12,7 +12,7 @@ make_strategy_group_lookup(strategies_config)
\link{get_golem_config}.}
}
\value{
-A data.frame.
+A lookup from strategy name to strategy group name.
}
\description{
Create a Simple Lookup of Strategies to Strategy Groups
diff --git a/man/md_file_to_html.Rd b/man/md_file_to_html.Rd
index 69bb163..d6e504f 100644
--- a/man/md_file_to_html.Rd
+++ b/man/md_file_to_html.Rd
@@ -11,7 +11,7 @@ md_file_to_html(...)
\link{file.path}).}
}
\value{
-A data.frame.
+A shiny HTML object.
}
\description{
Read an Markdown File and Convert to HTML
diff --git a/man/md_string_to_html.Rd b/man/md_string_to_html.Rd
new file mode 100644
index 0000000..5b05497
--- /dev/null
+++ b/man/md_string_to_html.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils_data.R
+\name{md_string_to_html}
+\alias{md_string_to_html}
+\title{Convert a Markdown String to HTML}
+\usage{
+md_string_to_html(text)
+}
+\arguments{
+\item{text}{Character string. Markdown text to convert to HTML.}
+}
+\value{
+A shiny HTML object.
+}
+\description{
+Convert a Markdown String to HTML
+}
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..99f4fcf
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,12 @@
+# This file is part of the standard setup for testthat.
+# It is recommended that you do not modify it.
+#
+# Where should you do additional test configuration?
+# Learn more about the roles of various files in:
+# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview
+# * https://testthat.r-lib.org/articles/special-files.html
+
+library(testthat)
+library(tpma.explorer)
+
+test_check("tpma.explorer")
diff --git a/tests/testthat/_snaps/app_ui.md b/tests/testthat/_snaps/app_ui.md
new file mode 100644
index 0000000..a45f2c0
--- /dev/null
+++ b/tests/testthat/_snaps/app_ui.md
@@ -0,0 +1,197 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
+
+
+
+
+
+
+
diff --git a/tests/testthat/_snaps/fct_plots.md b/tests/testthat/_snaps/fct_plots.md
new file mode 100644
index 0000000..b0492d0
--- /dev/null
+++ b/tests/testthat/_snaps/fct_plots.md
@@ -0,0 +1,44 @@
+# theme_rates (has_y_axis = TRUE)
+
+ Code
+ actual
+ Output
+ List of 3
+ $ legend.position : chr "none"
+ $ panel.background :
+ $ panel.grid.major.y:
+ ..@ colour : chr "#9d928a"
+ ..@ linewidth : NULL
+ ..@ linetype : chr "dotted"
+ ..@ lineend : NULL
+ ..@ linejoin : NULL
+ ..@ arrow : logi FALSE
+ ..@ arrow.fill : chr "#9d928a"
+ ..@ inherit.blank: logi FALSE
+ @ complete: logi FALSE
+ @ validate: logi TRUE
+
+# theme_rates (has_y_axis = FALSE)
+
+ Code
+ actual
+ Output
+ List of 7
+ $ legend.position : chr "none"
+ $ panel.background :
+ $ panel.grid.major.y:
+ ..@ colour : chr "#9d928a"
+ ..@ linewidth : NULL
+ ..@ linetype : chr "dotted"
+ ..@ lineend : NULL
+ ..@ linejoin : NULL
+ ..@ arrow : logi FALSE
+ ..@ arrow.fill : chr "#9d928a"
+ ..@ inherit.blank: logi FALSE
+ $ axis.title.y :
+ $ axis.text.y :
+ $ axis.ticks.x :
+ $ axis.ticks.y :
+ @ complete: logi FALSE
+ @ validate: logi TRUE
+
diff --git a/tests/testthat/_snaps/fct_plots/plot-age-sex-pyramid.svg b/tests/testthat/_snaps/fct_plots/plot-age-sex-pyramid.svg
new file mode 100644
index 0000000..d46e121
--- /dev/null
+++ b/tests/testthat/_snaps/fct_plots/plot-age-sex-pyramid.svg
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+0-4
+5-9
+10-14
+15-19
+20-24
+25-29
+30-34
+35-39
+40-44
+45-49
+50-54
+55-59
+60-64
+65-69
+70-74
+75-79
+80-84
+85-89
+90+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+20
+10
+0
+10
+20
+
+
+
+Females
+Males
+plot_age_sex_pyramid
+
+
diff --git a/tests/testthat/_snaps/fct_plots/plot-nee.svg b/tests/testthat/_snaps/fct_plots/plot-nee.svg
new file mode 100644
index 0000000..06afcab
--- /dev/null
+++ b/tests/testthat/_snaps/fct_plots/plot-nee.svg
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+0
+25
+50
+75
+100
+80% prediction interval
+plot_nee
+
+
diff --git a/tests/testthat/_snaps/fct_plots/plot-rates-box.svg b/tests/testthat/_snaps/fct_plots/plot-rates-box.svg
new file mode 100644
index 0000000..210403b
--- /dev/null
+++ b/tests/testthat/_snaps/fct_plots/plot-rates-box.svg
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+plot_rates_box
+
+
diff --git a/tests/testthat/_snaps/fct_plots/plot-rates-funnel.svg b/tests/testthat/_snaps/fct_plots/plot-rates-funnel.svg
new file mode 100644
index 0000000..0b58fa7
--- /dev/null
+++ b/tests/testthat/_snaps/fct_plots/plot-rates-funnel.svg
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+a
+b
+c
+d
+i
+
+
+0
+1,000
+2,000
+Population size
+plot_rates_funnel
+
+
diff --git a/tests/testthat/_snaps/fct_plots/plot-rates-trend.svg b/tests/testthat/_snaps/fct_plots/plot-rates-trend.svg
new file mode 100644
index 0000000..4e27879
--- /dev/null
+++ b/tests/testthat/_snaps/fct_plots/plot-rates-trend.svg
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+0.0%
+5.0%
+10.0%
+15.0%
+
+
+
+
+
+
+
+
+
+
+
+
+
+2015/16
+2016/17
+2017/18
+2018/19
+2019/20
+2020/21
+2021/22
+2022/23
+2023/24
+Financial year
+Rate per 100,000 population
+plot_rates_trend
+
+
diff --git a/tests/testthat/_snaps/fct_table.md b/tests/testthat/_snaps/fct_table.md
new file mode 100644
index 0000000..77d7d9f
--- /dev/null
+++ b/tests/testthat/_snaps/fct_table.md
@@ -0,0 +1,966 @@
+# entable_encounters (diagnoses)
+
+ Code
+ actual
+ Output
+
+
+
+
+
+ Diagnosis
+ Count of Activity (spells)
+ % of Total Activity
+
+
+
+ A
+ 100
+ 50.0%
+ B
+ 50
+ 25.0%
+ C
+ 25
+ 12.5%
+ Other
+ 25
+ 12.5%
+ Total
+ 200
+ —
+
+
+
+
+
+# entable_encounters (procedures)
+
+ Code
+ actual
+ Output
+
+
+
+
+
+ Procedures
+ Count of Activity (spells)
+ % of Total Activity
+
+
+
+ A
+ 100
+ 50.0%
+ B
+ 50
+ 25.0%
+ C
+ 25
+ 12.5%
+ Other
+ 25
+ 12.5%
+ Total
+ 200
+ —
+
+
+
+
+
diff --git a/tests/testthat/_snaps/mod_plot_age_sex_pyramid.md b/tests/testthat/_snaps/mod_plot_age_sex_pyramid.md
new file mode 100644
index 0000000..533b4f9
--- /dev/null
+++ b/tests/testthat/_snaps/mod_plot_age_sex_pyramid.md
@@ -0,0 +1,33 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_plot_nee.md b/tests/testthat/_snaps/mod_plot_nee.md
new file mode 100644
index 0000000..852453a
--- /dev/null
+++ b/tests/testthat/_snaps/mod_plot_nee.md
@@ -0,0 +1,35 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_plot_rates.md b/tests/testthat/_snaps/mod_plot_rates.md
new file mode 100644
index 0000000..563bd6f
--- /dev/null
+++ b/tests/testthat/_snaps/mod_plot_rates.md
@@ -0,0 +1,13 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
+ mod_plot_rates_trend
+ mod_plot_rates_funnel
+ mod_plot_rates_box
+
+
+
diff --git a/tests/testthat/_snaps/mod_plot_rates_box.md b/tests/testthat/_snaps/mod_plot_rates_box.md
new file mode 100644
index 0000000..7c707e2
--- /dev/null
+++ b/tests/testthat/_snaps/mod_plot_rates_box.md
@@ -0,0 +1,35 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_plot_rates_funnel.md b/tests/testthat/_snaps/mod_plot_rates_funnel.md
new file mode 100644
index 0000000..bd8abf8
--- /dev/null
+++ b/tests/testthat/_snaps/mod_plot_rates_funnel.md
@@ -0,0 +1,35 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_plot_rates_trend.md b/tests/testthat/_snaps/mod_plot_rates_trend.md
new file mode 100644
index 0000000..9c1911b
--- /dev/null
+++ b/tests/testthat/_snaps/mod_plot_rates_trend.md
@@ -0,0 +1,33 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_select_geography.md b/tests/testthat/_snaps/mod_select_geography.md
new file mode 100644
index 0000000..221ffd4
--- /dev/null
+++ b/tests/testthat/_snaps/mod_select_geography.md
@@ -0,0 +1,14 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_select_provider.md b/tests/testthat/_snaps/mod_select_provider.md
new file mode 100644
index 0000000..3d8e8df
--- /dev/null
+++ b/tests/testthat/_snaps/mod_select_provider.md
@@ -0,0 +1,13 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_select_strategy.md b/tests/testthat/_snaps/mod_select_strategy.md
new file mode 100644
index 0000000..453b61f
--- /dev/null
+++ b/tests/testthat/_snaps/mod_select_strategy.md
@@ -0,0 +1,22 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
+
diff --git a/tests/testthat/_snaps/mod_show_strategy_text.md b/tests/testthat/_snaps/mod_show_strategy_text.md
new file mode 100644
index 0000000..5d47839
--- /dev/null
+++ b/tests/testthat/_snaps/mod_show_strategy_text.md
@@ -0,0 +1,21 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_table_diagnoses.md b/tests/testthat/_snaps/mod_table_diagnoses.md
new file mode 100644
index 0000000..cbc40ca
--- /dev/null
+++ b/tests/testthat/_snaps/mod_table_diagnoses.md
@@ -0,0 +1,35 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/_snaps/mod_table_procedures.md b/tests/testthat/_snaps/mod_table_procedures.md
new file mode 100644
index 0000000..47c8ba9
--- /dev/null
+++ b/tests/testthat/_snaps/mod_table_procedures.md
@@ -0,0 +1,35 @@
+# ui
+
+ Code
+ ui
+ Output
+
+
diff --git a/tests/testthat/helper-app_server.R b/tests/testthat/helper-app_server.R
new file mode 100644
index 0000000..ae8104e
--- /dev/null
+++ b/tests/testthat/helper-app_server.R
@@ -0,0 +1,20 @@
+setup_app_server_tests <- function(.env = parent.frame()) {
+ shiny::shinyOptions(cache = cachem::cache_mem())
+
+ mocks <- list(
+ mod_select_geography_server = mockery::mock(shiny::reactiveVal("nhp")),
+ mod_select_provider_server = mockery::mock(shiny::reactiveVal("ABC")),
+ mod_select_strategy_server = mockery::mock(shiny::reactiveVal("strategy")),
+ get_all_geo_data = mockery::mock(inputs_data_sample, cycle = TRUE),
+ mod_show_strategy_text_server = mockery::mock(),
+ mod_plot_rates_server = mockery::mock(),
+ mod_table_procedures_server = mockery::mock(),
+ mod_table_diagnoses_server = mockery::mock(),
+ mod_plot_age_sex_pyramid_server = mockery::mock(),
+ mod_plot_nee_server = mockery::mock()
+ )
+
+ do.call(testthat::local_mocked_bindings, c(mocks, .env = .env))
+
+ mocks
+}
diff --git a/tests/testthat/helper-app_ui.R b/tests/testthat/helper-app_ui.R
new file mode 100644
index 0000000..e9d69b2
--- /dev/null
+++ b/tests/testthat/helper-app_ui.R
@@ -0,0 +1,24 @@
+setup_app_ui_tests <- function(.env = parent.frame()) {
+ setup_ui_test(.env)
+
+ mocks <- list(
+ "mod_show_strategy_text_ui" = mockery::mock("mod_show_strategy_text"),
+ "mod_plot_rates_ui" = mockery::mock("mod_plot_rates"),
+ "mod_table_procedures_ui" = mockery::mock("mod_table_procedures"),
+ "mod_table_diagnoses_ui" = mockery::mock("mod_table_diagnoses"),
+ "mod_plot_age_sex_pyramid_ui" = mockery::mock("mod_plot_age_sex_pyramid"),
+ "mod_plot_nee_ui" = mockery::mock("mod_plot_nee"),
+ "mod_select_geography_ui" = mockery::mock("mod_select_geography"),
+ "mod_select_provider_ui" = mockery::mock("mod_select_provider"),
+ "mod_select_strategy_ui" = mockery::mock("mod_select_strategy")
+ )
+
+ withr::local_envvar(
+ .local_envir = .env,
+ "FEEDBACK_FORM_URL" = "https://example.com/"
+ )
+
+ do.call(testthat::local_mocked_bindings, c(mocks, .env = .env))
+
+ mocks
+}
diff --git a/tests/testthat/helper-mod_plot_rates.R b/tests/testthat/helper-mod_plot_rates.R
new file mode 100644
index 0000000..fc7db37
--- /dev/null
+++ b/tests/testthat/helper-mod_plot_rates.R
@@ -0,0 +1,31 @@
+setup_mod_plot_rates_ui_tests <- function(.env = parent.frame()) {
+ setup_ui_test(.env)
+
+ mocks <- list(
+ "mod_plot_rates_trend_ui" = mockery::mock("mod_plot_rates_trend"),
+ "mod_plot_rates_funnel_ui" = mockery::mock("mod_plot_rates_funnel"),
+ "mod_plot_rates_box_ui" = mockery::mock("mod_plot_rates_box")
+ )
+
+ do.call(testthat::local_mocked_bindings, c(mocks, .env = .env))
+
+ mocks
+}
+
+server_mod_plot_rates_server_tests <- function(.env = parent.frame()) {
+ mocks <- list(
+ "mod_plot_rates_trend_server" = mockery::mock(),
+ "mod_plot_rates_funnel_server" = mockery::mock(),
+ "mod_plot_rates_box_server" = mockery::mock(),
+ "get_golem_config" = mockery::mock("strategies_config"),
+ "make_strategy_group_lookup" = mockery::mock("strategy_group_lookup"),
+ "get_peers_lookup" = mockery::mock("peers_lookup", cycle = TRUE),
+ "get_rates_data" = mockery::mock("rates"),
+ "get_rates_trend_data" = mockery::mock("rates_trend"),
+ "generate_rates_baseline_data" = mockery::mock("rates_baseline"),
+ "uprime_calculations" = mockery::mock("uprime")
+ )
+ do.call(testthat::local_mocked_bindings, c(mocks, .env = .env))
+
+ mocks
+}
diff --git a/tests/testthat/helper-mod_select_strategy.R b/tests/testthat/helper-mod_select_strategy.R
new file mode 100644
index 0000000..ca4623c
--- /dev/null
+++ b/tests/testthat/helper-mod_select_strategy.R
@@ -0,0 +1,24 @@
+setup_mod_select_strategy_server <- function(.env = parent.frame()) {
+ strategies <- list(
+ ip = tibble::tibble(
+ name = c("Strategy A", "Strategy B"),
+ strategy = c("a", "b")
+ ),
+ op = tibble::tibble(
+ name = c("Strategy C", "Strategy D"),
+ strategy = c("c", "d")
+ ),
+ ae = tibble::tibble(
+ name = c("Strategy E", "Strategy F"),
+ strategy = c("e", "f")
+ )
+ )
+ m <- mockery::mock(strategies)
+
+ testthat::local_mocked_bindings(
+ "mod_select_strategy_get_strategies" = m,
+ .env = .env
+ )
+
+ m
+}
diff --git a/tests/testthat/helper-sample_data.R b/tests/testthat/helper-sample_data.R
new file mode 100644
index 0000000..9eb2388
--- /dev/null
+++ b/tests/testthat/helper-sample_data.R
@@ -0,0 +1,33 @@
+# nolint start
+inputs_data_sample <- list(
+ "age_sex" = tibble::tribble(
+ ~provider , ~strategy , ~fyear ,
+ "R00" , "a" , 1 ,
+ "R00" , "a" , 2 ,
+ "R00" , "b" , 1 ,
+ "R00" , "b" , 2 ,
+ "R01" , "a" , 1 ,
+ "R01" , "a" , 2 ,
+ "R01" , "b" , 1 ,
+ "R01" , "b" , 2 ,
+ ),
+ "diagnoses" = "diagnoses",
+ "procedures" = "procedures",
+ "rates" = tibble::tribble(
+ ~fyear , ~provider , ~strategy , ~crude_rate , ~std_rate ,
+ 202223 , "a" , "Strategy A" , 1 , 2 ,
+ 202223 , "b" , "Strategy A" , 3 , 4 ,
+ 202223 , "national" , "Strategy A" , 5 , 6 ,
+ 202324 , "A" , "Strategy A" , 7 , 8 ,
+ 202324 , "B" , "Strategy A" , 9 , 10 ,
+ 202324 , "national" , "Strategy A" , 10 , 12 ,
+
+ 202223 , "a" , "Strategy B" , 2 , 1 ,
+ 202223 , "b" , "Strategy B" , 4 , 3 ,
+ 202223 , "national" , "Strategy B" , 6 , 5 ,
+ 202324 , "A" , "Strategy B" , 8 , 7 ,
+ 202324 , "B" , "Strategy B" , 10 , 9 ,
+ 202324 , "national" , "Strategy B" , 12 , 11
+ )
+)
+# nolint end
diff --git a/tests/testthat/helper-ui.R b/tests/testthat/helper-ui.R
new file mode 100644
index 0000000..4e0c76c
--- /dev/null
+++ b/tests/testthat/helper-ui.R
@@ -0,0 +1,7 @@
+setup_ui_test <- function(.env = parent.frame()) {
+ testthat::local_mocked_bindings(
+ "p_randomInt" = \(...) "X",
+ .package = "shiny",
+ .env = .env
+ )
+}
diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R
new file mode 100644
index 0000000..c284c35
--- /dev/null
+++ b/tests/testthat/setup.R
@@ -0,0 +1,9 @@
+options(
+ shiny.launch.browser = FALSE,
+ shiny.testmode = TRUE
+)
+
+Sys.setenv(TESTTHAT_PARALLEL = "false")
+
+library(mockery)
+library(shiny)
diff --git a/tests/testthat/test-add_external_resources.R b/tests/testthat/test-add_external_resources.R
new file mode 100644
index 0000000..f8494d9
--- /dev/null
+++ b/tests/testthat/test-add_external_resources.R
@@ -0,0 +1,13 @@
+test_that("app_sys", {
+ # arrange
+ m <- mock("path/to/file")
+ stub(app_sys, "system.file", m)
+
+ # act
+ actual <- app_sys("subdir", "file.txt")
+
+ # assert
+ expect_equal(actual, "path/to/file")
+ expect_called(m, 1)
+ expect_args(m, 1, "subdir", "file.txt", package = "tpma.explorer")
+})
diff --git a/tests/testthat/test-app_server.R b/tests/testthat/test-app_server.R
new file mode 100644
index 0000000..3fb6835
--- /dev/null
+++ b/tests/testthat/test-app_server.R
@@ -0,0 +1,270 @@
+test_that("mod_select_geography", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_select_geography_server, 1)
+ expect_args(mocks$mod_select_geography_server, 1, "mod_select_geography")
+ }
+ )
+})
+
+test_that("mod_select_provider", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_select_provider_server, 1)
+ expect_args(
+ mocks$mod_select_provider_server,
+ 1,
+ "mod_select_provider",
+ selected_geography
+ )
+ }
+ )
+})
+
+test_that("mod_select_strategy", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_select_strategy_server, 1)
+ expect_args(
+ mocks$mod_select_strategy_server,
+ 1,
+ "mod_select_strategy"
+ )
+ }
+ )
+})
+
+test_that("selected_year (env var not set)", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ withr::local_envvar("BASELINE_YEAR" = "")
+ expect_equal(selected_year(), 202324)
+ expect_called(mocks$get_all_geo_data, 1)
+ expect_args(mocks$get_all_geo_data, 1, "nhp")
+ }
+ )
+})
+
+
+test_that("selected_year (env var set)", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ withr::local_envvar("BASELINE_YEAR" = "3")
+ expect_equal(selected_year(), 3)
+ expect_called(mocks$get_all_geo_data, 0)
+ }
+ )
+})
+
+test_that("inputs_data", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ withr::local_envvar("BASELINE_YEAR" = "")
+ expect_equal(inputs_data(), inputs_data_sample)
+ expect_called(mocks$get_all_geo_data, 1)
+ expect_args(mocks$get_all_geo_data, 1, "nhp")
+
+ # test caching - not changing geography, so shouldn't call again
+ inputs_data()
+ expect_called(mocks$get_all_geo_data, 1)
+
+ # test caching - changing geography, so should call again
+ selected_geography("la")
+ inputs_data()
+ expect_called(mocks$get_all_geo_data, 2)
+ expect_args(mocks$get_all_geo_data, 2, "la")
+
+ # test caching - changing geography back, so shouldn't call again
+ selected_geography("nhp")
+ inputs_data()
+ expect_called(mocks$get_all_geo_data, 2)
+ }
+ )
+})
+
+test_that("sidebar accordion opens", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ m <- mock()
+ mockery::stub(app_server, "bslib::accordion_panel_open", m)
+ # act
+
+ shiny::testServer(
+ app_server,
+ {
+ session$flushReact() # trigger observer
+ expect_called(m, 1)
+ expect_args(m, 1, id = "sidebar_accordion", values = TRUE)
+ }
+ )
+})
+
+test_that("mod_show_strategy_text_server", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_show_strategy_text_server, 1)
+ expect_args(
+ mocks$mod_show_strategy_text_server,
+ 1,
+ "mod_show_strategy_text",
+ selected_strategy
+ )
+ }
+ )
+})
+
+test_that("mod_plot_rates_server", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_plot_rates_server, 1)
+ expect_args(
+ mocks$mod_plot_rates_server,
+ 1,
+ "mod_plot_rates",
+ inputs_data,
+ selected_geography,
+ selected_provider,
+ selected_strategy,
+ selected_year
+ )
+ }
+ )
+})
+
+test_that("mod_table_procedures_server", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_table_procedures_server, 1)
+ expect_args(
+ mocks$mod_table_procedures_server,
+ 1,
+ "mod_table_procedures",
+ inputs_data,
+ selected_provider,
+ selected_strategy,
+ selected_year
+ )
+ }
+ )
+})
+
+test_that("mod_table_diagnoses_server", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_table_diagnoses_server, 1)
+ expect_args(
+ mocks$mod_table_diagnoses_server,
+ 1,
+ "mod_table_diagnoses",
+ inputs_data,
+ selected_provider,
+ selected_strategy,
+ selected_year
+ )
+ }
+ )
+})
+
+test_that("mod_plot_age_sex_pyramid_server", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_plot_age_sex_pyramid_server, 1)
+ expect_args(
+ mocks$mod_plot_age_sex_pyramid_server,
+ 1,
+ "mod_plot_age_sex_pyramid",
+ inputs_data,
+ selected_provider,
+ selected_strategy,
+ selected_year
+ )
+ }
+ )
+})
+
+test_that("mod_plot_nee_server", {
+ # arrange
+ mocks <- setup_app_server_tests()
+
+ # act
+ shiny::testServer(
+ app_server,
+ {
+ # assert
+ expect_called(mocks$mod_plot_nee_server, 1)
+ expect_args(
+ mocks$mod_plot_nee_server,
+ 1,
+ "mod_plot_nee",
+ selected_strategy
+ )
+ }
+ )
+})
diff --git a/tests/testthat/test-app_ui.R b/tests/testthat/test-app_ui.R
new file mode 100644
index 0000000..4f0d78e
--- /dev/null
+++ b/tests/testthat/test-app_ui.R
@@ -0,0 +1,120 @@
+test_that("ui", {
+ # arrange
+ setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_snapshot(ui)
+})
+
+test_that("calls mod_show_strategy_text_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_show_strategy_text_ui, 1)
+ expect_args(mocks$mod_show_strategy_text_ui, 1, "mod_show_strategy_text")
+})
+
+test_that("calls mod_plot_rates_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_plot_rates_ui, 1)
+ expect_args(mocks$mod_plot_rates_ui, 1, "mod_plot_rates")
+})
+
+test_that("calls mod_table_procedures_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_table_procedures_ui, 1)
+ expect_args(mocks$mod_table_procedures_ui, 1, "mod_table_procedures")
+})
+
+
+test_that("calls mod_table_diagnoses_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_table_diagnoses_ui, 1)
+ expect_args(mocks$mod_table_diagnoses_ui, 1, "mod_table_diagnoses")
+})
+
+
+test_that("calls mod_plot_age_sex_pyramid", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_plot_age_sex_pyramid_ui, 1)
+ expect_args(mocks$mod_plot_age_sex_pyramid_ui, 1, "mod_plot_age_sex_pyramid")
+})
+
+test_that("calls mod_plot_nee_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_plot_nee_ui, 1)
+ expect_args(mocks$mod_plot_nee_ui, 1, "mod_plot_nee")
+})
+
+test_that("calls mod_select_geography_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_select_geography_ui, 1)
+ expect_args(mocks$mod_select_geography_ui, 1, "mod_select_geography")
+})
+
+test_that("calls mod_select_provider_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_select_provider_ui, 1)
+ expect_args(mocks$mod_select_provider_ui, 1, "mod_select_provider")
+})
+
+test_that("calls mod_select_strategy_ui", {
+ # arrange
+ mocks <- setup_app_ui_tests()
+
+ # act
+ ui <- app_ui("request")
+
+ # assert
+ expect_called(mocks$mod_select_strategy_ui, 1)
+ expect_args(mocks$mod_select_strategy_ui, 1, "mod_select_strategy")
+})
diff --git a/tests/testthat/test-fct_plots.R b/tests/testthat/test-fct_plots.R
new file mode 100644
index 0000000..d8f11a6
--- /dev/null
+++ b/tests/testthat/test-fct_plots.R
@@ -0,0 +1,200 @@
+test_that("plot_rates_trend", {
+ # arrange
+ # nolint start
+ rates_trend_data <- tibble::tribble(
+ ~fyear , ~rate ,
+ 201516 , 0.10 ,
+ 201617 , 0.12 ,
+ 201718 , 0.11 ,
+ 201819 , 0.09 ,
+ 201920 , 0.08 ,
+ 202021 , 0.07 ,
+ 202122 , 0.06 ,
+ 202223 , 0.05 ,
+ 202324 , 0.04
+ )
+ # nolint end
+
+ selected_year <- 202324
+ y_axis_limits <- c(0, 0.16)
+ x_axis_title <- "Financial year"
+ y_axis_title <- "Rate per 100,000 population"
+ y_labels <- scales::label_percent(accuracy = 0.1)
+
+ # act
+ actual <- plot_rates_trend(
+ rates_trend_data,
+ selected_year,
+ y_axis_limits,
+ x_axis_title,
+ y_axis_title,
+ y_labels
+ )
+
+ # assert
+ vdiffr::expect_doppelganger("plot_rates_trend", actual)
+})
+
+test_that("plot_rates_funnel", {
+ # arrange
+ set.seed(1)
+ # nolint start
+ rates_funnel_data <- tibble::tribble(
+ ~rate , ~denominator , ~national_rate , ~is_peer , ~provider ,
+ 0.10 , 1000 , 0.08 , TRUE , "a" ,
+ 0.12 , 1200 , 0.08 , TRUE , "b" ,
+ 0.11 , 1400 , 0.08 , FALSE , "c" ,
+ 0.09 , 1600 , 0.08 , TRUE , "d" ,
+ 0.08 , 1800 , 0.08 , NA , "e" ,
+ 0.07 , 2000 , 0.08 , NA , "f" ,
+ 0.06 , 2200 , 0.08 , NA , "g" ,
+ 0.05 , 2400 , 0.08 , NA , "h" ,
+ 0.04 , 2600 , 0.08 , TRUE , "i"
+ )
+ # nolint end
+
+ funnel_calculations <- uprime_calculations(rates_funnel_data)
+ y_axis_limits <- c(0, 0.16)
+ x_axis_title <- "Population size"
+
+ # act
+ actual <- plot_rates_funnel(
+ rates_funnel_data,
+ funnel_calculations,
+ y_axis_limits,
+ x_axis_title
+ )
+
+ # assert
+ vdiffr::expect_doppelganger("plot_rates_funnel", actual)
+})
+
+test_that("plot_rates_box", {
+ # arrange
+ # nolint start
+ rates_box_data <- tibble::tribble(
+ ~rate , ~denominator , ~national_rate , ~is_peer , ~provider ,
+ 0.10 , 1000 , 0.08 , TRUE , "a" ,
+ 0.12 , 1200 , 0.08 , TRUE , "b" ,
+ 0.11 , 1400 , 0.08 , FALSE , "c" ,
+ 0.09 , 1600 , 0.08 , TRUE , "d" ,
+ 0.08 , 1800 , 0.08 , NA , "e" ,
+ 0.07 , 2000 , 0.08 , NA , "f" ,
+ 0.06 , 2200 , 0.08 , NA , "g" ,
+ 0.05 , 2400 , 0.08 , NA , "h" ,
+ 0.04 , 2600 , 0.08 , TRUE , "i"
+ )
+ # nolint end
+
+ y_axis_limits <- c(0, 0.16)
+
+ # act
+ actual <- plot_rates_box(
+ rates_box_data,
+ y_axis_limits
+ )
+
+ # assert
+ vdiffr::expect_doppelganger("plot_rates_box", actual)
+})
+
+test_that("theme_rates (has_y_axis = TRUE)", {
+ actual <- theme_rates(TRUE)
+ expect_snapshot(actual)
+})
+
+test_that("theme_rates (has_y_axis = FALSE)", {
+ actual <- theme_rates(FALSE)
+ expect_snapshot(actual)
+})
+
+test_that("plot_age_sex_pyramid", {
+ # arrange
+ age_sex_data <- tibble::tribble(
+ ~age_group, ~sex, ~n,
+ "0-4", "Males", -1,
+ "5-9", "Males", -2,
+ "10-14", "Males", -3,
+ "15-19", "Males", -4,
+ "20-24", "Males", -5,
+ "25-29", "Males", -6,
+ "30-34", "Males", -7,
+ "35-39", "Males", -8,
+ "40-44", "Males", -9,
+ "45-49", "Males", -10,
+ "50-54", "Males", -11,
+ "55-59", "Males", -12,
+ "60-64", "Males", -13,
+ "65-69", "Males", -14,
+ "70-74", "Males", -15,
+ "75-79", "Males", -16,
+ "80-84", "Males", -17,
+ "85-89", "Males", -18,
+ "90+", "Males", -19,
+ "0-4", "Females", 19,
+ "5-9", "Females", 18,
+ "10-14", "Females", 17,
+ "15-19", "Females", 16,
+ "20-24", "Females", 15,
+ "25-29", "Females", 14,
+ "30-34", "Females", 13,
+ "35-39", "Females", 12,
+ "40-44", "Females", 11,
+ "45-49", "Females", 10,
+ "50-54", "Females", 9,
+ "55-59", "Females", 8,
+ "60-64", "Females", 7,
+ "65-69", "Females", 6,
+ "70-74", "Females", 5,
+ "75-79", "Females", 4,
+ "80-84", "Females", 3,
+ "85-89", "Females", 2,
+ "90+", "Females", 1
+ ) |>
+ dplyr::mutate(
+ age_group = factor(
+ age_group,
+ levels = c(
+ "0-4",
+ "5-9",
+ "10-14",
+ "15-19",
+ "20-24",
+ "25-29",
+ "30-34",
+ "35-39",
+ "40-44",
+ "45-49",
+ "50-54",
+ "55-59",
+ "60-64",
+ "65-69",
+ "70-74",
+ "75-79",
+ "80-84",
+ "85-89",
+ "90+"
+ )
+ )
+ )
+
+ # act
+ actual <- plot_age_sex_pyramid(age_sex_data)
+
+ # assert
+ vdiffr::expect_doppelganger("plot_age_sex_pyramid", actual)
+})
+
+test_that("plot_nee", {
+ # arrange
+ nee_data <- tibble::tribble(
+ ~strategy, ~percentile10, ~mean, ~percentile90,
+ "Strategy A", 20, 50, 80
+ )
+
+ # act
+ actual <- plot_nee(nee_data)
+
+ # assert
+ vdiffr::expect_doppelganger("plot_nee", actual)
+})
diff --git a/tests/testthat/test-fct_table.R b/tests/testthat/test-fct_table.R
new file mode 100644
index 0000000..dfc0512
--- /dev/null
+++ b/tests/testthat/test-fct_table.R
@@ -0,0 +1,35 @@
+test_that("entable_encounters (diagnoses)", {
+ # arrange
+ set.seed(1)
+ df <- tibble::tribble(
+ ~diagnosis_description, ~n, ~pcnt,
+ "A", 100, 0.5,
+ "B", 50, 0.25,
+ "C", 25, 0.125,
+ "Other", 25, 0.125
+ )
+
+ # act
+ actual <- entable_encounters(df)
+
+ # assert
+ expect_snapshot(actual)
+})
+
+test_that("entable_encounters (procedures)", {
+ # arrange
+ set.seed(1)
+ df <- tibble::tribble(
+ ~procedures_description, ~n, ~pcnt,
+ "A", 100, 0.5,
+ "B", 50, 0.25,
+ "C", 25, 0.125,
+ "Other", 25, 0.125
+ )
+
+ # act
+ actual <- entable_encounters(df)
+
+ # assert
+ expect_snapshot(actual)
+})
diff --git a/tests/testthat/test-mod_plot_age_sex_pyramid.R b/tests/testthat/test-mod_plot_age_sex_pyramid.R
new file mode 100644
index 0000000..c15ea9f
--- /dev/null
+++ b/tests/testthat/test-mod_plot_age_sex_pyramid.R
@@ -0,0 +1,99 @@
+# nolint start
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_plot_age_sex_pyramid_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("age_sex_data", {
+ # arrange
+ m <- mock("age_sex_data")
+ testthat::local_mocked_bindings("prepare_age_sex_data" = m)
+ expected <- tibble::tibble(provider = "R00", strategy = "a", fyear = 2)
+
+ # act
+ shiny::testServer(
+ mod_plot_age_sex_pyramid_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("a"),
+ selected_year = reactiveVal(2)
+ ),
+ {
+ actual <- age_sex_data()
+
+ # assert
+ expect_equal(actual, "age_sex_data")
+
+ expect_called(m, 1)
+ expect_args(m, 1, expected)
+ }
+ )
+})
+
+test_that("age_sex_pyramid (no rows)", {
+ # arrange
+ testthat::local_mocked_bindings("prepare_age_sex_data" = \(...) {
+ tibble::tibble()
+ })
+
+ # act
+ shiny::testServer(
+ mod_plot_age_sex_pyramid_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("a"),
+ selected_year = reactiveVal(2)
+ ),
+ {
+ # assert
+ expect_error(
+ output$age_sex_pyramid,
+ "No data available for these selections."
+ )
+ }
+ )
+})
+
+
+test_that("age_sex_pyramid (with rows)", {
+ # arrange
+ m <- mock("plot")
+ testthat::local_mocked_bindings(
+ "prepare_age_sex_data" = identity,
+ "plot_age_sex_pyramid" = m
+ )
+
+ # replace renderPlot to avoid actual plotting, replace with renderText so we
+ # can simply check the output
+ testthat::local_mocked_bindings(
+ "renderPlot" = shiny::renderText,
+ .package = "shiny"
+ )
+
+ expected <- tibble::tibble(provider = "R00", strategy = "a", fyear = 2)
+
+ # act
+ shiny::testServer(
+ mod_plot_age_sex_pyramid_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("a"),
+ selected_year = reactiveVal(2)
+ ),
+ {
+ actual <- output$age_sex_pyramid
+
+ # assert
+ expect_equal(actual, "plot")
+
+ expect_called(m, 1)
+ expect_args(m, 1, expected)
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_plot_nee.R b/tests/testthat/test-mod_plot_nee.R
new file mode 100644
index 0000000..d33719f
--- /dev/null
+++ b/tests/testthat/test-mod_plot_nee.R
@@ -0,0 +1,83 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_plot_nee_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("selected_nee_data", {
+ # arrange
+ expected <- tibble::tibble(
+ param_name = "smoking",
+ mean = 84.3,
+ percentile10 = 98.9,
+ percentile90 = 60.0
+ )
+
+ # act
+ shiny::testServer(
+ mod_plot_nee_server,
+ args = list(
+ selected_strategy = reactiveVal("smoking")
+ ),
+ {
+ actual <- selected_nee_data()
+
+ # assert
+ expect_equal(actual, expected, tolerance = 1e-1)
+ }
+ )
+})
+
+test_that("nee (no rows)", {
+ # arrange
+ testthat::local_mocked_bindings("prepare_age_sex_data" = \(...) {
+ tibble::tibble()
+ })
+
+ # act
+ shiny::testServer(
+ mod_plot_nee_server,
+ args = list(
+ selected_strategy = reactiveVal("X")
+ ),
+ {
+ # assert
+ expect_error(output$nee)
+ }
+ )
+})
+
+
+test_that("nee (with rows)", {
+ # arrange
+ m <- mock("plot")
+ testthat::local_mocked_bindings(
+ "plot_nee" = m
+ )
+
+ # replace renderPlot to avoid actual plotting, replace with renderText so we
+ # can simply check the output
+ testthat::local_mocked_bindings(
+ "renderPlot" = shiny::renderText,
+ .package = "shiny"
+ )
+
+ # act
+ shiny::testServer(
+ mod_plot_nee_server,
+ args = list(
+ selected_strategy = reactiveVal("smoking")
+ ),
+ {
+ actual <- output$nee
+
+ # assert
+ expect_equal(actual, "plot")
+
+ expect_called(m, 1)
+ expect_call(m, 1, plot_nee(df))
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_plot_rates.R b/tests/testthat/test-mod_plot_rates.R
new file mode 100644
index 0000000..e5dc136
--- /dev/null
+++ b/tests/testthat/test-mod_plot_rates.R
@@ -0,0 +1,409 @@
+test_that("ui", {
+ # arrange
+ setup_mod_plot_rates_ui_tests()
+
+ # act
+ ui <- mod_plot_rates_ui("test")
+
+ # assert
+ expect_snapshot(ui)
+})
+
+test_that("ui calls mod_plot_rates_trend_ui", {
+ # arrange
+ mocks <- setup_mod_plot_rates_ui_tests()
+
+ # act
+ ui <- mod_plot_rates_ui("test")
+
+ # assert
+ expect_called(mocks$mod_plot_rates_trend_ui, 1)
+ expect_args(mocks$mod_plot_rates_trend_ui, 1, "test-mod_plot_rates_trend")
+})
+
+test_that("ui calls mod_plot_rates_funnel_ui", {
+ # arrange
+ mocks <- setup_mod_plot_rates_ui_tests()
+
+ # act
+ ui <- mod_plot_rates_ui("test")
+
+ # assert
+ expect_called(mocks$mod_plot_rates_funnel_ui, 1)
+ expect_args(mocks$mod_plot_rates_funnel_ui, 1, "test-mod_plot_rates_funnel")
+})
+
+test_that("ui calls mod_plot_rates_box_ui", {
+ # arrange
+ mocks <- setup_mod_plot_rates_ui_tests()
+
+ # act
+ ui <- mod_plot_rates_ui("test")
+
+ # assert
+ expect_called(mocks$mod_plot_rates_box_ui, 1)
+ expect_args(mocks$mod_plot_rates_box_ui, 1, "test-mod_plot_rates_box")
+})
+
+test_that("server sets up strategies_config", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+ m <- mocks$get_golem_config
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(inputs_data_sample),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ # assert
+ expect_equal(strategies_config, "strategies_config")
+ expect_called(m, 1)
+ expect_args(m, 1, "mitigators_config")
+ }
+ )
+})
+
+test_that("server sets up strategy_group_lookup", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+ m <- mocks$get_golem_config
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(inputs_data_sample),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ # assert
+ expect_equal(strategy_group_lookup, "strategy_group_lookup")
+ expect_called(m, 1)
+ expect_args(m, 1, "mitigators_config")
+ }
+ )
+})
+
+test_that("modules are correctly instantiated", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(inputs_data_sample),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ # assert
+ expect_called(mocks$mod_plot_rates_trend_server, 1)
+ expect_args(
+ mocks$mod_plot_rates_trend_server,
+ 1,
+ "mod_plot_rates_trend",
+ rates_trend_data,
+ y_axis_limits,
+ y_axis_title,
+ y_labels,
+ selected_year
+ )
+
+ expect_called(mocks$mod_plot_rates_funnel_server, 1)
+ expect_args(
+ mocks$mod_plot_rates_funnel_server,
+ 1,
+ "mod_plot_rates_funnel",
+ rates_baseline_data,
+ rates_funnel_calculations,
+ y_axis_limits,
+ funnel_x_title
+ )
+
+ expect_called(mocks$mod_plot_rates_box_server, 1)
+ expect_args(
+ mocks$mod_plot_rates_box_server,
+ 1,
+ "mod_plot_rates_box",
+ rates_baseline_data,
+ y_axis_limits
+ )
+ }
+ )
+})
+
+test_that("peers_lookup", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+ m <- mocks$get_peers_lookup
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(inputs_data_sample),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ # assert
+ expect_equal(peers_lookup(), "peers_lookup")
+
+ expect_called(m, 1)
+ expect_args(m, 1, "nhp")
+
+ selected_geography("la")
+ peers_lookup()
+ expect_called(m, 2)
+ expect_args(m, 2, "la")
+ }
+ )
+})
+
+test_that("rates_data", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ actual <- rates_data()
+
+ # assert
+ expect_equal(actual, "rates")
+ expect_called(mocks$get_rates_data, 1)
+ expect_args(mocks$get_rates_data, 1, "sample_data", "Strategy A")
+ }
+ )
+})
+
+test_that("rates_trend_data", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ actual <- rates_trend_data()
+
+ # assert
+ expect_equal(actual, "rates_trend")
+ expect_called(mocks$get_rates_trend_data, 1)
+ expect_args(mocks$get_rates_trend_data, 1, "rates", "R00")
+ }
+ )
+})
+
+test_that("rates_baseline", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ actual <- rates_baseline_data()
+
+ # assert
+ expect_equal(actual, "rates_baseline")
+ expect_called(mocks$generate_rates_baseline_data, 1)
+ expect_args(
+ mocks$generate_rates_baseline_data,
+ 1,
+ "rates",
+ "R00",
+ "peers_lookup",
+ 202324
+ )
+ }
+ )
+})
+
+test_that("rates_funnel_calculations", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ actual <- rates_funnel_calculations()
+
+ # assert
+ expect_equal(actual, "uprime")
+ expect_called(mocks$uprime_calculations, 1)
+ expect_args(
+ mocks$uprime_calculations,
+ 1,
+ "rates_baseline"
+ )
+ }
+ )
+})
+
+test_that("y_axis_limits (funnel values are max)", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ trends_df <- tibble::tibble(
+ rate = 1:10
+ )
+
+ baseline_df <- tibble::tibble(
+ denominator = c(1, 10, 15, 100),
+ rate = c(50, 40, 30, 20)
+ )
+
+ uprime <- list(
+ z_i = c(2, 10, 2, 2)
+ )
+
+ local_mocked_bindings(
+ "get_rates_trend_data" = mock(trends_df),
+ "generate_rates_baseline_data" = mock(baseline_df),
+ "uprime_calculations" = mock(uprime),
+ )
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ actual <- y_axis_limits()
+
+ # assert
+ expect_equal(actual, c(0, 30))
+ }
+ )
+})
+
+test_that("y_axis_limits (trend values are max)", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ trends_df <- tibble::tibble(
+ rate = 300
+ )
+
+ baseline_df <- tibble::tibble(
+ denominator = c(1, 10, 15, 100),
+ rate = c(50, 40, 30, 20)
+ )
+
+ uprime <- list(
+ z_i = c(2, 10, 2, 2)
+ )
+
+ local_mocked_bindings(
+ "get_rates_trend_data" = mock(trends_df),
+ "generate_rates_baseline_data" = mock(baseline_df),
+ "uprime_calculations" = mock(uprime),
+ )
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ actual <- y_axis_limits()
+
+ # assert
+ expect_equal(actual, c(0, 300))
+ }
+ )
+})
+
+test_that("strategy_config", {
+ # arrange
+ mocks <- server_mod_plot_rates_server_tests()
+
+ strategy_config <- list(
+ "group a" = list(
+ y_axis_title = "y axis title",
+ number_type = "y labels",
+ funnel_x_title = "funnel x title"
+ )
+ )
+ strategy_group_lookup <- list(
+ "Strategy A" = "group a"
+ )
+
+ local_mocked_bindings(
+ "get_golem_config" = mock(strategy_config),
+ "make_strategy_group_lookup" = mock(strategy_group_lookup)
+ )
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_server,
+ args = list(
+ inputs_data = shiny::reactiveVal(list(rates = "sample_data")),
+ selected_geography = shiny::reactiveVal("nhp"),
+ selected_provider = shiny::reactiveVal("R00"),
+ selected_strategy = shiny::reactiveVal("Strategy A"),
+ selected_year = shiny::reactiveVal(202324)
+ ),
+ {
+ # assert
+ expect_equal(y_axis_title(), "y axis title")
+ expect_equal(y_labels(), "y labels")
+ expect_equal(funnel_x_title(), "funnel x title")
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_plot_rates_box.R b/tests/testthat/test-mod_plot_rates_box.R
new file mode 100644
index 0000000..9909e15
--- /dev/null
+++ b/tests/testthat/test-mod_plot_rates_box.R
@@ -0,0 +1,64 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_plot_rates_box_ui("test")
+
+ expect_snapshot(ui)
+})
+
+
+test_that("rates_box_plot (no rows)", {
+ # arrange
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_box_server,
+ args = list(
+ rates = \() tibble::tibble(),
+ y_axis_limits = \() c(0, 100)
+ ),
+ {
+ # assert
+ expect_error(
+ output$rates_box_plot,
+ "No data available for these selections."
+ )
+ }
+ )
+})
+
+
+test_that("rates_box_plot (with rows)", {
+ # arrange
+ m <- mock("plot")
+ testthat::local_mocked_bindings(
+ "plot_rates_box" = m
+ )
+
+ # replace renderPlot to avoid actual plotting, replace with renderText so we
+ # can simply check the output
+ testthat::local_mocked_bindings(
+ "renderPlot" = shiny::renderText,
+ .package = "shiny"
+ )
+
+ sample_data <- tibble::tibble(x = 1, y = 2)
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_box_server,
+ args = list(
+ rates = \() sample_data,
+ y_axis_limits = \() c(0, 100)
+ ),
+ {
+ actual <- output$rates_box_plot
+
+ # assert
+ expect_equal(actual, "plot")
+
+ expect_called(m, 1)
+ expect_args(m, 1, sample_data, c(0, 100))
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_plot_rates_funnel.R b/tests/testthat/test-mod_plot_rates_funnel.R
new file mode 100644
index 0000000..2b7c27c
--- /dev/null
+++ b/tests/testthat/test-mod_plot_rates_funnel.R
@@ -0,0 +1,68 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_plot_rates_funnel_ui("test")
+
+ expect_snapshot(ui)
+})
+
+
+test_that("rates_funnel_plot (no rows)", {
+ # arrange
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_funnel_server,
+ args = list(
+ rates = \() tibble::tibble(),
+ funnel_calculations = \() "funnel calculations",
+ y_axis_limits = \() c(0, 100),
+ x_axis_title = \() "X Axis"
+ ),
+ {
+ # assert
+ expect_error(
+ output$rates_funnel_plot,
+ "No data available for these selections."
+ )
+ }
+ )
+})
+
+
+test_that("rates_funnel_plot (with rows)", {
+ # arrange
+ m <- mock("plot")
+ testthat::local_mocked_bindings(
+ "plot_rates_funnel" = m
+ )
+
+ # replace renderPlot to avoid actual plotting, replace with renderText so we
+ # can simply check the output
+ testthat::local_mocked_bindings(
+ "renderPlot" = shiny::renderText,
+ .package = "shiny"
+ )
+
+ sample_data <- tibble::tibble(x = 1, y = 2)
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_funnel_server,
+ args = list(
+ rates = \() sample_data,
+ funnel_calculations = \() "funnel calculations",
+ y_axis_limits = \() c(0, 100),
+ x_axis_title = \() "X Axis"
+ ),
+ {
+ actual <- output$rates_funnel_plot
+
+ # assert
+ expect_equal(actual, "plot")
+
+ expect_called(m, 1)
+ expect_args(m, 1, sample_data, "funnel calculations", c(0, 100), "X Axis")
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_plot_rates_trend.R b/tests/testthat/test-mod_plot_rates_trend.R
new file mode 100644
index 0000000..eb7f3ae
--- /dev/null
+++ b/tests/testthat/test-mod_plot_rates_trend.R
@@ -0,0 +1,70 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_plot_rates_trend_ui("test")
+
+ expect_snapshot(ui)
+})
+
+
+test_that("rates_trend_plot (no rows)", {
+ # arrange
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_trend_server,
+ args = list(
+ rates = \() tibble::tibble(),
+ y_axis_limits = \() c(0, 100),
+ y_axis_title = \() "Y Axis",
+ y_labels = \() "Y Labels",
+ selected_year = \() 202324
+ ),
+ {
+ # assert
+ expect_error(
+ output$rates_trend_plot,
+ "No data available for these selections."
+ )
+ }
+ )
+})
+
+
+test_that("rates_trend_plot (with rows)", {
+ # arrange
+ m <- mock("plot")
+ testthat::local_mocked_bindings(
+ "plot_rates_trend" = m
+ )
+
+ # replace renderPlot to avoid actual plotting, replace with renderText so we
+ # can simply check the output
+ testthat::local_mocked_bindings(
+ "renderPlot" = shiny::renderText,
+ .package = "shiny"
+ )
+
+ sample_data <- tibble::tibble(x = 1, y = 2)
+
+ # act
+ shiny::testServer(
+ mod_plot_rates_trend_server,
+ args = list(
+ rates = \() sample_data,
+ y_axis_limits = \() c(0, 100),
+ y_axis_title = \() "Y Axis",
+ y_labels = \() "Y Labels",
+ selected_year = \() 202324
+ ),
+ {
+ actual <- output$rates_trend_plot
+
+ # assert
+ expect_equal(actual, "plot")
+
+ expect_called(m, 1)
+ expect_args(m, 1, sample_data, 202324, c(0, 100), "Y Axis", "Y Labels")
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_select_geography.R b/tests/testthat/test-mod_select_geography.R
new file mode 100644
index 0000000..3684c74
--- /dev/null
+++ b/tests/testthat/test-mod_select_geography.R
@@ -0,0 +1,23 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_select_geography_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("server returns reactive", {
+ # arrange
+ test_server <- function(input, output, session) {
+ selected_geography <- mod_select_geography_server("test")
+ }
+
+ # act
+ shiny::testServer(test_server, {
+ session$setInputs("test-geography_select" = "nhp")
+ expect_equal(selected_geography(), "nhp")
+
+ session$setInputs("test-geography_select" = "la")
+ expect_equal(selected_geography(), "la")
+ })
+})
diff --git a/tests/testthat/test-mod_select_provider.R b/tests/testthat/test-mod_select_provider.R
new file mode 100644
index 0000000..43b1658
--- /dev/null
+++ b/tests/testthat/test-mod_select_provider.R
@@ -0,0 +1,122 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_select_provider_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("server returns reactive", {
+ # arrange
+ test_server <- function(input, output, session) {
+ selected_provider <- mod_select_provider_server("test", reactiveVal("nhp"))
+ }
+
+ # act
+ shiny::testServer(test_server, {
+ session$setInputs("test-provider_select" = "a")
+ expect_equal(selected_provider(), "a")
+
+ session$setInputs("test-provider_select" = "b")
+ expect_equal(selected_provider(), "b")
+ })
+})
+
+test_that("providers reactive", {
+ # arrange
+ m <- mock("providers nhp", "providers la")
+
+ testthat::local_mocked_bindings(
+ "read_json" = m,
+ .package = "jsonlite"
+ )
+ testthat::local_mocked_bindings(
+ "app_sys" = \(...) file.path("inst", ...),
+ )
+
+ # act
+ shiny::testServer(
+ mod_select_provider_server,
+ args = list(
+ selected_geography = reactiveVal()
+ ),
+ {
+ # assert
+ selected_geography("nhp")
+ expect_equal(providers(), "providers nhp")
+
+ selected_geography("la")
+ expect_equal(providers(), "providers la")
+
+ selected_geography("other")
+ expect_error(providers())
+
+ expect_called(m, 2)
+ expect_args(
+ m,
+ 1,
+ "inst/app/data/nhp-datasets.json",
+ simplify_vector = TRUE
+ )
+ expect_args(
+ m,
+ 2,
+ "inst/app/data/la-datasets.json",
+ simplify_vector = TRUE
+ )
+ }
+ )
+})
+
+
+test_that("it updates the select input", {
+ # arrange
+ m <- mock()
+ testthat::local_mocked_bindings(
+ "updateSelectInput" = m,
+ .package = "shiny"
+ )
+
+ # mock what will happen to providers as we change the selected geography
+ testthat::local_mocked_bindings(
+ "read_json" = mock(
+ list("A" = "a", "B" = "b"),
+ list("C" = "c", "D" = "d")
+ ),
+ .package = "jsonlite"
+ )
+
+ # act
+ shiny::testServer(
+ mod_select_provider_server,
+ args = list(
+ selected_geography = reactiveVal()
+ ),
+ {
+ # assert
+ selected_geography("nhp")
+ session$private$flush()
+ expect_called(m, 1)
+ expect_args(
+ m,
+ 1,
+ session,
+ "provider_select",
+ label = "Choose a trust:",
+ choices = c("a" = "A", "b" = "B")
+ )
+
+ selected_geography("la")
+ session$private$flush()
+ expect_called(m, 2)
+ expect_args(
+ m,
+ 2,
+ session,
+ "provider_select",
+ label = "Choose an LA:",
+ choices = c("c" = "C", "d" = "D")
+ )
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_select_strategy.R b/tests/testthat/test-mod_select_strategy.R
new file mode 100644
index 0000000..0e6b5b0
--- /dev/null
+++ b/tests/testthat/test-mod_select_strategy.R
@@ -0,0 +1,137 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_select_strategy_ui("test")
+
+ expect_snapshot(ui)
+})
+
+
+test_that("mod_select_strategy_get_strategies works", {
+ # arrange
+ m <- mock(
+ list(
+ "a" = "Strategy A (IP-AA-001)",
+ "b" = "Strategy B (IP-AA-002)",
+ "c" = "Strategy C (IP-EF-001)",
+ "d" = "Strategy D (OP-AA-001)",
+ "e" = "Strategy E (OP-AA-002)"
+ )
+ )
+ local_mocked_bindings(
+ "read_json" = m,
+ .package = "jsonlite"
+ )
+ # nolint start
+ expected <- list(
+ "ip" = tibble::tribble(
+ ~strategy , ~name ,
+ "a" , "Strategy A (IP-AA-001)" ,
+ "b" , "Strategy B (IP-AA-002)" ,
+ "c" , "Strategy C (IP-EF-001)"
+ ),
+ op = tibble::tribble(
+ ~strategy , ~name ,
+ "d" , "Strategy D (OP-AA-001)" ,
+ "e" , "Strategy E (OP-AA-002)"
+ )
+ ) |>
+ dplyr::bind_rows(.id = "category") |>
+ dplyr::group_nest(.data$category) |>
+ tibble::deframe()
+ # nolint end
+
+ # act
+ actual <- mod_select_strategy_get_strategies()
+
+ # assert
+ expect_equal(actual, expected)
+ expect_called(m, 1)
+ expect_call(
+ m,
+ 1,
+ jsonlite::read_json(
+ app_sys("app", "data", "mitigators.json"),
+ simplify_vector = TRUE
+ )
+ )
+})
+
+test_that("server returns reactive", {
+ # arrange
+ setup_mod_select_strategy_server()
+
+ test_server <- function(input, output, session) {
+ selected_strategy <- mod_select_strategy_server("test")
+ }
+
+ # act
+ shiny::testServer(test_server, {
+ session$setInputs("test-strategy_select" = "a")
+ expect_equal(selected_strategy(), "a")
+
+ session$setInputs("test-strategy_select" = "b")
+ expect_equal(selected_strategy(), "b")
+ })
+})
+
+test_that("it calls mod_select_strategy_get_strategies", {
+ # arrange
+ m <- setup_mod_select_strategy_server()
+
+ # act
+ shiny::testServer(mod_select_strategy_server, {
+ # assert
+ expect_called(m, 1)
+ expect_args(m, 1)
+ })
+})
+
+test_that("selected_category", {
+ # arrange
+ setup_mod_select_strategy_server()
+
+ # act
+ shiny::testServer(mod_select_strategy_server, {
+ session$setInputs("strategy_category_select" = "ip")
+ actual <- selected_category()
+
+ # assert
+ expect_equal(actual, "ip")
+ })
+})
+
+test_that("it updates the strategy_select choices", {
+ # arrange
+ setup_mod_select_strategy_server()
+ m <- mock()
+ local_mocked_bindings(
+ "updateSelectInput" = m,
+ .package = "shiny"
+ )
+
+ # act
+ shiny::testServer(mod_select_strategy_server, {
+ # assert
+ session$setInputs("strategy_category_select" = "ip")
+ expect_called(m, 1)
+ expect_args(
+ m,
+ 1,
+ session,
+ "strategy_select",
+ choices = c("Strategy A" = "a", "Strategy B" = "b")
+ )
+
+ # assert
+ session$setInputs("strategy_category_select" = "op")
+ expect_called(m, 2)
+ expect_args(
+ m,
+ 2,
+ session,
+ "strategy_select",
+ choices = c("Strategy C" = "c", "Strategy D" = "d")
+ )
+ })
+})
diff --git a/tests/testthat/test-mod_show_strategy_text.R b/tests/testthat/test-mod_show_strategy_text.R
new file mode 100644
index 0000000..0e4ece2
--- /dev/null
+++ b/tests/testthat/test-mod_show_strategy_text.R
@@ -0,0 +1,134 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_show_strategy_text_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("mod_show_strategy_text_get_descriptions_lookup", {
+ # arrange
+ sample_descriptions_lookup <- c(
+ "strategy_a",
+ "strategy_b"
+ )
+ m <- mock(sample_descriptions_lookup)
+ local_mocked_bindings(
+ "read_json" = m,
+ .package = "jsonlite"
+ )
+
+ # act
+ actual <- mod_show_strategy_text_get_descriptions_lookup()
+
+ # assert
+ expect_equal(actual, sample_descriptions_lookup)
+})
+
+test_that("strategy_stub", {
+ # arrange
+ local_mocked_bindings(
+ "mod_show_strategy_text_get_descriptions_lookup" = \() {
+ c(
+ "strategy_a",
+ "strategy_b"
+ )
+ }
+ )
+
+ # act
+ shiny::testServer(
+ mod_show_strategy_text_server,
+ args = list(selected_strategy = reactiveVal("a")),
+ {
+ # assert
+ selected_strategy("strategy_a_acute")
+ actual1 <- strategy_stub()
+
+ selected_strategy("strategy_a_chronic")
+ actual2 <- strategy_stub()
+
+ selected_strategy("strategy_b")
+ actual3 <- strategy_stub()
+
+ expect_equal(actual1, "strategy_a")
+ expect_equal(actual2, "strategy_a")
+ expect_equal(actual3, "strategy_b")
+ }
+ )
+})
+
+test_that("strategy_text", {
+ # arrange
+ m <- mock("text_a", "text_b")
+ local_mocked_bindings(
+ "mod_show_strategy_text_get_descriptions_lookup" = \() {
+ c(
+ "strategy_a",
+ "strategy_b"
+ )
+ },
+ "fetch_strategy_text" = m
+ )
+
+ # act
+ shiny::testServer(
+ mod_show_strategy_text_server,
+ args = list(selected_strategy = reactiveVal("a")),
+ {
+ # act
+ selected_strategy("strategy_a_acute")
+ actual1 <- strategy_text()
+
+ selected_strategy("strategy_b")
+ actual2 <- strategy_text()
+
+ # validate caching: this should not call fetch_strategy_text again
+ selected_strategy("strategy_a_chronic")
+ actual3 <- strategy_text()
+
+ # assert
+ expect_equal(actual1, "text_a")
+ expect_equal(actual2, "text_b")
+ expect_equal(actual3, "text_a")
+
+ expect_called(m, 2)
+ expect_args(m, 1, "strategy_a")
+ expect_args(m, 2, "strategy_b")
+ }
+ )
+})
+
+test_that("strategy_text is rendered", {
+ # arrange
+ m <- mock("html")
+ local_mocked_bindings(
+ "mod_show_strategy_text_get_descriptions_lookup" = \() {
+ c(
+ "strategy_a",
+ "strategy_b"
+ )
+ },
+ "fetch_strategy_text" = \(...) "strategy text",
+ "md_string_to_html" = m
+ )
+
+ # act
+ shiny::testServer(
+ mod_show_strategy_text_server,
+ args = list(selected_strategy = reactiveVal("strategy_a")),
+ {
+ # assert
+ actual <- output$strategy_text
+
+ expect_equal(actual, "html")
+
+ expect_called(m, 1)
+ expect_args(
+ m,
+ 1,
+ "strategy text"
+ )
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_table_diagnoses.R b/tests/testthat/test-mod_table_diagnoses.R
new file mode 100644
index 0000000..cdcadc0
--- /dev/null
+++ b/tests/testthat/test-mod_table_diagnoses.R
@@ -0,0 +1,151 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_table_diagnoses_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("it loads the diagnoses csv", {
+ # arrange
+ m <- mock("diagnoses_lookup")
+ testthat::local_mocked_bindings("read_csv" = m, .package = "readr")
+
+ # act
+ shiny::testServer(
+ mod_table_diagnoses_server,
+ args = list(
+ inputs_data = reactiveVal(),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ # assert
+ expect_equal(diagnoses_lookup, "diagnoses_lookup")
+ expect_called(m, 1)
+ expect_call(
+ m,
+ 1,
+ readr::read_csv(
+ app_sys("app", "data", "diagnoses.csv"),
+ col_types = "c"
+ )
+ )
+ }
+ )
+})
+
+test_that("diagnoses_data", {
+ # arrange
+
+ # act
+ shiny::testServer(
+ mod_table_diagnoses_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ actual <- diagnoses_data()
+
+ # assert
+ expect_equal(actual, "diagnoses")
+ }
+ )
+})
+
+
+test_that("diagnoses_prepared", {
+ # arrange
+ testthat::local_mocked_bindings(
+ "read_csv" = \(...) "diagnoses_lookup",
+ .package = "readr"
+ )
+
+ m <- mock("diagnoses_prepared")
+ testthat::local_mocked_bindings(
+ "prepare_diagnoses_data" = m
+ )
+
+ # act
+ shiny::testServer(
+ mod_table_diagnoses_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ actual <- diagnoses_prepared()
+
+ # assert
+ expect_equal(actual, "diagnoses_prepared")
+ expect_called(m, 1)
+ expect_args(m, 1, "diagnoses", "diagnoses_lookup", "R00", "strategy", 1)
+ }
+ )
+})
+
+test_that("diagnoses_table (no rows)", {
+ # arrange
+ testthat::local_mocked_bindings("prepare_diagnoses_data" = \(...) {
+ tibble::tibble()
+ })
+
+ # act
+ shiny::testServer(
+ mod_table_diagnoses_server,
+ args = list(
+ inputs_data = reactiveVal(list(diagnoses = "diagnoses")),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ # assert
+ expect_error(
+ output$diagnoses_table,
+ "No diagnoses to display."
+ )
+ }
+ )
+})
+
+test_that("diagnoses_table (with rows)", {
+ # arrange
+ sample_prepared_data <- tibble::tibble(a = 1, b = 2)
+
+ m <- mock("entabled")
+ testthat::local_mocked_bindings(
+ "prepare_diagnoses_data" = \(...) sample_prepared_data,
+ "entable_encounters" = m
+ )
+
+ testthat::local_mocked_bindings(
+ "render_gt" = shiny::renderText,
+ .package = "gt"
+ )
+
+ # act
+ shiny::testServer(
+ mod_table_diagnoses_server,
+ args = list(
+ inputs_data = reactiveVal(list(diagnoses = "diagnoses")),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ actual <- output$diagnoses_table
+
+ # assert
+ expect_equal(actual, "entabled")
+ expect_called(m, 1)
+ expect_args(m, 1, sample_prepared_data)
+ }
+ )
+})
diff --git a/tests/testthat/test-mod_table_procedures.R b/tests/testthat/test-mod_table_procedures.R
new file mode 100644
index 0000000..b3eb4c5
--- /dev/null
+++ b/tests/testthat/test-mod_table_procedures.R
@@ -0,0 +1,151 @@
+test_that("ui", {
+ setup_ui_test()
+
+ ui <- mod_table_procedures_ui("test")
+
+ expect_snapshot(ui)
+})
+
+test_that("it loads the procedures csv", {
+ # arrange
+ m <- mock("procedures_lookup")
+ testthat::local_mocked_bindings("read_csv" = m, .package = "readr")
+
+ # act
+ shiny::testServer(
+ mod_table_procedures_server,
+ args = list(
+ inputs_data = reactiveVal(),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ # assert
+ expect_equal(procedures_lookup, "procedures_lookup")
+ expect_called(m, 1)
+ expect_call(
+ m,
+ 1,
+ readr::read_csv(
+ app_sys("app", "data", "procedures.csv"),
+ col_types = "c"
+ )
+ )
+ }
+ )
+})
+
+test_that("procedures_data", {
+ # arrange
+
+ # act
+ shiny::testServer(
+ mod_table_procedures_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ actual <- procedures_data()
+
+ # assert
+ expect_equal(actual, "procedures")
+ }
+ )
+})
+
+
+test_that("procedures_prepared", {
+ # arrange
+ testthat::local_mocked_bindings(
+ "read_csv" = \(...) "procedures_lookup",
+ .package = "readr"
+ )
+
+ m <- mock("procedures_prepared")
+ testthat::local_mocked_bindings(
+ "prepare_procedures_data" = m
+ )
+
+ # act
+ shiny::testServer(
+ mod_table_procedures_server,
+ args = list(
+ inputs_data = reactiveVal(inputs_data_sample),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ actual <- procedures_prepared()
+
+ # assert
+ expect_equal(actual, "procedures_prepared")
+ expect_called(m, 1)
+ expect_args(m, 1, "procedures", "procedures_lookup", "R00", "strategy", 1)
+ }
+ )
+})
+
+test_that("procedures_table (no rows)", {
+ # arrange
+ testthat::local_mocked_bindings("prepare_procedures_data" = \(...) {
+ tibble::tibble()
+ })
+
+ # act
+ shiny::testServer(
+ mod_table_procedures_server,
+ args = list(
+ inputs_data = reactiveVal(list(procedures = "procedures")),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ # assert
+ expect_error(
+ output$procedures_table,
+ "No procedures to display."
+ )
+ }
+ )
+})
+
+test_that("procedures_table (with rows)", {
+ # arrange
+ sample_prepared_data <- tibble::tibble(a = 1, b = 2)
+
+ m <- mock("entabled")
+ testthat::local_mocked_bindings(
+ "prepare_procedures_data" = \(...) sample_prepared_data,
+ "entable_encounters" = m
+ )
+
+ testthat::local_mocked_bindings(
+ "render_gt" = shiny::renderText,
+ .package = "gt"
+ )
+
+ # act
+ shiny::testServer(
+ mod_table_procedures_server,
+ args = list(
+ inputs_data = reactiveVal(list(procedures = "procedures")),
+ selected_provider = reactiveVal("R00"),
+ selected_strategy = reactiveVal("strategy"),
+ selected_year = reactiveVal(1)
+ ),
+ {
+ actual <- output$procedures_table
+
+ # assert
+ expect_equal(actual, "entabled")
+ expect_called(m, 1)
+ expect_args(m, 1, sample_prepared_data)
+ }
+ )
+})
diff --git a/tests/testthat/test-run_app.R b/tests/testthat/test-run_app.R
new file mode 100644
index 0000000..7ff39f6
--- /dev/null
+++ b/tests/testthat/test-run_app.R
@@ -0,0 +1,37 @@
+test_that("run_app", {
+ # arrange
+ m1 <- mock()
+ m2 <- mock("app")
+ m3 <- mock("cache")
+
+ local_mocked_bindings(
+ "shinyOptions" = m1,
+ "shinyApp" = m2,
+ .package = "shiny"
+ )
+ local_mocked_bindings(
+ "cache_disk" = m3,
+ .package = "cachem"
+ )
+
+ # act
+ app <- run_app()
+
+ # assert
+ expect_equal(app, "app")
+
+ expect_called(m1, 1)
+ expect_args(m1, 1, cache = "cache")
+
+ expect_called(m2, 1)
+ expect_args(
+ m2,
+ 1,
+ ui = app_ui,
+ server = app_server,
+ enableBookmarking = "server"
+ )
+
+ expect_called(m3, 1)
+ expect_args(m3, 1, ".cache")
+})
diff --git a/tests/testthat/test-utils_data.R b/tests/testthat/test-utils_data.R
new file mode 100644
index 0000000..59ba32f
--- /dev/null
+++ b/tests/testthat/test-utils_data.R
@@ -0,0 +1,156 @@
+test_that("prepare_age_sex_data", {
+ # arrange
+ # nolint start
+ age_sex_data <- tibble::tribble(
+ ~age_group , ~sex , ~n ,
+ "0-4" , 1 , 5 ,
+ "5-9" , 1 , 10 ,
+ "10-14" , 1 , 20 ,
+ "0-4" , 2 , 7 ,
+ "5-9" , 2 , 12 ,
+ "10-14" , 2 , 22 ,
+ )
+ # nolint end
+ expected <- structure(
+ list(
+ age_group = structure(
+ c(1L, 3L, 2L, 1L, 3L, 2L),
+ levels = c("0-4", "10-14", "5-9"),
+ class = "factor"
+ ),
+ sex = structure(
+ c(1L, 1L, 1L, 2L, 2L, 2L),
+ levels = c("Males", "Females"),
+ class = "factor"
+ ),
+ n = c(-5, -10, -20, 7, 12, 22)
+ ),
+ row.names = c(NA, -6L),
+ class = c("tbl_df", "tbl", "data.frame")
+ )
+
+ # act
+ actual <- prepare_age_sex_data(age_sex_data)
+
+ # assert
+ expect_equal(actual, expected)
+})
+
+test_that("get_golem_config", {
+ # arrange
+ m <- mock("config")
+ local_mocked_bindings(
+ "get" = m,
+ .package = "config"
+ )
+
+ local_mocked_bindings(
+ "app_sys" = \(...) file.path(...)
+ )
+
+ # act
+ actual <- get_golem_config("value", "config")
+
+ # assert
+ expect_equal(actual, "config")
+
+ expect_called(m, 1)
+ expect_args(
+ m,
+ 1,
+ value = "value",
+ config = "config",
+ file = "golem-config.yml",
+ use_parent = TRUE
+ )
+})
+
+test_that("make_strategy_group_lookup", {
+ # arrange
+ config <- list(
+ "a" = list(
+ strategy_subset = c("s1" = "S1", "s2" = "S2")
+ ),
+ b = list(
+ strategy_subset = c("s3" = "S3")
+ )
+ )
+ expected <- list(
+ "s1" = "a",
+ "s2" = "a",
+ "s3" = "b"
+ )
+
+ # act
+ actual <- make_strategy_group_lookup(config)
+
+ # assert
+ expect_equal(actual, expected)
+})
+
+test_that("md_file_to_html returns NULL if file doesn't exist", {
+ # act
+ actual <- md_file_to_html("nonexistent_file.md")
+
+ # assert
+ expect_null(actual)
+})
+
+test_that("md_file_to_html reads valid file", {
+ # arrange
+ local_mocked_bindings(
+ "app_sys" = \(...) file.path(...)
+ )
+
+ stub(md_file_to_html, "file.exists", TRUE)
+
+ m1 <- mock("content")
+ m2 <- mock("html")
+ local_mocked_bindings(
+ "mark_html" = m1,
+ .package = "markdown"
+ )
+ local_mocked_bindings(
+ "HTML" = m2,
+ .package = "shiny"
+ )
+
+ # act
+ actual <- md_file_to_html("file.md")
+
+ # assert
+ expect_equal(actual, "html")
+
+ expect_called(m1, 1)
+ expect_args(m1, 1, "file.md", output = FALSE, template = FALSE)
+
+ expect_called(m2, 1)
+ expect_args(m2, 1, "content")
+})
+
+
+test_that("md_string_to_html", {
+ # arrange
+ m1 <- mock("content")
+ m2 <- mock("html")
+ local_mocked_bindings(
+ "mark_html" = m1,
+ .package = "markdown"
+ )
+ local_mocked_bindings(
+ "HTML" = m2,
+ .package = "shiny"
+ )
+
+ # act
+ actual <- md_string_to_html("text")
+
+ # assert
+ expect_equal(actual, "html")
+
+ expect_called(m1, 1)
+ expect_args(m1, 1, "text", output = FALSE, template = FALSE)
+
+ expect_called(m2, 1)
+ expect_args(m2, 1, "content")
+})
diff --git a/tests/testthat/test-utils_plot.R b/tests/testthat/test-utils_plot.R
new file mode 100644
index 0000000..58d13b1
--- /dev/null
+++ b/tests/testthat/test-utils_plot.R
@@ -0,0 +1,108 @@
+test_that("isolate_provider_peers", {
+ # arrange
+ # nolint start
+ peers <- tibble::tribble(
+ ~procode , ~peer ,
+ "A" , "B" ,
+ "A" , "C" ,
+ "B" , "A" ,
+ "B" , "C"
+ )
+ # nolint end
+ expected <- c("B", "C")
+
+ # act
+ actual <- isolate_provider_peers("A", peers)
+
+ # assert
+ expect_equal(actual, expected)
+})
+
+test_that("generate_rates_baseline_data", {
+ # arrange
+ m <- mock(c("B", "C"))
+ local_mocked_bindings("isolate_provider_peers" = m)
+
+ # nolint start
+ rates <- tibble::tribble(
+ ~provider , ~fyear ,
+ "A" , 202223 ,
+ "B" , 202223 ,
+ "C" , 202223 ,
+ "D" , 202223 ,
+ "A" , 202324 ,
+ "B" , 202324 ,
+ "C" , 202324 ,
+ "D" , 202324 ,
+ )
+ provider <- "A"
+ selected_year <- 202324
+
+ expected <- tibble::tribble(
+ ~provider , ~fyear , ~is_peer ,
+ "B" , 202324 , TRUE ,
+ "C" , 202324 , TRUE ,
+ "A" , 202324 , FALSE ,
+ "D" , 202324 , NA
+ )
+ # nolint end
+
+ # act
+ actual <- generate_rates_baseline_data(
+ rates,
+ provider,
+ "peers_lookup",
+ selected_year
+ )
+
+ # assert
+ expect_equal(actual, expected)
+
+ expect_called(m, 1)
+ expect_args(m, 1, "A", "peers_lookup")
+})
+
+test_that("uprime_calculations", {
+ # arrange
+ # nolint start
+ df <- tibble::tribble(
+ ~denominator , ~rate , ~national_rate ,
+ 1000 , 2.0 , 1.9 ,
+ 2000 , 1.5 , 1.9 ,
+ 1500 , 2.5 , 1.9 ,
+ 2500 , 1.8 , 1.9
+ )
+ # nolint end
+
+ # act
+ actual <- uprime_calculations(df)
+
+ # assert
+ expect_equal(actual$cl, 1.9)
+ expect_equal(
+ actual$z_i,
+ c(0.1444332, 1.0613633, -0.8170378, -0.2283690),
+ tolerance = 1e-6
+ )
+
+ expect_equal(
+ actual$lcl3(c(1000, 1500)),
+ c(-0.1770841, 0.2040680),
+ tolerance = 1e-6
+ )
+ expect_equal(
+ actual$lcl2(c(1000, 1500)),
+ c(0.5152773, 0.7693786),
+ tolerance = 1e-6
+ )
+ expect_equal(
+ actual$ucl2(c(1000, 1500)),
+ c(3.284723, 3.030621),
+ tolerance = 1e-6
+ )
+ expect_equal(
+ actual$ucl3(c(1000, 1500)),
+ c(3.977084, 3.595932),
+ tolerance = 1e-6
+ )
+})
diff --git a/tests/testthat/test-utils_plot_rates.R b/tests/testthat/test-utils_plot_rates.R
new file mode 100644
index 0000000..0fc7302
--- /dev/null
+++ b/tests/testthat/test-utils_plot_rates.R
@@ -0,0 +1,93 @@
+test_that("get_peers_lookup", {
+ # arrange
+ m <- mock("peers", cycle = TRUE)
+ local_mocked_bindings(
+ "read_csv" = m,
+ .package = "readr"
+ )
+ local_mocked_bindings("app_sys" = file.path)
+
+ # act
+ actual_nhp <- get_peers_lookup("nhp")
+ actual_la <- get_peers_lookup("la")
+
+ # assert
+ expect_equal(actual_nhp, "peers")
+ expect_equal(actual_la, "peers")
+
+ expect_called(m, 2)
+ expect_args(m, 1, "app/data/nhp-peers.csv", "col_types" = "c")
+ expect_args(m, 2, "app/data/la-peers.csv", "col_types" = "c")
+
+ expect_error(get_peers_lookup("other"))
+})
+
+test_that("get_rates_data", {
+ # arrange
+ # nolint start
+ inputs_data_sample <- tibble::tribble(
+ ~fyear , ~provider , ~strategy , ~crude_rate , ~std_rate ,
+ 202223 , "a" , "Strategy A" , 1 , 2 ,
+ 202223 , "b" , "Strategy A" , 3 , 4 ,
+ 202223 , "national" , "Strategy A" , 5 , 6 ,
+ 202324 , "a" , "Strategy A" , 7 , 8 ,
+ 202324 , "b" , "Strategy A" , 9 , 10 ,
+ 202324 , "national" , "Strategy A" , 10 , 12 ,
+ 202223 , "a" , "Strategy B" , 2 , 1 ,
+ 202223 , "b" , "Strategy B" , 4 , 3 ,
+ 202223 , "national" , "Strategy B" , 6 , 5 ,
+ 202324 , "a" , "Strategy B" , 8 , 7 ,
+ 202324 , "b" , "Strategy B" , 10 , 9 ,
+ 202324 , "national" , "Strategy B" , 12 , 11
+ )
+
+ expected_a <- tibble::tribble(
+ ~fyear , ~provider , ~strategy , ~rate , ~national_rate ,
+ 202223 , "a" , "Strategy A" , 2 , 6 ,
+ 202223 , "b" , "Strategy A" , 4 , 6 ,
+ 202324 , "a" , "Strategy A" , 8 , 12 ,
+ 202324 , "b" , "Strategy A" , 10 , 12
+ )
+
+ expected_b <- tibble::tribble(
+ ~fyear , ~provider , ~strategy , ~rate , ~national_rate ,
+ 202223 , "a" , "Strategy B" , 1 , 5 ,
+ 202223 , "b" , "Strategy B" , 3 , 5 ,
+ 202324 , "a" , "Strategy B" , 7 , 11 ,
+ 202324 , "b" , "Strategy B" , 9 , 11
+ )
+ # nolint end
+
+ # act
+ actual_a <- get_rates_data(inputs_data_sample, "Strategy A")
+ actual_b <- get_rates_data(inputs_data_sample, "Strategy B")
+
+ # assert
+ expect_equal(actual_a, expected_a)
+ expect_equal(actual_b, expected_b)
+})
+
+test_that("get_rates_trend_data", {
+ # arrange
+ # nolint start
+ df <- tibble::tribble(
+ ~fyear , ~provider , ~strategy , ~rate , ~national_rate ,
+ 202223 , "a" , "Strategy A" , 2 , 6 ,
+ 202223 , "b" , "Strategy A" , 4 , 6 ,
+ 202324 , "a" , "Strategy A" , 8 , 12 ,
+ 202324 , "b" , "Strategy A" , 10 , 12
+ )
+
+ expected <- tibble::tribble(
+ ~fyear , ~provider , ~strategy , ~rate , ~national_rate ,
+ 202223 , "a" , "Strategy A" , 2 , 6 ,
+ 202324 , "a" , "Strategy A" , 8 , 12
+ )
+ # nolint end
+
+ # act
+ actual <- get_rates_trend_data(df, "a")
+
+ # assert
+ expect_equal(actual, expected)
+})
diff --git a/tests/testthat/test-utils_server.R b/tests/testthat/test-utils_server.R
new file mode 100644
index 0000000..ae60f17
--- /dev/null
+++ b/tests/testthat/test-utils_server.R
@@ -0,0 +1,216 @@
+test_that("get_container uses get_azure_token when not in a managed environment", {
+ # arrange
+ m_get_managed_token <- \() stop("expected error")
+ m_get_azure_token <- mock("token")
+ m_blob_endpoint <- mock("ep")
+ m_storage_container <- mock("container")
+
+ stub(get_container, "AzureAuth::get_managed_token", m_get_managed_token)
+ stub(get_container, "AzureAuth::get_azure_token", m_get_azure_token)
+ stub(get_container, "AzureStor::blob_endpoint", m_blob_endpoint)
+ stub(get_container, "AzureStor::storage_container", m_storage_container)
+
+ # act
+ actual <- get_container("ep_uri", "container_name")
+
+ # assert
+ expect_called(m_get_azure_token, 1)
+ expect_args(
+ m_get_azure_token,
+ 1,
+ resource = "https://storage.azure.com",
+ tenant = "common",
+ app = "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
+ use_cache = TRUE
+ )
+
+ expect_called(m_blob_endpoint, 1)
+ expect_args(m_blob_endpoint, 1, "ep_uri", token = "token")
+
+ expect_called(m_storage_container, 1)
+ expect_args(m_storage_container, 1, "ep", "container_name")
+
+ expect_equal(actual, "container")
+})
+
+test_that("get_container uses get_managed_token when in a managed environment", {
+ # arrange
+ m_get_managed_token <- mock("token")
+ m_get_azure_token <- mock()
+ m_blob_endpoint <- mock("ep")
+ m_storage_container <- mock("container")
+
+ stub(get_container, "AzureAuth::get_managed_token", m_get_managed_token)
+ stub(get_container, "AzureAuth::get_azure_token", m_get_azure_token)
+ stub(get_container, "AzureStor::blob_endpoint", m_blob_endpoint)
+ stub(get_container, "AzureStor::storage_container", m_storage_container)
+
+ # act
+ actual <- get_container("ep_uri", "container_name")
+
+ # assert
+ expect_called(m_get_managed_token, 1)
+ expect_args(m_get_managed_token, 1, "https://storage.azure.com/")
+
+ expect_called(m_get_azure_token, 0)
+
+ expect_called(m_blob_endpoint, 1)
+ expect_args(m_blob_endpoint, 1, "ep_uri", token = "token")
+
+ expect_called(m_storage_container, 1)
+ expect_args(m_storage_container, 1, "ep", "container_name")
+
+ expect_equal(actual, "container")
+})
+
+test_that("get_all_geo_data works for nhp geography", {
+ # arrange
+ data_types <- c(
+ "age_sex",
+ "diagnoses",
+ "procedures",
+ "rates"
+ )
+
+ mock_age_sex <- data.frame(provider = c("P1", "P2"), age = c(30, 40))
+ mock_diagnoses <- data.frame(
+ provider = c("P1", "P2"),
+ diagnosis = c("D1", "D2")
+ )
+ mock_procedures <- data.frame(
+ provider = c("P1", "P2"),
+ procedure = c("PR1", "PR2")
+ )
+ mock_rates <- data.frame(provider = c("P1", "P2"), rate = c(0.1, 0.2))
+
+ m_get_container <- mock("container")
+ m_read_azure_parquet <- mock(
+ mock_age_sex,
+ mock_diagnoses,
+ mock_procedures,
+ mock_rates
+ )
+
+ withr::local_envvar("DATA_VERSION" = "dev")
+
+ local_mocked_bindings("get_container" = m_get_container)
+ local_mocked_bindings(
+ "read_azure_parquet" = m_read_azure_parquet,
+ .package = "azkit"
+ )
+
+ # act
+ result <- get_all_geo_data("nhp")
+
+ # assert
+ expect_type(result, "list")
+ expect_length(result, 4)
+ expect_named(result, data_types)
+
+ # verify get_container was called correctly
+ expect_called(m_get_container, 1)
+ expect_args(m_get_container, 1)
+
+ # verify read_azure_parquet was called 4 times with correct arguments
+ expect_called(m_read_azure_parquet, 4)
+
+ for (i in seq_along(data_types)) {
+ expect_args(
+ m_read_azure_parquet,
+ i,
+ "container",
+ data_types[i],
+ "dev/provider"
+ )
+ }
+
+ # verify the data is returned correctly
+ expect_equal(result$age_sex, mock_age_sex)
+ expect_equal(result$diagnoses, mock_diagnoses)
+ expect_equal(result$procedures, mock_procedures)
+ expect_equal(result$rates, mock_rates)
+})
+
+test_that("get_all_geo_data works for la geography and renames lad23cd to provider", {
+ # arrange
+ data_types <- c(
+ "age_sex",
+ "diagnoses",
+ "procedures",
+ "rates"
+ )
+
+ mock_age_sex <- data.frame(lad23cd = c("P1", "P2"), age = c(30, 40))
+ mock_diagnoses <- data.frame(
+ lad23cd = c("P1", "P2"),
+ diagnosis = c("D1", "D2")
+ )
+ mock_procedures <- data.frame(
+ lad23cd = c("P1", "P2"),
+ procedure = c("PR1", "PR2")
+ )
+ mock_rates <- data.frame(lad23cd = c("P1", "P2"), rate = c(0.1, 0.2))
+
+ expected_age_sex <- dplyr::rename(mock_age_sex, provider = lad23cd)
+ expected_diagnoses <- dplyr::rename(mock_diagnoses, provider = lad23cd)
+ expected_procedures <- dplyr::rename(mock_procedures, provider = lad23cd)
+ expected_rates <- dplyr::rename(mock_rates, provider = lad23cd)
+
+ m_get_container <- mock("container")
+ m_read_azure_parquet <- mock(
+ mock_age_sex,
+ mock_diagnoses,
+ mock_procedures,
+ mock_rates
+ )
+
+ withr::local_envvar("DATA_VERSION" = "dev")
+
+ local_mocked_bindings("get_container" = m_get_container)
+ local_mocked_bindings(
+ "read_azure_parquet" = m_read_azure_parquet,
+ .package = "azkit"
+ )
+
+ # act
+ result <- get_all_geo_data("la")
+
+ # assert
+ expect_type(result, "list")
+ expect_length(result, 4)
+ expect_named(result, data_types)
+
+ # verify get_container was called correctly
+ expect_called(m_get_container, 1)
+ expect_args(m_get_container, 1)
+
+ # verify read_azure_parquet was called 4 times with correct arguments
+ expect_called(m_read_azure_parquet, 4)
+
+ for (i in seq_along(data_types)) {
+ expect_args(
+ m_read_azure_parquet,
+ i,
+ "container",
+ data_types[i],
+ "dev/lad23cd"
+ )
+ }
+
+ # verify lad23cd column was renamed to provider
+ expect_equal(result$age_sex, expected_age_sex)
+ expect_equal(result$diagnoses, expected_diagnoses)
+ expect_equal(result$procedures, expected_procedures)
+ expect_equal(result$rates, expected_rates)
+})
+
+test_that("get_all_geo_data throws error for unknown geography", {
+ # arrange
+ local_mocked_bindings("get_container" = mock())
+
+ # act and assert
+ expect_error(
+ get_all_geo_data("unknown_geography"),
+ "Unknown geography"
+ )
+})
diff --git a/tests/testthat/test-utils_show_strategy_text.R b/tests/testthat/test-utils_show_strategy_text.R
new file mode 100644
index 0000000..b31dc2f
--- /dev/null
+++ b/tests/testthat/test-utils_show_strategy_text.R
@@ -0,0 +1,32 @@
+test_that("fetch_strategy_text works", {
+ # arrange
+ m <- function(req) {
+ httr2::response(status_code = 200, body = charToRaw("strategy text"))
+ }
+ httr2::local_mocked_responses(m)
+
+ # act
+ text <- fetch_strategy_text("stub")
+
+ # assert
+ expect_equal(text, "strategy text")
+})
+
+test_that("fetch_strategy_text calls correct url", {
+ # arrange
+ local_mocked_bindings(
+ "req_perform" = identity,
+ "resp_body_string" = identity,
+ .package = "httr2"
+ )
+
+ # act
+ req <- fetch_strategy_text("stub")
+
+ # assert
+
+ expect_equal(
+ req$url,
+ "https://raw.githubusercontent.com/The-Strategy-Unit/nhp_inputs/refs/heads/main/inst/app/strategy_text/stub.md" # nolint
+ )
+})
diff --git a/tests/testthat/test-utils_table.R b/tests/testthat/test-utils_table.R
new file mode 100644
index 0000000..4728484
--- /dev/null
+++ b/tests/testthat/test-utils_table.R
@@ -0,0 +1,353 @@
+# Tests for prepare_procedures_data ----
+
+test_that("prepare_procedures_data returns NULL when no data matches filter", {
+ # arrange
+ procedures_data <- data.frame(
+ provider = c("ABC", "DEF"),
+ strategy = c("strategy1", "strategy2"),
+ fyear = c(202324, 202324),
+ procedure_code = c("P1", "P2"),
+ n = c(100, 200),
+ pcnt = c(0.5, 0.5)
+ )
+
+ procedures_lookup <- data.frame(
+ code = c("P1", "P2"),
+ description = c("Procedure 1", "Procedure 2")
+ )
+
+ # act
+ result <- prepare_procedures_data(
+ procedures_data,
+ procedures_lookup,
+ provider = "XYZ",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_null(result)
+})
+
+test_that("prepare_procedures_data filters and joins data correctly", {
+ # arrange
+ procedures_data <- data.frame(
+ provider = c("RCF", "RCF", "ABC"),
+ strategy = c("strategy1", "strategy1", "strategy1"),
+ fyear = c(202324, 202324, 202324),
+ procedure_code = c("P1", "P2", "P3"),
+ n = c(100, 200, 50),
+ pcnt = c(0.4, 0.6, 0.5)
+ )
+
+ procedures_lookup <- data.frame(
+ code = c("P1", "P2", "P3"),
+ description = c("Procedure 1", "Procedure 2", "Procedure 3")
+ )
+
+ # act
+ result <- prepare_procedures_data(
+ procedures_data,
+ procedures_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_equal(colnames(result), c("procedure_description", "n", "pcnt"))
+ expect_equal(result$procedure_description, c("Procedure 1", "Procedure 2"))
+ expect_equal(result$n, c(100, 200))
+ expect_equal(result$pcnt, c(0.4, 0.6))
+})
+
+test_that("prepare_procedures_data handles unknown procedure codes", {
+ # arrange
+ procedures_data <- data.frame(
+ provider = c("RCF", "RCF"),
+ strategy = c("strategy1", "strategy1"),
+ fyear = c(202324, 202324),
+ procedure_code = c("P1", "UNKNOWN"),
+ n = c(100, 50),
+ pcnt = c(0.6, 0.4)
+ )
+
+ procedures_lookup <- data.frame(
+ code = c("P1"),
+ description = c("Procedure 1")
+ )
+
+ # act
+ result <- prepare_procedures_data(
+ procedures_data,
+ procedures_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_equal(
+ result$procedure_description,
+ c("Procedure 1", "Unknown/Invalid Procedure Code")
+ )
+})
+
+test_that("prepare_procedures_data adds 'Other' row when pcnt_total < 1", {
+ # arrange
+ procedures_data <- data.frame(
+ provider = c("RCF", "RCF"),
+ strategy = c("strategy1", "strategy1"),
+ fyear = c(202324, 202324),
+ procedure_code = c("P1", "P2"),
+ n = c(100, 50),
+ pcnt = c(0.4, 0.2)
+ )
+
+ procedures_lookup <- data.frame(
+ code = c("P1", "P2"),
+ description = c("Procedure 1", "Procedure 2")
+ )
+
+ # act
+ result <- prepare_procedures_data(
+ procedures_data,
+ procedures_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 3)
+ expect_equal(
+ result$procedure_description,
+ c("Procedure 1", "Procedure 2", "Other")
+ )
+ expect_equal(result$pcnt[3], 0.4)
+ expect_equal(result$n[3], 150 * 0.4 / 0.6)
+})
+
+test_that("prepare_procedures_data does not add 'Other' row when pcnt_total = 1", {
+ # arrange
+ procedures_data <- data.frame(
+ provider = c("RCF", "RCF"),
+ strategy = c("strategy1", "strategy1"),
+ fyear = c(202324, 202324),
+ procedure_code = c("P1", "P2"),
+ n = c(100, 50),
+ pcnt = c(0.6, 0.4)
+ )
+
+ procedures_lookup <- data.frame(
+ code = c("P1", "P2"),
+ description = c("Procedure 1", "Procedure 2")
+ )
+
+ # act
+ result <- prepare_procedures_data(
+ procedures_data,
+ procedures_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_false("Other" %in% result$procedure_description)
+})
+
+# Tests for prepare_diagnoses_data ----
+
+test_that("prepare_diagnoses_data returns NULL when no data matches filter", {
+ # arrange
+ diagnoses_data <- data.frame(
+ provider = c("ABC", "DEF"),
+ strategy = c("strategy1", "strategy2"),
+ fyear = c(202324, 202324),
+ diagnosis = c("D1", "D2"),
+ n = c(100, 200),
+ pcnt = c(0.5, 0.5)
+ )
+
+ diagnoses_lookup <- data.frame(
+ diagnosis_code = c("D1", "D2"),
+ diagnosis_description = c("Diagnosis 1", "Diagnosis 2")
+ )
+
+ # act
+ result <- prepare_diagnoses_data(
+ diagnoses_data,
+ diagnoses_lookup,
+ provider = "XYZ",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_null(result)
+})
+
+test_that("prepare_diagnoses_data filters and joins data correctly", {
+ # arrange
+ diagnoses_data <- data.frame(
+ provider = c("RCF", "RCF", "ABC"),
+ strategy = c("strategy1", "strategy1", "strategy1"),
+ fyear = c(202324, 202324, 202324),
+ diagnosis = c("D1", "D2", "D3"),
+ n = c(100, 200, 50),
+ pcnt = c(0.4, 0.6, 0.5)
+ )
+
+ diagnoses_lookup <- data.frame(
+ diagnosis_code = c("D1", "D2", "D3"),
+ diagnosis_description = c("Diagnosis 1", "Diagnosis 2", "Diagnosis 3")
+ )
+
+ # act
+ result <- prepare_diagnoses_data(
+ diagnoses_data,
+ diagnoses_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_equal(colnames(result), c("diagnosis_description", "n", "pcnt"))
+ expect_equal(result$diagnosis_description, c("Diagnosis 1", "Diagnosis 2"))
+ expect_equal(result$n, c(100, 200))
+ expect_equal(result$pcnt, c(0.4, 0.6))
+})
+
+test_that("prepare_diagnoses_data handles unknown diagnosis codes", {
+ # arrange
+ diagnoses_data <- data.frame(
+ provider = c("RCF", "RCF"),
+ strategy = c("strategy1", "strategy1"),
+ fyear = c(202324, 202324),
+ diagnosis = c("D1", "UNKNOWN"),
+ n = c(100, 50),
+ pcnt = c(0.6, 0.4)
+ )
+
+ diagnoses_lookup <- data.frame(
+ diagnosis_code = c("D1"),
+ diagnosis_description = c("Diagnosis 1")
+ )
+
+ # act
+ result <- prepare_diagnoses_data(
+ diagnoses_data,
+ diagnoses_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_equal(
+ result$diagnosis_description,
+ c("Diagnosis 1", "Unknown/Invalid Diagnosis Code")
+ )
+})
+
+test_that("prepare_diagnoses_data adds 'Other' row when pcnt_total < 1", {
+ # arrange
+ diagnoses_data <- data.frame(
+ provider = c("RCF", "RCF"),
+ strategy = c("strategy1", "strategy1"),
+ fyear = c(202324, 202324),
+ diagnosis = c("D1", "D2"),
+ n = c(100, 50),
+ pcnt = c(0.4, 0.2)
+ )
+
+ diagnoses_lookup <- data.frame(
+ diagnosis_code = c("D1", "D2"),
+ diagnosis_description = c("Diagnosis 1", "Diagnosis 2")
+ )
+
+ # act
+ result <- prepare_diagnoses_data(
+ diagnoses_data,
+ diagnoses_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 3)
+ expect_equal(
+ result$diagnosis_description,
+ c("Diagnosis 1", "Diagnosis 2", "Other")
+ )
+ expect_equal(result$pcnt[3], 0.4)
+ expect_equal(result$n[3], 150 * 0.4 / 0.6)
+})
+
+test_that("prepare_diagnoses_data does not add 'Other' row when pcnt_total = 1", {
+ # arrange
+ diagnoses_data <- data.frame(
+ provider = c("RCF", "RCF"),
+ strategy = c("strategy1", "strategy1"),
+ fyear = c(202324, 202324),
+ diagnosis = c("D1", "D2"),
+ n = c(100, 50),
+ pcnt = c(0.6, 0.4)
+ )
+
+ diagnoses_lookup <- data.frame(
+ diagnosis_code = c("D1", "D2"),
+ diagnosis_description = c("Diagnosis 1", "Diagnosis 2")
+ )
+
+ # act
+ result <- prepare_diagnoses_data(
+ diagnoses_data,
+ diagnoses_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_false("Other" %in% result$diagnosis_description)
+})
+
+test_that("prepare_diagnoses_data filters by correct year", {
+ # arrange
+ diagnoses_data <- data.frame(
+ provider = c("RCF", "RCF", "RCF"),
+ strategy = c("strategy1", "strategy1", "strategy1"),
+ fyear = c(202324, 202425, 202324),
+ diagnosis = c("D1", "D2", "D3"),
+ n = c(100, 200, 50),
+ pcnt = c(0.5, 0.5, 0.5)
+ )
+
+ diagnoses_lookup <- data.frame(
+ diagnosis_code = c("D1", "D2", "D3"),
+ diagnosis_description = c("Diagnosis 1", "Diagnosis 2", "Diagnosis 3")
+ )
+
+ # act
+ result <- prepare_diagnoses_data(
+ diagnoses_data,
+ diagnoses_lookup,
+ provider = "RCF",
+ strategy = "strategy1",
+ selected_year = 202324
+ )
+
+ # assert
+ expect_equal(nrow(result), 2)
+ expect_equal(result$diagnosis_description, c("Diagnosis 1", "Diagnosis 3"))
+})