From cfa321ba6cb87cf57f138a2f11f836061deb97d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 13:50:40 +0100 Subject: [PATCH 01/11] Fix warning for partial argument match --- R/g_lineplot.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/g_lineplot.R b/R/g_lineplot.R index e2f77836..d2dc5098 100644 --- a/R/g_lineplot.R +++ b/R/g_lineplot.R @@ -607,6 +607,6 @@ unique_name <- function(newname, old_names) { } gg_color_hue <- function(n) { - hues <- seq(15, 375, length = n + 1) + hues <- seq(15, 375, length.out = n + 1) grDevices::hcl(h = hues, l = 65, c = 100)[1:n] } From 5353cba91fb28b7e456464b18275c42f024aa0e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 13:50:56 +0100 Subject: [PATCH 02/11] Upgrade testthat being used --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index f0a49e07..6bbd2512 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -57,6 +57,7 @@ Config/Needs/verdepcheck: tidyverse/dplyr, mllg/checkmate, yihui/knitr, insightsengineering/nestcolor, rstudio/rmarkdown, r-lib/testthat, tidyverse/tidyr Config/Needs/website: insightsengineering/nesttemplate +Config/testthat/edition: 3 Encoding: UTF-8 Language: en-US LazyData: true From deb000fc0c914462596bc9cca2be39b68b411d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 13:51:42 +0100 Subject: [PATCH 03/11] Add tests: up to 92.58% --- tests/testthat.R | 6 + tests/testthat/helper-data.R | 91 ++++++ tests/testthat/test-g_boxplot.R | 147 +++++++++ tests/testthat/test-g_correlationplot.R | 278 ++++++++++++++++++ .../test-g_density_distribution_plot.R | 95 ++++++ tests/testthat/test-g_lineplot.R | 176 +++++++++++ tests/testthat/test-g_scatterplot.R | 6 + tests/testthat/test-g_spaghettiplot.R | 99 +++++++ tests/testthat/test-geom_axes_line.R | 97 ++++++ tests/testthat/test-t_summarytable.R | 99 +++++++ tests/testthat/test-utils.R | 53 ++++ 11 files changed, 1147 insertions(+) create mode 100644 tests/testthat.R create mode 100644 tests/testthat/helper-data.R create mode 100644 tests/testthat/test-g_boxplot.R create mode 100644 tests/testthat/test-g_correlationplot.R create mode 100644 tests/testthat/test-g_density_distribution_plot.R create mode 100644 tests/testthat/test-g_lineplot.R create mode 100644 tests/testthat/test-g_scatterplot.R create mode 100644 tests/testthat/test-g_spaghettiplot.R create mode 100644 tests/testthat/test-geom_axes_line.R create mode 100644 tests/testthat/test-t_summarytable.R create mode 100644 tests/testthat/test-utils.R diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 00000000..a09dcc51 --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,6 @@ +# testthat is a suggested package: only run the tests when it is available. +if (requireNamespace("testthat", quietly = TRUE)) { + library(testthat) + library(goshawk) + test_check("goshawk") +} diff --git a/tests/testthat/helper-data.R b/tests/testthat/helper-data.R new file mode 100644 index 00000000..26454665 --- /dev/null +++ b/tests/testthat/helper-data.R @@ -0,0 +1,91 @@ +# Deterministic, self-contained lab data (ADaM-like) used by all tests. +# 12 subjects (4 per arm), 3 visits, 2 parameters (CRP, ALT). +make_adlb <- function() { + arms <- c("A: Drug X", "B: Placebo", "C: Combination") + subj <- data.frame( + USUBJID = sprintf("S-%02d", 1:12), + ARM = rep(arms, each = 4), + SEX = rep(c("F", "M"), 6), + stringsAsFactors = FALSE + ) + visits <- data.frame( + AVISITCD = c("BL", "W 1", "W 5"), + AVISITCDN = c(0, 1, 5), + stringsAsFactors = FALSE + ) + params <- data.frame( + PARAMCD = c("CRP", "ALT"), + PARAM = c("C-reactive protein", "Alanine transaminase"), + AVALU = c("mg/L", "U/L"), + ANRLO = c(0.5, 20), + ANRHI = c(1.5, 60), + stringsAsFactors = FALSE + ) + dat <- merge(merge(subj, visits, by = NULL), params, by = NULL) + dat <- dat[order(dat$PARAMCD, dat$USUBJID, dat$AVISITCDN), ] + rownames(dat) <- NULL + + idx <- seq_len(nrow(dat)) + scale_p <- ifelse(dat$PARAMCD == "CRP", 1, 40) + dat$AVAL <- round(scale_p * (0.6 + 0.05 * (idx %% 7) + 0.1 * dat$AVISITCDN + 0.02 * sin(idx)), 3) + dat$BASE <- round(scale_p * (0.7 + 0.04 * (idx %% 5) + 0.03 * cos(idx)), 3) + dat$STUDYID <- "STUDY-1" + dat$ARMCD <- c("A", "B", "C")[match(dat$ARM, arms)] + dat$TRTORD <- match(dat$ARM, arms) + dat$LBSTRESC <- as.character(dat$AVAL) + dat$LOQFL <- "N" + + # one lower and one upper limit of quantification per parameter + lloq <- dat$USUBJID == "S-01" & dat$AVISITCD == "BL" + uloq <- dat$USUBJID == "S-02" & dat$AVISITCD == "W 1" + dat$LBSTRESC[lloq & dat$PARAMCD == "CRP"] <- "< 0.5" + dat$LBSTRESC[uloq & dat$PARAMCD == "CRP"] <- "> 1.5" + dat$LBSTRESC[lloq & dat$PARAMCD == "ALT"] <- "< 20" + dat$LBSTRESC[uloq & dat$PARAMCD == "ALT"] <- "> 60" + dat$LOQFL[lloq | uloq] <- "Y" + + # one missing CRP result: S-03 at W 5 + dat$AVAL[dat$USUBJID == "S-03" & dat$AVISITCD == "W 5" & dat$PARAMCD == "CRP"] <- NA_real_ + + dat$ARM <- factor(dat$ARM, levels = arms) + attr(dat$ARM, "label") <- "Description of Planned Arm" + dat$SEX <- factor(dat$SEX) + attr(dat$ANRLO, "label") <- "Analysis Normal Range Lower Limit" + attr(dat$ANRHI, "label") <- "Analysis Normal Range Upper Limit" + + loqs <- goshawk:::h_identify_loq_values(dat, "LOQFL") + dat <- merge(dat, loqs, by = "PARAM", sort = FALSE) + dat <- dat[order(dat$PARAMCD, dat$USUBJID, dat$AVISITCDN), ] + rownames(dat) <- NULL + dat +} + +arm_colors <- c( + "A: Drug X" = "#000000", + "B: Placebo" = "#3498DB", + "C: Combination" = "#E74C3C" +) + +# Wide-format data for g_correlationplot: one row per subject and visit. +make_corr_data <- function() { + adlb <- make_adlb() + crp <- adlb[adlb$PARAMCD == "CRP", ] + alt <- adlb[adlb$PARAMCD == "ALT", ] + key <- c("USUBJID", "AVISITCD") + dat <- merge( + crp[, c(key, "ARM", "AVAL", "LBSTRESC", "PARAM", "LOQFL", "ANRHI", "ANRLO", "ULOQN", "LLOQN")], + alt[, c(key, "BASE", "LBSTRESC", "PARAM", "LOQFL", "ANRHI", "ANRLO", "ULOQN", "LLOQN")], + by = key, + suffixes = c(".CRP", ".ALT") + ) + names(dat)[names(dat) == "LBSTRESC.CRP"] <- "LBSTRESC_CRP" + names(dat)[names(dat) == "LBSTRESC.ALT"] <- "LBSTRESC_ALT" + names(dat)[names(dat) == "PARAM.CRP"] <- "PARAM_CRP" + names(dat)[names(dat) == "PARAM.ALT"] <- "PARAM_ALT" + dat$LOQFL_COMB <- ifelse(dat$LOQFL.CRP == "Y" | dat$LOQFL.ALT == "Y", "Y", "N") + dat <- dat[!is.na(dat$AVAL) & !is.na(dat$BASE), ] + dat$ARM <- factor(dat$ARM, levels = levels(adlb$ARM)) + attr(dat$ARM, "label") <- "Description of Planned Arm" + rownames(dat) <- NULL + dat +} diff --git a/tests/testthat/test-g_boxplot.R b/tests/testthat/test-g_boxplot.R new file mode 100644 index 00000000..20387a9e --- /dev/null +++ b/tests/testthat/test-g_boxplot.R @@ -0,0 +1,147 @@ +# Requirement: Function to create a boxplot. +adlb <- make_adlb() + +test_that("g_boxplot returns a ggplot object", { + p <- g_boxplot( + adlb, + biomarker = "CRP", + yaxis_var = "AVAL", + trt_group = "ARM", + xaxis_var = "STUDYID" + ) + expect_s3_class(p, "ggplot") +}) + +test_that("g_boxplot works with units, facets, colors, shapes and fonts", { + p <- g_boxplot( + adlb, + biomarker = "CRP", + param_var = "PARAMCD", + yaxis_var = "AVAL", + trt_group = "ARM", + xaxis_var = "STUDYID", + loq_flag_var = "LOQFL", + loq_legend = FALSE, + unit = "AVALU", + color_manual = arm_colors, + shape_manual = c("N" = 1, "Y" = 2), + facet_var = "AVISITCD", + facet_ncol = 2, + alpha = 0.5, + dot_size = 3, + font_size = 10, + rotate_xlab = TRUE + ) + expect_s3_class(p, "ggplot") + expect_match(p$labels$title, "mg/L") + expect_match(p$labels$y, "mg/L") +}) + +test_that("g_boxplot handles facet_ncol not set, no box and an empty unit", { + p <- g_boxplot( + adlb, + biomarker = "ALT", + yaxis_var = "BASE", + trt_group = "ARM", + xaxis_var = "STUDYID", + box = FALSE, + facet_var = "AVISITCD", + facet_ncol = NULL, + ylim = c(0, 100) + ) + expect_s3_class(p, "ggplot") + + adlb_nounit <- adlb + adlb_nounit$AVALU <- "" + p_nounit <- g_boxplot( + adlb_nounit, + biomarker = "CRP", + yaxis_var = "AVAL", + trt_group = "ARM", + xaxis_var = "STUDYID", + unit = "AVALU" + ) + expect_s3_class(p_nounit, "ggplot") + expect_false(grepl("\\(", p_nounit$labels$y)) +}) + +test_that("g_boxplot skips facetting for facet_var = 'None'", { + p <- g_boxplot( + adlb, + biomarker = "CRP", + yaxis_var = "AVAL", + trt_group = "ARM", + xaxis_var = "STUDYID", + facet_var = "None" + ) + expect_s3_class(p$facet, "FacetNull") +}) + +test_that("g_boxplot takes the most common unit of a factor unit variable", { + adlb_f <- adlb + adlb_f$AVALU <- factor(adlb_f$AVALU) + p <- g_boxplot( + adlb_f, + biomarker = "CRP", + yaxis_var = "AVAL", + trt_group = "ARM", + xaxis_var = "STUDYID", + unit = "AVALU" + ) + expect_match(p$labels$y, "mg/L") +}) + +test_that("g_boxplot adds horizontal lines from values and variables", { + p <- g_boxplot( + adlb, + biomarker = "CRP", + yaxis_var = "AVAL", + trt_group = "ARM", + xaxis_var = "STUDYID", + hline_arb = c(0.9, 1.2), + hline_arb_color = "blue", + hline_arb_label = "Arbitrary", + hline_vars = c("ANRHI", "ANRLO", "ULOQN", "LLOQN"), + hline_vars_colors = c("pink", "brown", "purple", "gray"), + hline_vars_labels = c("A", "B", "C", "D") + ) + expect_s3_class(p, "ggplot") + n_hline <- sum(vapply(p$layers, function(l) inherits(l$geom, "GeomHline"), logical(1))) + expect_equal(n_hline, 2) +}) + +test_that("g_boxplot validates its arguments", { + expect_error( + g_boxplot(adlb, biomarker = "CRP", param_var = "NOPE", yaxis_var = "AVAL", trt_group = "ARM", xaxis_var = "STUDYID"), + "is not in data" + ) + expect_error( + g_boxplot(adlb, biomarker = "XYZ", yaxis_var = "AVAL", trt_group = "ARM", xaxis_var = "STUDYID"), + "is not found in param_var" + ) + expect_error( + g_boxplot( + adlb, + biomarker = "CRP", yaxis_var = "AVAL", trt_group = "ARM", xaxis_var = "STUDYID", unit = "NOPE" + ), + "is not in data" + ) + expect_error( + g_boxplot( + adlb, + biomarker = "CRP", yaxis_var = "AVAL", trt_group = "ARM", xaxis_var = "STUDYID", loq_legend = "yes" + ) + ) + expect_error( + g_boxplot( + adlb, + biomarker = "CRP", yaxis_var = "AVAL", trt_group = "ARM", xaxis_var = "STUDYID", dot_size = "big" + ) + ) + expect_error( + g_boxplot( + adlb, + biomarker = "CRP", yaxis_var = "AVAL", trt_group = "ARM", xaxis_var = "STUDYID", ylim = c(1, 2, 3) + ) + ) +}) diff --git a/tests/testthat/test-g_correlationplot.R b/tests/testthat/test-g_correlationplot.R new file mode 100644 index 00000000..01539752 --- /dev/null +++ b/tests/testthat/test-g_correlationplot.R @@ -0,0 +1,278 @@ +# Requirement: Function to create a correlation plot. +corr <- make_corr_data() + +corr_args <- function(...) { + args <- list( + data = corr, + param_var = "PARAMCD", + xaxis_param = "CRP", + xaxis_var = "AVAL", + xvar = "AVAL", + yaxis_param = "ALT", + yaxis_var = "BASE", + yvar = "BASE", + trt_group = "ARM", + visit = "AVISITCD", + loq_flag_var = "LOQFL_COMB", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT" + ) + utils::modifyList(args, list(...)) +} + +test_that("g_correlationplot returns a ggplot object", { + p <- g_correlationplot( + data = corr, + param_var = "PARAMCD", + xaxis_param = "CRP", + xaxis_var = "AVAL", + xvar = "AVAL", + yaxis_param = "ALT", + yaxis_var = "BASE", + yvar = "BASE", + trt_group = "ARM", + visit = "AVISITCD", + loq_flag_var = "LOQFL_COMB", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT" + ) + expect_s3_class(p, "ggplot") + expect_equal(p$labels$title, "Correlation of ALT to CRP") +}) + +test_that("g_correlationplot supports the different facetting options", { + p_visit_arm <- g_correlationplot( + data = corr, + param_var = "PARAMCD", + xaxis_param = "CRP", + xaxis_var = "AVAL", + xvar = "AVAL", + yaxis_param = "ALT", + yaxis_var = "BASE", + yvar = "BASE", + trt_group = "ARM", + visit = "AVISITCD", + loq_flag_var = "LOQFL_COMB", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT", + visit_facet = TRUE, + facet = TRUE, + facet_var = "ARM") + expect_s3_class(p_visit_arm, "ggplot") + expect_s3_class(p_visit_arm$facet, "Facet") + + p_visit <- g_correlationplot( + data = corr, + param_var = "PARAMCD", + xaxis_param = "CRP", + xaxis_var = "AVAL", + xvar = "AVAL", + yaxis_param = "ALT", + yaxis_var = "BASE", + yvar = "BASE", + trt_group = "ARM", + visit = "AVISITCD", + loq_flag_var = "LOQFL_COMB", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT", + visit_facet = TRUE, + facet = TRUE, + facet_var = "ARM") + expect_s3_class(p_visit$facet, "Facet") + + p_arm <- g_correlationplot( + data = corr, + param_var = "PARAMCD", + xaxis_param = "CRP", + xaxis_var = "AVAL", + xvar = "AVAL", + yaxis_param = "ALT", + yaxis_var = "BASE", + yvar = "BASE", + trt_group = "ARM", + visit = "AVISITCD", + loq_flag_var = "LOQFL_COMB", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT", + facet = TRUE, + facet_var = "ARM", + visit_facet = FALSE) + expect_s3_class(p_arm$facet, "Facet") + + p_none <- g_correlationplot( + data = corr, + param_var = "PARAMCD", + xaxis_param = "CRP", + xaxis_var = "AVAL", + xvar = "AVAL", + yaxis_param = "ALT", + yaxis_var = "BASE", + yvar = "BASE", + trt_group = "ARM", + visit = "AVISITCD", + loq_flag_var = "LOQFL_COMB", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT", + facet = FALSE, + visit_facet = FALSE, + facet_var = "ARM") + + expect_s3_class(p_none$facet, "FacetNull") +}) + +test_that("Example", { + skip_if_not_installed("tidyr") + library(stringr) + library(tidyr) + + # original ARM value = dose value + arm_mapping <- list( + "A: Drug X" = "150mg QD", + "B: Placebo" = "Placebo", + "C: Combination" = "Combination" + ) + color_manual <- c("150mg QD" = "#000000", "Placebo" = "#3498DB", "Combination" = "#E74C3C") + # assign LOQ flag symbols: circles for "N" and triangles for "Y", squares for "NA" + shape_manual <- c("N" = 1, "Y" = 2, "NA" = 0) + + ADLB <- rADLB + var_labels <- lapply(ADLB, function(x) attributes(x)$label) + ADLB <- ADLB |> + mutate(AVISITCD = case_when( + AVISIT == "SCREENING" ~ "SCR", + AVISIT == "BASELINE" ~ "BL", + grepl("WEEK", AVISIT) ~ + paste( + "W", + trimws( + substr( + AVISIT, + start = 6, + stop = str_locate(AVISIT, "DAY") - 1 + ) + ) + ), + TRUE ~ NA_character_ + )) |> + mutate(AVISITCDN = case_when( + AVISITCD == "SCR" ~ -2, + AVISITCD == "BL" ~ 0, + grepl("W", AVISITCD) ~ as.numeric(gsub("\\D+", "", AVISITCD)), + TRUE ~ NA_real_ + )) |> + # use ARMCD values to order treatment in visualization legend + mutate(TRTORD = ifelse(grepl("C", ARMCD), 1, + ifelse(grepl("B", ARMCD), 2, + ifelse(grepl("A", ARMCD), 3, NA) + ) + )) |> + mutate(ARM = as.character(arm_mapping[match(ARM, names(arm_mapping))])) |> + mutate(ARM = factor(ARM) |> + reorder(TRTORD)) |> + mutate( + ANRHI = case_when( + PARAMCD == "ALT" ~ 60, + PARAMCD == "CRP" ~ 70, + PARAMCD == "IGA" ~ 80, + TRUE ~ NA_real_ + ), + ANRLO = case_when( + PARAMCD == "ALT" ~ 20, + PARAMCD == "CRP" ~ 30, + PARAMCD == "IGA" ~ 40, + TRUE ~ NA_real_ + ) + ) |> + rowwise() |> + group_by(PARAMCD) |> + mutate(LBSTRESC = ifelse( + USUBJID %in% sample(USUBJID, 1, replace = TRUE), + paste("<", round(runif(1, min = 25, max = 30))), LBSTRESC + )) |> + mutate(LBSTRESC = ifelse( + USUBJID %in% sample(USUBJID, 1, replace = TRUE), + paste(">", round(runif(1, min = 70, max = 75))), LBSTRESC + )) |> + ungroup() + attr(ADLB[["ARM"]], "label") <- var_labels[["ARM"]] + attr(ADLB[["ANRHI"]], "label") <- "Analysis Normal Range Upper Limit" + attr(ADLB[["ANRLO"]], "label") <- "Analysis Normal Range Lower Limit" + + # add LLOQ and ULOQ variables + ADLB_LOQS <- goshawk:::h_identify_loq_values(ADLB, flag_var = "LOQFL") + ADLB <- left_join(ADLB, ADLB_LOQS, by = "PARAM") + + # given the 2 param and 2 analysis vars we need to transform the data + plot_data_t1 <- suppressWarnings(ADLB |> + gather( + ANLVARS, ANLVALS, PARAM, LBSTRESC, BASE2, BASE, AVAL, BASE, LOQFL, + ANRHI, ANRLO, ULOQN, LLOQN + ) |> + mutate(ANL.PARAM = ifelse(ANLVARS %in% c("PARAM", "LBSTRESC", "LOQFL"), + paste0(ANLVARS, "_", PARAMCD), + paste0(ANLVARS, ".", PARAMCD) + )) |> + select(USUBJID, ARM, ARMCD, AVISITN, AVISITCD, ANL.PARAM, ANLVALS) |> + spread(ANL.PARAM, ANLVALS)) + + # the transformed analysis value variables are character and need to be converted to numeric for + # ggplot + # remove records where either of the analysis variables are NA since they will not appear on the + # plot and will ensure that LOQFL = NA level is removed + plot_data_t2 <- plot_data_t1 |> + filter(!is.na(BASE.CRP) & !is.na(AVAL.ALT)) |> + mutate_at(vars(contains(".")), as.numeric) |> + mutate( + LOQFL_COMB = ifelse(LOQFL_CRP == "Y" | LOQFL_ALT == "Y", "Y", "N") + ) + + expect_warning(p <- g_correlationplot( + label = "Correlation Plot", + data = plot_data_t2, + param_var = "PARAMCD", + xaxis_param = c("CRP"), + xaxis_var = "AVAL", + xvar = "AVAL.CRP", + yaxis_param = c("ALT"), + yaxis_var = "BASE", + yvar = "BASE.ALT", + trt_group = "ARM", + visit = "AVISITCD", + visit_facet = TRUE, + loq_legend = TRUE, + unit = "AVALU", + title_text = "Correlation of ALT to CRP", + xaxis_lab = "CRP", + yaxis_lab = "ALT", + color_manual = color_manual, + shape_manual = shape_manual, + facet_ncol = 4, + facet = FALSE, + facet_var = "ARM", + reg_line = FALSE, + hline_arb = c(15, 25), + hline_arb_color = c("gray", "green"), + hline_arb_label = "Hori_line_label", + vline_arb = c(.5, 1), + vline_arb_color = c("red", "black"), + vline_arb_label = c("Vertical Line A", "Vertical Line B"), + hline_vars = c("ANRHI.ALT", "ANRLO.ALT", "ULOQN.ALT", "LLOQN.ALT"), + hline_vars_colors = c("green", "blue", "purple", "cyan"), + hline_vars_labels = c("ANRHI ALT Label", "ANRLO ALT Label", "ULOQN ALT Label", "LLOQN ALT Label"), + vline_vars = c("ANRHI.CRP", "ANRLO.CRP", "ULOQN.CRP", "LLOQN.CRP"), + vline_vars_colors = c("yellow", "orange", "brown", "gold"), + vline_vars_labels = c("ANRHI CRP Label", "ANRLO CRP Label", "ULOQN CRP Label", "LLOQN CRP Label"), + rotate_xlab = FALSE, + font_size = 14, + dot_size = 2, + reg_text_size = 3 + ), "First value is taken from variable", fixed = TRUE) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) diff --git a/tests/testthat/test-g_density_distribution_plot.R b/tests/testthat/test-g_density_distribution_plot.R new file mode 100644 index 00000000..343ed300 --- /dev/null +++ b/tests/testthat/test-g_density_distribution_plot.R @@ -0,0 +1,95 @@ +# Requirement: Function to create a density distribution plot. +adlb <- make_adlb() + +test_that("g_density_distribution_plot returns a ggplot object", { + p <- g_density_distribution_plot( + data = adlb, + param_var = "PARAMCD", + param = "CRP", + xaxis_var = "AVAL", + trt_group = "ARM", + unit = "AVALU" + ) + expect_s3_class(p, "ggplot") + expect_no_error(expect_warning(ggplot2::ggplot_build(p), regexp = "Removed 2 rows", fixed = TRUE)) +}) + +test_that("g_density_distribution_plot works with all options", { + p <- g_density_distribution_plot( + label = "Density", + data = adlb, + param_var = "PARAMCD", + param = "ALT", + xaxis_var = "AVAL", + trt_group = "ARM", + unit = "AVALU", + loq_flag_var = "LOQFL", + xlim = c(0, 200), + ylim = c(0, 1), + color_manual = arm_colors, + color_comb = "#39ff14", + comb_line = TRUE, + facet_var = "AVISITCD", + hline_arb = c(0.1, 0.2), + hline_arb_color = c("red", "blue"), + hline_arb_label = c("a", "b"), + facet_ncol = 3, + rotate_xlab = TRUE, + font_size = 10, + line_size = 1, + rug_plot = TRUE + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_density_distribution_plot works without units and combined line", { + p <- g_density_distribution_plot( + data = adlb, + param = "CRP", + unit = NULL, + comb_line = FALSE, + font_size = NULL + ) + expect_s3_class(p, "ggplot") + expect_no_error(expect_warning(ggplot2::ggplot_build(p), regexp = "Removed 1 row", fixed = TRUE)) + + adlb_nounit <- adlb + adlb_nounit$AVALU <- "" + p_empty <- g_density_distribution_plot(data = adlb_nounit, param = "CRP", unit = "AVALU") + expect_s3_class(p_empty, "ggplot") + expect_no_error(expect_warning(ggplot2::ggplot_build(p), regexp = "Removed 1 row", fixed = TRUE)) +}) + +test_that("g_density_distribution_plot handles a manual color without the combined entry", { + p <- g_density_distribution_plot( + data = adlb, + param = "CRP", + color_manual = arm_colors, + color_comb = NULL + ) + expect_s3_class(p, "ggplot") + expect_no_error(expect_warning(ggplot2::ggplot_build(p), regexp = "Removed 2 rows", fixed = TRUE)) + + p_with_comb <- g_density_distribution_plot( + data = adlb, + param = "CRP", + color_manual = c(arm_colors, "Combined Dose" = "black"), + color_comb = NULL + ) + expect_s3_class(p_with_comb, "ggplot") + expect_no_error(expect_warning(ggplot2::ggplot_build(p_with_comb), regexp = "Removed 2 rows", fixed = TRUE)) +}) + +test_that("g_density_distribution_plot uses the ggplot2.discrete.colour option", { + old_opts <- options(ggplot2.discrete.colour = c("red", "green", "blue", "black")) + on.exit(options(old_opts), add = TRUE) + p <- g_density_distribution_plot(data = adlb, param = "CRP") + expect_s3_class(p, "ggplot") + expect_no_error(expect_warning(ggplot2::ggplot_build(p), regexp = "Removed 2 rows", fixed = TRUE)) +}) + +test_that("g_density_distribution_plot validates its arguments", { + expect_error(g_density_distribution_plot(data = adlb, param = "CRP", xlim = 1)) + expect_error(g_density_distribution_plot(data = adlb, param = "CRP", ylim = c(1, 2, 3))) +}) diff --git a/tests/testthat/test-g_lineplot.R b/tests/testthat/test-g_lineplot.R new file mode 100644 index 00000000..f99547a4 --- /dev/null +++ b/tests/testthat/test-g_lineplot.R @@ -0,0 +1,176 @@ +# Requirement: Function to create line plot of summary statistics over time. +adlb <- make_adlb() +line_types <- c("A: Drug X" = "solid", "B: Placebo" = "dashed", "C: Combination" = "dotted") + +test_that("g_lineplot returns a combined plot with the mean table", { + p <- g_lineplot( + data = adlb, + biomarker_var = "PARAMCD", + biomarker = "CRP", + value_var = "AVAL", + trt_group = "ARM", + time = "AVISITCDN" + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_lineplot works with median, manual styling and horizontal lines", { + p <- g_lineplot( + label = "Line Plot", + data = adlb, + biomarker = "ALT", + value_var = "AVAL", + trt_group = "ARM", + trt_group_level = c("C: Combination", "B: Placebo", "A: Drug X"), + time = "AVISITCDN", + color_manual = arm_colors, + line_type = line_types, + median = TRUE, + hline_arb = c(40, 60), + hline_arb_color = c("green", "red"), + hline_arb_label = c("A", "B"), + xtick = c(0, 1, 5), + xlabel = c("Baseline", "Week 1", "Week 5"), + rotate_xlab = TRUE, + ylim = c(0, 100), + plot_font_size = 10, + dot_size = 2, + dodge = 0.2, + plot_height = 800, + table_font_size = 10 + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_lineplot supports discrete time and hides the central value table", { + p <- g_lineplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + time = "AVISITCD", + time_level = c("BL", "W 1", "W 5"), + xtick = c("BL", "W 1", "W 5"), + xlabel = c("Baseline", "Week 1", "Week 5"), + display_center_tbl = FALSE + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) + + p_chr <- g_lineplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + time = "AVISITCD", + rotate_xlab = TRUE + ) + expect_s3_class(p_chr, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_lineplot supports a shape variable", { + p <- g_lineplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + shape = "SEX", + time = "AVISITCDN", + color_manual = arm_colors, + line_type = line_types, + plot_height = 3000, + dot_size = 1 + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) + + p_shape_type <- g_lineplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + shape = "SEX", + shape_type = c("F" = 16, "M" = 17), + time = "AVISITCDN", + median = TRUE, + plot_height = 3000 + ) + expect_s3_class(p_shape_type, "ggplot") + expect_no_error(ggplot2::ggplot_build(p_shape_type)) +}) + +test_that("g_lineplot repeats default shapes when there are many levels", { + adlb_many <- adlb + adlb_many$GRP <- factor(rep(sprintf("g%02d", 1:20), length.out = nrow(adlb_many))) + suppressWarnings(p <- g_lineplot( + data = adlb_many, + biomarker = "CRP", + trt_group = "ARM", + shape = "GRP", + time = "AVISITCDN", + plot_height = 20000 + )) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_lineplot marks cells below the count threshold", { + p <- g_lineplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + time = "AVISITCDN", + count_threshold = 4 + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_lineplot uses the ggplot2.discrete.colour option", { + old_opts <- options(ggplot2.discrete.colour = c("red", "green", "blue", "black")) + on.exit(options(old_opts), add = TRUE) + p <- g_lineplot(data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN") + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_lineplot validates its arguments", { + expect_error( + g_lineplot(data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN", ylim = 1) + ) + expect_error( + g_lineplot( + data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN", + color_manual = c("A: Drug X" = "red") + ) + ) + expect_error( + g_lineplot( + data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN", + line_type = c("A: Drug X" = "solid") + ) + ) + expect_error( + g_lineplot( + data = adlb, biomarker = "CRP", trt_group = "ARM", shape = "SEX", time = "AVISITCDN", + shape_type = c("F" = 16), plot_height = 3000 + ) + ) + expect_error( + g_lineplot(data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN", plot_height = 100), + "not sufficient" + ) +}) + +test_that("g_lineplot helpers build interaction names and colors", { + expect_equal( + goshawk:::new_interaction(list(ARM = c("a", "b")), sep = " "), + c("ARM:a", "ARM:b") + ) + res <- goshawk:::new_interaction(list(ARM = c("a", "b"), SEX = c("F", "M")), sep = " ") + expect_equal(as.character(res), c("ARM:a SEX:F", "ARM:b SEX:M")) + expect_equal(goshawk:::unique_name("int", c("a", "b")), "int") + expect_type(goshawk:::unique_name("int", c("int", "b")), "character") + cols <- goshawk:::gg_color_hue(3) + expect_length(cols, 3) + expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", cols))) +}) diff --git a/tests/testthat/test-g_scatterplot.R b/tests/testthat/test-g_scatterplot.R new file mode 100644 index 00000000..ec6d4266 --- /dev/null +++ b/tests/testthat/test-g_scatterplot.R @@ -0,0 +1,6 @@ +# Requirement: Function to create a scatter plot (deprecated, replaced by g_correlationplot). + +test_that("g_scatterplot is defunct and points to g_correlationplot", { + expect_error(g_scatterplot(), "g_scatterplot") + expect_error(g_scatterplot(data = mtcars, x = "wt"), "defunct") +}) diff --git a/tests/testthat/test-g_spaghettiplot.R b/tests/testthat/test-g_spaghettiplot.R new file mode 100644 index 00000000..a6b6354a --- /dev/null +++ b/tests/testthat/test-g_spaghettiplot.R @@ -0,0 +1,99 @@ +# Requirement: Function to create a spaghetti plot. +adlb <- make_adlb() + +test_that("g_spaghettiplot returns a ggplot object", { + p <- g_spaghettiplot( + data = adlb, + subj_id = "USUBJID", + biomarker_var = "PARAMCD", + biomarker = "CRP", + value_var = "AVAL", + trt_group = "ARM", + time = "AVISITCDN" + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_spaghettiplot works with continuous time and group statistics", { + p <- g_spaghettiplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + time = "AVISITCDN", + color_manual = arm_colors, + color_comb = "#39ff14", + alpha = 0.2, + xtick = c(0, 1, 5), + xlabel = c("Baseline", "Week 1", "Week 5"), + rotate_xlab = TRUE, + group_stats = "MEDIAN", + hline_arb = c(0.5, 0.7), + hline_arb_color = c("blue", "red"), + hline_arb_label = c("Arb A", "Arb B"), + hline_vars = c("ANRHI", "ANRLO", "ULOQN", "LLOQN"), + hline_vars_colors = c("pink", "brown", "purple", "gray"), + dot_size = 3 + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_spaghettiplot supports mean statistics and discrete time", { + p <- g_spaghettiplot( + data = adlb, + biomarker = "ALT", + trt_group = "ARM", + trt_group_level = c("C: Combination", "B: Placebo", "A: Drug X"), + time = "AVISITCD", + time_level = c("BL", "W 1", "W 5"), + xtick = c("BL", "W 1", "W 5"), + xlabel = c("Baseline", "Week 1", "Week 5"), + group_stats = "MEAN", + ylim = c(0, 100), + facet_ncol = 3, + font_size = 10 + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_spaghettiplot supports all facet scales and no group statistics", { + for (scales in c("fixed", "free", "free_x", "free_y")) { + p <- g_spaghettiplot( + data = adlb, + biomarker = "CRP", + trt_group = "ARM", + time = "AVISITCD", + facet_scales = scales, + group_stats = "NONE", + font_size = NULL + ) + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) + } +}) + +test_that("g_spaghettiplot uses the unit and handles an empty unit", { + adlb_nounit <- adlb + adlb_nounit$AVALU <- "" + p <- g_spaghettiplot(data = adlb_nounit, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN") + expect_s3_class(p, "ggplot") + expect_no_error(ggplot2::ggplot_build(p)) + expect_no_match(p$labels$title, "mg/L") + + p_unit <- g_spaghettiplot(data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN") + expect_match(p_unit$labels$title, "mg/L") + expect_no_error(ggplot2::ggplot_build(p_unit)) +}) + +test_that("g_spaghettiplot validates its arguments", { + expect_error( + g_spaghettiplot(data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN", ylim = 1) + ) + expect_error( + g_spaghettiplot( + data = adlb, biomarker = "CRP", trt_group = "ARM", time = "AVISITCDN", facet_scales = "other" + ) + ) +}) diff --git a/tests/testthat/test-geom_axes_line.R b/tests/testthat/test-geom_axes_line.R new file mode 100644 index 00000000..ce2f9c58 --- /dev/null +++ b/tests/testthat/test-geom_axes_line.R @@ -0,0 +1,97 @@ +# Helper functions adding horizontal and vertical lines to plots. +line_data <- data.frame( + x = seq_len(10), + y = seq_len(10), + lower = 2, + upper = 7, + varying = seq_len(10), + chr = "a", + stringsAsFactors = FALSE +) + +test_that("validate_line_args returns the values, colors and labels", { + res <- goshawk:::validate_line_args(line_data, vars = c("lower", "upper"), color = c("red", "blue")) + expect_s3_class(res, "data.frame") + expect_equal(res$values, c(2, 7)) + expect_equal(res$colors, c("red", "blue")) + expect_equal(res$labels, c("lower", "upper")) +}) + +test_that("validate_line_args uses variable labels and a single color", { + labelled <- line_data + attr(labelled$lower, "label") <- "Lower limit" + res <- goshawk:::validate_line_args(labelled, vars = c("lower", "upper"), color = "green") + expect_equal(res$labels, c("Lower limit", "upper")) + expect_equal(res$colors, c("green", "green")) +}) + +test_that("validate_line_args returns an empty data frame without variables", { + res <- goshawk:::validate_line_args(line_data) + expect_equal(nrow(res), 0) + expect_named(res, c("values", "colors", "labels")) +}) + +test_that("validate_line_args warns when a variable has several values", { + expect_warning( + res <- goshawk:::validate_line_args(line_data, vars = "varying"), + "First value is taken from variable 'varying'", + fixed = TRUE + ) + expect_equal(res$values, 1) +}) + +test_that("validate_line_args validates its input", { + expect_error(goshawk:::validate_line_args(line_data, vars = "missing")) + expect_error(goshawk:::validate_line_args(line_data, vars = "chr")) + expect_error(goshawk:::validate_line_args(line_data, vars = "lower", color = "notacolor")) + expect_error(goshawk:::validate_line_args(line_data, vars = c("lower", "upper"), color = c("red", "blue", "green"))) + expect_error(goshawk:::validate_line_args(line_data, vars = "lower", label = c("a", "b"))) +}) + +test_that("geom_axes_lines returns no layers by default", { + expect_length(goshawk:::geom_axes_lines(line_data), 0) +}) + +test_that("geom_axes_lines builds the requested layers", { + res <- goshawk:::geom_axes_lines( + line_data, + hline_arb = c(1, 2), + hline_arb_color = c("red", "blue"), + hline_arb_label = c("a", "b"), + hline_vars = c("lower", "upper"), + vline_arb = 3, + vline_vars = "upper" + ) + expect_length(res, 4) + expect_true(all(vapply(res, is.list, logical(1)))) + + p <- ggplot2::ggplot(line_data, ggplot2::aes(x, y)) + + ggplot2::geom_point() + + res + expect_s3_class(p, "ggplot") +}) + +test_that("geom_arb_hline and geom_arb_vline return a new scale, a line and a color scale", { + h <- goshawk:::geom_arb_hline(yintercept = c(2, 5), color = "blue", label = c("h1", "h2"), linetype = 2) + expect_length(h, 3) + expect_s3_class(h[[2]]$geom, "GeomHline") + v <- goshawk:::geom_arb_vline(xintercept = c(2, 5), color = c("blue", "red"), label = "v", linetype = 2) + expect_length(v, 3) + expect_s3_class(v[[2]]$geom, "GeomVline") +}) + +test_that("geom_arb_hline and geom_arb_vline validate their input", { + expect_error(goshawk:::geom_arb_hline(yintercept = "a")) + expect_error(goshawk:::geom_arb_hline(yintercept = 1, color = "notacolor")) + expect_error(goshawk:::geom_arb_hline(yintercept = 1, label = c("a", "b"))) + expect_error(goshawk:::geom_arb_vline(xintercept = "a")) + expect_error(goshawk:::geom_arb_vline(xintercept = 1, color = "notacolor")) + expect_error(goshawk:::geom_arb_vline(xintercept = 1, label = c("a", "b"))) +}) + +test_that("geom_range_hline and geom_range_vline use variables from the data", { + h <- goshawk:::geom_range_hline(vars = c("lower", "upper"), data = line_data, color = "blue", linetype = 2) + expect_length(h, 3) + v <- goshawk:::geom_range_vline(vars = c("lower", "upper"), data = line_data, color = "blue", linetype = 2) + expect_length(v, 3) +}) diff --git a/tests/testthat/test-t_summarytable.R b/tests/testthat/test-t_summarytable.R new file mode 100644 index 00000000..7793db0d --- /dev/null +++ b/tests/testthat/test-t_summarytable.R @@ -0,0 +1,99 @@ +# Requirement: Function to create a table of descriptive summary statistics to accompany plots. +adlb <- make_adlb() +stat_cols <- c("n", "Mean", "Median", "StdDev", "Min", "Max", "PctMiss", "PctLOQ") + +test_that("t_summarytable summarises by treatment and visit", { + tbl <- t_summarytable( + data = adlb, + trt_group = "ARM", + param_var = "PARAMCD", + param = "CRP", + xaxis_var = "AVAL", + facet_var = "AVISITCD", + loq_flag_var = "LOQFL" + ) + expect_s3_class(tbl, "data.frame") + expect_equal( + names(tbl), + c("StudyID", "AnlVar", "Biomarker", "Treatment", "Facet", stat_cols) + ) + # 3 arms + combined, for 3 visits + expect_equal(nrow(tbl), 12) + expect_true("Comb." %in% tbl$Treatment) + expect_equal(unique(as.character(tbl$StudyID)), "STUDY-1") + expect_equal(unique(as.character(tbl$AnlVar)), "AVAL") + expect_equal(unique(as.character(tbl$Biomarker)), "CRP") +}) + +test_that("t_summarytable counts non-missing values and LOQ percentages", { + tbl <- t_summarytable( + data = adlb, + trt_group = "ARM", + param_var = "PARAMCD", + param = "CRP", + xaxis_var = "AVAL" + ) + comb <- tbl$Treatment == "Comb." + expect_equal(tbl$n[comb & tbl$Facet == "BL"], 12) + expect_equal(tbl$n[comb & tbl$Facet == "W 5"], 11) + expect_equal(tbl$PctMiss[comb & tbl$Facet == "W 5"], round(100 / 12, 2)) + expect_equal(tbl$PctLOQ[comb & tbl$Facet == "BL"], round(100 / 12, 2)) + expect_equal(tbl$PctLOQ[comb & tbl$Facet == "W 5"], 0) +}) + +test_that("t_summarytable works without a facet variable", { + tbl <- t_summarytable( + data = adlb, + trt_group = "ARM", + param_var = "PARAMCD", + param = "ALT", + xaxis_var = "AVAL", + facet_var = NULL + ) + expect_equal(names(tbl), c("StudyID", "AnlVar", "Biomarker", "Treatment", stat_cols)) + expect_equal(nrow(tbl), 4) + expect_equal(tbl$n[tbl$Treatment == "Comb."], 36) +}) + +test_that("t_summarytable handles the treatment group being the facet variable", { + tbl <- t_summarytable( + data = adlb, + trt_group = "ARM", + param_var = "PARAMCD", + param = "CRP", + xaxis_var = "AVAL", + facet_var = "ARM" + ) + expect_true("Facet" %in% names(tbl)) + expect_equal(nrow(tbl), 3 + 3) +}) + +test_that("t_summarytable returns NA for min and max when all values are missing", { + adlb_na <- adlb + adlb_na$AVAL[adlb_na$PARAMCD == "CRP" & adlb_na$AVISITCD == "W 5" & adlb_na$ARM == "A: Drug X"] <- NA_real_ + tbl <- t_summarytable( + data = adlb_na, + trt_group = "ARM", + param_var = "PARAMCD", + param = "CRP", + xaxis_var = "AVAL" + ) + row <- tbl$Treatment == "A: Drug X" & tbl$Facet == "W 5" + expect_equal(tbl$n[row], 0) + expect_true(is.na(tbl$Min[row])) + expect_true(is.na(tbl$Max[row])) + expect_equal(tbl$PctMiss[row], 100) +}) + +test_that("t_summarytable combines several studies in the study id", { + adlb_2 <- adlb + adlb_2$STUDYID[adlb_2$USUBJID %in% sprintf("S-%02d", 7:12)] <- "STUDY-2" + tbl <- t_summarytable( + data = adlb_2, + trt_group = "ARM", + param_var = "PARAMCD", + param = "CRP", + xaxis_var = "AVAL" + ) + expect_equal(unique(as.character(tbl$StudyID)), "STUDY-1/STUDY-2") +}) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R new file mode 100644 index 00000000..6afafcb6 --- /dev/null +++ b/tests/testthat/test-utils.R @@ -0,0 +1,53 @@ +# Helper functions shared by the plot functions. +adlb <- make_adlb() + +test_that("h_identify_loq_values finds the first LLOQ and ULOQ per parameter", { + res <- goshawk:::h_identify_loq_values(adlb, "LOQFL") + expect_s3_class(res, "data.frame") + expect_setequal(res$PARAM, c("C-reactive protein", "Alanine transaminase")) + expect_equal(res$LLOQN[res$PARAM == "C-reactive protein"], 0.5) + expect_equal(res$ULOQN[res$PARAM == "C-reactive protein"], 1.5) + expect_equal(res$LLOQN[res$PARAM == "Alanine transaminase"], 20) + expect_equal(res$ULOQN[res$PARAM == "Alanine transaminase"], 60) + expect_equal(attr(res$LLOQN, "label"), "Lower Limit of Quantitation") + expect_equal(attr(res$ULOQC, "label"), "Upper Limit of Quantitation (C)") +}) + +test_that("h_identify_loq_values returns NA limits without any LOQ", { + no_loq <- adlb + no_loq$LOQFL <- "N" + res <- goshawk:::h_identify_loq_values(no_loq, "LOQFL") + expect_equal(nrow(res), 2) + expect_true(all(is.na(res$LLOQN))) + expect_true(all(is.na(res$ULOQN))) +}) + +test_that("h_caption_loqs_label describes the limits of quantification", { + crp <- adlb[adlb$PARAMCD == "CRP", ] + caption <- goshawk:::h_caption_loqs_label(crp, "LOQFL") + expect_length(caption, nrow(crp)) + expect_equal( + unique(caption), + "Limits of quantification read from study data for C-reactive protein: LLOQ is < 0.5, ULOQ is > 1.5" + ) + + no_loq <- crp + no_loq$LOQFL <- "N" + expect_match( + unique(goshawk:::h_caption_loqs_label(no_loq, "LOQFL")), + "LLOQ is NA, ULOQ is NA" + ) +}) + +test_that("check_color accepts valid colors and rejects invalid ones", { + expect_true(goshawk:::check_color("red")) + expect_true(goshawk:::check_color(c("red", "#00FF00"))) + expect_match(goshawk:::check_color("notacolor"), "valid color") + expect_false(isTRUE(goshawk:::check_color(1))) + expect_false(isTRUE(goshawk:::check_color(c("red", "blue"), len = 1))) +}) + +test_that("assert_color errors for invalid colors only", { + expect_silent(goshawk:::assert_color("red")) + expect_error(goshawk:::assert_color("notacolor")) +}) From 273d49685beaa75942cd39f81533cbf5ece09645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 14:05:09 +0100 Subject: [PATCH 04/11] Minor tweaks to make it work without warnings --- R/g_correlationplot.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/g_correlationplot.R b/R/g_correlationplot.R index 97d093db..1ac1d828 100644 --- a/R/g_correlationplot.R +++ b/R/g_correlationplot.R @@ -267,14 +267,14 @@ g_correlationplot <- function(label = "Correlation Plot", PARAM = !!sym(t_param_var_x), LBSTRESC = !!sym(t_lbstresc_var_x), sym(loq_flag_var) == !!sym(loq_flag_var) ) %>% - select("PARAM", "LBSTRESC", loq_flag_var) + select("PARAM", "LBSTRESC", all_of(loq_flag_var)) yaxis_param_loqs_data <- data %>% mutate( PARAM = !!sym(t_param_var_y), LBSTRESC = !!sym(t_lbstresc_var_y), sym(loq_flag_var) == !!sym(loq_flag_var) ) %>% - select("PARAM", "LBSTRESC", loq_flag_var) + select("PARAM", "LBSTRESC", all_of(loq_flag_var)) # add footnote to identify xaxis assay LLOQ and ULOQ values pulled from data caption_loqs_label_x <- h_caption_loqs_label(loqs_data = xaxis_param_loqs_data, flag_var = loq_flag_var) @@ -352,17 +352,17 @@ g_correlationplot <- function(label = "Correlation Plot", ) + ggplot2::geom_text( data = filter(sub_data, row_number() == 1), - ggplot2::aes_( + ggplot2::aes( x = -Inf, y = Inf, hjust = 0, vjust = 1, - label = ~ ifelse( + label = ifelse( !is.na(intercept) & !is.na(slope) & !is.na(corr), sprintf("y = %.2f+%.2fX\ncor = %.2f", intercept, slope, corr), paste0("Insufficient Data For Regression") ), - color = sym(trt_group) + color = !!trt_group ), size = reg_text_size, show.legend = FALSE From 52c362b145973e24a002f4aa774aca0cf9720905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 14:05:39 +0100 Subject: [PATCH 05/11] Add more tests for regression line --- tests/testthat/test-g_correlationplot.R | 96 +++++++++++++++++++------ 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/tests/testthat/test-g_correlationplot.R b/tests/testthat/test-g_correlationplot.R index 01539752..2bc04ffc 100644 --- a/tests/testthat/test-g_correlationplot.R +++ b/tests/testthat/test-g_correlationplot.R @@ -143,7 +143,7 @@ test_that("Example", { ADLB <- rADLB var_labels <- lapply(ADLB, function(x) attributes(x)$label) - ADLB <- ADLB |> + ADLB <- ADLB %>% mutate(AVISITCD = case_when( AVISIT == "SCREENING" ~ "SCR", AVISIT == "BASELINE" ~ "BL", @@ -159,22 +159,22 @@ test_that("Example", { ) ), TRUE ~ NA_character_ - )) |> + )) %>% mutate(AVISITCDN = case_when( AVISITCD == "SCR" ~ -2, AVISITCD == "BL" ~ 0, grepl("W", AVISITCD) ~ as.numeric(gsub("\\D+", "", AVISITCD)), TRUE ~ NA_real_ - )) |> + )) %>% # use ARMCD values to order treatment in visualization legend mutate(TRTORD = ifelse(grepl("C", ARMCD), 1, ifelse(grepl("B", ARMCD), 2, ifelse(grepl("A", ARMCD), 3, NA) ) - )) |> - mutate(ARM = as.character(arm_mapping[match(ARM, names(arm_mapping))])) |> - mutate(ARM = factor(ARM) |> - reorder(TRTORD)) |> + )) %>% + mutate(ARM = as.character(arm_mapping[match(ARM, names(arm_mapping))])) %>% + mutate(ARM = factor(ARM) %>% + reorder(TRTORD)) %>% mutate( ANRHI = case_when( PARAMCD == "ALT" ~ 60, @@ -188,17 +188,17 @@ test_that("Example", { PARAMCD == "IGA" ~ 40, TRUE ~ NA_real_ ) - ) |> - rowwise() |> - group_by(PARAMCD) |> + ) %>% + rowwise() %>% + group_by(PARAMCD) %>% mutate(LBSTRESC = ifelse( USUBJID %in% sample(USUBJID, 1, replace = TRUE), paste("<", round(runif(1, min = 25, max = 30))), LBSTRESC - )) |> + )) %>% mutate(LBSTRESC = ifelse( USUBJID %in% sample(USUBJID, 1, replace = TRUE), paste(">", round(runif(1, min = 70, max = 75))), LBSTRESC - )) |> + )) %>% ungroup() attr(ADLB[["ARM"]], "label") <- var_labels[["ARM"]] attr(ADLB[["ANRHI"]], "label") <- "Analysis Normal Range Upper Limit" @@ -209,30 +209,30 @@ test_that("Example", { ADLB <- left_join(ADLB, ADLB_LOQS, by = "PARAM") # given the 2 param and 2 analysis vars we need to transform the data - plot_data_t1 <- suppressWarnings(ADLB |> + plot_data_t1 <- suppressWarnings(ADLB %>% gather( ANLVARS, ANLVALS, PARAM, LBSTRESC, BASE2, BASE, AVAL, BASE, LOQFL, ANRHI, ANRLO, ULOQN, LLOQN - ) |> + ) %>% mutate(ANL.PARAM = ifelse(ANLVARS %in% c("PARAM", "LBSTRESC", "LOQFL"), paste0(ANLVARS, "_", PARAMCD), paste0(ANLVARS, ".", PARAMCD) - )) |> - select(USUBJID, ARM, ARMCD, AVISITN, AVISITCD, ANL.PARAM, ANLVALS) |> + )) %>% + select(USUBJID, ARM, ARMCD, AVISITN, AVISITCD, ANL.PARAM, ANLVALS) %>% spread(ANL.PARAM, ANLVALS)) # the transformed analysis value variables are character and need to be converted to numeric for # ggplot # remove records where either of the analysis variables are NA since they will not appear on the # plot and will ensure that LOQFL = NA level is removed - plot_data_t2 <- plot_data_t1 |> - filter(!is.na(BASE.CRP) & !is.na(AVAL.ALT)) |> - mutate_at(vars(contains(".")), as.numeric) |> + plot_data_t2 <- plot_data_t1 %>% + filter(!is.na(BASE.CRP) & !is.na(AVAL.ALT)) %>% + mutate_at(vars(contains(".")), as.numeric) %>% mutate( LOQFL_COMB = ifelse(LOQFL_CRP == "Y" | LOQFL_ALT == "Y", "Y", "N") ) - expect_warning(p <- g_correlationplot( + p <- g_correlationplot( label = "Correlation Plot", data = plot_data_t2, param_var = "PARAMCD", @@ -272,7 +272,61 @@ test_that("Example", { font_size = 14, dot_size = 2, reg_text_size = 3 - ), "First value is taken from variable", fixed = TRUE) + ) expect_s3_class(p, "ggplot") expect_no_error(ggplot2::ggplot_build(p)) }) + + +text_labels <- function(p) { + b <- ggplot2::ggplot_build(p) + idx <- which(vapply(p$layers, function(l) inherits(l$geom, "GeomText"), logical(1))) + b$data[[idx]]$label +} + +test_that("g_correlationplot regression text reports fit and correlation per group", { + p <- do.call(g_correlationplot, corr_args(reg_line = TRUE)) + lab <- text_labels(p) + expect_length(lab, 9) # 3 arms x 3 visits + expect_true(all(grepl("^y = .*\ncor = ", lab))) + expect_match(p$labels$caption, "Deming Regression Model, Spearman Correlation Method") + expect_match(p$labels$caption, "Limits of quantification") +}) + +test_that("g_correlationplot reports insufficient data when plot x is constant", { + d <- corr + d$AVAL[d$ARM == "A: Drug X"] <- 1 # sd(x) == 0 -> NA branch + p <- do.call(g_correlationplot, corr_args(data = d, reg_line = TRUE)) + lab <- text_labels(p) + expect_equal(sum(lab == "Insufficient Data For Regression"), 3) + expect_equal(sum(grepl("^y = ", lab)), 6) +}) + +test_that("g_correlationplot reports insufficient data when plot y is constant", { + d <- corr + d$BASE[d$ARM == "B: Placebo"] <- 5 # ratio == 0 -> NA branch + p <- do.call(g_correlationplot, corr_args(data = d, reg_line = TRUE)) + expect_equal(sum(text_labels(p) == "Insufficient Data For Regression"), 3) +}) + +test_that("g_correlationplot reg_line works with every facetting option", { + for (vf in c(TRUE, FALSE)) { + for (f in c(TRUE, FALSE)) { + p <- do.call(g_correlationplot, corr_args(reg_line = TRUE, visit_facet = vf, facet = f, facet_var = "ARM")) + expect_no_error(ggplot2::ggplot_build(p)) + } + } +}) + +test_that("g_correlationplot reg_line drops rows with missing x or y", { + d <- corr + d$AVAL[1:2] <- NA_real_ + p <- do.call(g_correlationplot, corr_args(data = d, reg_line = TRUE, reg_text_size = 5)) + expect_no_error(ggplot2::ggplot_build(p)) +}) + +test_that("g_correlationplot reg_line errors for a group with a single observation", { + d <- corr[!(corr$ARM == "A: Drug X" & corr$AVISITCD == "BL") | + corr$USUBJID == "S-01", ] + expect_error(do.call(g_correlationplot, corr_args(data = d, reg_line = TRUE))) +}) \ No newline at end of file From 82f1c85529f52d2404e3c92ea6923871a7e16ccc Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:15:26 +0000 Subject: [PATCH 06/11] [skip style] [skip vbump] Restyle files --- tests/testthat/test-g_correlationplot.R | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-g_correlationplot.R b/tests/testthat/test-g_correlationplot.R index 2bc04ffc..e5540dfe 100644 --- a/tests/testthat/test-g_correlationplot.R +++ b/tests/testthat/test-g_correlationplot.R @@ -60,7 +60,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", visit_facet = TRUE, facet = TRUE, - facet_var = "ARM") + facet_var = "ARM" + ) expect_s3_class(p_visit_arm, "ggplot") expect_s3_class(p_visit_arm$facet, "Facet") @@ -81,7 +82,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", visit_facet = TRUE, facet = TRUE, - facet_var = "ARM") + facet_var = "ARM" + ) expect_s3_class(p_visit$facet, "Facet") p_arm <- g_correlationplot( @@ -101,7 +103,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", facet = TRUE, facet_var = "ARM", - visit_facet = FALSE) + visit_facet = FALSE + ) expect_s3_class(p_arm$facet, "Facet") p_none <- g_correlationplot( @@ -121,7 +124,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", facet = FALSE, visit_facet = FALSE, - facet_var = "ARM") + facet_var = "ARM" + ) expect_s3_class(p_none$facet, "FacetNull") }) @@ -327,6 +331,6 @@ test_that("g_correlationplot reg_line drops rows with missing x or y", { test_that("g_correlationplot reg_line errors for a group with a single observation", { d <- corr[!(corr$ARM == "A: Drug X" & corr$AVISITCD == "BL") | - corr$USUBJID == "S-01", ] + corr$USUBJID == "S-01", ] expect_error(do.call(g_correlationplot, corr_args(data = d, reg_line = TRUE))) -}) \ No newline at end of file +}) From 114d34090ae12669797dd8d50b21ce1942bef689 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:18:52 +0000 Subject: [PATCH 07/11] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- DESCRIPTION | 2 +- man/g_boxplot.Rd | 2 +- man/g_correlationplot.Rd | 2 +- man/g_lineplot.Rd | 2 +- man/goshawk-package.Rd | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6bbd2512..bd1a913b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -62,4 +62,4 @@ Encoding: UTF-8 Language: en-US LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 +Config/roxygen2/version: 8.1.1 diff --git a/man/g_boxplot.Rd b/man/g_boxplot.Rd index 548af0b1..6657fc9a 100644 --- a/man/g_boxplot.Rd +++ b/man/g_boxplot.Rd @@ -104,7 +104,7 @@ to visually estimate various L-estimators, notably the interquartile range, midhinge, range, mid-range, and trimean. } \examples{ -\dontshow{if (require("nestcolor")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (require("nestcolor")) withAutoprint(\{ # examplesIf} # Example using ADaM structure analysis dataset. library(nestcolor) diff --git a/man/g_correlationplot.Rd b/man/g_correlationplot.Rd index d9e982be..2e67c16d 100644 --- a/man/g_correlationplot.Rd +++ b/man/g_correlationplot.Rd @@ -147,7 +147,7 @@ symbol attributed \code{LOQ} values. Regression uses \code{deming} model. } \examples{ -\dontshow{if (require("tidyr")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (require("tidyr")) withAutoprint(\{ # examplesIf} # Example using ADaM structure analysis dataset. library(stringr) diff --git a/man/g_lineplot.Rd b/man/g_lineplot.Rd index 3269e23d..7c66eda7 100644 --- a/man/g_lineplot.Rd +++ b/man/g_lineplot.Rd @@ -120,7 +120,7 @@ error bar denotes quartile. } \examples{ -\dontshow{if (require("nestcolor")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (require("nestcolor")) withAutoprint(\{ # examplesIf} # Example using ADaM structure analysis dataset. library(stringr) diff --git a/man/goshawk-package.Rd b/man/goshawk-package.Rd index 84135b62..2f7458a4 100644 --- a/man/goshawk-package.Rd +++ b/man/goshawk-package.Rd @@ -22,6 +22,7 @@ Useful links: Authors: \itemize{ + \item Nick Paszty \email{nick.paszty@gene.com} \item Dawid Kaledkowski \email{dawid.kaledkowski@roche.com} \item Ondrej Slama \email{ondrej.slama@roche.com} \item Adrian Waddell \email{adrian.waddell@gene.com} From b8ecb05628012770b070967ed0d4d81081679873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 15:24:32 +0100 Subject: [PATCH 08/11] Use latest roxygen2 package --- DESCRIPTION | 2 +- NAMESPACE | 8 +++++--- man/g_boxplot.Rd | 2 +- man/g_correlationplot.Rd | 2 +- man/g_lineplot.Rd | 2 +- man/goshawk-package.Rd | 1 + 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6bbd2512..bd1a913b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -62,4 +62,4 @@ Encoding: UTF-8 Language: en-US LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 +Config/roxygen2/version: 8.1.1 diff --git a/NAMESPACE b/NAMESPACE index f2a01e89..c2643ef3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,7 +11,9 @@ import(dplyr) import(mcr) importFrom(ggplot2,ggplot) importFrom(magrittr,extract2) -importFrom(rlang,"!!") -importFrom(rlang,":=") -importFrom(rlang,.data) +importFrom(rlang, + "!!", + ":=", + .data +) importFrom(utils,getFromNamespace) diff --git a/man/g_boxplot.Rd b/man/g_boxplot.Rd index 548af0b1..6657fc9a 100644 --- a/man/g_boxplot.Rd +++ b/man/g_boxplot.Rd @@ -104,7 +104,7 @@ to visually estimate various L-estimators, notably the interquartile range, midhinge, range, mid-range, and trimean. } \examples{ -\dontshow{if (require("nestcolor")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (require("nestcolor")) withAutoprint(\{ # examplesIf} # Example using ADaM structure analysis dataset. library(nestcolor) diff --git a/man/g_correlationplot.Rd b/man/g_correlationplot.Rd index d9e982be..2e67c16d 100644 --- a/man/g_correlationplot.Rd +++ b/man/g_correlationplot.Rd @@ -147,7 +147,7 @@ symbol attributed \code{LOQ} values. Regression uses \code{deming} model. } \examples{ -\dontshow{if (require("tidyr")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (require("tidyr")) withAutoprint(\{ # examplesIf} # Example using ADaM structure analysis dataset. library(stringr) diff --git a/man/g_lineplot.Rd b/man/g_lineplot.Rd index 3269e23d..7c66eda7 100644 --- a/man/g_lineplot.Rd +++ b/man/g_lineplot.Rd @@ -120,7 +120,7 @@ error bar denotes quartile. } \examples{ -\dontshow{if (require("nestcolor")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (require("nestcolor")) withAutoprint(\{ # examplesIf} # Example using ADaM structure analysis dataset. library(stringr) diff --git a/man/goshawk-package.Rd b/man/goshawk-package.Rd index 84135b62..2f7458a4 100644 --- a/man/goshawk-package.Rd +++ b/man/goshawk-package.Rd @@ -22,6 +22,7 @@ Useful links: Authors: \itemize{ + \item Nick Paszty \email{nick.paszty@gene.com} \item Dawid Kaledkowski \email{dawid.kaledkowski@roche.com} \item Ondrej Slama \email{ondrej.slama@roche.com} \item Adrian Waddell \email{adrian.waddell@gene.com} From d4eb77e6ced6558f142255b1a9f9d0fc31e1da38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 15:25:03 +0100 Subject: [PATCH 09/11] Fix links to teal --- R/g_correlationplot.R | 4 +++- README.md | 16 +--------------- vignettes/goshawk.Rmd | 2 +- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/R/g_correlationplot.R b/R/g_correlationplot.R index 1ac1d828..476ff711 100644 --- a/R/g_correlationplot.R +++ b/R/g_correlationplot.R @@ -345,6 +345,8 @@ g_correlationplot <- function(label = "Correlation Plot", stats::cor(!!sym(yvar), !!sym(xvar), method = "spearman", use = "complete.obs"), NA )) + # To avoid Note: no visible binding for global variable + corr <- intercept <- NULL plot1 <- plot1 + ggplot2::geom_abline( data = filter(sub_data, row_number() == 1), # only need to return 1 row within group_by @@ -360,7 +362,7 @@ g_correlationplot <- function(label = "Correlation Plot", label = ifelse( !is.na(intercept) & !is.na(slope) & !is.na(corr), sprintf("y = %.2f+%.2fX\ncor = %.2f", intercept, slope, corr), - paste0("Insufficient Data For Regression") + "Insufficient Data For Regression" ), color = !!trt_group ), diff --git a/README.md b/README.md index 285bdbdb..759870f8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Longitudinal biomarker/lab visualizations functions. These can be used stand alone but are also called by the [teal.goshawk](https://insightsengineering.github.io/teal.goshawk/) package which provides modules to be used -inside [`teal`](https://insightsengineering.github.io/teal/) applications. +inside [`teal`](https://pharmaverse.github.io/teal/) applications. ## Functions @@ -43,17 +43,3 @@ pak::pkg_install("insightsengineering/goshawk@*release") # install.packages("pak") pak::pkg_install("insightsengineering/goshawk") ``` - -## Stargazers and Forkers - -### Stargazers over time - -[![Stargazers over time](https://starchart.cc/insightsengineering/goshawk.svg)](https://starchart.cc/insightsengineering/goshawk) - -### Stargazers - -[![Stargazers repo roster for @insightsengineering/goshawk](https://reporoster.com/stars/insightsengineering/goshawk)](https://github.com/insightsengineering/goshawk/stargazers) - -### Forkers - -[![Forkers repo roster for @insightsengineering/goshawk](https://reporoster.com/forks/insightsengineering/goshawk)](https://github.com/insightsengineering/goshawk/network/members) diff --git a/vignettes/goshawk.Rmd b/vignettes/goshawk.Rmd index 65bc4c16..3584edbe 100644 --- a/vignettes/goshawk.Rmd +++ b/vignettes/goshawk.Rmd @@ -60,7 +60,7 @@ for more information on data pre-processing and data expectations for `goshawk`. The `goshawk` outputs can be easily accommodated into `shiny` apps. We recommend applying `goshawk` outputs into `teal` apps. -The [`teal` package](https://insightsengineering.github.io/teal/) is a shiny-based interactive exploration framework for analyzing data. +The [`teal` package](https://pharmaverse.github.io/teal/) is a shiny-based interactive exploration framework for analyzing data. `teal` shiny apps with `goshawk` outputs are available in the [`teal.goshawk` package](https://insightsengineering.github.io/teal.goshawk/). From 2116e38577564f43fc321de59c13f6523f68fe0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 15:30:29 +0100 Subject: [PATCH 10/11] Fix style --- R/g_boxplot.R | 2 -- tests/testthat/test-g_correlationplot.R | 16 ++++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/R/g_boxplot.R b/R/g_boxplot.R index c315cd12..b57b67e3 100755 --- a/R/g_boxplot.R +++ b/R/g_boxplot.R @@ -281,8 +281,6 @@ g_boxplot <- function(data, } - - # Format font size if (is_finite(font_size)) { plot1 <- plot1 + diff --git a/tests/testthat/test-g_correlationplot.R b/tests/testthat/test-g_correlationplot.R index 2bc04ffc..e5540dfe 100644 --- a/tests/testthat/test-g_correlationplot.R +++ b/tests/testthat/test-g_correlationplot.R @@ -60,7 +60,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", visit_facet = TRUE, facet = TRUE, - facet_var = "ARM") + facet_var = "ARM" + ) expect_s3_class(p_visit_arm, "ggplot") expect_s3_class(p_visit_arm$facet, "Facet") @@ -81,7 +82,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", visit_facet = TRUE, facet = TRUE, - facet_var = "ARM") + facet_var = "ARM" + ) expect_s3_class(p_visit$facet, "Facet") p_arm <- g_correlationplot( @@ -101,7 +103,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", facet = TRUE, facet_var = "ARM", - visit_facet = FALSE) + visit_facet = FALSE + ) expect_s3_class(p_arm$facet, "Facet") p_none <- g_correlationplot( @@ -121,7 +124,8 @@ test_that("g_correlationplot supports the different facetting options", { yaxis_lab = "ALT", facet = FALSE, visit_facet = FALSE, - facet_var = "ARM") + facet_var = "ARM" + ) expect_s3_class(p_none$facet, "FacetNull") }) @@ -327,6 +331,6 @@ test_that("g_correlationplot reg_line drops rows with missing x or y", { test_that("g_correlationplot reg_line errors for a group with a single observation", { d <- corr[!(corr$ARM == "A: Drug X" & corr$AVISITCD == "BL") | - corr$USUBJID == "S-01", ] + corr$USUBJID == "S-01", ] expect_error(do.call(g_correlationplot, corr_args(data = d, reg_line = TRUE))) -}) \ No newline at end of file +}) From 8b7fa1f54e12c8a681e9807462ab2780fd9ff67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Fri, 6 Nov 2026 15:32:05 +0100 Subject: [PATCH 11/11] Add News entry --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 63b0d0a6..849f968f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # goshawk 0.1.19.9001 * `g_scatterplot` was hard deprecated (#267). +* Code coverage has increased to over 90% (#268). +* Updated links to teal for the new location (#268). # goshawk 0.1.19