From 0b61017575db4201baa3b9e5221b24c780fb6ee3 Mon Sep 17 00:00:00 2001 From: aaron Date: Fri, 27 Feb 2026 14:17:20 +1100 Subject: [PATCH 1/6] Improve keyboard typing and screenshot verification - Add keyboard_type() method for reliable typing into any element type - Wrapper for Playwright keyboard API - Works with contenteditable divs (X.com, Medium, etc) - Returns system reminder to verify with screenshot - Update take_screenshot() with verification guidance - Add 1-second wait after screenshot to prevent focus loss - Add full_page parameter for complete page capture - System reminder about full-page vs targeted screenshots - Explains lazy loading and detail trade-offs - Add browser_config.py with browser-use anti-detection settings - 53 Chrome args for anti-bot detection - 30 disabled features including AutomationControlled - Synced from connectonion/cli/browser_agent/browser_config.py - Remove type_text() method (replaced by keyboard_type) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- tools/browser_config.py | 109 ++++++++++++++++++++++++++++++++++++++++ tools/web_automation.py | 66 ++++++++++++++++-------- 2 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 tools/browser_config.py diff --git a/tools/browser_config.py b/tools/browser_config.py new file mode 100644 index 0000000..cb499e5 --- /dev/null +++ b/tools/browser_config.py @@ -0,0 +1,109 @@ +""" +Browser configuration constants from browser-use. +Source: https://github.com/browser-use/browser-use/blob/main/browser_use/browser/profile.py + +This file contains only the Chrome arguments and disabled features needed for anti-detection. +Keep this in sync with browser-use for best compatibility. +""" + +# Chrome disabled components/features +# These are passed via --disable-features={list} +CHROME_DISABLED_COMPONENTS = [ + 'AcceptCHFrame', + 'AutoExpandDetailsElement', + 'AvoidUnnecessaryBeforeUnloadCheckSync', + 'CertificateTransparencyComponentUpdater', + 'DestroyProfileOnBrowserClose', + 'DialMediaRouteProvider', + 'ExtensionManifestV2Disabled', + 'GlobalMediaControls', + 'HttpsUpgrades', + 'ImprovedCookieControls', + 'LazyFrameLoading', + 'LensOverlay', + 'MediaRouter', + 'PaintHolding', + 'ThirdPartyStoragePartitioning', + 'Translate', + # Added by browser-use: + 'AutomationControlled', # KEY: Removes navigator.webdriver + 'BackForwardCache', + 'OptimizationHints', + 'ProcessPerSiteUpToMainFrameThreshold', + 'InterestFeedContentSuggestions', + 'CalculateNativeWinOcclusion', + 'HeavyAdPrivacyMitigations', + 'PrivacySandboxSettings4', + 'AutofillServerCommunication', + 'CrashReporting', + 'OverscrollHistoryNavigation', + 'InfiniteSessionRestore', + 'ExtensionDisableUnsupportedDeveloper', + 'ExtensionManifestV2Unsupported', +] + +# Chrome default arguments (53 args) +CHROME_DEFAULT_ARGS = [ + '--disable-field-trial-config', + '--disable-background-networking', + '--disable-background-timer-throttling', + '--disable-backgrounding-occluded-windows', + '--disable-back-forward-cache', + '--disable-breakpad', + '--disable-client-side-phishing-detection', + '--disable-component-extensions-with-background-pages', + '--disable-component-update', + '--no-default-browser-check', + '--disable-dev-shm-usage', + '--disable-hang-monitor', + '--disable-ipc-flooding-protection', + '--disable-popup-blocking', + '--disable-prompt-on-repost', + '--disable-renderer-backgrounding', + '--metrics-recording-only', + '--no-first-run', + '--no-service-autorun', + '--export-tagged-pdf', + '--disable-search-engine-choice-screen', + '--unsafely-disable-devtools-self-xss-warnings', + '--enable-features=NetworkService,NetworkServiceInProcess', + '--enable-network-information-downlink-max', + '--test-type=gpu', + '--disable-sync', + '--allow-legacy-extension-manifests', + '--allow-pre-commit-input', + '--disable-blink-features=AutomationControlled', # KEY: anti-detection + '--install-autogenerated-theme=0,0,0', + '--log-level=2', + '--disable-focus-on-load', + '--disable-window-activation', + '--generate-pdf-document-outline', + '--no-pings', + '--ash-no-nudges', + '--disable-infobars', + '--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"', + '--hide-crash-restore-bubble', + '--suppress-message-center-popups', + '--disable-domain-reliability', + '--disable-datasaver-prompt', + '--disable-speech-synthesis-api', + '--disable-speech-api', + '--disable-print-preview', + '--safebrowsing-disable-auto-update', + '--disable-external-intent-requests', + '--disable-desktop-notifications', + '--noerrdialogs', + '--silent-debugger-extension-api', + '--disable-extensions-http-throttling', + '--extensions-on-chrome-urls', + '--disable-default-apps', + f'--disable-features={",".join(CHROME_DISABLED_COMPONENTS)}', +] + +# Arguments to ignore from Playwright defaults +# These are automation markers we DON'T want +IGNORE_DEFAULT_ARGS = [ + '--enable-automation', # DON'T add this flag (anti-detection) + '--disable-extensions', # Allow extensions (looks more human) + '--hide-scrollbars', # Show scrollbars (more human-like) +] diff --git a/tools/web_automation.py b/tools/web_automation.py index c143d49..e6e1c51 100644 --- a/tools/web_automation.py +++ b/tools/web_automation.py @@ -13,6 +13,7 @@ from . import scroll_strategies from .element_finder import find_element +from .browser_config import CHROME_DEFAULT_ARGS, IGNORE_DEFAULT_ARGS @@ -55,12 +56,8 @@ def open_browser(self, headless: Union[bool, str, None] = None) -> str: self.context = self.playwright.chromium.launch_persistent_context( self.chrome_profile_path, headless=headless, - args=[ - '--no-sandbox', - '--disable-setuid-sandbox', - '--disable-blink-features=AutomationControlled', - ], - ignore_default_args=['--enable-automation'], + args=CHROME_DEFAULT_ARGS, + ignore_default_args=IGNORE_DEFAULT_ARGS + ['--use-mock-keychain'], timeout=120000, ) @@ -104,16 +101,27 @@ def click(self, description: str) -> str: self.page.click(element.locator) return f"Clicked on '{description}'" - def type_text(self, field_description: str, text: str) -> str: - """Type text into a form field using natural language description.""" - element = find_element(self.page, field_description) - if not element: - self.page.fill(f"text={field_description}", text) - return f"Typed into {field_description} (by text)" + @xray + def keyboard_type(self, text: str) -> str: + """Type text using keyboard input (Playwright keyboard API wrapper). + + Simulates keyboard typing character by character into the currently focused element. + Works with any element type (input, textarea, contenteditable, etc). + + Args: + text: The text to type + + Returns: + Success message with system reminder to verify with screenshot + """ + if not self.page: + return "Browser not open" + + self.page.keyboard.type(text) + + return f"""Typed: '{text}' - self.page.fill(element.locator, text) - self.form_data[field_description] = text - return f"Typed into {field_description}" +SYSTEM REMINDER: Please use take_screenshot() to verify the text was typed into the correct input box. If the text is NOT visible in the expected location, you may need to click the element first to focus it, then type again.""" def get_text(self) -> str: @@ -121,8 +129,16 @@ def get_text(self) -> str: return self.page.inner_text("body") @xray - def take_screenshot(self, filename: str = None) -> str: - """Take a screenshot of the current page and return base64 encoded image.""" + def take_screenshot(self, filename: str = None, full_page: bool = False) -> str: + """Take a screenshot of the current page and return base64 encoded image. + + Args: + filename: Optional filename for the screenshot + full_page: If True, captures entire page height (may lose details but shows overview) + + Returns: + Base64 encoded image data with system reminder for full-page screenshots + """ from datetime import datetime os.makedirs(self.screenshots_dir, exist_ok=True) @@ -134,10 +150,20 @@ def take_screenshot(self, filename: str = None) -> str: if not "/" in filename: filename = f"{self.screenshots_dir}/{filename}" - screenshot_bytes = self.page.screenshot(path=filename) + screenshot_bytes = self.page.screenshot(path=filename, full_page=full_page) + + # Wait for page to stabilize after screenshot (especially for full_page which scrolls/resizes) + # This prevents focus loss when typing after taking a screenshot + self.page.wait_for_timeout(1000) + screenshot_base64 = base64.b64encode(screenshot_bytes).decode('utf-8') - return f"data:image/png;base64,{screenshot_base64}" + if full_page: + return f"""data:image/png;base64,{screenshot_base64} + +SYSTEM REMINDER: Full-page screenshots provide an overview of the entire page but may not show details clearly. Some elements might not be loaded yet (lazy loading). If you need to verify specific content or see details clearly, consider: scroll() to the target area first, then take a regular screenshot (full_page=False), or use scroll() multiple times and take screenshots at each position.""" + else: + return f"data:image/png;base64,{screenshot_base64}" @@ -264,4 +290,4 @@ def analyze_page(self, question: str) -> str: ) # Default shared instance -web = WebAutomation(headless=True) \ No newline at end of file +web = WebAutomation(headless=False) \ No newline at end of file From 458ab2f1636c4a0ef64d90d5e76acedc76b31c2d Mon Sep 17 00:00:00 2001 From: aaron Date: Fri, 27 Feb 2026 14:30:21 +1100 Subject: [PATCH 2/6] Add missing utility methods synced from cli/browser_agent - Add wait() method for simple timeouts - Add get_current_url() to retrieve current page URL - Add get_current_page_html() to get page HTML content - Add get_urls() to extract all links from page with domain filtering - Add set_viewport() to control browser viewport size - Add _save_context() to ensure persistent context saves properly - Update close() to call _save_context() before cleanup - Add __enter__/__exit__ for context manager support These methods ensure browser-agent has feature parity with cli/browser_agent for session persistence and utility functions. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- tools/web_automation.py | 78 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/tools/web_automation.py b/tools/web_automation.py index e6e1c51..779c321 100644 --- a/tools/web_automation.py +++ b/tools/web_automation.py @@ -266,8 +266,75 @@ def google_search(self, query: str, max_results: int = 10) -> List[Dict[str, str return results + def wait(self, seconds: float) -> str: + """Wait for a specified number of seconds.""" + if not self.page: + return "Browser not open" + self.page.wait_for_timeout(seconds * 1000) + return f"Waited for {seconds} seconds" + + def get_current_url(self) -> str: + """Get the current page URL.""" + if not self.page: + return "Browser not open" + return self.page.url + + def get_current_page_html(self) -> str: + """Get the HTML content of the current page.""" + if not self.page: + return "Browser not open" + return self.page.content() + + def get_urls(self, domain_filter: str = "") -> List[str]: + """Extract all URLs from the page, optionally filtered by domain.""" + if not self.page: + return [] + + all_links = self.page.locator("a[href]").all() + urls = [] + + for link in all_links: + href = link.get_attribute("href") + if href and href.startswith("http"): + if not domain_filter or domain_filter in href: + urls.append(href) + + return list(set(urls)) # Remove duplicates + + def set_viewport(self, width: int, height: int) -> str: + """Set the browser viewport size.""" + if not self.page: + return "Browser not open" + self.page.set_viewport_size({"width": width, "height": height}) + return f"Viewport set to {width}x{height}" + + def _save_context(self) -> None: + """Force save browser state to disk. + + Called after critical actions (login, navigation) to ensure + context is persisted even if process crashes. + + With launch_persistent_context(), Playwright automatically saves: + - Cookies + - localStorage + - sessionStorage + - IndexedDB + - Service workers + - Cache + + This method just waits briefly to ensure async saves complete. + """ + if not self.context or not self.page: + return + + # Wait for any async operations to complete + # Playwright's persistent context auto-saves in background + self.page.wait_for_timeout(500) + def close(self, keep_browser_open: bool = False) -> str: - """Close the browser.""" + """Close the browser and save persistent context.""" + self._save_context() + if keep_browser_open: return "Browser kept open" @@ -281,6 +348,15 @@ def close(self, keep_browser_open: bool = False) -> str: return "Browser closed" + def __enter__(self): + """Context manager entry.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit - ensures browser cleanup.""" + self.close() + return False + def analyze_page(self, question: str) -> str: """Ask a question about page content using AI.""" return llm_do( From e3817641d53643feb33548bcf8de90984eb131d3 Mon Sep 17 00:00:00 2001 From: aaron Date: Fri, 27 Feb 2026 14:36:01 +1100 Subject: [PATCH 3/6] Remove get_current_page_html() - unused and redundant Raw HTML is too noisy for agents. Better alternatives exist: - get_text() for visible content - analyze_page() for AI-powered analysis - analyze_html() for structured analysis Also removed from cli/browser_agent to keep both in sync. --- tools/web_automation.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tools/web_automation.py b/tools/web_automation.py index 779c321..6f217c0 100644 --- a/tools/web_automation.py +++ b/tools/web_automation.py @@ -279,13 +279,7 @@ def get_current_url(self) -> str: return "Browser not open" return self.page.url - def get_current_page_html(self) -> str: - """Get the HTML content of the current page.""" - if not self.page: - return "Browser not open" - return self.page.content() - - def get_urls(self, domain_filter: str = "") -> List[str]: +def get_urls(self, domain_filter: str = "") -> List[str]: """Extract all URLs from the page, optionally filtered by domain.""" if not self.page: return [] From d84242cfb872a0e0ff26e89f8ad219924dac157a Mon Sep 17 00:00:00 2001 From: aaron Date: Fri, 27 Feb 2026 14:37:18 +1100 Subject: [PATCH 4/6] Rename get_urls() to get_links_from_page() for clarity - More descriptive name that clearly indicates it extracts links - Also fixes indentation error from previous commit - Renamed in both cli/browser_agent and browser-agent for sync --- tools/web_automation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/web_automation.py b/tools/web_automation.py index 6f217c0..c2e7397 100644 --- a/tools/web_automation.py +++ b/tools/web_automation.py @@ -279,8 +279,8 @@ def get_current_url(self) -> str: return "Browser not open" return self.page.url -def get_urls(self, domain_filter: str = "") -> List[str]: - """Extract all URLs from the page, optionally filtered by domain.""" + def get_links_from_page(self, domain_filter: str = "") -> List[str]: + """Extract all links from the page, optionally filtered by domain.""" if not self.page: return [] From 150dbb6d78c73973fac41759d63dbbb3d1d03f0e Mon Sep 17 00:00:00 2001 From: aaron Date: Fri, 27 Feb 2026 14:41:26 +1100 Subject: [PATCH 5/6] Rename web_automation.py to browser.py for consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Renamed tools/web_automation.py → tools/browser.py - Updated all imports across tests, agents, and examples - Matches connectonion's naming convention (browser.py) - All tests passing (3/3) This provides consistent naming across both projects: - connectonion: cli/browser_agent/browser.py - browser-agent: tools/browser.py --- agents/deep_research.py | 2 +- examples/demo_image_plugin.py | 2 +- examples/demo_trace_inspection.py | 2 +- examples/get_all_emails_and_analyze.py | 2 +- tests/conftest.py | 2 +- tests/test_auto_debug.py | 2 +- tests/test_chromium_login.py | 2 +- tests/test_direct.py | 2 +- tests/test_final_scroll.py | 2 +- tests/test_image_plugin.py | 2 +- tools/{web_automation.py => browser.py} | 0 11 files changed, 10 insertions(+), 10 deletions(-) rename tools/{web_automation.py => browser.py} (100%) diff --git a/agents/deep_research.py b/agents/deep_research.py index dd0ae3e..bfc5f2a 100644 --- a/agents/deep_research.py +++ b/agents/deep_research.py @@ -5,7 +5,7 @@ from pathlib import Path from connectonion import Agent from connectonion.useful_plugins import image_result_formatter -from tools.web_automation import web +from tools.browser import web from tools.file_tools import FileTools from tools.deep_research import perform_deep_research diff --git a/examples/demo_image_plugin.py b/examples/demo_image_plugin.py index 13efee8..875fbb4 100644 --- a/examples/demo_image_plugin.py +++ b/examples/demo_image_plugin.py @@ -14,7 +14,7 @@ from connectonion import Agent from connectonion.useful_plugins import image_result_formatter -from browser_agent.web_automation import WebAutomation +from browser_agent.browser import WebAutomation # Create web automation instance web = WebAutomation(use_chrome_profile=False) diff --git a/examples/demo_trace_inspection.py b/examples/demo_trace_inspection.py index f52a0da..dd1406a 100644 --- a/examples/demo_trace_inspection.py +++ b/examples/demo_trace_inspection.py @@ -19,7 +19,7 @@ from connectonion import Agent, xray from connectonion.useful_plugins import image_result_formatter -from browser_agent.web_automation import WebAutomation +from browser_agent.browser import WebAutomation # Create web automation instance web = WebAutomation(use_chrome_profile=False) diff --git a/examples/get_all_emails_and_analyze.py b/examples/get_all_emails_and_analyze.py index df72b3e..2bc7df6 100644 --- a/examples/get_all_emails_and_analyze.py +++ b/examples/get_all_emails_and_analyze.py @@ -8,7 +8,7 @@ 4. Creates a comprehensive summary """ -from browser_agent.web_automation import WebAutomation +from browser_agent.browser import WebAutomation import time import json diff --git a/tests/conftest.py b/tests/conftest.py index 2c6a841..e34d2ca 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -72,7 +72,7 @@ def cleanup_asyncio(): @pytest.fixture(scope="function") def web(tmp_path): """Create WebAutomation instance for each test using temp dir for screenshots and profile""" - from tools.web_automation import WebAutomation + from tools.browser import WebAutomation # Use a unique temporary directory for the Chrome profile to avoid locking issues in parallel tests profile_path = tmp_path / "chrome_profile" diff --git a/tests/test_auto_debug.py b/tests/test_auto_debug.py index 7a827c4..506da44 100644 --- a/tests/test_auto_debug.py +++ b/tests/test_auto_debug.py @@ -10,7 +10,7 @@ # Ensure tools can be imported sys.path.insert(0, str(Path(__file__).parent.parent)) -from tools.web_automation import WebAutomation +from tools.browser import WebAutomation @pytest.mark.manual diff --git a/tests/test_chromium_login.py b/tests/test_chromium_login.py index 8bef6a0..f8734f0 100644 --- a/tests/test_chromium_login.py +++ b/tests/test_chromium_login.py @@ -9,7 +9,7 @@ # Ensure tools can be imported sys.path.insert(0, str(Path(__file__).parent.parent)) -from tools.web_automation import WebAutomation +from tools.browser import WebAutomation @pytest.mark.manual @pytest.mark.chrome_profile diff --git a/tests/test_direct.py b/tests/test_direct.py index 90bbff4..249007b 100755 --- a/tests/test_direct.py +++ b/tests/test_direct.py @@ -6,7 +6,7 @@ import time from pathlib import Path import pytest -from tools.web_automation import WebAutomation +from tools.browser import WebAutomation import os @pytest.mark.manual diff --git a/tests/test_final_scroll.py b/tests/test_final_scroll.py index 793844f..9ef460f 100644 --- a/tests/test_final_scroll.py +++ b/tests/test_final_scroll.py @@ -4,7 +4,7 @@ """ import time import pytest -from tools.web_automation import WebAutomation +from tools.browser import WebAutomation @pytest.mark.manual diff --git a/tests/test_image_plugin.py b/tests/test_image_plugin.py index 7e8ffad..8647177 100644 --- a/tests/test_image_plugin.py +++ b/tests/test_image_plugin.py @@ -5,7 +5,7 @@ import pytest from connectonion import Agent from connectonion.useful_plugins import image_result_formatter -from tools.web_automation import WebAutomation +from tools.browser import WebAutomation from pathlib import Path diff --git a/tools/web_automation.py b/tools/browser.py similarity index 100% rename from tools/web_automation.py rename to tools/browser.py From 14682b3c0c61b1e2d95d79d543652a1046d712df Mon Sep 17 00:00:00 2001 From: aaron Date: Fri, 27 Feb 2026 14:45:20 +1100 Subject: [PATCH 6/6] Rename WebAutomation class to Browser for consistency with browser.py module Renames class and all references across tests and examples so the import reads naturally: from tools.browser import Browser --- examples/demo_image_plugin.py | 4 ++-- examples/demo_trace_inspection.py | 4 ++-- examples/get_all_emails_and_analyze.py | 4 ++-- test_deep_research_direct.py | 21 +++++++++++++++++++++ tests/conftest.py | 12 ++++++------ tests/test_auto_debug.py | 4 ++-- tests/test_chromium_login.py | 6 +++--- tests/test_direct.py | 4 ++-- tests/test_final_scroll.py | 8 ++++---- tests/test_image_plugin.py | 2 +- tools/browser.py | 4 ++-- 11 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 test_deep_research_direct.py diff --git a/examples/demo_image_plugin.py b/examples/demo_image_plugin.py index 875fbb4..6e1553e 100644 --- a/examples/demo_image_plugin.py +++ b/examples/demo_image_plugin.py @@ -14,10 +14,10 @@ from connectonion import Agent from connectonion.useful_plugins import image_result_formatter -from browser_agent.browser import WebAutomation +from browser_agent.browser import Browser # Create web automation instance -web = WebAutomation(use_chrome_profile=False) +web = Browser(use_chrome_profile=False) # Create agent with plugins agent = Agent( diff --git a/examples/demo_trace_inspection.py b/examples/demo_trace_inspection.py index dd1406a..1a11137 100644 --- a/examples/demo_trace_inspection.py +++ b/examples/demo_trace_inspection.py @@ -19,10 +19,10 @@ from connectonion import Agent, xray from connectonion.useful_plugins import image_result_formatter -from browser_agent.browser import WebAutomation +from browser_agent.browser import Browser # Create web automation instance -web = WebAutomation(use_chrome_profile=False) +web = Browser(use_chrome_profile=False) # Create agent with image_result_formatter plugin agent = Agent( diff --git a/examples/get_all_emails_and_analyze.py b/examples/get_all_emails_and_analyze.py index 2bc7df6..b5520ec 100644 --- a/examples/get_all_emails_and_analyze.py +++ b/examples/get_all_emails_and_analyze.py @@ -8,14 +8,14 @@ 4. Creates a comprehensive summary """ -from browser_agent.browser import WebAutomation +from browser_agent.browser import Browser import time import json def get_all_emails_and_analyze(): print("=== Getting ALL Gmail Emails and Analyzing ===\n") - web = WebAutomation(use_chrome_profile=True) + web = Browser(use_chrome_profile=True) web.open_browser(headless=False) web.go_to("https://gmail.com") time.sleep(3) diff --git a/test_deep_research_direct.py b/test_deep_research_direct.py new file mode 100644 index 0000000..7c527ff --- /dev/null +++ b/test_deep_research_direct.py @@ -0,0 +1,21 @@ +from agents.deep_research import DeepResearch +from tools.browser import Browser +import os + +def test_standalone_deep_research(): + print("Testing DeepResearch standalone...") + web = Browser(headless=True) + researcher = DeepResearch(web) + + # Simple topic + topic = "What is the capital of France and its population?" + result = researcher.perform_deep_research(topic) + + print("\nRESEARCH RESULT:") + print(result) + + web.close() + +if __name__ == "__main__": + test_standalone_deep_research() + diff --git a/tests/conftest.py b/tests/conftest.py index e34d2ca..a99988e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -71,13 +71,13 @@ def cleanup_asyncio(): @pytest.fixture(scope="function") def web(tmp_path): - """Create WebAutomation instance for each test using temp dir for screenshots and profile""" - from tools.browser import WebAutomation - + """Create Browser instance for each test using temp dir for screenshots and profile""" + from tools.browser import Browser + # Use a unique temporary directory for the Chrome profile to avoid locking issues in parallel tests profile_path = tmp_path / "chrome_profile" - - web_instance = WebAutomation(profile_path=str(profile_path)) + + web_instance = Browser(profile_path=str(profile_path)) # Redirect screenshots to temp directory web_instance.screenshots_dir = str(tmp_path / "screenshots") yield web_instance @@ -88,7 +88,7 @@ def web(tmp_path): @pytest.fixture(scope="function") def agent(web): - """Create Agent with WebAutomation tools""" + """Create Agent with Browser tools""" from connectonion import Agent agent_instance = Agent( name="test_agent", diff --git a/tests/test_auto_debug.py b/tests/test_auto_debug.py index 506da44..8b89a2f 100644 --- a/tests/test_auto_debug.py +++ b/tests/test_auto_debug.py @@ -10,7 +10,7 @@ # Ensure tools can be imported sys.path.insert(0, str(Path(__file__).parent.parent)) -from tools.browser import WebAutomation +from tools.browser import Browser @pytest.mark.manual @@ -18,7 +18,7 @@ def test_auto_debug_hacker_news(): """Test auto_debug feature with browser automation - requires user interaction.""" # Create the web automation instance - web = WebAutomation() + web = Browser() # Get correct path to prompt.md prompt_path = Path(__file__).parent.parent / "prompts" / "agent.md" diff --git a/tests/test_chromium_login.py b/tests/test_chromium_login.py index f8734f0..8217a36 100644 --- a/tests/test_chromium_login.py +++ b/tests/test_chromium_login.py @@ -9,7 +9,7 @@ # Ensure tools can be imported sys.path.insert(0, str(Path(__file__).parent.parent)) -from tools.browser import WebAutomation +from tools.browser import Browser @pytest.mark.manual @pytest.mark.chrome_profile @@ -21,7 +21,7 @@ def test_chromium_profile_persistence(): Step 2: Reopen browser -> Check if logged in automatically """ print("\n=== Step 1: Initial Login ===") - web1 = WebAutomation(headless=False) + web1 = Browser(headless=False) web1.open_browser() print("Navigating to Gmail...") @@ -38,7 +38,7 @@ def test_chromium_profile_persistence(): print("\n=== Step 2: Verification (New Session) ===") # Open a FRESH instance pointing to the SAME profile - web2 = WebAutomation(headless=False) + web2 = Browser(headless=False) web2.open_browser() print("Navigating to Gmail again (should be logged in)...") diff --git a/tests/test_direct.py b/tests/test_direct.py index 249007b..19c78df 100755 --- a/tests/test_direct.py +++ b/tests/test_direct.py @@ -6,7 +6,7 @@ import time from pathlib import Path import pytest -from tools.browser import WebAutomation +from tools.browser import Browser import os @pytest.mark.manual @@ -22,7 +22,7 @@ def test_google_search_direct(search_term): 2. Parametrized tests with Playwright can have async loop conflicts 3. It's more of an end-to-end test than a unit test """ - web = WebAutomation() + web = Browser() # Step 1: Open browser result = web.open_browser(headless=True) diff --git a/tests/test_final_scroll.py b/tests/test_final_scroll.py index 9ef460f..5e617fc 100644 --- a/tests/test_final_scroll.py +++ b/tests/test_final_scroll.py @@ -4,7 +4,7 @@ """ import time import pytest -from tools.browser import WebAutomation +from tools.browser import Browser @pytest.mark.manual @@ -18,7 +18,7 @@ def test_unified_scroll_gmail(): - AI strategy generation → fallback chain → screenshot comparison - Clean separation of concerns (automation vs scroll logic) """ - web = WebAutomation() + web = Browser() # Open visible browser for manual login web.open_browser(headless=False) @@ -50,10 +50,10 @@ def test_scroll_architecture_demo(): - One simple method: web.scroll() """ # Just verify the architecture is in place - web = WebAutomation() + web = Browser() # Verify scroll method exists - assert hasattr(web, 'scroll'), "WebAutomation should have scroll() method" + assert hasattr(web, 'scroll'), "Browser should have scroll() method" assert callable(web.scroll), "scroll should be callable" # Verify scroll_strategies module exists diff --git a/tests/test_image_plugin.py b/tests/test_image_plugin.py index 8647177..3732e4b 100644 --- a/tests/test_image_plugin.py +++ b/tests/test_image_plugin.py @@ -5,7 +5,7 @@ import pytest from connectonion import Agent from connectonion.useful_plugins import image_result_formatter -from tools.browser import WebAutomation +from tools.browser import Browser from pathlib import Path diff --git a/tools/browser.py b/tools/browser.py index c2e7397..2b9a89c 100644 --- a/tools/browser.py +++ b/tools/browser.py @@ -17,7 +17,7 @@ -class WebAutomation: +class Browser: """Web browser automation with form handling capabilities. Simple interface for complex web interactions. @@ -360,4 +360,4 @@ def analyze_page(self, question: str) -> str: ) # Default shared instance -web = WebAutomation(headless=False) \ No newline at end of file +web = Browser(headless=False) \ No newline at end of file