Shiratesを使ってみよう - セレクター式 -
セレクター式
Shiratesのセレクター式は画面要素をフィルターするための式です。 select関数はセレクター式を引数にとり、解析を行い、画面要素を取り出してフィルターし、結果として取得された要素を返します。 セレクター式は1つ以上のフィルター式から成ります。
フィルター式
フィルター | フォーマルな書式 | 省略形 | Androidの属性 | iOSの属性 |
---|---|---|---|---|
text | text=text1 | text1 | text | label |
textStartsWith | textStartsWith=text1 | text1* | text | label |
textContains | textContains=text1 | *text1* | text | label |
textEndsWith | textEndsWith=text1 | *text1 | text | label |
textMatches | textMatches=^text$ | n/a | text | label |
literal | literal=literal1 | 'literal1' | text | label |
id | id=id1 | #id1 | resource-id | name |
access | access=access1 | @access1 | content-desc | name |
accessStartsWith | accessStartsWith=access1 | @access1* | content-desc | name |
accessContains | accessContains=access1 | @*access1* | content-desc | name |
accessEndsWith | accessEndsWith=access1 | @*access1 | content-desc | name |
accessMatches | accessMatches=^access1$ | n/a | content-desc | name |
value | value=value1 | n/a | text | value |
valueStartsWith | valueStartsWith=value1 | n/a | text | value |
valueContains | valueContains=value1 | n/a | text | value |
valueEndsWith | valueEndsWith=value1 | n/a | text | value |
valueMatches | valueMatches=^value1$ | n/a | text | value |
class | class=class1 | .class1 | class | type |
focusable | focusable=true | n/a | focusable | n/a |
scrollable | scrollable=true | n/a | scrollable | n/a |
visible | visible=true | n/a | n/a | visible |
xpath | xpath=//*[@text='text1'] | n/a | (arbitrary) | (arbitrary) |
pos | pos=2 | [2] | n/a | n/a |
ignoreTypes | ignoreTypes=Class1,Class2 | n/a | class | type |
image | image=image1.png | image1.png | n/a | n/a |
フィルターにおける複数値の指定
フィルターは複数の値を (value1|value2)
のように丸括弧と"|"(or)演算子で受け取ることができます。
it.select("text=(text1|text2)") // フォーマルな書式 it.select("(text1|text2)") // 省略形の書式
以下のセレクター式はシンプルですが上記と等価です。
it.select("text1||text2") // 等価なセレクター式
もっと複雑な状況では"|"が必要になるかもしれません。
it.select("(text1|text2)&&.class1||(@access1|@access2)&&.class2)")
セレクター式の規則
フィルター式は単独でセレクター式として使用できます。
text1
フィルター式は"&&"(AND)演算子で結合することができます。
text1&&.class1&&visible=true
フィルター式は"||"(OR)演算子で結合することができます。
text1||text2||@access1
"&&"は"||"よりも優先されます。
text1&&.class1||@access1
上記は(text=text1 and class=class1) or access=access1
の意味となります。
ただし、以下のように丸括弧を使用することはできません(サポートしていません)。
悪い例
(text1&&.class1)||@access1
完全修飾ID
Androidのresource-id
はパッケージ名に対応したプレフィックスを持っています。例えば設定アプリは"com.android.settings
がパッケージ名です。
完全修飾形 | 省略形 |
---|---|
com.android.settings:id/search_bar | search_bar |
Shiratesでは完全修飾形への自動変換が行われ、かつ可読性が高いことから省略形の使用が推奨されます。 自動変換ではうまくいかない場合は完全修飾形を使用します。
セレクター式のためのプラットフォームアノテーション
プラットフォームアノテーション(@a, @i)を使用するとAndroid用とiOS用のセレクター式を1行で記述することができます。
@a<.android.widget.ImageButton>,@i<.XCUIElementTypeButton>
it.select("@a<.android.widget.ImageButton>,@i<.XCUIElementTypeButton>")
この記述を使用してニックネームを定義することができます。
"[Button1]": "@a<.android.widget.ImageButton>,@i<.XCUIElementTypeButton>"
否定フィルター
複雑な状況においては否定フィルターを使用することができます。
text=text1 // 通常のフィルター text!=text1 // 否定フィルター text1 // 通常のフィルター !text1 // 否定フィルター accessContains=text1 // 通常のフィルター accessContains!=text1 // 否定フィルター @*text1* // 通常のフィルター !@*text1* // 否定フィルター
画像フィルター
画像ファイルを使用して画像マッチング(テンプレートマッチング)を行うことができます。 画像による検証
image=image1.png // フォーマルな形式 image1.png // 省略形 image1.png?scale=0.5&threshold=20 // オプションあり(フォーマルな形式) image1.png?s=0.5&t=20 // オプションあり(省略形)
サンプルコード
サンプルコードの入手
本記事で説明するサンプルコードの完成版はこちらから入手してください。 https://github.com/wave1008/shirates-samples-selectors
SelectorTest2
import org.junit.jupiter.api.Order import org.junit.jupiter.api.Test import shirates.core.driver.branchextension.emulator import shirates.core.driver.branchextension.realDevice import shirates.core.driver.commandextension.* import shirates.core.testcode.UITest class SelectTest2 : UITest() { @Test @Order(10) fun selectByText() { scenario { case(1) { condition { it.restartApp() }.action { it.select("ネットワークとインターネット") }.expectation { it.textIs("ネットワークとインターネット") } } case(2) { action { it.select("ネットワークと*") }.expectation { it.textIs("ネットワークとインターネット") } } case(3) { action { it.select("*とインターネット") }.expectation { it.textIs("ネットワークとインターネット") } } case(4) { action { it.select("textMatches=^ネットワークとインターネット$") }.expectation { it.textIs("ネットワークとインターネット") } } case(5) { action { it.selectWithScrollDown("デバイス情報||エミュレートされたデバイスについて") }.expectation { realDevice { it.textIs("デバイス情報") }.emulator { it.textIs("エミュレートされたデバイスについて") } } } } } @Test @Order(20) fun selectById() { scenario { case(1) { condition { it.restartApp() }.action { it.select("#search_action_bar_title") }.expectation { it.textIs("設定を検索") } } } } @Test @Order(30) fun selectByAccessibility() { scenario { case(1) { condition { it.restartApp() .tap("ネットワークとインターネット") }.action { it.select("@ネットワークとインターネット") }.expectation { it.idIs("collapsing_toolbar") } } } } @Test @Order(40) fun selectByClass() { scenario { case(1) { condition { it.restartApp() }.action { it.select(".android.widget.ImageButton") }.expectation { it.classIs("android.widget.ImageButton") } } } } @Test @Order(50) fun selectByXpath() { scenario { case(1) { condition { it.restartApp() }.action { it.select("xpath=//*[@text='設定を検索']") }.expectation { it.textIs("設定を検索") } } } } @Test @Order(60) fun selectByPos() { scenario { case(1) { condition { it.restartApp() .tap("バッテリー") }.action { it.select("*バッテリー*&&[1]") }.expectation { it.textIs("バッテリー使用量") } } case(2) { action { it.select("*バッテリー*&&[2]") }.expectation { it.textIs("バッテリーセーバー") } } case(3) { action { it.select("*バッテリー*&&[3]") }.expectation { it.textIs("バッテリーを長持ちさせ、充電を最適化します") } } } } }
サンプルコードの実行結果