testcentricity 2.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +93 -0
- data/LICENSE.txt +28 -0
- data/README.md +1634 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/devices/devices.yml +344 -0
- data/lib/testcentricity.rb +144 -0
- data/lib/testcentricity/app_core/appium_connect_helper.rb +154 -0
- data/lib/testcentricity/app_core/appium_server.rb +69 -0
- data/lib/testcentricity/app_core/screen_objects_helper.rb +180 -0
- data/lib/testcentricity/app_core/screen_sections_helper.rb +332 -0
- data/lib/testcentricity/app_elements/app_element_helper.rb +293 -0
- data/lib/testcentricity/app_elements/button.rb +8 -0
- data/lib/testcentricity/app_elements/checkbox.rb +20 -0
- data/lib/testcentricity/app_elements/label.rb +8 -0
- data/lib/testcentricity/app_elements/list.rb +25 -0
- data/lib/testcentricity/app_elements/switch.rb +20 -0
- data/lib/testcentricity/app_elements/textfield.rb +12 -0
- data/lib/testcentricity/browser_helper.rb +174 -0
- data/lib/testcentricity/data_objects/data_objects_helper.rb +78 -0
- data/lib/testcentricity/data_objects/environment.rb +281 -0
- data/lib/testcentricity/data_objects/excel_helper.rb +242 -0
- data/lib/testcentricity/exception_queue_helper.rb +51 -0
- data/lib/testcentricity/utility_helpers.rb +28 -0
- data/lib/testcentricity/version.rb +3 -0
- data/lib/testcentricity/web_core/drag_drop_helper.rb +15 -0
- data/lib/testcentricity/web_core/page_objects_helper.rb +669 -0
- data/lib/testcentricity/web_core/page_sections_helper.rb +866 -0
- data/lib/testcentricity/web_core/webdriver_helper.rb +579 -0
- data/lib/testcentricity/web_elements/button.rb +8 -0
- data/lib/testcentricity/web_elements/cell_button.rb +8 -0
- data/lib/testcentricity/web_elements/cell_checkbox.rb +38 -0
- data/lib/testcentricity/web_elements/cell_element.rb +69 -0
- data/lib/testcentricity/web_elements/cell_image.rb +8 -0
- data/lib/testcentricity/web_elements/cell_radio.rb +31 -0
- data/lib/testcentricity/web_elements/checkbox.rb +100 -0
- data/lib/testcentricity/web_elements/file_field.rb +45 -0
- data/lib/testcentricity/web_elements/image.rb +34 -0
- data/lib/testcentricity/web_elements/label.rb +8 -0
- data/lib/testcentricity/web_elements/link.rb +8 -0
- data/lib/testcentricity/web_elements/list.rb +73 -0
- data/lib/testcentricity/web_elements/list_button.rb +8 -0
- data/lib/testcentricity/web_elements/list_checkbox.rb +38 -0
- data/lib/testcentricity/web_elements/list_element.rb +61 -0
- data/lib/testcentricity/web_elements/list_radio.rb +31 -0
- data/lib/testcentricity/web_elements/radio.rb +74 -0
- data/lib/testcentricity/web_elements/select_list.rb +197 -0
- data/lib/testcentricity/web_elements/siebel_open_ui_helper.rb +15 -0
- data/lib/testcentricity/web_elements/table.rb +612 -0
- data/lib/testcentricity/web_elements/textfield.rb +114 -0
- data/lib/testcentricity/web_elements/ui_elements_helper.rb +502 -0
- data/lib/testcentricity/world_extensions.rb +26 -0
- data/my_templates/default/method_details/setup.rb +3 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/testcentricity_spec.rb +9 -0
- data/testcentricity.gemspec +47 -0
- metadata +328 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'appium_lib'
|
2
|
+
|
3
|
+
|
4
|
+
module TestCentricity
|
5
|
+
module AppiumConnect
|
6
|
+
|
7
|
+
attr_accessor :running
|
8
|
+
attr_accessor :config_file
|
9
|
+
|
10
|
+
def self.initialize_appium(project_path = nil)
|
11
|
+
Environ.platform = :mobile
|
12
|
+
Environ.driver = :appium
|
13
|
+
Environ.device_type = ENV['DEVICE_TYPE'] if ENV['DEVICE_TYPE']
|
14
|
+
|
15
|
+
@config_file = project_path
|
16
|
+
|
17
|
+
if project_path.nil?
|
18
|
+
Environ.device_name = ENV['APP_DEVICE']
|
19
|
+
Environ.device_os = ENV['APP_PLATFORM_NAME']
|
20
|
+
Environ.device_orientation = ENV['ORIENTATION'] if ENV['ORIENTATION']
|
21
|
+
if ENV['UDID']
|
22
|
+
Environ.device = :device
|
23
|
+
else
|
24
|
+
Environ.device = :simulator
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@running = false
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.start_driver
|
31
|
+
if @config_file.nil?
|
32
|
+
desired_capabilities = {
|
33
|
+
caps: {
|
34
|
+
platformName: ENV['APP_PLATFORM_NAME'],
|
35
|
+
platformVersion: ENV['APP_VERSION'],
|
36
|
+
deviceName: ENV['APP_DEVICE'],
|
37
|
+
automationName: ENV['AUTOMATION_ENGINE']
|
38
|
+
}
|
39
|
+
}
|
40
|
+
capabilities = desired_capabilities[:caps]
|
41
|
+
capabilities[:avd] = ENV['APP_DEVICE'] if ENV['APP_PLATFORM_NAME'].downcase.to_sym == :android
|
42
|
+
capabilities[:orientation] = ENV['ORIENTATION'].upcase if ENV['ORIENTATION']
|
43
|
+
capabilities[:language] = ENV['LANGUAGE'] if ENV['LANGUAGE']
|
44
|
+
capabilities[:locale] = ENV['LOCALE'].gsub('-', '_') if ENV['LOCALE']
|
45
|
+
capabilities[:newCommandTimeout] = ENV['NEW_COMMAND_TIMEOUT'] if ENV['NEW_COMMAND_TIMEOUT']
|
46
|
+
capabilities[:noReset] = ENV['APP_NO_RESET'] if ENV['APP_NO_RESET']
|
47
|
+
capabilities[:fullReset] = ENV['APP_FULL_RESET'] if ENV['APP_FULL_RESET']
|
48
|
+
if ENV['UDID']
|
49
|
+
capabilities[:udid] = ENV['UDID']
|
50
|
+
capabilities[:bundleId] = ENV['BUNDLE_ID'] if ENV['BUNDLE_ID']
|
51
|
+
capabilities[:xcodeOrgId] = ENV['TEAM_ID'] if ENV['TEAM_ID']
|
52
|
+
capabilities[:xcodeSigningId] = ENV['TEAM_NAME'] if ENV['TEAM_NAME']
|
53
|
+
capabilities[:appActivity] = ENV['APP_ACTIVITY'] if ENV['APP_ACTIVITY']
|
54
|
+
capabilities[:appPackage] = ENV['APP_PACKAGE'] if ENV['APP_PACKAGE']
|
55
|
+
end
|
56
|
+
|
57
|
+
if ENV['APP']
|
58
|
+
capabilities[:app] = ENV['APP']
|
59
|
+
else
|
60
|
+
if Environ.is_android?
|
61
|
+
capabilities[:app] = Environ.current.android_apk_path
|
62
|
+
elsif Environ.is_ios?
|
63
|
+
if Environ.is_device?
|
64
|
+
capabilities[:app] = Environ.current.ios_ipa_path
|
65
|
+
else
|
66
|
+
capabilities[:app] = Environ.current.ios_app_path
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
else
|
72
|
+
base_path = 'config'
|
73
|
+
unless File.directory?(File.join(@config_file, base_path))
|
74
|
+
raise 'Could not find appium.txt files in /config folder'
|
75
|
+
end
|
76
|
+
appium_caps = File.join(@config_file, base_path, 'appium.txt')
|
77
|
+
desired_capabilities = Appium.load_appium_txt file: appium_caps
|
78
|
+
end
|
79
|
+
Appium::Driver.new(desired_capabilities).start_driver
|
80
|
+
@running = true
|
81
|
+
Appium.promote_appium_methods TestCentricity::ScreenObject
|
82
|
+
Appium.promote_appium_methods TestCentricity::AppUIElement
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.quit_driver
|
86
|
+
if @running
|
87
|
+
$driver.driver_quit
|
88
|
+
@running = false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.driver
|
93
|
+
$driver
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.take_screenshot(png_save_path)
|
97
|
+
FileUtils.mkdir_p(File.dirname(png_save_path))
|
98
|
+
$driver.driver.save_screenshot(png_save_path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.install_app(app_path)
|
102
|
+
$driver.install_app(app_path)
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.app_installed?(bundle_id)
|
106
|
+
$driver.app_installed?(bundle_id)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.launch_app
|
110
|
+
$driver.launch_app
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.close_app
|
114
|
+
$driver.close_app
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.reset_app
|
118
|
+
$driver.reset
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.remove_app(bundle_id)
|
122
|
+
$driver.remove_app(bundle_id)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.implicit_wait(timeout)
|
126
|
+
$driver.manage.timeouts.implicit_wait = timeout
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.hide_keyboard
|
130
|
+
$driver.hide_keyboard
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.keyboard_shown?
|
134
|
+
$driver.is_keyboard_shown
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.set_geolocation(latitude, longitude, altitude)
|
138
|
+
$driver.set_location(latitude, longitude, altitude)
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.current_context
|
142
|
+
$driver.current_context
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.set_context(context)
|
146
|
+
$driver.set_context(context)
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.available_contexts
|
150
|
+
$driver.available_contexts
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'childprocess'
|
2
|
+
|
3
|
+
#
|
4
|
+
# This class is designed to start and stop the Appium Server process. In order to use it you must have Appium
|
5
|
+
# and node installed on your computer. You can pass parameters to Appium at startup via the constructor.
|
6
|
+
#
|
7
|
+
module TestCentricity
|
8
|
+
class AppiumServer
|
9
|
+
|
10
|
+
attr_accessor :process
|
11
|
+
|
12
|
+
def initialize(params={})
|
13
|
+
@params = params
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Start the Appium Server
|
18
|
+
#
|
19
|
+
def start
|
20
|
+
# terminate any currently running Appium Server
|
21
|
+
if running?
|
22
|
+
system('killall -9 node')
|
23
|
+
puts 'Terminating existing Appium Server'
|
24
|
+
sleep(5)
|
25
|
+
puts 'Appium Server is being restarted'
|
26
|
+
else
|
27
|
+
puts 'Appium Server is being started'
|
28
|
+
end
|
29
|
+
# start new Appium Server
|
30
|
+
@process = ChildProcess.build(*parameters)
|
31
|
+
process.start
|
32
|
+
# wait until confirmation that Appium Server is running
|
33
|
+
wait = Selenium::WebDriver::Wait.new(timeout: 30)
|
34
|
+
wait.until { running? }
|
35
|
+
puts "Appium Server is running - PID = #{process.pid}"
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Check to see if Appium Server is running
|
40
|
+
#
|
41
|
+
def running?
|
42
|
+
response = nil
|
43
|
+
begin
|
44
|
+
response = Net::HTTP.get_response(URI('http://127.0.0.1:4723/wd/hub/sessions'))
|
45
|
+
rescue
|
46
|
+
end
|
47
|
+
response && response.code_type == Net::HTTPOK
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Stop the Appium Server
|
52
|
+
#
|
53
|
+
def stop
|
54
|
+
process.stop
|
55
|
+
puts 'Appium Server has been terminated'
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def parameters
|
61
|
+
cmd = ['appium']
|
62
|
+
@params.each do |key, value|
|
63
|
+
cmd << '--' + key.to_s
|
64
|
+
cmd << value.to_s if not value.nil? and value.size > 0
|
65
|
+
end
|
66
|
+
cmd
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
module TestCentricity
|
4
|
+
class ScreenObject
|
5
|
+
include Test::Unit::Assertions
|
6
|
+
|
7
|
+
attr_reader :strategy, :locator
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
raise "Screen object #{self.class.name} does not have a page_locator trait defined" unless defined?(page_locator)
|
11
|
+
@strategy = page_locator[0].to_sym
|
12
|
+
@locator = page_locator[1]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.trait(trait_name, &block)
|
16
|
+
define_method(trait_name.to_s, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.element(element_name, strategy, locator)
|
20
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppUIElement.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.button(element_name, strategy, locator)
|
24
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppButton.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.textfield(element_name, strategy, locator)
|
28
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppTextField.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.switch(element_name, strategy, locator)
|
32
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppSwitch.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.checkbox(element_name, strategy, locator)
|
36
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppCheckBox.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.label(element_name, strategy, locator)
|
40
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppLabel.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.list(element_name, strategy, locator)
|
44
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppList.new("#{element_name}", self, "#{strategy}", "#{locator}", :page);end))
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.section(section_name, class_name)
|
48
|
+
class_eval(%(def #{section_name};@#{section_name} ||= #{class_name}.new("#{section_name}", self, :page);end))
|
49
|
+
end
|
50
|
+
|
51
|
+
def open_portal
|
52
|
+
environment = Environ.current
|
53
|
+
|
54
|
+
Environ.portal_state = :open
|
55
|
+
end
|
56
|
+
|
57
|
+
def verify_page_exists
|
58
|
+
wait = Selenium::WebDriver::Wait.new(timeout: Environ.default_max_wait_time)
|
59
|
+
wait.until { $driver.find_element(@strategy.to_sym, @locator).displayed? }
|
60
|
+
PageManager.current_page = self
|
61
|
+
rescue
|
62
|
+
raise "Could not find page_locator for screen object '#{self.class.name}' (:#{@strategy}, #{@locator}) after #{Environ.default_max_wait_time} seconds"
|
63
|
+
end
|
64
|
+
|
65
|
+
def navigate_to; end
|
66
|
+
|
67
|
+
def verify_page_ui; end
|
68
|
+
|
69
|
+
def load_page
|
70
|
+
verify_page_exists
|
71
|
+
PageManager.current_page = self
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
def verify_ui_states(ui_states)
|
77
|
+
ui_states.each do |ui_object, object_states|
|
78
|
+
object_states.each do |property, state|
|
79
|
+
|
80
|
+
|
81
|
+
puts "#{ui_object.get_name} - #{property} = #{state}" if ENV['DEBUG']
|
82
|
+
|
83
|
+
|
84
|
+
case property
|
85
|
+
when :exists
|
86
|
+
actual = ui_object.exists?
|
87
|
+
when :enabled
|
88
|
+
actual = ui_object.enabled?
|
89
|
+
when :disabled
|
90
|
+
actual = ui_object.disabled?
|
91
|
+
when :visible
|
92
|
+
actual = ui_object.visible?
|
93
|
+
when :hidden
|
94
|
+
actual = ui_object.hidden?
|
95
|
+
when :checked
|
96
|
+
actual = ui_object.checked?
|
97
|
+
when :selected
|
98
|
+
actual = ui_object.selected?
|
99
|
+
when :value
|
100
|
+
actual = ui_object.get_value
|
101
|
+
when :caption
|
102
|
+
actual = ui_object.get_caption
|
103
|
+
when :name
|
104
|
+
actual = ui_object.tag_name
|
105
|
+
when :placeholder
|
106
|
+
actual = ui_object.get_placeholder
|
107
|
+
when :items
|
108
|
+
actual = ui_object.get_list_items
|
109
|
+
when :itemcount
|
110
|
+
actual = ui_object.get_item_count
|
111
|
+
end
|
112
|
+
|
113
|
+
if state.is_a?(Hash) && state.length == 1
|
114
|
+
error_msg = "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) #{property} property to"
|
115
|
+
state.each do |key, value|
|
116
|
+
case key
|
117
|
+
when :lt, :less_than
|
118
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be less than #{value} but found '#{actual}'") unless actual < value
|
119
|
+
when :lt_eq, :less_than_or_equal
|
120
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be less than or equal to #{value} but found '#{actual}'") unless actual <= value
|
121
|
+
when :gt, :greater_than
|
122
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be greater than #{value} but found '#{actual}'") unless actual > value
|
123
|
+
when :gt_eq, :greater_than_or_equal
|
124
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be greater than or equal to #{value} but found '#{actual}'") unless actual >= value
|
125
|
+
when :starts_with
|
126
|
+
ExceptionQueue.enqueue_exception("#{error_msg} start with '#{value}' but found '#{actual}'") unless actual.start_with?(value)
|
127
|
+
when :ends_with
|
128
|
+
ExceptionQueue.enqueue_exception("#{error_msg} end with '#{value}' but found '#{actual}'") unless actual.end_with?(value)
|
129
|
+
when :contains
|
130
|
+
ExceptionQueue.enqueue_exception("#{error_msg} contain '#{value}' but found '#{actual}'") unless actual.include?(value)
|
131
|
+
when :not_contains, :does_not_contain
|
132
|
+
ExceptionQueue.enqueue_exception("#{error_msg} not contain '#{value}' but found '#{actual}'") if actual.include?(value)
|
133
|
+
when :not_equal
|
134
|
+
ExceptionQueue.enqueue_exception("#{error_msg} not equal '#{value}' but found '#{actual}'") if actual == value
|
135
|
+
when :like, :is_like
|
136
|
+
actual_like = actual.delete("\n")
|
137
|
+
actual_like = actual_like.delete("\r")
|
138
|
+
actual_like = actual_like.delete("\t")
|
139
|
+
actual_like = actual_like.delete(' ')
|
140
|
+
actual_like = actual_like.downcase
|
141
|
+
expected = value.delete("\n")
|
142
|
+
expected = expected.delete("\r")
|
143
|
+
expected = expected.delete("\t")
|
144
|
+
expected = expected.delete(' ')
|
145
|
+
expected = expected.downcase
|
146
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be like '#{value}' but found '#{actual}'") unless actual_like.include?(expected)
|
147
|
+
when :translate
|
148
|
+
expected = I18n.t(value)
|
149
|
+
ExceptionQueue.enqueue_assert_equal(expected, actual, "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) translated #{property} property")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
else
|
153
|
+
ExceptionQueue.enqueue_assert_equal(state, actual, "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) #{property} property")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
ExceptionQueue.post_exceptions
|
158
|
+
end
|
159
|
+
|
160
|
+
def populate_data_fields(data, wait_time = nil)
|
161
|
+
timeout = wait_time.nil? ? 5 : wait_time
|
162
|
+
data.each do |data_field, data_param|
|
163
|
+
unless data_param.blank?
|
164
|
+
# make sure the intended UI target element is visible before trying to set its value
|
165
|
+
data_field.wait_until_visible(timeout)
|
166
|
+
if data_param == '!DELETE'
|
167
|
+
data_field.clear
|
168
|
+
else
|
169
|
+
case data_field.get_object_type
|
170
|
+
when :checkbox
|
171
|
+
data_field.set_checkbox_state(data_param.to_bool)
|
172
|
+
when :textfield
|
173
|
+
data_field.set("#{data_param}\t")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
module TestCentricity
|
4
|
+
class ScreenSection
|
5
|
+
include Test::Unit::Assertions
|
6
|
+
|
7
|
+
attr_reader :context, :name
|
8
|
+
attr_accessor :locator
|
9
|
+
attr_accessor :parent
|
10
|
+
attr_accessor :strategy
|
11
|
+
attr_accessor :parent_list
|
12
|
+
attr_accessor :list_index
|
13
|
+
|
14
|
+
def initialize(name, parent, context)
|
15
|
+
@name = name
|
16
|
+
@parent = parent
|
17
|
+
@context = context
|
18
|
+
@parent_list = nil
|
19
|
+
@list_index = nil
|
20
|
+
@strategy = nil
|
21
|
+
@locator = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_locator
|
25
|
+
if @locator.nil? && defined?(section_locator)
|
26
|
+
@strategy = section_locator[0]
|
27
|
+
@locator = section_locator[1]
|
28
|
+
end
|
29
|
+
|
30
|
+
locators = []
|
31
|
+
if @context == :section && !@parent.nil?
|
32
|
+
parent_strategy, parent_locator = @parent.get_locator
|
33
|
+
end
|
34
|
+
|
35
|
+
if @parent_list.nil?
|
36
|
+
if parent_strategy == @strategy
|
37
|
+
locators.push([@strategy, @locator])
|
38
|
+
else
|
39
|
+
locators.push([parent_strategy, parent_locator]) unless parent_strategy.nil?
|
40
|
+
locators.push([@strategy, "#{parent_locator}#{@locator}"])
|
41
|
+
end
|
42
|
+
else
|
43
|
+
list_strategy, list_locator = @parent_list.get_locator
|
44
|
+
if parent_strategy == list_strategy
|
45
|
+
list_locator = "#{parent_locator}#{list_locator}"
|
46
|
+
else
|
47
|
+
locators.push([parent_strategy, parent_locator]) unless parent_strategy.nil?
|
48
|
+
end
|
49
|
+
if list_strategy == @strategy
|
50
|
+
obj_locator = "#{list_locator}#{@locator}"
|
51
|
+
else
|
52
|
+
obj_locator = @locator
|
53
|
+
locators.push([list_strategy, list_locator])
|
54
|
+
end
|
55
|
+
unless @list_index.nil?
|
56
|
+
obj_locator = "#{obj_locator}[#{@list_index}]"
|
57
|
+
end
|
58
|
+
locators.push([@strategy, obj_locator])
|
59
|
+
end
|
60
|
+
locators
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def set_list_index(list, index = 1)
|
65
|
+
@parent_list = list unless list.nil?
|
66
|
+
@list_index = index
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_item_count
|
70
|
+
raise 'No parent list defined' if @parent_list.nil?
|
71
|
+
@parent_list.get_item_count
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_list_items
|
75
|
+
items = []
|
76
|
+
(1..get_item_count).each do |item|
|
77
|
+
set_list_index(nil, item)
|
78
|
+
items.push(get_value)
|
79
|
+
end
|
80
|
+
items
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_object_type
|
84
|
+
:section
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_name
|
88
|
+
@name
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_parent(parent)
|
92
|
+
@parent = parent
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def self.trait(trait_name, &block)
|
97
|
+
define_method(trait_name.to_s, &block)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.element(element_name, strategy, locator)
|
101
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppUIElement.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.button(element_name, strategy, locator)
|
105
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppButton.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.textfield(element_name, strategy, locator)
|
109
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppTextField.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.switch(element_name, strategy, locator)
|
113
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppSwitch.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.checkbox(element_name, strategy, locator)
|
117
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppCheckBox.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.label(element_name, strategy, locator)
|
121
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppLabel.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.list(element_name, strategy, locator)
|
125
|
+
class_eval(%(def #{element_name};@#{element_name} ||= TestCentricity::AppList.new("#{element_name}", self, "#{strategy}", "#{locator}", :section);end))
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.section(section_name, class_name)
|
129
|
+
class_eval(%(def #{section_name};@#{section_name} ||= #{class_name}.new("#{section_name}", self, :section);end))
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
def click
|
135
|
+
section = find_section
|
136
|
+
raise "Section object '#{get_name}' (#{get_locator}) not found" unless section
|
137
|
+
section.click
|
138
|
+
end
|
139
|
+
|
140
|
+
def tap
|
141
|
+
section = find_section
|
142
|
+
raise "Section object '#{get_name}' (#{get_locator}) not found" unless section
|
143
|
+
x = section.location.x
|
144
|
+
y = section.location.y
|
145
|
+
tap_action = Appium::TouchAction.new.tap(element: section, x: x, y: y)
|
146
|
+
tap_action.perform
|
147
|
+
end
|
148
|
+
|
149
|
+
def double_tap
|
150
|
+
section = find_section
|
151
|
+
raise "Section object '#{get_name}' (#{get_locator}) not found" unless section
|
152
|
+
x = section.location.x
|
153
|
+
y = section.location.y
|
154
|
+
tap_action = Appium::TouchAction.new.double_tap(element: section, x: x, y: y)
|
155
|
+
tap_action.perform
|
156
|
+
end
|
157
|
+
|
158
|
+
def exists?
|
159
|
+
section = find_section
|
160
|
+
section != nil
|
161
|
+
end
|
162
|
+
|
163
|
+
def enabled?
|
164
|
+
section = find_section
|
165
|
+
raise "Section object '#{get_name}' (#{get_locator}) not found" unless section
|
166
|
+
section.enabled?
|
167
|
+
end
|
168
|
+
|
169
|
+
def disabled?
|
170
|
+
section = find_section
|
171
|
+
raise "Section object '#{get_name}' (#{get_locator}) not found" unless section
|
172
|
+
section.enabled?
|
173
|
+
end
|
174
|
+
|
175
|
+
def visible?
|
176
|
+
if exists?
|
177
|
+
section.displayed?
|
178
|
+
else
|
179
|
+
false
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def hidden?
|
184
|
+
!visible?
|
185
|
+
end
|
186
|
+
|
187
|
+
def wait_until_exists(seconds = nil)
|
188
|
+
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
189
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
190
|
+
wait.until { exists? }
|
191
|
+
rescue
|
192
|
+
raise "Could not find Section object '#{get_name}' (#{get_locator}) after #{timeout} seconds" unless exists?
|
193
|
+
end
|
194
|
+
|
195
|
+
def wait_until_gone(seconds = nil)
|
196
|
+
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
197
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
198
|
+
wait.until { !exists? }
|
199
|
+
rescue
|
200
|
+
raise "Section object '#{get_name}' (#{get_locator}) remained visible after #{timeout} seconds" if exists?
|
201
|
+
end
|
202
|
+
|
203
|
+
def wait_until_visible(seconds = nil)
|
204
|
+
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
205
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
206
|
+
wait.until { visible? }
|
207
|
+
rescue
|
208
|
+
raise "Could not find Section object '#{get_name}' (#{get_locator}) after #{timeout} seconds" unless visible?
|
209
|
+
end
|
210
|
+
|
211
|
+
def wait_until_hidden(seconds = nil)
|
212
|
+
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
213
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
214
|
+
wait.until { hidden? }
|
215
|
+
rescue
|
216
|
+
raise "Section object '#{get_name}' (#{get_locator}) remained visible after #{timeout} seconds" if visible?
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def verify_ui_states(ui_states)
|
221
|
+
ui_states.each do |ui_object, object_states|
|
222
|
+
object_states.each do |property, state|
|
223
|
+
|
224
|
+
|
225
|
+
puts "#{ui_object.get_name} - #{property} = #{state}" if ENV['DEBUG']
|
226
|
+
|
227
|
+
|
228
|
+
case property
|
229
|
+
when :exists
|
230
|
+
actual = ui_object.exists?
|
231
|
+
when :enabled
|
232
|
+
actual = ui_object.enabled?
|
233
|
+
when :disabled
|
234
|
+
actual = ui_object.disabled?
|
235
|
+
when :visible
|
236
|
+
actual = ui_object.visible?
|
237
|
+
when :hidden
|
238
|
+
actual = ui_object.hidden?
|
239
|
+
when :checked
|
240
|
+
actual = ui_object.checked?
|
241
|
+
when :selected
|
242
|
+
actual = ui_object.selected?
|
243
|
+
when :value
|
244
|
+
actual = ui_object.get_value
|
245
|
+
when :caption
|
246
|
+
actual = ui_object.get_caption
|
247
|
+
when :name
|
248
|
+
actual = ui_object.tag_name
|
249
|
+
when :placeholder
|
250
|
+
actual = ui_object.get_placeholder
|
251
|
+
when :items
|
252
|
+
actual = ui_object.get_list_items
|
253
|
+
when :itemcount
|
254
|
+
actual = ui_object.get_item_count
|
255
|
+
end
|
256
|
+
|
257
|
+
if state.is_a?(Hash) && state.length == 1
|
258
|
+
error_msg = "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) #{property} property to"
|
259
|
+
state.each do |key, value|
|
260
|
+
case key
|
261
|
+
when :lt, :less_than
|
262
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be less than #{value} but found '#{actual}'") unless actual < value
|
263
|
+
when :lt_eq, :less_than_or_equal
|
264
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be less than or equal to #{value} but found '#{actual}'") unless actual <= value
|
265
|
+
when :gt, :greater_than
|
266
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be greater than #{value} but found '#{actual}'") unless actual > value
|
267
|
+
when :gt_eq, :greater_than_or_equal
|
268
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be greater than or equal to #{value} but found '#{actual}'") unless actual >= value
|
269
|
+
when :starts_with
|
270
|
+
ExceptionQueue.enqueue_exception("#{error_msg} start with '#{value}' but found '#{actual}'") unless actual.start_with?(value)
|
271
|
+
when :ends_with
|
272
|
+
ExceptionQueue.enqueue_exception("#{error_msg} end with '#{value}' but found '#{actual}'") unless actual.end_with?(value)
|
273
|
+
when :contains
|
274
|
+
ExceptionQueue.enqueue_exception("#{error_msg} contain '#{value}' but found '#{actual}'") unless actual.include?(value)
|
275
|
+
when :not_contains, :does_not_contain
|
276
|
+
ExceptionQueue.enqueue_exception("#{error_msg} not contain '#{value}' but found '#{actual}'") if actual.include?(value)
|
277
|
+
when :not_equal
|
278
|
+
ExceptionQueue.enqueue_exception("#{error_msg} not equal '#{value}' but found '#{actual}'") if actual == value
|
279
|
+
when :like, :is_like
|
280
|
+
actual_like = actual.delete("\n")
|
281
|
+
actual_like = actual_like.delete("\r")
|
282
|
+
actual_like = actual_like.delete("\t")
|
283
|
+
actual_like = actual_like.delete(' ')
|
284
|
+
actual_like = actual_like.downcase
|
285
|
+
expected = value.delete("\n")
|
286
|
+
expected = expected.delete("\r")
|
287
|
+
expected = expected.delete("\t")
|
288
|
+
expected = expected.delete(' ')
|
289
|
+
expected = expected.downcase
|
290
|
+
ExceptionQueue.enqueue_exception("#{error_msg} be like '#{value}' but found '#{actual}'") unless actual_like.include?(expected)
|
291
|
+
when :translate
|
292
|
+
expected = I18n.t(value)
|
293
|
+
ExceptionQueue.enqueue_assert_equal(expected, actual, "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) translated #{property} property")
|
294
|
+
end
|
295
|
+
end
|
296
|
+
else
|
297
|
+
ExceptionQueue.enqueue_assert_equal(state, actual, "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) #{property} property")
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
ExceptionQueue.post_exceptions
|
302
|
+
end
|
303
|
+
|
304
|
+
def populate_data_fields(data, wait_time = nil)
|
305
|
+
timeout = wait_time.nil? ? 5 : wait_time
|
306
|
+
data.each do |data_field, data_param|
|
307
|
+
unless data_param.blank?
|
308
|
+
# make sure the intended UI target element is visible before trying to set its value
|
309
|
+
data_field.wait_until_visible(timeout)
|
310
|
+
if data_param == '!DELETE'
|
311
|
+
data_field.clear
|
312
|
+
else
|
313
|
+
case data_field.get_object_type
|
314
|
+
when :checkbox
|
315
|
+
data_field.set_checkbox_state(data_param.to_bool)
|
316
|
+
when :textfield
|
317
|
+
data_field.set("#{data_param}\t")
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
|
326
|
+
def find_section
|
327
|
+
find_element(@strategy, @locator)
|
328
|
+
rescue
|
329
|
+
nil
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|