1 From 3af4cdd75682d5f1f264d233b4bc68d2f58ee6e0 Mon Sep 17 00:00:00 20012 From: gl0w <gl0w@world>3 Date: Thu, 10 Sep 2026 09:57:21 +01004 Subject: [PATCH] m6/cards2: file-table exempt, title-first cards5 6 ---7 src/build.rs | 57 ++++++++++++++++++++++++++----------------8 src/commits.rs | 12 ++++++---9 src/forge.rs | 47 +++++++++++++++++++++++++++++-----10 src/releases.rs | 8 ++++--11 static/style.css | 37 +++++++++++++++------------12 tests/forge_readers.rs | 10 ++++----13 tests/releases_api.rs | 8 +++---14 tests/truncate.rs | 18 +++++++++++--15 8 files changed, 137 insertions(+), 60 deletions(-)16 17 diff --git a/src/build.rs b/src/build.rs18 index 6a12e44..f1c0795 10064419 --- a/src/build.rs20 +++ b/src/build.rs21 @@ -579,13 +579,13 @@ pub fn file_table_html(22 history: &std::collections::BTreeMap<String, (String, String)>,23 ) -> String {24 let mut out = String::from(25 - "<div class=\"table-scroll\"><table class=\"file-table\">\n\26 + "<div class=\"table-scroll file-scroll\"><table class=\"file-table\">\n\27 <thead><tr><th>Name</th><th>Last commit</th><th>Age</th></tr></thead>\n<tbody>\n",28 );29 if let Some((href, label)) = parent {30 out.push_str(&format!(31 - "<tr class=\"parent\"><td class=\"trunc\" data-label=\"Name\" title=\"{}\"><a href=\"{}\">{}</a></td>\32 - <td class=\"trunc trunc-message\" data-label=\"Last commit\" title=\"\"></td><td class=\"trunc\" data-label=\"Age\" title=\"\"></td></tr>\n",33 + "<tr class=\"parent\"><td class=\"trunc\" title=\"{}\"><a href=\"{}\">{}</a></td>\34 + <td class=\"trunc trunc-message\" title=\"\"></td><td class=\"trunc\" title=\"\"></td></tr>\n",35 esc(label),36 esc(href),37 esc(label)38 @@ -601,8 +601,8 @@ pub fn file_table_html(39 let msg_short = truncate_msg(&msg);40 if e.is_dir {41 out.push_str(&format!(42 - "<tr class=\"dir\"><td class=\"trunc\" data-label=\"Name\" title=\"{}\"><a href=\"/files/{}.html\">{}/</a></td>\43 - <td class=\"title trunc trunc-message\" data-label=\"Last commit\" title=\"{}\">{}</td><td class=\"trunc\" data-label=\"Age\" title=\"{}\">{}</td></tr>\n",44 + "<tr class=\"dir\"><td class=\"trunc\" title=\"{}\"><a href=\"/files/{}.html\">{}/</a></td>\45 + <td class=\"title trunc trunc-message\" title=\"{}\">{}</td><td class=\"trunc\" title=\"{}\">{}</td></tr>\n",46 esc(&e.name),47 href_esc(&full),48 esc(&e.name),49 @@ -613,8 +613,8 @@ pub fn file_table_html(50 ));51 } else {52 out.push_str(&format!(53 - "<tr class=\"file\"><td class=\"trunc\" data-label=\"Name\" title=\"{}\"><a href=\"/files/{}.html\">{}</a></td>\54 - <td class=\"title trunc trunc-message\" data-label=\"Last commit\" title=\"{}\">{}</td><td class=\"trunc\" data-label=\"Age\" title=\"{}\">{}</td></tr>\n",55 + "<tr class=\"file\"><td class=\"trunc\" title=\"{}\"><a href=\"/files/{}.html\">{}</a></td>\56 + <td class=\"title trunc trunc-message\" title=\"{}\">{}</td><td class=\"trunc\" title=\"{}\">{}</td></tr>\n",57 esc(&e.name),58 href_esc(&full),59 esc(&e.name),60 @@ -1732,28 +1732,41 @@ mod tests {61 assert!(sub.contains("title=\""), "title attrs kept");62 }63 64 - /// m6/no-scroll (f0rge #39): file-table rows carry data-labels for cards.65 + /// m6/cards2 (f0rge #46): file table stays a real table on all widths —66 + /// no data-label card treatment, scroll wrapper kept, trunc + title kept.67 #[test]68 - fn file_table_cells_have_data_labels() {69 + fn file_table_stays_table_no_card_labels() {70 let css = include_str!("../static/style.css");71 - assert!(css.contains("content:attr(data-label)"), "label rule");72 assert!(css.contains("@media (max-width:767px)"), "narrow query");73 + assert!(74 + css.contains(".table-scroll:not(.file-scroll)"),75 + "cards scoped off file-scroll"76 + );77 + assert!(78 + css.contains(".file-scroll{overflow-x:auto}"),79 + "file-scroll keeps h-scroll on narrow"80 + );81 let (_repo, out) = build_fixture();82 for page in ["index.html", "files/sub.html"] {83 let html = std::fs::read_to_string(out.join(page)).expect(page);84 - let tds = html.matches("<td").count();85 - assert!(tds > 0, "{page} has cells");86 - assert_eq!(87 - html.matches("data-label=").count(),88 - tds,89 - "{page}: every td labeled"90 + assert!(91 + html.contains(92 + "<div class=\"table-scroll file-scroll\"><table class=\"file-table\">"93 + ),94 + "{page}: scroll wrapper kept"95 + );96 + assert!(!html.contains("data-label="), "{page}: no card labels");97 + assert!(98 + html.contains("<td class=\"title trunc trunc-message\""),99 + "{page}: message cell trunc kept"100 + );101 + assert!(html.contains("title=\""), "{page}: title attrs kept");102 + assert!(103 + html.contains("<th>Name</th>")104 + && html.contains("<th>Last commit</th>")105 + && html.contains("<th>Age</th>"),106 + "{page}: headers kept"107 );108 - for label in ["Name", "Last commit", "Age"] {109 - assert!(110 - html.contains(&format!("data-label=\"{label}\"")),111 - "{page}: label {label}"112 - );113 - }114 }115 }116 117 diff --git a/src/commits.rs b/src/commits.rs118 index 2324ee1..e3aa5de 100644119 --- a/src/commits.rs120 +++ b/src/commits.rs121 @@ -40,15 +40,15 @@ pub fn render_loaded(122 if log.is_empty() {123 body.push_str("<p class=\"muted\">No commits yet.</p>\n");124 } else {125 - body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>sha</th><th>subject</th><th>author</th><th>date</th></tr></thead>\n<tbody>\n");126 + body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>subject</th><th>sha</th><th>author</th><th>date</th></tr></thead>\n<tbody>\n");127 for c in log {128 body.push_str(&format!(129 - "<tr><td class=\"sha trunc trunc-sha\" data-label=\"sha\" title=\"{}\"><a href=\"/commit/{}.html\"><code>{}</code></a></td><td class=\"title trunc trunc-subject\" data-label=\"subject\" title=\"{}\">{}</td><td class=\"trunc trunc-author\" data-label=\"author\" title=\"{}\">{}</td><td class=\"trunc\" data-label=\"date\" title=\"{}\">{}</td></tr>\n",130 + "<tr><td class=\"title trunc trunc-subject\" data-label=\"subject\" title=\"{}\">{}</td><td class=\"sha trunc trunc-sha\" data-label=\"sha\" title=\"{}\"><a href=\"/commit/{}.html\"><code>{}</code></a></td><td class=\"trunc trunc-author\" data-label=\"author\" title=\"{}\">{}</td><td class=\"trunc\" data-label=\"date\" title=\"{}\">{}</td></tr>\n",131 + esc(&c.subject),132 + esc(&c.subject),133 esc(&c.sha),134 esc(&c.sha),135 esc(short_sha(&c.sha)),136 - esc(&c.subject),137 - esc(&c.subject),138 esc(&c.author),139 esc(&c.author),140 esc(&c.date),141 @@ -423,5 +423,9 @@ mod tests {142 "label {label}"143 );144 }145 + // m6/cards2 (f0rge #47): card DOM order is subject-first.146 + let subj = list.find("data-label=\"subject\"").expect("subject cell");147 + let sha = list.find("data-label=\"sha\"").expect("sha cell");148 + assert!(subj < sha, "card DOM: subject precedes sha");149 }150 }151 diff --git a/src/forge.rs b/src/forge.rs152 index 40aa052..d2827b6 100644153 --- a/src/forge.rs154 +++ b/src/forge.rs155 @@ -998,10 +998,10 @@ fn render_issues_index(156 if data.issues.is_empty() {157 body.push_str("<p class=\"muted\">No issues yet. <a href=\"/new-issue.html\">Suggest one</a> — no account needed.</p>\n");158 } else {159 - body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>#</th><th>title</th><th>state</th><th>comments</th><th>created</th></tr></thead>\n<tbody>\n");160 + body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>title</th><th>#</th><th>state</th><th>comments</th><th>created</th></tr></thead>\n<tbody>\n");161 for issue in &data.issues {162 body.push_str(&format!(163 - "<tr><td class=\"num trunc\" data-label=\"#\" title=\"{n}\">{n}</td><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"{title}\"><a href=\"/issues/{id}.html\">{title}</a></td><td class=\"trunc\" data-label=\"state\" title=\"{state}\">{state}</td><td class=\"trunc\" data-label=\"comments\" title=\"{comments}\">{comments}</td><td class=\"trunc\" data-label=\"created\" title=\"{created}\">{created}</td></tr>\n",164 + "<tr><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"{title}\"><a href=\"/issues/{id}.html\">{title}</a></td><td class=\"num trunc\" data-label=\"#\" title=\"{n}\">{n}</td><td class=\"trunc\" data-label=\"state\" title=\"{state}\">{state}</td><td class=\"trunc\" data-label=\"comments\" title=\"{comments}\">{comments}</td><td class=\"trunc\" data-label=\"created\" title=\"{created}\">{created}</td></tr>\n",165 n = issue.number,166 id = esc(&issue.id),167 title = esc(&issue.title),168 @@ -1059,14 +1059,14 @@ fn render_pulls_index(169 if prs.is_empty() {170 body.push_str("<p class=\"muted\">No pull requests yet. <a href=\"/new-pr.html\">Suggest one</a> — no account needed.</p>\n");171 } else {172 - body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>#</th><th>title</th><th>state</th><th>head→base</th><th>created</th></tr></thead>\n<tbody>\n");173 + body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>title</th><th>#</th><th>state</th><th>head→base</th><th>created</th></tr></thead>\n<tbody>\n");174 for pr in prs {175 let lanes = format!("{}→{}", esc(&pr.head), esc(&pr.base));176 let lanes_plain = format!("{}→{}", pr.head, pr.base);177 let draft = if pr.draft { " draft" } else { "" };178 let full_title = format!("{}{}", pr.title, draft);179 body.push_str(&format!(180 - "<tr><td class=\"num trunc\" data-label=\"#\" title=\"{n}\">{n}</td><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"{full}\"><a href=\"/pulls/{id}.html\">{title}</a>{draft}</td><td class=\"trunc\" data-label=\"state\" title=\"{state}\">{state}</td><td class=\"trunc\" data-label=\"head→base\" title=\"{lanes_title}\"><code>{lanes}</code></td><td class=\"trunc\" data-label=\"created\" title=\"{created}\">{created}</td></tr>\n",181 + "<tr><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"{full}\"><a href=\"/pulls/{id}.html\">{title}</a>{draft}</td><td class=\"num trunc\" data-label=\"#\" title=\"{n}\">{n}</td><td class=\"trunc\" data-label=\"state\" title=\"{state}\">{state}</td><td class=\"trunc\" data-label=\"head→base\" title=\"{lanes_title}\"><code>{lanes}</code></td><td class=\"trunc\" data-label=\"created\" title=\"{created}\">{created}</td></tr>\n",182 n = pr.number,183 id = esc(&pr.id),184 title = esc(&pr.title),185 @@ -1823,11 +1823,19 @@ mod tests {186 css.contains("content:attr(data-label)"),187 "label via ::before"188 );189 - assert!(css.contains(".table-scroll td::before"), "label rule");190 assert!(191 - css.contains(".table-scroll td.title::before"),192 + css.contains(".table-scroll:not(.file-scroll) td::before"),193 + "label rule"194 + );195 + assert!(196 + css.contains(".table-scroll:not(.file-scroll) td.title::before"),197 "title leads card"198 );199 + // m6/cards2 (f0rge #46): file table exempt from the card reflow.200 + assert!(201 + css.contains(".file-scroll{overflow-x:auto}"),202 + "file table keeps h-scroll on narrow"203 + );204 assert!(205 css.contains("display:block") && css.contains("display:flex"),206 "card layout"207 @@ -1894,8 +1902,35 @@ mod tests {208 );209 }210 }211 + // m6/cards2 (f0rge #47): card DOM order is title-first via template212 + // cell order (no CSS order hacks) — desktop column headers match.213 + for (page, first, second) in [214 + (215 + "issues/index.html",216 + "data-label=\"title\"",217 + "data-label=\"#\"",218 + ),219 + (220 + "pulls/index.html",221 + "data-label=\"title\"",222 + "data-label=\"#\"",223 + ),224 + (225 + "ci/index.html",226 + "data-label=\"step\"",227 + "data-label=\"exit\"",228 + ),229 + ] {230 + let html = std::fs::read_to_string(out.join(page)).expect(page);231 + let a = html.find(first).expect("first cell");232 + let b = html.find(second).expect("second cell");233 + assert!(a < b, "{page}: card DOM title precedes meta");234 + }235 let tasks = std::fs::read_to_string(out.join("tasks/index.html")).expect("tasks");236 assert!(!tasks.contains("table-scroll"), "tasks: no scroll wrapper");237 + let title_pos = tasks.find("Card task").expect("task title");238 + let meta_pos = tasks.find("<code>cccccccc").expect("task meta");239 + assert!(title_pos < meta_pos, "tasks: title precedes meta");240 assert!(tasks.contains("todo (1)"), "tasks group kept");241 let issues = std::fs::read_to_string(out.join("issues/index.html")).expect("issues");242 assert!(243 diff --git a/src/releases.rs b/src/releases.rs244 index 68c050b..cceb905 100644245 --- a/src/releases.rs246 +++ b/src/releases.rs247 @@ -395,10 +395,10 @@ pub fn render_loaded(248 if releases.is_empty() {249 body.push_str("<p class=\"muted\">No releases yet.</p>\n");250 } else {251 - body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>version</th><th>title</th><th>created</th><th>artifacts</th></tr></thead>\n<tbody>\n");252 + body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>title</th><th>version</th><th>created</th><th>artifacts</th></tr></thead>\n<tbody>\n");253 for rel in releases {254 body.push_str(&format!(255 - "<tr><td class=\"trunc\" data-label=\"version\" title=\"{v}\"><a href=\"/releases/{v}.html\">{ve}</a></td><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"{t}\">{t}</td><td class=\"trunc\" data-label=\"created\" title=\"{c}\">{c}</td><td class=\"trunc\" data-label=\"artifacts\" title=\"{n}\">{n}</td></tr>\n",256 + "<tr><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"{t}\">{t}</td><td class=\"trunc\" data-label=\"version\" title=\"{v}\"><a href=\"/releases/{v}.html\">{ve}</a></td><td class=\"trunc\" data-label=\"created\" title=\"{c}\">{c}</td><td class=\"trunc\" data-label=\"artifacts\" title=\"{n}\">{n}</td></tr>\n",257 v = esc(&rel.version),258 ve = esc(&rel.version),259 t = esc(rel.display_title()),260 @@ -584,5 +584,9 @@ mod tests {261 "label {label}"262 );263 }264 + // m6/cards2 (f0rge #47): card DOM order is title-first.265 + let title = index.find("data-label=\"title\"").expect("title cell");266 + let ver = index.find("data-label=\"version\"").expect("version cell");267 + assert!(title < ver, "card DOM: title precedes version");268 }269 }270 diff --git a/static/style.css b/static/style.css271 index d645df0..a305df3 100644272 --- a/static/style.css273 +++ b/static/style.css274 @@ -229,21 +229,26 @@ details.clonebox:not([open]){padding-bottom:.25rem}275 Desktop (>=768px) keeps .table-scroll horizontal scroll untouched above.276 Narrow: no scroll (overflow visible), thead clipped for SR only, each277 tr becomes a bordered card, each td a labeled flex row via data-label.278 - Title cells lead bold without a repeated label; nothing is deleted. */279 + Title cells lead bold without a repeated label; nothing is deleted.280 + File table exempt (m6/cards2, f0rge #46): dense reference data stays a281 + real table at every width — .file-scroll keeps horizontal scroll and282 + table layout on narrow while every other list table becomes cards. */283 @media (max-width:767px){284 - .table-scroll{overflow-x:visible}285 - .table-scroll table,.file-table table{width:100%;min-width:0}286 - .table-scroll thead{position:absolute;max-width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}287 - .table-scroll tbody,.table-scroll tr{display:block;width:auto}288 - .table-scroll tr{border:1px solid var(--border);background:var(--bg-alt);margin:0 0 .75rem;padding:.25rem 0}289 - .table-scroll th,.table-scroll td,.file-table th:nth-child(3),.file-table td:nth-child(3){white-space:normal}290 - .table-scroll td{display:flex;gap:.5rem;align-items:baseline;padding:.4rem .75rem;border:none;border-bottom:1px solid var(--border);min-width:0;overflow-wrap:anywhere}291 - .table-scroll td:last-child{border-bottom:none}292 - .table-scroll td::before{content:attr(data-label);flex:0 0 6rem;color:var(--muted);font-weight:600;font-size:.8rem;overflow-wrap:anywhere}293 - .table-scroll td.title::before{display:none}294 - .table-scroll td.title{font-weight:700;font-size:1.02rem;min-width:0}295 - .table-scroll td.num,.table-scroll td.sha{width:auto}296 - .table-scroll td code,.table-scroll td a{overflow-wrap:anywhere;word-break:break-all;min-width:0}297 - .table-scroll tbody tr:hover td{background:none}298 - .table-scroll td.trunc,.trunc{white-space:normal;max-width:none;overflow:visible;text-overflow:clip}299 + .table-scroll:not(.file-scroll){overflow-x:visible}300 + .table-scroll:not(.file-scroll) table{width:100%;min-width:0}301 + .table-scroll:not(.file-scroll) thead{position:absolute;max-width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}302 + .table-scroll:not(.file-scroll) tbody,.table-scroll:not(.file-scroll) tr{display:block;width:auto}303 + .table-scroll:not(.file-scroll) tr{border:1px solid var(--border);background:var(--bg-alt);margin:0 0 .75rem;padding:.25rem 0}304 + .table-scroll:not(.file-scroll) th,.table-scroll:not(.file-scroll) td{white-space:normal}305 + .table-scroll:not(.file-scroll) td{display:flex;gap:.5rem;align-items:baseline;padding:.4rem .75rem;border:none;border-bottom:1px solid var(--border);min-width:0;overflow-wrap:anywhere}306 + .table-scroll:not(.file-scroll) td:last-child{border-bottom:none}307 + .table-scroll:not(.file-scroll) td::before{content:attr(data-label);flex:0 0 6rem;color:var(--muted);font-weight:600;font-size:.8rem;overflow-wrap:anywhere}308 + .table-scroll:not(.file-scroll) td.title::before{display:none}309 + .table-scroll:not(.file-scroll) td.title{font-weight:700;font-size:1.02rem;min-width:0}310 + .table-scroll:not(.file-scroll) td.num,.table-scroll:not(.file-scroll) td.sha{width:auto}311 + .table-scroll:not(.file-scroll) td code,.table-scroll:not(.file-scroll) td a{overflow-wrap:anywhere;word-break:break-all;min-width:0}312 + .table-scroll:not(.file-scroll) tbody tr:hover td{background:none}313 + .table-scroll:not(.file-scroll) td.trunc,.trunc{white-space:normal;max-width:none;overflow:visible;text-overflow:clip}314 + .file-scroll{overflow-x:auto}315 + .file-scroll table{width:max-content;min-width:100%}316 }317 diff --git a/tests/forge_readers.rs b/tests/forge_readers.rs318 index c03348b..b43bfcf 100644319 --- a/tests/forge_readers.rs320 +++ b/tests/forge_readers.rs321 @@ -224,19 +224,19 @@ fn forge_fixture_renders_counts_numbers_and_ci() {322 323 // --- issues index: 2 rendered rows (broken one skipped) ---324 let issues = read("issues/index.html");325 - let rows = issues.matches("<tr><td class=\"num").count();326 + let rows = issues.matches("<tr><td class=\"title").count();327 assert_eq!(rows, 2, "rendered issues match file-count minus broken");328 assert!(!issues.contains("No title here"), "broken issue skipped");329 // 5-number spot check (1): #1 links the older uuid, #2 the newer uuid.330 assert!(331 issues.contains(&format!(332 - "<td class=\"num trunc\" data-label=\"#\" title=\"1\">1</td><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"Older issue\"><a href=\"/issues/{OLDER_UUID}.html\">"333 + "<td class=\"title trunc trunc-title\" data-label=\"title\" title=\"Older issue\"><a href=\"/issues/{OLDER_UUID}.html\">Older issue</a></td><td class=\"num trunc\" data-label=\"#\" title=\"1\">1</td>"334 )),335 "issue #1 -> older uuid"336 );337 assert!(338 issues.contains(&format!(339 - "<td class=\"num trunc\" data-label=\"#\" title=\"2\">2</td><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"Newer issue\"><a href=\"/issues/{NEWER_UUID}.html\">"340 + "<td class=\"title trunc trunc-title\" data-label=\"title\" title=\"Newer issue\"><a href=\"/issues/{NEWER_UUID}.html\">Newer issue</a></td><td class=\"num trunc\" data-label=\"#\" title=\"2\">2</td>"341 )),342 "issue #2 -> newer uuid"343 );344 @@ -263,13 +263,13 @@ fn forge_fixture_renders_counts_numbers_and_ci() {345 // (4): pulls — same shape + head/base/draft/ci + patch <pre>.346 let pulls = read("pulls/index.html");347 assert_eq!(348 - pulls.matches("<tr><td class=\"num").count(),349 + pulls.matches("<tr><td class=\"title").count(),350 1,351 "prs match file-count"352 );353 assert!(354 pulls.contains(&format!(355 - "<td class=\"num trunc\" data-label=\"#\" title=\"1\">1</td><td class=\"title trunc trunc-title\" data-label=\"title\" title=\"Fix the thing\"><a href=\"/pulls/{PR_UUID}.html\">"356 + "<td class=\"title trunc trunc-title\" data-label=\"title\" title=\"Fix the thing\"><a href=\"/pulls/{PR_UUID}.html\">Fix the thing</a></td><td class=\"num trunc\" data-label=\"#\" title=\"1\">1</td>"357 )),358 "pr #1 -> uuid"359 );360 diff --git a/tests/releases_api.rs b/tests/releases_api.rs361 index 0c7e472..04e6cee 100644362 --- a/tests/releases_api.rs363 +++ b/tests/releases_api.rs364 @@ -154,12 +154,14 @@ fn releases_artifacts_api_agents_feed() {365 366 // --- api counts == html counts (same struct pass) ---367 let issues_html = read("issues/index.html")368 - .matches("<tr><td class=\"num")369 + .matches("<tr><td class=\"title")370 .count();371 let pulls_html = read("pulls/index.html")372 - .matches("<tr><td class=\"num")373 + .matches("<tr><td class=\"title")374 + .count();375 + let commits_html = read("commits.html")376 + .matches("<tr><td class=\"title")377 .count();378 - let commits_html = read("commits.html").matches("<tr><td class=\"sha").count();379 let releases_html = read("releases/index.html").matches("<tr><td").count();380 let wiki_html = read("wiki/index.html")381 .matches("<li><a href=\"/wiki/")382 diff --git a/tests/truncate.rs b/tests/truncate.rs383 index 12d9004..2f08e2d 100644384 --- a/tests/truncate.rs385 +++ b/tests/truncate.rs386 @@ -135,6 +135,12 @@ fn build_evil_fixture() -> (PathBuf, String, String) {387 }388 389 fn assert_every_td_truncated(html: &str, page: &str) {390 + // m6/cards2 (f0rge #46): the file-table page (index.html) stays a real391 + // table — trunc + title= kept, no data-label card treatment.392 + assert_every_td_truncated_opts(html, page, page != "index.html");393 +}394 +395 +fn assert_every_td_truncated_opts(html: &str, page: &str, expect_labels: bool) {396 let parts: Vec<&str> = html.split("<td").skip(1).collect();397 assert!(!parts.is_empty(), "{page} has cells");398 for (i, p) in parts.iter().enumerate() {399 @@ -147,9 +153,17 @@ fn assert_every_td_truncated(html: &str, page: &str) {400 head.contains("title="),401 "{page} td #{i} carries title= (got {head:?})"402 );403 + if expect_labels {404 + assert!(405 + head.contains("data-label="),406 + "{page} td #{i} keeps data-label (got {head:?})"407 + );408 + }409 + }410 + if !expect_labels {411 assert!(412 - head.contains("data-label="),413 - "{page} td #{i} keeps data-label (got {head:?})"414 + !html.contains("data-label="),415 + "{page} has no card data-labels"416 );417 }418 }419 -- 420 2.55.0421