## Tests for Phase 4: twb_calc_complexity, twb_field_usage, twb_replication_brief
# ---- helpers -----------------------------------------------------------------
.make_calc_xml <- function(...) {
calcs <- list(...)
col_nodes <- vapply(calcs, function(c) {
caption_attr <- if (!is.null(c$caption)) sprintf(' caption="%s"', c$caption) else ""
sprintf(
'
',
c$name, caption_attr,
c$datatype %||% "string", c$role %||% "measure",
gsub('"', """, c$formula %||% "")
)
}, character(1L))
xml2::read_xml(sprintf(
'
%s
',
paste(col_nodes, collapse = "\n")
))
}
`%||%` <- function(a, b) if (!is.null(a)) a else b
# ---- twb_calc_complexity: columns -------------------------------------------
test_that("twb_calc_complexity returns expected columns", {
xml <- .make_calc_xml(
list(name = "[Sales]", formula = "SUM([Price])"),
list(name = "[Count]", formula = "COUNT([Orders])")
)
out <- twb_calc_complexity(xml)
expected_cols <- c("datasource", "name", "tableau_internal_name",
"datatype", "role", "calc_type", "lod_type",
"is_table_calc", "dep_depth", "n_deps", "formula")
expect_true(all(expected_cols %in% names(out)))
})
# ---- twb_calc_complexity: LOD detection -------------------------------------
test_that("twb_calc_complexity detects FIXED LOD", {
xml <- .make_calc_xml(
list(name = "[FixedSales]", formula = "{ FIXED [Category] : SUM([Sales]) }")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "lod")
expect_equal(out$lod_type, "fixed")
})
test_that("twb_calc_complexity detects INCLUDE LOD", {
xml <- .make_calc_xml(
list(name = "[IncSales]", formula = "{ INCLUDE [Category] : SUM([Sales]) }")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "lod")
expect_equal(out$lod_type, "include")
})
test_that("twb_calc_complexity detects EXCLUDE LOD", {
xml <- .make_calc_xml(
list(name = "[ExclSales]", formula = "{ EXCLUDE [Category] : SUM([Sales]) }")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "lod")
expect_equal(out$lod_type, "exclude")
})
# ---- twb_calc_complexity: table_calc detection ------------------------------
test_that("twb_calc_complexity detects table calcs", {
xml <- .make_calc_xml(
list(name = "[Running]", formula = "RUNNING_SUM(SUM([Sales]))")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "table_calc")
expect_true(out$is_table_calc)
})
# ---- twb_calc_complexity: aggregate detection -------------------------------
test_that("twb_calc_complexity classifies SUM as aggregate", {
xml <- .make_calc_xml(
list(name = "[TotalSales]", formula = "SUM([Sales])")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "aggregate")
expect_true(is.na(out$lod_type))
})
test_that("twb_calc_complexity classifies COUNTD as aggregate", {
xml <- .make_calc_xml(
list(name = "[UniqueCustomers]", formula = "COUNTD([Customer ID])")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "aggregate")
})
# ---- twb_calc_complexity: raw classification --------------------------------
test_that("twb_calc_complexity classifies string concat as raw", {
xml <- .make_calc_xml(
list(name = "[FullName]", formula = "[First] + " " + [Last]",
datatype = "string")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "raw")
expect_true(is.na(out$lod_type))
})
# ---- twb_calc_complexity: LOD wins over table_calc precedence ---------------
test_that("LOD wins over table_calc when both patterns match", {
# Contrived formula with both patterns
xml <- .make_calc_xml(
list(name = "[Weird]",
formula = "{ FIXED [Cat] : RUNNING_SUM(SUM([Sales])) }")
)
out <- twb_calc_complexity(xml)
expect_equal(out$calc_type, "lod")
})
# ---- twb_calc_complexity: dep_depth -----------------------------------------
test_that("dep_depth is 0 for direct formula with no calc deps", {
xml <- .make_calc_xml(
list(name = "[Revenue]", formula = "SUM([Sales])")
)
out <- twb_calc_complexity(xml)
expect_equal(out$dep_depth, 0L)
})
test_that("dep_depth is 1 for calc depending on another calc", {
xml <- .make_calc_xml(
list(name = "[Revenue]", formula = "SUM([Sales])"),
list(name = "[Revenue2]", formula = "[Revenue] * 2")
)
out <- twb_calc_complexity(xml)
revenue2 <- out[out$name == "Revenue2", ]
expect_equal(revenue2$dep_depth, 1L)
})
test_that("dep_depth is 2 for three-level calc chain", {
xml <- .make_calc_xml(
list(name = "[A]", formula = "SUM([x])"),
list(name = "[B]", formula = "[A] * 2"),
list(name = "[C]", formula = "[B] + 1")
)
out <- twb_calc_complexity(xml)
cc <- out[out$name == "C", ]
expect_equal(cc$dep_depth, 2L)
})
# ---- twb_calc_complexity: empty workbook ------------------------------------
test_that("twb_calc_complexity returns typed empty tibble for no calcs", {
xml <- xml2::read_xml(
''
)
out <- twb_calc_complexity(xml)
expect_equal(nrow(out), 0L)
expect_true("calc_type" %in% names(out))
expect_true("dep_depth" %in% names(out))
})
# ---- twb_field_usage: basic columns -----------------------------------------
test_that("twb_field_usage returns expected columns (long form)", {
xml <- xml2::read_xml(
'
'
)
out <- twb_field_usage(xml)
expect_true(all(c("field_clean", "datasource", "sheet",
"context", "n_appearances") %in% names(out)))
})
test_that("twb_field_usage wide=TRUE returns one row per field", {
xml <- xml2::read_xml(
'
'
)
out_long <- twb_field_usage(xml, wide = FALSE)
out_wide <- twb_field_usage(xml, wide = TRUE)
# Wide has no sheet / context / n_appearances columns
expect_false("context" %in% names(out_wide))
expect_false("n_appearances" %in% names(out_wide))
# Each unique (field_clean, datasource) becomes one row
expect_equal(nrow(out_wide), nrow(dplyr::distinct(out_long, field_clean, datasource)))
})
test_that("twb_field_usage include_shelves=FALSE shows only filters", {
xml <- xml2::read_xml(
'
'
)
out <- twb_field_usage(xml, include_shelves = FALSE)
if (nrow(out) > 0L)
expect_true(all(out$context == "filter"))
})
test_that("twb_field_usage returns empty tibble when both FALSE", {
xml <- xml2::read_xml('')
expect_message(
out <- twb_field_usage(xml, include_filters = FALSE, include_shelves = FALSE),
"both FALSE"
)
expect_equal(nrow(out), 0L)
})
test_that("twb_field_usage empty workbook returns empty tibble", {
xml <- xml2::read_xml('')
out <- twb_field_usage(xml)
expect_equal(nrow(out), 0L)
expect_true(all(c("field_clean", "datasource", "sheet", "context",
"n_appearances") %in% names(out)))
})
# ---- twb_replication_brief: list structure ----------------------------------
test_that("twb_replication_brief returns a list with expected elements", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
xml <- xml2::read_xml(twb)
brief <- twb_replication_brief(xml)
expect_type(brief, "list")
expected_keys <- c("meta", "datasources", "parameters", "custom_sql",
"calculated_fields", "field_usage", "filters", "sorts",
"chart_types", "dashboard_layout", "actions")
expect_true(all(expected_keys %in% names(brief)))
})
test_that("twb_replication_brief meta has correct columns and types", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
xml <- xml2::read_xml(twb)
meta <- twb_replication_brief(xml)$meta
expect_equal(nrow(meta), 1L)
expect_true(is.character(meta$workbook_file))
expect_true(is.integer(meta$n_datasources))
expect_true(is.integer(meta$n_worksheets))
expect_true(is.character(meta$generated_at))
})
test_that("twb_replication_brief format=text returns character(1)", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
xml <- xml2::read_xml(twb)
txt <- twb_replication_brief(xml, format = "text")
expect_type(txt, "character")
expect_length(txt, 1L)
expect_match(txt, "REPLICATION BRIEF")
expect_match(txt, "CALCULATED FIELDS")
})
test_that("twb_replication_brief include_sql=FALSE sets custom_sql to NULL", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
xml <- xml2::read_xml(twb)
brief <- twb_replication_brief(xml, include_sql = FALSE)
expect_null(brief$custom_sql)
})
test_that("twb_replication_brief include_formulas=TRUE adds formula_pretty", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
xml <- xml2::read_xml(twb)
brief <- twb_replication_brief(xml, include_formulas = TRUE)
cf <- brief$calculated_fields
if (nrow(cf) > 0L)
expect_true("formula_pretty" %in% names(cf))
})
# ---- TwbParser integration --------------------------------------------------
test_that("TwbParser exposes get_calc_complexity and active binding", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
parser <- TwbParser$new(twb)
method_out <- parser$get_calc_complexity()
binding_out <- parser$calc_complexity
expect_s3_class(method_out, "tbl_df")
expect_identical(binding_out, method_out)
})
test_that("TwbParser exposes get_field_usage and active binding", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
parser <- TwbParser$new(twb)
method_out <- parser$get_field_usage()
binding_out <- parser$field_usage
expect_s3_class(method_out, "tbl_df")
expect_identical(binding_out, method_out)
})
test_that("TwbParser get_replication_brief returns a list", {
twb <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
skip_if_not(nzchar(twb) && file.exists(twb), "example .twb not found")
parser <- TwbParser$new(twb)
brief <- parser$get_replication_brief()
expect_type(brief, "list")
expect_true("meta" %in% names(brief))
# workbook_file should be the basename of the .twb path
expect_equal(brief$meta$workbook_file, basename(twb))
})