appium_lib 0.24.1 → 1.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +17 -8
  3. data/android_tests/Gemfile +1 -0
  4. data/android_tests/LICENSE-2.0.txt +202 -0
  5. data/android_tests/Rakefile +61 -0
  6. data/android_tests/api.apk +0 -0
  7. data/android_tests/appium.txt +3 -0
  8. data/android_tests/flaky.txt +1 -0
  9. data/android_tests/lib/android/specs/android/dynamic.rb +5 -0
  10. data/android_tests/lib/android/specs/android/element/alert.rb +41 -0
  11. data/android_tests/lib/android/specs/android/element/button.rb +55 -0
  12. data/android_tests/lib/android/specs/android/element/generic.rb +48 -0
  13. data/android_tests/lib/android/specs/android/element/text.rb +39 -0
  14. data/android_tests/lib/android/specs/android/element/textfield.rb +60 -0
  15. data/android_tests/lib/android/specs/android/helper.rb +80 -0
  16. data/android_tests/lib/android/specs/android/patch.rb +14 -0
  17. data/android_tests/lib/android/specs/common/device.rb +117 -0
  18. data/android_tests/lib/android/specs/common/element/window.rb +9 -0
  19. data/android_tests/lib/android/specs/common/helper.rb +112 -0
  20. data/android_tests/lib/android/specs/common/patch.rb +69 -0
  21. data/android_tests/lib/android/specs/common/version.rb +9 -0
  22. data/android_tests/lib/android/specs/driver.rb +174 -0
  23. data/android_tests/lib/format.rb +49 -0
  24. data/android_tests/lib/run.rb +72 -0
  25. data/android_tests/readme.md +27 -0
  26. data/appium_lib.gemspec +8 -5
  27. data/docs/android_docs.md +1052 -716
  28. data/docs/ios_docs.md +657 -834
  29. data/docs_gen/make_docs.rb +1 -3
  30. data/ios_tests/Gemfile +1 -0
  31. data/ios_tests/LICENSE-2.0.txt +202 -0
  32. data/ios_tests/Rakefile +47 -0
  33. data/ios_tests/UICatalog.app.zip +0 -0
  34. data/ios_tests/UICatalog.app/12-6AM.png +0 -0
  35. data/ios_tests/UICatalog.app/12-6PM.png +0 -0
  36. data/ios_tests/UICatalog.app/6-12AM.png +0 -0
  37. data/ios_tests/UICatalog.app/6-12PM.png +0 -0
  38. data/ios_tests/UICatalog.app/Default-568h@2x.png +0 -0
  39. data/ios_tests/UICatalog.app/Default@2x.png +0 -0
  40. data/ios_tests/UICatalog.app/Info.plist +0 -0
  41. data/ios_tests/UICatalog.app/PkgInfo +1 -0
  42. data/ios_tests/UICatalog.app/UIButton_custom.png +0 -0
  43. data/ios_tests/UICatalog.app/UICatalog +0 -0
  44. data/ios_tests/UICatalog.app/blueButton.png +0 -0
  45. data/ios_tests/UICatalog.app/bookmarkImage.png +0 -0
  46. data/ios_tests/UICatalog.app/bookmarkImageHighlighted.png +0 -0
  47. data/ios_tests/UICatalog.app/divider.png +0 -0
  48. data/ios_tests/UICatalog.app/en.lproj/AlertsViewController.nib +0 -0
  49. data/ios_tests/UICatalog.app/en.lproj/ButtonsViewController.nib +0 -0
  50. data/ios_tests/UICatalog.app/en.lproj/ControlsViewController.nib +0 -0
  51. data/ios_tests/UICatalog.app/en.lproj/ImagesViewController.nib +0 -0
  52. data/ios_tests/UICatalog.app/en.lproj/Localizable.strings +0 -0
  53. data/ios_tests/UICatalog.app/en.lproj/MainWindow.nib +0 -0
  54. data/ios_tests/UICatalog.app/en.lproj/PickerViewController.nib +0 -0
  55. data/ios_tests/UICatalog.app/en.lproj/SearchBarController.nib +0 -0
  56. data/ios_tests/UICatalog.app/en.lproj/SegmentViewController.nib +0 -0
  57. data/ios_tests/UICatalog.app/en.lproj/TextFieldController.nib +0 -0
  58. data/ios_tests/UICatalog.app/en.lproj/TextViewController.nib +0 -0
  59. data/ios_tests/UICatalog.app/en.lproj/ToolbarViewController.nib +0 -0
  60. data/ios_tests/UICatalog.app/en.lproj/TransitionViewController.nib +0 -0
  61. data/ios_tests/UICatalog.app/en.lproj/WebViewController.nib +0 -0
  62. data/ios_tests/UICatalog.app/orangeslide.png +0 -0
  63. data/ios_tests/UICatalog.app/scene1.jpg +0 -0
  64. data/ios_tests/UICatalog.app/scene2.jpg +0 -0
  65. data/ios_tests/UICatalog.app/scene3.jpg +0 -0
  66. data/ios_tests/UICatalog.app/scene4.jpg +0 -0
  67. data/ios_tests/UICatalog.app/scene5.jpg +0 -0
  68. data/ios_tests/UICatalog.app/searchBarBackground.png +0 -0
  69. data/ios_tests/UICatalog.app/segment_check.png +0 -0
  70. data/ios_tests/UICatalog.app/segment_search.png +0 -0
  71. data/ios_tests/UICatalog.app/segment_tools.png +0 -0
  72. data/ios_tests/UICatalog.app/segmentedBackground.png +0 -0
  73. data/ios_tests/UICatalog.app/slider_ball.png +0 -0
  74. data/ios_tests/UICatalog.app/toolbarBackground.png +0 -0
  75. data/ios_tests/UICatalog.app/whiteButton.png +0 -0
  76. data/ios_tests/UICatalog.app/yellowslide.png +0 -0
  77. data/ios_tests/appium.txt +3 -0
  78. data/ios_tests/flaky.txt +1 -0
  79. data/ios_tests/lib/format.rb +25 -0
  80. data/ios_tests/lib/ios/specs/common/element/window.rb +15 -0
  81. data/ios_tests/lib/ios/specs/common/helper.rb +204 -0
  82. data/ios_tests/lib/ios/specs/common/patch.rb +50 -0
  83. data/ios_tests/lib/ios/specs/common/version.rb +17 -0
  84. data/ios_tests/lib/ios/specs/device/device.rb +82 -0
  85. data/ios_tests/lib/ios/specs/device/multi_touch.rb +12 -0
  86. data/ios_tests/lib/ios/specs/device/touch_actions.rb +15 -0
  87. data/ios_tests/lib/ios/specs/driver.rb +203 -0
  88. data/ios_tests/lib/ios/specs/ios/element/alert.rb +48 -0
  89. data/ios_tests/lib/ios/specs/ios/element/button.rb +58 -0
  90. data/ios_tests/lib/ios/specs/ios/element/generic.rb +35 -0
  91. data/ios_tests/lib/ios/specs/ios/element/text.rb +54 -0
  92. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +123 -0
  93. data/ios_tests/lib/ios/specs/ios/helper.rb +27 -0
  94. data/ios_tests/lib/ios/specs/ios/patch.rb +30 -0
  95. data/ios_tests/lib/run.rb +106 -0
  96. data/ios_tests/readme.md +30 -0
  97. data/ios_tests/upload/sauce_storage.rb +64 -0
  98. data/ios_tests/upload/upload.rb +6 -0
  99. data/lib/appium_lib.rb +4 -14
  100. data/lib/appium_lib/android/dynamic.rb +30 -32
  101. data/lib/appium_lib/android/element/alert.rb +34 -33
  102. data/lib/appium_lib/android/element/button.rb +91 -0
  103. data/lib/appium_lib/android/element/generic.rb +51 -146
  104. data/lib/appium_lib/android/element/text.rb +54 -0
  105. data/lib/appium_lib/android/element/textfield.rb +46 -41
  106. data/lib/appium_lib/android/helper.rb +248 -417
  107. data/lib/appium_lib/android/mobile_methods.rb +17 -0
  108. data/lib/appium_lib/android/patch.rb +9 -8
  109. data/lib/appium_lib/awesome_print/ostruct.rb +33 -0
  110. data/lib/appium_lib/common/element/window.rb +9 -8
  111. data/lib/appium_lib/common/helper.rb +182 -243
  112. data/lib/appium_lib/common/patch.rb +65 -79
  113. data/lib/appium_lib/common/version.rb +2 -3
  114. data/lib/appium_lib/device/device.rb +339 -0
  115. data/lib/appium_lib/device/multi_touch.rb +94 -0
  116. data/lib/appium_lib/device/touch_actions.rb +142 -0
  117. data/lib/appium_lib/driver.rb +217 -306
  118. data/lib/appium_lib/ios/element/alert.rb +16 -92
  119. data/lib/appium_lib/ios/element/button.rb +55 -0
  120. data/lib/appium_lib/ios/element/generic.rb +27 -160
  121. data/lib/appium_lib/ios/element/text.rb +54 -0
  122. data/lib/appium_lib/ios/element/textfield.rb +78 -65
  123. data/lib/appium_lib/ios/helper.rb +300 -190
  124. data/lib/appium_lib/ios/mobile_methods.rb +17 -0
  125. data/lib/appium_lib/ios/patch.rb +55 -41
  126. data/lib/appium_lib/logger.rb +13 -0
  127. data/lib/appium_lib/rails/duplicable.rb +116 -0
  128. data/readme.md +6 -1
  129. data/release_notes.md +118 -0
  130. metadata +170 -12
  131. data/lib/appium_lib/common/element/button.rb +0 -83
  132. data/lib/appium_lib/common/element/text.rb +0 -61
