Artifact edc649991374c2e72243f4d9956058995d666bb6145db219d6923b26ecd58c23:
- File
internal/web/syncurl_test.go
— part of check-in
[c2775268a4]
at
2026-08-27 10:54:06
on branch url-sync-doctabs
— Doc tabs: keep the URL on the tab you are reading
Switching README to LICENSE swapped the panel and left the address bar behind, so the tab on screen was unlinkable and a reload dropped you back on README — even though /r/REPO?doc=LICENSE has always been a real route, and the anchor was already carrying it as its href.
Moves the tabs onto syncurl, which corrects the address bar to that same href before fetching the fragment. (user: dmestas, size: 3140) [annotate] [blame] [check-ins using] [more...]
package web import ( "encoding/json" "strings" "testing" ) // The handler has three moving parts that all matter, and none of them // are visible in a rendered page: the address-bar correction reads the // link's OWN href, it is wrapped so a throw cannot cancel the fetch, and // the fetch still happens. Pin the shape, not the prose. func TestSyncURLAttrShape(t *testing.T) { got := string(syncURLAttr("/r/x/doc.frag?doc=README.md")) for _, want := range []string{ `data-on:click__prevent="`, `history.replaceState(null,''`, `evt.currentTarget.getAttribute('href')`, `try{`, `}catch(e){}`, `@get('/r/x/doc.frag?doc=README.md')`, } { if !strings.Contains(got, want) { t.Fatalf("attribute missing %q:\n%s", want, got) } } // replaceState, not pushState: a view toggle must not make the back // button walk every view the reader tried. if strings.Contains(got, "pushState") { t.Fatalf("view toggles must replace, not push:\n%s", got) } // The catch has to sit around replaceState and NOT around @get, or a // cosmetic failure takes the fetch down with it. if strings.Index(got, "}catch(e){}") > strings.Index(got, "@get(") { t.Fatalf("@get must sit outside the catch, after it:\n%s", got) } } // The fragment lands inside a JS string inside an HTML attribute with // escaping switched off. Nothing in it may close either quote. func TestSyncURLAttrNeutralizesQuotes(t *testing.T) { got := string(syncURLAttr(`/x?a='+alert(1)+'&b="&c=\`)) if strings.Contains(got, `'+alert(1)+'`) { t.Fatalf("single quote survived into the JS string:\n%s", got) } if strings.Contains(got, `b="`) { t.Fatalf("double quote survived into the attribute:\n%s", got) } for _, want := range []string{"%27", "%22", "%5C"} { if !strings.Contains(got, want) { t.Fatalf("expected %s escape in:\n%s", want, got) } } } // The README/LICENSE tabs are the surface this was built for: their // href is already the real, cold-loadable route, so the address bar must // follow it. Render the panel and check the anchor carries both halves. func TestDocTabsSyncTheURL(t *testing.T) { // Built the way the handler builds it — from the op's JSON — so the // test cannot drift from the shape the page actually renders. var about aboutData if err := json.Unmarshal([]byte(`{"docPath":"README.md","docs":[ {"path":"README.md","label":"README"}, {"path":"LICENSE","label":"License"}]}`), &about); err != nil { t.Fatal(err) } data := pageData{Repo: "demo", About: &about} var buf strings.Builder if err := treePage.ExecuteTemplate(&buf, "doc-panel", data); err != nil { t.Fatal(err) } got := buf.String() // The no-script path: a plain load of the same view. if !strings.Contains(got, `href="/r/demo?doc=LICENSE"`) { t.Fatalf("license tab lost its real href:\n%s", got) } // The scripted path: fragment fetch AND address-bar correction. if !strings.Contains(got, `@get('/r/demo/doc.frag?doc=LICENSE')`) { t.Fatalf("license tab lost its fragment fetch:\n%s", got) } if !strings.Contains(got, `history.replaceState`) { t.Fatalf("doc tabs swap content without correcting the URL:\n%s", got) } }