appium_lib 4.1.0 → 5.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/android_tests/api.apk +0 -0
  3. data/android_tests/lib/android/specs/android/element/text.rb +2 -2
  4. data/android_tests/lib/android/specs/android/helper.rb +2 -2
  5. data/android_tests/lib/android/specs/common/device.rb +10 -28
  6. data/android_tests/lib/android/specs/common/device_touchaction.rb +29 -0
  7. data/android_tests/lib/android/specs/common/helper.rb +4 -4
  8. data/android_tests/lib/android/specs/common/patch.rb +2 -2
  9. data/android_tests/lib/android/specs/driver.rb +11 -1
  10. data/android_tests/lib/android/specs/install.rb +7 -6
  11. data/appium_lib.gemspec +2 -10
  12. data/contributing.md +23 -0
  13. data/docs/android_docs.md +274 -217
  14. data/docs/docs.md +28 -0
  15. data/docs/ios_docs.md +372 -212
  16. data/ios_tests/UICatalog.app/Info.plist +0 -0
  17. data/ios_tests/appium.txt +2 -1
  18. data/ios_tests/lib/ios/specs/common/helper.rb +12 -21
  19. data/ios_tests/lib/ios/specs/common/web_context.rb +7 -2
  20. data/ios_tests/lib/ios/specs/device/device.rb +16 -10
  21. data/ios_tests/lib/ios/specs/driver.rb +13 -2
  22. data/ios_tests/lib/ios/specs/ios/element/button.rb +5 -4
  23. data/ios_tests/lib/ios/specs/ios/element/generic.rb +2 -2
  24. data/ios_tests/lib/ios/specs/ios/element/text.rb +11 -7
  25. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +9 -9
  26. data/lib/appium_lib.rb +1 -17
  27. data/lib/appium_lib/android/helper.rb +55 -5
  28. data/lib/appium_lib/common/helper.rb +26 -30
  29. data/lib/appium_lib/common/patch.rb +12 -0
  30. data/lib/appium_lib/common/version.rb +2 -2
  31. data/lib/appium_lib/device/device.rb +83 -12
  32. data/lib/appium_lib/driver.rb +26 -14
  33. data/lib/appium_lib/ios/element/button.rb +4 -4
  34. data/lib/appium_lib/ios/element/generic.rb +4 -4
  35. data/lib/appium_lib/ios/element/text.rb +4 -4
  36. data/lib/appium_lib/ios/element/textfield.rb +41 -22
  37. data/lib/appium_lib/ios/helper.rb +216 -47
  38. data/lib/appium_lib/ios/patch.rb +0 -19
  39. data/readme.md +1 -0
  40. data/release_notes.md +64 -0
  41. metadata +7 -5
@@ -1,8 +1,9 @@
1
1
  [caps]
2
2
  platformName = "ios"
3
+ platformVersion = "8.1"
3
4
  deviceName ="iPhone Simulator"
4
5
  app = "./UICatalog.app"
5
6
 
6
7
  [appium_lib]
7
8
  sauce_username = ""
8
- sauce_access_key = ""
9
+ sauce_access_key = ""
@@ -1,5 +1,6 @@
1
1
  # rake ios[common/helper]
2
2
  describe 'common/helper.rb' do
3
+
3
4
  def before_first
4
5
  screen.must_equal catalog
5
6
  end
@@ -91,11 +92,11 @@ must_not_raise is a no-op.
91
92
  end
92
93
 
93
94
  t 'xpaths' do
94
- xpaths('//UIAStaticText').length.must_equal 13
95
+ xpaths('//UIAStaticText').length.must_equal 25
95
96
  end
96
97
 
97
98
  def uibutton_text
98
- 'Buttons, Various uses of UIButton'
99
+ 'Buttons'
99
100
  end
100
101
 
101
102
  t 'ele_index' do
@@ -126,7 +127,7 @@ must_not_raise is a no-op.
126
127
 
127
128
  t 'find_ele_by_attr_include' do
128
129
  el_text = find_ele_by_attr_include('UIAStaticText', :name, 'button').text
129
- el_text.must_equal ''
130
+ el_text.must_equal uibutton_text
130
131
 
