[Add] browser-use and main.py
This commit is contained in:
parent
08e64bdf45
commit
96914d44ac
221 changed files with 30952 additions and 1 deletions
39
browser-use/browser_use/browser/tests/httpx_client_test.py
Normal file
39
browser-use/browser_use/browser/tests/httpx_client_test.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import httpx
|
||||
import pytest
|
||||
|
||||
from browser_use.browser.browser import Browser, BrowserConfig
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_browser_close_doesnt_affect_external_httpx_clients():
|
||||
"""
|
||||
Test that Browser.close() doesn't close HTTPX clients created outside the Browser instance.
|
||||
This test demonstrates the issue where Browser.close() is closing all HTTPX clients.
|
||||
"""
|
||||
# Create an external HTTPX client that should remain open
|
||||
external_client = httpx.AsyncClient()
|
||||
|
||||
# Create a Browser instance
|
||||
browser = Browser(config=BrowserConfig(headless=True))
|
||||
|
||||
# Close the browser (which should trigger cleanup_httpx_clients)
|
||||
await browser.close()
|
||||
|
||||
# Check if the external client is still usable
|
||||
try:
|
||||
# If the client is closed, this will raise RuntimeError
|
||||
# Using a simple HEAD request to a reliable URL
|
||||
await external_client.head('https://www.example.com', timeout=2.0)
|
||||
client_is_closed = False
|
||||
except RuntimeError as e:
|
||||
# If we get "Cannot send a request, as the client has been closed"
|
||||
client_is_closed = 'client has been closed' in str(e)
|
||||
except Exception:
|
||||
# Any other exception means the client is not closed but request failed
|
||||
client_is_closed = False
|
||||
finally:
|
||||
# Always clean up our test client properly
|
||||
await external_client.aclose()
|
||||
|
||||
# Our external client should not be closed by browser.close()
|
||||
assert not client_is_closed, 'External HTTPX client was incorrectly closed by Browser.close()'
|
||||
36
browser-use/browser_use/browser/tests/screenshot_test.py
Normal file
36
browser-use/browser_use/browser/tests/screenshot_test.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import asyncio
|
||||
import base64
|
||||
|
||||
import pytest
|
||||
|
||||
from browser_use.browser.browser import Browser, BrowserConfig
|
||||
|
||||
|
||||
async def test_take_full_page_screenshot():
|
||||
browser = Browser(config=BrowserConfig(headless=False, disable_security=True))
|
||||
try:
|
||||
async with await browser.new_context() as context:
|
||||
page = await context.get_current_page()
|
||||
# Go to a test page
|
||||
await page.goto('https://example.com')
|
||||
|
||||
await asyncio.sleep(3)
|
||||
# Take full page screenshot
|
||||
screenshot_b64 = await context.take_screenshot(full_page=True)
|
||||
await asyncio.sleep(3)
|
||||
# Verify screenshot is not empty and is valid base64
|
||||
assert screenshot_b64 is not None
|
||||
assert isinstance(screenshot_b64, str)
|
||||
assert len(screenshot_b64) > 0
|
||||
|
||||
# Test we can decode the base64 string
|
||||
try:
|
||||
base64.b64decode(screenshot_b64)
|
||||
except Exception as e:
|
||||
pytest.fail(f'Failed to decode base64 screenshot: {str(e)}')
|
||||
finally:
|
||||
await browser.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(test_take_full_page_screenshot())
|
||||
96
browser-use/browser_use/browser/tests/test_clicks.py
Normal file
96
browser-use/browser_use/browser/tests/test_clicks.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import asyncio
|
||||
import json
|
||||
|
||||
import anyio
|
||||
import pytest
|
||||
|
||||
from browser_use.browser.browser import Browser, BrowserConfig
|
||||
from browser_use.dom.views import DOMBaseNode, DOMElementNode, DOMTextNode
|
||||
from browser_use.utils import time_execution_sync
|
||||
|
||||
|
||||
class ElementTreeSerializer:
|
||||
@staticmethod
|
||||
def dom_element_node_to_json(element_tree: DOMElementNode) -> dict:
|
||||
def node_to_dict(node: DOMBaseNode) -> dict:
|
||||
if isinstance(node, DOMTextNode):
|
||||
return {'type': 'text', 'text': node.text}
|
||||
elif isinstance(node, DOMElementNode):
|
||||
return {
|
||||
'type': 'element',
|
||||
'tag_name': node.tag_name,
|
||||
'attributes': node.attributes,
|
||||
'highlight_index': node.highlight_index,
|
||||
'children': [node_to_dict(child) for child in node.children],
|
||||
}
|
||||
return {}
|
||||
|
||||
return node_to_dict(element_tree)
|
||||
|
||||
|
||||
# run with: pytest browser_use/browser/tests/test_clicks.py
|
||||
@pytest.mark.asyncio
|
||||
async def test_highlight_elements():
|
||||
browser = Browser(config=BrowserConfig(headless=False, disable_security=True))
|
||||
|
||||
async with await browser.new_context() as context:
|
||||
page = await context.get_current_page()
|
||||
# await page.goto('https://immobilienscout24.de')
|
||||
# await page.goto('https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/service-plans')
|
||||
# await page.goto('https://google.com/search?q=elon+musk')
|
||||
# await page.goto('https://kayak.com')
|
||||
# await page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe')
|
||||
# await page.goto('https://dictionary.cambridge.org')
|
||||
# await page.goto('https://github.com')
|
||||
await page.goto('https://huggingface.co/')
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
while True:
|
||||
try:
|
||||
# await asyncio.sleep(10)
|
||||
state = await context.get_state(True)
|
||||
|
||||
async with await anyio.open_file('./tmp/page.json', 'w') as f:
|
||||
await f.write(
|
||||
json.dumps(
|
||||
ElementTreeSerializer.dom_element_node_to_json(state.element_tree),
|
||||
indent=1,
|
||||
)
|
||||
)
|
||||
|
||||
# await time_execution_sync('highlight_selector_map_elements')(
|
||||
# browser.highlight_selector_map_elements
|
||||
# )(state.selector_map)
|
||||
|
||||
# Find and print duplicate XPaths
|
||||
xpath_counts = {}
|
||||
if not state.selector_map:
|
||||
continue
|
||||
for selector in state.selector_map.values():
|
||||
xpath = selector.xpath
|
||||
if xpath in xpath_counts:
|
||||
xpath_counts[xpath] += 1
|
||||
else:
|
||||
xpath_counts[xpath] = 1
|
||||
|
||||
print('\nDuplicate XPaths found:')
|
||||
for xpath, count in xpath_counts.items():
|
||||
if count > 1:
|
||||
print(f'XPath: {xpath}')
|
||||
print(f'Count: {count}\n')
|
||||
|
||||
print(list(state.selector_map.keys()), 'Selector map keys')
|
||||
print(state.element_tree.clickable_elements_to_string())
|
||||
action = input('Select next action: ')
|
||||
|
||||
await time_execution_sync('remove_highlight_elements')(context.remove_highlights)()
|
||||
|
||||
node_element = state.selector_map[int(action)]
|
||||
|
||||
# check if index of selector map are the same as index of items in dom_items
|
||||
|
||||
await context._click_element_node(node_element)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
Loading…
Add table
Add a link
Reference in a new issue