wda_lib 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|