Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

[Resolved] Как взаимодействовать с JS-календарем


(Александр Шиповалов) #1

Добрый день коллеги. Возникла следующая заморочка. Есть календарь очень похожий на
календарик тыц
На входе есть дата вида 02/16/2014 в американском формате - первым идет месяц. Так вот суть проблемы, не получается с помощью WebDriver нормально кликнуть по нужной дате. Распарсить дату, выбрать месяц - это да это несложно и решаемо. А вот именно кликнуть по нужной дате и проверить, что появилось то количество выделенных дат - которое задано в Select How many nights? - не получается. Думаю, что задача может решиться с помощью JS. Но вот вопрос - насколько это валидно и увы, я практически не владею JS - поэтому и применять его бы не хотелось.


(Sergey Korol) #2

Задача очень простая. Не понимаю даже, где возникла сложность и зачем вам JS. Быстрый набросок кода:

final int selectedDays = 4;

driver.get("http://www.classicvacations.com/hotels/caribbean/turks-caicos/amanyara");
new Select(driver.findElement(By.id("num_nights"))).selectByIndex(selectedDays);
driver.findElement(By.id("day_25")).click();

Assert.assertEquals(
    driver.findElements(By.xpath("//div[contains(@class, 'selected')]")).size(), 
    selectedDays);

Причем, у календаря все ячейки пронумерованы через id, т.е. вам не составит никакого труда формировать локатор динамически, подставляя соответствующий индекс.


(heartwilltell) #3

Если вам нужно именно пролистать календарь до нужного года месяца - тоже не вижу в этом никакой проблемы, и надобности применять JS. Ну а как кликнуть выделить ячейки - сказано постом выше


(Александр Шиповалов) #4

Вот это мне и не совсем понятно. Как определить нужный id ячейки - ведь 25 ячейка, не всегда 25 число. Если только искать по присутствию текста. Но ведь у меня в одном календаре может быть несколько первых и вторых и третьих чисел.


(vmaximv) #5

Конкретно в этом календаре “кликабельные” дни имеет класс ‘vcell cursorstyle’ и вполне адекватный onclick с датой.


(sidelnikovmike) #6

Но у них разные id, если посмотреть. То есть можно отделить предыдущий месяц от текущего.


(Александр Шиповалов) #7

Попробую для себя сформулировать как я понял советы. Разбирает дату на строку и месяц. С помощью какой нибудь Map - переводим цифру месяца в строку и ищем ее. Дальше, получается список элементов календаря с классом vcell cursorstyle (например) - и через for-each ищем соответствие атрибута ячейки - входной дате.


(sidelnikovmike) #8

Получается так:
1)разбираем дату
2)выбираем месяц(ведь он тоже может быть не текущий)
3)ищем среди vcell cursorstyle нужную дату и жмем. Для каждого месяца выбрать можно только числа именно его месяца, так что тут и отсекать не придется возможно ничего.
4)проверяем, что следующие даты выделены.
Вообщем вы суть правильно уловили :smile:


(Sergey Korol) #9

А зачем переводить цифры месяца во что-либо? Как уже сказал выше @vmaximv, td.cursorstyle содержит

onclick="checkInDateClicked('25', '12/24/2014', '12/26/2014');"

где первый параметр - индекс (который присутствует в id). Второй / третий - рендж (текущая дата ячейки + дата со смещением в кол-во дней, что вы выбрали в селекторе). Т.е. задав expected date в формате MM/dd/yyyy, вы сможете кликать по элементу в зависимости от содержимого onclick. Тут вам сразу будет и проверка на то, что нужная вам дата кликабельна. Как проверить кол-во выделенных ячеек - я написал выше. А конкретные даты опять-таки вытягиваются из onclick + сравнение с expected диапазоном. Хотя, если у вас будут неверные данные в onclick, это отразиться на результатах. Т.е. в принципе можно и перестраховаться, анализируя отображаемое значение. Зависит от того, что вы тестируете - сам календарь, или функционал брони.

П.С. Манипуляции с датами лучше всего осуществлять при помощи библиотеки Joda Time.


(sidelnikovmike) #10

А , и точно. Не обратил внимание на ответ от @vmaximv.
Тогда задача упрощается до нельзя :smile:

ПС: если нужно просто распарсить такую дату - есть стандартный класс DateFormat, там дата парсится в 2 строчки. А подключение JodaTime - это лишние зависимости в проекте. Хотя ничего против этой библиотеки не имею.


(Александр Шиповалов) #11

Много слышал про JodaTime, но случая не было попробовать. И судя по вышеприведенным ответам и не придется пока.


(Александр Шиповалов) #12

А вот здесь уже не совсем понятно. Как с помощью WebDriver вытащить данные из события OnClick


(sidelnikovmike) #13

element.getAttribute(“onClick”)


(Александр Шиповалов) #14

Спасибо огромное.


(Александр Шиповалов) #15

Попробовал так.

@FindBy(id = "rate_calendar")
private WebElement calendarWidget;

private void setDateOnCalendar(String checkIn) {
    List<WebElement> ActivecalendarCells = calendarWidget.findElements(By.cssSelector(".cursorstyle"));
    List <String> sn = null;
    for(WebElement e : ActivecalendarCells){
        String s = e.getAttribute("onClick");
        sn.add(s);
    }
}

Выдает исключение:

org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document

На строчке:

String s = e.getAttribute("onClick");

(Sergey Korol) #16

Во-первых, просто .cursorstyle выдает чуть больше элементов, чем нужно. :wink: Я ведь не зря писал:

Во-вторых, ваш календарик очень тормознутый в принципе. Т.е. ему нужно некоторое время, чтобы понять, что вообще вокруг происходит. Вывод, я думаю, сможете сделать сами. :wink:


(Александр Шиповалов) #17

Я работаю не совсем ним) Но вообще ошибка была в другом. Сейчас метод выглядит так

assertThat(calendarWidget, should(isDisplayed()).whileWaitingUntil(timeoutHasExpired(TimeUnit.SECONDS.toMillis(30))));
int n = ActivecalendarCells.size();
String[] sn = new String[n];
for (int i = 0; i <  n; i++) {
    WebElement e = ActivecalendarCells.get(i);
    String s = e.getAttribute("onclick");
    sn[i] = s;
}

Вроде бы все отрабатывает корректно. Хотя первая строчка подхватывается неверно

getHotelRateCalendar('1')

Но это не великая беда. А вот как разобрать такое выражение

checkInDateClicked('27', '12/26/2014', '01/04/2015');

Но это уже я так понимаю, решается с помощью reg exp


(barancev) #18

Возможно, не совсем именно про такой календарик, но: