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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/lib/automation_object/blue_print.rb +106 -0
  3. data/lib/automation_object/blue_print_validation/base_validation.rb +44 -0
  4. data/lib/automation_object/blue_print_validation/common_methods.rb +106 -0
  5. data/lib/automation_object/blue_print_validation/element_validation.rb +198 -0
  6. data/lib/automation_object/blue_print_validation/formatted_errors.rb +41 -0
  7. data/lib/automation_object/blue_print_validation/hook_validation.rb +393 -0
  8. data/lib/automation_object/blue_print_validation/key_value_constants.rb +75 -0
  9. data/lib/automation_object/blue_print_validation/modal_validation.rb +37 -0
  10. data/lib/automation_object/blue_print_validation/screen_modal_common_methods.rb +119 -0
  11. data/lib/automation_object/blue_print_validation/screen_validation.rb +21 -0
  12. data/lib/automation_object/blue_print_validation/validation_object.rb +32 -0
  13. data/lib/automation_object/driver/anonymous.rb +27 -0
  14. data/lib/automation_object/driver/driver.rb +281 -0
  15. data/lib/automation_object/driver/element.rb +243 -0
  16. data/lib/automation_object/element/element.rb +145 -0
  17. data/lib/automation_object/element/element_array.rb +12 -0
  18. data/lib/automation_object/element/element_cell.rb +126 -0
  19. data/lib/automation_object/element/element_group.rb +33 -0
  20. data/lib/automation_object/element/element_hash.rb +25 -0
  21. data/lib/automation_object/element/element_helpers.rb +29 -0
  22. data/lib/automation_object/element/element_methods.rb +134 -0
  23. data/lib/automation_object/element/elements_helpers.rb +204 -0
  24. data/lib/automation_object/framework/events.rb +8 -0
  25. data/lib/automation_object/framework/helpers.rb +101 -0
  26. data/lib/automation_object/framework/print_objects.rb +67 -0
  27. data/lib/automation_object/framework/screen_monitor.rb +55 -0
  28. data/lib/automation_object/framework/screen_routing.rb +310 -0
  29. data/lib/automation_object/framework/window_helpers.rb +181 -0
  30. data/lib/automation_object/framework.rb +408 -0
  31. data/lib/automation_object/hash.rb +6 -0
  32. data/lib/automation_object/hook_helpers.rb +27 -0
  33. data/lib/automation_object/logger.rb +179 -0
  34. data/lib/automation_object/object.rb +22 -0
  35. data/lib/automation_object/screen/modal.rb +8 -0
  36. data/lib/automation_object/screen/screen.rb +209 -0
  37. data/lib/automation_object/screen/screen_hook_helpers.rb +319 -0
  38. data/lib/automation_object/screen/screen_modal_helpers.rb +101 -0
  39. data/lib/automation_object/screen/screen_prompt_helpers.rb +21 -0
  40. data/lib/automation_object/screen/screen_window_helpers.rb +149 -0
  41. data/lib/automation_object/version.rb +3 -0
  42. data/lib/automation_object.rb +80 -0
  43. 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