Appium-Python-Client 5.2.0__py3-none-any.whl
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.
- appium/__init__.py +17 -0
- appium/common/__init__.py +17 -0
- appium/common/exceptions.py +26 -0
- appium/common/helper.py +42 -0
- appium/common/logger.py +28 -0
- appium/options/__init__.py +0 -0
- appium/options/android/__init__.py +2 -0
- appium/options/android/common/__init__.py +0 -0
- appium/options/android/common/adb/__init__.py +0 -0
- appium/options/android/common/adb/adb_exec_timeout_option.py +41 -0
- appium/options/android/common/adb/adb_port_option.py +38 -0
- appium/options/android/common/adb/allow_delay_adb_option.py +39 -0
- appium/options/android/common/adb/build_tools_version_option.py +42 -0
- appium/options/android/common/adb/clear_device_logs_on_start_option.py +40 -0
- appium/options/android/common/adb/ignore_hidden_api_policy_error_option.py +40 -0
- appium/options/android/common/adb/logcat_filter_specs_option.py +42 -0
- appium/options/android/common/adb/logcat_format_option.py +39 -0
- appium/options/android/common/adb/mock_location_app_option.py +42 -0
- appium/options/android/common/adb/remote_adb_host_option.py +39 -0
- appium/options/android/common/adb/skip_logcat_capture_option.py +40 -0
- appium/options/android/common/adb/suppress_kill_server_option.py +39 -0
- appium/options/android/common/app/__init__.py +0 -0
- appium/options/android/common/app/allow_test_packages_option.py +40 -0
- appium/options/android/common/app/android_install_timeout_option.py +43 -0
- appium/options/android/common/app/app_activity_option.py +39 -0
- appium/options/android/common/app/app_package_option.py +39 -0
- appium/options/android/common/app/app_wait_activity_option.py +40 -0
- appium/options/android/common/app/app_wait_duration_option.py +41 -0
- appium/options/android/common/app/app_wait_for_launch_option.py +41 -0
- appium/options/android/common/app/app_wait_package_option.py +40 -0
- appium/options/android/common/app/auto_grant_premissions_option.py +40 -0
- appium/options/android/common/app/enforce_app_install_option.py +40 -0
- appium/options/android/common/app/intent_action_option.py +40 -0
- appium/options/android/common/app/intent_category_option.py +40 -0
- appium/options/android/common/app/intent_flags_option.py +40 -0
- appium/options/android/common/app/optional_intent_arguments_option.py +40 -0
- appium/options/android/common/app/remote_apps_cache_limit_option.py +42 -0
- appium/options/android/common/app/uninstall_other_packages_option.py +39 -0
- appium/options/android/common/avd/__init__.py +0 -0
- appium/options/android/common/avd/avd_args_option.py +38 -0
- appium/options/android/common/avd/avd_env_option.py +38 -0
- appium/options/android/common/avd/avd_launch_timeout_option.py +41 -0
- appium/options/android/common/avd/avd_option.py +41 -0
- appium/options/android/common/avd/avd_ready_timeout_option.py +41 -0
- appium/options/android/common/avd/gps_enabled_option.py +39 -0
- appium/options/android/common/avd/network_speed_option.py +41 -0
- appium/options/android/common/context/__init__.py +0 -0
- appium/options/android/common/context/auto_webview_timeout_option.py +41 -0
- appium/options/android/common/context/chrome_logging_prefs_option.py +41 -0
- appium/options/android/common/context/chrome_options_option.py +40 -0
- appium/options/android/common/context/chromedriver_args_option.py +41 -0
- appium/options/android/common/context/chromedriver_chrome_mapping_file_option.py +43 -0
- appium/options/android/common/context/chromedriver_disable_build_check_option.py +41 -0
- appium/options/android/common/context/chromedriver_executable_dir_option.py +43 -0
- appium/options/android/common/context/chromedriver_executable_option.py +38 -0
- appium/options/android/common/context/chromedriver_port_option.py +39 -0
- appium/options/android/common/context/chromedriver_ports_option.py +39 -0
- appium/options/android/common/context/chromedriver_use_system_executable_option.py +40 -0
- appium/options/android/common/context/ensure_webviews_have_pages_option.py +40 -0
- appium/options/android/common/context/extract_chrome_android_package_from_context_name_option.py +40 -0
- appium/options/android/common/context/native_web_screenshot_option.py +40 -0
- appium/options/android/common/context/recreate_chrome_driver_sessions_option.py +41 -0
- appium/options/android/common/context/show_chromedriver_log_option.py +39 -0
- appium/options/android/common/context/webview_devtools_port_option.py +40 -0
- appium/options/android/common/localization/__init__.py +0 -0
- appium/options/android/common/localization/locale_script_option.py +40 -0
- appium/options/android/common/locking/__init__.py +0 -0
- appium/options/android/common/locking/skip_unlock_option.py +42 -0
- appium/options/android/common/locking/unlock_key_option.py +40 -0
- appium/options/android/common/locking/unlock_strategy_option.py +40 -0
- appium/options/android/common/locking/unlock_success_timeout_option.py +43 -0
- appium/options/android/common/locking/unlock_type_option.py +40 -0
- appium/options/android/common/mjpeg/__init__.py +0 -0
- appium/options/android/common/mjpeg/mjpeg_screenshot_url_option.py +40 -0
- appium/options/android/common/other/__init__.py +0 -0
- appium/options/android/common/other/disable_suppress_accessibility_service_option.py +40 -0
- appium/options/android/common/other/user_profile_option.py +42 -0
- appium/options/android/common/signing/__init__.py +0 -0
- appium/options/android/common/signing/key_alias_option.py +40 -0
- appium/options/android/common/signing/key_password_option.py +40 -0
- appium/options/android/common/signing/keystore_password_option.py +40 -0
- appium/options/android/common/signing/keystore_path_option.py +40 -0
- appium/options/android/common/signing/no_sign_option.py +42 -0
- appium/options/android/common/signing/use_keystore_option.py +42 -0
- appium/options/android/espresso/__init__.py +0 -0
- appium/options/android/espresso/activity_options_option.py +41 -0
- appium/options/android/espresso/app_locale_option.py +44 -0
- appium/options/android/espresso/base.py +221 -0
- appium/options/android/espresso/espresso_build_config_option.py +46 -0
- appium/options/android/espresso/espresso_server_launch_timeout_option.py +43 -0
- appium/options/android/espresso/force_espresso_rebuild_option.py +41 -0
- appium/options/android/espresso/intent_options_option.py +41 -0
- appium/options/android/espresso/show_gradle_log_option.py +39 -0
- appium/options/android/uiautomator2/__init__.py +0 -0
- appium/options/android/uiautomator2/base.py +221 -0
- appium/options/android/uiautomator2/disable_window_animation_option.py +40 -0
- appium/options/android/uiautomator2/mjpeg_server_port_option.py +41 -0
- appium/options/android/uiautomator2/skip_device_initialization_option.py +40 -0
- appium/options/android/uiautomator2/skip_server_installation_option.py +44 -0
- appium/options/android/uiautomator2/uiautomator2_server_install_timeout_option.py +44 -0
- appium/options/android/uiautomator2/uiautomator2_server_launch_timeout_option.py +44 -0
- appium/options/android/uiautomator2/uiautomator2_server_read_timeout_option.py +46 -0
- appium/options/common/__init__.py +1 -0
- appium/options/common/app_option.py +41 -0
- appium/options/common/auto_web_view_option.py +40 -0
- appium/options/common/automation_name_option.py +38 -0
- appium/options/common/base.py +125 -0
- appium/options/common/browser_name_option.py +38 -0
- appium/options/common/bundle_id_option.py +38 -0
- appium/options/common/clear_system_files_option.py +38 -0
- appium/options/common/device_name_option.py +38 -0
- appium/options/common/enable_performance_logging_option.py +38 -0
- appium/options/common/event_timings_option.py +40 -0
- appium/options/common/full_reset_option.py +38 -0
- appium/options/common/is_headless_option.py +39 -0
- appium/options/common/language_option.py +38 -0
- appium/options/common/locale_option.py +38 -0
- appium/options/common/new_command_timeout_option.py +41 -0
- appium/options/common/no_reset_option.py +38 -0
- appium/options/common/orientation_option.py +40 -0
- appium/options/common/other_apps_option.py +39 -0
- appium/options/common/platform_version_option.py +40 -0
- appium/options/common/postrun_option.py +39 -0
- appium/options/common/prerun_option.py +40 -0
- appium/options/common/print_page_source_on_find_failure_option.py +40 -0
- appium/options/common/skip_log_capture_option.py +38 -0
- appium/options/common/supports_capabilities.py +26 -0
- appium/options/common/system_host_option.py +38 -0
- appium/options/common/system_port_option.py +38 -0
- appium/options/common/udid_option.py +38 -0
- appium/options/flutter_integration/__init__.py +15 -0
- appium/options/flutter_integration/base.py +39 -0
- appium/options/flutter_integration/flutter_element_wait_timeout_option.py +50 -0
- appium/options/flutter_integration/flutter_enable_mock_camera_option.py +44 -0
- appium/options/flutter_integration/flutter_server_launch_timeout_option.py +51 -0
- appium/options/flutter_integration/flutter_system_port_option.py +45 -0
- appium/options/gecko/__init__.py +1 -0
- appium/options/gecko/android_storage_option.py +39 -0
- appium/options/gecko/base.py +51 -0
- appium/options/gecko/firefox_options_option.py +38 -0
- appium/options/gecko/marionette_port_option.py +43 -0
- appium/options/gecko/verbosity_option.py +40 -0
- appium/options/ios/__init__.py +2 -0
- appium/options/ios/safari/__init__.py +0 -0
- appium/options/ios/safari/automatic_inspection_option.py +41 -0
- appium/options/ios/safari/automatic_profiling_option.py +41 -0
- appium/options/ios/safari/base.py +51 -0
- appium/options/ios/safari/device_name_option.py +43 -0
- appium/options/ios/safari/device_type_option.py +41 -0
- appium/options/ios/safari/device_udid_option.py +43 -0
- appium/options/ios/safari/platform_build_version_option.py +41 -0
- appium/options/ios/safari/platform_version_option.py +41 -0
- appium/options/ios/safari/use_simulator_option.py +41 -0
- appium/options/ios/safari/webkit_webrtc_option.py +52 -0
- appium/options/ios/xcuitest/__init__.py +0 -0
- appium/options/ios/xcuitest/app/__init__.py +0 -0
- appium/options/ios/xcuitest/app/app_install_strategy_option.py +46 -0
- appium/options/ios/xcuitest/app/app_push_timeout_option.py +42 -0
- appium/options/ios/xcuitest/app/localizable_strings_dir_option.py +39 -0
- appium/options/ios/xcuitest/base.py +223 -0
- appium/options/ios/xcuitest/general/__init__.py +0 -0
- appium/options/ios/xcuitest/general/include_device_caps_to_session_info_option.py +41 -0
- appium/options/ios/xcuitest/general/reset_location_service_option.py +39 -0
- appium/options/ios/xcuitest/other/__init__.py +0 -0
- appium/options/ios/xcuitest/other/command_timeouts_option.py +57 -0
- appium/options/ios/xcuitest/other/launch_with_idb_option.py +42 -0
- appium/options/ios/xcuitest/other/show_ios_log_option.py +39 -0
- appium/options/ios/xcuitest/other/use_json_source_option.py +39 -0
- appium/options/ios/xcuitest/simulator/__init__.py +0 -0
- appium/options/ios/xcuitest/simulator/calendar_access_authorized_option.py +41 -0
- appium/options/ios/xcuitest/simulator/calendar_format_option.py +38 -0
- appium/options/ios/xcuitest/simulator/connect_hardware_keyboard_option.py +43 -0
- appium/options/ios/xcuitest/simulator/custom_ssl_cert_option.py +39 -0
- appium/options/ios/xcuitest/simulator/enforce_fresh_simulator_creation_option.py +39 -0
- appium/options/ios/xcuitest/simulator/force_simulator_software_keyboard_presence_option.py +45 -0
- appium/options/ios/xcuitest/simulator/ios_simulator_logs_predicate_option.py +38 -0
- appium/options/ios/xcuitest/simulator/keep_key_chains_option.py +39 -0
- appium/options/ios/xcuitest/simulator/keychains_exclude_patterns_option.py +44 -0
- appium/options/ios/xcuitest/simulator/permissions_option.py +50 -0
- appium/options/ios/xcuitest/simulator/reduce_motion_option.py +40 -0
- appium/options/ios/xcuitest/simulator/reset_on_session_start_only_option.py +41 -0
- appium/options/ios/xcuitest/simulator/scale_factor_option.py +44 -0
- appium/options/ios/xcuitest/simulator/shutdown_other_simulators_option.py +44 -0
- appium/options/ios/xcuitest/simulator/simulator_devices_set_path_option.py +41 -0
- appium/options/ios/xcuitest/simulator/simulator_pasteboard_automatic_sync_option.py +42 -0
- appium/options/ios/xcuitest/simulator/simulator_startup_timeout_option.py +46 -0
- appium/options/ios/xcuitest/simulator/simulator_trace_pointer_option.py +41 -0
- appium/options/ios/xcuitest/simulator/simulator_window_center_option.py +41 -0
- appium/options/ios/xcuitest/wda/__init__.py +0 -0
- appium/options/ios/xcuitest/wda/allow_provisioning_device_regitration_option.py +40 -0
- appium/options/ios/xcuitest/wda/auto_accept_alerts_option.py +39 -0
- appium/options/ios/xcuitest/wda/auto_disimiss_alerts_option.py +39 -0
- appium/options/ios/xcuitest/wda/derived_data_path_option.py +41 -0
- appium/options/ios/xcuitest/wda/disable_automatic_screenshots_option.py +40 -0
- appium/options/ios/xcuitest/wda/force_app_launch_option.py +42 -0
- appium/options/ios/xcuitest/wda/keychain_password_option.py +39 -0
- appium/options/ios/xcuitest/wda/keychain_path_option.py +39 -0
- appium/options/ios/xcuitest/wda/max_typing_frequency_option.py +40 -0
- appium/options/ios/xcuitest/wda/mjpeg_server_port_option.py +42 -0
- appium/options/ios/xcuitest/wda/process_arguments_option.py +42 -0
- appium/options/ios/xcuitest/wda/result_bundle_path_option.py +42 -0
- appium/options/ios/xcuitest/wda/screenshot_quality_option.py +42 -0
- appium/options/ios/xcuitest/wda/should_terminate_app_option.py +42 -0
- appium/options/ios/xcuitest/wda/should_use_singleton_test_manager_option.py +39 -0
- appium/options/ios/xcuitest/wda/show_xcode_log_option.py +40 -0
- appium/options/ios/xcuitest/wda/simple_is_visible_check_option.py +42 -0
- appium/options/ios/xcuitest/wda/updated_wda_bundle_id_option.py +39 -0
- appium/options/ios/xcuitest/wda/use_native_caching_strategy_option.py +41 -0
- appium/options/ios/xcuitest/wda/use_new_wda_option.py +51 -0
- appium/options/ios/xcuitest/wda/use_prebuilt_wda_option.py +39 -0
- appium/options/ios/xcuitest/wda/use_simple_build_test_option.py +40 -0
- appium/options/ios/xcuitest/wda/use_xctestrun_file_option.py +49 -0
- appium/options/ios/xcuitest/wda/wait_for_idle_timeout_option.py +45 -0
- appium/options/ios/xcuitest/wda/wait_for_quiescence_option.py +42 -0
- appium/options/ios/xcuitest/wda/wda_base_url_option.py +41 -0
- appium/options/ios/xcuitest/wda/wda_connection_timeout_option.py +43 -0
- appium/options/ios/xcuitest/wda/wda_eventloop_idle_delay_option.py +46 -0
- appium/options/ios/xcuitest/wda/wda_launch_timeout_option.py +41 -0
- appium/options/ios/xcuitest/wda/wda_local_port_option.py +41 -0
- appium/options/ios/xcuitest/wda/wda_startup_retries_option.py +39 -0
- appium/options/ios/xcuitest/wda/wda_startup_retry_interval_option.py +43 -0
- appium/options/ios/xcuitest/wda/web_driver_agent_url_option.py +39 -0
- appium/options/ios/xcuitest/wda/xcode_org_id_option.py +39 -0
- appium/options/ios/xcuitest/wda/xcode_signing_id_option.py +39 -0
- appium/options/ios/xcuitest/webview/__init__.py +0 -0
- appium/options/ios/xcuitest/webview/absolute_web_locations_option.py +42 -0
- appium/options/ios/xcuitest/webview/additional_webview_bundle_ids_option.py +40 -0
- appium/options/ios/xcuitest/webview/enable_async_execute_from_https_option.py +39 -0
- appium/options/ios/xcuitest/webview/full_context_list_option.py +42 -0
- appium/options/ios/xcuitest/webview/include_safari_in_webviews_option.py +41 -0
- appium/options/ios/xcuitest/webview/native_web_tap_option.py +40 -0
- appium/options/ios/xcuitest/webview/safari_garbage_collect_option.py +39 -0
- appium/options/ios/xcuitest/webview/safari_ignore_fraud_warning_option.py +39 -0
- appium/options/ios/xcuitest/webview/safari_ignore_web_hostnames_option.py +42 -0
- appium/options/ios/xcuitest/webview/safari_initial_url_option.py +38 -0
- appium/options/ios/xcuitest/webview/safari_log_all_communication_hex_dump_option.py +43 -0
- appium/options/ios/xcuitest/webview/safari_log_all_communication_option.py +40 -0
- appium/options/ios/xcuitest/webview/safari_open_links_in_background_option.py +39 -0
- appium/options/ios/xcuitest/webview/safari_socket_chunk_size_option.py +41 -0
- appium/options/ios/xcuitest/webview/safari_web_inspector_max_frame_length_option.py +41 -0
- appium/options/ios/xcuitest/webview/webkit_response_timeout_option.py +43 -0
- appium/options/ios/xcuitest/webview/webview_connect_retries_option.py +40 -0
- appium/options/ios/xcuitest/webview/webview_connect_timeout_option.py +43 -0
- appium/options/mac/__init__.py +1 -0
- appium/options/mac/mac2/__init__.py +0 -0
- appium/options/mac/mac2/app_path_option.py +39 -0
- appium/options/mac/mac2/arguments_option.py +39 -0
- appium/options/mac/mac2/base.py +113 -0
- appium/options/mac/mac2/bootstrap_root_option.py +41 -0
- appium/options/mac/mac2/environment_option.py +41 -0
- appium/options/mac/mac2/server_startup_timeout_option.py +44 -0
- appium/options/mac/mac2/show_server_logs_option.py +39 -0
- appium/options/mac/mac2/skip_app_kill_option.py +40 -0
- appium/options/mac/mac2/web_driver_agent_mac_url_option.py +39 -0
- appium/options/windows/__init__.py +1 -0
- appium/options/windows/windows/__init__.py +0 -0
- appium/options/windows/windows/app_arguments_option.py +40 -0
- appium/options/windows/windows/app_top_level_window_option.py +40 -0
- appium/options/windows/windows/app_working_dir_option.py +40 -0
- appium/options/windows/windows/base.py +97 -0
- appium/options/windows/windows/create_session_timeout_option.py +45 -0
- appium/options/windows/windows/expreimental_web_driver_option.py +39 -0
- appium/options/windows/windows/wait_for_app_launch_option.py +43 -0
- appium/protocols/__init__.py +13 -0
- appium/protocols/webdriver/__init__.py +13 -0
- appium/protocols/webdriver/can_execute_commands.py +23 -0
- appium/protocols/webdriver/can_execute_scripts.py +27 -0
- appium/protocols/webdriver/can_find_elements.py +32 -0
- appium/protocols/webdriver/can_remember_extension_presence.py +23 -0
- appium/py.typed +0 -0
- appium/version.py +22 -0
- appium/webdriver/__init__.py +20 -0
- appium/webdriver/appium_connection.py +65 -0
- appium/webdriver/appium_service.py +330 -0
- appium/webdriver/applicationstate.py +21 -0
- appium/webdriver/client_config.py +38 -0
- appium/webdriver/clipboard_content_type.py +19 -0
- appium/webdriver/command_method.py +27 -0
- appium/webdriver/common/__init__.py +17 -0
- appium/webdriver/common/appiumby.py +54 -0
- appium/webdriver/connectiontype.py +42 -0
- appium/webdriver/errorhandler.py +125 -0
- appium/webdriver/extensions/__init__.py +13 -0
- appium/webdriver/extensions/action_helpers.py +188 -0
- appium/webdriver/extensions/android/__init__.py +0 -0
- appium/webdriver/extensions/android/activities.py +65 -0
- appium/webdriver/extensions/android/common.py +59 -0
- appium/webdriver/extensions/android/display.py +48 -0
- appium/webdriver/extensions/android/gsm.py +147 -0
- appium/webdriver/extensions/android/nativekey.py +1119 -0
- appium/webdriver/extensions/android/network.py +175 -0
- appium/webdriver/extensions/android/performance.py +85 -0
- appium/webdriver/extensions/android/power.py +80 -0
- appium/webdriver/extensions/android/sms.py +50 -0
- appium/webdriver/extensions/android/system_bars.py +58 -0
- appium/webdriver/extensions/applications.py +274 -0
- appium/webdriver/extensions/clipboard.py +107 -0
- appium/webdriver/extensions/context.py +63 -0
- appium/webdriver/extensions/device_time.py +75 -0
- appium/webdriver/extensions/execute_driver.py +60 -0
- appium/webdriver/extensions/execute_mobile_command.py +62 -0
- appium/webdriver/extensions/flutter_integration/__init__.py +13 -0
- appium/webdriver/extensions/flutter_integration/flutter_commands.py +296 -0
- appium/webdriver/extensions/flutter_integration/flutter_finder.py +55 -0
- appium/webdriver/extensions/flutter_integration/scroll_directions.py +6 -0
- appium/webdriver/extensions/hw_actions.py +149 -0
- appium/webdriver/extensions/images_comparison.py +132 -0
- appium/webdriver/extensions/keyboard.py +168 -0
- appium/webdriver/extensions/location.py +98 -0
- appium/webdriver/extensions/log_event.py +68 -0
- appium/webdriver/extensions/logs.py +53 -0
- appium/webdriver/extensions/remote_fs.py +110 -0
- appium/webdriver/extensions/screen_record.py +207 -0
- appium/webdriver/extensions/session.py +41 -0
- appium/webdriver/extensions/settings.py +49 -0
- appium/webdriver/locator_converter.py +29 -0
- appium/webdriver/mobilecommand.py +104 -0
- appium/webdriver/switch_to.py +35 -0
- appium/webdriver/webdriver.py +495 -0
- appium/webdriver/webelement.py +130 -0
- appium_python_client-5.2.0.dist-info/METADATA +573 -0
- appium_python_client-5.2.0.dist-info/RECORD +324 -0
- appium_python_client-5.2.0.dist-info/WHEEL +4 -0
- appium_python_client-5.2.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
|
|
16
|
+
|
|
17
|
+
from selenium import webdriver
|
|
18
|
+
from selenium.common.exceptions import (
|
|
19
|
+
InvalidArgumentException,
|
|
20
|
+
SessionNotCreatedException,
|
|
21
|
+
UnknownMethodException,
|
|
22
|
+
WebDriverException,
|
|
23
|
+
)
|
|
24
|
+
from selenium.webdriver.remote.command import Command as RemoteCommand
|
|
25
|
+
from selenium.webdriver.remote.remote_connection import RemoteConnection
|
|
26
|
+
from typing_extensions import Self
|
|
27
|
+
|
|
28
|
+
from appium.common.logger import logger
|
|
29
|
+
from appium.options.common.base import AppiumOptions
|
|
30
|
+
|
|
31
|
+
from .appium_connection import AppiumConnection
|
|
32
|
+
from .client_config import AppiumClientConfig
|
|
33
|
+
from .errorhandler import MobileErrorHandler
|
|
34
|
+
from .extensions.action_helpers import ActionHelpers
|
|
35
|
+
from .extensions.android.activities import Activities
|
|
36
|
+
from .extensions.android.common import Common
|
|
37
|
+
from .extensions.android.display import Display
|
|
38
|
+
from .extensions.android.gsm import Gsm
|
|
39
|
+
from .extensions.android.network import Network
|
|
40
|
+
from .extensions.android.performance import Performance
|
|
41
|
+
from .extensions.android.power import Power
|
|
42
|
+
from .extensions.android.sms import Sms
|
|
43
|
+
from .extensions.android.system_bars import SystemBars
|
|
44
|
+
from .extensions.applications import Applications
|
|
45
|
+
from .extensions.clipboard import Clipboard
|
|
46
|
+
from .extensions.context import Context
|
|
47
|
+
from .extensions.device_time import DeviceTime
|
|
48
|
+
from .extensions.execute_driver import ExecuteDriver
|
|
49
|
+
from .extensions.execute_mobile_command import ExecuteMobileCommand
|
|
50
|
+
from .extensions.hw_actions import HardwareActions
|
|
51
|
+
from .extensions.images_comparison import ImagesComparison
|
|
52
|
+
from .extensions.keyboard import Keyboard
|
|
53
|
+
from .extensions.location import Location
|
|
54
|
+
from .extensions.log_event import LogEvent
|
|
55
|
+
from .extensions.logs import Logs
|
|
56
|
+
from .extensions.remote_fs import RemoteFS
|
|
57
|
+
from .extensions.screen_record import ScreenRecord
|
|
58
|
+
from .extensions.session import Session
|
|
59
|
+
from .extensions.settings import Settings
|
|
60
|
+
from .locator_converter import AppiumLocatorConverter
|
|
61
|
+
from .mobilecommand import MobileCommand as Command
|
|
62
|
+
from .switch_to import MobileSwitchTo
|
|
63
|
+
from .webelement import WebElement as MobileWebElement
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ExtensionBase:
|
|
67
|
+
"""
|
|
68
|
+
Used to define an extension command as driver's methods.
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
When you want to add `example_command` which calls a get request to
|
|
72
|
+
`session/$sessionId/path/to/your/custom/url`.
|
|
73
|
+
|
|
74
|
+
#. Defines an extension as a subclass of `ExtensionBase`
|
|
75
|
+
.. code-block:: python
|
|
76
|
+
|
|
77
|
+
class YourCustomCommand(ExtensionBase):
|
|
78
|
+
def method_name(self):
|
|
79
|
+
return 'custom_method_name'
|
|
80
|
+
|
|
81
|
+
# Define a method with the name of `method_name`
|
|
82
|
+
def custom_method_name(self):
|
|
83
|
+
# Generally the response of Appium follows `{ 'value': { data } }`
|
|
84
|
+
# format.
|
|
85
|
+
return self.execute()['value']
|
|
86
|
+
|
|
87
|
+
# Used to register the command pair as "Appium command" in this driver.
|
|
88
|
+
def add_command(self):
|
|
89
|
+
return ('GET', 'session/$sessionId/path/to/your/custom/url')
|
|
90
|
+
|
|
91
|
+
#. Creates a session with the extension.
|
|
92
|
+
.. code-block:: python
|
|
93
|
+
|
|
94
|
+
# Appium capabilities
|
|
95
|
+
options = AppiumOptions()
|
|
96
|
+
driver = webdriver.Remote('http://localhost:4723/wd/hub', options=options,
|
|
97
|
+
extensions=[YourCustomCommand])
|
|
98
|
+
|
|
99
|
+
#. Calls the custom command
|
|
100
|
+
.. code-block:: python
|
|
101
|
+
|
|
102
|
+
# Then, the driver calls a get request against
|
|
103
|
+
# `session/$sessionId/path/to/your/custom/url`. `$sessionId` will be
|
|
104
|
+
# replaced properly in the driver. Then, the method returns
|
|
105
|
+
# the `value` part of the response.
|
|
106
|
+
driver.custom_method_name()
|
|
107
|
+
|
|
108
|
+
#. Remove added commands (if needed)
|
|
109
|
+
.. code-block:: python
|
|
110
|
+
|
|
111
|
+
# New commands are added by `setattr`. They remain in the module,
|
|
112
|
+
# so you should explicitly delete them to define the same name method
|
|
113
|
+
# with different arguments or process in the method.
|
|
114
|
+
driver.delete_extensions()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
You can give arbitrary arguments for the command like the below.
|
|
118
|
+
|
|
119
|
+
.. code-block:: python
|
|
120
|
+
|
|
121
|
+
class YourCustomCommand(ExtensionBase):
|
|
122
|
+
def method_name(self):
|
|
123
|
+
return 'custom_method_name'
|
|
124
|
+
|
|
125
|
+
def test_command(self, argument):
|
|
126
|
+
return self.execute(argument)['value']
|
|
127
|
+
|
|
128
|
+
def add_command(self):
|
|
129
|
+
return ('post', 'session/$sessionId/path/to/your/custom/url')
|
|
130
|
+
|
|
131
|
+
driver = webdriver.Remote('http://localhost:4723/wd/hub', options=options,
|
|
132
|
+
extensions=[YourCustomCommand])
|
|
133
|
+
|
|
134
|
+
# Then, the driver sends a post request to `session/$sessionId/path/to/your/custom/url`
|
|
135
|
+
# with `{'dummy_arg': 'as a value'}` JSON body.
|
|
136
|
+
driver.custom_method_name({'dummy_arg': 'as a value'})
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
When you customize the URL dynamically with element id.
|
|
140
|
+
|
|
141
|
+
.. code-block:: python
|
|
142
|
+
|
|
143
|
+
class CustomURLCommand(ExtensionBase):
|
|
144
|
+
def method_name(self):
|
|
145
|
+
return 'custom_method_name'
|
|
146
|
+
|
|
147
|
+
def custom_method_name(self, element_id):
|
|
148
|
+
return self.execute({'id': element_id})['value']
|
|
149
|
+
|
|
150
|
+
def add_command(self):
|
|
151
|
+
return ('GET', 'session/$sessionId/path/to/your/custom/$id/url')
|
|
152
|
+
|
|
153
|
+
driver = webdriver.Remote('http://localhost:4723/wd/hub', options=options,
|
|
154
|
+
extensions=[YourCustomCommand])
|
|
155
|
+
element = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='id')
|
|
156
|
+
|
|
157
|
+
# Then, the driver calls a get request to `session/$sessionId/path/to/your/custom/$id/url`
|
|
158
|
+
# with replacing the `$id` with the given `element.id`
|
|
159
|
+
driver.custom_method_name(element.id)
|
|
160
|
+
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def __init__(self, execute: Callable[[str, Dict], Dict[str, Any]]):
|
|
164
|
+
self._execute = execute
|
|
165
|
+
|
|
166
|
+
def execute(self, parameters: Union[Dict[str, Any], None] = None) -> Any:
|
|
167
|
+
param = {}
|
|
168
|
+
if parameters:
|
|
169
|
+
param = parameters
|
|
170
|
+
return self._execute(self.method_name(), param)
|
|
171
|
+
|
|
172
|
+
def method_name(self) -> str:
|
|
173
|
+
"""
|
|
174
|
+
Expected to return a method name.
|
|
175
|
+
This name will be available as a driver method.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
'str' The method name.
|
|
179
|
+
"""
|
|
180
|
+
raise NotImplementedError()
|
|
181
|
+
|
|
182
|
+
def add_command(self) -> Tuple[str, str]:
|
|
183
|
+
"""
|
|
184
|
+
Expected to define the pair of HTTP method and its URL.
|
|
185
|
+
"""
|
|
186
|
+
raise NotImplementedError()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _get_remote_connection_and_client_config(
|
|
190
|
+
command_executor: Union[str, AppiumConnection], client_config: Optional[AppiumClientConfig] = None
|
|
191
|
+
) -> tuple[AppiumConnection, Optional[AppiumClientConfig]]:
|
|
192
|
+
"""Return the pair of command executor and client config.
|
|
193
|
+
If the given command executor is a custom one, returned client config will
|
|
194
|
+
be None since the custom command executor has its own client config already.
|
|
195
|
+
The custom command executor's one will be prior than the given client config.
|
|
196
|
+
"""
|
|
197
|
+
if not isinstance(command_executor, str):
|
|
198
|
+
# client config already defined in the custom command executor
|
|
199
|
+
# will be prior than the given one.
|
|
200
|
+
return (command_executor, None)
|
|
201
|
+
|
|
202
|
+
# command_executor is str
|
|
203
|
+
|
|
204
|
+
# Do not keep None to avoid warnings in Selenium
|
|
205
|
+
# which can prevent with ClientConfig instance usage.
|
|
206
|
+
new_client_config = AppiumClientConfig(remote_server_addr=command_executor) if client_config is None else client_config
|
|
207
|
+
return (AppiumConnection(client_config=new_client_config), new_client_config)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class WebDriver(
|
|
211
|
+
webdriver.Remote,
|
|
212
|
+
ActionHelpers,
|
|
213
|
+
Activities,
|
|
214
|
+
Applications,
|
|
215
|
+
Clipboard,
|
|
216
|
+
Context,
|
|
217
|
+
Common,
|
|
218
|
+
DeviceTime,
|
|
219
|
+
Display,
|
|
220
|
+
ExecuteDriver,
|
|
221
|
+
ExecuteMobileCommand,
|
|
222
|
+
Gsm,
|
|
223
|
+
HardwareActions,
|
|
224
|
+
ImagesComparison,
|
|
225
|
+
Keyboard,
|
|
226
|
+
Location,
|
|
227
|
+
LogEvent,
|
|
228
|
+
Logs,
|
|
229
|
+
Network,
|
|
230
|
+
Performance,
|
|
231
|
+
Power,
|
|
232
|
+
RemoteFS,
|
|
233
|
+
ScreenRecord,
|
|
234
|
+
Session,
|
|
235
|
+
Settings,
|
|
236
|
+
Sms,
|
|
237
|
+
SystemBars,
|
|
238
|
+
):
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
command_executor: Union[str, AppiumConnection] = 'http://127.0.0.1:4723',
|
|
242
|
+
extensions: Optional[List[Type['ExtensionBase']]] = None,
|
|
243
|
+
options: Union[AppiumOptions, List[AppiumOptions], None] = None,
|
|
244
|
+
client_config: Optional[AppiumClientConfig] = None,
|
|
245
|
+
):
|
|
246
|
+
command_executor, client_config = _get_remote_connection_and_client_config(
|
|
247
|
+
command_executor=command_executor, client_config=client_config
|
|
248
|
+
)
|
|
249
|
+
super().__init__(
|
|
250
|
+
command_executor=command_executor,
|
|
251
|
+
options=options, # type: ignore[arg-type]
|
|
252
|
+
locator_converter=AppiumLocatorConverter(),
|
|
253
|
+
web_element_cls=MobileWebElement,
|
|
254
|
+
client_config=client_config,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# to explicitly set type after the initialization
|
|
258
|
+
self.command_executor: RemoteConnection
|
|
259
|
+
|
|
260
|
+
self._add_commands()
|
|
261
|
+
|
|
262
|
+
self.error_handler = MobileErrorHandler()
|
|
263
|
+
|
|
264
|
+
if client_config and client_config.direct_connection:
|
|
265
|
+
self._update_command_executor(keep_alive=client_config.keep_alive)
|
|
266
|
+
|
|
267
|
+
self._absent_extensions: Set[str] = set()
|
|
268
|
+
|
|
269
|
+
self._extensions = extensions or []
|
|
270
|
+
for extension in self._extensions:
|
|
271
|
+
instance = extension(self.execute)
|
|
272
|
+
method_name = instance.method_name()
|
|
273
|
+
if hasattr(WebDriver, method_name):
|
|
274
|
+
logger.debug(f"Overriding the method '{method_name}'")
|
|
275
|
+
|
|
276
|
+
# add a new method named 'instance.method_name()' and call it
|
|
277
|
+
setattr(WebDriver, method_name, getattr(instance, method_name))
|
|
278
|
+
method, url_cmd = instance.add_command()
|
|
279
|
+
self.command_executor.add_command(method_name, method.upper(), url_cmd)
|
|
280
|
+
|
|
281
|
+
if TYPE_CHECKING:
|
|
282
|
+
|
|
283
|
+
def find_element(self, by: str, value: Union[str, Dict, None] = None) -> 'MobileWebElement': # type: ignore[override]
|
|
284
|
+
...
|
|
285
|
+
|
|
286
|
+
def find_elements(self, by: str, value: Union[str, Dict, None] = None) -> List['MobileWebElement']: # type: ignore[override]
|
|
287
|
+
...
|
|
288
|
+
|
|
289
|
+
def delete_extensions(self) -> None:
|
|
290
|
+
"""Delete extensions added in the class with 'setattr'"""
|
|
291
|
+
for extension in self._extensions:
|
|
292
|
+
instance = extension(self.execute)
|
|
293
|
+
method_name = instance.method_name()
|
|
294
|
+
if hasattr(WebDriver, method_name):
|
|
295
|
+
delattr(WebDriver, method_name)
|
|
296
|
+
|
|
297
|
+
def _update_command_executor(self, keep_alive: bool) -> None:
|
|
298
|
+
"""Update command executor following directConnect feature"""
|
|
299
|
+
direct_protocol = 'directConnectProtocol'
|
|
300
|
+
direct_host = 'directConnectHost'
|
|
301
|
+
direct_port = 'directConnectPort'
|
|
302
|
+
direct_path = 'directConnectPath'
|
|
303
|
+
|
|
304
|
+
assert self.caps, 'Driver capabilities must be defined'
|
|
305
|
+
if not {direct_protocol, direct_host, direct_port, direct_path}.issubset(set(self.caps)):
|
|
306
|
+
message = 'Direct connect capabilities from server were:\n'
|
|
307
|
+
for key in [direct_protocol, direct_host, direct_port, direct_path]:
|
|
308
|
+
message += f"{key}: '{self.caps.get(key, '')}' "
|
|
309
|
+
logger.debug(message)
|
|
310
|
+
return
|
|
311
|
+
|
|
312
|
+
protocol = self.caps[direct_protocol]
|
|
313
|
+
hostname = self.caps[direct_host]
|
|
314
|
+
port = self.caps[direct_port]
|
|
315
|
+
path = self.caps[direct_path]
|
|
316
|
+
executor = f'{protocol}://{hostname}:{port}{path}'
|
|
317
|
+
|
|
318
|
+
logger.debug('Updated request endpoint to %s', executor)
|
|
319
|
+
# Override command executor.
|
|
320
|
+
if isinstance(self.command_executor, AppiumConnection): # type: ignore
|
|
321
|
+
self.command_executor = AppiumConnection(executor, keep_alive=keep_alive)
|
|
322
|
+
else:
|
|
323
|
+
self.command_executor = RemoteConnection(executor, keep_alive=keep_alive)
|
|
324
|
+
self._add_commands()
|
|
325
|
+
|
|
326
|
+
# https://github.com/SeleniumHQ/selenium/blob/06fdf2966df6bca47c0ae45e8201cd30db9b9a49/py/selenium/webdriver/remote/webdriver.py#L277
|
|
327
|
+
# noinspection PyAttributeOutsideInit
|
|
328
|
+
def start_session(self, capabilities: Union[Dict, AppiumOptions], browser_profile: Optional[str] = None) -> None:
|
|
329
|
+
"""Creates a new session with the desired capabilities.
|
|
330
|
+
|
|
331
|
+
Override for Appium
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
capabilities: Read https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md
|
|
335
|
+
for more details.
|
|
336
|
+
browser_profile: Browser profile
|
|
337
|
+
"""
|
|
338
|
+
if not isinstance(capabilities, (dict, AppiumOptions)):
|
|
339
|
+
raise InvalidArgumentException('Capabilities must be a dictionary or AppiumOptions instance')
|
|
340
|
+
|
|
341
|
+
w3c_caps = AppiumOptions.as_w3c(capabilities) if isinstance(capabilities, dict) else capabilities.to_w3c()
|
|
342
|
+
response = self.execute(RemoteCommand.NEW_SESSION, w3c_caps)
|
|
343
|
+
# https://w3c.github.io/webdriver/#new-session
|
|
344
|
+
if not isinstance(response, dict):
|
|
345
|
+
raise SessionNotCreatedException(
|
|
346
|
+
f'A valid W3C session creation response must be a dictionary. Got "{response}" instead'
|
|
347
|
+
)
|
|
348
|
+
# Due to a W3C spec parsing misconception some servers
|
|
349
|
+
# pack the createSession response stuff into 'value' dictionary and
|
|
350
|
+
# some other put it to the top level of the response JSON nesting hierarchy
|
|
351
|
+
get_response_value: Callable[[str], Optional[Any]] = lambda key: response.get(key) or (
|
|
352
|
+
response['value'].get(key) if isinstance(response.get('value'), dict) else None
|
|
353
|
+
)
|
|
354
|
+
session_id = get_response_value('sessionId')
|
|
355
|
+
if not session_id:
|
|
356
|
+
raise SessionNotCreatedException(
|
|
357
|
+
f'A valid W3C session creation response must contain a non-empty "sessionId" entry. Got "{response}" instead'
|
|
358
|
+
)
|
|
359
|
+
self.session_id = session_id
|
|
360
|
+
self.caps = get_response_value('capabilities') or {}
|
|
361
|
+
|
|
362
|
+
def get_status(self) -> Dict:
|
|
363
|
+
"""
|
|
364
|
+
Get the Appium server status
|
|
365
|
+
|
|
366
|
+
Usage:
|
|
367
|
+
driver.get_status()
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
dict: The status information
|
|
371
|
+
|
|
372
|
+
"""
|
|
373
|
+
return self.execute(Command.GET_STATUS)['value']
|
|
374
|
+
|
|
375
|
+
def create_web_element(self, element_id: Union[int, str]) -> MobileWebElement:
|
|
376
|
+
"""Creates a web element with the specified element_id.
|
|
377
|
+
|
|
378
|
+
Overrides method in Selenium WebDriver in order to always give them
|
|
379
|
+
Appium WebElement
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
element_id: The element id to create a web element
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
`MobileWebElement`
|
|
386
|
+
"""
|
|
387
|
+
return MobileWebElement(self, element_id)
|
|
388
|
+
|
|
389
|
+
@property
|
|
390
|
+
def switch_to(self) -> MobileSwitchTo:
|
|
391
|
+
"""Returns an object containing all options to switch focus into
|
|
392
|
+
|
|
393
|
+
Override for appium
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
`appium.webdriver.switch_to.MobileSwitchTo`
|
|
397
|
+
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
return MobileSwitchTo(self)
|
|
401
|
+
|
|
402
|
+
# MJSONWP for Selenium v4
|
|
403
|
+
@property
|
|
404
|
+
def orientation(self) -> str:
|
|
405
|
+
"""
|
|
406
|
+
Gets the current orientation of the device
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
|
|
410
|
+
.. code-block:: python
|
|
411
|
+
|
|
412
|
+
orientation = driver.orientation
|
|
413
|
+
"""
|
|
414
|
+
return self.execute(Command.GET_SCREEN_ORIENTATION)['value']
|
|
415
|
+
|
|
416
|
+
# MJSONWP for Selenium v4
|
|
417
|
+
@orientation.setter
|
|
418
|
+
def orientation(self, value: str) -> None:
|
|
419
|
+
"""
|
|
420
|
+
Sets the current orientation of the device
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
- value: orientation to set it to.
|
|
424
|
+
|
|
425
|
+
Example:
|
|
426
|
+
.. code-block:: python
|
|
427
|
+
|
|
428
|
+
driver.orientation = 'landscape'
|
|
429
|
+
"""
|
|
430
|
+
allowed_values = ['LANDSCAPE', 'PORTRAIT']
|
|
431
|
+
if value.upper() in allowed_values:
|
|
432
|
+
self.execute(Command.SET_SCREEN_ORIENTATION, {'orientation': value})
|
|
433
|
+
else:
|
|
434
|
+
raise WebDriverException("You can only set the orientation to 'LANDSCAPE' and 'PORTRAIT'")
|
|
435
|
+
|
|
436
|
+
def assert_extension_exists(self, ext_name: str) -> Self:
|
|
437
|
+
"""
|
|
438
|
+
Verifies if the given extension is not present in the list of absent extensions
|
|
439
|
+
for the given driver instance.
|
|
440
|
+
This API is designed for private usage.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
ext_name: extension name
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
self instance for chaining
|
|
447
|
+
|
|
448
|
+
Raises:
|
|
449
|
+
UnknownMethodException: If the extension has been marked as absent once
|
|
450
|
+
"""
|
|
451
|
+
if ext_name in self._absent_extensions:
|
|
452
|
+
raise UnknownMethodException()
|
|
453
|
+
return self
|
|
454
|
+
|
|
455
|
+
def mark_extension_absence(self, ext_name: str) -> Self:
|
|
456
|
+
"""
|
|
457
|
+
Marks the given extension as absent for the given driver instance.
|
|
458
|
+
This API is designed for private usage.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
ext_name: extension name
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
self instance for chaining
|
|
465
|
+
"""
|
|
466
|
+
logger.debug(f'Marking driver extension "{ext_name}" as absent for the current instance')
|
|
467
|
+
self._absent_extensions.add(ext_name)
|
|
468
|
+
return self
|
|
469
|
+
|
|
470
|
+
def _add_commands(self) -> None:
|
|
471
|
+
# call the overridden command binders from all mixin classes except for
|
|
472
|
+
# appium.webdriver.webdriver.WebDriver and its sub-classes
|
|
473
|
+
# https://github.com/appium/python-client/issues/342
|
|
474
|
+
for mixin_class in filter(lambda x: not issubclass(x, WebDriver), self.__class__.__mro__):
|
|
475
|
+
if hasattr(mixin_class, self._add_commands.__name__):
|
|
476
|
+
get_atter = getattr(mixin_class, self._add_commands.__name__, None)
|
|
477
|
+
if get_atter:
|
|
478
|
+
get_atter(self)
|
|
479
|
+
|
|
480
|
+
self.command_executor.add_command(Command.GET_STATUS, 'GET', '/status')
|
|
481
|
+
|
|
482
|
+
# TODO Move commands for element to webelement
|
|
483
|
+
self.command_executor.add_command(Command.CLEAR, 'POST', '/session/$sessionId/element/$id/clear')
|
|
484
|
+
self.command_executor.add_command(
|
|
485
|
+
Command.LOCATION_IN_VIEW,
|
|
486
|
+
'GET',
|
|
487
|
+
'/session/$sessionId/element/$id/location_in_view',
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
# MJSONWP for Selenium v4
|
|
491
|
+
self.command_executor.add_command(Command.IS_ELEMENT_DISPLAYED, 'GET', '/session/$sessionId/element/$id/displayed')
|
|
492
|
+
self.command_executor.add_command(Command.GET_CAPABILITIES, 'GET', '/session/$sessionId')
|
|
493
|
+
|
|
494
|
+
self.command_executor.add_command(Command.GET_SCREEN_ORIENTATION, 'GET', '/session/$sessionId/orientation')
|
|
495
|
+
self.command_executor.add_command(Command.SET_SCREEN_ORIENTATION, 'POST', '/session/$sessionId/orientation')
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Union
|
|
16
|
+
|
|
17
|
+
from selenium.webdriver.common.utils import keys_to_typing
|
|
18
|
+
from selenium.webdriver.remote.command import Command as RemoteCommand
|
|
19
|
+
from selenium.webdriver.remote.webelement import WebElement as SeleniumWebElement
|
|
20
|
+
from typing_extensions import Self
|
|
21
|
+
|
|
22
|
+
from .mobilecommand import MobileCommand as Command
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class WebElement(SeleniumWebElement):
|
|
26
|
+
_execute: Callable
|
|
27
|
+
_id: str
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
|
|
31
|
+
def find_element(self, by: str, value: Union[str, Dict, None] = None) -> Self: # type: ignore[override]
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def find_elements(self, by: str, value: Union[str, Dict, None] = None) -> List[Self]: # type: ignore[override]
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def get_attribute(self, name: str) -> Optional[Union[str, Dict]]: # type: ignore[override]
|
|
38
|
+
"""Gets the given attribute or property of the element.
|
|
39
|
+
|
|
40
|
+
Override for Appium
|
|
41
|
+
|
|
42
|
+
This method will first try to return the value of a property with the
|
|
43
|
+
given name. If a property with that name doesn't exist, it returns the
|
|
44
|
+
value of the attribute with the same name. If there's no attribute with
|
|
45
|
+
that name, ``None`` is returned.
|
|
46
|
+
|
|
47
|
+
Values which are considered truthy, that is equals "true" or "false",
|
|
48
|
+
are returned as booleans. All other non-``None`` values are returned
|
|
49
|
+
as strings. For attributes or properties which do not exist, ``None``
|
|
50
|
+
is returned.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
name: Name of the attribute/property to retrieve.
|
|
54
|
+
|
|
55
|
+
Usage:
|
|
56
|
+
# Check if the "active" CSS class is applied to an element.
|
|
57
|
+
|
|
58
|
+
is_active = "active" in target_element.get_attribute("class")
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
The given attribute or property of the element
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
resp = self._execute(RemoteCommand.GET_ELEMENT_ATTRIBUTE, {'name': name})
|
|
65
|
+
attribute_value = resp.get('value')
|
|
66
|
+
|
|
67
|
+
if attribute_value is None:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
if isinstance(attribute_value, dict):
|
|
71
|
+
return attribute_value
|
|
72
|
+
|
|
73
|
+
# Convert to str along to the spec
|
|
74
|
+
if not isinstance(attribute_value, str):
|
|
75
|
+
attribute_value = str(attribute_value)
|
|
76
|
+
|
|
77
|
+
if name != 'value' and attribute_value.lower() in ('true', 'false'):
|
|
78
|
+
return attribute_value.lower()
|
|
79
|
+
|
|
80
|
+
return attribute_value
|
|
81
|
+
|
|
82
|
+
def is_displayed(self) -> bool:
|
|
83
|
+
"""Whether the element is visible to a user.
|
|
84
|
+
|
|
85
|
+
Override for Appium
|
|
86
|
+
"""
|
|
87
|
+
return self._execute(Command.IS_ELEMENT_DISPLAYED)['value']
|
|
88
|
+
|
|
89
|
+
def clear(self) -> Self: # type: ignore[override]
|
|
90
|
+
"""Clears text.
|
|
91
|
+
|
|
92
|
+
Override for Appium
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
`appium.webdriver.webelement.WebElement`
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
# NOTE: this method is overridden because the selenium client returned None instead of self.
|
|
99
|
+
# Appium python client would like to allow users to chain methods.
|
|
100
|
+
data = {'id': self.id}
|
|
101
|
+
self._execute(Command.CLEAR, data)
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def location_in_view(self) -> Dict[str, int]:
|
|
106
|
+
"""Gets the location of an element relative to the view.
|
|
107
|
+
|
|
108
|
+
Usage:
|
|
109
|
+
| location = element.location_in_view
|
|
110
|
+
| x = location['x']
|
|
111
|
+
| y = location['y']
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
dict: The location of an element relative to the view
|
|
115
|
+
"""
|
|
116
|
+
return self._execute(Command.LOCATION_IN_VIEW)['value']
|
|
117
|
+
|
|
118
|
+
# Override
|
|
119
|
+
def send_keys(self, *value: str) -> Self: # type: ignore[override]
|
|
120
|
+
"""Simulates typing into the element.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
value: A string for typing.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
`appium.webdriver.webelement.WebElement`
|
|
127
|
+
"""
|
|
128
|
+
keys = keys_to_typing(value)
|
|
129
|
+
self._execute(RemoteCommand.SEND_KEYS_TO_ELEMENT, {'text': ''.join(keys), 'value': keys})
|
|
130
|
+
return self
|