testcentricity_mobile 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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