@@ -0,0 +1,17 @@
1
+ module Appium
2
+ module Android
3
+ class << self
4
+ # @!method uiautomator_find
5
+ # find_element/s can be used with a [UISelector](http://developer.android.com/tools/help/uiautomator/UiSelector.html).
6
+ #
7
+ # ```ruby
8
+ # find_elements :uiautomator, 'new UiSelector().clickable(true)'
9
+ # ```
10
+ def extended(mod)
11
+ Selenium::WebDriver::SearchContext.class_eval do
12
+ Selenium::WebDriver::SearchContext::FINDERS[:uiautomator] = '-android uiautomator'
13
+ end
14
+ end
15
+ end # class << self
16
+ end # module Android
17
+ end # module Appium
@@ -1,10 +1,10 @@
1
- # encoding: utf-8
2
- module Appium::Android
3
- # @private
4
- # class_eval inside a method because class Selenium::WebDriver::Element
5
- # will trigger as soon as the file is required. in contrast a method
6
- # will trigger only when invoked.
7
- def patch_webdriver_element
1
+ module Appium
2
+ module Android
3
+ # @private
4
+ # class_eval inside a method because class Selenium::WebDriver::Element
5
+ # will trigger as soon as the file is required. in contrast a method
6
+ # will trigger only when invoked.
7
+ def patch_webdriver_element
8
8
  Selenium::WebDriver::Element.class_eval do
