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 check
4
5import (
6 "context"
7 "encoding/json"
8 "testing"
9 "time"
10
11 "github.com/chromedp/cdproto/emulation"
12 "github.com/chromedp/cdproto/input"
13 "github.com/chromedp/chromedp"
14 "github.com/chromedp/chromedp/kb"
15)
16
17// selection reads the colour box's value, its selection and how many
18// rows its list shows.
19const selectionJS = `(() => { const i = document.getElementById("colour-combo"), l = document.getElementById(i.getAttribute("aria-controls"));
20 return JSON.stringify({Value: i.value, Start: i.selectionStart, End: i.selectionEnd, Focused: document.activeElement === i,
21 Rows: l ? [...l.querySelectorAll("[role=option]")].filter(o => o.checkVisibility()).length : -1}); })()`
22
23type comboSelection struct {
24 Value string
25 Start, End, Rows int
26 Focused bool
27}
28
29// TestSelectFocusSelectsThePick: focusing a combobox that shows its pick
30// selects the whole pick, so typing or Delete replaces it, by a click as
31// much as by Tab. A click put the caret where it landed, and clearing
32// "Red" took a double-click or a run of backspaces first. Only the
33// focusing click does it: a second click in the focused box places the
34// caret, so a partial edit still works, and a drag across the pick as it
35// is focused keeps what the drag selected. Real pointer and key events,
36// because what is fenced is the order the browser runs mousedown, focus,
37// mouseup and click in, which a scripted focus() never goes through. In
38// both directions of text.
39func TestSelectFocusSelectsThePick(t *testing.T) {
40 for _, dir := range []string{"ltr", "rtl"} {
41 t.Run(dir, func(t *testing.T) {
42 rig := sizingRig(t, false, selectFilterPages(t))
43 ctx, cancel := context.WithTimeout(rig.Context(), 60*time.Second)
44 defer cancel()
45 mustRun(t, ctx, emulation.SetFocusEmulationEnabled(true), chromedp.EmulateViewport(1000, 900), chromedp.Navigate(rig.Origin+"/"),
46 chromedp.WaitVisible(`#colour-combo`, chromedp.ByQuery),
47 chromedp.Evaluate(`document.documentElement.dir = "`+dir+`"; document.getElementById("colour-combo").scrollIntoView({block: "center"}); true`, nil))
48 var box struct{ L, R, Y, PadL, PadR, Gr float64 }
49 at(t, ctx, `(() => { const i = document.getElementById("colour-combo"), r = i.getBoundingClientRect(), s = getComputedStyle(i);
50 return JSON.stringify({L: r.left, R: r.right, Y: r.top + r.height / 2, PadL: parseFloat(s.paddingLeft) + parseFloat(s.borderLeftWidth), PadR: parseFloat(s.paddingRight) + parseFloat(s.borderRightWidth),
51 Gr: Object.assign(document.createElement("canvas").getContext("2d"), {font: s.font}).measureText("Gr").width}); })()`, &box)
52 // After a frame: the focusing click selects in the next one.
53 read := func() comboSelection {
54 var raw string
55 mustRun(t, ctx, chromedp.Evaluate(afterFrame, nil, awaitPromise), chromedp.Evaluate(selectionJS, &raw))
56 var s comboSelection
57 if err := json.Unmarshal([]byte(raw), &s); err != nil {
58 t.Fatal(err)
59 }
60 return s
61 }
62 // Inside the box, clear of the lead and the chevron: a third
63 // of the way in from either end.
64 x1, x2 := box.L+(box.R-box.L)/3, box.L+2*(box.R-box.L)/3
65 blur := func() {
66 mustRun(t, ctx, chromedp.Evaluate(`document.activeElement.blur(); document.querySelectorAll("[rst-combo] [role=listbox]").forEach(l => l.hidden = true); true`, nil))
67 }
68
69 mustRun(t, ctx, chromedp.MouseClickXY(x1, box.Y))
70 if s := read(); !s.Focused || s.Value != "Red" || s.Start != 0 || s.End != len(s.Value) {
71 t.Errorf("a click into the box showing %q left the selection at %d–%d (focused %v); want the whole pick, 0–%d", s.Value, s.Start, s.End, s.Focused, len(s.Value))
72 }
73 mustRun(t, ctx, chromedp.MouseClickXY(x2, box.Y))
74 if s := read(); s.Start != s.End {
75 t.Errorf("a second click in the focused box left %d–%d selected; it should place the caret", s.Start, s.End)
76 }
77
78 blur()
79 mustRun(t, ctx, chromedp.Focus(`#dept-combo`, chromedp.ByQuery), chromedp.KeyEvent(kb.Tab))
80 if s := read(); !s.Focused || s.Start != 0 || s.End != len(s.Value) {
81 t.Errorf("Tab into the box left %d–%d selected (focused %v); want the whole pick", s.Start, s.End, s.Focused)
82 }
83 mustRun(t, ctx, chromedp.KeyEvent("gr"))
84 if s := read(); s.Value != "gr" || s.Rows != 1 {
85 t.Errorf("typing over the selected pick left %q with %d rows showing; want \"gr\", filtered to Green", s.Value, s.Rows)
86 }
87
88 // A drag across "Green" as it focuses the box: from just past
89 // its "Gr" to well past its end, which selects "een".
90 // Whatever the drag selected stays.
91 blur()
92 mustRun(t, ctx, chromedp.Evaluate(`document.getElementById("colour").value = "green"; document.getElementById("colour").dispatchEvent(new Event("change")); true`, nil))
93 from, to := box.L+box.PadL+box.Gr+1, x2
94 if dir == "rtl" {
95 from, to = box.R-box.PadR-box.Gr-1, x1
96 }
97 mustRun(t, ctx, chromedp.ActionFunc(func(ctx context.Context) error {
98 if err := input.DispatchMouseEvent(input.MousePressed, from, box.Y).WithButton(input.Left).WithClickCount(1).Do(ctx); err != nil {
99 return err
100 }
101 if err := input.DispatchMouseEvent(input.MouseMoved, to, box.Y).WithButton(input.Left).Do(ctx); err != nil {
102 return err
103 }
104 return input.DispatchMouseEvent(input.MouseReleased, to, box.Y).WithButton(input.Left).WithClickCount(1).Do(ctx)
105 }))
106 if s := read(); !s.Focused || s.Start == 0 && s.End == len(s.Value) {
107 t.Errorf("a drag as the box was focused ended with %d–%d of %q selected (focused %v); the drag's own selection should stay", s.Start, s.End, s.Value, s.Focused)
108 }
109
110 // A tap selects the pick too: a tap selects on focus and a
111 // mouse on its click, so each path is read. Not a second tap:
112 // with the protocol's raw touch events Chromium keeps a
113 // selection under any later tap, with or without this file's
114 // handlers, so it would fence the emulation, not select.js.
115 blur()
116 tap := func(x float64) {
117 mustRun(t, ctx, chromedp.ActionFunc(func(ctx context.Context) error {
118 pt := []*input.TouchPoint{{X: x, Y: box.Y}}
119 if err := input.DispatchTouchEvent(input.TouchStart, pt).Do(ctx); err != nil {
120 return err
121 }
122 return input.DispatchTouchEvent(input.TouchEnd, []*input.TouchPoint{}).Do(ctx)
123 }), chromedp.Evaluate(afterFrame, nil, awaitPromise))
124 }
125 mustRun(t, ctx, emulation.SetTouchEmulationEnabled(true))
126 tap(x1)
127 if s := read(); !s.Focused || s.Start != 0 || s.End != len(s.Value) {
128 t.Errorf("a tap into the box showing %q left %d–%d selected (focused %v); want the whole pick", s.Value, s.Start, s.End, s.Focused)
129 }
130 })
131 }
132}
133