-
Notifications
You must be signed in to change notification settings - Fork 8
Recipes
Kumak edited this page Nov 22, 2025
·
5 revisions
Common workflows and automation patterns.
Prefer high-level commands when available:
- Use
bdg dom evalinstead ofbdg cdp Runtime.evaluate - Use
bdg network getCookiesinstead ofbdg cdp Network.getCookies - Use
bdg domcommands for DOM interaction (they handle waiting automatically)
Use --json flag when piping to jq for explicit JSON output.
Automatic waiting: bdg dom click, bdg dom fill, and bdg dom pressKey automatically wait for page stability - no sleep needed in most cases.
bdg https://example.com
bdg dom eval "Array.from(document.querySelectorAll('a')).map(a => ({text: a.textContent.trim(), href: a.href}))"
bdg stopbdg https://example.com
bdg dom eval "document.title"
bdg dom eval "document.querySelector('meta[name=description]')?.content"bdg https://example.com
bdg cdp Emulation.setDeviceMetricsOverride --params '{"width":1920,"height":1080,"deviceScaleFactor":1,"mobile":false}'
bdg dom screenshot desktop.png
bdg cdp Emulation.setDeviceMetricsOverride --params '{"width":375,"height":812,"deviceScaleFactor":3,"mobile":true}'
bdg dom screenshot mobile.pngbdg https://app.example.com/login
bdg dom fill "#username" "admin"
bdg dom fill "#password" "secret"
bdg dom click "#login-btn" # Automatically waits for navigation/stability
# Verify logged in
bdg dom eval "window.location.href"bdg https://example.com
bdg dom fill "#search" "query"
bdg dom pressKey "#search" Enter # Automatically waits for stability
bdg dom query ".result-item" --json | jq '.count'bdg https://example.com
bdg peek --network --json | jq '.data.network[] | select(.status >= 400)'bdg https://example.com
# Browse around...
bdg network har session.har
bdg stop
# Open session.har in Chrome DevTools or HAR Viewerbdg https://example.com
bdg network headers --header content-security-policy
bdg network headers --header strict-transport-security
bdg network headers --header x-frame-optionsbdg https://app.example.com
bdg tail --network
# Or filter to API calls only
bdg peek --type XHR,Fetch --jsonbdg https://example.com
bdg dom a11y query role=button --json | jq '.nodes[] | select(.name == null or .name == "")'bdg dom a11y query role=textbox --json | jq '.nodes[] | {id: .nodeId, label: .name}'bdg dom a11y query role=heading --json | jq '.nodes[] | {level: .properties.level, name: .name}'bdg https://example.com
bdg cdp Performance.enable
bdg cdp Performance.getMetrics --json | jq '.metrics[] | select(.name | contains("Heap"))'bdg cdp Tracing.start --params '{"categories":"devtools.timeline"}'
# Load page or perform actions
bdg cdp Tracing.endwhile ! bdg dom query "#loaded" --json | jq -e '.count > 0'; do
sleep 0.5
done
echo "Element appeared"bdg https://example.com
# Network idle is automatic, but for manual control:
while bdg peek --network --json | jq -e '[.data.network[] | select(.status == null)] | length > 0'; do
sleep 0.5
donebdg network getCookies
bdg network getCookies --json | jq '.cookies'bdg network getCookies --json | jq '.cookies[] | select(.httpOnly)'bdg cdp Network.clearBrowserCookiesbdg https://example.com
bdg console # Current page: errors/warnings deduplicated
bdg console --json # JSON with summary stats
bdg console --history # All page loads (if you navigated)bdg console --follow # Live streaming (like tail -f)bdg console --list # All messages chronologically
bdg console --list --last 50 # Last 50 messagesbdg details console 0 # Full details for first messagebdg peek --console --json | jq '.data.console | group_by(.type) | map({type: .[0].type, count: length})'#!/bin/bash
set -e
bdg https://example.com --headless
# Run tests (--json is required for jq)
bdg dom query ".error" --json | jq -e '.count == 0' || exit 1
# Take evidence screenshot
bdg dom screenshot evidence.png
# Export network log
bdg network har ci-run.har
bdg stopurls=("https://example1.com" "https://example2.com" "https://example3.com")
for url in "${urls[@]}"; do
bdg "$url" --headless
bdg dom screenshot "$(echo $url | sed 's/[^a-zA-Z0-9]/_/g').png"
bdg stop
donebdg https://example.com
# Navigate to another page (Page.navigate waits for load automatically)
bdg cdp Page.navigate --params '{"url":"https://example.com/page2"}'
# Check current URL
bdg dom eval "window.location.href"
# Alternative: Navigate via JavaScript
bdg dom eval "window.location.href = 'https://example.com'"bdg https://example.com
# Click link (automatically waits for navigation and stability)
bdg dom click "a[href='/about']"
# Verify new page
bdg dom eval "document.title"bdg https://app.example.com
# For React Router/Vue Router apps
bdg dom click "[href='/dashboard']"
# Wait for route change
while ! bdg dom query "#dashboard" --json | jq -e '.count > 0'; do
sleep 0.5
donebdg https://example.com
bdg cdp Page.getFrameTree --json | jq '.frameTree'# Note: This requires execution context ID, not frame ID
# Get frame ID first
FRAME_ID=$(bdg cdp Page.getFrameTree | jq -r '.frameTree.childFrames[0].frame.id')
# Option 1: Use DOM navigation (simpler for most cases)
bdg dom eval '
const iframe = document.querySelector("iframe");
iframe.contentWindow.document.title;
'
# Option 2: Use CDP with proper context (advanced)
# First enable Runtime domain, then get execution contexts for the frame
bdg cdp Runtime.enable
# Then evaluate in the specific frame using frameId parameter
bdg cdp Runtime.evaluate --params '{
"expression": "document.title",
"returnByValue": true,
"frameId": "'$FRAME_ID'"
}'# Main frame
bdg dom query "button"
# For iframe content, use CDP directly
bdg cdp DOM.getDocument
bdg cdp DOM.querySelector --params '{"nodeId":1,"selector":"iframe"}'bdg https://app.example.com
# Use CDP to pierce shadow roots
bdg cdp DOM.getDocument
# Query with piercing selector (if supported)
bdg dom eval '
document.querySelector("my-component")
.shadowRoot.querySelector("button")
.textContent
'# Get element via JavaScript
bdg dom eval '
const component = document.querySelector("my-component");
const button = component.shadowRoot.querySelector("button");
button.click();
'bdg https://react-app.example.com
# Method 1: Wait for React DevTools hook (works in dev mode)
while ! bdg dom eval 'window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 0'; do
sleep 0.5
done
# Method 2: Wait for specific element that appears after hydration
while ! bdg dom query "#dashboard" --json | jq -e '.count > 0'; do
sleep 0.5
done
# Method 3: Check for React 18+ root (Note: internal API, may change)
while ! bdg dom eval 'document.querySelector("#root")?._reactRootContainer || document.querySelector("#root")?._reactRoot'; do
sleep 0.5
done
# Now safe to interact
bdg dom query "button"# Access React internals (dev mode only)
bdg dom eval '
const element = document.querySelector("#app");
const fiber = Object.keys(element).find(k => k.startsWith("__reactFiber"));
element[fiber].memoizedState;
'bdg dom eval '
const app = document.querySelector("#app").__vue__;
app.$data;
'bdg https://angular-app.example.com
# Wait for Angular to be ready
while ! bdg dom eval 'typeof ng !== "undefined"' --json | jq -e '.result.value == true'; do
sleep 0.5
donebdg https://example.com/upload
# Set file input value (limited - files must be accessible to Chrome)
bdg cdp DOM.setFileInputFiles --params '{
"files": ["/path/to/file.txt"],
"nodeId": 123
}'bdg https://example.com
# Enable download tracking
bdg cdp Browser.setDownloadBehavior --params '{
"behavior": "allow",
"downloadPath": "/tmp/downloads"
}'
# Click download link
bdg dom click "a[download]"
# Monitor download events via CDP
bdg cdp Browser.downloadWillBeginbdg https://example.com
# Enable fetch interception
bdg cdp Fetch.enable --params '{"patterns":[{"urlPattern":"*"}]}'
# Pause requests
bdg cdp Fetch.requestPaused
# Modify or continue
bdg cdp Fetch.continueRequest --params '{"requestId":"..."}'bdg https://example.com
bdg cdp Emulation.setDeviceMetricsOverride --params '{
"width": 375,
"height": 812,
"deviceScaleFactor": 3,
"mobile": true
}'
bdg cdp Emulation.setUserAgentOverride --params '{
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15"
}'bdg https://example.com --headless
# Start screencast
bdg cdp Page.startScreencast --params '{
"format": "png",
"quality": 80,
"maxWidth": 1920,
"maxHeight": 1080
}'
# Perform actions...
# Stop screencast
bdg cdp Page.stopScreencast