# Load Sample Databases ----
db_data_classic <- readRDS(system.file("testdata/db_data_classic.RDS", package = "REDCapTidieR"))
db_metadata_classic <- readRDS(system.file("testdata/db_metadata_classic.RDS", package = "REDCapTidieR"))
test_that("update_data_field_names works", {
test_data <- tibble::tribble(
~`checkbox____99`, ~`checkbox____98`,
0, 1,
1, 0,
1, 0
)
test_meta <- tibble::tribble(
~field_name_updated,
"checkbox___-99",
"checkbox___-98",
"test_column"
)
out <- update_data_col_names(
db_data = test_data,
db_metadata = test_meta
)
expect_true(all(c("checkbox___-99", "checkbox___-98") %in% names(out)))
})
test_that("multi_choice_to_labels works", {
db_data_classic <- update_data_col_names(
db_data_classic,
db_metadata_classic
)
# Expect warning on misconfigured variables
multi_choice_to_labels(
db_data = db_data_classic,
db_metadata = db_metadata_classic
) %>%
# radio button is created for a descriptive text field
expect_warning(class = "empty_parse_warning") %>%
expect_warning(class = "field_missing_categories") %>%
# radio button with duplicate labels
expect_warning(class = "duplicate_labels")
out <- multi_choice_to_labels(
db_data = db_data_classic,
db_metadata = db_metadata_classic
) %>%
suppressWarnings(classes = c(
"empty_parse_warning",
"field_missing_categories",
"duplicate_labels"
))
# Test general structure
expect_true(is.data.frame(out))
expect_true(nrow(out) > 0)
# Test multichoice options return expected values and datatypes
expect_logical(out$yesno)
expect_logical(out$truefalse)
expect_logical(out$checkbox_multiple___1)
expect_logical(out$checkbox_multiple_2___4eeee5)
expect_factor(out$dropdown_single)
expect_equal(levels(out$dropdown_single), c("one", "two", "three"))
expect_factor(out$radio_single)
expect_equal(levels(out$radio_single), c("A", "B", "C"))
expect_factor(out$data_field_types_complete)
expect_equal(levels(out$data_field_types_complete), c("Incomplete", "Unverified", "Complete"))
# Haven option works
skip_if_not_installed("labelled")
out <- multi_choice_to_labels(
db_data = db_data_classic,
db_metadata = db_metadata_classic,
raw_or_label = "haven"
) %>%
suppressWarnings(classes = c(
"empty_parse_warning",
"field_missing_categories",
"duplicate_labels"
))
expect_s3_class(out$dropdown_single, "haven_labelled")
expect_equal(
labelled::val_labels(out$dropdown_single),
c("one" = "choice_1", "two" = "choice_2", "three" = "choice_3")
)
expect_s3_class(out$radio_single, "haven_labelled")
expect_equal(labelled::val_labels(out$radio_single), c("A" = "choice_1", "B" = "choice_2", "C" = "choice_3"))
expect_s3_class(out$data_field_types_complete, "haven_labelled")
expect_equal(
labelled::val_labels(out$data_field_types_complete), c("Incomplete" = 0, "Unverified" = 1, "Complete" = 2)
)
expect_s3_class(out$repeatsurvey_radio_v2, "haven_labelled")
expect_equal(labelled::val_labels(out$repeatsurvey_radio_v2), c("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3))
})
test_that("parse_labels works", {
# Note: implicitly testing strip_html_field_embedding() by checking that
# parse_labels successfully stips html tags and field embedding logic
valid_string <- "choice_1, one | choice_2, two {abc} | choice_3, three"
valid_tibble_output <- tibble::tribble(
~raw, ~label,
"choice_1", "one",
"choice_2", "two",
"choice_3", "three"
)
valid_vector_output <- c("one", "two", "three")
names(valid_vector_output) <- c("choice_1", "choice_2", "choice_3")
invalid_string_1 <- "raw, label | that has | pipes but no other | commas"
invalid_string_2 <- "raw, la|bel with pipe"
warning_string_1 <- NA_character_
expect_equal(parse_labels(valid_string), valid_tibble_output)
expect_equal(
parse_labels(valid_string, return_vector = TRUE),
valid_vector_output
)
expect_error(
parse_labels(invalid_string_1),
class = "label_parse_error"
)
expect_error(
parse_labels(invalid_string_2),
class = "label_parse_error"
)
expect_warning(
parse_labels(warning_string_1),
class = "empty_parse_warning"
)
# Check that parse_labels can account for splits where no white space exists
## between pipes
valid_string_no_ws1 <- "choice_1, one|choice_2, two {abc}|choice_3, three"
expect_equal(parse_labels(valid_string_no_ws1), valid_tibble_output)
## between commas
valid_string_no_ws2 <- "choice_1,one|choice_2,two {abc}|choice_3,three"
expect_equal(parse_labels(valid_string_no_ws2), valid_tibble_output)
# Check that return_stripped_text_flag works
## When html/embedding was stripped
expect_equal(
parse_labels(valid_string, return_stripped_text_flag = TRUE),
list(valid_tibble_output, TRUE)
)
expect_equal(
parse_labels(valid_string, return_vector = TRUE, return_stripped_text_flag = TRUE),
list(valid_vector_output, TRUE)
)
## When nothing was stripped
valid_string_no_html <- "choice_1, one | choice_2, two | choice_3, three"
expect_equal(
parse_labels(valid_string_no_html, return_stripped_text_flag = TRUE),
list(valid_tibble_output, FALSE)
)
expect_equal(
parse_labels(valid_string_no_html, return_vector = TRUE, return_stripped_text_flag = TRUE),
list(valid_vector_output, FALSE)
)
## Flag returns FALSE when there were no labels
parse_labels(warning_string_1, return_stripped_text_flag = TRUE) |>
suppressWarnings(classes = "empty_parse_warning") |>
purrr::pluck(2) |>
expect_equal(FALSE)
})
test_that("parse_logical_cols", {
db_data <- tibble::tibble(
record_id = 1:3,
yes_no = c("1", "0", "UNK"),
other_field = letters[1:3]
)
db_metadata <- tibble::tibble(
field_name_updated = c("yes_no", "other_field"),
field_type = c("yesno", "text")
)
expect_warning(parse_logical_cols(db_data, db_metadata), class = "field_is_logical")
out <- parse_logical_cols(db_data, db_metadata) |>
suppressWarnings(classes = "field_is_logical")
expect_equal(dim(out), dim(db_data))
expect_equal(out$record_id, db_data$record_id)
expect_equal(out$yes_no, c(TRUE, FALSE, NA))
expect_equal(out$other_field, db_data$other_field)
db_data <- tibble::tibble(
record_id = 1:3,
other_field = letters[1:3]
)
db_metadata <- tibble::tibble(
field_name_updated = "other_field",
field_type = "text"
)
expect_equal(parse_logical_cols(db_data, db_metadata), db_data)
})
test_that("link_arms works", {
skip_on_cran()
out <- link_arms(Sys.getenv("REDCAP_URI"), Sys.getenv("REDCAPTIDIER_LONGITUDINAL_API"))
# output is a tibble
expect_s3_class(out, "tbl")
# output contains expected columns
expected_cols <- c("arm_num", "unique_event_name", "form", "arm_name")
expect_setequal(expected_cols, names(out))
# all arms are represented in output (test redcap has 2 arms)
n_unique_arms <- length(unique(out$arm_num))
expect_equal(n_unique_arms, 2)
})
test_that("update_field_names works", {
# nolint start: line_length_linter
test_meta <- tibble::tribble(
~field_name, ~form_name, ~field_type, ~field_label, ~select_choices_or_calculations,
"record_id", NA_character_, "text", NA_character_, NA_character_,
"checkbox", "my_form", "checkbox", "Field Label", "1, 1 | -99, Unknown {embedded logic}",
"checkbox_no_label", "my_form", "checkbox", NA_character_, "1, 1",
"checkbox_w_colon", "my_form", "checkbox", "Field Label:", "1, 1",
"checkbox_no_opts", "my_form", "checkbox", "Field Label:", NA_character_,
"field", "my_form", "text", "Field Label {embedded logic}", NA_character_
)
# nolint end: line_length_linter
out <- update_field_names(test_meta) %>%
suppressWarnings(classes = "empty_parse_warning")
# Check cols are present and correctly ordered
expected_cols <- c(
"field_name", "form_name", "field_type", "field_label",
"select_choices_or_calculations", "field_name_updated"
)
expect_equal(colnames(out), expected_cols)
# Check field_name_updated was created correctly
field_name_updated <- out$field_name_updated[-1] # drop record_id row
expect_equal(
field_name_updated,
c(
"checkbox___1", "checkbox___-99", "checkbox_no_label___1",
"checkbox_w_colon___1", "checkbox_no_opts___NA", "field"
)
)
# Check field_label was correctly updated in place
## Checkbox labs appended in parentheses
## field embedding logic stripped
## Missing field labels converted to NA
field_label <- out$field_label[-1] # drop record_id row
expect_equal(
field_label,
c(
"Field Label: 1", "Field Label: Unknown", NA_character_,
"Field Label: 1", NA_character_, "Field Label"
)
)
})
test_that("update_field_names handles metadata without checkbox fields", {
test_meta <- tibble::tribble(
~field_name, ~form_name, ~field_type, ~field_label, ~select_choices_or_calculations,
"record_id", NA_character_, "text", NA_character_, NA_character_,
"my_radio", NA_character_, "radio", "xyz", "abc"
)
out <- update_field_names(test_meta)
# field_name_update is the same as field_name
expect_equal(out$field_name, out$field_name_updated)
# field_label is unchanged
expect_equal(out$field_label, test_meta$field_label)
})
test_that("try_redcapr works", {
# Passes along data when success is TRUE
try_redcapr(list(success = TRUE, data = "some_data")) %>%
expect_equal("some_data")
# Throws expected errors for internal REDCapR errors
try_redcapr(stop()) %>%
expect_error(class = "unexpected_error")
# Throws expected errors when REDCapR returns success = FALSE
try_redcapr(stop("Could not resolve host")) %>%
expect_error(class = "cannot_resolve_host")
try_redcapr(list(success = FALSE, status_code = 403)) %>%
expect_error(class = "api_token_rejected")
try_redcapr(list(success = FALSE, status_code = 405)) %>%
expect_error(class = "cannot_post")
# Unexpected error, no message
cnd <- try_redcapr(list(success = FALSE, status_code = "")) %>%
catch_cnd()
expect_s3_class(cnd, "unexpected_error")
expect_null(cnd$parent)
# Unexpected error, w/ message
cnd <- try_redcapr(list(success = FALSE, status_code = "", outcome_message = "some error")) %>%
catch_cnd()
expect_s3_class(cnd, "unexpected_error")
expect_equal(cnd$parent$message, "some error")
# Unexpected error, w/ message, works with multiline expr
cnd <- try_redcapr({
list(success = FALSE, status_code = "", outcome_message = "some error")
}) %>%
catch_cnd()
expect_s3_class(cnd, "unexpected_error")
expect_equal(cnd$parent$message, "some error")
try_redcapr(list(success = TRUE, data = as.numeric("A"))) %>%
expect_warning(class = "unexpected_warning")
})
test_that("add_partial_keys works", {
test_data <- tibble::tribble(
~record_id, ~redcap_event_name, ~redcap_repeat_instrument, ~redcap_repeat_instance,
1, "nr_event_arm_1", NA, NA,
1, "nr_event_arm_1", "r_instrument", 1,
3, "nr_event_arm_1", "r_instrument", 1,
4, "r_event_arm_1", NA, 1
)
out <- test_data %>%
add_partial_keys(var = .data$redcap_event_name)
expected_cols <- c(
"record_id",
"redcap_event_name",
"redcap_repeat_instrument",
"redcap_form_instance",
"redcap_event_instance",
"redcap_event",
"redcap_arm"
)
expect_true(all(expected_cols %in% names(out)))
expect_s3_class(out, "data.frame")
expect_true(nrow(out) > 0)
})
test_that("create_repeat_instance_vars works", {
repeat_events <- tibble::tribble(
~record_id, ~redcap_event, ~redcap_arm, ~redcap_repeat_instrument, ~redcap_repeat_instance,
1, "nr_event", 1, NA, NA,
1, "nr_event", 1, "r_instrument", 1,
3, "nr_event", 1, "r_instrument", 1,
4, "r_event", 1, NA, 1
)
no_repeat_events <- tibble::tribble(
~record_id, ~redcap_event, ~redcap_arm, ~redcap_repeat_instrument, ~redcap_repeat_instance,
1, "nr_event", 1, NA, NA,
1, "nr_event", 1, "r_instrument", 1,
3, "nr_event", 1, "r_instrument", 1
)
expected_cols <- c(
"record_id",
"redcap_event",
"redcap_arm",
"redcap_repeat_instrument",
"redcap_form_instance",
"redcap_event_instance"
)
repeat_out <- create_repeat_instance_vars(repeat_events)
nonrepeat_out <- create_repeat_instance_vars(no_repeat_events)
expect_true(all(expected_cols %in% names(repeat_out)))
expect_s3_class(repeat_out, "data.frame")
expect_true(nrow(repeat_out) > 0)
expect_true(all(expected_cols[1:5] %in% names(nonrepeat_out)))
expect_s3_class(nonrepeat_out, "data.frame")
expect_true(nrow(nonrepeat_out) > 0)
})
test_that("remove_empty_rows works", {
my_record_id <- "record_id"
complete_out <- tibble::tribble(
~record_id, ~redcap_event, ~redcap_event_instance, ~data, ~form_status_complete,
1, "event1", 1, "A", "Complete",
1, "event2", 2, "B", "Incomplete",
2, "event1", 1, "C", "Complete",
)
complete_out %>%
remove_empty_rows(my_record_id) %>%
expect_equal(complete_out)
empty_out <- tibble::tribble(
~record_id, ~redcap_event, ~redcap_event_instance, ~data, ~form_status_complete,
1, "event1", 1, "A", "Complete",
1, "event2", 2, NA, "Incomplete",
2, "event1", 1, "C", "Complete",
)
expected_out <- tibble::tribble(
~record_id, ~redcap_event, ~redcap_event_instance, ~data, ~form_status_complete,
1, "event1", 1, "A", "Complete",
2, "event1", 1, "C", "Complete",
)
empty_out %>%
remove_empty_rows(my_record_id) %>%
expect_equal(expected_out)
})
test_that("is_labelled works", {
df <- tibble::tribble(
~"one", ~"two", ~"three",
1, 2, 3
)
is_labelled <- is_labelled(df)
expect_false(is_labelled)
labels <- c("One", "Two", "Three")
labelled::var_label(df) <- labels
is_labelled <- is_labelled(df)
expect_true(is_labelled)
})
test_that("make_skimr_labels works", {
skimr_labels <- make_skimr_labels()
# Test for named character vector output
expect_true(is.vector(skimr_labels))
expect_true(is.character(skimr_labels))
expect_true(!is.null(attr(skimr_labels, "name")))
})
test_that("invert_vec works", {
expect_equal(invert_vec(c("a" = 1, "b" = 2)), c("1" = "a", "2" = "b"))
expect_equal(invert_vec(1:2), 1:2)
})
test_that("apply_labs_haven works", {
skip_if_not_installed("labelled")
# Testing all classes where labelled has built in val_labels<- methods
out <- apply_labs_haven(1:3, c("1" = "a", "2" = "b", "3" = "c"), integer())
expect_s3_class(out, "haven_labelled")
expect_equal(labelled::val_labels(out), c(a = 1, b = 2, c = 3))
out <- apply_labs_haven(c(1.1, 2, 3.3), c("1.1" = "a", "2" = "b", "3.3" = "c"), numeric())
expect_s3_class(out, "haven_labelled")
expect_equal(labelled::val_labels(out), c(a = 1.1, b = 2.0, c = 3.3))
out <- apply_labs_haven(letters[1:3], c("a" = "x", "b" = "y", "c" = "z"), character())
expect_s3_class(out, "haven_labelled")
expect_equal(labelled::val_labels(out), c(x = "a", y = "b", z = "c"))
# Case with mismatching data types between labels from metadata and data values
out <- apply_labs_haven(1:2, c("1" = "a", "2" = "b", "3.3" = "c"), integer())
expect_s3_class(out, "haven_labelled")
expect_equal(labelled::val_labels(out), c(a = "1", b = "2", c = "3.3"))
})
test_that("apply_labs_factor works", {
out <- apply_labs_factor(1:3, c("1" = "a", "2" = "b", "3" = "c"))
expect_s3_class(out, "factor")
expect_equal(out, factor(letters[1:3]))
})
test_that("force_cast works", {
expect_s3_class(force_cast("2023-01-01", as.Date(NA)), "Date")
expect_s3_class(force_cast("12:00", as.difftime(0, units = "secs")), "difftime")
expect_s3_class(force_cast("2023-01-01 12:00", as.POSIXct(NA)), "POSIXct")
})