Appium - Гибридное приложение - не открывается нативный Date Picker

Ребята, мне очень не по себе создавать уже третью тему… Но изучать Appium - это что-то…

Автоматизирую гибридное приложение. Пока что использую андроид эмулятор. И все бы неплохо, да попытка вызвать нативный Date Picker проваливается с треском.
Ни в какую он вызываться не хочет.

Кликаю по div, к которому привязано его появление - ничего не происходит.
Пытаюсь делать тач - тоже ничего не происходит.
Пытаться сделать sendKeys() бесполезно - exception “cannot focus element”, что логично.

Нажимаешь ручками/мышью в эмуляторе - все прекрасно, пикер появляется без вопросов.

Что происходит?
Спасибо за неравнодушие!

1 лайк

А вы на WEBVIEW переключаетесь?

Да, конечно. Я работаю на WEBVIEW и все кнопки, текстовые поля, линки прекрасно на клики реагируют. А вот вызов date пикера - нет.
Если я правильно понимаю, после появления нативного дата пикера мне надо будет переключиться на NATIVE вью и поработать с ним… но вот не появляется он ни в какую.

Наши ребята пишут приложение используя React.js и этот плагин GitHub - zilverline/react-tap-event-plugin: Instant TapEvents for React для вызова пикера, может он какой-то особенный…

Попробуйте сделать тач в NATIVE_VIEW по координатам элемента который должен вызвать пикер

1 лайк

Тапы работают на нативных элементах, в WebView это не проработано. Надо переключаться на нативный контекст, делать тап, и потом обратно.

Учтите, что координаты веб-элементов могут не совпадать с экранными координатами (не только из-за смещения, но и по масштабу, в вебе своя метрика используется), т.е. придется писать трансформацию.

1 лайк

Спасибо за совет! А можно немного подробнее про трансформацию, где про это почитать можно? Пока что тап по координатам элемента ничего не дает, видно, не попадаю.

Делаю так:

    WebElement element = driver.findElement(By.xpath(AboutMeSelectors.DUE_DATE_INPUT_MOBILE));
    int x = element.getLocation().getX() + element.getSize().getWidth()/2;
    int y = element.getLocation().getY() + element.getSize().getHeight()/2;
    System.out.println(x + " " + y);
    Set<String> contextNames = driver.getContextHandles();
    driver.context(contextNames.toArray()[0].toString());
    System.out.println(driver.getContext()); // для проверки
    driver.tap(1, x, y, 300);

Аппиум пишет вполне корректные логи:

info: <-- GET /wd/hub/session/c278f508-57d9-4aa2-b819-6dfa918b023a/context 200 0.855 ms - 84 {"status":0,"value":"NATIVE_APP","sessionId":"c278f508-57d9-4aa2-b819-6dfa918b023a"}

