| 1 | //go:build browser |
| 2 | |
| 3 | package calendartest |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "io" |
| 8 | "log/slog" |
| 9 | "net/http" |
| 10 | "path/filepath" |
| 11 | "testing" |
| 12 | "time" |
| 13 | |
| 14 | "amadan.net/rastrillo/rastrillo/harness" |
| 15 | "github.com/chromedp/cdproto/emulation" |
| 16 | "github.com/chromedp/cdproto/network" |
| 17 | "github.com/chromedp/chromedp" |
| 18 | |
| 19 | calendar "amadan.net/oficina/calendar/internal/calendar" |
| 20 | ) |
| 21 | |
| 22 | // TestQuickAddOnANarrowScreen: on a phone the quick-add panel stops |
| 23 | // floating and drops into the page (calendar.css, max-width 40rem), and |
| 24 | // at 390px it ran about 15px past the right edge of the screen — the |
| 25 | // textarea's and the Add button's right ends were off the side, and the |
| 26 | // page scrolled sideways. Every string check in the Go suite passes a |
| 27 | // panel like that. This opens it at 320 and 390 and fails if anything |
| 28 | // inside it reaches past the viewport, or the page grows wider than the |
| 29 | // screen. |
| 30 | func TestQuickAddOnANarrowScreen(t *testing.T) { |
| 31 | var app *calendar.App |
| 32 | rig := harness.New(t, func(origin string) http.Handler { |
| 33 | a, err := calendar.New(calendar.Config{ |
| 34 | DBPath: filepath.Join(t.TempDir(), "calendar.db"), |
| 35 | Env: "development", |
| 36 | Origin: origin, |
| 37 | Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), |
| 38 | }) |
| 39 | if err != nil { |
| 40 | t.Fatalf("calendar.New: %v", err) |
| 41 | } |
| 42 | t.Cleanup(func() { a.Close() }) |
| 43 | app = a |
| 44 | return a.Handler() |
| 45 | }) |
| 46 | session := signInOverHTTP(t, app, rig.Origin, "host@example.com") |
| 47 | |
| 48 | for _, width := range []int64{320, 390} { |
| 49 | var problems []string |
| 50 | ctx, cancel := context.WithTimeout(rig.Context(), 20*time.Second) |
| 51 | err := chromedp.Run(ctx, |
| 52 | network.SetCookie(session.Name, session.Value).WithURL(rig.Origin+"/"). |
| 53 | WithHTTPOnly(session.HttpOnly).WithSameSite(network.CookieSameSiteLax), |
| 54 | emulation.SetDeviceMetricsOverride(width, 844, 1, true), |
| 55 | chromedp.Navigate(rig.Origin+"/"), |
| 56 | chromedp.WaitVisible(`[cal-quick] > summary`, chromedp.ByQuery), |
| 57 | chromedp.Click(`[cal-quick] > summary`, chromedp.ByQuery), |
| 58 | chromedp.WaitVisible(`[cal-quick-text]`, chromedp.ByQuery), |
| 59 | chromedp.Evaluate(quickAddProblems, &problems), |
| 60 | ) |
| 61 | cancel() |
| 62 | if err != nil { |
| 63 | t.Fatalf("%dpx: %v", width, err) |
| 64 | } |
| 65 | for _, p := range problems { |
| 66 | t.Errorf("%dpx, the open quick-add panel: %s", width, p) |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | // quickAddProblems lists every visible element of the open quick-add |
| 72 | // panel that reaches past either side of the viewport, and the page |
| 73 | // itself if it scrolls sideways. |
| 74 | const quickAddProblems = `(() => { |
| 75 | const vw = document.documentElement.clientWidth, out = [] |
| 76 | const els = [document.querySelector('[cal-quick]'), ...document.querySelectorAll('[cal-quick] *')] |
| 77 | for (const el of els) { |
| 78 | const r = el.getBoundingClientRect() |
| 79 | if (r.width === 0 && r.height === 0) continue |
| 80 | if (r.right > vw + 0.5 || r.left < -0.5) { |
| 81 | const name = el.tagName.toLowerCase() + [...el.attributes].filter(a => a.name.startsWith('cal-')).map(a => '[' + a.name + ']').join('') |
| 82 | out.push(name + ' spans ' + Math.round(r.left) + '-' + Math.round(r.right) + ' in a ' + vw + 'px viewport') |
| 83 | } |
| 84 | } |
| 85 | if (document.documentElement.scrollWidth > vw) out.push('the page is ' + document.documentElement.scrollWidth + 'px wide in a ' + vw + 'px viewport') |
| 86 | return out |
| 87 | })()` |
| 88 | |