appium_lib 6.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +28 -0
  4. data/.travis.yml +10 -0
  5. data/Rakefile +9 -1
  6. data/android_tests/Gemfile +1 -1
  7. data/android_tests/Rakefile +20 -13
  8. data/android_tests/lib/android/specs/android/element/alert.rb +1 -1
  9. data/android_tests/lib/android/specs/android/element/button.rb +2 -2
  10. data/android_tests/lib/android/specs/android/element/generic.rb +1 -2
  11. data/android_tests/lib/android/specs/android/element/text.rb +2 -3
  12. data/android_tests/lib/android/specs/android/element/textfield.rb +2 -2
  13. data/android_tests/lib/android/specs/android/helper.rb +5 -3
  14. data/android_tests/lib/android/specs/android/patch.rb +2 -2
  15. data/android_tests/lib/android/specs/common/device.rb +16 -9
  16. data/android_tests/lib/android/specs/common/device_touchaction.rb +5 -2
  17. data/android_tests/lib/android/specs/common/element/window.rb +1 -1
  18. data/android_tests/lib/android/specs/common/helper.rb +14 -15
  19. data/android_tests/lib/android/specs/common/patch.rb +11 -9
  20. data/android_tests/lib/android/specs/common/version.rb +3 -3
  21. data/android_tests/lib/android/specs/common/web_context.rb +2 -3
  22. data/android_tests/lib/android/specs/driver.rb +38 -29
  23. data/android_tests/lib/android/specs/install.rb +3 -3
  24. data/android_tests/lib/format.rb +6 -8
  25. data/android_tests/lib/run.rb +25 -17
  26. data/android_tests/readme.md +4 -2
  27. data/appium_lib.gemspec +13 -11
  28. data/contributing.md +1 -1
  29. data/docs/android_docs.md +358 -274
  30. data/docs/ios_docs.md +333 -270
  31. data/docs/migration.md +10 -0
  32. data/docs_gen/make_docs.rb +3 -1
  33. data/ios_tests/Gemfile +1 -1
  34. data/ios_tests/Rakefile +17 -10
  35. data/ios_tests/appium.txt +1 -1
  36. data/ios_tests/lib/common.rb +8 -4
  37. data/ios_tests/lib/format.rb +5 -7
  38. data/ios_tests/lib/ios/specs/common/element/window.rb +1 -1
  39. data/ios_tests/lib/ios/specs/common/helper.rb +40 -39
  40. data/ios_tests/lib/ios/specs/common/patch.rb +15 -11
  41. data/ios_tests/lib/ios/specs/common/version.rb +3 -3
  42. data/ios_tests/lib/ios/specs/common/web_context.rb +1 -2
  43. data/ios_tests/lib/ios/specs/device/device.rb +7 -7
  44. data/ios_tests/lib/ios/specs/device/multi_touch.rb +6 -8
  45. data/ios_tests/lib/ios/specs/device/touch_actions.rb +12 -12
  46. data/ios_tests/lib/ios/specs/driver.rb +23 -22
  47. data/ios_tests/lib/ios/specs/ios/element/alert.rb +6 -2
  48. data/ios_tests/lib/ios/specs/ios/element/button.rb +2 -2
  49. data/ios_tests/lib/ios/specs/ios/element/generic.rb +1 -1
  50. data/ios_tests/lib/ios/specs/ios/element/text.rb +4 -1
  51. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +6 -6
  52. data/ios_tests/lib/ios/specs/ios/helper.rb +5 -5
  53. data/ios_tests/lib/ios/specs/ios/patch.rb +2 -2
  54. data/ios_tests/lib/run.rb +1 -1
  55. data/ios_tests/readme.md +3 -3
  56. data/ios_tests/upload/sauce_storage.rb +8 -8
  57. data/ios_tests/upload/upload.rb +1 -1
  58. data/lib/appium_lib/android/client_xpath.rb +7 -7
  59. data/lib/appium_lib/android/element/alert.rb +2 -2
  60. data/lib/appium_lib/android/element/button.rb +16 -16
  61. data/lib/appium_lib/android/element/generic.rb +12 -13
  62. data/lib/appium_lib/android/element/text.rb +5 -5
  63. data/lib/appium_lib/android/element/textfield.rb +5 -5
  64. data/lib/appium_lib/android/helper.rb +82 -52
  65. data/lib/appium_lib/android/mobile_methods.rb +2 -2
  66. data/lib/appium_lib/android/patch.rb +3 -3
  67. data/lib/appium_lib/common/element/window.rb +1 -1
  68. data/lib/appium_lib/common/helper.rb +30 -35
  69. data/lib/appium_lib/common/patch.rb +22 -20
  70. data/lib/appium_lib/common/version.rb +3 -3
  71. data/lib/appium_lib/common/wait.rb +9 -10
  72. data/lib/appium_lib/device/device.rb +39 -33
  73. data/lib/appium_lib/device/multi_touch.rb +5 -7
  74. data/lib/appium_lib/device/touch_actions.rb +14 -15
  75. data/lib/appium_lib/driver.rb +97 -76
  76. data/lib/appium_lib/ios/element/alert.rb +1 -1
  77. data/lib/appium_lib/ios/element/button.rb +5 -5
  78. data/lib/appium_lib/ios/element/generic.rb +5 -6
  79. data/lib/appium_lib/ios/element/text.rb +5 -5
  80. data/lib/appium_lib/ios/element/textfield.rb +15 -15
  81. data/lib/appium_lib/ios/helper.rb +103 -90
  82. data/lib/appium_lib/ios/mobile_methods.rb +2 -2
  83. data/lib/appium_lib/ios/patch.rb +4 -4
  84. data/lib/appium_lib/logger.rb +7 -5
  85. data/lib/appium_lib/rails/duplicable.rb +3 -1
  86. data/readme.md +7 -1
  87. data/release_notes.md +152 -0
  88. metadata +28 -54