131
132
  el_name = find_ele_by_attr_include('UIAStaticText', :name, 'button').name
132
133
  el_name.must_equal uibutton_text
@@ -134,7 +135,7 @@ must_not_raise is a no-op.
134
135
 
135
136
  t 'find_eles_by_attr_include' do
136
137
  ele_count = find_eles_by_attr_include('UIAStaticText', :name, 'e').length
137
- ele_count.must_equal 12
138
+ ele_count.must_equal 19
138
139
  end
139
140
 
140
141
  t 'first_ele' do
@@ -143,8 +144,8 @@ must_not_raise is a no-op.
143
144
 
144
145
  t 'last_ele' do
145
146
  el = last_ele('UIAStaticText')
146
- el.text.must_equal ''
147
- el.name.must_equal 'Transitions, Shows UIViewAnimationTransitions'
147
+ el.text.must_equal 'Transitions'
148
+ el.name.must_equal 'Transitions'
148
149
  end
149
150
 
150
151
  # t 'source' do # tested by get_source
@@ -158,24 +159,14 @@ must_not_raise is a no-op.
158
159
  end
159
160
 
160
161
  t 'invalid id should error' do
161
- begin
162
- id 'does not exist'
163
- rescue Exception => e
164
- message = e.message
165
- end
166
- message.must_equal 'Invalid id `does not exist`'
162
+ proc { id 'does not exist' }.must_raise Selenium::WebDriver::Error::NoSuchElementError
167
163
 
168
164
  # resource id should error on ios
169
- begin
170
- id 'android:id/text1'
171
- rescue Exception => e
172
- message = e.message
173
- end
174
- message.must_equal 'Invalid id `android:id/text1`'
165
+ proc { id 'android:id/text1' }.must_raise Selenium::WebDriver::Error::NoSuchElementError
175
166
  end
176
167
 
177
168
  t 'tag' do
178
- tag('UIATableCell').name.must_equal 'Buttons, Various uses of UIButton'
169
+ tag('UIATableCell').name.must_equal uibutton_text
179
170
  end
180
171
 
181
172
  t 'tags' do
@@ -183,7 +174,7 @@ must_not_raise is a no-op.
183
174
  end
184
175
 
185
176
  t 'find_eles_by_attr_include' do
186
- find_eles_by_attr_include('UIATableCell', 'name', 'Use').length.must_equal 7
177
+ find_eles_by_attr_include('UIAStaticText', 'name', 'Use').length.must_equal 7
187
178
  end
188
179
 
189
180
  t 'get_page_class' do
@@ -209,4 +200,4 @@ xpath_visible_exact
209
200
  xpaths_visible_exact
210
201
  raise_no_element_error
211
202
  =end
212
- end
203
+ end
@@ -2,10 +2,15 @@
2
2
  describe 'the web context' do
3
3
 
4
4
  t 'get_android_inspect' do
5
- text('Web, Use of UIWebView').click
5
+ text('Web').click
6
6
  set_context 'WEBVIEW'
7
7
  current_context.must_equal 'WEBVIEW_1'
8
8
  sleep 1 #Give a chance to load
9
9
  page.start_with?("\nhtml\n").must_equal true
10
10
  end
11
- end
11
+
12
+ t 'after_last' do
13
+ set_context 'NATIVE_APP'
14
+ back_click
15
+ end
16
+ end
@@ -3,6 +3,12 @@ describe 'device/device' do
3
3
  screen.must_equal catalog
4
4
  end
5
5
 
6
+ # go back to the main page
7
+ def go_back
8
+ back
9
+ wait { ! exists { id 'ArrowButton' } } # successfully transitioned back
10
+ end
11
+
6
12
  t 'before_first' do
7
13
  before_first
8
14
  end
@@ -13,7 +19,7 @@ describe 'device/device' do
13
19
 
14
20
  # It appears that lockForDuration doesn't.
15
21
  close_app
16
- launch
22
+ launch_app
17
23
  end
18
24
 
19
25
  t 'background_app' do
@@ -32,7 +38,7 @@ describe 'device/device' do
32
38
 
33
39
  t 'close and launch' do
34
40
  close_app
