automation_object 0.0.4
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/lib/automation_object/blue_print.rb +106 -0
- data/lib/automation_object/blue_print_validation/base_validation.rb +44 -0
- data/lib/automation_object/blue_print_validation/common_methods.rb +106 -0
- data/lib/automation_object/blue_print_validation/element_validation.rb +198 -0
- data/lib/automation_object/blue_print_validation/formatted_errors.rb +41 -0
- data/lib/automation_object/blue_print_validation/hook_validation.rb +393 -0
- data/lib/automation_object/blue_print_validation/key_value_constants.rb +75 -0
- data/lib/automation_object/blue_print_validation/modal_validation.rb +37 -0
- data/lib/automation_object/blue_print_validation/screen_modal_common_methods.rb +119 -0
- data/lib/automation_object/blue_print_validation/screen_validation.rb +21 -0
- data/lib/automation_object/blue_print_validation/validation_object.rb +32 -0
- data/lib/automation_object/driver/anonymous.rb +27 -0
- data/lib/automation_object/driver/driver.rb +281 -0
- data/lib/automation_object/driver/element.rb +243 -0
- data/lib/automation_object/element/element.rb +145 -0
- data/lib/automation_object/element/element_array.rb +12 -0
- data/lib/automation_object/element/element_cell.rb +126 -0
- data/lib/automation_object/element/element_group.rb +33 -0
- data/lib/automation_object/element/element_hash.rb +25 -0
- data/lib/automation_object/element/element_helpers.rb +29 -0
- data/lib/automation_object/element/element_methods.rb +134 -0
- data/lib/automation_object/element/elements_helpers.rb +204 -0
- data/lib/automation_object/framework/events.rb +8 -0
- data/lib/automation_object/framework/helpers.rb +101 -0
- data/lib/automation_object/framework/print_objects.rb +67 -0
- data/lib/automation_object/framework/screen_monitor.rb +55 -0
- data/lib/automation_object/framework/screen_routing.rb +310 -0
- data/lib/automation_object/framework/window_helpers.rb +181 -0
- data/lib/automation_object/framework.rb +408 -0
- data/lib/automation_object/hash.rb +6 -0
- data/lib/automation_object/hook_helpers.rb +27 -0
- data/lib/automation_object/logger.rb +179 -0
- data/lib/automation_object/object.rb +22 -0
- data/lib/automation_object/screen/modal.rb +8 -0
- data/lib/automation_object/screen/screen.rb +209 -0
- data/lib/automation_object/screen/screen_hook_helpers.rb +319 -0
- data/lib/automation_object/screen/screen_modal_helpers.rb +101 -0
- data/lib/automation_object/screen/screen_prompt_helpers.rb +21 -0
- data/lib/automation_object/screen/screen_window_helpers.rb +149 -0
- data/lib/automation_object/version.rb +3 -0
- data/lib/automation_object.rb +80 -0
- metadata +279 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
module Driver
|
3
|
+
class Anonymous
|
4
|
+
def initialize(driver_object, object)
|
5
|
+
@driver_object = driver_object
|
6
|
+
@object = object
|
7
|
+
end
|
8
|
+
|
9
|
+
def respond_to?(method_symbol, include_private = false)
|
10
|
+
@driver_object.mutex_object.synchronize do
|
11
|
+
return @object.respond_to?(method_symbol, include_private)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(method_symbol, *arguments, &block)
|
16
|
+
start_time = Time.new.to_f
|
17
|
+
|
18
|
+
@driver_object.mutex_object.synchronize do
|
19
|
+
object_return = @object.send(method_symbol, *arguments, &block)
|
20
|
+
|
21
|
+
total_time_taken = (Time.new.to_f-start_time)
|
22
|
+
return object_return
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
#Thread Safe Execution Purposes
|
2
|
+
module AutomationObject
|
3
|
+
module Driver
|
4
|
+
class Driver
|
5
|
+
@@anonymous_skip_classes = [TrueClass, FalseClass, String, Numeric, Array, Hash]
|
6
|
+
attr_accessor :mutex_object
|
7
|
+
|
8
|
+
attr_accessor :is_browser, :is_mobile, :supports_contexts
|
9
|
+
|
10
|
+
@@throttle_methods = {
|
11
|
+
'navigate' => 2,
|
12
|
+
'close' => 2,
|
13
|
+
'manage' => 1
|
14
|
+
}
|
15
|
+
|
16
|
+
@@minimum_speed = 0 #0.01 #Speed it up to see where faults may lay
|
17
|
+
|
18
|
+
@@implicit_wait_timeout = 30
|
19
|
+
@@script_timeout = 500
|
20
|
+
@@page_load_timeout = 500
|
21
|
+
|
22
|
+
def self.throttle_methods
|
23
|
+
@@throttle_methods
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.throttle_methods=(value)
|
27
|
+
@@throttle_methods = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.minimum_speed
|
31
|
+
@@minimum_speed
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.minimum_speed=(value)
|
35
|
+
@@minimum_speed = value
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(driver_object)
|
39
|
+
@driver_object = driver_object
|
40
|
+
self.mutex_object = Mutex.new
|
41
|
+
|
42
|
+
return if self.is_mobile?
|
43
|
+
|
44
|
+
self.mutex_object.synchronize do
|
45
|
+
@driver_object.manage.timeouts.implicit_wait = @@implicit_wait_timeout
|
46
|
+
@driver_object.manage.timeouts.script_timeout = @@script_timeout
|
47
|
+
@driver_object.manage.timeouts.page_load = @@page_load_timeout
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def respond_to?(method_symbol, include_private = false)
|
52
|
+
self.mutex_object.synchronize do
|
53
|
+
return true if super
|
54
|
+
return @driver_object.respond_to?(method_symbol, include_private)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(method_symbol, *arguments, &block)
|
59
|
+
anonymous_object = nil
|
60
|
+
start_time = Time.new.to_f
|
61
|
+
|
62
|
+
self.mutex_object.synchronize do
|
63
|
+
#For Appium because it's under driver method
|
64
|
+
if @driver_object.respond_to?(:driver) and not @driver_object.respond_to?(method_symbol)
|
65
|
+
driver_return = @driver_object.driver.send(method_symbol, *arguments, &block)
|
66
|
+
else
|
67
|
+
driver_return = @driver_object.send(method_symbol, *arguments, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
self.throttle_speed(method_symbol, start_time)
|
71
|
+
|
72
|
+
total_time_taken = (Time.new.to_f-start_time)
|
73
|
+
AutomationObject::Logger::add_driver_message(total_time_taken, caller_locations, method_symbol, *arguments, &block)
|
74
|
+
|
75
|
+
@@anonymous_skip_classes.each { |skip_class|
|
76
|
+
if driver_return.is_a?(skip_class)
|
77
|
+
return driver_return
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
anonymous_object = AutomationObject::Driver::Anonymous.new(self, driver_return)
|
82
|
+
end
|
83
|
+
|
84
|
+
return anonymous_object
|
85
|
+
end
|
86
|
+
|
87
|
+
def exists?(selector_type, selector_path)
|
88
|
+
start_time = Time.new.to_f
|
89
|
+
|
90
|
+
self.mutex_object.synchronize do
|
91
|
+
if @driver_object.respond_to?(:exists) #Appium
|
92
|
+
exists = @driver_object.exists { @driver_object.find_element(selector_type, selector_path) }
|
93
|
+
else
|
94
|
+
begin
|
95
|
+
@driver_object.manage.timeouts.implicit_wait = 0
|
96
|
+
element_objects = @driver_object.find_elements(selector_type, selector_path)
|
97
|
+
@driver_object.manage.timeouts.implicit_wait = @@implicit_wait_timeout
|
98
|
+
if element_objects.length == 0
|
99
|
+
exists = false
|
100
|
+
else
|
101
|
+
exists = true
|
102
|
+
end
|
103
|
+
rescue
|
104
|
+
exists = false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
self.throttle_speed(:exists?, start_time)
|
109
|
+
|
110
|
+
total_time_taken = (Time.new.to_f-start_time)
|
111
|
+
AutomationObject::Logger::add_driver_message(total_time_taken, caller_locations, :exists?, [selector_type, selector_path])
|
112
|
+
|
113
|
+
return exists
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def find_element(selector_type, selector_path)
|
118
|
+
start_time = Time.new.to_f
|
119
|
+
|
120
|
+
self.mutex_object.synchronize do
|
121
|
+
element_object = @driver_object.find_element(selector_type, selector_path)
|
122
|
+
|
123
|
+
self.throttle_speed(:find_element, start_time)
|
124
|
+
|
125
|
+
total_time_taken = (Time.new.to_f-start_time)
|
126
|
+
AutomationObject::Logger::add_driver_message(total_time_taken, caller_locations, :find_element, [selector_type, selector_path])
|
127
|
+
|
128
|
+
return AutomationObject::Driver::Element.new(self, element_object)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_elements(selector_type, selector_path)
|
133
|
+
start_time = Time.new.to_f
|
134
|
+
|
135
|
+
self.mutex_object.synchronize do
|
136
|
+
element_objects = @driver_object.find_elements(selector_type, selector_path)
|
137
|
+
|
138
|
+
parsed_element_objects = Array.new
|
139
|
+
element_objects.each { |element_object|
|
140
|
+
parsed_element_objects.push(AutomationObject::Driver::Element.new(self, element_object))
|
141
|
+
}
|
142
|
+
|
143
|
+
self.throttle_speed(:find_elements, start_time)
|
144
|
+
|
145
|
+
total_time_taken = (Time.new.to_f-start_time)
|
146
|
+
AutomationObject::Logger::add_driver_message(total_time_taken, caller_locations, :find_elements, [selector_type, selector_path])
|
147
|
+
|
148
|
+
return parsed_element_objects
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def throttle_speed(method_symbol, start_time)
|
153
|
+
method_string = method_symbol.to_s
|
154
|
+
|
155
|
+
throttle_time_minimum = @@minimum_speed
|
156
|
+
if @@throttle_methods.is_a?(Hash)
|
157
|
+
if @@throttle_methods.has_key?(method_string)
|
158
|
+
throttle_time_minimum = @@throttle_methods[method_string].to_f
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
time_difference = Time.new.to_f - start_time
|
163
|
+
|
164
|
+
if time_difference < throttle_time_minimum
|
165
|
+
sleep((throttle_time_minimum-time_difference))
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def screenshot(path)
|
170
|
+
start_time = Time.new.to_f
|
171
|
+
|
172
|
+
if self.driver_object.respond_to?(:screenshot)
|
173
|
+
driver_return = self.driver_object.screenshot(path)
|
174
|
+
else
|
175
|
+
driver_return = self.driver_object.save_screenshot(path)
|
176
|
+
end
|
177
|
+
|
178
|
+
self.throttle_speed(:find_elements, start_time)
|
179
|
+
|
180
|
+
total_time_taken = (Time.new.to_f-start_time)
|
181
|
+
AutomationObject::Logger::add_driver_message(total_time_taken, caller_locations, :screenshot, [path])
|
182
|
+
|
183
|
+
return driver_return
|
184
|
+
end
|
185
|
+
|
186
|
+
def accept_prompt
|
187
|
+
start_time = Time.new.to_f
|
188
|
+
|
189
|
+
if self.driver_object.respond_to?(:alert_accept)
|
190
|
+
self.driver_object.alert_accept
|
191
|
+
else
|
192
|
+
alert = self.driver_object.switch_to.alert
|
193
|
+
alert.accept
|
194
|
+
end
|
195
|
+
|
196
|
+
self.throttle_speed(:accept_prompt, start_time)
|
197
|
+
return nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def dismiss_prompt
|
201
|
+
start_time = Time.new.to_f
|
202
|
+
|
203
|
+
begin
|
204
|
+
@driver_object.alert_dismiss
|
205
|
+
rescue NoMethodError
|
206
|
+
alert = @driver_object.switch_to.alert
|
207
|
+
alert.dismiss
|
208
|
+
@driver_object.switch_to.default_content
|
209
|
+
end
|
210
|
+
|
211
|
+
self.throttle_speed(:dismiss_prompt, start_time)
|
212
|
+
return nil
|
213
|
+
end
|
214
|
+
|
215
|
+
def is_mobile?
|
216
|
+
if self.is_mobile == nil
|
217
|
+
self.is_mobile = self.supports_contexts?
|
218
|
+
end
|
219
|
+
|
220
|
+
return self.is_mobile
|
221
|
+
end
|
222
|
+
|
223
|
+
def is_browser?
|
224
|
+
unless self.is_browser == nil
|
225
|
+
return self.is_browser
|
226
|
+
end
|
227
|
+
|
228
|
+
#If Selenium the yeah we are using a browser
|
229
|
+
if self.supports_window_handles?
|
230
|
+
self.is_browser = true
|
231
|
+
return self.is_browser
|
232
|
+
end
|
233
|
+
|
234
|
+
#Now we need to check Appium's contexts to see if WEBVIEW is in available_contexts
|
235
|
+
available_contexts = nil
|
236
|
+
self.mutex_object.synchronize do
|
237
|
+
available_contexts = @driver_object.available_contexts
|
238
|
+
end
|
239
|
+
self.supports_contexts = true
|
240
|
+
|
241
|
+
available_contexts.each { |context|
|
242
|
+
if context.match(/^WEBVIEW_\d+$/)
|
243
|
+
self.is_browser = true
|
244
|
+
return self.is_browser
|
245
|
+
end
|
246
|
+
}
|
247
|
+
|
248
|
+
self.is_browser = false
|
249
|
+
return self.is_browser
|
250
|
+
end
|
251
|
+
|
252
|
+
#Selenium's use of window_handles
|
253
|
+
def supports_window_handles?
|
254
|
+
begin
|
255
|
+
self.mutex_object.synchronize do
|
256
|
+
@driver_object.window_handles
|
257
|
+
end
|
258
|
+
return true
|
259
|
+
rescue NoMethodError, Selenium::WebDriver::Error::UnknownError
|
260
|
+
return false
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
#Appium's use of window_handles
|
265
|
+
def supports_contexts?
|
266
|
+
return self.supports_contexts if self.supports_contexts != nil
|
267
|
+
|
268
|
+
self.mutex_object.synchronize do
|
269
|
+
begin
|
270
|
+
@driver_object.available_contexts
|
271
|
+
self.supports_contexts = true
|
272
|
+
rescue NoMethodError
|
273
|
+
self.supports_contexts = false
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
return self.supports_contexts
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
#Thread Safe Execution Purposes
|
2
|
+
module AutomationObject
|
3
|
+
module Driver
|
4
|
+
class Element
|
5
|
+
@@throttle_methods = {
|
6
|
+
'click' => 2, #Slow down, browser given back before navigation is finished
|
7
|
+
#'location_once_scrolled_into_view' => 0.5 #Appeared where Firefox was failing, add a little more time
|
8
|
+
}
|
9
|
+
@@minimum_speed = 0 #0.01
|
10
|
+
|
11
|
+
@@scroll_into_view_methods = [:click, :tap, :hover, :send_keys]
|
12
|
+
|
13
|
+
def self.throttle_methods
|
14
|
+
@@throttle_methods
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.throttle_methods=(value)
|
18
|
+
@@throttle_methods = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.minimum_speed
|
22
|
+
@@minimum_speed
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.minimum_speed=(value)
|
26
|
+
@@minimum_speed = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(driver_object, element_object)
|
30
|
+
@driver_object = driver_object
|
31
|
+
@element_object = element_object
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to?(method_symbol, include_private = false)
|
35
|
+
return true if super
|
36
|
+
return @element_object.respond_to?(method_symbol, include_private)
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(method_symbol, *arguments, &block)
|
40
|
+
if @@scroll_into_view_methods.include?(method_symbol)
|
41
|
+
self.scroll_into_view
|
42
|
+
end
|
43
|
+
|
44
|
+
@driver_object.mutex_object.synchronize do
|
45
|
+
start_time = Time.new.to_f
|
46
|
+
element_object_return = @element_object.send(method_symbol, *arguments, &block)
|
47
|
+
|
48
|
+
self.throttle_speed(method_symbol, start_time)
|
49
|
+
|
50
|
+
total_time_taken = (Time.new.to_f-start_time)
|
51
|
+
AutomationObject::Logger::add_driver_element_message(total_time_taken, caller_locations, method_symbol, *arguments, &block)
|
52
|
+
|
53
|
+
return element_object_return
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def throttle_speed(method_symbol, start_time)
|
58
|
+
method_string = method_symbol.to_s
|
59
|
+
|
60
|
+
throttle_time_minimum = @@minimum_speed
|
61
|
+
if @@throttle_methods.is_a?(Hash)
|
62
|
+
if @@throttle_methods.has_key?(method_string)
|
63
|
+
throttle_time_minimum = @@throttle_methods[method_string].to_f
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
time_difference = Time.new.to_f - start_time
|
68
|
+
|
69
|
+
if time_difference < throttle_time_minimum
|
70
|
+
sleep((throttle_time_minimum-time_difference))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def collides_with_element?(second_element_object, collision_tolerance = false)
|
75
|
+
box_one = self.get_box_coordinates
|
76
|
+
box_two = second_element_object.get_box_coordinates
|
77
|
+
|
78
|
+
collision_tolerance = 0 unless collision_tolerance.is_a?(Numeric)
|
79
|
+
|
80
|
+
if box_one[:x2] > box_two[:x1] and box_one[:x1] < box_two[:x2] and box_one[:y2] > box_two[:y1] and box_one[:y1] < box_two[:y2]
|
81
|
+
if box_one[:x2] > (box_two[:x1] + collision_tolerance) and (box_one[:x1] + collision_tolerance) < box_two[:x2] and
|
82
|
+
box_one[:y2] > (box_two[:y1] + collision_tolerance) and (box_one[:y1] + collision_tolerance) < box_two[:y2]
|
83
|
+
return true
|
84
|
+
else
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
else
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_box_coordinates
|
93
|
+
element_location = self.location
|
94
|
+
element_size = self.size
|
95
|
+
|
96
|
+
box_coordinates = Hash.new
|
97
|
+
box_coordinates[:x1] = element_location.x.to_f
|
98
|
+
box_coordinates[:y1] = element_location.y.to_f
|
99
|
+
box_coordinates[:x2] = element_location.x.to_f + element_size.width.to_f
|
100
|
+
box_coordinates[:y2] = element_location.y.to_f + element_size.height.to_f
|
101
|
+
|
102
|
+
return box_coordinates
|
103
|
+
end
|
104
|
+
|
105
|
+
def attribute(key, value = false)
|
106
|
+
@driver_object.mutex_object.synchronize do
|
107
|
+
return @element_object.attribute(key) unless value
|
108
|
+
end
|
109
|
+
|
110
|
+
script = "return arguments[0].#{key} = '#{value}'"
|
111
|
+
@driver_object.execute_script(script, @element_object)
|
112
|
+
|
113
|
+
@driver_object.mutex_object.synchronize do
|
114
|
+
return @element_object.attribute(key)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def switch_to_iframe
|
119
|
+
@driver_object.switch_to.frame(self.get_iframe_switch_value)
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_iframe_switch_value
|
123
|
+
iframe_switch_value = self.attribute('id')
|
124
|
+
if iframe_switch_value.length == 0
|
125
|
+
iframe_switch_value = self.attribute('name')
|
126
|
+
end
|
127
|
+
|
128
|
+
unless iframe_switch_value
|
129
|
+
iframe_switch_value = self.attribute('name', SecureRandom.hex(16))
|
130
|
+
end
|
131
|
+
|
132
|
+
return iframe_switch_value
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_element_center
|
136
|
+
element_location = self.location
|
137
|
+
element_size = self.size
|
138
|
+
|
139
|
+
center = Hash.new
|
140
|
+
center[:x] = (element_location.x.to_f + element_size.width.to_f/2).to_f
|
141
|
+
center[:y] = (element_location.y.to_f + element_size.height.to_f/2).to_f
|
142
|
+
|
143
|
+
return center
|
144
|
+
end
|
145
|
+
|
146
|
+
def x
|
147
|
+
return self.location.x
|
148
|
+
end
|
149
|
+
|
150
|
+
def y
|
151
|
+
return self.location.y
|
152
|
+
end
|
153
|
+
|
154
|
+
def width
|
155
|
+
return self.size.width
|
156
|
+
end
|
157
|
+
|
158
|
+
def height
|
159
|
+
return self.size.height
|
160
|
+
end
|
161
|
+
|
162
|
+
def hover
|
163
|
+
self.scroll_into_view
|
164
|
+
@driver_object.action.move_to(@element_object).perform
|
165
|
+
end
|
166
|
+
|
167
|
+
def visible?
|
168
|
+
return self.displayed?
|
169
|
+
end
|
170
|
+
|
171
|
+
def invisible?
|
172
|
+
return self.displayed? ? false : true
|
173
|
+
end
|
174
|
+
|
175
|
+
def id
|
176
|
+
return self.attribute('id').to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
def href
|
180
|
+
return self.attribute('href').to_s
|
181
|
+
end
|
182
|
+
|
183
|
+
def content
|
184
|
+
return self.attribute('content').to_s
|
185
|
+
end
|
186
|
+
|
187
|
+
def scroll_into_view
|
188
|
+
return self.scroll_to_view
|
189
|
+
end
|
190
|
+
|
191
|
+
def scroll_to_view
|
192
|
+
self.location_once_scrolled_into_view
|
193
|
+
|
194
|
+
if @driver_object.is_mobile?
|
195
|
+
return unless @driver_object.is_browser?
|
196
|
+
|
197
|
+
element_center = self.get_element_center
|
198
|
+
window_height = @driver_object.execute_script('return window.innerHeight;').to_f
|
199
|
+
current_y_position = @driver_object.execute_script('var doc = document.documentElement; return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);').to_f
|
200
|
+
|
201
|
+
if element_center[:y] < (window_height/2)
|
202
|
+
ideal_y_position = (current_y_position + element_center[:y] - (window_height.to_f / 2.0)).abs
|
203
|
+
else
|
204
|
+
ideal_y_position = (current_y_position - element_center[:y] + (window_height.to_f / 2.0)).abs
|
205
|
+
end
|
206
|
+
|
207
|
+
@driver_object.execute_script("window.scroll(#{element_center[:x].to_f},#{ideal_y_position});")
|
208
|
+
#Just in case in close to the top or bottom bounds of the window
|
209
|
+
element_location = self.location_once_scrolled_into_view
|
210
|
+
|
211
|
+
if element_location[:y] < 0
|
212
|
+
current_y_position = @driver_object.execute_script('var doc = document.documentElement; return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);').to_f
|
213
|
+
scroll_y_position = current_y_position + element_location[:y]
|
214
|
+
@driver_object.execute_script("window.scroll(#{element_location[:x].to_f},#{scroll_y_position});")
|
215
|
+
end
|
216
|
+
else
|
217
|
+
element_location = self.location
|
218
|
+
|
219
|
+
window_height = @driver_object.execute_script('return window.innerHeight;').to_f
|
220
|
+
current_scroll_position = @driver_object.execute_script('var doc = document.documentElement; return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);').to_f
|
221
|
+
|
222
|
+
middle_y_bounds = current_scroll_position + window_height/2
|
223
|
+
|
224
|
+
if middle_y_bounds > element_location.y
|
225
|
+
#Add
|
226
|
+
y_difference = middle_y_bounds - element_location.y
|
227
|
+
scroll_y_position = current_scroll_position - y_difference
|
228
|
+
else
|
229
|
+
#Subtract
|
230
|
+
y_difference = element_location.y - middle_y_bounds
|
231
|
+
scroll_y_position = current_scroll_position + y_difference
|
232
|
+
end
|
233
|
+
|
234
|
+
#Get the element to halfway
|
235
|
+
scroll_x_position = element_location.x.to_f
|
236
|
+
|
237
|
+
javascript_string = "return window.scroll(#{scroll_x_position}, #{scroll_y_position});"
|
238
|
+
@driver_object.execute_script(javascript_string)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
class Element
|
3
|
+
include EventEmitter
|
4
|
+
include AutomationObject::ElementHelpers
|
5
|
+
|
6
|
+
WINDOW_CHECK_ON_METHOD = [:click, :drag_to_element]
|
7
|
+
SCROLL_TO_VIEW_ON_METHOD = [:click, :drag_to_element, :send_keys]
|
8
|
+
|
9
|
+
attr_accessor :framework_object, :driver_object,
|
10
|
+
:framework_location,
|
11
|
+
:screen_object, :element_object,
|
12
|
+
:screen_name, :element_name,
|
13
|
+
:element_methods_object
|
14
|
+
|
15
|
+
attr_accessor :configuration
|
16
|
+
|
17
|
+
def initialize(params)
|
18
|
+
#Set params to properties
|
19
|
+
self.framework_object = params[:framework_object] || raise(ArgumentError, 'framework_object is required in params')
|
20
|
+
|
21
|
+
self.screen_object = params[:screen_object] || raise(ArgumentError, 'screen_object is required in params')
|
22
|
+
self.driver_object = params[:driver_object] || raise(ArgumentError, 'driver_object is required in params')
|
23
|
+
#Clone configuration, this will allow edit/deletes on different levels
|
24
|
+
self.configuration = params[:blue_prints].clone || raise(ArgumentError, 'configuration is required in params')
|
25
|
+
|
26
|
+
self.screen_name = params[:screen_name] || raise(ArgumentError, 'screen_name is required in params')
|
27
|
+
self.element_name = params[:element_name] || raise(ArgumentError, 'elements_name is required in params')
|
28
|
+
|
29
|
+
#Set element object if requested
|
30
|
+
self.element_object = params[:element_object] if params[:element_object]
|
31
|
+
|
32
|
+
#Validate properties
|
33
|
+
raise ArgumentError, 'framework_object should be an Automation Object' unless self.framework_object.is_a? Framework
|
34
|
+
raise ArgumentError, 'screen_object should be an Screen Object' unless self.screen_object.is_a? Screen
|
35
|
+
raise ArgumentError, 'configuration should be a Hash Object' unless self.configuration.is_a? Hash
|
36
|
+
raise ArgumentError, 'screen_name should be a String Object' unless self.screen_name.is_a? String
|
37
|
+
raise ArgumentError, 'elements_name should be a String Object' unless self.element_name.is_a? String
|
38
|
+
|
39
|
+
#This can stay alive, will reset object internally when issued to this class
|
40
|
+
#ElementMethod Object will have the available methods for elements, while
|
41
|
+
#allowing for thread locking on this level
|
42
|
+
#Pass self into it, so it can hit above levels without much issue
|
43
|
+
self.element_methods_object = ElementMethods.new(self)
|
44
|
+
|
45
|
+
self.framework_location = self.screen_object.framework_location + ".#{self.element_name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to?(method_symbol, include_private = false)
|
49
|
+
#If this Element class has the method then return true
|
50
|
+
return true if super
|
51
|
+
|
52
|
+
#Check ElementMethods respond_to?, need to wrap in driver mutex object
|
53
|
+
return self.element_methods_object.respond_to?(method_symbol, include_private)
|
54
|
+
end
|
55
|
+
|
56
|
+
#Avoid deadlocking on resets
|
57
|
+
def reset_element
|
58
|
+
self.element_methods_object.reset_element
|
59
|
+
end
|
60
|
+
|
61
|
+
def check_new_window?(method_symbol)
|
62
|
+
return false unless self.framework_object.is_browser?
|
63
|
+
WINDOW_CHECK_ON_METHOD.include?(method_symbol)
|
64
|
+
end
|
65
|
+
|
66
|
+
def scroll_to_view?(method_symbol)
|
67
|
+
return false unless self.framework_object.is_browser?
|
68
|
+
SCROLL_TO_VIEW_ON_METHOD.include?(method_symbol)
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_missing(method_symbol, *arguments, &block)
|
72
|
+
method_string = method_symbol.to_s
|
73
|
+
created_window_handle = false
|
74
|
+
em_object_return = nil
|
75
|
+
|
76
|
+
AutomationObject::Logger::add("Calling method #{method_symbol} on element", [self.framework_location])
|
77
|
+
|
78
|
+
#Run any before hooks
|
79
|
+
self.hook(method_string, 'before')
|
80
|
+
|
81
|
+
#Deal with iFrames
|
82
|
+
if self.framework_object.is_browser?
|
83
|
+
if self.in_iframe?
|
84
|
+
self.screen_object.switch_to_iframe(self.element_name)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#Check for difference in window handles if needed
|
89
|
+
if self.check_new_window?(method_symbol)
|
90
|
+
before_window_handles = self.framework_object.get_window_handles
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
begin
|
95
|
+
#Use automation object level mutex object which will lock any element operations will complete
|
96
|
+
begin
|
97
|
+
AutomationObject::Logger::add("Executing method #{method_symbol} on element", [self.framework_location])
|
98
|
+
em_object_return = self.element_methods_object.send(method_symbol, *arguments, &block)
|
99
|
+
rescue Selenium::WebDriver::Error::ElementNotVisibleError, Selenium::WebDriver::Error::UnknownError => e
|
100
|
+
raise e
|
101
|
+
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
102
|
+
sleep(2)
|
103
|
+
AutomationObject::Logger::add("Stale element error, retrying method #{method_symbol} on element", [self.framework_location])
|
104
|
+
em_object_return = self.element_methods_object.send(method_symbol, *arguments, &block)
|
105
|
+
rescue Selenium::WebDriver::Error::InvalidElementStateError
|
106
|
+
sleep(2)
|
107
|
+
AutomationObject::Logger::add("Invalid element state error, retrying method #{method_symbol} on element", [self.framework_location])
|
108
|
+
self.element_methods_object.find_element
|
109
|
+
em_object_return = self.element_methods_object.send(method_symbol, *arguments, &block)
|
110
|
+
end
|
111
|
+
rescue
|
112
|
+
raise $!, "#{self.framework_location} #{$!}", $!.backtrace
|
113
|
+
end
|
114
|
+
|
115
|
+
if self.check_new_window?(method_symbol)
|
116
|
+
after_window_handles = self.framework_object.get_window_handles
|
117
|
+
window_handle_difference = after_window_handles - before_window_handles
|
118
|
+
if window_handle_difference.length > 1
|
119
|
+
raise "Click on element (#{self.element_name}) in screen (#{self.screen_name}) has opened more than one window."
|
120
|
+
end
|
121
|
+
created_window_handle = window_handle_difference.shift
|
122
|
+
end
|
123
|
+
|
124
|
+
self.hook(method_string, 'after', created_window_handle)
|
125
|
+
return em_object_return
|
126
|
+
end
|
127
|
+
|
128
|
+
def hook(action, timing, created_window_handle = false)
|
129
|
+
self.before_mutex_lock if action == 'before'
|
130
|
+
|
131
|
+
#Return out if it doesn't meet the given requirements
|
132
|
+
return unless self.configuration.class == Hash
|
133
|
+
return unless self.configuration[action].class == Hash
|
134
|
+
return unless self.configuration[action][timing].class == Hash
|
135
|
+
|
136
|
+
hook_configuration = self.configuration[action][timing].clone
|
137
|
+
self.emit :hook, {
|
138
|
+
:configuration => hook_configuration,
|
139
|
+
:created_window_handle => created_window_handle
|
140
|
+
}
|
141
|
+
|
142
|
+
self.after_mutex_lock if action == 'after'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
class ElementArray < Array
|
3
|
+
include AutomationObject::ElementsHelpers
|
4
|
+
|
5
|
+
def load_elements
|
6
|
+
modified_elements_array = self.load_elements_global
|
7
|
+
modified_elements_array.each { |modified_element|
|
8
|
+
self.push(modified_element)
|
9
|
+
}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|