Mobile Application Plugin

The plugin provides functionality to test mobile application on real devices, Android Emulators and iOS Simulators.

Installation

  1. Copy the below line to dependencies section of the project build.gradle file

    Example 1. build.gradle
    implementation(group: 'org.vividus', name: 'vividus-plugin-mobile-app', version: '0.5.6')
  2. If the project was imported to the IDE before adding new plugin, re-generate the configuration files for the used IDE and then refresh the project in the used IDE.

Locator

By.<locatorType>(<locatorValue>):<visibility>->filter.<filterType>(<filterValue>)
By. prefix is optional.
  1. locatorType - [mandatory] type of the locator

  2. locatorValue - [mandatory] value of the locator

  3. visibility - [optional] visibility of element (visible by default)

  4. filterType - [optional] type of the filter

  5. filterValue - [required if filter type defined] value of the filter

Reusable locators

Feature allows to define custom reusable locator types via properties. These new locator types should rely on one of the already existing ones.

Example 2. Properties
ui.locator.<locatorName>.pattern
ui.locator.<locatorName>.locator-type
  1. locatorName - The name of the reusable locator. Supported characters are A-Z, a-z, -.

  2. pattern - The pattern of the reusable locator.

  3. locator-type - The base locator type.

Example 3. Definition of custom locator
ui.locator.anyAttOrText.locator-type=xpath
ui.locator.anyAttOrText.pattern=//*[@*='%s' or text()='%s']
Example 4. Usage of custom locator
Given I am on main application page
Then number of elements found by `anyAttOrText(attribute, text)` is = `1`
Example 5. Usage of custom locator with comma in parameter
Given I am on main application page
Then number of elements found by `anyAttOrText(attribute, with comma\, text)` is = `1`
Description Pattern Usage Result

Single parameter doesn’t require comma escaping.

//[@='%1$s' or text()='%1$s']

customLocator(Hello, Vlad!)

//[@='Hello, Vlad!' or text()='Hello, Vlad!']

The pattern with a single parameter was referenced twice.

//[@='%1$s' or text()='%1$s']

customLocator(text)

//[@='text' or text()='text']

The pattern with a , in value.

//[@='%s' or text()='%s']

customLocator(te\,xt1, text2)

//[@='te,xt1' or text()='text2']

The pattern containing % character

//input[@id="username" and text()="%%%s"]

customLocator(admin)

//input[@id="username" and text()="%admin]

The pattern without parameters

//input[@id="username"]

customLocator()

//input[@id="username"]

The pattern with two parameter placeholders was used with invalid number of parameters.

//[@='%s' or text()='%s']

customLocator(text)

Exception will be thrown!

Filter Types

Type Description Examples

index

Get an element by the specified index from a collection of elements found by a locator. Indexes start from 1.

tagName(div)→filter.index(2)

textPart

Filter elements by their text parts.

tagName(h3)→filter.textPart(Welcome)

text

Filter elements by their exact text.

tagName(android.widget.EditText)→filter.text(Welcome)

attribute

Filter elements by their attribute values

tagName(android.widget.TextView)→filter.attribute(text=Hi) - android.widget.TextView element has the text attribute with Hi value tagName(android.widget.TextView)→filter.attribute(text) - android.widget.TextView element has the text attribute with any value tagName(android.widget.TextView)→filter.attribute(text=) - android.widget.TextView element has the text attribute with an empty value

Locator Types

Type Platform Description Examples

xPath

any

Search the app XML source using xpath (not recommended, has performance issues)