9
9
  # Cross platform way of entering text into a textfield
10
10
  def type text
@@ -12,4 +12,5 @@ module Appium::Android
12
12
  end
13
13
  end
14
14
  end
15
- end
15
+ end # Android
16
+ end # Appium
@@ -0,0 +1,33 @@
1
+ # Enable OpenStruct support in awesome print
2
+ # Code from @marshallshen https://github.com/michaeldv/awesome_print/pull/145
3
+ #
4
+ # Copyright (c) 2010-2013 Michael Dvorkin
5
+ #
6
+ # Awesome Print is freely distributable under the terms of MIT license.
7
+ # See LICENSE file or http://www.opensource.org/licenses/mit-license.php
8
+ #------------------------------------------------------------------------------
9
+
10
+ if defined?(OpenStruct)
11
+ module AwesomePrint
12
+ module OpenStruct
13
+ def self.included(base)
14
+ base.send :alias_method, :cast_without_ostruct, :cast
15
+ base.send :alias_method, :cast, :cast_with_ostruct
16
+ end
17
+
18
+ def cast_with_ostruct(object, type)
19
+ cast = cast_without_ostruct(object, type)
20
+ if (defined?(::OpenStruct)) && (object.is_a?(::OpenStruct))
21
+ cast = :open_struct_instance
22
+ end
23
+ cast
24
+ end
25
+
26
+ def awesome_open_struct_instance(object)
27
+ "#{object.class} #{awesome_hash(object.marshal_dump)}"
28
+ end
29
+ end # module OpenStruct
30
+ end # module AwesomePrint
31
+
32
+ AwesomePrint::Formatter.send(:include, AwesomePrint::OpenStruct)
33
+ end
@@ -1,9 +1,10 @@
1
- # encoding: utf-8
2
1
  # UIAWindow methods