@@ -8,7 +8,7 @@ describe 'driver' do
8
8
  before_first
9
9
  end
10
10
 
11
- def is_sauce
11
+ def sauce?
12
12
  ENV['UPLOAD_FILE'] && ENV['SAUCE_USERNAME']
13
13
  end
14
14
 
@@ -20,7 +20,7 @@ describe 'driver' do
20
20
  t 'load_appium_txt' do
21
21
  # skip this test if we're using Sauce
22
22
  # the storage API doesn't have an on disk file
23
- skip if is_sauce
23
+ skip if sauce?
24
24
  appium_txt = File.expand_path(File.join(Dir.pwd, 'lib'))
25
25
  opts = Appium.load_appium_txt file: appium_txt, verbose: true
26
26
 
@@ -36,7 +36,7 @@ describe 'driver' do
36
36
  actual = driver_attributes
37
37
  actual[:caps][:app] = File.basename actual[:caps][:app]
38
38
  expected = { caps: { platformName: 'ios',
39
- platformVersion: '8.1',
39
+ platformVersion: '8.3',
40
40
  deviceName: 'iPhone Simulator',
41
41
  app: 'UICatalog.app' },
42
42
  custom_url: false,
@@ -82,7 +82,7 @@ describe 'driver' do
82
82
  t 'app_path attr' do
83
83
  apk_name = File.basename driver_attributes[:caps][:app]
84
84
 
85
- if is_sauce
85
+ if sauce?
86
86
  apk_name.must_equal 'sauce-storage:UICatalog6.1.app.zip'
87
87
  else
88
88
  apk_name.must_equal 'UICatalog.app'
@@ -92,7 +92,7 @@ describe 'driver' do
92
92
  # Only used for Sauce Labs
93
93
  t 'app_name attr' do
94
94
  name_attr = driver_attributes[:caps][:name]
95
- if is_sauce
95
+ if sauce?
96
96
  name_attr.must_equal 'appium_lib_ios'
97
97
  else
98
98
  name_attr.must_be_nil
@@ -101,7 +101,7 @@ describe 'driver' do
101
101
 
102
102
  t 'sauce_username attr' do
103
103
  sauce_username = driver_attributes[:sauce_username]
104
- if is_sauce
104
+ if sauce?
105
105
  sauce_username.must_equal 'appiumci'
106
106
  else
107
107
  sauce_username.must_be_nil
@@ -110,8 +110,8 @@ describe 'driver' do
110
110
 
111
111
  t 'sauce_access_key attr' do
112
112
  sauce_access_key = driver_attributes[:sauce_access_key]
113
- if is_sauce
114
- sauce_access_key.must_match /\h{8}-\h{4}-\h{4}-\h{4}-\h{12}/
113
+ if sauce?
114
+ sauce_access_key.must_match(/\h{8}-\h{4}-\h{4}-\h{4}-\h{12}/)
115
115
  else
116
116
  sauce_access_key.must_be_nil
117
117
  end
@@ -119,11 +119,6 @@ describe 'driver' do
119
119
  end
120
120
 
121
121
  describe 'Appium::Driver' do
122
- t '@@loaded' do
123
- loaded = $driver.class.class_variable_get :@@loaded
124
- loaded.must_equal true
125
- end
126
-
127
122
  t '$driver.class' do
128
123
  $driver.class.must_equal Appium::Driver
129
124
  end
@@ -136,10 +131,10 @@ describe 'driver' do
136
131
 
137
132
  t 'server_version' do
138
133
  server_version = appium_server_version['build']['version']
139
- if is_sauce
134
+ if sauce?
140
135
  server_version.must_match 'Sauce OnDemand'
141
136
  else
142
- server_version.must_match /(\d+)\.(\d+).(\d+)/
137
+ server_version.must_match(/(\d+)\.(\d+).(\d+)/)
143
138
  end
144
139
  end
145
140
 
@@ -152,12 +147,13 @@ describe 'driver' do
152
147
  driver.browser.must_equal :iOS
153
148
  end
154
149
 
155
- =begin
156
- Skip:
157
- screenshot # this is slow and already tested by Appium
158
- driver_quit # tested by restart
159
- start_driver # tested by restart
160
- =end
150
+ #
151
+ # Skip:
152
+ # screenshot # this is slow and already tested by Appium
153
+ # driver_quit # tested by restart
154
+ # start_driver # tested by restart
155
+ #
156
+
161
157
  t 'set_wait' do
162
158
  # fill the @last_waits array with: [30, 30]
163
159
  set_wait(30).must_equal(30)
@@ -186,7 +182,12 @@ describe 'driver' do
186
182
  # returns true unless an error is raised
187
183
  t 'exists' do
188
184
  exists(0, 0) { true }.must_equal true
189
- exists(0, 0) { raise 'error' }.must_equal false
185
+ exists(0, 0) { fail 'error' }.must_equal false
186
+ end
187
+
188
+ # simple integration sanity test to check for unexpected exceptions
189
+ t 'set_location' do
190
+ set_location latitude: 55, longitude: -72, altitude: 33
190
191
  end
191
192
 
192
193
  # any script
@@ -2,7 +2,11 @@
2
2
  describe 'ios/element/alert' do
3
3
  def nav_once
4
4
  screen.must_equal catalog
5
- wait_true { text('alerts').click; tag('UIANavigationBar').name == 'Alerts' } # wait for true
5
+ wait_true do
6
+ text('alerts').click
7
+ tag('UIANavigationBar').name == 'Alerts' # wait for true
8
+ end
9
+
6
10
  tag('UIANavigationBar').name.must_equal 'Alerts'
7
11
 
8
12
  # redefine method as no-op after it's invoked once
@@ -40,4 +44,4 @@ describe 'ios/element/alert' do
40
44
  t 'after_last' do
41
45
  after_last
42
46
  end
43
- end
47
+ end
@@ -30,7 +30,7 @@ describe 'ios/element/button' do
30
30
  t 'buttons' do
31
31
  exp = ['Back', 'Back', 'Gray', 'Right pointing arrow']
32
32
  target_buttons = buttons('a')
33
- target_buttons.map { |e| e.name }.must_equal exp
33
+ target_buttons.map(&:name).must_equal exp
34
34
  target_buttons.length.must_equal exp.length
35
35
  end
36
36
 
@@ -53,4 +53,4 @@ describe 'ios/element/button' do
53
53
  t 'after_last' do
54
54
  after_last
55
55
  end
56
- end
56
+ end
@@ -8,7 +8,7 @@ describe 'ios/element/generic' do
8
8
  'Buttons'
9
9
  end
10
10
 
11
- def verify element
11
+ def verify(element)
12
12
  element = element.first if element.is_a? Array
13
13
  element.name.must_equal uibutton_text
14
14
  end
@@ -1,3 +1,5 @@
1
+ # rubocop:disable Lint/HandleExceptions
2
+
1
3
  # rake ios['ios/element/text']
2
4
  describe 'ios/element/text' do
3
5
  def ui_catalog
@@ -34,7 +36,7 @@ describe 'ios/element/text' do
34
36
  t 'texts' do
35
37
  exp = ['Controls', 'Various uses of UIControl', 'Various uses of UISegmentedControl']
36
38
  texts.length.must_equal 24
37
- texts('trol').map { |e| e.name }.must_equal exp
39
+ texts('trol').map(&:name).must_equal exp
38
40
  texts('uses').length.must_equal 7
39
41
  end
40
42
 
@@ -44,6 +46,7 @@ describe 'ios/element/text' do
44
46
  act = begin
45
47
  text_exact 'mos'
46
48
  rescue
49
+ # nop
47
50
  end
48
51
  act.must_be_nil
49
52
  set_wait
@@ -27,14 +27,14 @@ describe 'ios/element/textfield' do
27
27
  end
28
28
 
29
29
  t 'textfields' do
30
- values = textfields('enter').map { |e| e.value }
30
+ values = textfields('enter').map(&:value)
31
31
  values.include?('<enter text>').must_equal true
32
32
  values.include?('<enter password>').must_equal true
33
33
  textfields.length.must_equal 4
34
34
  end
35
35
 
36
36
  t 'predicate textfields' do
37
- textfield_count = execute_script(%Q(au.mainApp().getAllWithPredicate("type contains[c] 'textfield'", true))).length
37
+ textfield_count = execute_script(%(au.mainApp().getAllWithPredicate("type contains[c] 'textfield'", true))).length
38
38
  textfield_count.must_equal 4
39
39
  end
40
40
 
@@ -72,14 +72,14 @@ describe 'ios/element/textfield' do
72
72
  textfield(1).send_keys 'ok'
73
73
  keyboard_must_exist
74
74
 
75
- # type will dismiss the keyboard
75
+ # type should not dismiss the keyboard
76
76
  message = 'type test type'
77
77
  textfield(1).type message
78
- keyboard_must_not_exist
78
+ keyboard_must_exist
79
79
  textfield(1).text.must_equal message
80
80
  end
81
81
 
82
- def must_raise_no_element &block
82
+ def must_raise_no_element(&block)
83
83
  proc { block.call }.must_raise Selenium::WebDriver::Error::NoSuchElementError
84
84
  end
85
85
 
@@ -123,4 +123,4 @@ describe 'ios/element/textfield' do
123
123
  t 'after_last' do
124
124
  after_last
125
125
  end
126
- end
126
+ end
@@ -13,7 +13,7 @@ describe 'ios/helper' do
13
13
  ios_password(2).must_equal 8226.chr('UTF-8') * 2
14
14
  end
15
15
 
16
- # todo: t 'get_page' do
16
+ # TODO: t 'get_page' do
17
17
 
18
18
  # t 'page' do # writes to std out
19
19
 
@@ -21,7 +21,7 @@ describe 'ios/helper' do
21
21
  source_window.length.must_equal 11
22
22
  end
23
23
 
24
- # todo: t 'page_window' do
25
- # todo: t 'id' do
26
- # todo: t 'ios_version' do
27
- end
24
+ # TODO: t 'page_window' do
25
+ # TODO: t 'id' do
26
+ # TODO: t 'ios_version' do
27
+ end
@@ -13,7 +13,7 @@ describe 'ios/patch' do
13
13
  before_first
14
14
  end
15
15
 
16
- # todo: test 'label'
16
+ # TODO: test 'label'
17
17
 
18
18
  t 'type' do
19
19
  # nav to textfield
@@ -27,4 +27,4 @@ describe 'ios/patch' do
27
27
  t 'after_last' do
28
28
  after_last
29
29
  end
30
- end
30
+ end
@@ -1,4 +1,4 @@
1
1
  # ios tests have a set of common helper methods
2
2
  require_relative 'common'
3
3
  # run file is identical to android
4
- require_relative '../../android_tests/lib/run'
4
+ require_relative '../../android_tests/lib/run'
@@ -12,7 +12,7 @@ ruby_lib's iOS tests. Requires `Ruby 1.9.3` or better.
12
12
 
13
13
  `UICatalog6.1` is from [appium/appium](https://github.com/appium/appium/blob/master/assets/UICatalog6.1.app.zip)
14
14
 
15
- The tests are now run against `iPhone Simulator 7.0.3 (11B508)`
15
+ The tests are now run against `iPhone 6 Simulator 8.3 (12F69)`
16
16
 
17
17
  #### Documentation
18
18
 
@@ -24,7 +24,7 @@ The tests are now run against `iPhone Simulator 7.0.3 (11B508)`
24
24
  --
25
25
 
26
26
  ```java
27
- Finished in 1 min 49 secs
27
+ Finished in 1 min 57 secs
28
28
 
29
- 101 runs, 130 assertions, 0 failures, 0 errors, 0 skips
29
+ 123 runs, 164 assertions, 0 failures, 0 errors, 0 skips
30
30
  ```
@@ -6,13 +6,13 @@ s = SauceStorage.new username: 'my_user_name', key: '00', debug: true
6
6
  # or if you have SAUCE_USERNAME and SAUCE_ACCESS_KEY in env already
7
7
 
8
8
  s = SauceStorage.new debug: true
9
-
9
+
10
10
  # list all files
11
11
  s.files
12
-
12
+
13
13
  # upload a file
14
14
  s.upload '/tmp/sauce/test.zip'
15
-
15
+
16
16
  > s.files
17
17
  => [{"size"=>8,
18
18
  "mtime"=>1367700857.1011374,
@@ -30,20 +30,20 @@ require 'json'
30
30
  class SauceStorage
31
31
  attr_reader :username, :key, :url, :debug
32
32
 
33
- def initialize opts
33
+ def initialize(opts)
34
34
  @username = opts.fetch :username, ENV['SAUCE_USERNAME']
35
35
  @key = opts.fetch :key, ENV['SAUCE_ACCESS_KEY']
36
36
  @url = "https://#{@username}:#{@key}@saucelabs.com/rest/v1/storage/#{@username}"
37
37
  @debug = opts.fetch :debug, false
38
38
  end
39
39
 
40
- def upload file_path
40
+ def upload(file_path)
41
41
  file_name = File.basename file_path
42
42
  file = File.new file_path
43
43
  local_md5 = Digest::MD5.hexdigest File.read file_path
44
44
 
45
- self.files.each do |file|
46
- if file['md5'] == local_md5
45
+ files.each do |f|
46
+ if f['md5'] == local_md5
47
47
  puts 'File already uploaded' if @debug
48
48
  return true
49
49
  end
@@ -62,4 +62,4 @@ class SauceStorage
62
62
  def files
63
63
  JSON.parse(RestClient.get @url)['files']
64
64
  end
65
- end
65
+ end
@@ -3,4 +3,4 @@ require File.expand_path '../sauce_storage', __FILE__
3
3
  s = SauceStorage.new debug: true
4
4
  apk = File.expand_path '../' * 2 + ENV['UPLOAD_FILE'], __FILE__
5
5
 
6
- s.upload apk
6
+ s.upload apk
@@ -2,7 +2,7 @@ require 'nokogiri'
2
2
 
3
3
  module Appium
4
4
  module Android
5
- def _nodeset_to_uiselector opts={}
5
+ def _nodeset_to_uiselector(opts = {})
6
6
  results = ''
7
7
 
8
8
  nodes = opts[:nodes]
@@ -11,16 +11,16 @@ module Appium
11
11
  nodes = [nodes[0]] if first
12
12
 
13
13
  nodes.each do |node|
14
- results += %Q(new UiSelector().className("#{node.name}").instance(#{node.attr('instance')});)
14
+ results += %(new UiSelector().className("#{node.name}").instance(#{node.attr('instance')});)
15
15
  end
16
16
 
17
17
  results.strip
18
18
  end
19
19
 
20
- def _client_xpath opts={}
20
+ def _client_xpath(opts = {})
21
21
  root_node = Nokogiri::XML(get_source).children.first
22
22
 
23
- instance = Hash.new -1
23
+ instance = Hash.new(-1)
24
24
 
25
25
  root_node.traverse do |node|
26
26
  number = instance[node.name] += 1
@@ -33,15 +33,15 @@ module Appium
33
33
  _nodeset_to_uiselector nodes: nodes, first: first
34
34
  end
35
35
 
36
- def client_xpath xpath
36
+ def client_xpath(xpath)
37
37
  find_element :uiautomator, _client_xpath(xpath: xpath, first: true)
38
38
  end
39
39
 
40
- def client_xpaths xpath
40
+ def client_xpaths(xpath)
41
41
  find_elements :uiautomator, _client_xpath(xpath: xpath, first: false)
42
42
  end
43
43
  end
44
44
  end
45
45
 
46
46
  # http://stackoverflow.com/questions/9199415/getting-first-node-in-xpath-result-set
47
- # '(//android.widget.TextView)[1]' not '//android.widget.TextView[1]'
47
+ # '(//android.widget.TextView)[1]' not '//android.widget.TextView[1]'
@@ -3,7 +3,7 @@ module Appium
3
3
  # Click the first alert button that contains value or by index.
4
4
  # @param value [Integer, String] either an integer index of the button or the button's name
5
5
  # @return [void]
6
- def alert_click value
6
+ def alert_click(value)
7
7
  button(value).click
8
8
  end
9
9
 
@@ -35,4 +35,4 @@ module Appium
35
35
  first_button.text
36
36
  end
37
37
  end # module Android
38
- end # module Appium
38
+ end # module Appium
@@ -7,28 +7,28 @@ module Appium
7
7
  private
8
8
 
9
9
  # @private
10
- def _button_visible_selectors opts={}
10
+ def _button_visible_selectors(opts = {})
11
11
  button_index = opts.fetch :button_index, false
12
12
  image_button_index = opts.fetch :image_button_index, false
13
13
 
14
14
  if button_index && image_button_index
15
- "new UiSelector().className(#{Button}).instance(#{button_index});" +
16
- "new UiSelector().className(#{ImageButton}).instance(#{image_button_index});"
15
+ "new UiSelector().className(#{Button}).instance(#{button_index});" \
16
+ "new UiSelector().className(#{ImageButton}).instance(#{image_button_index});"
17
17
  else
18
- "new UiSelector().className(#{Button});" +
19
- "new UiSelector().className(#{ImageButton});"
18
+ "new UiSelector().className(#{Button});" \
19
+ "new UiSelector().className(#{ImageButton});"
20
20
  end
21
21
  end
22
22
 
23
23
  # @private
24
- def _button_exact_string value
24
+ def _button_exact_string(value)
25
25
  button = string_visible_exact Button, value
26
26
  image_button = string_visible_exact ImageButton, value
27
27
  button + image_button
28
28
  end
29
29
 
30
30
  # @private
31
- def _button_contains_string value
31
+ def _button_contains_string(value)
32
32
  button = string_visible_contains Button, value
33
33
  image_button = string_visible_contains ImageButton, value
34
34
  button + image_button
@@ -40,12 +40,12 @@ module Appium
40
40
  # @param value [String, Integer] the value to exactly match.
41
41
  # If int then the button at that index is returned.
42
42
  # @return [Button]
43
- def button value
43
+ def button(value)
44
44
  # Don't use ele_index because that only works on one element type.
45
45
  # Android needs to combine button and image button to match iOS.
46
46
  if value.is_a? Numeric
47
47
  index = value
48
- raise "#{index} is not a valid index. Must be >= 1" if index <= 0
48
+ fail "#{index} is not a valid index. Must be >= 1" if index <= 0
49
49
 
50
50
  return find_element :uiautomator, _button_visible_selectors(index: index)
51
51
  end
@@ -57,7 +57,7 @@ module Appium
57
57
  # If value is omitted, all buttons are returned.
58
58
  # @param value [String] the value to search for
59
59
  # @return [Array<Button>]
60
- def buttons value=false
60
+ def buttons(value = false)
61
61
  return find_elements :uiautomator, _button_visible_selectors unless value
62
62
  find_elements :uiautomator, _button_contains_string(value)
63
63
  end
@@ -73,28 +73,28 @@ module Appium
73
73
  def last_button
74
74
  # uiautomator index doesn't support last
75
75
  # and it's 0 indexed
76
- button_index = tags(Button).length
77
- button_index -= 1 if button_index > 0
76
+ button_index = tags(Button).length
77
+ button_index -= 1 if button_index > 0
78
78
  image_button_index = tags(ImageButton).length
79
79
  image_button_index -= 1 if image_button_index > 0
80
80
 
81
81
  find_element :uiautomator,
82
82
  _button_visible_selectors(button_index: button_index,
83
- image_button_index: image_button_index)
83
+ image_button_index: image_button_index)
84
84
  end
85
85
 
86
86
  # Find the first button that exactly matches value.
87
87
  # @param value [String] the value to match exactly
88
88
  # @return [Button]
89
- def button_exact value
89
+ def button_exact(value)
90
90
  find_element :uiautomator, _button_exact_string(value)
91
91
  end
92
92
 
93
93
  # Find all buttons that exactly match value.
94
94
  # @param value [String] the value to match exactly
95
95
  # @return [Array<Button>]
96
- def buttons_exact value
96
+ def buttons_exact(value)
97
97
  find_elements :uiautomator, _button_exact_string(value)
98
98
  end
99
99
  end # module Android
100
- end # module Appium
100
+ end # module Appium