appium_lib_core 1.4.0 → 1.4.1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +11 -0
- data/appium_lib_core.gemspec +0 -1
- data/lib/appium_lib_core.rb +1 -6
- data/lib/appium_lib_core/common.rb +1 -1
- data/lib/appium_lib_core/common/base.rb +1 -2
- data/lib/appium_lib_core/common/base/bridge/{msjsonwp.rb → mjsonwp.rb} +1 -1
- data/lib/appium_lib_core/common/base/bridge/w3c.rb +1 -1
- data/lib/appium_lib_core/common/command.rb +3 -131
- data/lib/appium_lib_core/common/command/common.rb +88 -0
- data/lib/appium_lib_core/common/command/mjsonwp.rb +14 -0
- data/lib/appium_lib_core/common/command/w3c.rb +44 -0
- data/lib/appium_lib_core/common/wait.rb +162 -0
- data/lib/appium_lib_core/common/wait/timer.rb +30 -0
- data/lib/appium_lib_core/{common/device.rb → device.rb} +8 -6
- data/lib/appium_lib_core/device/image_comparison.rb +178 -0
- data/lib/appium_lib_core/driver.rb +4 -68
- data/lib/appium_lib_core/version.rb +2 -2
- data/release_notes.md +9 -0
- data/script/commands.rb +2 -2
- metadata +10 -19
- data/lib/appium_lib_core/common/base/wait.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f5dd97d1cbe46c9a3de56622d0235855c17d5fa
|
4
|
+
data.tar.gz: 8ae9a77dd25ac92e3c6c2028f2e2366f770d4b7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6239c7d4e68eb97e9f9bb55cd727f585a671baee664d89fb8f08f7ff86f40fc7bc68753575154709ce5ee963d1a90bc3a02a3234c182b49051ef08cab4fe635
|
7
|
+
data.tar.gz: 66925a81fe4e906508ac090bc606053ebc0fcb05a870fdbbc7cd81b9cad56229b2301b7d8109c6618d8dfe259275713c9eacfc5306b89170c65d314159bde129
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,17 @@ All notable changes to this project will be documented in this file.
|
|
8
8
|
|
9
9
|
### Deprecations
|
10
10
|
|
11
|
+
## [1.4.1] - 2018-04-22
|
12
|
+
### Enhancements
|
13
|
+
- add base image comparison
|
14
|
+
- `match_images_features`, `find_image_occurrence`, `get_images_similarity`, `compare_images`
|
15
|
+
- [internal] No longer have dependency for Selenium's wait
|
16
|
+
- [internal] Separate mjsonwp commands module and w3c commands module from one command module
|
17
|
+
|
18
|
+
### Bug fixes
|
19
|
+
|
20
|
+
### Deprecations
|
21
|
+
|
11
22
|
## [1.4.0] - 2018-04-15
|
12
23
|
### Enhancements
|
13
24
|
- Add a support for WebSocket client based on Faye::WebSocket::Client [#74](https://github.com/appium/ruby_lib_core/pull/74)
|
data/appium_lib_core.gemspec
CHANGED
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
25
|
spec.add_runtime_dependency 'selenium-webdriver', '~> 3.5'
|
26
|
-
spec.add_runtime_dependency 'json', '>= 1.8'
|
27
26
|
spec.add_runtime_dependency 'faye-websocket', '~> 0.10.0'
|
28
27
|
|
29
28
|
spec.add_development_dependency 'bundler', '~> 1.14'
|
data/lib/appium_lib_core.rb
CHANGED
@@ -5,12 +5,7 @@ require_relative 'appium_lib_core/common'
|
|
5
5
|
require_relative 'appium_lib_core/patch'
|
6
6
|
require_relative 'appium_lib_core/driver'
|
7
7
|
|
8
|
-
|
9
|
-
require_relative 'appium_lib_core/device/touch_actions'
|
10
|
-
require_relative 'appium_lib_core/device/multi_touch'
|
11
|
-
require_relative 'appium_lib_core/device/screen_record'
|
12
|
-
require_relative 'appium_lib_core/device/app_state'
|
13
|
-
require_relative 'appium_lib_core/device/clipboard_content_type'
|
8
|
+
require_relative 'appium_lib_core/device'
|
14
9
|
|
15
10
|
require_relative 'appium_lib_core/android'
|
16
11
|
require_relative 'appium_lib_core/android_uiautomator2'
|
@@ -2,6 +2,6 @@ require_relative 'common/logger'
|
|
2
2
|
require_relative 'common/error'
|
3
3
|
require_relative 'common/log'
|
4
4
|
require_relative 'common/command'
|
5
|
-
require_relative 'common/device'
|
6
5
|
require_relative 'common/base'
|
6
|
+
require_relative 'common/wait'
|
7
7
|
require_relative 'common/ws/websocket'
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# The following files have selenium-webdriver related stuff.
|
2
2
|
require_relative 'base/driver'
|
3
3
|
require_relative 'base/bridge'
|
4
|
-
require_relative 'base/bridge/
|
4
|
+
require_relative 'base/bridge/mjsonwp'
|
5
5
|
require_relative 'base/bridge/w3c'
|
6
6
|
require_relative 'base/capabilities'
|
7
7
|
require_relative 'base/http_default'
|
8
8
|
require_relative 'base/search_context'
|
9
9
|
require_relative 'base/command'
|
10
|
-
require_relative 'base/wait'
|
@@ -4,7 +4,7 @@ module Appium
|
|
4
4
|
class Bridge
|
5
5
|
class MJSONWP < ::Selenium::WebDriver::Remote::OSS::Bridge
|
6
6
|
def commands(command)
|
7
|
-
::Appium::Core::Commands::
|
7
|
+
::Appium::Core::Commands::MJSONWP::COMMANDS[command]
|
8
8
|
end
|
9
9
|
end # class MJSONWP
|
10
10
|
end # class Bridge
|
@@ -8,7 +8,7 @@ module Appium
|
|
8
8
|
::Selenium::WebDriver::PointerActions::DEFAULT_MOVE_DURATION = 0.05
|
9
9
|
|
10
10
|
def commands(command)
|
11
|
-
::Appium::Core::Commands::
|
11
|
+
::Appium::Core::Commands::W3C::COMMANDS[command]
|
12
12
|
end
|
13
13
|
|
14
14
|
# Perform touch actions for W3C module.
|
@@ -1,132 +1,4 @@
|
|
1
1
|
require_relative 'base/command'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
# ref: https://github.com/appium/appium-base-driver/blob/master/lib/mjsonwp/routes.js
|
6
|
-
module Commands
|
7
|
-
# Some commands differ for each driver.
|
8
|
-
COMMAND = {
|
9
|
-
# common
|
10
|
-
available_contexts: [:get, 'session/:session_id/contexts'.freeze],
|
11
|
-
set_context: [:post, 'session/:session_id/context'.freeze],
|
12
|
-
current_context: [:get, 'session/:session_id/context'.freeze],
|
13
|
-
|
14
|
-
touch_actions: [:post, 'session/:session_id/touch/perform'.freeze],
|
15
|
-
multi_touch: [:post, 'session/:session_id/touch/multi/perform'.freeze],
|
16
|
-
|
17
|
-
set_immediate_value: [:post, 'session/:session_id/appium/element/:id/value'.freeze],
|
18
|
-
replace_value: [:post, 'session/:session_id/appium/element/:id/replace_value'.freeze],
|
19
|
-
|
20
|
-
launch_app: [:post, 'session/:session_id/appium/app/launch'.freeze],
|
21
|
-
close_app: [:post, 'session/:session_id/appium/app/close'.freeze],
|
22
|
-
reset: [:post, 'session/:session_id/appium/app/reset'.freeze],
|
23
|
-
background_app: [:post, 'session/:session_id/appium/app/background'.freeze],
|
24
|
-
app_strings: [:post, 'session/:session_id/appium/app/strings'.freeze],
|
25
|
-
|
26
|
-
device_locked?: [:post, 'session/:session_id/appium/device/is_locked'.freeze],
|
27
|
-
unlock: [:post, 'session/:session_id/appium/device/unlock'.freeze],
|
28
|
-
lock: [:post, 'session/:session_id/appium/device/lock'.freeze],
|
29
|
-
device_time: [:get, 'session/:session_id/appium/device/system_time'.freeze],
|
30
|
-
install_app: [:post, 'session/:session_id/appium/device/install_app'.freeze],
|
31
|
-
remove_app: [:post, 'session/:session_id/appium/device/remove_app'.freeze],
|
32
|
-
app_installed?: [:post, 'session/:session_id/appium/device/app_installed'.freeze],
|
33
|
-
activate_app: [:post, 'session/:session_id/appium/device/activate_app'.freeze],
|
34
|
-
terminate_app: [:post, 'session/:session_id/appium/device/terminate_app'.freeze],
|
35
|
-
app_state: [:post, 'session/:session_id/appium/device/app_state'.freeze],
|
36
|
-
shake: [:post, 'session/:session_id/appium/device/shake'.freeze],
|
37
|
-
hide_keyboard: [:post, 'session/:session_id/appium/device/hide_keyboard'.freeze],
|
38
|
-
press_keycode: [:post, 'session/:session_id/appium/device/press_keycode'.freeze],
|
39
|
-
long_press_keycode: [:post, 'session/:session_id/appium/device/long_press_keycode'.freeze],
|
40
|
-
# keyevent is only for Selendroid
|
41
|
-
keyevent: [:post, 'session/:session_id/appium/device/keyevent'.freeze],
|
42
|
-
push_file: [:post, 'session/:session_id/appium/device/push_file'.freeze],
|
43
|
-
pull_file: [:post, 'session/:session_id/appium/device/pull_file'.freeze],
|
44
|
-
pull_folder: [:post, 'session/:session_id/appium/device/pull_folder'.freeze],
|
45
|
-
get_clipboard: [:post, 'session/:session_id/appium/device/get_clipboard'.freeze],
|
46
|
-
set_clipboard: [:post, 'session/:session_id/appium/device/set_clipboard'.freeze],
|
47
|
-
get_settings: [:get, 'session/:session_id/appium/settings'.freeze],
|
48
|
-
update_settings: [:post, 'session/:session_id/appium/settings'.freeze],
|
49
|
-
stop_recording_screen: [:post, 'session/:session_id/appium/stop_recording_screen'.freeze],
|
50
|
-
start_recording_screen: [:post, 'session/:session_id/appium/start_recording_screen'.freeze]
|
51
|
-
}.freeze
|
52
|
-
|
53
|
-
COMMAND_ANDROID = {
|
54
|
-
open_notifications: [:post, 'session/:session_id/appium/device/open_notifications'.freeze],
|
55
|
-
toggle_airplane_mode: [:post, 'session/:session_id/appium/device/toggle_airplane_mode'.freeze],
|
56
|
-
start_activity: [:post, 'session/:session_id/appium/device/start_activity'.freeze],
|
57
|
-
current_activity: [:get, 'session/:session_id/appium/device/current_activity'.freeze],
|
58
|
-
current_package: [:get, 'session/:session_id/appium/device/current_package'.freeze],
|
59
|
-
get_system_bars: [:get, 'session/:session_id/appium/device/system_bars'.freeze],
|
60
|
-
get_display_density: [:get, 'session/:session_id/appium/device/display_density'.freeze],
|
61
|
-
is_keyboard_shown: [:get, 'session/:session_id/appium/device/is_keyboard_shown'.freeze],
|
62
|
-
toggle_wifi: [:post, 'session/:session_id/appium/device/toggle_wifi'.freeze],
|
63
|
-
toggle_data: [:post, 'session/:session_id/appium/device/toggle_data'.freeze],
|
64
|
-
toggle_location_services: [:post, 'session/:session_id/appium/device/toggle_location_services'.freeze],
|
65
|
-
end_coverage: [:post, 'session/:session_id/appium/app/end_test_coverage'.freeze],
|
66
|
-
get_performance_data_types: [:post, 'session/:session_id/appium/performanceData/types'.freeze],
|
67
|
-
get_performance_data: [:post, 'session/:session_id/appium/getPerformanceData'.freeze],
|
68
|
-
get_network_connection: [:get, 'session/:session_id/network_connection'.freeze], # defined also in OSS
|
69
|
-
set_network_connection: [:post, 'session/:session_id/network_connection'.freeze], # defined also in OSS
|
70
|
-
|
71
|
-
# only emulator
|
72
|
-
send_sms: [:post, 'session/:session_id/appium/device/send_sms'.freeze],
|
73
|
-
gsm_call: [:post, 'session/:session_id/appium/device/gsm_call'.freeze],
|
74
|
-
gsm_signal: [:post, 'session/:session_id/appium/device/gsm_signal'.freeze],
|
75
|
-
gsm_voice: [:post, 'session/:session_id/appium/device/gsm_voice'.freeze],
|
76
|
-
set_network_speed: [:post, 'session/:session_id/appium/device/network_speed'.freeze],
|
77
|
-
set_power_capacity: [:post, 'session/:session_id/appium/device/power_capacity'.freeze],
|
78
|
-
set_power_ac: [:post, 'session/:session_id/appium/device/power_ac'.freeze]
|
79
|
-
}.freeze
|
80
|
-
|
81
|
-
COMMAND_IOS = {
|
82
|
-
touch_id: [:post, 'session/:session_id/appium/simulator/touch_id'.freeze],
|
83
|
-
toggle_touch_id_enrollment: [:post, 'session/:session_id/appium/simulator/toggle_touch_id_enrollment'.freeze]
|
84
|
-
}.freeze
|
85
|
-
|
86
|
-
COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).merge(COMMAND_IOS).freeze
|
87
|
-
|
88
|
-
COMMANDS_EXTEND_MJSONWP = COMMANDS.merge(::Appium::Core::Base::Commands::OSS).merge(
|
89
|
-
{
|
90
|
-
# W3C already has.
|
91
|
-
take_element_screenshot: [:get, 'session/:session_id/element/:id/screenshot'.freeze]
|
92
|
-
}
|
93
|
-
).freeze
|
94
|
-
COMMANDS_EXTEND_W3C = COMMANDS.merge(::Appium::Core::Base::Commands::W3C).merge(
|
95
|
-
{
|
96
|
-
# ::Appium::Core::Base::Commands::OSS has the following commands and Appium also use them.
|
97
|
-
# Delegated to ::Appium::Core::Base::Commands::OSS commands
|
98
|
-
status: [:get, 'status'.freeze],
|
99
|
-
is_element_displayed: [:get, 'session/:session_id/element/:id/displayed'.freeze],
|
100
|
-
|
101
|
-
# FIXME: remove after apply https://github.com/SeleniumHQ/selenium/pull/5249
|
102
|
-
# The fix will be included in selenium-3.8.2
|
103
|
-
get_page_source: [:get, 'session/:session_id/source'.freeze],
|
104
|
-
|
105
|
-
get_timeouts: [:get, 'session/:session_id/timeouts'.freeze],
|
106
|
-
|
107
|
-
## Add OSS commands to W3C commands. We can remove them if we would like to remove them from W3C module.
|
108
|
-
### Session capability
|
109
|
-
get_capabilities: [:get, 'session/:session_id'.freeze],
|
110
|
-
|
111
|
-
### rotatable
|
112
|
-
get_screen_orientation: [:get, 'session/:session_id/orientation'.freeze],
|
113
|
-
set_screen_orientation: [:post, 'session/:session_id/orientation'.freeze],
|
114
|
-
|
115
|
-
get_location: [:get, 'session/:session_id/location'.freeze],
|
116
|
-
set_location: [:post, 'session/:session_id/location'.freeze],
|
117
|
-
|
118
|
-
### For IME
|
119
|
-
ime_get_available_engines: [:get, 'session/:session_id/ime/available_engines'.freeze],
|
120
|
-
ime_get_active_engine: [:get, 'session/:session_id/ime/active_engine'.freeze],
|
121
|
-
ime_is_activated: [:get, 'session/:session_id/ime/activated'.freeze],
|
122
|
-
ime_deactivate: [:post, 'session/:session_id/ime/deactivate'.freeze],
|
123
|
-
ime_activate_engine: [:post, 'session/:session_id/ime/activate'.freeze],
|
124
|
-
|
125
|
-
### Logs
|
126
|
-
get_available_log_types: [:get, 'session/:session_id/log/types'.freeze],
|
127
|
-
get_log: [:post, 'session/:session_id/log'.freeze]
|
128
|
-
}
|
129
|
-
).freeze
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
2
|
+
require_relative 'command/common'
|
3
|
+
require_relative 'command/mjsonwp'
|
4
|
+
require_relative 'command/w3c'
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Appium
|
2
|
+
module Core
|
3
|
+
# ref: https://github.com/appium/appium-base-driver/blob/master/lib/mjsonwp/routes.js
|
4
|
+
module Commands
|
5
|
+
# Some commands differ for each driver.
|
6
|
+
COMMAND = {
|
7
|
+
# common
|
8
|
+
available_contexts: [:get, 'session/:session_id/contexts'.freeze],
|
9
|
+
set_context: [:post, 'session/:session_id/context'.freeze],
|
10
|
+
current_context: [:get, 'session/:session_id/context'.freeze],
|
11
|
+
|
12
|
+
touch_actions: [:post, 'session/:session_id/touch/perform'.freeze],
|
13
|
+
multi_touch: [:post, 'session/:session_id/touch/multi/perform'.freeze],
|
14
|
+
|
15
|
+
set_immediate_value: [:post, 'session/:session_id/appium/element/:id/value'.freeze],
|
16
|
+
replace_value: [:post, 'session/:session_id/appium/element/:id/replace_value'.freeze],
|
17
|
+
|
18
|
+
launch_app: [:post, 'session/:session_id/appium/app/launch'.freeze],
|
19
|
+
close_app: [:post, 'session/:session_id/appium/app/close'.freeze],
|
20
|
+
reset: [:post, 'session/:session_id/appium/app/reset'.freeze],
|
21
|
+
background_app: [:post, 'session/:session_id/appium/app/background'.freeze],
|
22
|
+
app_strings: [:post, 'session/:session_id/appium/app/strings'.freeze],
|
23
|
+
|
24
|
+
device_locked?: [:post, 'session/:session_id/appium/device/is_locked'.freeze],
|
25
|
+
unlock: [:post, 'session/:session_id/appium/device/unlock'.freeze],
|
26
|
+
lock: [:post, 'session/:session_id/appium/device/lock'.freeze],
|
27
|
+
device_time: [:get, 'session/:session_id/appium/device/system_time'.freeze],
|
28
|
+
install_app: [:post, 'session/:session_id/appium/device/install_app'.freeze],
|
29
|
+
remove_app: [:post, 'session/:session_id/appium/device/remove_app'.freeze],
|
30
|
+
app_installed?: [:post, 'session/:session_id/appium/device/app_installed'.freeze],
|
31
|
+
activate_app: [:post, 'session/:session_id/appium/device/activate_app'.freeze],
|
32
|
+
terminate_app: [:post, 'session/:session_id/appium/device/terminate_app'.freeze],
|
33
|
+
app_state: [:post, 'session/:session_id/appium/device/app_state'.freeze],
|
34
|
+
shake: [:post, 'session/:session_id/appium/device/shake'.freeze],
|
35
|
+
hide_keyboard: [:post, 'session/:session_id/appium/device/hide_keyboard'.freeze],
|
36
|
+
press_keycode: [:post, 'session/:session_id/appium/device/press_keycode'.freeze],
|
37
|
+
long_press_keycode: [:post, 'session/:session_id/appium/device/long_press_keycode'.freeze],
|
38
|
+
# keyevent is only for Selendroid
|
39
|
+
keyevent: [:post, 'session/:session_id/appium/device/keyevent'.freeze],
|
40
|
+
push_file: [:post, 'session/:session_id/appium/device/push_file'.freeze],
|
41
|
+
pull_file: [:post, 'session/:session_id/appium/device/pull_file'.freeze],
|
42
|
+
pull_folder: [:post, 'session/:session_id/appium/device/pull_folder'.freeze],
|
43
|
+
get_clipboard: [:post, 'session/:session_id/appium/device/get_clipboard'.freeze],
|
44
|
+
set_clipboard: [:post, 'session/:session_id/appium/device/set_clipboard'.freeze],
|
45
|
+
get_settings: [:get, 'session/:session_id/appium/settings'.freeze],
|
46
|
+
update_settings: [:post, 'session/:session_id/appium/settings'.freeze],
|
47
|
+
stop_recording_screen: [:post, 'session/:session_id/appium/stop_recording_screen'.freeze],
|
48
|
+
start_recording_screen: [:post, 'session/:session_id/appium/start_recording_screen'.freeze],
|
49
|
+
compare_images: [:post, 'session/:session_id/appium/compare_images'.freeze]
|
50
|
+
}.freeze
|
51
|
+
|
52
|
+
COMMAND_ANDROID = {
|
53
|
+
open_notifications: [:post, 'session/:session_id/appium/device/open_notifications'.freeze],
|
54
|
+
toggle_airplane_mode: [:post, 'session/:session_id/appium/device/toggle_airplane_mode'.freeze],
|
55
|
+
start_activity: [:post, 'session/:session_id/appium/device/start_activity'.freeze],
|
56
|
+
current_activity: [:get, 'session/:session_id/appium/device/current_activity'.freeze],
|
57
|
+
current_package: [:get, 'session/:session_id/appium/device/current_package'.freeze],
|
58
|
+
get_system_bars: [:get, 'session/:session_id/appium/device/system_bars'.freeze],
|
59
|
+
get_display_density: [:get, 'session/:session_id/appium/device/display_density'.freeze],
|
60
|
+
is_keyboard_shown: [:get, 'session/:session_id/appium/device/is_keyboard_shown'.freeze],
|
61
|
+
toggle_wifi: [:post, 'session/:session_id/appium/device/toggle_wifi'.freeze],
|
62
|
+
toggle_data: [:post, 'session/:session_id/appium/device/toggle_data'.freeze],
|
63
|
+
toggle_location_services: [:post, 'session/:session_id/appium/device/toggle_location_services'.freeze],
|
64
|
+
end_coverage: [:post, 'session/:session_id/appium/app/end_test_coverage'.freeze],
|
65
|
+
get_performance_data_types: [:post, 'session/:session_id/appium/performanceData/types'.freeze],
|
66
|
+
get_performance_data: [:post, 'session/:session_id/appium/getPerformanceData'.freeze],
|
67
|
+
get_network_connection: [:get, 'session/:session_id/network_connection'.freeze], # defined also in OSS
|
68
|
+
set_network_connection: [:post, 'session/:session_id/network_connection'.freeze], # defined also in OSS
|
69
|
+
|
70
|
+
# only emulator
|
71
|
+
send_sms: [:post, 'session/:session_id/appium/device/send_sms'.freeze],
|
72
|
+
gsm_call: [:post, 'session/:session_id/appium/device/gsm_call'.freeze],
|
73
|
+
gsm_signal: [:post, 'session/:session_id/appium/device/gsm_signal'.freeze],
|
74
|
+
gsm_voice: [:post, 'session/:session_id/appium/device/gsm_voice'.freeze],
|
75
|
+
set_network_speed: [:post, 'session/:session_id/appium/device/network_speed'.freeze],
|
76
|
+
set_power_capacity: [:post, 'session/:session_id/appium/device/power_capacity'.freeze],
|
77
|
+
set_power_ac: [:post, 'session/:session_id/appium/device/power_ac'.freeze]
|
78
|
+
}.freeze
|
79
|
+
|
80
|
+
COMMAND_IOS = {
|
81
|
+
touch_id: [:post, 'session/:session_id/appium/simulator/touch_id'.freeze],
|
82
|
+
toggle_touch_id_enrollment: [:post, 'session/:session_id/appium/simulator/toggle_touch_id_enrollment'.freeze]
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).merge(COMMAND_IOS).freeze
|
86
|
+
end # module Commands
|
87
|
+
end # module Core
|
88
|
+
end # module Appium
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Appium
|
2
|
+
module Core
|
3
|
+
module Commands
|
4
|
+
module MJSONWP
|
5
|
+
COMMANDS = ::Appium::Core::Commands::COMMANDS.merge(::Appium::Core::Base::Commands::OSS).merge(
|
6
|
+
{
|
7
|
+
# W3C already has.
|
8
|
+
take_element_screenshot: [:get, 'session/:session_id/element/:id/screenshot'.freeze]
|
9
|
+
}
|
10
|
+
).freeze
|
11
|
+
end # module MJSONWP
|
12
|
+
end # module Commands
|
13
|
+
end # module Core
|
14
|
+
end # Appium
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Appium
|
2
|
+
module Core
|
3
|
+
module Commands
|
4
|
+
module W3C
|
5
|
+
COMMANDS = ::Appium::Core::Commands::COMMANDS.merge(::Appium::Core::Base::Commands::W3C).merge(
|
6
|
+
{
|
7
|
+
# ::Appium::Core::Base::Commands::OSS has the following commands and Appium also use them.
|
8
|
+
# Delegated to ::Appium::Core::Base::Commands::OSS commands
|
9
|
+
status: [:get, 'status'.freeze],
|
10
|
+
is_element_displayed: [:get, 'session/:session_id/element/:id/displayed'.freeze],
|
11
|
+
|
12
|
+
# FIXME: remove after apply https://github.com/SeleniumHQ/selenium/pull/5249
|
13
|
+
# The fix will be included in selenium-3.8.2
|
14
|
+
get_page_source: [:get, 'session/:session_id/source'.freeze],
|
15
|
+
|
16
|
+
get_timeouts: [:get, 'session/:session_id/timeouts'.freeze],
|
17
|
+
|
18
|
+
## Add OSS commands to W3C commands. We can remove them if we would like to remove them from W3C module.
|
19
|
+
### Session capability
|
20
|
+
get_capabilities: [:get, 'session/:session_id'.freeze],
|
21
|
+
|
22
|
+
### rotatable
|
23
|
+
get_screen_orientation: [:get, 'session/:session_id/orientation'.freeze],
|
24
|
+
set_screen_orientation: [:post, 'session/:session_id/orientation'.freeze],
|
25
|
+
|
26
|
+
get_location: [:get, 'session/:session_id/location'.freeze],
|
27
|
+
set_location: [:post, 'session/:session_id/location'.freeze],
|
28
|
+
|
29
|
+
### For IME
|
30
|
+
ime_get_available_engines: [:get, 'session/:session_id/ime/available_engines'.freeze],
|
31
|
+
ime_get_active_engine: [:get, 'session/:session_id/ime/active_engine'.freeze],
|
32
|
+
ime_is_activated: [:get, 'session/:session_id/ime/activated'.freeze],
|
33
|
+
ime_deactivate: [:post, 'session/:session_id/ime/deactivate'.freeze],
|
34
|
+
ime_activate_engine: [:post, 'session/:session_id/ime/activate'.freeze],
|
35
|
+
|
36
|
+
### Logs
|
37
|
+
get_available_log_types: [:get, 'session/:session_id/log/types'.freeze],
|
38
|
+
get_log: [:post, 'session/:session_id/log'.freeze]
|
39
|
+
}
|
40
|
+
).freeze
|
41
|
+
end # module W3C
|
42
|
+
end # module Commands
|
43
|
+
end # module Core
|
44
|
+
end # module Appium
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require_relative 'wait/timer'
|
2
|
+
|
3
|
+
module Appium
|
4
|
+
module Core
|
5
|
+
module Wait
|
6
|
+
class TimeoutError < StandardError; end
|
7
|
+
|
8
|
+
DEFAULT_TIMEOUT = 30
|
9
|
+
DEFAULT_INTERVAL = 0.5
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Check every interval seconds to see if yield doesn't raise an exception.
|
13
|
+
# Give up after timeout seconds.
|
14
|
+
#
|
15
|
+
# If only a number is provided then it's treated as the timeout value.
|
16
|
+
#
|
17
|
+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
|
18
|
+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
|
19
|
+
# @param [String] message: Exception message if timed out.
|
20
|
+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
|
21
|
+
# @param [Object, NilClass] object: Object to evaluate block against
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
#
|
25
|
+
# result = Appium::Core::Wait.until { @driver.find_element(:id, 'something') }
|
26
|
+
#
|
27
|
+
# result = Appium::Core::Wait.until(object: 'some object') { |object|
|
28
|
+
# @driver.find_element(:id, object)
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
def until(timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL, message: nil, ignored: nil, object: nil)
|
32
|
+
ignored = Array(ignored || ::Exception)
|
33
|
+
|
34
|
+
last_error = nil
|
35
|
+
|
36
|
+
begin
|
37
|
+
run_with_timer(timeout, interval) { return yield(object) }
|
38
|
+
rescue ::Errno::ECONNREFUSED => e
|
39
|
+
raise e
|
40
|
+
rescue *ignored => last_error # rubocop:disable Lint/HandleExceptions
|
41
|
+
# swallowed
|
42
|
+
end
|
43
|
+
|
44
|
+
msg = message_for timeout, message
|
45
|
+
msg << " (#{last_error.message})" if last_error
|
46
|
+
|
47
|
+
raise TimeoutError, msg
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check every interval seconds to see if yield returns a truthy value.
|
51
|
+
# Note this isn't a strict boolean true, any truthy value is accepted.
|
52
|
+
# false and nil are considered failures.
|
53
|
+
# Give up after timeout seconds.
|
54
|
+
#
|
55
|
+
# If only a number is provided then it's treated as the timeout value.
|
56
|
+
#
|
57
|
+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
|
58
|
+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
|
59
|
+
# @param [String] message: Exception message if timed out.
|
60
|
+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
|
61
|
+
# @param [Object, NilClass] object: Object to evaluate block against
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
#
|
65
|
+
# Appium::Core::Wait.until_true { @driver.find_element(:id, 'something') }
|
66
|
+
#
|
67
|
+
# Appium::Core::Wait.until_true(object: 'some object') { |object|
|
68
|
+
# @driver.find_element(:id, object)
|
69
|
+
# }
|
70
|
+
#
|
71
|
+
def until_true(timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL, message: nil, ignored: nil, object: nil)
|
72
|
+
ignored = Array(ignored || ::Exception)
|
73
|
+
|
74
|
+
last_error = nil
|
75
|
+
|
76
|
+
begin
|
77
|
+
run_with_timer(timeout, interval) do
|
78
|
+
result = yield(object)
|
79
|
+
return result if result
|
80
|
+
end
|
81
|
+
rescue ::Errno::ECONNREFUSED => e
|
82
|
+
raise e
|
83
|
+
rescue *ignored => last_error # rubocop:disable Lint/HandleExceptions
|
84
|
+
# swallowed
|
85
|
+
end
|
86
|
+
|
87
|
+
msg = message_for timeout, message
|
88
|
+
msg << " (#{last_error.message})" if last_error
|
89
|
+
|
90
|
+
raise TimeoutError, msg
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def message_for(timeout, message)
|
96
|
+
msg = "timed out after #{timeout} seconds"
|
97
|
+
msg << ", #{message}" if message
|
98
|
+
msg
|
99
|
+
end
|
100
|
+
|
101
|
+
def run_with_timer(timeout, interval, &block)
|
102
|
+
if timeout.zero?
|
103
|
+
block.call # rubocop:disable Performance/RedundantBlockCall
|
104
|
+
else
|
105
|
+
Timer.wait timeout do
|
106
|
+
block.call # rubocop:disable Performance/RedundantBlockCall
|
107
|
+
sleep interval
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end # self
|
112
|
+
end # module Wait
|
113
|
+
|
114
|
+
module Waitable
|
115
|
+
# Check every interval seconds to see if yield returns a truthy value.
|
116
|
+
# Note this isn't a strict boolean true, any truthy value is accepted.
|
117
|
+
# false and nil are considered failures.
|
118
|
+
# Give up after timeout seconds.
|
119
|
+
#
|
120
|
+
# If only a number is provided then it's treated as the timeout value.
|
121
|
+
#
|
122
|
+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
|
123
|
+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
|
124
|
+
# @param [String] message: Exception message if timed out.
|
125
|
+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
#
|
129
|
+
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
|
130
|
+
#
|
131
|
+
def wait_true(timeout: nil, interval: nil, message: nil, ignored: nil)
|
132
|
+
Wait.until_true(timeout: timeout || @wait_timeout,
|
133
|
+
interval: interval || @wait_interval,
|
134
|
+
message: message,
|
135
|
+
ignored: ignored,
|
136
|
+
object: self) { yield }
|
137
|
+
end
|
138
|
+
|
139
|
+
# Check every interval seconds to see if yield doesn't raise an exception.
|
140
|
+
# Give up after timeout seconds.
|
141
|
+
#
|
142
|
+
# If only a number is provided then it's treated as the timeout value.
|
143
|
+
#
|
144
|
+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
|
145
|
+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
|
146
|
+
# @param [String] message: Exception message if timed out.
|
147
|
+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
#
|
151
|
+
# @core.wait { @driver.find_element :accessibility_id, 'something' }
|
152
|
+
#
|
153
|
+
def wait(timeout: nil, interval: nil, message: nil, ignored: nil)
|
154
|
+
Wait.until(timeout: timeout || @wait_timeout,
|
155
|
+
interval: interval || @wait_interval,
|
156
|
+
message: message,
|
157
|
+
ignored: ignored,
|
158
|
+
object: self) { yield }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end # module Core
|
162
|
+
end # module Appium
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Appium
|
2
|
+
module Core
|
3
|
+
module Wait
|
4
|
+
module Timer
|
5
|
+
class << self
|
6
|
+
# @private
|
7
|
+
def wait(timeout, &block)
|
8
|
+
end_time = current_time + timeout
|
9
|
+
loop do
|
10
|
+
yield(block)
|
11
|
+
break if current_time > end_time
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
18
|
+
def current_time
|
19
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
def current_time
|
23
|
+
::Time.now.to_f
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end # module Timer
|
28
|
+
end # module Wait
|
29
|
+
end # module Core
|
30
|
+
end # module Appium
|
@@ -1,3 +1,10 @@
|
|
1
|
+
require_relative 'device/touch_actions'
|
2
|
+
require_relative 'device/multi_touch'
|
3
|
+
require_relative 'device/screen_record'
|
4
|
+
require_relative 'device/app_state'
|
5
|
+
require_relative 'device/clipboard_content_type'
|
6
|
+
require_relative 'device/image_comparison'
|
7
|
+
|
1
8
|
require 'base64'
|
2
9
|
|
3
10
|
module Appium
|
@@ -513,6 +520,7 @@ module Appium
|
|
513
520
|
add_app_management
|
514
521
|
add_device_lock
|
515
522
|
add_file_management
|
523
|
+
Core::Device::ImageComparison.extended
|
516
524
|
end
|
517
525
|
|
518
526
|
# def extended
|
@@ -522,7 +530,6 @@ module Appium
|
|
522
530
|
block_given? ? create_bridge_command(method, &Proc.new) : create_bridge_command(method)
|
523
531
|
|
524
532
|
delegate_driver_method method
|
525
|
-
delegate_from_appium_driver method
|
526
533
|
end
|
527
534
|
|
528
535
|
# @private CoreBridge
|
@@ -539,11 +546,6 @@ module Appium
|
|
539
546
|
::Appium::Core::Base::Driver.class_eval { def_delegator :@bridge, method }
|
540
547
|
end
|
541
548
|
|
542
|
-
# @private
|
543
|
-
def delegate_from_appium_driver(method, delegation_target = :driver)
|
544
|
-
def_delegator delegation_target, method
|
545
|
-
end
|
546
|
-
|
547
549
|
# @private
|
548
550
|
def create_bridge_command(method)
|
549
551
|
::Appium::Core::Base::Bridge::MJSONWP.class_eval do
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Appium
|
4
|
+
module Core
|
5
|
+
module Device
|
6
|
+
module ImageComparison
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
MODE = [:matchFeatures, :getSimilarity, :matchTemplate].freeze
|
10
|
+
|
11
|
+
MATCH_FEATURES = {
|
12
|
+
detector_name: %w(AKAZE AGAST BRISK FAST GFTT KAZE MSER SIFT ORB),
|
13
|
+
match_func: %w(FlannBased BruteForce BruteForceL1 BruteForceHamming BruteForceHammingLut BruteForceSL2),
|
14
|
+
goodMatchesFactor: nil, # Integer
|
15
|
+
visualize: [true, false]
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
MATCH_TEMPLATE = {
|
19
|
+
visualize: [true, false]
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
GET_SIMILARITY = {
|
23
|
+
visualize: [true, false]
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
# @!method match_images_features(first_image:, second_image:, detector_name: 'ORB',
|
27
|
+
# match_func: 'BruteForce', good_matches_factor: 100, visualize: false)
|
28
|
+
# Performs images matching by features with default options. Read https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.html
|
29
|
+
# for more details on this topic.
|
30
|
+
#
|
31
|
+
# @param [String] first_image An image data. All image formats, that OpenCV library itself accepts, are supported.
|
32
|
+
# @param [String] second_image An image data. All image formats, that OpenCV library itself accepts, are supported.
|
33
|
+
# @param [String] detector_name Sets the detector name for features matching
|
34
|
+
# algorithm. Some of these detectors (FAST, AGAST, GFTT, FAST, SIFT and MSER) are
|
35
|
+
# not available in the default OpenCV installation and have to be enabled manually
|
36
|
+
# before library compilation. The default detector name is 'ORB'.
|
37
|
+
# @param [String] match_func The name of the matching function. The default one is 'BruteForce'.
|
38
|
+
# @param [String] good_matches_factor The maximum count of "good" matches (e. g. with minimal distances).
|
39
|
+
# The default one is nil.
|
40
|
+
# @param [Bool] visualise Makes the endpoint to return an image, which contains the visualized result of
|
41
|
+
# the corresponding picture matching operation. This option is disabled by default.
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# @driver.match_images_features first_image: "image data 1", second_image: "image data 2"
|
45
|
+
#
|
46
|
+
# visual = @@driver.match_images_features first_image: image1, second_image: image2, visualize: true
|
47
|
+
# File.write 'match_images_visual.png', Base64.decode64(visual['visualization']) # if the image is PNG
|
48
|
+
#
|
49
|
+
|
50
|
+
# @!method find_image_occurrence(full_image:, partial_image:, detector_name: 'ORB', visualize: false)
|
51
|
+
# Performs images matching by template to find possible occurrence of the partial image
|
52
|
+
# in the full image with default options. Read https://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/template_matching/template_matching.html
|
53
|
+
# for more details on this topic.
|
54
|
+
#
|
55
|
+
# @param [String] full_image: A full image data.
|
56
|
+
# @param [String] partial_image: A partial image data. All image formats, that OpenCV library itself accepts,
|
57
|
+
# are supported.
|
58
|
+
# @param [Bool] visualise: Makes the endpoint to return an image, which contains the visualized result of
|
59
|
+
# the corresponding picture matching operation. This option is disabled by default.
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# @driver.find_image_occurrence full_image: "image data 1", partial_image: "image data 2"
|
63
|
+
#
|
64
|
+
# visual = @@driver.find_image_occurrence full_image: image1, partial_image: image2, visualize: true
|
65
|
+
# File.write 'find_result_visual.png', Base64.decode64(visual['visualization']) # if the image is PNG
|
66
|
+
#
|
67
|
+
|
68
|
+
# @!method get_images_similarity(first_image:, second_image:, detector_name: 'ORB', visualize: false)
|
69
|
+
# Performs images matching to calculate the similarity score between them
|
70
|
+
# with default options. The flow there is similar to the one used in `find_image_occurrence`
|
71
|
+
# but it is mandatory that both images are of equal size.
|
72
|
+
#
|
73
|
+
# @param [String] first_image: An image data. All image formats, that OpenCV library itself accepts, are supported.
|
74
|
+
# @param [String] second_image: An image data. All image formats, that OpenCV library itself accepts, are supported.
|
75
|
+
# @param [Bool] visualise: Makes the endpoint to return an image, which contains the visualized result of
|
76
|
+
# the corresponding picture matching operation. This option is disabled by default.
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# @driver.get_images_similarity first_image: "image data 1", second_image: "image data 2"
|
80
|
+
#
|
81
|
+
# visual = @@driver.get_images_similarity first_image: image1, second_image: image2, visualize: true
|
82
|
+
# File.write 'images_similarity_visual.png', Base64.decode64(visual['visualization']) # if the image is PNG
|
83
|
+
#
|
84
|
+
|
85
|
+
# @!method compare_images(mode:, first_image:, second_image:, options:)
|
86
|
+
#
|
87
|
+
# Performs images comparison using OpenCV framework features.
|
88
|
+
# It is expected that both OpenCV framework and opencv4nodejs
|
89
|
+
# module are installed on the machine where Appium server is running.
|
90
|
+
#
|
91
|
+
# @param [Symbol] mode: One of possible comparison modes: `:matchFeatures`, `:getSimilarity`, `:matchTemplate`.
|
92
|
+
# `:matchFeatures is by default.
|
93
|
+
# @param [String] first_image: An image data. All image formats, that OpenCV library itself accepts, are supported.
|
94
|
+
# @param [String] second_image: An image data. All image formats, that OpenCV library itself accepts, are supported.
|
95
|
+
# @param [Hash] options: The content of this dictionary depends on the actual `mode` value.
|
96
|
+
# See the documentation on `appium-support` module for more details.
|
97
|
+
# @returns [Hash] The content of the resulting dictionary depends on the actual `mode` and `options` values.
|
98
|
+
# See the documentation on `appium-support` module for more details.
|
99
|
+
#
|
100
|
+
|
101
|
+
####
|
102
|
+
## class << self
|
103
|
+
####
|
104
|
+
|
105
|
+
def self.extended
|
106
|
+
::Appium::Core::Device.add_endpoint_method(:match_images_features) do
|
107
|
+
def match_images_features(first_image:, # rubocop:disable Metrics/ParameterLists
|
108
|
+
second_image:,
|
109
|
+
detector_name: 'ORB',
|
110
|
+
match_func: 'BruteForce',
|
111
|
+
good_matches_factor: nil,
|
112
|
+
visualize: false)
|
113
|
+
unless ::Appium::Core::Device::ImageComparison::MATCH_FEATURES[:detector_name].member?(detector_name.to_s)
|
114
|
+
raise "detector_name should be #{::Appium::Core::Device::ImageComparison::MATCH_FEATURES[:detector_name]}"
|
115
|
+
end
|
116
|
+
unless ::Appium::Core::Device::ImageComparison::MATCH_FEATURES[:match_func].member?(match_func.to_s)
|
117
|
+
raise "match_func should be #{::Appium::Core::Device::ImageComparison::MATCH_FEATURES[:match_func]}"
|
118
|
+
end
|
119
|
+
unless ::Appium::Core::Device::ImageComparison::MATCH_FEATURES[:visualize].member?(visualize)
|
120
|
+
raise "visualize should be #{::Appium::Core::Device::ImageComparison::MATCH_FEATURES[:visualize]}"
|
121
|
+
end
|
122
|
+
|
123
|
+
options = {}
|
124
|
+
options[:detectorName] = detector_name.to_s.upcase
|
125
|
+
options[:matchFunc] = match_func.to_s
|
126
|
+
options[:goodMatchesFactor] = good_matches_factor.to_i unless good_matches_factor.nil?
|
127
|
+
options[:visualize] = visualize
|
128
|
+
|
129
|
+
compare_images(mode: :matchFeatures, first_image: first_image, second_image: second_image, options: options)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
::Appium::Core::Device.add_endpoint_method(:find_image_occurrence) do
|
134
|
+
def find_image_occurrence(full_image:, partial_image:, visualize: false)
|
135
|
+
unless ::Appium::Core::Device::ImageComparison::MATCH_TEMPLATE[:visualize].member?(visualize)
|
136
|
+
raise "visualize should be #{::Appium::Core::Device::ImageComparison::MATCH_TEMPLATE[:visualize]}"
|
137
|
+
end
|
138
|
+
|
139
|
+
options = {}
|
140
|
+
options[:visualize] = visualize
|
141
|
+
|
142
|
+
compare_images(mode: :matchTemplate, first_image: full_image, second_image: partial_image, options: options)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
::Appium::Core::Device.add_endpoint_method(:get_images_similarity) do
|
147
|
+
def get_images_similarity(first_image:, second_image:, visualize: false)
|
148
|
+
unless ::Appium::Core::Device::ImageComparison::GET_SIMILARITY[:visualize].member?(visualize)
|
149
|
+
raise "visualize should be #{::Appium::Core::Device::ImageComparison::GET_SIMILARITY[:visualize]}"
|
150
|
+
end
|
151
|
+
|
152
|
+
options = {}
|
153
|
+
options[:visualize] = visualize
|
154
|
+
|
155
|
+
compare_images(mode: :getSimilarity, first_image: first_image, second_image: second_image, options: options)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
::Appium::Core::Device.add_endpoint_method(:compare_images) do
|
160
|
+
def compare_images(mode: :matchFeatures, first_image:, second_image:, options: nil)
|
161
|
+
unless ::Appium::Core::Device::ImageComparison::MODE.member?(mode)
|
162
|
+
raise "content_type should be #{::Appium::Core::Device::ImageComparison::MODE}"
|
163
|
+
end
|
164
|
+
|
165
|
+
params = {}
|
166
|
+
params[:mode] = mode
|
167
|
+
params[:firstImage] = Base64.encode64 first_image
|
168
|
+
params[:secondImage] = Base64.encode64 second_image
|
169
|
+
params[:options] = options if options
|
170
|
+
|
171
|
+
execute(:compare_images, {}, params)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end # self
|
175
|
+
end # module ImageComparison
|
176
|
+
end # module Device
|
177
|
+
end # module Core
|
178
|
+
end # module Appium
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Appium
|
2
2
|
module Core
|
3
3
|
class Driver
|
4
|
+
include Waitable
|
4
5
|
# Selenium webdriver capabilities
|
5
6
|
# @return [Core::Base::Capabilities]
|
6
7
|
attr_reader :caps
|
@@ -278,73 +279,8 @@ module Appium
|
|
278
279
|
@driver.save_screenshot png_save_path
|
279
280
|
end
|
280
281
|
|
281
|
-
# Check every interval seconds to see if yield returns a truthy value.
|
282
|
-
# Note this isn't a strict boolean true, any truthy value is accepted.
|
283
|
-
# false and nil are considered failures.
|
284
|
-
# Give up after timeout seconds.
|
285
|
-
#
|
286
|
-
# Wait code from the selenium Ruby gem
|
287
|
-
# https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
|
288
|
-
#
|
289
|
-
# If only a number is provided then it's treated as the timeout value.
|
290
|
-
#
|
291
|
-
# @param [Hash] opts Options
|
292
|
-
# @option opts [Numeric] :timeout Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
|
293
|
-
# @option opts [Numeric] :interval Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
|
294
|
-
# @option opts [String] :message Exception message if timed out.
|
295
|
-
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
|
296
|
-
#
|
297
|
-
# @example
|
298
|
-
#
|
299
|
-
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
|
300
|
-
#
|
301
|
-
def wait_true(opts = {})
|
302
|
-
opts = process_wait_opts(opts).merge(return_if_true: true)
|
303
|
-
|
304
|
-
opts[:timeout] ||= @wait_timeout
|
305
|
-
opts[:interval] ||= @wait_interval
|
306
|
-
|
307
|
-
wait = ::Appium::Core::Base::Wait.new opts
|
308
|
-
wait.until { yield }
|
309
|
-
end
|
310
|
-
|
311
|
-
# Check every interval seconds to see if yield doesn't raise an exception.
|
312
|
-
# Give up after timeout seconds.
|
313
|
-
#
|
314
|
-
# Wait code from the selenium Ruby gem
|
315
|
-
# https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
|
316
|
-
#
|
317
|
-
# If only a number is provided then it's treated as the timeout value.
|
318
|
-
#
|
319
|
-
# @param [Hash] opts Options
|
320
|
-
# @option opts [Numeric] :timeout Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
|
321
|
-
# @option opts [Numeric] :interval Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
|
322
|
-
# @option opts [String] :message Exception message if timed out.
|
323
|
-
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
|
324
|
-
#
|
325
|
-
# @example
|
326
|
-
#
|
327
|
-
# @core.wait { @driver.find_element :accessibility_id, 'something' }
|
328
|
-
#
|
329
|
-
def wait(opts = {})
|
330
|
-
opts = process_wait_opts(opts).merge(return_if_true: false)
|
331
|
-
|
332
|
-
opts[:timeout] ||= @wait_timeout
|
333
|
-
opts[:interval] ||= @wait_interval
|
334
|
-
|
335
|
-
wait = ::Appium::Core::Base::Wait.new opts
|
336
|
-
wait.until { yield }
|
337
|
-
end
|
338
|
-
|
339
282
|
private
|
340
283
|
|
341
|
-
# @private
|
342
|
-
def process_wait_opts(opts)
|
343
|
-
opts = { timeout: opts } if opts.is_a?(Numeric)
|
344
|
-
raise 'opts must be a hash' unless opts.is_a? Hash
|
345
|
-
opts
|
346
|
-
end
|
347
|
-
|
348
284
|
# @private
|
349
285
|
def extend_for(device:, automation_name:, target:)
|
350
286
|
target.extend Appium::Core
|
@@ -432,8 +368,8 @@ module Appium
|
|
432
368
|
@port = appium_lib_opts.fetch :port, DEFAULT_APPIUM_PORT
|
433
369
|
|
434
370
|
# timeout and interval used in ::Appium::Comm.wait/wait_true
|
435
|
-
@wait_timeout = appium_lib_opts.fetch :wait_timeout,
|
436
|
-
@wait_interval = appium_lib_opts.fetch :wait_interval,
|
371
|
+
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
|
372
|
+
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
|
437
373
|
|
438
374
|
# to pass it in Selenium.new.
|
439
375
|
# `listener = opts.delete(:listener)` is called in Selenium::Driver.new
|
@@ -467,7 +403,7 @@ module Appium
|
|
467
403
|
|
468
404
|
# @private
|
469
405
|
def write_session_id(session_id, export_path = '/tmp/appium_lib_session')
|
470
|
-
File.
|
406
|
+
File.write(export_path, session_id)
|
471
407
|
rescue IOError => e
|
472
408
|
::Appium::Logger.warn e
|
473
409
|
nil
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Appium
|
2
2
|
module Core
|
3
|
-
VERSION = '1.4.
|
4
|
-
DATE = '2018-04-
|
3
|
+
VERSION = '1.4.1'.freeze unless defined? ::Appium::Core::VERSION
|
4
|
+
DATE = '2018-04-22'.freeze unless defined? ::Appium::Core::DATE
|
5
5
|
end
|
6
6
|
end
|
data/release_notes.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
#### v1.4.1 2018-04-22
|
2
|
+
|
3
|
+
- [afb5d33](https://github.com/appium/ruby_lib_core/commit/afb5d334a786a12bd407848312ddd35c8c9ad308) Release 1.4.1
|
4
|
+
- [7fd0ccc](https://github.com/appium/ruby_lib_core/commit/7fd0ccca947e742f03ac41022d0670bf809d3969) add base image comparison (#80)
|
5
|
+
- [0b10980](https://github.com/appium/ruby_lib_core/commit/0b10980cbbb48cfba0352fa0ef4e15b2fd16d086) split timer loop from wait
|
6
|
+
- [009f679](https://github.com/appium/ruby_lib_core/commit/009f679c08fd4c019b4876454d758ef5cf30daae) Refactor commands (#79)
|
7
|
+
- [4b1c086](https://github.com/appium/ruby_lib_core/commit/4b1c0868c6258106faae24978be324b79785fc5b) Extract wait (#78)
|
8
|
+
|
9
|
+
|
1
10
|
#### v1.4.0 2018-04-15
|
2
11
|
|
3
12
|
- [5cc89aa](https://github.com/appium/ruby_lib_core/commit/5cc89aa4a9533e174527db5b805b96cf2b45a9ac) Release 1.4.0
|
data/script/commands.rb
CHANGED
@@ -16,8 +16,8 @@ module Script
|
|
16
16
|
# - webdriver_w3c_commands: ::Selenium::WebDriver::Remote::W3C::Bridge::COMMANDS
|
17
17
|
#
|
18
18
|
def initialize
|
19
|
-
@implemented_mjsonwp_commands = convert_driver_commands Appium::Core::Commands::
|
20
|
-
@implemented_w3c_commands = convert_driver_commands Appium::Core::Commands::
|
19
|
+
@implemented_mjsonwp_commands = convert_driver_commands Appium::Core::Commands::MJSONWP::COMMANDS
|
20
|
+
@implemented_w3c_commands = convert_driver_commands Appium::Core::Commands::W3C::COMMANDS
|
21
21
|
@implemented_core_commands = convert_driver_commands Appium::Core::Commands::COMMANDS
|
22
22
|
|
23
23
|
@webdriver_oss_commands = convert_driver_commands Appium::Core::Base::Commands::OSS
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appium_lib_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kazuaki MATSUO
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.5'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: json
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.8'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.8'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: faye-websocket
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -247,22 +233,27 @@ files:
|
|
247
233
|
- lib/appium_lib_core/common.rb
|
248
234
|
- lib/appium_lib_core/common/base.rb
|
249
235
|
- lib/appium_lib_core/common/base/bridge.rb
|
250
|
-
- lib/appium_lib_core/common/base/bridge/
|
236
|
+
- lib/appium_lib_core/common/base/bridge/mjsonwp.rb
|
251
237
|
- lib/appium_lib_core/common/base/bridge/w3c.rb
|
252
238
|
- lib/appium_lib_core/common/base/capabilities.rb
|
253
239
|
- lib/appium_lib_core/common/base/command.rb
|
254
240
|
- lib/appium_lib_core/common/base/driver.rb
|
255
241
|
- lib/appium_lib_core/common/base/http_default.rb
|
256
242
|
- lib/appium_lib_core/common/base/search_context.rb
|
257
|
-
- lib/appium_lib_core/common/base/wait.rb
|
258
243
|
- lib/appium_lib_core/common/command.rb
|
259
|
-
- lib/appium_lib_core/common/
|
244
|
+
- lib/appium_lib_core/common/command/common.rb
|
245
|
+
- lib/appium_lib_core/common/command/mjsonwp.rb
|
246
|
+
- lib/appium_lib_core/common/command/w3c.rb
|
260
247
|
- lib/appium_lib_core/common/error.rb
|
261
248
|
- lib/appium_lib_core/common/log.rb
|
262
249
|
- lib/appium_lib_core/common/logger.rb
|
250
|
+
- lib/appium_lib_core/common/wait.rb
|
251
|
+
- lib/appium_lib_core/common/wait/timer.rb
|
263
252
|
- lib/appium_lib_core/common/ws/websocket.rb
|
253
|
+
- lib/appium_lib_core/device.rb
|
264
254
|
- lib/appium_lib_core/device/app_state.rb
|
265
255
|
- lib/appium_lib_core/device/clipboard_content_type.rb
|
256
|
+
- lib/appium_lib_core/device/image_comparison.rb
|
266
257
|
- lib/appium_lib_core/device/multi_touch.rb
|
267
258
|
- lib/appium_lib_core/device/screen_record.rb
|
268
259
|
- lib/appium_lib_core/device/touch_actions.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# rubocop:disable Lint/HandleExceptions
|
2
|
-
module Appium
|
3
|
-
module Core
|
4
|
-
class Base
|
5
|
-
class Wait < ::Selenium::WebDriver::Wait
|
6
|
-
require 'timeout' # for wait
|
7
|
-
|
8
|
-
def initialize(opts = {})
|
9
|
-
valid_keys = %i(timeout interval message ignore return_if_true)
|
10
|
-
invalid_keys = []
|
11
|
-
opts.each_key { |key| invalid_keys << key unless valid_keys.include?(key) }
|
12
|
-
# [:one, :two] => :one, :two
|
13
|
-
unless invalid_keys.empty?
|
14
|
-
raise "Invalid keys #{invalid_keys.to_s[1..-2]}. Valid keys are #{valid_keys.to_s[1..-2]}"
|
15
|
-
end
|
16
|
-
|
17
|
-
@timeout = opts.fetch(:timeout, DEFAULT_TIMEOUT)
|
18
|
-
@interval = opts.fetch(:interval, DEFAULT_INTERVAL)
|
19
|
-
@message = opts[:message]
|
20
|
-
@ignored = Array(opts[:ignore] || ::Exception)
|
21
|
-
@return_if_true = opts[:return_if_true]
|
22
|
-
|
23
|
-
super(timeout: @timeout, interval: @interval, message: @message, ignore: @ignored)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Wait code from the selenium Ruby gem
|
27
|
-
# https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
|
28
|
-
def until
|
29
|
-
end_time = Time.now + @timeout
|
30
|
-
last_error = nil
|
31
|
-
|
32
|
-
until Time.now > end_time
|
33
|
-
begin
|
34
|
-
return yield unless @return_if_true
|
35
|
-
|
36
|
-
result = yield
|
37
|
-
return result if result
|
38
|
-
rescue ::Errno::ECONNREFUSED => e
|
39
|
-
raise e
|
40
|
-
rescue *@ignored => last_error
|
41
|
-
# swallowed
|
42
|
-
end
|
43
|
-
|
44
|
-
sleep @interval
|
45
|
-
end
|
46
|
-
|
47
|
-
msg = @message ? @message.dup : "timed out after #{@timeout} seconds"
|
48
|
-
|
49
|
-
msg << " (#{last_error.message})" if last_error
|
50
|
-
|
51
|
-
raise Selenium::WebDriver::Error::TimeOutError, msg
|
52
|
-
end
|
53
|
-
end # module Wait
|
54
|
-
end # module Base
|
55
|
-
end # module Core
|
56
|
-
end # module Appium
|