35
- launch
41
+ launch_app
36
42
  tag('UIANavigationBar').name.must_equal 'UICatalog'
37
43
  end
38
44
 
@@ -45,12 +51,12 @@ describe 'device/device' do
45
51
  end
46
52
 
47
53
  t 'current_context' do
48
- current_context.must_equal nil
54
+ current_context.must_equal 'NATIVE_APP'
49
55
  end
50
56
 
51
57
  t 'switch_to_default_context' do
52
58
  switch_to_default_context
53
- current_context.must_equal nil
59
+ current_context.must_equal 'NATIVE_APP'
54
60
  end
55
61
 
56
62
  t 'app_strings' do
@@ -59,11 +65,9 @@ describe 'device/device' do
59
65
  end
60
66
 
61
67
  t 'action_chain' do
62
- ac = Appium::TouchAction.new
63
- e = find_element(:name, 'Buttons, Various uses of UIButton')
64
- ac.press element: e, x: 10, y: 10
65
- ac.perform
66
- back
68
+ Appium::TouchAction.new.press(element: id('ButtonsExplain')).perform
69
+ wait { id 'ArrowButton' } # successfully transitioned to buttons page
70
+ go_back
67
71
  end
68
72
 
69
73
  t 'swipe' do
@@ -71,9 +75,11 @@ describe 'device/device' do
71
75
  end
72
76
 
73
77
  t 'pinch & zoom' do
74
- text('Images, Use of UIImageView').click
78
+ wait { id('ImagesExplain').click }
79
+ # both of these appear to do nothing on iOS 8
75
80
  zoom 200
76
81
  pinch 75
82
+ go_back
77
83
  end
78
84
 
79
85
  t 'pull_file' do
@@ -36,6 +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
40
  deviceName: 'iPhone Simulator',
40
41
  app: 'UICatalog.app' },
41
42
  custom_url: false,
@@ -195,7 +196,7 @@ describe 'driver' do
195
196
 
196
197
  # any elements
197
198
  t 'find_elements' do
198
- find_elements(:class, 'UIAStaticText').length.must_equal 13
199
+ find_elements(:class, 'UIATableCell').length.must_equal 12
199
200
  end
200
201
 
201
202
  # any element
@@ -203,6 +204,16 @@ describe 'driver' do
203
204
  find_element(:class, 'UIAStaticText').class.must_equal Selenium::WebDriver::Element
204
205
  end
205
206
 
207
+ # settings
208
+ t 'get settings' do
209
+ get_settings.wont_be_nil
210
+ end
211
+
212
+ t 'update settings' do
213
+ update_settings cyberdelia: 'open'
214
+ get_settings['cyberdelia'].must_equal 'open'
215
+ end
216
+
206
217
  # Skip: x # x is only used in Pry
207
218
  end
208
- end
219
+ end
@@ -21,16 +21,17 @@ describe 'ios/element/button' do
21
21
 
22
22
  t 'button' do
23
23
  # by index
24
- button(2).name.must_equal gray
24
+ button(3).name.must_equal gray
25
25
 
26
26
  # by name contains
27
27
  button('ray').name.must_equal gray
28
28
  end
29
29
 
30
30
  t 'buttons' do
31
- exp = ['Back', 'Gray', 'Right pointing arrow']
32
- buttons('a').map { |e| e.name }.must_equal exp
33
- buttons.length.must_equal 4
31
+ exp = ['Back', 'Back', 'Gray', 'Right pointing arrow']
32
+ target_buttons = buttons('a')
33
+ target_buttons.map { |e| e.name }.must_equal exp
34
+ target_buttons.length.must_equal exp.length
34
35
  end
35
36
 
36
37
  t 'first_button' do
@@ -5,7 +5,7 @@ describe 'ios/element/generic' do
5
5
  end
6
6
 
7
7
  def uibutton_text
8
- 'Buttons, Various uses of UIButton'
8
+ 'Buttons'
9
9
  end
10
10
 
11
11
  def verify element
@@ -32,4 +32,4 @@ describe 'ios/element/generic' do
32
32
  t 'finds_exact' do
33
33
  verify finds_exact uibutton_text
34
34
  end
