How to use Relative Selector in Shirates - Part 2 -
Published: 2022-10-19 13:10:55

This article is an introduction of how to use [Shirates](https://dev.to/wave1008/introducing-shirates-a-mobile-testing-automation-tool-3nmc), a mobile testing automation tool.

## Selector expression
You can use **selector expression** for selecting an element in the screen. This is simple and powerful expression(See [How to use selector expression in Shirates](https://dev.to/wave1008/how-to-use-selector-expression-in-shirates-2po4)).

## Relative selector
Once you selected an element on the screen, you might want to get another element around the element relatively. In this case, you can use Shirates's **relative selector**.

----

## Relative selector(Widget flow based)
You can get a **widget** relatively along with **widget flow**.

### Widget flow algorithm
Widget flow algorithm groups widgets with vertical position (1st group - 6th group), then searches widgets in each groups from left to right, then up to down.


![widget_flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b8hhhhmgei5dki96ggga.png)

In the 1st group, widget flow order is (0),(1),(2).
In the 2nd group, widget flow order is (3),(4),(5),(6),(7).
In the 3nd group, widget flow order is (8),(9).
In the 4th group, widget flow order is (10),(11).
In the 5th group, widget flow order is (12).
In the 6th group, widget flow order is (13).

### Grouping patterns
![widget_flow_grouping](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnwbnkbd765xuyt1y0qt.png)

## Relative selectors (Widget flow based)

| relative selector | alias    | description                                            |
|:------------------|:---------|:-------------------------------------------------------|
| :flow             | -        | widget in widget flow                                  |
| :flowLabel        | :label   | label in widget flow                                   |
| :flowInput        | :input   | input in widget flow                                   |
| :flowImage        | :image   | image in widget flow                                   |
| :flowButton       | :button  | button in widget flow                                  |
| :flowSwitch       | :switch  | switch in widget flow                                  |
| :innerFlow        | :inner   | widget in widget flow inside of the container          |
| :innerLabel       | -        | label in widget flow inside of the container           |
| :innerInput       | -        | input in widget flow inside of the container           |
| :innerImage       | -        | image in widget flow inside of the container           |
| :innerButton      | -        | button in widget flow inside of the container          |
| :innerSwitch      | -        | switch in widget flow inside of the container          |
| :vflow            | -        | widget in vertical widget flow                         |
| :innerVflow       | :innerV  | widget in vertical widget flow inside of the container |
| :innerVlabel      | -        | label in vertical widget flow inside of the container  |
| :innerVinput      | -        | input in vertical widget flow inside of the container  |
| :innerVimage      | -        | image in vertical widget flow inside of the container  |
| :innerVbutton     | -        | button in vertical widget flow inside of the container |
| :innerVswitch     | -        | switch in vertical widget flow inside of the container |

## Relative selector examples

| example | description |
|:--------|:------------|
| `<text1>:input` | Select the first element that text is "text1", then select the first input in widget flow.   |
| `<.Class1>:flow(2)` | Select the first element that type is Class1, then select the second widget in widget flow.  |

## Material
You can get complete sample project from [https://github.com/wave1008/shirates-samples-selectors].

### SelectWithWidgetFlowTest
```kotlin
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import shirates.core.driver.commandextension.classIs
import shirates.core.driver.commandextension.restartApp
import shirates.core.driver.commandextension.select
import shirates.core.driver.commandextension.textIs
import shirates.core.testcode.UITest

class SelectWithWidgetFlowTest : UITest() {

    @Test
    @Order(10)
    fun selectWithWidgetFlow() {

        scenario {
            case(1) {
                condition {
                    it.restartApp()
                }.expectation {
                    it.select("<Settings>:flow")
                        .classIs("android.widget.ImageButton")
                    it.select("<Settings>:flow(2)")
                        .textIs("Search settings")
                    it.select("<Settings>:flow(3)")
                        .classIs("android.widget.ImageView")
                    it.select("<Settings>:flow(4)")
                        .textIs("Network & internet")
                    it.select("<Settings>:flow(5)")
                        .textIs("Mobile, Wi‑Fi, hotspot")
                }
            }
            case(2) {
                expectation {
                    it.select("<Settings>") {
                        // Select relatively from <Settings>
                        select(":flow").classIs("android.widget.ImageButton")
                        select(":flow(2)").textIs("Search settings")
                        select(":flow(3)").classIs("android.widget.ImageView")
                        select(":flow(4)").textIs("Network & internet")
                        select(":flow(5)").textIs("Mobile, Wi‑Fi, hotspot")
                    }
                }
            }
        }
    }

    @Test
    @Order(20)
    fun selectWithWidgetFlow2() {

        scenario {
            case(1) {
                condition {
                    it.restartApp()
                }.expectation {
                    it.select("<Settings>") {
                        // Select relatively from <Settings>
                        select(":label").textIs("Search settings")
                        select(":label(2)").textIs("Network & internet")
                        select(":label(3)").textIs("Mobile, Wi‑Fi, hotspot")
                    }
                }
            }
            case(2) {
                expectation {
                    it.select("<Network & internet>") {
                        // Select relatively from <Network & internet>
                        select(":image").classIs("android.widget.ImageView")
                        select(":label").textIs("Mobile, Wi‑Fi, hotspot")
                    }
                }
            }
        }
    }

}
```

#### selectWithWidgetFlow/case(1),case(2)

![selectWithWidgetFlow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52tz1qw9lj2v4pe3g80g.png)

#### selectWithWidgetFlow2/case(1)

![selectWithWidgetFlow2(1)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ml0dw7airfoj62gtndo1.png)

#### selectWithWidgetFlow2/case(2)

![selectWithWidgetFlow2(2)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggjrrv6y41pl2dwxdyfj.png)

### Test Result

#### HTML-Report

![HTML-Report](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65dh72aqqh2t2z1vyzci.png)

---

## For more information
See [Relative selector(Widget flow based)](https://github.com/ldi-github/shirates-core/blob/main/doc/markdown/basic/selector_and_nickname/relative_selector/relative_selector_flow.md)

---

## Conclusion
Once you selected an element on the screen, you can get another element around the element relatively using Shirates's **relative selector**.

You can get a widget(input, label, image, button, switch) relatively along with **widget flow**.