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,296 @@
|
|
|
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
|
+
import os
|
|
16
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
17
|
+
|
|
18
|
+
from appium.common.helper import encode_file_to_base64
|
|
19
|
+
from appium.webdriver.extensions.flutter_integration.flutter_finder import FlutterFinder
|
|
20
|
+
from appium.webdriver.extensions.flutter_integration.scroll_directions import ScrollDirection
|
|
21
|
+
from appium.webdriver.webdriver import WebDriver
|
|
22
|
+
from appium.webdriver.webelement import WebElement
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FlutterCommand:
|
|
26
|
+
def __init__(self, driver: WebDriver) -> None:
|
|
27
|
+
self.driver = driver
|
|
28
|
+
|
|
29
|
+
# wait commands
|
|
30
|
+
|
|
31
|
+
def wait_for_visible(
|
|
32
|
+
self,
|
|
33
|
+
locator: Union[WebElement, FlutterFinder],
|
|
34
|
+
timeout: Optional[float] = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Waits for a element to become visible.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
locator (Union[WebElement, FlutterFinder]): The element to wait for; can be a WebElement or a FlutterFinder.
|
|
41
|
+
timeout (Optional[float]): Maximum wait time in seconds. Defaults to a predefined timeout if not specified.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
None
|
|
45
|
+
"""
|
|
46
|
+
opts: Dict[str, Any] = self.__get_locator_options(locator)
|
|
47
|
+
if timeout is not None:
|
|
48
|
+
opts['timeout'] = timeout
|
|
49
|
+
|
|
50
|
+
self.execute_flutter_command('waitForVisible', opts)
|
|
51
|
+
|
|
52
|
+
def wait_for_invisible(
|
|
53
|
+
self,
|
|
54
|
+
locator: Union[WebElement, FlutterFinder],
|
|
55
|
+
timeout: Optional[float] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Waits for a element to become invisible.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
locator (Union[WebElement, FlutterFinder]): The element to wait for; can be a WebElement or a FlutterFinder.
|
|
62
|
+
timeout (Optional[float]): Maximum wait time in seconds. Defaults to a predefined timeout if not specified.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
None:
|
|
66
|
+
"""
|
|
67
|
+
opts: Dict[str, Any] = self.__get_locator_options(locator)
|
|
68
|
+
if timeout is not None:
|
|
69
|
+
opts['timeout'] = timeout
|
|
70
|
+
|
|
71
|
+
self.execute_flutter_command('waitForAbsent', opts)
|
|
72
|
+
|
|
73
|
+
# flutter action commands
|
|
74
|
+
|
|
75
|
+
def perform_double_click(self, element: WebElement, offset: Optional[Tuple[int, int]] = None) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Performs a double-click on the given element, with an optional offset.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
element (WebElement): The element to double-click on. This parameter is required.
|
|
81
|
+
offset (Optional[Tuple[int, int]]): The x and y offsets from the element to click at. If not specified, the click is performed at the element's center.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
None:
|
|
85
|
+
"""
|
|
86
|
+
opts: Dict[str, Union[WebElement, Dict[str, int]]] = {'origin': element}
|
|
87
|
+
if offset is not None:
|
|
88
|
+
opts['offset'] = {'x': offset[0], 'y': offset[1]}
|
|
89
|
+
self.execute_flutter_command('doubleClick', opts)
|
|
90
|
+
|
|
91
|
+
def perform_long_press(self, element: WebElement, offset: Optional[Tuple[int, int]] = None) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Performs a long press on the given element, with an optional offset.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
element (WebElement): The element to perform the long press on. This parameter is required.
|
|
97
|
+
offset (Optional[Tuple[int, int]]): The x and y offsets from the element to perform the long press at. If not specified, the long press is performed at the element's center.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
None:
|
|
101
|
+
"""
|
|
102
|
+
opts: Dict[str, Union[WebElement, Dict[str, int]]] = {'origin': element}
|
|
103
|
+
if offset is not None:
|
|
104
|
+
opts['offset'] = {'x': offset[0], 'y': offset[1]}
|
|
105
|
+
self.execute_flutter_command('longPress', opts)
|
|
106
|
+
|
|
107
|
+
def perform_drag_and_drop(self, source: WebElement, target: WebElement) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Performs a drag-and-drop operation from a source element to a target element.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
source (WebElement): The element to drag from.
|
|
113
|
+
target (WebElement): The element to drop onto.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
None:
|
|
117
|
+
"""
|
|
118
|
+
self.execute_flutter_command('dragAndDrop', {'source': source, 'target': target})
|
|
119
|
+
|
|
120
|
+
def scroll_till_visible(
|
|
121
|
+
self,
|
|
122
|
+
scroll_to: FlutterFinder,
|
|
123
|
+
scroll_direction: ScrollDirection = ScrollDirection.DOWN,
|
|
124
|
+
**opts: Any,
|
|
125
|
+
) -> WebElement:
|
|
126
|
+
"""
|
|
127
|
+
Scrolls until the specified element becomes visible.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
scroll_to (FlutterFinder): The Flutter element to scroll to.
|
|
131
|
+
scroll_direction (ScrollDirection): The direction to scroll up or down. Defaults to `ScrollDirection.DOWN`.
|
|
132
|
+
|
|
133
|
+
KeywordArgs:
|
|
134
|
+
scrollView (str): The view of the scroll. Default value is 'Scrollable'
|
|
135
|
+
delta (int): delta for the scroll. Default value is 64
|
|
136
|
+
maxScrolls (int): Max times to scroll. Default value is 15
|
|
137
|
+
settleBetweenScrollsTimeout (float): settle timeout in milliseconds. Default value is 5000
|
|
138
|
+
dragDuration (float): time gap between each scroll in milliseconds. Default value is 100
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Webelement: scrolled element
|
|
142
|
+
"""
|
|
143
|
+
opts['finder'] = scroll_to.to_dict()
|
|
144
|
+
opts['scrollDirection'] = scroll_direction.value
|
|
145
|
+
return self.execute_flutter_command('scrollTillVisible', opts)
|
|
146
|
+
|
|
147
|
+
def inject_mock_image(self, value: str) -> str:
|
|
148
|
+
"""
|
|
149
|
+
Injects a mock image to the device. The input can be a file path or a base64-encoded string.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
value (str): The file path of the image or a base64-encoded string.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
str: Image ID of the injected image.
|
|
156
|
+
"""
|
|
157
|
+
if os.path.isfile(value):
|
|
158
|
+
base64_encoded_image = encode_file_to_base64(value)
|
|
159
|
+
else:
|
|
160
|
+
base64_encoded_image = value
|
|
161
|
+
return self.execute_flutter_command('injectImage', {'base64Image': base64_encoded_image})
|
|
162
|
+
|
|
163
|
+
def activate_injected_image(self, image_id: str) -> None:
|
|
164
|
+
"""
|
|
165
|
+
Activates an injected image with image ID.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
image_id (str): The ID of the injected image to activate.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
None:
|
|
172
|
+
"""
|
|
173
|
+
self.execute_flutter_command('activateInjectedImage', {'imageId': image_id})
|
|
174
|
+
|
|
175
|
+
def get_render_tree(
|
|
176
|
+
self,
|
|
177
|
+
widget_type: Optional[str] = None,
|
|
178
|
+
key: Optional[str] = None,
|
|
179
|
+
text: Optional[str] = None,
|
|
180
|
+
) -> List[Optional[Dict]]:
|
|
181
|
+
"""
|
|
182
|
+
Returns the render tree of the root widget.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
widget_type (Optional[str]): The type of the widget to primary filter by.
|
|
186
|
+
key (Optional[str]): The key of the widget to filter by.
|
|
187
|
+
text (Optional[str]): The text of the widget to filter by.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List[Optional[Dict]]: A list of dictionaries or None values representing the render tree.
|
|
191
|
+
|
|
192
|
+
The result is a nested list of dictionaries representing each widget and its properties,
|
|
193
|
+
such as type, key, size, attribute, state, visual information, and hierarchy.
|
|
194
|
+
|
|
195
|
+
The example widget includes the following code, which is rendered as part of the widget tree:
|
|
196
|
+
|
|
197
|
+
.. code-block:: dart
|
|
198
|
+
|
|
199
|
+
Semantics(
|
|
200
|
+
key: const Key('add_activity_semantics'),
|
|
201
|
+
label: 'add_activity_button',
|
|
202
|
+
button: true,
|
|
203
|
+
child: FloatingActionButton.small(
|
|
204
|
+
key: const Key('add_activity_button'),
|
|
205
|
+
tooltip: 'add_activity_button',
|
|
206
|
+
heroTag: 'add',
|
|
207
|
+
backgroundColor: const Color(0xFF2E2E3A),
|
|
208
|
+
onPressed: null,
|
|
209
|
+
child: Icon(
|
|
210
|
+
Icons.add,
|
|
211
|
+
size: 16,
|
|
212
|
+
color: Colors.amber.shade200.withOpacity(0.5),
|
|
213
|
+
semanticLabel: 'Add icon',
|
|
214
|
+
),
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
Example execute command:
|
|
219
|
+
|
|
220
|
+
.. code-block:: python
|
|
221
|
+
|
|
222
|
+
>>> flutter_command = FlutterCommand(driver) # noqa
|
|
223
|
+
>>> flutter_command.get_render_tree(widget_type='Semantics', key='add_activity_semantics')
|
|
224
|
+
output >> [
|
|
225
|
+
{
|
|
226
|
+
"type": "Semantics",
|
|
227
|
+
"elementType": "SingleChildRenderObjectElement",
|
|
228
|
+
"description": "Semantics-[<'add_activity_semantics'>]",
|
|
229
|
+
"depth": 0,
|
|
230
|
+
"key": "[<'add_activity_semantics'>]",
|
|
231
|
+
"attributes": {
|
|
232
|
+
"semanticsLabel": "add_activity_button"
|
|
233
|
+
},
|
|
234
|
+
"visual": {},
|
|
235
|
+
"state": {},
|
|
236
|
+
"rect": {
|
|
237
|
+
"x": 0,
|
|
238
|
+
"y": 0,
|
|
239
|
+
"width": 48,
|
|
240
|
+
"height": 48
|
|
241
|
+
},
|
|
242
|
+
"children": [
|
|
243
|
+
{
|
|
244
|
+
"type": "FloatingActionButton",
|
|
245
|
+
"elementType": "StatelessElement",
|
|
246
|
+
"description": "FloatingActionButton-[<'add_activity_button'>]",
|
|
247
|
+
"depth": 1,
|
|
248
|
+
"key": "[<'add_activity_button'>]",
|
|
249
|
+
"attributes": {},
|
|
250
|
+
"visual": {},
|
|
251
|
+
"state": {},
|
|
252
|
+
"rect": {
|
|
253
|
+
"x": 0,
|
|
254
|
+
"y": 0,
|
|
255
|
+
"width": 48,
|
|
256
|
+
"height": 48
|
|
257
|
+
},
|
|
258
|
+
"children": [
|
|
259
|
+
{...},
|
|
260
|
+
"children": [...]
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
"""
|
|
268
|
+
opts = {}
|
|
269
|
+
if widget_type is not None:
|
|
270
|
+
opts['widgetType'] = widget_type
|
|
271
|
+
if key is not None:
|
|
272
|
+
opts['key'] = key
|
|
273
|
+
if text is not None:
|
|
274
|
+
opts['text'] = text
|
|
275
|
+
|
|
276
|
+
return self.execute_flutter_command('renderTree', opts)
|
|
277
|
+
|
|
278
|
+
def execute_flutter_command(self, scriptName: str, params: dict) -> Any:
|
|
279
|
+
"""
|
|
280
|
+
Executes a Flutter command by sending a script and parameters to the flutter integration driver.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
scriptName (str): The name of the Flutter command to execute.
|
|
284
|
+
This will be prefixed with 'flutter:' when passed to the driver.
|
|
285
|
+
params (dict): A dictionary of parameters to be passed along with the Flutter command.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Any: The result of the command execution. The return value depends on the
|
|
289
|
+
specific Flutter command being executed.
|
|
290
|
+
"""
|
|
291
|
+
return self.driver.execute_script(f'flutter: {scriptName}', params)
|
|
292
|
+
|
|
293
|
+
def __get_locator_options(self, locator: Union[WebElement, 'FlutterFinder']) -> Dict[str, Union[dict, WebElement]]:
|
|
294
|
+
if isinstance(locator, WebElement):
|
|
295
|
+
return {'element': locator}
|
|
296
|
+
return {'locator': locator.to_dict()}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
|
3
|
+
# distributed with this work for additional information
|
|
4
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
|
6
|
+
# "License"); you may not use this file except in compliance
|
|
7
|
+
# with the License. You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
|
12
|
+
# software distributed under the License is distributed on an
|
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
# KIND, either express or implied. See the License for the
|
|
15
|
+
# specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
from typing import Tuple, Union, cast
|
|
19
|
+
|
|
20
|
+
from selenium.webdriver.common.by import ByType as SeleniumByType
|
|
21
|
+
|
|
22
|
+
from appium.webdriver.common.appiumby import AppiumBy
|
|
23
|
+
from appium.webdriver.common.appiumby import ByType as AppiumByType
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FlutterFinder:
|
|
27
|
+
def __init__(self, using: Union[SeleniumByType, AppiumByType], value: str) -> None:
|
|
28
|
+
self.using = using
|
|
29
|
+
self.value = value
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def by_key(value: str) -> 'FlutterFinder':
|
|
33
|
+
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_KEY), value)
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def by_text(value: str) -> 'FlutterFinder':
|
|
37
|
+
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_TEXT), value)
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def by_semantics_label(value: str) -> 'FlutterFinder':
|
|
41
|
+
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_SEMANTICS_LABEL), value)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def by_type(value: str) -> 'FlutterFinder':
|
|
45
|
+
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_TYPE), value)
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def by_text_containing(value: str) -> 'FlutterFinder':
|
|
49
|
+
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_TEXT_CONTAINING), value)
|
|
50
|
+
|
|
51
|
+
def to_dict(self) -> dict:
|
|
52
|
+
return {'using': self.using, 'value': self.value}
|
|
53
|
+
|
|
54
|
+
def as_args(self) -> Tuple[str, str]:
|
|
55
|
+
return self.using, self.value
|
|
@@ -0,0 +1,149 @@
|
|
|
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 Optional
|
|
16
|
+
|
|
17
|
+
from selenium.common.exceptions import UnknownMethodException
|
|
18
|
+
from typing_extensions import Self
|
|
19
|
+
|
|
20
|
+
from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands
|
|
21
|
+
from appium.protocols.webdriver.can_execute_scripts import CanExecuteScripts
|
|
22
|
+
from appium.protocols.webdriver.can_remember_extension_presence import CanRememberExtensionPresence
|
|
23
|
+
|
|
24
|
+
from ..mobilecommand import MobileCommand as Command
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class HardwareActions(CanExecuteCommands, CanExecuteScripts, CanRememberExtensionPresence):
|
|
28
|
+
def lock(self, seconds: Optional[int] = None) -> Self:
|
|
29
|
+
"""Lock the device. No changes are made if the device is already unlocked.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
seconds: The duration to lock the device, in seconds.
|
|
33
|
+
The device is going to be locked forever until `unlock` is called
|
|
34
|
+
if it equals or is less than zero, otherwise this call blocks until
|
|
35
|
+
the timeout expires and unlocks the screen automatically.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Union['WebDriver', 'HardwareActions']: Self instance
|
|
39
|
+
"""
|
|
40
|
+
ext_name = 'mobile: lock'
|
|
41
|
+
args = {'seconds': seconds or 0}
|
|
42
|
+
try:
|
|
43
|
+
self.assert_extension_exists(ext_name).execute_script(ext_name, args)
|
|
44
|
+
except UnknownMethodException:
|
|
45
|
+
# TODO: Remove the fallback
|
|
46
|
+
self.mark_extension_absence(ext_name).execute(Command.LOCK, args)
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def unlock(self) -> Self:
|
|
50
|
+
"""Unlock the device. No changes are made if the device is already locked.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Union['WebDriver', 'HardwareActions']: Self instance
|
|
54
|
+
"""
|
|
55
|
+
ext_name = 'mobile: unlock'
|
|
56
|
+
try:
|
|
57
|
+
if not self.assert_extension_exists(ext_name).execute_script('mobile: isLocked'):
|
|
58
|
+
return self
|
|
59
|
+
self.execute_script(ext_name)
|
|
60
|
+
except UnknownMethodException:
|
|
61
|
+
# TODO: Remove the fallback
|
|
62
|
+
self.mark_extension_absence(ext_name).execute(Command.UNLOCK)
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def is_locked(self) -> bool:
|
|
66
|
+
"""Checks whether the device is locked.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
`True` if the device is locked
|
|
70
|
+
"""
|
|
71
|
+
ext_name = 'mobile: isLocked'
|
|
72
|
+
try:
|
|
73
|
+
return self.assert_extension_exists(ext_name).execute_script('mobile: isLocked')
|
|
74
|
+
except UnknownMethodException:
|
|
75
|
+
# TODO: Remove the fallback
|
|
76
|
+
return self.mark_extension_absence(ext_name).execute(Command.IS_LOCKED)['value']
|
|
77
|
+
|
|
78
|
+
def shake(self) -> Self:
|
|
79
|
+
"""Shake the device.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Union['WebDriver', 'HardwareActions']: Self instance
|
|
83
|
+
"""
|
|
84
|
+
ext_name = 'mobile: shake'
|
|
85
|
+
try:
|
|
86
|
+
self.assert_extension_exists(ext_name).execute_script(ext_name)
|
|
87
|
+
except UnknownMethodException:
|
|
88
|
+
# TODO: Remove the fallback
|
|
89
|
+
self.mark_extension_absence(ext_name).execute(Command.SHAKE)
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def touch_id(self, match: bool) -> Self:
|
|
93
|
+
"""Simulate touchId on iOS Simulator
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
match: Simulates a successful touch (`True`) or a failed touch (`False`)
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Union['WebDriver', 'HardwareActions']: Self instance
|
|
100
|
+
"""
|
|
101
|
+
self.execute_script(
|
|
102
|
+
'mobile: sendBiometricMatch',
|
|
103
|
+
{
|
|
104
|
+
'type': 'touchId',
|
|
105
|
+
'match': match,
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
return self
|
|
109
|
+
|
|
110
|
+
def toggle_touch_id_enrollment(self) -> Self:
|
|
111
|
+
"""Toggle enroll touchId on iOS Simulator
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Union['WebDriver', 'HardwareActions']: Self instance
|
|
115
|
+
"""
|
|
116
|
+
is_enrolled = self.execute_script('mobile: isBiometricEnrolled')
|
|
117
|
+
self.execute_script('mobile: enrollBiometric', {'isEnabled': not is_enrolled})
|
|
118
|
+
return self
|
|
119
|
+
|
|
120
|
+
def finger_print(self, finger_id: int) -> Self:
|
|
121
|
+
"""Authenticate users by using their finger print scans on supported Android emulators.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
finger_id: Finger prints stored in Android Keystore system (from 1 to 10)
|
|
125
|
+
"""
|
|
126
|
+
ext_name = 'mobile: fingerprint'
|
|
127
|
+
args = {'fingerprintId': finger_id}
|
|
128
|
+
try:
|
|
129
|
+
self.assert_extension_exists(ext_name).execute_script(ext_name, args)
|
|
130
|
+
except UnknownMethodException:
|
|
131
|
+
self.mark_extension_absence(ext_name).execute(Command.FINGER_PRINT, args)
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
def _add_commands(self) -> None:
|
|
135
|
+
self.command_executor.add_command(Command.LOCK, 'POST', '/session/$sessionId/appium/device/lock')
|
|
136
|
+
self.command_executor.add_command(Command.UNLOCK, 'POST', '/session/$sessionId/appium/device/unlock')
|
|
137
|
+
self.command_executor.add_command(Command.IS_LOCKED, 'POST', '/session/$sessionId/appium/device/is_locked')
|
|
138
|
+
self.command_executor.add_command(Command.SHAKE, 'POST', '/session/$sessionId/appium/device/shake')
|
|
139
|
+
self.command_executor.add_command(Command.TOUCH_ID, 'POST', '/session/$sessionId/appium/simulator/touch_id')
|
|
140
|
+
self.command_executor.add_command(
|
|
141
|
+
Command.TOGGLE_TOUCH_ID_ENROLLMENT,
|
|
142
|
+
'POST',
|
|
143
|
+
'/session/$sessionId/appium/simulator/toggle_touch_id_enrollment',
|
|
144
|
+
)
|
|
145
|
+
self.command_executor.add_command(
|
|
146
|
+
Command.FINGER_PRINT,
|
|
147
|
+
'POST',
|
|
148
|
+
'/session/$sessionId/appium/device/finger_print',
|
|
149
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
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 Any, Dict, Union
|
|
16
|
+
|
|
17
|
+
from appium.protocols.webdriver.can_execute_commands import CanExecuteCommands
|
|
18
|
+
|
|
19
|
+
from ..mobilecommand import MobileCommand as Command
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ImagesComparison(CanExecuteCommands):
|
|
23
|
+
def match_images_features(self, base64_image1: bytes, base64_image2: bytes, **opts: Any) -> Dict[str, Any]:
|
|
24
|
+
"""Performs images matching by features.
|
|
25
|
+
|
|
26
|
+
Read
|
|
27
|
+
https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.html
|
|
28
|
+
for more details on this topic.
|
|
29
|
+
The method supports all image formats, which are supported by OpenCV itself.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
base64_image1: base64-encoded content of the first image
|
|
33
|
+
base64_image2: base64-encoded content of the second image
|
|
34
|
+
|
|
35
|
+
Keyword Args:
|
|
36
|
+
visualize (bool): Set it to True in order to return the visualization of the matching operation.
|
|
37
|
+
matching visualization. False by default
|
|
38
|
+
detectorName (str): One of possible feature detector names:
|
|
39
|
+
'AKAZE', 'AGAST', 'BRISK', 'FAST', 'GFTT', 'KAZE', 'MSER', 'SIFT', 'ORB'
|
|
40
|
+
Some of these detectors are not enabled in the default OpenCV deployment.
|
|
41
|
+
'ORB' By default.
|
|
42
|
+
matchFunc (str): One of supported matching functions names:
|
|
43
|
+
'FlannBased', 'BruteForce', 'BruteForceL1', 'BruteForceHamming',
|
|
44
|
+
'BruteForceHammingLut', 'BruteForceSL2'
|
|
45
|
+
'BruteForce' by default
|
|
46
|
+
goodMatchesFactor (int): The maximum count of "good" matches (e. g. with minimal distances).
|
|
47
|
+
This count is unlimited by default.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The dictionary containing the following entries:
|
|
51
|
+
|
|
52
|
+
visualization (bytes): base64-encoded content of PNG visualization of the current comparison
|
|
53
|
+
operation. This entry is only present if `visualize` option is enabled
|
|
54
|
+
count (int): The count of matched edges on both images.
|
|
55
|
+
The more matching edges there are no both images the more similar they are.
|
|
56
|
+
totalCount (int): The total count of matched edges on both images.
|
|
57
|
+
It is equal to `count` if `goodMatchesFactor` does not limit the matches,
|
|
58
|
+
otherwise it contains the total count of matches before `goodMatchesFactor` is
|
|
59
|
+
applied.
|
|
60
|
+
points1 (dict): The array of matching points on the first image. Each point is a dictionary
|
|
61
|
+
with 'x' and 'y' keys
|
|
62
|
+
rect1 (dict): The bounding rect for the `points1` array or a zero rect if not enough matching points
|
|
63
|
+
were found. The rect is represented by a dictionary with 'x', 'y', 'width' and 'height' keys
|
|
64
|
+
points2 (dict): The array of matching points on the second image. Each point is a dictionary
|
|
65
|
+
with 'x' and 'y' keys
|
|
66
|
+
rect2 (dict): The bounding rect for the `points2` array or a zero rect if not enough matching points
|
|
67
|
+
were found. The rect is represented by a dictionary with 'x', 'y', 'width' and 'height' keys
|
|
68
|
+
"""
|
|
69
|
+
options = {'mode': 'matchFeatures', 'firstImage': base64_image1, 'secondImage': base64_image2, 'options': opts}
|
|
70
|
+
return self.execute(Command.COMPARE_IMAGES, options)['value']
|
|
71
|
+
|
|
72
|
+
def find_image_occurrence(
|
|
73
|
+
self, base64_full_image: bytes, base64_partial_image: bytes, **opts: Any
|
|
74
|
+
) -> Dict[str, Union[bytes, Dict]]:
|
|
75
|
+
"""Performs images matching by template to find possible occurrence of the partial image
|
|
76
|
+
in the full image.
|
|
77
|
+
|
|
78
|
+
Read
|
|
79
|
+
https://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/template_matching/template_matching.html
|
|
80
|
+
for more details on this topic.
|
|
81
|
+
The method supports all image formats, which are supported by OpenCV itself.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
base64_full_image: base64-encoded content of the full image
|
|
85
|
+
base64_partial_image: base64-encoded content of the partial image
|
|
86
|
+
|
|
87
|
+
Keyword Args:
|
|
88
|
+
visualize (bool): Set it to True in order to return the visualization of the matching operation.
|
|
89
|
+
False by default
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
The dictionary containing the following entries:
|
|
93
|
+
visualization (bytes): base64-encoded content of PNG visualization of the current comparison
|
|
94
|
+
operation. This entry is only present if `visualize` option is enabled
|
|
95
|
+
rect (dict): The region of the partial image occurrence on the full image.
|
|
96
|
+
The rect is represented by a dictionary with 'x', 'y', 'width' and 'height' keys
|
|
97
|
+
"""
|
|
98
|
+
options = {
|
|
99
|
+
'mode': 'matchTemplate',
|
|
100
|
+
'firstImage': base64_full_image,
|
|
101
|
+
'secondImage': base64_partial_image,
|
|
102
|
+
'options': opts,
|
|
103
|
+
}
|
|
104
|
+
return self.execute(Command.COMPARE_IMAGES, options)['value']
|
|
105
|
+
|
|
106
|
+
def get_images_similarity(self, base64_image1: bytes, base64_image2: bytes, **opts: Any) -> Dict[str, Union[bytes, Dict]]:
|
|
107
|
+
"""Performs images matching to calculate the similarity score between them.
|
|
108
|
+
|
|
109
|
+
The flow there is similar to the one used in
|
|
110
|
+
`find_image_occurrence`, but it is mandatory that both images are of equal resolution.
|
|
111
|
+
The method supports all image formats, which are supported by OpenCV itself.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
base64_image1: base64-encoded content of the first image
|
|
115
|
+
base64_image2: base64-encoded content of the second image
|
|
116
|
+
|
|
117
|
+
Keyword Args:
|
|
118
|
+
visualize (boo): Set it to True in order to return the visualization of the matching operation.
|
|
119
|
+
False by default
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
The dictionary containing the following entries:
|
|
123
|
+
visualization (bytes): base64-encoded content of PNG visualization of the current comparison
|
|
124
|
+
operation. This entry is only present if `visualize` option is enabled
|
|
125
|
+
score (float): The similarity score as a float number in range [0.0, 1.0].
|
|
126
|
+
1.0 is the highest score (means both images are totally equal).
|
|
127
|
+
"""
|
|
128
|
+
options = {'mode': 'getSimilarity', 'firstImage': base64_image1, 'secondImage': base64_image2, 'options': opts}
|
|
129
|
+
return self.execute(Command.COMPARE_IMAGES, options)['value']
|
|
130
|
+
|
|
131
|
+
def _add_commands(self) -> None:
|
|
132
|
+
self.command_executor.add_command(Command.COMPARE_IMAGES, 'POST', '/session/$sessionId/appium/compare_images')
|