xpath(//android.widget.TextView[@text='Home']) or By.xpath(//XCUIElementTypeStaticText[@value='Home'])

accessibilityId

any

See Accessibility ID selector strategy

accessibilityId(menu-toggler) or By.accessibilityId(menu-toggler)

id

any

See ID selector strategy

id(org.vividus:id/menu-toggler) or By.id(menu-toggler)

iosClassChain

iOS

See Class Chain Queries for more details

iosClassChain(**/XCUIElementTypeCell[$name BEGINSWITH "A"$]) or By.iosClassChain(**/XCUIElementTypeCell[$name BEGINSWITH "B"$])

iosNsPredicate

iOS

See iOS Predicate Syntax for more details

iosNsPredicate(name BEGINSWITH 'A') or By.iosNsPredicate(name BEGINSWITH 'B')

Visibility types

Visibility type Usage example Description

VISIBLE

xpath(//a)

Default visibility option. Only visible elements will be found

INVISIBLE

xpath(//a):i

Only invisible elements will be found

all

xpath(//a):a

Either visible and invisible elements will be found

Properties

The properties marked with bold are mandatory.
Property Name Acceptable values Default Description

mobile-application.session-scope

story

scenario

story

The test lifecycle of the mobile application:

  • the mobile application opens at the first step requiring mobile application interaction

  • the mobile application closes after story or scenario depending on the property value

  • each scenario-level examples table iteration is treated as a separate scenario

An empty property value will lead to the error: "Application session scope is not set".

ui.publish-source-on-failure

true false

true

Whether to publish the application source code on failure or not

ui.context.self-healing

true
false

false

If the value is set to true, an attempt to reset context will be performed when it becomes stale.

selenium.grid.host

hostname

<empty>

Remote grid host to be used to create a new session

selenium.grid.username

username

<empty>

Remote grid username to be used to create a new session

selenium.grid.password

password

<empty>

Password to be used to create a new session

ui.wait.timeout

ISO-8601 Durations format

PT1M

Total duration to wait for UI condition

ui.wait.polling-period

ISO-8601 Durations format

PT2S

Used together with ui.wait.timeout. Total duration of time between two iterations of UI condition check

ui.report.image-compression-quality

The value between 0 and 1 e.g. 0.75

1

Enables compressions of the images published to the report using the specified compression quality. If 1 is set then no compression will be performed. It may help to decrease total report size.

selenium.grid.platform-name

Android

iOS

Depends on the set profile

Defines the mobile OS to use

selenium.grid.automation-name

UIAutomator2 XCUITest

Depends on the set profile

Defines automation engine to use

Platform Recommended engine

Android

UIAutomator2

iOS

XCUITest

mobile-environment.real-device

true

false

Depends on the set profile

Defines whether the tests run on real device

Profile Value

browserstack/mobile_app

true

mobile_app/ios

false

mobile_app/android

false

mobile-environment.device.folder-for-file-upload

string

Depends on the set profile

Defines a destination folder on a device for a file being uploaded

Profile Value

mobile_app/ios

/private/var/mobile/Media/DCIM/

mobile_app/android

/sdcard/Pictures

proxy.enabled

true false

false

Enable proxy for the whole test run

proxy.recording.enabled

true false

false

Enable proxy recording

proxy.host

any host name resolving on the machine

<empty>

Overrides the host which will be used by proxy

proxy.ports

ports range (e.g., 10000-10005)

<empty>

Ports range which could be occupied for proxy

proxy.connectable-host

host.docker.internal

<empty>

Overrides the proxy host which will be passed to browser

proxy.publish-har-on-failure

true false

false

Enables publishing of attachments with HAR to each failed step. @noHarOnFailure meta tag can be used to disable HAR publishing at the story level.

proxy.trust-all-servers

true false

false

Whether to disable verification of upstream servers SSL certificates

proxy.capture-types

HAR capture types: REQUEST_HEADERS, REQUEST_COOKIES, REQUEST_CONTENT, REQUEST_BINARY_CONTENT, RESPONSE_HEADERS, RESPONSE_COOKIES, RESPONSE_CONTENT, RESPONSE_BINARY_CONTENT

REQUEST_CONTENT, RESPONSE_CONTENT, REQUEST_BINARY_CONTENT, RESPONSE_BINARY_CONTENT, REQUEST_HEADERS, RESPONSE_HEADERS

List of HAR capture types

proxy.mitm.enabled

true false

false

Whether to enable MITM proxy for the whole test run

proxy.mitm.mitm-manager-type

SELF_SIGNED IMPERSONATED

IMPERSONATED

The MITM type using the corresponding certificates. SELF_SIGNED MITM manager uses self-signed certificates that are generated lazily if the given keystore file doesn’t yet exist. Please note that certificates are usually generated in the current working directory

proxy.mitm.key-store.type

keystore type

JKS

The keystore type

proxy.mitm.key-store.path

path to keystore

<empty>

The relative to src/main/resources path to keystore

proxy.mitm.key-store.password

password for keystore

<empty>

The password for keystore

proxy.mitm.key-store.alias

keystore alias

<empty>

The alias for certificate entry in keystore

Meta Tags

@proxy - some steps require proxy to be turned on: it can be done by setting the corresponding properties or by switching on this meta tag at story level.

For device configuration, we need to perform the following steps: install CA certificate(download CA certificate from a browserup repository), configure device proxy, and configure the test app to allow proxy in cases of necessity.

Profiles

iOS

The profile can be enabled by adding mobile_app/ios to configuration.profiles property

Default profile configuration

selenium.grid.platform-name=iOS
selenium.grid.automation-name=XCUITest

tvOS

The profile can be enabled by adding mobile_app/tvos to configuration.profiles property

Default profile configuration

selenium.grid.platform-name=tvOS
selenium.grid.automation-name=XCUITest

Android

The profile can be enabled by adding mobile_app/android to configuration.profiles property

Default profile configuration

selenium.grid.platform-name=Android
selenium.grid.automation-name=UIAutomator2

Local

The local profiles can be used in conjunction with:

  • mobile_app/android,

  • mobile_app/ios,

  • mobile_app/tvos.

mobile_app/local

The profile simplifies configuration of tests execution against the local Appium 1.x server.

Example 6. Configure local execution of the tests for iOS native application
configuration.profiles=mobile_app/local,mobile_app/ios

mobile_app/local2

The profile simplifies configuration of tests execution against the local Appium 2.x server.

Example 7. Configure local execution of the tests for Android native application
configuration.profiles=mobile_app/local2,mobile_app/android

Dynamic variables

Get clipboard text

Gets the text of the system clipboard of simulator or real device

Properties

Property Name Default Description

mobile-environment.wda-bundle-id

empty

The property is for iOS real devices only and it specifies the WebDriverAgent bundle ID used to get clipboard text. The property value may vary depending on test cloud providers and can be found in the XCode logs.

${clipboard-text}
Example 8. Verify clipboard content
Then `${clipboard-text}` is equal to `uarlouski@gmail.com`

Context element rectangle

The set of dynamic variables provides ability to access context element coordinates, width and height.

The variables rely on the contextual approach: it is necessary to switch context to the target element.
In case of missing search context the error will be logged and -1 will be returned as a result

Variable name

Variable name Description

context-height

the height of the context element

context-width

the width of the context element

context-x-coordinate

the absolute X coordinate of the context element

context-y-coordinate

the absolute Y coordinate of the context element

Example 9. Check the size and the location of the image
When I change context to element located by `id(userpic)`
Then `${context-height}` is > `0`
Then `${context-width}` is > `0`
Then `${context-x-coordinate}` is > `0`
Then `${context-y-coordinate}` is > `0`

Source code

Variable provides source code of the UI of the application under test.

Variable name

${source-code}
Example 10. Check if element exists on UI
Then `${source-code}` matches `.+Home.+`

Steps

Start mobile application

Starts mobile application with capabilities from user’s *.properties files and profiles set by a user.

Given I start mobile application

Start mobile application with capabilities

Starts mobile application with capabilities from user’s *.properties files and profiles set by a user. Additionally the step takes a list of capabilities as an argument that overrides globally-defined capabilities.

Given I start mobile application with capabilities:$capabilities
  • $capabilities - The capabilities to start application with, these capabilities have higher priority than capabilities defined in *.properties files and profiles.

Example 11. Start mobile application with 2 custom capabilities
Given I start mobile application with capabilities:
|name                           |value|
|bstack:options.networkLogs     |true |
|appium:options.clearSystemFiles|false|

Activate mobile application

Activates the existing application on the device/emulator/simulator and moves it to the foreground. The application should be already running in order to activate it.

When I activate application with bundle identifier `$bundleId`
  • $bundleId - Package name for Android or Bundle identifier from Plist.info for iOS

Example 12. Switch to Safari browser application
When I activate application with bundle identifier `com.apple.mobilesafari`

Reinstall mobile application

Removes the mobile application from device/emulator/simulator and installs it again.

When I reinstall mobile application with bundle identifier `$bundleId`
  • $bundleId - Package name for Android or Bundle identifier from Plist.info for iOS.

The key benefit is that instead of the full remote session recreation (which includes device re-allocation in testing clouds, like SauceLabs, BrowserStack, etc.) achivable via the following example:

When I close mobile application
Given I start mobile application with capabilities:
|name     |value|
|fullReset|true |

the step re-uses the current session which significantly improves performance.

Example 13. Reinstall mobile application
When I reinstall mobile application with bundle identifier `vividus-mobile.app`

Terminate mobile application

Terminates the running application on the device/emulator/simulator and navigates to the home device screen. The remote session will not be closed.

When I terminate application with bundle identifier `$bundleId`
  • $bundleId - Package name for Android or Bundle identifier from Plist.info for iOS

Example 14. Terminates Safari browser application
When I terminate application with bundle identifier `com.apple.mobilesafari`

Close mobile application

Closes the mobile application and quits the remote session.

When I close mobile application

Run mobile application in background

Runs mobile application in the background for specific period of time and either activates the app after a certain amount of time, or leave the app deactivated (when PT-1S is used). See Appium docs for more details.

When I send mobile application to background for `$period` period
Example 15. Run app in background
When I send mobile application to background for `PT5S` period
Then number of elements found by `accessibilityId(login-button)` is equal to `1`

Change session settings

Changes the behavior of the Appium session.

When I change Appium session settings:$settings
Example 16. Configure to show invisible elements
When I change Appium session settings:
|name                  |value|
|allowInvisibleElements|true |

Upload a file to the device

Uploads a file to the device/emulator/simulator.

When I upload file `$filePath` to device
  • $filePath - The path of the file to upload to the device.

The destination folder on the device for the uploaded file is configured by mobile-environment.device.folder-for-file-upload property. See more information in the properties section.
Example 17. Upload file
When I upload file `images/avatar.png` to device
Then the avatar icon is displayed with the uploaded image

Upload the file with data to the device

Upload the file with data to the device/emulator/simulator.

When I upload file with name `$fileName` and data `$data` to device
  • $data - The data to store as a file.

  • $fileName - The name of the uploaded file.

The destination folder on the device for the uploaded file is configured by mobile-environment.device.folder-for-file-upload property. See more information in the properties section.
Example 18. Upload the file with the image data to the device
When I execute HTTP GET request for resource with URL `https://raw.githubusercontent.com/vividus-framework/vividus/master/vividus-tests/src/main/resources/baselines/context.png`
When I upload file with name `context.png` and data `${response-as-bytes}` to device
Then image is displayed in gallery
Example 19. Upload the file with the text data to the device
When I upload file with name `message.txt` and data `This is a message` to device
Then message file is displayed in file explorer

Delete a file from the device

Deletes a file from the device/emulator/simulator.

When I delete file `$filePath` from device
Argument Platform Description Examples

$filePath

iOS

The path to an existing remote file on the device. This variable can be prefixed with bundle id, so then the file will be deleted from the corresponding application container instead of the default media folder. Use @<app_bundle_id>:<optional_container_type>/<path_to_the_file_or_folder_inside_container> format to delete a file or a folder from an application container of the given type. The only supported container type is 'documents'. If the container type is not set explicitly for a bundle id, then the default application container is going to be mounted (aka --container ifuse argument) e.g. If @com.myapp.bla:documents/111.png is provided, On My iPhone/<app name> in Files app will be mounted to the host machine. `@com.myapp.bla:documents/ means On My iPhone/<app name>.

/DCIM/100APPLE/image.png or @com.mycompany.myapp:documents/myfile.txt

Android

The full path to the remote file or a file inside an application bundle

/sdcard/myfile.txt or @my.app.id/path/in/bundle

Example 20. Delete file
When I delete file `/sdcard/Pictures/avatar.png` from device
On iOS real device to add or delete photo or video to/from "Camera Roll" you need upload/delete photos or videos to/from /DCIM/100APPLE/ folder and then trigger a rebuild of the "Camera Roll" database by deleting the old databases. You can accomplish it by deleting following files: /PhotoData/Photos.sqlite, /PhotoData/Photos.sqlite-shm, /PhotoData/Photos.sqlite-wal. Restart device is necessary after that.
On iOS simulator to delete photo or video from "Camera Roll" you need to delete photo or video from /Media/DCIM/100APPLE/ and then trigger a rebuild of the "Camera Roll" database by deleting the old databases. You can accomplish it by deleting following files: /Media/PhotoData/Photos.sqlite, /Media/PhotoData/Photos.sqlite-shm, /Media/PhotoData/Photos.sqlite-wal. And then restart simulator.

Switch to Web view by name

Switches context to a web view with name that matches the rule (see Automating hybrid apps for more information). Also, Appium capabilities for iOS and Android are available for the fine-tuning configuration.

When I switch to web view with name that $comparisonRule `$value`
Example 21. Switch to web view by name
Then number of elements found by `xpath(html)` is equal to `0`
When I switch to web view with name that contains `vividustestapp`
Then number of elements found by `xpath(html)` is equal to `1`

Switch to native context

Switches context to a mobile native context. See Automating hybrid apps for more information.

When I switch to native context
Example 22. Switch to native context
Then number of elements found by `xpath(html)` is equal to `1`
When I switch to native context
Then number of elements found by `xpath(html)` is equal to `0`

Tap on element

Taps on the element located by the locator.

The atomic actions performed are:

  • press on the element;

  • release.

When I tap on element located by `$locator`

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

When I tap on element located `$locator`
Example 23. Tap on element
When I tap on element located by `accessibilityId(menu-toggler)`

Tap on element with duration

Taps on the element located by the locator with the specified duration.

The atomic actions performed are:

  • press on the element;

  • wait for the duration;

  • release.

When I tap on element located by `$locator` with duration `$duration`

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

When I tap on element located `$locator` with duration `$duration`
Example 24. Tap on element with duration
When I tap on element located by `accessibilityId(menu-toggler)` with duration `PT0.5S`

Double tap on element

Performs double tap on the element located by the locator.

The step is supported for Android and iOS platforms.
When I double tap on element located by `$locator`
Example 25. Double tap on element
When I double tap on element located by `accessibilityId(increment)`

Press key

Presses iOS keys or Android keys.

When I press $key key
  • $key - The key to press.

Example 26. Go to Home screen
When I press Home key

Long press key

Performs long press of iOS keys or Android keys.

to modify long press duration for iOS and tvOS you could use mobile-application.long-press-duration property.
When I long press $key key
  • $key - The key to perform long press action.

Example 27. Go to Home screen
When I long press Home key

Press keys

Presses a sequence of iOS keys or Android keys.

When I press keys:$keys
  • $keys - The keys to press.

Example 28. Enter password
When I press keys:
|key|
|P  |
|a  |
|s  |
|1  |
|$  |

Type text sequentially

Types the provided text

All the characters will be treated as is and passed sequentially. To send special key like HOME please use Press keys.
When I type text `$text`
  • $text - The text to type.

Example 29. Enter password
When I type text `pas1$`

Type text in a field

Types the text into the element located by the locator.

The existing text is re-written by the text passed into the step.

The atomic actions performed are:

  • type text into the element;

  • hide keyboard.

For iOS real devices the hide keyboard action works only for XCUIElementTypeTextField elements and skipped for XCUIElementTypeTextView elements, in case of XCUIElementTypeTextView element tap outside the text view is performed to close the keyboard.
When I type `$text` in field located `$locator`
  • $text - The text to type into the element.

  • $locator - Locator.

Example 30. Type text
When I type `Bob` in field located `accessibilityId(username)`

Type text and keep keyboard opened

Only types text into the element. The keyboard hiding is skipped.

When I type `$text` in field located `$locator` and keep keyboard opened
  • $text - The text to type into the element.

  • $locator - Locator.

Example 31. Type text and keep keyboard opened
When I type `Bob` in field located `accessibilityId(username)` and keep keyboard opened

Clear field

Clears the field located by the locator.

The atomic actions performed are:

  • clear the field;

  • hide keyboard.

For iOS real devices the hide keyboard action works only for XCUIElementTypeTextField elements and skipped for XCUIElementTypeTextView elements, in case of XCUIElementTypeTextView element tap outside the text view is performed to close the keyboard.
When I clear field located `$locator`
Example 32. Clear the field
When I clear field located `accessibilityId(username)`

Clear field and keep keyboard open

Only clears the field located by the locator. The keyboard hiding is skipped.

When I clear field located `$locator` and keep keyboard open
Example 33. Clear text and keep keyboard open
When I clear field located `accessibilityId(username)` and keep keyboard open

Swipe to an element

Swipes to an element in either UP, DOWN, LEFT or RIGHT direction with the specified swipe duration.

The step takes into account the current context. If you need to perform swipe on the element, you need to switch the context to this element.

Properties

  • mobile-application.swipe.limit - defines max numbers of swipes to perform (if it’s reached the swipe process is stopped and the error is thrown)

  • mobile-application.swipe.stabilization-duration - defines a duration to wait after swipe until view becomes stable

  • mobile-application.swipe.vertical.x-position - defines a vertical swipe x axis position in percentage from 0 to 100, where 0 corresponds to x=1 and 100 corresponds to x = mobile screen width - 1, default value is 0

  • mobile-application.swipe.horizontal.y-position - defines a horizontal swipe y axis position in percentage from 0 to 100, where 0 corresponds to y=1 and 100 corresponds to y = mobile screen height - 1, default value is 50

When I swipe $direction to element located by `$locator` with duration $swipeDuration

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

When I swipe $direction to element located `$locator` with duration $swipeDuration
  • $direction - The direction to swipe: UP, DOWN, LEFT or RIGHT.

  • $locator - Locator.

  • $swipeDuration - The swipe duration in ISO-8601 Durations format.

Example 34. Swipe to an element
Then number of elements found by `accessibilityId(end-of-screen)` is equal to `0`
When I swipe UP to element located by `accessibilityId(end-of-screen)` with duration PT1S
Then number of elements found by `accessibilityId(end-of-screen)` is equal to `1`
Example 35. Swipe the context element
Scenario: Switch slides
When I change context to element located by `accessibilityId(carousel)`
Then number of elements found by `accessibilityId(slide 2)` is equal to `0`
When I swipe LEFT to element located `accessibilityId(slide 2)` with duration PT1S
Then number of elements found by `accessibilityId(slide 2)` is equal to `1`

Execute sequence of touch actions

Executes the sequence of touch actions.

When I execute sequence of touch actions: $actions
  • $actions - The table with actions to execute.

    Table 1. Possible actions
    type argument Argument example

    TAP

    Element locator or empty

    By.id(Name)

    TAP_AND_HOLD

    Element locator or empty

    By.id(Name)

    MOVE_TO

    Element locator

    By.id(username)

    MOVE_BY_OFFSET

    Point, that specifies x and y distance to move from its current position or from upper left corner (0,0), if the pointer has not previously been moved

    (10, 15) where x is 10 and y is 15

    RELEASE

    empty

    WAIT

    Duration

    PT1S - wait 1 second

RELEASE action releases the finger at the current touch location. For example, it can be used in the following sequence of actions:`TAP_AND_HOLD` → MOVE_BY_OFFSETRELEASE
Example 36. Execute touch actions
When I execute sequence of touch actions:
|type          |argument          |
|TAP_AND_HOLD  |By.id(Hello World)|
|MOVE_BY_OFFSET|(0,200)           |
|RELEASE       |                  |

Zoom in/out

Performs zoom in/out

Zoom in
zoom in
Zoom out
zoom out
The step takes into account the current context. If you need to perform zoom on the element, you need to switch the context to this element.

Properties

zoom indent
Property Name Acceptable values Default value Description

mobile-application.zoom.indent.top

size in percentage

30

The size of the top part of the context to be excluded from zoom area

mobile-application.zoom.indent.bottom

size in percentage

30

The size of the bottom part of the context to be excluded from zoom area

mobile-application.zoom.indent.right

size in percentage

20

The size of the right part of the context to be excluded from zoom area

mobile-application.zoom.indent.left

size in percentage

20

The size of the left part of the context to be excluded from zoom area

The indent percentage value should be from 0 to 99.

The sum of top and bottom indent percentage values should be from 0 to 99.

The sum of right and left indent percentage values should be from 0 to 99.

Example 37. Property configuration
mobile-application.zoom.indent.top=30
mobile-application.zoom.indent.bottom=30
mobile-application.zoom.indent.right=20
mobile-application.zoom.indent.left=20

Step

When I zoom $zoomType context
  • $zoomType - The type of zoom: IN or OUT

Example 38. Zoom in/out
When I zoom in context
When I wait until element located by `xpath(<welcomeMessageXpath>)` disappears
When I zoom out context
When I wait until element located by `xpath(<welcomeMessageXpath>)` appears

Select date in date picker

Selects a next or previous picker wheel value in date picker

  • Only iOS platform is supported.

  • The target element must be of type XCUIElementTypePickerWheel.

When I select $direction value with `$offset` offset in picker wheel located `$locator`
  • $direction - The direction to search the value: NEXT or PREVIOUS.

  • $offset - The ofset to pick from a middle of the wheel, see Select Picker Wheel Value guide.

  • $locator - The locator to find XCUIElementTypePickerWheel element.

Example 39. Select next item in the picker wheel
When I select next value with `0.1` offset in picker wheel located `xpath(//XCUIElementTypePickerWheel)->filter.index(3)`

Recording iOS Performance metrics

Records performance metrics of iOS application. It is based on the Instruments utility distributed by Xcode which provides a number of built-in analyses and measurements. See Capturing Performance Data for Native iOS Apps and Getting Started with Instruments for more information.

Appium server should be started with the --relaxed-security flag. This is because Instruments can gather data from the system as a whole, not just the AUT. (It’s thus a security risk to expose potentially sensitive system information to Appium sessions running from a remote client).
Archive with results contains *.trace file which could be opened by XCode. There is possibility to parse this file using XCode’s utility xctrace and use generated data in tests
When I start recording `$instrument` metrics
When I stop recording `$instrument` metrics and save results to file `$path`
  1. $instrument - The instrument of Xcode Instruments utility. Some useful instruments: Time Profiler, Leaks, File Activity, etc.

  2. $path - The path to the file to save an archive with collected data.

Example 40. Perform profiling during application usage
When I start recording `Time Profiler` metrics
!-- set of actions to measure performance
When I stop recording `Time Profiler` metrics and save results to file `${outputDirectory}/time_profiler.zip`

Set slider value

iOS

Sets the slider value in percents.

  • The accuracy of setting the slider value is not guaranteed and may vary depending on the device screen resolution.

  • The target element must be of type XCUIElementTypeSlider.

  • The percent number must be between 0 and 100 inclusively.

When I set value of iOS slider located `$locator` to `$percent` percents
  • $locator - The locator to find XCUIElementTypeSlider element.

  • $percent - The target value of the slider in percents.

Example 41. Set value of iOS slider (with range 0:50) to 45
When I set value of iOS slider located `xpath(//XCUIElementTypeSlider)` to `90` percents
Example 42. Set value of iOS slider (with range -50:50) to 20
When I set value of iOS slider located `xpath(//XCUIElementTypeSlider)` to `70` percents
Example 43. Set value of iOS slider (with range 18:65) to 55
When I set value of iOS slider located `xpath(//XCUIElementTypeSlider)` to `80` percents

Android

Sets the slider value to the number relatively to the slider leftmost side of its range.

  • The step is expected to set accurate value without deviations.

  • The target element must be of type android.widget.SeekBar.

  • The number must be greater than or equal to 0.

  • Make sure the passed number does not exceed the right limit of the slider, this may lead to unexpected failure.

When I set value of Android slider located `$locator` to `$number`
  • $locator - The locator to find android.widget.SeekBar element.

  • $number - The number to set on the slider

Example 44. Set value of Android slider (with range 0:50) to 13
When I set value of Android slider located `xpath(//android.widget.SeekBar)` to `13`
Example 45. Set value of Android slider (with range -50:50) to -25
When I set value of Android slider located `xpath(//android.widget.SeekBar)` to `25`
Example 46. Set value of Android slider (with range 18:65) to 20
When I set value of Android slider located `xpath(//android.widget.SeekBar)` to `2`

Wait for element appearance

Waits for appearance of the element by the locator.

It’s forbidden to use Visibility types in the locator.
When I wait until element by located `$locator` appears

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

When I wait until element located `$locator` appears
Example 47. Wait for appearance of the element with the specified name
When I wait until element located by `name(welcome-image)` appears

Wait for element disappearance

Waits for disappearance of the element by the locator.

If the element doesn’t exist on the page/context, the step will immediately complete successfully. Checking the element on the page (if needed) should be done in a separate step (e.g. Wait for element appearance or Validate elements).
It’s forbidden to use Visibility types in the locator.
When I wait until element located by `$locator` disappears

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

When I wait until element located `$locator` disappears
Example 48. Wait for disappearance of the element with the specified name
When I wait until element located by `name(welcome-image)` disappears

Wait for expected elements number

Waits for the expected number of elements located by locator.

In order to wait for invisible elements one rely on Visibility types.
When I wait until number of elements located by `$locator` is $comparisonRule $number
Example 49. Wait for the visible elements
When I wait until number of elements located by `tagName(img)` is equal to `5`
Example 50. Wait for the invisible elements
When I wait until number of elements located by `tagName(img):1` is equal to `5`

Verify elements number

Verifies if the number of elements located by locator matches the desired number.

Then number of elements found by `$locator` is $comparisonRule `$quantity`
Example 51. Verify the number of elements
Then number of elements found by `tagName(img)` is equal to `5`

Verify elements state

Verifies if the number of elements located by locator matches the number condition and all of them are in the desired state.

If you describe number condition as equal to 5 in case if there are 10 elements by this locator and only 5 of them in the desired state. You will get two failed assertions. The first one about number condition violation. The second one about state check failure.
In case when Visibility types are used and checked state are equal (For an example ':i' and 'NOT_VISIBLE') exception will be thrown. In such cases please use step: Verify elements number. Contradictory visibility parameters (locator - ':i' and checked state - 'VISIBLE') are also not allowed.
Then number of $state elements found by `$locator` is $comparisonRule `$quantity`
Example 52. Verify the number of elements in the state
Then number of VISIBLE elements found by `tagName(img)` is = `1`

Save the text of the context

Saves the text of the context element into a variable.

Step will throw an error if the context element is not set.
When I save text of context element to $scopes variable `$variableName`
Example 53. Save the text of the context element
When I change context to element located by `id(username)`
When I save text of context element to SCENARIO variable `username`

Saves the attribute value of the context

Saves the attribute value of the context element into a variable.

Step will throw an error if the context element is not set.
When I save `$attributeName` attribute value of context element to $scopes variable `$variableName`
Example 54. Save the attribute value of the context element
When I change context to element located by `id(username)`
When I save `innerText` attribute value of context element to SCENARIO variable `username`

Save the attribute value of the element

Saves the attribute value of the element located by locator into a variable.

When I save `$attributeName` attribute value of element located `$locator` to $scopes variable `$variableName`

Save the attribute value of the element

When I save `innerText` attribute value of element located `By.id(username)` to SCENARIO variable `username`

Save number of elements

Saves number of elements located by locator into a variable.

When I save number of elements located `$locator` to $scopes variable `$variableName`
Example 55. Save number of elements
When I save number of elements located `tagName(a)` to scenario variable `numberOfLinks`
Then `${numberOfLinks}` is equal to `1`

Change context

Changes the context to an element located by locator for limiting area of subsequent UI interactions.

When I change context to element located by `$locator` in scope of current context

Deprecated syntax (will be removed in VIVIDUS 0.7.0):

When I change context to element located `$locator` in scope of current context
Example 56. Change context
Then number of elements found by `By.xpath(html)` is equal to `1`
When I change context to element located by `xpath(//body)` in scope of current context
Then number of elements found by `By.xpath(html)` is equal to `0`

Reset and change context

Resets current context and changes the context to an element located by locator for limiting area of subsequent UI interactions.

When I change context to element located by `$locator`

Deprecated syntax (will be removed in VIVIDUS 0.7.0):

When I change context to element located `$locator`
Example 57. Change context
Then number of elements found by `By.xpath(html)` is equal to `1`
When I change context to element located by `xpath(//body)`
Then number of elements found by `By.xpath(html)` is equal to `0`

Reset context

Resets the context.

When I reset context
Example 58. Reset context
Then number of elements found by `By.xpath(html)` is equal to `1`
When I change context to element located by `xpath(//body)`
Then number of elements found by `By.xpath(html)` is equal to `0`
When I reset context
Then number of elements found by `By.xpath(html)` is equal to `1`

Element existence during the time period

Validates the element located by the locator has existed for the period specified by the duration.

The actions performed by the step:

  • check the search context is set,

  • search for the element to validate existence (this search may include wait for element appearance if it’s configured),

  • validate the element has presented for the period specified by the duration.

Then element located by `$locator` exists for `$duration` duration

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

Then element located `$locator` exists for `$duration` duration
  • $locator - The Locator to find an element.

  • $duration - The period of time the element should have existed in ISO-8601 Durations format.

Example 59. The element should have existed for 5 second
Then element located by `id(banner)` exists for `PT5S` duration

Navigate back

Navigates back to the previous view.

When I navigate back
Example 60. Navigate back
Then number of elements found by `xpath(//*[@*='Welcome'])` is equal to `1`
When I navigate back
Then number of elements found by `xpath(//*[@*='Welcome'])` is equal to `0`

Verify elements order

Gets a collection of elements by locator and checks that they are sorted by their text in the specified order. The collection should have more than 1 element with not empty text, otherwise the step fails.

Then elements located by $locator are sorted by text in $sortingOrder order

Deprecated syntax (will be removed in VIVIDUS 0.6.0):

Then elements located $locator are sorted by text in $sortingOrder order
  • $locator - Locator.

  • $sortingOrder

Plain Readable Description

ASCENDING

ascending

Verify that elements are sorted in ascending order

DESCENDING

descending

Verify that elements are sorted in descending order

CASE_INSENSITIVE_ASCENDING

case-insensitive ascending

Verify that elements are sorted in case-insensitive ascending order

CASE_INSENSITIVE_DESCENDING

case-insensitive descending

Verify that elements are sorted in case-insensitive descending order

Example 61. Check items are sorted
Given I am on page with URL `https://grocery.by`
When I click on element located by `id(a-z)`
Then elements located by `xpath(//span[@id='item-to-purchase'])` are sorted by text in ascending order

Take a screenshot

Takes a screenshot and publish it to the report.

When I take screenshot

Take a screenshot and save it to the folder

Takes a screenshot and saves it to the default folder defined by user.

The full path to a screenshot will be posted to logs and allure
When I take screenshot and save it to folder `$path`
  • $path - The absolute or relative path to the folder to save taken screenshot.

Example 62. Take a screenshot to a path
When I take screenshot and save it to folder `${screenshot-directory}/#{generate(Ancient.god)}.png`

Scan barcode

Scan a barcode and save its value to the variable with the specified name. Only the first founded barcode will be scanned.

Supported Code Formats

1D product 1D industrial 2D
  • UPC-A

  • UPC-E

  • EAN-8

  • EAN-13

  • UPC/EAN Extension 2/5

  • RSS-14

  • RSS-Expanded

  • Code 39

  • Code 93

  • Code 128

  • Codabar

  • ITF

  • QR Code

  • Data Matrix

  • Aztec

  • PDF 417

  • MaxiCode

Actions performed at this step:

  • Takes a viewport screenshot

  • Scans a barcode from the screenshot and save its value to the variable

When I scan barcode from screen and save result to $scopes variable `$variableName`
Example 63. Scan a barcode
When I scan barcode from screen and save result to scenario variable `qrCodeLink`
Then `${qrCodeLink}` is equal to `https://www.example.com`

Perform steps while elements exist

Executes the steps while the found elements exist.

To avoid infinite loops the iterationLimit parameter is used. If iteration’s limit reached the step will fail.
When I find $comparisonRule `$number` elements `$locator` and while they exist do up to $iterationLimit iteration of$stepsToExecute

Alias:

When I find $comparisonRule '$number' elements $locator and while they exist do up to $iterationLimit iteration of$stepsToExecute
  • $comparisonRule - The comparison rule.

  • $number - The number of elements to find.

  • $locator - Locator.

  • $iterationLimit - The maximum number of iterations to perform.

  • $stepsToExecute - The ExamplesTable with a single column containing the steps to execute.

Example 64. Disable menus
When I find >= `0` elements `xpath(//*[@class='menu enabled'])` and while they exist do up to 10 iteration of
|step                                            |
|When I click on element located by `id(disable)`|

Execute JavaScript with arguments

Executes JavaScript via JavascriptExecutor. Step executes the script either without any arguments or with String or Object argument types.

When I execute javascript `$script` with arguments:$arguments
  • $script - The JavaScript code to execute.

  • $arguments - The examples table with set of arguments and their type:

    • value - The value of the argument.

    • type - The type of the argument. Either string or object.

Example 65. Remove noisy assistant
When I execute javascript `document.querySelector('#assistant').remove()` with arguments:
Example 66. Inject image
When I execute javascript `sauce:inject-image` with arguments:
|value                                                                                       |type  |
|iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=|string|
Example 67. Throttle CPU
When I execute javascript `sauce:throttleCPU` with arguments:
|value                                                                                       |type  |
|{"rate" : 4}|object|

Execute JavaScript and save result.

Executes JavaScript code and saves result into variable.

When I execute javascript `$jsCode` and save result to $scopes variable `$variableName`
Example 68. Validate timings
Given I am on page with URL `https://vividus-test-site.onrender.com/`
When I execute javascript `return JSON.stringify(window.performance.timing)` and save result to scenario variable `timings`
Then number of JSON elements from `${timings}` by JSON path `$.connectStart` is = 1

Scan barcode from context

Scan a barcode from the specified context and save its value to the variable with the specified name. If context not set - takes a screenshot of the current screen. Only the first found barcode will be scanned.

Supported Code Formats

1D product 1D industrial 2D
  • UPC-A

  • UPC-E

  • EAN-8

  • EAN-13

  • UPC/EAN Extension 2/5

  • RSS-14

  • RSS-Expanded

  • Code 39

  • Code 93

  • Code 128

  • Codabar

  • ITF

  • QR Code

  • Data Matrix

  • Aztec

  • PDF 417

  • MaxiCode

Actions performed at this step:

  • Takes a screenshot of the specified context. If it’s not set - takes a screenshot of the current screen (viewport and system bars)

  • Scans a barcode from the screenshot and save its value to the variable

When I scan barcode from context and save result to $scopes variable `$variableName`
Example 69. Scan the barcode
When I change context to element located by `xpath(//android.view.ViewGroup[@resource-id='qrContainer'])`
When I scan barcode from context and save result to scenario variable `qrCodeLink`
Then `${qrCodeLink}` is equal to `https://www.example.com`

Proxy Steps

Check the number of HTTP requests

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level.

The actions preformed by the step:

  • extract HTTP messages from the recorded proxy archive

  • filter out the HTTP messages with the response status code 302 Moved Temporarily

  • find HTTP requests matching the provided HTTP methods and the URL regular expression

  • check that the total number of the found HTTP messages satisfies the desired condition

In case of failure the full HTTP archive (HAR) is attached to the report.

Then number of HTTP $httpMethods requests with URL pattern `$urlPattern` is $comparisonRule `$number`
  • $httpMethods - The comma-separated HTTP methods to filter by

  • $urlPattern - The regular expression to match HTTP request URL

  • $comparisonRule - Comparison Rule

  • $number - The number to compare with

Example 70. Check the number of HTTP GET and POST requests matching URL regular expression is equal to 1
Then number of HTTP GET, POST requests with URL pattern `http://httpbin\.org/get` is equal to `1`

Capture HTTP message

Save the HTTP message part from the HTTP request with given URL-pattern into the variable with specified name and the scopes.

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level.

The actions preformed by the step:

  • extract HTTP messages from the recorded proxy archive

  • filter out the HTTP messages with the response status code 302 Moved Temporarily

  • find HTTP requests matching the provided HTTP methods and the URL regular expression

  • check that total number of the found HTTP messages is equal to 1

  • save the HTTP message part to the specified variable

In case of failure the full HTTP archive (HAR) is attached to the report.

When I capture HTTP $httpMethods request with URL pattern `$urlPattern` and save $httpMessagePart to $scopes variable `$variableName`
  • $httpMethods - The "or"-separated set of HTTP methods to filter by, e.g. GET or POST or PUT.

  • $urlPattern - The regular expression to match HTTP request URL.

  • $httpMessagePart - The HTTP message part to save. One of:

    • URL - The request URL.

    • URL query - The request URL query parameters.

    • request data - The request data includes the following keys:

      • query - The URL query is stored as a collection of key and value pairs, where key is the name of the query parameter and value is the list of query parameter values. The query parameter values are accessible via zero-based index.

      • requestBody.mimeType - The MIME type of posted data, the variable will not be saved if MIME type is not present.

      • requestBody.text - The posted data as plain text, the variable will not be saved if the request body is not present.

      • requestBodyParameters - The form data parameters are stored as a collection of key and value pairs, where key is the name of the form parameter and value is the list of form parameter values. The form parameter values are accessible via zero-based index.

      • responseStatus - The response status, the variable will not be saved if the response is not present.

    • response data - The response data includes the following keys:

      • responseBody.mimeType - The MIME type of response data, the variable will not be saved if MIME type is not present.

      • responseBody.text - The response data as plain text, the variable will not be saved if the response body is not present.

  • $scopes - The comma-separated set of the variables scopes.

  • $variableName - The variable name to save the HTTP message part.

Example 71. Validate the URL of the matching HTTP request
Given I start mobile application
When I capture HTTP GET or POST request with URL pattern `.*/search.*=vividus` and save URL to scenario variable `URL`
Then `${URL}` is equal to `https://www.google.com/search?q=vividus`
Example 72. Validate the URL query of the matching HTTP request
Given I start mobile application
When I capture HTTP GET request with URL pattern `.*/search.*=vividus` and save URL query to scenario variable `query`
Then `${query.q[0]}` is equal to `vividus`
Then `${query.q}` is equal to `[vividus]`
Then `${query}` is equal to `{q=[vividus]}`
Example 73. Validate the request data from the matching HTTP message
Given I start mobile application
When I capture HTTP GET request with URL pattern `https://www.google.com/search?q=vividus` and save request data to scenario variable `data`
Then `${data.query}` is equal to `{}`
Then `${data.requestBodyParameters}` is equal to `{delivery=, custtel=, comments=, custemail=, custname=}`
Then `${data.requestBody}` is not equal to `null`
Then `${data.responseStatus}` is equal to `200`
Example 74. Validate the response data from the matching HTTP message
Given I start mobile application
When I capture HTTP GET request with URL pattern `https://www.google.com/search?q=vividus` and save request data to scenario variable `data`
Then `${data.responseBody.text}` is equal to `{"status": "ok"}`
Then `${data.responseBody.mimeType}` is equal to `application/json`

Wait for the HTTP request

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. Waits for the HTTP requests matching the provided HTTP methods and URL regular expression. If no HTTP request is sent and wait timeout is reached, then the step will fail.

When I wait until HTTP $httpMethods request with URL pattern `$urlPattern` exists in proxy log
  • $httpMethods - the "or"-separated HTTP methods to filter by, e.g. 'GET or POST or PUT'

  • $urlPattern - the regular expression to match HTTP request URL

Example 75. Wait for the HTTP request
Given I start mobile application
When I wait until HTTP GET or POST request with URL pattern `https://www.google.com/search?q=vividus` exists in proxy log
Then number of HTTP GET or POST requests with URL pattern `https://www.google.com/search?q=vividus` is equal to `1`

Add headers to the HTTP request

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. Add headers to the proxied HTTP request satisfying the desired condition

When I add headers to proxied requests with URL pattern which $comparisonRule `$url`:$headers
  • $comparisonRule - String comparison rule

  • $url - The input value of URL to filter by

  • $headers - The ExamplesTable representing the list of the headers with columns "name" and "value" specifying HTTP header names and values respectively

Example 76. Add headers to the proxied HTTP request
When I add headers to proxied requests with URL pattern which is equal to `https://www.google.com/search?q=vividus`:
|name     |value     |
|testName1|testValue1|
|testName2|testValue2|
Given I start mobile application
Then a JSON element from '${response}' by the JSON path '$.headers' is equal to '
{
    "Testname1": "testValue1",
    "Testname2": "testValue2"
}
'ignoring extra fields

Clear the recordings

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. The step clears the HTTP requests and responses recorded by the proxy

When I clear proxy log
Example 77. Clear the data recorded by the proxy
Given I start mobile application
When I clear proxy log
Then number of HTTP GET requests with URL pattern 'https://www.google.com/search?q=vividus' is equal to `0`

Clear the mocks

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. The step clears previously created mocks

When I clear proxy mocks
Example 78. Clear the proxy mocks
When I mock HTTP responses with request URL which CONTAINS `vividus` using response code `200`, content `#{loadResource(page.html)}` and headers:
|name        |value    |
|Content-Type|text/html|
When I clear proxy mocks
Given I start mobile application
Then number of elements found by `id(sw)` is = `0`

Mock the HTTP response by methods with content

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. Mocks HTTP response by methods with a provided content

No actual request will be executed. Short-circuited response will be returned.
When I mock HTTP $httpMethods responses with request URL which $comparisonRule `$url` using response code `$responseCode`, content `$payload` and headers:$headers
  • $httpMethods - The "or"-separated set of HTTP methods to filter by, e.g. GET or POST or PUT.

  • $rule - String comparison rule

  • $url - The input value of URL to filter by

  • $code - The response status code

  • $content - The content to send within a response

  • $headers - The ExamplesTable representing the list of the headers with columns "name" and "value" specifying HTTP header names and values respectively

Example 79. Hijack a page
When I mock HTTP POST responses with request URL which CONTAINS `example` using response code `202`, content `#{loadResource(mocked-example.json)}` and headers:
|name        |value           |
|Content-Type|application/json|

Mock the HTTP response with content

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. Mocks HTTP response with a provided content

No actual request will be executed. Short-circuited response will be returned.
When I mock HTTP responses with request URL which $comparisonRule `$url` using response code `$responseCode`, content `$payload` and headers:$headers
  • $rule - String comparison rule

  • $url - The input value of URL to filter by

  • $code - The response status code

  • $content - The content to send within a response

  • $headers - The ExamplesTable representing the list of the headers with columns "name" and "value" specifying HTTP header names and values respectively

Example 80. Hijack a page
When I mock HTTP responses with request URL which CONTAINS `example.com` using response code `200`, content `#{loadResourceAsByteArray(page.html)}` and headers:
|name        |value    |
|Content-Type|text/html|

Mock the HTTP response without content

This step requires proxy to be turned on. It can be done in properties or by switching on @proxy meta tag at the story level. Mocks HTTP response

No actual request will be executed. Short-circuited response will be returned.
When I mock HTTP responses with request URL which $comparisonRule `$url` using response code `$responseCode` and headers:$headers
  • $rule - String comparison rule

  • $url - The input value of URL to filter by

  • $code - The response status code

  • $headers - The ExamplesTable representing the list of the headers with columns "name" and "value" specifying HTTP header names and values respectively

Example 81. 404 page
When I mock HTTP responses with request URL which CONTAINS `example.com` using response code `404` and headers:
|name          |value|
|Content-Length|0    |

Change network connection

Turns ON/OFF network connection.

To be able to use this step in the cloud testing platform, review the documentation related to simulating the mobile network conditions on the selected platform.
When I turn $toggle `$connectionName` network connection
  • $toggle - The modes of the toggle: ON or OFF.

  • $connectionName - The name of the network connect to toggle.

Connection name Supported platforms Description

WIFI

Android emulators, Android and iOS real devices

Wi-Fi connection

MOBILE_DATA

Android emulators, Android and iOS real devices

Mobile Data connection

AIRPLANE

Android real devices, Android emulators

Airplane Mode

WIFI_AND_MOBILE_DATA

Android real devices, Android emulators

Mobile Data and Wi-Fi connections

For iOS real devices: to return to the application you must use the step:

When I activate application with bundle identifier `$bundleId`.
Example 82. Turn OFF Wi-Fi for Android
When I turn OFF `WIFI` network connection
Example 83. Turn OFF Wi-Fi for iOS
When I turn OFF `WIFI` network connection
When I activate application with bundle identifier `my-application-bundle-id`

Change device screen orientation

Changes the device screen orientation.

When I change device screen orientation to $orientation
  • $orientation - The device screen orientation, either portrait or landscape.

Example 84. Change the device screen orientation
When I change device screen orientation to portrait