skip_on_cran() # This test suite is long-running (on cran) and is skipped test_that("plotting does not induce state changes in guides", { guides <- guides( x = guide_axis(title = "X-axis"), colour = guide_colourbar(title = "Colourbar"), shape = guide_legend(title = "Legend"), size = guide_bins(title = "Bins") ) p <- ggplot(mpg, aes(displ, hwy, colour = cty, shape = factor(cyl), size = cyl)) + geom_point() + guides snapshot <- serialize(as.list(p$guides), NULL) grob <- ggplotGrob(p) expect_identical(as.list(p$guides), unserialize(snapshot)) }) test_that("adding guides doesn't change plot state", { p1 <- ggplot(mtcars, aes(disp, mpg)) expect_length(p1$guides$guides, 0) p2 <- p1 + guides(y = guide_axis(angle = 45)) expect_length(p1$guides$guides, 0) expect_length(p2$guides$guides, 1) p3 <- p2 + guides(y = guide_axis(angle = 90)) expect_length(p3$guides$guides, 1) expect_equal(p3$guides$guides[[1]]$params$angle, 90) expect_equal(p2$guides$guides[[1]]$params$angle, 45) }) test_that("colourbar trains without labels", { g <- guide_colorbar() sc <- scale_colour_continuous(limits = c(0, 4), labels = NULL) out <- g$train(scale = sc) expect_equal(names(out$key), c("colour", ".value")) }) test_that("Colorbar respects show.legend in layer", { df <- data_frame(x = 1:3, y = 1) p <- ggplot(df, aes(x = x, y = y, color = x)) + geom_point(size = 20, shape = 21, show.legend = FALSE) expect_length(ggplot_build(p)$plot$guides$guides, 0L) p <- ggplot(df, aes(x = x, y = y, color = x)) + geom_point(size = 20, shape = 21, show.legend = TRUE) expect_length(ggplot_build(p)$plot$guides$guides, 1L) }) test_that("show.legend handles named vectors", { n_legends <- function(p) { g <- ggplotGrob(p) gb <- grep("guide-box", g$layout$name) n <- vapply(g$grobs[gb], function(x) { if (is.zero(x)) return(0) length(x$grobs) - 1 }, numeric(1)) sum(n) } df <- data_frame(x = 1:3, y = 20:22) p <- ggplot(df, aes(x = x, y = y, color = x, shape = factor(y))) + geom_point(size = 20) expect_equal(n_legends(p), 2) p <- ggplot(df, aes(x = x, y = y, color = x, shape = factor(y))) + geom_point(size = 20, show.legend = c(color = FALSE)) expect_equal(n_legends(p), 1) p <- ggplot(df, aes(x = x, y = y, color = x, shape = factor(y))) + geom_point(size = 20, show.legend = c(color = FALSE, shape = FALSE)) expect_equal(n_legends(p), 0) # c.f.https://github.com/tidyverse/ggplot2/issues/3461 p <- ggplot(df, aes(x = x, y = y, color = x, shape = factor(y))) + geom_point(size = 20, show.legend = c(shape = FALSE, color = TRUE)) expect_equal(n_legends(p), 1) }) test_that("axis_label_overlap_priority always returns the correct number of elements", { expect_identical(axis_label_priority(0), numeric(0)) expect_setequal(axis_label_priority(1), seq_len(1)) expect_setequal(axis_label_priority(5), seq_len(5)) expect_setequal(axis_label_priority(10), seq_len(10)) expect_setequal(axis_label_priority(100), seq_len(100)) }) test_that("a warning is generated when guides are drawn at a location that doesn't make sense", { plot <- ggplot(mpg, aes(class, hwy)) + geom_point() + scale_y_continuous(guide = guide_axis(position = "top")) expect_warning(ggplot_build(plot), "Position guide is perpendicular") }) test_that("a warning is not generated when a guide is specified with duplicate breaks", { plot <- ggplot(mpg, aes(class, hwy)) + geom_point() + scale_y_continuous(breaks = c(20, 20)) built <- expect_silent(ggplot_build(plot)) expect_silent(ggplot_gtable(built)) }) test_that("a warning is generated when more than one position guide is drawn at a location", { plot <- ggplot(mpg, aes(class, hwy)) + geom_point() + guides( y = guide_axis(position = "left"), y.sec = guide_axis(position = "left") ) built <- expect_silent(ggplot_build(plot)) expect_warning(ggplot_gtable(built), "Discarding guide") }) test_that("a warning is not generated when properly changing the position of a guide_axis()", { plot <- ggplot(mpg, aes(class, hwy)) + geom_point() + guides( y = guide_axis(position = "right") ) built <- expect_silent(ggplot_build(plot)) expect_silent(ggplot_gtable(built)) }) test_that("guide_none() can be used in non-position scales", { p <- ggplot(mpg, aes(cty, hwy, colour = class)) + geom_point() + scale_color_discrete(guide = guide_none()) built <- ggplot_build(p) plot <- built$plot guides <- guides_list(plot$guides) guides <- guides$build( plot$scales, plot$layers, plot$labels ) expect_length(guides$guides, 0) }) test_that("Using non-position guides for position scales results in an informative error", { p <- ggplot(mpg, aes(cty, hwy)) + geom_point() + scale_x_continuous(guide = guide_legend()) expect_snapshot_warning(ggplot_build(p)) }) test_that("guide merging for guide_legend() works as expected", { merge_test_guides <- function(scale1, scale2) { scale1$guide <- guide_legend(direction = "vertical") scale2$guide <- guide_legend(direction = "vertical") scales <- scales_list() scales$add(scale1) scales$add(scale2) scales <- scales$scales aesthetics <- lapply(scales, `[[`, "aesthetics") scales <- rep.int(scales, lengths(aesthetics)) aesthetics <- unlist(aesthetics, FALSE, FALSE) guides <- guides_list(NULL) guides <- guides$setup(scales, aesthetics) guides$train(scales, labs()) guides$merge() guides$params } different_limits <- merge_test_guides( scale_colour_discrete(limits = c("a", "b", "c", "d")), scale_linetype_discrete(limits = c("a", "b", "c")) ) expect_length(different_limits, 2) same_limits <- merge_test_guides( scale_colour_discrete(limits = c("a", "b", "c")), scale_linetype_discrete(limits = c("a", "b", "c")) ) expect_length(same_limits, 1) expect_equal(same_limits[[1]]$key$.label, c("a", "b", "c")) same_labels_different_limits <- merge_test_guides( scale_colour_discrete(limits = c("a", "b", "c")), scale_linetype_discrete(limits = c("one", "two", "three"), labels = c("a", "b", "c")) ) expect_length(same_labels_different_limits, 1) expect_equal(same_labels_different_limits[[1]]$key$.label, c("a", "b", "c")) same_labels_different_scale <- merge_test_guides( scale_colour_continuous(limits = c(0, 4), breaks = 1:3, labels = c("a", "b", "c")), scale_linetype_discrete(limits = c("a", "b", "c")) ) expect_length(same_labels_different_scale, 1) expect_equal(same_labels_different_scale[[1]]$key$.label, c("a", "b", "c")) repeated_identical_labels <- merge_test_guides( scale_colour_discrete(limits = c("one", "two", "three"), labels = c("label1", "label1", "label2")), scale_linetype_discrete(limits = c("1", "2", "3"), labels = c("label1", "label1", "label2")) ) expect_length(repeated_identical_labels, 1) expect_equal(repeated_identical_labels[[1]]$key$.label, c("label1", "label1", "label2")) }) test_that("size = NA doesn't throw rendering errors", { df = data.frame( x = c(1, 2), group = c("a","b") ) p <- ggplot(df, aes(x = x, y = 0, colour = group)) + geom_point(size = NA, na.rm = TRUE) expect_silent(plot(p)) }) test_that("guide specifications are properly checked", { expect_snapshot_error(validate_guide("test")) expect_snapshot_error(validate_guide(1)) p <- ggplot(mtcars) + geom_point(aes(mpg, disp, shape = factor(gear))) + guides(shape = "colourbar") expect_snapshot_warning(ggplotGrob(p)) p <- p + guides(shape = guide_legend(theme = theme(legend.title.position = "leftish"))) expect_snapshot_error(ggplotGrob(p)) expect_snapshot_error(guide_colourbar()$transform()) p <- ggplot(mtcars) + geom_point(aes(mpg, disp, colour = gear)) + guides(colour = guide_colourbar(theme = theme(legend.text.position = "top"))) expect_snapshot_error(ggplotGrob(p)) p <- ggplot(mtcars) + geom_point(aes(mpg, disp, colour = gear)) + guides(colour = guide_colourbar(direction = "horizontal", theme = theme(legend.text.position = "left"))) expect_snapshot_error(ggplotGrob(p)) p <- ggplot(mtcars) + geom_point(aes(mpg, disp, colour = gear)) + guides(colour = guide_legend(theme = theme(legend.text.position = "test"))) expect_snapshot_error(ggplotGrob(p)) p <- ggplot(mtcars) + geom_point(aes(mpg, disp, colour = gear)) + guides(colour = guide_legend(nrow = 2, ncol = 2)) expect_snapshot_error(ggplotGrob(p)) }) test_that("colorsteps and bins checks the breaks format", { p <- ggplot(mtcars) + geom_point(aes(mpg, disp, colour = paste("A", gear))) + guides(colour = "colorsteps") expect_snapshot_error(suppressWarnings(ggplotGrob(p))) p <- ggplot(mtcars) + geom_point(aes(mpg, disp, colour = paste("A", gear))) + guides(colour = "bins") expect_snapshot_error(suppressWarnings(ggplotGrob(p))) }) test_that("legend reverse argument reverses the key", { scale <- scale_colour_discrete() scale$train(LETTERS[1:4]) guides <- guides_list(NULL) guides <- guides$setup(list(scale), "colour") guides$params[[1]]$reverse <- FALSE guides$train(list(scale), labels = labs()) fwd <- guides$get_params(1)$key guides$params[[1]]$reverse <- TRUE guides$train(list(scale), labels = labs()) rev <- guides$get_params(1)$key expect_equal(fwd$colour, rev(rev$colour)) }) test_that("guide_coloursteps and guide_bins return ordered breaks", { scale <- scale_colour_viridis_c(breaks = c(2, 3, 1)) scale$train(c(0, 4)) # Coloursteps guide is increasing order g <- guide_colorsteps() key <- g$train(scale = scale, aesthetic = "colour")$key expect_true(all(diff(key$.value) > 0)) # Bins guide is increasing order g <- guide_bins() key <- g$train(scale = scale, aesthetics = "colour")$key expect_true(all(diff(key$.value) > 0)) }) test_that("guide_coloursteps can parse (un)even steps from discrete scales", { val <- cut(1:10, breaks = c(0, 3, 5, 10), include.lowest = TRUE) scale <- scale_colour_viridis_d() scale$train(val) g <- guide_coloursteps(even.steps = TRUE) decor <- g$train(scale = scale, aesthetics = "colour")$decor expect_equal(decor$max - decor$min, rep(1/3, 3)) g <- guide_coloursteps(even.steps = FALSE) decor <- g$train(scale = scale, aesthetics = "colour")$decor expect_equal(decor$max - decor$min, c(0.3, 0.2, 0.5)) }) test_that("guide_colourbar merging preserves both aesthetics", { # See issue 5324 scale1 <- scale_colour_viridis_c() scale1$train(c(0, 2)) scale2 <- scale_fill_viridis_c() scale2$train(c(0, 2)) g <- guide_colourbar() p <- g$params p1 <- g$train(p, scale1, "colour") p2 <- g$train(p, scale2, "fill") merged <- g$merge(p1, g, p2) expect_true(all(c("colour", "fill") %in% names(merged$params$key))) }) test_that("get_guide_data retrieves keys appropriately", { p <- ggplot(mtcars, aes(mpg, disp, colour = drat, size = drat, fill = wt)) + geom_point(shape = 21) + facet_wrap(vars(cyl), scales = "free_x") + guides(colour = "legend") b <- ggplot_build(p) # Test facetted panel test <- get_guide_data(b, "x", panel = 2) expect_equal(test$.label, c("18", "19", "20", "21")) # Test plain legend test <- get_guide_data(b, "fill") expect_equal(test$.label, c("2", "3", "4", "5")) # Test merged legend test <- get_guide_data(b, "colour") expect_true(all(c("colour", "size") %in% colnames(test))) # Unmapped data expect_null(get_guide_data(b, "shape")) # Non-existent panels expect_null(get_guide_data(b, "x", panel = 4)) expect_error(get_guide_data(b, 1), "must be a single string") expect_error(get_guide_data(b, "x", panel = "a"), "must be a whole number") }) test_that("get_guide_data retrieves keys from exotic coords", { p <- ggplot(mtcars, aes(mpg, disp)) + geom_point() # Sanity check test <- get_guide_data(p + coord_cartesian(), "x") expect_equal(test$.label, c("10", "15", "20", "25", "30", "35")) # We're not testing the formatting, so just testing output shape test <- get_guide_data(p + coord_sf(crs = 3347), "y") expect_equal(nrow(test), 5) expect_true(all(c("x", ".value", ".label", "x") %in% colnames(test))) # For coords that don't use guide system, we expect a list test <- get_guide_data(p + coord_polar(), "theta") expect_true(is.list(test) && !is.data.frame(test)) expect_equal(test$theta.labels, c("15", "20", "25", "30")) }) test_that("guide_colourbar warns about discrete scales", { g <- guide_colourbar() s <- scale_colour_discrete() s$train(LETTERS[1:3]) expect_warning(g <- g$train(g$params, s, "colour"), "needs continuous scales") expect_null(g) }) test_that("legend directions are set correctly", { p <- ggplot(mtcars, aes(disp, mpg, shape = factor(cyl), colour = drat)) + geom_point() + theme_test() expect_doppelganger( "vertical legend direction", p + theme(legend.direction = "vertical") ) expect_doppelganger( "horizontal legend direction", p + theme(legend.direction = "horizontal") ) }) test_that("guide_axis_logticks calculates appropriate ticks", { test_scale <- function(transform = transform_identity(), limits = c(NA, NA)) { scale <- scale_x_continuous(transform = transform) scale$train(scale$transform(limits)) view_scale_primary(scale) } train_guide <- function(guide, scale) { params <- guide$params params$position <- "bottom" guide$train(params, scale, "x") } guide <- guide_axis_logticks(negative.small = 10) outcome <- c((1:10)*10, (2:10)*100) # Test the classic log10 transformation scale <- test_scale(transform_log10(), c(10, 1000)) key <- train_guide(guide, scale)$logkey expect_equal(sort(key$x), log10(outcome)) expect_equal(key$.type, rep(c(1,2,3), c(3, 2, 14))) # Test compound transformation scale <- test_scale(transform_compose(transform_log10(), transform_reverse()), c(10, 1000)) key <- train_guide(guide, scale)$logkey expect_equal(sort(key$x), -log10(rev(outcome))) # Test transformation with negatives scale <- test_scale(transform_pseudo_log(), c(-1000, 1000)) key <- train_guide(guide, scale)$logkey unlog <- sort(transform_pseudo_log()$inverse(key$x)) expect_equal(unlog, c(-rev(outcome), 0, outcome)) expect_equal(key$.type, rep(c(1,2,3), c(7, 4, 28))) # Test expanded argument scale <- test_scale(transform_log10(), c(20, 900)) scale$continuous_range <- c(1, 3) guide <- guide_axis_logticks(expanded = TRUE) key <- train_guide(guide, scale)$logkey expect_equal(sort(key$x), log10(outcome)) guide <- guide_axis_logticks(expanded = FALSE) key <- train_guide(guide, scale)$logkey expect_equal(sort(key$x), log10(outcome[-c(1, length(outcome))])) # Test with prescaled input guide <- guide_axis_logticks(prescale.base = 2) scale <- test_scale(limits = log2(c(10, 1000))) key <- train_guide(guide, scale)$logkey expect_equal(sort(key$x), log2(outcome)) # Should warn when scale also has transformation scale <- test_scale(transform_log10(), limits = c(10, 1000)) expect_snapshot_warning(train_guide(guide, scale)$logkey) }) test_that("guide_legend uses key.spacing correctly", { p <- ggplot(mtcars, aes(disp, mpg, colour = factor(carb))) + geom_point() + guides(colour = guide_legend(ncol = 2)) + theme_test() + theme( legend.key.spacing.x = unit(2, "lines"), legend.key.spacing.y = unit(1, "lines") ) expect_doppelganger("legend with widely spaced keys", p) }) test_that("empty guides are dropped", { df <- data.frame(x = 1:2) # Making a guide where all breaks are out-of-bounds p <- ggplot(df, aes(x, x, colour = x)) + geom_point() + scale_colour_continuous( limits = c(0.25, 0.75), breaks = c(1, 2), guide = "legend" ) p <- ggplot_build(p) # Empty guide that survives most steps gd <- get_guide_data(p, "colour") expect_equal(nrow(gd), 0) # Draw guides guides <- p$plot$guides$draw(theme_gray(), direction = "vertical") # All guide-boxes should be empty expect_equal(lengths(guides, use.names = FALSE), rep(0, 5)) }) test_that("bins can be parsed by guides for all scale types", { breaks <- c(90, 100, 200, 300) limits <- c(0, 1000) sc <- scale_colour_continuous(breaks = breaks) sc$train(limits) expect_equal(parse_binned_breaks(sc)$breaks, breaks) sc <- scale_colour_binned(breaks = breaks) sc$train(limits) expect_equal(parse_binned_breaks(sc)$breaks, breaks) # Note: discrete binned breaks treats outer breaks as limits cut <- cut(c(0, 95, 150, 250, 1000), breaks = breaks) sc <- scale_colour_discrete() sc$train(cut) parsed <- parse_binned_breaks(sc) expect_equal( sort(c(parsed$limits, parsed$breaks)), breaks ) }) # Visual tests ------------------------------------------------------------ test_that("axis guides are drawn correctly", { theme_test_axis <- theme_test() + theme(axis.line = element_line(linewidth = 0.5)) test_draw_axis <- function(n_breaks = 3, break_positions = seq_len(n_breaks) / (n_breaks + 1), labels = as.character, positions = c("top", "right", "bottom", "left"), theme = theme_test_axis, ...) { break_labels <- labels(seq_along(break_positions)) # create the axes axes <- lapply(positions, function(position) { draw_axis(break_positions, break_labels, axis_position = position, theme = theme, ...) }) axes_grob <- gTree(children = do.call(gList, axes)) # arrange them so there's some padding on each side gt <- gtable( widths = unit(c(0.05, 0.9, 0.05), "npc"), heights = unit(c(0.05, 0.9, 0.05), "npc") ) gt <- gtable_add_grob(gt, list(axes_grob), 2, 2, clip = "off") plot(gt) } # basic expect_doppelganger("axis guides basic", function() test_draw_axis()) expect_doppelganger("axis guides, zero breaks", function() test_draw_axis(n_breaks = 0)) # overlapping text expect_doppelganger( "axis guides, check overlap", function() test_draw_axis(20, labels = function(b) comma(b * 1e9), check.overlap = TRUE) ) # rotated text expect_doppelganger( "axis guides, zero rotation", function() test_draw_axis(10, labels = function(b) comma(b * 1e3), angle = 0) ) expect_doppelganger( "axis guides, positive rotation", function() test_draw_axis(10, labels = function(b) comma(b * 1e3), angle = 45) ) expect_doppelganger( "axis guides, negative rotation", function() test_draw_axis(10, labels = function(b) comma(b * 1e3), angle = -45) ) expect_doppelganger( "axis guides, vertical rotation", function() test_draw_axis(10, labels = function(b) comma(b * 1e3), angle = 90) ) expect_doppelganger( "axis guides, vertical negative rotation", function() test_draw_axis(10, labels = function(b) comma(b * 1e3), angle = -90) ) # dodged text expect_doppelganger( "axis guides, text dodged into rows/cols", function() test_draw_axis(10, labels = function(b) comma(b * 1e9), n.dodge = 2) ) }) test_that("axis guides are drawn correctly in plots", { expect_doppelganger("align facet labels, facets horizontal", ggplot(mpg, aes(hwy, reorder(model, hwy))) + geom_point() + facet_grid(manufacturer ~ ., scales = "free", space = "free") + theme_test() + theme(strip.text.y = element_text(angle = 0)) ) expect_doppelganger("align facet labels, facets vertical", ggplot(mpg, aes(reorder(model, hwy), hwy)) + geom_point() + facet_grid(. ~ manufacturer, scales = "free", space = "free") + theme_test() + theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) ) expect_doppelganger("thick axis lines", ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_test() + theme(axis.line = element_line(linewidth = 5, lineend = "square")) ) }) test_that("axis guides can be customized", { plot <- ggplot(mpg, aes(class, hwy)) + geom_point() + scale_y_continuous( sec.axis = dup_axis(guide = guide_axis(n.dodge = 2)), guide = guide_axis(n.dodge = 2) ) + scale_x_discrete(guide = guide_axis(n.dodge = 2)) expect_doppelganger("guide_axis() customization", plot) }) test_that("guides can be specified in guides()", { plot <- ggplot(mpg, aes(class, hwy)) + geom_point() + guides( x = guide_axis(n.dodge = 2), y = guide_axis(n.dodge = 2), x.sec = guide_axis(n.dodge = 2), y.sec = guide_axis(n.dodge = 2) ) expect_doppelganger("guides specified in guides()", plot) }) test_that("guides have the final say in x and y", { df <- data_frame(x = 1, y = 1) plot <- ggplot(df, aes(x, y)) + geom_point() + guides( x = guide_none(title = "x (primary)"), y = guide_none(title = "y (primary)"), x.sec = guide_none(title = "x (secondary)"), y.sec = guide_none(title = "y (secondary)") ) expect_doppelganger("position guide titles", plot) }) test_that("Axis titles won't be blown away by coord_*()", { df <- data_frame(x = 1, y = 1) plot <- ggplot(df, aes(x, y)) + geom_point() + guides( x = guide_axis(title = "x (primary)"), y = guide_axis(title = "y (primary)"), x.sec = guide_axis(title = "x (secondary)"), y.sec = guide_axis(title = "y (secondary)") ) expect_doppelganger("guide titles with coord_trans()", plot + coord_trans()) # TODO # expect_doppelganger("guide titles with coord_polar()", plot + coord_polar()) # TODO # expect_doppelganger("guide titles with coord_sf()", plot + coord_sf()) }) test_that("guide_axis() draws minor ticks correctly", { p <- ggplot(mtcars, aes(wt, disp)) + geom_point() + theme(axis.ticks.length = unit(1, "cm"), axis.ticks.x.bottom = element_line(linetype = 2), axis.ticks.length.x.top = unit(-0.5, "cm"), axis.minor.ticks.x.bottom = element_line(colour = "red"), axis.minor.ticks.length.y.left = unit(-0.5, "cm"), axis.minor.ticks.length.x.top = unit(-0.5, "cm"), axis.minor.ticks.length.x.bottom = unit(0.75, "cm"), axis.minor.ticks.length.y.right = unit(5, "cm")) + scale_x_continuous(labels = label_math()) + guides( # Test for styling and style inheritance x = guide_axis(minor.ticks = TRUE), # # Test for opposed lengths y = guide_axis(minor.ticks = TRUE), # # Test for flipped lenghts x.sec = guide_axis(minor.ticks = TRUE), # # Test that minor.length doesn't influence spacing when no minor ticks are drawn y.sec = guide_axis(minor.ticks = FALSE) ) expect_doppelganger("guides with minor ticks", p) }) test_that("absent titles don't take up space", { p <- ggplot(mtcars, aes(disp, mpg, colour = factor(cyl))) + geom_point() + theme( legend.title = element_blank(), legend.margin = margin(), legend.position = "top", legend.justification = "left", legend.key = element_rect(colour = "black"), axis.line = element_line(colour = "black") ) expect_doppelganger("left aligned legend key", p) }) test_that("axis guides can be capped", { p <- ggplot(mtcars, aes(hp, disp)) + geom_point() + theme(axis.line = element_line()) + guides( x = guide_axis(cap = "both"), y = guide_axis(cap = "upper"), y.sec = guide_axis(cap = "lower"), x.sec = guide_axis(cap = "none") ) expect_doppelganger("axis guides with capped ends", p) }) test_that("guide_axis_stack stacks axes", { left <- guide_axis_stack("axis", guide_axis(cap = "both"), title = "left") right <- guide_axis_stack("axis", guide_axis(cap = "both"), title = "right") bottom <- guide_axis_stack("axis", guide_axis(cap = "both"), title = "bottom") top <- guide_axis_stack("axis", guide_axis(cap = "both"), title = "top") p <- ggplot(mtcars, aes(hp, disp)) + geom_point() + theme(axis.line = element_line()) + guides(x = bottom, x.sec = top, y = left, y.sec = right) expect_doppelganger("stacked axes", p) bottom <- guide_axis_stack("axis_theta", guide_axis_theta(cap = "both")) top <- guide_axis_stack("axis_theta", guide_axis_theta(cap = "both")) p <- ggplot(mtcars, aes(hp, disp)) + geom_point() + theme(axis.line = element_line()) + coord_radial(start = 0.25 * pi, end = 1.75 * pi, inner.radius = 0.5) + guides(theta = top, theta.sec = bottom, r = left, r.sec = right) expect_doppelganger("stacked radial axes", p) }) test_that("logticks look as they should", { p <- ggplot(data.frame(x = c(-100, 100), y = c(10, 1000)), aes(x, y)) + geom_point() + scale_y_continuous( transform = transform_compose(transform_log10(), transform_reverse()), expand = expansion(add = 0.5) ) + scale_x_continuous( breaks = c(-100, -10, -1, 0, 1, 10, 100) ) + coord_trans(x = transform_pseudo_log()) + theme_test() + theme(axis.line = element_line(colour = "black"), panel.border = element_blank(), axis.ticks.length.x.top = unit(-2.75, "pt")) + guides( x = guide_axis_logticks( title = "Pseudo-logticks with 1 as smallest tick", negative.small = 1 ), y = guide_axis_logticks( title = "Inverted logticks with swapped tick lengths", long = 0.75, short = 2.25 ), x.sec = guide_axis_logticks( negative.small = 0.1, title = "Negative length pseudo-logticks with 0.1 as smallest tick" ), y.sec = guide_axis_logticks( expanded = FALSE, cap = "both", title = "Capped and not-expanded inverted logticks" ) ) expect_doppelganger("logtick axes with customisation", p) }) test_that("guides are positioned correctly", { df <- data_frame(x = 1, y = 1, z = factor("a")) p1 <- ggplot(df, aes(x, y, colour = z)) + geom_point() + labs(title = "title of plot") + theme_test() + theme( axis.text.x = element_text(angle = 90, vjust = 0.5), legend.background = element_rect(fill = "grey90"), legend.key = element_rect(fill = "grey90") ) + scale_x_continuous(breaks = 1, labels = "very long axis label") + scale_y_continuous(breaks = 1, labels = "very long axis label") expect_doppelganger("legend on left", p1 + theme(legend.position = "left") ) expect_doppelganger("legend on bottom", p1 + theme(legend.position = "bottom") ) expect_doppelganger("legend on right", p1 + theme(legend.position = "right") ) expect_doppelganger("legend on top", p1 + theme(legend.position = "top") ) expect_doppelganger("facet_grid, legend on left", p1 + facet_grid(x~y) + theme(legend.position = "left") ) expect_doppelganger("facet_grid, legend on bottom", p1 + facet_grid(x~y) + theme(legend.position = "bottom") ) expect_doppelganger("facet_grid, legend on right", p1 + facet_grid(x~y) + theme(legend.position = "right") ) expect_doppelganger("facet_grid, legend on top", p1 + facet_grid(x~y) + theme(legend.position = "top") ) expect_doppelganger("facet_wrap, legend on left", p1 + facet_wrap(~ x) + theme(legend.position = "left") ) expect_doppelganger("facet_wrap, legend on bottom", p1 + facet_wrap(~ x) + theme(legend.position = "bottom") ) expect_doppelganger("facet_wrap, legend on right", p1 + facet_wrap(~ x) + theme(legend.position = "right") ) expect_doppelganger("facet_wrap, legend on top", p1 + facet_wrap(~ x) + theme(legend.position = "top") ) # padding dat <- data_frame(x = LETTERS[1:3], y = 1) p2 <- ggplot(dat, aes(x, y, fill = x, colour = 1:3)) + geom_bar(stat = "identity") + guides(color = guide_colourbar(order = 1)) + theme_test() + theme(legend.background = element_rect(colour = "black")) expect_doppelganger("padding in legend box", p2) p2 <- p2 + theme(legend.position = "inside") # Placement of legend inside expect_doppelganger("legend inside plot, centered", p2 + theme(legend.position.inside = c(.5, .5)) ) expect_doppelganger("legend inside plot, bottom left", p2 + theme(legend.justification = c(0,0), legend.position.inside = c(0,0)) ) expect_doppelganger("legend inside plot, top right", p2 + theme(legend.justification = c(1,1), legend.position.inside = c(1,1)) ) expect_doppelganger("legend inside plot, bottom left of legend at center", p2 + theme(legend.justification = c(0,0), legend.position.inside = c(.5,.5)) ) }) test_that("guides title and text are positioned correctly", { df <- data_frame(x = 1:3, y = 1:3) p <- ggplot(df, aes(x, y, color = factor(x), fill = y)) + geom_point(shape = 21) + # setting the order explicitly removes the risk for failed doppelgangers # due to legends switching order guides(color = guide_legend(order = 2), fill = guide_colorbar(order = 1)) + theme_test() expect_doppelganger("multi-line guide title works", p + scale_color_discrete(name = "the\ndiscrete\ncolorscale") + scale_fill_continuous(name = "the\ncontinuous\ncolorscale") ) expect_doppelganger("vertical gap of 1cm between guide title and guide", p + theme(legend.title = element_text(margin = margin(b = 1, unit = "cm"))) ) expect_doppelganger("horizontal gap of 1cm between guide and guide text", p + theme(legend.text = element_text(margin = margin(l = 1, unit = "cm"))) ) # now test label positioning, alignment, etc df <- data_frame(x = c(1, 10, 100)) p <- ggplot(df, aes(x, x, color = x, size = x)) + geom_point() + # setting the order explicitly removes the risk for failed doppelgangers # due to legends switching order guides(shape = guide_legend(order = 1), color = guide_colorbar(order = 2)) + theme_test() expect_doppelganger("guide title and text positioning and alignment via themes", p + theme( legend.title = element_text(hjust = 0.5, margin = margin(t = 30, b = 5.5)), legend.text = element_text(hjust = 1, margin = margin(l = 10.5, t = 10, b = 10)) ) ) # title and label rotation df <- data_frame(x = c(5, 10, 15)) p <- ggplot(df, aes(x, x, color = x, fill = 15 - x)) + geom_point(shape = 21, size = 5, stroke = 3) + scale_colour_continuous( name = "value", guide = guide_colorbar( theme = theme( legend.title = element_text(size = 11, angle = 0, hjust = 0.5, vjust = 1), legend.text = element_text(size = 0.8 * 11, angle = 270, hjust = 0.5, vjust = 1) ), order = 2 # set guide order to keep visual test stable ) ) + scale_fill_continuous( breaks = c(5, 10, 15), limits = c(5, 15), labels = paste("long", c(5, 10, 15)), name = "fill value", guide = guide_legend( direction = "horizontal", theme = theme( legend.title.position = "top", legend.text.position = "bottom", legend.title = element_text(size = 11, angle = 180, hjust = 0, vjust = 1), legend.text = element_text(size = 0.8 * 11, angle = 90, hjust = 1, vjust = 0.5) ), order = 1 ) ) expect_doppelganger("rotated guide titles and labels", p ) # title justification p <- ggplot(data.frame(x = 1:2)) + aes(x, x, colour = factor(x), fill = factor(x), shape = factor(x), alpha = x) + geom_point() + scale_alpha(breaks = 1:2) + guides( colour = guide_legend( "colour title with hjust = 0", order = 1, theme = theme(legend.title = element_text(hjust = 0)) ), fill = guide_legend( "fill title with hjust = 1", order = 2, theme = theme( legend.title = element_text(hjust = 1), legend.title.position = "bottom" ), override.aes = list(shape = 21) ), alpha = guide_legend( "Title\nfor\nalpha\nwith\nvjust=0", order = 3, theme = theme( legend.title = element_text(vjust = 0), legend.title.position = "left" ) ), shape = guide_legend( "Title\nfor\nshape\nwith\nvjust=1", order = 4, theme = theme( legend.title = element_text(vjust = 1), legend.title.position = "right" ) ) ) expect_doppelganger("legends with all title justifications", p) }) test_that("size and linewidth affect key size", { df <- data_frame(x = c(0, 1, 2)) p <- ggplot(df, aes(x, x)) + geom_point(aes(size = x)) + geom_line(aes(linewidth = 2 - x)) + scale_size_continuous(range = c(1, 12)) + scale_linewidth_continuous(range = c(1, 20)) expect_doppelganger("enlarged guides", p) }) test_that("colorbar can be styled", { df <- data_frame(x = c(0, 1, 2)) p <- ggplot(df, aes(x, x, color = x)) + geom_point() expect_doppelganger("white-to-red colorbar, white ticks, no frame", p + scale_color_gradient(low = 'white', high = 'red') ) expect_doppelganger("customized colorbar", p + scale_color_gradient( low = 'white', high = 'red', guide = guide_colorbar( theme = theme( legend.frame = element_rect(colour = "green", linewidth = 1.5 / .pt), legend.ticks = element_line("black", linewidth = 2.5 / .pt), legend.ticks.length = unit(0.4, "npc") ), alpha = 0.75 ) ) + labs(subtitle = "white-to-red semitransparent colorbar, long thick black ticks, green frame") ) }) test_that("guides can handle multiple aesthetics for one scale", { df <- data_frame(x = c(1, 2, 3), y = c(6, 5, 7)) p <- ggplot(df, aes(x, y, color = x, fill = y)) + geom_point(shape = 21, size = 3, stroke = 2) + scale_colour_viridis_c( name = "value", option = "B", aesthetics = c("colour", "fill") ) expect_doppelganger("one combined colorbar for colour and fill aesthetics", p) }) test_that("bin guide can be styled correctly", { df <- data_frame(x = c(1, 2, 3), y = c(6, 5, 7)) p <- ggplot(df, aes(x, y, size = x)) + geom_point() + scale_size_binned() expect_doppelganger("guide_bins looks as it should", p) expect_doppelganger("guide_bins can show limits", p + guides(size = guide_bins(show.limits = TRUE)) ) expect_doppelganger("guide_bins can show arrows", p + guides(size = guide_bins()) + theme_test() + theme( legend.axis.line = element_line( linewidth = 0.5 / .pt, arrow = arrow(length = unit(1.5, "mm"), ends = "both") ) ) ) expect_doppelganger("guide_bins can remove axis", p + guides(size = guide_bins()) + theme_test() + theme( legend.axis.line = element_blank() ) ) expect_doppelganger("guide_bins work horizontally", p + guides(size = guide_bins(direction = "horizontal")) ) }) test_that("coloursteps guide can be styled correctly", { df <- data_frame(x = c(1, 2, 4), y = c(6, 5, 7)) p <- ggplot(df, aes(x, y, colour = x)) + geom_point() + scale_colour_binned(breaks = c(1.5, 2, 3)) expect_doppelganger("guide_coloursteps looks as it should", p) expect_doppelganger("guide_coloursteps can show limits", p + guides(colour = guide_coloursteps(show.limits = TRUE)) ) expect_doppelganger("guide_coloursteps can have bins relative to binsize", p + guides(colour = guide_coloursteps(even.steps = FALSE)) ) expect_doppelganger("guide_bins can show ticks and transparancy", p + guides(colour = guide_coloursteps( alpha = 0.75, theme = theme(legend.ticks = element_line(linewidth = 0.5 / .pt, colour = "white")) )) ) }) test_that("binning scales understand the different combinations of limits, breaks, labels, and show.limits", { p <- ggplot(mpg, aes(cty, hwy, color = year)) + geom_point() expect_doppelganger("guide_bins understands coinciding limits and bins", p + scale_color_binned(limits = c(1999, 2008), breaks = c(1999, 2000, 2002, 2004, 2006), guide = 'bins') ) expect_doppelganger("guide_bins understands coinciding limits and bins 2", p + scale_color_binned(limits = c(1999, 2008), breaks = c(2000, 2002, 2004, 2006, 2008), guide = 'bins') ) expect_doppelganger("guide_bins understands coinciding limits and bins 3", p + scale_color_binned(limits = c(1999, 2008), breaks = c(1999, 2000, 2002, 2004, 2006), guide = 'bins', show.limits = TRUE) ) expect_doppelganger("guide_bins sets labels when limits is in breaks", p + scale_color_binned(limits = c(1999, 2008), breaks = c(1999, 2000, 2002, 2004, 2006), labels = 1:5, guide = 'bins') ) expect_snapshot_warning(ggplotGrob(p + scale_color_binned(labels = 1:4, show.limits = TRUE, guide = "bins"))) expect_doppelganger("guide_colorsteps understands coinciding limits and bins", p + scale_color_binned(limits = c(1999, 2008), breaks = c(1999, 2000, 2002, 2004, 2006)) ) expect_doppelganger("guide_colorsteps understands coinciding limits and bins 2", p + scale_color_binned(limits = c(1999, 2008), breaks = c(2000, 2002, 2004, 2006, 2008)) ) expect_doppelganger("guide_colorsteps understands coinciding limits and bins 3", p + scale_color_binned(limits = c(1999, 2008), breaks = c(1999, 2000, 2002, 2004, 2006), show.limits = TRUE) ) expect_doppelganger("guide_colorsteps sets labels when limits is in breaks", p + scale_color_binned(limits = c(1999, 2008), breaks = c(1999, 2000, 2002, 2004, 2006), labels = 1:5) ) expect_snapshot_warning(ggplotGrob(p + scale_color_binned(labels = 1:4, show.limits = TRUE))) }) test_that("guide_axis_theta sets relative angle", { p <- ggplot(mtcars, aes(disp, mpg)) + geom_point() + scale_x_continuous(breaks = breaks_width(25)) + coord_radial(inner.radius = 0.5) + guides( theta = guide_axis_theta(angle = 0, cap = "none"), theta.sec = guide_axis_theta(angle = 90, cap = "both") ) + theme(axis.line = element_line(colour = "black")) expect_doppelganger("guide_axis_theta with angle adapting to theta", p) }) test_that("guide_axis_theta can be used in cartesian coordinates", { p <- ggplot(mtcars, aes(disp, mpg)) + geom_point() + guides(x = "axis_theta", y = "axis_theta", x.sec = "axis_theta", y.sec = "axis_theta") + theme( axis.line.x.bottom = element_line(colour = "tomato"), axis.line.x.top = element_line(colour = "limegreen"), axis.line.y.left = element_line(colour = "dodgerblue"), axis.line.y.right = element_line(colour = "orchid") ) expect_doppelganger("guide_axis_theta in cartesian coordinates", p) }) test_that("a warning is generated when guides( = FALSE) is specified", { df <- data_frame(x = c(1, 2, 4), y = c(6, 5, 7)) # warn on guide( = FALSE) expect_warning(g <- guides(colour = FALSE), "The `` argument of `guides()` cannot be `FALSE`. Use \"none\" instead as of ggplot2 3.3.4.", fixed = TRUE) expect_equal(g$guides[["colour"]], "none") # warn on scale_*(guide = FALSE) p <- ggplot(df, aes(x, y, colour = x)) + scale_colour_continuous(guide = FALSE) expect_snapshot_warning(ggplot_build(p)) }) test_that("guides() warns if unnamed guides are provided", { expect_warning( guides("axis"), "All guides are unnamed." ) expect_warning( guides(x = "axis", "axis"), "The 2nd guide is unnamed" ) expect_null(guides()) }) test_that("legend.byrow works in `guide_legend()`", { df <- data.frame(x = 1:6, f = LETTERS[1:6]) p <- ggplot(df, aes(x, x, colour = f)) + geom_point() + scale_colour_discrete( guide = guide_legend( ncol = 3, theme = theme(legend.byrow = TRUE) ) ) expect_doppelganger("legend.byrow = TRUE", p) }) test_that("old S3 guides can be implemented", { my_env <- env() my_env$guide_circle <- function() { structure( list(available_aes = c("x", "y"), position = "bottom"), class = c("guide", "circle") ) } registerS3method( "guide_train", "circle", function(guide, ...) guide, envir = my_env ) registerS3method( "guide_transform", "circle", function(guide, ...) guide, envir = my_env ) registerS3method( "guide_merge", "circle", function(guide, ...) guide, envir = my_env ) registerS3method( "guide_geom", "circle", function(guide, ...) guide, envir = my_env ) registerS3method( "guide_gengrob", "circle", function(guide, ...) { absoluteGrob( gList(circleGrob()), height = unit(1, "cm"), width = unit(1, "cm") ) }, envir = my_env ) withr::local_environment(my_env) expect_snapshot_warning( expect_doppelganger( "old S3 guide drawing a circle", ggplot(mtcars, aes(disp, mpg)) + geom_point() + guides(x = "circle") ) ) })