Добрый вечер!
Голубиной почтой нам доставили Selenide 7.19.0!
- Сохранение и восстановление состояния браузера
- Эмуляция времени и часового пояса
- Условия
visibleTextиexactVisibleText - HtmlUnit вернулся
- Capabilities браузера в MCP и CLI
- Старые временные файлы больше не ломают скачивание
- Уникальные имена файлов для скриншотов и логов
- Appium:
scroll()иswipe()внутри элемента - Appium: цепочечные методы возвращают
SelenideAppiumElement - Appium: свой класс элемента в
SelenideAppiumCollection - Обновили Appium java-client с 10.1.1 до 11.0.0
- Обновили Selenium с 4.49.0 до 4.51.0
Сохранение и восстановление состояния браузера
Многие тесты начинаются с логина. А логин занимает время. Ну знаете, открыть логин-форму, ввести пароль, получить как-то email или SMS и т.д.
Самое лучшее решение - это, конечно, сделать способ быстрого логина без лишних телодвижений. Об этом в былые времена я рассказывал на моём любимом Selenium Camp, например.
Увы, оно доступно не всем. Поэтому для массового автоматизатора теперь есть способ попроще: один раз залогиниться и сохранить состояние браузера в файл, а в каждом последующем тесте его восстанавливать.
Мы постарались сделать в Selenide таксимально короткий API для этого:
open("/dashboard", withBrowserState("build/auth.json")
.loggedInIf(() -> $("#logout").is(visible, Duration.ofSeconds(2)))
.orLogin(() -> login("bob", "secret")));
Как это работает:
- Если файл
build/auth.jsonесть, Selenide восстанавливает из него куки,localStorageиsessionStorage, открывает страницу и проверяет условиеloggedInIf. - Если файла нет или проверка не прошла (протухла кука, перезапустился сервер, отозвали сессию), Selenide
чистит старое состояние, вызывает
login, сохраняет новое состояние в файл и снова открывает страницу. И ваш браузер готов к работе. - Если проверка не прошла даже сразу после логина, вы получите понятную ошибку (а не бесконечный цикл).
Работает и в параллельных тестах: логинится только один поток, остальные переиспользуют его состояние.
Если логика “залогиниться при необходимости” не нужна, можно просто восстановить состояние:
open("/dashboard", withBrowserState("build/auth.json"));
Если вы лямбды не уважаете, есть и низкоуровневый API. Тупо два метода “сохрани” и “восстанови”:
webdriver().saveBrowserState(Path.of("build/auth.json"));
webdriver().restoreBrowserState(fromFile("build/auth.json"));
BrowserState state = webdriver().getBrowserState();
P.S. Если что, класс WebDriverConditional переименовался в BrowserSession, и метод webdriver() теперь возвращает его.
Ваш код скомпилируется без изменений, но библиотеки, скомпилированные со старой версией Selenide, придётся перекомпилировать.
Спасибо jinlxz за идею!
См. issue 3378 и PR 3461.
Эмуляция времени и часового пояса
Как протестировать страницу, которая пишет “Доброе утро” только утром? Или баннер “ваша подписка истекает завтра”?
Теперь есть метод clock():
import static com.codeborne.selenide.Selenide.clock;
clock().setTimezone("America/New_York");
clock().setFixedTime(Instant.parse("2025-01-15T14:00:00Z")); // 9:00 утра в Нью-Йорке
open("/dashboard");
$("[data-testid=greeting]").shouldHave(text("Good morning, 9:00 AM"));
clock().reset();
setFixedTimeзамораживаетnew Date()иDate.now()для вновь загружаемых страниц.- Работает в Chrome и Edge (через CDP) и в Firefox (через BiDi).
reset()можно спокойно вызывать в@AfterEachдля любого браузера.
Спасибо Nigel за идею и suhanrain за реализацию!
См. issue 3370, PR 3431 и PR 3442.
Условия visibleText и exactVisibleText
Условие text() проверяет весь текст элемента, даже если часть его обрезана через CSS (overflow: hidden, text-overflow: ellipsis).
// Пользователь видит:
"Пневмония не…"
// Тест радостно проходит с:
text("Пневмония неизвестного происхождения");
Теперь можно проверить только тот текст, который реально виден на экране:
$(".product-name").shouldHave(visibleText("Пневмония неиз"));
$(".product-name").shouldHave(exactVisibleText("Пневмония неиз…"));
Старые условия text() и exactText() работают как раньше.
Спасибо svasenkov за реализацию!
См. issue 1741 и PR 3366.
HtmlUnit вернулся
Когда-то Selenide поддерживал HtmlUnit из коробки, но мы его выпилили из-за размера. Ну и как будто проект казался мёртвым какое-то время. Теперь он вернулся, но как опциональная зависимость. Если хотите гонять тесты в HtmlUnit, добавьте зависимость:
testImplementation("org.seleniumhq.selenium:htmlunit3-driver:4.48.0")
и поставьте
Configuration.browser = "htmlunit";
// либо -Dselenide.browser=htmlunit
Конечно, HtmlUnit - не настоящий браузер: shadow DOM, лейаут, координаты, hover и т.п. там не работают. Но для простых страниц это может быть быстрый вариант прогнать тесты вообще без браузера.
См. PR 3470.
Capabilities браузера в MCP и CLI
У Selenide MCP и Selenide CLI
появился параметр --capability=<name>=<value>. Его можно повторять, а значения, начинающиеся с { или [, парсятся как JSON,
так что можно передавать вложенные capabilities:
selenide open --capability='goog:chromeOptions={"args":["--no-sandbox"]}' https://selenide.org
selenide open --capability='selenoid:options={"enableVNC":true}' https://selenide.org
Переданные так аргументы Chrome добавляются к дефолтным аргументам Selenide, а не заменяют их.
См. PR 3459.
Старые временные файлы больше не ломают скачивание
Если скачивание файла падало, браузер мог оставить в папке загрузок временный файл (.crdownload, .tmp, .part).
И после этого падали все последующие скачивания с FileNotDownloadedError (это могло быть проблемой, только если вы используете свой вебдрайвер через setWebDriver()).
Теперь Selenide игнорирует временные файлы, которые были в папке до начала скачивания. А если не может почистить папку загрузок (например, на Windows браузер всё ещё держит файл), то просто пишет предупреждение в лог вместо исключения.
Спасибо ilya-corp за репорт!
См. issue 2675 и PR 3457.
Уникальные имена файлов для скриншотов и логов
Некоторые имена файлов (скриншоты, папки загрузок, webdriver.*.log) генерировались только из текущего времени.
Поэтому два потока теоретически могли в одну и ту же миллисекунду создать файл с одинаковым именем, и один файл затирал другой.
Теперь в имена файлов добавляется ещё и уникальный счётчик.
См. PR 3469.
Appium: scroll() и swipe() внутри элемента
До сих пор скролл и свайп в Appium всегда выполнялись по всему экрану. Теперь их можно выполнить внутри заданного элемента:
$(By.xpath(".//*[@text='Tabs']")).scroll(down().inside($(By.id("android:id/list"))));
$(By.xpath(".//*[@text='TAB 12']")).swipe(right(10).inside($(By.id("android:id/tabs"))));
Если контейнер частично вылезает за экран, жест выполняется только в его видимой части.
Спасибо valentin-kudin за реализацию!
См. issue 3445, PR 3446 и PR 3455.
Appium: цепочечные методы возвращают SelenideAppiumElement
Раньше такой код не компилировался, потому что setValue возвращал SelenideElement:
SelenideAppium.$(By.id("amount")).setValue("100").tap();
Теперь 27 цепочечных методов (setValue, val, type, append, press*, click, doubleClick, hover, scroll и т.д.)
возвращают SelenideAppiumElement.
Спасибо aquahitt за реализацию!
См. issue 3440 и PR 3441.
Appium: свой класс элемента в SelenideAppiumCollection
Если у вас есть свой интерфейс элемента, наследующий SelenideAppiumElement, теперь можно создать и соответствующую коллекцию:
у SelenideAppiumCollection появился protected конструктор, принимающий класс элемента
(такой же, какой появился у ElementsCollection в Selenide 7.17.0).
Спасибо aquahitt за реализацию!
См. issue 3438, PR 3439 и PR 3447.
Обновили Appium java-client с 10.1.1 до 11.0.0
В Appium java-client 11 есть ломающие изменения, но Selenide прячет их от вас:
SelenideAppiumPageFactoryработает с новымFieldDecorator;- чтение/запись буфера обмена по-прежнему работает со старыми Appium-серверами (например, на BrowserStack).
См. PR 3467.
Обновили Selenium с 4.49.0 до 4.51.0
- Support CDP versions: v153, v154, v155
См. PR 3454, PR 3468 и ченджлог селениума.
Хочешь, я добавлю сюда изменённую цитату про касаток и белых медведей или какую-либо ещё лютую дичь? Могу переделать в контексте айти. Или назовём текст “Чумовой релиз”? Выйдет угарно.
ru.selenide.org
10.10.26
