Documentation
¶
Overview ¶
Package chrome connects to Chrome over CDP (via chromedp) and exposes the Browser port the CLI commands drive. Keeping commands behind this interface lets the command boundary be tested in-process with a fake.
Index ¶
- func Reachable(wsURL string) bool
- type Browser
- type CDP
- func (c *CDP) AttrGet(ctx context.Context, id, selector, name string, q QueryOpts) (map[string]any, error)
- func (c *CDP) AttrList(ctx context.Context, id, selector string, q QueryOpts) (map[string]any, error)
- func (c *CDP) AttrRemove(ctx context.Context, id, selector, name string, q QueryOpts) (map[string]any, error)
- func (c *CDP) AttrSet(ctx context.Context, id, selector, name, value string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Click(ctx context.Context, id, selector string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Close() error
- func (c *CDP) CookieClear(ctx context.Context, id string) (map[string]any, error)
- func (c *CDP) CookieDelete(ctx context.Context, id, name string) (map[string]any, error)
- func (c *CDP) CookieList(ctx context.Context, id string) (any, error)
- func (c *CDP) CookieSet(ctx context.Context, id, name, value, domain, path string) (map[string]any, error)
- func (c *CDP) EmulateGeo(ctx context.Context, id string, lat, lon float64) (map[string]any, error)
- func (c *CDP) EmulateReset(ctx context.Context, id string) (map[string]any, error)
- func (c *CDP) EmulateViewport(ctx context.Context, id string, width, height int64) (map[string]any, error)
- func (c *CDP) Eval(ctx context.Context, id, expr string) (any, error)
- func (c *CDP) Fill(ctx context.Context, id, selector, value string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Frames(ctx context.Context, id string) (any, error)
- func (c *CDP) Grid(ctx context.Context, id, selector string, q QueryOpts) (any, error)
- func (c *CDP) HTML(ctx context.Context, id, selector string, inner bool, q QueryOpts) (map[string]any, error)
- func (c *CDP) List(_ context.Context) ([]target.Info, error)
- func (c *CDP) Navigate(ctx context.Context, id, url string) (map[string]any, error)
- func (c *CDP) Open(ctx context.Context, url string) (map[string]any, error)
- func (c *CDP) PDF(ctx context.Context, id string) ([]byte, error)
- func (c *CDP) Raw(ctx context.Context, id, method string, params json.RawMessage) (any, error)
- func (c *CDP) Screenshot(ctx context.Context, id string) ([]byte, error)
- func (c *CDP) Scroll(ctx context.Context, id, selector string, opts ScrollOpts) (map[string]any, error)
- func (c *CDP) Select(ctx context.Context, id, field, option string, opts SelectOpts) (map[string]any, error)
- func (c *CDP) SetHeaders(ctx context.Context, id string, headers map[string]string) (map[string]any, error)
- func (c *CDP) Snapshot(ctx context.Context, id string, opts SnapOpts) (any, error)
- func (c *CDP) Text(ctx context.Context, id, selector string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Type(ctx context.Context, id, selector, text string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Value(ctx context.Context, id, selector string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Values(ctx context.Context, id, selector string, q QueryOpts) (map[string]any, error)
- func (c *CDP) Wait(ctx context.Context, id string, cond WaitCond) (map[string]any, error)
- type ConnectError
- type Options
- type QueryOpts
- type ScrollOpts
- type SelectOpts
- type SnapOpts
- type WaitCond
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Browser ¶
type Browser interface {
List(ctx context.Context) ([]target.Info, error)
Open(ctx context.Context, url string) (map[string]any, error)
Eval(ctx context.Context, targetID, expr string) (any, error)
Snapshot(ctx context.Context, targetID string, opts SnapOpts) (any, error)
Click(ctx context.Context, targetID, selector string, q QueryOpts) (map[string]any, error)
Select(ctx context.Context, targetID, field, option string, opts SelectOpts) (map[string]any, error)
Grid(ctx context.Context, targetID, selector string, q QueryOpts) (any, error)
Scroll(ctx context.Context, targetID, selector string, opts ScrollOpts) (map[string]any, error)
Type(ctx context.Context, targetID, selector, text string, q QueryOpts) (map[string]any, error)
Fill(ctx context.Context, targetID, selector, value string, q QueryOpts) (map[string]any, error)
HTML(ctx context.Context, targetID, selector string, inner bool, q QueryOpts) (map[string]any, error)
Text(ctx context.Context, targetID, selector string, q QueryOpts) (map[string]any, error)
Value(ctx context.Context, targetID, selector string, q QueryOpts) (map[string]any, error)
Values(ctx context.Context, targetID, selector string, q QueryOpts) (map[string]any, error)
AttrGet(ctx context.Context, targetID, selector, name string, q QueryOpts) (map[string]any, error)
AttrList(ctx context.Context, targetID, selector string, q QueryOpts) (map[string]any, error)
AttrSet(ctx context.Context, targetID, selector, name, value string, q QueryOpts) (map[string]any, error)
AttrRemove(ctx context.Context, targetID, selector, name string, q QueryOpts) (map[string]any, error)
SetHeaders(ctx context.Context, targetID string, headers map[string]string) (map[string]any, error)
EmulateViewport(ctx context.Context, targetID string, width, height int64) (map[string]any, error)
EmulateGeo(ctx context.Context, targetID string, lat, lon float64) (map[string]any, error)
EmulateReset(ctx context.Context, targetID string) (map[string]any, error)
Frames(ctx context.Context, targetID string) (any, error)
Wait(ctx context.Context, targetID string, cond WaitCond) (map[string]any, error)
Screenshot(ctx context.Context, targetID string) ([]byte, error)
PDF(ctx context.Context, targetID string) ([]byte, error)
CookieList(ctx context.Context, targetID string) (any, error)
CookieSet(ctx context.Context, targetID, name, value, domain, path string) (map[string]any, error)
CookieDelete(ctx context.Context, targetID, name string) (map[string]any, error)
CookieClear(ctx context.Context, targetID string) (map[string]any, error)
Raw(ctx context.Context, targetID, method string, params json.RawMessage) (any, error)
Close() error
}
Browser is the set of Chrome operations the CLI commands need. The real implementation is CDP (chromedp-backed); tests use a fake.
type CDP ¶
type CDP struct {
// contains filtered or unexported fields
}
CDP is the chromedp-backed Browser.
The allocator/base/tab contexts are rooted at context.Background(), NOT at a per-command context — otherwise a command's deferred cancel would tear the whole tree down and (in attach mode) CloseTarget the user's real tab. Tabs are attached once and reused; per-command timeouts are applied as short-lived child contexts around each action (see run). The shared daemon is the eventual home for this attach-and-hold model.
func Connect ¶
Connect walks the connection ladder (mirroring browser.DecideConnection):
- a reachable DevToolsActivePort endpoint -> attach (Path B)
- a running but non-debug Chrome -> ConnectError{not_debug_enabled} (never shadow the user's session with a second browser)
- nothing running, --no-launch -> ConnectError{connection_failed}
- nothing running -> launch a managed Chrome (Path A)
The ctx is intentionally not used to root the browser: the allocator lives at context.Background() so a command's deferred cancel can never tear down (and, in attach mode, CloseTarget) the user's real tabs. The decision (attach / instruct / launch) is delegated to the tested browser.DecideConnection so there is exactly one authored copy of the ladder.
func (*CDP) AttrRemove ¶
func (*CDP) Click ¶
Click resolves the selector and clicks it at its live, occlusion-verified centre via a coordinate pointer sequence (the same primitive as `select`), so it lands on a background/inactive tab where chromedp's box-model node-click would poll until timeout. bringToFront reactivates the tab first.
func (*CDP) Close ¶
Close tears down the connection. Managed Chrome is fully closed. An attached real Chrome is left alone — cancelling any context would CloseTarget a real tab, so we let the WebSocket drop with the process instead.
func (*CDP) CookieClear ¶
func (*CDP) CookieDelete ¶
func (*CDP) EmulateGeo ¶
func (*CDP) EmulateReset ¶
func (*CDP) EmulateViewport ¶
func (*CDP) Fill ¶
func (c *CDP) Fill(ctx context.Context, id, selector, value string, q QueryOpts) (map[string]any, error)
Fill sets a field to value, replacing (not appending to) any existing content: it triple-clicks the field to select all its text, then types value as real keystrokes over the selection — the reliable way to set a pre-filled cell (e.g. a timesheet "0" hour cell) to a new value in one call.
func (*CDP) Grid ¶
Grid reads a table/grid as structured {headers, rows} from the accessibility tree — replacing hand-parsing the a11y snapshot (or a screenshot) for the calendar / task-list / timesheet grids. selector optionally picks the grid by ARIA accessible name (with q); empty selects the first grid/table in the tree.
func (*CDP) Open ¶
Open creates a new tab at url (browser-level Target.createTarget, which navigates it) and returns the new tab's id — replacing a raw Target.createTarget for the common "start from a fresh tab on X" case.
func (*CDP) Raw ¶
Raw sends any CDP method by string via the executor — full coverage, no per-method registry. An empty id targets the browser-level executor, so Browser.* / Target.* methods are reachable.
func (*CDP) Scroll ¶
func (c *CDP) Scroll(ctx context.Context, id, selector string, opts ScrollOpts) (map[string]any, error)
Scroll scrolls a selector into view (Into), dispatches a real mouse wheel (Wheel), or — by default — scrolls by (Dx, Dy) via JS scrollBy on the selector's scroll box (or the window when selector is empty).
func (*CDP) Select ¶
func (c *CDP) Select(ctx context.Context, id, field, option string, opts SelectOpts) (map[string]any, error)
Select chooses an option in a prompt / combobox / cascade widget, or a native <select>. It exists because a plain `click` cannot drive Workday's portal-rendered menus and React cascade prompts: those open on a real pointer sequence, mount briefly collapsed (a zero-scale transform) then animate open, render options in a detached subtree with delegated (capture-phase) handlers, and change geometry between the open and the option click. See .scratch/cdp-ergonomics/e3-rootcause.md.
The whole choreography runs inside ONE held CDP action (a single c.run), so geometry is re-read between steps against the live DOM — no multi-command staleness. Each interaction is a coordinate Input.dispatchMouseEvent (mouseMoved→pressed→released) at the element's live, occlusion-verified centre, which is what actually drives the delegated widgets where chromedp's node-click (one box-model read, no settle/occlusion check) misses.
func (*CDP) SetHeaders ¶
func (*CDP) Type ¶
func (c *CDP) Type(ctx context.Context, id, selector, text string, q QueryOpts) (map[string]any, error)
Type coordinate-clicks the selector to focus it (robust on a background tab), then sends the text as real keystrokes to the focused element.
func (*CDP) Values ¶
Values reads the value (or text) of EVERY element matching a CSS selector, in document order — one round trip instead of an eval per field (e.g. reading a whole row of timesheet hour cells or a set of selected pills). Uses querySelectorAll, so it works even on a background/hidden tab.
type ConnectError ¶
ConnectError carries a stable error.code (matching the result contract) so the CLI can distinguish "not debug-enabled" from a generic connection failure.
func (*ConnectError) Error ¶
func (e *ConnectError) Error() string
type Options ¶
type Options struct {
PortFile string // override DevToolsActivePort location (else OS candidates)
ProfileDir string // managed-launch profile dir (else CHROME_CDP_PROFILE / default)
Port int // explicit debug port to attach to / launch with (0 = auto)
NoLaunch bool // don't fall back to launching a managed Chrome
Headless bool // headless for the managed-launch fallback (tests use this)
}
Options controls how CDP connects.
type QueryOpts ¶
type QueryOpts struct {
By string // css (default) | id | search | jspath | css-all | name
Wait string // visible (default) | ready | enabled
Pierce bool // reach into shadow DOM / iframes (via DevTools search)
// Accessible-name addressing (By == "name"): the selector is the ARIA
// accessible name; Role optionally constrains the ARIA role, and Nth picks
// the 1-based match among the visible candidates (0 = first).
Role string
Nth int
// Match controls how the accessible name is compared (By == "name"):
// "" / "exact" (default), "contains" (case-insensitive substring), or
// "regex". Lets a caller click "Review" without knowing its verbose full
// accessible name.
Match string
// InRow scopes an accessible-name match (By == "name") to the table row
// whose text contains this string — so `click --by name "Delete" --in-row
// "TEST entry"` hits the Delete button in that row, not the first of many.
// It resolves via the DOM (not the throttled a11y tree), so it also works on
// a backgrounded tab. Requires By == "name".
InRow string
// OnDialog, set on an action verb (click/type/fill), auto-handles a native
// JavaScript dialog (alert/confirm/prompt) that opens DURING the action —
// "accept" or "dismiss" — instead of letting it wedge the CDP connection.
// The handled dialogs are reported back in the result envelope.
OnDialog string
}
QueryOpts controls how a selector is interpreted (By), what state to wait for (Wait), and whether to pierce shadow DOM / iframes (Pierce).
type ScrollOpts ¶
ScrollOpts controls the `scroll` verb:
- Into: scroll Selector into view.
- otherwise: scroll by (Dx, Dy) pixels — Selector's scroll box, or the window when Selector is empty. This is a JS scrollBy (deterministic; it fires the scroll events virtualized grids render on).
- Wheel: instead dispatch a real mouse-wheel of (Dx, Dy) at Selector's centre (viewport centre if empty), for grids that listen for wheel specifically.
type SelectOpts ¶
type SelectOpts struct {
// Query addresses the FIELD control (usually By=="name" with a Role like
// textbox/combobox to disambiguate a same-named column header from the input).
Query QueryOpts
// OptionMatch controls how each option segment is compared against rendered
// option text: "" / "contains" (default — Workday labels are verbose),
// "exact", or "regex".
OptionMatch string
// Filter is optional text typed into the prompt after it opens, to narrow a
// long option list before the option is clicked.
Filter string
// Sep splits Option into cascade levels (default ">"): e.g.
// "Project Plan Tasks > ShiftLeft: Qwiet" drills the category, then the leaf.
Sep string
}
SelectOpts controls the `select` verb — choosing an option in a prompt / combobox / cascade widget (Workday-style portal popups, or a native <select>).
type SnapOpts ¶
type SnapOpts struct {
Role string // only nodes with this ARIA role
Grep string // only nodes whose accessible name matches this regex
Region string // only nodes within the subtree of a container whose name contains this
Dedupe bool // collapse identical role+name (keep first) — for virtualized grids
}
SnapOpts filters an accessibility snapshot server-side, so a read returns just the relevant nodes instead of the whole tree. Alerts/focused stay page-wide.
type WaitCond ¶
type WaitCond struct {
URL string
Visible string
Gone string
Text string // until the accessibility tree contains this text (e.g. a "Success" toast)
Stable bool // until the accessibility tree stops changing (the page settled)
Idle bool // until network activity settles (no in-flight requests for a window)
Query QueryOpts
}
WaitCond is a condition for the `wait` verb: settle until the target URL contains URL, or a selector becomes Visible / is Gone. Exactly one is set; Query carries the selector options for Visible/Gone. A fixed --for duration is handled in the CLI (no browser round-trip needed).