[Resolved] Не работает DragAndDrop на сервере

Selenium WebDriver, Junit, Ant, Java
Не работает d’n’d при запуске тестов на сервере
На локальной машине удалось выполнить d’n’d. Код следующий (пример):

        public class HTML5DD1 { 
    static WebDriver driver; 
    public static void main(String[] args) throws Exception { 
    FirefoxProfile ffProfile = new FirefoxProfile(); 
    ffProfile.setEnableNativeEvents(true); 
    ffProfile.setPreference("permissions.default.image", 2); 
    driver = new FirefoxDriver(ffProfile); 
    driver = new FirefoxDriver(); 
    Thread.sleep(3000); 
    driver.get("http://www.w3schools...draganddrop.htm"); 
    driver.manage().window().maximize();
    
    WebElement dragFrom = driver.findElement(By.id("drag1")); 
    WebElement dragTo = driver.findElement(By.id("div1")); dragAndDropElement(dragFrom, dragTo, 0, 90); 
    System.out.println("Done Dragging!");
    
    dragAndDropElement2(dragFrom, dragTo, 0, 90); 
    System.out.println("Done Dragging! 2"); 
    } 


    public static void dragAndDropElement(WebElement dragFrom, WebElement dragTo, int xOffset, int yOffset) throws Exception { 
    System.out.println("dragFrom ="+dragFrom + " dragTo = "+dragTo +"xOffset = "+xOffset +" yOffset ="+yOffset ); 
    // Setup robot 
    Robot robot = new Robot(); 
    robot.setAutoDelay(500);
    
    // Get size of elements 
    Dimension fromSize = dragFrom.getSize(); 
    Dimension toSize = dragTo.getSize();
    
    //Get centre distance 
    int xCentreFrom = fromSize.width / 2; 
    int yCentreFrom = fromSize.height / 2; 
    int xCentreTo = toSize.width / 2; 
    int yCentreTo = toSize.height / 2;
    
    Point toLocation = dragTo.getLocation(); 
    Point fromLocation = dragFrom.getLocation(); 
    System.out.println(fromLocation.toString());
    
    //Make Mouse coordinate centre of element 
    toLocation.x += xOffset + xCentreTo; 
    toLocation.y += yOffset + yCentreTo; 
    fromLocation.x += xOffset + xCentreFrom; 
    fromLocation.y += yOffset + yCentreFrom;
    
    System.out.println(fromLocation.toString());
    
    //Move mouse to drag from location 
    robot.mouseMove(fromLocation.x, fromLocation.y); 
    //Click and drag 
    robot.mousePress(InputEvent.BUTTON1_MASK); 
    robot.mouseMove(((toLocation.x - fromLocation.x) / 2) + fromLocation.x, ((toLocation.y - fromLocation.y) / 2) + fromLocation.y);
    
    //Move to final position 
    robot.mouseMove(toLocation.x, toLocation.y); 
    //Drop 
    robot.mouseRelease(InputEvent.BUTTON1_MASK); 
    } 
    }

Необходимо запустить на Jenkins. OS Debian.
Настройки браузера на Jenkins:

/usr/bin/Xvfb :1 -screen 1 1360x768x24&
export DISPLAY=:1 

Тест проваливается, в консоли выходит:

[junit]     Caused an ERROR
[junit] headless environment
[junit] java.awt.AWTException: headless environment
[junit]     at java.awt.Robot.<init>(Robot.java:94)
[junit]     at WeekCalendar.dragAndDropElement(WeekCalendar.java:318)
[junit]     at WeekCalendar.weekcalendar(WeekCalendar.java:203)

Java Robot:

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

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

@ArtOfLife awt.Robot вообще умеет с web работать?
@luisa_s Robot осознанно выбран или случайно так получилось?

Robot был выбран после долгих поисков и попыток сделать d’n’d. Ничем больше не удается.

Есть ли альтернатива Robot’у?

Технически, ему без разницы, web это или нет. Он умеет эмулировать работу мыши / клавы, но сугубо в рамках текущей JVM. Т.е. при его помощи можно спокойно взаимодействовать с ОС диалогами открытия / сохранения при file upload / download, чтобы ввести путь и кликнуть Enter, к примеру. Но это будет работать только локально. Чтобы запустить робота ремоутно, его нужно внедрить в клиент-серверную платформу и запускать на той тачке, где исполняются тесты. Ну т.е. теоретически, его можно прикрутить в selenium-standalone и дергать какой-то кастомный end-point. У того же Sikuli, его внедряли в серверную часть.

Попробуйте Sikuli. Я тут недавно постил тему с RESTful SikuliX. Эту клиент-серверную платформу можно использовать для вызова нативного сикулишного drag&drop (SikuliX API уже внедрены в серверную часть). Но придется дописать соответствующую обертку. Ну или можете внедрить того же робота в качестве доп. функционала. В любом случае вам придется поднимать доп. клиент-сервер или редактировать существующий для вызова кода на той тачке, где запускаются сами тесты. Кстати у нас на проекте мы как раз юзаем сикулишный d&d (тянем файл с десктопа в браузер).

П.С. А нативный селениумовский d&d не работает? Ну или можно JSовский попробовать - JS executor via RemoteWebDriver API.

Спасибо!
А есть ли какая-нибудь инструкция по внедрению робота в клиент-серверную платформу?

Нативный селениумовский не работает совсем (скорее всего дело в HTML5), а с помощью JS некорректное перемещение получалось.

В случае с HTML 5 - действительно нативный не будет работать. Чуть позже отпишу по поводу интеграции Robot’а в серверную часть.

Документации никакой нет. С вашим кодом в идеале надо модифицировать selenium-server-standalone, копаться в исходниках, искать серверный код и добавлять свои эндпоинты. При этом ваш код можно оформить в виде утилити класса. Сколько времени на это уйдет - не имею понятия, ибо надо хорошо знать структуру проекта.

А вот по структуре SikuliX клиент-сервера могу подсказать.

  • Собственно, как я уже выше намекнул, код нужно поместить в отдельный
    утилити класс на сервере, по примеру одного из уже имеющихся (utils package).
  • WebDriver / WebElement вам не пригодятся, исходя из того, что вы оттуда тянете
    лишь x,y координаты элемента. Плюс ко всему, нам не нужны лишние тяжеловесные
    библиотеки в dependencies.
  • После создания утилити класса с необходимым нам методом, нужно
    добавить end-point, с которым может взаимодействовать клиент. Примеры таких end-points можно найти в service package.
  • В самом примитивном случае можете создать POST обработчик с 4 query
    param
    : x1, y1, x2, y2. Или создать 2 Point (не селениумовских) объекта и передавать их в качестве списка в post entity (клиентская часть).
  • Если end-point будет в новом классе, нужно добавить его в
    ServerConfiguration.java
  • Как собрать сервер, описано в READMDE.md.
  • Когда сервер готов, теперь нужно написать клиентские REST API, примеров которых предостаточно в Client.java
  • С синтаксисом Jersey, можно ознакомиться тут.
  • Клиент собирается точно так же, как и сервер.
  • В принципе, вы можете скипнуть сикули и прочие утилиты, а оставить лишь ваш код на девственной серверной платформе. Аналогично и с клиентом.
1 лайк

Спасибо большое!
Было принято решение настроить CI таким образом, чтобы при выполнении тестов на машине была сессия с desktop.