appium_lib 0.24.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +17 -8
- data/android_tests/Gemfile +1 -0
- data/android_tests/LICENSE-2.0.txt +202 -0
- data/android_tests/Rakefile +61 -0
- data/android_tests/api.apk +0 -0
- data/android_tests/appium.txt +3 -0
- data/android_tests/flaky.txt +1 -0
- data/android_tests/lib/android/specs/android/dynamic.rb +5 -0
- data/android_tests/lib/android/specs/android/element/alert.rb +41 -0
- data/android_tests/lib/android/specs/android/element/button.rb +55 -0
- data/android_tests/lib/android/specs/android/element/generic.rb +48 -0
- data/android_tests/lib/android/specs/android/element/text.rb +39 -0
- data/android_tests/lib/android/specs/android/element/textfield.rb +60 -0
- data/android_tests/lib/android/specs/android/helper.rb +80 -0
- data/android_tests/lib/android/specs/android/patch.rb +14 -0
- data/android_tests/lib/android/specs/common/device.rb +117 -0
- data/android_tests/lib/android/specs/common/element/window.rb +9 -0
- data/android_tests/lib/android/specs/common/helper.rb +112 -0
- data/android_tests/lib/android/specs/common/patch.rb +69 -0
- data/android_tests/lib/android/specs/common/version.rb +9 -0
- data/android_tests/lib/android/specs/driver.rb +174 -0
- data/android_tests/lib/format.rb +49 -0
- data/android_tests/lib/run.rb +72 -0
- data/android_tests/readme.md +27 -0
- data/appium_lib.gemspec +8 -5
- data/docs/android_docs.md +1052 -716
- data/docs/ios_docs.md +657 -834
- data/docs_gen/make_docs.rb +1 -3
- data/ios_tests/Gemfile +1 -0
- data/ios_tests/LICENSE-2.0.txt +202 -0
- data/ios_tests/Rakefile +47 -0
- data/ios_tests/UICatalog.app.zip +0 -0
- data/ios_tests/UICatalog.app/12-6AM.png +0 -0
- data/ios_tests/UICatalog.app/12-6PM.png +0 -0
- data/ios_tests/UICatalog.app/6-12AM.png +0 -0
- data/ios_tests/UICatalog.app/6-12PM.png +0 -0
- data/ios_tests/UICatalog.app/Default-568h@2x.png +0 -0
- data/ios_tests/UICatalog.app/Default@2x.png +0 -0
- data/ios_tests/UICatalog.app/Info.plist +0 -0
- data/ios_tests/UICatalog.app/PkgInfo +1 -0
- data/ios_tests/UICatalog.app/UIButton_custom.png +0 -0
- data/ios_tests/UICatalog.app/UICatalog +0 -0
- data/ios_tests/UICatalog.app/blueButton.png +0 -0
- data/ios_tests/UICatalog.app/bookmarkImage.png +0 -0
- data/ios_tests/UICatalog.app/bookmarkImageHighlighted.png +0 -0
- data/ios_tests/UICatalog.app/divider.png +0 -0
- data/ios_tests/UICatalog.app/en.lproj/AlertsViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ButtonsViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ControlsViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ImagesViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/Localizable.strings +0 -0
- data/ios_tests/UICatalog.app/en.lproj/MainWindow.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/PickerViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/SearchBarController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/SegmentViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/TextFieldController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/TextViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ToolbarViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/TransitionViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/WebViewController.nib +0 -0
- data/ios_tests/UICatalog.app/orangeslide.png +0 -0
- data/ios_tests/UICatalog.app/scene1.jpg +0 -0
- data/ios_tests/UICatalog.app/scene2.jpg +0 -0
- data/ios_tests/UICatalog.app/scene3.jpg +0 -0
- data/ios_tests/UICatalog.app/scene4.jpg +0 -0
- data/ios_tests/UICatalog.app/scene5.jpg +0 -0
- data/ios_tests/UICatalog.app/searchBarBackground.png +0 -0
- data/ios_tests/UICatalog.app/segment_check.png +0 -0
- data/ios_tests/UICatalog.app/segment_search.png +0 -0
- data/ios_tests/UICatalog.app/segment_tools.png +0 -0
- data/ios_tests/UICatalog.app/segmentedBackground.png +0 -0
- data/ios_tests/UICatalog.app/slider_ball.png +0 -0
- data/ios_tests/UICatalog.app/toolbarBackground.png +0 -0
- data/ios_tests/UICatalog.app/whiteButton.png +0 -0
- data/ios_tests/UICatalog.app/yellowslide.png +0 -0
- data/ios_tests/appium.txt +3 -0
- data/ios_tests/flaky.txt +1 -0
- data/ios_tests/lib/format.rb +25 -0
- data/ios_tests/lib/ios/specs/common/element/window.rb +15 -0
- data/ios_tests/lib/ios/specs/common/helper.rb +204 -0
- data/ios_tests/lib/ios/specs/common/patch.rb +50 -0
- data/ios_tests/lib/ios/specs/common/version.rb +17 -0
- data/ios_tests/lib/ios/specs/device/device.rb +82 -0
- data/ios_tests/lib/ios/specs/device/multi_touch.rb +12 -0
- data/ios_tests/lib/ios/specs/device/touch_actions.rb +15 -0
- data/ios_tests/lib/ios/specs/driver.rb +203 -0
- data/ios_tests/lib/ios/specs/ios/element/alert.rb +48 -0
- data/ios_tests/lib/ios/specs/ios/element/button.rb +58 -0
- data/ios_tests/lib/ios/specs/ios/element/generic.rb +35 -0
- data/ios_tests/lib/ios/specs/ios/element/text.rb +54 -0
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +123 -0
- data/ios_tests/lib/ios/specs/ios/helper.rb +27 -0
- data/ios_tests/lib/ios/specs/ios/patch.rb +30 -0
- data/ios_tests/lib/run.rb +106 -0
- data/ios_tests/readme.md +30 -0
- data/ios_tests/upload/sauce_storage.rb +64 -0
- data/ios_tests/upload/upload.rb +6 -0
- data/lib/appium_lib.rb +4 -14
- data/lib/appium_lib/android/dynamic.rb +30 -32
- data/lib/appium_lib/android/element/alert.rb +34 -33
- data/lib/appium_lib/android/element/button.rb +91 -0
- data/lib/appium_lib/android/element/generic.rb +51 -146
- data/lib/appium_lib/android/element/text.rb +54 -0
- data/lib/appium_lib/android/element/textfield.rb +46 -41
- data/lib/appium_lib/android/helper.rb +248 -417
- data/lib/appium_lib/android/mobile_methods.rb +17 -0
- data/lib/appium_lib/android/patch.rb +9 -8
- data/lib/appium_lib/awesome_print/ostruct.rb +33 -0
- data/lib/appium_lib/common/element/window.rb +9 -8
- data/lib/appium_lib/common/helper.rb +182 -243
- data/lib/appium_lib/common/patch.rb +65 -79
- data/lib/appium_lib/common/version.rb +2 -3
- data/lib/appium_lib/device/device.rb +339 -0
- data/lib/appium_lib/device/multi_touch.rb +94 -0
- data/lib/appium_lib/device/touch_actions.rb +142 -0
- data/lib/appium_lib/driver.rb +217 -306
- data/lib/appium_lib/ios/element/alert.rb +16 -92
- data/lib/appium_lib/ios/element/button.rb +55 -0
- data/lib/appium_lib/ios/element/generic.rb +27 -160
- data/lib/appium_lib/ios/element/text.rb +54 -0
- data/lib/appium_lib/ios/element/textfield.rb +78 -65
- data/lib/appium_lib/ios/helper.rb +300 -190
- data/lib/appium_lib/ios/mobile_methods.rb +17 -0
- data/lib/appium_lib/ios/patch.rb +55 -41
- data/lib/appium_lib/logger.rb +13 -0
- data/lib/appium_lib/rails/duplicable.rb +116 -0
- data/readme.md +6 -1
- data/release_notes.md +118 -0
- metadata +170 -12
- data/lib/appium_lib/common/element/button.rb +0 -83
- data/lib/appium_lib/common/element/text.rb +0 -61
@@ -0,0 +1,54 @@
|
|
1
|
+
# TextView methods
|
2
|
+
module Appium
|
3
|
+
module Android
|
4
|
+
TextView = 'android.widget.TextView'
|
5
|
+
|
6
|
+
# Find the first TextView that contains value or by index.
|
7
|
+
# @param value [String, Integer] the value to find.
|
8
|
+
# If int then the TextView at that index is returned.
|
9
|
+
# @return [TextView]
|
10
|
+
def s_text value
|
11
|
+
return ele_index TextView, value if value.is_a? Numeric
|
12
|
+
xpath_visible_contains TextView, value
|
13
|
+
end
|
14
|
+
|
15
|
+
# Find all TextViews containing value.
|
16
|
+
# @param value [String] the value to search for
|
17
|
+
# @return [Array<TextView>]
|
18
|
+
def s_texts value
|
19
|
+
xpaths_visible_contains TextView, value
|
20
|
+
end
|
21
|
+
|
22
|
+
# Find the first TextView.
|
23
|
+
# @return [TextView]
|
24
|
+
def first_s_text
|
25
|
+
first_ele TextView
|
26
|
+
end
|
27
|
+
|
28
|
+
# Find the last TextView.
|
29
|
+
# @return [TextView]
|
30
|
+
def last_s_text
|
31
|
+
last_ele TextView
|
32
|
+
end
|
33
|
+
|
34
|
+
# Find the first TextView that exactly matches value.
|
35
|
+
# @param value [String] the value to match exactly
|
36
|
+
# @return [TextView]
|
37
|
+
def s_text_exact value
|
38
|
+
xpath_visible_exact TextView, value
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find all TextViews that exactly match value.
|
42
|
+
# @param value [String] the value to match exactly
|
43
|
+
# @return [Array<TextView>]
|
44
|
+
def s_texts_exact value
|
45
|
+
xpaths_visible_exact TextView, value
|
46
|
+
end
|
47
|
+
|
48
|
+
# Find all TextViews.
|
49
|
+
# @return [Array<TextView>]
|
50
|
+
def e_s_texts
|
51
|
+
tags TextView
|
52
|
+
end
|
53
|
+
end # module Android
|
54
|
+
end # module Appium
|
@@ -1,48 +1,53 @@
|
|
1
|
-
|
2
|
-
module
|
3
|
-
|
1
|
+
module Appium
|
2
|
+
module Android
|
3
|
+
EditText = 'android.widget.EditText'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# Find the first EditText that contains value or by index.
|
6
|
+
# @param value [String, Integer] the text to match exactly.
|
7
|
+
# If int then the EditText at that index is returned.
|
8
|
+
# @return [EditText]
|
9
|
+
def textfield value
|
10
|
+
return ele_index EditText, value if value.is_a? Numeric
|
11
|
+
xpath_visible_contains EditText, value
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
# Find all EditTexts containing value.
|
15
|
+
# @param value [String] the value to search for
|
16
|
+
# @return [Array<EditText>]
|
17
|
+
def textfields value
|
18
|
+
xpaths_visible_contains EditText, value
|
19
|
+
end
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
# Find the first EditText.
|
22
|
+
# @return [EditText]
|
23
|
+
def first_textfield
|
24
|
+
first_ele EditText
|
25
|
+
end
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
# Find the last EditText.
|
28
|
+
# @return [EditText]
|
29
|
+
def last_textfield
|
30
|
+
last_ele EditText
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
# Find the first EditText that exactly matches value.
|
34
|
+
# @param value [String] the value to match exactly
|
35
|
+
# @return [EditText]
|
36
|
+
def textfield_exact value
|
37
|
+
xpath_visible_exact EditText, value
|
38
|
+
end
|
34
39
|
|
35
|
-
#
|
36
|
-
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
# Find all EditTexts that exactly match value.
|
41
|
+
# @param value [String] the value to match exactly
|
42
|
+
# @return [Array<EditText>]
|
43
|
+
def textfields_exact value
|
44
|
+
xpaths_visible_exact EditText, value
|
45
|
+
end
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end # module Appium
|
47
|
+
# Find all EditTexts.
|
48
|
+
# @return [Array<EditText>]
|
49
|
+
def e_textfields
|
50
|
+
tags EditText
|
51
|
+
end
|
52
|
+
end # module Android
|
53
|
+
end # module Appium
|
@@ -1,447 +1,278 @@
|
|
1
|
-
|
2
|
-
module
|
3
|
-
# Returns an array of android classes that match the tag name
|
4
|
-
# @param tag_name [String] the tag name to convert to an android class
|
5
|
-
# @return [String]
|
6
|
-
def tag_name_to_android tag_name
|
7
|
-
tag_name = tag_name.to_s.downcase.strip
|
8
|
-
|
1
|
+
module Appium
|
2
|
+
module Android
|
9
3
|
# @private
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# because android can't tell what a secure textfield is
|
15
|
-
# they're all edittexts.
|
16
|
-
|
17
|
-
# must match names in AndroidElementClassMap (Appium's Java server)
|
18
|
-
case tag_name
|
19
|
-
when 'abslist'
|
20
|
-
prefix 'AbsListView'
|
21
|
-
when 'absseek'
|
22
|
-
prefix 'AbsSeekBar'
|
23
|
-
when 'absspinner'
|
24
|
-
prefix 'AbsSpinner'
|
25
|
-
when 'absolute'
|
26
|
-
prefix 'AbsoluteLayout'
|
27
|
-
when 'adapterview'
|
28
|
-
prefix 'AdapterView'
|
29
|
-
when 'adapterviewanimator'
|
30
|
-
prefix 'AdapterViewAnimator'
|
31
|
-
when 'adapterviewflipper'
|
32
|
-
prefix 'AdapterViewFlipper'
|
33
|
-
when 'analogclock'
|
34
|
-
prefix 'AnalogClock'
|
35
|
-
when 'appwidgethost'
|
36
|
-
prefix 'AppWidgetHostView'
|
37
|
-
when 'autocomplete'
|
38
|
-
prefix 'AutoCompleteTextView'
|
39
|
-
when 'button'
|
40
|
-
prefix 'Button', 'ImageButton'
|
41
|
-
when 'breadcrumbs'
|
42
|
-
prefix 'FragmentBreadCrumbs'
|
43
|
-
when 'calendar'
|
44
|
-
prefix 'CalendarView'
|
45
|
-
when 'checkbox'
|
46
|
-
prefix 'CheckBox'
|
47
|
-
when 'checked'
|
48
|
-
prefix 'CheckedTextView'
|
49
|
-
when 'chronometer'
|
50
|
-
prefix 'Chronometer'
|
51
|
-
when 'compound'
|
52
|
-
prefix 'CompoundButton'
|
53
|
-
when 'datepicker'
|
54
|
-
prefix 'DatePicker'
|
55
|
-
when 'dialerfilter'
|
56
|
-
prefix 'DialerFilter'
|
57
|
-
when 'digitalclock'
|
58
|
-
prefix 'DigitalClock'
|
59
|
-
when 'drawer'
|
60
|
-
prefix 'SlidingDrawer'
|
61
|
-
when 'expandable'
|
62
|
-
prefix 'ExpandableListView'
|
63
|
-
when 'extract'
|
64
|
-
prefix 'ExtractEditText'
|
65
|
-
when 'fragmenttabhost'
|
66
|
-
prefix 'FragmentTabHost'
|
67
|
-
when 'frame'
|
68
|
-
prefix 'FrameLayout'
|
69
|
-
when 'gallery'
|
70
|
-
prefix 'Gallery'
|
71
|
-
when 'gesture'
|
72
|
-
prefix 'GestureOverlayView'
|
73
|
-
when 'glsurface'
|
74
|
-
prefix 'GLSurfaceView'
|
75
|
-
when 'grid'
|
76
|
-
prefix 'GridView'
|
77
|
-
when 'gridlayout'
|
78
|
-
prefix 'GridLayout'
|
79
|
-
when 'horizontal'
|
80
|
-
prefix 'HorizontalScrollView'
|
81
|
-
when 'image'
|
82
|
-
prefix 'ImageView'
|
83
|
-
when 'imagebutton'
|
84
|
-
prefix 'ImageButton'
|
85
|
-
when 'imageswitcher'
|
86
|
-
prefix 'ImageSwitcher'
|
87
|
-
when 'keyboard'
|
88
|
-
prefix 'KeyboardView'
|
89
|
-
when 'linear'
|
90
|
-
prefix 'LinearLayout'
|
91
|
-
when 'list'
|
92
|
-
prefix 'ListView'
|
93
|
-
when 'media'
|
94
|
-
prefix 'MediaController'
|
95
|
-
when 'mediaroutebutton'
|
96
|
-
prefix 'MediaRouteButton'
|
97
|
-
when 'multiautocomplete'
|
98
|
-
prefix 'MultiAutoCompleteTextView'
|
99
|
-
when 'numberpicker'
|
100
|
-
prefix 'NumberPicker'
|
101
|
-
when 'pagetabstrip'
|
102
|
-
prefix 'PageTabStrip'
|
103
|
-
when 'pagetitlestrip'
|
104
|
-
prefix 'PageTitleStrip'
|
105
|
-
when 'progress'
|
106
|
-
prefix 'ProgressBar'
|
107
|
-
when 'quickcontactbadge'
|
108
|
-
prefix 'QuickContactBadge'
|
109
|
-
when 'radio'
|
110
|
-
prefix 'RadioButton'
|
111
|
-
when 'radiogroup'
|
112
|
-
prefix 'RadioGroup'
|
113
|
-
when 'rating'
|
114
|
-
prefix 'RatingBar'
|
115
|
-
when 'relative'
|
116
|
-
prefix 'RelativeLayout'
|
117
|
-
when 'row'
|
118
|
-
prefix 'TableRow'
|
119
|
-
when 'rssurface'
|
120
|
-
prefix 'RSSurfaceView'
|
121
|
-
when 'rstexture'
|
122
|
-
prefix 'RSTextureView'
|
123
|
-
when 'scroll'
|
124
|
-
prefix 'ScrollView'
|
125
|
-
when 'search'
|
126
|
-
prefix 'SearchView'
|
127
|
-
when 'seek'
|
128
|
-
prefix 'SeekBar'
|
129
|
-
when 'space'
|
130
|
-
prefix 'Space'
|
131
|
-
when 'spinner'
|
132
|
-
prefix 'Spinner'
|
133
|
-
when 'stack'
|
134
|
-
prefix 'StackView'
|
135
|
-
when 'surface'
|
136
|
-
prefix 'SurfaceView'
|
137
|
-
when 'switch'
|
138
|
-
prefix 'Switch'
|
139
|
-
when 'tabhost'
|
140
|
-
prefix 'TabHost'
|
141
|
-
when 'tabwidget'
|
142
|
-
prefix 'TabWidget'
|
143
|
-
when 'table'
|
144
|
-
prefix 'TableLayout'
|
145
|
-
when 'text'
|
146
|
-
prefix 'TextView'
|
147
|
-
when 'textclock'
|
148
|
-
prefix 'TextClock'
|
149
|
-
when 'textswitcher'
|
150
|
-
prefix 'TextSwitcher'
|
151
|
-
when 'texture'
|
152
|
-
prefix 'TextureView'
|
153
|
-
when 'textfield'
|
154
|
-
prefix 'EditText'
|
155
|
-
when 'timepicker'
|
156
|
-
prefix 'TimePicker'
|
157
|
-
when 'toggle'
|
158
|
-
prefix 'ToggleButton'
|
159
|
-
when 'twolinelistitem'
|
160
|
-
prefix 'TwoLineListItem'
|
161
|
-
when 'view'
|
162
|
-
'android.view.View'
|
163
|
-
when 'video'
|
164
|
-
prefix 'VideoView'
|
165
|
-
when 'viewanimator'
|
166
|
-
prefix 'ViewAnimator'
|
167
|
-
when 'viewflipper'
|
168
|
-
prefix 'ViewFlipper'
|
169
|
-
when 'viewgroup'
|
170
|
-
prefix 'ViewGroup'
|
171
|
-
when 'viewpager'
|
172
|
-
prefix 'ViewPager'
|
173
|
-
when 'viewstub'
|
174
|
-
prefix 'ViewStub'
|
175
|
-
when 'viewswitcher'
|
176
|
-
prefix 'ViewSwitcher'
|
177
|
-
when 'web'
|
178
|
-
'android.webkit.WebView' # WebView is not a widget
|
179
|
-
when 'window'
|
180
|
-
prefix 'FrameLayout'
|
181
|
-
when 'zoom'
|
182
|
-
prefix 'ZoomButton'
|
183
|
-
when 'zoomcontrols'
|
184
|
-
prefix 'ZoomControls'
|
185
|
-
else
|
186
|
-
raise "Invalid tag name #{tag_name}"
|
187
|
-
end # return result of case
|
188
|
-
end
|
189
|
-
# Find all elements matching the attribute
|
190
|
-
# On android, assume the attr is name (which falls back to text).
|
191
|
-
#
|
192
|
-
# ```ruby
|
193
|
-
# find_eles_attr :text
|
194
|
-
# ```
|
195
|
-
#
|
196
|
-
# @param tag_name [String] the tag name to search for
|
197
|
-
# @return [Element]
|
198
|
-
def find_eles_attr tag_name, attribute=nil
|
199
|
-
=begin
|
200
|
-
sel1 = [ [4, 'android.widget.Button'], [100] ]
|
201
|
-
sel2 = [ [4, 'android.widget.ImageButton'], [100] ]
|
202
|
-
|
203
|
-
args = [ 'all', sel1, sel2 ]
|
204
|
-
|
205
|
-
mobile :find, args
|
206
|
-
=end
|
207
|
-
array = ['all']
|
208
|
-
|
209
|
-
tag_name_to_android(tag_name).each do |name|
|
210
|
-
# sel.className(name).getStringAttribute("name")
|
211
|
-
array.push [ [4, name], [100] ]
|
212
|
-
end
|
4
|
+
# http://nokogiri.org/Nokogiri/XML/SAX.html
|
5
|
+
class AndroidElements < Nokogiri::XML::SAX::Document
|
6
|
+
# TODO: Support strings.xml ids
|
7
|
+
attr_reader :result, :keys
|
213
8
|
|
214
|
-
|
215
|
-
|
9
|
+
def filter
|
10
|
+
@filter
|
11
|
+
end
|
216
12
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
r = []
|
13
|
+
# convert to string to support symbols
|
14
|
+
def filter= value
|
15
|
+
# nil and false disable the filter
|
16
|
+
return @filter = false unless value
|
17
|
+
@filter = value.to_s.downcase
|
18
|
+
end
|
224
19
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
20
|
+
def initialize
|
21
|
+
reset
|
22
|
+
@filter = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset
|
26
|
+
@result = ''
|
27
|
+
@keys = %w[text resource-id content-desc]
|
28
|
+
end
|
29
|
+
|
30
|
+
# http://nokogiri.org/Nokogiri/XML/SAX/Document.html
|
31
|
+
def start_element name, attrs = []
|
32
|
+
return if filter && !name.downcase.include?(filter)
|
33
|
+
|
34
|
+
attributes = {}
|
35
|
+
|
36
|
+
attrs.each do |key, value|
|
37
|
+
if keys.include?(key) && !value.empty?
|
38
|
+
attributes[key] = value
|
39
|
+
end
|
229
40
|
end
|
230
41
|
|
231
|
-
|
232
|
-
|
42
|
+
string = ''
|
43
|
+
text = attributes['text']
|
44
|
+
desc = attributes['content-desc']
|
45
|
+
id = attributes['resource-id']
|
233
46
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
obj.merge!( { shown: node['shown'] } ) if keys.include?('shown')
|
47
|
+
if !text.nil? && text == desc
|
48
|
+
string += " text, desc: #{text}\n"
|
49
|
+
else
|
50
|
+
string += " text: #{text}\n" unless text.nil?
|
51
|
+
string += " desc: #{desc}\n" unless desc.nil?
|
52
|
+
end
|
53
|
+
string += " id: #{id}\n" unless id.nil?
|
242
54
|
|
243
|
-
|
244
|
-
run_internal.call node['children'] if keys.include?('children')
|
55
|
+
@result += "\n#{name}\n#{string}" unless attributes.empty?
|
245
56
|
end
|
57
|
+
end # class AndroidElements
|
58
|
+
|
59
|
+
# Android only.
|
60
|
+
# Returns a string containing interesting elements.
|
61
|
+
# The text, content description, and id are returned.
|
62
|
+
# @param class_name [String] the class name to filter on.
|
63
|
+
# if false (default) then all classes will be inspected
|
64
|
+
# @return [String]
|
65
|
+
def get_android_inspect class_name=false
|
66
|
+
parser = @android_elements_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
|
67
|
+
|
68
|
+
parser.document.reset
|
69
|
+
parser.document.filter = class_name
|
70
|
+
parser.parse get_source
|
71
|
+
|
72
|
+
parser.document.result
|
73
|
+
end
|
74
|
+
|
75
|
+
# Intended for use with console.
|
76
|
+
# Inspects and prints the current page.
|
77
|
+
# @param class_name [String] the class name to filter on.
|
78
|
+
# if false (default) then all classes will be inspected
|
79
|
+
# @return [void]
|
80
|
+
def page class_name=false
|
81
|
+
puts get_android_inspect class_name
|
82
|
+
nil
|
83
|
+
end
|
246
84
|
|
247
|
-
|
248
|
-
|
85
|
+
# Lists package, activity, and adb shell am start -n value for current app.
|
86
|
+
# Works on local host only (not remote).
|
87
|
+
# noinspection RubyArgCount
|
88
|
+
def current_app
|
89
|
+
line = `adb shell dumpsys window windows`.each_line.grep(/mFocusedApp/).first.strip
|
90
|
+
pair = line.split(' ').last.gsub('}', '').split '/'
|
91
|
+
pkg = pair.first
|
92
|
+
act = pair.last
|
93
|
+
OpenStruct.new(line: line,
|
94
|
+
package: pkg,
|
95
|
+
activity: act,
|
96
|
+
am_start: pkg + '/' + act)
|
249
97
|
end
|
250
98
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
# find_element(:link_text, 'text')
|
273
|
-
out += " text: #{e[:text]}\n" unless no_text
|
274
|
-
# label is name. default is 'null'
|
275
|
-
# find_element(:link_text, 'Facebook')
|
276
|
-
out += " name: #{e[:name]}\n" unless no_name
|
277
|
-
# out += " visible: #{e[:shown]}\n" unless e[:shown].nil?
|
278
|
-
}
|
279
|
-
out
|
280
|
-
end
|
281
|
-
|
282
|
-
def get_page_class
|
283
|
-
r = []
|
284
|
-
run_internal = lambda do |node|
|
285
|
-
if node.kind_of? Array
|
286
|
-
node.each { |node| run_internal.call node }
|
287
|
-
return
|
99
|
+
# Find by id
|
100
|
+
# @param id [String] the id to search for
|
101
|
+
# @return [Element]
|
102
|
+
def id id
|
103
|
+
value = resolve_id id
|
104
|
+
# If the id doesn't resolve in strings.xml then use it as is
|
105
|
+
# It's probably a resource id which won't be in strings.xml
|
106
|
+
value = id unless value
|
107
|
+
exact = string_visible_exact '*', value
|
108
|
+
contains = string_visible_contains '*', value
|
109
|
+
xpath "#{exact} | #{contains}"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find the element of type class_name at matching index.
|
113
|
+
# @param class_name [String] the class name to find
|
114
|
+
# @param index [Integer] the index
|
115
|
+
# @return [Element] the found element of type class_name
|
116
|
+
def ele_index class_name, index
|
117
|
+
unless index == 'last()'
|
118
|
+
# XPath index starts at 1.
|
119
|
+
raise "#{index} is not a valid xpath index. Must be >= 1" if index <= 0
|
288
120
|
end
|
121
|
+
find_element :xpath, %Q(//#{class_name}[#{index}])
|
122
|
+
end
|
289
123
|
|
290
|
-
|
291
|
-
|
292
|
-
|
124
|
+
# @private
|
125
|
+
def string_attr_exact class_name, attr, value
|
126
|
+
%Q(//#{class_name}[@#{attr}='#{value}'])
|
127
|
+
end
|
293
128
|
|
294
|
-
|
129
|
+
# Find the first element exactly matching class and attribute value.
|
130
|
+
# @param class_name [String] the class name to search for
|
131
|
+
# @param attr [String] the attribute to inspect
|
132
|
+
# @param value [String] the expected value of the attribute
|
133
|
+
# @return [Element]
|
134
|
+
def find_ele_by_attr class_name, attr, value
|
135
|
+
@driver.find_element :xpath, string_attr_exact(class_name, attr, value)
|
295
136
|
end
|
296
|
-
json = get_source
|
297
|
-
run_internal.call json['hierarchy']
|
298
137
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
138
|
+
# Find all elements exactly matching class and attribute value.
|
139
|
+
# @param class_name [String] the class name to match
|
140
|
+
# @param attr [String] the attribute to compare
|
141
|
+
# @param value [String] the value of the attribute that the element must have
|
142
|
+
# @return [Array<Element>]
|
143
|
+
def find_eles_by_attr class_name, attr, value
|
144
|
+
@driver.find_elements :xpath, string_attr_exact(class_name, attr, value)
|
303
145
|
end
|
304
|
-
|
305
|
-
res.sort(&count_sort).join ''
|
306
|
-
end
|
307
|
-
|
308
|
-
# Count all classes on screen and print to stdout.
|
309
|
-
# Useful for appium_console.
|
310
|
-
def page_class
|
311
|
-
puts get_page_class
|
312
|
-
nil
|
313
|
-
end
|
314
|
-
|
315
|
-
# Android only.
|
316
|
-
# Returns a string containing interesting elements.
|
317
|
-
# If an element has no content desc or text, then it's not returned by this method.
|
318
|
-
# @return [String]
|
319
|
-
def get_android_inspect
|
146
|
+
|
320
147
|
# @private
|
321
|
-
def
|
322
|
-
|
148
|
+
def string_attr_include class_name, attr, value
|
149
|
+
%Q(//#{class_name}[contains(translate(@#{attr},'#{value.upcase}', '#{value}'), '#{value}')])
|
150
|
+
end
|
323
151
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
152
|
+
# Find the first element by attribute that exactly matches value.
|
153
|
+
# @param class_name [String] the class name to match
|
154
|
+
# @param attr [String] the attribute to compare
|
155
|
+
# @param value [String] the value of the attribute that the element must include
|
156
|
+
# @return [Element] the element of type tag who's attribute includes value
|
157
|
+
def find_ele_by_attr_include class_name, attr, value
|
158
|
+
@driver.find_element :xpath, string_attr_include(class_name, attr, value)
|
159
|
+
end
|
329
160
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
161
|
+
# Find elements by attribute that include value.
|
162
|
+
# @param class_name [String] the tag name to match
|
163
|
+
# @param attr [String] the attribute to compare
|
164
|
+
# @param value [String] the value of the attribute that the element must include
|
165
|
+
# @return [Array<Element>] the elements of type tag who's attribute includes value
|
166
|
+
def find_eles_by_attr_include class_name, attr, value
|
167
|
+
@driver.find_elements :xpath, string_attr_include(class_name, attr, value)
|
168
|
+
end
|
336
169
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
# If it only has a class, then don't save it.
|
345
|
-
obj = {}
|
346
|
-
obj.merge!( { desc: node[n_content] } ) if keys.include?(n_content) && !node[n_content].empty?
|
347
|
-
obj.merge!( { text: node[n_text] } ) if keys.include?(n_text) && !node[n_text].empty?
|
348
|
-
obj.merge!( { resource_id: node[n_resource] } ) if keys.include?(n_resource) && !node[n_resource].empty?
|
349
|
-
obj.merge!( { class: node[n_class] } ) if keys.include?(n_class) && !obj.empty?
|
350
|
-
|
351
|
-
r.push obj if !obj.empty?
|
352
|
-
run_internal.call node[n_node] if keys.include?(n_node)
|
353
|
-
end
|
170
|
+
# Find the first element that matches class_name
|
171
|
+
# @param class_name [String] the tag to match
|
172
|
+
# @return [Element]
|
173
|
+
def first_ele class_name
|
174
|
+
# XPath index starts at 1
|
175
|
+
ele_index class_name, 1
|
176
|
+
end
|
354
177
|
|
355
|
-
|
356
|
-
|
178
|
+
# Find the last element that matches class_name
|
179
|
+
# @param class_name [String] the tag to match
|
180
|
+
# @return [Element]
|
181
|
+
def last_ele class_name
|
182
|
+
ele_index class_name, 'last()'
|
357
183
|
end
|
358
184
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
e_desc = e[:desc]
|
367
|
-
e_text = e[:text]
|
368
|
-
e_class = e[:class]
|
369
|
-
e_resource_id = e[:resource_id]
|
370
|
-
out += e_class.split('.').last + "\n"
|
371
|
-
|
372
|
-
out += " class: #{e_class}\n"
|
373
|
-
if e_text == e_desc
|
374
|
-
out += " text, name: #{e_text}\n" unless e_text.nil?
|
375
|
-
else
|
376
|
-
out += " text: #{e_text}\n" unless e_text.nil?
|
377
|
-
out += " name: #{e_desc}\n" unless e_desc.nil?
|
378
|
-
end
|
185
|
+
# Find the first element of type class_name
|
186
|
+
#
|
187
|
+
# @param class_name [String] the class_name to search for
|
188
|
+
# @return [Element]
|
189
|
+
def tag class_name
|
190
|
+
xpath %Q(//#{class_name})
|
191
|
+
end
|
379
192
|
|
380
|
-
|
193
|
+
# Find all elements of type class_name
|
194
|
+
#
|
195
|
+
# @param class_name [String] the class_name to search for
|
196
|
+
# @return [Element]
|
197
|
+
def tags class_name
|
198
|
+
xpaths %Q(//#{class_name})
|
199
|
+
end
|
381
200
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
201
|
+
# @private
|
202
|
+
# Returns a string xpath that matches the first element that contains value
|
203
|
+
#
|
204
|
+
# example: xpath_visible_contains 'UIATextField', 'sign in'
|
205
|
+
#
|
206
|
+
# @param element [String] the class name for the element
|
207
|
+
# @param value [String] the value to search for
|
208
|
+
# @return [String]
|
209
|
+
def string_visible_contains element, value
|
210
|
+
result = []
|
211
|
+
attributes = %w[content-desc text]
|
212
|
+
|
213
|
+
value_up = value.upcase
|
214
|
+
value_down = value.downcase
|
215
|
+
|
216
|
+
attributes.each do |attribute|
|
217
|
+
result << %Q(contains(translate(@#{attribute},"#{value_up}","#{value_down}"), "#{value_down}"))
|
386
218
|
end
|
387
219
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
220
|
+
# never partial match on a resource id
|
221
|
+
result << %Q(@resource-id="#{value}")
|
222
|
+
|
223
|
+
result = result.join(' or ')
|
224
|
+
|
225
|
+
"//#{element}[#{result}]"
|
226
|
+
end
|
227
|
+
|
228
|
+
# Find the first element that contains value
|
229
|
+
# @param element [String] the class name for the element
|
230
|
+
# @param value [String] the value to search for
|
231
|
+
# @return [Element]
|
232
|
+
def xpath_visible_contains element, value
|
233
|
+
xpath string_visible_contains element, value
|
234
|
+
end
|
235
|
+
|
236
|
+
# Find all elements containing value
|
237
|
+
# @param element [String] the class name for the element
|
238
|
+
# @param value [String] the value to search for
|
239
|
+
# @return [Array<Element>]
|
240
|
+
def xpaths_visible_contains element, value
|
241
|
+
xpaths string_visible_contains element, value
|
242
|
+
end
|
243
|
+
|
244
|
+
# @private
|
245
|
+
# Create an xpath string to exactly match the first element with target value
|
246
|
+
# @param element [String] the class name for the element
|
247
|
+
# @param value [String] the value to search for
|
248
|
+
# @return [String]
|
249
|
+
def string_visible_exact element, value
|
250
|
+
result = []
|
251
|
+
attributes = %w[content-desc resource-id text]
|
252
|
+
|
253
|
+
attributes.each do |attribute|
|
254
|
+
result << %Q(@#{attribute}="#{value}")
|
395
255
|
end
|
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
|
-
# We want steps to be exactly 1. If it's zero then a tap is used instead of a swipe.
|
421
|
-
def fast_duration
|
422
|
-
0.0357 # 1.0/28.0
|
423
|
-
end
|
424
|
-
|
425
|
-
# Lists package, activity, and adb shell am start -n value for current app.
|
426
|
-
# Works on local host only (not remote).
|
427
|
-
def current_app
|
428
|
-
line = `adb shell dumpsys window windows`.each_line.grep(/mFocusedApp/).first.strip
|
429
|
-
pair = line.split(' ').last.gsub('}','').split '/'
|
430
|
-
pkg = pair.first
|
431
|
-
act = pair.last
|
432
|
-
OpenStruct.new line: line,
|
433
|
-
package: pkg,
|
434
|
-
activity: act,
|
435
|
-
am_start: pkg + '/' + act
|
436
|
-
end
|
437
|
-
|
438
|
-
# Find by id. Useful for selendroid
|
439
|
-
# @param id [String] the id to search for
|
440
|
-
# @return [Element]
|
441
|
-
def id id
|
442
|
-
lazy_load_strings
|
443
|
-
# resource ids must include ':' and they're not contained in strings_xml
|
444
|
-
raise "Invalid id `#{id}`" unless @strings_xml[id] || id.include?(':')
|
445
|
-
find_element :id, id
|
446
|
-
end
|
447
|
-
end # module Appium::Android
|
256
|
+
|
257
|
+
result = result.join(' or ')
|
258
|
+
|
259
|
+
"//#{element}[#{result}]"
|
260
|
+
end
|
261
|
+
|
262
|
+
# Find the first element exactly matching value
|
263
|
+
# @param element [String] the class name for the element
|
264
|
+
# @param value [String] the value to search for
|
265
|
+
# @return [Element]
|
266
|
+
def xpath_visible_exact element, value
|
267
|
+
xpath string_visible_exact element, value
|
268
|
+
end
|
269
|
+
|
270
|
+
# Find all elements exactly matching value
|
271
|
+
# @param element [String] the class name for the element
|
272
|
+
# @param value [String] the value to search for
|
273
|
+
# @return [Element]
|
274
|
+
def xpaths_visible_exact element, value
|
275
|
+
xpaths string_visible_exact element, value
|
276
|
+
end
|
277
|
+
end # module Android
|
278
|
+
end # module Appium
|