589c5b06fe
- Auto-fixed 70 lint errors (import sorting, aliased errors, datetime.UTC)
- Fixed 85 remaining errors manually:
- E501: wrapped long lines in queen_profiles, catalog, routes_credentials
- F821: added missing TYPE_CHECKING imports for AgentHost, ToolRegistry,
HookContext, HookResult; added runtime imports where needed
- F811: removed duplicate method definitions in queen_lifecycle_tools
- F841/B007: removed unused variables in discovery.py
- W291: removed trailing whitespace in queen nodes
- E402: moved import to top of queen_memory_v2.py
- Fixed AgentRuntime -> AgentHost in example template type annotations
- Reformatted 343 files with ruff format
157 lines
5.4 KiB
Python
157 lines
5.4 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Test #10: LinkedIn Huge DOM Tree
|
|
|
|
Symptom: browser_snapshot() hangs forever
|
|
Root Cause: 10k+ DOM nodes, accessibility tree has 50k+ nodes
|
|
Detection: document.querySelectorAll('*').length > 5000
|
|
Fix: Add timeout (10s default), truncate tree at 2000 nodes
|
|
"""
|
|
|
|
import asyncio
|
|
import sys
|
|
import time
|
|
import base64
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "tools" / "src"))
|
|
|
|
from gcu.browser.bridge import BeelineBridge
|
|
|
|
CONTEXT_NAME = "huge-dom-test"
|
|
|
|
|
|
async def test_huge_dom():
|
|
"""Test snapshot performance on huge DOM trees."""
|
|
print("=" * 70)
|
|
print("TEST #10: Huge DOM Tree (LinkedIn-style)")
|
|
print("=" * 70)
|
|
|
|
bridge = BeelineBridge()
|
|
|
|
try:
|
|
await bridge.start()
|
|
|
|
for i in range(10):
|
|
await asyncio.sleep(1)
|
|
if bridge.is_connected:
|
|
print("✓ Extension connected!")
|
|
break
|
|
else:
|
|
print("✗ Extension not connected")
|
|
return
|
|
|
|
context = await bridge.create_context(CONTEXT_NAME)
|
|
tab_id = context.get("tabId")
|
|
group_id = context.get("groupId")
|
|
print(f"✓ Created tab: {tab_id}")
|
|
|
|
# Test 1: Small DOM (baseline)
|
|
print("\n--- Test 1: Small DOM (baseline) ---")
|
|
small_html = """
|
|
<!DOCTYPE html>
|
|
<html><body>
|
|
<h1>Small Page</h1>
|
|
<p>A few elements</p>
|
|
<button>Click me</button>
|
|
</body></html>
|
|
"""
|
|
data_url = f"data:text/html;base64,{base64.b64encode(small_html.encode()).decode()}"
|
|
await bridge.navigate(tab_id, data_url, wait_until="load")
|
|
|
|
start = time.perf_counter()
|
|
snapshot = await bridge.snapshot(tab_id, timeout_s=5.0)
|
|
elapsed = time.perf_counter() - start
|
|
tree_len = len(snapshot.get("tree", ""))
|
|
print(f"Small DOM snapshot: {elapsed:.3f}s, {tree_len} chars")
|
|
|
|
# Test 2: Generate huge DOM
|
|
print("\n--- Test 2: Huge DOM (5000+ elements) ---")
|
|
huge_html = """
|
|
<!DOCTYPE html>
|
|
<html><body>
|
|
<h1>Huge DOM Test</h1>
|
|
<div id="container"></div>
|
|
<script>
|
|
const container = document.getElementById('container');
|
|
for (let i = 0; i < 5000; i++) {
|
|
const div = document.createElement('div');
|
|
div.className = 'item-' + i;
|
|
div.innerHTML = '<span>Item ' + i + '</span><button>Action</button>';
|
|
container.appendChild(div);
|
|
}
|
|
</script>
|
|
</body></html>
|
|
"""
|
|
data_url = f"data:text/html;base64,{base64.b64encode(huge_html.encode()).decode()}"
|
|
await bridge.navigate(tab_id, data_url, wait_until="load")
|
|
|
|
# Count elements
|
|
count_result = await bridge.evaluate(tab_id, "(function() { return document.querySelectorAll('*').length; })()")
|
|
elem_count = count_result.get("result", 0)
|
|
print(f"DOM elements: {elem_count}")
|
|
|
|
# Skip screenshot on huge DOM - it can timeout
|
|
# Instead verify page loaded by checking DOM
|
|
print("✓ Page verified (skipping screenshot on huge DOM)")
|
|
|
|
# Test snapshot with timeout
|
|
print("\n--- Testing snapshot with 10s timeout ---")
|
|
start = time.perf_counter()
|
|
try:
|
|
snapshot = await bridge.snapshot(tab_id, timeout_s=10.0)
|
|
elapsed = time.perf_counter() - start
|
|
tree_len = len(snapshot.get("tree", ""))
|
|
truncated = "(truncated)" in snapshot.get("tree", "")
|
|
print(f"✓ Huge DOM snapshot: {elapsed:.3f}s, {tree_len} chars, truncated={truncated}")
|
|
|
|
if elapsed < 5.0:
|
|
print("✓ PASS: Snapshot completed quickly")
|
|
else:
|
|
print(f"⚠ WARNING: Snapshot took {elapsed:.1f}s")
|
|
|
|
if truncated:
|
|
print("✓ PASS: Tree was truncated to prevent hang")
|
|
else:
|
|
print("⚠ WARNING: Tree not truncated (may need adjustment)")
|
|
|
|
except asyncio.TimeoutError:
|
|
print("✗ FAIL: Snapshot timed out (this shouldn't happen)")
|
|
|
|
# Test 3: Real LinkedIn
|
|
print("\n--- Test 3: Real LinkedIn Feed ---")
|
|
await bridge.navigate(tab_id, "https://www.linkedin.com/feed", wait_until="load", timeout_ms=30000)
|
|
await asyncio.sleep(2)
|
|
|
|
count_result = await bridge.evaluate(tab_id, "(function() { return document.querySelectorAll('*').length; })()")
|
|
elem_count = count_result.get("result", 0)
|
|
print(f"LinkedIn DOM elements: {elem_count}")
|
|
|
|
start = time.perf_counter()
|
|
try:
|
|
snapshot = await bridge.snapshot(tab_id, timeout_s=15.0)
|
|
elapsed = time.perf_counter() - start
|
|
tree_len = len(snapshot.get("tree", ""))
|
|
truncated = "(truncated)" in snapshot.get("tree", "")
|
|
print(f"LinkedIn snapshot: {elapsed:.3f}s, {tree_len} chars, truncated={truncated}")
|
|
|
|
if elapsed < 5.0:
|
|
print("✓ PASS: LinkedIn snapshot fast enough")
|
|
elif elapsed < 15.0:
|
|
print("⚠ WARNING: LinkedIn snapshot slow but within timeout")
|
|
else:
|
|
print("✗ FAIL: LinkedIn snapshot too slow")
|
|
|
|
except asyncio.TimeoutError:
|
|
print("✗ FAIL: LinkedIn snapshot timed out")
|
|
|
|
await bridge.destroy_context(group_id)
|
|
print("\n✓ Context destroyed")
|
|
|
|
finally:
|
|
await bridge.stop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(test_huge_dom())
|