#!/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())