- name
- api
- description
- One-line reference for every Biloba Go method and matcher, grouped by area — selectors/locators, lifecycle, poll-config (WithTimeout/WithPolling/WithContext/Immediate), capturing a matcher's observed value (.Capture), navigation (GetLocation/GetTitle), cookies/storage, tabs, cross-origin iframes (b.Frame frame handles), DOM existence/visibility/contents/properties/forms, clicking and interactions (incl. drag/scroll/tap/modifiers/text-selection), realistic mode, keyboard, uploads, element JS, dialogs, downloads, arbitrary JS (incl. the GetJSValue app-state barrier), network stubbing/aborting/modifying/observing/holding (HoldResponse + Limit/ReleaseNext), screenshots/outline/window, and visual regression (HaveScreenshot + Mask/Tolerance/ChannelTolerance/Animated/InColorSchemes). Use to look up the exact Go method or matcher name and shape. Methods marked (dual) poll until they succeed when fully applied and return a pollable matcher when under-applied.
# Biloba API reference
Terse lookup for Biloba's Go API. Docs: <https://onsi.github.io/biloba/>. Sibling skills are named here without a prefix; invoke one with the same plugin prefix you loaded this skill under.
**Naming conventions — assume these, they're only restated where an entry breaks them:**
| Shape | Means |
|---|---|
| `Have*` / `Be*` / `Each*` / `Match*` | a Gomega matcher. `Each*` = **≥1 match AND all satisfy** (fails on zero matches). |
| `Get*` | polls the **first** match until ready, returns the value. Optional trailing `&ptr` decodes it. |
| `Current*ForEach` | **snapshot** over **all** matches, no poll, returns a slice (empty when nothing matches). |
| `*Immediately` | acts on **all** current matches now, no poll. The long suffix is a "know what you're doing" smell. |
| **(dual)** | fully applied → **polls**, acts once, stops (`b.Click("#go")`). Under-applied → a matcher you poll (`Eventually("#go").Should(b.Click())`). |
Selectors: CSS strings, `XPath` (`xpath`), or `Locator`s. `b.Immediate()` opts out of poll-by-default; rarely needed.
## Selectors / locators
**CSS is the default** (stable `#id`/`[data-testid]`, not styling classes); **locators** for a11y + text/label identifiers; **XPath** the rare axis/ordinal tool. Speed: CSS > XPath > locators (full-document ARIA scan).
- **CSS**: `"#id"`, `.cls`, `:has()`. `>>>` pierces one open-shadow-root / same-origin-iframe boundary per occurrence. A **cross-origin** iframe needs a frame handle (`b.Frame`, below) — no selector reaches into it from the page.
- **XPath**: `b.XPath(...)` — pierces neither.
- **Locators** (`*Contains` variant on every text-valued one): `b.ByRole(r)`, `b.ByText`, `b.ByLabel`, `b.ByPlaceholder`, `b.ByAltText`, `b.ByTitle`, `b.ByTestID(id)` (attr = `biloba.TestIDAttribute`, default `"data-testid"`), `b.ByCSS(sel)` — raw CSS into the algebra, the only *structural* constructor (`b.ByCSS(".story").Nth(1)` for "the 2nd", not `:nth-of-type`). They **pierce open shadow roots** automatically. Accessible name covers aria-labelledby/aria-label/`<label>`/alt/placeholder/value/text/figcaption/caption/title.
- **Role refinements**: `.WithName(n)`/`.WithNameContains(n)`, `.Level(n)`, `.Checked()`/`.Disabled()`/`.Expanded()`/`.Pressed()`/`.Selected()`.
- **Composition** (each accepts any CSS/XPath/Locator): `.ContainingText`/`.NotContainingText`, `.Containing`/`.NotContaining`, `.And`/`.Or`, `.Within`/`.NotWithin`, `.Nth(i)`/`.First()`/`.Last()`. E.g. `b.ByRole("listitem").Containing(b.ByText("Delete")).Within("#cart").First()`.
- **Trap — an unresolved scope matches nothing, so a negative assertion is vacuous.** Anchor the scope first:
```go
Eventually("#published-list").Should(b.Exist())
Consistently(b.ByTextContains("Draft").Within("#published-list")).ShouldNot(b.Exist())
```
## Lifecycle
- `biloba.SpinUpChrome(GinkgoT(), ...SpinUpOption)` — start Chrome (process 1). Options: `HighFidelityHeadless()`, `AutoInstallHeadlessShell()`, `HeadlessShellPath(p)`, `StartingWindowSize(w,h)`, `ChromeFlags(...)`, `ChromeSandbox(enabled)` (overrides the automatic Linux `--no-sandbox`). → `setup`
- `biloba.ConnectToChrome(GinkgoT(), ...BilobaConfig)` — open this process's root tab `b`. Config → `debug-failures`
- `b.Prepare()` — reset the root tab between specs (`BeforeEach`, `OncePerOrdered`).
- `b.Context` — the tab's `chromedp` context (escape hatch).
## Poll config
Shallow `*Biloba` clones (like `Realistic()`); not reset by `Prepare()`. Use per-call: `b.WithTimeout(5*time.Second).Click("#go")`.
`b.WithTimeout(d)` · `b.WithPolling(d)` · `b.WithContext(ctx)` (cancellation aborts the wait) · `b.Immediate()` (act once, fail fast).
**Four-bucket rule — misapplying a knob is a hard error:**
| Bucket | Methods | `WithTimeout`/`WithContext` | `WithPolling` | `Immediate` |
|---|---|---|---|---|
| **Polling** | dual actions, value-getters (incl. `GetLocation`/`GetTitle`/`GetJSValue`) | ✓ | ✓ | ✓ |
| **Waiting command** | `Navigate`, `Capture*Screenshot*` (own ~30s/~5s defaults), `HoldResponse`+`ResponseHold.Await` (own 30s) | ✓ (overrides own default) | error | error |
| **Snapshot** | `HasElement`/`Count`/`Current*ForEach`/`ResponseHold.Count`/… | error | error | error |
| **One-shot mutation** | `SetCookie`/`StubRequest`/`*Immediately`/`Run`/`RunAsync`/… | error | error | error |
Configuring anything that resolves to a **bare matcher** — a `(matcher)` method (`b.HaveScreenshot(name)`, `b.Exist()`, …), or the under-applied dual form (`b.WithTimeout(d).Click()`) — is also a hard error. Configure the `Eventually`, not the matcher.
**Separate from all four: the CDP backstop.** Every command Biloba sends Chrome carries a deadline (30s; 2min for `RunAsync`, whose length is set by the promise your page awaits) so an unresponsive Chrome fails the spec instead of hanging the suite. Not configurable and not a knob — `WithTimeout` bounds retries, not liveness, and does **not** shorten it. Every bucket gets it, including the two that reject every knob. Failures read `deadline_exceeded` / `page_crashed` / `browser_gone` → `debug-failures`.
## `.Capture(&target)`
Every matcher that **reads a value off the page** returns a `*ValueMatcher` with `.Capture(&x)`: it writes what it observed on a *successful* match (nothing on `ShouldNot`), so the gate and the read are one read (asserting then re-reading with a getter is two reads of a page that may have changed — TOCTOU). Decoded into your Go type, à la `encoding/json`. Full rules — typed decode, absent-vs-zero (`**T`), returns-a-new-matcher, narrowing JS objects vs. Biloba structs — → `flaky-specs` §1.
```go
var blockID string
Eventually(".figure-frame").Should(b.HaveAttribute("data-block-id", Not(BeEmpty())).Capture(&blockID))
```
- **Capturable:** `HaveAttribute`, `HaveProperty` (both forms incl. existence-only), `HaveValue`, `HaveInnerText`, `HaveTextContent`, `HaveText`, `HaveClass`, `HaveCount`, `HaveDistinctCount`, `HaveJSONAttribute`, `HaveComputedStyle`, `HaveComputedStyleNumeric`, `EachHaveInnerText`, `EachHaveTextContent`, `EachHaveClass`, `EachHaveProperty` (both forms — the existence-only form reads values in the same round-trip, so no-arg `EachHaveInnerText()`/`EachHaveTextContent()` capture too), `HaveBoundingBox`, `HaveScrollOffset`, `HaveOffsetTopWithin`/`HaveOffsetLeftWithin` (both forms incl. existence-only), `HaveGapBetween` (both forms incl. existence-only), `EvaluateTo`, `HaveURL`, `HaveTitle`, `HaveLocalStorageItem`/`HaveSessionStorageItem`, `HaveNumLocalStorageItems`/`HaveNumSessionStorageItems`, `HaveNumCookies`, `HaveCookie`.
- **Not capturable (won't compile):** value-less matchers — `Exist`, `BeVisible`, `BeEnabled`, `BeClickable`, `BeFocused`, `BeChecked`, `BeInViewport`, `BeAbove`/`BeBelow`/`BeLeftOf`/`BeRightOf`/`Encloses`/`Overlaps`/`BePrecededBy`/`BeFollowedBy`, `BeNetworkIdle`, `HaveScreenshot` — and the actions-as-matchers (`Click`, `SetValue`, `Type`, …). Two read something but stay bare on purpose: **`MatchColor`** (a sub-matcher — capture off the `HaveComputedStyle` you hand it to) and **`HaveMadeRequest`** (a `*RequestQuery` builder; it renders observed requests in its failure message, and the request object comes from `b.AllRequests().Find(b.RequestMatching(...))`).
- **Getter counterpart:** the `any`-returning getters take an optional trailing pointer with the same decode rules — `b.GetProperty("#row", "offsetWidth", &n)`, `b.GetAttribute`, `b.GetValue`, `b.CurrentPropertyForEach`, `b.CurrentAttributeForEach`, `b.CurrentValueForEach`. The value is still returned.
## Navigation
- `b.Navigate(url)` — navigate, assert `200`. / `b.NavigateWithStatus(url, code)`.
- `b.GetLocation()` / `b.GetTitle()` → string — **polling getters**, all four knobs. A transient CDP error mid-navigation (`Inspected target navigated or closed (-32000)`) is a retryable miss.
- `b.HaveURL(string|matcher)` / `b.HaveTitle(string|matcher)` — same transient-error behavior.
- **Gate on a DOM anchor, not the URL** — `Eventually("#dashboard-root").Should(b.Exist())` proves the navigation landed; a `GetLocation()` read is then a formality.
## Cookies & storage (navigate to a real origin first)
- `b.SetCookie(...Cookie)` (default domain = current URL) · `b.GetCookies()` → `Cookies` · `b.ClearCookies()`.
- `b.HaveCookie(name|matcher)` — chain `.WithValue/.WithPath/.WithDomain/.WithSameSite/.WithSecure(...)/.WithHTTPOnly(...)`.
- `b.CookieMatching(...)` — predicate for `Cookies.Find/Filter`. · `b.HaveNumCookies(int|matcher)`.
- `b.LocalStorage()` / `b.SessionStorage()` → `Set(k,v)`, `Get(k,&ptr)`, `GetAll()`, `Remove(k)`, `Clear()`, `Length()` (JSON round-tripped).
- `b.HaveLocalStorageItem(key[, value])` / `HaveSessionStorageItem` / `HaveNumLocalStorageItems` / `HaveNumSessionStorageItems`.
## Tabs
- `b.NewTab()` → `*Biloba` — isolated tab (own context); closed by `Prepare()`.
- `tab.Close()` → error (`Eventually(tab.Close).Should(Succeed())` during downloads).
- `b.AllTabs()` / `b.AllSpawnedTabs()` → `Tabs`.
- `b.HaveTab()` / `b.HaveSpawnedTab()` — chain `.WithURL/.WithTitle/.WithDOMElement(selector)`.
- `b.TabMatching()` — predicate for `Tabs.Find/Filter`.
## Cross-origin iframes (frame handles)
- `b.Frame(query)` → `*Biloba` frame handle — **polls** until a cross-origin frame below `b` matches (honors all four knobs; fails the spec on timeout). Every DOM method, matcher, getter, `.Capture`, `Run`/`JSFunc`, `SetUpload`, `Realistic()`, screenshot and `HaveScreenshot` works on the handle and runs in the frame's document.
- `b.FrameMatching()` / `b.HaveFrame()` → `FrameQuery` — chain `.WithURL/.WithTitle/.WithDOMElement(selector)`; `Eventually(b).Should(b.HaveFrame()...)` polls it.
- `b.AllFrames()` → `Frames` snapshot (`Find`/`Filter`); includes frames nested behind a boundary. `frame.Frame(...)` / `frame.AllFrames()` search inside a frame.
- `frame.IsFrame()`; `frame.Close()` detaches the handle (`Prepare()` discards all of them).
- On a frame handle, tab-level methods fail the spec: `Navigate`, `Prepare`, `NewTab`, `SetWindowSize`, `SetCookie`/`ClearCookies`, `StubRequest`/`AbortRequest`/`ModifyRequest`/`ModifyResponse`/`HoldResponse`, dialog handlers/`Dialogs`, downloads. Call them on the tab — its stubs cover the frame's requests and its dialog handling covers the frame's dialogs.
- A handle belongs to one document: after the iframe is removed/navigated or the tab navigates, calls fail with `frame_detached` — call `b.Frame(...)` again.
- An **out-of-process** frame (full Chrome runs cross-site frames in their own renderer; chrome-headless-shell doesn't) is driven the same way, except: no screenshots of it (capture the `iframe` element from the tab), the tab's network stubs don't reach its requests, and realistic mode can't see what the embedding page draws over it.
## Existence, count, visibility, enabled
Auf GitHub ansehen