wavexis 2.3.0__tar.gz → 2.3.1__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.
- {wavexis-2.3.0 → wavexis-2.3.1}/CHANGELOG.md +7 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/PKG-INFO +1 -1
- {wavexis-2.3.0 → wavexis-2.3.1}/pyproject.toml +1 -1
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_auto_wait.py +29 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/__init__.py +1 -1
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/backend/base.py +9 -3
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/backend/bidi.py +36 -9
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/backend/cdp.py +18 -6
- {wavexis-2.3.0 → wavexis-2.3.1}/.dockerignore +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.github/workflows/ci.yml +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.github/workflows/docs.yml +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.github/workflows/release.yml +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/.gitignore +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/CODE_OF_CONDUCT.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/CONTRIBUTING.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/Dockerfile +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/LICENSE +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/README.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/SECURITY.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/actions.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/backends.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/cli.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/config.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/exceptions.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/multi.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/plugins.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/api/serve.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/auth.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/ci-cd.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/css.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/debug.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/record-replay.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/scraping.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/screenshots.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/cookbook/serve-mode.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/assert.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/backends.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/commands.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/init.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/installation.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/multi.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/perf.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/plugins.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/quickstart.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/raw.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/repl.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/guide/troubleshooting.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/docs/index.md +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/mkdocs.yml +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/stubs/bidiwave/__init__.pyi +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/stubs/cdpwave/__init__.pyi +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/__init__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/conftest.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/__init__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_a11y.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_animation.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_backend_selection.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_browser.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_console.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_css.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_debug.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_dialog.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_dom.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_dom_snapshot.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_emulation.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_emulation_advanced.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_eval.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_har.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_input.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_media.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_multi.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_navigate.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_network.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_network_advanced.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_overlay.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_pdf.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_perf.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_permissions.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_raw.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_record.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_scrape.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_screenshot.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_security.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_serve.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_service_worker.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_storage.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_tabs.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_webaudio.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/integration/test_webauthn.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/__init__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_abstract_backend_phase5.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_actions.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_actions_phase5.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_animation_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_assert_multi.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_auth.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_backend_manager.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_bidi_backend.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_bidi_phase5.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_bluetooth_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_cast_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_cleanup.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_cli_phase5.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_config.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_config_phase5.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_cookies_headers_actions.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_css_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_debug_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_dom_snapshot_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_download_screencast.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_emulation_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_error_handling.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_exceptions.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_format_record.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_global_options.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_init.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_input_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_interactions_crawl.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_manager.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_media_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_multi.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_multi_improvements.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_new_features.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_output.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_overlay_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_perf_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_perf_console.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_plugins.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_raw.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_record.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_repl.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_serve.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_service_worker_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_storage_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_webaudio_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/tests/unit/test_webauthn_action.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/__main__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/__init__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/accessibility.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/animation.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/base.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/bluetooth.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/browser.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/cast.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/console.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/cookies.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/crawl.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/css.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/debug.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/dialog.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/dom.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/dom_snapshot.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/download.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/emulation.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/eval.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/extract.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/form.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/har.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/headers.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/input.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/lighthouse.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/media.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/multi.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/navigate.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/network.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/overlay.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/pdf.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/performance.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/permissions.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/record.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/scrape.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/screencast.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/screenshot.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/security.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/service_worker.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/session.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/storage.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/tabs.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/wait.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/webaudio.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/webauthn.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/actions/websocket.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/auth.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/backend/__init__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/backend/manager.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cleanup.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/__init__.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_advanced.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_capture.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_config.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_debug.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_emulation.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_experimental.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_input.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_navigation.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_network.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_perf.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_serve.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_session.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_shared.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/_workflow.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/cli/app.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/config.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/exceptions.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/init.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/multi.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/output.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/plugins.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/record.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/repl.py +0 -0
- {wavexis-2.3.0 → wavexis-2.3.1}/wavexis/serve.py +0 -0
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to wavexis are documented in this file.
|
|
4
4
|
|
|
5
|
+
## v2.3.1 — 2026-07-07
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `auto_wait: bool = True` parameter in `click`, `fill`, `hover` (both backends)
|
|
10
|
+
- Pass `auto_wait=False` to skip element visibility polling for instant execution
|
|
11
|
+
|
|
5
12
|
## v2.3.0 — 2026-07-07
|
|
6
13
|
|
|
7
14
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wavexis
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Browser automation CLI — wraps cdpwave and bidiwave, no Node.js, no Chromium download
|
|
5
5
|
Project-URL: Homepage, https://github.com/MathiasPaulenko/wavexis
|
|
6
6
|
Project-URL: Repository, https://github.com/MathiasPaulenko/wavexis
|
|
@@ -68,6 +68,35 @@ class TestAutoWait:
|
|
|
68
68
|
asyncio.run(backend.click("#btn"))
|
|
69
69
|
assert call_order == ["wait", "scroll", "box"]
|
|
70
70
|
|
|
71
|
+
def test_cdp_click_auto_wait_false_skips_wait(self) -> None:
|
|
72
|
+
"""Test that click with auto_wait=False skips _wait_for_element."""
|
|
73
|
+
from wavexis.backend.cdp import CDPBackend
|
|
74
|
+
|
|
75
|
+
backend = CDPBackend()
|
|
76
|
+
backend._session = MagicMock()
|
|
77
|
+
backend._session.input = MagicMock()
|
|
78
|
+
backend._session.input.dispatch_mouse_event = AsyncMock()
|
|
79
|
+
|
|
80
|
+
call_order: list[str] = []
|
|
81
|
+
|
|
82
|
+
async def _mock_wait(selector: str, timeout_ms: int = 30000) -> None:
|
|
83
|
+
call_order.append("wait")
|
|
84
|
+
|
|
85
|
+
async def _mock_scroll(selector: str) -> None:
|
|
86
|
+
call_order.append("scroll")
|
|
87
|
+
|
|
88
|
+
async def _mock_box(selector: str) -> tuple[float, float]:
|
|
89
|
+
call_order.append("box")
|
|
90
|
+
return 50.0, 25.0
|
|
91
|
+
|
|
92
|
+
backend._wait_for_element = _mock_wait # type: ignore[assignment]
|
|
93
|
+
backend._scroll_into_view_if_needed = _mock_scroll # type: ignore[assignment]
|
|
94
|
+
backend._get_box_center = _mock_box # type: ignore[assignment]
|
|
95
|
+
|
|
96
|
+
asyncio.run(backend.click("#btn", auto_wait=False))
|
|
97
|
+
assert "wait" not in call_order
|
|
98
|
+
assert call_order == ["scroll", "box"]
|
|
99
|
+
|
|
71
100
|
def test_bidi_wait_for_element_found(self) -> None:
|
|
72
101
|
"""Test BiDi _wait_for_element returns when element is visible."""
|
|
73
102
|
from wavexis.backend.bidi import BiDiBackend
|
|
@@ -237,7 +237,11 @@ class AbstractBackend(ABC):
|
|
|
237
237
|
|
|
238
238
|
@abstractmethod
|
|
239
239
|
async def click(
|
|
240
|
-
self,
|
|
240
|
+
self,
|
|
241
|
+
selector: str,
|
|
242
|
+
button: str = "left",
|
|
243
|
+
click_count: int = 1,
|
|
244
|
+
auto_wait: bool = True,
|
|
241
245
|
) -> None:
|
|
242
246
|
"""Click an element matching a CSS selector."""
|
|
243
247
|
|
|
@@ -246,7 +250,9 @@ class AbstractBackend(ABC):
|
|
|
246
250
|
"""Type text into an element, optionally with delay between keystrokes."""
|
|
247
251
|
|
|
248
252
|
@abstractmethod
|
|
249
|
-
async def fill(
|
|
253
|
+
async def fill(
|
|
254
|
+
self, selector: str, value: str, auto_wait: bool = True
|
|
255
|
+
) -> None:
|
|
250
256
|
"""Fill an input element with a value (replaces existing content)."""
|
|
251
257
|
|
|
252
258
|
@abstractmethod
|
|
@@ -254,7 +260,7 @@ class AbstractBackend(ABC):
|
|
|
254
260
|
"""Select an option in a <select> element by value."""
|
|
255
261
|
|
|
256
262
|
@abstractmethod
|
|
257
|
-
async def hover(self, selector: str) -> None:
|
|
263
|
+
async def hover(self, selector: str, auto_wait: bool = True) -> None:
|
|
258
264
|
"""Hover over an element matching a CSS selector."""
|
|
259
265
|
|
|
260
266
|
@abstractmethod
|
|
@@ -853,11 +853,23 @@ class BiDiBackend(AbstractBackend):
|
|
|
853
853
|
await client.script.evaluate(self._context, js)
|
|
854
854
|
|
|
855
855
|
async def click(
|
|
856
|
-
self,
|
|
856
|
+
self,
|
|
857
|
+
selector: str,
|
|
858
|
+
button: str = "left",
|
|
859
|
+
click_count: int = 1,
|
|
860
|
+
auto_wait: bool = True,
|
|
857
861
|
) -> None:
|
|
858
|
-
"""Click an element via BiDi script.evaluate.
|
|
862
|
+
"""Click an element via BiDi script.evaluate.
|
|
863
|
+
|
|
864
|
+
Args:
|
|
865
|
+
selector: CSS selector for the target element.
|
|
866
|
+
button: Mouse button (unused in BiDi, for API compatibility).
|
|
867
|
+
click_count: Number of clicks to dispatch.
|
|
868
|
+
auto_wait: If True, wait for element to be visible before clicking.
|
|
869
|
+
"""
|
|
859
870
|
client = self._require_client()
|
|
860
|
-
|
|
871
|
+
if auto_wait:
|
|
872
|
+
await self._wait_for_element(selector)
|
|
861
873
|
await self._scroll_into_view_if_needed(selector)
|
|
862
874
|
escaped = selector.replace("'", "\\'")
|
|
863
875
|
js = (
|
|
@@ -887,10 +899,19 @@ class BiDiBackend(AbstractBackend):
|
|
|
887
899
|
if delay > 0:
|
|
888
900
|
await _asyncio.sleep(delay / 1000)
|
|
889
901
|
|
|
890
|
-
async def fill(
|
|
891
|
-
|
|
902
|
+
async def fill(
|
|
903
|
+
self, selector: str, value: str, auto_wait: bool = True
|
|
904
|
+
) -> None:
|
|
905
|
+
"""Fill an input element with a value via BiDi.
|
|
906
|
+
|
|
907
|
+
Args:
|
|
908
|
+
selector: CSS selector for the target element.
|
|
909
|
+
value: Value to set in the input field.
|
|
910
|
+
auto_wait: If True, wait for element to be visible before filling.
|
|
911
|
+
"""
|
|
892
912
|
client = self._require_client()
|
|
893
|
-
|
|
913
|
+
if auto_wait:
|
|
914
|
+
await self._wait_for_element(selector)
|
|
894
915
|
await self._scroll_into_view_if_needed(selector)
|
|
895
916
|
escaped = selector.replace("'", "\\'")
|
|
896
917
|
escaped_val = value.replace("\\", "\\\\").replace("'", "\\'")
|
|
@@ -906,10 +927,16 @@ class BiDiBackend(AbstractBackend):
|
|
|
906
927
|
js = f"document.querySelector('{escaped}').value = '{escaped_val}'"
|
|
907
928
|
await self._client.script.evaluate(self._context, js)
|
|
908
929
|
|
|
909
|
-
async def hover(self, selector: str) -> None:
|
|
910
|
-
"""Hover over an element via BiDi script.evaluate.
|
|
930
|
+
async def hover(self, selector: str, auto_wait: bool = True) -> None:
|
|
931
|
+
"""Hover over an element via BiDi script.evaluate.
|
|
932
|
+
|
|
933
|
+
Args:
|
|
934
|
+
selector: CSS selector for the target element.
|
|
935
|
+
auto_wait: If True, wait for element to be visible before hovering.
|
|
936
|
+
"""
|
|
911
937
|
client = self._require_client()
|
|
912
|
-
|
|
938
|
+
if auto_wait:
|
|
939
|
+
await self._wait_for_element(selector)
|
|
913
940
|
await self._scroll_into_view_if_needed(selector)
|
|
914
941
|
escaped = selector.replace("'", "\\'")
|
|
915
942
|
js = (
|
|
@@ -1072,7 +1072,11 @@ class CDPBackend(AbstractBackend):
|
|
|
1072
1072
|
await session.runtime.evaluate(js)
|
|
1073
1073
|
|
|
1074
1074
|
async def click(
|
|
1075
|
-
self,
|
|
1075
|
+
self,
|
|
1076
|
+
selector: str,
|
|
1077
|
+
button: str = "left",
|
|
1078
|
+
click_count: int = 1,
|
|
1079
|
+
auto_wait: bool = True,
|
|
1076
1080
|
) -> None:
|
|
1077
1081
|
"""Click an element matching a CSS selector.
|
|
1078
1082
|
|
|
@@ -1080,9 +1084,11 @@ class CDPBackend(AbstractBackend):
|
|
|
1080
1084
|
selector: CSS selector for the target element.
|
|
1081
1085
|
button: Mouse button — "left", "right", or "middle".
|
|
1082
1086
|
click_count: Number of clicks to dispatch.
|
|
1087
|
+
auto_wait: If True, wait for element to be visible before clicking.
|
|
1083
1088
|
"""
|
|
1084
1089
|
session = self._require_session()
|
|
1085
|
-
|
|
1090
|
+
if auto_wait:
|
|
1091
|
+
await self._wait_for_element(selector)
|
|
1086
1092
|
await self._scroll_into_view_if_needed(selector)
|
|
1087
1093
|
x, y = await self._get_box_center(selector)
|
|
1088
1094
|
btn_map = {"left": "left", "right": "right", "middle": "middle"}
|
|
@@ -1113,15 +1119,19 @@ class CDPBackend(AbstractBackend):
|
|
|
1113
1119
|
if delay > 0:
|
|
1114
1120
|
await asyncio.sleep(delay / 1000)
|
|
1115
1121
|
|
|
1116
|
-
async def fill(
|
|
1122
|
+
async def fill(
|
|
1123
|
+
self, selector: str, value: str, auto_wait: bool = True
|
|
1124
|
+
) -> None:
|
|
1117
1125
|
"""Fill an input element with a value (replaces existing content).
|
|
1118
1126
|
|
|
1119
1127
|
Args:
|
|
1120
1128
|
selector: CSS selector for the target element.
|
|
1121
1129
|
value: Value to set in the input field.
|
|
1130
|
+
auto_wait: If True, wait for element to be visible before filling.
|
|
1122
1131
|
"""
|
|
1123
1132
|
session = self._require_session()
|
|
1124
|
-
|
|
1133
|
+
if auto_wait:
|
|
1134
|
+
await self._wait_for_element(selector)
|
|
1125
1135
|
await self._scroll_into_view_if_needed(selector)
|
|
1126
1136
|
escaped = selector.replace("'", "\\'")
|
|
1127
1137
|
js = (
|
|
@@ -1155,14 +1165,16 @@ class CDPBackend(AbstractBackend):
|
|
|
1155
1165
|
if not result.get("result", {}).get("value"):
|
|
1156
1166
|
raise ElementNotFoundError(selector)
|
|
1157
1167
|
|
|
1158
|
-
async def hover(self, selector: str) -> None:
|
|
1168
|
+
async def hover(self, selector: str, auto_wait: bool = True) -> None:
|
|
1159
1169
|
"""Hover over an element matching a CSS selector.
|
|
1160
1170
|
|
|
1161
1171
|
Args:
|
|
1162
1172
|
selector: CSS selector for the target element.
|
|
1173
|
+
auto_wait: If True, wait for element to be visible before hovering.
|
|
1163
1174
|
"""
|
|
1164
1175
|
session = self._require_session()
|
|
1165
|
-
|
|
1176
|
+
if auto_wait:
|
|
1177
|
+
await self._wait_for_element(selector)
|
|
1166
1178
|
await self._scroll_into_view_if_needed(selector)
|
|
1167
1179
|
x, y = await self._get_box_center(selector)
|
|
1168
1180
|
await session.input.dispatch_mouse_event(
|
|
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
|
|
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
|