info: --> POST /wd/hub/session/c278f508-57d9-4aa2-b819-6dfa918b023a/touch/perform {“actions”:[{“action”:“press”,“options”:{“x”:254,“y”:146}},{“action”:“wait”,“options”:{“ms”:300}},{“action”:“release”,“options”:{}}]}
info: [debug] Pushing command to appium work queue: [“element:touchDown”,{“x”:254,“y”:146}]
info: [debug] [BOOTSTRAP] [debug] Got data from client: {“cmd”:“action”,“action”:“element:touchDown”,“params”:{“x”:254,“y”:146}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: touchDown
info: [debug] [BOOTSTRAP] [debug] Display bounds: [0,0][320,480]
info: [debug] [BOOTSTRAP] [debug] Performing TouchDown using element? false x: 254, y: 146
info: [debug] [BOOTSTRAP] [debug] Returning result: {“value”:true,“status”:0}
info: [debug] Pushing command to appium work queue: [“element:touchUp”,{“x”:254,“y”:146}]
info: [debug] [BOOTSTRAP] [debug] Got data from client: {“cmd”:“action”,“action”:“element:touchUp”,“params”:{“x”:254,“y”:146}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: touchUp
info: [debug] [BOOTSTRAP] [debug] Display bounds: [0,0][320,480]
info: [debug] [BOOTSTRAP] [debug] Performing TouchUp using element? false x: 254, y: 146
info: [debug] Responding to client with success: {“status”:0,“value”:true,“sessionId”:“c278f508-57d9-4aa2-b819-6dfa918b023a”}
info: <-- POST /wd/hub/session/c278f508-57d9-4aa2-b819-6dfa918b023a/touch/perform 200 357.618 ms - 76 {“status”:0,“value”:true,“sessionId”:“c278f508-57d9-4aa2-b819-6dfa918b023a”}
info: [debug] [BOOTSTRAP] [debug] Returning result: {“value”:true,“status”:0}

Если прикидывать по координатам экрана - то элемент как раз и располагается в области 254 х 146, на экране 320 х 480. Даже линейкой меряла…
Но пикера как не было, так и нет(

Пикер появился!)

Сейчас опишу для новичков, как я, что надо сделать, чтобы его поймать:

  1. Надо знать параметры монитора девайса (у меня было 320 * 480);
  2. Где-нибудь из инспектора хрома добыть параметры вьюшки, на которой отображается наше приложение
    У меня было: div.Screen : clientHeight: 455, clientWidth: 320

Как видно, есть смещение в 25 пикселей, которое занято не приложением, чем-то другим (в моем случае - верхней панелью управления).

Потом надо добыть координаты центра нашего веб-элемента, как у меня в коде выше.
И наконец, скорректировать их учитывая это смещение.

У меня:

int y = element.getLocation().getY() + element.getSize().getHeight()/2 + 25;

И пикер появляется как миленький.

Теперь надо подумать, как это сделать, чтобы все высчитывалось автоматически для разных девайсов. Но это уже технические детали.

Спасибо огромнейшее всем неравнодушным!

Буду признательна за совет, в чем лучше смотреть нативные локаторы для Андроида на Windows. А то прекрасного мака с прекрасным инспектором Aппиума не предвидится, увы.

1 лайк

Лично для себя ничего прекрасного в маке и инспекторе аппиума не обнаружил:)
В плане координат, я делаю немного по другому. Просто снимаю дамп hierarchy view, подходит для инспектирования элементов( в принципе тоже самое делает и аппиум), но дамп делается только нативных элементов, но все что есть с HTML помещается в контейнер класса android.webkit.WebView и таким способом можно достучаться почти до любого элемента через UIAutomator.
Т.е. не возникает головной боли с трансформацией. Но в моем случае я это все делаю на реальном девайсе и в нативном приложении.

Буду признательна за совет, в чем лучше смотреть нативные локаторы для Андроида на Windows. А то прекрасного мака с прекрасным инспектором Aппиума не предвидится, увы.

Я использую утилитку monitor, которая входит в android sdk

1 лайк

А если не секрет, как снимаете дамп, и не сложно ли его потом парсить?

Необязательно знать заранее.
В контексте WebView получаем самый “большой” элемент:

WebElement body = driver.findElement(By.xpath("//body"));

В нативном контексте получаем сам WebView:

WebElement webView = driver.findElement(By.xpath("//android.webkit.WebView"));

Снимаем их размер и положение - это даст и масштаб, и смещение, подходящие для всех элементов.

Оба эти подхода дадут размер веб вью, а не экрана девайса, верно?

Да, но это даст больше информации, чем размер экрана.
Во-первых, getLocation() даст смещение.
Во-вторых, getSize() даст размер элемента в разных контекстах.
Я, например, столкнулся с тем, что в вебе координаты совсем не равны нативным пикселям, и через getSize как раз ловлю пропорцию.

Алгоритм не привожу, он у меня разнесен по методам, но в целом так:
Scale_x = W_native(WebView) / W_web (body)
Scale_y = H_native(WebView) / H_web (body)
W_native(element) = W_web(element) * Scale_x, аналогично для H
X_native(element) = X_native(WebView) + X_web(element) * Scale_x, аналогично для Y
W - ширина, H - высота, X и Y - положение

Если у Вас масштабирования нет - повезло :smile:

1 лайк

С помощью утилитки monitor, которая входит в android sdk
При стандартных настройках это выглядит примерно так
картинка

т.е. вы получаете скриншот вашего экрана + иерархичный вид этой страницы с описанием всех элементов, если разработчики удосужились их описать)

2 лайка

Да как раз таки и есть… Только что выяснила, что на девайсе размером 480 на 800, element.getLocation(), getWidth() и getHeight() дают те же цифры, что на экране 320 х 480. Совершенно неправдободобные цифры, на которые невозможно опираться в вычислениях. Спасибо Вам, сейчас разберу Ваш метод и буду пользоваться.

Спасибо, с dump уже познакомилась, и даже попробовала успешно поработать с элементами. Вот только нативный всплывающий на несколько секунд и плавно исчезающий pop up не видно там в структуре. Интересно, его вообще можно зацапать, или не судьба.

Он делает дамп только то что видит на экране.
Соответственно ловить этот попап можно только удачным стечением обстоятельств и ловкостью рук:)
Нажимаете кнопку сделать дамп за секйнду-две до того, как вызовете пикер

А я его словила, на скришноте он был, а вот в структуре его не было)
Может он и не нативный вовсе…
Вот, самое верхнее из того, что он видит - это дата пикер, никаких поп апов

А что за попап, в какой момент всплывает до того как пикер появится, после, во время?
Вообще у вас тема была про пикер, а тут уже попап пошел :smile:

И уточните у разработки, если этот попап - это toas message, то ничего вы с ним не сможете сделать.

Да, вы правы) Увлеклась. Это поп ап, который предупреждает пользователя о том, что дата, которую он пытается установить, некорректна. Скорее всего это toast message и есть, очень похож. Не доросла еще автоматизаторская наука до их обработки)