test: testmark HTML snapshots of the browse pages
commit 619b45eb155c50a255def20b05c701cff3c16faa · parent 44017bb364
· browse this tree
Ximianth, 2026-10-26 20:06:41 UTC
Ten cases in testdata/pages.md over the hermetic fixture: index, home, tree listings, file views (branch- and sha-addressed), log, and the two error shapes. Regenerable with -testmark.regen; watched failing against a perturbed template before trusting. Writing the first snapshots surfaced a real bug, fixed here: path.Dir's "." result for top-level entries leaked into parent links and crumbs (hrefs like /tree/main/./); parentDir now returns "" for the root.
diff --git a/pages.go b/pages.go index d5d8d68..d1acc8e 100644 --- a/pages.go +++ b/pages.go @@ -281,7 +281,7 @@ func (s *server) serveTreeListing(w http.ResponseWriter, rp *repo, ref, sha, pth d.Title = rp.name } if pth != "" { - d.Rows = append(d.Rows, treeRow{Name: "..", Href: treeHref(rp.name, ref, path.Dir(strings.TrimSuffix(pth, "/"))) + "/"}) + d.Rows = append(d.Rows, treeRow{Name: "..", Href: treeHref(rp.name, ref, parentDir(pth)) + "/"}) } for _, e := range entries { full := e.name @@ -319,6 +319,14 @@ func treeCrumbs(name, ref, pth string, more ...crumbLink) []display.Crumb { return repoCrumbs(name, append(cs, more...)...) } +// parentDir is path.Dir for tree paths: "" (the root), not ".", for a top-level entry. +func parentDir(pth string) string { + if d := path.Dir(pth); d != "." { + return d + } + return "" +} + func pageTitle(name, ref, pth string) string { t := name if pth != "" { @@ -360,7 +368,7 @@ func (s *server) serveFileView(w http.ResponseWriter, rp *repo, ref, sha, pth st raw := rawHref(rp.name, ref, pth) pd := display.PageData{ Title: pageTitle(rp.name, ref, pth), - Crumbs: treeCrumbs(rp.name, ref, path.Dir(pth), crumbLink{Name: name}), + Crumbs: treeCrumbs(rp.name, ref, parentDir(pth), crumbLink{Name: name}), RawHref: raw, } if isImagePath(pth) { diff --git a/pages_test.go b/pages_test.go new file mode 100644 index 0000000..53e8116 --- /dev/null +++ b/pages_test.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "sort" + "strings" + "testing" + + "github.com/warpfork/go-testmark" +) + +// TestPageSnapshots drives the browse pages through the fixture cases in testdata/pages.md. +// Each case is a pair of hunks: <case>/url (the GET target) and <case>/response +// (status line, the interesting headers, a blank line, then the body). +// Run with -testmark.regen to regenerate the response hunks and review via git diff. +// +// Determinism rests on the hermetic fixture repo (pinned shas), the pinned theme seed, +// and httptest's fixed request host. The fixture shas appear verbatim in the snapshots; +// if they drift, gitfixture_test.go is the place to look. +func TestPageSnapshots(t *testing.T) { + doc, err := testmark.ReadFile("testdata/pages.md") + if err != nil { + t.Fatalf("reading fixtures: %v", err) + } + doc.BuildDirIndex() + s := fixtureServer(t) + var pa testmark.PatchAccumulator + for _, dirent := range doc.DirEnt.ChildrenList { + t.Run(dirent.Name, func(t *testing.T) { + urlEnt := dirent.Children["url"] + resEnt := dirent.Children["response"] + if urlEnt == nil || urlEnt.Hunk == nil || resEnt == nil || resEnt.Hunk == nil { + t.Fatalf("fixture %q needs both a url and a response hunk", dirent.Name) + } + target := strings.TrimSpace(string(urlEnt.Hunk.Body)) + got := describeResponse(s, target) + if *testmark.Regen { + pa.AppendPatchIfBodyDiffers(*resEnt.Hunk, []byte(got)) + } else if got != string(resEnt.Hunk.Body) { + t.Errorf("response mismatch for %s:\n--- got:\n%s--- want:\n%s", target, got, resEnt.Hunk.Body) + } + }) + } + if *testmark.Regen { + if err := pa.WriteFileWithPatches(doc, "testdata/pages.md"); err != nil { + t.Fatal(err) + } + } +} + +// describeResponse renders one GET as the stable text the fixtures assert on: +// the status, every header except the content-length (dull) and date-ish ones, then the body. +func describeResponse(s *server, target string) string { + rec := do(s, target, nil, nil) + var sb strings.Builder + fmt.Fprintf(&sb, "status: %d\n", rec.Code) + names := make([]string, 0, len(rec.Header())) + for name := range rec.Header() { + if name != "Content-Length" { + names = append(names, name) + } + } + sort.Strings(names) + for _, name := range names { + fmt.Fprintf(&sb, "%s: %s\n", name, strings.Join(rec.Header()[name], ", ")) + } + sb.WriteString("\n") + sb.Write(rec.Body.Bytes()) + return sb.String() +} diff --git a/testdata/pages.md b/testdata/pages.md new file mode 100644 index 0000000..6b6412d --- /dev/null +++ b/testdata/pages.md @@ -0,0 +1,357 @@ +pages.md: browse-page snapshot fixtures +======================================= + +HTML snapshots of socle's browse pages, served over the hermetic fixture repo +(see gitfixture_test.go; its pinned shas appear verbatim below). +Each case is a `url` hunk (the GET target) and a `response` hunk +(status, headers minus content-length, blank line, body). +Regenerate with `go test -run TestPageSnapshots . -testmark.regen` and review the diff. + + +index +----- + +The repo index: names, descriptions, newest commit subjects. + +[testmark]:# (index/url) +```text +/ +``` + +[testmark]:# (index/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>socle</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +socle<span class="theme">@test</span> + +</nav> + +<article><h1>repositories</h1> +<table class="log"> +<tbody> +<tr><td><a href="/fixturama/">fixturama</a></td><td>the fixture</td><td class="date">add docs and src</td></tr> +</tbody> +</table> +</article> + +</body> +</html> +``` + + +repo home +--------- + +Clone URL, branches and tags, and the README rendered at HEAD. + +[testmark]:# (repo-home/url) +```text +/fixturama/ +``` + +[testmark]:# (repo-home/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / fixturama + +</nav> + +<article><p>the fixture</p> +<p class="clone">clone: <code>git clone http://socle.example/fixturama</code></p> +<p><a href="/fixturama/tree/main/">files</a> · <a href="/fixturama/log/main">log</a> +· branches: <a href="/fixturama/tree/dev/">dev</a>, <a href="/fixturama/tree/main/">main</a> +· tags: <a href="/fixturama/tree/v1.0/">v1.0</a></p> +<h1 id="fixturama">Fixturama<a href="#fixturama" class="anchor">🔗</a></h1> +<p>A repo that exists to be looked at.</p> +</article> + +</body> +</html> +``` + + +tree listings +------------- + +The root tree, and a subdirectory (note the escaped space in the file link). + +[testmark]:# (tree-root/url) +```text +/fixturama/tree/main/ +``` + +[testmark]:# (tree-root/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama @ main</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / <a href="/fixturama/">fixturama</a> / main + +</nav> + +<article><h1>fixturama</h1> +<table class="tree"> +<tbody> +<tr><td><a href="/fixturama/tree/main/README.md">README.md</a></td><td class="size">49</td></tr> +<tr><td><a href="/fixturama/tree/main/docs/">docs/</a></td><td class="size"></td></tr> +<tr><td><a href="/fixturama/tree/main/src/">src/</a></td><td class="size"></td></tr> +</tbody> +</table> +</article> + +</body> +</html> +``` + +[testmark]:# (tree-docs/url) +```text +/fixturama/tree/main/docs/ +``` + +[testmark]:# (tree-docs/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama/docs @ main</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / <a href="/fixturama/">fixturama</a> / <a href="/fixturama/tree/main/">main</a> / docs + +</nav> + +<article><h1>docs</h1> +<table class="tree"> +<tbody> +<tr><td><a href="/fixturama/tree/main/">..</a></td><td class="size"></td></tr> +<tr><td><a href="/fixturama/tree/main/docs/notes.md">notes.md</a></td><td class="size">17</td></tr> +<tr><td><a href="/fixturama/tree/main/docs/with%20space.txt">with space.txt</a></td><td class="size">25</td></tr> +</tbody> +</table> +</article> + +</body> +</html> +``` + + +file views +---------- + +Markdown renders; plain text shows preformatted. +The sha-addressed variant also demonstrates the immutable cache header. + +[testmark]:# (file-markdown/url) +```text +/fixturama/tree/main/README.md +``` + +[testmark]:# (file-markdown/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama/README.md @ main</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / <a href="/fixturama/">fixturama</a> / <a href="/fixturama/tree/main/">main</a> / README.md +<a class="raw" href="/fixturama/raw/main/README.md">raw</a> +</nav> + +<article><h1>README.md</h1> +<h1 id="fixturama">Fixturama<a href="#fixturama" class="anchor">🔗</a></h1> +<p>A repo that exists to be looked at.</p> +</article> + +</body> +</html> +``` + +[testmark]:# (file-text/url) +```text +/fixturama/tree/main/docs/with%20space.txt +``` + +[testmark]:# (file-text/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama/docs/with space.txt @ main</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / <a href="/fixturama/">fixturama</a> / <a href="/fixturama/tree/main/">main</a> / <a href="/fixturama/tree/main/docs/">docs</a> / with space.txt +<a class="raw" href="/fixturama/raw/main/docs/with%20space.txt">raw</a> +</nav> + +<article><h1>with space.txt</h1> +<pre class="file">even this filename works +</pre></article> + +</body> +</html> +``` + +[testmark]:# (file-at-sha/url) +```text +/fixturama/tree/9db23702e9ae3e8317b5dba562b0f3bd1cb25e7f/README.md +``` + +[testmark]:# (file-at-sha/response) +```text +status: 200 +Cache-Control: public, max-age=31536000, immutable +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama/README.md @ 9db23702e9</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / <a href="/fixturama/">fixturama</a> / <a href="/fixturama/tree/9db23702e9ae3e8317b5dba562b0f3bd1cb25e7f/">9db23702e9ae3e8317b5dba562b0f3bd1cb25e7f</a> / README.md +<a class="raw" href="/fixturama/raw/9db23702e9ae3e8317b5dba562b0f3bd1cb25e7f/README.md">raw</a> +</nav> + +<article><h1>README.md</h1> +<h1 id="fixturama">Fixturama<a href="#fixturama" class="anchor">🔗</a></h1> +<p>A repo that exists to be looked at.</p> +</article> + +</body> +</html> +``` + + +log +--- + +[testmark]:# (log-main/url) +```text +/fixturama/log/main +``` + +[testmark]:# (log-main/response) +```text +status: 200 +Content-Type: text/html; charset=utf-8 + +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>fixturama log @ main</title> +<link rel="stylesheet" href="/-/style.css"> +</head> +<body> +<nav> +<a href="/">socle</a><span class="theme">@test</span> / <a href="/fixturama/">fixturama</a> / log / main + +</nav> + +<article><h1>log: main</h1> +<table class="log"> +<thead><tr><th>commit</th><th>date</th><th>author</th><th>subject</th></tr></thead> +<tbody> +<tr><td class="hash"><a href="/fixturama/tree/9db23702e9ae3e8317b5dba562b0f3bd1cb25e7f/">9db23702e9</a></td><td class="date">2026-01-02</td><td>Ada Fixture</td><td>add docs and src</td></tr> +<tr><td class="hash"><a href="/fixturama/tree/64ac67a35d36079e167cb8f929fd57f859c8b5d1/">64ac67a35d</a></td><td class="date">2026-01-02</td><td>Ada Fixture</td><td>inaugurate with a readme</td></tr> +</tbody> +</table> +</article> + +</body> +</html> +``` + + +error pages +----------- + +A malformed ref is the client's fault; an unknown repo simply isn't there. + +[testmark]:# (err-bad-ref/url) +```text +/fixturama/tree/main..dev/ +``` + +[testmark]:# (err-bad-ref/response) +```text +status: 400 +Content-Type: text/plain; charset=utf-8 +X-Content-Type-Options: nosniff + +invalid: ref "main..dev" contains ".." +``` + +[testmark]:# (err-unknown-repo/url) +```text +/nosuch/ +``` + +[testmark]:# (err-unknown-repo/response) +```text +status: 404 +Content-Type: text/plain; charset=utf-8 +X-Content-Type-Options: nosniff + +404 page not found +```