FlaNium.WinAPI
Библиотека расширяющая протокол Selenium REST API
и добавляющая дополнительные возможности по настройке и взаимодействию с FlaNium драйвером
. Также данная библиотека позволяет типизировать стандартный Selenium WebElement
и привести его к компонентам тестируемого приложения, тем самым добавить дополнительные методы взаимодействия характерные определенному типу элемента.
- Исправлены ошибки перехода на стандарт w3c.
- Добавлена поддержка системных переменных в параметре пути запускаемого приложения (applicationPath).
- Добавлены методы удаления/проверки наличия файлов через драйвер.
- Добавлена возможность инициализация сессии драйвера без запуска приложения.
- Добавлена возможность ручного запуска приложения после создания сессии.
- Добавлена поддержка selenium 4.10 (w3c standard)
- Расширен протокол FlaNium драйвера - добавлено несколько новых методов.
- Произведен рефакторинг кода библиотеки для упрощения взаимодействия и более быстрого погружения.
- Упрощен механизм настройки и запуска драйвера.
- Добавлена поддержка Selenium 4.
-
Изменена логика запуска приложения и подключения к процессу.
Основные моменты
Работу с драйвером можно разбить на 4 основных этапа:
-
Подключение к драйверу:
1.1. Подключение к удаленному или локальному ранее запущенному экземпляру драйвера по :
1.2. Запуск локального экземпляра драйвера и подключение к нему
-
Инициализация новой сессии:
2.1. С запуском нового процесса тестируемого приложения
2.2. Подключением к ранее запущенному процессу
-
Поиск необходимого элемента для выполнения взаимодействия
-
Взаимодействие с найденным элементом или получение каких-либо данных
1. Настройка и инициализация драйвера
pom.xml
...
<dependency>
<groupId>com.github.lanit-exp</groupId>
<artifactId>FlaNium.WinAPI</artifactId>
<version>2.2.0</version>
</dependency>
...
Первое, что необходимо сделать, это создать экземпляр класса FlaNiumDriver
Выполнить это можно 2 способами:
// Используя ранее запущенный экземпляр драйвера:
FlaNiumDriver driver = new FlaNiumDriver(URL remoteAddress, FlaNiumOptions options);
// Или выполнить инициализацию и запуск нового экземпляра драйвера
FlaNiumDriver driver = new FlaNiumDriver(FlaNiumDriverService service, FlaNiumOptions options);
// где URL remoteAddress - адрес запущенного экземпляра FlaNium драйвера
// FlaNiumOptions options - параметры инициализации и запуска тестируемого процесса приложения
// FlaNiumDriverService service - параметры инициализации и запуска экземпляра драйвера
FlaNiumDriverService
FlaNiumDriverService
- Класс используемый для настройки и запуска экземпляра драйвера.
Пример кода:
String DRIVER_PATH = "src/main/resources/driver/FlaNium.Desktop.Driver/FlaNium.Driver.exe";
String LOG_PATH = "logs/log.txt";
int driverPort = 7885;
FlaNiumDriverService service = new FlaNiumDriverService.Builder()
// Указание пути до драйвера
.usingDriverExecutable(new File(DRIVER_PATH).getAbsoluteFile())
// Установка порта (если не указывать - то по умолчанию 9999; если 0 - то используется случайный свободный)
.usingPort(driverPort)
// Включение режима отладки (true - вывод логов в консоль; по умолчанию false - логи не выводятся)
.withVerbose(true)
// Отключение логирования (по умолчанию false)
.withSilent(false)
// Тайм-аут ожидания запуска сервера драйвера (по умолчанию 20 сек)
.withTimeout(Duration.ofSeconds(20))
// Путь файла логирования драйвера
.withLogFile(new File(LOG_PATH).getAbsoluteFile())
.build();
p.s.
usingDriverExecutable()
- Является единственным обязательным параметром, остальные можно использовать по умолчанию.
usingPort()
- Если не указывать то используется порт 9999; если указать порт 0, то будет использоваться каждый раз случайный свободный порт.
withVerbose()
- Для удобства отладки желательно указывать true.
FlaNiumOptions -> DesktopOptions
DesktopOptions
- Класс используемый для инициализации и запуска процесса тестируемого приложения.
Пример кода:
DesktopOptions options = new DesktopOptions()
// Путь до тестируемого приложения
.setApplicationPath(String appPath)
// Аргументы командной строки используемые при запуске приложения
.setArguments(String args)
// Подключение к ранее запущенному процессу приложения
.setConnectToRunningApp(Boolean connectToRunningApp)
// Ожидание в мс на запуск приложения (по умолчанию 2 сек)
.setLaunchDelay(Integer launchDelay)
// Подключение к процессу по имени
.setProcessName(String processName)
// Время поиска процесса в мс (по умолчанию 30 сек)
.setProcessFindTimeOut(Integer processFindTimeOut)
// Тайм-аут ответа драйвера в мс (по умолчанию 120 сек)
.setResponseTimeout(Integer responceTimeout)
// *Опционально, требуется библиотека для инжекта:
// Использование инжекта
.setInjectionActivate(Boolean injectionActivate)
// Выбор библиотеки для инжекта
.setInjectionDllType(String injectionDllType);
p.s.
setApplicationPath()
- Запускает исполняемый файл по указанному пути. Имеется возможность указать системные переменные, например:
<LOCALAPPDATA>/TestApp/test.exe
setArguments()
- Опциональный параметр, при необходимости указываются дополнительные параметры командной строки при запуске тестируемого приложения.
setConnectToRunningApp()
- Если false (по умолчанию) - всегда запускается новый процесс приложения (с закрытием текущего, если имеется). Если true и приложение не запущено, то запускает приложение. Если true и приложение запущено - то просто использует текущее состояние приложения (для подключения использует параметр processName, если не указан, то значение из applicationPath). Так же если true - то приложение не закрывается по завершении сессии.
setLaunchDelay()
- Статическое ожидание в мс между запуском приложения и дальнейшими действиями (такими как поиск и инициализация процесса или выполнение других запросов). По умолчанию 2 сек.
setProcessName()
- Если запускаемое приложение порождает несколько процессов или процесс в момент запуска сменяется другим (например запуск java приложений), то можно указать имя процесса к которому необходимо цепляться при запуске драйвера. Так же необходимо указать время ожидания setLaunchDelay()
и setProcessFindTimeOut()
.
setProcessFindTimeOut()
- Время в мс в течении которого будет происходить поиск процесса используя параметр из метода setProcessName()
. По умолчанию 30 сек.
setResponseTimeout()
- Поскольку FlaNium драйвер взаимодействует с внешними библиотеками WinAPI, не редки случаи зависания или долгого выполнения запросов. Для этого был добавлен механизм прерывания запроса по таймауту (по умолчанию 120 сек).
setInjectionActivate()
и setInjectionDllType()
- Используются при взаимодействии с приложением через технологию инжекта. Требуется dll и библиотека расширения - в открытом доступе пока нет!
Начиная с версии 2.0.0 при запуске и инициализации приложения (а так же при присоединении к процессу из параметра ProcessName) не происходит поиск и регистрация окна процесса. Поиск окна происходит при первом обращении к методам выполняющим поиск элементов или другие действия задействующие объект окна.
Начиная с версии 2.2.0 applicationPath является не обязательным параметром. Имеется возможность подключения к драйверу без запуска приложения.
2. Работа с драйвером
Поиск
После получения экземпляра FlaNiumDriver
можно осуществлять поиск контролов приложения и осуществлять взаимодействие с ними через стандартные методы библиотеки Selenium.
WebElement edit = driver.findElement(By.xpath("//*[(@ControlType = 'Edit') and contains(@Name,'Text')]"));
edit.sendKeys("Test text");
При использовании Selenide
необходимо установить полученный ранее экземпляр FlaNium драйвера в качестве текущего драйвера:
WebDriverRunner.setWebDriver(flaniumDriver);
// Затем можно использовать стандартный механизм поиска (вызов метода open() - не требуется)
SelenideElement edit = Selenide.$x("//*[(@ControlType = 'Edit') and contains(@Name,'Text')]");
Есть возможность поиска элементов по xpath
, Name
, Id (AutomationId)
и ClassName
, а также поддерживаются 5 параметров поиска с помощью xpath – AutomationId, Name, ClassName, HelpText, ControlType
.
driver.findElement(By.xpath("//*[(@AutomationId = '') or (@Name = '') or (@ClassName = '') or (@HelpText = '') or (@ControlType = '')]"));
driver.findElement(By.name("Checkbox1"));
driver.findElement(By.id("Form1"));
driver.findElement(By.className("MenuItem"));
driver.findElements(By.xpath("#//*");
// символ '#' в начале Xpath - позволяет искать элементы относительно Рабочего стола(элементы на всех открытых окнах), а не текущего окна.
Для более комфортной работы рекомендую воспользоваться такими инструментами как FlaUInspect
, UISpy
и подобными, так как они значительно упрощают написание тестов приложения, позволяя визуально отобразить структуру и параметры элементов приложения, а также позволяют понять, как можно обратиться к тому или иному элементу или какие паттерны поддерживает тот или иной элемент.
Расширение возможностей Selenium протокола
ВАЖНО! Начиная с версии FlaNium.WinAPI-2.1 и с переходом селениума на W3C протокол, часть методов стандартного класса WebElement работает НЕКОРРЕКТНО,
поэтому рекомендуется отказаться от работы со стандартным WebElement и WebDriver и использовать экземпляры классов DesktopElement и FlaNiumDriver.DesktopElement и FlaNiumDriver - два основных класса при работе с FlaNium драйвером.
Начиная с версии 2.0.0 появилась возможность приводить WebElement непосредственно к DesktopElement
- основному узлу по взаимодействию с элементами.
Теперь,для упрощения поиска, основные методы класса разбиты на группы:
Actions:
mouseActions()
- позволяет получить доступ к методам взаимодействия на основе мыши (отдельная реализация, не относится к протоколу Selenium). Например: dragAndDrop, mouseMove, mouseClick и др.
touchActions()
- методы использующие API сенсорного ввода windows. Также поддерживает multitouch. Например: tap, pinch, rotate и др.
screenshotActions()
- методы по работе со скриншотами.
Cast:
castTo()
- позволяет привести DesktopElement
к типизированному элементу.
WebElement edit = driver.findElement(By.xpath("//*[(@ControlType = 'Edit') and contains(@Name,'Text')]"));
DesktopElement editDesktopElement = new DesktopElement(edit);
TextBox editTextBox = editDesktopElement.castTo().toTextBox();
editTextBox.setText("Test text2");
toCoordinateElement()
- приведение WebElement к “виртуальному” объекту класса CoordinateElement.
Аналогичная группировка выполнена для методов FlaNiumDriver класса.
3. RootElement и работа с процессами
Было:
Изначально механизм старта и закрытия новой сессии выглядел так:
- Запуск приложения по указанному пути и подключение к процессу с поиском главного окна.
- Статическое ожидание (если указано).
- Подключение к другому процессу и поиск главного окна (если было указано имя процесса).
- Далее осуществлялась работа с использованием главного окна процесса в качестве корневого элемента при поиске элементов.
- Закрытие процесса приложения (только по которому осуществлялся запуск или к которому подцепились позже).
Данный алгоритм в некоторых задачах оказался недостаточно гибким, поэтому был полностью переработан.
Стало:
Теперь алгоритм старта новой сессии выглядит так:
- Запуск приложения по указанному пути и подключение к процессу. Поиск и инициализация окон приложения не происходит.
- Статическое ожидание. Теперь по умолчанию 2 сек.
- Если указано имя процесса, то подключение к нему с возможностью указания динамического ожидания. Так же НЕ происходит поиск окон процесса.
- (Опционально) инжект исполняемой dll в процесс приложения
RootElement ?
RootElement - это элемент выступающий в качестве корневого при поиске элементов и выполнении других действия требующих базового элемента.
По умолчанию в качестве корневого элемента выступает главное окно процесса, первый поиск окна осуществляется только при первой необходимости.
Теперь корневой элемент можно менять, для этого есть 4 метода:
FlaNiumDriver driver;
DesktopElement element;
// Устанавливает в качестве корневого элемента Рабочий стол, таким образом при поиске видны все Окна и все элементы всех процессов.
driver.setDesktopAsRootElement();
// Возвращает в качестве корневого элемента главное окно подключенного процесса.
// Поиск окна осуществляется при первой необходимости (сразу не происходит).
driver.resetRootElement();
// Так же в качестве корневого можно установить любой другой элемент.
driver.setRootElement(RemoteWebElement webElement);
// или
element.setAsRootElement();
Processes
Так же, помимо изменения корневого элемента в ходе работы с драйвером, добавлена возможность динамического изменения активного процесса.
FlaNiumDriver driver;
driver.changeProcess(String processName, int timeOut);
При смене процесса происходит:
- Сброс корневого элемента. Поиск главного окна нового процесса и установка в качестве корневого элемента происходит при первой необходимости.
- Закрытие нового процесса по завершению сессии.
Добавлено ручное закрытие всех процессов по имени:
FlaNiumDriver driver;
driver.killAllProcessesByName(String processName);
4. Упрощенная инициализация
Получить экземпляр драйвера и начать работать можно вызвав лишь один метод:
FlaNiumDriver driver = FlaNium.initDriver();
На этом всё! Больше ни чего в коде указывать не надо. После вызова метода произойдет инициализация и запуск драйвера и инициализация и запуск приложения.
Но чтобы FlaNium понял что где и как запускать, в корень проекта в папку ресурсов нужно положить 2 файла: flanium_driver.properties
и flanium_app.properties
.
Синтаксис и примеры файлов:
flanium_driver.properties
flanium.driver.remote=true
flanium.driver.remoteUrl=http://192.168.100.10:9999
flanium.driver.exe=src/main/resources/driver/FlaNium.Desktop.Driver/FlaNium.Driver.exe
flanium.driver.port=0
flanium.driver.verbose=true
flanium.driver.silent=false
flanium.driver.timeout=20
#flanium.driver.logFile=
flanium_app.properties
flanium.app.path=src/main/resources/apps/Application.exe
#flanium.app.args=
flanium.app.connectToRunningApp=false
flanium.app.launchDelay=5
flanium.app.processFindTimeOut=30
flanium.app.processName=Application
flanium.app.injectionActivate=false
#flanium.app.injectionDllType=
#flanium.app.responseTimeout=
Описание всех параметров есть выше, добавлю только основные моменты:
- Параметры
flanium.app.path
и (flanium.driver.remoteUrl
илиflanium.driver.exe
) - обязательны. flanium.driver.remote
по умолчанию false.- Все временные параметры указываются в секундах.
- Любой из параметров можно установить или изменить через код используя метод
System.setProperty("flanium.driver.remote", "true");
или извне через-Dflanium.driver.remote=true
. - Если требуется работа с несколькими конфигами приложений, то можно использовать метод принимающий относительный путь до файла properties:
FlaNiumDriver driver = FlaNium.initDriver("apps/app1.properties");
- Файл конфигурации драйвера может быть только один.
5. Ручной запуск приложения
Начиная с версии 2.2.0 есть возможность инициализации драйвера без запуска приложения, т.е. параметр applicationPath теперь необязательный. Это необходимо, например, когда перед запуском приложения требуется выполнить какие-то подготовительные операции (удаление файлов сессии и т.п.).
Для отключения автостарта приложения необходимо:
- Оставить параметр applicationPath пустым при создании экземпляра класса DesktopOptions. При этом остальные заполненные параметры будут игнорироваться.
- Или использовать метод FlaNium.initDriverWithoutStartApp() при использовании упрощенной инициализации. В качестве параметра может принимать параметр таймаута ответа. Значения файла flanium_app.properties игнорируются, если он есть.
Для ручного запуска приложения используется метод startApp(String appPath, String appArguments, Integer launchDelayMs):
FlaNiumDriver driver;
driver.startApp("C:/App/launch.exe", null, 10_000);
//or
driver.startApp("<LOCALAPPDATA>/TestApp/test.exe", "-d", 50_000);
driver.changeProcess("test", 30_000);
Пока не произведен запуск приложения, в качестве корневого элемента используется рабочий стол
.