요소 찾기

화면 덤프를 읽는 법, 계속 통하는 선택자를 고르는 법, 중복 요소와 목록, 아이콘, 웹 페이지, 요소가 없는 화면을 다루는 법, 그리고 맞지 않는 선택자를 디버깅하는 법을 설명합니다.

번역된 페이지입니다. 영어 문서와 내용이 다르면 영어 문서가 기준입니다. English

Droidline 스크립트에서 하는 일의 대부분은 어느 요소를 다룰지 알려 주는 것입니다. 이 가이드는 Droidline이 보는 방식대로 화면을 살펴보는 법과, 앱이 업데이트되거나 폰의 언어가 바뀌어도 계속 통하는 선택자를 고르는 법을 보여 줍니다.

쓰기 전에 먼저 보기

폰에서 해당 화면을 연 다음, Droidline이 보는 내용을 저장하세요.

droidline dump screen.json
droidline screenshot screen.png
d.dump("screen.json")
d.screenshot("screen.png")
await d.dump("screen.json");
await d.screenshot("screen.png");

screen.json을 에디터에서 스크린샷과 나란히 열고, 폰에 보이는 단어를 검색하세요. 검색에 걸린 곳마다 요소가 하나 있고, 그 주변의 속성이 고를 수 있는 선택지입니다.

{
  "text": "Keep me signed in",
  "id": "dev.droidline.demo:id/auto_login",
  "desc": "",
  "class": "android.widget.CheckBox",
  "bounds": [60, 860, 1020, 960],
  "clickable": true,
  "checkable": true,
  "checked": false
}

bounds는 픽셀 단위의 [left, top, right, bottom]입니다. 파일의 어느 요소가 화면의 무엇인지 헷갈리면 bounds를 스크린샷과 비교하세요. 위 요소는 가로로 60에서 1020픽셀, 세로로 860에서 960픽셀 사이에 있습니다.

선택자 고르기

선택자는 속성 하나와 값 하나로 이루어집니다. 다음 순서로 고르세요.

  1. 요소에 id가 있다면 id. ID는 앱 개발자가 정하는 값이라 폰의 언어에 따라 바뀌지 않습니다. 앱이 업데이트돼도 대부분 그대로입니다. :id/ 뒤의 부분만 씁니다. 예: ("id", "auto_login").
  2. 버튼과 레이블에는 text. 코드에서 읽기 쉽지만, 화면에 보이는 문구이므로 언어에 따라, 때로는 업데이트에 따라 바뀝니다. 개수처럼 달라지는 부분이 텍스트에 들어 있으면 textContains를 쓰세요. ("textContains", "unread")는 "3 unread"와 일치합니다.
  3. 아이콘에는 desc. 아이콘 버튼은 보통 텍스트가 없지만, "Search"나 "More options"처럼 스크린 리더용 설명이 있습니다.
  4. 최후의 수단으로 class와 nth. "세 번째 체크박스" 같은 지정은 레이아웃이 바뀌는 순간 깨집니다.
d.touch("id", "auto_login")
d.touch("text", "Log in")
d.touch("textContains", "unread")
d.touch("desc", "More options")
d.touch("class", "android.widget.CheckBox", nth=2)
await d.touch("id", "auto_login");
await d.touch("text", "Log in");
await d.touch("textContains", "unread");
await d.touch("desc", "More options");
await d.touch("class", "android.widget.CheckBox", { nth: 2 });
droidline touch id auto_login
droidline touch text "Log in"
droidline touch textContains unread
droidline touch desc "More options"
droidline touch class android.widget.CheckBox --nth 2

직접 탭할 수 없는 텍스트

텍스트가 클릭 가능한 더 큰 행 안의 레이블이고, 레이블 자체는 클릭할 수 없는 경우가 많습니다. 행을 따로 찾을 필요는 없습니다. touch는 레이블이 클릭을 받지 않는다는 것을 알아차리고, 대신 가장 가까운 클릭 가능한 상위 요소를 탭합니다. 이렇게 했을 때는 응답에 "via": "parent"가 나옵니다.

설정 행 끝의 스위치처럼 레이블이 원하는 컨트롤의 옆에 있다면, 레이블의 행을 탭하세요. 보통 스위치가 전환됩니다. 그렇지 않으면 스위치 자체를 선택하세요. 흔히 class와 nth로 지정합니다.

여러 요소가 일치할 때

요소가 둘 이상 일치하면 명령은 화면 순서상 첫 번째 요소를 다룹니다. 다른 요소를 고르려면 nth를 쓰고(0부터 셉니다), 몇 개인지 보려면 count를 쓰세요.

print(d.count("text", "Delete"))          # 3
d.touch("text", "Delete", nth=1)          # 두 번째 요소
console.log(await d.count("text", "Delete"));   // 3
await d.touch("text", "Delete", { nth: 1 });    // 두 번째 요소
droidline count text Delete
droidline touch text Delete --nth 1

