SolveitCDP API

Source and API details
from fastcore.test import test_eq
len(_cdp_domains), [d['domain'] for d in _cdp_domains[:5]]
(55, ['Accessibility', 'Animation', 'Audits', 'Autofill', 'BackgroundService'])

source

js_cdp


async def js_cdp(
    method, tabId:NoneType=None, params:VAR_KEYWORD
):

Call self as a function.

await js_cdp('Runtime.evaluate', tabId=tid, expression='document.title')
{'type': 'string', 'value': '6. Example Domain'}

source

JsCDP


def JsCDP(
    tid
):

Chrome DevTools Protocol via JS bridge with event support

jc = await JsCDP.new()

We can subscribe to events thanks to the solveit-chrome extension bridge:


source

JsCDP.on


def on(
    events:VAR_POSITIONAL
):

Call self as a function.


source

JsCDP.unsubscribe


async def unsubscribe(
    sub_id
):

Call self as a function.


source

JsSub


def JsSub(
    cdp, events
):

Initialize self. See help(type(self)) for accurate signature.

async with jc.on('Page.loadEventFired') as sub:
    await jc('Page.navigate', url='https://example.com')
    evt = await sub()
await jc.close()

source

JsCDPMethod


def JsCDPMethod(
    cdp, domain, method
):

Initialize self. See help(type(self)) for accurate signature.


source

JsCDPDomain


def JsCDPDomain(
    cdp, domain
):

Initialize self. See help(type(self)) for accurate signature.


source

JsCDP.eval


async def eval(
    expr
):

Call self as a function.


source

JsCDP.wait_for_selector


async def wait_for_selector(
    sel, timeout:int=10
):

Wait for CSS selector to match an element


source

JsCDP.wait_for


async def wait_for(
    expr, timeout:int=10
):

Wait for JS expression to be truthy, return its value

jc = await JsCDP.new()
await jc.page.navigate(url='https://example.com')
await jc.wait_for('document.title')
'Example Domain'
await jc.close()

source

JsCDP.goto


async def goto(
    url, kwargs:VAR_KEYWORD
):

Navigate to url and wait for load+idle


source

JsCDP.wait_for_ready


async def wait_for_ready(
    timeout:int=10, idle_ms:int=500
):

Wait until network is idle for idle_ms

jc = await JsCDP.new()
await jc.goto('https://example.com')
await jc.eval('document.title')
'6. Example Domain'

source

JsCDP.pages


async def pages(
    
):

Call self as a function.

pages = await jc.pages
for p in pages[:3]: print(f"{p.get('tabId', '?'):>10} {p.get('title', '')[:60]}")
pg = first(p for p in pages if 'Example' in p['title'])
pg['url']
 895199784 8. Database Transactions — PlanetScale
 895200613 2. l:SolveIT - nbs◀️dialoghelper◀️aai-ws
 895199539 2. [AnswerDotAI/safepyrun] Run failed: Deploy to GitHub Page
'https://example.com/'
jc2 = await JsCDP.new(pg['tabId'])
await jc2.eval('document.title')
'5. Example Domain'

source

JsCDP.screenshot


async def screenshot(
    
):

Call self as a function.

# await jc2.screenshot()
await jc2.close()

LLMs and accessibility

page = await JsCDP.new(url='https://httpbin.org/forms/post')
await page.eval('document.title')
'5. httpbin.org/forms/post'
await page.accessibility.enable()
tree = await page.accessibility.getFullAXTree()
len(tree)
90
tree[0]
{ 'backendDOMNodeId': 14,
  'childIds': ['15'],
  'chromeRole': {'type': 'internalRole', 'value': 144},
  'frameId': '92354B6BB2A591B3DDB0267C24E21365',
  'ignored': False,
  'name': { 'sources': [{'attribute': 'aria-labelledby', 'type': 'relatedElement'}, {'attribute': 'aria-label', 'type': 'attribute'}, {'attribute': 'aria-label', 'superseded': True, 'type': 'attribute'}, {'nativeSource': 'title', 'type': 'relatedElement', 'value': {'type': 'computedString', 'value': '5. httpbin.org/forms/post'}}],
            'type': 'computedString',
            'value': '5. httpbin.org/forms/post'},
  'nodeId': '14',
  'properties': [{'name': 'focusable', 'value': {'type': 'booleanOrUndefined', 'value': True}}, {'name': 'url', 'value': {'type': 'string', 'value': 'https://httpbin.org/forms/post'}}],
  'role': {'type': 'internalRole', 'value': 'RootWebArea'}}
await page.close()

source

AXTree


def AXTree(
    raw
):

Chrome accessibility tree node with compact repr


source

AXNode


def AXNode(
    raw
):

Chrome accessibility tree node with compact repr


source

build_ax_tree


def build_ax_tree(
    nodes
):

Build AXNode tree from flat CDP accessibility node list


source

