//go:build browser package gallery import ( "context" "fmt" "testing" "time" "github.com/chromedp/cdproto/runtime" "github.com/chromedp/chromedp" "github.com/chromedp/chromedp/kb" "amadan.net/rastrillo/fiesta" "amadan.net/rastrillo/fiesta/internal/harness" ) // TestEveryLanguageIsReachableFromThePhoneIndex: every language in the // phone index's display settings menu can be reached by Tab, with // scripts, and by scrolling the way a finger would, with scripts and // without, at 390px and 320px, left to right and right to left, with // anchor positioning and without. The menu is at the top of the index // and drops down, and the language list opens in place inside its // card, so the long list is the card's to scroll. Then every link is // followed once, since reaching a link is not using it. // // "On screen" is inside documentElement's client box, on a rig that // draws its scrollbars: see requireDrawnScrollbar. func TestEveryLanguageIsReachableFromThePhoneIndex(t *testing.T) { rig := phoneRig(t, harness.WithScrollbars()) ctx, cancel := context.WithTimeout(rig.Context(), 300*time.Second) defer cancel() theme := fiesta.ThemeNames()[0] open := `document.getElementById("ds-prefs").open = true; document.querySelector("#ds-prefs [rst-locale]").open = true; true` for _, scripts := range []bool{true, false} { tab := ctx if !scripts { tab = noScripts(t, ctx) } for _, w := range []int64{390, 320} { for _, locale := range []string{"en", "ar"} { for _, anchored := range []bool{true, false} { where := fmt.Sprintf("%s/%s index at %dpx, scripts %v, anchor positioning %v", theme, locale, w, scripts, anchored) mustRun(t, tab, chromedp.EmulateViewport(w, 700), chromedp.Navigate(rig.Origin+pageAt(theme, locale, "index.html")), chromedp.WaitVisible(`#ds-prefs`, chromedp.ByQuery)) if !scripts { requireScriptsOff(t, tab, where) } if !anchored { mustRun(t, tab, chromedp.Evaluate(withoutAnchorPositioning, nil, func(p *runtime.EvaluateParams) *runtime.EvaluateParams { return p.WithAwaitPromise(true) })) } requireAnchorPositioning(t, tab, where, "#ds-prefs > [rst-dropdown-menu]", anchored) requireCoarse(t, tab) requireDrawnScrollbar(t, tab, where) mustRun(t, tab, chromedp.Evaluate(`document.getElementById("ds-prefs").open = true; const d = document.querySelector("#ds-prefs [rst-locale]"); d.open = true; d.querySelector("summary").focus(); true`, nil)) for i, code := range Locales { if scripts { var got string // 0.5px slack: layout at a fractional rem size lands a few // hundredths of a pixel past the edge, which no eye and no // finger can tell from on screen. mustRun(t, tab, chromedp.KeyEvent(kb.Tab), chromedp.Evaluate(`(() => { const a = document.activeElement, r = a.getBoundingClientRect(); const de = document.documentElement; return r.top >= -0.5 && r.bottom <= de.clientHeight + 0.5 && r.left >= -0.5 && r.right <= de.clientWidth + 0.5 ? a.lang : "off screen: " + a.outerHTML.slice(0, 60); })()`, &got)) if got != code { t.Errorf("%s: tab %d is %s, want the %s link on screen", where, i+1, got, code) } } var hit bool mustRun(t, tab, chromedp.Evaluate(fmt.Sprintf(`(() => { const a = document.querySelector('#ds-prefs [rst-dropdown-menu] a[lang=%q]'); a.scrollIntoView({block: "nearest", inline: "nearest"}); const r = a.getBoundingClientRect(); return document.elementFromPoint((r.left + r.right) / 2, (r.top + r.bottom) / 2) === a; })()`, code), &hit)) if !hit { t.Errorf("%s: scrolled into view, the %s link is not what a finger at its centre touches", where, code) } } } } } } // Each link followed once, at 390px with scripts: a plain // navigation, so the leg polls with until. where := theme + "/en index at 390px, scripts on" for _, code := range Locales { mustRun(t, ctx, chromedp.EmulateViewport(390, 700), chromedp.Navigate(rig.Origin+pageAt(theme, "en", "index.html")), chromedp.WaitVisible(`#ds-prefs`, chromedp.ByQuery), chromedp.Evaluate(open, nil), chromedp.Evaluate(fmt.Sprintf(`document.querySelector('#ds-prefs [rst-dropdown-menu] a[lang=%q]').click(); true`, code), nil)) until(t, ctx, where+", landed on "+code, fmt.Sprintf(`document.documentElement.lang === %q && location.pathname === %q`, code, pageAt(theme, code, "index.html"))) } // With scripts off the link is nothing but its href: one activation // through a real navigation. offWhere := theme + "/en index at 390px, scripts off" off := noScripts(t, ctx) mustRun(t, off, chromedp.EmulateViewport(390, 700), chromedp.Navigate(rig.Origin+pageAt(theme, "en", "index.html")), chromedp.WaitVisible(`#ds-prefs`, chromedp.ByQuery)) requireScriptsOff(t, off, offWhere) mustRun(t, off, chromedp.Evaluate(open, nil), chromedp.Evaluate(`document.querySelector('#ds-prefs [rst-dropdown-menu] a[lang="ar"]').click(); true`, nil)) until(t, off, offWhere+", landed on ar", fmt.Sprintf(`document.documentElement.lang === "ar" && location.pathname === %q`, pageAt(theme, "ar", "index.html"))) requireScriptsOff(t, off, offWhere+", on the ar index") }