목록 아래쪽의 요소

dump에는 화면에 보이는 것만 들어 있고, 일부 목록에서는 조금 더 들어 있습니다. 더 아래쪽에 있는 요소는 아직 존재하지 않으므로 touch가 찾을 수 없습니다. scroll_to로 요소가 나타날 때까지 스와이프한 다음 그 요소를 다루세요.

d.scroll_to("text", "Developer options")
d.touch("text", "Developer options")

d.scroll_to("text", "Airplane mode", direction="up")   # 위쪽으로 찾기
await d.scrollTo("text", "Developer options");
await d.touch("text", "Developer options");

await d.scrollTo("text", "Airplane mode", { direction: "up" });   // 위쪽으로 찾기
droidline scroll_to text "Developer options"
droidline touch text "Developer options"

20번 스와이프해도 찾지 못하면(max_swipes) NOT_FOUND로 실패합니다.

팝업, 키보드, 상태 표시줄

기본적으로 dump는 앞에 있는 앱을 보여 줍니다. 팝업 대화상자는 그 앱에 속하므로 평소처럼 나타납니다. 키보드, 상태 표시줄, 안드로이드가 직접 그리는 권한 대화상자 같은 시스템 창도 보려면 모든 창을 요청하세요.

d.dump("all.json", all_windows=True)
await d.dump("all.json", { all_windows: true });
droidline dump all.json --all_windows

나타날 수도 있고 안 나타날 수도 있는 팝업은 기다리지 말고 조건 확인으로 검사하세요.

if d.exists("text", "Allow"):
    d.touch("text", "Allow")
if (await d.exists("text", "Allow")) await d.touch("text", "Allow");
[ "$(droidline exists text Allow)" = "true" ] && droidline touch text Allow

웹 페이지와 웹뷰

Chrome의 페이지와 앱 안의 웹뷰도 보통 링크, 버튼, 입력란을 요소로 드러냅니다. 보이는 텍스트는 text에 들어가고, 입력란의 레이블이 desc에 들어가기도 합니다. 다른 화면과 똑같이 다루세요. 먼저 dump를 합니다. dump에 쓸 만한 내용이 없는 웹 페이지는 캔버스에 그려진 것이니, 다음 절에서 설명하는 대로 좌표를 쓰세요.

요소가 없는 화면

게임, 동영상 플레이어, 지도, 그리고 화면을 직접 그리는 일부 앱은 요소를 전혀 드러내지 않습니다. dump에는 큰 상자 하나만 나옵니다. 이런 화면에서는 좌표와 색으로 작업하세요.

d.tap(540, 1650)                         # 한 지점을 탭(화면 픽셀 단위)
print(d.color(540, 1650))                # "#FF6B21": 그 지점의 색
d.swipe(540, 1600, 540, 400, 300)        # 한 지점에서 다른 지점으로 300ms 동안 드래그
await d.tap(540, 1650);
console.log(await d.color(540, 1650));
await d.swipe(540, 1600, 540, 400, 300);
droidline tap 540 1650
droidline color 540 1650
droidline swipe 540 1600 540 400 300

좌표는 원래 크기의 스크린샷에서 가져오세요. 이미지의 픽셀 위치가 곧 폰 화면의 픽셀입니다. color를 쓰면 탭하기 전에 버튼이 있는지 확인할 수 있습니다. 예를 들어 어떤 픽셀이 예상한 색으로 바뀔 때까지 기다리는 식입니다.

선택자가 맞지 않을 때

touch는 타임아웃이 지나면 NOT_FOUND로 실패하고, 그때 화면에 대신 무엇이 있었는지 알려 줍니다.

NOT_FOUND: Could not find text 'Log in' within 10s. Current screen: com.example / .MainActivity

다음 항목을 순서대로 확인하세요.

  1. 예상한 화면인가요? 메시지에 당시 떠 있던 앱과 화면이 나옵니다. 팝업, 광고, 느린 네트워크 때문에 다른 곳에 가 있을 수 있습니다.
  2. 텍스트가 정확한가요? 대소문자, 공백, 문장 부호까지 정확히 일치해야 합니다. 값을 직접 입력하지 말고 dump에서 복사하세요. 일부가 달라지는 텍스트라면 textContains를 쓰세요.
  3. 화면에 이미 나와 있나요? 화면 아래쪽에 있다면 scroll_to를 쓰세요. 아직 로딩 중이라면 timeout을 더 길게 넘기세요.
  4. 다른 창에 있나요? all_windows=True로 dump하세요.
  5. 화면에 요소가 있기는 한가요? dump가 큰 상자 하나뿐이라면 좌표를 쓰세요.