testcentricity_mobile 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,1795 @@
1
+ # TestCentricity™ Mobile
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/testcentricity_mobile.svg)](https://badge.fury.io/rb/testcentricity_mobile)
4
+ [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause)
5
+ ![Gem Downloads](https://img.shields.io/gem/dt/testcentricity_mobile)
6
+ ![Maintained](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
7
+ [![Docs](https://img.shields.io/badge/docs-rubydoc-blue.svg)](http://www.rubydoc.info/gems/testcentricity_mobile)
8
+
9
+
10
+ The TestCentricity™ Mobile core framework for native mobile iOS and Android app testing implements a Screen Object Model
11
+ DSL for use with Cucumber (version 7.x or greater) and Appium. It also facilitates the configuration of the appropriate
12
+ Appium capabilities and driver required to establish a connection with locally or cloud hosted iOS and Android real devices
13
+ or simulators.
14
+
15
+ The TestCentricity™ Mobile gem supports automated testing of native iOS and Android apps running on the following mobile
16
+ test targets:
17
+ * locally hosted iOS device simulators or physical iOS devices (using Appium and XCode on macOS)
18
+ * locally hosted Android devices or Android Studio virtual device emulators (using Appium and Android Studio)
19
+ * cloud hosted physical devices and simulators from the following service:
20
+ * [Browserstack](https://www.browserstack.com/list-of-browsers-and-platforms/app_automate)
21
+ * [Sauce Labs](https://saucelabs.com/platform/mobile-testing)
22
+ * [TestingBot](https://testingbot.com/mobile/realdevicetesting)
23
+
24
+
25
+ ## What's New
26
+
27
+ A complete history of bug fixes and new features can be found in the {file:CHANGELOG.md CHANGELOG} file.
28
+
29
+ The RubyDocs for this gem can be found [here](https://www.rubydoc.info/gems/testcentricity_mobile/).
30
+
31
+
32
+ ### Which gem should I use?
33
+
34
+ * The [TestCentricity **Mobile** gem](https://rubygems.org/gems/testcentricity_mobile) only supports testing of native iOS and Android mobile apps
35
+ * The [TestCentricity **Web** gem](https://rubygems.org/gems/testcentricity_web) only supports testing of web interfaces via desktop and mobile web browsers
36
+ * The TestCentricity gem supports testing of native mobile apps and/or web interfaces via desktop and mobile web browsers.
37
+
38
+ | Tested platforms | TestCentricity Mobile | TestCentricity Web | TestCentricity |
39
+ |----------------------------------------------------|-----------------------|--------------------|----------------|
40
+ | Native mobile iOS and/or Android apps only | Yes | No | No |
41
+ | Desktop/mobile web browsers only | No | Yes | No |
42
+ | Native mobile apps and desktop/mobile web browsers | No | No | Yes |
43
+
44
+
45
+ ## Installation
46
+
47
+ TestCentricity Mobile version 3.0 and above requires Ruby 3.0.0 or later. To install the TestCentricity Mobile gem, add
48
+ this line to your automation project's Gemfile:
49
+
50
+ gem 'testcentricity_mobile'
51
+
52
+ And then execute:
53
+
54
+ $ bundle
55
+
56
+ Or install it yourself as:
57
+
58
+ $ gem install testcentricity_mobile
59
+
60
+
61
+ ---
62
+ ## Setup
63
+ ### Using Cucumber
64
+
65
+ If you are using Cucumber, you need to require the following in your `env.rb` file:
66
+ ```ruby
67
+ require 'testcentricity_mobile'
68
+ ```
69
+
70
+ ### Using RSpec
71
+
72
+ If you are using RSpec instead, you need to require the following in your `spec_helper.rb` file:
73
+ ```ruby
74
+ require 'testcentricity_mobile'
75
+ ```
76
+
77
+ ---
78
+ ## ScreenObjects
79
+
80
+ The **Screen Object Model** is a test automation pattern that aims to create an abstraction of your native mobile app's
81
+ User Interface that can be used in tests. The **Screen** Object Model in native mobile app test automation is equivalent
82
+ to the **Page** Object Model in web user interface test automation.
83
+
84
+ A **Screen Object** is an object that represents a single screen in your AUT (Application Under Test). **Screen Objects**
85
+ encapsulate the implementation details of a mobile app screen and expose an API that supports interaction with, and validation
86
+ of the UI elements on the screen.
87
+
88
+ **Screen Objects** makes it easier to maintain automated tests because changes to screen UI elements are updated in only
89
+ one location - in the `ScreenObject` class definition. By adopting a **Screen Object Model**, Cucumber feature files and
90
+ step definitions are no longer required to hold specific information about a screen's UI objects, thus minimizing maintenance
91
+ requirements. If any element on, or property of a screen changes (text field attributes, button captions, element states,
92
+ etc.), maintenance is performed in the `ScreenObject` class definition only, typically with no need to update the affected
93
+ feature files, scenarios, or step definitions.
94
+
95
+
96
+ ### Defining a ScreenObject
97
+
98
+ Your `ScreenObject` class definitions should be contained within individual `.rb` files in the `features/support/<platform>/screens`
99
+ folder of your test automation project, where `<platform>` is typically `ios` or `android`. For each screen in your app,
100
+ you will typically have to define two `ScreenObjects` - one for the iOS version of your app and the other for the Android
101
+ version of your app.
102
+
103
+ my_automation_project
104
+ ├── config
105
+ ├── features
106
+ │ ├── step_definitions
107
+ │ ├── support
108
+ │ │ ├── android
109
+ | | | └── screens
110
+ │ │ ├── ios
111
+ | | | └── screens
112
+ │ │ ├── env.rb
113
+ │ │ └── hooks.rb
114
+ ├── Gemfile
115
+ └── README.md
116
+
117
+
118
+ You define a new `ScreenObject` as shown below:
119
+ ```ruby
120
+ class LoginScreen < TestCentricity::ScreenObject
121
+ end
122
+
123
+
124
+ class ProductsScreen < TestCentricity::ScreenObject
125
+ end
126
+
127
+
128
+ class CheckoutAddressScreen < TestCentricity::ScreenObject
129
+ end
130
+ ```
131
+
132
+ ### Adding Traits to your ScreenObject
133
+
134
+ Native app screens typically have names associated with them. Screens also typically have a unique object or attribute
135
+ that, when present, indicates that the screen's contents have fully loaded.
136
+
137
+ The `screen_name` trait is registered with the `ScreenManager` object, which includes a `find_screen` method that takes a
138
+ screen name as a parameter and returns an instance of the associated `ScreenObject`. If you intend to use the `ScreenManager`,
139
+ you must define a`screen_name` trait for each `ScreenObject` to be registered.
140
+
141
+ The `screen_name` trait is usually a `String` value that represents the name of the screen that will be matched by the
142
+ `ScreenManager.find_screen` method. `screen_name` traits are case and white-space sensitive. For screens that may be
143
+ referenced with multiple names, the `screen_name` trait may also be an `Array` of `String` values representing those
144
+ screen names.
145
+
146
+ The `screen_locator` trait specifies a locator for a unique object that exists once the screen's contents have been fully
147
+ rendered. The `screen_locator` trait is a locator strategy that uniquely identifies the object. The `ScreenObject.verify_screen_exists`
148
+ method waits for the `screen_locator` trait to exist, and raises an exception if the wait time exceeds the `default_max_wait_time`.
149
+
150
+ A `deep_link` trait should be defined if a screen can be directly loaded using a deep link. Specifying a `deep_link` trait
151
+ is optional, as not all screens can be directly accessed via a deep link.
152
+
153
+ You define your screen's **Traits** as shown below:
154
+ ```ruby
155
+ class LoginScreen < TestCentricity::ScreenObject
156
+ trait(:screen_name) { 'Login' }
157
+ trait(:screen_locator) { { accessibility_id: 'login screen' } }
158
+ trait(:deep_link) { 'mydemoapprn://login' }
159
+ end
160
+
161
+
162
+ class ProductsScreen < TestCentricity::ScreenObject
163
+ trait(:screen_name) { 'Products' }
164
+ trait(:screen_locator) { { accessibility_id: 'products screen' } }
165
+ trait(:deep_link) { 'mydemoapprn://store-overview' }
166
+ end
167
+
168
+
169
+ class CheckoutAddressScreen < TestCentricity::ScreenObject
170
+ trait(:screen_name) { 'Checkout - Address' }
171
+ trait(:screen_locator) { { accessibility_id: 'checkout address screen' } }
172
+ trait(:deep_link) { 'mydemoapprn://checkout-address' }
173
+ end
174
+ ```
175
+
176
+ ### Adding UI Elements to your ScreenObject
177
+
178
+ Native app screens are made up of UI elements like text fields, check boxes, switches, lists, buttons, etc. **UI Elements**
179
+ are added to your `ScreenObject` class definition as shown below:
180
+ ```ruby
181
+ class LoginScreen < TestCentricity::ScreenObject
182
+ trait(:screen_name) { 'Login' }
183
+ trait(:screen_locator) { { accessibility_id: 'login screen' } }
184
+ trait(:deep_link) { 'mydemoapprn://login' }
185
+
186
+ # Login screen UI elements
187
+ labels username_label: { accessibility_id: 'Username'},
188
+ password_label: { xpath: '(//XCUIElementTypeStaticText[@name="Password"])[1]'},
189
+ username_error: { accessibility_id: 'Username-error-message' },
190
+ password_error: { accessibility_id: 'Password-error-message' },
191
+ generic_error: { accessibility_id: 'generic-error-message' }
192
+ textfields username_field: { accessibility_id: 'Username input field' },
193
+ password_field: { accessibility_id: 'Password input field' }
194
+ button :login_button, { accessibility_id: 'Login button' }
195
+ end
196
+
197
+
198
+ class CheckoutAddressScreen < TestCentricity::ScreenObject
199
+ trait(:screen_name) { 'Checkout - Address' }
200
+ trait(:screen_locator) { { accessibility_id: 'checkout address screen' } }
201
+ trait(:deep_link) { 'mydemoapprn://checkout-address' }
202
+
203
+ # Checkout Address screen UI elements
204
+ textfields fullname_field: { accessibility_id: 'Full Name* input field' },
205
+ address1_field: { accessibility_id: 'Address Line 1* input field' },
206
+ address2_field: { accessibility_id: 'Address Line 2 input field' },
207
+ city_field: { accessibility_id: 'City* input field' },
208
+ state_region_field: { accessibility_id: 'State/Region input field' },
209
+ zip_code_field: { accessibility_id: 'Zip Code* input field' },
210
+ country_field: { accessibility_id: 'Country* input field' }
211
+ button :to_payment_button, { accessibility_id: 'To Payment button' }
212
+ end
213
+ ```
214
+
215
+ ### Adding Methods to your ScreenObject
216
+
217
+ It is good practice for your Cucumber step definitions to call high level methods in your your `ScreenObject` instead of
218
+ directly accessing and interacting with a screen object's UI elements. You can add high level methods to your `ScreenObject`
219
+ class definition for interacting with the UI to hide implementation details, as shown below:
220
+ ```ruby
221
+ class LoginScreen < TestCentricity::ScreenObject
222
+ trait(:screen_name) { 'Login' }
223
+ trait(:screen_locator) { { accessibility_id: 'login screen' } }
224
+ trait(:deep_link) { 'mydemoapprn://login' }
225
+
226
+ # Login screen UI elements
227
+ labels username_label: { accessibility_id: 'Username'},
228
+ password_label: { xpath: '(//XCUIElementTypeStaticText[@name="Password"])[1]'},
229
+ username_error: { accessibility_id: 'Username-error-message' },
230
+ password_error: { accessibility_id: 'Password-error-message' },
231
+ generic_error: { accessibility_id: 'generic-error-message' }
232
+ textfields username_field: { accessibility_id: 'Username input field' },
233
+ password_field: { accessibility_id: 'Password input field' }
234
+ button :login_button, { accessibility_id: 'Login button' }
235
+
236
+ def verify_screen_ui
237
+ ui = {
238
+ header_label => { visible: true, caption: 'Login' },
239
+ username_label => { visible: true, caption: 'Username' },
240
+ username_field => { visible: true, enabled: true },
241
+ password_label => { visible: true, caption: 'Password' },
242
+ password_field => { visible: true, enabled: true },
243
+ login_button => { visible: true, enabled: true, caption: 'Login' }
244
+ }
245
+ verify_ui_states(ui)
246
+ end
247
+
248
+ def login(username, password)
249
+ fields = {
250
+ username_field => username,
251
+ password_field => password
252
+ }
253
+ populate_data_fields(fields)
254
+ login_button.tap
255
+ end
256
+
257
+ def verify_entry_error(reason)
258
+ ui = case reason.gsub(/\s+/, '_').downcase.to_sym
259
+ when :invalid_password, :invalid_user
260
+ { generic_error => { visible: true, caption: 'Provided credentials do not match any user in this service.' } }
261
+ when :locked_account
262
+ { generic_error => { visible: true, caption: 'Sorry, this user has been locked out.' } }
263
+ when :no_username
264
+ { username_error => { visible: true, caption: 'Username is required' } }
265
+ when :no_password
266
+ { password_error => { visible: true, caption: 'Password is required' } }
267
+ else
268
+ raise "#{reason} is not a valid selector"
269
+ end
270
+ verify_ui_states(ui)
271
+ end
272
+ end
273
+ ```
274
+
275
+ Once your `ScreenObject` has been instantiated, you can call your methods as shown below:
276
+ ```ruby
277
+ login_screen.login('snicklefritz', 'Pa55w0rd')
278
+ login_screen.verify_entry_error('invalid user')
279
+ ```
280
+
281
+ ### Loading your App's ScreenObjects using Deeplinks
282
+
283
+ Users typically move between an app's screens (or a web portal's pages) by interacting with various navigation metaphors,
284
+ usually by tapping on buttons or links, or making selections from menu, grid, carousel, or list items. When testing web
285
+ interfaces using automated tests, time consuming interactions with the user interface can usually be reduced by using URLs
286
+ to quickly load pages without following a strict workflow.
287
+
288
+ Being able to use a combination of public or private APIs and URLs to bypass the time consuming interactions with a user
289
+ interface that may be undergoing refactoring during ongoing development (and which could lead to test failures due to bugs
290
+ in the new UI) can result in significant reduction in test execution time. While all UI interactions should be comprehensively
291
+ tested, most of the repetitive time intensive UI workflow interactions required to establish a stable base state for testing
292
+ downstream functionality can be avoided by leveraging testability "shortcuts" provided by your app's developers.
293
+
294
+ For example, in order to verify the functionality of finalizing the purchase of products via an ecommerce app or web portal,
295
+ a typical workflow might require a user to search for products to purchase, select product specific options (color, size,
296
+ quantity, etc.), add the products to a shopping cart, and log in to their account before they can finalize the purchase.
297
+ By utilizing developer provided APIs, URLs, or deeplinks, test execution time can be greatly reduced.
298
+
299
+ The `ScreenObject.load_screen` method is used to load a screen using its defined `deep_link` trait. When testing on physical
300
+ iOS devices running iOS/iPadOS versions earlier than version 16.4, deep links can only be opened by sending the deeplink URL
301
+ to the mobile Safari web browser, and then accepting the confirmation modal that pops up. The `load_screen` method handles
302
+ invoking deeplinks on Android and iOS/iPadOS simulators and physical devices.
303
+
304
+ Refer to the [Speeding Up Tests With Deep Links](https://appiumpro.com/editions/7-speeding-up-tests-with-deep-links) post on [AppiumPro](https://appiumpro.com/) for more information about deeplinks.
305
+
306
+
307
+ ---
308
+ ## ScreenSections
309
+
310
+ A `ScreenSection` is a collection of **UI Elements** that may appear in multiple locations on a screen, or on multiple
311
+ screens in an app. It is a collection of **UI Elements** that represent a conceptual area of functionality, like a menu,
312
+ a navigation bar, or a search capability. **UI Elements** and functional behavior are confined to the scope of a `ScreenSection`
313
+ object.
314
+
315
+ A `ScreenSection` may contain other `ScreenSection` objects.
316
+
317
+
318
+ ### Defining a ScreenSection
319
+
320
+ Your `ScreenSection` class definitions should be contained within individual `.rb` files in the `features/support/<platform>/sections`
321
+ folder of your test automation project, where `<platform>` is typically `ios` or `android`. For each screen section in your
322
+ app, you will typically have to define two `ScreenSections` - one for your iOS app and the other for your Android app.
323
+
324
+ my_automation_project
325
+ ├── config
326
+ ├── features
327
+ │ ├── step_definitions
328
+ │ ├── support
329
+ │ │ ├── android
330
+ | | | ├── screens
331
+ | | | └── sections
332
+ │ │ ├── ios
333
+ | | | ├── screens
334
+ | | | └── sections
335
+ │ │ ├── env.rb
336
+ │ │ └── hooks.rb
337
+ ├── Gemfile
338
+ └── README.md
339
+
340
+
341
+ You define a new `ScreenSection` as shown below:
342
+ ```ruby
343
+ class NavMenu < TestCentricity::ScreenSection
344
+ end
345
+ ```
346
+
347
+ ### Adding Traits to a ScreenSection
348
+
349
+ A `ScreenSection` typically has a root node object that encapsulates a collection of `UIElements`. The `section_locator`
350
+ trait specifies the CSS or Xpath expression that uniquely identifies that root node object.
351
+
352
+ You define your section's **Traits** as shown below:
353
+ ```ruby
354
+ class NavMenu < TestCentricity::ScreenSection
355
+ trait(:section_name) { 'Nav Menu' }
356
+ trait(:section_locator) { { xpath: '//XCUIElementTypeScrollView' } }
357
+ end
358
+ ```
359
+
360
+ ### Adding UI Elements to your ScreenSection
361
+
362
+ A `ScreenSection` is typically made up of UI elements like text fields, check boxes, switches, lists, buttons, etc. **UI
363
+ Elements** are added to your `ScreenSection` class definition as shown below:
364
+ ```ruby
365
+ class NavMenu < TestCentricity::ScreenSection
366
+ trait(:section_name) { 'Nav Menu' }
367
+ trait(:section_locator) { { xpath: '//XCUIElementTypeScrollView' } }
368
+
369
+ # Nav Menu UI elements
370
+ buttons close_button: { accessibility_id: 'close menu' },
371
+ webview_button: { accessibility_id: 'menu item webview' },
372
+ qr_code_button: { accessibility_id: 'menu item qr code scanner' },
373
+ geo_location_button: { accessibility_id: 'menu item geo location' },
374
+ drawing_button: { accessibility_id: 'menu item drawing' },
375
+ report_a_bug_button: { accessibility_id: 'menu item report a bug' },
376
+ about_button: { accessibility_id: 'menu item about' },
377
+ reset_app_button: { accessibility_id: 'menu item reset app' },
378
+ biometrics_button: { accessibility_id: 'menu item biometrics' },
379
+ log_in_button: { accessibility_id: 'menu item log in' },
380
+ log_out_button: { accessibility_id: 'menu item log out' },
381
+ api_calls_button: { accessibility_id: 'menu item api calls' },
382
+ sauce_video_button: { accessibility_id: 'menu item sauce bot video' }
383
+ end
384
+ ```
385
+
386
+ ### Adding Methods to your ScreenSection
387
+
388
+ You can add methods to your `ScreenSection` class definition, as shown below:
389
+ ```ruby
390
+ class NavMenu < TestCentricity::ScreenSection
391
+ trait(:section_name) { 'Nav Menu' }
392
+ trait(:section_locator) { { xpath: '//XCUIElementTypeScrollView' } }
393
+
394
+ # Nav Menu UI elements
395
+ buttons close_button: { accessibility_id: 'close menu' },
396
+ webview_button: { accessibility_id: 'menu item webview' },
397
+ qr_code_button: { accessibility_id: 'menu item qr code scanner' },
398
+ geo_location_button: { accessibility_id: 'menu item geo location' },
399
+ drawing_button: { accessibility_id: 'menu item drawing' },
400
+ report_a_bug_button: { accessibility_id: 'menu item report a bug' },
401
+ about_button: { accessibility_id: 'menu item about' },
402
+ reset_app_button: { accessibility_id: 'menu item reset app' },
403
+ biometrics_button: { accessibility_id: 'menu item biometrics' },
404
+ log_in_button: { accessibility_id: 'menu item log in' },
405
+ log_out_button: { accessibility_id: 'menu item log out' },
406
+ api_calls_button: { accessibility_id: 'menu item api calls' },
407
+ sauce_video_button: { accessibility_id: 'menu item sauce bot video' }
408
+
409
+ def verify_ui
410
+ ui = {
411
+ self => { visible: true },
412
+ close_button => { visible: true, enabled: true },
413
+ webview_button => { visible: true, enabled: true, caption: 'Webview' },
414
+ qr_code_button => { visible: true, enabled: true, caption: 'QR Code Scanner' },
415
+ geo_location_button => { visible: true, enabled: true, caption: 'Geo Location' },
416
+ drawing_button => { visible: true, enabled: true, caption: 'Drawing' },
417
+ report_a_bug_button => { visible: true, enabled: true, caption: 'Report A Bug' },
418
+ about_button => { visible: true, enabled: true, caption: 'About' },
419
+ reset_app_button => { visible: true, enabled: true, caption: 'Reset App State' },
420
+ biometrics_button => { visible: true, enabled: true, caption: 'FaceID' },
421
+ log_in_button => { visible: true, enabled: true, caption: 'Log In' },
422
+ log_out_button => { visible: true, enabled: true, caption: 'Log Out' },
423
+ api_calls_button => { visible: true, enabled: true, caption: 'Api Calls' },
424
+ sauce_video_button => { visible: true, enabled: true, caption: 'Sauce Bot Video' }
425
+ }
426
+ verify_ui_states(ui)
427
+ end
428
+
429
+ def close
430
+ close_button.click
431
+ self.wait_until_hidden(3)
432
+ end
433
+
434
+ def verify_closed
435
+ ui = {
436
+ self => { visible: true },
437
+ close_button => { visible: false }
438
+ }
439
+ verify_ui_states(ui)
440
+ end
441
+ end
442
+ ```
443
+
444
+ ### Adding ScreenSections to your ScreenObject
445
+
446
+ You add a `ScreenSection` to its associated `ScreenObject` as shown below:
447
+ ```ruby
448
+ class BaseAppScreen < TestCentricity::ScreenObject
449
+ # Base App screen UI elements
450
+ label :header_label, { accessibility_id: 'container header' }
451
+ sections nav_bar: NavBar,
452
+ nav_menu: NavMenu
453
+ end
454
+ ```
455
+ Once your `ScreenObject` has been instantiated, you can call its `ScreenSection` methods as shown below:
456
+
457
+ base_screen.nav_menu.verify_ui
458
+
459
+
460
+ ---
461
+ ## AppUIElements
462
+
463
+ Native app `ScreenObjects` and `ScreenSections` are typically made up of **UI Element** like text fields, switches, lists,
464
+ buttons, etc. **UI Elements** are declared and instantiated within the class definition of the `ScreenObject` or `ScreenSection`
465
+ in which they are contained. With TestCentricity, all native app screen UI elements are based on the `AppUIElement` class.
466
+
467
+
468
+ ### Declaring and Instantiating AppUIElements
469
+
470
+ Single `AppUIElement` declarations have the following format:
471
+
472
+ elementType :elementName, { locator_strategy: locator_identifier }
473
+
474
+ * The `elementName` is the unique name that you will use to refer to the UI element and is specified as a `Symbol`.
475
+ * The `locator_strategy` specifies the [selector strategy](https://appium.io/docs/en/commands/element/find-elements/index.html#selector-strategies)
476
+ that Appium will use to find the `AppUIElement`. Valid selectors are `accessibility_id:`, `id:`, `name:`, `class:`, `xpath:`,
477
+ `predicate:` (iOS only), `class_chain:` (iOS only), and `css:` (WebViews in hybrid apps only).
478
+ * The `locator_identifier` is the value or attribute that uniquely and unambiguously identifies the `AppUIElement`.
479
+
480
+ Multiple `AppUIElement` declarations for a collection of elements of the same type can be performed by passing a hash table
481
+ containing the names and locators of each individual element.
482
+
483
+ ### Example AppUIElement Declarations
484
+
485
+ Supported `AppUIElement` elementTypes and their declarations have the following format:
486
+
487
+ *Single element declarations:*
488
+ ```ruby
489
+ class SampleScreen < TestCentricity::ScreenObject
490
+ button :button_name, { locator_strategy: locator_identifier }
491
+ textfield :field_name, { locator_strategy: locator_identifier }
492
+ checkbox :checkbox_name, { locator_strategy: locator_identifier }
493
+ label :label_name, { locator_strategy: locator_identifier }
494
+ list :list_name, { locator_strategy: locator_identifier }
495
+ image :image_name, { locator_strategy: locator_identifier }
496
+ switch :switch_name, { locator_strategy: locator_identifier }
497
+ element :element_name, { locator_strategy: locator_identifier }
498
+ alert :alert_name, { locator_strategy: locator_identifier }
499
+ end
500
+ ```
501
+ *Multiple element declarations:*
502
+ ```ruby
503
+ class SampleScreen < TestCentricity::ScreenObject
504
+ buttons button_1_name: { locator_strategy: locator_identifier },
505
+ button_2_name: { locator_strategy: locator_identifier },
506
+ button_X_name: { locator_strategy: locator_identifier }
507
+ textfields field_1_name: { locator_strategy: locator_identifier },
508
+ field_2_name: { locator_strategy: locator_identifier },
509
+ field_X_name: { locator_strategy: locator_identifier }
510
+ checkboxes check_1_name: { locator_strategy: locator_identifier },
511
+ check_2_name: { locator_strategy: locator_identifier },
512
+ check_X_name: { locator_strategy: locator_identifier }
513
+ labels label_1_name: { locator_strategy: locator_identifier },
514
+ label_X_name: { locator_strategy: locator_identifier }
515
+ images image_1_name: { locator_strategy: locator_identifier },
516
+ image_X_name: { locator_strategy: locator_identifier }
517
+ end
518
+ ```
519
+ Refer to the Class List documentation for the `ScreenObject` and `ScreenSection` classes for details on the class methods
520
+ used for declaring and instantiating `AppUIElements`. Examples of UI element declarations can be found in the ***Adding
521
+ UI Elements to your ScreenObject*** and ***Adding UI Elements to your ScreenSection*** sections above.
522
+
523
+
524
+ ### AppUIElement Inherited Methods
525
+
526
+ With TestCentricity, all native app UI elements are based on the `AppUIElement` class, and inherit the following methods:
527
+
528
+ **Action methods:**
529
+
530
+ element.click
531
+ element.tap
532
+ element.double_tap
533
+ element.long_press
534
+ element.scroll_into_view
535
+ element.drag_by(right_offset, down_offset)
536
+ element.drag_and_drop(target)
537
+ element.swipe_gesture(direction, distance)
538
+
539
+ **Object state methods:**
540
+
541
+ element.exists?
542
+ element.visible?
543
+ element.hidden?
544
+ element.enabled?
545
+ element.disabled?
546
+ element.selected?
547
+ element.tag_name
548
+ element.width
549
+ element.height
550
+ element.x_loc
551
+ element.y_loc
552
+ element.count
553
+ element.get_attribute(attrib)
554
+
555
+ **Waiting methods:**
556
+
557
+ element.wait_until_exists(seconds)
558
+ element.wait_until_gone(seconds)
559
+ element.wait_until_visible(seconds)
560
+ element.wait_until_hidden(seconds)
561
+ element.wait_until_enabled(seconds)
562
+ element.wait_until_value_is(value, seconds)
563
+ element.wait_until_value_changes(seconds)
564
+
565
+
566
+ ### Populating your ScreenObject or ScreenSection with data
567
+
568
+ A typical automated test may be required to perform the entry of test data by interacting with various `AppUIElements` on
569
+ your `ScreenObject` or `ScreenSection`. This data entry can be performed using the various object action methods (listed
570
+ above) for each `AppUIElement` that needs to be interacted with.
571
+
572
+ The `ScreenObject.populate_data_fields` and `ScreenSection.populate_data_fields` methods support the entry of test data
573
+ into a collection of `AppUIElements`. The `populate_data_fields` method accepts a hash containing key/hash pairs of
574
+ `AppUIElements` and their associated data to be entered. Data values must be in the form of a `String` for `textfield`
575
+ controls. For `checkbox` controls, data must either be a `Boolean` or a `String` that evaluates to a `Boolean` value (Yes,
576
+ No, 1, 0, true, false).
577
+
578
+ The `populate_data_fields` method verifies that data attributes associated with each `AppUIElement` is not `nil` or `empty`
579
+ before attempting to enter data into the `AppUIElement`.
580
+
581
+ The optional `wait_time` parameter is used to specify the time (in seconds) to wait for each `AppUIElement` to become
582
+ viable for data entry (the `AppUIElement` must be visible and enabled) before entering the associated data value. This
583
+ option is useful in situations where entering data, or setting the state of a `AppUIElement` might cause other `AppUIElements`
584
+ to become visible or active. Specifying a wait_time value ensures that the subsequent `AppUIElements` will be ready to
585
+ be interacted with as states are changed. If the wait time is `nil`, then the wait time will be 5 seconds.
586
+
587
+ If any of the specified UI elements are not currently visible, the `populate_data_fields` method will attempt to scroll
588
+ the UI object in view on the vertical axis (down, then up).
589
+ ```ruby
590
+ def enter_data(user_data)
591
+ fields = {
592
+ first_name_field => user_data.first_name,
593
+ last_name_field => user_data.last_name,
594
+ email_field => user_data.email,
595
+ phone_number_field => user_data.phone_number
596
+ }
597
+ populate_data_fields(fields, wait_time = 2)
598
+ end
599
+ ```
600
+
601
+ ### Verifying AppUIElements on your ScreenObject or ScreenSection
602
+
603
+ A typical automated test executes one or more interactions with the user interface, and then performs a validation to
604
+ verify whether the expected state of the UI has been achieved. This verification can be performed using the various object
605
+ state methods(listed above) for each `AppUIElement` that requires verification. Depending on the complexity and number of
606
+ `AppUIElements` to be verified, the code required to verify the presence of `AppUIElements` and their correct states can
607
+ become cumbersome.
608
+
609
+ The `ScreenObject.verify_ui_states` and `ScreenSection.verify_ui_states` methods support the verification of multiple
610
+ properties of multiple UI elements on a `ScreenObject` or `ScreenSection`. The `verify_ui_states` method accepts a hash
611
+ containing key/hash pairs of UI elements and their properties or attributes to be verified.
612
+ ```ruby
613
+ ui = {
614
+ object1 => { property: expected_state },
615
+ object2 => { property1: expected_state, property2: expected_state },
616
+ object3 => { property: expected_state }
617
+ }
618
+ verify_ui_states(ui)
619
+ ```
620
+ The `verify_ui_states` method automatically scrolls UI elements that are expected to be visible into view. Auto-scrolling
621
+ only occurs on the vertical axis (down, then up). Setting the `auto_scroll` parameter to `false` prevents automatic scrolling
622
+ from occurring.
623
+
624
+ The `verify_ui_states` method queues up any exceptions that occur while verifying each object's properties until all
625
+ `AppUIElements`and their properties have been checked, and then posts any exceptions encountered upon completion. Posted
626
+ exceptions include a screenshot of the screen where expected results did not match actual results.
627
+
628
+ The `verify_ui_states` method supports the following property/state pairs:
629
+
630
+ **All Objects:**
631
+
632
+ :exists Boolean
633
+ :enabled Boolean
634
+ :disabled Boolean
635
+ :visible Boolean
636
+ :hidden Boolean
637
+ :width Integer
638
+ :height Integer
639
+ :x Integer
640
+ :y Integer
641
+ :count Integer
642
+ :value String
643
+ :caption String
644
+ :attribute Hash
645
+ :class String
646
+
647
+ **Text Fields:**
648
+
649
+ :placeholder String
650
+ :readonly Boolean (WebViews only)
651
+ :maxlength Integer (WebViews only)
652
+
653
+ **Checkboxes and Switches:**
654
+
655
+ :checked Boolean
656
+
657
+ #### Comparison States
658
+
659
+ The `verify_ui_states` method supports comparison states using property/comparison state pairs:
660
+
661
+ object => { property: { comparison_state: value } }
662
+
663
+ Comparison States:
664
+
665
+ :lt or :less_than Integer or String
666
+ :lt_eq or :less_than_or_equal Integer or String
667
+ :gt or :greater_than Integer or String
668
+ :gt_eq or :greater_than_or_equal Integer or String
669
+ :starts_with String
670
+ :ends_with String
671
+ :contains String
672
+ :not_contains or :does_not_contain Integer or String
673
+ :not_equal Integer, String, or Boolean
674
+
675
+
676
+ #### I18n Translation Validation
677
+
678
+ The `verify_ui_states` method also supports I18n string translations using property/I18n key name pairs:
679
+
680
+ object => { property: { translate_key: 'name of key in I18n compatible .yml file' } }
681
+
682
+ **I18n Translation Keys:**
683
+
684
+ :translate String
685
+ :translate_upcase String
686
+ :translate_downcase String
687
+ :translate_capitalize String
688
+ :translate_titlecase String
689
+
690
+ The example below depicts the usage of the `verify_ui_states` method to verify that the captions for menu items are correctly
691
+ translated.
692
+ ```ruby
693
+ def verify_menu
694
+ ui = {
695
+ account_settings_item => {
696
+ visible: true,
697
+ caption: { translate: 'Settings.account' }
698
+ },
699
+ help_item => {
700
+ visible: true,
701
+ caption: { translate: 'Settings.help' }
702
+ },
703
+ feedback_item => {
704
+ visible: true,
705
+ caption: { translate: 'Settings.feedback' }
706
+ },
707
+ legal_item => {
708
+ visible: true,
709
+ caption: { translate: 'Settings.legal' }
710
+ },
711
+ configurations_item => {
712
+ visible: true,
713
+ caption: { translate: 'Settings.configurations' }
714
+ },
715
+ contact_us_item => {
716
+ visible: true,
717
+ caption: { translate: 'Settings.contact' }
718
+ },
719
+ sign_out_item => {
720
+ visible: true,
721
+ caption: { translate: 'Settings.sign_out' }
722
+ }
723
+ }
724
+ verify_ui_states(ui)
725
+ end
726
+ ```
727
+ I18n `.yml` files contain key/value pairs representing the name of a translated string (key) and the string value. For the
728
+ menu example above, the translated strings for English, Spanish, and French are represented below:
729
+
730
+ **English** - `en.yml`
731
+ ```yaml
732
+ en:
733
+ Settings:
734
+ account: 'Account'
735
+ help: 'Help'
736
+ feedback: 'Feedback'
737
+ legal: 'Legal'
738
+ configurations: 'Configurations'
739
+ contact: 'Contact'
740
+ sign_out: 'Sign out'
741
+ ```
742
+ **Spanish** - `es.yml`
743
+ ```yaml
744
+ es:
745
+ Settings:
746
+ account: 'Cuenta'
747
+ help: 'Ayuda'
748
+ feedback: 'Comentario'
749
+ legal: 'Legal'
750
+ configurations: 'Configuraciones'
751
+ contact: 'Contacto'
752
+ sign_out: 'Cerrar sesión'
753
+ ```
754
+ **French** - `fr.yml`
755
+ ```yaml
756
+ fr:
757
+ Settings:
758
+ account: 'Compte'
759
+ help: 'Aide'
760
+ feedback: 'Retour'
761
+ legal: 'Légal'
762
+ configurations: 'Configurations'
763
+ contact: 'Contact'
764
+ sign_out: 'Fermer la session'
765
+ ```
766
+
767
+ Each supported language/locale combination has a corresponding `.yml` file. I18n `.yml` file naming convention uses
768
+ [ISO-639 language codes and ISO-3166 country codes](https://docs.oracle.com/cd/E13214_01/wli/docs92/xref/xqisocodes.html).
769
+ For example:
770
+
771
+ | Language (Country) | File name |
772
+ |-----------------------|-----------|
773
+ | English | en.yml |
774
+ | English (Canada) | en-CA.yml |
775
+ | French (Canada) | fr-CA.yml |
776
+ | French | fr.yml |
777
+ | Spanish | es.yml |
778
+ | German | de.yml |
779
+ | Portuguese (Brazil) | pt-BR.yml |
780
+ | Portuguese (Portugal) | pt-PT.yml |
781
+
782
+ Baseline translation strings are stored in `.yml` files in the `config/locales/` folder.
783
+
784
+ my_automation_project
785
+ ├── config
786
+ │ ├── locales
787
+ │ │ ├── en.yml
788
+ │ │ ├── es.yml
789
+ │ │ ├── fr.yml
790
+ │ │ ├── fr-CA.yml
791
+ │ │ └── en-AU.yml
792
+ │ ├── test_data
793
+ │ └── cucumber.yml
794
+ ├── features
795
+ ├── Gemfile
796
+ └── README.md
797
+
798
+
799
+ ---
800
+ ## Instantiating ScreenObjects and Utilizing the ScreenManager
801
+
802
+ Before you can call the methods in your `ScreenObjects` and `ScreenSections`, you must instantiate the `ScreenObjects` of
803
+ your native mobile application, as well as create instance variables which can be used when calling `ScreenObject` methods
804
+ from your step definitions or specs.
805
+
806
+ The `ScreenManager` class provides methods for supporting the instantiation and management of `ScreenObjects`. In the code
807
+ example below, the `screen_objects` method contains a hash table of your `ScreenObject` instances and their associated
808
+ `ScreenObject` classes to be instantiated by `ScreenManager`:
809
+ ```ruby
810
+ module WorldScreens
811
+ def screen_objects
812
+ {
813
+ login_screen: LoginScreen,
814
+ registration_screen: RegistrationScreen,
815
+ search_results_screen: SearchResultsScreen,
816
+ products_grid_screen: ProductsCollectionScreen,
817
+ product_detail_screen: ProductDetailScreen,
818
+ shopping_basket_screen: ShoppingBasketScreen,
819
+ payment_method_screen: PaymentMethodScreen,
820
+ confirm_purchase_screen: PurchaseConfirmationScreen,
821
+ my_account_screen: MyAccountScreen,
822
+ my_order_history_screen: MyOrderHistoryScreen
823
+ }
824
+ end
825
+ end
826
+
827
+ World(WorldScreens)
828
+ ```
829
+
830
+ The `WorldScreens` module above should be defined in the `world_screens.rb` file in the `features/support` folder.
831
+
832
+ Include the code below in your `env.rb` file to ensure that your `ScreenObjects` are instantiated before your Cucumber
833
+ scenarios are executed:
834
+ ```ruby
835
+ include WorldScreens
836
+ WorldPages.instantiate_screen_objects
837
+ ```
838
+ **NOTE:** If you intend to use the `WorldScreens`, you must define a `screen_name` trait for each of the `ScreenObjects`
839
+ to be registered.
840
+
841
+
842
+ ### Leveraging the ScreenManager in your Cucumber tests
843
+
844
+ Many Cucumber based automated tests suites include scenarios that verify that mobile app screens are correctly loaded,
845
+ displayed, or can be navigated to by clicking associated menus and navigation elements. One such Cucumber navigation
846
+ scenario is displayed below:
847
+ ```gherkin
848
+ Scenario Outline: Verify screen navigation features
849
+ Given I am on the Products screen
850
+ When I tap the <screen_name> navigation menu item
851
+ Then I expect the <screen_name> screen to be correctly displayed
852
+
853
+ Examples:
854
+ |screen_name |
855
+ |Registration |
856
+ |Shopping Basket |
857
+ |My Account |
858
+ |My Order History |
859
+ ```
860
+ In the above example, the step definitions associated with the 3 steps can be implemented using the `ScreenManager.find_screen`
861
+ method to match the specified `screen_name` argument with the corresponding `ScreenObject` as shown below:
862
+ ```ruby
863
+ include TestCentricity
864
+
865
+ When(/^I (?:load|am on) the (.*) screen$/) do |screen_name|
866
+ # find and load the specified target screen
867
+ target_screen = ScreenManager.find_screen(screen_name)
868
+ target_screen.load_screen
869
+ end
870
+
871
+
872
+ When(/^I (?:click|tap) the ([^\"]*) navigation menu item$/) do |screen_name|
873
+ # find and navigate to the specified target screen
874
+ target_screen = ScreenManager.find_screen(screen_name)
875
+ target_screen.navigate_to
876
+ end
877
+
878
+
879
+ Then(/^I expect the (.*) screen to be correctly displayed$/) do |screen_name|
880
+ # find and verify that the specified target screen is loaded
881
+ target_screen = ScreenManager.find_screen(screen_name)
882
+ target_screen.verify_screen_exists
883
+ # verify that target screen is correctly displayed
884
+ target_screen.verify_screen_ui
885
+ end
886
+ ```
887
+
888
+ ---
889
+ ## Connecting to a Mobile Simulator or Device
890
+
891
+ The `AppiumConnect.initialize_appium` method configures the appropriate Appium capabilities required to establish a connection
892
+ with a locally or cloud hosted target iOS or Android simulator or real device.
893
+
894
+ Since its inception, TestCentricity has provided support for establishing a single connection to a target iOS or Android
895
+ simulator or real device by instantiating an Appium driver object. **Environment Variables** are used to specify the local
896
+ or remote cloud hosted target platform, and the various Appium capability parameters required to configure the driver object.
897
+ The appropriate **Environment Variables** are typically specified in the command line at runtime through the use of profiles
898
+ set in a `cucumber.yml` file (Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below).
899
+
900
+ However, due to the growing number of optional Appium capabilities that are being offered by cloud hosted service providers
901
+ (like BrowserStack, Sauce Labs, TestingBot, or LambdaTest), **Environment Variables** may not effectively address.
902
+
903
+ Beginning with TestCentricity version 4.0.0, the `TestCentricity::AppiumConnect.initialize_appium` method accepts an optional
904
+ `options` hash for specifying desired capabilities (using the W3C protocol), driver type, driver name, endpoint URL, and
905
+ device type information.
906
+
907
+
908
+ ### Specifying Options and Capabilities in the `options` Hash
909
+
910
+ For those test scenarios where cumbersome **Environment Variables** are less than ideal, call the `AppiumConnect.initialize_appium`
911
+ method with an `options` hash that specifies the Appium desired capabilities, the driver type, and the device type, as depicted
912
+ in the example below:
913
+ ```ruby
914
+ options = {
915
+ driver: :appium,
916
+ devicetype: :phone or :tablet,
917
+ capabilities: {
918
+ platformName: :ios or :android,
919
+ 'appium:platformVersion': os_version,
920
+ 'appium:deviceName': device_name,
921
+ 'appium:automationName': 'XCUITest' or 'UiAutomator2',
922
+ 'appium:app': path_to_app, }
923
+ }
924
+ AppiumConnect.initialize_appium(options)
925
+ ```
926
+ Additional options that can be specified in an `options` hash include the following:
927
+
928
+ | Option | Purpose |
929
+ |------------------|--------------------------------------------------------------------------------|
930
+ | `driver_name:` | optional driver name |
931
+ | `endpoint:` | optional endpoint URL for local Appium server or cloud hosted service provider |
932
+ | `global_driver:` | define new driver with global scope if `true` |
933
+
934
+
935
+ Details on specifying desired capabilities, driver type, endpoint URL, global driver scope, and default driver names are
936
+ provided in each of the platform hosting sections below.
937
+
938
+ #### Specifying the Driver Type
939
+
940
+ The `driver:` type is a required entry in the `options` hash when instantiating an Appium driver object using the
941
+ `initialize_appium` method. Valid `driver:` type values are listed in the table below:
942
+
943
+ | `driver:` | **Driver Type** |
944
+ |-----------------|-----------------------------------------------------------------------|
945
+ | `:appium` | locally hosted native iOS/Android device simulator or physical device |
946
+ | `:browserstack` | remote hosted on BrowserStack |
947
+ | `:saucelabs` | remote hosted on Sauce Labs |
948
+ | `:testingbot` | remote hosted on TestingBot |
949
+ | `:custom` | remote hosted on unsupported cloud based hosting services |
950
+
951
+ #### Specifying a Driver Name
952
+
953
+ An optional user defined `driver_name:` can be specified in the `options` hash when instantiating an Appium driver object
954
+ using the `TestCentricity::AppiumConnect.initialize_appium` method. If a driver name is not specified, the `initialize_appium`
955
+ method will assign a default driver name comprised of the specified driver type (`driver:`) and the device OS and device
956
+ type specified in the `capabilities:` hash. Details on default driver names are provided in each of the device/simulator
957
+ hosting sections below.
958
+
959
+
960
+ ### Connecting to Locally Hosted Simulators or Physical Devices
961
+
962
+ Refer to [this page](https://appium.io/docs/en/2.4/guides/caps/) for information regarding specifying Appium capabilities. The Appium server must be running prior
963
+ to invoking Cucumber to run your features/scenarios on locally hosted iOS or Android simulators or physical devices. Refer
964
+ to [**section 8.2.3 (Starting and Stopping Appium Server)**](#starting-and-stopping-appium-server) below.
965
+
966
+ #### Connecting to Locally Hosted iOS Simulators or Physical Devices
967
+
968
+ You can run your automated tests on locally hosted iOS simulators or physically connected devices using Appium and XCode
969
+ on macOS. You must install Appium, XCode, and the iOS version-specific device simulators for XCode. Information about
970
+ Appium setup and configuration requirements with the XCUITest driver for testing on physically connected iOS devices can
971
+ be found on [this page](https://github.com/appium/appium-xcuitest-driver/blob/master/docs/real-device-config.md). Refer to [this page](https://appium.github.io/appium-xcuitest-driver/5.12/capabilities/) for information regarding specifying Appium capabilities that are
972
+ specific to the XCUITest driver.
973
+
974
+ ##### Local iOS Simulators or Physical Devices using Environment Variables
975
+
976
+ If the `options` hash is not provided when calling the `TestCentricity::AppiumConnect.initialize_appium` method, the following
977
+ **Environment Variables** must be set as described in the table below.
978
+
979
+ | **Environment Variable** | **Description** |
980
+ |--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
981
+ | `DRIVER` | Must be set to `appium` |
982
+ | `APP_PLATFORM_NAME` | Must be set to `iOS` |
983
+ | `AUTOMATION_ENGINE` | Must be set to `XCUITest` |
984
+ | `APP_VERSION` | Must be set to `17.4`, `16.2`, or which ever iOS version you wish to run within the XCode Simulator |
985
+ | `APP_DEVICE` | Set to iOS device name supported by the iOS Simulator (`iPhone 13 Pro Max`, `iPad Pro (12.9-inch) (5th generation)`, etc.) or name of physically connected iOS device |
986
+ | `DEVICE_TYPE` | Must be set to `phone` or `tablet` |
987
+ | `APP` | Must be set to path where iOS app can be accessed and loaded |
988
+ | `UDID` | UDID of physically connected iOS device (not used for simulators) |
989
+ | `TEAM_ID` | unique 10-character Apple developer team identifier string (not used for simulators) |
990
+ | `TEAM_NAME` | String representing a signing certificate (not used for simulators) |
991
+ | `APP_NO_RESET` | [Optional] Don't reset app state after each test. Set to `true` or `false` |
992
+ | `APP_FULL_RESET` | [Optional] Perform a complete reset. Set to `true` or `false` |
993
+ | `WDA_LOCAL_PORT` | [Optional] Used to forward traffic from Mac host to real iOS devices over USB. Default value is same as port number used by WDA on device. |
994
+ | `LOCALE` | [Optional] Locale to set for the simulator. e.g. `fr_CA` |
995
+ | `LANGUAGE` | [Optional] Language to set for the simulator. e.g. `fr` |
996
+ | `ORIENTATION` | [Optional] Set to `portrait` or `landscape` (only for iOS simulators) |
997
+ | `NEW_COMMAND_TIMEOUT` | [Optional] Time (in Seconds) that Appium will wait for a new command from the client |
998
+ | `SHOW_SIM_KEYBOARD` | [Optional] Show the simulator keyboard during text entry. Set to `true` or `false` |
999
+ | `SHUTDOWN_OTHER_SIMS` | [Optional] Close any other running simulators. Set to `true` or `false`. See note below. |
1000
+
1001
+ The `SHUTDOWN_OTHER_SIMS` environment variable can only be set if you are running Appium Server with the `--relaxed-security`
1002
+ or `--allow-insecure=shutdown_other_sims` arguments passed when starting it from the command line, or when running the server
1003
+ from the Appium Server GUI app. A security violation error will occur without relaxed security enabled.
1004
+
1005
+ Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below.
1006
+
1007
+
1008
+ ##### Local iOS Simulators or Physical Devices using the `options` Hash
1009
+
1010
+ When using the `options` hash, the following options and capabilities must be specified:
1011
+ - `driver:` must be set to `:appium`
1012
+ - `device_type:` must be set to `:tablet` or `:phone`
1013
+ - `platformName:` must be set to `ios` in the `capabilities:` hash
1014
+ - `'appium:automationName':` must be set to `xcuitest` in the `capabilities:` hash
1015
+ - `'appium:platformVersion':` must be set to the version of iOS on the simulator or physical device
1016
+ - `'appium:deviceName':` must be set to the name of the iOS simulator or physical device
1017
+ - `'appium:app'`: must be set to path where iOS app can be accessed and loaded
1018
+
1019
+ ```ruby
1020
+ options = {
1021
+ driver: :appium,
1022
+ device_type: phone_or_tablet,
1023
+ capabilities: {
1024
+ platformName: :ios,
1025
+ 'appium:automationName': 'xcuitest',
1026
+ 'appium:platformVersion': ios_version,
1027
+ 'appium:deviceName': device_or_simulator_name,
1028
+ 'appium:app': path_to_ios_app
1029
+ },
1030
+ endpoint: 'http://127.0.0.1:4723/wd/hub'
1031
+ }
1032
+ AppiumConnect.initialize_appium(options)
1033
+ ```
1034
+ > ℹ️ If an optional user defined `driver_name:` is not specified in the `options` hash, the default driver name will be set to
1035
+ `appium_<device_os>_<device_type>` - e.g. `:appium_ios_phone` or `:appium_ios_tablet`.
1036
+ >
1037
+ > ℹ️ If an `endpoint:` is not specified in the `options` hash, then the default remote endpoint URL of `http://127.0.0.1:4723/wd/hub`
1038
+ will be used.
1039
+ >
1040
+ > ℹ️ If `global_driver:` is not specified in the `options` hash, then the driver will be initialized without global scope.
1041
+
1042
+ Below is an example of an `options` hash for specifying a connection to a locally hosted mobile app running on an iPad Pro
1043
+ simulator. The `options` hash includes options for specifying the driver name, global driver scope, and setting the simulated
1044
+ device orientation to portrait mode.
1045
+ ```ruby
1046
+ options = {
1047
+ driver: :appium,
1048
+ device_type: :tablet,
1049
+ driver_name: :my_custom_ipad_driver,
1050
+ global_driver: true,
1051
+ capabilities: {
1052
+ platformName: :ios,
1053
+ 'appium:platformVersion': '15.4',
1054
+ 'appium:deviceName': 'iPad Pro (12.9-inch) (5th generation)',
1055
+ 'appium:automationName': 'XCUITest',
1056
+ 'appium:orientation': 'PORTRAIT',
1057
+ 'appium:app': Environ.current.ios_app_path
1058
+ }
1059
+ }
1060
+ AppiumConnect.initialize_appium(options)
1061
+ ```
1062
+
1063
+ #### Connecting to Locally Hosted Android Simulators or Physical Devices
1064
+
1065
+ You can run your automated tests on locally hosted Android simulators or physically connected devices using Appium and Android
1066
+ Studio on macOS. You must install Android Studio, the desired Android version-specific virtual device emulators, and
1067
+ Appium. Refer to [this page](https://appium.io/docs/en/2.2/quickstart/uiauto2-driver/) for information on configuring Appium to work with the Android SDK. Refer to [this page](https://github.com/appium/appium-uiautomator2-driver)
1068
+ for information regarding specifying Appium capabilities that are specific to the UiAutomator2 driver.
1069
+
1070
+ ##### Local Android Simulators or Physical Devices using Environment Variables
1071
+
1072
+ If the `options` hash is not provided when calling the `TestCentricity::AppiumConnect.initialize_appium` method, the following
1073
+ **Environment Variables** must be set as described in the table below.
1074
+
1075
+ | **Environment Variable** | **Description** |
1076
+ |---------------------------|--------------------------------------------------------------------------------------------------------------------------------|
1077
+ | `DRIVER` | Must be set to `appium` |
1078
+ | `APP_PLATFORM_NAME` | Must be set to `Android` |
1079
+ | `AUTOMATION_ENGINE` | Must be set to `UiAutomator2` |
1080
+ | `APP_VERSION` | Must be set to `12.0`, or which ever Android OS version you wish to run with the Android Virtual Device |
1081
+ | `APP_DEVICE` | Set to Android Virtual Device ID (`Pixel_2_XL_API_26`, `Nexus_6_API_23`, etc.) found in Advanced Settings of AVD Configuration |
1082
+ | `DEVICE_TYPE` | Must be set to `phone` or `tablet` |
1083
+ | `APP` | Must be set to path where Android `.apk` file can be accessed and loaded || `UDID` | UDID of physically connected Android device (not used for simulators) |
1084
+ | `ORIENTATION` | [Optional] Set to `portrait` or `landscape` |
1085
+ | `APP_NO_RESET` | [Optional] Don't reset app state after each test. Set to `true` or `false` |
1086
+ | `APP_FULL_RESET` | [Optional] Perform a complete reset. Set to `true` or `false` |
1087
+ | `LOCALE` | [Optional] Locale to set for the simulator. e.g. `fr_CA` |
1088
+ | `LANGUAGE` | [Optional] Language to set for the simulator. e.g. `fr` |
1089
+ | `NEW_COMMAND_TIMEOUT` | [Optional] Time (in Seconds) that Appium will wait for a new command from the client |
1090
+ | `CHROMEDRIVER_EXECUTABLE` | [Optional] Absolute local path to ChromeDriver executable |
1091
+
1092
+ Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below.
1093
+
1094
+
1095
+ ##### Local Android Simulators or Physical Devices using the `options` Hash
1096
+
1097
+ When using the `options` hash, the following options and capabilities must be specified:
1098
+ - `driver:` must be set to `:appium`
1099
+ - `device_type:` must be set to `:tablet` or `:phone`
1100
+ - `platformName:` must be set to `Android` in the `capabilities:` hash
1101
+ - `'appium:automationName':` must be set to `UiAutomator2` in the `capabilities:` hash
1102
+ - `'appium:platformVersion':` must be set to the version of Android on the simulator or physical device
1103
+ - `'appium:deviceName':` must be set to the Android Virtual Device ID
1104
+ - `'appium:app'`: must be set to path where Android `.apk` file can be accessed and loaded
1105
+
1106
+ ```ruby
1107
+ options = {
1108
+ driver: :appium,
1109
+ device_type: phone_or_tablet,
1110
+ capabilities: {
1111
+ platformName: :android,
1112
+ 'appium:automationName': 'UiAutomator2',
1113
+ 'appium:platformVersion': android_version,
1114
+ 'appium:deviceName': simulator_name,
1115
+ 'appium:avd': simulator_name,
1116
+ 'appium:app': path_to_android_app
1117
+ },
1118
+ endpoint: 'http://localhost:4723/wd/hub'
1119
+ }
1120
+ AppiumConnect.initialize_appium(options)
1121
+ ```
1122
+ > ℹ️ If an optional user defined `driver_name:` is not specified in the `options` hash, the default driver name will be set to
1123
+ `appium_<device_os>_<device_type>` - e.g. `:appium_android_phone` or `:appium_android_tablet`.
1124
+ >
1125
+ > ℹ️ If an `endpoint:` is not specified in the `options` hash, then the default remote endpoint URL of ``http://127.0.0.1:4723/wd/hub``
1126
+ will be used.
1127
+ >
1128
+ > ℹ️ If `global_driver:` is not specified in the `options` hash, then the driver will be initialized without global scope.
1129
+
1130
+
1131
+ Below is an example of an `options` hash for specifying a connection to a locally hosted mobile app running on an Android
1132
+ tablet simulator. The `options` hash includes options for specifying the driver name and setting the simulated device orientation
1133
+ to landscape mode.
1134
+ ```ruby
1135
+ options = {
1136
+ driver: :appium,
1137
+ device_type: :tablet,
1138
+ driver_name: :admin_tablet,
1139
+ capabilities: {
1140
+ platformName: 'Android',
1141
+ 'appium:platformVersion': '12.0',
1142
+ 'appium:deviceName': 'Pixel_C_API_31',
1143
+ 'appium:avd': 'Pixel_C_API_31',
1144
+ 'appium:automationName': 'UiAutomator2',
1145
+ 'appium:orientation': 'LANDSCAPE',
1146
+ 'appium:app': Environ.current.android_apk_path
1147
+ }
1148
+ }
1149
+ AppiumConnect.initialize_appium(options)
1150
+ ```
1151
+ #### Starting and Stopping Appium Server
1152
+
1153
+ ##### Using Appium Server with Cucumber
1154
+
1155
+ The Appium server must be running prior to invoking Cucumber to run your features/scenarios on locally hosted mobile simulators
1156
+ or physical devices. To programmatically control the starting and stopping of Appium server with the execution of your automated
1157
+ tests, place the code shown below in your `hooks.rb` file.
1158
+ ```ruby
1159
+ BeforeAll do
1160
+ # start Appium Server if APPIUM_SERVER = 'run'
1161
+ if ENV['APPIUM_SERVER'] == 'run'
1162
+ $server = TestCentricity::AppiumServer.new
1163
+ $server.start
1164
+ end
1165
+ end
1166
+
1167
+ AfterAll do
1168
+ # close Appium driver
1169
+ TestCentricity::AppiumConnect.quit_driver
1170
+ # terminate Appium Server if command line option was specified and Appium server is running
1171
+ if ENV['APPIUM_SERVER'] == 'run' && Environ.driver == :appium && $server.running?
1172
+ $server.stop
1173
+ end
1174
+ end
1175
+ ```
1176
+ The `APPIUM_SERVER` environment variable must be set to `run` in order to programmatically start and stop the Appium server.
1177
+ This can be set by adding the following to your `cucumber.yml` file and including `-p run_appium` in your command line when
1178
+ starting your Cucumber test suite(s):
1179
+
1180
+ run_appium: APPIUM_SERVER=run
1181
+
1182
+ Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below.
1183
+
1184
+
1185
+ ##### Using Appium Server with RSpec
1186
+
1187
+ The Appium server must be running prior to executing test specs on locally hosted mobile simulators or physical device. To
1188
+ control the starting and stopping of the Appium server with the execution of your specs, place the code shown below in the
1189
+ body of an example group:
1190
+ ```ruby
1191
+ before(:context) do
1192
+ # start Appium server before all of the examples in this group
1193
+ $server = TestCentricity::AppiumServer.new
1194
+ $server.start
1195
+ end
1196
+
1197
+ after(:context) do
1198
+ # terminate Appium Server after all of the examples in this group
1199
+ $server.stop if Environ.driver == :appium && $server.running?
1200
+ end
1201
+ ```
1202
+
1203
+ ### Connecting to Remote Cloud Hosted iOS and Android Simulators or Physical Devices
1204
+
1205
+ You can run your automated tests against remote cloud hosted iOS and Android simulators and real devices using the BrowserStack,
1206
+ SauceLabs, or TestingBot services.
1207
+
1208
+ #### Remote iOS and Android Mobile Devices on the BrowserStack service
1209
+
1210
+ For remotely hosted iOS and Android real devices on the BrowserStack service, refer to the [Browserstack-specific capabilities chart page](https://www.browserstack.com/app-automate/capabilities?tag=w3c)
1211
+ for information regarding the options and capabilities available for the various supported mobile operating systems and
1212
+ devices. BrowserStack uses only real physical devices - simulators are not available on this service.
1213
+
1214
+
1215
+ ##### Uploading your mobile app(s) to BrowserStack
1216
+
1217
+ Refer to the following pages for information on uploading your iOS `.ipa` or Android `.apk` app files to the BrowserStack
1218
+ servers:
1219
+ - [Upload apps from filesystem](https://www.browserstack.com/docs/app-automate/appium/upload-app-from-filesystem)
1220
+ - [Upload apps using public URL](https://www.browserstack.com/docs/app-automate/appium/upload-app-using-public-url)
1221
+ - [Define custom ID for app](https://www.browserstack.com/docs/app-automate/appium/upload-app-define-custom-id)
1222
+
1223
+ The preferred method of uploading an app to BrowserStack is to define a custom test ID for your apps to avoid having to
1224
+ modify your test configuration data with a new `app_url` after every app upload. Use the same custom test ID every time
1225
+ you upload a new build of the app.
1226
+
1227
+ If the `UPLOAD_APP` Environment Variable is set to `true` prior to calling the `initialize_appium` method, your iOS `.ipa`
1228
+ or Android `.apk` file will automatically be uploaded to the BrowserStack servers prior to running your tests. If you have
1229
+ not specified a custom test ID for your apps, your tests will most likely fail as a new `app_url` will be generated, and
1230
+ you will have to update your test configuration data to use the new `app_url`. If you have specified a custom test ID for
1231
+ your apps, your tests should be able to run immediately after the app file upload has completed.
1232
+
1233
+
1234
+ ##### BrowserStack Mobile Devices using Environment Variables
1235
+
1236
+ If the `options` hash is not provided when calling the `TestCentricity::AppiumConnect.initialize_appium` method, the following
1237
+ **Environment Variables** must be set as described in the table below.
1238
+
1239
+ | **Environment Variable** | **Description** |
1240
+ |--------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
1241
+ | `DRIVER` | Must be set to `browserstack` |
1242
+ | `BS_USERNAME` | Must be set to your BrowserStack account user name |
1243
+ | `BS_AUTHKEY` | Must be set to your BrowserStack account access key |
1244
+ | `BS_OS` | Must be set to `ios` or `android` |
1245
+ | `BS_DEVICE` | Refer to `deviceName` capability in chart |
1246
+ | `BS_OS_VERSION` | Set to the OS version specified in the `platformVersion` capability in the chart |
1247
+ | `DEVICE_TYPE` | Must be set to `phone` or `tablet` |
1248
+ | `AUTOMATION_ENGINE` | Must be set to `XCUITest` for iOS or `UiAutomator2` for Android |
1249
+ | `APP` | Must be set to URL or custom test ID of uploaded iOS `.ipa` or Android `.apk` file |
1250
+ | `ORIENTATION` | [Optional] Set to `portrait` or `landscape` |
1251
+ | `RECORD_VIDEO` | [Optional] Enable screen video recording during test execution (`true` or `false`) |
1252
+ | `TIME_ZONE` | [Optional] Specify custom time zone. Refer to `browserstack.timezone` capability in chart |
1253
+ | `IP_GEOLOCATION` | [Optional] Specify IP Geolocation. Refer to [IP Geolocation](https://www.browserstack.com/ip-geolocation) to select a country code. |
1254
+ | `SCREENSHOTS` | [Optional] Generate screenshots for debugging (`true` or `false`) |
1255
+ | `NETWORK_LOGS` | [Optional] Capture network logs (`true` or `false`) |
1256
+ | `APPIUM_LOGS` | [Optional] Generate Appium logs (`true` or `false`) |
1257
+ | `UPLOAD_APP` | [Optional] Automatically upload the app to BrowserStack servers if true (`true` or `false`) |
1258
+
1259
+ Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below.
1260
+
1261
+
1262
+ ##### BrowserStack Mobile Devices using the `options` Hash
1263
+
1264
+ When using the `options` hash, the following options and capabilities must be specified:
1265
+ - `driver:` must be set to `:browserstack`
1266
+ - `device_type:` must be set to `:tablet` or `:phone`
1267
+ - `platformName:` must be set to `ios` or `android` in the `capabilities:` hash
1268
+ - `'appium:automationName':` must be set to to `XCUITest` for iOS or `UiAutomator2` for Android in the `capabilities:` hash
1269
+ - `'appium:platformVersion':` must be set to the version of iOS on the simulator or physical device
1270
+ - `'appium:deviceName':` must be set to the name of the iOS simulator or physical device
1271
+ - `'appium:app'`: must be set to URL or custom test ID of uploaded iOS `.ipa` or Android `.apk` file
1272
+
1273
+ ```ruby
1274
+ options = {
1275
+ driver: :browserstack,
1276
+ device_type: phone_or_tablet,
1277
+ capabilities: {
1278
+ platformName: platform,
1279
+ 'appium:automationName': automation_name,
1280
+ 'appium:platformVersion': os_version,
1281
+ 'appium:deviceName': device_name,
1282
+ 'appium:app': app_url_or_custom_ID,
1283
+ 'bstack:options': {
1284
+ userName: bs_account_user_name,
1285
+ accessKey: bs_account_access_key
1286
+ }
1287
+ }
1288
+ }
1289
+ AppiumConnect.initialize_appium(options)
1290
+ ```
1291
+ > ℹ️ If an optional user defined `driver_name:` is not specified in the `options` hash, the default driver name will be set to
1292
+ `:browserstack_<device_os>_<device_type>` - e.g. `:browserstack_ios_phone` or `:browserstack_android_tablet`.
1293
+ >
1294
+ > ℹ️ If an `endpoint:` is not specified in the `options` hash, then the default remote endpoint URL will be set to the following:
1295
+ >
1296
+ > `https://#{ENV['BS_USERNAME']}:#{ENV['BS_AUTHKEY']}@hub-cloud.browserstack.com/wd/hub`
1297
+ >
1298
+ > ℹ️ If `global_driver:` is not specified in the `options` hash, then the driver will be initialized without global scope.
1299
+
1300
+ This default endpoint requires that the `BS_USERNAME` Environment Variable is set to your BrowserStack account user name and
1301
+ the `BS_AUTHKEY` Environment Variable is set to your BrowserStack access key.
1302
+
1303
+ Below is an example of an `options` hash for specifying a connection to a mobile app running on an iOS tablet hosted on
1304
+ BrowserStack. The `options` hash includes options for specifying the driver name, and capabilities for setting geoLocation,
1305
+ time zone, Appium version, device orientation, language, locale, and various test configuration options.
1306
+ ```ruby
1307
+ options = {
1308
+ driver: :browserstack,
1309
+ device_type: :tablet,
1310
+ driver_name: :admin_tablet,
1311
+ endpoint: "https://#{ENV['BS_USERNAME']}:#{ENV['BS_AUTHKEY']}@hub-cloud.browserstack.com/wd/hub",
1312
+ capabilities: {
1313
+ platformName: 'ios',
1314
+ 'appium:platformVersion': '17',
1315
+ 'appium:deviceName': 'iPad Pro 12.9 2021',
1316
+ 'appium:automationName': 'XCUITest',
1317
+ 'appium:app': 'RNDemoAppiOS',
1318
+ 'bstack:options': {
1319
+ userName: ENV['BS_USERNAME'],
1320
+ accessKey: ENV['BS_AUTHKEY'],
1321
+ projectName: 'ALP AP',
1322
+ buildName: "Test Build #{ENV['BUILD_NUM']}",
1323
+ sessionName: 'AU Regression Suite',
1324
+ appiumVersion: '2.0.1',
1325
+ geoLocation: 'AU',
1326
+ timezone: 'Perth',
1327
+ deviceOrientation: 'landscape'
1328
+ },
1329
+ language: 'En',
1330
+ locale: 'en_AU'
1331
+ }
1332
+ }
1333
+ AppiumConnect.initialize_appium(options)
1334
+ ```
1335
+
1336
+
1337
+ #### Remote iOS and Android Physical Devices and Simulators on the TestingBot service
1338
+
1339
+ For remotely hosted iOS and Android simulators and real devices on the TestingBot service, the following **Environment
1340
+ Variables** must be set as described in the table below. Refer to the [TestingBot List of Devices page](https://testingbot.com/support/devices) for information
1341
+ regarding the specific capabilities.
1342
+
1343
+
1344
+ ##### Uploading your mobile app(s) to TestingBot
1345
+
1346
+ Refer to the following pages for information on uploading your iOS `.ipa` or `.app` or Android `.apk` app files to the
1347
+ TestingBot servers:
1348
+ - [Upload your App](https://testingbot.com/support/mobile/upload.html)
1349
+ - [TestingBot Storage - Upload File API doc](https://testingbot.com/support/api#upload)
1350
+
1351
+ The preferred method of uploading an app to TestingBot is to define a custom test ID for your apps to avoid having to
1352
+ modify your test configuration data with a new `app_url` after every app upload. Use the same custom test ID every time
1353
+ you upload a new build of the app.
1354
+
1355
+ If the `UPLOAD_APP` Environment Variable is set to `true` prior to calling the `initialize_appium` method, your iOS `.ipa`
1356
+ or `.app`, or Android `.apk` file will automatically be uploaded to the TestingBot servers prior to running your tests. If
1357
+ you have not specified a custom test ID for your apps, your tests will most likely fail as a new `app_url` will be generated,
1358
+ and you will have to update your test configuration data to use the new `app_url`. If you have specified a custom test ID
1359
+ for your apps, your tests should be able to run immediately after the app file upload has completed.
1360
+
1361
+ When specifying you app's custom test ID in either the `APP` Environment Variable or as part of the `options` hash, the
1362
+ custom test ID is specified as `tb://your_custom_id`.
1363
+
1364
+
1365
+ ##### TestingBot Mobile Devices using Environment Variables
1366
+
1367
+ If the `options` hash is not provided when calling the `TestCentricity::AppiumConnect.initialize_appium` method, the following
1368
+ **Environment Variables** must be set as described in the table below.
1369
+
1370
+ | **Environment Variable** | **Description** |
1371
+ |--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
1372
+ | `DRIVER` | Must be set to `testingbot` |
1373
+ | `TB_USERNAME` | Must be set to your TestingBot account user name |
1374
+ | `TB_AUTHKEY` | Must be set to your TestingBot account access key |
1375
+ | `TB_OS` | Must be set to `ios` or `android` |
1376
+ | `TB_DEVICE` | Refer to `deviceName` capability in chart |
1377
+ | `TB_OS_VERSION` | Refer to `version` capability in chart |
1378
+ | `DEVICE_TYPE` | Must be set to `phone` or `tablet` |
1379
+ | `AUTOMATION_ENGINE` | Must be set to `XCUITest` for iOS or `UiAutomator2` for Android |
1380
+ | `REAL_DEVICE` | Must be set to `true` for real devices |
1381
+ | `APP` | Must be set to URL or custom test ID of uploaded iOS `.ipa` or `.app`, or Android `.apk` file |
1382
+ | `TIME_ZONE` | [Optional] Specify custom time zone. Refer to [list of time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) |
1383
+ | `IP_GEOLOCATION` | [Optional] Specify IP Geolocation. Refer to [Geolocation Testing](https://testingbot.com/support/mobile/options.html#geo) to select a country code. |
1384
+ | `RECORD_VIDEO` | [Optional] Enable screen video recording during test execution (`true` or `false`) |
1385
+ | `SCREENSHOTS` | [Optional] Generate screenshots for debugging (`true` or `false`) |
1386
+ | `UPLOAD_APP` | [Optional] Automatically upload the app to TestingBot servers if true (`true` or `false`) |
1387
+
1388
+ Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below.
1389
+
1390
+
1391
+ ##### TestingBot Mobile Devices using the `options` Hash
1392
+
1393
+ When using the `options` hash, the following options and capabilities must be specified:
1394
+ - `driver:` must be set to `:testingbot`
1395
+ - `device_type:` must be set to `:tablet` or `:phone`
1396
+ - `platformName:` must be set to `ios` or `android` in the `capabilities:` hash
1397
+ - `'appium:automationName':` must be set to to `XCUITest` for iOS or `UiAutomator2` for Android in the `capabilities:` hash
1398
+ - `'appium:platformVersion':` must be set to the version of iOS on the simulator or physical device
1399
+ - `'appium:deviceName':` must be set to the name of the iOS simulator or physical device
1400
+ - `'appium:realDevice':` must be set to `true` if testing on real physical device
1401
+ - `'appium:app'`: must be set to URL or custom test ID of uploaded iOS `.ipa` or `.app`, or Android `.apk` file
1402
+
1403
+ ```ruby
1404
+ options = {
1405
+ driver: :testingbot,
1406
+ device_type: phone_or_tablet,
1407
+ capabilities: {
1408
+ platformName: platform,
1409
+ 'appium:automationName': automation_name,
1410
+ 'appium:platformVersion': os_version,
1411
+ 'appium:deviceName': device_name,
1412
+ 'appium:realDevice': true_or_false,
1413
+ 'appium:app': app_url_or_custom_ID,
1414
+ 'tb:options': {
1415
+ # other platform specific options
1416
+ }
1417
+ }
1418
+ }
1419
+ AppiumConnect.initialize_appium(options)
1420
+ ```
1421
+ > ℹ️ If an optional user defined `driver_name:` is not specified in the `options` hash, the default driver name will be set to
1422
+ `:testingbot_<device_os>_<device_type>` - e.g. `:testingbot_ios_phone` or `:testingbot_android_tablet`.
1423
+ >
1424
+ > ℹ️ If an `endpoint:` is not specified in the `options` hash, then the default remote endpoint URL will be set to the following:
1425
+ >
1426
+ > `http://#{ENV['TB_USERNAME']}:#{ENV['TB_AUTHKEY']}@hub.testingbot.com/wd/hub`
1427
+ >
1428
+ > ℹ️ If `global_driver:` is not specified in the `options` hash, then the driver will be initialized without global scope.
1429
+
1430
+ This default endpoint requires that the `TB_USERNAME` Environment Variable is set to your TestingBot account user name and
1431
+ the `TB_AUTHKEY` Environment Variable is set to your TestingBot access key.
1432
+
1433
+ Below is an example of an `options` hash for specifying a connection to a mobile app running on a real physical iPhone hosted
1434
+ on TestingBot. The `options` hash includes options for specifying the driver name, and capabilities for setting geoLocation,
1435
+ time zone, Appium version, and various test configuration options.
1436
+ ```ruby
1437
+ options = {
1438
+ driver: :testingbot,
1439
+ device_type: :phone,
1440
+ driver_name: :tb_ios_phone,
1441
+ endpoint: "http://#{ENV['TB_USERNAME']}:#{ENV['TB_AUTHKEY']}@hub.testingbot.com/wd/hub",
1442
+ capabilities: {
1443
+ platformName: 'ios',
1444
+ 'appium:platformVersion': '17.0',
1445
+ 'appium:deviceName': 'iPhone 14',
1446
+ 'appium:realDevice': true,
1447
+ 'appium:automationName': 'XCUITest',
1448
+ 'appium:app': 'tb://RNDemoAppiOS',
1449
+ 'tb:options': {
1450
+ name: ENV['AUTOMATE_PROJECT'],
1451
+ build: "Test Build #{ENV['BUILD_NUM']}",
1452
+ appiumVersion: '2.2.1'
1453
+ }
1454
+ }
1455
+ }
1456
+ AppiumConnect.initialize_appium(options)
1457
+ ```
1458
+
1459
+
1460
+ #### Remote iOS and Android Physical Devices and Simulators on the Sauce Labs service
1461
+
1462
+ For remotely hosted iOS and Android simulators and real devices on the Sauce Labs service, the following **Environment Variables**
1463
+ must be set as described in the table below. Refer to the [Platform Configurator page](https://saucelabs.com/platform/platform-configurator)
1464
+ to obtain information regarding the specific capabilities.
1465
+
1466
+
1467
+ ##### Uploading your mobile app(s) to Sauce Labs
1468
+
1469
+ Refer to the following pages for information on uploading your iOS `.ipa` or `.app` or Android `.apk` app files to the
1470
+ Sauce Labs servers:
1471
+ - [Mobile App Storage](https://docs.saucelabs.com/mobile-apps/app-storage/)
1472
+
1473
+ The TestCentricity Mobile gem does not currently support automatic upload of app files to Sauce Labs servers. Uploading
1474
+ will have to be performed manually or via your CI workflow. If you have not specified a custom test ID for your apps, your
1475
+ tests will most likely fail as a new `app_url` will be generated, and you will have to update your test configuration data
1476
+ to use the new `app_url`. If you have specified a custom test ID for your apps, your tests should be able to run without
1477
+ modifying your test configs.
1478
+
1479
+
1480
+ ##### Sauce Labs Mobile Devices using Environment Variables
1481
+
1482
+ If the `options` hash is not provided when calling the `TestCentricity::AppiumConnect.initialize_appium` method, the following
1483
+ **Environment Variables** must be set as described in the table below.
1484
+
1485
+ | **Environment Variable** | **Description** |
1486
+ |--------------------------|-----------------------------------------------------------------------------------------------------------------|
1487
+ | `DRIVER` | Must be set to `saucelabs` |
1488
+ | `SL_USERNAME` | Must be set to your Sauce Labs account user name or email address |
1489
+ | `SL_AUTHKEY` | Must be set to your Sauce Labs account access key |
1490
+ | `SL_DATA_CENTER` | Must be set to your Sauce Labs account Data Center assignment (`us-west-1`, `eu-central-1`, `apac-southeast-1`) |
1491
+ | `SL_OS` | Must be set to `ios` or `android` |
1492
+ | `SL_DEVICE` | Refer to `deviceName` capability in chart |
1493
+ | `SL_OS_VERSION` | Refer to `platformVersion` capability in the Config Script section of the Platform Configurator page |
1494
+ | `AUTOMATION_ENGINE` | Must be set to `XCUITest` for iOS or `UiAutomator2` for Android |
1495
+ | `DEVICE_TYPE` | Must be set to `phone` or `tablet` |
1496
+ | `ORIENTATION` | [Optional] Set to `portrait` or `landscape` |
1497
+ | `RECORD_VIDEO` | [Optional] Enable screen video recording during test execution (`true` or `false`) |
1498
+ | `SCREENSHOTS` | [Optional] Generate screenshots for debugging (`true` or `false`) |
1499
+
1500
+ Refer to [**section 8.4 (Using Configuration Specific Profiles in `cucumber.yml`)**](#using-configuration-specific-profiles-in-cucumber-yml) below.
1501
+
1502
+
1503
+ ##### Sauce Labs Mobile Devices using the `options` Hash
1504
+
1505
+ When using the `options` hash, the following options and capabilities must be specified:
1506
+ - `driver:` must be set to `:saucelabs`
1507
+ - `device_type:` must be set to `:tablet` or `:phone`
1508
+ - `platformName:` must be set to `ios` or `android` in the `capabilities:` hash
1509
+ - `'appium:automationName':` must be set to to `XCUITest` for iOS or `UiAutomator2` for Android in the `capabilities:` hash
1510
+ - `'appium:platformVersion':` must be set to the version of iOS on the simulator or physical device
1511
+ - `'appium:deviceName':` must be set to the name of the iOS simulator or physical device
1512
+ - `'appium:app'`: must be set to URL or custom test ID of uploaded iOS `.ipa` or `.app`, or Android `.apk` file
1513
+
1514
+ ```ruby
1515
+ options = {
1516
+ driver: :saucelabs,
1517
+ device_type: phone_or_tablet,
1518
+ capabilities: {
1519
+ platformName: platform,
1520
+ 'appium:automationName': automation_name,
1521
+ 'appium:platformVersion': os_version,
1522
+ 'appium:deviceName': device_name,
1523
+ 'appium:app': app_url_or_custom_ID,
1524
+ 'sauce:options': {
1525
+ # other platform specific options
1526
+ }
1527
+ }
1528
+ }
1529
+ AppiumConnect.initialize_appium(options)
1530
+ ```
1531
+ > ℹ️ If an optional user defined `driver_name:` is not specified in the `options` hash, the default driver name will be set to
1532
+ `:saucelabs_<device_os>_<device_type>` - e.g. `:saucelabs_ios_phone` or `:saucelabs_android_tablet`.
1533
+ >
1534
+ > ℹ️ If an `endpoint:` is not specified in the `options` hash, then the default remote endpoint URL will be set to the following:
1535
+ >
1536
+ > `https://#{ENV['SL_USERNAME']}:#{ENV['SL_AUTHKEY']}@ondemand.#{ENV['SL_DATA_CENTER']}.saucelabs.com:443/wd/hub`
1537
+ >
1538
+ > ℹ️ If `global_driver:` is not specified in the `options` hash, then the driver will be initialized without global scope.
1539
+
1540
+ This default endpoint requires that the `SL_USERNAME` Environment Variable is set to your Sauce Labs account user name, the
1541
+ `SL_AUTHKEY` Environment Variable is set to your Sauce Labs access key, and the `SL_DATA_CENTER` Environment Variable is
1542
+ set to your Sauce Labs account Data Center assignment (`us-west-1`, `eu-central-1`, `apac-southeast-1`).
1543
+
1544
+
1545
+ #### Remote iOS and Android Physical Devices and Simulators on Unsupported Cloud Hosting Services
1546
+
1547
+ Limited support is provided for executing automated tests against remotely hosted iOS and Android simulators and real devices
1548
+ on other cloud hosting services that are currently not supported. You must call the `AppiumConnect.initialize_appium` method
1549
+ with an `options` hash - Environment Variables cannot be used to specify a user-defined custom Appium driver instance.
1550
+
1551
+ Prior to calling the `AppiumConnect.initialize_appium` method, you must set the following `Environ` attributes:
1552
+ - `Environ.platform` set to `:mobile`
1553
+ - `Environ.device_os` to either `:ios` or `:android`
1554
+ - `Environ.device` to either `:simulator` or `:device`, dependent on whether the target mobile platform is a real device
1555
+ or simulator.
1556
+ - `Environ.device_name` set to device name specified by hosting service
1557
+
1558
+ The following options and capabilities must be specified:
1559
+ - `driver:` must be set to `:custom`
1560
+ - `device_type:` must be set to `:tablet` or `:phone`
1561
+ - `endpoint:` must be set to the endpoint URL configuration specified by the hosting service
1562
+
1563
+ All other required capabilities specified by the hosting service configuration documentation should be included in the
1564
+ `capabilities:` hash.
1565
+ ```ruby
1566
+ # specify mobile platform, device type, device os, and device name
1567
+ Environ.platform = :mobile
1568
+ Environ.device = :device
1569
+ Environ.device_os = :ios
1570
+ Environ.device_name = device_name_from_chart
1571
+ # instantiate a cloud hosted mobile device or simulator on an unsupported hosting service
1572
+ options = {
1573
+ driver: :custom,
1574
+ device_type: :phone,
1575
+ endpoint: endpoint_url,
1576
+ capabilities: {
1577
+ # capabilities as specified by the hosting service
1578
+ }
1579
+ }
1580
+ AppiumConnect.initialize_appium(options)
1581
+ ```
1582
+ > ℹ️ If an optional user defined `driver_name:` is not specified in the `options` hash, the default driver name will be set to
1583
+ `:custom_<device_os>_<device_type>` - e.g. `:custom_ios_phone` or `:custom_android_tablet`.
1584
+ >
1585
+ > ℹ️ If `global_driver:` is not specified in the `options` hash, then the driver will be initialized without global scope.
1586
+
1587
+
1588
+ ### Using Configuration Specific Profiles in cucumber.yml
1589
+
1590
+ While you can set **Environment Variables** in the command line when invoking Cucumber, a preferred method of specifying
1591
+ and managing target platforms is to create platform specific **Profiles** that set the appropriate **Environment Variables**
1592
+ for each target platform in your `cucumber.yml` file.
1593
+
1594
+ Below is a list of Cucumber **Profiles** for supported locally and remotely hosted iOS and Android simulators and real
1595
+ devices (put these in in your `cucumber.yml` file). Before you can use the BrowserStack, SauceLabs, or TestingBot services,
1596
+ you will need to replace the *INSERT USER NAME HERE* and *INSERT PASSWORD HERE* placeholder text with your user account
1597
+ and authorization code for the cloud service(s) that you intend to connect with.
1598
+
1599
+ > ⚠️ Cloud service credentials should not be stored as text in your `cucumber.yml` file where it can be exposed by anyone
1600
+ with access to your version control system.
1601
+
1602
+
1603
+ #==============
1604
+ # conditionally load Screen Object implementations based on which target platform we're running on
1605
+ #==============
1606
+
1607
+ ios: PLATFORM=ios --tags @ios -r features/support/ios -e features/support/android
1608
+ android: PLATFORM=android --tags @android -r features/support/android -e features/support/ios
1609
+
1610
+
1611
+ #==============
1612
+ # profiles for mobile device screen orientation
1613
+ #==============
1614
+
1615
+ landscape: ORIENTATION=landscape
1616
+ portrait: ORIENTATION=portrait
1617
+
1618
+
1619
+ #==============
1620
+ # profile to start Appium Server prior to running locally hosted mobile app tests on iOS or Android simulators or
1621
+ # physical devices
1622
+ #==============
1623
+ run_appium: APPIUM_SERVER=run
1624
+
1625
+
1626
+ #==============
1627
+ # profiles for native iOS apps hosted within XCode iOS simulators
1628
+ # NOTE: Requires installation of XCode, iOS version specific target simulators, and Appium
1629
+ #==============
1630
+
1631
+ appium_ios: DRIVER=appium --profile ios AUTOMATION_ENGINE=XCUITest APP_PLATFORM_NAME="iOS" NEW_COMMAND_TIMEOUT="30" <%= mobile %>
1632
+ app_ios_14: --profile appium_ios APP_VERSION="14.5"
1633
+ app_ios_15: --profile appium_ios APP_VERSION="15.4"
1634
+
1635
+ iphone_12PM_14_sim: --profile app_ios_14 DEVICE_TYPE=phone APP_DEVICE="iPhone 12 Pro Max"
1636
+ iphone_13PM_15_sim: --profile app_ios_15 DEVICE_TYPE=phone APP_DEVICE="iPhone 13 Pro Max"
1637
+ iphone_11_14_sim: --profile app_ios_14 DEVICE_TYPE=phone APP_DEVICE="iPhone 11"
1638
+ ipad_pro_12_15_sim: --profile app_ios_15 DEVICE_TYPE=tablet APP_DEVICE="iPad Pro (12.9-inch) (5th generation)"
1639
+
1640
+
1641
+ #==============
1642
+ # profiles for native Android apps hosted within Android Studio Android Virtual Device emulators
1643
+ # NOTE: Requires installation of Android Studio, Android version specific virtual device simulators, and Appium
1644
+ #==============
1645
+
1646
+ appium_android: DRIVER=appium --profile android AUTOMATION_ENGINE=UiAutomator2 APP_PLATFORM_NAME="Android" <%= mobile %>
1647
+ app_android_12: --profile appium_android APP_VERSION="12.0"
1648
+ pixel_5_api31_sim: --profile app_android_12 DEVICE_TYPE=phone APP_DEVICE="Pixel_5_API_31"
1649
+
1650
+
1651
+ #==============
1652
+ # profiles for remotely hosted devices on the BrowserStack service
1653
+ # WARNING: Credentials should not be stored as text in your cucumber.yml file where it can be exposed by anyone with access
1654
+ # to your version control system
1655
+ #==============
1656
+
1657
+ browserstack: DRIVER=browserstack BS_USERNAME="<INSERT USER NAME HERE>" BS_AUTHKEY="<INSERT PASSWORD HERE>" TEST_CONTEXT="TestCentricity"
1658
+
1659
+ # BrowserStack iOS real device native app profiles
1660
+ bs_ios: --profile browserstack --profile ios BS_OS=ios <%= mobile %>
1661
+ bs_iphone: --profile bs_ios DEVICE_TYPE=phone
1662
+ bs_iphone13PM_15: --profile bs_iphone BS_OS_VERSION="15" BS_DEVICE="iPhone 13 Pro Max"
1663
+ bs_iphone11_14: --profile bs_iphone BS_OS_VERSION="14" BS_DEVICE="iPhone 11"
1664
+
1665
+ # BrowserStack Android real device native app profiles
1666
+ bs_android: --profile browserstack --profile android BS_OS=android <%= mobile %>
1667
+ bs_pixel5: --profile bs_android BS_DEVICE="Google Pixel 5" BS_OS_VERSION="12.0" DEVICE_TYPE=phone
1668
+
1669
+
1670
+ #==============
1671
+ # profiles for remotely hosted devices on the SauceLabs service
1672
+ # WARNING: Credentials should not be stored as text in your cucumber.yml file where it can be exposed by anyone with access
1673
+ # to your version control system
1674
+ #==============
1675
+
1676
+ saucelabs: DRIVER=saucelabs SL_USERNAME="<INSERT USER NAME HERE>" SL_AUTHKEY="<INSERT PASSWORD HERE>" DATA_CENTER="us-west-1" AUTOMATE_PROJECT="TestCentricity - SauceLabs"
1677
+
1678
+ # SauceLabs iOS real device native app profiles
1679
+ sl_ios: --profile saucelabs --profile ios SL_OS=ios <%= mobile %>
1680
+ sl_iphone: --profile sl_ios DEVICE_TYPE=phone
1681
+ sl_iphone13PM_15: --profile sl_iphone SL_DEVICE="iPhone 13 Pro Max Simulator" SL_OS_VERSION="15.4"
1682
+
1683
+ # SauceLabs Android real device native app profiles
1684
+ sl_android: --profile saucelabs --profile android SL_OS=android <%= mobile %>
1685
+ sl_pixel5: --profile sl_android SL_DEVICE="Google Pixel 5 GoogleAPI Emulator" SL_OS_VERSION="12.0" DEVICE_TYPE=phone
1686
+
1687
+
1688
+ #==============
1689
+ # profiles for remotely hosted devices on the TestingBot service
1690
+ # WARNING: Credentials should not be stored as text in your cucumber.yml file where it can be exposed by anyone with access
1691
+ # to your version control system
1692
+ #==============
1693
+
1694
+ testingbot: DRIVER=testingbot TB_USERNAME="<INSERT USER NAME HERE>" TB_AUTHKEY="<INSERT PASSWORD HERE>" AUTOMATE_PROJECT="TestCentricity - TestingBot"
1695
+
1696
+ # TestingBot iOS real device native app profiles
1697
+ tb_ios: --profile testingbot --profile ios TB_OS=iOS <%= mobile %>
1698
+ tb_iphone: --profile tb_ios DEVICE_TYPE=phone
1699
+ tb_iphone11_14_dev: --profile tb_iphone TB_OS_VERSION="14.0" TB_DEVICE="iPhone 11" REAL_DEVICE=true
1700
+ tb_iphone11_14_sim: --profile tb_iphone TB_OS_VERSION="14.2" TB_DEVICE="iPhone 11"
1701
+ tb_iphone13PM_15_sim: --profile tb_iphone TB_OS_VERSION="15.4" TB_DEVICE="iPhone 13 Pro Max"
1702
+
1703
+ # TestingBot Android real device native app profiles
1704
+ tb_android: --profile testingbot --profile android TB_OS=Android <%= mobile %>
1705
+ tb_pixel_dev: --profile tb_android TB_DEVICE="Pixel" TB_OS_VERSION="9.0" DEVICE_TYPE=phone REAL_DEVICE=true
1706
+ tb_pixel6_sim: --profile tb_android TB_DEVICE="Pixel 6" TB_OS_VERSION="12.0" DEVICE_TYPE=phone
1707
+
1708
+
1709
+ To specify a mobile simulator or real device target using a profile at runtime, you use the flag `--profile` or `-p` followed
1710
+ by the profile name when invoking Cucumber in the command line. For instance, the following command specifies that Cucumber will
1711
+ run tests against an iPad Pro (12.9-inch) (5th generation) with iOS version 15.4 in an XCode Simulator in portrait orientation:
1712
+
1713
+ cucumber -p ipad_pro_12_15_sim -p portrait
1714
+
1715
+ NOTE: Appium must be running prior to executing this command
1716
+
1717
+ You can ensure that Appium Server is running by including `-p run_appium` in your command line:
1718
+
1719
+ cucumber -p ipad_pro_12_15_sim -p portrait -p run_appium
1720
+
1721
+
1722
+ The following command specifies that Cucumber will run tests against a cloud hosted iPhone 13 Pro Max running iOS 15.4 on the
1723
+ BrowserStack service:
1724
+
1725
+ cucumber -p bs_iphone13PM_15
1726
+
1727
+
1728
+ ---
1729
+ ## Recommended Project Organization and Structure
1730
+
1731
+ Below is an example of the project structure of a typical Cucumber based native mobile app test automation framework with a Screen
1732
+ Object Model architecture. `ScreenObject` class definitions should be stored in the `/features/support/<platform>/screens`
1733
+ folders, organized in functional area sub-folders as needed. Likewise, `ScreenSection` class definitions should be stored in
1734
+ the `/features/support/<platform>/sections` folder, where `<platform>` is typically `ios` or `android`.
1735
+
1736
+ my_automation_project
1737
+ ├── config
1738
+ │ ├── locales
1739
+ │ ├── test_data
1740
+ │ └── cucumber.yml
1741
+ ├── features
1742
+ │ ├── step_definitions
1743
+ │ ├── support
1744
+ │ │ ├── android
1745
+ | | | ├── screens
1746
+ | | | └── sections
1747
+ │ │ ├── ios
1748
+ | | | ├── screens
1749
+ | | | └── sections
1750
+ │ │ ├── shared_components
1751
+ | | | ├── screens
1752
+ | | | └── sections
1753
+ │ │ ├── env.rb
1754
+ │ │ ├── hooks.rb
1755
+ │ │ └── world_screens.rb
1756
+ ├── Gemfile
1757
+ └── README.md
1758
+
1759
+
1760
+ ---
1761
+ ## Mobile Test Automation Framework Implementation
1762
+
1763
+ ![TestCentricity Mobile Framework Overview](https://raw.githubusercontent.com/TestCentricity/testcentricity_mobile/main/.github/images/TC_Mobile.jpg "TestCentricity Mobile Framework Overview")
1764
+
1765
+
1766
+ ---
1767
+ ## Copyright and License
1768
+
1769
+ TestCentricity™ Framework is Copyright (c) 2014-2024, Tony Mrozinski.
1770
+ All rights reserved.
1771
+
1772
+ Redistribution and use in source and binary forms, with or without
1773
+ modification, are permitted provided that the following conditions are met:
1774
+
1775
+ 1. Redistributions of source code must retain the above copyright notice,
1776
+ this list of conditions and the following disclaimer.
1777
+
1778
+ 2. Redistributions in binary form must reproduce the above copyright
1779
+ notice, this list of conditions and the following disclaimer in the
1780
+ documentation and/or other materials provided with the distribution.
1781
+
1782
+ 3. Neither the name of the copyright holder nor the names of its contributors
1783
+ may be used to endorse or promote products derived from this software without
1784
+ specific prior written permission.
1785
+
1786
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1787
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1788
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1789
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
1790
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1791
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
1792
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
1793
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1794
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1795
+ POSSIBILITY OF SUCH DAMAGE.