testcentricity_mobile 4.0.0

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.
@@ -0,0 +1,416 @@
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
+ def initialize(data)
124
+ @user_id = data['USER_ID']
125
+ @password = data['PASSWORD']
126
+ @app_id = data['APP_ID']
127
+ @api_key = data['API_KEY']
128
+ @option1 = data['OPTIONAL_1']
129
+ @option2 = data['OPTIONAL_2']
130
+ @option3 = data['OPTIONAL_3']
131
+ @option4 = data['OPTIONAL_4']
132
+ @dns = data['DNS']
133
+ @db_username = data['DB_USERNAME']
134
+ @db_password = data['DB_PASSWORD']
135
+ @ios_app_path = data['IOS_APP_PATH']
136
+ @ios_ipa_path = data['IOS_IPA_PATH']
137
+ @ios_bundle_id = data['IOS_BUNDLE_ID']
138
+ @ios_test_id = data['IOS_TEST_ID']
139
+ @android_apk_path = data['ANDROID_APK_PATH']
140
+ @android_app_id = data['ANDROID_APP_ID']
141
+ @android_test_id = data['ANDROID_TEST_ID']
142
+ @deep_link_prefix = data['DEEP_LINK_PREFIX']
143
+
144
+ super
145
+ end
146
+
147
+ def self.new_app_session
148
+ @app_session_id = Time.now.strftime('%Y%m%d%H%M%S%L')
149
+ end
150
+
151
+ def self.app_session_id
152
+ @app_session_id
153
+ end
154
+
155
+ def self.session_code
156
+ if @session_code.nil?
157
+ characters = ('a'..'z').to_a
158
+ @session_code = (0..12).map { characters.sample }.join
159
+ end
160
+ @session_code
161
+ end
162
+
163
+ def self.session_id
164
+ @session_id
165
+ end
166
+
167
+ def self.session_time_stamp
168
+ @session_time_stamp
169
+ end
170
+
171
+ def self.parallel=(state)
172
+ @parallel = state
173
+ end
174
+
175
+ def self.parallel
176
+ @parallel
177
+ end
178
+
179
+ def self.process_num=(num)
180
+ @process_num = num
181
+ end
182
+
183
+ def self.process_num
184
+ @process_num
185
+ end
186
+
187
+ def self.test_environment
188
+ if @test_environment.blank?
189
+ nil
190
+ else
191
+ @test_environment.downcase.to_sym
192
+ end
193
+ end
194
+
195
+ def self.default_max_wait_time=(timeout)
196
+ @default_max_wait_time = timeout
197
+ end
198
+
199
+ def self.default_max_wait_time
200
+ @default_max_wait_time
201
+ end
202
+
203
+ def self.session_state=(session_state)
204
+ @session_state = session_state
205
+ end
206
+
207
+ def self.session_state
208
+ @session_state
209
+ end
210
+
211
+ def self.os=(os)
212
+ @os = os
213
+ end
214
+
215
+ def self.os
216
+ @os
217
+ end
218
+
219
+ def self.device=(device)
220
+ @device = device
221
+ end
222
+
223
+ def self.device
224
+ @device
225
+ end
226
+
227
+ def self.is_device?
228
+ @device == :device
229
+ end
230
+
231
+ def self.is_simulator?
232
+ @device == :simulator
233
+ end
234
+
235
+ def self.is_web?
236
+ @device == :web
237
+ end
238
+
239
+ def self.device_type=(type)
240
+ type = type.downcase.to_sym if type.is_a?(String)
241
+ @device_type = type
242
+ end
243
+
244
+ def self.device_type
245
+ @device_type
246
+ end
247
+
248
+ def self.device_name=(name)
249
+ @device_name = name
250
+ end
251
+
252
+ def self.device_name
253
+ @device_name
254
+ end
255
+
256
+ def self.device_os=(os)
257
+ @device_os = os.downcase.to_sym
258
+ end
259
+
260
+ def self.device_os
261
+ @device_os
262
+ end
263
+
264
+ def self.device_os_version=(version)
265
+ @device_os_version = version
266
+ end
267
+
268
+ def self.device_os_version
269
+ @device_os_version
270
+ end
271
+
272
+ def self.is_ios?
273
+ @device_os == :ios
274
+ end
275
+
276
+ def self.is_android?
277
+ @device_os == :android
278
+ end
279
+
280
+ def self.device_orientation=(orientation)
281
+ orientation = orientation.downcase.to_sym if orientation.is_a?(String)
282
+ @device_orientation = orientation
283
+ end
284
+
285
+ def self.device_orientation
286
+ @device_orientation
287
+ end
288
+
289
+ def self.screen_size=(size)
290
+ @screen_size = size
291
+ end
292
+
293
+ def self.screen_size
294
+ @screen_size
295
+ end
296
+
297
+ def self.driver=(type)
298
+ @driver = type
299
+ end
300
+
301
+ def self.driver
302
+ @driver
303
+ end
304
+
305
+ def self.driver_name=(name)
306
+ name = name.downcase.to_sym if name.is_a?(String)
307
+ @driver_name = name
308
+ end
309
+
310
+ def self.driver_name
311
+ @driver_name
312
+ end
313
+
314
+ def self.appium_driver=(driver_instance)
315
+ @appium_driver = driver_instance
316
+ end
317
+
318
+ def self.appium_driver
319
+ @appium_driver
320
+ end
321
+
322
+ def self.tunneling=(state)
323
+ @tunneling = state
324
+ end
325
+
326
+ def self.tunneling
327
+ @tunneling
328
+ end
329
+
330
+ def self.language=(language)
331
+ @language = language
332
+ end
333
+
334
+ def self.language
335
+ @language
336
+ end
337
+
338
+ def self.locale=(locale)
339
+ @locale = locale
340
+ end
341
+
342
+ def self.locale
343
+ @locale
344
+ end
345
+
346
+ def self.platform=(platform)
347
+ @platform = platform
348
+ end
349
+
350
+ def self.platform
351
+ @platform
352
+ end
353
+
354
+ def self.is_mobile?
355
+ @platform == :mobile
356
+ end
357
+
358
+ def self.set_signed_in(signed_in)
359
+ @signed_in = signed_in
360
+ end
361
+
362
+ def self.is_signed_in?
363
+ @signed_in
364
+ end
365
+
366
+ def self.portal_state=(portal_state)
367
+ @portal_status = portal_state
368
+ end
369
+
370
+ def self.portal_state
371
+ @portal_status
372
+ end
373
+
374
+ def self.portal_context=(portal_context)
375
+ @portal_context = portal_context
376
+ end
377
+
378
+ def self.portal_context
379
+ @portal_context
380
+ end
381
+
382
+ def self.set_external_page(state)
383
+ @external_page = state
384
+ end
385
+
386
+ def self.save_screen_shot(screen_shot)
387
+ @screen_shots.push(screen_shot)
388
+ end
389
+
390
+ def self.get_screen_shots
391
+ @screen_shots
392
+ end
393
+
394
+ def self.reset_contexts
395
+ @screen_shots = []
396
+ end
397
+
398
+ # :nocov:
399
+ def self.report_header
400
+ # build the Cucumber HTML report header
401
+ report_header = "\n<b><u>TEST ENVIRONMENT</u>:</b> #{ENV['TEST_ENVIRONMENT']}\n"
402
+ report_header = "#{report_header} <b>Driver:</b>\t #{Environ.driver}\n" if Environ.driver
403
+ report_header = "#{report_header} <b>Driver Name:</b>\t #{Environ.driver_name}\n" if Environ.driver_name
404
+ report_header = "#{report_header} <b>Platform:</b>\t #{Environ.platform}\n" if Environ.platform
405
+ report_header = "#{report_header} <b>Device:</b>\t #{Environ.device_name}\n" if Environ.device_name
406
+ report_header = "#{report_header} <b>Device OS:</b>\t #{Environ.device_os} #{Environ.device_os_version}\n" if Environ.device_os
407
+ report_header = "#{report_header} <b>Device type:</b>\t #{Environ.device_type}\n" if Environ.device_type
408
+ report_header = "#{report_header} <b>OS:</b>\t\t #{Environ.os}\n" if Environ.os
409
+ report_header = "#{report_header} <b>Locale:</b>\t #{Environ.locale}\n" if Environ.locale
410
+ report_header = "#{report_header} <b>Language:</b>\t #{Environ.language}\n" if Environ.language
411
+ report_header = "#{report_header} <b>Country:</b>\t #{ENV['COUNTRY']}\n" if ENV['COUNTRY']
412
+ "#{report_header}\n\n"
413
+ end
414
+ # :nocov:
415
+ end
416
+ end
@@ -0,0 +1,160 @@
1
+ module TestCentricity
2
+ class ExceptionQueue
3
+ attr_accessor :error_queue
4
+ attr_accessor :active_ui_element
5
+
6
+ def self.enqueue_assert_equal(expected, actual, error_message)
7
+ is_equal = if expected.is_a?(String) && actual.is_a?(String)
8
+ expected.downcase.strip == actual.downcase.strip
9
+ else
10
+ expected == actual
11
+ end
12
+ unless is_equal
13
+ enqueue("#{error_message} to be\n '#{expected}'\nbut found\n '#{actual}'")
14
+ enqueue_screenshot
15
+ end
16
+ end
17
+
18
+ def self.enqueue_assert_not_equal(expected, actual, error_message)
19
+ unless expected != actual
20
+ enqueue("#{error_message} to not be equal to '#{expected}'")
21
+ enqueue_screenshot
22
+ end
23
+ end
24
+
25
+ def self.enqueue_exception(error_message)
26
+ enqueue(error_message)
27
+ end
28
+
29
+ def self.post_exceptions(preample_message = nil)
30
+ unless @error_queue.nil?
31
+ unless preample_message.nil?
32
+ @error_queue = "#{preample_message} - The following errors were found:\n_____________________________\n#{@error_queue}"
33
+ end
34
+ raise @error_queue
35
+ end
36
+ ensure
37
+ @error_queue = nil
38
+ @active_ui_element = nil
39
+ end
40
+
41
+ def self.enqueue_comparison(ui_object, state, actual, error_msg)
42
+ @active_ui_element = ui_object
43
+ if state.is_a?(Hash) && state.length == 1
44
+ state.each do |key, value|
45
+ case key
46
+ when :lt, :less_than
47
+ enqueue_exception("#{error_msg} be less than #{value} but found '#{actual}'") unless actual < value
48
+ when :lt_eq, :less_than_or_equal
49
+ enqueue_exception("#{error_msg} be less than or equal to #{value} but found '#{actual}'") unless actual <= value
50
+ when :gt, :greater_than
51
+ enqueue_exception("#{error_msg} be greater than #{value} but found '#{actual}'") unless actual > value
52
+ when :gt_eq, :greater_than_or_equal
53
+ enqueue_exception("#{error_msg} be greater than or equal to #{value} but found '#{actual}'") unless actual >= value
54
+ when :starts_with
55
+ enqueue_exception("#{error_msg} start with '#{value}' but found '#{actual}'") unless actual.start_with?(value)
56
+ when :ends_with
57
+ enqueue_exception("#{error_msg} end with '#{value}' but found '#{actual}'") unless actual.end_with?(value)
58
+ when :contains
59
+ enqueue_exception("#{error_msg} contain '#{value}' but found '#{actual}'") unless actual.include?(value)
60
+ when :not_contains, :does_not_contain
61
+ enqueue_exception("#{error_msg} not contain '#{value}' but found '#{actual}'") if actual.include?(value)
62
+ when :not_equal
63
+ enqueue_exception("#{error_msg} not equal '#{value}' but found '#{actual}'") if actual == value
64
+ when :like, :is_like
65
+ actual_like = actual.delete("\n")
66
+ actual_like = actual_like.delete("\r")
67
+ actual_like = actual_like.delete("\t")
68
+ actual_like = actual_like.delete(' ')
69
+ actual_like = actual_like.downcase
70
+ expected = value.delete("\n")
71
+ expected = expected.delete("\r")
72
+ expected = expected.delete("\t")
73
+ expected = expected.delete(' ')
74
+ expected = expected.downcase
75
+ enqueue_exception("#{error_msg} be like '#{value}' but found '#{actual}'") unless actual_like.include?(expected)
76
+ when :translate
77
+ expected = translate(value)
78
+ enqueue_assert_equal(expected, actual, error_msg)
79
+ when :translate_upcase
80
+ expected = translate(value)
81
+ expected = expected.is_a?(Array) ? expected.map(&:upcase) : expected.upcase
82
+ enqueue_assert_equal(expected, actual, error_msg)
83
+ when :translate_downcase
84
+ expected = translate(value)
85
+ expected = expected.is_a?(Array) ? expected.map(&:downcase) : expected.downcase
86
+ enqueue_assert_equal(expected, actual, error_msg)
87
+ when :translate_capitalize
88
+ expected = translate(value)
89
+ expected = expected.is_a?(Array) ? expected.map(&:capitalize) : expected.capitalize
90
+ enqueue_assert_equal(expected, actual, error_msg)
91
+ when :translate_titlecase
92
+ expected = translate(value)
93
+ expected = if expected.is_a?(Array)
94
+ result = []
95
+ expected.each do |item|
96
+ result.push("#{item.split.each{ |item| item.capitalize! }.join(' ')}")
97
+ end
98
+ result
99
+ else
100
+ "#{expected.split.each{ |expected| expected.capitalize! }.join(' ')}"
101
+ end
102
+ enqueue_assert_equal(expected, actual, error_msg)
103
+ else
104
+ raise "#{key} is not a valid comparison key"
105
+ end
106
+ end
107
+ else
108
+ enqueue_assert_equal(state, actual, error_msg)
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def self.enqueue(message)
115
+ @error_queue = "#{@error_queue}#{message}\n\n"
116
+ end
117
+
118
+ def self.enqueue_screenshot
119
+ timestamp = Time.now.strftime('%Y%m%d%H%M%S%L')
120
+ filename = "Screenshot-#{timestamp}.png"
121
+ path = File.join Dir.pwd, 'reports/screenshots/', filename
122
+ # take screenshot
123
+ AppiumConnect.take_screenshot(path)
124
+ # add screenshot to queue
125
+ puts "Screenshot saved at #{path}"
126
+ screen_shot = {path: path, filename: filename}
127
+ Environ.save_screen_shot(screen_shot)
128
+ end
129
+
130
+ def self.translate(*args, **opts)
131
+ opts[:locale] ||= I18n.locale
132
+ opts[:raise] = true
133
+ I18n.translate(*args, **opts)
134
+ rescue I18n::MissingTranslationData => err
135
+ puts err
136
+ opts[:locale] = :en
137
+
138
+ # fallback to en if the translation is missing. If the translation isn't
139
+ # in en, then raise again.
140
+ disable_enforce_available_locales do
141
+ I18n.translate(*args, **opts)
142
+ end
143
+ end
144
+
145
+ def self.disable_enforce_available_locales
146
+ saved_enforce_available_locales = I18n.enforce_available_locales
147
+ I18n.enforce_available_locales = false
148
+ yield
149
+ ensure
150
+ I18n.enforce_available_locales = saved_enforce_available_locales
151
+ end
152
+ end
153
+
154
+
155
+ class ObjectNotFoundError < StandardError
156
+ def initialize(message)
157
+ super(message)
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,48 @@
1
+ class Object
2
+ def blank?
3
+ respond_to?(:empty?) ? empty? : !self
4
+ end
5
+
6
+ def present?
7
+ !blank?
8
+ end
9
+
10
+ def boolean?
11
+ [true, false].include? self
12
+ end
13
+ end
14
+
15
+
16
+ class String
17
+ def to_bool
18
+ return true if self == true || self =~ (/(true|t|yes|y|x|1)$/i)
19
+ return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
20
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
21
+ end
22
+
23
+ def string_between(marker1, marker2)
24
+ self[/#{Regexp.escape(marker1)}(.*?)#{Regexp.escape(marker2)}/m, 1]
25
+ end
26
+
27
+ def format_date_time(date_time_format)
28
+ return if self.blank?
29
+ new_date = DateTime.parse(self)
30
+ if ENV['LOCALE'] && date_time_format.is_a?(Symbol)
31
+ I18n.l(new_date, format: date_time_format)
32
+ else
33
+ new_date.strftime(date_time_format)
34
+ end
35
+ end
36
+
37
+ def titlecase
38
+ "#{self.split.each{ |text| text.capitalize! }.join(' ')}"
39
+ end
40
+
41
+ def is_int?
42
+ Integer(self) && true rescue false
43
+ end
44
+
45
+ def is_float?
46
+ Float(self) && true rescue false
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module TestCentricityMobile
2
+ VERSION = '4.0.0'
3
+ end
@@ -0,0 +1,61 @@
1
+ module WorldData
2
+ def environs
3
+ @environs ||= TestCentricity::EnvironData
4
+ end
5
+
6
+ # instantiate and register all data objects specified in data_objects method
7
+ #
8
+ def instantiate_data_objects
9
+ # return if data objects have already been instantiated and registered
10
+ return if TestCentricity::DataManager.loaded?
11
+
12
+ # instantiate all data objects
13
+ @data = {}
14
+ data_objects.each do |data_type, data_class|
15
+ @data[data_type] = data_class.new unless @data.has_key?(data_type)
16
+ # define data object accessor method
17
+ define_method(data_type) do
18
+ if instance_variable_defined?(:"@#{data_type}")
19
+ instance_variable_get(:"@#{data_type}")
20
+ else
21
+ instance_variable_set(:"@#{data_type}", TestCentricity::DataManager.find_data_object(data_type))
22
+ end
23
+ end
24
+ end
25
+ # register all data objects with DataManager
26
+ TestCentricity::DataManager.register_data_objects(@data)
27
+ end
28
+ end
29
+
30
+
31
+ module WorldScreens
32
+ # instantiate and register all screen objects specified in screen_objects method
33
+ #
34
+ def instantiate_screen_objects
35
+ # return if screen objects have already been instantiated and registered
36
+ return if TestCentricity::ScreenManager.loaded?
37
+
38
+ # instantiate all screen objects
39
+ @screens = {}
40
+ screen_objects.each do |screen_object, screen_class|
41
+ obj = screen_class.new
42
+ @screens[screen_object] = obj unless @screens.has_key?(screen_object)
43
+ screen_names = obj.screen_name
44
+ screen_names = Array(screen_names) if screen_names.is_a? String
45
+ screen_names.each do |name|
46
+ screen_key = name.gsub(/\s+/, '').downcase.to_sym
47
+ @screens[screen_key] = obj unless @screens.has_key?(screen_key)
48
+ end
49
+ # define screen object accessor method
50
+ define_method(screen_object) do
51
+ if instance_variable_defined?(:"@#{screen_object}")
52
+ instance_variable_get(:"@#{screen_object}")
53
+ else
54
+ instance_variable_set(:"@#{screen_object}", TestCentricity::ScreenManager.find_screen(screen_object))
55
+ end
56
+ end
57
+ end
58
+ # register all screen objects with ScreenManager
59
+ TestCentricity::ScreenManager.register_screen_objects(@screens)
60
+ end
61
+ end