Browser Bind & Screencast Fixtures
Praman ships two opt-in fixtures that unlock agentic workflows — where a running test exposes its live browser to external Playwright CLI agents, and video frames stream to AI vision pipelines in real time.
Overview
The agentic workflow follows three phases:
┌──────────────────────────────────────────────────────────┐
│ 1. TEST STARTS 2. AGENT CONNECTS 3. TEST ENDS │
│ │
│ Test runs with CLI agent attaches browser. │
│ PRAMAN_BIND=1 via endpoint URL unbind() │
│ → browser.bind() → inspects live UI called in │
│ → logs endpoint → discovers controls finally block│
│ │
│ screencast records frame handlers screencast │
│ chapters + actions feed AI vision .stop() │
└──────────────────────────────────────────────────────────┘
| Fixture | Scope | Opt-in via | Playwright version |
|---|---|---|---|
browserBind | Test | PRAMAN_BIND=1 env | 1.59+ |
screencast | Test | Always available | 1.59+ (degrades) |
Prerequisites
- Playwright 1.59 or later —
browser.bind()andpage.screencastwere introduced in 1.59. PRAMAN_BINDenvironment variable — set to1ortrueto enable browser binding.- Praman installed —
npm install playwright-praman.
Both fixtures require Playwright 1.59+. On older versions, browserBind throws
ERR_BIND_NOT_SUPPORTED when enabled, while screencast methods silently no-op
(except onFrame, which throws ERR_SCREENCAST_NOT_STARTED).
Quick Start
import { browserBindTest } from 'playwright-praman';
// Run with: PRAMAN_BIND=1 npx playwright test
browserBindTest('agent-assisted SAP test', async ({ browserBind }) => {
if (browserBind.bound) {
// A Playwright CLI agent can now connect at browserBind.endpointUrl
// and inspect the live browser while the test runs.
}
});
When PRAMAN_BIND is not set (or is '0'), the fixture returns
{ bound: false, endpointUrl: undefined, bindTitle: undefined } with no runtime cost.
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PRAMAN_BIND | No | (unset) | Set to 1 or true to enable browser binding |
PRAMAN_BIND_TITLE | No | 'praman-agent' | Override the bind title passed to browser.bind() |
# Enable binding with a custom title
PRAMAN_BIND=1 PRAMAN_BIND_TITLE=my-sap-agent npx playwright test
BrowserBindResult
The browserBind fixture provides a BrowserBindResult to the test body:
interface BrowserBindResult {
/** Whether the browser is currently bound. */
readonly bound: boolean;
/** The endpoint URL for Playwright CLI. undefined when bound is false. */
readonly endpointUrl: string | undefined;
/** The bind title used. undefined when bound is false. */
readonly bindTitle: string | undefined;
}
How It Works with CLI Agents
When PRAMAN_BIND=1, the fixture calls browser.bind(title) at the start of the test,
which opens an ephemeral endpoint. The endpoint URL is logged at info level so that a
Playwright CLI agent can connect without polling:
INFO browser-bind: Browser bound — Playwright CLI agent can connect
endpointUrl: "ws://127.0.0.1:43657/xyz"
title: "praman-agent"
A CLI agent attaches to this endpoint to inspect the live UI, discover controls, or take
snapshots — while the test continues running. On teardown, browser.unbind() is called
in a finally block regardless of test outcome.
# Terminal 1: Run the test with binding enabled
PRAMAN_BIND=1 npx playwright test tests/e2e/purchase-order.spec.ts
# Terminal 2: CLI agent connects to the bound browser
playwright-cli attach praman-agent
playwright-cli snapshot --filename=live-state.yml
Screencast Fixture
The screencast fixture wraps page.screencast (Playwright 1.59+) to provide structured
video recording with chapter markers, action annotations, and real-time frame streaming.
Chapter Markers
Use showChapter(title) inside test.step() to label logical sections of the video:
import { screencastTest } from 'playwright-praman';
screencastTest('purchase order workflow', async ({ page, screencast }) => {
await screencast.showChapter('Navigate to PO list');
await page.goto('/fiori#PurchaseOrder-manage');
await screencast.showChapter('Create new PO');
await page.getByRole('button', { name: 'Create' }).click();
});
Action Annotations
showActions(options?) enables DOM-level overlays for click, fill, and keyboard events:
await screencast.showActions({
position: 'bottom-right',
duration: 800,
fontSize: 20,
});
// Subsequent actions are annotated in the video
await page.getByRole('button', { name: 'Save' }).click();
| Option | Type | Default | Description |
|---|---|---|---|
duration | number | 500 | How long each annotation displays (ms) |
fontSize | number | 24 | Font size of the action title (px) |
position | string | 'top-right' | Overlay position on the video frame |
UI5 Control Tree Overlay
Toggle a visual overlay of the current UI5 control tree on video frames:
screencast.showUI5ControlTree(true);
// Frame handlers can now use the enabled flag for annotation
The UI5 overlay is currently state-only — it sets a flag that downstream frame handlers or a bridge extension can use to annotate each frame. Visual overlay injection will be added in a future release.
Frame Streaming
Register handlers for every raw video frame with onFrame(handler). Frames are JPEG
buffers with a monotonic timestamp. The screencast starts lazily when the first handler
is registered.
screencast.onFrame(async ({ buffer, timestamp }) => {
// Send frame to an AI vision pipeline for anomaly detection
const analysis = await aiVision.analyze(buffer);
if (analysis.anomaly) {
throw new Error(analysis.reason);
}
});
You can register multiple frame handlers. They are called in registration order.
Errors from handlers are caught and logged at debug level (non-fatal to the test).
Fixture Composition with mergeTests
Both fixtures can be composed with coreTest and each other using Playwright's
mergeTests():
import { mergeTests } from '@playwright/test';
import { coreTest } from 'playwright-praman';
import { browserBindTest } from 'playwright-praman';
import { screencastTest } from 'playwright-praman';
const test = mergeTests(coreTest, browserBindTest, screencastTest);
test('full agentic workflow', async ({ browserBind, screencast, page }) => {
// browserBind: CLI agent connects to the live browser
if (browserBind.bound) {
console.info('Agent endpoint:', browserBind.endpointUrl);
}
// screencast: structured video with chapter markers
await screencast.showChapter('Navigate to app');
await page.goto('/fiori#PurchaseOrder-manage');
await screencast.showActions({ position: 'bottom' });
screencast.onFrame(async ({ buffer }) => {
// Real-time AI vision analysis
});
});
The screencast fixture declares rootLogger as a { option: true } placeholder
(PW-MERGE-1 pattern). When composed with coreTest via mergeTests(), it receives
the shared pino logger. Without coreTest, it falls back to its own logger instance.
Error Handling
Both fixtures throw structured PramanError instances with error codes, suggestions,
and retry information:
browserBind Errors
| Error Code | When | Retryable |
|---|---|---|
ERR_BIND_NOT_SUPPORTED | browser.bind() missing (Playwright < 1.59) | No |
ERR_BIND_FAILED | browser.bind() rejects at runtime | No |
// Example error shape
PramanError {
code: 'ERR_BIND_NOT_SUPPORTED',
message: 'browser.bind() is not available — requires Playwright 1.59 or later',
attempted: 'Bind browser for Playwright CLI agent access (PRAMAN_BIND=1)',
retryable: false,
suggestions: [
'Upgrade Playwright to 1.59 or later: npm install -D @playwright/test@latest',
'Remove PRAMAN_BIND=1 to disable browser binding',
],
}
screencast Errors
| Error Code | When | Retryable |
|---|---|---|
ERR_SCREENCAST_CHAPTER_FAILED | showChapter() rejects | No |
ERR_SCREENCAST_NOT_STARTED | onFrame() called on Playwright < 1.59 | No |
ERR_SCREENCAST_FRAME_HANDLER | page.screencast.start() rejects | No |
Troubleshooting
| Problem | Solution |
|---|---|
ERR_BIND_NOT_SUPPORTED thrown | Upgrade Playwright to 1.59+: npm install -D @playwright/test@latest |
ERR_BIND_FAILED thrown | Check that no other process has already bound this browser instance |
browserBind.bound is always false | Set PRAMAN_BIND=1 in the environment before running the test |
| Endpoint URL not visible in logs | Set log level to info or lower in your Praman config |
showChapter() has no effect | Verify Playwright 1.59+ and that video recording is enabled in config |
onFrame() throws on registration | page.screencast is unavailable — upgrade Playwright to 1.59+ |
| Frame handlers never fire | Ensure the screencast has time to start — avoid immediate test teardown |
unbind() warning in teardown logs | Non-fatal — the browser may have already been closed by the test |
| Custom bind title not applied | Set PRAMAN_BIND_TITLE env var before the test process starts |
Next Steps
- CLI Agents -- Praman CLI Agent definitions for Claude Code, Copilot, and Cursor
- Running Your Agent -- the MCP-based agent workflow
- Agent & IDE Setup -- full installation and IDE configuration