35
- end
35
+ end
@@ -4,6 +4,10 @@ describe 'ios/element/text' do
4
4
  'UICatalog'
5
5
  end
6
6
 
7
+ def uiview_transitions
8
+ 'Transitions'
9
+ end
10
+
7
11
  def before_first
8
12
  screen.must_equal catalog
9
13
  end
@@ -17,19 +21,19 @@ describe 'ios/element/text' do
17
21
  end
18
22
 
19
23
  t 'last_text' do
20
- last_text.text.must_equal ''
21
- last_text.name.must_equal 'Transitions, Shows UIViewAnimationTransitions'
24
+ last_text.text.must_equal uiview_transitions
25
+ last_text.name.must_equal uiview_transitions
22
26
  end
23
27
 
24
28
  t 'text' do
25
- text('mat').text.must_equal ''
29
+ text('siti').text.must_equal uiview_transitions
26
30
  text(1).text.must_equal ui_catalog
27
- text('mat').name.must_equal 'Transitions, Shows UIViewAnimationTransitions'
31
+ text('siti').name.must_equal uiview_transitions
28
32
  end
29
33
 
30
34
  t 'texts' do
31
- exp = ['Controls, Various uses of UIControl', 'Segments, Various uses of UISegmentedControl']
32
- texts.length.must_equal 13
35
+ exp = ['Controls', 'Various uses of UIControl', 'Various uses of UISegmentedControl']
36
+ texts.length.must_equal 24
33
37
  texts('trol').map { |e| e.name }.must_equal exp
34
38
  texts('uses').length.must_equal 7
35
39
  end
@@ -51,4 +55,4 @@ describe 'ios/element/text' do
51
55
  t 'texts_exact' do
52
56
  texts_exact('UICatalog').length.must_equal 1
53
57
  end
54
- end
58
+ end
@@ -23,7 +23,7 @@ describe 'ios/element/textfield' do
23
23
  t 'textfield' do
24
24
  textfield(1).text.must_equal(enter_text)
25
25
  textfield(enter_text).text.must_equal(enter_text)
26
- textfield('word').text.must_equal enter_password
26
+ textfield('word').value.must_equal enter_password
27
27
  end
28
28
 
29
29
  t 'textfields' do
@@ -43,15 +43,15 @@ describe 'ios/element/textfield' do
43
43
  end
44
44
 
45
45
  t 'last_textfield' do
46
- last_textfield.text.must_equal enter_password
46
+ last_textfield.text.must_equal enter_text
47
47
  end
48
48
 
49
49
  t 'textfield_exact' do
50
- textfield_exact(enter_password).text.must_equal enter_password
50
+ textfield_exact(enter_password).value.must_equal enter_password
51
51
  end
52
52
 
53
53
  t 'textfields_exact' do
54
- textfields_exact(enter_password).first.text.must_equal enter_password
54
+ textfields_exact(enter_password).first.value.must_equal enter_password
55
55
  end
56
56
 
57
57
  def keyboard_exists?
@@ -83,6 +83,11 @@ describe 'ios/element/textfield' do
83
83
  proc { block.call }.must_raise Selenium::WebDriver::Error::NoSuchElementError
84
84
  end
85
85
 
86
+ t 'hide_keyboard' do
87
+ first_textfield.click
88
+ hide_keyboard
89
+ end
90
+
86
91
  # test textfield methods with no textfields
87
92
 
88
93
  t 'leave textfields' do
@@ -115,11 +120,6 @@ describe 'ios/element/textfield' do
115
120
  textfields_exact('does not exist').length.must_equal 0
116
121
  end
117
122
 
118
- t 'hide_keyboard' do
119
- first_textfield.click
120
- hide_keyboard
121
- end
122
-
123
123
  t 'after_last' do
124
124
  after_last
125
125
  end
@@ -5,24 +5,8 @@ Encoding.default_internal = Encoding::UTF_8
5
5
  require 'forwardable' unless defined? Forwardable
6
6
  require_relative 'appium_lib/rails/duplicable'
7
7
 
8
+ # Init global driver
8
9
  $driver = nil
9
10
 
