README.md

FlaNium.WinAPI

Библиотека расширяющая протокол Selenium REST API и добавляющая дополнительные возможности по настройке и взаимодействию с FlaNium драйвером. Также данная библиотека позволяет типизировать стандартный Selenium WebElement и привести его к компонентам тестируемого приложения, тем самым добавить дополнительные методы взаимодействия характерные определенному типу элемента.

Новое в версии 2.2.0
  • Исправлены ошибки перехода на стандарт w3c.
  • Добавлена поддержка системных переменных в параметре пути запускаемого приложения (applicationPath).
  • Добавлены методы удаления/проверки наличия файлов через драйвер.
  • Добавлена возможность инициализация сессии драйвера без запуска приложения.
  • Добавлена возможность ручного запуска приложения после создания сессии.
Новое в версии 2.1.0
  • Добавлена поддержка selenium 4.10 (w3c standard)
Новое в версии 2.0.0
  • Расширен протокол FlaNium драйвера - добавлено несколько новых методов.
  • Произведен рефакторинг кода библиотеки для упрощения взаимодействия и более быстрого погружения.
  • Упрощен механизм настройки и запуска драйвера.
  • Добавлена поддержка Selenium 4.
  • Изменена логика запуска приложения и подключения к процессу.

Основные моменты

Работу с драйвером можно разбить на 4 основных этапа:

  1. Подключение к драйверу:

    1.1. Подключение к удаленному или локальному ранее запущенному экземпляру драйвера по :

    1.2. Запуск локального экземпляра драйвера и подключение к нему

  2. Инициализация новой сессии:

    2.1. С запуском нового процесса тестируемого приложения

    2.2. Подключением к ранее запущенному процессу

  3. Поиск необходимого элемента для выполнения взаимодействия

  4. Взаимодействие с найденным элементом или получение каких-либо данных

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);

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

Конвейеры
0 успешных
0 с ошибкой