testcentricity_mobile 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.