tests/testthat/test-pagination.R

# --- Test Setup Data ---
test_data_rt <- data.frame(
  Group = factor(rep(c("A", "B"), each = 20)),
  Sub = factor(rep(c("X", "Y", "X", "Y"), each = 10)),
  Val = rnorm(40),
  stringsAsFactors = TRUE
)

test_data_rl <- data.frame(
  ID = rep(1:5, each = 4), # Make it slightly longer
  Time = rep(1:4, times = 5),
  Result = sample(LETTERS, 20, replace = TRUE),
  Comment = paste("Comment", 1:20),
  stringsAsFactors = FALSE
)

# Simple rtable layout
lyt_rt <- basic_table(title = "RTables Test") %>%
  split_cols_by("Group") %>%
  analyze("Val", afun = function(x) list(mean = mean(x), n = length(x)))

# Simple rlisting
res_rl <- as_listing(test_data_rl,
  key_cols = "ID",
  disp_cols = c("Time", "Result", "Comment"),
  main_title = "RListing Test"
)


# --- Tests ---
test_that("export_as_docx handles list generated by tt_to_flextable(paginate=TRUE)", {
  # Setup: Create rtable with implicit pagination defined (e.g., via page_by)
  lyt_paged <- basic_table() %>%
    split_rows_by("Group", page_by = TRUE, page_prefix = "Group") %>% # Page per Group
    analyze("Val", afun = function(x) list(mean = mean(x), sd = sd(x)))

  # Build table - assume tbl is a single VTableTree object representing the full structure
  tbl <- build_table(lyt_paged, test_data_rt)
  expect_s4_class(tbl, "VTableTree") # Check assumption

  # Action 1: Generate list of flextables using tt_to_flextable pagination
  # Use a reasonable lpp value that forces pagination based on content/structure
  # (Exact value depends on table structure, title height etc.)
  result_pages <- NULL
  LPP_TEST_VALUE <- 15 # Adjust as needed based on expected table height per page
  expect_no_error(
    result_pages <- tt_to_flextable(tbl, paginate = TRUE, lpp = LPP_TEST_VALUE)
  )

  # Verification 1: Check output of tt_to_flextable pagination
  expect_true(
    is.list(result_pages) && !is.data.frame(result_pages),
    "tt_to_flextable with paginate=TRUE should produce a list"
  )
  n_groups <- length(unique(test_data_rt$Group)) # Number of pages expected
  expect_equal(length(result_pages), n_groups,
    info = "Number of list elements should match number of pages defined by page_by"
  )
  expect_true(
    all(sapply(result_pages, inherits, "flextable")),
    "All elements in the list should be flextable objects"
  )

  # Verification 2: Check error handling for lpp in tt_to_flextable
  # This tests tt_to_flextable's pagination logic, not export_as_docx
  expect_error(
    tt_to_flextable(tbl, paginate = TRUE, lpp = 1), # lpp=1 typically too small
    # Optionally add regexp to match specific error message if known:
    # regexp = "Header rows are more than selected lines per pages",
    label = "tt_to_flextable should error if lpp is too small"
  )

  # File handling for export
  tf <- tempfile(fileext = ".docx")
  on.exit(unlink(tf), add = TRUE) # Ensure cleanup

  # Action 2: Export the list of flextables generated by tt_to_flextable
  expect_no_error(
    # Pass the list 'result_pages' directly to export_as_docx
    export_as_docx(tt = result_pages, file = tf, add_page_break = TRUE)
  )

  # Verification 3: Basic checks on the output DOCX file
  expect_true(file.exists(tf), "DOCX file was not created by export_as_docx")
  expect_gt(file.info(tf)$size, 0, "DOCX file appears empty")
  # NOTE: Verifying content (multiple tables, page breaks) requires docx parsing tools.
})

