library(ggplot2) # --- civilytics_colors ------------------------------------------------------- test_that("civilytics_colors is a named character vector of hex codes", { expect_type(civilytics_colors, "character") expect_named(civilytics_colors) expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", civilytics_colors))) }) test_that("civilytics_colors contains expected brand keys", { expected <- c("paper", "ink", "navy_600", "ember_600", "teal_600", "plum_600", "moss_600", "paper_2", "rule") expect_true(all(expected %in% names(civilytics_colors))) }) test_that("civilytics_colors values match brand specification", { expect_equal(unname(civilytics_colors["paper"]), "#FAF7F2") expect_equal(unname(civilytics_colors["ink"]), "#0E1A2B") expect_equal(unname(civilytics_colors["ember_600"]), "#C25311") expect_equal(unname(civilytics_colors["navy_700"]), "#1A2E4A") expect_equal(unname(civilytics_colors["ember_400"]), "#EA8A49") }) # --- civilytics_palettes / civilytics_palette -------------------------------- test_that("civilytics_palettes contains all expected palettes", { expected <- c("qual", "qual_warm", "qual_cool", "seq_ember", "seq_navy", "seq_violet", "seq_paper_ink", "div_navy_ember", "div_violet_ember") expect_true(all(expected %in% names(civilytics_palettes))) }) test_that("civilytics_palette returns character vector of hex codes", { result <- civilytics_palette() expect_type(result, "character") expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result))) }) test_that("civilytics_palette respects n argument", { expect_length(civilytics_palette("qual", n = 3), 3) expect_length(civilytics_palette("seq_navy", n = 5), 5) }) test_that("civilytics_palette interpolates sequential palettes", { result <- civilytics_palette("seq_ember", n = 20) expect_length(result, 20) expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result))) }) test_that("civilytics_palette warns when recycling qualitative", { expect_warning(civilytics_palette("qual", n = 10), "recycling") }) test_that("civilytics_palette reverse works", { fwd <- civilytics_palette("seq_navy") rev <- civilytics_palette("seq_navy", reverse = TRUE) expect_equal(fwd, rev(rev)) }) test_that("civilytics_palette errors on unknown palette name", { expect_error(civilytics_palette("nope"), "'nope' is not a valid palette") }) # --- civilytics_pal (closure) ------------------------------------------------ test_that("civilytics_pal returns a function", { f <- civilytics_pal() expect_type(f, "closure") result <- f(4) expect_length(result, 4) expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result))) }) # --- scale_color/fill_civilytics --------------------------------------------- test_that("scale_color_civilytics returns a Scale object (discrete)", { sc <- scale_color_civilytics() expect_s3_class(sc, "Scale") }) test_that("scale_color_civilytics returns a Scale object (continuous)", { sc <- scale_color_civilytics("seq_navy", discrete = FALSE) expect_s3_class(sc, "Scale") }) test_that("scale_fill_civilytics returns a Scale object (discrete)", { sc <- scale_fill_civilytics() expect_s3_class(sc, "Scale") }) test_that("scale_fill_civilytics returns a Scale object (continuous)", { sc <- scale_fill_civilytics("seq_ember", discrete = FALSE) expect_s3_class(sc, "Scale") }) test_that("scale_color_civilytics reverse parameter works", { sc <- scale_color_civilytics("qual", reverse = TRUE) expect_s3_class(sc, "Scale") }) test_that("scale_color_civilytics works in a ggplot", { p <- ggplot(mpg, aes(displ, hwy, colour = class)) + geom_point() + scale_color_civilytics() expect_no_error(ggplot_build(p)) }) test_that("scale_fill_civilytics works in a ggplot", { p <- ggplot(mpg, aes(class, fill = class)) + geom_bar() + scale_fill_civilytics() expect_no_error(ggplot_build(p)) }) # --- theme_civilytics -------------------------------------------------------- test_that("theme_civilytics returns a complete ggplot2 theme", { th <- theme_civilytics() expect_s3_class(th, "theme") expect_true(attr(th, "complete")) }) test_that("theme_civilytics applies to a ggplot without error", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics() expect_no_error(ggplot_build(p)) }) test_that("theme_civilytics uses brand ink color for text", { th <- theme_civilytics() expect_equal(th$text$colour, unname(civilytics_colors["ink"])) }) test_that("theme_civilytics has transparent background by default", { th <- theme_civilytics() expect_true(is.na(th$plot.background$fill)) expect_true(is.na(th$panel.background$fill)) }) test_that("theme_civilytics paper_bg=TRUE opt-in fills with cream", { th <- theme_civilytics(paper_bg = TRUE) expect_equal(th$plot.background$fill, unname(civilytics_colors["paper"])) expect_equal(th$panel.background$fill, unname(civilytics_colors["paper"])) }) test_that("theme_civilytics uses paper_2 for strip background by default", { th <- theme_civilytics() expect_equal(th$strip.background$fill, unname(civilytics_colors["paper_2"])) }) test_that("theme_civilytics strip_color parameter is respected", { th <- theme_civilytics(strip_color = "#FF0000") expect_equal(th$strip.background$fill, "#FF0000") }) test_that("theme_civilytics font_size parameter scales text elements", { th_big <- theme_civilytics(font_size = 20) th_sml <- theme_civilytics(font_size = 10) expect_gt(th_big$text$size, th_sml$text$size) }) test_that("theme_civilytics grid parameter controls gridlines", { th_y <- theme_civilytics(grid = "y") th_x <- theme_civilytics(grid = "x") th_both <- theme_civilytics(grid = "both") th_none <- theme_civilytics(grid = "none") # grid="y" shows y gridlines, blanks x expect_s3_class(th_y$panel.grid.major.y, "element_line") expect_s3_class(th_y$panel.grid.major.x, "element_blank") # grid="x" shows x gridlines, blanks y expect_s3_class(th_x$panel.grid.major.x, "element_line") expect_s3_class(th_x$panel.grid.major.y, "element_blank") # grid="both" shows both expect_s3_class(th_both$panel.grid.major.x, "element_line") expect_s3_class(th_both$panel.grid.major.y, "element_line") # grid="none" blanks both expect_s3_class(th_none$panel.grid.major.x, "element_blank") expect_s3_class(th_none$panel.grid.major.y, "element_blank") }) test_that("theme_civilytics paper_bg=FALSE gives transparent background", { th <- theme_civilytics(paper_bg = FALSE) expect_true(is.na(th$plot.background$fill)) expect_true(is.na(th$panel.background$fill)) }) test_that("theme_civilytics has plot-aligned title and caption", { th <- theme_civilytics() expect_equal(th$plot.title.position, "plot") expect_equal(th$plot.caption.position, "plot") }) # --- theme_civilytics_dark --------------------------------------------------- test_that("theme_civilytics_dark returns a complete ggplot2 theme", { th <- theme_civilytics_dark() expect_s3_class(th, "theme") expect_true(attr(th, "complete")) }) test_that("theme_civilytics_dark applies to a ggplot without error", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics_dark() expect_no_error(ggplot_build(p)) }) test_that("theme_civilytics_dark uses paper color as ink (light text on dark bg)", { th <- theme_civilytics_dark() expect_equal(th$text$colour, unname(civilytics_colors["paper"])) }) test_that("theme_civilytics_dark uses navy_700 as background", { th <- theme_civilytics_dark() expect_equal(th$plot.background$fill, unname(civilytics_colors["navy_700"])) }) test_that("theme_civilytics_dark uses navy_600 for strip background", { th <- theme_civilytics_dark() expect_equal(th$strip.background$fill, unname(civilytics_colors["navy_600"])) }) test_that("theme_civilytics_dark inherits grid parameter", { th <- theme_civilytics_dark(grid = "both") expect_s3_class(th$panel.grid.major.x, "element_line") expect_s3_class(th$panel.grid.major.y, "element_line") }) test_that("theme_civilytics_dark uses readable subtitle and caption colors", { th <- theme_civilytics_dark() # Subtitle should use navy_200, not the dark ink_2 expect_equal(th$plot.subtitle$colour, unname(civilytics_colors["navy_200"])) # Caption should use navy_300, not the dark ink_3 expect_equal(th$plot.caption$colour, unname(civilytics_colors["navy_300"])) }) test_that("theme_civilytics_dark uses readable axis text colors", { th <- theme_civilytics_dark() expect_equal(th$axis.text$colour, unname(civilytics_colors["navy_200"])) expect_equal(th$axis.title.x$colour, unname(civilytics_colors["navy_300"])) expect_equal(th$axis.title.y$colour, unname(civilytics_colors["navy_300"])) }) # --- theme_civilytics_slide -------------------------------------------------- test_that("theme_civilytics_slide returns a complete ggplot2 theme", { th <- theme_civilytics_slide() expect_s3_class(th, "theme") expect_true(attr(th, "complete")) }) test_that("theme_civilytics_slide has transparent background by default", { th <- theme_civilytics_slide() expect_true(is.na(th$plot.background$fill)) }) test_that("theme_civilytics_slide uses larger base font", { th_slide <- theme_civilytics_slide() th_base <- theme_civilytics() expect_gt(th_slide$text$size, th_base$text$size) }) test_that("theme_civilytics_slide applies to a ggplot without error", { p <- ggplot(mpg, aes(displ, hwy, colour = class)) + geom_point() + scale_color_civilytics() + theme_civilytics_slide() expect_no_error(ggplot_build(p)) }) # --- theme_civilytics_map ---------------------------------------------------- test_that("theme_civilytics_map returns a complete theme", { th <- theme_civilytics_map() expect_s3_class(th, "theme") expect_true(attr(th, "complete")) }) test_that("theme_civilytics_map suppresses axes and gridlines", { th <- theme_civilytics_map() expect_s3_class(th$axis.line.x, "element_blank") expect_s3_class(th$axis.text, "element_blank") expect_s3_class(th$axis.ticks, "element_blank") expect_s3_class(th$axis.title.x, "element_blank") expect_s3_class(th$axis.title.y, "element_blank") expect_s3_class(th$panel.grid.major.x, "element_blank") expect_s3_class(th$panel.grid.major.y, "element_blank") }) test_that("theme_civilytics_map preserves title and caption elements", { th <- theme_civilytics_map() expect_s3_class(th$plot.title, "element_text") expect_s3_class(th$plot.subtitle, "element_text") expect_s3_class(th$plot.caption, "element_text") }) test_that("theme_civilytics_map applies to a ggplot without error", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics_map() expect_no_error(ggplot_build(p)) }) # --- theme_civilytics_dark_map ----------------------------------------------- test_that("theme_civilytics_dark_map returns a complete theme", { th <- theme_civilytics_dark_map() expect_s3_class(th, "theme") expect_true(attr(th, "complete")) }) test_that("theme_civilytics_dark_map suppresses axes on dark background", { th <- theme_civilytics_dark_map() expect_s3_class(th$axis.line.x, "element_blank") expect_s3_class(th$axis.text, "element_blank") expect_s3_class(th$axis.ticks, "element_blank") expect_equal(th$plot.background$fill, unname(civilytics_colors["navy_700"])) }) test_that("theme_civilytics_dark_map applies to a ggplot without error", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics_dark_map() expect_no_error(ggplot_build(p)) }) # --- theme_civilytics_slide_map ---------------------------------------------- test_that("theme_civilytics_slide_map returns a complete theme", { th <- theme_civilytics_slide_map() expect_s3_class(th, "theme") expect_true(attr(th, "complete")) }) test_that("theme_civilytics_slide_map has larger font and suppressed axes", { th <- theme_civilytics_slide_map() th_base <- theme_civilytics_map() expect_gt(th$text$size, th_base$text$size) expect_s3_class(th$axis.line.x, "element_blank") expect_s3_class(th$axis.text, "element_blank") }) test_that("theme_civilytics_slide_map has transparent background by default", { th <- theme_civilytics_slide_map() expect_true(is.na(th$plot.background$fill)) }) test_that("theme_civilytics_slide_map applies to a ggplot without error", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics_slide_map() expect_no_error(ggplot_build(p)) }) # --- make_logo_grob ---------------------------------------------------------- test_that("make_logo_grob returns a gg object for all type/variant combos", { for (type in c("wordmark", "mark")) { for (variant in c("light", "dark")) { logo <- make_logo_grob(type = type, variant = variant) expect_s3_class(logo, "gg") } } }) test_that("make_logo_grob defaults to wordmark light", { expect_no_error(make_logo_grob()) }) test_that("make_logo_grob errors on invalid type", { expect_error(make_logo_grob(type = "banner")) }) test_that("make_logo_grob errors on invalid variant", { expect_error(make_logo_grob(variant = "purple")) }) # --- civilytics_logo (pipe-friendly) ----------------------------------------- test_that("civilytics_logo returns a grob", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics() result <- suppressWarnings(civilytics_logo(p)) expect_s3_class(result, "grob") }) test_that("civilytics_logo works with pipe operator", { # |> has higher precedence than +, so parens are required result <- suppressWarnings( (ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics()) |> civilytics_logo() ) expect_s3_class(result, "grob") }) test_that("civilytics_logo accepts type and variant params", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics_dark() result <- suppressWarnings(civilytics_logo(p, type = "mark", variant = "dark")) expect_s3_class(result, "grob") }) # --- position argument ------------------------------------------------------- test_that("civilytics_logo accepts all four position values", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics() for (pos in c("bottom-right", "bottom-left", "top-right", "top-left")) { result <- suppressWarnings(civilytics_logo(p, position = pos)) expect_s3_class(result, "grob") } }) test_that("civilytics_logo errors on invalid position", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics() expect_error(civilytics_logo(p, position = "center")) }) test_that("make_logo_grob accepts position argument", { for (pos in c("bottom-right", "bottom-left", "top-right", "top-left")) { logo <- make_logo_grob(position = pos) expect_s3_class(logo, "gg") } }) test_that("top positions place logo before plot in arrangeGrob", { p <- ggplot(mpg, aes(displ, hwy)) + geom_point() + theme_civilytics() result_top <- suppressWarnings(civilytics_logo(p, position = "top-right")) result_bot <- suppressWarnings(civilytics_logo(p, position = "bottom-right")) # Top: logo is first grob; bottom: plot is first grob. # The arrangeGrob heights differ in order expect_s3_class(result_top, "grob") expect_s3_class(result_bot, "grob") })