3
- module Appium::Common
4
- # Get the window's size
5
- def window_size
6
- return nil if @driver.nil?
7
- @driver.manage.window.size
8
- end
9
- end # module Appium::Common
2
+ module Appium
3
+ module Common
4
+ # Get the window's size
5
+ def window_size
6
+ return nil if @driver.nil?
7
+ @driver.manage.window.size
8
+ end
9
+ end # module Common
10
+ end # module Appium
@@ -1,271 +1,210 @@
1
- # encoding: utf-8
2
1
  # Generic helper methods not specific
3
2
  # to a particular tag name
4
- module Appium::Common
5
- # json and ap are required for the source method.
6
- require 'json'
7
- require 'ap' # awesome print
8
- require 'timeout' # for wait
9
-
10
- # iOS .name returns the accessibility attribute if it's set. if not set, the string value is used.
11
- # Android .name returns the accessibility attribute and nothing if it's not set.
12
- #
13
- # .text should be cross platform so prefer that over name, unless both
14
- # Android and iOS have proper accessibility attributes.
15
- # .text and .value should be the same so use .text over .value.
16
- #
17
- # secure tag_name is iOS only because it can't be implemented using uiautomator for Android.
18
- #
19
- # find_element :text doesn't work so use XPath to find by text.
20
-
21
- # Check every 0.5 seconds to see if block.call doesn't raise an exception.
22
- # if .call raises an exception then it will be tried again.
23
- # if .call doesn't raise an exception then it will stop waiting.
24
- #
25
- # Example: wait { name('back').click }
26
- #
27
- # Give up after 30 seconds.
28
- # @param max_wait [Integer] the maximum time in seconds to wait for.
29
- # Note that max wait 0 means infinity.
30
- # @param interval [Float] the time in seconds to wait after calling the block
31
- # @param block [Block] the block to call
32
- # @return [Object] the result of block.call
33
- def wait max_wait=30, interval=0.5, &block
34
- max_wait = 1 if max_wait <= 0
35
- result = nil
36
- timeout max_wait do
37
- until (result = begin; block.call || true; rescue; end)
38
- sleep interval
3
+ module Appium
4
+ module Common
5
+ # json and ap are required for the source method.
6
+ require 'json'
7
+ require 'ap' # awesome print
8
+ require 'timeout' # for wait
9
+
10
+ # iOS .name returns the accessibility attribute if it's set. if not set, the string value is used.
11
+ # Android .name returns the accessibility attribute and nothing if it's not set.
12
+ #
13
+ # .text should be cross platform so prefer that over name, unless both
14
+ # Android and iOS have proper accessibility attributes.
15
+ # .text and .value should be the same so use .text over .value.
16
+ #
17
+ # secure class_name is iOS only because it can't be implemented using uiautomator for Android.
18
+ #
19
+ # find_element :text doesn't work so use XPath to find by text.
20
+
21
+ # Check every 0.5 seconds to see if block.call doesn't raise an exception.
22
+ # if .call raises an exception then it will be tried again.
23
+ # if .call doesn't raise an exception then it will stop waiting.
24
+ #
25
+ # Example: wait { name('back').click }
26
+ #
27
+ # Give up after 30 seconds.
28
+ # @param max_wait [Integer] the maximum time in seconds to wait for.
29
+ # Note that max wait 0 means infinity.
30
+ # @param interval [Float] the time in seconds to wait after calling the block
31
+ # @param block [Block] the block to call
32
+ # @return [Object] the result of block.call
33
+ def wait max_wait=30, interval=0.5, &block
34
+ max_wait = 1 if max_wait <= 0
35
+ result = nil
36
+ timeout max_wait do
37
+ until (
38
+ begin
39
+ result = block.call || true
40
+ rescue Exception
41
+ end)
42
+ sleep interval
43
+ end
39
44
  end
45
+ result
40
46
  end
41
- result
42
- end
43
47
 
