From biloba
Wires Biloba into a Ginkgo suite: go get, bootstrap file with SynchronizedBeforeSuite and Prepare, installing chrome-headless-shell, choosing high-fidelity vs fast headless shell, three bootstrap variations, window size, and running the suite.
How this skill is triggered — by the user, by Claude, or both
Slash command
/biloba:setupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This is the one-time wiring. For the authoring model see `biloba:write-tests`; for the mental model see `biloba:overview`. Docs: <https://onsi.github.io/biloba/#getting-started>.
This is the one-time wiring. For the authoring model see biloba:write-tests; for the mental model see biloba:overview. Docs: https://onsi.github.io/biloba/#getting-started.
go get github.com/onsi/biloba
mkdir browser && cd browser
ginkgo bootstrap
Then edit the generated *_suite_test.go to wire in Biloba:
package browser_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/biloba"
)
func TestBrowser(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Browser Suite")
}
var b *biloba.Biloba
var _ = SynchronizedBeforeSuite(func() {
biloba.SpinUpChrome(GinkgoT())
}, func() {
b = biloba.ConnectToChrome(GinkgoT())
})
var _ = BeforeEach(func() {
b.Prepare()
}, OncePerOrdered)
GinkgoT() is the seam: Chrome errors become suite failures.SpinUpChrome runs once (process 1) and writes connection info to disk; ConnectToChrome runs on every parallel process and opens that process's reusable root tab b.b.Prepare() resets b between specs (closes other tabs, clears state, navigates to about:blank). OncePerOrdered keeps it from resetting between Its inside an Ordered container.chrome-headless-shellBy default SpinUpChrome drives chrome-headless-shell — the lightweight headless build that is ~an order of magnitude faster and lets one Chrome process drive many isolated contexts in parallel (see #headless-fidelity). It's a standalone binary, separate from your Chrome install. Biloba looks for it in this order:
biloba.HeadlessShellPath("/path/to/chrome-headless-shell")BILOBA_CHROME_HEADLESS_SHELL env varPATH@puppeteer/browsers and Biloba download cachesIf none turn it up, Biloba fails fast with instructions (it will not silently download). Install it once:
npx @puppeteer/browsers install chrome-headless-shell@stable
Or have Biloba download+cache it the first time (opt-in, since it reaches the network):
biloba.SpinUpChrome(GinkgoT(), biloba.AutoInstallHeadlessShell())
biloba.SpinUpChrome(GinkgoT(), biloba.HighFidelityHeadless())
This runs the full ("new") headless Chrome — pixel-accurate, extensions, etc. — but markedly slower and it serializes parallel work. Keep the bulk of the suite on the shell and run a focused high-fidelity lane where it earns its keep.
Trade isolation against performance by editing the bootstrap. All three are simple code changes — try them on your suite.
Default — shared browser, reused root tab (most performant, good-enough isolation): the snippet in step 1.
Per-process browser (stronger isolation, slower startup):
var _ = BeforeSuite(func() {
biloba.SpinUpChrome(GinkgoT())
b = biloba.ConnectToChrome(GinkgoT())
})
var _ = BeforeEach(func() { b.Prepare() }, OncePerOrdered)
Fresh tab per spec (per-spec cleanup, a per-spec cost):
var rootB *biloba.Biloba
var b *biloba.Biloba
var _ = SynchronizedBeforeSuite(func() {
biloba.SpinUpChrome(GinkgoT())
}, func() {
rootB = biloba.ConnectToChrome(GinkgoT())
})
var _ = BeforeEach(func() {
rootB.Prepare()
b = rootB.NewTab()
}, OncePerOrdered)
You can mix per-process browsers with fresh-tab-per-spec.
SpinUpChrome(GinkgoT(), ...) options:
biloba.HighFidelityHeadless() — full headless Chrome.biloba.AutoInstallHeadlessShell() — download the shell if missing.biloba.HeadlessShellPath(path) — point at a specific shell binary.biloba.StartingWindowSize(w, h) — default tab size (default 1024x768); a process-wide setting.biloba.ChromeFlags(...) — raw chromedp.ExecAllocatorOptions (e.g. chromedp.Flag("headless", false) to watch).ConnectToChrome(GinkgoT(), ...) carries Biloba-specific config — most of it about failure artifacts (outlines, screenshots, inline images), which is covered in biloba:debug-failures. Under CI or an AI agent, failure artifacts need zero config — Biloba auto-detects and emits a DOM outline plus screenshot files on disk.
ginkgo -r -p # parallel — Biloba is built for this
ginkgo -r -p -randomize-all
npx claudepluginhub onsi/biloba --plugin bilobaExplains the Biloba mental model for writing browser tests with Ginkgo/Gomega: performance via parallelization, stability via pragmatic simulation, conciseness, and the chromedp escape hatch.
Wires Ginkgo into a Go package: installs CLI, bootstraps suite_test.go, handles _test package convention, dot-imports, and *testing.T interop.
Sets up Playwright test projects with installation commands, basic/advanced playwright.config.ts configurations, multi-browser projects, and webServer for scalable test suites.