-
-
Notifications
You must be signed in to change notification settings - Fork 160
/
driver.rb
896 lines (782 loc) · 27.3 KB
/
driver.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
# frozen_string_literal: true
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Load only Minitest is loaded
if defined?(Minitest::VERSION)
# Fix uninitialized constant Minitest (NameError)
module Minitest
# Fix superclass mismatch for class Spec
class Runnable
end
begin
class Test < Runnable
end
rescue TypeError => te
# http://docs.seattlerb.org/minitest/History_rdoc.html#label-5.11.0+-2F+2018-01-01
# for 5.11.0/5.11.1
# `Minitest::Test` became a subclass of `Minitest::Result`
raise TypeError, te.message unless te.message == 'superclass mismatch for class Test'
class Test < Result
end
end
end
end
require 'appium_lib_core'
require 'uri'
module Appium
class Driver
# @private
class << self
def convert_to_symbol(value)
if value.nil?
value
else
value.to_sym
end
end
# @private
def get_cap(caps, name)
name_with_prefix = "#{::Appium::Core::Base::Bridge::APPIUM_PREFIX}#{name}"
caps[convert_to_symbol name] ||
caps[name] ||
caps[convert_to_symbol name_with_prefix] ||
caps[name_with_prefix]
end
end
# attr readers are promoted to global scope. To avoid clobbering, they're
# made available via the driver_attributes method
#
# attr_accessor is repeated for each one so YARD documents them properly.
# The amount to sleep in seconds before every webdriver http call.
attr_accessor :global_webdriver_http_sleep
# SauceLab's settings
attr_reader :sauce
# Username for use on Sauce Labs. Set `false` to disable Sauce, even when SAUCE_USERNAME is in ENV.
# same as @sauce.username
attr_reader :sauce_username
# Access Key for use on Sauce Labs. Set `false` to disable Sauce, even when SAUCE_ACCESS_KEY is in ENV.
# same as @sauce.access_key
attr_reader :sauce_access_key
# Override the Sauce Appium endpoint to allow e.g. TestObject tests
# same as @sauce.endpoint
attr_reader :sauce_endpoint
# from Core
# read http://www.rubydoc.info/github/appium/ruby_lib_core/Appium/Core/Driver
attr_reader :caps
attr_reader :custom_url
attr_reader :default_wait
attr_reader :appium_port
attr_reader :appium_device
attr_reader :automation_name
attr_reader :listener
attr_reader :http_client
attr_reader :appium_wait_timeout
attr_reader :appium_wait_interval
# Appium's server version
attr_reader :appium_server_status
# Boolean debug mode for the Appium Ruby bindings
attr_reader :appium_debug
# Returns the driver
# @return [Driver] the driver
attr_reader :driver
# Instance of Appium::Core::Driver
attr_reader :core
# Creates a new driver. The driver is defined as global scope by default.
# We can avoid defining global driver.
#
# @example
#
# require 'rubygems'
# require 'appium_lib'
#
# # platformName takes a string or a symbol.
# # Start iOS driver with global scope
# opts = {
# caps: {
# platformName: :ios,
# app: '/path/to/MyiOS.app'
# },
# appium_lib: {
# server_url: 'http://127.0.0.1:4723'
# wait_timeout: 30
# }
# }
# appium_driver = Appium::Driver.new(opts, true)
# appium_driver.start_driver
#
# # Start Android driver with global scope
# opts = {
# caps: {
# platformName: :android,
# app: '/path/to/my.apk'
# },
# appium_lib: {
# wait_timeout: 30,
# wait_interval: 1
# }
# }
# appium_driver = Appium::Driver.new(opts, true)
# appium_driver.start_driver
#
# # Start iOS driver without global scope
# opts = {
# caps: {
# platformName: :ios,
# app: '/path/to/MyiOS.app'
# },
# appium_lib: {
# wait_timeout: 30
# }
# }
# appium_driver = Appium::Driver.new(opts, false)
# appium_driver.start_driver
#
# # Start iOS driver without global scope
# opts = {
# caps: {
# platformName: :ios,
# app: '/path/to/MyiOS.app'
# },
# appium_lib: {
# wait_timeout: 30
# },
# global_driver: false
# }
# appium_driver = Appium::Driver.new(opts)
# appium_driver.start_driver
#
# @param opts [Object] A hash containing various options.
# @param global_driver [Bool] A bool require global driver before initialize.
# @return [Driver]
def initialize(opts = {}, global_driver = false)
# Capybara can't put `global_driver` as the 2nd argument.
global_driver = opts.delete :global_driver if global_driver.nil?
$driver&.driver_quit if global_driver
raise ArgumentError, 'opts must be a hash' unless opts.is_a? Hash
@core = ::Appium::Core.for(opts)
extend ::Appium::Core::Device
opts = Appium.symbolize_keys opts
appium_lib_opts = opts[:appium_lib] || {}
@caps = @core.caps
@custom_url = @core.custom_url
@default_wait = @core.default_wait || 0
@appium_port = @core.port
@appium_wait_timeout = @core.wait_timeout
@appium_wait_interval = @core.wait_interval
@listener = @core.listener
@appium_device = @core.device
@automation_name = @core.automation_name
# Arrange the app capability. This must be after @core = ::Appium::Core.for(opts)
set_app_path(opts)
# enable debug patch
@appium_debug = appium_lib_opts.fetch :debug, !!defined?(Pry) # rubocop:disable Style/DoubleNegation
set_sauce_related_values(appium_lib_opts)
# Extend Common methods
extend Appium::Common
extend Appium::Device
# Extend each driver's methods
extend_for(device: @core.device, automation_name: @core.automation_name)
# for command
if @appium_debug
Appium::Logger.debug opts unless opts.empty?
Appium::Logger.debug "Debug is: #{@appium_debug}"
Appium::Logger.debug "Device is: #{@core.device}"
end
# Save global reference to last created Appium driver for top level methods.
$driver = self if global_driver
self # rubocop:disable Lint/Void # return newly created driver
end
private
# @private
def extend_for(device:, automation_name:)
case device
when :android
case automation_name
when :uiautomator2
::Appium::Android::Uiautomator2::Bridge.for(self)
when :espresso
::Appium::Android::Espresso::Bridge.for(self)
else # default and UiAutomator
::Appium::Android::Bridge.for(self)
end
when :ios, :tvos
# default and XCUITest
::Appium::Ios::Xcuitest::Bridge.for(self)
when :mac
# no Mac specific extentions
Appium::Logger.debug('mac')
when :windows
# no windows specific extentions
Appium::Logger.debug('windows')
when :tizen
# https://github.com/Samsung/appium-tizen-driver
Appium::Logger.debug('tizen')
when :youiengine
# https://github.com/YOU-i-Labs/appium-youiengine-driver
Appium::Logger.debug('YouiEngine')
else
case automation_name
when :youiengine
# https://github.com/YOU-i-Labs/appium-youiengine-driver
Appium::Logger.debug('YouiEngine')
else
Appium::Logger.debug('no device matched') # core also shows warning message
end
end
end
# @private
# Deprecated. TODO: remove
def set_app_path(opts)
return unless @core.caps
# return the path exists on the local
app_path = Driver.get_cap(@core.caps, 'app')
return if app_path.nil?
return if File.exist?(app_path)
@core.caps['app'] = self.class.absolute_app_path opts
end
# @private
def set_sauce_related_values(appium_lib_opts)
@sauce = Appium::SauceLabs.new(appium_lib_opts)
@sauce_username = @sauce.username
@sauce_access_key = @sauce.access_key
@sauce_endpoint = @sauce.endpoint
end
public
# Returns a hash of the driver attributes
def driver_attributes
{
caps: @core.caps,
automation_name: @core.automation_name,
custom_url: @core.custom_url,
default_wait: @default_wait,
sauce_username: @sauce.username,
sauce_access_key: @sauce.access_key,
sauce_endpoint: @sauce.endpoint,
port: @core.port,
device: @core.device,
debug: @appium_debug,
listener: @listener,
wait_timeout: @core.wait_timeout,
wait_interval: @core.wait_interval
}
end
def device_is_android?
@core.device == :android
end
def device_is_ios?
@core.device == :ios
end
def device_is_windows?
@core.device == :windows
end
# Return true if automationName is 'uiautomator2'
# @return [Boolean]
def automation_name_is_uiautomator2?
!@core.automation_name.nil? && @core.automation_name == :uiautomator2
end
# Return true if automationName is 'Espresso'
# @return [Boolean]
def automation_name_is_espresso?
!@core.automation_name.nil? && @core.automation_name == :espresso
end
# Return true if automationName is 'XCUITest'
# @return [Boolean]
def automation_name_is_xcuitest?
!@core.automation_name.nil? && @core.automation_name == :xcuitest
end
# An entry point to chain W3C actions
# Read https://www.rubydoc.info/github/appium/ruby_lib_core/Appium/Core/Base/Bridge/W3C#action-instance_method
#
# @return [Selenium::WebDriver::PointerActions]
#
# @example
#
# element = find_element(:id, "some id")
# action.click(element).perform # The `click` is a part of `PointerActions`
#
def action
@driver&.action
end
# Returns the server's version info
#
# @example
# {
# "build" => {
# "version" => "0.18.1",
# "revision" => "d242ebcfd92046a974347ccc3a28f0e898595198"
# }
# }
#
# @return [Hash]
def appium_server_version
@core.appium_server_version
rescue Selenium::WebDriver::Error::WebDriverError => ex
raise ::Appium::Core::Error::ServerError unless ex.message.include?('content-type=""')
# server (TestObject for instance) does not respond to status call
{}
end
alias remote_status appium_server_version
# Return the platform version as an array of integers
# @return [Array<Integer>]
def platform_version
return [] if @driver.nil?
p_version = @driver.capabilities['platformVersion']
p_version.split('.').map(&:to_i)
end
# Returns the client's version info
#
# @example
#
# {
# "version" => "9.1.1"
# }
#
# @return [Hash]
def appium_client_version
{ version: ::Appium::VERSION }
end
# [Deprecated] Converts app_path to an absolute path.
#
# opts is the full options hash (caps and appium_lib). If server_url is set
# then the app path is used as is.
#
# if app isn't set then an error is raised.
#
# @return [String] APP_PATH as an absolute path
def self.absolute_app_path(opts)
raise ArgumentError, 'opts must be a hash' unless opts.is_a? Hash
caps = opts[:caps] || opts['caps'] || {}
app_path = get_cap(caps, 'app')
raise ArgumentError, 'absolute_app_path invoked and app is not set!' if app_path.nil? || app_path.empty?
# Sauce storage API. http://saucelabs.com/docs/rest#storage
return app_path if app_path.start_with? 'sauce-storage:'
return app_path if app_path =~ URI::DEFAULT_PARSER.make_regexp # public URL for Sauce
::Appium::Logger.warn('[Deprecation] Converting the path to absolute path will be removed. ' \
'Please specify the full path which can be accessible from the appium server')
absolute_app_path = File.expand_path app_path
if File.exist? absolute_app_path
absolute_app_path
else
::Appium::Logger.info("Use #{app_path}")
app_path
end
end
# Get the server url
# @return [String] the server url
def server_url
return @core.custom_url if @core.custom_url
return @sauce.server_url if @sauce.sauce_server_url?
"http://127.0.0.1:#{@core.port}/wd/hub"
end
# Restarts the driver
# @return [Driver] the driver
def restart
driver_quit
start_driver
end
# Takes a png screenshot and saves to the target path.
#
# @example
#
# screenshot '/tmp/hi.png'
#
# @param png_save_path [String] the full path to save the png
# @return [File]
def screenshot(png_save_path)
@driver&.save_screenshot png_save_path
end
# Takes a png screenshot of particular element's area
#
# @example
#
# el = find_element :accessibility_id, zzz
# element_screenshot el, '/tmp/hi.png'
#
# @param [String] element Element take a screenshot
# @param [String] png_save_path the full path to save the png
# @return [File]
def element_screenshot(element, png_save_path)
@driver&.take_element_screenshot element, png_save_path
nil
end
# Quits the driver
# @return [void]
def driver_quit
@driver&.quit
@driver = nil
rescue Selenium::WebDriver::Error::WebDriverError
nil
end
alias quit_driver driver_quit
# Get the device window's size.
# @return [Selenium::WebDriver::Dimension]
#
# @example
#
# size = @driver.window_size
# size.width #=> Integer
# size.height #=> Integer
#
def window_size
# maybe exception is expected as no driver created
raise NoDriverInstanceError if @driver.nil?
@driver.window_size
end
# Get the device window's rect.
# @return [Selenium::WebDriver::Rectangle]
#
# @example
#
# size = @driver.window_size
# size.width #=> Integer
# size.height #=> Integer
# size.x #=> Integer
# size.y #=> Integer
#
def window_rect
raise NoDriverInstanceError if @driver.nil?
@driver.window_rect
end
# Creates a new global driver and quits the old one if it exists.
# You can customise http_client as the following
#
# Read http://www.rubydoc.info/github/appium/ruby_lib_core/Appium/Core/Device to understand more what the driver
# can call instance methods.
#
# @example
#
# require 'rubygems'
# require 'appium_lib'
#
# # platformName takes a string or a symbol.
# # Start iOS driver
# opts = {
# caps: {
# platformName: :ios,
# app: '/path/to/MyiOS.app'
# },
# appium_lib: {
# wait_timeout: 30
# }
# }
# appium_driver = Appium::Driver.new(opts) #=> return an Appium::Driver instance
# appium_driver.start_driver #=> return an Appium::Core::Base::Driver
#
# @option http_client_ops [Hash] :http_client Custom HTTP Client
# @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
# @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
# @return [Selenium::WebDriver] the new global driver
def start_driver(http_client_ops = { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
if http_client_ops[:http_client].nil?
http_client = ::Appium::Http::Default.new(open_timeout: http_client_ops[:open_timeout],
read_timeout: http_client_ops[:read_timeout])
end
# TODO: do not kill the previous session in the future version.
if $driver.nil?
driver_quit
else
$driver.driver_quit
end
# If automationName is set only in server side, then the following automation_name should be nil before
# starting driver.
automation_name = @core.automation_name
@driver = @core.start_driver(server_url: server_url,
http_client_ops: {
http_client: http_client,
open_timeout: 999_999,
read_timeout: 999_999
})
@http_client = @core.http_client
# if automation_name was nil before start_driver, then re-extend driver specific methods
# to be able to extend correctly.
extend_for(device: @core.device, automation_name: @core.automation_name) if automation_name.nil?
@appium_server_status = appium_server_version
@driver
end
# To ignore error for Espresso Driver
def set_implicit_wait(wait)
@driver.manage.timeouts.implicit_wait = wait
rescue Selenium::WebDriver::Error::UnknownError => e
unless e.message.include?('The operation requested is not yet implemented by Espresso driver')
raise ::Appium::Core::Error::ServerError
end
{}
end
# Set implicit wait to zero.
def no_wait
@driver&.manage&.timeouts&.implicit_wait = 0
end
# Set implicit wait. Default to @default_wait.
#
# @example
#
# set_wait 2
# set_wait # @default_wait
#
#
# @param timeout [Integer] the timeout in seconds
# @return [void]
def set_wait(timeout = nil)
timeout = @default_wait if timeout.nil?
@driver&.manage&.timeouts&.implicit_wait = timeout
end
# Returns existence of element.
#
# Example:
#
# exists { button('sign in') } ? puts('true') : puts('false')
#
# @param [Integer] pre_check The amount in seconds to set the
# wait to before checking existence
# @param [Integer] post_check The amount in seconds to set the
# wait to after checking existence
# @yield The block to call
# @return [Boolean]
def exists(pre_check = 0, post_check = @default_wait)
# do not uset set_wait here.
# it will cause problems with other methods reading the default_wait of 0
# which then gets converted to a 1 second wait.
@driver&.manage&.timeouts&.implicit_wait = pre_check
# the element exists unless an error is raised.
exists = true
begin
yield # search for element
rescue StandardError
exists = false # error means it's not there
end
# restore wait
@driver&.manage&.timeouts&.implicit_wait = post_check if post_check != pre_check
exists
end
# The same as @driver.execute_script
# @param [String] script The script to execute
# @param [*args] args The args to pass to the script
# @return [Object]
def execute_script(script, *args)
raise NoDriverInstanceError if @driver.nil?
@driver.execute_script script, *args
end
###
# Wrap calling selenium webdrier APIs via ruby_core
###
# Get the window handles of open browser windows
def execute_async_script(script, *args)
raise NoDriverInstanceError if @driver.nil?
@driver.execute_async_script script, *args
end
# Run a set of script against the current session, allowing execution of many commands in one Appium request.
# Supports {https://webdriver.io/docs/api.html WebdriverIO} API so far.
# Please read {http://appium.io/docs/en/commands/session/execute-driver command API} for more details
# about acceptable scripts and the output.
#
# @param [String] script The string consisting of the script itself
# @param [String] type The name of the script type.
# Defaults to 'webdriverio'. Depends on server implementation which type is supported.
# @param [Integer] timeout_ms The number of `ms` Appium should wait for the script to finish
# before killing it due to timeout.
#
# @return [Appium::Core::Base::Device::ExecuteDriver::Result] The script result parsed by
# Appium::Core::Base::Device::ExecuteDriver::Result.
#
# @raise [::Selenium::WebDriver::Error::UnknownError] If something error happens in the script.
# It has the original message.
#
# @example
# script = <<~SCRIPT
# const status = await driver.status();
# console.warn('warning message');
# return [status];
# SCRIPT
# r = @@driver.execute_driver(script: script, type: 'webdriverio', timeout: 10_000)
# r #=> An instance of Appium::Core::Base::Device::ExecuteDriver::Result
# r.result #=> The `result` key part as the result of the script
# r.logs #=> The `logs` key part as `{'log' => [], 'warn' => [], 'error' => []}`
#
def execute_driver(script: '', type: 'webdriverio', timeout_ms: nil)
raise NoDriverInstanceError if @driver.nil?
@driver.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
end
def window_handles
raise NoDriverInstanceError if @driver.nil?
@driver.window_handles
end
# Get the current window handle
def window_handle
raise NoDriverInstanceError if @driver.nil?
@driver.window_handle
end
def navigate
raise NoDriverInstanceError if @driver.nil?
@driver.navigate
end
def manage
raise NoDriverInstanceError if @driver.nil?
@driver.manage
end
def get(url)
raise NoDriverInstanceError if @driver.nil?
@driver.get(url)
end
def current_url
raise NoDriverInstanceError if @driver.nil?
@driver.current_url
end
def title
raise NoDriverInstanceError if @driver.nil?
@driver.title
end
# @return [TargetLocator]
# @see TargetLocator
def switch_to
raise NoDriverInstanceError if @driver.nil?
@driver.switch_to
end
###
# End core
###
# Calls @driver.find_elements_with_appium
#
# @example
#
# @driver = Appium::Driver.new(opts, false)
# @driver.start_driver
# @driver.find_elements :predicate, yyy
#
# If you call `Appium.promote_appium_methods`, you can call `find_elements` directly.
#
# @example
#
# @driver = Appium::Driver.new(opts, false)
# @driver.start_driver
# @driver.find_elements :predicate, yyy
#
# If you call `Appium.promote_appium_methods`, you can call `find_elements` directly.
#
# @param [*args] args The args to use
# @return [Array<Element>] Array is empty when no elements are found.
def find_elements(*args)
raise NoDriverInstanceError if @driver.nil?
@driver.find_elements(*args)
end
# Calls @driver.find_element
#
# @example
#
# @driver = Appium::Driver.new(opts, false)
# @driver.start_driver
# @driver.find_element :accessibility_id, zzz
#
# If you call `Appium.promote_appium_methods`, you can call `find_element` directly.
#
# @param [*args] args The args to use
# @return [Element]
def find_element(*args)
raise NoDriverInstanceError if @driver.nil?
@driver.find_element(*args)
end
# Return ImageElement if current view has a partial image
#
# @param [String] png_img_path A path to a partial image you'd like to find
#
# @return [::Appium::Core::ImageElement]
# @raise [::Appium::Core::Error::NoSuchElementError|::Appium::Core::Error::CoreError] No such element
#
# @example
#
# @driver.find_element_by_image './test/functional/data/test_element_image.png'
#
def find_element_by_image(png_img_path)
raise NoDriverInstanceError if @driver.nil?
@driver.find_element_by_image(png_img_path)
end
# Return ImageElement if current view has partial images
#
# @param [[String]] png_img_paths Paths to a partial image you'd like to find
#
# @return [[::Appium::Core::ImageElement]]
# @return [::Appium::Core::Error::CoreError]
#
# @example
#
# @driver.find_elements_by_image ['./test/functional/data/test_element_image.png']
#
def find_elements_by_image(png_img_paths)
raise NoDriverInstanceError if @driver.nil?
@driver.find_elements_by_image(png_img_paths)
end
# Calls @driver.set_location
#
# @note This method does not work on real devices.
#
# @param [Hash] opts consisting of:
# @option opts [Float] :latitude the latitude in degrees (required)
# @option opts [Float] :longitude the longitude in degees (required)
# @option opts [Float] :altitude the altitude, defaulting to 75
# @return [Selenium::WebDriver::Location] the location constructed by the selenium webdriver
def set_location(opts = {})
raise NoDriverInstanceError if @driver.nil?
latitude = opts.fetch(:latitude)
longitude = opts.fetch(:longitude)
altitude = opts.fetch(:altitude, 75)
@driver.set_location(latitude, longitude, altitude)
end
# @since Appium 1.16.0
#
# Logs a custom event. The event is available via {::Appium::Core::Events#get}.
#
# @param [String] vendor The vendor prefix for the event
# @param [String] event The name of event
# @return [nil]
#
# @example
#
# log_event vendor: 'appium', event: 'funEvent'
#
# log_event = { vendor: 'appium', event: 'anotherEvent' }
# log_events #=> {...., 'appium:funEvent' => [1572957315, 1572960305],
# # 'appium:anotherEvent' => 1572959315}
#
def log_event(vendor:, event:)
raise NoDriverInstanceError if @driver.nil?
@driver.logs.event vendor: vendor, event: event
end
def log_event=(log_event)
raise if @driver.nil?
unless log_event.is_a?(Hash)
raise ::Appium::Core::Error::ArgumentError('log_event should be Hash like { vendor: "appium", event: "funEvent"}')
end
@driver.logs.event vendor: log_event[:vendor], event: log_event[:event]
end
# @since Appium 1.16.0
# Returns events with filtering with 'type'. Defaults to all available events.
#
# @param [String] type The type of events to get
# @return [Hash]
#
# @example
#
# log_events #=> {}
# log_events #=> {'commands' => [{'cmd' => 123455, ....}], 'startTime' => 1572954894127, }
#
def log_events(type = nil)
raise NoDriverInstanceError if @driver.nil?
@driver.logs.events(type)
end
# Quit the driver and Pry.
# quit and exit are reserved by Pry.
# @return [void]
def x
driver_quit
exit # exit pry
end
end # class Driver
end # module Appium