44
- # Return block.call and ignore any exceptions.
45
- def ignore &block
46
- begin; block.call; rescue; end
47
- end
48
-
49
- # Check every 0.5 seconds to see if block.call returns true. nil is considered a failure.
50
- # Give up after 30 seconds.
51
- # @param max_wait [Integer] the maximum time in seconds to wait for
52
- # @param interval [Float] the time in seconds to wait after calling the block
53
- # @param block [Block] the block to call
54
- # @return [Object] the result of block.call
55
- def wait_true max_wait=30, interval=0.5, &block
56
- max_wait = 1 if max_wait <= 0
57
- result = nil
58
- timeout max_wait do
59
- until (result = begin; block.call; rescue; end)
60
- sleep interval
48
+ # Return block.call and ignore any exceptions.
49
+ def ignore &block
50
+ begin
51
+ block.call
52
+ rescue Exception
61
53
  end
62
54
  end
63
- result
64
- end
65
-
66
- # Navigate back.
67
- # @return [void]
68
- def back
69
- @driver.navigate.back
70
- end
71
-
72
- # For Sauce Labs reporting. Returns the current session id.
73
- def session_id
74
- @driver.session_id
75
- end
76
-
77
- # Returns the first element that matches the provided xpath.
78
- #
79
- # @param xpath_str [String] the XPath string
80
- # @return [Element]
81
- def xpath xpath_str
82
- find_element :xpath, xpath_str
83
- end
84
-
85
- # Returns all elements that match the provided xpath.
86
- #
87
- # @param xpath_str [String] the XPath string
88
- # @return [Array<Element>]
89
- def xpaths xpath_str
90
- find_elements :xpath, xpath_str
91
- end
92
-
93
- # Get the element of type tag_name at matching index.
94
- # @param tag_name [String] the tag name to find
95
- # @param index [Integer] the index
96
- # @return [Element] the found element of type tag_name
97
- def ele_index tag_name, index
98
- # XPath index starts at 1.
99
- raise "#{index} is not a valid xpath index. Must be >= 1" if index <= 0
100
- find_element :xpath, "//#{tag_name}[#{index}]"
101
- end
102
55
 
