Artifact d113e8bdb4851c19c93fdfff8298eae7d32b4e3a69755004f6cf2d10d0e6d5b8:
- File
internal/web/timeline_test.go
— part of check-in
[6b6121e609]
at
2026-08-22 20:48:43
on branch trunk
— timeline: the mainline always holds the leftmost rail
A branch that committed most recently was taking rail 0, and trunk was inheriting a branch's column at the fork point because the branch line followed its parents there. The default branch (timeline.list now names it) reserves rail 0 whenever it is on the page; other lines converge into it. Absent from view, nothing is reserved and the rest close up. (user: dmestas, size: 4298) [annotate] [blame] [check-ins using] [more...]
package web import ( "strconv" "strings" "testing" ) // A branch tip is drawn as the quarry mark is: an open ring with the // check-in dot at its centre. The ring alone only said "nothing follows // this"; the dot says "and this is where the branch is now", which is // the fact a reader is actually scanning the graph for. // // Every other check-in stays a solid node — if the two ever render the // same, the graph stops answering "where is each branch now?" and the // only way to find a tip is to read the branch list instead. func TestBranchTipsCarryTheCentreDot(t *testing.T) { days := buildTimeline("r", "trunk", []timelineEntry{ {Kind: "checkin", Hash: "aaa", Date: "2026-08-18 10:00:00", Branch: "trunk", IsLeaf: true, Parents: []string{"bbb"}}, {Kind: "checkin", Hash: "bbb", Date: "2026-08-18 09:00:00", Branch: "trunk"}, }) var tip, mid string for _, d := range days { for _, row := range d.Rows { switch row.Hash { case "aaa": tip = string(row.Head) case "bbb": mid = string(row.Head) } } } if tip == "" || mid == "" { t.Fatalf("timeline dropped a row: tip=%q mid=%q", tip, mid) } if !strings.Contains(tip, `r="4"`) || !strings.Contains(tip, `fill:var(--bg)`) { t.Errorf("the tip is not an open ring:\n%s", tip) } if !strings.Contains(tip, `r="1.6"`) { t.Errorf("the tip has no centre dot, so it reads as a dead end:\n%s", tip) } // The ring must stay hollow: a filled ring plus a dot is just a // slightly larger solid node and tells the reader nothing. if strings.Count(tip, "<circle") != 2 { t.Errorf("a tip should be exactly ring + dot, got:\n%s", tip) } if strings.Contains(mid, `r="1.6"`) || strings.Contains(mid, `fill:var(--bg)`) { t.Errorf("an ordinary check-in is being drawn as a tip:\n%s", mid) } if strings.Count(mid, "<circle") != 1 { t.Errorf("an ordinary check-in should be one solid node, got:\n%s", mid) } } // railOf reports which rail column a row's node was drawn on, by // reading the node dot's x back out of the head SVG. func railOf(t *testing.T, row tlRow) int { t.Helper() head := string(row.Head) i := strings.Index(head, `<circle cx="`) if i < 0 { t.Fatalf("row %q drew no node", row.Title) } rest := head[i+len(`<circle cx="`):] x, err := strconv.Atoi(rest[:strings.Index(rest, `"`)]) if err != nil { t.Fatal(err) } return (x - railW/2) / railW } // The mainline owns the leftmost rail whenever it is on the page, even // when a branch holds the newest commit — otherwise a branch that // forked yesterday takes the column a reader scans for trunk. func TestMainlineKeepsTheLeftmostRail(t *testing.T) { // Newest first: the branch commits, then trunk's. days := buildTimeline("r", "trunk", []timelineEntry{ {Kind: "ci", Hash: "b2", Date: "2026-08-22T16:50:00", Branch: "ticket-list", Parents: []string{"b1"}, Comment: "branch tip"}, {Kind: "ci", Hash: "b1", Date: "2026-08-22T16:46:00", Branch: "ticket-list", Parents: []string{"t1"}, Comment: "branch base"}, {Kind: "ci", Hash: "t1", Date: "2026-08-22T16:35:00", Branch: "trunk", Parents: []string{"t0"}, Comment: "trunk work"}, {Kind: "ci", Hash: "t0", Date: "2026-08-22T16:30:00", Branch: "trunk", Comment: "trunk root"}, }) var rows []tlRow for _, d := range days { rows = append(rows, d.Rows...) } if len(rows) != 4 { t.Fatalf("rows = %d", len(rows)) } for _, row := range rows { got := railOf(t, row) if row.Branch == "trunk" && got != 0 { t.Errorf("trunk commit %q drew on rail %d, want 0", row.Title, got) } if row.Branch != "trunk" && got == 0 { t.Errorf("branch commit %q took the mainline's rail", row.Title) } } } // With the mainline absent from the page entirely, nothing is reserved // and the visible lines close up to the left edge. func TestWithoutMainlineTheLinesCloseUp(t *testing.T) { days := buildTimeline("r", "trunk", []timelineEntry{ {Kind: "ci", Hash: "b2", Date: "2026-08-22T16:50:00", Branch: "ticket-list", Parents: []string{"b1"}, Comment: "tip"}, {Kind: "ci", Hash: "b1", Date: "2026-08-22T16:46:00", Branch: "ticket-list", Comment: "base"}, }) for _, d := range days { for _, row := range d.Rows { if got := railOf(t, row); got != 0 { t.Errorf("with no mainline in view, %q drew on rail %d, want 0", row.Title, got) } } } }