testcentricity_apps 4.0.10

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 (31) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +3 -0
  3. data/CHANGELOG.md +193 -0
  4. data/LICENSE.md +27 -0
  5. data/README.md +2297 -0
  6. data/lib/testcentricity_apps/app_core/appium_connect_helper.rb +667 -0
  7. data/lib/testcentricity_apps/app_core/screen_object.rb +494 -0
  8. data/lib/testcentricity_apps/app_core/screen_objects_helper.rb +211 -0
  9. data/lib/testcentricity_apps/app_core/screen_section.rb +669 -0
  10. data/lib/testcentricity_apps/app_elements/alert.rb +152 -0
  11. data/lib/testcentricity_apps/app_elements/app_element.rb +728 -0
  12. data/lib/testcentricity_apps/app_elements/button.rb +10 -0
  13. data/lib/testcentricity_apps/app_elements/checkbox.rb +61 -0
  14. data/lib/testcentricity_apps/app_elements/image.rb +10 -0
  15. data/lib/testcentricity_apps/app_elements/label.rb +10 -0
  16. data/lib/testcentricity_apps/app_elements/list.rb +188 -0
  17. data/lib/testcentricity_apps/app_elements/menu.rb +159 -0
  18. data/lib/testcentricity_apps/app_elements/menubar.rb +78 -0
  19. data/lib/testcentricity_apps/app_elements/radio.rb +61 -0
  20. data/lib/testcentricity_apps/app_elements/selectlist.rb +126 -0
  21. data/lib/testcentricity_apps/app_elements/switch.rb +66 -0
  22. data/lib/testcentricity_apps/app_elements/textfield.rb +51 -0
  23. data/lib/testcentricity_apps/appium_server.rb +76 -0
  24. data/lib/testcentricity_apps/data_objects/data_objects_helper.rb +100 -0
  25. data/lib/testcentricity_apps/data_objects/environment.rb +423 -0
  26. data/lib/testcentricity_apps/exception_queue_helper.rb +160 -0
  27. data/lib/testcentricity_apps/utility_helpers.rb +48 -0
  28. data/lib/testcentricity_apps/version.rb +3 -0
  29. data/lib/testcentricity_apps/world_extensions.rb +61 -0
  30. data/lib/testcentricity_apps.rb +103 -0
  31. metadata +322 -0
