| 1 | //go:build browser |
| 2 | |
| 3 | package harness |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "net/http" |
| 8 | "strings" |
| 9 | "testing" |
| 10 | |
| 11 | "github.com/chromedp/chromedp" |
| 12 | ) |
| 13 | |
| 14 | // The origin chicken-and-egg, resolved: build gets the localhost |
| 15 | // origin before the server exists, the page renders it, and the |
| 16 | // browser reads it back off the page it navigated — one loop through |
| 17 | // listener, handler and Chromium proving the boot order. |
| 18 | func TestNewHandsBuildTheOriginBeforeServing(t *testing.T) { |
| 19 | var gotOrigin string |
| 20 | r := New(t, func(origin string) http.Handler { |
| 21 | gotOrigin = origin |
| 22 | mux := http.NewServeMux() |
| 23 | mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, req *http.Request) { |
| 24 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 25 | fmt.Fprintf(w, `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>rig</title></head><body><p id="origin">%s</p></body></html>`, origin) |
| 26 | }) |
| 27 | return mux |
| 28 | }) |
| 29 | if !strings.HasPrefix(r.Origin, "http://localhost:") { |
| 30 | t.Fatalf("rig origin %q is not a localhost origin — an IP is not a WebAuthn RP ID", r.Origin) |
| 31 | } |
| 32 | if gotOrigin != r.Origin { |
| 33 | t.Fatalf("build was handed %q, the rig navigates %q", gotOrigin, r.Origin) |
| 34 | } |
| 35 | var onPage string |
| 36 | r.Run( |
| 37 | chromedp.Navigate(r.Origin+"/"), |
| 38 | chromedp.WaitVisible("#origin", chromedp.ByQuery), |
| 39 | chromedp.Text("#origin", &onPage, chromedp.ByQuery), |
| 40 | ) |
| 41 | if onPage != r.Origin { |
| 42 | t.Fatalf("page shows %q, want %q", onPage, r.Origin) |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | // WithCoarsePointer is the only way a drive gets a phone's pointer: |
| 47 | // CDP's touch emulation leaves (pointer: coarse) false in this engine. |
| 48 | // The default rig is the control — if it already reported coarse, the |
| 49 | // option's true would prove nothing about the option. |
| 50 | func TestWithCoarsePointerMakesThePrimaryPointerCoarse(t *testing.T) { |
| 51 | build := func(string) http.Handler { |
| 52 | mux := http.NewServeMux() |
| 53 | mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, _ *http.Request) { |
| 54 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 55 | fmt.Fprint(w, `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>pointer</title></head><body><p>pointer</p></body></html>`) |
| 56 | }) |
| 57 | return mux |
| 58 | } |
| 59 | read := func(r *Rig) (coarse, hover bool) { |
| 60 | r.Run( |
| 61 | chromedp.Navigate(r.Origin+"/"), |
| 62 | chromedp.Evaluate(`matchMedia("(pointer: coarse)").matches`, &coarse), |
| 63 | chromedp.Evaluate(`matchMedia("(hover: hover)").matches`, &hover), |
| 64 | ) |
| 65 | return coarse, hover |
| 66 | } |
| 67 | if coarse, _ := read(New(t, build)); coarse { |
| 68 | t.Fatal("CONTROL: a default rig already reports a coarse pointer, so the option's reading below proves nothing") |
| 69 | } |
| 70 | coarse, hover := read(New(t, build, WithCoarsePointer())) |
| 71 | if !coarse { |
| 72 | t.Error("WithCoarsePointer: (pointer: coarse) is false; every touch leg would measure desktop density") |
| 73 | } |
| 74 | if hover { |
| 75 | t.Error("WithCoarsePointer: (hover: hover) is true; a phone's primary pointer cannot hover") |
| 76 | } |
| 77 | } |
| 78 | |