| Marc Kupietz | b066e74 | 2026-08-23 09:57:06 +0300 | [diff] [blame^] | 1 | test_that("parse_duration_seconds handles all duration formats", { |
| 2 | expect_equal(parse_duration_seconds("00:30"), 30) |
| 3 | expect_equal(parse_duration_seconds("1:05"), 65) |
| 4 | expect_equal(parse_duration_seconds("45"), 45) |
| 5 | expect_equal(parse_duration_seconds("1:00:00"), 3600) |
| 6 | expect_equal(parse_duration_seconds("1:30:15"), 5415) |
| 7 | expect_true(is.na(parse_duration_seconds("abc"))) |
| 8 | }) |
| 9 | |
| 10 | test_that("extract_timing_comments finds comments", { |
| 11 | lines <- c( |
| 12 | "---", |
| 13 | "title: x", |
| 14 | "---", |
| 15 | "", |
| 16 | "## Slide one", |
| 17 | "", |
| 18 | "<!-- MK 00:30 -->", |
| 19 | "", |
| 20 | "## Slide two", |
| 21 | "", |
| 22 | "<!-- AB 1:00 -->", |
| 23 | "<!-- MK 90 -->" |
| 24 | ) |
| 25 | comments <- extract_timing_comments(lines) |
| 26 | expect_equal(comments$speaker, c("MK", "AB", "MK")) |
| 27 | expect_equal(comments$seconds, c(30, 60, 90)) |
| 28 | expect_equal(assign_slides(lines, comments, slide_level = 2), |
| 29 | c("Slide one", "Slide two", "Slide two")) |
| 30 | }) |
| 31 | |
| 32 | test_that("comments without speaker codes are supported", { |
| 33 | lines <- c("## Slide one", "<!-- 00:30 -->", "## Slide two", "<!-- 1:05 -->") |
| 34 | comments <- extract_timing_comments(lines) |
| 35 | expect_true(all(is.na(comments$speaker))) |
| 36 | expect_equal(comments$seconds, c(30, 65)) |
| 37 | |
| 38 | # a plain number must not be split into a speaker code plus duration |
| 39 | comments2 <- extract_timing_comments("<!-- 45 -->") |
| 40 | expect_true(is.na(comments2$speaker)) |
| 41 | expect_equal(comments2$seconds, 45) |
| 42 | |
| 43 | result <- apply_timing_attributes(c( |
| 44 | "<section id='s1' class='level2'>", |
| 45 | "<!-- 00:30 -->", |
| 46 | "</section>" |
| 47 | )) |
| 48 | expect_match(result[1], "data-timing=\"30\"") |
| 49 | |
| 50 | report <- timing_report_message(extract_timing_comments(lines)) |
| 51 | expect_equal(report, "Slide timing -- total 1:35") |
| 52 | }) |
| 53 | |
| 54 | test_that("comments before first heading belong to title slide", { |
| 55 | lines <- c("<!-- MK 00:10 -->", "", "# Title", "", "## Slide") |
| 56 | comments <- extract_timing_comments(lines) |
| 57 | expect_equal(assign_slides(lines, comments, slide_level = 2), "(title)") |
| 58 | }) |
| 59 | |
| 60 | test_that("apply_timing_attributes adds data-timing to sections", { |
| 61 | lines <- c( |
| 62 | "<html>", |
| 63 | "<section id='title-slide'>", |
| 64 | "title", |
| 65 | "</section>", |
| 66 | "<section id='s1' class='level2'>", |
| 67 | "<!-- MK 00:30 -->", |
| 68 | "</section>", |
| 69 | "<section id='s2' class='level2'>", |
| 70 | "<!-- MK 0:20 -->", |
| 71 | "<!-- AB 00:30 -->", |
| 72 | "</section>" |
| 73 | ) |
| 74 | result <- apply_timing_attributes(lines) |
| 75 | html <- paste(result, collapse = "\n") |
| 76 | expect_match(result[2], "<section id='title-slide'>") |
| 77 | expect_match(result[5], "data-timing=\"30\"") |
| 78 | # multiple speakers on one slide are summed |
| 79 | expect_match(result[8], "data-timing=\"50\"") |
| 80 | comments <- attr(result, "comments") |
| 81 | expect_equal(sum(comments$seconds), 80) |
| 82 | }) |
| 83 | |
| 84 | test_that("apply_timing_attributes is a no-op without comments", { |
| 85 | lines <- c("<section id='s1'>", "x", "</section>") |
| 86 | result <- apply_timing_attributes(lines) |
| 87 | expect_identical(as.vector(result), lines) |
| 88 | expect_equal(nrow(attr(result, "comments")), 0) |
| 89 | }) |
| 90 | |
| 91 | test_that("inject_total_time activates the pacing timer", { |
| 92 | lines <- c( |
| 93 | "var opts = {", |
| 94 | " controls: true,", |
| 95 | "};", |
| 96 | "Reveal.initialize({", |
| 97 | " controls: true,", |
| 98 | " slideNumber: true", |
| 99 | "});" |
| 100 | ) |
| 101 | result <- inject_total_time(lines, 90) |
| 102 | expect_contains(result, "\t\t\ttotalTime: 90,") |
| 103 | |
| 104 | # existing pacing config is respected |
| 105 | with_default <- c(lines, "", "defaultTiming: 60") |
| 106 | expect_identical(inject_total_time(with_default, 90), with_default) |
| 107 | with_total <- c("totalTime: 120", lines) |
| 108 | expect_identical(inject_total_time(with_total, 90), with_total) |
| 109 | |
| 110 | # nothing to hook into -- leave the document alone |
| 111 | expect_identical(inject_total_time(c("<html>", "</html>"), 90), |
| 112 | c("<html>", "</html>")) |
| 113 | }) |
| 114 | |
| 115 | test_that("rendered presentations carry data-timing attributes", { |
| 116 | skip_if_not_pandoc() |
| 117 | skip_if_not_installed("xml2") |
| 118 | rmd <- local_temp_rmd_file( |
| 119 | "---", |
| 120 | "title: Timing test", |
| 121 | "output: revealjs.ids::revealjs_presentation", |
| 122 | "---", |
| 123 | "", |
| 124 | "## Slide A", |
| 125 | "", |
| 126 | "Content A", |
| 127 | "", |
| 128 | "<!-- MK 00:30 -->", |
| 129 | "", |
| 130 | "## Slide B", |
| 131 | "", |
| 132 | "Content B", |
| 133 | "", |
| 134 | "<!-- MK 01:00 -->", |
| 135 | "<!-- AB 0:30 -->" |
| 136 | ) |
| 137 | html <- .render_and_read(rmd) |
| 138 | timings <- xml2::xml_attr( |
| 139 | xml2::xml_find_all(html, "//section[@data-timing]"), |
| 140 | "data-timing" |
| 141 | ) |
| 142 | expect_equal(timings, c("30", "90")) |
| 143 | # the grand total activates the pacing timer of the notes plugin |
| 144 | expect_true(any(grepl("totalTime:\\s*120", html))) |
| 145 | }) |
| 146 | |
| 147 | test_that("a user-supplied totalTime is respected", { |
| 148 | skip_if_not_pandoc() |
| 149 | rmd <- local_temp_rmd_file( |
| 150 | "---", |
| 151 | "title: Timing test", |
| 152 | "output: revealjs.ids::revealjs_presentation", |
| 153 | "---", |
| 154 | "", |
| 155 | "## Slide A", |
| 156 | "", |
| 157 | "<!-- 00:30 -->" |
| 158 | ) |
| 159 | html <- .render_and_read(rmd, output_options = list( |
| 160 | reveal_options = list(totalTime = 1800) |
| 161 | )) |
| 162 | # passed through to the config... |
| 163 | expect_true(any(grepl("totalTime:\\s*1800", html))) |
| 164 | # ... and not overwritten by the calculated total |
| 165 | expect_false(any(grepl("totalTime:\\s*30", html))) |
| 166 | }) |
| 167 | |
| 168 | test_that("slide_timing reports totals", { |
| 169 | rmd_file <- tempfile(fileext = ".Rmd") |
| 170 | writeLines(c( |
| 171 | "---", |
| 172 | "title: x", |
| 173 | "output: revealjs.ids::revealjs_presentation", |
| 174 | "---", |
| 175 | "", |
| 176 | "## Slide one", |
| 177 | "", |
| 178 | "<!-- MK 00:30 -->", |
| 179 | "", |
| 180 | "## Slide two", |
| 181 | "", |
| 182 | "<!-- AB 1:00 -->", |
| 183 | "<!-- MK 90 -->" |
| 184 | ), rmd_file) |
| 185 | expect_output(result <- slide_timing(rmd_file), "total") |
| 186 | expect_equal(result$total, 180) |
| 187 | expect_equal(result$speakers$seconds[result$speakers$speaker == "MK"], 120) |
| 188 | expect_equal(result$speakers$seconds[result$speakers$speaker == "AB"], 60) |
| 189 | unlink(rmd_file) |
| 190 | }) |