@@ -0,0 +1,66 @@
1
+ module TestCentricity
2
+ module AppElements
3
+ class AppSwitch < AppUIElement
4
+ def initialize(name, parent, locator, context)
5
+ super
6
+ @type = :switch
7
+ end
8
+
9
+ # Is switch in ON state?
10
+ #
11
+ # @return [Boolean]
12
+ # @example
13
+ # use_face_id_switch.on?
14
+ #
15
+ def on?
16
+ obj = element
17
+ object_not_found_exception(obj)
18
+
19
+ if Environ.is_ios?
20
+ state = obj.attribute(:value)
21
+ state.to_bool
22
+ else
23
+ state = obj.attribute(:checked)
24
+ state.boolean? ? state : state == 'true'
25
+ end
26
+ end
27
+
28
+ alias checked? on?
29
+ alias selected? on?
30
+
31
+ # Set the state of a switch object to ON.
32
+ #
33
+ # @example
34
+ # use_face_id_switch.on
35
+ #
36
+ def on
37
+ obj = element
38
+ object_not_found_exception(obj)
39
+ obj.click unless on?
40
+ end
41
+
42
+ # Set the state of a switch object to OFF.
43
+ #
44
+ # @example
45
+ # use_face_id_switch.off
46
+ #
47
+ def off
48
+ obj = element
49
+ object_not_found_exception(obj)
50
+ obj.click if on?
51
+ end
52
+
53
+ # Set the ON/OFF state of a switch object.
54
+ #
55
+ # @param state [Boolean] true = on / false = off
56
+ # @example
57
+ # use_face_id_switch.set_switch_state(true)
58
+ #
59
+ def set_switch_state(state)
60
+ obj = element
61
+ object_not_found_exception(obj)
62
+ state ? on : off
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,51 @@
1
+ module TestCentricity
2
+ module AppElements
3
+ class AppTextField < AppUIElement
4
+ def initialize(name, parent, locator, context)
5
+ super
6
+ @type = :textfield
7
+ end
8
+
9
+ # Is text field set to read-only?
10
+ #
11
+ # @return [Boolean]
12
+ # @example
13
+ # comments_field.read_only?
14
+ #
15
+ def read_only?
16
+ obj = element
17
+ object_not_found_exception(obj)
18
+ !!obj.attribute('readonly')
19
+ end
20
+
21
+ # Return maxlength character count of a text field.
22
+ #
23
+ # @return [Integer]
24
+ # @example
25
+ # max_num_chars = comments_field.get_max_length
26
+ #
27
+ def get_max_length
28
+ obj = element
29
+ object_not_found_exception(obj)
30
+ max_length = obj.attribute('maxlength')
31
+ max_length.to_i unless max_length.blank?
32
+ end
33
+
34
+ # Return placeholder text of a text field.
35
+ #
36
+ # @return [String]
37
+ # @example
38
+ # placeholder_message = username_field.get_placeholder
39
+ #
40
+ def get_placeholder
41
+ obj = element
42
+ object_not_found_exception(obj)
43
+ if AppiumConnect.is_webview?
44
+ obj.attribute('placeholder')
45
+ else
46
+ obj.text
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
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
+ attr_accessor :process
10
+
11
+ def initialize(params = {})
12
+ @params = params
13
+ end
14
+
15
+ #
16
+ # Start the Appium Server
17
+ #
18
+ def start
19
+ # terminate any currently running Appium Server
20
+ if running?
21
+ system('killall -9 node')
22
+ puts 'Terminating existing Appium Server'
23
+ sleep(5)
24
+ puts 'Appium Server is being restarted'
25
+ else
26
+ puts 'Appium Server is starting'
27
+ end
28
+ # start new Appium Server
29
+ @process = ChildProcess.build(*parameters)
30
+ process.start
31
+ # wait until confirmation that Appium Server is running
32
+ wait = Selenium::WebDriver::Wait.new(timeout: 30)
33
+ wait.until { running? }
34
+ puts "Appium Server is running - PID = #{process.pid}"
35
+ end
36
+
37
+ #
38
+ # Check to see if Appium Server is running
39
+ #
40
+ def running?
41
+ endpoint = if ENV['APPIUM_SERVER_VERSION'] && ENV['APPIUM_SERVER_VERSION'].to_i == 1
42
+ 'http://0.0.0.0:4723/wd/hub/sessions'
43
+ else
44
+ 'http://0.0.0.0:4723/sessions'
45
+ end
46
+ response = false
47
+ begin
48
+ response = Net::HTTP.get_response(URI(endpoint))
49
+ rescue
50
+ end
51
+ response && response.code_type == Net::HTTPOK
52
+ end
53
+
54
+ #
55
+ # Stop the Appium Server
56
+ #
57
+ def stop
58
+ process.stop
59
+ puts 'Appium Server has been terminated'
60
+ end
61
+
62
+ private
63
+
64
+ # :nocov:
65
+ def parameters
66
+ cmd = ['appium']
67
+ @params.each do |key, value|
68
+ cmd << '--' + key.to_s
69
+ cmd << value.to_s if not value.nil? and value.size > 0
70
+ end
71
+ cmd
72
+ end
73
+ # :nocov:
74
+ end
75
+ end
76
+
@@ -0,0 +1,100 @@
1
+ require 'yaml'
2
+ require 'json'
3
+ require 'virtus'
4
+ require 'time'
5
+ require 'chronic'
6
+ require 'faker'
7
+
8
+
9
+ module TestCentricity
10
+
11
+ PRIMARY_DATA_PATH ||= 'config/test_data/'
12
+ PRIMARY_DATA_FILE ||= "#{PRIMARY_DATA_PATH}data."
13
+ YML_PRIMARY_DATA_FILE ||= "#{PRIMARY_DATA_FILE}yml"
14
+ JSON_PRIMARY_DATA_FILE ||= "#{PRIMARY_DATA_FILE}json"
15
+
16
+
17
+ class DataObject
18
+ attr_accessor :current
19
+ attr_accessor :context
20
+ attr_accessor :hash_table
21
+
22
+ def initialize(data)
23
+ @hash_table = data
24
+ end
25
+
26
+ def self.current
27
+ @current
28
+ end
29
+
30
+ def self.current=(current)
31
+ @current = current
32
+ end
33
+ end
34
+
35
+
36
+ class DataPresenter
37
+ include Virtus.model
38
+
39
+ attr_accessor :current
40
+ attr_accessor :context
41
+
42
+ def initialize(data)
43
+ self.attributes = data unless data.nil?
44
+ end
45
+
46
+ def self.current
47
+ @current
48
+ end
49
+
50
+ def self.current=(current)
51
+ @current = current
52
+ end
53
+ end
54
+
55
+
56
+ # :nocov:
57
+ class DataSource
58
+ attr_accessor :file_path
59
+ attr_accessor :node
60
+
61
+ def read_yaml_node_data(file_name, node_name)
62
+ @file_path = "#{PRIMARY_DATA_PATH}#{file_name}"
63
+ @node = node_name
64
+ data = YAML.load_file(@file_path)
65
+ data[node_name]
66
+ end
67
+
68
+ def read_json_node_data(file_name, node_name)
69
+ @file_path = "#{PRIMARY_DATA_PATH}#{file_name}"
70
+ @node = node_name
71
+ raw_data = File.read(@file_path)
72
+ data = JSON.parse(raw_data)
73
+ data[node_name]
74
+ end
75
+
76
+ private
77
+
78
+ def self.calculate_dynamic_value(value)
79
+ test_value = value.split('!', 2)
80
+ parameter = test_value[1].split('.', 2)
81
+ case parameter[0]
82
+ when 'Date'
83
+ result = eval("Chronic.parse('#{parameter[1]}')")
84
+ when 'FormattedDate', 'FormatDate'
85
+ date_time_params = parameter[1].split(' format! ', 2)
86
+ date_time = eval("Chronic.parse('#{date_time_params[0].strip}')")
87
+ result = date_time.to_s.format_date_time("#{date_time_params[1].strip}")
88
+ else
89
+ result = if Faker.constants.include?(parameter[0].to_sym)
90
+ eval("Faker::#{parameter[0]}.#{parameter[1]}")
91
+ else
92
+ eval(test_value[1])
93
+ end
94
+ end
95
+ result.to_s
96
+ end
97
+ end
98
+ # :nocov:
99
+ end
100
+
@@ -0,0 +1,423 @@
1
+ module TestCentricity
2
+ class EnvironData < TestCentricity::DataSource
3
+ attr_accessor :current
4
+ attr_accessor :data_source_type
5
+ attr_accessor :generic_data
6
+ attr_accessor :environ_specific_data
7
+
8
+ def self.find_environ(environ_name, source_type = :yaml)
9
+ raise 'No environment specified' if environ_name.nil?
10
+
11
+ @data_source_type = source_type
12
+ data = case source_type
13
+ when :yaml
14
+ # read generic test data from data.yml file
15
+ @generic_data ||= YAML.load_file(YML_PRIMARY_DATA_FILE)
16
+ # read environment specific test data
17
+ data_file = "#{PRIMARY_DATA_PATH}#{environ_name}_data.yml"
18
+ @environ_specific_data = if File.exist?(data_file)
19
+ YAML.load_file(data_file)
20
+ else
21
+ {}
22
+ end
23
+ read('Environments', environ_name)
24
+ when :json
25
+ # read generic test data from data.json file
26
+ raw_data = File.read(JSON_PRIMARY_DATA_FILE)
27
+ @generic_data = JSON.parse(raw_data)
28
+ # read environment specific test data
29
+ data_file = "#{PRIMARY_DATA_PATH}#{environ_name}_data.json"
30
+ @environ_specific_data = if File.exist?(data_file)
31
+ raw_data = File.read(data_file)
32
+ JSON.parse(raw_data)
33
+ else
34
+ {}
35
+ end
36
+
37
+ read('Environments', environ_name)
38
+ else
39
+ raise "#{source_type} is not a supported data source type"
40
+ end
41
+ @current = Environ.new(data)
42
+ Environ.current = @current
43
+ end
44
+
45
+ def self.read(key_name, node_name)
46
+ if @environ_specific_data.key?(key_name) && @environ_specific_data[key_name].key?(node_name)
47
+ node_data = @environ_specific_data[key_name][node_name]
48
+ else
49
+ raise "No key named #{key_name} in generic and environment-specific data" unless @generic_data.key?(key_name)
50
+ raise "No node named #{node_name} in #{key_name} section of generic and environment-specific data" unless @generic_data[key_name].key?(node_name)
51
+
52
+ node_data = @generic_data[key_name][node_name]
53
+ end
54
+
55
+ if node_data.is_a?(Hash)
56
+ node_data.each do |key, value|
57
+ node_data[key] = calculate_dynamic_value(value) if value.to_s.start_with?('eval!')
58
+ end
59
+ end
60
+ node_data
61
+ end
62
+ end
63
+
64
+
65
+ class Environ < TestCentricity::DataObject
66
+ @session_id = Time.now.strftime('%d%H%M%S%L')
67
+ @session_time_stamp = Time.now.strftime('%Y%m%d%H%M%S')
68
+ @test_environment = ENV['TEST_ENVIRONMENT']
69
+ @a11y_standard = ENV['ACCESSIBILITY_STANDARD'] || 'best-practice'
70
+ @locale = ENV['LOCALE'] || 'en'
71
+ @language = ENV['LANGUAGE'] || 'English'
72
+ @screen_shots = []
73
+
74
+ attr_accessor :test_environment
75
+ attr_accessor :session_state
76
+ attr_accessor :session_code
77
+ attr_accessor :app_session_id
78
+ attr_accessor :os
79
+ attr_accessor :device
80
+ attr_accessor :device_name
81
+ attr_accessor :device_type
82
+ attr_accessor :device_os
83
+ attr_accessor :device_os_version
84
+ attr_accessor :device_orientation
85
+ attr_accessor :screen_size
86
+ attr_accessor :platform
87
+ attr_accessor :driver
88
+ attr_accessor :driver_name
89
+ attr_accessor :appium_driver
90
+ attr_accessor :tunneling
91
+ attr_accessor :locale
92
+ attr_accessor :language
93
+
94
+ attr_accessor :parallel
95
+ attr_accessor :process_num
96
+
97
+ attr_accessor :signed_in
98
+ attr_accessor :portal_status
99
+ attr_accessor :portal_context
100
+
101
+ attr_accessor :user_id
102
+ attr_accessor :password
103
+ attr_accessor :app_id
104
+ attr_accessor :api_key
105
+ attr_accessor :option1
106
+ attr_accessor :option2
107
+ attr_accessor :option3
108
+ attr_accessor :option4
109
+ attr_accessor :dns
110
+ attr_accessor :db_username
111
+ attr_accessor :db_password
112
+
113
+ attr_accessor :ios_app_path
114
+ attr_accessor :ios_ipa_path
115
+ attr_accessor :ios_bundle_id
116
+ attr_accessor :ios_test_id
117
+ attr_accessor :android_apk_path
118
+ attr_accessor :android_app_id
119
+ attr_accessor :android_test_id
120
+ attr_accessor :default_max_wait_time
121
+ attr_accessor :deep_link_prefix
122
+
123
+ attr_accessor :mac_bundle_id
124
+
125
+ def initialize(data)
126
+ @user_id = data['USER_ID']
127
+ @password = data['PASSWORD']
128
+ @app_id = data['APP_ID']
129
+ @api_key = data['API_KEY']
130
+ @option1 = data['OPTIONAL_1']
131
+ @option2 = data['OPTIONAL_2']
132
+ @option3 = data['OPTIONAL_3']
133
+ @option4 = data['OPTIONAL_4']
134
+ @dns = data['DNS']
135
+ @db_username = data['DB_USERNAME']
136
+ @db_password = data['DB_PASSWORD']
137
+ @ios_app_path = data['IOS_APP_PATH']
138
+ @ios_ipa_path = data['IOS_IPA_PATH']
139
+ @ios_bundle_id = data['IOS_BUNDLE_ID']
140
+ @ios_test_id = data['IOS_TEST_ID']
141
+ @android_apk_path = data['ANDROID_APK_PATH']
142
+ @android_app_id = data['ANDROID_APP_ID']
143
+ @android_test_id = data['ANDROID_TEST_ID']
144
+ @deep_link_prefix = data['DEEP_LINK_PREFIX']
145
+ @mac_bundle_id = data['MAC_BUNDLE_ID']
146
+
147
+ super
148
+ end
149
+
150
+ def self.new_app_session
151
+ @app_session_id = Time.now.strftime('%Y%m%d%H%M%S%L')
152
+ end
153
+
154
+ def self.app_session_id
155
+ @app_session_id
156
+ end
157
+
158
+ def self.session_code
159
+ if @session_code.nil?
160
+ characters = ('a'..'z').to_a
161
+ @session_code = (0..12).map { characters.sample }.join
162
+ end
163
+ @session_code
164
+ end
165
+
166
+ def self.session_id
167
+ @session_id
168
+ end
169
+
170
+ def self.session_time_stamp
171
+ @session_time_stamp
172
+ end
173
+
174
+ def self.parallel=(state)
175
+ @parallel = state
176
+ end
177
+
178
+ def self.parallel
179
+ @parallel
180
+ end
181
+
182
+ def self.process_num=(num)
183
+ @process_num = num
184
+ end
185
+
186
+ def self.process_num
187
+ @process_num
188
+ end
189
+
190
+ def self.test_environment
191
+ if @test_environment.blank?
192
+ nil
193
+ else
194
+ @test_environment.downcase.to_sym
195
+ end
196
+ end
197
+
198
+ def self.default_max_wait_time=(timeout)
199
+ @default_max_wait_time = timeout
200
+ end
201
+
202
+ def self.default_max_wait_time
203
+ @default_max_wait_time
204
+ end
205
+
206
+ def self.session_state=(session_state)
207
+ @session_state = session_state
208
+ end
209
+
210
+ def self.session_state
211
+ @session_state
212
+ end
213
+
214
+ def self.os=(os)
215
+ @os = os
216
+ end
217
+
218
+ def self.os
219
+ @os
220
+ end
221
+
222
+ def self.device=(device)
223
+ @device = device
224
+ end
225
+
226
+ def self.device
227
+ @device
228
+ end
229
+
230
+ def self.is_device?
231
+ @device == :device
232
+ end
233
+
234
+ def self.is_simulator?
235
+ @device == :simulator
236
+ end
237
+
238
+ def self.is_web?
239
+ @device == :web
240
+ end
241
+
242
+ def self.device_type=(type)
243
+ type = type.downcase.to_sym if type.is_a?(String)
244
+ @device_type = type
245
+ end
246
+
247
+ def self.device_type
248
+ @device_type
249
+ end
250
+
251
+ def self.device_name=(name)
252
+ @device_name = name
253
+ end
254
+
255
+ def self.device_name
256
+ @device_name
257
+ end
258
+
259
+ def self.device_os=(os)
260
+ @device_os = os.downcase.to_sym
261
+ end
262
+
263
+ def self.device_os
264
+ @device_os
265
+ end
266
+
267
+ def self.device_os_version=(version)
268
+ @device_os_version = version
269
+ end
270
+
271
+ def self.device_os_version
272
+ @device_os_version
273
+ end
274
+
275
+ def self.is_ios?
276
+ @device_os == :ios
277
+ end
278
+
279
+ def self.is_android?
280
+ @device_os == :android
281
+ end
282
+
283
+ def self.is_macos?
284
+ @device_os == :mac
285
+ end
286
+
287
+ def self.device_orientation=(orientation)
288
+ orientation = orientation.downcase.to_sym if orientation.is_a?(String)
289
+ @device_orientation = orientation
290
+ end
291
+
292
+ def self.device_orientation
293
+ @device_orientation
294
+ end
295
+
296
+ def self.screen_size=(size)
297
+ @screen_size = size
298
+ end
299
+
300
+ def self.screen_size
301
+ @screen_size
302
+ end
303
+
304
+ def self.driver=(type)
305
+ @driver = type
306
+ end
307
+
308
+ def self.driver
309
+ @driver
310
+ end
311
+
312
+ def self.driver_name=(name)
313
+ name = name.downcase.to_sym if name.is_a?(String)
314
+ @driver_name = name
315
+ end
316
+
317
+ def self.driver_name
318
+ @driver_name
319
+ end
320
+
321
+ def self.appium_driver=(driver_instance)
322
+ @appium_driver = driver_instance
323
+ end
324
+
325
+ def self.appium_driver
326
+ @appium_driver
327
+ end
328
+
329
+ def self.tunneling=(state)
330
+ @tunneling = state
331
+ end
332
+
333
+ def self.tunneling
334
+ @tunneling
335
+ end
336
+
337
+ def self.language=(language)
338
+ @language = language
339
+ end
340
+
341
+ def self.language
342
+ @language
343
+ end
344
+
345
+ def self.locale=(locale)
346
+ @locale = locale
347
+ end
348
+
349
+ def self.locale
350
+ @locale
351
+ end
352
+
353
+ def self.platform=(platform)
354
+ @platform = platform
355
+ end
356
+
357
+ def self.platform
358
+ @platform
359
+ end
360
+
361
+ def self.is_mobile?
362
+ @platform == :mobile
363
+ end
364
+
365
+ def self.set_signed_in(signed_in)
366
+ @signed_in = signed_in
367
+ end
368
+
369
+ def self.is_signed_in?
370
+ @signed_in
371
+ end
372
+
373
+ def self.portal_state=(portal_state)
374
+ @portal_status = portal_state
375
+ end
376
+
377
+ def self.portal_state
378
+ @portal_status
379
+ end
380
+
381
+ def self.portal_context=(portal_context)
382
+ @portal_context = portal_context
383
+ end
384
+
385
+ def self.portal_context
386
+ @portal_context
387
+ end
388
+
389
+ def self.set_external_page(state)
390
+ @external_page = state
391
+ end
392
+
393
+ def self.save_screen_shot(screen_shot)
394
+ @screen_shots.push(screen_shot)
395
+ end
396
+
397
+ def self.get_screen_shots
398
+ @screen_shots
399
+ end
400
+
401
+ def self.reset_contexts
402
+ @screen_shots = []
403
+ end
404
+
405
+ # :nocov:
406
+ def self.report_header
407
+ # build the Cucumber HTML report header
408
+ report_header = "\n<b><u>TEST ENVIRONMENT</u>:</b> #{ENV['TEST_ENVIRONMENT']}\n"
409
+ report_header = "#{report_header} <b>Driver:</b>\t #{Environ.driver}\n" if Environ.driver
410
+ report_header = "#{report_header} <b>Driver Name:</b>\t #{Environ.driver_name}\n" if Environ.driver_name
411
+ report_header = "#{report_header} <b>Platform:</b>\t #{Environ.platform}\n" if Environ.platform
412
+ report_header = "#{report_header} <b>Device:</b>\t #{Environ.device_name}\n" if Environ.device_name
413
+ report_header = "#{report_header} <b>Device OS:</b>\t #{Environ.device_os} #{Environ.device_os_version}\n" if Environ.device_os
414
+ report_header = "#{report_header} <b>Device type:</b>\t #{Environ.device_type}\n" if Environ.device_type
415
+ report_header = "#{report_header} <b>OS:</b>\t\t #{Environ.os}\n" if Environ.os
416
+ report_header = "#{report_header} <b>Locale:</b>\t #{Environ.locale}\n" if Environ.locale
417
+ report_header = "#{report_header} <b>Language:</b>\t #{Environ.language}\n" if Environ.language
418
+ report_header = "#{report_header} <b>Country:</b>\t #{ENV['COUNTRY']}\n" if ENV['COUNTRY']
419
+ "#{report_header}\n\n"
420
+ end
421
+ # :nocov:
422
+ end
423
+ end