.forge/prs/0a15e49a-092a-4c82-a56c-fc03a36d0dae/patch.diff 24.0 KiB 421 lines Raw
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