wda_lib 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/README.md +171 -0
- data/lib/selenium_patch/commands.rb +136 -0
- data/lib/selenium_patch/element_patch.rb +53 -0
- data/lib/selenium_patch/w3c_bridge_patch.rb +31 -0
- data/lib/wda_lib/alert.rb +21 -0
- data/lib/wda_lib/custom.rb +33 -0
- data/lib/wda_lib/debug.rb +26 -0
- data/lib/wda_lib/element.rb +183 -0
- data/lib/wda_lib/find_element.rb +244 -0
- data/lib/wda_lib/orientation.rb +21 -0
- data/lib/wda_lib/screenshot.rb +24 -0
- data/lib/wda_lib/session.rb +80 -0
- data/lib/wda_lib/touch_id.rb +15 -0
- data/lib/wda_lib/type.rb +91 -0
- data/lib/wda_lib/version.rb +3 -0
- data/lib/wda_lib.rb +111 -0
- data/wda_lib.gemspec +27 -0
- metadata +132 -0
@@ -0,0 +1,244 @@
|
|
1
|
+
class WDA
|
2
|
+
module FindElement
|
3
|
+
### Searching for elements
|
4
|
+
|
5
|
+
# Find element with given type and value, return first found element
|
6
|
+
# @param type [String, Symbol], value [Stirng]
|
7
|
+
# @return [Hash]
|
8
|
+
def find(type, value)
|
9
|
+
element = post('/element', { using: stringlize(type), value: value })
|
10
|
+
fail "Can't find #{value} with type #{type}" if element['status'] !=0
|
11
|
+
Element.new self, element['value']
|
12
|
+
end
|
13
|
+
|
14
|
+
# Find all elements with given type and value, return in an Array
|
15
|
+
# @param type [String, Symbol], value [Stirng]
|
16
|
+
# @return [Array]
|
17
|
+
def finds(type, value)
|
18
|
+
elements = post('/elements', { using: stringlize(type), value: value })['value']
|
19
|
+
elements.map { |element| Element.new self, element }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Find child element by given type and value, return first found element
|
23
|
+
# @param id [String] parent element id, type [String, Symbol], value [Stirng]
|
24
|
+
# @return [Object] found element
|
25
|
+
def find_subl_element(id, type, value)
|
26
|
+
element = post('/element/' + id + '/element', { using: type, value: value })
|
27
|
+
Element.new self, element['ELEMENT']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Find children elements by given type and value, return elements array
|
31
|
+
# @param id [String] parent element id, type [String, Symbol], value [Stirng]
|
32
|
+
# @return [Array<Element>]
|
33
|
+
def find_subl_elements(id, type, value)
|
34
|
+
elements = post('/element/' + id + '/elements', { using: type, value: value })
|
35
|
+
elements.map { |element| Element.new self, element['ELEMENT'] }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Search with given value (Complete value)
|
39
|
+
# @param value [String]
|
40
|
+
# @retrun [Hash]
|
41
|
+
def text(value)
|
42
|
+
find :link_text, "label=#{value}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Search with given value (Complete value)
|
46
|
+
# @param value [String]
|
47
|
+
# @return [Array]
|
48
|
+
def texts(value)
|
49
|
+
finds :link_text, "label=#{value}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Search with given partial value (partial link text)
|
53
|
+
# @param value [String]
|
54
|
+
# @retrun [Hash]
|
55
|
+
def partial_text(value)
|
56
|
+
find :partial_link_text, "label=#{value}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Search with given partial value (partial link text)
|
60
|
+
# @param value [String]
|
61
|
+
# @return [Array]
|
62
|
+
def partial_texts(value)
|
63
|
+
finds :partial_link_text, "label=#{value}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Search with class name
|
67
|
+
# @param class_name [String] XCUIElementType*, class name in XCUITest, ex: XCUIElementTypeButton
|
68
|
+
# @return [Array] contains all XCUIElementType* elements
|
69
|
+
def name(class_name)
|
70
|
+
finds :class_name, class_name
|
71
|
+
end
|
72
|
+
|
73
|
+
# Search with xpath
|
74
|
+
# @param value [String]
|
75
|
+
# example:
|
76
|
+
# "//XCUIElementTypeButton[@name='Share']",
|
77
|
+
# "//XCUIElementTypeTextField[@value='Value']"
|
78
|
+
# @return [Array] contains all elements which match given xpath
|
79
|
+
def xpath(value)
|
80
|
+
finds :xpath, value
|
81
|
+
end
|
82
|
+
|
83
|
+
# Xpath search with element type, element attribute, element attribute value
|
84
|
+
# @param type [String], attribute [String], value [String]
|
85
|
+
# example:
|
86
|
+
# xpath('Button', 'name', 'Share') for "//XCUIElementTypeButton[@name='Share']"
|
87
|
+
# @return [Array] contains all elements which match given variables
|
88
|
+
def xpath_search(type, attribute, value)
|
89
|
+
finds :xpath, "//#{match(type)}[@#{attribute}='#{value}']"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Search predicate string, return first found element
|
93
|
+
# @param value [String] 'isWDVisible=1'
|
94
|
+
# example: 'isWDAccessible=1', 'isWDEnabled=0'
|
95
|
+
# @return [Hash]
|
96
|
+
def predicate_text(predicate_value)
|
97
|
+
find :predicate_string, predicate_value
|
98
|
+
end
|
99
|
+
|
100
|
+
# Search with predicate string
|
101
|
+
# @param value [String] 'isWDVisible=1'
|
102
|
+
# example: 'isWDAccessible=1', 'isWDEnabled=0', 'isWDAccessibilityContainer=1'
|
103
|
+
# @return [Array]
|
104
|
+
def predicate_texts(predicate_value)
|
105
|
+
finds :predicate_string, predicate_value
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# @return element's visible cells [Array]
|
110
|
+
def visible_cell(id)
|
111
|
+
get '/uiaElement/' + id + '/getVisibleCells'
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
# Find button by given index or value
|
116
|
+
# @param value [String, Integer]
|
117
|
+
# If value is integer then return the button at that index
|
118
|
+
# If value is string then return the first button which contains given value
|
119
|
+
# @return [Button]
|
120
|
+
def button(value = 0)
|
121
|
+
if value.is_a? Numeric
|
122
|
+
finds(:xpath, "//XCUIElementTypeButton")[value]
|
123
|
+
else
|
124
|
+
find(:xpath, "//XCUIElementTypeButton[@name='#{value}']")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [Hash] All buttons
|
129
|
+
def buttons
|
130
|
+
finds(:xpath, "//XCUIElementTypeButton")
|
131
|
+
end
|
132
|
+
|
133
|
+
# Find the first Button
|
134
|
+
# @return [Hash] button
|
135
|
+
def first_button
|
136
|
+
finds(:xpath, "//XCUIElementTypeButton").first
|
137
|
+
end
|
138
|
+
|
139
|
+
# Find the last Button
|
140
|
+
# @return [Hash] button
|
141
|
+
def last_button
|
142
|
+
finds(:xpath, "//XCUIElementTypeButton").last
|
143
|
+
end
|
144
|
+
|
145
|
+
# Find textfield by given index or value
|
146
|
+
# @param value [String, Integer]
|
147
|
+
# If value is integer then return the textField at that index
|
148
|
+
# If value is string then return the first textField which contains given value
|
149
|
+
# @return [TextField]
|
150
|
+
def textfield(value = 0)
|
151
|
+
if value.is_a? Numeric
|
152
|
+
finds(:xpath, "//XCUIElementTypeTextField")[value]
|
153
|
+
else
|
154
|
+
find :xpath, "//XCUIElementTypeTextField[@value='#{value}']"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Find the all TextField.
|
159
|
+
# @return [Array]
|
160
|
+
def textfields
|
161
|
+
finds(:xpath, "//XCUIElementTypeTextField")
|
162
|
+
end
|
163
|
+
|
164
|
+
# Find the first TextField.
|
165
|
+
# @return [TextField]
|
166
|
+
def first_textfield
|
167
|
+
finds(:xpath, "//XCUIElementTypeTextField").first
|
168
|
+
end
|
169
|
+
|
170
|
+
# Find the last TextField.
|
171
|
+
# @return [TextField]
|
172
|
+
def first_textfield
|
173
|
+
finds(:xpath, "//XCUIElementTypeTextField").last
|
174
|
+
end
|
175
|
+
|
176
|
+
# Find secure_textfield by given index or value
|
177
|
+
# @param value [String, Integer]
|
178
|
+
# If value is integer then return the secure_textField at that index
|
179
|
+
# If value is string then return the first secure_textField which contains given value
|
180
|
+
# @return [TextField]
|
181
|
+
def secure_textfield(value = 0)
|
182
|
+
if value.is_a? Numeric
|
183
|
+
finds(:xpath, "//XCUIElementTypeSecureTextField")[value]
|
184
|
+
else
|
185
|
+
find :xpath, "//XCUIElementTypeSecureTextField[@value='#{value}']"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Find the all SecureTextField.
|
190
|
+
# @return [Array]
|
191
|
+
def secure_textfields
|
192
|
+
finds(:xpath, "//XCUIElementTypeSecureTextField")
|
193
|
+
end
|
194
|
+
|
195
|
+
# Find StaticText by given index or value
|
196
|
+
# @param value [String, Integer]
|
197
|
+
# If value is integer then return the StaticText at that index
|
198
|
+
# If value is string then return the first StaticText which contains given value
|
199
|
+
# @return [TextField]
|
200
|
+
def statictext(value = 0)
|
201
|
+
if value.is_a? Numeric
|
202
|
+
finds(:xpath, "//XCUIElementTypeStaticText")[value]
|
203
|
+
else
|
204
|
+
find :xpath, "//XCUIElementTypeStaticText[@value='#{value}']"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Find the all StaticText.
|
209
|
+
# @return [Array]
|
210
|
+
def statictexts
|
211
|
+
finds(:xpath, "//XCUIElementTypeStaticText")
|
212
|
+
end
|
213
|
+
|
214
|
+
########### This is calling selenium find_element* methods##########
|
215
|
+
# Find element with given type and value, return first found element
|
216
|
+
# @param args [*args]
|
217
|
+
# @return [Object]
|
218
|
+
def find_element(*args)
|
219
|
+
@driver.find_element(*args)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Find elements with given type and value, return in an Array
|
223
|
+
# @param args [*args]
|
224
|
+
# @return [Array<Element>]
|
225
|
+
def find_elements(*args)
|
226
|
+
@driver.find_elements(*args)
|
227
|
+
end
|
228
|
+
####################################################################
|
229
|
+
|
230
|
+
# Turn symbol to string, replace '_' to ' '
|
231
|
+
# @param instring [Symbol]
|
232
|
+
# @return [String]
|
233
|
+
def stringlize(instring)
|
234
|
+
if instring.is_a? Symbol
|
235
|
+
instring.to_s.gsub('_', ' ')
|
236
|
+
elsif instring.is_a? String
|
237
|
+
return instring
|
238
|
+
else
|
239
|
+
fail 'Xpath searching type should be a String'
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class WDA
|
2
|
+
module Orientation
|
3
|
+
def orientation
|
4
|
+
get @base_url + '/orientation'
|
5
|
+
end
|
6
|
+
|
7
|
+
def set_orientation
|
8
|
+
post @base_url + '/orientation'
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_rotation
|
12
|
+
get @base_url + '/rotation'
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_rotation
|
16
|
+
post @base_url + '/rotation'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
|
4
|
+
class WDA
|
5
|
+
module Screenshot
|
6
|
+
# Take a screenshot and save to the given path. Format: png.
|
7
|
+
#
|
8
|
+
# Example: screenshot '/screenshot_folder/screenshot.png'
|
9
|
+
#
|
10
|
+
# @param path [String] the full path to save the png
|
11
|
+
# @return screenshot result [Hash]
|
12
|
+
def screenshot(path)
|
13
|
+
path.include?('.png')? path : path += '.png'
|
14
|
+
p response = get(@base_url + '/screenshot')
|
15
|
+
begin
|
16
|
+
File.open(path, 'wb') {|f| f.write Base64.decode64(response['value']) }
|
17
|
+
rescue IOError => e
|
18
|
+
raise "Fail to save to #{path}, error: #{e}"
|
19
|
+
end
|
20
|
+
p "Screenshot is saved to #{path}"
|
21
|
+
return { sessionId: response['sessionId'], status: response['status'] }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class WDA
|
2
|
+
module Session
|
3
|
+
|
4
|
+
# Get current status
|
5
|
+
# @return [Obj]
|
6
|
+
def status
|
7
|
+
@status = get(@base_url + '/status')
|
8
|
+
update_status(@status)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Get current valid session
|
12
|
+
# @return [Session]
|
13
|
+
def session
|
14
|
+
response = get(@url)
|
15
|
+
if response['status'] != 0
|
16
|
+
if response['status'] == 10
|
17
|
+
response['value']
|
18
|
+
restart_session
|
19
|
+
response = get(@url)
|
20
|
+
elsif response['status'] == 1 || response['status'] == 6
|
21
|
+
@session_id = response['sessionId']
|
22
|
+
@url = @base_url + "/session/#{@session_id}"
|
23
|
+
response = get(@url)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
@session_id = response['sessionId']
|
27
|
+
@caps[:desiredCapabilities][:session_id] = @session_id
|
28
|
+
@caps[:desiredCapabilities][:session_valid] = true
|
29
|
+
@url = @base_url + "/session/#{@session_id}"
|
30
|
+
end
|
31
|
+
response
|
32
|
+
end
|
33
|
+
|
34
|
+
# Post desiredCapabilities to wda server to create a session
|
35
|
+
# This is not realy needed since we can find and click app icon to start app
|
36
|
+
# @return [self]
|
37
|
+
def launch_app(app_name_or_id)
|
38
|
+
fail 'Either app name or bundle id should be given for launch app' if app_name_or_id.nil?
|
39
|
+
if app_name_or_id.include?('.') # Given value is app's bundle id
|
40
|
+
@bundle_id = app_name_or_id
|
41
|
+
@session_id = nil # Prepare to create new session
|
42
|
+
@session_valid = false # Prepare to create new session
|
43
|
+
capabilities
|
44
|
+
@driver = Selenium::WebDriver::Driver.for(:remote, :url => @base_url, :desired_capabilities => @caps[:desiredCapabilities])
|
45
|
+
status
|
46
|
+
else
|
47
|
+
app_name = app_name_or_id
|
48
|
+
# TODO find app icon with given app name, click it
|
49
|
+
end
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# recreate a session
|
54
|
+
# @return [self]
|
55
|
+
def restart(app_name_or_id = nil)
|
56
|
+
quit
|
57
|
+
launch_app(app_name_or_id ||= @bundle_id )
|
58
|
+
end
|
59
|
+
|
60
|
+
# Exit current app by delete session with given session_id
|
61
|
+
# @params session_id [String]
|
62
|
+
# @return delete session response [Json]
|
63
|
+
def quit(session_id = nil)
|
64
|
+
(session_id.nil?)? sessionId = @session_id : sessionId = session_id
|
65
|
+
@base_url + '/session/' + sessionId
|
66
|
+
delete(@base_url + '/session/' + sessionId)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Same as quit, but delete default @session_id
|
70
|
+
def x
|
71
|
+
quit
|
72
|
+
end
|
73
|
+
|
74
|
+
# Healthcheck
|
75
|
+
def healthcheck
|
76
|
+
response = get(@base_url + '/healthcheck')
|
77
|
+
update_status(response)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class WDA
|
2
|
+
module TouchID
|
3
|
+
|
4
|
+
# Touch ID
|
5
|
+
# Match TouchID
|
6
|
+
def match_touchid
|
7
|
+
post(@base_url + '/simulator/touch_id', { match: 1 }.to_json)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Do not match TouchID
|
11
|
+
def match_touchid
|
12
|
+
post(@base_url + '/Simulator/touch_id', { match: 0 }.to_json)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/wda_lib/type.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
class WDA
|
2
|
+
module Type
|
3
|
+
def match(type)
|
4
|
+
type = ('XCUIElementType'+type).downcase
|
5
|
+
types = [
|
6
|
+
"XCUIElementTypeAny",
|
7
|
+
"XCUIElementTypeOther",
|
8
|
+
"XCUIElementTypeApplication",
|
9
|
+
"XCUIElementTypeGroup",
|
10
|
+
"XCUIElementTypeWindow",
|
11
|
+
"XCUIElementTypeSheet",
|
12
|
+
"XCUIElementTypeDrawer",
|
13
|
+
"XCUIElementTypeAlert",
|
14
|
+
"XCUIElementTypeDialog",
|
15
|
+
"XCUIElementTypeButton",
|
16
|
+
"XCUIElementTypeRadioButton",
|
17
|
+
"XCUIElementTypeRadioGroup",
|
18
|
+
"XCUIElementTypeCheckBox",
|
19
|
+
"XCUIElementTypeDisclosureTriangle",
|
20
|
+
"XCUIElementTypePopUpButton",
|
21
|
+
"XCUIElementTypeComboBox",
|
22
|
+
"XCUIElementTypeMenuButton",
|
23
|
+
"XCUIElementTypeToolbarButton",
|
24
|
+
"XCUIElementTypePopover",
|
25
|
+
"XCUIElementTypeKeyboard",
|
26
|
+
"XCUIElementTypeKey",
|
27
|
+
"XCUIElementTypeNavigationBar",
|
28
|
+
"XCUIElementTypeTabBar",
|
29
|
+
"XCUIElementTypeTabGroup",
|
30
|
+
"XCUIElementTypeToolbar",
|
31
|
+
"XCUIElementTypeStatusBar",
|
32
|
+
"XCUIElementTypeTable",
|
33
|
+
"XCUIElementTypeTableRow",
|
34
|
+
"XCUIElementTypeTableColumn",
|
35
|
+
"XCUIElementTypeOutline",
|
36
|
+
"XCUIElementTypeOutlineRow",
|
37
|
+
"XCUIElementTypeBrowser",
|
38
|
+
"XCUIElementTypeCollectionView",
|
39
|
+
"XCUIElementTypeSlider",
|
40
|
+
"XCUIElementTypePageIndicator",
|
41
|
+
"XCUIElementTypeProgressIndicator",
|
42
|
+
"XCUIElementTypeActivityIndicator",
|
43
|
+
"XCUIElementTypeSegmentedControl",
|
44
|
+
"XCUIElementTypePicker",
|
45
|
+
"XCUIElementTypePickerWheel",
|
46
|
+
"XCUIElementTypeSwitch",
|
47
|
+
"XCUIElementTypeToggle",
|
48
|
+
"XCUIElementTypeLink",
|
49
|
+
"XCUIElementTypeImage",
|
50
|
+
"XCUIElementTypeIcon",
|
51
|
+
"XCUIElementTypeScrollBar",
|
52
|
+
"XCUIElementTypeSearchField",
|
53
|
+
"XCUIElementTypeScrollView",
|
54
|
+
"XCUIElementTypeStaticText",
|
55
|
+
"XCUIElementTypeTextField",
|
56
|
+
"XCUIElementTypeSecureTextField",
|
57
|
+
"XCUIElementTypeDatePicker",
|
58
|
+
"XCUIElementTypeTextView",
|
59
|
+
"XCUIElementTypeMenu",
|
60
|
+
"XCUIElementTypeMenuItem",
|
61
|
+
"XCUIElementTypeMenuBar",
|
62
|
+
"XCUIElementTypeMenuBarItem",
|
63
|
+
"XCUIElementTypeMap",
|
64
|
+
"XCUIElementTypeWebView",
|
65
|
+
"XCUIElementTypeIncrementArrow",
|
66
|
+
"XCUIElementTypeDecrementArrow",
|
67
|
+
"XCUIElementTypeTimeline",
|
68
|
+
"XCUIElementTypeRatingIndicator",
|
69
|
+
"XCUIElementTypeValueIndicator",
|
70
|
+
"XCUIElementTypeSplitGroup",
|
71
|
+
"XCUIElementTypeSplitter",
|
72
|
+
"XCUIElementTypeRelevanceIndicator",
|
73
|
+
"XCUIElementTypeColorWell",
|
74
|
+
"XCUIElementTypeHelpTag",
|
75
|
+
"XCUIElementTypeMatte",
|
76
|
+
"XCUIElementTypeDockItem",
|
77
|
+
"XCUIElementTypeRuler",
|
78
|
+
"XCUIElementTypeRulerMarker",
|
79
|
+
"XCUIElementTypeGrid",
|
80
|
+
"XCUIElementTypeLevelIndicator",
|
81
|
+
"XCUIElementTypeCell",
|
82
|
+
"XCUIElementTypeLayoutArea",
|
83
|
+
"XCUIElementTypeLayoutItem",
|
84
|
+
"XCUIElementTypeHandle",
|
85
|
+
"XCUIElementTypeStepper",
|
86
|
+
"XCUIElementTypeTab"
|
87
|
+
]
|
88
|
+
types.select{|t|t.downcase == type.downcase}.first
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/wda_lib.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/wda_lib/*.rb'].each {|file| require file }
|
2
|
+
require 'json'
|
3
|
+
require 'httparty'
|
4
|
+
require 'selenium-webdriver'
|
5
|
+
Dir[File.dirname(__FILE__) + '/selenium_patch/*.rb'].each {|file| require_relative file }
|
6
|
+
|
7
|
+
|
8
|
+
class WDA
|
9
|
+
|
10
|
+
include HTTParty
|
11
|
+
headers 'Content-Type' => 'application/json'
|
12
|
+
# extend Forwardable
|
13
|
+
# def_delegators :@driver, :find_element, :find_elements, :action, :touch, :element_scroll
|
14
|
+
# def_delegator :@driver, :get, :get_url
|
15
|
+
|
16
|
+
include ::WDA::Alert
|
17
|
+
include ::WDA::Custome
|
18
|
+
include ::WDA::Debug
|
19
|
+
include ::WDA::FindElement
|
20
|
+
include ::WDA::Orientation
|
21
|
+
include ::WDA::Screenshot
|
22
|
+
include ::WDA::Session
|
23
|
+
include ::WDA::TouchID
|
24
|
+
include ::WDA::Type
|
25
|
+
|
26
|
+
Point = Struct.new(:x, :y)
|
27
|
+
Dimension = Struct.new(:width, :height)
|
28
|
+
|
29
|
+
attr_accessor :session_id, :http, :bundle_id, :caps, :device, :driver
|
30
|
+
attr_reader :base_url, :server_host, :server_port, :url, :window_w, :window_h
|
31
|
+
|
32
|
+
# Get base_url from XCTRunner logs: ServerURLHere->http://x.x.x.x:8100<-ServerURLHere
|
33
|
+
# Default base_url 'http://localhost:8100' for running XCTRunner on simulator
|
34
|
+
BASE_URL = 'http://localhost:8100'
|
35
|
+
|
36
|
+
# Create a new client
|
37
|
+
#
|
38
|
+
# ```ruby
|
39
|
+
# require 'rubygems'
|
40
|
+
# require 'wda_lib'
|
41
|
+
#
|
42
|
+
# Start wda client
|
43
|
+
# opts = { caps: { bundleId: @bundle_id }, device_url: 'http://x.x.x.x:8100' }
|
44
|
+
# WDA.new(opts).start_session
|
45
|
+
def initialize(opts = {})
|
46
|
+
# fail 'opts should be a hash' unless opts.is_a? Hash
|
47
|
+
# fail 'bundleId should be provided' if opts[:bundleId].nil?
|
48
|
+
url = opts[:device_url] || BASE_URL
|
49
|
+
parsed_url = URI.parse(url)
|
50
|
+
@base_url = parsed_url.scheme + '://' + parsed_url.host + ':' + parsed_url.port.to_s
|
51
|
+
@url = @base_url
|
52
|
+
@server_host = parsed_url.host
|
53
|
+
@server_port = parsed_url.port
|
54
|
+
status
|
55
|
+
capabilities(opts)
|
56
|
+
@driver = Selenium::WebDriver::Driver.for(:remote, :url => @base_url, :desired_capabilities => @caps[:desiredCapabilities])
|
57
|
+
end
|
58
|
+
|
59
|
+
# Build desired_capabilities with options
|
60
|
+
# @param opts [Hash]
|
61
|
+
# @return attribute caps [Hash]
|
62
|
+
def capabilities(opts = {})
|
63
|
+
@bundle_id = opts[:bundleId] if !opts[:bundleId].nil?
|
64
|
+
selenium_capabilities = Selenium::WebDriver::Remote::W3CCapabilities.new(
|
65
|
+
browser_name: 'iphone',
|
66
|
+
platform_name: @platform_name,
|
67
|
+
platform_version: @platform_version,
|
68
|
+
session_id: @session_id,
|
69
|
+
session_valid: @session_valid,
|
70
|
+
bundleId: @bundle_id
|
71
|
+
)
|
72
|
+
@caps = { desiredCapabilities: selenium_capabilities }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Update current running sessionId to @session_id
|
76
|
+
# @param session_response [object] : http GET session response body
|
77
|
+
# @return session_reponse [object]
|
78
|
+
def update_status(status_response)
|
79
|
+
@session_id = status_response['sessionId']
|
80
|
+
@session_valid = true
|
81
|
+
@session_id = status_response['sessionId']
|
82
|
+
@platform_name = status_response['value']['os']['name']
|
83
|
+
@platform_version = status_response['value']['os']['version']
|
84
|
+
@url = @base_url + "/session/#{@session_id}"
|
85
|
+
capabilities
|
86
|
+
status_response
|
87
|
+
end
|
88
|
+
|
89
|
+
# HTTP methods for dealing with all supported motheds in FB WDA
|
90
|
+
# I don't like to write such pieces of code, it removes all query params and headers,
|
91
|
+
# But as FB WDA is doing like this, this can simplify codes
|
92
|
+
def get(path)
|
93
|
+
if URI.parse(path).absolute?
|
94
|
+
WDA.get(path)
|
95
|
+
else
|
96
|
+
WDA.get(@url + path)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def post(path, body = nil)
|
101
|
+
if URI.parse(path).absolute?
|
102
|
+
WDA.post(path, body: body.to_json)
|
103
|
+
else
|
104
|
+
WDA.post(@url + path, body: body.to_json)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def delete(path)
|
109
|
+
WDA.delete(path)
|
110
|
+
end
|
111
|
+
end
|
data/wda_lib.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'wda_lib/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'wda_lib'
|
8
|
+
s.version = WDA::VERSION
|
9
|
+
s.authors = ["MIN Yi"]
|
10
|
+
s.email = 'minsparky@gmail.com'
|
11
|
+
|
12
|
+
s.summary = "A simple ruby lib of binding methods for WebDriverAgent"
|
13
|
+
s.description = "A simple ruby lib of binding methods for WebDriverAgent"
|
14
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
s.bindir = "exe"
|
16
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.homepage =
|
19
|
+
'http://rubygems.org/gems/wda_lib'
|
20
|
+
s.license = 'MIT'
|
21
|
+
|
22
|
+
s.add_development_dependency "bundler", "~> 1.12"
|
23
|
+
s.add_development_dependency "rake", "~> 10.0"
|
24
|
+
s.add_development_dependency "httparty"
|
25
|
+
s.add_development_dependency "rspec"
|
26
|
+
s.add_development_dependency "selenium-webdriver", "~> 3.0.0.beta3.1"
|
27
|
+
end
|