4.0 KiB
JS-Rendered API Documentation Pages
Pattern observed when browsing OpenAPI/Redoc-styled docs (like RingLogix, Swagger, Stoplight):
What the Accessibility Tree Shows
- Sidebar/nav tree — fully accessible via
browser_snapshot(full=true). Category names, endpoint paths, and hierarchical structure appear as text nodes. - Main content pane — the actual API spec (endpoint details, parameters, schemas, sample requests) renders in a JavaScript-driven viewer (Redoc, Swagger UI, Scalar). The accessibility tree may show these as opaque elements with
role="region"or similar, with no rich text content. - Search/filter box at the top — accessible for navigation.
Recommended Probe Sequence
browser_navigate(url)— loads the pagebrowser_snapshot(full=true)— get the full sidebar nav structure- If sidebar content is present but main pane is empty:
browser_click(element)on a sidebar link to navigate to a specific endpointbrowser_snapshot(full=true)again after navigation (JS may then render that section)- Or use
browser_visionto screenshot and analyze visually
CDP Bulk Extraction (for large Redoc/Swagger pages)
When the SPA loads a huge page (e.g. 238 endpoints, 186K chars of docs), browser_snapshot truncates at ~18K lines and browser_console JS selectors may find no <nav> element (Redoc renders into custom elements or shadow DOM). Use this CDP-based probe sequence instead:
Step 1: Navigate and identify the correct target
browser_navigate(url)
// Wait ~2s for SPA bootstrap
browser_snapshot(full=true) // Sidebar should be visible
Step 2: List all browser targets via CDP
browser_cdp(method="Target.getTargets", params={})
Look for the page with the expected title (NOT chrome://newtab). Returns targetInfos array — the target you navigated to has the real URL and title field.
Step 3: Extract text via CDP on the correct target
browser_cdp(
target_id="<target ID from Step 2>",
method="Runtime.evaluate",
params={
expression: "document.body.innerText.substring(0, 8000)",
returnByValue: true
}
)
Page through sections by changing the substring range. For large docs (e.g. RingLogix: 186K chars), use intervals of 3000-4000 at a time.
Step 4: Get full sidebar nav list via CDP
browser_cdp(
target_id="<target ID>",
method="Runtime.evaluate",
params={
expression: "Array.from(document.querySelectorAll('li a')).map(a => a.textContent.trim()).filter(t => t)",
returnByValue: true
}
)
Returns the complete sidebar as an ordered array of link text.
Step 5: Confirm page identity
browser_cdp(
target_id="<target ID>",
method="Runtime.evaluate",
params={
expression: "document.title",
returnByValue: true
}
)
Key differences from browser_console
| Aspect | browser_console |
browser_cdp + Runtime.evaluate |
|---|---|---|
| Cap | Expression length | None (full function bodies ok) |
| Target | Current browser tab | Any target by ID |
| Shadow DOM | Limited | Limited (same as console) |
| Return size | Moderate | Large (substring approach) |
| Syntax errors | Reported inline | Fail silently on malformed arrow functions — use (function(){...})() not ()=>{...} |
Confirmed Case: RingLogix API Docs
URL: https://apidocs.ringlogix.com/ Status: Public, no login required Content: ~186K characters of API documentation on page load Structure: Sidebar with OAuth2, Agents, Agent Log, Answer Rule, Audio, CDR2, CDR Export, CDR Schedule, Call, Call Center Stats, Call Queue, Call Queue Stats, Subscriber, Device, Message, Emergency Address ~7,800+ elements in the DOM
Confirmed Case: browser_console caveats
Two pitfalls observed:
querySelector('nav')returns null on Redoc pages — the sidebar renders as custom elements, not<nav>.- Arrow functions (
() => {...}) with template literals can triggerSyntaxError: Unexpected end of inputviabrowser_console. Use(function(){...})()syntax instead for complex expressions.