| 1 | //go:build browser |
| 2 | |
| 3 | // The block components' spacing and baselines, from rastrillo's |
| 4 | // ui/blocks_browser_test.go at the seed commit, each page read |
| 5 | // through fixture. |
| 6 | package check |
| 7 | |
| 8 | import ( |
| 9 | "context" |
| 10 | "testing" |
| 11 | "time" |
| 12 | |
| 13 | "github.com/chromedp/chromedp" |
| 14 | ) |
| 15 | |
| 16 | // segTabsPage is seg-tabs over a list card, as a list screen stacks |
| 17 | // them, and over a box, whose own top margin must not double the gap. |
| 18 | func segTabsPage(t *testing.T) map[string]string { |
| 19 | t.Helper() |
| 20 | return map[string]string{"/": sizingDoc("seg-tabs", fixture(t, "blocks_browser_test.go", "segTabsPage", ""))} |
| 21 | } |
| 22 | |
| 23 | // TestSegTabsKeepAGapBelowThem: every block component leaves |
| 24 | // var(--rst-sp-4) under itself, and seg-tabs left nothing, so on a |
| 25 | // list screen the tabs sat flush on the card below them, the two |
| 26 | // reading as one control. |
| 27 | func TestSegTabsKeepAGapBelowThem(t *testing.T) { |
| 28 | for _, leg := range []struct { |
| 29 | name string |
| 30 | w int64 |
| 31 | coarse bool |
| 32 | }{{"1280 mouse", 1280, false}, {"390 touch", 390, true}} { |
| 33 | t.Run(leg.name, func(t *testing.T) { |
| 34 | rig := sizingRig(t, leg.coarse, segTabsPage(t)) |
| 35 | ctx, cancel := context.WithTimeout(rig.Context(), 60*time.Second) |
| 36 | defer cancel() |
| 37 | mustRun(t, ctx, chromedp.EmulateViewport(leg.w, 844), chromedp.Navigate(rig.Origin+"/"), chromedp.WaitVisible("#box", chromedp.ByQuery)) |
| 38 | requirePointer(t, ctx, leg.coarse) |
| 39 | var g struct{ Want, Card, Box float64 } |
| 40 | at(t, ctx, `(() => { const gap = id => document.getElementById(id).getBoundingClientRect().top - document.querySelector("#" + id + "-tabs [rst-seg-tabs]").getBoundingClientRect().bottom; |
| 41 | return JSON.stringify({Want: parseFloat(getComputedStyle(document.getElementById("box")).marginTop), Card: gap("card"), Box: gap("box")}); })()`, &g) |
| 42 | if g.Want <= 0 { |
| 43 | t.Fatalf("the box's top margin reads %.1fpx; there is no block spacing to compare with and this leg proves nothing", g.Want) |
| 44 | } |
| 45 | if d := g.Card - g.Want; d < -0.5 || d > 0.5 { |
| 46 | t.Errorf("the gap between the tabs and the card under them is %.1fpx, want the block spacing, %.1fpx", g.Card, g.Want) |
| 47 | } |
| 48 | if d := g.Box - g.Want; d < -0.5 || d > 0.5 { |
| 49 | t.Errorf("the gap between the tabs and the box under them is %.1fpx, want the block spacing, %.1fpx, once", g.Box, g.Want) |
| 50 | } |
| 51 | }) |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // TestADetailListsLabelSitsOnItsValuesFirstLine: a label is smaller |
| 56 | // than its value, and the grid stretched both to the row and set each |
| 57 | // at its own top, so every label rode a pixel or two above the line it |
| 58 | // names. A label must share its value's first baseline, and stay on the |
| 59 | // first line when the value wraps rather than drift to the middle. |
| 60 | func TestADetailListsLabelSitsOnItsValuesFirstLine(t *testing.T) { |
| 61 | dl := fixture(t, "blocks_browser_test.go", "TestADetailListsLabelSitsOnItsValuesFirstLine", "dl") |
| 62 | pages := map[string]string{"/": sizingDoc("detail-list", `<div rst-page><section rst-box id="box">`+dl+`</section></div>`)} |
| 63 | for _, leg := range []struct { |
| 64 | name string |
| 65 | w int64 |
| 66 | coarse bool |
| 67 | }{{"1280 mouse", 1280, false}, {"390 touch", 390, true}} { |
| 68 | t.Run(leg.name, func(t *testing.T) { |
| 69 | rig := sizingRig(t, leg.coarse, pages) |
| 70 | ctx, cancel := context.WithTimeout(rig.Context(), 60*time.Second) |
| 71 | defer cancel() |
| 72 | mustRun(t, ctx, chromedp.EmulateViewport(leg.w, 844), chromedp.Navigate(rig.Origin+"/"), chromedp.WaitVisible("#box", chromedp.ByQuery)) |
| 73 | requirePointer(t, ctx, leg.coarse) |
| 74 | // A zero-size inline-block's bottom edge is the baseline of the |
| 75 | // line it sits on; put first in the element, it marks the first. |
| 76 | var rows []struct { |
| 77 | Label string |
| 78 | LabelLine, ValueLine float64 |
| 79 | LabelSize, ValueSize, VH float64 |
| 80 | } |
| 81 | at(t, ctx, `(() => { const line = el => { const m = document.createElement("span"); m.style.cssText = "display: inline-block; inline-size: 0; block-size: 0"; el.prepend(m); const y = m.getBoundingClientRect().bottom; m.remove(); return y; }; |
| 82 | return JSON.stringify([...document.querySelectorAll("[rst-detail] dt")].map(dt => { const dd = dt.nextElementSibling; |
| 83 | return {Label: dt.textContent, LabelLine: line(dt), ValueLine: line(dd), LabelSize: parseFloat(getComputedStyle(dt).fontSize), ValueSize: parseFloat(getComputedStyle(dd).fontSize), VH: dd.getBoundingClientRect().height / parseFloat(getComputedStyle(dd).lineHeight)}; })); })()`, &rows) |
| 84 | if len(rows) != 4 { |
| 85 | t.Fatalf("measured %d rows, want 4", len(rows)) |
| 86 | } |
| 87 | if rows[0].LabelSize >= rows[0].ValueSize { |
| 88 | t.Fatalf("the label is %.1fpx and its value %.1fpx; with no difference in size the tops line up anyway and this leg proves nothing", rows[0].LabelSize, rows[0].ValueSize) |
| 89 | } |
| 90 | if rows[3].VH < 1.9 { |
| 91 | t.Fatalf("the summary is %.1f lines tall; it must wrap for the multi-line leg to prove anything", rows[3].VH) |
| 92 | } |
| 93 | for _, r := range rows { |
| 94 | if d := r.LabelLine - r.ValueLine; d < -0.5 || d > 0.5 { |
| 95 | t.Errorf("%s: the label's baseline is at %.1f and its value's first is at %.1f, %.1fpx apart", r.Label, r.LabelLine, r.ValueLine, d) |
| 96 | } |
| 97 | } |
| 98 | }) |
| 99 | } |
| 100 | } |
| 101 | |