appium_lib_core 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|