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,126 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
class ElementCell
|
3
|
+
include EventEmitter
|
4
|
+
|
5
|
+
attr_accessor :framework_object, :screen_object, :driver_object, :configuration, :screen_name, :element_name,
|
6
|
+
:sub_elements, :element_object
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
#Set params to properties
|
10
|
+
self.framework_object = params[:framework_object] || raise(ArgumentError, 'framework_object is required in params')
|
11
|
+
self.screen_object = params[:screen_object] || raise(ArgumentError, 'screen_object is required in params')
|
12
|
+
self.driver_object = params[:driver_object] || raise(ArgumentError, 'driver_object is required in params')
|
13
|
+
#Clone configuration, this will allow edit/deletes on different levels
|
14
|
+
self.configuration = params[:blue_prints].clone || raise(ArgumentError, 'configuration is required in params')
|
15
|
+
|
16
|
+
self.screen_name = params[:screen_name] || raise(ArgumentError, 'screen_name is required in params')
|
17
|
+
self.element_name = params[:element_name] || raise(ArgumentError, 'element_name is required in params')
|
18
|
+
|
19
|
+
#Validate properties
|
20
|
+
raise ArgumentError, 'framework_object should be an Framework Object' unless self.framework_object.is_a? Framework
|
21
|
+
raise ArgumentError, 'screen_object should be an Screen Object' unless self.screen_object.is_a? Screen
|
22
|
+
raise ArgumentError, 'configuration should be a Hash Object' unless self.configuration.is_a? Hash
|
23
|
+
raise ArgumentError, 'screen_name should be a String Object' unless self.screen_name.is_a? String
|
24
|
+
raise ArgumentError, 'element_name should be a String Object' unless self.element_name.is_a? String
|
25
|
+
|
26
|
+
self.sub_elements = Array.new
|
27
|
+
|
28
|
+
#Add Sub-Elements
|
29
|
+
return unless self.configuration['sub_elements'].is_a?(Hash)
|
30
|
+
|
31
|
+
self.configuration['sub_elements'].each { |sub_element_name, sub_element_configuration|
|
32
|
+
next unless sub_element_configuration.is_a?(Hash)
|
33
|
+
modified_element_configuration = self.combine_selector_path(self.configuration, sub_element_configuration)
|
34
|
+
|
35
|
+
element_class_name = self.translate_string_to_class(sub_element_name)
|
36
|
+
setter = "#{element_class_name}="
|
37
|
+
self.class.send(:attr_accessor, element_class_name) unless self.respond_to?(setter)
|
38
|
+
|
39
|
+
sub_element_object_options = {
|
40
|
+
:framework_object => self.framework_object,
|
41
|
+
:screen_object => self.screen_object,
|
42
|
+
:driver_object => self.driver_object,
|
43
|
+
:blue_prints => modified_element_configuration,
|
44
|
+
:screen_name => self.screen_name,
|
45
|
+
:element_name => element_name
|
46
|
+
}
|
47
|
+
|
48
|
+
send setter, AutomationObject::Element.new(sub_element_object_options)
|
49
|
+
|
50
|
+
this = self
|
51
|
+
self.send(element_class_name).on :hook do |args|
|
52
|
+
this.emit :hook, args
|
53
|
+
end
|
54
|
+
|
55
|
+
self.sub_elements.push(sub_element_name)
|
56
|
+
}
|
57
|
+
|
58
|
+
#Add Element Object
|
59
|
+
sub_element_object_options = {
|
60
|
+
:framework_object => self.framework_object,
|
61
|
+
:screen_object => self.screen_object,
|
62
|
+
:driver_object => self.driver_object,
|
63
|
+
:blue_prints => self.configuration,
|
64
|
+
:screen_name => self.screen_name,
|
65
|
+
:element_name => self.element_name,
|
66
|
+
:element_object => params[:element_object]
|
67
|
+
}
|
68
|
+
|
69
|
+
self.element_object = AutomationObject::Element.new(sub_element_object_options)
|
70
|
+
this = self
|
71
|
+
self.element_object.on :hook do |args|
|
72
|
+
this.emit :hook, args
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def combine_selector_path(cell_configuration, sub_element_configuration)
|
77
|
+
modified_configuration = sub_element_configuration.clone
|
78
|
+
if cell_configuration['xpath']
|
79
|
+
sub_element_configuration.has_key?('xpath').should == true
|
80
|
+
modified_configuration['xpath'] = cell_configuration['xpath'] + sub_element_configuration['xpath']
|
81
|
+
elsif cell_configuration['css']
|
82
|
+
sub_element_configuration.has_key?('css').should == true
|
83
|
+
modified_configuration['css'] = cell_configuration['css'] + ' ' + sub_element_configuration['css']
|
84
|
+
else
|
85
|
+
raise 'Expecting either css or xpath for selector in element cell configuration'
|
86
|
+
end
|
87
|
+
|
88
|
+
return modified_configuration
|
89
|
+
end
|
90
|
+
|
91
|
+
def respond_to?(method_symbol, include_private = false)
|
92
|
+
#Translate method in possible internal storage attribute
|
93
|
+
class_symbol = self.translate_string_to_class(method_symbol)
|
94
|
+
instance_symbol = class_symbol.to_s.gsub(/^@/, '')
|
95
|
+
instance_symbol = "@#{instance_symbol}".to_sym
|
96
|
+
|
97
|
+
self.instance_variables.each { |instance_variable|
|
98
|
+
return true if instance_variable == instance_symbol
|
99
|
+
}
|
100
|
+
|
101
|
+
#If not then do the super on the method_symbol
|
102
|
+
return true if super.respond_to?(method_symbol, include_private)
|
103
|
+
|
104
|
+
#If not try element object
|
105
|
+
return self.element_object.respond_to?(method_symbol, include_private)
|
106
|
+
end
|
107
|
+
|
108
|
+
def element_respond_to?(method_symbol, include_private = false)
|
109
|
+
return self.element_object.respond_to?(method_symbol, include_private)
|
110
|
+
end
|
111
|
+
|
112
|
+
def method_missing(method_requested, *args, &block)
|
113
|
+
class_symbol = self.translate_string_to_class(method_requested)
|
114
|
+
|
115
|
+
if self.element_respond_to?(method_requested)
|
116
|
+
return self.element_object.send(method_requested, *args, &block)
|
117
|
+
end
|
118
|
+
|
119
|
+
if self.respond_to?(class_symbol)
|
120
|
+
return self.send(class_symbol, *args, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
raise "Element (#{method_requested}) is not defined in ElementCell object"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
class ElementGroup < Array
|
3
|
+
include AutomationObject::ElementsHelpers
|
4
|
+
|
5
|
+
def load_elements
|
6
|
+
elements_array = self.get_elements
|
7
|
+
elements_array.each_with_index { |element_object, index|
|
8
|
+
element_cell_configuration = self.get_individual_configuration(index, self.configuration)
|
9
|
+
|
10
|
+
group_cell_options = {
|
11
|
+
:framework_object => self.framework_object,
|
12
|
+
:screen_object => self.screen_object,
|
13
|
+
:driver_object => self.driver_object,
|
14
|
+
:blue_prints => element_cell_configuration,
|
15
|
+
:screen_name => self.screen_name,
|
16
|
+
:element_name => (self.element_name + "(#{index})"),
|
17
|
+
:element_object => element_object
|
18
|
+
}
|
19
|
+
|
20
|
+
element_cell_object = AutomationObject::ElementCell.new(group_cell_options)
|
21
|
+
|
22
|
+
self.push(element_cell_object)
|
23
|
+
|
24
|
+
this = self
|
25
|
+
element_cell_object.on :hook do |args|
|
26
|
+
this.emit :hook, args
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
self.elements_loaded = true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
class ElementHash < Hash
|
3
|
+
include AutomationObject::ElementsHelpers
|
4
|
+
|
5
|
+
def load_elements
|
6
|
+
modified_elements_array = self.load_elements_global
|
7
|
+
|
8
|
+
modified_elements_array.each { |modified_element|
|
9
|
+
element_key = self.get_element_key(modified_element)
|
10
|
+
#Change element name so that it includes the hash index instead of the array index
|
11
|
+
modified_element.element_name = (self.element_name + "(#{element_key})")
|
12
|
+
self[element_key] = modified_element
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_element_key(element_object)
|
17
|
+
element_method = self.configuration['define_elements_by'].to_sym
|
18
|
+
unless element_object.respond_to?(element_method)
|
19
|
+
raise ArgumentError, "Element object does not have method (#{element_method}), for ElementHash (#{self.element_name}) in Screen (#{self.screen_name})"
|
20
|
+
end
|
21
|
+
|
22
|
+
element_object.send(self.configuration['define_elements_by'])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
module ElementHelpers
|
3
|
+
def get_selector_params(configuration)
|
4
|
+
params = Hash.new
|
5
|
+
if configuration['xpath']
|
6
|
+
params[:selector_method] = :xpath
|
7
|
+
params[:selector] = configuration['xpath']
|
8
|
+
elsif configuration['css']
|
9
|
+
params[:selector_method] = :css
|
10
|
+
params[:selector] = configuration['css']
|
11
|
+
else
|
12
|
+
raise(ArgumentError, 'xPath and CSS are the only element selectors available')
|
13
|
+
end
|
14
|
+
|
15
|
+
params
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_multiple?
|
19
|
+
return true if self.configuration['multiple'] == true
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
def in_iframe?
|
24
|
+
return false unless configuration.is_a?(Hash)
|
25
|
+
return false unless configuration.has_key?('in_iframe')
|
26
|
+
return true if configuration['in_iframe']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#Provides wrapper for mutex safe executions on a single driver
|
2
|
+
#Without results in pain in the ass errors involving IO interruptions with driver
|
3
|
+
module AutomationObject
|
4
|
+
class ElementMethods
|
5
|
+
include AutomationObject::ElementHelpers
|
6
|
+
|
7
|
+
attr_accessor :parent_element_object, :driver_object,
|
8
|
+
:framework_object,
|
9
|
+
:element_object, :configuration,
|
10
|
+
:screen_name, :element_name
|
11
|
+
|
12
|
+
def initialize(parent_element_object)
|
13
|
+
self.parent_element_object = parent_element_object
|
14
|
+
self.framework_object = self.parent_element_object.framework_object
|
15
|
+
|
16
|
+
self.screen_name = self.parent_element_object.screen_name
|
17
|
+
self.element_name = self.parent_element_object.element_name
|
18
|
+
|
19
|
+
self.driver_object = self.parent_element_object.driver_object
|
20
|
+
self.element_object = parent_element_object.element_object if parent_element_object.element_object
|
21
|
+
self.configuration = self.parent_element_object.configuration
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to?(method_symbol, include_private = false)
|
25
|
+
return true if super
|
26
|
+
|
27
|
+
#Check custom_methods configuration, return true if it does have the key
|
28
|
+
if self.configuration['custom_methods'].class == Hash
|
29
|
+
if self.configuration['custom_methods'].has_key?(method_symbol.to_s)
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#Now we need to check the element
|
35
|
+
element_object = self.get_element
|
36
|
+
return element_object.respond_to?(method_symbol, include_private)
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(method_symbol, *arguments, &block)
|
40
|
+
unless element_object.respond_to?(method_symbol)
|
41
|
+
configuration = self.configuration
|
42
|
+
if configuration['custom_methods'].class == Hash
|
43
|
+
if configuration['custom_methods'].has_key?(method_symbol.to_s)
|
44
|
+
method_eval_configuration = configuration['custom_methods'][method_symbol.to_s]
|
45
|
+
return self.custom_evaluation(method_symbol.to_s, method_eval_configuration)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
element_object = self.get_element
|
51
|
+
element_object.send(method_symbol, *arguments, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_element
|
55
|
+
#Return element_object if already set
|
56
|
+
return self.element_object if self.element_object
|
57
|
+
#Get Element
|
58
|
+
self.find_element
|
59
|
+
self.element_object
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_element
|
63
|
+
#Find element if not already set and set it to self
|
64
|
+
selector_params = self.get_selector_params(self.configuration)
|
65
|
+
#ap selector_params
|
66
|
+
self.element_object = self.driver_object.find_element(selector_params[:selector_method], selector_params[:selector])
|
67
|
+
end
|
68
|
+
|
69
|
+
def reset_element
|
70
|
+
if self.element_object
|
71
|
+
AutomationObject::Logger::add('Resetting element', [self.parent_element_object.framework_location])
|
72
|
+
end
|
73
|
+
self.element_object = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def exists?
|
77
|
+
selector_params = self.get_selector_params(self.configuration)
|
78
|
+
element_exists = self.driver_object.exists?(selector_params[:selector_method], selector_params[:selector])
|
79
|
+
|
80
|
+
self.reset_element unless element_exists
|
81
|
+
return element_exists
|
82
|
+
end
|
83
|
+
|
84
|
+
def custom_evaluation(custom_method_name, method_eval_configuration)
|
85
|
+
return nil if method_eval_configuration.class != Hash
|
86
|
+
|
87
|
+
unless method_eval_configuration['element_method']
|
88
|
+
raise ArgumentError, "element_method key is needed for the custom method #{custom_method_name}"
|
89
|
+
end
|
90
|
+
|
91
|
+
unless method_eval_configuration['evaluate']
|
92
|
+
raise ArgumentError, "evaluate key is needed for the custom method #{custom_method_name}"
|
93
|
+
end
|
94
|
+
|
95
|
+
element_method = method_eval_configuration['element_method'].to_sym
|
96
|
+
|
97
|
+
element_object = self.get_element
|
98
|
+
if self.respond_to?(element_method)
|
99
|
+
evaluation_value = self.send(element_method)
|
100
|
+
else
|
101
|
+
evaluation_value = element_object.send(element_method)
|
102
|
+
end
|
103
|
+
|
104
|
+
evaluation_script = "evaluation_value.#{method_eval_configuration['evaluate']}"
|
105
|
+
|
106
|
+
AutomationObject::Logger::add("Running custom evaluation method #{custom_method_name}", [self.parent_element_object.framework_location])
|
107
|
+
|
108
|
+
return eval(evaluation_script)
|
109
|
+
end
|
110
|
+
|
111
|
+
def drag_to_element(secondary_element)
|
112
|
+
if self.parent_element_object.driver_object.device_is_android?
|
113
|
+
raise 'This has not been implemented for Android services yet'
|
114
|
+
end
|
115
|
+
|
116
|
+
#Do this internally to avoid thread locking
|
117
|
+
secondary_element = self.parent_element_object.screen_object.send(secondary_element)
|
118
|
+
|
119
|
+
unless self.parent_element_object.framework_object.is_mobile?
|
120
|
+
return self.driver_object.drag_and_drop(self.get_element, secondary_element.get_element)
|
121
|
+
end
|
122
|
+
|
123
|
+
primary_center = self.get_element_center
|
124
|
+
secondary_center = secondary_element.get_element_center
|
125
|
+
|
126
|
+
location_string = "{x:#{primary_center[:x]}, y:#{primary_center[:y]}}, {x:#{secondary_center[:x]}, y:#{secondary_center[:y]}}"
|
127
|
+
ios_command = "UIATarget.localTarget().dragFromToForDuration(#{location_string}, 0.7);"
|
128
|
+
|
129
|
+
AutomationObject::Logger::add("Running drag command on element to element #{secondary_element}", [self.parent_element_object.framework_location])
|
130
|
+
|
131
|
+
self.driver_object.execute_script(ios_command)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
module ElementsHelpers
|
3
|
+
include EventEmitter
|
4
|
+
include AutomationObject::ElementHelpers
|
5
|
+
|
6
|
+
attr_accessor :framework_object, :screen_object, :driver_object,
|
7
|
+
:configuration,
|
8
|
+
:screen_name, :element_name,
|
9
|
+
:elements_loaded #Boolean
|
10
|
+
|
11
|
+
#Same initialize in both Array and Hash Classes
|
12
|
+
def initialize(params)
|
13
|
+
self.elements_loaded = false #Set to false as default
|
14
|
+
|
15
|
+
#Set params to properties
|
16
|
+
self.framework_object = params[:framework_object] || raise(ArgumentError, 'framework_object is required in params')
|
17
|
+
self.screen_object = params[:screen_object] || raise(ArgumentError, 'screen_object is required in params')
|
18
|
+
self.driver_object = params[:driver_object] || raise(ArgumentError, 'driver_object is required in params')
|
19
|
+
#Clone configuration, this will allow edit/deletes on different levels
|
20
|
+
self.configuration = params[:blue_prints].clone || raise(ArgumentError, 'configuration is required in params')
|
21
|
+
|
22
|
+
self.screen_name = params[:screen_name] || raise(ArgumentError, 'screen_name is required in params')
|
23
|
+
self.element_name = params[:element_name] || raise(ArgumentError, 'element_name is required in params')
|
24
|
+
|
25
|
+
#Validate properties
|
26
|
+
raise ArgumentError, 'framework_object should be an Framework Object' unless self.framework_object.is_a? Framework
|
27
|
+
raise ArgumentError, 'screen_object should be an Screen Object' unless self.screen_object.is_a? Screen
|
28
|
+
raise ArgumentError, 'configuration should be a Hash Object' unless self.configuration.is_a? Hash
|
29
|
+
raise ArgumentError, 'screen_name should be a String Object' unless self.screen_name.is_a? String
|
30
|
+
raise ArgumentError, 'element_name should be a String Object' unless self.element_name.is_a? String
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_elements
|
34
|
+
#Deal with iFrames
|
35
|
+
if self.framework_object.is_browser?
|
36
|
+
if self.in_iframe?
|
37
|
+
self.screen_object.switch_to_iframe(self.element_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
selector_params = self.get_selector_params(self.configuration)
|
42
|
+
|
43
|
+
if self.configuration['multiple_ios_fix'] and self.framework_object.is_mobile?
|
44
|
+
unless self.driver_object.device_is_android?
|
45
|
+
return self.get_elements_temp_ios_fix
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
elements_array = self.driver_object.find_elements(selector_params[:selector_method], selector_params[:selector])
|
50
|
+
|
51
|
+
return elements_array unless elements_array.is_a?(Array)
|
52
|
+
return elements_array if elements_array.length == 0
|
53
|
+
|
54
|
+
if self.configuration['remove_duplicates']
|
55
|
+
elements_array = self.remove_duplicates(elements_array, self.configuration['remove_duplicates'])
|
56
|
+
end
|
57
|
+
|
58
|
+
return elements_array if elements_array.length == 0
|
59
|
+
|
60
|
+
if self.configuration['custom_range']
|
61
|
+
elements_array = self.get_custom_range_elements(elements_array, self.configuration['custom_range'])
|
62
|
+
end
|
63
|
+
|
64
|
+
return elements_array
|
65
|
+
end
|
66
|
+
|
67
|
+
#Dump array into Hash so that we can skip already existing keys that return Hash values Array
|
68
|
+
def remove_duplicates(elements_array, element_method)
|
69
|
+
element_method = element_method.to_sym
|
70
|
+
elements_hash = Hash.new
|
71
|
+
|
72
|
+
elements_array.each { |element|
|
73
|
+
unless element.respond_to?(element_method)
|
74
|
+
raise "Expecting element to respond to method #{element_method} defined in remove_duplicates for #{self.screen_name}->#{self.element_name}"
|
75
|
+
end
|
76
|
+
|
77
|
+
element_value = element.send(element_method)
|
78
|
+
|
79
|
+
next if elements_hash.has_key?(element_value)
|
80
|
+
elements_hash[element.send(element_method)] = element
|
81
|
+
}
|
82
|
+
|
83
|
+
#Return the values array
|
84
|
+
return elements_hash.values
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_custom_range_elements(elements_array, range_hash)
|
88
|
+
unless range_hash.is_a?(Hash)
|
89
|
+
raise "Expecting custom_range to be a Hash, got #{range_hash.class}. Defined in #{self.screen_name}->#{self.element_name}"
|
90
|
+
end
|
91
|
+
|
92
|
+
start_index = 0
|
93
|
+
if range_hash['start'].is_a?(Numeric)
|
94
|
+
start_index = range_hash['start'].to_i
|
95
|
+
end
|
96
|
+
|
97
|
+
end_index = 0
|
98
|
+
if range_hash['end'].is_a?(Numeric)
|
99
|
+
end_index = range_hash['end'].to_i
|
100
|
+
end
|
101
|
+
|
102
|
+
new_elements_array = Array.new
|
103
|
+
elements_array.each_with_index { |element, index|
|
104
|
+
element_index = index + 1 #Since elements start at 1
|
105
|
+
next if element_index < start_index
|
106
|
+
|
107
|
+
if end_index != 0
|
108
|
+
break if element_index > end_index
|
109
|
+
end
|
110
|
+
|
111
|
+
new_elements_array.push(element)
|
112
|
+
}
|
113
|
+
|
114
|
+
return new_elements_array
|
115
|
+
end
|
116
|
+
|
117
|
+
#Todo: remove when Appium is fixed for iOS Apps
|
118
|
+
#Multiple elements in iOS App is killing the whole thing
|
119
|
+
#Get around the problem by appending [index] to each using find_element til failure
|
120
|
+
def get_elements_temp_ios_fix
|
121
|
+
selector_params = self.get_selector_params(self.configuration)
|
122
|
+
#Get rid of any shit at the end, //blah/element[1] becomes //blah/element only for numeric indexes, going to add a numeric index
|
123
|
+
|
124
|
+
elements_array = Array.new
|
125
|
+
|
126
|
+
index = 1
|
127
|
+
find_element_failure = false
|
128
|
+
until find_element_failure
|
129
|
+
selector_params[:selector] = selector_params[:selector].gsub(/\[\d+\]$/, '')
|
130
|
+
selector_params[:selector] = selector_params[:selector] + "[#{index}]"
|
131
|
+
|
132
|
+
begin
|
133
|
+
if self.driver_object.exists?(selector_params[:selector_method], selector_params[:selector])
|
134
|
+
elements_array.push(self.driver_object.find_element(selector_params[:selector_method], selector_params[:selector]))
|
135
|
+
else
|
136
|
+
find_element_failure = true
|
137
|
+
break
|
138
|
+
end
|
139
|
+
rescue
|
140
|
+
find_element_failure = true
|
141
|
+
break
|
142
|
+
end
|
143
|
+
|
144
|
+
index += 1
|
145
|
+
end
|
146
|
+
|
147
|
+
return elements_array
|
148
|
+
end
|
149
|
+
|
150
|
+
#Cannot override module method, call this in Hash/Array and do specifics in
|
151
|
+
# load_elements of those classes
|
152
|
+
def load_elements_global
|
153
|
+
elements_array = self.get_elements
|
154
|
+
|
155
|
+
modified_elements_array = Array.new
|
156
|
+
elements_array.each_with_index do |element_object, index|
|
157
|
+
#Get params for individual element, given the index
|
158
|
+
element_configuration = self.get_individual_configuration(index, self.configuration)
|
159
|
+
|
160
|
+
element_object_options = {
|
161
|
+
:framework_object => self.framework_object,
|
162
|
+
:screen_object => self.screen_object,
|
163
|
+
:driver_object => self.driver_object,
|
164
|
+
:blue_prints => element_configuration,
|
165
|
+
:screen_name => self.screen_name,
|
166
|
+
:element_name => (self.element_name + "(#{index})"),
|
167
|
+
:element_object => element_object
|
168
|
+
}
|
169
|
+
|
170
|
+
new_element = AutomationObject::Element.new(element_object_options)
|
171
|
+
|
172
|
+
this = self
|
173
|
+
new_element.on :hook do |args|
|
174
|
+
this.emit :hook, args
|
175
|
+
end
|
176
|
+
|
177
|
+
modified_elements_array.push(new_element)
|
178
|
+
end
|
179
|
+
|
180
|
+
self.elements_loaded = true
|
181
|
+
return modified_elements_array
|
182
|
+
end
|
183
|
+
|
184
|
+
def get_individual_configuration(index, configuration)
|
185
|
+
element_configuration = configuration.clone
|
186
|
+
element_index = index + 1 #Elements start at one instead of zero, so add one
|
187
|
+
|
188
|
+
if element_configuration['xpath']
|
189
|
+
element_configuration['xpath'] = '(' + element_configuration['xpath'].to_s + ')' + "[position() = #{element_index}]"
|
190
|
+
elsif element_configuration['css']
|
191
|
+
element_configuration['css'] = element_configuration['css'].to_s + ":nth-of-type(#{element_index})"
|
192
|
+
else
|
193
|
+
raise(ArgumentError, 'xPath and CSS are the only element selectors available')
|
194
|
+
end
|
195
|
+
|
196
|
+
return element_configuration
|
197
|
+
end
|
198
|
+
|
199
|
+
def reset_elements
|
200
|
+
self.clear #Same method for both Hashes and Arrays
|
201
|
+
self.elements_loaded = false
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module AutomationObject
|
2
|
+
module FrameworkHelpers
|
3
|
+
attr_accessor :is_mobile, :is_browser, :supports_contexts
|
4
|
+
|
5
|
+
def is_mobile?
|
6
|
+
if self.is_mobile == nil
|
7
|
+
self.is_mobile = self.supports_contexts?
|
8
|
+
end
|
9
|
+
|
10
|
+
return self.is_mobile
|
11
|
+
end
|
12
|
+
|
13
|
+
def is_browser?
|
14
|
+
unless self.is_browser == nil
|
15
|
+
return self.is_browser
|
16
|
+
end
|
17
|
+
|
18
|
+
#If Selenium the yeah we are using a browser
|
19
|
+
if self.supports_window_handles?
|
20
|
+
self.is_browser = true
|
21
|
+
return self.is_browser
|
22
|
+
end
|
23
|
+
|
24
|
+
#Now we need to check Appium's contexts to see if WEBVIEW is in available_contexts
|
25
|
+
available_contexts = self.driver_object.available_contexts
|
26
|
+
self.supports_contexts = true
|
27
|
+
|
28
|
+
available_contexts.each { |context|
|
29
|
+
if context.match(/^WEBVIEW_\d+$/)
|
30
|
+
self.is_browser = true
|
31
|
+
return self.is_browser
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
self.is_browser = false
|
36
|
+
return self.is_browser
|
37
|
+
end
|
38
|
+
|
39
|
+
#Selenium's use of window_handles
|
40
|
+
def supports_window_handles?
|
41
|
+
begin
|
42
|
+
self.driver_object.window_handles
|
43
|
+
return true
|
44
|
+
rescue NoMethodError, Selenium::WebDriver::Error::UnknownError
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#Appium's use of window_handles
|
50
|
+
def supports_contexts?
|
51
|
+
return self.supports_contexts if self.supports_contexts != nil
|
52
|
+
|
53
|
+
begin
|
54
|
+
self.driver_object.available_contexts
|
55
|
+
self.supports_contexts = true
|
56
|
+
return true
|
57
|
+
rescue NoMethodError
|
58
|
+
return false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def current_screen
|
63
|
+
current_screen_object = Object.new
|
64
|
+
current_screen_object.class.module_eval { attr_accessor :framework_object }
|
65
|
+
current_screen_object.framework_object = self
|
66
|
+
|
67
|
+
def current_screen_object.method_missing(method_symbol, *args, &block)
|
68
|
+
return self.framework_object.send(self.framework_object.get_current_screen).send(method_symbol, *args, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
current_screen_object
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_current_screen
|
75
|
+
return self.translate_class_to_string(self.current_screen_class).to_sym
|
76
|
+
end
|
77
|
+
|
78
|
+
def kill_monitor_threads
|
79
|
+
begin
|
80
|
+
self.screen_monitor_thread.each_value { |thread|
|
81
|
+
Thread.kill(thread) if thread
|
82
|
+
}
|
83
|
+
rescue
|
84
|
+
# ignored
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def quit
|
89
|
+
AutomationObject::Logger::add('Quitting the framework')
|
90
|
+
self.kill_monitor_threads
|
91
|
+
|
92
|
+
if self.driver_object.respond_to?(:quit)
|
93
|
+
self.driver_object.quit if self.driver_object.respond_to?(:quit)
|
94
|
+
elsif self.driver_object.respond_to?(:driver_quit)
|
95
|
+
self.driver_object.driver_quit if self.driver_object.respond_to?(:driver_quit)
|
96
|
+
else
|
97
|
+
raise 'Unable to find provided driver quit method'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|