Commit 55d70f99b2
55d70f99b2841a1f97936a5f6c4607ddce0c5da9
parent: 9def4a7f1d
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-18 04:19 UTC
httpd, e2e: the tree root of the default branch is the repo home
layout.html's own comment says the repohead is identical on every page
within a tab, but /owner/repo and /owner/repo/tree/<default>/ rendered
different identity markup (h1 vs p+link) because only the repoHome
handler set RepoHome; the tree handler never did, even when it served
the exact same content by a more explicit URL. tree() now sets
RepoHome when the ref is the default branch and the path is empty.
TestReadmeRelativeLinks gains a check that the repohead is
byte-identical between an issue list and a single issue, and between
the repo home and its tree root, since inst.get is anonymous and both
pairs share a tab.
Ref #221
e2e/design_test.go
+24
| @@ -149,6 +149,30 @@ func TestReadmeRelativeLinks(t *testing.T) { |
| 149 | 149 | if _, body := inst.get(t, "/alice/site"); !strings.Contains(body, `class="chip topic"`) { |
| 150 | 150 | t.Error("repo home lost its topics") |
| 151 | 151 | } |
| 152 | // The repohead is the same markup on every tab within a repository — |
| 153 | // aria-current on the tab link and the watch/bookmark toggles are the |
| 154 | // only things that could vary, and they depend on who is signed in, |
| 155 | // not which tab is shown. inst.get is anonymous, so two tabs' headers |
| 156 | // must be byte-identical. |
| 157 | inst.ssh(t, aliceKey, "", "issue", "create", "alice/site", "--title", "one") |
| 158 | header := func(body string) string { |
| 159 | i := strings.Index(body, `<header class="repohead">`) |
| 160 | j := strings.Index(body, "</header>") |
| 161 | if i < 0 || j < 0 || j < i { |
| 162 | t.Fatalf("repohead not found:\n%.2000s", body) |
| 163 | } |
| 164 | return body[i:j] |
| 165 | } |
| 166 | _, issuesBody := inst.get(t, "/alice/site/issues") |
| 167 | _, issueBody := inst.get(t, "/alice/site/issues/1") |
| 168 | if a, b := header(issuesBody), header(issueBody); a != b { |
| 169 | t.Errorf("issues list and issue page headers differ:\n%s\nvs\n%s", a, b) |
| 170 | } |
| 171 | _, homeBody := inst.get(t, "/alice/site") |
| 172 | _, treeBody := inst.get(t, "/alice/site/tree/main/") |
| 173 | if a, b := header(homeBody), header(treeBody); a != b { |
| 174 | t.Errorf("repo home and tree headers differ:\n%s\nvs\n%s", a, b) |
| 175 | } |
| 152 | 176 | } |
| 153 | 177 | |
| 154 | 178 | // TestLandingRoutes checks the landing page's copy and the two routes. |
internal/httpd/web.go
+6 −1
| @@ -481,7 +481,12 @@ func (s *Server) tree(w http.ResponseWriter, r *http.Request) { |
| 481 | 481 | return |
| 482 | 482 | } |
| 483 | 483 | p.Tab = "files" |
| 484 | | s.renderTree(w, r, p, strings.Trim(r.PathValue("path"), "/")) |
| 484 | path := strings.Trim(r.PathValue("path"), "/") |
| 485 | // The root of the default branch is the same page as the bare repo |
| 486 | // URL, so its header must match: RepoHome is what picks the h1 over |
| 487 | // the p+link identity, not which route was typed. |
| 488 | p.RepoHome = path == "" && p.Ref == p.Repo.DefaultBranch |
| 489 | s.renderTree(w, r, p, path) |
| 485 | 490 | } |
| 486 | 491 | |
| 487 | 492 | // treePage is shared by the populated and empty-repository renders: two |