103
- # Get all elements exactly matching tag name
104
- # @param tag_name [String] the tag name to find
105
- # @return [Array<Element>] the found elements of type tag_name
106
- def find_eles tag_name
107
- @driver.find_elements :tag_name, tag_name
108
- end
109
-
110
- # Get the first tag that exactly matches tag and text.
111
- # @param tag [String] the tag name to match
112
- # @param text [String] the text to exactly match
113
- # @return [Element] the element of type tag exactly matching text
114
- def find_ele_by_text tag, text
115
- @driver.find_element :xpath, %Q(#{tag}[@text='#{text}'])
116
- end
117
-
118
- # Get all tags that exactly match tag and text.
119
- # @param tag [String] the tag name to match
120
- # @param text [String] the text to exactly match
121
- # @return [Array<Element>] the elements of type tag exactly matching text
122
- def find_eles_by_text tag, text
123
- @driver.find_elements :xpath, %Q(#{tag}[@text='#{text}'])
124
- end
125
-
126
- # Get the first tag by attribute that exactly matches value.
127
- # @param tag [String] the tag name to match
128
- # @param attr [String] the attribute to compare
129
- # @param value [String] the value of the attribute that the element must include
130
- # @return [Element] the element of type tag who's attribute includes value
131
- def find_ele_by_attr_include tag, attr, value
132
- @driver.find_element :xpath, %Q(#{tag}[contains(@#{attr}, '#{value}')])
133
- end
134
-
135
- # Get tags by attribute that include value.
136
- # @param tag [String] the tag name to match
137
- # @param attr [String] the attribute to compare
138
- # @param value [String] the value of the attribute that the element must include
139
- # @return [Array<Element>] the elements of type tag who's attribute includes value
140
- def find_eles_by_attr_include tag, attr, value
141
- @driver.find_elements :xpath, %Q(#{tag}[contains(@#{attr}, '#{value}')])
142
- end
56
+ # Check every 0.5 seconds to see if block.call returns a truthy value.
57
+ # Note this isn't a strict boolean true, any truthy value is accepted.
58
+ # false and nil are considered failures.
59
+ # Give up after 30 seconds.
60
+ # @param max_wait [Integer] the maximum time in seconds to wait for
61
+ # @param interval [Float] the time in seconds to wait after calling the block
62
+ # @param block [Block] the block to call
63
+ # @return [Object] the result of block.call
64
+ def wait_true max_wait=30, interval=0.5, &block
65
+ max_wait = 1 if max_wait <= 0
66
+ result = nil
67
+ timeout max_wait do
68
+ until (
69
+ begin
70
+ result = block.call
71
+ rescue Exception
72
+ end)
73
+ sleep interval
74
+ end
75
+ end
76
+ result
77
+ end
143
78
 
144
- # Get the first tag that includes text.
145
- # @param tag [String] the tag name to match
146
- # @param text [String] the text the element must include
147
- # @return [Element] the element of type tag that includes text
148
- # element.attribute(:text).include? text
149
- def find_ele_by_text_include tag, text
150
- find_ele_by_attr_include tag, :text, text
151
- end
79
+ # Navigate back.
80
+ # @return [void]
81
+ def back
82
+ @driver.navigate.back
83
+ end
152
84
 
153
- # Get the tags that include text.
154
- # @param tag [String] the tag name to match
155
- # @param text [String] the text the element must include
156
- # @return [Array<Element>] the elements of type tag that includes text
157
- # element.attribute(:text).include? text
158
- def find_eles_by_text_include tag, text
159
- find_eles_by_attr_include tag, :text, text
160
- end
85
+ # For Sauce Labs reporting. Returns the current session id.
86
+ def session_id
87
+ @driver.session_id
88
+ end
161
89
 
162
- # Get the first tag that matches tag_name
163
- # @param tag_name [String] the tag to match
164
- # @return [Element]
165
- def first_ele tag_name
166
- # XPath index starts at 1
167
- find_element :xpath, "//#{tag_name}[1]"
168
- end
90
+ # Returns the first element that matches the provided xpath.
91
+ #
92
+ # @param xpath_str [String] the XPath string
93
+ # @return [Element]
94
+ def xpath xpath_str
95
+ find_element :xpath, xpath_str
96
+ end
169
97
 
170
- # Get the last tag that matches tag_name
171
- # @param tag_name [String] the tag to match
172
- # @return [Element]
173
- def last_ele tag_name
174
- xpath "//#{tag_name}[last()]"
175
- end
98
+ # Returns all elements that match the provided xpath.
99
+ #
100
+ # @param xpath_str [String] the XPath string
101
+ # @return [Array<Element>]
102
+ def xpaths xpath_str
103
+ find_elements :xpath, xpath_str
104
+ end
176
105
 
177
- # Prints a JSON view of the current page
178
- # @return [void]
179
- def source
180
- ap get_source
181
- end
106
+ # Prints xml of the current page
107
+ # @return [void]
108
+ def source
109
+ doc = Nokogiri::XML(@driver.page_source) do |config|
110
+ config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
111
+ end
112
+ puts doc.to_xml indent: 2
113
+ end
182
114
 
183
- # Gets a JSON view of the current page
184
- # @return [JSON]
185
- def get_source
186
- # must set max nesting. default limit of 20 is too low for selendroid
187
- JSON.parse @driver.page_source, max_nesting: 9999
188
- end
115
+ # Returns XML string for the current page
116
+ # Same as driver.page_source
117
+ # @return [String]
118
+ def get_source
119
+ @driver.page_source
120
+ end
189
121
 
190
- # Returns the first element that exactly matches name
191
- #
192
- # @param name [String] the name to exactly match
193
- # @return [Element]
194
- def find_name name
195
- find_element :name, name
196
- end
122
+ # @private
123
+ # http://nokogiri.org/Nokogiri/XML/SAX.html
124
+ class CountElements < Nokogiri::XML::SAX::Document
125
+ attr_reader :result
197
126
 
198
- # Returns all elements that exactly match name
199
- #
200
- # @param name [String] the name to exactly match
201
- # @return [Array<Element>]
202
- def find_names name
203
- find_elements :name, name
204
- end
127
+ def initialize
128
+ reset
129
+ end
205
130
 
206
- # Returns the first element matching tag_name
207
- #
208
- # @param tag_name [String] the tag_name to search for
209
- # @return [Element]
210
- def tag tag_name
211
- find_element :tag_name, tag_name
212
- end
131
+ def reset
132
+ @result = Hash.new 0
133
+ end
213
134
 
214
- # Returns all elements matching tag_name
215
- #
216
- # @param tag_name [String] the tag_name to search for
217
- # @return [Element]
218
- def tags tag_name
219
- find_elements :tag_name, tag_name
220
- end
135
+ # http://nokogiri.org/Nokogiri/XML/SAX/Document.html
136
+ def start_element name, attrs = []
137
+ @result[name] += 1
138
+ end
221
139
 
222
- # Converts pixel values to window relative values
223
- #
224
- # ```ruby
225
- # px_to_window_rel x: 50, y: 150
226
- # ```
140
+ def formatted_result
141
+ message = ''
142
+ sorted = @result.sort_by { |element, count| count }.reverse
143
+ sorted.each do |element, count|
144
+ message += "#{count}x #{element}\n"
145
+ end
146
+ message.strip
147
+ end
148
+ end # class CountElements
227
149
 
228
- def px_to_window_rel opts={}
150
+ # Returns a string of class counts.
151
+ def get_page_class
152
+ parser = @count_elements_parser ||= Nokogiri::XML::SAX::Parser.new(CountElements.new)
229
153
 
230
- w = $driver.window_size
231
- x = opts.fetch :x, 0
232
- y = opts.fetch :y, 0
154
+ parser.document.reset
155
+ parser.parse get_source
233
156
 
234
- OpenStruct.new( x: "#{x.to_f} / #{w.width.to_f}",
235
- y: "#{y.to_f} / #{w.height.to_f}" )
236
- end
157
+ parser.document.formatted_result
158
+ end
237
159
 
238
- def lazy_load_strings
239
- @strings_xml ||= mobile(:getStrings)
240
- end
160
+ # Count all classes on screen and print to stdout.
161
+ # Useful for appium_console.
162
+ def page_class
163
+ puts get_page_class
164
+ nil
165
+ end
241
166
 
242
- # Search strings.xml's values for target.
243
- # @param target [String] the target to search for in strings.xml values
244
- # @return [Array]
245
- def xml_keys target
246
- lazy_load_strings
247
- @strings_xml.select { |key, value| key.downcase.include? target.downcase }
248
- end
167
+ # Converts pixel values to window relative values
168
+ #
169
+ # ```ruby
170
+ # px_to_window_rel x: 50, y: 150
171
+ # ```
172
+ def px_to_window_rel opts={}
173
+ w = $driver.window_size
174
+ x = opts.fetch :x, 0
175
+ y = opts.fetch :y, 0
176
+
177
+ OpenStruct.new(x: "#{x.to_f} / #{w.width.to_f}",
178
+ y: "#{y.to_f} / #{w.height.to_f}")
179
+ end
249
180
 
250
- # Search strings.xml's keys for target.
251
- # @param target [String] the target to search for in strings.xml keys
252
- # @return [Array]
253
- def xml_values target
254
- lazy_load_strings
255
- @strings_xml.select { |key, value| value.downcase.include? target.downcase }
256
- end
181
+ # @private
182
+ def lazy_load_strings
183
+ @strings_xml ||= app_strings
184
+ end
257
185
 
258
- # Resolve id in strings.xml and return the value.
259
- # @param id [String] the id to resolve
260
- # @return [String]
261
- def resolve_id id
262
- lazy_load_strings
263
- @strings_xml[id]
264
- end
186
+ # Search strings.xml's values for target.
187
+ # @param target [String] the target to search for in strings.xml values
188
+ # @return [Array]
189
+ def xml_keys target
190
+ lazy_load_strings
191
+ @strings_xml.select { |key, value| key.downcase.include? target.downcase }
192
+ end
265
193
 
266
- # Used to error when finding a single element fails.
267
- def raise_no_element_error
268
- raise Selenium::WebDriver::Error::NoSuchElementError, 'An element could not be located on the page using the given search parameters.'
269
- end
194
+ # Search strings.xml's keys for target.
195
+ # @param target [String] the target to search for in strings.xml keys
196
+ # @return [Array]
197
+ def xml_values target
198
+ lazy_load_strings
199
+ @strings_xml.select { |key, value| value.downcase.include? target.downcase }
200
+ end
270
201
 
271
- end # module Appium::Common
202
+ # Resolve id in strings.xml and return the value.
203
+ # @param id [String] the id to resolve
204
+ # @return [String]
205
+ def resolve_id id
206
+ lazy_load_strings
207
+ @strings_xml[id]
208
+ end
209
+ end # module Common
210
+ end # module Appium