//go:build browser package gallery import ( "context" "net/http" "testing" "time" "github.com/chromedp/cdproto/emulation" "github.com/chromedp/chromedp" "amadan.net/rastrillo/fiesta/internal/gallery/galleryrig" "amadan.net/rastrillo/fiesta/internal/harness" ) // The shared drive helpers, under the names the ported drives are // written with. One line each: the implementation is galleryrig's, // which the history package uses too, so the two cannot drift. // Fiesta's own drives keep newTree in a11y_browser_test.go, which // records every request as well. func treeHandler(t *testing.T) http.Handler { t.Helper() return galleryrig.Tree(t, render(t, mountPath), mountPath+"/") } func eagerly(t *testing.T, ctx context.Context, where string) { t.Helper() galleryrig.Eagerly(t, ctx, where) } func mobileSettle(t *testing.T, ctx context.Context, where string) { t.Helper() galleryrig.MobileSettle(t, ctx, where) } func addInit(js string) chromedp.Action { return galleryrig.AddInit(js) } func until(t *testing.T, ctx context.Context, where, expr string) { t.Helper() galleryrig.Until(t, ctx, where, expr) } func phoneRig(t *testing.T, opts ...harness.Option) *harness.Rig { t.Helper() return galleryrig.PhoneRig(t, func() http.Handler { return treeHandler(t) }, opts...) } func requireDrawnScrollbar(t *testing.T, ctx context.Context, where string) { t.Helper() galleryrig.RequireDrawnScrollbar(t, ctx, where) } func requireCoarse(t *testing.T, ctx context.Context) { t.Helper() galleryrig.RequireCoarse(t, ctx) } func settleMotion(t *testing.T, ctx context.Context, where string) { t.Helper() galleryrig.SettleMotion(t, ctx, where) } func noScripts(t *testing.T, ctx context.Context) context.Context { t.Helper() return galleryrig.NoScripts(t, ctx) } func requireScriptsOff(t *testing.T, ctx context.Context, where string) { t.Helper() galleryrig.RequireScriptsOff(t, ctx, where) } // TestTheRigServesTheTreeAtItsMount: the shared tree answers for a // file of the render under the mount and records a miss for a path // it has not got, so a drive that frames a missing document fails by // name. The bindings above are exercised by the drives that use them. func TestTheRigServesTheTreeAtItsMount(t *testing.T) { files := render(t, mountPath) var missed []string h := galleryrig.TreeRecording(t, files, mountPath+"/", func(p string) { missed = append(missed, p) }) rig := harness.New(t, func(string) http.Handler { return h }) rig.Allow(http.MethodGet, mountPath+"/no-such-file.html", http.StatusNotFound) ctx, cancel := context.WithTimeout(rig.Context(), 30*time.Second) defer cancel() var title string mustRun(t, ctx, chromedp.Navigate(rig.Origin+href("day/en/index.html")), chromedp.WaitReady("body"), chromedp.Evaluate(`document.title`, &title), ) if title == "" { t.Error("the Overview served through the rig has no title") } mustRun(t, ctx, chromedp.Navigate(rig.Origin+href("no-such-file.html")), chromedp.WaitReady("body")) if len(missed) != 1 || missed[0] != href("no-such-file.html") { t.Errorf("the rig recorded the misses %v, want the one path asked for", missed) } } // TestTheSharedRigDrivesAPhone exercises every binding above on one // page, so a helper that stopped matching the markup fails here rather // than in the first ported drive that leans on it: a phone rig with a // drawn scrollbar, an init script that runs before the page, the frames // settled at a phone's width, motion waited out, a condition polled, // and a second tab with scripts off that proves it. func TestTheSharedRigDrivesAPhone(t *testing.T) { rig := phoneRig(t, harness.WithScrollbars()) ctx, cancel := context.WithTimeout(rig.Context(), 2*time.Minute) defer cancel() mustRun(t, ctx, addInit(`window.__galleryInit = true`), chromedp.ActionFunc(func(c context.Context) error { return emulation.SetDeviceMetricsOverride(320, 640, 1, false).Do(c) }), chromedp.Navigate(rig.Origin+href("day/en/button.html")), chromedp.WaitReady("body"), ) requireCoarse(t, ctx) requireDrawnScrollbar(t, ctx, "button") var init bool mustRun(t, ctx, chromedp.Evaluate(`window.__galleryInit === true`, &init)) if !init { t.Error("the init script did not run before the page's own") } eagerly(t, ctx, "button") mobileSettle(t, ctx, "button") settleMotion(t, ctx, "button") until(t, ctx, "button", `document.querySelectorAll(".ds-view__frame[data-gallery-painted]").length > 0`) var on bool mustRun(t, ctx, chromedp.Evaluate(`document.documentElement.hasAttribute("data-gallery-js")`, &on)) if !on { t.Error("the scripted tab did not run the gallery's script") } off := noScripts(t, rig.Context()) mustRun(t, off, chromedp.Navigate(rig.Origin+href("day/en/button.html")), chromedp.WaitReady("body")) requireScriptsOff(t, off, "button with scripts off") rig.Screen("main", "the button page on a phone") }