From 9d79dd1183ef8b731674a985f8fc14f965ead37e Mon Sep 17 00:00:00 2001 From: omartelo Date: Wed, 28 Oct 2026 13:43:23 -0300 Subject: [PATCH] test(browser): draw the page before holding a dialog in the pane test TestChromeAPaneOpenedOnADialogShowsThePage failed on every macos-latest run since #958: headless Chrome on macOS has not painted the page when Navigate returns, and a dialog held open before the first paint leaves the screencast no frame to send. The test asserted a frame of a page that was never drawn, which the contract never promised; a dialog opened by an agent's click comes after the page has been drawn. Measured on the runner: with the page drawn first (a screenshot, a prior screencast or two idle seconds) the held page casts within 25 ms; without, no frame in 15 s. The gap for a dialog opened before the first paint is written down in docs/ceilings.md. --- docs/ceilings.md | 6 ++++++ internal/browser/dialogs_live_test.go | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/docs/ceilings.md b/docs/ceilings.md index 7e0ac466..c0f17cfa 100644 --- a/docs/ceilings.md +++ b/docs/ceilings.md @@ -1002,6 +1002,12 @@ work when nobody knows it and that the call site never shows. The mechanism and sibling covers keeps its pin. Notes not sent yet live in the window: closing it, or the browser, loses them. +- **On macOS, a JavaScript dialog a page opens before its first paint leaves the pane black behind the dialog's + card** (`internal/browser/chrome_view.go`): headless Chrome there paints a page some time after its load, and + a page held by a dialog cannot paint, so a screencast started then has no frame to send until the dialog is + answered (Chrome 155 on the macos-latest runner, measured). A dialog opened once the page has been drawn, as + one an agent's click opens, shows the page behind it. Linux and Windows paint before the load returns. + - **A session browser's downloads go straight to the user's Downloads folder** (`internal/browser/downloads.go`, `session_downloads.go`, `docs/browser-tab.md`): `XDG_DOWNLOAD_DIR` on Linux, `~/Downloads` on macOS and Windows, even for a Windows user who moved that known folder. Chrome writes each one under its download id into a staging diff --git a/internal/browser/dialogs_live_test.go b/internal/browser/dialogs_live_test.go index 2ed3c67f..ff42103a 100644 --- a/internal/browser/dialogs_live_test.go +++ b/internal/browser/dialogs_live_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/chromedp/cdproto/page" "github.com/chromedp/chromedp" "github.com/lichdotdev/lich/api" "github.com/lichdotdev/lich/internal/chromium" @@ -105,6 +106,16 @@ func TestChromeAPaneOpenedOnADialogShowsThePage(t *testing.T) { if err := p.Navigate(ctx, srv.URL); err != nil { t.Fatal(err) } + // The page is drawn once first, as a page the agent clicked on was: + // headless Chrome on macOS has not painted it when Navigate returns, and a + // dialog held open before its first paint leaves no frame to cast + // (Chrome 155, macos-latest runner, measured 2026-10-28). + if err := p.runFree(ctx, chromedp.ActionFunc(func(ctx context.Context) error { + _, err := page.CaptureScreenshot().Do(ctx) + return err + })); err != nil { + t.Fatal(err) + } if err := p.runFree(ctx, chromedp.Evaluate(`setTimeout(() => confirm('Delete?'), 0)`, nil)); err != nil { t.Fatal(err) }