test_that("export_as_docx works with explicit list of rlistings/rtables objects", {
  # Setup: Create rtable and rlisting
  res_rt <- build_table(lyt_rt, test_data_rt)
  # res_rl created above

  # Create the mixed list
  mixed_list <- list(Table = res_rt, Listing = res_rl)

  # File handling
  tf <- tempfile(fileext = ".docx")
  on.exit(unlink(tf), add = TRUE)

  # Action: Export the mixed list directly
  # export_as_docx should internally convert list elements using tt_to_flextable
  expect_no_error(
    export_as_docx(mixed_list, file = tf, add_page_break = TRUE)
  )

  # Basic Verification
  expect_true(file.exists(tf), "DOCX file was not created")
  expect_gt(file.info(tf)$size, 0, "DOCX file is empty")
  # NOTE: Assumes export_as_docx correctly calls tt_to_flextable for list elements.
})

test_that("export_as_docx works with pagination via lpp/cpp arguments", {
  # Setup: Create a single rtable potentially spanning multiple pages
  # Use a layout without explicit page_by
  lyt_long <- basic_table(title = "Long Table for Pagination") %>%
    split_cols_by("Group") %>%
    split_rows_by("Sub") %>%
    analyze("Val", afun = function(x) list(mean = mean(x), sd = sd(x), n = length(x)))

  tbl_long <- build_table(lyt_long, test_data_rt)

  # File handling
  tf <- tempfile(fileext = ".docx")
  on.exit(unlink(tf), add = TRUE)

  # Action: Export with pagination arguments passed via ...
  # Choose lpp small enough to likely force pagination
  test_lpp <- 10
  expect_no_error(
    export_as_docx(
      tbl_long,
      file = tf,
      paginate = TRUE, # This tells tt_to_flextable to paginate
      lpp = test_lpp, # Passed via ... to tt_to_flextable
      # cpp = 80,      # Optionally pass cpp via ...
      add_page_break = TRUE # Applies between generated pages
    )
  )

  # Basic Verification
  expect_true(file.exists(tf), "DOCX file was not created")
  expect_gt(file.info(tf)$size, 0, "DOCX file is empty")
  # NOTE: This test confirms the call path works. Verifying *actual* pagination
  # (i.e., multiple flextables were created and exported) requires either
  # inspecting the DOCX or intercepting the list passed to officer internally.
  # We assume if it runs without error, the pagination args were processed.
})


test_that("export_as_docx works with column widths and autofit settings", {
  # Setup: Create a basic rtable
  res_rt <- build_table(lyt_rt, test_data_rt)
  num_cols <- ncol(res_rt) + 1 # +1 for row labels column

  # File handling
  tf1 <- tempfile(fileext = ".docx")
  tf2 <- tempfile(fileext = ".docx")
  on.exit(unlink(c(tf1, tf2)), add = TRUE)

  # Action 1: Manual Column Widths (autofit should become FALSE)
  # Provide relative widths matching number of columns
  manual_widths <- rep(1 / num_cols, num_cols) # Equal widths
  expect_no_error(
    expect_warning(
      out <- export_as_docx(
        res_rt,
        file = tf1,
        colwidths = manual_widths, # Passed via ... to tt_to_flextable
        # autofit_to_page = FALSE, # Should be set automatically by tt_to_flextable
        section_properties = section_properties_default(orientation = "landscape") # Ensure width context
      )
    )
  )

  # Basic Verification 1
  expect_true(file.exists(tf1), "DOCX file (manual width) was not created")
  expect_gt(file.info(tf1)$size, 0, "DOCX file (manual width) is empty")

  # Action 2: Autofit (Default Behavior)
  expect_no_error(
    export_as_docx(
      res_rt,
      file = tf2
      # Default autofit_to_page = TRUE in tt_to_flextable is used
    )
  )
  # Basic Verification 2
  expect_true(file.exists(tf2), "DOCX file (autofit) was not created")
  expect_gt(file.info(tf2)$size, 0, "DOCX file (autofit) is empty")

  # NOTE: Verifying the actual layout ("fixed" vs "autofit") or specific
  # column widths within the DOCX requires parsing the document.
  # These tests primarily ensure the arguments can be passed without error.
})

Try the rtables.officer package in your browser

Any scripts or data that you put into this service are public.

rtables.officer documentation built on June 8, 2025, 12:44 p.m.