appium_lib_core 8.0.2 → 9.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/appium_lib_core.gemspec +2 -2
- data/lib/appium_lib_core/common/base/bridge.rb +21 -75
- data/lib/appium_lib_core/common/base/capabilities.rb +14 -0
- data/lib/appium_lib_core/common/base/driver.rb +5 -3
- data/lib/appium_lib_core/common/base/http_default.rb +14 -7
- data/lib/appium_lib_core/common/base/search_context.rb +98 -168
- data/lib/appium_lib_core/driver.rb +17 -20
- data/lib/appium_lib_core/element.rb +2 -1
- data/lib/appium_lib_core/version.rb +2 -2
- data/lib/appium_lib_core.rb +5 -3
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8679d6bb72c4130e216ac363eaed0c49f13b6d8818db2cba0b44c8f5b9dcef7b
|
4
|
+
data.tar.gz: d226eeb911201d9afd12117bb73871afa95e4b0d7f3844f2df2e4efdb45563ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ad279ca22ef8f33ad8634fbfcaffaac1f6b16a8904a90f45a39427ed060c444fec5e1130589cac30fd35cdff74df0626e9768e9f1a819f49db678ab6044d2d3
|
7
|
+
data.tar.gz: 2f6c31b0718f6155db25a647832d68d370c4dee839fe962f5b99bc6f4d082bdc0d96fdde807f5e89192f814815e998752ab9f2fb772f0a01d823425c7b90ad09
|
data/CHANGELOG.md
CHANGED
@@ -10,6 +10,21 @@ Read `release_notes.md` for commit level details.
|
|
10
10
|
|
11
11
|
### Deprecations
|
12
12
|
|
13
|
+
## [9.1.0] - 2024-05-18
|
14
|
+
|
15
|
+
### Enhancements
|
16
|
+
- Require Selenium 4.21.0+
|
17
|
+
- Simplify internal code with Selenium 4.21.0. Now it requires selenium webdriver v4.21.0.
|
18
|
+
|
19
|
+
## [9.0.0] - 2024-05-14
|
20
|
+
|
21
|
+
### Deprecations
|
22
|
+
- Stop converting snake cases to camel case for symbols in capabilities
|
23
|
+
- Please define camel/snake cases in capabilities as-is for the WebDriver capabilities
|
24
|
+
- Stop implicit symbolizing capabilities
|
25
|
+
- Historically ruby_lib/ruby_lib_core symbolized capabilities keys from string internally.
|
26
|
+
- `Appium.symbolize_keys` to build symbolized capabilities if issues occurred by this change.
|
27
|
+
|
13
28
|
## [8.0.2] - 2024-04-26
|
14
29
|
|
15
30
|
### Enhancements
|
data/appium_lib_core.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
|
-
spec.add_runtime_dependency 'selenium-webdriver', '~> 4.
|
25
|
+
spec.add_runtime_dependency 'selenium-webdriver', '~> 4.21'
|
26
26
|
spec.add_runtime_dependency 'faye-websocket', '~> 0.11.0'
|
27
27
|
|
28
28
|
spec.add_development_dependency 'rake', '~> 13.0'
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
31
31
|
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
32
32
|
spec.add_development_dependency 'webmock', '~> 3.23.0'
|
33
|
-
spec.add_development_dependency 'rubocop', '1.63.
|
33
|
+
spec.add_development_dependency 'rubocop', '1.63.5'
|
34
34
|
spec.add_development_dependency 'appium_thor', '~> 2.0'
|
35
35
|
spec.add_development_dependency 'parallel_tests'
|
36
36
|
spec.add_development_dependency 'simplecov'
|
@@ -15,6 +15,12 @@
|
|
15
15
|
module Appium
|
16
16
|
module Core
|
17
17
|
class Base
|
18
|
+
class LocatorConverter
|
19
|
+
def convert(how, what)
|
20
|
+
[how, what]
|
21
|
+
end
|
22
|
+
end # LocatorConverter
|
23
|
+
|
18
24
|
class Bridge < ::Selenium::WebDriver::Remote::Bridge
|
19
25
|
include Device::DeviceLock
|
20
26
|
include Device::Keyboard
|
@@ -31,6 +37,8 @@ module Appium
|
|
31
37
|
include Device::ExecuteDriver
|
32
38
|
include Device::Orientation
|
33
39
|
|
40
|
+
Bridge.locator_converter = LocatorConverter.new
|
41
|
+
|
34
42
|
# Prefix for extra capability defined by W3C
|
35
43
|
APPIUM_PREFIX = 'appium:'
|
36
44
|
|
@@ -153,6 +161,18 @@ module Appium
|
|
153
161
|
public
|
154
162
|
|
155
163
|
# command for Appium 2.0.
|
164
|
+
|
165
|
+
# Example:
|
166
|
+
# driver.add_command(name: :available_contexts, method: :get, url: 'session/:session_id/contexts') do
|
167
|
+
# execute(:available_contexts, {}) || []
|
168
|
+
# end
|
169
|
+
# Then,
|
170
|
+
# driver.available_contexts #=> ["NATIVE_APP"]
|
171
|
+
|
172
|
+
# def add_command(method:, url:, name:, &block)
|
173
|
+
# Bridge.add_command name, method, url, &block
|
174
|
+
# end
|
175
|
+
|
156
176
|
def add_command(method:, url:, name:, &block)
|
157
177
|
::Appium::Logger.info "Overriding the method '#{name}' for '#{url}'" if @available_commands.key? name
|
158
178
|
|
@@ -162,7 +182,7 @@ module Appium
|
|
162
182
|
end
|
163
183
|
|
164
184
|
def commands(command)
|
165
|
-
@available_commands[command]
|
185
|
+
@available_commands[command] || Bridge.extra_commands[command]
|
166
186
|
end
|
167
187
|
|
168
188
|
def status
|
@@ -216,52 +236,8 @@ module Appium
|
|
216
236
|
end
|
217
237
|
|
218
238
|
# For Appium
|
219
|
-
# override
|
220
|
-
def active_element
|
221
|
-
::Appium::Core::Element.new self, element_id_from(execute(:get_active_element))
|
222
|
-
end
|
223
239
|
alias switch_to_active_element active_element
|
224
240
|
|
225
|
-
# For Appium
|
226
|
-
# override
|
227
|
-
def find_element_by(how, what, parent_ref = [])
|
228
|
-
how, what = convert_locator(how, what)
|
229
|
-
|
230
|
-
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
|
231
|
-
|
232
|
-
parent_type, parent_id = parent_ref
|
233
|
-
id = case parent_type
|
234
|
-
when :element
|
235
|
-
execute :find_child_element, { id: parent_id }, { using: how, value: what.to_s }
|
236
|
-
when :shadow_root
|
237
|
-
execute :find_shadow_child_element, { id: parent_id }, { using: how, value: what.to_s }
|
238
|
-
else
|
239
|
-
execute :find_element, {}, { using: how, value: what.to_s }
|
240
|
-
end
|
241
|
-
|
242
|
-
::Appium::Core::Element.new self, element_id_from(id)
|
243
|
-
end
|
244
|
-
|
245
|
-
# For Appium
|
246
|
-
# override
|
247
|
-
def find_elements_by(how, what, parent_ref = [])
|
248
|
-
how, what = convert_locator(how, what)
|
249
|
-
|
250
|
-
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
|
251
|
-
|
252
|
-
parent_type, parent_id = parent_ref
|
253
|
-
ids = case parent_type
|
254
|
-
when :element
|
255
|
-
execute :find_child_elements, { id: parent_id }, { using: how, value: what.to_s }
|
256
|
-
when :shadow_root
|
257
|
-
execute :find_shadow_child_elements, { id: parent_id }, { using: how, value: what.to_s }
|
258
|
-
else
|
259
|
-
execute :find_elements, {}, { using: how, value: what.to_s }
|
260
|
-
end
|
261
|
-
|
262
|
-
ids.map { |id| ::Appium::Core::Element.new self, element_id_from(id) }
|
263
|
-
end
|
264
|
-
|
265
241
|
# For Appium
|
266
242
|
# @param [Hash] id The id which can get as a response from server
|
267
243
|
# @return [::Appium::Core::Element]
|
@@ -370,36 +346,6 @@ module Appium
|
|
370
346
|
arg
|
371
347
|
end
|
372
348
|
end
|
373
|
-
|
374
|
-
def element_id_from(id)
|
375
|
-
id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
|
376
|
-
end
|
377
|
-
|
378
|
-
# Don't convert locators for Appium in native context
|
379
|
-
def convert_locator(how, what)
|
380
|
-
# case how
|
381
|
-
# when 'class name'
|
382
|
-
# how = 'css selector'
|
383
|
-
# what = ".#{escape_css(what)}"
|
384
|
-
# when 'id'
|
385
|
-
# how = 'css selector'
|
386
|
-
# what = "##{escape_css(what)}"
|
387
|
-
# when 'name'
|
388
|
-
# how = 'css selector'
|
389
|
-
# what = "*[name='#{escape_css(what)}']"
|
390
|
-
# when 'tag name'
|
391
|
-
# how = 'css selector'
|
392
|
-
# end
|
393
|
-
#
|
394
|
-
# if what.is_a?(Hash)
|
395
|
-
# what = what.each_with_object({}) do |(h, w), hash|
|
396
|
-
# h, w = convert_locator(h.to_s, w)
|
397
|
-
# hash[h] = w
|
398
|
-
# end
|
399
|
-
# end
|
400
|
-
|
401
|
-
[how, what]
|
402
|
-
end
|
403
349
|
end # class Bridge
|
404
350
|
end # class Base
|
405
351
|
end # module Core
|
@@ -23,6 +23,20 @@ module Appium
|
|
23
23
|
# Appium's capabilities could change by depending on Appium versions. So it does not have
|
24
24
|
# standard options like chrome and firefox etc. So, the implementation should differ from
|
25
25
|
# other browsers. But here should inherit `Options` to follow Selenium.
|
26
|
+
|
27
|
+
# Method override
|
28
|
+
# FIXME: when we drop "symbolize_keys", this can be removed.
|
29
|
+
def convert_key(key)
|
30
|
+
case key
|
31
|
+
when String
|
32
|
+
key.to_s
|
33
|
+
when Symbol
|
34
|
+
# here do not convert to camel case
|
35
|
+
key.to_s
|
36
|
+
else
|
37
|
+
raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
|
38
|
+
end
|
39
|
+
end
|
26
40
|
end
|
27
41
|
end
|
28
42
|
end
|
@@ -32,7 +32,6 @@ module Appium
|
|
32
32
|
include ::Selenium::WebDriver::DriverExtensions::HasWebStorage
|
33
33
|
|
34
34
|
include ::Appium::Core::Base::Rotatable
|
35
|
-
include ::Appium::Core::Base::SearchContext
|
36
35
|
include ::Appium::Core::Base::TakesScreenshot
|
37
36
|
include ::Appium::Core::Base::HasRemoteStatus
|
38
37
|
include ::Appium::Core::Base::HasLocation
|
@@ -40,6 +39,8 @@ module Appium
|
|
40
39
|
|
41
40
|
include ::Appium::Core::Waitable
|
42
41
|
|
42
|
+
::Selenium::WebDriver::SearchContext.extra_finders = APPIUM_EXTRA_FINDERS
|
43
|
+
|
43
44
|
# Private API.
|
44
45
|
# Do not use this for general use. Used by flutter driver to get bridge for creating a new element
|
45
46
|
attr_reader :bridge
|
@@ -57,6 +58,7 @@ module Appium
|
|
57
58
|
@bidi = nil
|
58
59
|
|
59
60
|
# in the selenium webdriver as well
|
61
|
+
::Selenium::WebDriver::Remote::Bridge.element_class = ::Appium::Core::Element
|
60
62
|
bridge ||= create_bridge(**opts)
|
61
63
|
add_extensions(bridge.browser)
|
62
64
|
@bridge = listener ? ::Appium::Support::EventFiringBridge.new(bridge, listener, **original_opts) : bridge
|
@@ -1023,8 +1025,8 @@ module Appium
|
|
1023
1025
|
# ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
|
1024
1026
|
# ele.rect #=> Can get the rect of the element
|
1025
1027
|
#
|
1026
|
-
def convert_to_element(
|
1027
|
-
@bridge.convert_to_element
|
1028
|
+
def convert_to_element(response_id)
|
1029
|
+
@bridge.convert_to_element response_id
|
1028
1030
|
end
|
1029
1031
|
end # class Driver
|
1030
1032
|
end # class Base
|
@@ -31,6 +31,9 @@ module Appium
|
|
31
31
|
class Default < ::Selenium::WebDriver::Remote::Http::Default
|
32
32
|
attr_reader :additional_headers
|
33
33
|
|
34
|
+
::Selenium::WebDriver::Remote::Http::Common.user_agent = \
|
35
|
+
"appium/ruby_lib_core/#{VERSION} (#{::Selenium::WebDriver::Remote::Http::Common.user_agent})"
|
36
|
+
|
34
37
|
# override
|
35
38
|
def initialize(open_timeout: nil, read_timeout: nil)
|
36
39
|
@open_timeout = open_timeout
|
@@ -39,6 +42,17 @@ module Appium
|
|
39
42
|
super
|
40
43
|
end
|
41
44
|
|
45
|
+
def set_additional_header(key, value)
|
46
|
+
@additional_headers[key] = value
|
47
|
+
::Selenium::WebDriver::Remote::Http::Common.extra_headers = @additional_headers
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete_additional_header(key)
|
51
|
+
@additional_headers.delete key
|
52
|
+
::Selenium::WebDriver::Remote::Http::Common.extra_headers = @additional_headers
|
53
|
+
@common_headers.delete key if defined? @common_headers
|
54
|
+
end
|
55
|
+
|
42
56
|
# Update <code>server_url</code> provided when ruby_lib _core created a default http client.
|
43
57
|
# Set <code>@http</code> as nil to re-create http client for the <code>server_url</code>
|
44
58
|
#
|
@@ -59,13 +73,6 @@ module Appium
|
|
59
73
|
@server_url = URI.parse "#{scheme}://#{host}:#{port}#{path}"
|
60
74
|
end
|
61
75
|
|
62
|
-
def request(verb, url, headers, payload, redirects = 0)
|
63
|
-
headers['User-Agent'] = "appium/ruby_lib_core/#{VERSION} (#{headers['User-Agent']})"
|
64
|
-
headers = headers.merge @additional_headers unless @additional_headers.empty?
|
65
|
-
|
66
|
-
super(verb, url, headers, payload, redirects)
|
67
|
-
end
|
68
|
-
|
69
76
|
private
|
70
77
|
|
71
78
|
def validate_url_param(scheme, host, port, path)
|
@@ -12,172 +12,102 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# @driver.find_elements :predicate, 'wdValue == "SearchBar" AND isWDDivisible == 1'
|
101
|
-
#
|
102
|
-
# ## With Class Chain
|
103
|
-
# ### select the third child button of the first child window element
|
104
|
-
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]'
|
105
|
-
# ### select all the children windows
|
106
|
-
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow'
|
107
|
-
# ### select the second last child of the second child window
|
108
|
-
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]'
|
109
|
-
# ### matching predicate. <code>'</code> is the mark.
|
110
|
-
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow['visible = 1]['name = "bla"']'
|
111
|
-
# ### containing predicate. '$' is the mark.
|
112
|
-
# ### Require appium-xcuitest-driver 2.54.0+. PR: https://github.com/facebook/WebDriverAgent/pull/707/files
|
113
|
-
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow[$name = \"bla$$$bla\"$]'
|
114
|
-
# e = find_element :class_chain, "**/XCUIElementTypeWindow[$name == 'Buttons'$]"
|
115
|
-
# e.tag_name #=> "XCUIElementTypeWindow"
|
116
|
-
# e = find_element :class_chain, "**/XCUIElementTypeStaticText[$name == 'Buttons'$]"
|
117
|
-
# e.tag_name #=> "XCUIElementTypeStaticText"
|
118
|
-
#
|
119
|
-
# rubocop:enable Layout/LineLength
|
120
|
-
def find_element(*args)
|
121
|
-
how, what = extract_args(args)
|
122
|
-
by = _set_by_from_finders(how)
|
123
|
-
begin
|
124
|
-
bridge.find_element_by by, what.to_s, ref
|
125
|
-
rescue Selenium::WebDriver::Error::TimeoutError
|
126
|
-
raise Selenium::WebDriver::Error::NoSuchElementError
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
#
|
131
|
-
# Find all elements matching the given arguments
|
132
|
-
#
|
133
|
-
# @return [Array<Selenium::WebDriver::Element>]
|
134
|
-
#
|
135
|
-
# @see SearchContext#find_elements
|
136
|
-
#
|
137
|
-
def find_elements(*args)
|
138
|
-
how, what = extract_args(args)
|
139
|
-
by = _set_by_from_finders(how)
|
140
|
-
begin
|
141
|
-
bridge.find_elements_by by, what.to_s, ref
|
142
|
-
rescue Selenium::WebDriver::Error::TimeoutError
|
143
|
-
[]
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
private
|
148
|
-
|
149
|
-
def _set_by_from_finders(how)
|
150
|
-
by = FINDERS[how.to_sym]
|
151
|
-
unless by
|
152
|
-
raise ::Appium::Core::Error::ArgumentError,
|
153
|
-
"cannot find element by #{how.inspect}. Available finders are #{FINDERS.keys}."
|
154
|
-
end
|
155
|
-
|
156
|
-
by
|
157
|
-
end
|
158
|
-
|
159
|
-
def extract_args(args)
|
160
|
-
case args.size
|
161
|
-
when 2
|
162
|
-
args
|
163
|
-
when 1
|
164
|
-
arg = args.first
|
165
|
-
|
166
|
-
unless arg.respond_to?(:shift)
|
167
|
-
raise ::Appium::Core::Error::ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
|
168
|
-
end
|
169
|
-
|
170
|
-
# this will be a single-entry hash, so use #shift over #first or #[]
|
171
|
-
arr = arg.dup.shift
|
172
|
-
|
173
|
-
raise ::Appium::Core::Error::ArgumentError, "expected #{arr.inspect} to have 2 elements" unless arr.size == 2
|
15
|
+
# rubocop:disable Layout/LineLength
|
16
|
+
#
|
17
|
+
# Find the first element matching the given arguments
|
18
|
+
#
|
19
|
+
# - Android can find with uiautomator like a {http://developer.android.com/tools/help/uiautomator/UiSelector.html UISelector}.
|
20
|
+
# - iOS can find with a {https://developer.apple.com/library/ios/documentation/ToolsLanguages/Reference/UIAWindowClassReference/UIAWindow/UIAWindow.html#//apple_ref/doc/uid/TP40009930 UIAutomation command}.
|
21
|
+
# - iOS, only for XCUITest(WebDriverAgent), can find with a {https://github.com/facebook/WebDriverAgent/wiki/Queries class chain}
|
22
|
+
#
|
23
|
+
# == Find with image
|
24
|
+
# Return an element if current view has a partial image. The logic depends on template matching by OpenCV.
|
25
|
+
# {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
|
26
|
+
#
|
27
|
+
# You can handle settings for the comparision following {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 here}
|
28
|
+
#
|
29
|
+
# == Espresso viewmatcher and datamatcher
|
30
|
+
# Espresso has {https://developer.android.com/training/testing/espresso/basics _onView_ matcher}
|
31
|
+
# and {https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf _onData_ matcher} for more reference
|
32
|
+
# that allows you to target adapters instead of Views. This method find methods based on reflections
|
33
|
+
#
|
34
|
+
# This is a selector strategy that allows users to pass a selector of the form:
|
35
|
+
#
|
36
|
+
# <code>{ name: '<name>', args: ['arg1', 'arg2', '...'], class: '<optional class>' }</code>
|
37
|
+
#
|
38
|
+
# - _name_: The name of a method to invoke. The method must return
|
39
|
+
# a Hamcrest {http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html Matcher}
|
40
|
+
# - _args_: The args provided to the method
|
41
|
+
# - _class_: The class name that the method is part of (defaults to <code>org.hamcrest.Matchers</code>).
|
42
|
+
# Can be fully qualified, or simple, and simple defaults to <code>androidx.test.espresso.matcher</code> package
|
43
|
+
# (e.g.: <code>class=CursorMatchers</code> fully qualified is <code>class=androidx.test.espresso.matcher.CursorMatchers</code>
|
44
|
+
#
|
45
|
+
# See example how to send viewmatcher and datamatcher in Ruby client
|
46
|
+
#
|
47
|
+
#
|
48
|
+
# @overload find_element(how, what)
|
49
|
+
# @param [Symbol, String] how The method to find the element by
|
50
|
+
# @param [String] what The locator to use
|
51
|
+
#
|
52
|
+
# @overload find_element(opts)
|
53
|
+
# @param [Hash] opts Find options
|
54
|
+
# @option opts [Symbol] :how Key named after the method to find the element by, containing the locator
|
55
|
+
# @return [Element]
|
56
|
+
# @raise [Error::NoSuchElementError] if the element doesn't exist
|
57
|
+
#
|
58
|
+
# @example Find element with each keys
|
59
|
+
#
|
60
|
+
# # with accessibility id. All platforms.
|
61
|
+
# @driver.find_elements :accessibility_id, 'Animation'
|
62
|
+
# @driver.find_elements :accessibility_id, 'Animation'
|
63
|
+
#
|
64
|
+
# # with base64 encoded template image. All platforms.
|
65
|
+
# @driver.find_elements :image, Base64.strict_encode64(File.read(file_path))
|
66
|
+
#
|
67
|
+
# # For Android
|
68
|
+
# ## With uiautomator
|
69
|
+
# @driver.find_elements :uiautomator, 'new UiSelector().clickable(true)'
|
70
|
+
# ## With viewtag, but only for Espresso
|
71
|
+
# ## 'setTag'/'getTag' in https://developer.android.com/reference/android/view/View
|
72
|
+
# @driver.find_elements :viewtag, 'new UiSelector().clickable(true)'
|
73
|
+
# # With data_matcher. The argument should be JSON format.
|
74
|
+
# @driver.find_elements :data_matcher, { name: 'hasEntry', args: %w(title Animation) }.to_json
|
75
|
+
#
|
76
|
+
# # For iOS
|
77
|
+
# ## With :predicate
|
78
|
+
# @driver.find_elements :predicate, "isWDVisible == 1"
|
79
|
+
# @driver.find_elements :predicate, 'wdName == "Buttons"'
|
80
|
+
# @driver.find_elements :predicate, 'wdValue == "SearchBar" AND isWDDivisible == 1'
|
81
|
+
#
|
82
|
+
# ## With Class Chain
|
83
|
+
# ### select the third child button of the first child window element
|
84
|
+
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]'
|
85
|
+
# ### select all the children windows
|
86
|
+
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow'
|
87
|
+
# ### select the second last child of the second child window
|
88
|
+
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]'
|
89
|
+
# ### matching predicate. <code>'</code> is the mark.
|
90
|
+
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow['visible = 1]['name = "bla"']'
|
91
|
+
# ### containing predicate. '$' is the mark.
|
92
|
+
# ### Require appium-xcuitest-driver 2.54.0+. PR: https://github.com/facebook/WebDriverAgent/pull/707/files
|
93
|
+
# @driver.find_elements :class_chain, 'XCUIElementTypeWindow[$name = \"bla$$$bla\"$]'
|
94
|
+
# e = find_element :class_chain, "**/XCUIElementTypeWindow[$name == 'Buttons'$]"
|
95
|
+
# e.tag_name #=> "XCUIElementTypeWindow"
|
96
|
+
# e = find_element :class_chain, "**/XCUIElementTypeStaticText[$name == 'Buttons'$]"
|
97
|
+
# e.tag_name #=> "XCUIElementTypeStaticText"
|
98
|
+
#
|
99
|
+
# rubocop:enable Layout/LineLength
|
174
100
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
101
|
+
APPIUM_EXTRA_FINDERS = {
|
102
|
+
accessibility_id: 'accessibility id',
|
103
|
+
image: '-image',
|
104
|
+
custom: '-custom',
|
105
|
+
# Android
|
106
|
+
uiautomator: '-android uiautomator', # Unavailable in Espresso
|
107
|
+
viewtag: '-android viewtag', # Available in Espresso
|
108
|
+
data_matcher: '-android datamatcher', # Available in Espresso
|
109
|
+
view_matcher: '-android viewmatcher', # Available in Espresso
|
110
|
+
# iOS
|
111
|
+
predicate: '-ios predicate string',
|
112
|
+
class_chain: '-ios class chain'
|
113
|
+
}.freeze
|
@@ -338,12 +338,6 @@ module Appium
|
|
338
338
|
def setup_for_new_session(opts = {})
|
339
339
|
@custom_url = opts.delete :url # to set the custom url as :url
|
340
340
|
|
341
|
-
# TODO: Remove when we implement Options
|
342
|
-
# The symbolize_keys is to keep compatiility for the legacy code, which allows capabilities to give 'string' as the key.
|
343
|
-
# The toplevel `caps`, `capabilities` and `appium_lib` are expected to be symbol.
|
344
|
-
# FIXME: First, please try to remove `nested: true` to `nested: false`.
|
345
|
-
opts = Appium.symbolize_keys(opts, nested: true)
|
346
|
-
|
347
341
|
@caps = get_caps(opts)
|
348
342
|
|
349
343
|
set_appium_lib_specific_values(get_appium_lib_opts(opts))
|
@@ -408,7 +402,7 @@ module Appium
|
|
408
402
|
|
409
403
|
if @enable_idempotency_header
|
410
404
|
if @http_client.instance_variable_defined? :@additional_headers
|
411
|
-
@http_client.
|
405
|
+
@http_client.set_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency], SecureRandom.uuid
|
412
406
|
else
|
413
407
|
::Appium::Logger.warn 'No additional_headers attribute in this http client instance'
|
414
408
|
end
|
@@ -432,7 +426,7 @@ module Appium
|
|
432
426
|
|
433
427
|
if @http_client.instance_variable_defined? :@additional_headers
|
434
428
|
# We only need the key for a new session request. Should remove it for other following commands.
|
435
|
-
@http_client.
|
429
|
+
@http_client.delete_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
|
436
430
|
end
|
437
431
|
|
438
432
|
# TODO: this method can be removed after releasing Appium 2.0, and after a while
|
@@ -635,22 +629,27 @@ module Appium
|
|
635
629
|
opts[:appium_lib] || {}
|
636
630
|
end
|
637
631
|
|
632
|
+
# @private
|
633
|
+
def get_app
|
634
|
+
@caps[:app] || @caps['app']
|
635
|
+
end
|
636
|
+
|
638
637
|
# @private
|
639
638
|
# Path to the .apk, .app or .app.zip.
|
640
639
|
# The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
|
641
640
|
# Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
|
642
641
|
def set_app_path
|
643
642
|
# FIXME: maybe `:app` should check `app` as well.
|
644
|
-
return unless @caps &&
|
645
|
-
return if
|
646
|
-
|
647
|
-
app_path = File.expand_path(
|
648
|
-
@caps[
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
643
|
+
return unless @caps && get_app && !get_app.empty?
|
644
|
+
return if get_app =~ URI::DEFAULT_PARSER.make_regexp
|
645
|
+
|
646
|
+
app_path = File.expand_path(get_app)
|
647
|
+
@caps['app'] = if File.exist? app_path
|
648
|
+
app_path
|
649
|
+
else
|
650
|
+
::Appium::Logger.warn("Use #{get_app} directly since #{app_path} does not exist.")
|
651
|
+
get_app
|
652
|
+
end
|
654
653
|
end
|
655
654
|
|
656
655
|
# @private
|
@@ -676,7 +675,6 @@ module Appium
|
|
676
675
|
# @private
|
677
676
|
def set_appium_device
|
678
677
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
679
|
-
# TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
|
680
678
|
@device = @caps[:platformName] || @caps['platformName']
|
681
679
|
return @device unless @device
|
682
680
|
|
@@ -685,7 +683,6 @@ module Appium
|
|
685
683
|
|
686
684
|
# @private
|
687
685
|
def set_automation_name
|
688
|
-
# TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
|
689
686
|
candidate = @caps[:automationName] || @caps['automationName']
|
690
687
|
@automation_name = candidate if candidate
|
691
688
|
@automation_name = convert_downcase @automation_name if @automation_name
|
@@ -17,9 +17,10 @@ module Appium
|
|
17
17
|
# Implement useful features for element.
|
18
18
|
# Patch for Selenium Webdriver.
|
19
19
|
class Element < ::Selenium::WebDriver::Element
|
20
|
-
include ::Appium::Core::Base::SearchContext
|
21
20
|
include ::Appium::Core::Base::TakesScreenshot
|
22
21
|
|
22
|
+
::Selenium::WebDriver::SearchContext.extra_finders = APPIUM_EXTRA_FINDERS
|
23
|
+
|
23
24
|
# Retuns the element id.
|
24
25
|
#
|
25
26
|
# @return [String]
|
@@ -14,7 +14,7 @@
|
|
14
14
|
|
15
15
|
module Appium
|
16
16
|
module Core
|
17
|
-
VERSION = '
|
18
|
-
DATE = '2024-
|
17
|
+
VERSION = '9.1.0' unless defined? ::Appium::Core::VERSION
|
18
|
+
DATE = '2024-05-18' unless defined? ::Appium::Core::DATE
|
19
19
|
end
|
20
20
|
end
|
data/lib/appium_lib_core.rb
CHANGED
@@ -22,15 +22,17 @@ require_relative 'appium_lib_core/element'
|
|
22
22
|
require_relative 'appium_lib_core/support/event_firing_bridge'
|
23
23
|
|
24
24
|
module Appium
|
25
|
-
# @private
|
26
|
-
#
|
27
25
|
# convert the top level keys to symbols.
|
28
26
|
#
|
29
27
|
# @param [Hash] hash Hash value to make symbolise
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# opts = Appium.symbolize_keys(opts)
|
31
|
+
#
|
30
32
|
def self.symbolize_keys(hash, nested: false, enable_deprecation_msg: true)
|
31
33
|
# FIXME: As https://github.com/appium/ruby_lib/issues/945, we must remove this implicit string to symbol.
|
32
34
|
# But appium_lib_core's some capability handling expect to be symbol, so we should test to remove
|
33
|
-
# the
|
35
|
+
# the methods which expect the symbol first.
|
34
36
|
raise ::Appium::Core::Error::ArgumentError, 'symbolize_keys requires a hash' unless hash.is_a? Hash
|
35
37
|
|
36
38
|
hash.each_with_object({}) do |pair, acc|
|
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:
|
4
|
+
version: 9.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kazuaki MATSUO
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '4.
|
19
|
+
version: '4.21'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '4.
|
26
|
+
version: '4.21'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: faye-websocket
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - '='
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.63.
|
117
|
+
version: 1.63.5
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - '='
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.63.
|
124
|
+
version: 1.63.5
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: appium_thor
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|