rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1//go:build browser
2
3package gallery
4
5import (
6 "context"
7 "fmt"
8 "math"
9 "net/http"
10 "testing"
11 "time"
12
13 "github.com/chromedp/chromedp"
14
15 "amadan.net/rastrillo/fiesta/internal/harness"
16)
17
18// realSize reads every showing frame's transform, its laid-out and
19// rendered widths, a text field's computed font size where the
20// document has one, and the box's inline edges against the document's.
21// The field is the first plain one: the headline field is sized on
22// purpose at --rst-fs-lg.
23const realSize = `JSON.stringify([...document.querySelectorAll(".ds-view")].filter(v => getComputedStyle(v.querySelector(".ds-view__stage")).display !== "none").map(v => {
24 const box = v.querySelector(".ds-view__box"), f = v.querySelector(".ds-view__frame"), b = box.getBoundingClientRect(), fr = f.getBoundingClientRect();
25 const lit = [...v.querySelectorAll(".ds-view__tab")].findIndex(l => getComputedStyle(l).fontWeight === "600");
26 let field = null;
27 try { const i = f.contentDocument.querySelector(':is(input:not([type]), input[type=text], input[type=email], input[type=url]):not(.rst-input--primary, [rst-input~="primary"])'); if (i) field = parseFloat(f.contentDocument.defaultView.getComputedStyle(i).fontSize); } catch (e) {}
28 return {ID: (v.closest("article, section") || {}).id || "", Lit: lit, Transform: getComputedStyle(f).transform,
29 Layout: f.offsetWidth, Rendered: fr.width, Field: field, BoxL: b.left, BoxR: b.right, DocW: document.documentElement.clientWidth};
30}))`
31
32type realReading struct {
33 ID string
34 Lit int
35 Transform string
36 Layout, Rendered, BoxL, BoxR, DocW float64
37 Field *float64
38}
39
40func readReal(t *testing.T, ctx context.Context, where string) []realReading {
41 t.Helper()
42 var rs []realReading
43 at(t, ctx, realSize, &rs)
44 if len(rs) == 0 {
45 t.Fatalf("%s: no frame showing", where)
46 }
47 return rs
48}
49
50// TestMobileIsThePageAtItsRealSize: on a phone, Mobile is the page at
51// its real size: the frame is laid out at the stage's width up to
52// 390px and never scaled, a 16px field is 16px, and the box reaches
53// the screen's edges. On a coarse pointer at 390px and 320px and a
54// fine one at 390px, in English and Arabic, on a component page and a
55// page-class one. The control: on a desktop the Mobile rendering is the
56// 390px phone, centred in its stage.
57func TestMobileIsThePageAtItsRealSize(t *testing.T) {
58 coarse := harness.New(t, func(string) http.Handler { return treeHandler(t) }, harness.WithCoarsePointer(), harness.WithScrollbars())
59 cctx, ccancel := context.WithTimeout(coarse.Context(), 240*time.Second)
60 defer ccancel()
61 fine := harness.New(t, func(string) http.Handler { return treeHandler(t) }, harness.WithScrollbars())
62 fctx, fcancel := context.WithTimeout(fine.Context(), 240*time.Second)
63 defer fcancel()
64 for _, rig := range []struct {
65 name string
66 ctx context.Context
67 rig *harness.Rig
68 widths []int64
69 }{{"coarse", cctx, coarse, []int64{390, 320}}, {"fine", fctx, fine, []int64{390}}} {
70 for _, file := range []string{"field.html", "layout-column.html"} {
71 for _, locale := range []string{"en", "ar"} {
72 for _, w := range rig.widths {
73 where := fmt.Sprintf("day/%s %s at %dpx, %s pointer", locale, file, w, rig.name)
74 openPage(t, rig.ctx, where, pageURL(rig.rig, locale, file), w, 844)
75 if rig.name == "coarse" {
76 requireCoarse(t, rig.ctx)
77 }
78 eagerly(t, rig.ctx, where)
79 requireDrawnScrollbar(t, rig.ctx, where)
80 var sideways float64
81 mustRun(t, rig.ctx, chromedp.Evaluate(`document.documentElement.scrollWidth - document.documentElement.clientWidth`, &sideways))
82 if sideways > 0.5 {
83 t.Errorf("%s: the page scrolls %.1fpx sideways", where, sideways)
84 }
85 fields := 0
86 for _, r := range readReal(t, rig.ctx, where) {
87 if r.Lit != 1 {
88 t.Fatalf("%s: %s opens with tab %d lit; on a phone the widget opens on Mobile, and the readings below assume it", where, r.ID, r.Lit)
89 }
90 if r.Transform != "matrix(1, 0, 0, 1, 0, 0)" && r.Transform != "none" {
91 t.Errorf("%s: %s is transformed %s; Mobile is never scaled", where, r.ID, r.Transform)
92 }
93 if math.Abs(r.Layout-r.Rendered) > 0.5 || r.Layout > 390 {
94 t.Errorf("%s: %s lays out at %.1fpx and renders at %.1fpx; want one width, at most 390px", where, r.ID, r.Layout, r.Rendered)
95 }
96 if math.Abs(r.BoxL) > 1 || math.Abs(r.BoxR-r.DocW) > 1 {
97 t.Errorf("%s: %s's box runs %.1f…%.1f in a %.0fpx document; on a phone the stage is the screen's width", where, r.ID, r.BoxL, r.BoxR, r.DocW)
98 }
99 if r.Field != nil {
100 fields++
101 if rendered := *r.Field * r.Rendered / r.Layout; math.Abs(*r.Field-16) > 0.1 || math.Abs(rendered-16) > 0.1 {
102 t.Errorf("%s: %s's text field computes %.2fpx and renders %.2fpx; a phone's field is 16px", where, r.ID, *r.Field, rendered)
103 }
104 }
105 }
106 if file == "field.html" && fields == 0 {
107 t.Errorf("%s: no text field measured on the field page; the 16px claim was checked against nothing", where)
108 }
109 }
110 }
111 }
112 }
113 where := "day/en field.html at 1280px, Mobile chosen"
114 openPage(t, fctx, where, pageURL(fine, "en", "field.html"), 1280, 900)
115 eagerly(t, fctx, where)
116 clickAll(t, fctx, where, clickedMobile, "Mobile")
117 const want = 3
118 shown := readReal(t, fctx, where)
119 if len(shown) < want {
120 t.Fatalf("%s: %d frames showing, want at least %d to check", where, len(shown), want)
121 }
122 for _, r := range shown[:want] {
123 if r.Layout != 390 {
124 t.Errorf("%s: %s lays out at %.1fpx, want 390", where, r.ID, r.Layout)
125 }
126 }
127}
128
129// TestDesktopPansFromTheInlineStart: Desktop on a phone pans, and from
130// the page's start in either direction: in Arabic the frame is
131// anchored at the box's right edge and the box scrolls left.
132func TestDesktopPansFromTheInlineStart(t *testing.T) {
133 rig := harness.New(t, func(string) http.Handler { return treeHandler(t) })
134 ctx, cancel := context.WithTimeout(rig.Context(), 180*time.Second)
135 defer cancel()
136 for _, c := range []struct {
137 file string
138 width float64
139 }{{"field.html", 900}, {"layout-column.html", 1200}} {
140 for _, locale := range []string{"ar", "en"} {
141 where := fmt.Sprintf("day/%s %s at 390px, Desktop chosen", locale, c.file)
142 openPage(t, ctx, where, pageURL(rig, locale, c.file), 390, 844)
143 eagerly(t, ctx, where)
144 clickAll(t, ctx, where, clickedDesktop, "Desktop")
145 var rs []struct {
146 ID string
147 Checked bool
148 Layout float64
149 Pans bool
150 Start, End float64
151 }
152 at(t, ctx, `(() => {
153 const rtl = document.documentElement.dir === "rtl", out = [];
154 for (const v of document.querySelectorAll(".ds-view")) {
155 const box = v.querySelector(".ds-view__box"), f = v.querySelector(".ds-view__frame");
156 const checked = v.querySelector(".ds-view__tab--d input").checked;
157 const startEdge = () => { const b = box.getBoundingClientRect(), r = f.getBoundingClientRect(); return rtl ? r.right - b.right : r.left - b.left; };
158 const endEdge = () => { const b = box.getBoundingClientRect(), r = f.getBoundingClientRect(); return rtl ? b.left - r.left : r.right - b.right; };
159 const atStart = startEdge();
160 box.scrollLeft = rtl ? -(box.scrollWidth - box.clientWidth) : box.scrollWidth - box.clientWidth;
161 const atEnd = endEdge();
162 box.scrollLeft = 0;
163 out.push({ID: (v.closest("article, section") || {}).id || "", Checked: checked, Layout: f.offsetWidth,
164 Pans: box.scrollWidth > box.clientWidth, Start: atStart, End: atEnd});
165 }
166 return JSON.stringify(out);
167 })()`, &rs)
168 for _, r := range rs {
169 if !r.Checked || r.Layout != c.width {
170 t.Fatalf("%s: %s has Desktop checked %v and lays out at %.0fpx, want %.0fpx", where, r.ID, r.Checked, r.Layout, c.width)
171 }
172 }
173 for _, r := range rs {
174 if !r.Pans {
175 t.Errorf("%s: %s's box does not scroll", where, r.ID)
176 }
177 if math.Abs(r.Start) > 1 {
178 t.Errorf("%s: at rest %s's frame starts %.1fpx from its box's inline start", where, r.ID, r.Start)
179 }
180 if math.Abs(r.End) > 1 {
181 t.Errorf("%s: scrolled to the end, %s's frame ends %.1fpx from its box's inline end", where, r.ID, r.End)
182 }
183 }
184 }
185 }
186}
187