680 lines
23 KiB
Python
Raw Normal View History

2025-08-11 13:28:18 +02:00
#!/usr/bin/env python3
"""
Comprehensive Demo of Playwright-style WebSocket Browser Control
Shows both backward-compatible and new Playwright-style APIs.
"""
2025-07-17 00:51:02 +02:00
import asyncio
2025-08-11 13:28:18 +02:00
import logging
import sys
import os
from datetime import datetime
from client import Browser, Playwright, browser
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
async def demo_basic_navigation():
"""Demo basic navigation features"""
print("\n=== Basic Navigation Demo ===")
# Create browser instance
browser_instance = Browser("ws://localhost:8765")
await browser_instance.connect()
try:
# Create a page
page = await browser_instance.new_page()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Navigate to a website
print("Navigating to example.com...")
await page.goto("https://example.com")
2025-07-17 00:51:02 +02:00
await asyncio.sleep(2)
2025-08-11 13:28:18 +02:00
# Get page info
title = await page.title()
url = await page.url()
print(f"Title: {title}")
print(f"URL: {url}")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Navigate to another page
print("\nNavigating to Python.org...")
await page.goto("https://www.python.org")
await asyncio.sleep(2)
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Go back
print("Going back...")
await page.go_back()
await asyncio.sleep(2)
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Go forward
print("Going forward...")
await page.go_forward()
await asyncio.sleep(2)
# Reload
print("Reloading page...")
await page.reload()
await asyncio.sleep(2)
finally:
await browser_instance.close()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async def demo_playwright_style():
"""Demo Playwright-style API usage"""
print("\n=== Playwright-Style API Demo ===")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Using Playwright launcher
playwright = Playwright()
browser_instance = await playwright.chromium.launch(ws_endpoint="ws://localhost:8765")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
try:
# Create context and page
context = await browser_instance.new_context()
page = await context.new_page()
# Navigate and wait for load
print("Navigating with Playwright-style API...")
await page.goto("https://www.wikipedia.org")
await page.wait_for_load_state("load")
# Use locators
print("Using locators to find elements...")
search_box = page.locator('input[name="search"]')
# Type into search box
print("Typing 'Python programming' into search...")
await search_box.type("Python programming", delay=50)
await asyncio.sleep(1)
# Press Enter
print("Pressing Enter...")
await search_box.press("Enter")
await page.wait_for_load_state("load")
await asyncio.sleep(2)
# Take screenshot
print("Taking screenshot...")
screenshot_data = await page.screenshot()
with open("wikipedia_search.png", "wb") as f:
f.write(screenshot_data)
print("Screenshot saved as wikipedia_search.png")
finally:
await browser_instance.close()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async def demo_form_interaction():
"""Demo form interaction capabilities"""
print("\n=== Form Interaction Demo ===")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async with browser(ws_endpoint="ws://localhost:8765") as browser_instance:
page = await browser_instance.new_page()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Create a test form
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>Test Form</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input, select, textarea { padding: 8px; width: 300px; }
button { padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; }
button:hover { background: #0056b3; }
</style>
</head>
<body>
<h1>Test Form</h1>
<form id="testForm">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="country">Country:</label>
<select id="country" name="country">
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
<option value="au">Australia</option>
</select>
</div>
<div class="form-group">
<label>
<input type="checkbox" id="newsletter" name="newsletter">
Subscribe to newsletter
</label>
</div>
<div class="form-group">
<label>Gender:</label>
<label><input type="radio" name="gender" value="male"> Male</label>
<label><input type="radio" name="gender" value="female"> Female</label>
<label><input type="radio" name="gender" value="other"> Other</label>
</div>
<div class="form-group">
<label for="comments">Comments:</label>
<textarea id="comments" name="comments" rows="4"></textarea>
</div>
<button type="submit">Submit</button>
</form>
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
<div id="result" style="margin-top: 20px; padding: 20px; background: #f0f0f0; display: none;">
<h2>Form Submitted!</h2>
<div id="resultContent"></div>
</div>
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
<script>
document.getElementById('testForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData.entries());
document.getElementById('resultContent').innerHTML =
'<pre>' + JSON.stringify(data, null, 2) + '</pre>';
document.getElementById('result').style.display = 'block';
});
</script>
</body>
</html>
"""
# Set the content
print("Loading test form...")
await page.set_content(html_content)
# Fill the form using different methods
print("\nFilling form fields...")
# Method 1: Using fill()
await page.fill("#name", "John Doe")
# Method 2: Using type() for more realistic typing
await page.type("#email", "john.doe@example.com", delay=50)
# Method 3: Using locator
country_select = page.locator("#country")
await country_select.select_option("us")
# Check checkbox
print("Checking newsletter checkbox...")
await page.check("#newsletter")
# Select radio button
print("Selecting gender radio button...")
await page.click('input[name="gender"][value="male"]')
# Fill textarea
print("Adding comments...")
comments_field = page.locator("#comments")
await comments_field.fill("This is a test comment\nWith multiple lines\nUsing Playwright-style API")
# Submit form
print("Submitting form...")
await page.click('button[type="submit"]')
await asyncio.sleep(1)
# Check if result is visible
result_div = page.locator("#result")
is_visible = await result_div.is_visible()
print(f"Result visible: {is_visible}")
# Get the result content
result_text = await page.inner_text("#resultContent")
print("Form submission result:")
print(result_text)
# Take a screenshot of the filled form
await page.screenshot()
print("Screenshot taken of submitted form")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async def demo_element_queries():
"""Demo element querying and manipulation"""
print("\n=== Element Query Demo ===")
browser_instance = Browser("ws://localhost:8765")
await browser_instance.connect()
try:
page = await browser_instance.new_page()
# Create a test page with multiple elements
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>Element Query Test</title>
<style>
.item { padding: 10px; margin: 5px; background: #f0f0f0; }
.hidden { display: none; }
.highlight { background: yellow; }
</style>
</head>
<body>
<h1>Element Query Test</h1>
<div class="container">
<div class="item" data-id="1">Item 1</div>
<div class="item" data-id="2">Item 2</div>
<div class="item hidden" data-id="3">Item 3 (Hidden)</div>
<div class="item" data-id="4">Item 4</div>
<button id="toggleBtn">Toggle Item 3</button>
<button id="highlightBtn">Highlight All</button>
</div>
<script>
document.getElementById('toggleBtn').addEventListener('click', () => {
const item3 = document.querySelector('[data-id="3"]');
item3.classList.toggle('hidden');
});
document.getElementById('highlightBtn').addEventListener('click', () => {
document.querySelectorAll('.item').forEach(item => {
item.classList.add('highlight');
});
});
</script>
</body>
</html>
"""
await page.set_content(html_content)
print("Test page loaded")
# Query single element
print("\nQuerying single element...")
first_item = await page.query_selector(".item")
if first_item:
text = await first_item.inner_text()
print(f"First item text: {text}")
# Query all elements
print("\nQuerying all items...")
all_items = await page.query_selector_all(".item")
print(f"Found {len(all_items)} items")
# Check visibility
print("\nChecking visibility of items...")
for i, item in enumerate(all_items):
is_visible = await item.is_visible()
print(f"Item {i+1} visible: {is_visible}")
# Get attributes
print("\nGetting data attributes...")
for item in all_items:
data_id = await item.get_attribute("data-id")
print(f"Item has data-id: {data_id}")
# Click toggle button to show hidden item
print("\nClicking toggle button...")
await page.click("#toggleBtn")
await asyncio.sleep(0.5)
# Check visibility again
hidden_item = page.locator('[data-id="3"]')
is_visible_after = await hidden_item.is_visible()
print(f"Hidden item visible after toggle: {is_visible_after}")
# Click highlight button
print("\nClicking highlight button...")
await page.click("#highlightBtn")
await asyncio.sleep(0.5)
# Use evaluate to check if highlighting worked
has_highlight = await page.evaluate("""
() => {
const items = document.querySelectorAll('.item.highlight');
return items.length;
}
""")
print(f"Number of highlighted items: {has_highlight}")
finally:
await browser_instance.close()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async def demo_wait_conditions():
"""Demo various wait conditions"""
print("\n=== Wait Conditions Demo ===")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async with browser(ws_endpoint="ws://localhost:8765") as browser_instance:
page = await browser_instance.new_page()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Create a dynamic page
html_content = """
<!DOCTYPE html>
2025-07-17 00:51:02 +02:00
<html>
<head>
2025-08-11 13:28:18 +02:00
<title>Dynamic Content Test</title>
2025-07-17 00:51:02 +02:00
<style>
2025-08-11 13:28:18 +02:00
.content { padding: 20px; margin: 20px; background: #f0f0f0; }
.loading { color: #999; }
.loaded { color: #333; font-weight: bold; }
2025-07-17 00:51:02 +02:00
</style>
</head>
<body>
2025-08-11 13:28:18 +02:00
<h1>Dynamic Content Test</h1>
<button id="loadBtn">Load Content</button>
<div id="content" class="content loading">Content will appear here...</div>
2025-07-17 00:51:02 +02:00
<script>
2025-08-11 13:28:18 +02:00
document.getElementById('loadBtn').addEventListener('click', () => {
const content = document.getElementById('content');
content.textContent = 'Loading...';
content.className = 'content loading';
// Simulate async loading
setTimeout(() => {
content.textContent = 'Content loaded successfully!';
content.className = 'content loaded';
// Add more content after another delay
setTimeout(() => {
const extra = document.createElement('div');
extra.id = 'extraContent';
extra.textContent = 'Extra content added!';
content.appendChild(extra);
}, 1000);
}, 2000);
});
2025-07-17 00:51:02 +02:00
</script>
</body>
</html>
"""
2025-08-11 13:28:18 +02:00
await page.set_content(html_content)
print("Dynamic page loaded")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Click load button
print("\nClicking load button...")
await page.click("#loadBtn")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Wait for content to change
print("Waiting for content to load...")
await page.wait_for_function("""
() => {
const content = document.getElementById('content');
return content && content.classList.contains('loaded');
}
""", timeout=5000)
print("Content loaded!")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Wait for extra content
print("Waiting for extra content...")
extra_content = await page.wait_for_selector("#extraContent", timeout=5000)
if extra_content:
extra_text = await extra_content.inner_text()
print(f"Extra content appeared: {extra_text}")
# Wait with timeout
print("\nWaiting for 2 seconds...")
await page.wait_for_timeout(2000)
print("Wait completed")
async def demo_javascript_execution():
"""Demo JavaScript execution capabilities"""
print("\n=== JavaScript Execution Demo ===")
browser_instance = Browser("ws://localhost:8765")
await browser_instance.connect()
try:
page = await browser_instance.new_page()
# Navigate to a simple page
await page.goto("https://example.com")
await asyncio.sleep(2) # Wait for page to load
# Execute simple JavaScript
print("\nExecuting simple JavaScript...")
try:
result = await page.evaluate("1 + 2")
print(f"1 + 2 = {result}")
except Exception as e:
print(f"Error executing simple math: {e}")
# Get page dimensions
try:
dimensions = await page.evaluate("""
() => {
return {
width: window.innerWidth,
height: window.innerHeight,
devicePixelRatio: window.devicePixelRatio || 1
}
}
""")
print(f"Page dimensions: {dimensions}")
except Exception as e:
print(f"Error getting dimensions: {e}")
# Modify page content
print("\nModifying page content...")
try:
await page.evaluate("""
() => {
const h1 = document.querySelector('h1');
if (h1) {
h1.style.color = 'red';
h1.textContent = 'Modified by Playwright-style API!';
}
}
""")
print("Page modified successfully")
except Exception as e:
print(f"Error modifying page: {e}")
# Create new elements
try:
await page.evaluate("""
() => {
const div = document.createElement('div');
div.id = 'custom-div';
div.style.cssText = 'position: fixed; top: 10px; right: 10px; ' +
'background: yellow; padding: 20px; ' +
'border: 2px solid black; z-index: 9999;';
div.textContent = 'Created via JavaScript!';
document.body.appendChild(div);
}
""")
print("New element created")
except Exception as e:
print(f"Error creating element: {e}")
await asyncio.sleep(2)
# Pass arguments to JavaScript
print("\nPassing arguments to JavaScript...")
try:
greeting = await page.evaluate(
"(name) => `Hello, ${name}!`",
"Playwright User"
)
print(f"Greeting: {greeting}")
except Exception as e:
print(f"Error with argument passing: {e}")
except Exception as e:
print(f"Error in demo: {e}")
finally:
await browser_instance.close()
async def demo_event_handling():
"""Demo event handling capabilities"""
print("\n=== Event Handling Demo ===")
browser_instance = Browser("ws://localhost:8765")
await browser_instance.connect()
# Set up event listeners
events_received = []
def on_browser_ready(data):
events_received.append(("browser_ready", data))
print(f"Event: Browser ready - {data}")
def on_load_started(data):
events_received.append(("load_started", data))
print(f"Event: Load started - {data.get('url', 'unknown')}")
def on_load_finished(data):
events_received.append(("load_finished", data))
print(f"Event: Load finished - {data.get('url', 'unknown')}")
def on_title_changed(data):
events_received.append(("title_changed", data))
print(f"Event: Title changed - {data.get('title', 'unknown')}")
# Register event listeners
browser_instance.on("browser_ready", on_browser_ready)
browser_instance.on("load_started", on_load_started)
browser_instance.on("load_finished", on_load_finished)
browser_instance.on("title_changed", on_title_changed)
try:
page = await browser_instance.new_page()
print("\nNavigating to trigger events...")
await page.goto("https://www.python.org")
await asyncio.sleep(2)
print(f"\nTotal events received: {len(events_received)}")
# Navigate to another page
print("\nNavigating to another page...")
await page.goto("https://example.com")
await asyncio.sleep(2)
print(f"Total events received: {len(events_received)}")
finally:
await browser_instance.close()
async def demo_backward_compatibility():
"""Demo backward compatible API usage"""
print("\n=== Backward Compatibility Demo ===")
browser_instance = Browser("ws://localhost:8765")
await browser_instance.connect()
try:
page = await browser_instance.new_page()
# Old-style commands still work
print("Using backward-compatible commands...")
# navigate command
result = await browser_instance._send_command({
"command": "navigate",
"url": "https://example.com"
})
print(f"Navigate result: {result}")
await asyncio.sleep(2)
# execute_js command
result = await browser_instance._send_command({
2025-07-17 00:51:02 +02:00
"command": "execute_js",
2025-08-11 13:28:18 +02:00
"script": "document.title"
})
print(f"Page title via execute_js: {result}")
# simulate_typing command
result = await browser_instance._send_command({
"command": "simulate_typing",
"selector": "h1",
"text": "Hello",
"delay": 0.1
})
print(f"Simulate typing result: {result}")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# get_info command
info = await browser_instance._send_command({
"command": "get_info"
})
print(f"Browser info: {info}")
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
finally:
await browser_instance.close()
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
async def run_all_demos():
"""Run all demo functions"""
demos = [
demo_basic_navigation,
demo_playwright_style,
demo_form_interaction,
demo_element_queries,
demo_wait_conditions,
demo_javascript_execution,
demo_event_handling,
demo_backward_compatibility
]
print("=" * 60)
print("Playwright-style WebSocket Browser Control Demo")
print("=" * 60)
print("\nMake sure the browser server is running on ws://localhost:8765")
print("Start it with: python browser_server_playwright.py")
input("\nPress Enter to start demos...")
for demo in demos:
try:
await demo()
await asyncio.sleep(2) # Pause between demos
except Exception as e:
logger.error(f"Error in {demo.__name__}: {e}")
import traceback
traceback.print_exc()
print("\n" + "=" * 60)
print("All demos completed!")
print("=" * 60)
2025-07-17 00:51:02 +02:00
2025-08-11 13:28:18 +02:00
# Individual demo runners for testing
2025-07-17 00:51:02 +02:00
async def main():
2025-08-11 13:28:18 +02:00
"""Main entry point"""
if len(sys.argv) > 1:
demo_name = sys.argv[1]
demos = {
"navigation": demo_basic_navigation,
"playwright": demo_playwright_style,
"form": demo_form_interaction,
"elements": demo_element_queries,
"wait": demo_wait_conditions,
"javascript": demo_javascript_execution,
"events": demo_event_handling,
"compatibility": demo_backward_compatibility,
"all": run_all_demos
}
if demo_name in demos:
await demos[demo_name]()
else:
print(f"Unknown demo: {demo_name}")
print(f"Available demos: {', '.join(demos.keys())}")
2025-07-17 00:51:02 +02:00
else:
2025-08-11 13:28:18 +02:00
await run_all_demos()
2025-07-17 00:51:02 +02:00
if __name__ == "__main__":
2025-08-11 13:28:18 +02:00
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nDemo interrupted by user")
except Exception as e:
logger.error(f"Fatal error: {e}")
import traceback
traceback.print_exc()