JsCDP.ax_tree


async def ax_tree(
    sid:NoneType=None
):

Get accessibility tree for session

page = await JsCDP.new(url='https://httpbin.org/forms/post')
root = await page.ax_tree()
root
  • RootWebArea “5. httpbin.org/forms/post” focusable=True url=https://httpbin.org/forms/post [#14]
    • LabelText “” [#20]
      • StaticText “Customer name:” [#62]
        • InlineTextBox “Customer name:”
      • textbox “Customer name:” focusable=True editable=plaintext settable=True [#2]
    • LabelText “” [#23]
      • StaticText “Telephone:” [#63]
        • InlineTextBox “Telephone:”
      • textbox “Telephone:” focusable=True editable=plaintext settable=True [#3]
    • LabelText “” [#26]
      • StaticText “E-mail address:” [#64]
        • InlineTextBox “E-mail address:”
      • textbox “E-mail address:” focusable=True editable=plaintext settable=True [#4]
    • group “Pizza Size” [#28]
      • Legend “” [#29]
        • StaticText “Pizza Size” [#65]
          • InlineTextBox “Pizza Size”
      • radio ” Small” focusable=True [#6]
      • radio ” Medium” focusable=True [#7]
      • radio ” Large” focusable=True [#8]
    • group “Pizza Toppings” [#36]
      • Legend “” [#37]
        • StaticText “Pizza Toppings” [#69]
          • InlineTextBox “Pizza Toppings”
      • checkbox ” Bacon” focusable=True [#9]
      • checkbox ” Extra Cheese” focusable=True [#10]
      • checkbox ” Onion” focusable=True [#11]
      • checkbox ” Mushroom” focusable=True [#12]
    • LabelText “” [#47]
      • StaticText “Preferred delivery time:” [#74]
        • InlineTextBox “Preferred delivery time:”
      • InputTime “Preferred delivery time:” focusable=True settable=True [#13]
        • spinbutton “Hours Hours” focusable=True settable=True valuemin=1 valuemax=12 [#51]
          • StaticText “–” [#75]
            • InlineTextBox “–”
        • StaticText “:” [#76]
          • InlineTextBox “:”
        • spinbutton “Minutes Minutes” focusable=True settable=True valuemax=59 [#53]
          • StaticText “–” [#77]
            • InlineTextBox “–”
        • StaticText ” ” [#78]
          • InlineTextBox ” ”
        • spinbutton “AM/PM AM/PM” focusable=True settable=True valuemin=1 valuemax=2 [#55]
          • StaticText “–” [#79]
            • InlineTextBox “–”
        • button “Show time picker” focusable=True hasPopup=menu [#56]
    • LabelText “” [#58]
      • StaticText “Delivery instructions:” [#80]
        • InlineTextBox “Delivery instructions:”
      • textbox “Delivery instructions:” focusable=True editable=plaintext settable=True multiline=True [#5]
    • button “Submit order” focusable=True [#61]
      • StaticText “Submit order” [#81]
        • InlineTextBox “Submit order”

source

AXNode.find_all


def find_all(
    role:NoneType=None, name:NoneType=None
):

Find all descendants matching role and/or name substring


source

AXNode.find_id


def find_id(
    role:NoneType=None, name:NoneType=None
):

Find first descendant matching role and/or name substring


source

AXNode.find


def find(
    role:NoneType=None, name:NoneType=None
):

Find first descendant matching role and/or name substring

nmid = root.find_id('textbox', 'Customer name')
phid = root.find_id('textbox', 'Telephone')
nmid
2

source

JsCDP.click


async def click(
    backendNodeId, sid:NoneType=None
):

Call self as a function.


source

JsCDP.js_node_run


async def js_node_run(
    code, backendNodeId, sid:NoneType=None
):

Call self as a function.


source

JsCDP.js_node


async def js_node(
    fn, backendNodeId, sid:NoneType=None
):

Call self as a function.

await page.DOM.focus(backendNodeId=nmid)
await page.input.insertText(text='Jeremy Howard')

await page.DOM.focus(backendNodeId=phid)
await page.input.insertText(text='555-1234')
{}
await page.click(root.find_id('radio', 'Large'))
await page.click(root.find_id('checkbox', 'Extra Cheese'))

source

JsCDP.fill_text


async def fill_text(
    backendNodeId, text, sid:NoneType=None
):

Call self as a function.

await page.fill_text(root.find_id('textbox', 'Delivery'), 'Ring the doorbell twice')
await page.js_node_run('this.value = "18:30"', root.find_id('InputTime', 'delivery time'));

source

JsCDP.click_and_wait


async def click_and_wait(
    backendNodeId, kwargs:VAR_KEYWORD
):

Click element and wait for load+idle

await page.click_and_wait(root.find_id('button', 'Submit order'))

source

cdp_yolo


def cdp_yolo(
    
):

Allow all CDP classes in safepyrun