10
- # @private
11
- # Invoke top level methods on last created Appium driver.
12
- def self.method_missing method, *args, &block
13
- raise "driver is nil. called #{method}" if $driver == nil
14
-
15
- if $driver.respond_to?(method)
16
- # puts "[method_missing] Calling driver.send for #{method}"
17
- $driver.send(method, *args, &block)
18
- elsif self.respond_to?(method)
19
- # puts "[method_missing] Calling super with args for #{method}"
20
- super(*args, &block)
21
- else
22
- # puts "[method_missing] Calling super (no args) for #{method}"
23
- super
24
- end
25
- end
26
-
27
11
  require_relative 'appium_lib/logger'
28
12
  require_relative 'appium_lib/driver'
@@ -18,13 +18,13 @@ module Appium
18
18
 
19
19
  def initialize
20
20
  reset
21
- @filter = false
21
+ @filter = false
22
22
  @instance = Hash.new -1
23
23
  end
24
24
 
25
25
  def reset
26
- @result = ''
27
- @keys = %w[text resource-id content-desc]
26
+ @result = ''
27
+ @keys = %w[text resource-id content-desc]
28
28
  @instance = Hash.new -1
29
29
  end
30
30
 
@@ -83,6 +83,41 @@ module Appium
83
83
  end
84
84
  end # class AndroidElements
85
85
 
86
+ # Fix uiautomator's xml dump.
87
+ # https://github.com/appium/appium/issues/2822
88
+ # https://code.google.com/p/android/issues/detail?id=74143
89
+ def _fix_android_native_source source
90
+ # <android.app.ActionBar$Tab
91
+ # <android.app.ActionBar $ Tab
92
+
93
+ # find each closing tag that contains a dollar sign.
94
+ source.scan(/<\/([^>]*\$[^>]*)>/).flatten.uniq.each do |problem_tag|
95
+ # "android.app.ActionBar$Tab"
96
+ before, after = problem_tag.split('$')
97
+ before.strip!
98
+ after.strip!
99
+
100
+ fixed = "#{before}.#{after}"
101
+
102
+ # now escape . in before/after because they're used in regex
103
+ before.gsub!('.', '\.')
104
+ after.gsub!('.', '\.')
105
+
106
+ # <android.app.ActionBar$Tab => <android.app.ActionBar.Tab
107
+ # </android.app.ActionBar$Tab> => </android.app.ActionBar.Tab>
108
+ source = source.gsub(/<#{before}\s*\$\s*#{after}/, "<#{fixed}").
109
+ gsub(/<\/#{before}\s*\$\s*#{after}>/, "</#{fixed}>")
110
+ end
111
+
112
+ source
113
+ end
114
+
115
+ # Prints xml of the current page
116
+ # @return [void]
117
+ def source
118
+ _print_source get_source
119
+ end
120
+
86
121
  # Android only.
87
122
  # Returns a string containing interesting elements.
88
123
  # The text, content description, and id are returned.
@@ -91,10 +126,15 @@ module Appium
91
126
  # @return [String]
92
127
  def get_android_inspect class_name=false
93
128
  source = get_source
94
- if source.start_with? '<html>'
129
+
130
+ doctype_string = '<!doctyp'
131
+ source_header = source[0..doctype_string.length].downcase
132
+ source_is_html = source_header.start_with?(doctype_string, '<html')
133
+
134
+ if source_is_html # parse html from webview
95
135
  parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
96
136
  else
97
- parser = @android_webview_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
137
+ parser = @android_native_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
98
138
  end
99
139
  parser.document.reset # ensure document is reset before parsing
100
140
  parser.document.filter = class_name
@@ -283,5 +323,15 @@ module Appium
283
323
  def complex_finds_exact class_name, value
284
324
  find_elements :uiautomator, string_visible_exact(class_name, value)
285
325
  end
326
+
327
+ # Returns XML string for the current page
328
+ # Fixes uiautomator's $ in node names.
329
+ # `android.app.ActionBar$Tab` becomes `android.app.ActionBar.Tab`
330
+ # @return [String]
331
+ def get_source
332
+ src = @driver.page_source
333
+ src = _fix_android_native_source src unless src && src.start_with?('<html>')
334
+ src
335
+ end
286
336
  end # module Android
287
337
  end # module Appium