titanic_df <- as.data.frame(Titanic) #' Build geom_marimekko and return computed layer data. build_marimekko <- function(df, mapping, ..., layer_index = 1L) { ggplot_build(ggplot(df) + geom_marimekko(mapping, ...))$data[[layer_index]] } #' Extract sorted unique column geometries from built data. column_geom <- function(d) { cols <- unique(d[, c("xmin", "xmax")]) cols <- cols[order(cols$xmin), ] cols$width <- cols$xmax - cols$xmin rownames(cols) <- NULL cols } #' Extract sorted segments within a single column (by xmin value). column_segments <- function(d, col_xmin) { seg <- d[d$xmin == col_xmin, ] seg[order(seg$ymin), ] } describe("geom_marimekko", { describe("rectangle geometry", { it("computes exact tile positions for a 2x2 equal-weight table with gap=0", { # A: Y=50, N=50; B: Y=50, N=50. Grand=200, each column 50%. # Fill levels alphabetical: N, Y. Each cond_prop=0.5. df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(50, 50, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) cols <- column_geom(d) expect_equal(cols$xmin, c(0, 0.5)) expect_equal(cols$xmax, c(0.5, 1.0)) seg_a <- column_segments(d, 0) expect_equal(seg_a$ymin, c(0, 0.5)) expect_equal(seg_a$ymax, c(0.5, 1.0)) }) it("computes exact widths proportional to weight (3:1 -> 0.75 and 0.25)", { df <- data.frame(x = c("A", "B"), fill = c("Y", "Y"), weight = c(3, 1)) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) cols <- column_geom(d) expect_equal(cols$xmin, c(0, 0.75)) expect_equal(cols$xmax, c(0.75, 1.0)) expect_equal(cols$width, c(0.75, 0.25)) }) it("computes exact vertical split for 75/25 weights within a column", { # Single column, fills N(25) and Y(75). Factor order: N first. df <- data.frame(x = c("A", "A"), fill = c("Y", "N"), weight = c(75, 25)) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) d <- d[order(d$ymin), ] expect_equal(nrow(d), 2L) expect_equal(d$ymin, c(0, 0.25)) expect_equal(d$ymax, c(0.25, 1.0)) }) it("computes exact geometry for asymmetric 2x2 table (30,70,50,50)", { # A: Y=30, N=70 (total=100); B: Y=50, N=50 (total=100). Grand=200. # Both columns width=0.5. Within A: N=0.7, Y=0.3; Within B: N=0.5, Y=0.5. df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(30, 70, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) seg_a <- column_segments(d, 0) expect_equal(seg_a$ymin, c(0, 0.7)) expect_equal(seg_a$ymax, c(0.7, 1.0)) seg_b <- column_segments(d, 0.5) expect_equal(seg_b$ymin, c(0, 0.5)) expect_equal(seg_b$ymax, c(0.5, 1.0)) }) it("places tile center x,y at midpoints of xmin/xmax and ymin/ymax", { d <- build_marimekko(titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) expect_equal(d$x, (d$xmin + d$xmax) / 2) expect_equal(d$y, (d$ymin + d$ymax) / 2) }) it("produces correct tile count for 3x3 table", { df <- expand.grid(x = c("A", "B", "C"), fill = c("X", "Y", "Z")) df$weight <- 1:9 d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill) expect_equal(nrow(d), 9L) }) it("keeps all tiles within [0,1] x [0,1]", { d <- build_marimekko(titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) expect_true(all(d$xmin >= 0 & d$xmax <= 1)) expect_true(all(d$ymin >= 0 & d$ymax <= 1)) }) it("tiles each column to exactly ymax=1 when gap=0", { d <- build_marimekko( titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived, gap = 0 ) max_y <- tapply(d$ymax, d$xmin, max) expect_equal(as.numeric(max_y), rep(1, length(max_y))) }) }) describe("gap parameter", { it("with gap=0 adjacent columns touch exactly", { df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(50, 50, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) cols <- column_geom(d) expect_equal(cols$xmax[1], cols$xmin[2]) }) it("with gap=0.1 columns are separated by exactly 0.1", { # 2 equal columns, gap=0.1: usable_width=0.9, each col=0.45 # A: [0, 0.45], B: [0.55, 1.0] df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(50, 50, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0.1) cols <- column_geom(d) expect_equal(cols$xmin[2] - cols$xmax[1], 0.1) expect_equal(cols$width, c(0.45, 0.45)) }) it("with gap=0.1 vertical segments are separated by 0.1", { df <- data.frame( x = c("A", "A"), fill = c("Y", "N"), weight = c(50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0.1) d <- d[order(d$ymin), ] # usable_height=0.9, each segment=0.45 expect_equal(d$ymin[2] - d$ymax[1], 0.1) expect_equal(d$ymax[1] - d$ymin[1], 0.45) }) }) describe("gap_x / gap_y parameters", { it("gap_x controls horizontal spacing independently", { df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(50, 50, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap_x = 0.05, gap_y = 0 ) cols <- column_geom(d) expect_equal(cols$xmin[2] - cols$xmax[1], 0.05) seg <- column_segments(d, cols$xmin[1]) expect_equal(seg$ymin[2], seg$ymax[1]) }) it("gap_y controls vertical spacing independently", { df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(50, 50, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap_x = 0, gap_y = 0.08 ) cols <- column_geom(d) expect_equal(cols$xmax[1], cols$xmin[2]) seg <- column_segments(d, cols$xmin[1]) expect_equal(seg$ymin[2] - seg$ymax[1], 0.08) }) it("gap_x and gap_y override gap", { df <- data.frame( x = rep(c("A", "B"), each = 2), fill = rep(c("Y", "N"), 2), weight = c(50, 50, 50, 50) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0.1, gap_x = 0.02, gap_y = 0.04 ) cols <- column_geom(d) expect_equal(cols$xmin[2] - cols$xmax[1], 0.02) seg <- column_segments(d, cols$xmin[1]) expect_equal(seg$ymin[2] - seg$ymax[1], 0.04) }) }) describe("formula-based API", { it("formula = ~ a | b produces 2-variable mosaic", { d <- ggplot_build( ggplot(titanic_df) + geom_marimekko( aes(fill = Survived, weight = Freq), formula = ~ Class | Survived ) )$data[[1]] expect_equal(nrow(d), 8L) # 4 classes * 2 survival expect_true(all(d$xmin >= 0 & d$xmax <= 1)) expect_true(all(d$ymin >= 0 & d$ymax <= 1)) }) it("formula = ~ a | b | c produces 3-variable mosaic", { d <- ggplot_build( ggplot(titanic_df) + geom_marimekko( aes(fill = Survived, weight = Freq), formula = ~ Class | Survived | Sex ) )$data[[1]] # More tiles than 2-var expect_equal(nrow(d), 16L) }) it("formula = ~ a + b | c produces double-decker pattern", { d <- ggplot_build( ggplot(titanic_df) + geom_marimekko( aes(fill = Survived, weight = Freq), formula = ~ Class + Sex | Survived ) )$data[[1]] expect_equal(nrow(d), 16L) }) it("errors when formula is not provided", { expect_error( geom_marimekko(aes(fill = Survived, weight = Freq)), "formula.*required" ) }) it("errors if formula is not a formula object", { expect_error( ggplot(titanic_df) + geom_marimekko(aes(weight = Freq), formula = "~ Class | Survived"), "must be a formula" ) }) it("errors if formula is two-sided", { expect_error( ggplot(titanic_df) + geom_marimekko(aes(weight = Freq), formula = Class ~ Survived), "one-sided" ) }) it("errors if formula has no variables", { expect_error( ggplot(titanic_df) + geom_marimekko(aes(weight = Freq), formula = ~1), "at least one variable" ) }) }) describe("computed variables", { it("weight matches aggregated input values", { d <- build_marimekko(titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) # Total weight should equal sum of all Freq expect_equal(sum(d$weight), sum(titanic_df$Freq)) }) }) describe("default aesthetics", { it("returns NA colour and alpha=0.9 when no colour or alpha is specified", { df <- data.frame(x = c("A", "B"), fill = c("Y", "Y"), weight = c(1, 1)) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill) expect_true(all(is.na(d$colour))) expect_true(all(d$alpha == 0.9)) }) it("returns colour='red' and alpha=0.5 when colour='red' and alpha=0.5 are specified", { df <- data.frame(x = c("A", "B"), fill = c("Y", "Y"), weight = c(1, 1)) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, colour = "red", alpha = 0.5 ) expect_true(all(d$colour == "red")) expect_true(all(d$alpha == 0.5)) }) }) describe("factor level ordering", { it("respects factor level order for column placement", { # B(3 obs) before A(1 obs) per factor levels df <- data.frame( x = factor(c("B", "B", "B", "A"), levels = c("B", "A")), fill = c("Y", "Y", "Y", "Y") ) d <- build_marimekko(df, aes(fill = fill), formula = ~ x | fill, gap = 0) d <- d[order(d$xmin), ] # B has 3/4 weight, A has 1/4 expect_equal(d$xmax[1] - d$xmin[1], 0.75) expect_equal(d$xmax[2] - d$xmin[2], 0.25) expect_equal(d$xmin[1], 0) }) }) describe("edge cases", { it("single x category spans full width", { df <- data.frame(x = c("A", "A"), fill = c("Y", "N"), weight = c(60, 40)) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) expect_equal(min(d$xmin), 0) expect_equal(max(d$xmax), 1) }) it("single fill category fills full height with ymin=0 and ymax=1", { df <- data.frame(x = c("A", "B", "C"), fill = c("Y", "Y", "Y"), weight = 1:3) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill, gap = 0) expect_true(all(d$ymin == 0)) expect_true(all(d$ymax == 1)) }) it("zero-weight combination is excluded from output", { df <- data.frame( x = c("A", "A", "B"), fill = c("Y", "N", "Y"), weight = c(10, 0, 5) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill) expect_equal(nrow(d), 2L) expect_true(all(d$weight > 0)) }) it("without explicit weight uses row counts", { set.seed(42) df <- data.frame( x = sample(c("A", "B", "C"), 100, replace = TRUE), fill = sample(c("Y", "N"), 100, replace = TRUE) ) d <- build_marimekko(df, aes(fill = fill), formula = ~ x | fill) # 3 x-levels * 2 fill-levels = 6 expect_equal(nrow(d), 6L) }) it("handles formula with column transformations", { # Users should pre-compute in the data; formula uses column names mtcars2 <- mtcars mtcars2$cyl_f <- factor(mtcars2$cyl) mtcars2$gear_f <- factor(mtcars2$gear) d <- ggplot_build( ggplot(mtcars2) + geom_marimekko(aes(fill = gear_f), formula = ~ cyl_f | gear_f) )$data[[1]] expect_equal( nrow(d), nrow(unique(mtcars[, c("cyl", "gear")])) ) }) it("formula supports factor() calls", { d <- ggplot_build( ggplot(mtcars) + geom_marimekko(formula = ~ factor(cyl) | factor(gear)) )$data[[1]] expect_equal( nrow(d), nrow(unique(mtcars[, c("cyl", "gear")])) ) expect_true(all(d$xmin >= 0 & d$xmax <= 1)) }) it("formula supports cut() calls", { d <- ggplot_build( ggplot(mtcars) + geom_marimekko( aes(fill = factor(gear)), formula = ~ cut(mpg, breaks = 3) | factor(gear) ) )$data[[1]] expect_gt(nrow(d), 0L) expect_true(all(d$xmin >= 0 & d$xmax <= 1)) }) it("formula supports paste() calls", { d <- ggplot_build( ggplot(mtcars) + geom_marimekko(formula = ~ paste0("cyl", cyl) | factor(gear)) )$data[[1]] expect_gt(nrow(d), 0L) }) it("formula supports calls with + grouping", { d <- ggplot_build( ggplot(titanic_df) + geom_marimekko( aes(fill = Survived, weight = Freq), formula = ~ factor(Class) + Sex | Survived ) )$data[[1]] expect_gt(nrow(d), 8L) }) it("formula call auto-fills from last expression", { d <- ggplot_build( ggplot(mtcars) + geom_marimekko(formula = ~ factor(cyl) | factor(gear)) )$data[[1]] # fill should be set (auto-defaulted to factor(gear)) expect_true("fill" %in% names(d)) }) }) describe("faceting", { it("facet_wrap produces correct number of independent panels", { d <- ggplot_build( ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + facet_wrap(~Sex) )$data[[1]] expect_equal(length(unique(d$PANEL)), 2L) # Each panel should have 4 classes * 2 fills = 8 tiles expect_equal(nrow(d[d$PANEL == 1, ]), 8L) expect_equal(nrow(d[d$PANEL == 2, ]), 8L) }) it("faceted panels compute different column widths per panel", { d <- ggplot_build( ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + facet_wrap(~Sex) )$data[[1]] male_widths <- sort(unique(round( d$xmax[d$PANEL == 1] - d$xmin[d$PANEL == 1], 6 ))) female_widths <- sort(unique(round( d$xmax[d$PANEL == 2] - d$xmin[d$PANEL == 2], 6 ))) expect_false(isTRUE(all.equal(male_widths, female_widths))) }) it("facet_grid renders without error", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + facet_grid(~Sex) expect_no_error(print(p)) }) }) describe("ggplot2 layer composition", { it("composes with scale_fill_manual, coord_flip, theme_marimekko", { base <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) expect_no_error(print(base + theme_marimekko())) expect_no_error(print( base + scale_fill_manual(values = c("No" = "red", "Yes" = "green")) )) expect_no_error(print(base + coord_flip())) }) }) describe("namespace-qualified usage", { it("marimekko::geom_marimekko works with explicit namespacing", { p <- ggplot2::ggplot(titanic_df) + marimekko::geom_marimekko( ggplot2::aes(fill = Survived, weight = Freq), formula = ~ Class | Survived ) built <- ggplot2::ggplot_build(p) expect_equal(nrow(built$data[[1]]), 8L) }) }) }) describe("geom_marimekko_text", { it("renders text with after_stat(weight) at correct tile positions", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes(label = after_stat(weight))) built <- ggplot_build(p) # layer 1 = marimekko tiles, layer 2 = text tiles <- built$data[[1]] text <- built$data[[2]] # text x,y should match tile centers expect_equal(sort(text$x), sort(tiles$x)) expect_equal(sort(text$y), sort(tiles$y)) }) it("renders with after_stat(cond_prop) without error", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes( label = after_stat(paste0(round(cond_prop * 100), "%")) )) expect_no_error(print(p)) }) it("renders with after_stat(.tooltip) without error", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes(label = after_stat(.tooltip))) expect_no_error(print(p)) }) it("defaults to white text colour", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes(label = after_stat(weight))) built <- ggplot_build(p) text <- built$data[[2]] expect_true(all(text$colour == "white")) }) it("respects custom colour", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes(label = after_stat(weight)), colour = "red") built <- ggplot_build(p) text <- built$data[[2]] expect_true(all(text$colour == "red")) }) it("warns without a preceding geom_marimekko layer", { .marimekko_env$tiles <- NULL p <- ggplot(titanic_df) + geom_marimekko_text(aes(label = after_stat(weight))) expect_warning(ggplot_build(p), "geom_marimekko") }) }) describe("geom_marimekko_label", { it("renders label boxes at tile positions", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_label(aes(label = after_stat(weight))) built <- ggplot_build(p) tiles <- built$data[[1]] labels <- built$data[[2]] expect_equal(sort(labels$x), sort(tiles$x)) expect_equal(sort(labels$y), sort(tiles$y)) }) it("defaults to black text on semi-transparent white background", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_label(aes(label = after_stat(weight))) built <- ggplot_build(p) labels <- built$data[[2]] expect_true(all(labels$colour == "black")) expect_true(all(labels$fill == alpha("white", 0.7))) }) it("respects custom colour and fill", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_label(aes(label = after_stat(weight)), colour = "red", fill = "blue") built <- ggplot_build(p) labels <- built$data[[2]] expect_true(all(labels$colour == "red")) expect_true(all(labels$fill == "blue")) }) }) describe("auto x-axis labels", { it("places breaks at column midpoints", { df <- data.frame( x = c("A", "A", "B", "B"), fill = c("Y", "N", "Y", "N"), weight = c(75, 25, 50, 50) ) p <- ggplot(df) + geom_marimekko(aes(fill = fill, weight = weight), formula = ~ x | fill) d <- ggplot_build(p)$data[[1]] cols <- column_geom(d) expected_mids <- (cols$xmin + cols$xmax) / 2 scale_info <- layer_scales(p)$x breaks <- scale_info$break_info(c(0, 1)) expect_equal(breaks$major_source, expected_mids, tolerance = 0.001) }) it("labels include the original category names", { p <- ggplot(titanic_df) + geom_marimekko( aes(fill = Survived, weight = Freq), formula = ~ Class | Survived ) scale_info <- layer_scales(p)$x breaks_info <- scale_info$break_info(c(0, 1)) labels <- scale_info$get_labels(breaks_info$major_source) expect_true(all(c("1st", "2nd", "3rd", "Crew") %in% labels)) }) it("show_percentages=TRUE renders without error", { df <- data.frame(x = c("A", "B"), fill = c("Y", "Y"), weight = c(3, 1)) p <- ggplot(df) + geom_marimekko(aes(fill = fill, weight = weight), formula = ~ x | fill, show_percentages = TRUE ) expect_no_error(print(p)) }) }) describe("auto y-axis labels", { it("renders without error and provides fill category labels", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) expect_no_error(print(p)) scale_info <- layer_scales(p)$y # Breaks should exist at segment midpoints breaks_info <- scale_info$break_info(c(0, 1)) expect_true(length(breaks_info$major_source) > 0) }) it("labels include the original category names", { p <- ggplot(titanic_df) + geom_marimekko( aes(fill = Survived, weight = Freq), formula = ~ Class | Survived ) scale_info <- layer_scales(p)$y breaks_info <- scale_info$break_info(c(0, 1)) labels <- scale_info$get_labels(breaks_info$major_source) expect_true(all(c("No", "Yes") %in% labels)) }) }) describe("theme_marimekko", { it("returns a list containing a theme and a fill scale", { th <- theme_marimekko() expect_type(th, "list") expect_s3_class(th[[1]], "theme") expect_identical(th[[1]]$panel.grid, element_blank()) expect_identical(th[[1]]$axis.ticks, element_blank()) expect_s3_class(th[[2]], "Scale") }) it("sets marimekko_pal colours to each variable level", { p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + theme_marimekko() built <- ggplot_build(p) fill_values <- unique(built$data[[1]]$fill) expect_true(all(fill_values %in% marimekko_pal)) }) }) describe("fortify_marimekko", { it("returns all expected columns with formula", { result <- fortify_marimekko(titanic_df, formula = ~ Class | Survived, weight = Freq ) expected_cols <- c( "Class", "Survived", "fill", "xmin", "xmax", "ymin", "ymax", "x", "y", "weight", ".proportion", ".marginal", ".residuals" ) expect_true(all(expected_cols %in% names(result))) expect_equal(nrow(result), 8L) # 4 classes * 2 survival }) it("returns colour column with same values as fill", { result <- fortify_marimekko(titanic_df, formula = ~ Class | Survived, weight = Freq ) expect_true("colour" %in% names(result)) expect_equal(as.character(result$colour), as.character(result$fill)) }) it("tiles fill [0,1] x [0,1] with gap=0", { result <- fortify_marimekko(titanic_df, formula = ~ Class | Survived, weight = Freq, gap = 0 ) expect_equal(min(result$xmin), 0) expect_equal(max(result$xmax), 1) expect_equal(min(result$ymin), 0) expect_equal(max(result$ymax), 1) }) it("includes .residuals with non-zero values", { result <- fortify_marimekko(titanic_df, formula = ~ Class | Survived, weight = Freq ) expect_true(".residuals" %in% names(result)) expect_true(any(result$.residuals != 0)) }) it("3-variable formula produces more tiles", { result <- fortify_marimekko(titanic_df, formula = ~ Class | Survived | Sex, weight = Freq ) expect_gt(nrow(result), 8L) expect_true(all(c("Class", "Survived", "Sex") %in% names(result))) }) it("without weight argument uses row counts", { df <- data.frame(x = c("A", "A", "B"), fill = c("Y", "N", "Y")) result <- fortify_marimekko(df, formula = ~ x | fill) expect_equal(nrow(result), 3L) }) it("drops internal columns (group, PANEL)", { result <- fortify_marimekko(titanic_df, formula = ~ Class | Survived, weight = Freq ) expect_false("group" %in% names(result)) expect_false("PANEL" %in% names(result)) }) }) describe("plotly conversion", { it("converts geom_marimekko to plotly", { skip_if_not_installed("plotly") p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) expect_no_error(plotly::ggplotly(p)) }) it("converts geom_marimekko_text to plotly", { skip_if_not_installed("plotly") p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes(label = after_stat(weight))) expect_no_error(plotly::ggplotly(p)) }) it("converts geom_marimekko_label to plotly (warns for GeomLabel)", { skip_if_not_installed("plotly") p <- ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_label(aes(label = after_stat(weight))) suppressWarnings(expect_no_error(plotly::ggplotly(p))) }) }) describe("visual regression", { it("basic Titanic marimekko", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-basic", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + labs(y = "Proportion") }) }) it("no-gap marimekko", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-no-gap", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived, gap = 0) }) }) it("marimekko with text labels", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-text-labels", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + geom_marimekko_text(aes(label = after_stat(weight))) }) }) it("fully themed marimekko", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-themed", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + theme_marimekko() }) }) it("custom colour and alpha", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-red-borders", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived, colour = "red", alpha = 0.5 ) }) }) it("faceted marimekko", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-faceted", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived) + facet_wrap(~Sex) }) }) it("x-axis with percentages", { skip_if_not_installed("vdiffr") vdiffr::expect_doppelganger("titanic-x-percentages", { ggplot(titanic_df) + geom_marimekko(aes(fill = Survived, weight = Freq), formula = ~ Class | Survived, show_percentages = TRUE ) }) }) }) describe("zero-weight edge cases", { it("returns empty plot when all weights are zero", { df <- data.frame( x = factor(c("A", "B")), fill = factor(c("Y", "N")), weight = c(0, 0) ) d <- build_marimekko(df, aes(fill = fill, weight = weight), formula = ~ x | fill) expect_true(is.null(d) || nrow(d) == 0) }) }) describe("colour and fill aesthetics", { it("returns NA colour for all tiles when colour is not specified", { d <- build_marimekko( titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived ) expect_true(all(is.na(d$colour))) }) it("returns 'white' colour for all tiles when colour='white' is specified", { d <- build_marimekko( titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived, colour = "white" ) expect_true(all(d$colour == "white")) }) it("returns distinct colour per Class level when colour is mapped to variable in aes()", { d <- build_marimekko( titanic_df, aes(fill = Survived, colour = Class, weight = Freq), formula = ~ Class | Survived ) expect_identical( d$colour, c( "#F8766D", "#F8766D", "#7CAE00", "#7CAE00", "#00BFC4", "#00BFC4", "#C77CFF", "#C77CFF" ) ) }) it("returns distinct fill per Class level when fill is mapped to variable in aes()", { d <- build_marimekko( titanic_df, aes(fill = Survived, weight = Freq), formula = ~ Class | Survived ) expect_identical( d$fill, c("#F8766D", "#00BFC4", "#F8766D", "#00BFC4", "#F8766D", "#00BFC4", "#F8766D", "#00BFC4") ) }) }) describe("marimekko_pal", { it("contains 8 valid hex colour codes", { expect_length(marimekko_pal, 8) expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", marimekko_pal))) }) })