Files
hive/tools/manual_browser_test.py
T
2026-04-03 20:31:14 -07:00

280 lines
8.9 KiB
Python

#!/usr/bin/env python
"""
Manual browser tools test - connects to real Chrome extension.
Prerequisites:
1. Chrome with Beeline extension installed and enabled
2. Run: uv run python manual_browser_test.py
This will test:
- Bridge connection
- Tab group creation
- Navigation
- Click, type, scroll interactions
- Snapshot/screenshot
- Complex JS execution (LinkedIn-style selectors)
"""
import asyncio
import sys
from pathlib import Path
# Add src to path
sys.path.insert(0, str(Path(__file__).parent / "src"))
from gcu.browser.bridge import BeelineBridge
async def test_connection(bridge: BeelineBridge) -> bool:
"""Test 1: Extension connection."""
print("\n=== Test 1: Extension Connection ===")
print("Starting bridge on port 9229...")
await bridge.start()
for i in range(5):
await asyncio.sleep(1)
if bridge.is_connected:
print("✓ Extension connected!")
return True
print(f" Waiting... ({i + 1}/5)")
print("✗ Extension not connected. Ensure Chrome extension is installed.")
return False
async def test_context_creation(bridge: BeelineBridge) -> dict | None:
"""Test 2: Create tab group/context."""
print("\n=== Test 2: Create Tab Group ===")
try:
result = await bridge.create_context("manual-test-agent")
print(f"✓ Created context: groupId={result.get('groupId')}, tabId={result.get('tabId')}")
return result
except Exception as e:
print(f"✗ Failed: {e}")
return None
async def test_navigation(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 3: Navigate to example.com."""
print("\n=== Test 3: Navigation ===")
try:
result = await bridge.navigate(tab_id, "https://example.com", wait_until="load")
print(f"✓ Navigated to: {result.get('url')}")
await asyncio.sleep(1)
return True
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_snapshot(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 4: Get accessibility snapshot."""
print("\n=== Test 4: Accessibility Snapshot ===")
try:
result = await bridge.snapshot(tab_id)
tree = result.get("tree", "")
lines = tree.split("\n")[:10]
print(f"✓ Got snapshot ({len(tree)} chars)")
print(" First 10 lines:")
for line in lines:
print(f" {line}")
return True
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_click(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 5: Click a link."""
print("\n=== Test 5: Click Element ===")
try:
# example.com has a link to "More information..."
result = await bridge.click(tab_id, "a", timeout_ms=5000)
if result.get("ok"):
print(f"✓ Clicked link at ({result.get('x')}, {result.get('y')})")
await asyncio.sleep(2)
# Go back
await bridge.go_back(tab_id)
await asyncio.sleep(1)
return True
else:
print(f"✗ Click failed: {result.get('error')}")
return False
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_type_and_clear(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 6: Type into an input field."""
print("\n=== Test 6: Type Text ===")
try:
# Navigate to a page with an input
await bridge.navigate(tab_id, "https://www.google.com", wait_until="load")
await asyncio.sleep(2)
# Type in search box
result = await bridge.type_text(tab_id, "textarea[name='q']", "hello world")
if result.get("ok"):
print("✓ Typed 'hello world' into search box")
await asyncio.sleep(0.5)
# Clear and type something else
await bridge.press_key(tab_id, "Control+a")
await asyncio.sleep(0.2)
await bridge.type_text(tab_id, "textarea[name='q']", "new search")
print("✓ Replaced with 'new search'")
return True
else:
print(f"✗ Type failed: {result.get('error')}")
return False
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_scroll(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 7: Scroll page."""
print("\n=== Test 7: Scroll ===")
try:
# Scroll down
result = await bridge.scroll(tab_id, "down", 500)
if result.get("ok"):
print("✓ Scrolled down 500px")
await asyncio.sleep(0.5)
# Scroll up
await bridge.scroll(tab_id, "up", 250)
print("✓ Scrolled up 250px")
return True
else:
print(f"✗ Scroll failed: {result.get('error')}")
return False
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_evaluate_js(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 8: Execute JavaScript."""
print("\n=== Test 8: JavaScript Execution ===")
try:
# Simple return
result = await bridge.evaluate(tab_id, "return document.title;")
print(f"✓ Page title: {result.get('result', {}).get('value')}")
# Complex selector (LinkedIn-style)
complex_script = """
const links = document.querySelectorAll('a');
return {
total: links.length,
first: links[0]?.href || null
};
"""
result = await bridge.evaluate(tab_id, complex_script)
value = result.get("result", {}).get("value", {})
print(f"✓ Found {value.get('total')} links, first: {value.get('first', 'N/A')[:50]}...")
return True
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_screenshot(bridge: BeelineBridge, tab_id: int) -> bool:
"""Test 9: Take screenshot."""
print("\n=== Test 9: Screenshot ===")
try:
result = await bridge.screenshot(tab_id, full_page=False)
if result.get("ok"):
data = result.get("data", "")
print(f"✓ Screenshot captured ({len(data)} chars base64)")
return True
else:
print(f"✗ Screenshot failed: {result.get('error')}")
return False
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def test_tab_management(bridge: BeelineBridge, group_id: int, tab_id: int) -> bool:
"""Test 10: Create and close tabs."""
print("\n=== Test 10: Tab Management ===")
try:
# Create new tab
new_tab = await bridge.create_tab(group_id, "https://httpbin.org")
new_tab_id = new_tab.get("tabId")
print(f"✓ Created new tab: {new_tab_id}")
await asyncio.sleep(2)
# List tabs
tabs = await bridge.list_tabs(group_id)
print(f"✓ Group has {len(tabs.get('tabs', []))} tabs")
# Close the new tab
await bridge.close_tab(new_tab_id)
print(f"✓ Closed tab {new_tab_id}")
return True
except Exception as e:
print(f"✗ Failed: {e}")
return False
async def main():
print("=" * 60)
print("MANUAL BROWSER TOOLS TEST")
print("=" * 60)
bridge = BeelineBridge()
try:
# Test 1: Connection
if not await test_connection(bridge):
print("\n❌ Cannot proceed without extension connection")
return
# Test 2: Context creation
context = await test_context_creation(bridge)
if not context:
print("\n❌ Cannot proceed without context")
return
tab_id = context.get("tabId")
group_id = context.get("groupId")
results = []
# Run all tests
results.append(("Navigation", await test_navigation(bridge, tab_id)))
results.append(("Snapshot", await test_snapshot(bridge, tab_id)))
results.append(("Click", await test_click(bridge, tab_id)))
results.append(("Type", await test_type_and_clear(bridge, tab_id)))
results.append(("Scroll", await test_scroll(bridge, tab_id)))
results.append(("Evaluate JS", await test_evaluate_js(bridge, tab_id)))
results.append(("Screenshot", await test_screenshot(bridge, tab_id)))
results.append(("Tab Management", await test_tab_management(bridge, group_id, tab_id)))
# Cleanup
print("\n=== Cleanup ===")
await bridge.destroy_context(group_id)
print("✓ Destroyed context")
# Summary
print("\n" + "=" * 60)
print("RESULTS SUMMARY")
print("=" * 60)
passed = sum(1 for _, r in results if r)
total = len(results)
for name, result in results:
status = "✓ PASS" if result else "✗ FAIL"
print(f" {status}: {name}")
print(f"\nTotal: {passed}/{total} passed")
finally:
await bridge.stop()
print("\nBridge stopped.")
if __name__ == "__main__":
asyncio.run(main())