Skip to content

Recipes

Kumak edited this page Nov 22, 2025 · 5 revisions

Recipes

Common workflows and automation patterns.

Best Practices

Prefer high-level commands when available:

  • Use bdg dom eval instead of bdg cdp Runtime.evaluate
  • Use bdg network getCookies instead of bdg cdp Network.getCookies
  • Use bdg dom commands 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.

Web Scraping

Extract All Links

bdg https://example.com
bdg dom eval "Array.from(document.querySelectorAll('a')).map(a => ({text: a.textContent.trim(), href: a.href}))"
bdg stop

Get Page Title and Meta

bdg https://example.com
bdg dom eval "document.title"
bdg dom eval "document.querySelector('meta[name=description]')?.content"

Screenshot with Custom Viewport

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.png

Form Automation

Login Flow

bdg 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"

Search and Extract Results

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'

Network Analysis

Find Failed Requests

bdg https://example.com
bdg peek --network --json | jq '.data.network[] | select(.status >= 400)'

Export HAR for Analysis

bdg https://example.com
# Browse around...
bdg network har session.har
bdg stop
# Open session.har in Chrome DevTools or HAR Viewer

Check Security Headers

bdg https://example.com
bdg network headers --header content-security-policy
bdg network headers --header strict-transport-security
bdg network headers --header x-frame-options

Monitor XHR/Fetch Requests

bdg https://app.example.com
bdg tail --network
# Or filter to API calls only
bdg peek --type XHR,Fetch --json

Accessibility Testing

Find Unlabeled Buttons

bdg https://example.com
bdg dom a11y query role=button --json | jq '.nodes[] | select(.name == null or .name == "")'

Verify Form Labels

bdg dom a11y query role=textbox --json | jq '.nodes[] | {id: .nodeId, label: .name}'

Check Heading Structure

bdg dom a11y query role=heading --json | jq '.nodes[] | {level: .properties.level, name: .name}'

Performance

Get Performance Metrics

bdg https://example.com
bdg cdp Performance.enable
bdg cdp Performance.getMetrics --json | jq '.metrics[] | select(.name | contains("Heap"))'

Trace Page Load

bdg cdp Tracing.start --params '{"categories":"devtools.timeline"}'
# Load page or perform actions
bdg cdp Tracing.end

Polling Patterns

Wait for Element

while ! bdg dom query "#loaded" --json | jq -e '.count > 0'; do
  sleep 0.5
done
echo "Element appeared"

Wait for Network Idle

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
done

Cookie Management

Get All Cookies

bdg network getCookies
bdg network getCookies --json | jq '.cookies'

Get HttpOnly Cookies

bdg network getCookies --json | jq '.cookies[] | select(.httpOnly)'

Clear Cookies

bdg cdp Network.clearBrowserCookies

Console Monitoring

Smart Error Summary

bdg 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)

Watch for Errors in Real-Time

bdg console --follow                 # Live streaming (like tail -f)

List All Console Messages

bdg console --list                   # All messages chronologically
bdg console --list --last 50         # Last 50 messages

Get Message Details with Stack Trace

bdg details console 0                # Full details for first message

Count Console Messages by Type

bdg peek --console --json | jq '.data.console | group_by(.type) | map({type: .[0].type, count: length})'

Headless Automation

CI/CD Pattern

#!/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 stop

Batch Processing

urls=("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
done

Multi-Page Navigation

Navigate Through Pages

bdg 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'"

Click Link and Wait for Navigation

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"

Handle SPA Navigation

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
done

Working with Iframes

List All Frames

bdg https://example.com
bdg cdp Page.getFrameTree --json | jq '.frameTree'

Execute Script in Iframe

# 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'"
}'

Query Elements Across Frames

# 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"}'

Shadow DOM

Pierce Shadow DOM

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
'

Interact with Shadow DOM Elements

# Get element via JavaScript
bdg dom eval '
  const component = document.querySelector("my-component");
  const button = component.shadowRoot.querySelector("button");
  button.click();
'

Framework-Specific Patterns

React: Wait for Hydration

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"

React: Get Component State

# 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;
'

Vue: Check Reactive Data

bdg dom eval '
  const app = document.querySelector("#app").__vue__;
  app.$data;
'

Angular: Wait for Bootstrap

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
done

File Upload/Download

Simulate File Upload

bdg 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
}'

Monitor Downloads

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.downloadWillBegin

Advanced Patterns

Intercept Network Requests

bdg 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":"..."}'

Emulate Mobile Device

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"
}'

Capture Video (Advanced)

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

Clone this wiki locally