ai-dev-browser 0.12.2__tar.gz → 0.12.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/.github/workflows/ci.yml +2 -1
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/PKG-INFO +1 -1
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/_tab.py +23 -25
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/PKG-INFO +1 -1
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/SOURCES.txt +1 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/scm_file_list.json +1 -0
- ai_dev_browser-0.12.3/ai_dev_browser.egg-info/scm_version.json +8 -0
- ai_dev_browser-0.12.3/tests/integration/test_scroll_embedded_target.py +176 -0
- ai_dev_browser-0.12.2/ai_dev_browser.egg-info/scm_version.json +0 -8
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/.github/workflows/publish.yml +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/.gitignore +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/.gitmodules +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/.pre-commit-config.yaml +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/.secrets.baseline +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/LICENSE +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/README.md +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/_cli.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/_version.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/accessibility.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/animation.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/audits.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/autofill.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/background_service.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/browser.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/cache_storage.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/cast.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/console.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/crash_report_context.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/css.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/debugger.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/device_access.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/device_orientation.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/dom.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/dom_debugger.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/dom_storage.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/emulation.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/extensions.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/fed_cm.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/fetch.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/file_system.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/headless_experimental.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/heap_profiler.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/indexed_db.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/input_.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/inspector.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/io.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/layer_tree.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/log.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/media.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/memory.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/network.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/overlay.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/page.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/performance.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/performance_timeline.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/preload.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/profiler.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/pwa.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/py.typed +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/runtime.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/schema.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/security.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/service_worker.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/storage.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/system_info.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/target.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/tethering.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/tracing.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/util.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/web_audio.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/web_authn.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/cdp/web_mcp.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/_case.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/_element.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/_image_cap.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/_transport.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/ax.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/browser.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/cdp.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/chrome.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/cleanup.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/config.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/connection.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/cookies.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/cookies_import.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/dialog.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/download.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/elements.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/human.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/login.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/mouse.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/navigation.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/overlays.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/page.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/port.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/process.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/snapshot.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/storage.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/tabs.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/text_match.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/core/window.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/pool/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/pool/job.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/pool/persistence.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/pool/pool.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/pool/worker.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/profile.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/py.typed +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/_generate.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/browser_list.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/browser_start.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/browser_stop.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/cdp_send.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/click_by_html_id.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/click_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/click_by_text.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/click_by_xpath.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/cookies_extract.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/cookies_import.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/cookies_list.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/cookies_load.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/cookies_save.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/dialog_respond.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/download.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/drag_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/find_by_html_id.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/find_by_text.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/find_by_xpath.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/focus_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/hover_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/html_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/js_evaluate.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/login_interactive.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/mouse_click.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/mouse_drag.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/mouse_move.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_discover.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_goto.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_html.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_info.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_pdf.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_reload.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_screenshot.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_scroll.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_wait_element.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_wait_ready.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/page_wait_url.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/select_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/storage_get.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/storage_set.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/tab_close.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/tab_list.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/tab_new.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/tab_switch.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/type_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/type_by_text.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/upload_by_ref.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser/tools/window_set.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/requires.txt +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/top_level.txt +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/pyproject.toml +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/scripts/sync_cdp.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/setup.cfg +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/conftest.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/__init__.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/scenarios_workspace.json +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_ci_integration.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_cli_unicode_help.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_cookies_extract_windows.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_cookies_import.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_dialog_respond.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_find_and_interact_workflows.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_image_cap.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_locator_workflows.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_multi_profile_pool.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_page_reload.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_port_detection.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_startup_timeout.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_timeout_and_retry.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_workspace_workflows.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/unit/test_case.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/unit/test_cli_param_type.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/unit/test_no_acronym_rot.py +0 -0
- {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/uv.lock +0 -0
|
@@ -71,7 +71,8 @@ jobs:
|
|
|
71
71
|
tests/integration/test_ci_integration.py \
|
|
72
72
|
tests/integration/test_cookies_extract_windows.py \
|
|
73
73
|
tests/integration/test_cli_unicode_help.py \
|
|
74
|
-
tests/integration/test_image_cap.py
|
|
74
|
+
tests/integration/test_image_cap.py \
|
|
75
|
+
tests/integration/test_scroll_embedded_target.py
|
|
75
76
|
|
|
76
77
|
# ─────────────────────────────────────────────────────────────────────
|
|
77
78
|
# CI-integration dogfood: prove the documented "consumer's CI" recipe
|
|
@@ -493,36 +493,34 @@ class Tab:
|
|
|
493
493
|
# Scroll
|
|
494
494
|
# =========================================================================
|
|
495
495
|
|
|
496
|
+
async def _scroll_by_viewport_percent(self, percent: float) -> None:
|
|
497
|
+
"""Scroll by `percent` of viewport height (positive = down,
|
|
498
|
+
negative = up). Reads viewport height AND performs the scroll
|
|
499
|
+
in a single `Runtime.evaluate` call — no CDP Browser or
|
|
500
|
+
Input.synthesizeScrollGesture calls involved.
|
|
501
|
+
|
|
502
|
+
The previous implementation used `Browser.getWindowForTarget`
|
|
503
|
+
(browser-domain) to fetch bounds, then
|
|
504
|
+
`Input.synthesizeScrollGesture` for a smooth scroll. Both are
|
|
505
|
+
Chrome-specific CDP methods that embedded targets (Electron,
|
|
506
|
+
CEF, packaged Chromium apps exposing CDP via
|
|
507
|
+
`--remote-debugging-port`) may not implement — attaching
|
|
508
|
+
would 32601 the whole scroll path even though basic JS
|
|
509
|
+
`window.scrollBy` works fine. `Runtime.evaluate` is
|
|
510
|
+
universally supported, so we lean on that instead. Loses the
|
|
511
|
+
gesture-like smoothness, but agent scroll doesn't need
|
|
512
|
+
animation — the scroll event still fires so lazy-load and
|
|
513
|
+
scroll-listeners still trigger.
|
|
514
|
+
"""
|
|
515
|
+
await self.evaluate(f"window.scrollBy(0, window.innerHeight * {percent / 100})")
|
|
516
|
+
|
|
496
517
|
async def scroll_down(self, amount: int = 25):
|
|
497
518
|
"""Scroll down by percentage of viewport height."""
|
|
498
|
-
|
|
499
|
-
await self.send(
|
|
500
|
-
cdp_input.synthesize_scroll_gesture(
|
|
501
|
-
x=0,
|
|
502
|
-
y=0,
|
|
503
|
-
y_distance=-(bounds.height * (amount / 100)),
|
|
504
|
-
y_overscroll=0,
|
|
505
|
-
x_overscroll=0,
|
|
506
|
-
prevent_fling=True,
|
|
507
|
-
repeat_delay_ms=0,
|
|
508
|
-
speed=7777,
|
|
509
|
-
)
|
|
510
|
-
)
|
|
519
|
+
await self._scroll_by_viewport_percent(amount)
|
|
511
520
|
|
|
512
521
|
async def scroll_up(self, amount: int = 25):
|
|
513
522
|
"""Scroll up by percentage of viewport height."""
|
|
514
|
-
|
|
515
|
-
await self.send(
|
|
516
|
-
cdp_input.synthesize_scroll_gesture(
|
|
517
|
-
x=0,
|
|
518
|
-
y=0,
|
|
519
|
-
y_distance=(bounds.height * (amount / 100)),
|
|
520
|
-
x_overscroll=0,
|
|
521
|
-
prevent_fling=True,
|
|
522
|
-
repeat_delay_ms=0,
|
|
523
|
-
speed=7777,
|
|
524
|
-
)
|
|
525
|
-
)
|
|
523
|
+
await self._scroll_by_viewport_percent(-amount)
|
|
526
524
|
|
|
527
525
|
# =========================================================================
|
|
528
526
|
# Navigation
|
|
@@ -185,6 +185,7 @@ tests/integration/test_locator_workflows.py
|
|
|
185
185
|
tests/integration/test_multi_profile_pool.py
|
|
186
186
|
tests/integration/test_page_reload.py
|
|
187
187
|
tests/integration/test_port_detection.py
|
|
188
|
+
tests/integration/test_scroll_embedded_target.py
|
|
188
189
|
tests/integration/test_startup_timeout.py
|
|
189
190
|
tests/integration/test_timeout_and_retry.py
|
|
190
191
|
tests/integration/test_workspace_workflows.py
|
|
@@ -175,6 +175,7 @@
|
|
|
175
175
|
"tests/integration/test_cookies_import.py",
|
|
176
176
|
"tests/integration/test_multi_profile_pool.py",
|
|
177
177
|
"tests/integration/test_port_detection.py",
|
|
178
|
+
"tests/integration/test_scroll_embedded_target.py",
|
|
178
179
|
"tests/integration/test_startup_timeout.py",
|
|
179
180
|
"tests/integration/test_image_cap.py",
|
|
180
181
|
"tests/integration/test_locator_workflows.py",
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Regression: page_scroll must work on embedded CDP targets that
|
|
2
|
+
don't implement Chrome's `Browser` domain (Electron / CEF / packaged
|
|
3
|
+
Chromium apps exposing CDP via `--remote-debugging-port`).
|
|
4
|
+
|
|
5
|
+
Pre-fix, `page_scroll(direction="down")` walked:
|
|
6
|
+
page_scroll → tab.scroll_down → tab.get_window (Browser.getWindowForTarget)
|
|
7
|
+
|
|
8
|
+
That CDP method is Chrome-browser-specific; embedded targets return
|
|
9
|
+
`-32601 Method not found` and the whole scroll path aborts. Reported
|
|
10
|
+
by a downstream integrator attaching to a packaged Electron app.
|
|
11
|
+
|
|
12
|
+
Fix (in `_tab.py`): rewrite `scroll_down` / `scroll_up` to use
|
|
13
|
+
`Runtime.evaluate` with `window.scrollBy(0, window.innerHeight * pct)`.
|
|
14
|
+
Runtime is universally supported. Bounds lookup and gesture
|
|
15
|
+
synthesis (Browser + Input.synthesizeScrollGesture) removed from the
|
|
16
|
+
scroll path entirely.
|
|
17
|
+
|
|
18
|
+
Tests below verify BOTH sides of the contract:
|
|
19
|
+
1. Positive — scroll_down actually scrolls a real Chrome fixture
|
|
20
|
+
(proves the JS-based rewrite works).
|
|
21
|
+
2. Negative — scroll_down succeeds even if `tab.get_window` is
|
|
22
|
+
patched to raise (proves the code path no longer depends on
|
|
23
|
+
the Browser-domain call that fails on Electron).
|
|
24
|
+
|
|
25
|
+
The negative test is the load-bearing one: without it, a future
|
|
26
|
+
"cleanup" that reintroduces `get_window()` in the scroll path would
|
|
27
|
+
silently re-break Electron users and CI wouldn't notice (regular
|
|
28
|
+
Chrome has getWindowForTarget so the code would appear to work).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import base64
|
|
34
|
+
import contextlib
|
|
35
|
+
import os
|
|
36
|
+
|
|
37
|
+
import pytest
|
|
38
|
+
|
|
39
|
+
from ai_dev_browser.core import page_goto, page_scroll
|
|
40
|
+
from ai_dev_browser.core.browser import browser_start, browser_stop
|
|
41
|
+
from ai_dev_browser.core.connection import connect_browser, get_active_tab
|
|
42
|
+
|
|
43
|
+
SKIP_INTEGRATION = os.environ.get("SKIP_INTEGRATION", "").lower() in (
|
|
44
|
+
"1",
|
|
45
|
+
"true",
|
|
46
|
+
"yes",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.fixture(autouse=True)
|
|
51
|
+
def _integration_guard():
|
|
52
|
+
if SKIP_INTEGRATION:
|
|
53
|
+
pytest.skip("SKIP_INTEGRATION is set")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.fixture
|
|
57
|
+
async def tab():
|
|
58
|
+
"""Headless Chrome loaded with a tall data:URL so scrollY has
|
|
59
|
+
room to change. Deliberately >5x viewport so a 25% scroll is a
|
|
60
|
+
detectable delta."""
|
|
61
|
+
result = browser_start(headless=True, temp=True, reuse="none")
|
|
62
|
+
assert "error" not in result, f"browser_start failed: {result}"
|
|
63
|
+
port = result["port"]
|
|
64
|
+
browser_client = None
|
|
65
|
+
try:
|
|
66
|
+
browser_client = await connect_browser(port=port)
|
|
67
|
+
the_tab = await get_active_tab(browser_client)
|
|
68
|
+
html = (
|
|
69
|
+
"<html><body style='margin:0'>"
|
|
70
|
+
+ "".join(
|
|
71
|
+
f"<div style='height:200px;background:hsl({i * 37 % 360},60%,50%);"
|
|
72
|
+
f"color:white;padding:20px;font-size:22px'>Row {i}</div>"
|
|
73
|
+
for i in range(30)
|
|
74
|
+
)
|
|
75
|
+
+ "</body></html>"
|
|
76
|
+
)
|
|
77
|
+
data_url = "data:text/html;base64," + base64.b64encode(html.encode()).decode()
|
|
78
|
+
await page_goto(the_tab, data_url)
|
|
79
|
+
yield the_tab
|
|
80
|
+
finally:
|
|
81
|
+
if browser_client is not None:
|
|
82
|
+
with contextlib.suppress(Exception):
|
|
83
|
+
await browser_client.close()
|
|
84
|
+
with contextlib.suppress(Exception):
|
|
85
|
+
browser_stop(port=port)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def _get_scroll_y(tab) -> float:
|
|
89
|
+
"""Read window.scrollY through the same Runtime.evaluate channel
|
|
90
|
+
the fix uses — deep-serialization deserialization of a numeric
|
|
91
|
+
RemoteObject varies, so pull via .value from the eval envelope."""
|
|
92
|
+
result = await tab.evaluate("window.scrollY", return_by_value=True)
|
|
93
|
+
return float(result if result is not None else 0)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def test_page_scroll_down_actually_scrolls(tab):
|
|
97
|
+
"""Positive: real headless Chrome + tall fixture → scroll_down
|
|
98
|
+
must observably move window.scrollY forward. Baseline that proves
|
|
99
|
+
the JS-based rewrite works at all before we go asserting Electron
|
|
100
|
+
compat."""
|
|
101
|
+
before = await _get_scroll_y(tab)
|
|
102
|
+
assert before == 0, f"fixture should start at scrollY=0, got {before}"
|
|
103
|
+
|
|
104
|
+
ok = await page_scroll(tab, direction="down", amount=50)
|
|
105
|
+
assert ok is True
|
|
106
|
+
|
|
107
|
+
after = await _get_scroll_y(tab)
|
|
108
|
+
assert after > before, (
|
|
109
|
+
f"scroll_down(50%) did not move scrollY: before={before}, after={after}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
async def test_page_scroll_up_after_down_reverses(tab):
|
|
114
|
+
"""Positive: scroll_up unwinds a prior scroll_down. Two-step
|
|
115
|
+
verifies the sign convention (positive percent = down / content
|
|
116
|
+
moves up) is preserved after the rewrite — a reversed sign would
|
|
117
|
+
slip past 'did it scroll?' asserts."""
|
|
118
|
+
await page_scroll(tab, direction="down", amount=50)
|
|
119
|
+
peak = await _get_scroll_y(tab)
|
|
120
|
+
assert peak > 0
|
|
121
|
+
|
|
122
|
+
await page_scroll(tab, direction="up", amount=50)
|
|
123
|
+
after_up = await _get_scroll_y(tab)
|
|
124
|
+
assert after_up < peak, (
|
|
125
|
+
f"scroll_up did not reduce scrollY: peak={peak}, after_up={after_up}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def test_page_scroll_survives_broken_get_window(tab, monkeypatch):
|
|
130
|
+
"""Regression pin: even if `tab.get_window` is completely broken —
|
|
131
|
+
the exact failure mode Electron / embedded targets exhibit —
|
|
132
|
+
page_scroll must still work. The rewrite guarantees this by never
|
|
133
|
+
calling get_window() from the scroll path.
|
|
134
|
+
|
|
135
|
+
If a future edit reintroduces `get_window()` into scroll_down or
|
|
136
|
+
a helper it calls, this test will fail on ProtocolException before
|
|
137
|
+
the scroll runs. That's the whole point — Electron users would
|
|
138
|
+
otherwise be silently broken again because regular Chrome CI
|
|
139
|
+
always has getWindowForTarget so the code would look green."""
|
|
140
|
+
|
|
141
|
+
async def _boom():
|
|
142
|
+
raise RuntimeError(
|
|
143
|
+
"Browser.getWindowForTarget wasn't found — simulated Electron target"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
monkeypatch.setattr(tab, "get_window", _boom)
|
|
147
|
+
|
|
148
|
+
before = await _get_scroll_y(tab)
|
|
149
|
+
ok = await page_scroll(tab, direction="down", amount=50)
|
|
150
|
+
assert ok is True
|
|
151
|
+
|
|
152
|
+
after = await _get_scroll_y(tab)
|
|
153
|
+
assert after > before, (
|
|
154
|
+
f"scroll_down with get_window broken failed to scroll: "
|
|
155
|
+
f"before={before}, after={after}"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async def test_page_scroll_to_bottom_survives_broken_get_window(tab, monkeypatch):
|
|
160
|
+
"""The other two page_scroll branches (to_bottom / to_top) already
|
|
161
|
+
used tab.evaluate, so they should already work on embedded
|
|
162
|
+
targets. This test pins that assumption so a future refactor
|
|
163
|
+
doesn't route them through get_window()."""
|
|
164
|
+
|
|
165
|
+
async def _boom():
|
|
166
|
+
raise RuntimeError("simulated Electron: Browser domain unavailable")
|
|
167
|
+
|
|
168
|
+
monkeypatch.setattr(tab, "get_window", _boom)
|
|
169
|
+
|
|
170
|
+
ok = await page_scroll(tab, to_bottom=True)
|
|
171
|
+
assert ok is True
|
|
172
|
+
|
|
173
|
+
after = await _get_scroll_y(tab)
|
|
174
|
+
assert after > 500, (
|
|
175
|
+
f"to_bottom should scroll a 6000px page substantially; got scrollY={after}"
|
|
176
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/ai_dev_browser.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_cookies_extract_windows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_multi_profile_pool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_dev_browser-0.12.2 → ai_dev_browser-0.12.3}/tests/integration/test_workspace_workflows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|