Selenide2023-04-01T08:24:51+00:00http://ru.selenide.orgAndrei Solntsevandrei.solntsev@gmail.comВышла Selenide 6.13.02023-04-01T00:00:00+00:00http://ru.selenide.org/2023/04/01/selenide-6.13.0
<p>Всем привет!</p>
<p>Ура, сегодня у нас новый релиз <strong>Selenide 6.13.0</strong></p>
<ul>
<li><a href="#banners-support">Поддержка баннеров</a></li>
<li><a href="#news">Новости</a>
<p><br /></p>
</li>
</ul>
<h3 id="banners-support">Поддержка баннеров</h3>
<h4 id="проблема">Проблема</h4>
<p>Очень часто люди спрашивают: “А что делать, если посреди теста на экране может выскочить баннер или какой-то ещё неожиданный элемент?”</p>
<p>Ведь он может перекрыть другие элементы, закрыть важную кнопку и т.п. - и таким образом сломать тест.</p>
<blockquote>
<p><a href="https://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide/">Я всегда на это люто ругался</a>
и настоятельно советовал взять тестовую среду под контроль и самому из теста регулировать, когда баннеры должны появляться, а когда нет.</p>
</blockquote>
<p>Алексей Виноградов сделал выступление на эту тему: <a href="https://www.youtube.com/watch?v=MLxf9q9qXu4&ab_channel=%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D0%B8%D0%B2%D0%9A%D0%BE%D0%BD%D1%82%D1%83%D1%80%D0%B5">Spinner-driven-development</a>.</p>
<p><br /></p>
<h4 id="стадия-принятия">Стадия принятия</h4>
<p>Но слишком уж это распространённая проблема. В конце концов, Селенид задуман для того, чтобы помогать людям решать проблемы, а не читать нотации.</p>
<p>Мы решили пойти людям навстречу и создать решение для проблемы баннеров.
Теперь в начале теста вы можете вызвать метод <code class="language-plaintext highlighter-rouge">Selenide.onBanner()</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">onBanner</span><span class="o">(</span><span class="s">"#pizzaAds"</span><span class="o">,</span> <span class="no">CLOSE</span><span class="o">,</span> <span class="s">".btn-close"</span><span class="o">);</span>
</code></pre></div></div>
<p><br /></p>
<h4 id="как-это-работает">Как это работает</h4>
<p>По умолчанию, этот метод добавит вебдрайверу листенер, который будет перед каждым кликом или проверкой проверять, не
появился ли элемент с данным селектором <code class="language-plaintext highlighter-rouge">"#pizzaAds"</code>. Если появился, селенид попытается закрыть его, нажав на кнопку закрытия <code class="language-plaintext highlighter-rouge">".btn-close"</code>.</p>
<p>Этот метод называется <code class="language-plaintext highlighter-rouge">POLL</code>. Он самый простой и надёжный, но и самый медленный.
Ведь селенид должен будет кучу раз спрашивать у браузера, не появился ли баннер. Каждый такой запрос занимает некоторое время.</p>
<p><br /></p>
<h4 id="альтернативные-методы">Альтернативные методы</h4>
<p>Поэтому мы добавили ещё два альтернативных метода, которыми селенид может проверять, а не появился ли баннер.
Их можно включить либо глобально настройкой:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">bannerCloseMode</span> <span class="o">=</span> <span class="no">POLL</span><span class="o">;</span> <span class="c1">// по умолчанию</span>
<span class="c1">// Слушает события по изменению DOM через CDP:</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">bannerCloseMode</span> <span class="o">=</span> <span class="no">CDP</span><span class="o">;</span> <span class="c1">// Работает только в Chromium-браузерах</span>
<span class="c1">// Скармливает html текущей страницы ChatGPT и спрашивает, нет ли там баннера:</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">bannerCloseMode</span> <span class="o">=</span> <span class="no">CHATGPT</span><span class="o">;</span>
</code></pre></div></div>
<p>Либо вы можете задать его параметром с помощью <code class="language-plaintext highlighter-rouge">using</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">onBanner</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">CDP</span><span class="o">)</span>
<span class="o">.</span><span class="na">withBannerSelector</span><span class="o">(</span><span class="s">"#pizzaAds"</span><span class="o">)</span>
<span class="o">.</span><span class="na">withCloseButtonSelector</span><span class="o">(</span><span class="s">".btn-close"</span><span class="o">)</span>
<span class="o">.</span><span class="na">withAction</span><span class="o">(</span><span class="no">CLOSE</span><span class="o">)</span>
<span class="o">);</span>
</code></pre></div></div>
<h4 id="кастомный-обработчик">Кастомный обработчик</h4>
<p>Есть также более общий вариант, в котором вы сможете залямбдашить свою специфическую логику обработки баннера.
Вы же не всегда хотите сразу закрыть баннер - иногда сначала нужно из него прочитать какую-то информацию.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">onBanner</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">CHATGPT</span><span class="o">)</span>
<span class="o">.</span><span class="na">withBannerSelector</span><span class="o">(</span><span class="s">"#pizzaAds"</span><span class="o">)</span>
<span class="o">.</span><span class="na">withCloseButtonSelector</span><span class="o">(</span><span class="s">".btn-close"</span><span class="o">)</span>
<span class="o">.</span><span class="na">withAction</span><span class="o">((</span><span class="n">banner</span><span class="o">,</span> <span class="n">closeButton</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">banner</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">".title"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Ваш пароль изменён."</span><span class="o">));</span>
<span class="nc">String</span> <span class="n">newPassword</span> <span class="o">=</span> <span class="n">banner</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">".new-password"</span><span class="o">).</span><span class="na">text</span><span class="o">();</span>
<span class="n">closeButton</span><span class="o">.</span><span class="na">doubleClick</span><span class="o">();</span>
<span class="o">})</span>
<span class="o">);</span>
</code></pre></div></div>
<p><br /></p>
<p>Пробуйте, пишите, заводите тикеты на гитхабе. Предлагайте свои алгоритмы для обнаружения баннеров.</p>
<p><em>Хватит примерять на себя белые пальто и ссориться из-за дурацких принципов.</em></p>
<blockquote>
<p>Давайте объединимся против баннеров!</p>
</blockquote>
<h3 id="news">News</h3>
<p>Сегодня у нас три видоса про Селенид</p>
<ul>
<li>на русском: <a href="https://youtu.be/k0A8HFWV_iE">ChatGPT: Как скачать файл в Selenide</a></li>
<li>на английском: <a href="https://www.youtube.com/watch?v=GwHG550moGc">ChatGPT: How to download file in Selenide</a></li>
<li><a href="https://www.youtube.com/watch?v=SohZfPKicZQ&ab_channel=OlehPendrak">Ускоряем UI Автотесты с помощью подстановки Cookies</a> by Oleh Pendrak</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.12.42023-03-23T00:00:00+00:00http://ru.selenide.org/2023/03/23/selenide-6.12.4
<p>Привет!</p>
<p>А вы следили за новостями 23 марта?
Молодцы!</p>
<p>Сегодня у нас <em>Точка Отсчёта</em> - релиз <a href="https://github.com/selenide/selenide/milestone/180?closed=1">Selenide 6.12.4</a>.</p>
<ul>
<li><a href="#workaround-for-chromedriver-bug">Костыль для Хрома</a></li>
<li><a href="#support-jdk-http-client">Добавили поддержку jdk-http-client</a></li>
<li><a href="#restore-proxy-after-using">Исправили $.download(PROXY) после “using”</a></li>
<li><a href="#fix-clear-when-element-disappears">Исправили $.clear() при пропадании элемента</a></li>
<li><a href="#update-dependencies">Обновили зависимости</a></li>
<li><a href="#release-selenide-appium">Выпустили selenide-appium 2.7.0</a></li>
<li><a href="#release-selenide-selenoid">Выпустили selenide-selenoid 2.3.6</a></li>
<li><a href="#news">Новости</a>
<p><br /></p>
</li>
</ul>
<h3 id="workaround-for-chromedriver-bug">Костыль для Хрома</h3>
<p>Напоминание: если вы всё ещё роете интернет в поисках лекарства против</p>
<blockquote>
<p>Invalid Status code=403 text=Forbidden</p>
</blockquote>
<p>то лекарство уже было выпущено в <a href="/2023/03/09/selenide-6.12.2/">Selenide 6.12.2</a>.</p>
<p><br /></p>
<h3 id="support-jdk-http-client">Добавили поддержку <code class="language-plaintext highlighter-rouge">jdk-http-client</code> вместо <code class="language-plaintext highlighter-rouge">netty-client</code></h3>
<p>По умолчанию по-прежнему используется <code class="language-plaintext highlighter-rouge">netty-client</code>, но если
вы хотите <a href="https://www.selenium.dev/blog/2022/using-java11-httpclient/">перейти на jdk-http-client</a>,
то можете это легко сделать:</p>
<ol>
<li>Добавить зависимость <br />
<code class="language-plaintext highlighter-rouge">org.seleniumhq.selenium:selenium-http-jdk-client:$seleniumVersion</code></li>
<li>Добавить пропертю перед открытием браузера: <br />
<code class="language-plaintext highlighter-rouge">System.setProperty("webdriver.http.factory", "jdk-http-client");</code></li>
</ol>
<p>См. <a href="https://github.com/selenide/selenide/issues/2215">issue 2215</a>
и <a href="https://github.com/selenide/selenide/pull/2216">PR 2216</a>.</p>
<p><br /></p>
<h3 id="restore-proxy-after-using">Исправили работу прокси после метода <code class="language-plaintext highlighter-rouge">using</code></h3>
<p>Это почти такая же проблема, как в <a href="https://github.com/selenide/selenide/issues/2202">issue 2202</a>,
но только с прокси.</p>
<p>В общем, стоило вам разочек использовать метод <code class="language-plaintext highlighter-rouge">using</code>,
как у вас сразу пропадал прокси. И дальнейшее скачивание файлов (или для чего ещё вы
использовали прокси) больше не работало.</p>
<p>Но судя по всему, про этот <a href="/2019/10/16/selenide-5.4.0/#add-method-using">супер-удобный метод <code class="language-plaintext highlighter-rouge">using</code></a>
мало кто знает, потому что жалоба была всего одна. :)</p>
<p>Ну вот, теперь и <code class="language-plaintext highlighter-rouge">using</code> починили, и про него узнает больше народу. :)</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/2208">PR 2208</a> и
<a href="https://github.com/selenide/selenide/pull/2209">PR 2209</a>.</p>
<p><br /></p>
<h3 id="fix-clear-when-element-disappears">Исправили <code class="language-plaintext highlighter-rouge">$.clear()</code> при пропадании элемента</h3>
<p>Как вы знаете, в селениде есть метод <code class="language-plaintext highlighter-rouge">$("input").clear()</code>, который очищает поле ввода.
Оказалось, что метод мог упасть в одной редкой ситуации.
А именно, если в результате очистки элемент пропадает.
В этот момент метод <code class="language-plaintext highlighter-rouge">$.clear()</code> пытался сгенерировать событие <code class="language-plaintext highlighter-rouge">on change</code> на этом элементе - и падал.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2207">issue 2207</a> и
<a href="https://github.com/selenide/selenide/pull/2221">PR 2221</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>#2210 Bump nettyVersion from 4.1.89.Final to 4.1.90.Final</li>
<li>#2218 Bump slf4jVersion from 2.0.6 to 2.0.7</li>
</ul>
<p><br /></p>
<h3 id="release-selenide-appium">Выпустили <code class="language-plaintext highlighter-rouge">selenide-appium:2.7.0</code></h3>
<ul>
<li>Добавили скролл вверх-вниз для мобильников (#139)</li>
<li>Исправили функцию <code class="language-plaintext highlighter-rouge">terminateApp</code> (#146)</li>
<li>Обновились на Selenide 6.12.4 (#143)</li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide-appium/releases/tag/v2.7.0">release notes</a>.</p>
<p><br /></p>
<h3 id="release-selenide-selenoid">Выпустили <code class="language-plaintext highlighter-rouge">selenide-selenoid:2.3.6</code></h3>
<ul>
<li>Обновились на Selenide 6.12.4</li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.3.6">release notes</a>.</p>
<p><br /></p>
<h3 id="news">News</h3>
<ul>
<li>Я запилил первый видос после 9-летнего перерыва :)
<a href="https://youtu.be/k0A8HFWV_iE">Как скачать файл в Selenide: спросим у ChatGPT</a></li>
<li>Наконец-то у меня появился <a href="https://www.youtube.com/watch?v=18J2_4a4Cl4&ab_channel=Jfokus">доклад про Flaky tests на английском</a> - конференция JFokus, Стокгольм, 8.02.2023</li>
<li>Видос <a href="https://www.youtube.com/watch?v=9S6xaAvJc9M&t=4226s&ab_channel=HillelITSchool">Selenide: як тестувати веб-елементи</a> от Hillel IT School</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.12.22023-03-09T00:00:00+00:00http://ru.selenide.org/2023/03/09/selenide-6.12.2
<p>Привет!</p>
<p>Срочно обновляйтесь на <a href="https://github.com/selenide/selenide/milestone/178?closed=1">Selenide 6.12.2</a>.</p>
<p>Он содержит один важный костыль для баги в Chromedriver 111.</p>
<blockquote>
<p>Invalid Status code=403 text=Forbidden</p>
</blockquote>
<h3 id="в-чём-проблема">В чём проблема?</h3>
<p>Недавно вышла новая версия Chrome и Chromedriver 111.
И у всех, кто на неё обновился, резко посыпались тесты.
Браузер открывался, но при этом тест получал от вебдрайвера ошибку и дальше не мог ничего
сделать, в том числе закрыть этот самый браузер.</p>
<p>В логах было видно такое вот красноречивое сообщение:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Starting</span> <span class="nc">ChromeDriver</span> <span class="mf">111.0</span><span class="o">.</span><span class="mf">5563.64</span> <span class="n">on</span> <span class="n">port</span> <span class="mi">31021</span>
<span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">remote</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">WebSocket</span><span class="n">$Listener</span> <span class="n">onError</span>
<span class="nl">WARNING:</span> <span class="nc">Invalid</span> <span class="nc">Status</span> <span class="n">code</span><span class="o">=</span><span class="mi">403</span> <span class="n">text</span><span class="o">=</span><span class="nc">Forbidden</span>
<span class="n">java</span><span class="o">.</span><span class="na">io</span><span class="o">.</span><span class="na">IOException</span><span class="o">:</span> <span class="nc">Invalid</span> <span class="nc">Status</span> <span class="n">code</span><span class="o">=</span><span class="mi">403</span> <span class="n">text</span><span class="o">=</span><span class="nc">Forbidden</span>
<span class="o">...</span>
</code></pre></div></div>
<p>И хотя это <a href="https://bugs.chromium.org/p/chromedriver/issues/detail?id=4361">бага хромдрайвера</a>,
для неё нашёлся один простой костыль, и мы его по-быстрому запилили в Селениде.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2192">issue 2192</a> и
<a href="https://github.com/selenide/selenide/pull/2194">PR 2194</a>.</p>
<blockquote>
<p>А вообще, обновляйтесь на <a href="/2023/03/22/selenide-6.12.4/#support-jdk-http-client">HttpClient</a>.
Он требует Java11+, но зато таких багов там не будет.</p>
</blockquote>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.12.02023-02-24T00:00:00+00:00http://ru.selenide.org/2023/02/24/selenide-6.12.0
<p>Привет!</p>
<p>В этот трагичный и одновременно праздничный день мы выпустили
релиз <a href="https://github.com/selenide/selenide/milestone/173?closed=1">Selenide 6.12.0</a>.</p>
<p>В нём почти нет фич, но есть новый режим, который может сильно повлиять на ваши тесты.</p>
<ul class="blogpost-menu">
<li><a href="#new-headless-mode">Новый безбашенный режим</a></li>
<li><a href="#improve-logs-when-downloading-file">Улучшили логирование при скачивании файлов</a></li>
<li><a href="#improve-download-in-edge">Улучшили скачивание файлов в Edge под Windows</a></li>
<li><a href="#update-dependencies">Обновили зависимости</a></li>
<li><a href="#rename-master-to-main">Переименовали master в main</a></li>
</ul>
<p><br /></p>
<h3 id="new-headless-mode">Новый безбашенный режим</h3>
<p>В Chromium-браузерах появился новый безголовый (headless) режим.
Подробнее о нём <a href="https://developer.chrome.com/articles/new-headless/">в их блоге</a>.</p>
<p>Если вкратце, этот новый режим внутри использует тот же код, что и обычный headful, и поэтому должен вылечить все
болячки headless режима. А вот старый headless режим по сути был отдельным браузером со своими особенностями и багами.</p>
<p>В теории вы всё ещё можете переключаться между старым и новым headless режимом. Но на практике вам придётся переключиться на новый, т.к.
при обновлении Chrome и Edge перестало работать скачивание файлов в старом headless режиме.</p>
<p>В общем, в Selenide 6.12.0 новый headless режим будет включаться по умолчанию.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2104">issue 2104</a>.
Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/2105">PR 2105</a>
и <a href="https://github.com/selenide/selenide/pull/2169">PR 2169</a>.</p>
<p><br /></p>
<h3 id="improve-logs-when-downloading-file">Улучшили логирование при скачивании файлов</h3>
<p>Если скачивание файла падает (особенно методом <code class="language-plaintext highlighter-rouge">FOLDER</code>), иногда бывает сложно понять, почему именно.
Мы немного доработали логирование внутри метода <code class="language-plaintext highlighter-rouge">$.download()</code>, так что теперь должно стать полегче.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/2167">PR 2167</a>.</p>
<p><br /></p>
<h3 id="improve-download-in-edge">Улучшили скачивание файлов в Edge под Windows</h3>
<p>До сих пор селенид отслеживал только временные файлы “<em>.crdownload”, которые хромиум использует при скачивании файлов.
Но оказалось, что на Windows браузер Edge создаёт ещё и временные файлы “</em>.tmp”. Теперь селенид отслеживает и их.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/2167">PR 2167</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>bump Selenium from 4.8.0 to 4.8.1, см. <a href="https://github.com/selenide/selenide/pull/2161">PR 2161</a> и <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a>.</li>
<li>Bump nettyVersion from 4.1.87.Final to 4.1.89.Final, см. <a href="https://github.com/selenide/selenide/pull/2158">PR 2158</a>.</li>
</ul>
<p><br /></p>
<h3 id="rename-master-to-main">Переименовали ветку <code class="language-plaintext highlighter-rouge">master</code> в <code class="language-plaintext highlighter-rouge">main</code></h3>
<p>Да-да, это отголоски того самого BLM. Один умный человек убедил меня, что стоит это сделать. :)</p>
<p><br /></p>
<h3 id="news">News</h3>
<ul>
<li>Пост <a href="https://oleksandr-podoliako.medium.com/test-automation-framework-for-ui-testing-with-java-fddd1e3fd75b">Test automation framework for UI testing with java</a> by Oleksandr Podoliako</li>
<li>Пост <a href="https://pradappandiyan.medium.com/running-test-automation-with-selenide-on-gitlab-fb13c0a0dddf">Running test automation with Selenide on GitLab</a> by Pradap Pandiyan</li>
<li>Моё видео с Continuous Testing Meetup: <a href="https://www.youtube.com/watch?v=5qiuRoUcICs&t=48m02s">Selenide UI tests in java</a>, 23.01.2023</li>
<li>Видео <a href="https://www.youtube.com/watch?v=G7iYLr_IgGA&ab_channel=FullStackQA">Автотесты с нуля для начинающих Java + Selenide + TestNG + Maven</a> на канале FullStackQA</li>
</ul>
<p><br />
Ну а почему же я назвал этот день праздничным?
Да потому, что 24 февраля вообще-то День Независимости Эстонии.
105 лет назад эстонские войска напихали оркам в панамку и провозгласили новое независимое государство.</p>
<center>
<img src="/images/2023/02/independence-day-estonia.png" width="300" />
</center>
<blockquote>
<p>Если бы не те бравые ребята на бронепоезде, не было бы сейчас Селенида. :)</p>
</blockquote>
<p><br /></p>
<h1 id="upd-вышла-selenide-6121">UPD Вышла Selenide 6.12.1</h1>
<p>Исправили старинную багу в методе <code class="language-plaintext highlighter-rouge">using</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/milestone/176?closed=1">changelog</a></p>
<p><br /></p>
<h1 id="upd-вышла-selenide-6122">UPD Вышла Selenide 6.12.2</h1>
<p>По-быстрому залепили костыль для баги в Chromedriver 111:<br />
<code class="language-plaintext highlighter-rouge">Invalid Status code=403 text=Forbidden</code></p>
<p>См. <a href="https://github.com/selenide/selenide/milestone/178?closed=1">changelog</a></p>
<p><br /></p>
<h1 id="upd-вышла-selenide-6123">UPD Вышла Selenide 6.12.3</h1>
<p>Исправили одну старую багу в методе <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code>, если его вызвать после <code class="language-plaintext highlighter-rouge">using</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/milestone/179?closed=1">changelog</a></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.11.12023-01-20T00:00:00+00:00http://ru.selenide.org/2023/01/20/selenide-6.11.1
<p>Хаюшки!</p>
<p>Одна моя знакомая из Италии рассказала, что вышел новый
релиз <a href="https://github.com/selenide/selenide/milestone/174?closed=1">Selenide 6.11.1</a>.</p>
<ul class="blogpost-menu">
<li><a href="#truncate-webdriver-exception-message">Обрезаем только WebDriverException</a></li>
<li><a href="#fix-download-to-folder">Чутка подправили $.download(FOLDER)</a></li>
<li><a href="#update-dependencies">Обновили зависимости</a></li>
<li><a href="#statistics">Статистика</a></li>
</ul>
<h3 id="truncate-webdriver-exception-message">Обрезаем только сообщения <code class="language-plaintext highlighter-rouge">WebDriverException</code></h3>
<p>В довольно редких ситуациях - если вы</p>
<ul>
<li>используете кастомное действие (custom command),</li>
<li>и из него кидаете какую-то свою ошибку (assertion error),</li>
<li>и текст этой ошибки содержит несколько строк,</li>
</ul>
<p>то Селенид обрезает этот текст, оставляя лишь первую строку.
Изначально планировалось обрезать только текст <code class="language-plaintext highlighter-rouge">WebDriverException</code>, потому что он объективно содержит многострочный мусор:</p>
<blockquote>
<p>The element could not be found (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 21 milliseconds
For documentation on this error …
Build info: version: ‘2.29.1’, …
System info: os.name: ‘Linux’, …
Session ID: 610138404f5c18…
Driver info: org.openqa.selenium.chrome.ChromeDriver</p>
</blockquote>
<p>Но как-то так вышло, что селенид обрезал текст не только <code class="language-plaintext highlighter-rouge">WebDriverException</code>, а вообще всех ошибок.
Теперь подправили, и свой многострочный текст вы увидите целиком.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/2131">PR 2131</a>.</p>
<p><br /></p>
<h3 id="fix-download-to-folder">Чутка подправили <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code></h3>
<p>Это очень редко случай, так что вы наверняка этого не замечали.</p>
<p>Но наши тесты изредка моргали, и я покопал-покопал, да и раскопал парочку редких багов в методе <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code>.
Иногда он мог кинуть ошибку, что файл не скачался (хоть он на самом деле скачался), в двух случаях:</p>
<ul>
<li>
<p><a href="https://github.com/selenide/selenide/pull/2116">#2116</a> Если время изменения оказалось в предыдущей секунде от начала скачивания<br />
(такое бывает, т.к. разные файловые системы выдают время изменения файла с погрешностью до секунды, и иногда оно может даже оказаться в прошлом)</p>
</li>
<li>
<p><a href="https://github.com/selenide/selenide/pull/2119">#2119</a> Если ОС вернула время последнего изменения файла “0”<br />
(оказываете, и такое бывает - если файловая система почему-то решила, что имя файла некорректное)</p>
</li>
</ul>
<p>Теперь должно качаться как намасленное.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>bump WebdriverManager from 5.3.1 to 5.3.2</li>
<li>bump Netty from 4.1.86.Final to 4.1.87.Final, см. <a href="https://github.com/selenide/selenide/pull/2126">PR 2126</a>.</li>
</ul>
<p><br /></p>
<h3 id="news">News</h3>
<ul>
<li>Пост от Amuthan Sakthivel <a href="https://creatingvalue.substack.com/p/why-we-chose-selenide-over-selenium">Почему мы выбрали Селенид, а не Селениум</a></li>
<li>Свежая статья на хабре <a href="https://habr.com/ru/company/rostelecom/blog/707710/">Как написать UI-автотесты, если не умеешь программировать?</a></li>
<li>Мой доклад в девклубе <a href="https://www.youtube.com/watch?v=VtX7IpCHMS8&ab_channel=DEVCLUB.EU">Как законтрибьютить в опенсорс, чтобы не сгореть со стыда</a></li>
<li>Мой доклад в девклубе <a href="https://www.youtube.com/watch?v=JKxzELiwO_o&ab_channel=DEVCLUB.EU">WTF Thread Pools</a></li>
</ul>
<p><br /></p>
<h3 id="statistics">Статистика</h3>
<p>Количество ежемесячных скачиваний Селенида перевалило за 490 тыщ!</p>
<center>
<img src="/images/2023/01/selenide.downloads.png" width="800" />
</center>
<p>Поднажмём…</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.11.02023-01-03T00:00:00+00:00http://ru.selenide.org/2023/01/03/selenide-6.11.0
<center>
<img src="/images/2023/01/selenide-6.11.0.png" width="700" />
</center>
<p><br /></p>
<h1 id="с-новым-годом-друзья">С Новым Годом, друзья!</h1>
<p>Дедушка Мороз принёс нам новый релиз <a href="https://github.com/selenide/selenide/milestone/169?closed=1">Selenide 6.11.0</a>.</p>
<ul class="blogpost-menu">
<li><a href="#added-copy-paste-methods">Методы для копирования текста</a></li>
<li><a href="#fix-download-with-credentials">Скачивание файла за BasicAuth</a></li>
<li><a href="#download-large-files-via-proxy">Скачиваем большие файлы через прокси</a></li>
<li><a href="#can-handle-unexpected-alerts">Неожиданные алерты</a></li>
<li><a href="#fix-screenshot-file-permission">Пермиссии файла скриншота</a></li>
<li><a href="#support-as-annotation">Аннотация @As для полей без @FindBy</a></li>
<li><a href="#last-page-source">Последний исходник страницы</a></li>
<li><a href="#page-url-in-error-message">URL страницы в ошибке</a></li>
</ul>
<h3 id="added-copy-paste-methods">Добавили методы для копирования текста</h3>
<p>Мы добавили два новых метода:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Selenide.copy()</code> - копирует выделенный текст в буфер обмена, и</li>
<li><code class="language-plaintext highlighter-rouge">$.paste()</code> вставляет текст из буфера обмена в поле ввода.</li>
</ul>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://best-propaganda-quotes.ru"</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#solovjov"</span><span class="o">).</span><span class="na">doubleClick</span><span class="o">();</span> <span class="c1">// select the quote text</span>
<span class="nc">Selenide</span><span class="o">.</span><span class="na">copy</span><span class="o">();</span>
<span class="err">$</span><span class="o">(</span><span class="s">"[name=q]"</span><span class="o">).</span><span class="na">paste</span><span class="o">();</span>
<span class="err">$</span><span class="o">(</span><span class="s">"[name=q]"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Обсираться мелкими алмазами на берегу озера Комо"</span><span class="o">));</span>
</code></pre></div></div>
<p>Известные ограничения: пока что эти методы не работают на серверных линуксах (без графического окружения).
Но запуск xvfb должен помочь.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1817">issue 1817</a>.<br />
Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/2027">PR 2027</a>.</p>
<p><br /></p>
<h3 id="fix-download-with-credentials">Исправили метод <code class="language-plaintext highlighter-rouge">Selenide.download(url)</code></h3>
<p>…для случая, когда url содержит логин/пароль (т.е. ресурс защищён BasicAuth).</p>
<p>Например, такой код раньше работал, но сломался после обновления Apache Http client:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">download</span><span class="o">(</span><span class="s">"https://admin:tiger@the-internet.herokuapp.com/basic_auth"</span><span class="o">);</span>
</code></pre></div></div>
<p>Оказалось, что в Apache Http client усилили валидацию URL, и часть с логином и паролем больше не разрешена
(это типа небезопасная практика).</p>
<p>Ну а селенид теперь вырезает эту часть и посылает с запросом в заголовке <code class="language-plaintext highlighter-rouge">Authorization</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2037">issue 2037</a>
и <a href="https://github.com/selenide/selenide/pull/2102">PR 2102</a>.</p>
<p><br /></p>
<h3 id="download-large-files-via-proxy">Позволяем скачивать большие файлы через прокси</h3>
<p>Если вы скачиваете файлы методом <code class="language-plaintext highlighter-rouge">PROXY</code>, то размер файла не мог превышать 64 мегабайта.
Изначально это казалось разумным ограничением: зачем кому-то нужно нагружать тестовый стенд, скачивая гигантские файлы?
(64 и то многовато, по умолчанию в BrowserUpProxy вообще было 2 мегабайта; мы увеличили лимит до 64 в селениде)</p>
<p>Но оказалось, что некоторых хотят тестировать именно скачивание больших файлов.
После долгих споров мы решили просто убрать это ограничение. Вам виднее, качайте сколько хотите.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">favoriteMovie</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#topMovie"</span><span class="o">).</span><span class="na">download</span><span class="o">();</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">favoriteMovie</span><span class="o">)</span>
<span class="o">.</span><span class="na">hasName</span><span class="o">(</span><span class="s">"BadSanta.avi"</span><span class="o">)</span>
<span class="o">.</span><span class="na">as</span><span class="o">(</span><span class="s">"2 GB"</span><span class="o">).</span><span class="na">hasSize</span><span class="o">(</span><span class="mi">2147483648L</span><span class="o">);</span>
</code></pre></div></div>
<p>P.S. Но имейте в виду, со слишком большими файлами, скорее всего, прокси всё равно не справится (либо будет скачивать слишком долго).
И BrowserUpProxy по-прежнему имеет встроенное техническое ограничение в 2 гигабайта (потому, что тип данных - Integer).</p>
<p>Но хотя бы файлы по 200 мегабайтов вы вполне сможете скачивать.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2082">issue 2082</a>
и <a href="https://github.com/selenide/selenide/pull/2098">PR 2098</a>.</p>
<p><br /></p>
<h3 id="can-handle-unexpected-alerts">Можно обрабатывать неожиданные алерты</h3>
<p>По умолчанию, селенид игнорирует неожиданные алерты в браузере
(точнее, запускает вебдрайвер с опцией <code class="language-plaintext highlighter-rouge">capabilities.setCapability(UNHANDLED_PROMPT_BEHAVIOUR, ACCEPT)</code>).</p>
<p>С одной стороны, это удобно, чтобы тесты не ломались от внезапно всплывающих реклам и других бесполезных сообщений.
С другой стороны, иногда эти алерты могут содержать полезную информацию - в частности, ценное сообщение об ошибке.</p>
<p>Если это ваш случай, теперь вы можете переопределить эту настройку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">remote</span><span class="o">.</span><span class="na">CapabilityType</span><span class="o">.</span><span class="na">UNHANDLED_PROMPT_BEHAVIOUR</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">UnexpectedAlertBehaviour</span><span class="o">.</span><span class="na">ACCEPT_AND_NOTIFY</span><span class="o">;</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="no">UNHANDLED_PROMPT_BEHAVIOUR</span><span class="o">,</span> <span class="no">ACCEPT_AND_NOTIFY</span><span class="o">);</span>
</code></pre></div></div>
<p>И тогда при возникновении нежданных алертов вы узнаете, какой текст в них был:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">UnhandledAlertException:</span> <span class="n">unexpected</span> <span class="n">alert</span> <span class="nl">open:</span> <span class="o">{</span><span class="nc">Alert</span> <span class="n">text</span> <span class="o">:</span> <span class="nc">Как</span> <span class="n">жопа</span><span class="o">,</span> <span class="n">рогозин</span><span class="o">?}</span> <span class="c1">// chrome</span>
<span class="nl">UnhandledAlertException:</span> <span class="nc">Accepted</span> <span class="n">user</span> <span class="n">prompt</span> <span class="nl">dialog:</span> <span class="nc">Маршрут</span> <span class="n">через</span> <span class="n">мост</span> <span class="n">недоступен</span><span class="o">.</span> <span class="nc">Чот</span> <span class="n">бахнуло</span><span class="o">.</span> <span class="c1">// firefox</span>
<span class="nl">UnhandledAlertException:</span> <span class="n">unexpected</span> <span class="n">alert</span> <span class="nl">open:</span> <span class="o">{</span><span class="nc">Alert</span> <span class="n">text</span> <span class="o">:</span> <span class="nc">Обнаружены</span> <span class="n">боевые</span> <span class="n">комары</span><span class="o">-</span><span class="n">убийцы</span><span class="o">}</span> <span class="c1">// edge</span>
<span class="nl">UnhandledAlertException:</span> <span class="o">:</span> <span class="nc">Обнаружена</span> <span class="n">грязная</span> <span class="n">бомба</span> <span class="c1">// safari</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/2054">issue 2054</a>
и <a href="https://github.com/selenide/selenide/pull/2095">PR 2095</a>.</p>
<p><br /></p>
<h3 id="fix-screenshot-file-permission">Исправили пермиссии файла скриншота</h3>
<p>Как вы знаете, при падении теста селенид автоматически сохраняет скриншот и создаёт два файла: <code class="language-plaintext highlighter-rouge">*.png</code> и <code class="language-plaintext highlighter-rouge">*.html</code>.
Так вот выяснилось, что селенид создаёт эти два файла с разными пермиссиями:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-rw------- 1 root root 300295 Dec 19 10:24 1671441847908.0.png
-rw-r--r-- 1 root root 185070 Dec 19 10:24 1671441847908.0.html
</code></pre></div></div>
<p>И кое-кому это помешало писать сложносочинённые CI скрипты, выполняющие команды из-под разных юзеров.
<em>Чем бы девопс не тешился… :)</em></p>
<p>В общем, теперь у обоих будет “обычные” пермиссии <code class="language-plaintext highlighter-rouge">-rw-r--r--</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2081">issue 2081</a>
и <a href="https://github.com/selenide/selenide/pull/2084">PR 2084</a>.</p>
<p><br /></p>
<h3 id="support-as-annotation">Поддержка аннотации <code class="language-plaintext highlighter-rouge">@As</code> для полей без <code class="language-plaintext highlighter-rouge">@FindBy</code></h3>
<p>В селениде уже давно можно задавать удобные имена элементам пэдж обжекты (т.н. “алиасы”) с помощью метода <code class="language-plaintext highlighter-rouge">as</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="o">{</span>
<span class="nc">SelenideElement</span> <span class="n">loginButton</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Но кому-то показалось, что удобно было бы повесить имя элемента в начало строки, а то справа от длинного селектора оно
визуально теряется.</p>
<p>В общем, теперь алиас можно задать и с помощью аннотации <code class="language-plaintext highlighter-rouge">@As</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="o">{</span>
<span class="nd">@As</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">)</span>
<span class="nc">SelenideElement</span> <span class="n">loginButton</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[3]"</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Но естественно, аннотация сработает, только если вы инициализируете пэдж обжект с помощью метода <code class="language-plaintext highlighter-rouge">Selenide.page()</code> или <code class="language-plaintext highlighter-rouge">Selenide.open()</code></p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2087">issue 2087</a>
и <a href="https://github.com/selenide/selenide/pull/2088">PR 2088</a>.</p>
<p><br /></p>
<h3 id="last-page-source">Метод для последнего сохранённого исходника страницы</h3>
<p>Редко когда это нужно, так что не заморачивайтесь.
По сути добавили методы <code class="language-plaintext highlighter-rouge">ScreenShotLaboratory</code>:
<code class="language-plaintext highlighter-rouge">threadScreenshots()</code>, <code class="language-plaintext highlighter-rouge">contextScreenshots()</code>, <code class="language-plaintext highlighter-rouge">lastThreadScreenshot()</code>, <code class="language-plaintext highlighter-rouge">lastContextScreenshot()</code>.</p>
<p>Спасибо <a href="https://github.com/armanayvazyan">Arman Ayvazyan</a> за <a href="https://github.com/selenide/selenide/pull/2065">PR 2065</a>.</p>
<p><br /></p>
<h3 id="page-url-in-error-message">Теперь можно добавлять URL страницы в сообщение об ошибке</h3>
<p>Кому-то это полезно, кому-то вредно. После долгих споров сделали эту возможность опциональной (в виде плагина).</p>
<p>Пусть пока побудет в статусе экспериментальной возможности.
Вероятно, с форматами сообщений об ошибках мы ещё будем играться.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/980">issue 980</a> и <a href="https://github.com/selenide/selenide/pull/2097">PR 2097</a>.</p>
<p><br /></p>
<h3 id="year-summary">Итоги года</h3>
<p>Подведём итоги 2022 года? За этот год Selenide был упомянут в нескольких топах:</p>
<ul>
<li><a href="https://qameta.io/blog/5-testing-automation-tools/">5 Testing Automation Tools</a> в блоге компании Qameta Software</li>
<li><a href="https://hackernoon.com/top-java-libraries-for-automation-testing-in-2022">Top Java Libraries for Automation Testing in 2022</a></li>
<li><a href="https://aglowiditsolutions.com/blog/top-java-frameworks/">Top Java Frameworks to Use in 2022</a></li>
</ul>
<p>И наконец,</p>
<ul>
<li>Selenide упомянут на <a href="https://www.selenium.dev/ecosystem/">официальном сайте Selenium</a> в разделе “Экосистема”.</li>
</ul>
<p>Количество скачиваний селенида выросло аж <strong>в полтора раза</strong>:</p>
<blockquote>
<p>с 302 тысяч в январе до 469 тысяч в ноябре.</p>
</blockquote>
<h3 id="new-year-wishes">И всё-таки</h3>
<p>Это был ужасный год. Но я рад, что я смог использовать накопленный багаж в виде сайта selenide.org и его
аудитории, чтобы распространять антивоенную позицию. Кажется, здесь собрались люди, которые эту позицию разделяют.
Чему я тоже очень рад.</p>
<p>Надеюсь, в наступившем году война закончится, виновные будут наказаны, граждане угнетённых стран сбросят наконец своих
тиранов. А сомневающиеся вытащат наконец голову из жопы.</p>
<p>А прекрасная свободная страна Украина отстроится и будет цвести пуще прежнего.</p>
<blockquote>
<p>И через год мы с вами увидимся в Киеве на замечательной <a href="https://seleniumcamp.com/">конференции SeleniumCamp</a>.</p>
</blockquote>
<p>Героям слава!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.10.22022-12-08T00:00:00+00:00http://ru.selenide.org/2022/12/08/selenide-6.10.2
<p><br /></p>
<h1 id="всем-привет">Всем привет!</h1>
<p>У нас вышел новый мини-релиз <a href="https://github.com/selenide/selenide/milestone/171?closed=1">Selenide 6.10.2</a>.</p>
<ul class="blogpost-menu">
<li><a href="#added-method-press">Добавили метод $.press()</a></li>
<li><a href="#trigger-change-events-by-select-methods">Генерируем события change в методах $.select*</a></li>
<li><a href="#friendly-select-option-in-reports">selectOption в отчётах</a></li>
<li><a href="#friendly-local-storage-in-reports">localStorage в отчётах</a></li>
<li><a href="#news">Новости</a></li>
</ul>
<h3 id="added-method-press">Добавили метод <code class="language-plaintext highlighter-rouge">$.press()</code></h3>
<p>По сути это то же самое, что <code class="language-plaintext highlighter-rouge">sendKeys()</code>, только он не <code class="language-plaintext highlighter-rouge">void</code>. То есть его можно <em>чейнить</em> с другими методами:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">)</span>
<span class="o">.</span><span class="na">press</span><span class="o">(</span><span class="s">"x"</span><span class="o">)</span>
<span class="o">.</span><span class="na">press</span><span class="o">(</span><span class="no">TAB</span><span class="o">,</span> <span class="no">CONTROL</span><span class="o">,</span> <span class="no">ALT</span><span class="o">,</span> <span class="no">ENTER</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide/pull/2032">PR 2032</a>.</p>
<p><br /></p>
<h3 id="trigger-change-events-by-select-methods">Генерируем события <code class="language-plaintext highlighter-rouge">change</code> в методах <code class="language-plaintext highlighter-rouge">$.select*</code></h3>
<p>Как вы помните, в предыдущем релизе мы <a href="/2022/11/21/selenide-6.10.0/#select-options-using-javascript">переделали работу с селектами на JavaScript</a>.
Но при этом забыли, что надо ещё и генерировать события <code class="language-plaintext highlighter-rouge">focus</code>, <code class="language-plaintext highlighter-rouge">click</code>, <code class="language-plaintext highlighter-rouge">change</code>. Теперь генерируем.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/2050">issue 2050</a>.
Спасибо <a href="https://github.com/cocorossello">Vicente Rossello Jaume</a> за <a href="https://github.com/selenide/selenide/pull/2051">PR 2051</a>.</p>
<p>UPD. Исправили ещё раз в <a href="https://github.com/selenide/selenide/milestone/172?closed=1">Selenide 6.10.3</a>.</p>
<p><br /></p>
<h3 id="friendly-select-option-in-reports">Показываем <code class="language-plaintext highlighter-rouge">$.selectOption()</code> в отчётах по-человечески</h3>
<p>Ещё один косячок, вылезший после предыдущего рефакторинга селектов: в отчётах выскочили нечитаемые параметры.
Это всё потому, что в Java у массивов нет стандартного метода <code class="language-plaintext highlighter-rouge">toString()</code>, приходится изобретать велосипед.</p>
<p>Было:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| #blockChannel | select option([Канал Дождь, [Ljava.lang.String;@6732726]) | PASS | 487 |
</code></pre></div></div>
<p>Стало:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| #blockChannel | select option(Канал Дождь) | PASS | 487 |
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/2047">issue 2047</a> и <a href="https://github.com/selenide/selenide/pull/2052">PR 2052</a>.</p>
<p><br /></p>
<h3 id="friendly-local-storage-in-reports">Показываем <code class="language-plaintext highlighter-rouge">localStorage</code> в отчётах по-человечески</h3>
<p>Почти такая же проблема: операции с <code class="language-plaintext highlighter-rouge">sessionStorage</code> и <code class="language-plaintext highlighter-rouge">localStorage</code> выглядели в отчётах нечитабельно.</p>
<p>Было:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| com.codeborne.selenide.LocalStorage@138a952f | set item(['Бут', 9125]) |
| com.codeborne.selenide.SessionStorage@549w123gg | set item(['Грайнер', 3285]) |
</code></pre></div></div>
<p>Стало:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| localStorage | set item(['Бут', 9125]) |
| sessionStorage | set item(['Грайнер', 3285]) |
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/2045">issue 2045</a> и <a href="https://github.com/selenide/selenide/pull/2046">PR 2046</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>#2044 #2057 bump Selenium from 4.6.0 to 4.7.1</li>
<li>#2036 bump browserup-proxy-core from 2.2.5 to 2.2.6</li>
<li>#2058 bump httpclient5 from 5.2 to 5.2.1</li>
<li>bump slf4j from 2.0.4 to 2.0.5</li>
</ul>
<p><br /></p>
<h3 id="дочерние-проекты">Дочерние проекты</h3>
<p>Также зарелизили наши дочерние проекты:</p>
<ul>
<li><a href="https://github.com/selenide/selenide-appium/releases/tag/v2.4.0">selenide-appium 2.4.0</a></li>
<li><a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.3.3">selenide-selenoid 2.3.3</a></li>
</ul>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li>Selenide Tutorial: <a href="https://www.youtube.com/watch?v=5vrYMfsxkGY&list=PL9ok7C7Yn9A9YyRISFrxHdaxb5qqrxp_i&index=4&ab_channel=TestingMiniBytes">Replacement for Selenium?</a> на канале Testing Mini Bytes</li>
<li><a href="https://oleksandr-podoliako.medium.com/test-automation-framework-for-ui-testing-with-java-fddd1e3fd75b">Test automation framework for UI testing with java</a> от Oleksandr Podoliako</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.10.02022-11-21T00:00:00+00:00http://ru.selenide.org/2022/11/21/selenide-6.10.0
<p><br /></p>
<h1 id="недобрый-вечер">Недобрый вечер!</h1>
<p>Вышел новый релиз <a href="https://github.com/selenide/selenide/milestone/167?closed=1">Selenide 6.10.0</a>.</p>
<ul class="blogpost-menu">
<li><a href="#slow-download-in-firefox">Улучшили алгоритм скачивания файлов</a></li>
<li><a href="#fail-download-early">Ускорили отрицательное скачивание файла</a></li>
<li><a href="#select-options-using-javascript">Выбираем опции через JavaScript</a></li>
<li><a href="#make-click-chainable">Сделали клик “чейнебл”</a></li>
<li><a href="#fix-size-for-new-tabs">Размер окна для новых вкладок</a></li>
<li><a href="#encode-basic-auth-credentials-in-url">BasicAuth со спецсимволами</a></li>
<li><a href="#news">Новости</a></li>
</ul>
<h3 id="slow-download-in-firefox">Улучшили алгоритм скачивания файлов</h3>
<p>Один из алгоритмов скачивания в Selenide - это FOLDER.
Чтобы скачать файл, он кликает нужную кнопку и ждёт, пока в папке “Downloads” появится нужный файл.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">еноты</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#stolenRaccoonsReport"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">));</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">еноты</span><span class="o">).</span><span class="na">hasName</span><span class="o">(</span><span class="s">"еноты.xls"</span><span class="o">);</span>
</code></pre></div></div>
<p>Проблема в том, что этот алгоритм не очень хорошо работал в Firefox, когда скачивание идёт очень медленно.
Оказалось, что Firefox сразу создаёт в папке два файла: “еноты.xls” и “еноты.xls.part”, и оба пустые.
И лишь затем начинает их потихоньку заполнять.</p>
<p><br />
В общем, наш алгоритм стал умнее. Теперь он</p>
<ol>
<li>Ждёт, когда в папке появятся хоть какие-нибудь подходящие файлы.</li>
<li>Ждёт, когда пропадут все файлы <code class="language-plaintext highlighter-rouge">*part</code> (в Firefox).</li>
<li>Ждёт, когда пропадут все файлы <code class="language-plaintext highlighter-rouge">*crdownload</code> (в Chrome).</li>
<li>Ждёт, когда все файлы замрут как минимум на секунду (в остальных браузерах).</li>
</ol>
<p>Такой подход должен обеспечить более надёжное скачивание файлов.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1989">issue 1989</a> и <a href="https://github.com/selenide/selenide/pull/2003">PR 2003</a>.</p>
<p><br /></p>
<h3 id="fail-download-early">Ускорили отрицательное скачивание файла</h3>
<p>Часто люди выставляют большой таймаут на скачивание файла. Особенно если файл большой:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">video</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#skaebova"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">)</span>
<span class="o">.</span><span class="na">withTimeout</span><span class="o">(</span><span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">99</span><span class="o">))</span>
<span class="o">.</span><span class="na">withName</span><span class="o">(</span><span class="s">"пыня мобшиза.MP4"</span><span class="o">));</span>
</code></pre></div></div>
<p>Но незачем ждать так долго, если скачивание даже не началось. Например, если клик тупо не попал по кнопке
(как это бывает, я рассказывал в <a href="https://www.youtube.com/watch?v=elQ2LGGU2bg&ab_channel=DEVCLUB.EU&t=21m32s">видео “Flaky tests”</a>.</p>
<p>Для ускорения падения теперь можно задать второй параметр “increment timeout”:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">video</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#skaebova"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">)</span>
<span class="o">.</span><span class="na">withTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">99</span><span class="o">))</span>
<span class="o">.</span><span class="na">withIncrementTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">2</span><span class="o">))</span>
<span class="o">.</span><span class="na">withName</span><span class="o">(</span><span class="s">"пыня мобшиза.MP4"</span><span class="o">));</span>
</code></pre></div></div>
<p>В этом случае общий таймаут на скачивание 99 секунд, НО если в течение 2 секунд не было никаких изменений в папке
для скачивания, то метод выкинет ошибку сразу.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1990">issue 1990</a> и <a href="https://github.com/selenide/selenide/pull/2023">PR 2023</a>.</p>
<p><br /></p>
<h3 id="select-options-using-javascript">Выбираем опции в <code class="language-plaintext highlighter-rouge"><select></code> через JavaScript</h3>
<p>Это должно сделать работу с селектами быстрее. И теперь селенид выкидывает более подробную ошибку, если <code class="language-plaintext highlighter-rouge"><select></code> (или <code class="language-plaintext highlighter-rouge"><option></code>) оказался <code class="language-plaintext highlighter-rouge">disabled</code>.</p>
<p>Например, для такого селекта:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><select</span> <span class="na">id=</span><span class="s">"region"</span><span class="nt">></span>
<span class="nt"><option</span> <span class="na">value=</span><span class="s">"belgorod"</span><span class="nt">></span>Belgorod<span class="nt"></option></span>
<span class="nt"><option</span> <span class="na">value=</span><span class="s">"kherson"</span> <span class="na">disabled</span><span class="nt">></span>Kherson<span class="nt"></option></span>
<span class="nt"><option</span> <span class="na">value=</span><span class="s">"zaporozhia"</span> <span class="na">disabled</span><span class="nt">></span>Zaporozhia<span class="nt"></option></span>
<span class="nt"></select></span>
</code></pre></div></div>
<p>Попытка выбрать <code class="language-plaintext highlighter-rouge">disabled</code> опцию:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#region"</span><span class="o">).</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"Kherson"</span><span class="o">);</span>
</code></pre></div></div>
<p>выдаст понятную ошибку с подробным объяснением:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Invalid</span> <span class="n">element</span> <span class="n">state</span> <span class="o">[</span><span class="err">#</span><span class="n">region</span><span class="o">/</span><span class="n">option</span><span class="o">[</span><span class="nl">text:</span><span class="nc">Kherson</span><span class="o">]]:</span> <span class="nc">Cannot</span> <span class="n">select</span> <span class="n">a</span> <span class="n">disabled</span> <span class="n">option</span>
</code></pre></div></div>
<p>Раньше эта ошибка была не такой подробной:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">UnsupportedOperationException</span><span class="o">:</span> <span class="nc">You</span> <span class="n">may</span> <span class="n">not</span> <span class="n">select</span> <span class="n">a</span> <span class="n">disabled</span> <span class="n">option</span>
</code></pre></div></div>
<p>(а ещё раньше такой тест вообще не падал, но и опцию не выбирал)</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1553">issue 1553</a> и <a href="https://github.com/selenide/selenide/pull/1876">PR 1876</a>.</p>
<blockquote>
<p>Отдельное спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1553">PR 1553</a>,
который хоть и не попал в селенид, но инициировал целую дискуссию и в селениде, и в самом селениуме, в т.ч. о том, за
что должен или не должен отвечать селениум, и что такое “просто фреймворк” и “опионейтед фреймворк”.</p>
</blockquote>
<p><br /></p>
<h3 id="make-click-chainable">Сделали <code class="language-plaintext highlighter-rouge">$.click(options)</code> “чейнебл”</h3>
<p>Люди часто жалуются, что метод <code class="language-plaintext highlighter-rouge">$.click()</code> имеет тип <code class="language-plaintext highlighter-rouge">void</code>, то есть его нельзя чейнить:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">click</span><span class="o">().</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span>
</code></pre></div></div>
<p>Увы, это мы исправить не можем, т.к. класс <code class="language-plaintext highlighter-rouge">SelenideElement</code> наследует метод <code class="language-plaintext highlighter-rouge">void click()</code> из селениумовского <code class="language-plaintext highlighter-rouge">WebElement</code>.</p>
<p>Но мы сделали “чейнебл” другой <code class="language-plaintext highlighter-rouge">click</code> метод, который с параметрами (т.н. “оверлоадед”).
Теперь хотя бы его можно “чейнить”:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">)</span>
<span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">())</span>
<span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">)</span>
<span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">withOffset</span><span class="o">(</span><span class="mi">42</span><span class="o">,</span> <span class="mi">42</span><span class="o">))</span>
<span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"alert"</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/2007">issue 2007</a> и <a href="https://github.com/selenide/selenide/pull/2008">PR 2008</a>.</p>
<p><br /></p>
<h3 id="fix-size-for-new-tabs">Исправили размер окна для новых вкладок</h3>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/2017">PR 2017</a>.</p>
<p>P.S. Исправили ещё раз в <a href="https://github.com/selenide/selenide/milestone/170?closed=1">Selenide 6.10.1</a></p>
<p><br /></p>
<h3 id="encode-basic-auth-credentials-in-url">Поддерживаем логин/пароль BasicAuth со спецсимволами</h3>
<p>Selenide умеет открывать сайты, защищённые паролем (т.н. BasicAuth):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">""</span><span class="o">,</span> <span class="s">"Královec"</span><span class="o">,</span> <span class="s">"is Czechia /:)"</span><span class="o">));</span>
</code></pre></div></div>
<p>Эти логин-пароль добавляются либо в http заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> (если прокси включён), либо в URL (если прокси выключен).</p>
<p>Недавно я обнаружил, что второй способ работает некорректно, если логин или пароль содержат спецсимволы.
При добавлении в URL эти символы не экранировались, и получался невалидный URL:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://Královec:is Czechia /:)@127.0.0.1:4405/basic-auth/hello
</code></pre></div></div>
<p>и браузер не мог открыть страницу.</p>
<blockquote>
<p>Мне по-настоящему неловко, что я как-то не увидел этой страницы. Надеюсь, вы меня простите.</p>
</blockquote>
<p>В общем, теперь такие хитрые пароли тоже должны работать.
Селенид экранирует все спецсимволы и генерирует корректный URL:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://Kr%C3%A1lovec:is+Czechia+%2F%3A%29@127.0.0.1:27663/basic-auth/hello
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/2020">issue 2020</a> и <a href="https://github.com/selenide/selenide/pull/2021">PR 2021</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>Selenium from 4.5.0 to 4.6.0, см. <a href="https://www.selenium.dev/blog/2022/selenium-4-6-0-released/">ченджлог</a></li>
<li>WebDriverManager from 5.3.0 to 5.3.1, см. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md#531---2022-11-04">ченджлог</a></li>
<li>BrowserUpProxy from 2.2.3 to 2.2.5, см. <a href="https://github.com/valfirst/browserup-proxy/blob/master/CHANGELOG.md">ченджлог</a></li>
<li>Netty from 4.1.82.Final to 4.1.85.Final</li>
<li>LittleProxy from 2.0.13 to 2.0.14, см. <a href="https://github.com/LittleProxy/LittleProxy/milestone/19?closed=1">ченджлог</a></li>
<li>#2014 Bump httpclient5 from 5.1.3 to 5.2, см. <a href="https://github.com/selenide/selenide/pull/2014">PR 2014</a></li>
<li>#2025 bump slf4j from 2.0.3 to 2.0.4, см. <a href="https://github.com/selenide/selenide/pull/2025">PR 2025</a></li>
</ul>
<p><br /></p>
<h3 id="дочерние-проекты">Дочерние проекты</h3>
<p>Также зарелизили наши дочерние проекты:</p>
<ul>
<li><a href="https://github.com/selenide/selenide-appium/releases/tag/v2.3.0">selenide-appium 2.3.0</a></li>
<li><a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.3.2">selenide-selenoid 2.3.2</a></li>
</ul>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li>JetBrains выпустила AQUA - новую <a href="https://www.jetbrains.com/aqua/">IDE для автотестов</a>! Клёвое лого!</li>
<li>В Selenium появился свой встроенный <a href="https://www.selenium.dev/blog/2022/introducing-selenium-manager/">аналог WebDriverManager</a>. Кто-то уже попробовал?</li>
<li>Новый фреймворк <a href="https://gitlab.com/brewcode/selenide-pages">Selenide Pages</a> на базе Selenide от Maxim Kochetkov. Клёвое лого!</li>
</ul>
<h3 id="my-videos">Мои свежие видосы</h3>
<p>Появилось видео моих осенних докладов из таллиннского девклуба:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=VtX7IpCHMS8&ab_channel=DEVCLUB.EU">Как законтрибьютить в опенсорс, чтобы не сгореть со стыда</a></li>
<li><a href="https://www.youtube.com/watch?v=JKxzELiwO_o&ab_channel=DEVCLUB.EU">WTF Thread Pools</a> - как накосячить с потоками в Java (с зубодробительными примерами из Selenium)</li>
</ul>
<h3 id="videos">Видосы и читосы</h3>
<ul>
<li>Видос <a href="https://www.youtube.com/watch?v=_X0rL1eUMK0&ab_channel=OlehPendrak">Чтение логов из браузера через Selenide</a> от Oleh Pendrak</li>
<li>Пост <a href="https://www.linkedin.com/pulse/all-selenide-muhammad-naeem/">All About Selenide</a> от Muhammad Naeem</li>
<li>Видос <a href="https://morioh.com/p/018678871de9">Working with web elements using Selenide</a> от Automation Bro</li>
<li>Пост <a href="https://dev.to/automationbro/page-object-model-selenide-tutorial-series-g3g">Page Object Model</a> из серии “Selenide Tutorial Series” от Automation Bro</li>
<li>Пост <a href="https://habr.com/ru/company/innotech/blog/696140/">Как кастомизировать UI артефакты для Selenide + Selenoid + Allure (with TestOPS)</a> на Хабре</li>
</ul>
<p><br /></p>
<h3 id="statistics">Статистика</h3>
<ul>
<li>Наша <a href="https://www.linkedin.com/groups/9154550/">группа в LinkedIn</a> насчитывает уже 176 участников. Присоединяйтесь!</li>
<li>Подъехала свежая статистика скачиваний Селенида. Перевалили за 470 тыщ!</li>
</ul>
<center>
<img src="/images/2022/11/selenide.downloads.png" width="800" />
</center>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.9.02022-10-07T00:00:00+00:00http://ru.selenide.org/2022/10/07/selenide-6.9.0
<p><br /></p>
<h1 id="привет">Привет!</h1>
<p>Позвольте прервать ваш думскроллинг хорошей новостью.</p>
<p>Вы выпустили <a href="https://github.com/selenide/selenide/milestone/166?closed=1">Selenide 6.9.0</a>!
В основном прокачали прокси и обновили селениум.</p>
<ul class="blogpost-menu">
<li><a href="#proxy-mock-response">Подменяем ответ сервера в прокси</a></li>
<li><a href="#secure-authorization-header">Заголовок авторизации</a></li>
<li><a href="#resolve-proxy-hostname">Подкрутили имя прокси хоста</a></li>
<li><a href="#upgrade-to-selenium-4.5.0">Обновились на Selenium 4.5.0</a></li>
<li><a href="#remove-opera-support">Выкинули поддержку Opera</a></li>
<li><a href="#news">Новости</a></li>
</ul>
<h3 id="proxy-mock-response">Научили прокси подменять ответ сервера</h3>
<p>Как вы знаете, селенид умеет запускать свой встроенный прокси между браузером и тестируемым приложением. До сих пор мы
использовали его в основном для отслеживания запросов (логирования, скачивания файлов), но теперь ещё и добавили возможность
подменить ответ сервера. Это удобно, например, чтобы замокать ответ какого-то сервиса.</p>
<p>Давайте рассмотрим на каком-нибудь простеньком абстрактном примере.</p>
<h4 id="пример">Пример</h4>
<p>Допустим, вы тестируете сайт, показывающий результаты референдума.
Это html-страница по адресу, скажем, https://referendum.ru, которая обращается к сервису https://cik.ru и тянет с него
json с результатами голосования.</p>
<p>Поскольку результаты заранее неизвестны, а сайт протестировать нужно здесь и сейчас, мы хотим проверить, как он будет
выглядеть в разных пограничных случаях (corner cases).</p>
<h4 id="тест">Тест</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">();</span>
<span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">responseMocker</span><span class="o">().</span><span class="na">mockText</span><span class="o">(</span><span class="s">"cik-mock"</span><span class="o">,</span>
<span class="n">urlStartsWith</span><span class="o">(</span><span class="no">GET</span><span class="o">,</span> <span class="s">"https://cik.ru/api/gov/no/referendum"</span><span class="o">),</span>
<span class="o">()</span> <span class="o">-></span> <span class="s">"{votes: 2133326, for: 99.23, against: 0.77}"</span><span class="o">);</span>
<span class="n">open</span><span class="o">(</span><span class="s">"https://referendum.ru"</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#votesFor"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"99.23%"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#h3"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Не только порадовали, но и удивили"</span><span class="o">));</span>
</code></pre></div></div>
<h4 id="детали">Детали</h4>
<p>Остановимся подробнее на первом параметре <code class="language-plaintext highlighter-rouge">cik-mock</code>. Это <em>имя</em> мока.
Нужно оно для того, чтобы после теста его отменить (чтобы случайно не повлиять на следующие тесты):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@AfterEach</span>
<span class="kt">void</span> <span class="nf">tearDown</span><span class="o">()</span> <span class="o">{</span>
<span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">responseMocker</span><span class="o">().</span><span class="na">reset</span><span class="o">(</span><span class="s">"cik-mock"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Ну или для надёжности можно отменить <em>все</em> моки:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@AfterEach</span>
<span class="kt">void</span> <span class="nf">tearDown</span><span class="o">()</span> <span class="o">{</span>
<span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">responseMocker</span><span class="o">().</span><span class="na">resetAll</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<h4 id="ограничения">Ограничения</h4>
<p>Если страница и сервис работают на одном домене, ограничений нет.
А вот если страница обращается к сервису на другом домене, то мок сработает только при выполнении условий:</p>
<ul>
<li>страница и сервис бегают на https (не http)</li>
<li>сервис работает и физически доступен из прокси</li>
</ul>
<h4 id="в-общем">В общем</h4>
<p>Использование моков - мощная техника, позволяющая вам проверять разные пограничные условия, которые иначе повторить
сложно или невозможно (“146% голосов за”, “никто не явился на выборы”, “сервис недоступен” и т.д.)</p>
<blockquote>
<p>Не стесняйтесь своего желания что-то подправлять, подчищать, добавлять.
Будет так, как вы захотите.</p>
</blockquote>
<p>См. <a href="https://github.com/selenide/selenide/issues/1254">issue 1254</a> и <a href="https://github.com/selenide/selenide/pull/1978">PR 1978</a>.</p>
<p><br /></p>
<h3 id="secure-authorization-header">Посылаем заголовок авторизации только нужному домену</h3>
<p>Некоторые сайты, которые вам приходится тестировать, закрыты от внешнего мира с помощью BasicAuth.
Селенид позволяет их открыть с помощью метода open с доп. параметрами:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://referendum.ru/admin"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">"vlastelin"</span><span class="o">,</span> <span class="s">"gojda!!!"</span><span class="o">));</span>
</code></pre></div></div>
<p>Этот метод умеет обходить BasicAuth:</p>
<ul>
<li>если прокси выключен - добавлением логина-пароля в URL,</li>
<li>а если прокси включен - добавлением заголовка <code class="language-plaintext highlighter-rouge">Authorization</code> в запросы от браузера к серверу.</li>
</ul>
<p>Но вот незадача. Недавно мы обнаружили, что селенид слал заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> не только тестируемому приложению, но
и всем сторонним сервисам, к которым оно могло обращаться (например, S3 или Google authentication).</p>
<p>Теперь селенид будет посылать заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> только на нужный домен, но и вам придётся указать этот домен в конструкторе:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://referendum.ru/admin"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">"referendum.ru"</span><span class="o">,</span> <span class="s">"vlastelin"</span><span class="o">,</span> <span class="s">"gojda!!!"</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1974">issue 1974</a> и <a href="https://github.com/selenide/selenide/pull/1975">PR 1975</a>.</p>
<p><br /></p>
<h3 id="resolve-proxy-hostname">Подкрутили имя прокси хоста</h3>
<p>Когда вы включаете прокси в селениде (например, через <code class="language-plaintext highlighter-rouge">Configuration.proxyEnabled = true</code>),
селенид запускает встроенный прокси-сервер на случайном порту. И открывает браузер с указанием использовать
прокси-сервер <code class="language-plaintext highlighter-rouge">HOST:PORT</code>. Вопрос, какой HOST тут использовать?</p>
<p>До сих пор по умолчанию использовался результат команды <code class="language-plaintext highlighter-rouge">ClientUtil.getConnectableAddress()</code> (это поведение по умолчанию в BrowserUpProxy).
Но с этого релиза будет использоваться <code class="language-plaintext highlighter-rouge">new NetworkUtils().getNonLoopbackAddressOfThisMachine()</code> (это метод из Selenium).
Мне лень разбираться, в чём там их специфика, но
на моём компе они иногда выдают разный результат:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">new NetworkUtils().getNonLoopbackAddressOfThisMachine()</code> -> <code class="language-plaintext highlighter-rouge">192.168.0.18</code></li>
<li><code class="language-plaintext highlighter-rouge">ClientUtil.getConnectableAddress()</code> -> <code class="language-plaintext highlighter-rouge">127.0.0.1</code></li>
</ul>
<p>И первый однозначно лучше, когда браузер бежит на другой машине или в контейнере.
Под адресом <code class="language-plaintext highlighter-rouge">127.0.0.1</code> прокси просто не будет виден с другой машины.</p>
<blockquote>
<p>Кстати, если этот механизм почему-то вам не помог, вы всегда можете явно задать имя хоста для прокси через настройку
<code class="language-plaintext highlighter-rouge">Configuration.proxyHost = "my.comp.eu";</code></p>
</blockquote>
<p>См. <a href="https://github.com/selenide/selenide/pull/1970">PR 1970</a>.</p>
<p><br /></p>
<h3 id="upgrade-to-selenium-4.5.0">Обновились на Selenium 4.5.0</h3>
<p>Из заметных изменений:</p>
<ul>
<li>удалили поддержку браузера Opera</li>
<li>добавили альтернативную реализацию вебдрайвера на JDK 11 HTTP client вместо Netty client</li>
<li>Добавили проверки на <code class="language-plaintext highlighter-rouge">disabled</code> в класс <code class="language-plaintext highlighter-rouge">Select</code> (больше не получится выбрать задизейбленный <code class="language-plaintext highlighter-rouge">option</code> в выпадающем списке)</li>
<li>Убрали имя хоста из селениумовских эксепшенов
<blockquote>
<p>цуко, я просил об этом <a href="https://github.com/SeleniumHQ/selenium/issues/489">ещё в 2015 году</a>!!!</p>
</blockquote>
</li>
</ul>
<p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог</a> и
<a href="https://github.com/selenide/selenide/pull/1967">PR 1967</a>.</p>
<p><br /></p>
<h3 id="remove-opera-support">Выкинули поддержку Opera</h3>
<p>Следствие предыдущего пункта. Селениум выкинул - и мы выкинули.
Тестировать в опере не имеет особого смысла, т.к. это по сути тот же Chrome.
Если очень хочется - можно, просто используйте chromedriver.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1967">PR 1967</a>.</p>
<p><br /></p>
<h3 id="do-not-log-get-alias">Убрали <code class="language-plaintext highlighter-rouge">getAlias</code> из отчётов</h3>
<p>Спасибо <a href="https://github.com/reserved-word">Reserved Word</a> за <a href="https://github.com/selenide/selenide/pull/1971">PR 1971</a>.</p>
<p><br /></p>
<h3 id="restore-setting-connection-timeout">Вернули настройку “connection timeout”</h3>
<p>Это мало кому нужно, так что смело пропускайте.</p>
<p class="small">Когда-то у нас было две настройки для http клиента вебдрайвера: “connection timeout” и “read timeout”.
Первую пришлось убрать при обновлении на Selenium 4, потому что её там выпилили.
Теперь её реанимировали в Selenium 4.5.0, ну и мы реанимировали.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1977">PR 1977</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>LittleProxy from 2.0.12 to 2.0.13</li>
<li>slf4j from 2.0.2 to 2.0.3</li>
</ul>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li>Доклад Сергея Брита <a href="https://www.youtube.com/watch?v=obsJWnBsYwk&ab_channel=SQAANALYSTSECR">Selenide + Playwright Java = объединяй и властвуй</a> - конференция SQA Days EA, 01.10.2022</li>
<li>Доклад Алексея Виноградова <a href="https://www.youtube.com/watch?v=MLxf9q9qXu4&ab_channel=%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D0%B8%D0%B2%D0%9A%D0%BE%D0%BD%D1%82%D1%83%D1%80%D0%B5">Spinner-driven-development</a></li>
</ul>
<p><br /></p>
<p>Айтишники, учите английский <a href="https://www.youtube.com/watch?v=laIGavOMcw8&ab_channel=FoilArmsandHog">по ирландским комикам</a>!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.8.12022-09-27T00:00:00+00:00http://ru.selenide.org/2022/09/27/selenide-6.8.1
<p><br /></p>
<h1 id="добре">Добре!</h1>
<p>Ловите мини-багфикс <a href="https://github.com/selenide/selenide/milestone/165?closed=1">Selenide 6.8.1</a>.</p>
<p>Это касается только тех, кто напрямую вызывает в своих тестах</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nc">RemoteWebDriver</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="n">options</span><span class="o">)</span>
</code></pre></div></div>
<p>и словил <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code> после обновления на <a href="/2022/09/24/selenide-6.8.0/">Selenide 6.8.0</a>.</p>
<p><br /></p>
<h3 id="pre-history">Предыстория</h3>
<p>Что такое OpenTelemetry?
Это какая-то штука, которую зачем-то добавили в Selenium 4, но людям забыли сообщить, зачем это вообще нужно и как
этим пользоваться. Ну и по факту всем пофиг, никто не пользуется.</p>
<p>Да и в самом селениде мы её явно выпилили в <a href="https://github.com/selenide/selenide/pull/1763">PR 1763</a>.</p>
<blockquote>
<p>Зависимость OpenTelemetry в Selenium - как специальная военная операция. Никто не знает, зачем она нужна.</p>
</blockquote>
<h3 id="the-problem">Проблема</h3>
<p>В общем, у нормальных пользователей Селенида проблемы и не возникнет.</p>
<p>НО
если вы в своих тестах явно вызываете конструктор <code class="language-plaintext highlighter-rouge">new RemoteWebDriver(url, options)</code>, то он, к сожалению, требует зависимости
OpenTelemetry. И после обновления на <a href="/2022/09/24/selenide-6.8.0/">Selenide 6.8.0</a> такие пользователи начали
получать <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code>.</p>
<p><br /></p>
<h3 id="temporary-solution">Временное решение</h3>
<p>Добавьте параметр <code class="language-plaintext highlighter-rouge">false</code> в конструктор: <code class="language-plaintext highlighter-rouge">new RemoteWebDriver(url, options, false)</code>.
Этот <code class="language-plaintext highlighter-rouge">false</code> говорит “не используй телеметрию”.</p>
<p><br /></p>
<h3 id="restored-opentelemetry">Демобилизовали зависимость OpenTelemetry</h3>
<p>В общем, мы по-быстрому выпустили Selenide 6.8.1, в котором вернули OpenTelemetry.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1965">issue 1965</a> и <a href="https://github.com/selenide/selenide/pull/1966">PR 1966</a>.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.8.02022-09-24T00:00:00+00:00http://ru.selenide.org/2022/09/24/selenide-6.8.0
<p><br /></p>
<h1 id="недоброе-утро">Недоброе утро!</h1>
<p>Пока в мире творится здипец, мы выпускаем релиз <a href="https://github.com/selenide/selenide/milestone/161?closed=1">Selenide 6.8.0</a>.<br />
Фич немного, больше обновление зависимостей.</p>
<ul class="blogpost-menu">
<li><a href="#deep-shadow-selectors">Рекурсивный поиск по shadow dom</a></li>
<li><a href="#add-page-without-class">Метод page() без параметров</a></li>
<li><a href="#as-annotation">Аннотация @As</a></li>
<li><a href="#news">Новости</a></li>
</ul>
<h3 id="deep-shadow-selectors">Рекурсивный поиск по shadow dom</h3>
<p>Как вы знаете, в селениде <a href="/2020/03/18/selenide-5.10.0">есть возможность</a> искать элементы внутри shadow dom.
И даже в нескольких вложенных shadow dom:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"#reportButton"</span><span class="o">,</span>
<span class="s">"#shadow-host"</span><span class="o">,</span> <span class="s">"#inner-shadow-host"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<p>Хорошо, что это возможно, но это по-прежнему требует ваших временных затрат на то, чтобы изучить dom и найти точные
локаторы для всех этих теневых элементов.</p>
<p>Теперь вы сможете сэкономить время с помощью нового метода <code class="language-plaintext highlighter-rouge">shadowDeepCss</code> - он ищет элемент во всех shadow root по всему dom.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowDeepCss</span><span class="o">(</span><span class="s">"#reportButton"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<p>Естественно, остаётся риск, что такой “слишком широкий” поиск может найти и не тот элемент, если в ваших теневых областях есть
несколько элементов в искомым локатором. В этом случае придётся вернуться к более точечному поиску.</p>
<blockquote>
<p>Как и в случае с базами данных и сосисками, “внутрь лучше не заглядывать” - там начинка из мудрёного джаваскрипта для поиска по дереву.</p>
</blockquote>
<p>См. <a href="https://github.com/selenide/selenide/issues/1946">issue 1946</a>.</p>
<p>Спасибо</p>
<ol>
<li><a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1947">PR 1947</a>, а также</li>
<li><a href="https://github.com/Georgegriff">Georgegriff</a> за проект <a href="https://github.com/Georgegriff/query-selector-shadow-dom">query-selector-shadow-dom</a>, откуда мы этот код и спёрли.</li>
</ol>
<p><br /></p>
<h3 id="add-page-without-class">Добавили метод <code class="language-plaintext highlighter-rouge">page()</code>, не требующий параметр <code class="language-plaintext highlighter-rouge">Class</code></h3>
<p>Дорогие фанаты пэдж обжектов, садитесь поудобнее, это для вас. В вашей жизни наступает переломный момент.</p>
<p>Теперь в метод <code class="language-plaintext highlighter-rouge">page(Class pageObjectClass)</code> больше не нужно передавать параметр <code class="language-plaintext highlighter-rouge">Class</code>. Да-да!
Вместо</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelectsPage</span> <span class="n">page</span> <span class="o">=</span> <span class="n">page</span><span class="o">(</span><span class="nc">SelectsPage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</code></pre></div></div>
<p>вы сможете писать просто</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelectsPage</span> <span class="n">page</span> <span class="o">=</span> <span class="n">page</span><span class="o">();</span>
</code></pre></div></div>
<p>Раньше я думал, что Java так не умеет, но потом нашёл <a href="https://twitter.com/tagir_valeev/status/1262763570904719361">хак</a>.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1961">PR 1961</a> и
спасибо <a href="https://twitter.com/tagir_valeev">Тагиру Валееву</a> за хак.</p>
<p><br /></p>
<h3 id="as-annotation">Добавили аннотацию <code class="language-plaintext highlighter-rouge">@As</code></h3>
<p>… для задания алиасов полям пэдж обжекта.</p>
<h4 id="1-без-алиасов">1. Без алиасов</h4>
<p>Если у вас есть пэдж обжект с аннотациями типа <code class="language-plaintext highlighter-rouge">@FindBy</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PageObject</span> <span class="o">{</span>
<span class="nd">@FindBy</span><span class="o">(</span><span class="n">xpath</span><span class="o">=</span><span class="s">"//table/div[3]/span[4]/h1"</span><span class="o">)</span>
<span class="nc">SelenideElement</span> <span class="n">header</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>И тест типа <code class="language-plaintext highlighter-rouge">page.header.shouldBe(visible)</code>,</p>
<p>То в отчётах он выглядит громоздко и непонятно:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| open | https://devclub.eu | PASS |
| By.xpath: //table/div[3]/span[4]/h1 | should be(visible) | FAIL |
</code></pre></div></div>
<p>А если тест упадёт, то в сообщении не очень-то понятно, о каком элементе идёт речь:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">:</span> <span class="c1">//table/div[3]/span[4]/h1}</span>
</code></pre></div></div>
<p>Люди часто хотят дать какое-то человеческое название этому элементу, чтобы не видеть в отчётах длинный непонятный xpath.</p>
<h4 id="2-сопротивление">2. Сопротивление</h4>
<p>Я всегда был против этого.
Я считаю, что не только отчёты, но и сами тесты должны быть читабельными. Я за чистый код.
Не нравится уродливый xpath - ну так измени его, а не прячь!</p>
<p>Но потом я решил дать заднюю.</p>
<blockquote>
<p>Ведь если мы не дадим заднюю, то они могут дать заднюю, а вот если мы её дадим, они её не дадут.
Заднюю никто не даст больше, кроме нас.</p>
</blockquote>
<p>Убедительно же! Поэтому в версии <a href="/2020/12/26/selenide-5.17.0/">Selenide 5.17.0</a> мы добавили метод <code class="language-plaintext highlighter-rouge">$.as("alias")</code>.</p>
<h4 id="3-с-алиасами">3. С алиасами</h4>
<p>А с этого релиза - ещё и можете навесить аннотацию <code class="language-plaintext highlighter-rouge">@As</code> на поля поля пэджобжектов.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PageObject</span> <span class="o">{</span>
<span class="nd">@As</span><span class="o">(</span><span class="s">"Large header"</span><span class="o">)</span>
<span class="nd">@FindBy</span><span class="o">(</span><span class="n">tagName</span> <span class="o">=</span> <span class="s">"h1"</span><span class="o">)</span>
<span class="nc">SelenideElement</span> <span class="n">header1</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>В отчётах он будет выглядеть короче:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| open | https://devclub.eu | PASS |
| title | should be(visible) | FAIL |
</code></pre></div></div>
<p>А при падении теста будет легче понять, о каком элементе речь:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="s">"title"</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">:</span> <span class="c1">//table/div[3]/span[4]/h1}</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1903">issue 1903</a> и <a href="https://github.com/selenide/selenide/pull/1956">PR 1956</a>.</p>
<blockquote>
<p>Не забывайте: врут все.</p>
<p>Документация врёт, комментарии врут, пользователи врут.
Рано или поздно соврёт и алиас. Локаторы поменяли, а алиас забыли. Или элемент скопировали, а алиас забыли поменять. И т.д.</p>
<p>Отчёты - это хорошо, но в конечном итоге верить можно только коду.</p>
</blockquote>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>BrowserUpProxy from 2.2.2 to 2.2.3</li>
<li>LittleProxy from 2.0.11 to 2.0.12</li>
<li>Netty from 4.1.80.Final to 4.1.82.Final</li>
<li>slf4j from 2.0.0 to 2.0.2</li>
<li>JUnit from 5.9.0 to 5.9.1 – см. <a href="https://junit.org/junit5/docs/5.9.1/release-notes/">release notes</a></li>
</ul>
<blockquote>
<p>Объявлена частичная мобилизация зависимостей!</p>
</blockquote>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li>Продолжается серия от Dilpreet Johal - теперь <a href="https://dev.to/automationbro/upload-file-with-selenide-1f2a">Upload file with Selenide</a></li>
<li><a href="https://sdet-tomaszbuga.medium.com/test-automation-framework-selenium-with-java-alchemy-or-translating-jira-with-selenide-with-e8831ebfe337">Alchemy or Translating JIRA with Selenide</a> от Tomasz Buga</li>
<li>Вышла Java 19.</li>
</ul>
<h3 id="statistics">Статистика</h3>
<p>Свежая статистика скачиваний Селенида нас только радует. Перевалили за 400 тыщ!</p>
<center>
<img src="/images/2022/09/selenide.downloads.png" width="800" />
</center>
<p><br /></p>
<p>Смотрите <a href="https://www.youtube.com/watch?v=m6TwkNAmI3A&ab_channel=MasyanyaKuvaeva">Масяню!</a></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.7.42022-09-05T00:00:00+00:00http://ru.selenide.org/2022/09/05/selenide-6.7.4
<p><br /></p>
<h1 id="привет">Привет!</h1>
<p><br />
Я календарь перевернул,
и снова релиз <a href="https://github.com/selenide/selenide/milestone/164?closed=1">Selenide 6.7.4</a>.</p>
<p><br /></p>
<h3 id="add-remote-read-setting">Добавили настройку <code class="language-plaintext highlighter-rouge">remoteReadTimeout</code></h3>
<p>Ещё недавно <a href="https://www.youtube.com/watch?v=iIIsZRHya-w&ab_channel=DEVCLUB.EU">я жаловался</a> на то, что в селениде и так
слишком много настроек, но нам не оставили выбора.</p>
<p>В общем, мы добавили ещё одну настройку <code class="language-plaintext highlighter-rouge">Configuration.remoteReadTimeout</code> (также известную как <code class="language-plaintext highlighter-rouge">-Dselenide.remoteReadTimeout</code>).</p>
<p>Она пригодится тем, кто запускает браузер удалённо, но при этом браузеров на сервере не хватает, и приходится ждать в
очереди. И поэтому дефалтового таймаута в 1.5 минуты оказывается недостаточно, чтобы элементарно открыть браузер.</p>
<blockquote>
<p>Лично я называю это адом на проекте, но кто мы такие, чтобы мешать людям добровольно жариться на сковородке…</p>
</blockquote>
<p>В общем, теперь вы можете указать какой угодно таймаут в билд-скриптах, в командой строке и т.п.</p>
<p>Спасибо <a href="https://github.com/rodion-goritskov">Rodion Goritskov</a> за <a href="https://github.com/selenide/selenide/pull/1936">PR 1936</a>.</p>
<p><br /></p>
<p><br /></p>
<h3 id="fix-dead-threads-watchdog">Исправили мини-косяк в “Dead threads watchdog”</h3>
<p>Вы видели когда-нибудь <code class="language-plaintext highlighter-rouge">java.lang.IllegalStateException: Shutdown in progress</code> в логах?</p>
<p>Значит, не обновились на <a href="/2022/08/14/selenide-6.7.2/">Selenide 6.7.2</a> :)</p>
<p>В общем, обновляйтесь сразу на 6.7.4 - и всё будет <em>заерысь</em>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1942">issue 1942</a> и <a href="https://github.com/selenide/selenide/pull/1943">PR 1943</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li><a href="https://github.com/selenide/selenide/pull/1932">PR 1932</a> Bump Netty from 4.1.79 to 4.1.80</li>
</ul>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li><a href="https://blog.devgenius.io/selenide-101-in-5-minutes-2703086ee228">Selenide 101 in 5 minutes</a> by <a href="https://sophieer.medium.com/">Sophie R.</a></li>
<li><a href="https://www.youtube.com/watch?v=0vlV8_4EDAg&list=PL6AdzyjjD5HC4NJuc083bzFq86JekmASF&ab_channel=AutomationBro-DilpreetJohal">Selenide Java Tutorial Series</a> by <a href="https://www.youtube.com/c/AutomationBro">Dilpreet Johal</a></li>
<li><a href="https://mszeles.com/save-dozens-of-minutes-daily-during-writing-selenide-and-selenium-e2e-automated-tests-using-the-selenideium-element-inspector-chrome-extension">Selenideium Element Inspector</a> by <a href="https://hashnode.com/@mszeles">Miki Szeles</a></li>
<li><a href="https://mszeles.com/selenium-javascript-python-c-cypress-testcafe-playwright-squish-selector-generation-has-been-added-to-selenideium-element-inspector-v20">Selenideium Element Inspector 2.0</a> by <a href="https://hashnode.com/@mszeles">Miki Szeles</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.7.32022-08-27T00:00:00+00:00http://ru.selenide.org/2022/08/27/selenide-6.7.3
<p><br /></p>
<h1 id="привет">Привет!</h1>
<p><br />
Вы читаете пресс-релиз <a href="https://github.com/selenide/selenide/milestone/163?closed=1">Selenide 6.7.3</a> в пиратском переводе.</p>
<p>А что делать, <em>все хотят кушать</em>…</p>
<p><br /></p>
<h3 id="condition-partial-value">Добавили условие <code class="language-plaintext highlighter-rouge">partialValue</code></h3>
<p>По аналогии с <code class="language-plaintext highlighter-rouge">$.shouldHave(partialText("Добрый ко"))</code> теперь появилась и <code class="language-plaintext highlighter-rouge">$.shouldHave(partialValue("cola"))</code>.</p>
<p>Это если вы установили настройку <code class="language-plaintext highlighter-rouge">Configuration.textCheck = FULL_TEXT</code>, но хотите проверить значение какого-то инпута
или textarea частично, а не полностью.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1923">issue 1923</a> и <a href="https://github.com/selenide/selenide/pull/1924">PR 1924</a>.</p>
<p><br /></p>
<h3 id="condition-tag-name">Добавили условие <code class="language-plaintext highlighter-rouge">tagName</code></h3>
<p>Наверное, оно нечасто нужно, т.к. мы часто ищем элемент по тэгу, и уж потом проверяем другие атрибуты.
Да и вообще, тэг - это внутренности, которые пользователь не видит, поэтому, может, и проверять его не нужно.</p>
<p>Тем не менее, теперь вы можете проверять тэг:</p>
<p><code class="language-plaintext highlighter-rouge">$(".btn-primary").shouldHave(tagName("button"));</code></p>
<p>Или фильтровать коллекцию по тэгу:</p>
<p><code class="language-plaintext highlighter-rouge">$$(byText("Submit!")).filterBy(tagName("button"));</code></p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1928">issue 1928</a> и <a href="https://github.com/selenide/selenide/pull/1929">PR 1929</a>.</p>
<p><br /></p>
<h3 id="check-select-tag">Проверяем, что элемент - <code class="language-plaintext highlighter-rouge"><select></code></h3>
<p>… в методах <code class="language-plaintext highlighter-rouge">$.getSelectedText()</code> и <code class="language-plaintext highlighter-rouge">getSelectedValue()</code>.</p>
<p>Изначальная задумка этих методов была в том, чтобы найти выбранную опцию в селекте, и вернуть её текст или значение.</p>
<p>Но</p>
<ol>
<li>во-первых, из названия это неочевидно (и поэтому мы переименовали их в <code class="language-plaintext highlighter-rouge">$.getSelectedOptionText()</code> и <code class="language-plaintext highlighter-rouge">getSelectedOptionValue()</code>)</li>
<li>а во-вторых, эти методы можно было вызвать на любом другом элементе и получить непредсказуемое поведение.</li>
</ol>
<p>Теперь попытка вызова <code class="language-plaintext highlighter-rouge">$("div").getSelectedOptionText()</code> выкинет <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1934">PR 1934</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li><a href="https://github.com/selenide/selenide/pull/1932">PR 1932</a> Bump webdrivermanager from 5.2.3 to 5.3.0</li>
<li><a href="https://github.com/selenide/selenide/pull/1931">PR 1931</a> Bump slf4jVersion from 1.7.36 to 2.0.0</li>
<li><a href="https://github.com/selenide/selenide/pull/1921">PR 1921</a> Bump browserup-proxy-core from 2.2.1 to 2.2.2</li>
</ul>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li>Selenide puzzler <a href="/2022/08/22/selenide-puzzler/">Логическое И или ИЛИ?</a></li>
<li>Selenide упомянут в <a href="https://aglowiditsolutions.com/blog/top-java-frameworks/">Top Java Frameworks to Use in 2022</a></li>
</ul>
<p>И наконец, <strong>невиданное</strong>!</p>
<ul>
<li>Selenide упомянут на <a href="https://www.selenium.dev/ecosystem/">официальном сайте Selenium</a> в разделе “Экосистема”.</li>
</ul>
<p>Не может быть!</p>
<p>Не прошло и 10 лет!</p>
<p>А нет, прошло…</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Selenide puzzler2022-08-22T00:00:00+00:00http://ru.selenide.org/2022/08/22/selenide-puzzler
<p><br /></p>
<h1 id="привет">Привет!</h1>
<p>Вы любите пазлеры?
Соскучались по пазлерам?
В последний раз мы показывали пазлеры аж в 2017 году, кто не смотрел - <a href="https://www.youtube.com/watch?v=y-ZyxTWHH08&ab_channel=Heisenbug">смотрим</a>!</p>
<p>И вот наконец мы <a href="https://twitter.com/selenide/status/1560192824536088580">опубликовали в твиттере</a> новый пазлер:</p>
<blockquote>
<p>Одинаково ли работают эти строки? Или есть какая-то разница?</p>
</blockquote>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span><span class="o">.</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">,</span> <span class="n">enabled</span><span class="o">);</span>
<span class="mi">2</span><span class="o">.</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">enabled</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="варианты-ответов">Варианты ответов</h3>
<p>Люди в твиттере и чатиках накидали разных вариантов:</p>
<ul>
<li>обе строчки работают одинаково (самый популярный ответ)</li>
<li>первый случай: логическое or, второй: логический and</li>
<li>Второй работает на %ожидание по умолчанию% дольше.</li>
<li>Первый должен отработать с одним тайм-аутом, второй должен с двумя тайм-аутами</li>
<li>в первом случае упадёт, если отсутствует любое из условий, во втором - если не visible, но enabled.</li>
</ul>
<h3 id="правильный-ответ">Правильный ответ</h3>
<p>В большинстве случаев оба варианта работают одинаково.</p>
<p>Оба варианта - это логическое AND. В обоих вариантах селенид проверит, что элемент соответствует обоим условиям.</p>
<table>
<tbody>
<tr>
<td> </td>
<td><strong>enabled</strong></td>
<td><strong>disabled</strong></td>
</tr>
<tr>
<td><strong>видимый</strong></td>
<td>ok</td>
<td>nok</td>
</tr>
<tr>
<td><strong>невидимый</strong></td>
<td>nok</td>
<td>nok</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p>Но есть нюанс.</p>
<h3 id="нюанс">Нюанс</h3>
<p>Разница проявится в том случае, если элемент соответствует условиям не сразу, а через какое-то время.
Причём первому условию меньше, чем за 4 секунды, а второму - больше.
(Допустим, visible он становится через 3.5 секунды, а enabled - ещё через 3.5 секунды).</p>
<blockquote>
<p>Всё дело в том, что таймаут (по умолчанию 4 секунды) есть у метода <code class="language-plaintext highlighter-rouge">shouldHave</code>, а у проверок
(<code class="language-plaintext highlighter-rouge">visible</code>, <code class="language-plaintext highlighter-rouge">enabled</code>, <code class="language-plaintext highlighter-rouge">cssClass</code>, <code class="language-plaintext highlighter-rouge">text</code>) никакого таймаута нет - они просто умеют проверить, да или нет.</p>
</blockquote>
<h3 id="пример">Пример</h3>
<p>Откроем для примера <a href="https://selenide.org/traffic-light.html">Светофор</a>.
Светофор становится зелёным через 3.5 секунды, а текст на нём появляется ещё через 3.5 секунды.</p>
<p>Первый вариант проверяет, что светофор удовлетворит обоим условиям в течение 4 секунд:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#light"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"green"</span><span class="o">),</span> <span class="n">text</span><span class="o">(</span><span class="s">"GO!"</span><span class="o">));</span> <span class="c1">// таймаут 4 секунды</span>
</code></pre></div></div>
<p>И эта проверка упадёт, т.к. таймаут на эту проверку - 4 секунды, но элемент не приобретёт и цвет, и текст в течение 4 секунд.</p>
<p>А второй вариант не упадёт:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#light"</span><span class="o">)</span>
<span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"green"</span><span class="o">))</span> <span class="c1">// таймаут 4 секунды</span>
<span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"GO!"</span><span class="o">));</span> <span class="c1">// и плюс ещё 4 секунды</span>
</code></pre></div></div>
<p>Первая проверка дождётся, пока элемент станет зелёным (и он станет в течение 4 секунд).
После этого запустится вторая проверка и запустит новый таймаут с целью дождаться, пока у элемента появится текст.
И он дождётся.</p>
<p>Видимо, наиболее близкий к правильному был ответ “Первый должен отработать с одним тайм-аутом, второй должен с двумя тайм-аутами”.</p>
<h3 id="ваши-пазлеры">Ваши пазлеры</h3>
<p>Есть ли у вас свои пазлеры?
Присылайте их нам, давайте будем отгадывать вместе!</p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.7.22022-08-14T00:00:00+00:00http://ru.selenide.org/2022/08/14/selenide-6.7.2
<p><br /></p>
<h1 id="привет">Привет!</h1>
<p><br />
Мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/162?closed=1">Selenide 6.7.2</a> с исправлением
утечек памяти.</p>
<p>Да-да, мы серьёзные ребята, у нас тоже бывают утечки.</p>
<p>Но не пугайтесь, они некритические. Вряд ли кто-то из вас их вообще замечал.</p>
<h3 id="fix-selenide-memory-leak">Исправили утечки с shutdown hooks в Selenide</h3>
<p>Если в тесте много-много раз открывать и закрывать вебдрайвер</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">1000</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">open</span><span class="o">(</span><span class="s">"about:blank"</span><span class="o">);</span>
<span class="n">closeWebDriver</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>то расход памяти потихоньку растёт:</p>
<p><img src="https://user-images.githubusercontent.com/279773/184475079-bbf8ee0f-d245-43ec-8e72-a010262e099c.png" style="width: 100%" alt="Memory consumption: before" /></p>
<p><em>Красная линия, ты где?</em></p>
<p>А вот что случилось на самом деле.</p>
<p>Это просто сдетонировало несколько shutdown хуков, которые селенид складировал для каждого открытого вебдрайвера.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1917">issue 1917</a>
и <a href="https://github.com/selenide/selenide/pull/1919">PR 1919</a>.</p>
<p>После обновления на версию 6.7.2 память больше не растёт:</p>
<p><img src="https://user-images.githubusercontent.com/279773/184499249-f6e1e3cb-bc88-4c04-8a7b-a080f3f08887.png" style="width: 100%" alt="Memory consumption: after" /></p>
<p><em>Обновляемся на Selenide 6.7.2, выдыхаем и идём на пляж.</em></p>
<p><br /></p>
<h3 id="fix-little-proxy-memory-leak">Исправили утечку в LittleProxy</h3>
<p>Мы обновились на версию LittleProxy 2.0.11, в которой была исправлена ещё одна утечка памяти.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1918">PR 1918</a>
и <a href="https://github.com/LittleProxy/LittleProxy/pull/141">PR 141</a></p>
<p><br /></p>
<h3 id="upgrade-to-selenium-4.4.0">Обновились на Selenium 4.4.0</h3>
<p>в котором даже есть одно лично моё исправление, так-то! Которое, кстати, помогло нам исправить и следующую проблему.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1913">PR 1913</a>
и <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">Selenium changelog</a></p>
<p><br /></p>
<h3 id="fix-full-size-screenshot">Исправили плагин <code class="language-plaintext highlighter-rouge">full-size-screenshot</code></h3>
<p>В нашем новом плагине <code class="language-plaintext highlighter-rouge">full-size-screenshot</code> была одна известная проблема (на самом деле вызванная багой в селениуме):
если вы запускаете браузер удалённо и открываете несколько вкладок или окон, то селенид мог взять скриншот не с того окна.</p>
<p>Эта ошибка была исправлена в Selenium 4.4.0, и соответственно, у нас она теперь тоже исправится.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1920">PR 1920</a> и <a href="https://github.com/SeleniumHQ/selenium/pull/10811">PR 10811</a>.</p>
<p><br /></p>
<h3 id="news">Ссылки</h3>
<ul>
<li>Статья <a href="https://blogs.perficient.com/2022/06/22/working-efficiently-with-selenide-the-subset-of-selenium-webdriver/">Working Efficiently with Selenide</a> by Zainab Firdos, 22.06.2022</li>
<li>Серия руководств <a href="https://dev.to/automationbro/selenide-tutorial-series-58p5">Selenide Tutorial Series</a> by Dilpreet Johal, 13.06.2022</li>
<li>Видео-руководство <a href="https://www.youtube.com/watch?v=0vlV8_4EDAg&t=318s&ab_channel=AutomationBro-DilpreetJohal">Selenide Java Tutorial Series</a> by Automation Bro, 13.06.2022</li>
<li>Статья <a href="https://hackernoon.com/how-to-start-your-friendship-with-selenide">How to Start Your Friendship with Selenide</a> by Miki Szeles, 30.03.2022</li>
<li>Оффтопик: моё выступление на типа открытом микрофоне <a href="https://youtu.be/AOBEqZ0T51c">про поездки на конференции в Киев</a> - “Твой выход”, 23.07.2022, Таллинн</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.7.02022-08-04T00:00:00+00:00http://ru.selenide.org/2022/08/04/selenide-6.7.0
<p><br /></p>
<h1 id="привет">Привет!</h1>
<p><br />
У нас случился толстенький релиз <a href="https://github.com/selenide/selenide/milestone/154?closed=1">Selenide 6.7.0</a></p>
<h3 id="holy-whole-string">Теперь <code class="language-plaintext highlighter-rouge">$.shouldHave(text)</code> может проверять текст целиком</h3>
<p>Испокон веков команда <code class="language-plaintext highlighter-rouge">$.shouldHave(text)</code> проверяла не текст целиком, а только подстроку:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><div</span> <span class="na">id=</span><span class="s">"freedom-to"</span><span class="nt">></span>Britney Spears<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#freedom-to"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Brit"</span><span class="o">));</span> <span class="c1">// was OK</span>
</code></pre></div></div>
<p>Когда-то это показалось удачной идеей, потому что веб-страничка часто содержит всякие левые символы
(переводы строк, символы табуляции, непереносимые пробелы, множественные пробелы и т.п.).</p>
<p>Но похоже, это всё-таки была неудачная идея, т.к. новички по умолчанию предполагают, что текст проверяется целиком, и
выхватывают свою порцию WTF, когда обнаруживают, что все их проверки проверяли немного не то. :)</p>
<p>А левые символы селенид научился правильно игнорировать (например, несколько пробелов/табов/переводов подряд эквивалентны одному пробелу).</p>
<p>NB! Поскольку это изменение слишком уж кардинальное и наверняка сломает кучу ваших тестов, мы пока оставили по умолчанию
старое поведение. А новое можно включить такой настройкой:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Configuration</span><span class="o">.</span><span class="na">textCheck</span> <span class="o">=</span> <span class="no">FULL_TEXT</span><span class="o">;</span>
</code></pre></div></div>
<p>А когда вам нужно проверить именно подстроку, можете использовать новую проверку <code class="language-plaintext highlighter-rouge">partialText</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#freedom-to"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">partialText</span><span class="o">(</span><span class="s">"ey Spear"</span><span class="o">))</span>
</code></pre></div></div>
<p>Возможно, мы включим этот режим по умолчанию в следующей мажорной версии Selenide 7.0.0 - а до тех пор
<strong>вы можете поиграться и дать нам обратную связь</strong>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1780">issue 1780</a> и <a href="https://github.com/selenide/selenide/pull/1783">PR 1783</a>.</p>
<p><br /></p>
<h3 id="full-size-screenshots">Научили Селенид делать большие скриншоты</h3>
<p>Ну наконец-то!</p>
<p>Как вы знаете, при падении теста селенид автоматически делает снимок экрана (скриншот), чтобы помочь вам понять, почему тест упал.
По умолчанию вебдрайвер умеет снимать не всё окно браузера, а только видимую его часть. А самое важно часто оказывается
за пределами экрана - тест валит какая-нибудь неактивная кнопка внизу экрана или всплывшее уведомление где-нибудь в углу.</p>
<p>Теперь вы можете одной строчкой подключить плагин, который снимает скриншот с полным содержимым веб-странички:</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">testImplementation</span> <span class="s2">"com.codeborne:selenide-full-screenshot:6.7.0"</span>
</code></pre></div></div>
<p>P.S. Фича работает только для браузеров, поддерживающих CDP (Chrome, Edge и т.д.) и Firefox.
Для остальных будут по-прежнему обычные скриншоты.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1799">issue 1799</a> и <a href="https://github.com/selenide/selenide/pull/1858">PR 1858</a>.
Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1800">PR 1800</a>.</p>
<p><br /></p>
<h3 id="cache-lookup-annotation">Добавили кэширование элементов в пэдж обжектах</h3>
<p>Как вы знаете, селенид поддерживает обычные селениумовские пэдж-обжекты с аннотациями <code class="language-plaintext highlighter-rouge">@FindBy</code>.
Но вот аннотацию <code class="language-plaintext highlighter-rouge">@CacheLookup</code> селенид до сих пор не поддерживал - нам это казалось ненужной оптимизацией, ведь в
тестах большая часть времени теряется вовсе не на поиск элементов.</p>
<p>Но теперь вот поддерживаем, почему бы нет. Вдруг у кого-то всё настолько оптимизировано, что именно поиск элементов
стал узким горлышком. Хотел бы я посмотреть на такой проект. :)</p>
<p>Спасибо <a href="https://github.com/groov1kk">Ilya Koshaleu</a> за <a href="https://github.com/selenide/selenide/pull/1894">PR 1894</a>.</p>
<p>P.S. Встаёт вопрос, а не надо ли добавить кэширование и для обычных <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code>. А как это оформить - параметром, настройкой? Новым методом?</p>
<p><strong>Предлагайте ваши идеи!</strong></p>
<p><br /></p>
<h3 id="cancel-report-testng-annotation">Отменили аннотацию <code class="language-plaintext highlighter-rouge">@Report</code></h3>
<p>Вот культура отмены добралась и до аннотаций!</p>
<p>Это изменение касается любителей TestNG, которые использовали селенидовский отчёт. Для этого им надо было повесить на
тесты две аннотации:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Listeners</span><span class="o">({</span><span class="nc">TextReport</span><span class="o">.</span><span class="na">class</span><span class="o">})</span>
<span class="nd">@Report</span>
<span class="kd">class</span> <span class="nc">MyTest</span> <span class="o">{...}</span>
</code></pre></div></div>
<p>Почему так сделали - это долгая история про корявые принципы работы листенеров в TestNG
(вкратце: добавление аннотации листенера одному классу внезапно влияет на все остальные классы).
Но теперь мы поняли, что систему можно упростить: аннотация <code class="language-plaintext highlighter-rouge">@Report</code> больше не нужна.</p>
<p>А отчёт будет генерироваться для всех тестов, помеченных аннотацией <code class="language-plaintext highlighter-rouge">@Listeners({TextReport.class}</code> <strong>и их потомков</strong>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1891">issue 1891</a> и <a href="https://github.com/selenide/selenide/pull/1909">PR 1909</a>.</p>
<p><br /></p>
<h3 id="decode-downloaded-file-name">Декодируем имя скачанного файла</h3>
<p>Оказалось, что при скачивании файла имя файла в ответе сервера иногда бывает закодировано в Base64.
А селенид считывал имя файла как есть. Теперь вот умеет расшифровывать и Base64.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1886">issue 1886</a> и <a href="https://github.com/selenide/selenide/pull/1889">PR 1889</a>.</p>
<p><br /></p>
<h3 id="restore-ie-support-set-value">Починили <code class="language-plaintext highlighter-rouge">$.setValue()</code> в IE</h3>
<p>Оказалось, мы недавно сломали метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> в Internet Explorer.
Не то чтобы случайно: IE ведь официально отдал концы. Но жалобы были, и мы его вернули.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1907">PR 1907</a>.</p>
<p><br /></p>
<h3 id="made-http-client-timeouts-public">Сделали публичным <code class="language-plaintext highlighter-rouge">HttpClientTimeouts</code></h3>
<p>Но лишний раз туда лучше не лазать.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1902">PR 1902</a>.</p>
<p><br /></p>
<h3 id="restore-set-value-string">Вернули тип параметра <code class="language-plaintext highlighter-rouge">String</code> для <code class="language-plaintext highlighter-rouge">$.setValue()</code></h3>
<p>Лично у меня подгорает от того, что такая проблема вообще возникла.</p>
<p>Подробности в <a href="https://github.com/selenide/selenide/issues/1885">issue 1885</a> и <a href="https://github.com/selenide/selenide/pull/1888">PR 1888</a>.</p>
<p><br /></p>
<h3 id="validate-file-extension">Добавили валидацию расширения файла</h3>
<p>См. <a href="https://github.com/selenide/selenide/pull/1887">PR 1887</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>JUnit <a href="https://github.com/selenide/selenide/pull/1900">from 5.8.2 to 5.9.0</a></li>
<li>WebDriverManager <a href="https://github.com/selenide/selenide/pull/1901">from 5.2.1 to 5.2.3</a></li>
<li>Netty <a href="https://github.com/selenide/selenide/pull/1892">from 4.1.78.Final to 4.1.79.Final</a></li>
<li>BrowserUpProxy <a href="https://github.com/selenide/selenide/pull/1895">from 2.2.0 to 2.2.1</a></li>
<li>LittleProxy <a href="https://github.com/selenide/selenide/pull/1896">from 2.0.9 to 2.0.10</a></li>
<li>ByteBuddy <a href="https://github.com/selenide/selenide/pull/1904">from 1.12.12 to 1.12.13</a></li>
</ul>
<p><br /></p>
<h3 id="selenide-6.7.1">UPD Selenide 6.7.1</h3>
<ul>
<li>вернули параметр <code class="language-plaintext highlighter-rouge">Driver</code> в метод <code class="language-plaintext highlighter-rouge">SelenidePageFactory.findSelector()</code> - он используется в <code class="language-plaintext highlighter-rouge">selenide-appium</code>.</li>
</ul>
<p><br /></p>
<h3 id="news">Новости</h3>
<ul>
<li><a href="https://github.com/iSYS-Software/SelenideReporter">Opinionated reporting framework for Selenide</a> от Ulrich Mayring</li>
<li>Новая версия <a href="https://www.youtube.com/watch?v=C8MIGAjgVqE&ab_channel=KVHigh-TechClub">Пацан накодил - пацан протестил</a> - Career Day, Минск, 04.06.2022</li>
<li><a href="https://www.youtube.com/watch?v=-KGtZoFVzr8&list=PL9Z-JgiTsOYRfoG_mcRBlTUIFPIknhQ6S">Extending open-source libraries: Selenide & Selenium</a> - Selenium Conf, 30.07.2022</li>
<li><a href="https://www.youtube.com/watch?v=SqiYAJfpQwY&list=PLULFH3b6unlcUrwT9hJycTvAWPFqXZO4m&ab_channel=QAClubLviv">Автоматизація з Selenide, ввідне заняття</a> by Роман Маринский</li>
<li><a href="https://www.youtube.com/watch?v=-c5XT2v5gRY&ab_channel=DEVCLUB.EE">Flaky Tests</a> - devclub.ee, Tallinn, 05.07.2022</li>
<li>Оффтопик: моё выступление на типа открытом микрофоне <a href="https://youtu.be/AOBEqZ0T51c">про поездки на конференции в Киев</a> - “Твой выход”, 23.07.2022, Таллинн</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.6.62022-07-01T00:00:00+00:00http://ru.selenide.org/2022/07/01/selenide-6.6.6
<p><br /></p>
<h1 id="добър-вечер">Добър вечер!</h1>
<p><br />
У нас вышел ещё один мини-релиз <a href="https://github.com/selenide/selenide/milestone/160?closed=1">Selenide 6.6.6</a></p>
<h3 id="remove-deprecated-capabilities">Удалили старые капабилити</h3>
<p>Некоторые настройки вебдрайвера (которые селенид проставлял с испокон веков) были помечены как устаревшие, а с недавних
пор селениум начал ругаться на нах в логах.</p>
<p>Теперь селенид их больше не проставляет: <code class="language-plaintext highlighter-rouge">acceptSslCerts</code>, <code class="language-plaintext highlighter-rouge">handlesAlerts</code>, <code class="language-plaintext highlighter-rouge">javascriptEnabled</code>, <code class="language-plaintext highlighter-rouge">takesScreenshot</code>.
Мы не забудем вас, друзья! Вы служили нам верой и правдой больше 10 лет.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1862">issue 1862</a>, <a href="https://github.com/selenide/selenide/issues/1866">issue 1866</a>
и <a href="https://github.com/selenide/selenide/pull/1870">PR 1870</a>.</p>
<p><br /></p>
<h3 id="fix-clear-with-shortcut">Исправили ClearWithShortcut</h3>
<p>… при работе с вебдрайвером, завёрнутым в листенеры. Редкая ситуация, не заморачивайтесь.</p>
<p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1856">PR 1856</a>.</p>
<p><br /></p>
<h3 id="shorter-syntax-for-click">Добавили короткие варианты для вызова <code class="language-plaintext highlighter-rouge">$.click</code></h3>
<p>Раньше, чтобы кликнуть со сдвигом или с кастомным таймаутом, приходилось писать довольно длинную строку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withDefaultMethod</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span>
<span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withDefaultMethod</span><span class="o">().</span><span class="na">timeout</span><span class="o">(...));</span>
</code></pre></div></div>
<p>Теперь же можно написать короче:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withOffset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span>
<span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withTimeout</span><span class="o">(...));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/pull/1875">PR 1875</a>.</p>
<p><br /></p>
<h3 id="support-mobile-apps-in-webdriver-health-checker">Добавили поддержку мобилок при проверке, жив ли вебдрайвер</h3>
<p>Мелкое исправление для <code class="language-plaintext highlighter-rouge">selenide-appium</code>.
Чтобы проверить, а жив ли браузер, селенид периодически дёргает метод <code class="language-plaintext highlighter-rouge">WebDriver.getTitle()</code> (если вы переиспользуете
один вебдрайвер между разными тестами).</p>
<p>А тут обнаружилось, что метод <code class="language-plaintext highlighter-rouge">getTitle()</code> не поддерживается в Appium. Пришлось учесть.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1878">issue 1878</a> и <a href="https://github.com/selenide/selenide/pull/1879">PR 1879</a>.</p>
<p><br /></p>
<h3 id="fix-reopen-browser-on-fail">Исправили логику настройки <code class="language-plaintext highlighter-rouge">reopenBrowserOnFail</code></h3>
<p>Ещё одно исправление для улучшения поддержки мобилок.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1880">issue 1880</a> и <a href="https://github.com/selenide/selenide/pull/1881">PR 1881</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">с 5.2.0 на 5.2.1</a>.</li>
<li>byteBuddyVersion <a href="https://github.com/selenide/selenide/pull/1872">с 1.12.11 на 1.12.12</a>.</li>
</ul>
<p><br /></p>
<h3 id="release-selenide-appium-2.1.0">Выпустили selenide-appium 2.1.0</h3>
<p>Немного улучшили там поддержку тестов на iOS.</p>
<p>См. <a href="https://github.com/selenide/selenide-appium/blob/master/CHANGELOG">selenide-appium</a>.</p>
<p><br /></p>
<p><em>Этим релизом мы решили продемонстрировать мировому сообществу, что не препятствуем созданию автоматических тестов для мобильных приложений.</em></p>
<p><em>Теперь слово за автоматизаторами.</em></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.6.42022-06-20T00:00:00+00:00http://ru.selenide.org/2022/06/20/selenide-6.6.4
<p><br /></p>
<h1 id="приветос">Приветос!</h1>
<p><br />
У нас вышел ещё один мини-релиз <a href="https://github.com/selenide/selenide/milestone/158?closed=1">Selenide 6.6.4</a>.</p>
<h3 id="exact-texts-case-sensitive">Добавили условие <code class="language-plaintext highlighter-rouge">exactTextsCaseSensitive</code> для коллекций</h3>
<p>В селениде есть несколько проверок для текстов коллекций:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">textsInAnyOrder</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTexts</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span>
<span class="c1">// и др.</span>
</code></pre></div></div>
<p>Теперь к ним добавилась ещё одна:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTextsCaseSensitive</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/ben-nc2">Ben Heap</a> за <a href="https://github.com/selenide/selenide/pull/1861">PR 1861</a>.</p>
<p><br /></p>
<h3 id="selected-option-lazy-loaded">Сделали метод <code class="language-plaintext highlighter-rouge">$.getSelectedOption()</code> ленивым</h3>
<p>По задумке, (почти) все методы Селенида <a href="https://github.com/selenide/selenide/wiki/Lazy-loading">должны быть ленивыми</a>.</p>
<p>Например, просто вызов <code class="language-plaintext highlighter-rouge">$("#nope")</code> не должен падать, если элемента нет. Это позволяет писать отрицательные условия:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#nope"</span><span class="o">).</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span>
</code></pre></div></div>
<p>Оказалось, что метод <code class="language-plaintext highlighter-rouge">$("select").getSelectedOption()</code> не был ленивым и падал сразу, если селект ещё не успел подгрузиться и т.д.<br />
То есть вы в принципе не могли написать такую проверку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">var</span> <span class="n">option</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"select#gender"</span><span class="o">).</span><span class="na">getSelectedOption</span><span class="o">();</span> <span class="c1">// падало на этом шаге</span>
<span class="n">option</span><span class="o">.</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span>
</code></pre></div></div>
<p>Теперь мы исправили это недоразумение. <em>Лень победила!</em>
См. <a href="https://github.com/selenide/selenide/issues/1581">issue 1581</a> и <a href="https://github.com/selenide/selenide/pull/1864">PR 1864</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>Netty <a href="https://github.com/selenide/selenide/pull/1857">с 4.1.77.Final на 4.1.78.Final</a>.</li>
<li>BrowserUp proxy <a href="https://github.com/selenide/selenide/pull/1860">с 2.1.5 на 2.2.0</a>.</li>
</ul>
<p><br /></p>
<h3 id="release-selenide-6.6.5">UPD Выпустили Selenide 6.6.5</h3>
<p>И ещё мини-релиз <a href="https://github.com/selenide/selenide/milestone/159?closed=1">Selenide 6.6.5</a>.</p>
<p>Обновили Selenium с 4.2.2 на 4.3.0.</p>
<p><br />
<br /></p>
<p><em>Это наш откровенный респонс на ваш откровенный реквест.</em></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.6.32022-06-12T00:00:00+00:00http://ru.selenide.org/2022/06/12/selenide-6.6.3
<p><br /></p>
<h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1>
<p><br />
Мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/157?closed=1">Selenide 6.6.3</a>.</p>
<h3 id="improve-click-timeout">Доработали таймаут для клика</h3>
<p>В версии 6.6.0 мы добавили опциональный параметр - таймаут для метода <code class="language-plaintext highlighter-rouge">$.click()</code>.<br />
Но оказалось, что тот параметр решал <a href="/2022/06/08/selenide-6.6.0/#click-timeout">лишь половину проблемы</a>.</p>
<p>Теперь мы исправили и вторую половину: вызов</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#slow-link"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">8</span><span class="o">)));</span>
</code></pre></div></div>
<p>ждёт до 8 секунд как появления элемента <code class="language-plaintext highlighter-rouge">#slow-link</code>, так и последующей загрузки страницы.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1572">issue 1572</a> и <a href="https://github.com/selenide/selenide/pull/1853">PR 1853</a>.</p>
<p><br /></p>
<h3 id="selenide-6.6.2">Selenide 6.6.2</h3>
<p>А чуть раньше мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/156?closed=1">Selenide 6.6.2</a> с обновлением на Selenium 4.2.2</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1851">PR 1851</a>.</p>
<p><br /></p>
<h3 id="selenide-6.6.1">Selenide 6.6.1</h3>
<p>А чуть раньше мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/155?closed=1">Selenide 6.6.1</a>
с исправлением старой <a href="https://github.com/selenide/selenide/issues/1850">issue 1850</a>.
Там мы вернули зависимость <code class="language-plaintext highlighter-rouge">byte-buddy</code>, без которой не работало добавление листенеров для вебдрайвера.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.6.02022-06-08T00:00:00+00:00http://ru.selenide.org/2022/06/08/selenide-6.6.0
<p><br /></p>
<h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1>
<p><br />
Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/152?closed=1">Selenide 6.6.0</a>.</p>
<h3 id="selenide-clear-with-shortcut">Появился новый плагин <code class="language-plaintext highlighter-rouge">selenide-clear-with-shortcut</code></h3>
<p>В Selenide 6.5.0 мы поменяли реализацию <code class="language-plaintext highlighter-rouge">$.clear()</code> со стандартной селениумовской на шорткат (“Выделить всё” - Удалить).
Оказалось, что этот шорткат не работал достаточно стабильно во всех браузерах, пришлось его доработать. Вроде
стабилизировался, но стал медленнее, чем просто <code class="language-plaintext highlighter-rouge">WebElement.clear()</code>. А поскольку это не всем нужно, решили в конце концов
в Селениде оставить старую добрую селениумовскую реализацию.</p>
<p>А кому нужна очистка шорткатом - может подключить себе наш новый плагин:</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">testImplementation</span><span class="o">(</span><span class="s1">'com.codeborne:selenide:6.6.0'</span><span class="o">)</span>
<span class="n">testImplementation</span><span class="o">(</span><span class="s1">'com.codeborne:selenide-clear-with-shortcut:6.6.0'</span><span class="o">)</span>
</code></pre></div></div>
<p>Зависимость <code class="language-plaintext highlighter-rouge">com.codeborne:selenide-clear-with-shortcut</code> переопределит метод <code class="language-plaintext highlighter-rouge">$.clear()</code> - а соответственно, и <code class="language-plaintext highlighter-rouge">$.setValue()</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1497">issue 1497</a>, <a href="https://github.com/selenide/selenide/pull/1847">PR 1847</a> и <a href="https://github.com/selenide/selenide/pull/1838">PR 1838</a>.</p>
<p><br /></p>
<h3 id="fix-clear-in-safari">Исправили метод <code class="language-plaintext highlighter-rouge">$.clear()</code> в Safari</h3>
<p>Внезапно выяснилось, что новый <code class="language-plaintext highlighter-rouge">$.clear()</code> не работал в Safari (кто его вообще использует?). <br />
Вроде починили. Делитесь опытом, как у вас, починилось?</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1819">issue 1819</a>, <a href="https://github.com/selenide/selenide/pull/1820">PR 1820</a>.</p>
<p><br /></p>
<h3 id="new-own-test-checks">Новые проверки для текста элемента</h3>
<p>Добавили две новых проверки для “своего текста” (own text) элемента:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$("#child_div1").shouldHave(ownTextCaseSensitive("Son"));</code></li>
<li><code class="language-plaintext highlighter-rouge">$("#child_div1").shouldHave(exactOwnTextCaseSensitive("Son"))</code></li>
</ul>
<p>Спасибо <a href="https://github.com/kachurinaa">Kachurin Alexandr</a> за
<a href="https://github.com/selenide/selenide/pull/1811">PR 1811</a> и <a href="https://github.com/selenide/selenide/pull/1812">PR 1812</a>.</p>
<p><br /></p>
<h3 id="click-timeout">Добавили метод <code class="language-plaintext highlighter-rouge">$.click()</code> с таймаутом</h3>
<p>По умолчанию у метода <code class="language-plaintext highlighter-rouge">$.click()</code> стандартный селенидовский таймаут (который по умолчанию 4 секунды).<br />
Иногда этого недостаточно - например,</p>
<ol>
<li>Когда клик по ссылке ведёт на следующую страницу, которая грузится дольше 4 секунд.</li>
<li>Когда элемент, по которому нужно кликнуть, ещё не появился на экране - и появляется больше, чем через 4 секунды.</li>
</ol>
<p>В этом релизе мы добавили таймаут методу <code class="language-plaintext highlighter-rouge">$.click()</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#slow-link"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">8</span><span class="o">)));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#slow-link"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">8</span><span class="o">)));</span>
</code></pre></div></div>
<p>Правда, этот параметр решает пока только первую проблему, но не вторую. В следующем релизе займёмся и второй. :)</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1572">issue 1572</a> и <a href="https://github.com/selenide/selenide/pull/1845">PR 1845</a>.</p>
<p><br /></p>
<h3 id="modal-timeout">Добавили таймаут методам <code class="language-plaintext highlighter-rouge">confirm()</code>, <code class="language-plaintext highlighter-rouge">dismiss()</code> и <code class="language-plaintext highlighter-rouge">prompt()</code></h3>
<p>Издревле эти три метода позволяют работать с модальными джаваскриптовскими окошками (<code class="language-plaintext highlighter-rouge">alert</code>, <code class="language-plaintext highlighter-rouge">confirm</code>, <code class="language-plaintext highlighter-rouge">prompt</code>).
Например,</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">confirm</span><span class="o">();</span>
<span class="n">confirm</span><span class="o">(</span><span class="s">"Are you sure you want to delete all files?"</span><span class="o">);</span>
</code></pre></div></div>
<p>Но не было варианта с таймаутом - на тот случай, если алерт появляется не сразу, а больше, чем через 4 секунды.
Теперь можно задать таймаут:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">confirm</span><span class="o">(</span><span class="n">withTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">2</span><span class="o">)));</span>
<span class="n">confirm</span><span class="o">(</span><span class="n">withExpectedText</span><span class="o">(</span><span class="s">"Are you sure?"</span><span class="o">).</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">2</span><span class="o">)));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1721">issue 1721</a> и <a href="https://github.com/selenide/selenide/pull/1846">PR 1846</a>.</p>
<p><br /></p>
<h3 id="fix-execute-javascript">Подправили метод <code class="language-plaintext highlighter-rouge">Driver.executeJavaScript()</code></h3>
<p>…чтобы он работал и с “завёрнутым” вебдрайвером (т.е. когда на вебдрайвер навешаны листенеры).</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1848">PR 1848</a>.</p>
<p><br /></p>
<h3 id="fix-checks-wording">Подправили формулировку некоторых проверок</h3>
<p>… чтобы он звучали корректно по-английски. А именно:</p>
<table>
<thead>
<tr>
<th>В тесте</th>
<th>В отчёте было</th>
<th>В отчёте стало</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">$.should(appear)</code></td>
<td>“Element should visible”</td>
<td>“Element should be visible”</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">$.should(disappear)</code></td>
<td>“Element should hidden”</td>
<td>“Element should be hidden”</td>
</tr>
</tbody>
</table>
<p>См. <a href="https://github.com/selenide/selenide/pull/1840">PR 1840</a>.</p>
<p><br /></p>
<h3 id="restore-safari-options">Добавили опции для Safari</h3>
<p>Выяснилось, что при открытии браузера Safari селенид терял почти все его настройки. Сломалось при переходе на Selenium 4.
Починить это оказалось легко, но почему никто до сих пор не жаловался?</p>
<p>Похоже, Safari всё-таки никто не использует. :)</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1836">issue 1836</a> и <a href="https://github.com/selenide/selenide/pull/1841">PR 1841</a>.</p>
<p><br /></p>
<h3 id="fix-testng-soft-asserts">Починили софт ассерты на TestNG</h3>
<p>После обновления на TestNG 7.5 перестали правильно работать селенидовские софт ассерты. Если какая-то проверка падала,
тест по-прежнему оставался зелёным.</p>
<p>Пришлось откатиться до TestNG 7.4.0 и ждать исправления на той стороне.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1834">issue 1834</a> и <a href="https://github.com/selenide/selenide/pull/1843">PR 1843</a>.</p>
<p><br /></p>
<h3 id="update-dependencies">Обновили зависимости</h3>
<ul>
<li>Selenium <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">4.1.4 -> 4.2.1</a>.</li>
<li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">5.1.1 -> 5.2.0</a></li>
</ul>
<p><br /></p>
<h3 id="statistics">Статистика</h3>
<p>Давно мы не публиковали статистика скачиваний Селенида</p>
<center>
<img src="/images/2022/06/selenide.downloads.png" width="800" />
</center>
<p>Мы почти вернулись к рекордному показателю: 324 тысяч скачек за май.</p>
<p><br /></p>
<p>Не переключайтесь!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.5.02022-05-17T00:00:00+00:00http://ru.selenide.org/2022/05/17/selenide-6.5.0
<p><br /></p>
<h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1>
<p><br />
Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/151?closed=1">Selenide 6.5.0</a>.</p>
<h3 id="теперь-можно-маскировать-пароли-в-отчётах">Теперь можно маскировать пароли в отчётах</h3>
<p>Иногда в автотестах приходится вбивать в поля пароли и другие секретные данные. И некоторые люди
переживают, что эти данные потом видны в отчётах (Allure или TextReport). И просят их как-нибудь скрыть.</p>
<p>Лично я не понимаю, почему кого-то должен волновать пароль от тестовой среды.
(Вы же не используете одни и те же пароли во всех средах? Вы же не гоняете тесты на продакшине?)</p>
<p>Но мы пошли навстречу пожеланиям переживающих, и теперь вы можете маскировать или заменять секретные данные:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#password"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"admin"</span><span class="o">).</span><span class="na">sensitive</span><span class="o">());</span>
<span class="c1">// заменяет на звёздочки</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"john"</span><span class="o">).</span><span class="na">withDisplayedText</span><span class="o">(</span><span class="s">"J* username"</span><span class="o">));</span>
<span class="c1">// заменяет на "J* username"</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1768">issue 1768</a> и <a href="https://github.com/selenide/selenide/pull/1770">PR 1770</a>.</p>
<p><br /></p>
<h3 id="теперь-можно-легко-выбрать-дату-в-input-typedate">Теперь можно легко выбрать дату в <code class="language-plaintext highlighter-rouge"><input type=date></code></h3>
<p>Оказалось, что выбрать дату в селениуме не так-то просто.<br />
Если в вашем приложении есть элемент вида <code class="language-plaintext highlighter-rouge"><input type=date></code>, то при клике в него разные браузеры
открывают разные календарики, и при этом используют разные форматы даты. По идее они должны использовать
формат из настроек пользователя, но на деле каждый браузер играет в свою игру.</p>
<p>Всё это приводят к флейки тестам, которые могут работать на одном компьютере, но внезапно упасть на другом.</p>
<p>В результате моего исследования я пришёл к выводу, что единственный надёжный способ выбрать дату - это установить
значение в фиксированном формате <code class="language-plaintext highlighter-rouge">yyyy-mm-dd</code> через JavaScript. Похоже, это работает одинаково во всех ОС и браузерах.</p>
<p>И теперь Селенид предоставляет такую возможность с помощью нового метода <code class="language-plaintext highlighter-rouge">withDate</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">LocalDate</span> <span class="n">birthday</span> <span class="o">=</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"1979-12-31"</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#birthday"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withDate</span><span class="o">(</span><span class="n">birthday</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1753">issue 1753</a> и <a href="https://github.com/selenide/selenide/pull/1770">PR 1770</a>.</p>
<p><br /></p>
<h3 id="теперь-метод-setvalue-поддерживает-react-vuejs-и-тп">Теперь метод <code class="language-plaintext highlighter-rouge">$.setValue("")</code> поддерживает React, Vue.js и т.п.</h3>
<p>Давно замечено, что стандартный селениумовский метод <code class="language-plaintext highlighter-rouge">WebElement.clean()</code> (в частности, использующийся в селенидовском
<code class="language-plaintext highlighter-rouge">$.setValue()</code>) не всегда правильно работает, когда имеют дело не с обычным <code class="language-plaintext highlighter-rouge"><input></code>, а с обёрнутыми/обвязанными
модными конструкциями, генерируемыми всякими современными JS-фреймворками типа React, Vue.js и прочей хипстоты.</p>
<p>По идее мы это починили, ждём ваших отзывов.</p>
<p>Теперь следующие методы реализованы не через стандартный селениумовский <code class="language-plaintext highlighter-rouge">WebElement.clear()</code>, а через
нажатия комбинации клавиш:</p>
<h4 id="1-method-clear">1. Method <code class="language-plaintext highlighter-rouge">$.clear()</code>:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">input</span><span class="o">.</span><span class="na">sendKeys</span><span class="o">(</span><span class="no">HOME</span><span class="o">,</span> <span class="n">chord</span><span class="o">(</span><span class="no">SHIFT</span><span class="o">,</span> <span class="no">END</span><span class="o">),</span> <span class="no">BACK_SPACE</span><span class="o">);</span>
</code></pre></div></div>
<h4 id="2-method-setvalue">2. Method <code class="language-plaintext highlighter-rouge">$.setValue("")</code>:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">input</span><span class="o">.</span><span class="na">sendKeys</span><span class="o">(</span><span class="no">HOME</span><span class="o">,</span> <span class="n">chord</span><span class="o">(</span><span class="no">SHIFT</span><span class="o">,</span> <span class="no">END</span><span class="o">),</span> <span class="no">BACK_SPACE</span><span class="o">,</span> <span class="no">TAB</span><span class="o">);</span>
</code></pre></div></div>
<p>Такой способ, похоже, работает во всех ОС и браузерах.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1497">issue 1497</a> и <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p>
<p>Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за исследование проблемы в <a href="https://github.com/selenide/selenide/pull/1499">PR 1499</a>.</p>
<p><br /></p>
<h3 id="убрали-двойное-событие-blur-и-change">Убрали двойное событие <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code></h3>
<p>Из предыдущего изменения автоматически следует, что метод <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> вызывает события <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code> только единожды.</p>
<p>А раньше метод <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> работал в два шага и вызывал их дважды:</p>
<ul>
<li>Шаг 1. <code class="language-plaintext highlighter-rouge">$.clear()</code> // вызывало <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code></li>
<li>Шаг 2. <code class="language-plaintext highlighter-rouge">$.sendKeys("blah")</code> // ещё раз вызывало <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code></li>
</ul>
<p>И события на первом шаге иногда приводили к неожиданным эффектам - например, исчезанию самого поля.<br />
См. <a href="https://github.com/selenide/selenide/issues/960">issue 960</a>.</p>
<p><br /></p>
<h3 id="вызываем-событие-blur-на-предыдущем-элементе">Вызываем событие <code class="language-plaintext highlighter-rouge">blur</code> на предыдущем элементе</h3>
<p>В предыдущем пулреквесте заодно сделали так, что метод <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> вызывает событие <code class="language-plaintext highlighter-rouge">blur</code> на предыдущем
активном элементе. Как минимум это помогло исправить некоторые наши флейки тесты. Надеемся, поможет и вам.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1784">issue 1784</a> и <a href="https://github.com/selenide/selenide/commit/593e6fc900500d9">commit 593e6fc9005</a>.</p>
<p><br /></p>
<h3 id="теперь-методы-setvalue-и-append-проверяют-что-элемент-не-выключен">Теперь методы <code class="language-plaintext highlighter-rouge">$.setValue()</code> и <code class="language-plaintext highlighter-rouge">$.append()</code> проверяют, что элемент не выключен</h3>
<p>Точнее, что элемент не находится в состоянии “disabled” или “readonly”.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1523">issue 1523</a> и <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p>
<p><br /></p>
<h3 id="добавили-новые-проверки-interactable-и-editable">Добавили новые проверки “interactable” и “editable”</h3>
<p>Эти проверки уже существовали и использовались раньше внутри Селенида, но не торчали наружу.
А теперь и вы сможете их использовать:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"input[type=file]"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">interactable</span><span class="o">);</span>
<span class="c1">// interactable = visible OR "opacity: 0"</span>
<span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">editable</span><span class="o">);</span>
<span class="c1">// editable = interactable AND enabled AND !readonly</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1523">issue 1523</a> и <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p>
<p><br /></p>
<h3 id="метод-downloadfolder-дожидается-полной-загрузки-файла">Метод <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code> дожидается полной загрузки файла</h3>
<p>Британские учёные обнаружили, что иногда флейки тесты вызваны тем, что файл скачивается довольно медленно, и селенид
успевает обнаружить и скопировать к себе файл, который уже появился в <code class="language-plaintext highlighter-rouge">C:\Downloads</code>,
но ещё не полностью загружен.</p>
<p>Теперь селенид ждёт, пока в <code class="language-plaintext highlighter-rouge">C:\Downloads</code> исчезнут все файлы “<em>.crdownload” (для хрома) и “</em>.part” (для фаерфокса).
Это временный файл, создаваемый браузером на короткий период, пока скачивается файл.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1779">issue 1779</a>,
<a href="https://github.com/selenide/selenide/pull/1804">PR 1804</a> и <a href="https://github.com/selenide/selenide/pull/1769">PR 1769</a>.</p>
<p><br /></p>
<h3 id="добавили-метод-stream-для-коллекций">Добавили метод <code class="language-plaintext highlighter-rouge">stream()</code> для коллекций</h3>
<p>Точнее, метод <code class="language-plaintext highlighter-rouge">$$.stream()</code> существовал и раньше, но в версии <a href="/2022/01/10/selenide-6.2.0/">6.2.0</a> был помечен как deprecated.
Теперь вместо него есть на выбор два не-deprecated метода:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$$.asFixedIterable().stream()</code></li>
<li><code class="language-plaintext highlighter-rouge">$$.asDynamicIterable().stream()</code></li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide/issues/1773">issue 1773</a> и <a href="https://github.com/selenide/selenide/pull/1774">PR 1774</a>.</p>
<p><br /></p>
<h3 id="удалили-встроенную-селениумовскую-телеметрию-opentelemetry">Удалили встроенную селениумовскую телеметрию (OpenTelemetry)</h3>
<p>Я так понимаю, эта телеметрия появилась в Selenium 4.
Не знаю, зачем она вообще нужна, но кому-то мешала (потому, что у него уже была какая-то своя телеметрия).
Ну и нам было проще её удалить, чем поддерживать обе.</p>
<p>Спасибо <a href="https://github.com/zzz">Petro Ovcharenko</a> и <a href="https://github.com/zzz">Aliaksandr Rasolka</a>
за <a href="https://github.com/selenide/selenide/pull/1763">PR 1763</a>.</p>
<p><br /></p>
<h3 id="обновили-зависимости">Обновили зависимости</h3>
<ul>
<li>Selenium <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">4.1.3 -> 4.1.4</a>.</li>
<li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">5.1.0 -> 5.1.1</a></li>
<li>Netty <a href="https://netty.io/news/2022/04/12/4-1-76-Final.html">4.1.75.Final -> 4.1.76.Final</a></li>
<li>Netty <a href="https://netty.io/news/2022/05/06/2-1-77-Final.html">4.1.76.Final -> 4.1.77.Final</a></li>
<li>LittleProxy <a href="https://github.com/LittleProxy/LittleProxy/releases">2.0.7 -> 2.0.9</a></li>
</ul>
<p>Кстати, проект LittleProxy переехал в отдельную <a href="https://github.com/LittleProxy/LittleProxy">организацию на гитхабе</a>, и
теперь я его главный мейнтейнер. Не то, чтобы я был фанатом проксей, но он используется в селениде, и
поддерживать его как-то надо…</p>
<p><br /></p>
<h1 id="upd-selenide-651">UPD Selenide 6.5.1</h1>
<p>Небольшое обновление <a href="https://github.com/selenide/selenide/milestone/153?closed=1">Selenide 6.5.1</a>:</p>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1808">#1808</a> Починили <code class="language-plaintext highlighter-rouge">$.clear()</code>, чтобы он не жмякал таб - см. <a href="https://github.com/selenide/selenide/pull/1809">PR #1809</a></li>
<li><a href="https://github.com/selenide/selenide/pull/1806">#1806</a> Обновились с browserup-proxy-core 2.1.4 на 2.1.5</li>
</ul>
<p><br /></p>
<h1 id="upd-selenide-652">UPD Selenide 6.5.2</h1>
<p><a href="https://github.com/selenide/selenide/issues/1497">#1497</a> Починили <code class="language-plaintext highlighter-rouge">$.clear()</code> на свежем фаерфоксе: теперь жмякаем “Ctrl+A -> Delete” вместо “Home -> Shift+A -> Delete”.
См. <a href="https://github.com/selenide/selenide/pull/1838">PR #1838</a>.</p>
<p><br /></p>
<h3 id="новости">Новости</h3>
<p>Отдельный привет всем, кто ждал, что же тут будет в конце пресс-релиза. ;)</p>
<p>А будет моя презентация с последнего фестиваля TechTrain.</p>
<iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRIZ4w6bc7wTjWVkHTfCdevRGMo81VRIgNQ-V5Bzy1FV9ldG3jtexEXnbJ79CMtOTKhao36VHKZlfs6/embed?start=false&loop=true&delayms=3000" frameborder="0" width="960" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
<p><br /></p>
<p>Видео с TechTrain будет через несколько месяцев, а пока можете посмотреть похожее <a class="video" href="//www.youtube.com/watch?v=iIIsZRHya-w">видео из девклуба</a>.
Оно немного другое, ибо ориентировано на другую аудиторию, но общее представление получить можно.</p>
<p><br /></p>
<p>Не переключайтесь!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.4.02022-04-07T00:00:00+00:00http://ru.selenide.org/2022/04/07/selenide-6.4.0
<p><br /></p>
<h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1>
<p><br />
Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/145?closed=1">Selenide 6.4.0</a>.</p>
<h3 id="показываем-и-алиас-и-локатор-при-падении-тестов">Показываем и алиас, и локатор при падении тестов</h3>
<p>Как вы знаете, в Селениде с помощью метода <code class="language-plaintext highlighter-rouge">as</code> можно задавать элементам “алиас”, или понятное имя.
Это полезно в тех случаях, когда нет хорошего локатора, и приходится писать какой-нибудь длинный сложный xpath, который
потом в отчётах сложно читать.</p>
<p>Например:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">$x</span><span class="o">(</span><span class="s">"/long/ugly/xpath/div[2]/span[3]/li[4]"</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<p>И у нас всегда была дилемма: нужно ли</p>
<ol>
<li>показывать только алиас (плюс: легко читается, минус: не видно селектора - вдруг он нужен?)</li>
<li>показывать и алиас, и селектор (плюс: видно селектор, минус: трудно читается).</li>
</ol>
<p><br /></p>
<p>И вот наконец мы поняли, как правильно.
Начиная с версии 6.4.0, селенид будет:</p>
<ol>
<li>В отчётах на каждом шаге показывать только алиас (коротко, легко читается)</li>
<li>А вот при падении теста в сообщение об ошибке добавлять и алиас, и локатор
(длинно, зато будет вся необходимая информация для изучения падения).</li>
</ol>
<p>Например, вышеупомянутая строка в отчётах будет выглядеть вот так:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------------------+--------------------+------------+------------+
| Element | Subject | Status | ms. |
+----------------------+--------------------+------------+------------+
| Login button | click() | FAIL | 206 |
+----------------------+--------------------+------------+------------+
</code></pre></div></div>
<p>А сообщение об ошибке - так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="s">"Login button"</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">:</span> <span class="o">/</span><span class="kt">long</span><span class="o">/</span><span class="n">ugly</span><span class="o">/</span><span class="n">xpath</span><span class="o">[</span><span class="mi">1</span><span class="o">][</span><span class="mi">2</span><span class="o">][</span><span class="mi">3</span><span class="o">]}</span>
<span class="nl">Expected:</span> <span class="n">exist</span>
<span class="nl">Screenshot:</span> <span class="o">...</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1765">issue 1765</a> и <a href="https://github.com/selenide/selenide/pull/1766">PR 1766</a>.</p>
<p><br /></p>
<h3 id="добавили-пробелы-в-селенидовском-отчёте">Добавили пробелы в селенидовском отчёте</h3>
<p>Например, вышеупомянутый отчёт раньше выглядел так:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------------------+-------------------+------------+------------+
|Element |Subject | Status | ms. |
+---------------------+-------------------+------------+------------+
|open |https://google.com/some-long-url.html?q=selenide|PASS |1285 |
|Login button |click() | FAIL | 206 |
+---------------------+-------------------+------------+------------+
</code></pre></div></div>
<p>Проблема была как минимум в том, что вокруг URL слева и справа вплотную прилегают символы <code class="language-plaintext highlighter-rouge">|</code>, так что его
невозможно быстро выделить двойным кликом. Теперь вокруг URL и других значений будут пробелы.
Заодно отчёт и покрасивее стал.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1764">issue 1764</a> и <a href="https://github.com/selenide/selenide/pull/1767">PR 1767</a>.</p>
<p><br /></p>
<h3 id="обновились-на-selenium-413">Обновились на Selenium 4.1.3</h3>
<p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a> и <a href="https://github.com/selenide/selenide/pull/1759">PR 1759</a>.</p>
<p><br /></p>
<p><br /></p>
<h1 id="важно">ВАЖНО</h1>
<p>Меня зовут Андрей Солнцев, я 10 лет делаю библиотеку Selenide и делюсь опытом на конференциях.<br />
Просто так, бесплатно. Я у вас никогда ничего за это не просил.</p>
<p>А теперь я прошу всего лишь услышать меня. Вот как вижу ситуацию я - в целом неглупый человек, мнение которого, надеюсь,
стало для многих из вас авторитетным за эти 10 лет.</p>
<p><br /></p>
<p>Россия начала <strong>войну в Украине</strong>. Бессмысленную и бесчеловечную.</p>
<p>Там сейчас под бомбёжками, под обстрелами сидят мирные люди, которые никому ничего плохого не сделали.<br />
В том числе и пользователи селенида.</p>
<ul>
<li>Те, перед которыми я многократно выступал на конференциях.</li>
<li>Те, которых я слушал и учился.</li>
<li>Те, которые помогали развивать селенид, и результатами работы которых пользуются люди во всём мире, в том числе
и России. Пользуетесь лично вы.</li>
</ul>
<p>Это тяжело признать, об этом тяжело думать, от этого ужаса хочется спрятаться - но такова реальность.</p>
<p><br />
Я не знаю, как это остановить.<br />
Но я прошу вас хотя бы <em>не поддерживать это</em>.<br />
Я призываю вас не верить этой <em>чудовищной лжи</em> про нацистов, расширение НАТО, фейки и т.п.<br />
Я прошу вас оставаться людьми.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.3.02022-02-07T00:00:00+00:00http://ru.selenide.org/2022/02/07/selenide-6.3.0
<p><br /></p>
<h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1>
<p><br />
Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/143?closed=1">Selenide 6.3.0</a>.</p>
<p>Приготовьте бутеры и погнали!</p>
<p><br /></p>
<h1 id="добавили-метод-switchtoframetimeout-с-кастомным-таймаутом">Добавили метод <code class="language-plaintext highlighter-rouge">switchTo().frame(timeout)</code> с кастомным таймаутом</h1>
<p>В селениде есть метод <code class="language-plaintext highlighter-rouge">switchTo().frame(name)</code> для переключения между фреймами. Как всегда, со встроенной ожидалкой и другими плюшками.
Но что, если фрейм грузится дольше, чем таймаут по умолчанию (4 секунды)?</p>
<p>Теперь вы можете передать дополнительный параметр <code class="language-plaintext highlighter-rouge">Duration</code> - таймаут для переключения во фрейм:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">switchTo</span><span class="o">().</span><span class="na">frame</span><span class="o">(</span><span class="s">"ifrm"</span><span class="o">);</span> <span class="c1">// по умолчанию ждёт до 4 секунд</span>
<span class="n">switchTo</span><span class="o">().</span><span class="na">frame</span><span class="o">(</span><span class="s">"ifrm"</span><span class="o">,</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">6</span><span class="o">));</span> <span class="c1">// а вот теперь до 6 секунд</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/donesvad">@donesvad</a> за <a href="https://github.com/selenide/selenide/pull/1722">PR 1722</a>.</p>
<p><br /></p>
<h1 id="добавили-селекторы-bytagandtext-и-withtagandtext">Добавили селекторы <code class="language-plaintext highlighter-rouge">byTagAndText</code> и <code class="language-plaintext highlighter-rouge">withTagAndText</code></h1>
<p>До сих пор в селениде были методы для поиска элементов по тексту:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selectors</span><span class="o">.*;</span>
<span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Hello world"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span>
</code></pre></div></div>
<p>Иногда этого мало: когда элементов с таким текстом несколько, и хочется из них выбрать только один - с нужным тэгом.</p>
<p>Теперь и для этого есть методы:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selectors</span><span class="o">.*;</span>
<span class="err">$</span><span class="o">(</span><span class="n">byTagAndText</span><span class="o">(</span><span class="s">"h1"</span><span class="o">,</span> <span class="s">"Hello world"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="n">withTagAndText</span><span class="o">(</span><span class="s">"h1"</span><span class="o">,</span> <span class="s">"Hello"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/maurizio-lattuada">Maurizio Lattuada</a> за <a href="https://github.com/selenide/selenide/issues/1650">issue 1650</a> и <a href="https://github.com/selenide/selenide/pull/1651">PR 1651</a>.</p>
<p><br /></p>
<h1 id="исправили-ошибку-в-bytextcaseinsensitive">Исправили ошибку в <code class="language-plaintext highlighter-rouge">byTextCaseInsensitive</code></h1>
<p>Теперь этот селектор игнорирует множественные пробелы и переводы строк в начале и конце текста (как и каноничный <code class="language-plaintext highlighter-rouge">byText</code>).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1723">issue 1723</a> и <a href="https://github.com/selenide/selenide/pull/1724">PR 1724</a>.</p>
<p><br /></p>
<h1 id="добавили-записи-webdriver-create-в-webdriver-close-в-отчёт">Добавили записи “webdriver create” в “webdriver close” в отчёт</h1>
<p>Если вы используете селенидовский <code class="language-plaintext highlighter-rouge">TextReport</code> или плагин <code class="language-plaintext highlighter-rouge">AllureSelenide</code>, вы привыкли в конце каждого теста
видеть отчёт, в котором видны все действия: открыли страничку - нашли элемент - кликнули и т.д.</p>
<p>Теперь там добавилась запись, когда был запущен сам вебдрайвер. И когда закрыт. Иногда эта информация может быть
полезна при отладке различных проблем с тестами.</p>
<p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1715">PR 1715</a>.</p>
<p><br /></p>
<h1 id="исправили-переопределение-селениумовского-таймаута">Исправили переопределение селениумовского таймаута</h1>
<p>В <a href="/2021/06/08/selenide-5.22.0/">Selenide 5.22.0</a> мы сделали хак, который менял дефалтовый селениумовский таймаут для общения с вебдрайвером с дичайших 3 часов до 2 минут.</p>
<p>Однако выяснилось, что при обновлении на Selenium 4 этот хак сломался (как и положено всем хакам).
Теперь мы его реанимировали.</p>
<p>Напоминаю, теперь при общении между тестами и вебдрайвером таймаут такой:</p>
<ul>
<li>таймаут на соединение - 10 секунд</li>
<li>таймаут на чтение - 1.5 минуты</li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide/commit/cf02da5">commit cf02da5</a>.</p>
<p><br /></p>
<h1 id="убрали-двойное-заворачивание-ошибок-element-not-found-друг-в-друга">Убрали двойное заворачивание ошибок “Element not found” друг в друга</h1>
<p>См. <a href="https://github.com/selenide/selenide/issues/1705">issue 1705</a> и <a href="https://github.com/selenide/selenide/pull/1706">PR 1706</a>.</p>
<p><br /></p>
<h1 id="добавили-поддержку-типа-аутентификации-bearer">Добавили поддержку типа аутентификации <code class="language-plaintext highlighter-rouge">BEARER</code></h1>
<p>В селениде давно уже есть способ аутентификации с разными типами:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="s">"scott"</span><span class="o">,</span> <span class="s">"tiger"</span><span class="o">);</span>
</code></pre></div></div>
<p>но точно правильно работал только тип <code class="language-plaintext highlighter-rouge">BASIC</code>. Остальные особо никто не проверял. :)</p>
<p>Оказалось, что тип <code class="language-plaintext highlighter-rouge">BEARER</code> не работал. Теперь вот работает (а остальные по-прежнему никто не проверял :)).</p>
<p>Использовать так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">open</span><span class="o">(</span><span class="s">"/bearer-token-auth/hello"</span><span class="o">,</span> <span class="no">BEARER</span><span class="o">,</span> <span class="k">new</span> <span class="nc">BearerTokenCredentials</span><span class="o">(</span><span class="s">"token-123"</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/pull/1714">PR 1714</a>.</p>
<p><br /></p>
<h1 id="теперь-пустые-настройки-типа-selenideremote-считаются-незаданными">Теперь пустые настройки типа <code class="language-plaintext highlighter-rouge">selenide.remote</code> считаются незаданными</h1>
<p>Это должно чутка упростить жизнь девопсам.<br />
При настройках всяких там джобов и пайплайнов на CI сервере, часто приходится использовать переменные для задания настроек селенида:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.remote=${env.GRID_URL}
</code></pre></div></div>
<p>И если в какой-нибудь среде переменной <code class="language-plaintext highlighter-rouge">GRID_URL</code> не окажется, то селенид грохнется, т.к. попытается использовать пустой урл для <code class="language-plaintext highlighter-rouge">selenide.remote</code>.</p>
<p>Так было раньше. А теперь селенид будет считать, что <code class="language-plaintext highlighter-rouge">selenide.remote</code> просто не задан, и продолжить работать как обычно.</p>
<p>См. <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/issues/1656">issue 1656</a> и <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1663">PR 1663</a>.</p>
<p><br /></p>
<h1 id="обновились-на-selenium-412">Обновились на Selenium 4.1.2</h1>
<p>После обновления на Selenium 4.1.2 у многих слетела версия Guava. Селенид явно определяет нужную версию Guava, так что
вас эта проблема затронуть не должна. Но если что, убедитесь, что в вашем проекте никакой там градловский или мавеновский плагин не переопределяет версию Guava. Старый добрый <a href="/2020/11/17/why-proxy-does-not-work-in-selenoid/">пост про зависимости</a> в помощь.</p>
<p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a> и <a href="https://github.com/selenide/selenide/pull/1719">PR 1719</a>.</p>
<p><br /></p>
<h1 id="upd-selenide-631">UPD Selenide 6.3.1</h1>
<p>Небольшое обновление <a href="https://github.com/selenide/selenide/milestone/146?closed=1">Selenide 6.3.1</a>:</p>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1731">#1731</a> вернули возможность использовать софт ассерты в TestNG в методах <code class="language-plaintext highlighter-rouge">@Before*</code> и <code class="language-plaintext highlighter-rouge">@After*</code> (ранее мы их случайно запретили в Selenide 6.2.0) - см. <a href="https://github.com/selenide/selenide/pull/1732">PR #1732</a></li>
<li><a href="https://github.com/selenide/selenide/pull/1729">#1729</a> Обновились с Netty 4.1.73.Final на 4.1.74.Final</li>
</ul>
<p><br /></p>
<h1 id="upd-selenide-632">UPD Selenide 6.3.2</h1>
<p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/147?closed=1">Selenide 6.3.2</a>:</p>
<ul>
<li><a href="https://github.com/selenide/selenide/pull/1733">#1733</a> Запилили костыль для баги селениума <a href="https://github.com/SeleniumHQ/selenium/issues/10345">10345</a>, из-за которой метод <code class="language-plaintext highlighter-rouge">FirefoxDriver.close()</code> валится после обновления на Firefox 97.</li>
<li><a href="https://github.com/selenide/selenide/pull/1736">#1736</a> Обновились с BrowserUpProxy 2.1.3 на 2.1.4</li>
<li><a href="https://github.com/selenide/selenide/pull/1611">#1611</a> Обновили версию Java с 8 до 17.</li>
</ul>
<p>А вот тут давайте остановимся поподробнее. Теперь проект Selenide собирается на Java 17, но бинарники <code class="language-plaintext highlighter-rouge">selenide-*.jar</code>
по-прежнему собираются для Java 8. Поэтому</p>
<ul>
<li>разработчики селенида могут использовать все последние фичи языка Java, а</li>
<li>пользователи селенида могут по-прежнему запускать свои тесты на Java 8 (хоть мы и советуем обновить джавушку, конечно).</li>
</ul>
<p>А стало это возможным благодаря инструменту <a href="https://github.com/bsideup/jabel">Jabel</a> и лично <a href="https://github.com/bsideup">
Sergei Egorov</a>, придумавшему этот изящный хак
(а ещё причастному к <a href="https://github.com/testcontainers/testcontainers-java">TestContainers</a> и <a href="https://www.atomicjar.com/">AtomicJar</a>).</p>
<p><br /></p>
<h1 id="upd-selenide-633">UPD Selenide 6.3.3</h1>
<p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/148?closed=1">Selenide 6.3.3</a>:</p>
<ul>
<li>#1737 позволили переопределять настройки Firefox для скачивания файлов</li>
<li>#1740 обновились на WebDriverManager 5.1.0</li>
</ul>
<p><br /></p>
<h1 id="upd-selenide-634">UPD Selenide 6.3.4</h1>
<p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/149?closed=1">Selenide 6.3.4</a>:</p>
<ul>
<li>#1746 показываем ожидаемый атрибут при падении <code class="language-plaintext highlighter-rouge">$.shouldHave(attribute(...))</code>.</li>
<li>#1748 исправили имя модуля в сгенерированных бинарниках селенида</li>
</ul>
<p><br /></p>
<h1 id="upd-selenide-635">UPD Selenide 6.3.5</h1>
<p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/150?closed=1">Selenide 6.3.5</a>:</p>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1755">#1755</a> починили скачивание файлов через прокси, если ответ сервера закодирован - см. <a href="https://github.com/selenide/selenide/pull/1756">PR #1756</a></li>
</ul>
<p><br /></p>
<h1 id="новости">Новости</h1>
<ul>
<li>Мы создали группу в LinkedIn <a href="https://www.linkedin.com/groups/9154550/">Selenide User Group</a>!</li>
<li>Пост от Miklós Szeles <a href="https://www.linkedin.com/pulse/selenium-selenide-mikl%25C3%25B3s-szeles/">Selenium or Selenide?</a></li>
<li>Селенид попал в подборку <a href="https://qameta.io/blog/5-testing-automation-tools/">5 Testing Automation Tools</a> в блоге компании Qameta Software (это которая Allure Report)</li>
<li>Селенид попал в подборку <a href="https://hackernoon.com/top-java-libraries-for-automation-testing-in-2022">Top Java Libraries for Automation Testing in 2022</a></li>
<li>Пост <a href="https://blog.knoldus.com/selenide-concise-ui-test-in-java/">про Селенид</a> в блоге компании Knoldus</li>
<li>Пост <a href="https://medium.com/@gaveen0513/selenide-the-software-that-doesnt-need-documentation-cda8535cb7e6">The software that doesn’t need documentation</a> от Gaveen Nayanajith</li>
<li>Пост <a href="https://mszeles.com/selenide-i-think-this-is-the-beginning-of-a-beautiful-friendship">начало прекрасной дружбы</a> от Miklós Szeles</li>
</ul>
<p><br /></p>
<h4 id="я-думаю-это-начало-прекрасной-дружбы">Я думаю, это начало прекрасной дружбы!</h4>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.2.02022-01-10T00:00:00+00:00http://ru.selenide.org/2022/01/10/selenide-6.2.0
<p><br /></p>
<h1 id="с-новым-годом">С новым годом!</h1>
<p><br />
Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/140?closed=1">Selenide 6.2.0</a>.</p>
<p>Давайте посмотрим, что нам приготовил первый релиз в новом году.</p>
<p>Наливайте чай и погнали!
<br /></p>
<h1 id="добавили-ссылку-click-to-see-difference-для-большинства-селенидовских-ошибок">Добавили ссылку “<Click to see difference>” для большинства селенидовских ошибок</h1>
<p>Как вы помните, в <a href="/2021/09/28/selenide-5.25.0/">Selenide 5.25.0</a> мы добавили поддержку OpenTest4J, в результате чего у
селенидовских ошибок в IDE появилась ссылочка “<Click to see difference>”, позволяющая удобно посмотреть различия
между ожидаемым и актуальным значением.</p>
<p>Но тогда мы всё порефакторить не успели, и ссылочка появилась не у всех типов ошибок. <br />
А теперь должна появиться у всех.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1589">issue 1589</a> и <a href="https://github.com/selenide/selenide/pull/1676">PR 1676</a>.</p>
<p><br /></p>
<h1 id="заменили-iterator-на-asdynamiciterable-и-asfixediterable">Заменили <code class="language-plaintext highlighter-rouge">$$.iterator()</code> на <code class="language-plaintext highlighter-rouge">$$.asDynamicIterable()</code> и <code class="language-plaintext highlighter-rouge">$$.asFixedIterable()</code></h1>
<blockquote>
<p><em>Ух какую старую болячку мы исправили!</em>
<em>Ну молодцы же!</em></p>
</blockquote>
<p>Есть в селениде метод <code class="language-plaintext highlighter-rouge">$$</code> для коллекции элементов. Возвращает он объект класса <code class="language-plaintext highlighter-rouge">ElementsCollection</code>.<br />
Изначально у него должен был быть только один метод <code class="language-plaintext highlighter-rouge">$$.shouldHave(<условие>)</code>. Хочешь проверить коллекцию элементов на
соответствие какому-то условию - передай нужное условие. Не нашёл готового условия - напиши своё, благо это легко.</p>
<h3 id="the-ошибка">The ошибка</h3>
<p>Но в те давние времена случилось так, что я наследовал класс <code class="language-plaintext highlighter-rouge">ElementsCollection</code> от <code class="language-plaintext highlighter-rouge">AbstractList<SelenideElement></code>.
Об этом решении я неоднократно жалел впоследствии.</p>
<p>Что же случилось, спросите вы? А случилось то, что люди начали абьюзить коллекции как не в себя.</p>
<ul>
<li>Например, начали итерировать все элементы в коллекции (это <em>случайно</em> стало
возможным благодаря наследованию от <code class="language-plaintext highlighter-rouge">AbstractList</code>).</li>
<li>И ещё люди ожидают, что элементы в коллекции будут постоянно обновляться, ведь до сих пор селенид автоматически обновлял любые веб-элементы.</li>
<li>Помимо сомнительного тест-дизайна, это вызвало ещё и проблемы с производительностью, ведь на каждом шаге итерации селенид
должен загрузить заново всю коллекцию элементов. А это может быть медленно, особенно есть элементов много или коллекция
фильтруется: <code class="language-plaintext highlighter-rouge">$$("div").filter(visible).filterBy(text("Hello"))</code>.</li>
</ul>
<h3 id="the-дилемма">The дилемма</h3>
<p>Получилась дилемма:</p>
<ul>
<li>одним хотелось, чтобы селенид при итерировании перегружал элементы каждый раз, ведь это позволяет не получать <code class="language-plaintext highlighter-rouge">StaleElementException</code>, когда элементы на странице исчезают и появляются;</li>
<li>а другим хотелось, наоборот, чтобы селенид не перегружал элементы при итерировании, ведь это ускоряет тест на больших коллекциях.</li>
</ul>
<p>Какой вариант ни выберешь - всем не угодишь.</p>
<h3 id="the-решение">The решение</h3>
<p>И наконец мы придумали, как решить эту дилемму. Случайно унаследованные от <code class="language-plaintext highlighter-rouge">AbstractList</code> методы типа <code class="language-plaintext highlighter-rouge">$$.iterator()</code> мы
пометили как <code class="language-plaintext highlighter-rouge">@Deprecated</code>. Вместо них предлагаем вам два метода, из которых вы сами сможете выбрать согласно вашим потребностям:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$$.asDynamicIterable()</code> - перегружает элементы коллекции при итерировании. Может быть медленным на больших коллекциях.</li>
<li><code class="language-plaintext highlighter-rouge">$$.asFixedIterable()</code> - не перегружает элементы при итерировании. Он быстрее, но не получает обновлений,
если элементы удаляются или добавляются во время итерирования.</li>
</ul>
<h3 id="the-совет">The совет</h3>
<p>Какой из них я вам советую?</p>
<p>НИКАКОЙ! :)</p>
<p>Напоминаю, что в хорошем тесте, скорее всего, не должно быть циклов, условий и т.п.<br />
Вы должны точно знать, сколько элементов ожидается на странице, и какие свойства должны быть у каждого из них! <em>Просто проверьте эти свойства.</em></p>
<p>Вместо итерирования всегда лучше написать <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/CustomCollectionConditionTest.java">свой <code class="language-plaintext highlighter-rouge">CollectionCondition</code></a>. Благо это легко.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/797">issue 797</a> и <a href="https://github.com/selenide/selenide/pull/1688">PR 1688</a>.</p>
<p><br /></p>
<h1 id="исправили-softassert-чтобы-он-не-валил-тест">Исправили SoftAssert, чтобы он не валил тест</h1>
<p>в довольно редкой ситуации:</p>
<ol>
<li>если софт ассерт listener/rule/extension добавлен к тесту, но</li>
<li>софт ассерты <strong>выключены</strong>, и</li>
<li>внутри теста кидается и тут же ловится ошибка (try/catch).</li>
</ol>
<p>Вообще в такой ситуации надо исправлять сам тест. :(</p>
<p>И всё же. До сих пор селенидовский софт ассерт валил тест, потому что в ходе теста была ошибка, и листенер это заметил.
А теперь листенер стал чуть умнее и тест не валит.</p>
<blockquote>
<p><em>Уф, ну и проблемки вы иногда подкидываете, дорогие пользователи… :)</em></p>
</blockquote>
<p>См. <a href="https://github.com/selenide/selenide/issues/1646">issue 1646</a> и <a href="https://github.com/selenide/selenide/pull/1680">PR 1680</a>.</p>
<p><br /></p>
<h1 id="исправили-софт-ассерты-чтобы-они-включали-все-падения">Исправили софт ассерты, чтобы они включали все падения</h1>
<p>Ещё одна редкая проблема с софт ассертами. Представьте ситуацию:</p>
<ol>
<li>Софт ассерты включены;</li>
<li>В ходе теста случились какие-то селенидовские ошибки (пойманы софт ассерт листенером);</li>
<li>А также в ходе теста случилась какая-то другая ошибка (банальный <code class="language-plaintext highlighter-rouge">NPE</code> или <code class="language-plaintext highlighter-rouge">assertEquals(2, 3)</code>).</li>
</ol>
<p>В этом случае селенидовский софт ассерт листенер валил тест (что правильно), но показывал только селенидовские ошибки (п. 2) и
терял “другую” ошибку (п.3).</p>
<p>Теперь листенер стал умнее, и показывает все ошибки.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1661">issue 1661</a> и <a href="https://github.com/selenide/selenide/pull/1679">PR 1679</a>.</p>
<p><br /></p>
<h1 id="добавили-локаторы-к-некоторым-селенидовским-ошибкам">Добавили локаторы к некоторым селенидовским ошибкам</h1>
<p>Мелочь, но стоит упомянуть.</p>
<p>Мы добавили селектор к некоторым селенидовским ошибкам.<br />
Например, там, где селенид раньше кидал такую ошибку:</p>
<blockquote>
<p>“Invalid element state: Cannot change invisible element”</p>
</blockquote>
<p>Теперь он будет кидать такую:</p>
<blockquote>
<p>“Invalid element state [.btn.btn-primary]: Cannot change invisible element”</p>
</blockquote>
<p><br /></p>
<h1 id="обновили-browserupproxy-с-212-на-213">Обновили BrowserUpProxy с 2.1.2 на 2.1.3</h1>
<p>Важно отметить, что версия 2.1.3 - это форк оригинального BrowserUpProxy.
Автор объявил о прекращении поддержки, добровольцы переняли инициативу и зарелизили версию 2.1.3 <a href="https://github.com/browserup/browserup-proxy/issues/388#issuecomment-1004097733">из форка</a>.</p>
<p>Будем следить за ситуацией. В идеале хорошо бы перейти с BrowserUpProxy на mitmproxy. Есть добровольцы?</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1678">PR 1678</a>.</p>
<p><br /></p>
<h1 id="обновили-testng-с-740-на-75">Обновили TestNG с 7.4.0 на 7.5</h1>
<p>Список изменений <a href="https://github.com/cbeust/testng/blob/7.5/CHANGES.txt">внушительный</a>.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1682">PR 1682</a>.</p>
<p><br /></p>
<p>С Новым Годом, друзья!<br />
Стабильных тестов вам и красивых отчётиков!</p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.1.12021-11-24T00:00:00+00:00http://ru.selenide.org/2021/11/24/selenide-6.1.1
<p><br /></p>
<h1 id="tere-hommikust">TERE HOMMIKUST!</h1>
<p><br />
Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/141?closed=1">Selenide 6.1.1</a>.</p>
<p>В этом маленьком релизе мы исправили сразу пачку проблем с настройками браузеров.<br />
Они все всплыли после обновления на Selenium 4, в котором <code class="language-plaintext highlighter-rouge">ChromeOptions</code> и другие <code class="language-plaintext highlighter-rouge">Capabilities</code> были серьёзно переработаны.</p>
<p>Ну вот, теперь мы погрузились в тему и разом все проблемы исправили.</p>
<p><br /></p>
<h1 id="early-conflicts-detection">Раннее обнаружение конфликтов</h1>
<p>Если вы попытаетесь открыть хром с настройками файерфокса:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">browser</span> <span class="o">=</span> <span class="s">"chrome"</span><span class="o">;</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="no">FIREFOX_OPTIONS</span><span class="o">,</span> <span class="k">new</span> <span class="nc">FirefoxOptions</span><span class="o">());</span>
</code></pre></div></div>
<p>то Селенид версии 5.x ругался, а Селенид 6.0.x перестал ругаться.
Теперь мы ругань восстановили, и вы снова увидите старое доброе</p>
<blockquote>
<p>IllegalArgumentException: Conflicting browser name: ‘chrome’ vs. ‘firefox’</p>
</blockquote>
<p>См. <a href="https://github.com/selenide/selenide/issues/1591">issue 1591</a> и <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p>
<p><br /></p>
<h1 id="merging-chrome-arguments">Слияние аргументов хрома</h1>
<p>Если вы попытаетесь задать хрому, например, полноэкранный режим или язык:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ChromeOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChromeOptions</span><span class="o">();</span>
<span class="n">options</span><span class="o">.</span><span class="na">addArguments</span><span class="o">(</span><span class="s">"--start-fullscreen"</span><span class="o">,</span> <span class="s">"--start-incognito"</span><span class="o">);</span>
<span class="n">options</span><span class="o">.</span><span class="na">setExperimentalOption</span><span class="o">(</span><span class="s">"prefs"</span><span class="o">,</span> <span class="nc">ImmutableMap</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"intl.accept_languages"</span><span class="o">,</span> <span class="s">"de_DE"</span><span class="o">));</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">chromeOptions</span><span class="o">;</span>
<span class="n">open</span><span class="o">(</span><span class="s">"https://codeborne.com"</span><span class="o">;)</span>
</code></pre></div></div>
<p>то эти настройки терялись в Selenide 6.0.x
Теперь мы их восстановили.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1626">issue 1626</a>,
<a href="https://github.com/selenide/selenide/issues/1630">issue 1630</a> и
<a href="https://github.com/selenide/selenide/issues/1631">issue 1631</a>.</p>
<p>Исправление в <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p>
<p><br /></p>
<h1 id="wrapping-browser-capabilities">ВАЖНО</h1>
<p>Если вы использовали <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities</code>, то с большой вероятностью заворачивали
их в <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ChromeOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChromeOptions</span><span class="o">();</span>
<span class="n">options</span><span class="o">.</span><span class="na">addArguments</span><span class="o">(...);</span>
<span class="nc">DesiredCapabilities</span> <span class="n">caps</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DesiredCapabilities</span><span class="o">();</span>
<span class="n">caps</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="nc">ChromeOptions</span><span class="o">.</span><span class="na">CAPABILITY</span><span class="o">,</span> <span class="n">options</span><span class="o">);</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">caps</span><span class="o">;</span>
</code></pre></div></div>
<p>Так вот, теперь этот <strong>код нужно упростить</strong>, чтобы настройки больше не терялись:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ChromeOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChromeOptions</span><span class="o">();</span>
<span class="n">options</span><span class="o">.</span><span class="na">addArguments</span><span class="o">(...);</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">options</span><span class="o">;</span>
</code></pre></div></div>
<p><br /></p>
<h1 id="webdriver-provider-parameter-type">Поменяли тип параметра в <code class="language-plaintext highlighter-rouge">WebDriverProvider</code></h1>
<p>… с <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> на просто <code class="language-plaintext highlighter-rouge">Capabilities</code>.</p>
<p>Для вас почти ничего не меняется. Если вы используете в своих тестах <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>, просто
поменяйте <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> на <code class="language-plaintext highlighter-rouge">Capabilities</code>, и всё будет по-старому.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p>
<p><br /></p>
<p><em>Больше капабилитей богу капабилитей!</em></p>
<p><br /></p>
<h1 id="upd-selenide-6.1.2">UPD Selenide 6.1.2</h1>
<p>Мы выпустили Selenide 6.1.2 с обновлением на Selenium 4.1.1 - эта версия содержит несколько заметных исправлений.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.1.02021-11-23T00:00:00+00:00http://ru.selenide.org/2021/11/23/selenide-6.1.0
<p><br /></p>
<h1 id="tere">TERE!</h1>
<p><br />
Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/137?closed=1">Selenide 6.1.0</a>.</p>
<p><br /></p>
<h1 id="добавили-поддержку-selenideproperties">Добавили поддержку <code class="language-plaintext highlighter-rouge">selenide.properties</code></h1>
<p>Теперь Селенид умеет читать настройки из отдельного файлика <code class="language-plaintext highlighter-rouge">selenide.properties</code>, если таковой найдётся в classpath.</p>
<p>NB! Лично я всё ещё не вижу пользы от этого, ведь проще задать настройки</p>
<ol>
<li>прямо в коде: <code class="language-plaintext highlighter-rouge">Configuration.timeout = 8000;</code></li>
<li>или через system properties: <code class="language-plaintext highlighter-rouge">-Dselenide.timeout=8000</code>.</li>
</ol>
<p>Прошу, не надо резко ломиться генерировать эти файлики. Используйте <code class="language-plaintext highlighter-rouge">selenide.properties</code>, только если у вас есть хорошие причины для этого, а не просто потому, что теперь это модно или “так красиво”.</p>
<p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за
<a href="https://github.com/selenide/selenide/pull/1601">PR 1601</a>.</p>
<p><br /></p>
<h1 id="добавили-возможность-тонкой-настройки-прокси">Добавили возможность тонкой настройки прокси</h1>
<p>Как вы знаете, Селенид умеет запускать свой прокси-сервер, который даёт нам некоторые дополнительные возможности. Но возможности для настройки прокси до сих пор были ограниченные. Только <code class="language-plaintext highlighter-rouge">Configuration.proxyHost</code> и <code class="language-plaintext highlighter-rouge">Configuration.proxyPort</code>.</p>
<p>Теперь же можно будет получить инстанс BrowserModProxy и настроить его как угодно до запуска браузера.</p>
<p>NB! Пожалуйста, не переусердствуйте с этим. Тут очень легко выстрелить себе в ногу.</p>
<p>И если ваши настройки действительно помогли вам, то возможно, они помогут и другим. Расскажите нам, что вы там такого настроили - может, стоит сделать это в селениде по умолчанию?</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1561">issue 1561</a>.
Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за
<a href="https://github.com/selenide/selenide/pull/1620">PR 1620</a>.</p>
<p><br /></p>
<h1 id="добавили-костыль-для-избежания-случайных-noclassdeffounderror-в-webdriverexception">Добавили костыль для избежания случайных <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code> в <code class="language-plaintext highlighter-rouge">WebDriverException</code>.</h1>
<p>В Селениум есть <a href="https://github.com/SeleniumHQ/selenium/issues/9784">бага</a>, которая полностью до сих пор не исправлена. Но вы её больше не увидите, потому что теперь в селениде есть костыль против неё. :)</p>
<p>См. <a href="https://github.com/selenide/selenide/commit/2eff0307e3a">костыль</a>.</p>
<p><br /></p>
<h1 id="поменяли-тип-параметра-selenideconfigbrowsercapabilities">Поменяли тип параметра <code class="language-plaintext highlighter-rouge">SelenideConfig.browserCapabilities()</code></h1>
<p>… с <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> на <code class="language-plaintext highlighter-rouge">MutableCapabilities</code>.</p>
<p>Это позволяет упростить ваш код и не заворачивать <code class="language-plaintext highlighter-rouge">ChromeOptions</code> в <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code>.
Больше об упрощении капабилитей этом будет в следующем релизе <code class="language-plaintext highlighter-rouge">Selenide 6.1.1</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1637">PR 1637</a>.</p>
<p><br /></p>
<h1 id="обновились-на-selenium-webdriver-410">Обновились на Selenium Webdriver 4.1.0</h1>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1638">PR 1638</a>.</p>
<p><br /></p>
<h1 id="удалили-метод-shadowroot">Удалили метод <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></h1>
<p>Этот метод сломался после обновления на Chrome 96, и вероятно, вскоре сломается и в остальных браузерах. Починить его муторно, и при этом <a href="https://github.com/selenide/selenide/issues/1515#issuecomment-894476289">пользы от этого метода немного</a>, ведь для поиска элементов внутри Shadow DOM есть <a href="/2020/03/18/selenide-5.10.0/">более удобные и быстрые методы</a>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1640">issue 1640</a> и
<a href="https://github.com/selenide/selenide/pull/1641">PR 1641</a>.</p>
<p><br /></p>
<h1 id="новости">Новости</h1>
<ul>
<li>Первый <a href="https://www.eventbrite.com/e/propeller-testops-hackathon-registration-194333114577">TestOps Hackathon</a> от PropellerAds и Qameta Software перенесли на 1-6 декабря. Регистрируйтесь!</li>
<li><a href="https://www.lambdatest.com/selenium-automation-testing-with-selenide-framework">Как использовать Selenide с сервисом Lambdatest</a></li>
<li>Пост <a href="https://mbbaig.blog/selenide-webdriverfactory/">Selenide - Create a Custom WebDriver</a> от Boris Bay</li>
<li>Оказывается, <a href="https://www.linkedin.com/feed/update/urn:li:activity:6867477909766979584/">LinkedIn проводит курсы по Селениду</a> и даже выдаёт красивенькие дипломы.</li>
</ul>
<p><br /></p>
<h1 id="статистика-использования-селенида">Статистика использования Селенида</h1>
<center>
<img src="/images/2021/11/selenide.downloads.png" width="800" />
</center>
<p><br />
В октябре мы сделали мощный скачок и перевалили за <strong>280 тысяч скачиваний</strong> в месяц. Эгегей!</p>
<p><em>Больше скачиваний богу скачиваний!</em></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 6.0.12021-10-25T00:00:00+00:00http://ru.selenide.org/2021/10/25/selenide-6.0.1
<p><br /></p>
<h1 id="день-рождения">День рождения!</h1>
<p>Ура! Сложно поверить, но сегодня Селениду исполняется… 10 лет!<br />
Именно в этот день 10 лет назад был сделан <a href="https://github.com/selenide/selenide/commit/3716078fc7fda8c5da01d871882d513cbd97cd0e">первый коммит</a> в репозитории.</p>
<p>Огромное спасибо всем, кто участвовал в проекте, коммитил, репортил баги, предлагал идеи, отвечал на вопросы в форумах,
критиковал и рассказывал про селенид на конференциях и митапах. Да и просто решился использовать селенид в своих проектах.
Вы все - часть этого движения.</p>
<p><br />
Ну а мы в честь юбилея зарелизили мажорную версию <a href="https://github.com/selenide/selenide/milestone/136?closed=1">Selenide 6.0.1</a>.</p>
<p><br /></p>
<h1 id="обновились-на-selenium-webdriver-400">Обновились на Selenium Webdriver 4.0.0</h1>
<p>Если вы не используете Grid, то для вас особо ничего и не должно поменяться. Всё более-менее работает как раньше.
Ну там, переименовали или задеприкейтили некоторые классы, вроде нестрашно.</p>
<p>В Selenium 4 появились и <a href="https://www.browserstack.com/guide/selenium-4-features">новые возможности</a> (такие как CDP и Relative locators), но какой-то отдельной поддержки для них в
селениде мы пока не делали. Если понадобится, будем делать в следующих версиях.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1162">issue 1162</a>,
<a href="https://github.com/selenide/selenide/pull/1605">PR 1605</a>,
<a href="https://github.com/selenide/selenide/pull/1614">PR 1614</a> и
<a href="https://github.com/selenide/selenide/pull/1617">PR 1617</a>.</p>
<p><br /></p>
<h1 id="разбили-selenide-на-несколько-артефактов">Разбили Selenide на несколько артефактов</h1>
<p>Раньше весь код селенида поставлялся одним джарником: <code class="language-plaintext highlighter-rouge">selenide-5.25.0.jar</code>.<br />
Предполагалось, что дополнительно к нему вы должны были сами добавить и другие зависимости: JUnit или TestNG, и возможно, BrowserMobProxy.</p>
<p>Теперь же мы решили упростить вам задачу (особенно с прокси).</p>
<ol>
<li>Для пользователей Selenide и JUnit5 ничего не меняется: <br /><code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide:6.0.1)</code></li>
<li>Пользователи JUnit4 должны будут использовать <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-junit4:6.0.1)</code></li>
<li>Пользователи TestNG должны будут использовать <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-testng:6.0.1)</code></li>
<li>Если вы хотите использовать прокси, достаточно добавить в проект зависимость <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-proxy:6.0.1)</code> - и можно больше не заморачиваться с версиями BrowserUpProxy, Netty и т.п.</li>
<li>Ну и если среди вас есть радикальные ненавистники статических методов, теперь и вы сможете быть счастливы, используя <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-core:6.0.1)</code> - и тогда у вас в проекте не будет статических методов <code class="language-plaintext highlighter-rouge">Selenide.*</code>, а только чистый <code class="language-plaintext highlighter-rouge">SelenideDriver</code>.</li>
</ol>
<p>См. <a href="https://github.com/selenide/selenide/pull/1612">PR 1612</a>.</p>
<p><br /></p>
<h1 id="подчистили-кучу-deprecated-методов">Подчистили кучу deprecated методов</h1>
<p>По <a href="https://github.com/selenide/selenide/pull/1607/files">этой ссылке</a> всегда можете найти, что именно удалили и чем это заменить.<br />
В частности,</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">ElementsCollection.shouldHaveSize()</code> -> <code class="language-plaintext highlighter-rouge">ElementsCollection.shouldHave(size())</code></li>
<li><code class="language-plaintext highlighter-rouge">$.waitUntil(_, timeout)</code> -> <code class="language-plaintext highlighter-rouge">$.should(_, Duration.ofMillis(timeout))</code></li>
<li><code class="language-plaintext highlighter-rouge">$.waitWhile(_, timeout)</code> -> <code class="language-plaintext highlighter-rouge">$.shouldNot(_, Duration.ofMillis(timeout))</code></li>
<li><code class="language-plaintext highlighter-rouge">Condition.disappears</code> -> <code class="language-plaintext highlighter-rouge">Condition.hidden</code></li>
<li><code class="language-plaintext highlighter-rouge">Condition.matchesText</code> -> <code class="language-plaintext highlighter-rouge">Condition.matchText</code></li>
<li><code class="language-plaintext highlighter-rouge">Selenide.close</code> -> <code class="language-plaintext highlighter-rouge">Selenide.closeWebDriver()</code> или <code class="language-plaintext highlighter-rouge">Selenide.closeWindow()</code>
<br /></li>
</ul>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за
<a href="https://github.com/selenide/selenide/pull/1607/files">PR 1607</a> и
<a href="https://github.com/selenide/selenide/pull/1609">PR 1609</a>.</p>
<p><br /></p>
<h1 id="удалили-поддержку-браузера-legacy_firefox">Удалили поддержку браузера “legacy_firefox”</h1>
<p>Это был такой старый вебдрайвер, который работал только с Firefox 52 и старше. Полагаю, ему пора на покой.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1610">PR 1610</a>.</p>
<p><br /></p>
<h1 id="удалили-старые-настройки">Удалили старые настройки</h1>
<h3 id="configurationstartmaximized"><code class="language-plaintext highlighter-rouge">Configuration.startMaximized</code></h3>
<p>Есть мнение, что это плохая практика, потому что размер окна браузера зависит от текущего окружения, что может привести к моргающим тестам.
Рекомендуем использовать <code class="language-plaintext highlighter-rouge">Configuration.browserSize</code> (по умолчанию <code class="language-plaintext highlighter-rouge">1366x768</code>).</p>
<h3 id="configurationversatilesetvalue"><code class="language-plaintext highlighter-rouge">Configuration.versatileSetValue</code></h3>
<p>Скорее всего вы её и не использовали, ведь она давным-давно была <code class="language-plaintext highlighter-rouge">false</code> по умолчанию.<br />
Для выбора значений в <code class="language-plaintext highlighter-rouge"><select></code> и <code class="language-plaintext highlighter-rouge"><input type=radio></code> можно использовать старый добрые методы
<code class="language-plaintext highlighter-rouge">$.selectOptionByValue()</code> и <code class="language-plaintext highlighter-rouge">$.selectRadio()</code>.</p>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1619">PR 1619</a>.</p>
<p><br /></p>
<h1 id="починили-метод-selenidesleepn">Починили метод <code class="language-plaintext highlighter-rouge">Selenide.sleep(N)</code></h1>
<p>Казалось бы, разве такой просто однострочный метод может быть сломан? А вот может.
Оказывается, стандартный джавовый метод <code class="language-plaintext highlighter-rouge">Thread.sleep(N)</code> не обязательно спит именно N мс, он может проснуться и раньше.
И это может привести к моргающим тестам, если ваш тест рассчитывал именно на определённую паузу.
Век живи - век учись.</p>
<p>Теперь метод <code class="language-plaintext highlighter-rouge">Selenide.sleep(N)</code> гарантированно спит заданное количество миллисекунд.</p>
<p>См. <a href="https://github.com/selenide/selenide/blob/b05d53dfb794ee02e795587867c6ec8022171040/statics/src/main/java/com/codeborne/selenide/Selenide.java#L258">реализацию</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-для-добавления-и-удаления-webdriverlistener">Добавили метод для добавления и удаления <code class="language-plaintext highlighter-rouge">WebDriverListener</code></h1>
<p>Раньше в селениде можно было добавлять <code class="language-plaintext highlighter-rouge">WebDriverEventListener</code>, но этот класс был заменён на <code class="language-plaintext highlighter-rouge">WebDriverListener</code> в Selenium 4.
Ну вот, пришлось поддерживать оба.</p>
<p><br /></p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1615">issue 1615</a> и <a href="https://github.com/selenide/selenide/pull/1616">PR 1616</a>.</p>
<p><br /></p>
<h1 id="поменяли-сигнатуру-метода-conditionapply">Поменяли сигнатуру метода <code class="language-plaintext highlighter-rouge">Condition.apply</code></h1>
<p>Если вы не писали свои Conditions, то вас это не касается.</p>
<p>А если успели наваять, то придётся их немножко дополнить.</p>
<p>Итак,
раньше в классе <code class="language-plaintext highlighter-rouge">Condition</code> был метод <code class="language-plaintext highlighter-rouge">apply</code>, который возвращал boolean:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">apply</span><span class="o">(</span><span class="nc">Driver</span> <span class="n">driver</span><span class="o">,</span> <span class="nc">WebElement</span> <span class="n">element</span><span class="o">)</span>
</code></pre></div></div>
<p>Теперь его нужно переименовать в <code class="language-plaintext highlighter-rouge">check</code>, и возвращать он должен не просто boolean, а <code class="language-plaintext highlighter-rouge">CheckResult</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">CheckResult</span> <span class="nf">check</span><span class="o">(</span><span class="nc">Driver</span> <span class="n">driver</span><span class="o">,</span> <span class="nc">WebElement</span> <span class="n">element</span><span class="o">)</span>
</code></pre></div></div>
<p>А этот <code class="language-plaintext highlighter-rouge">CheckResult</code> содержит не только признак “проверка прошла / не прошла”, но и какое значение было у элемента в тот момент.
Это позволить формировать более точное сообщение об ошибке в случае падения теста.</p>
<p>P.S. Впрочем, старый метод <code class="language-plaintext highlighter-rouge">apply</code> всё ещё остался как deprecated, поэтому какое-то время вы можете сидеть на старых кондишинах.
Просто при падении теста строчка “Actual value: “ не будет добавляться к сообщению об ошибке. Но это и не всегда критично.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/217">issue 217</a>,
<a href="https://github.com/selenide/selenide/pull/1586">PR 1586</a> и
<a href="https://github.com/selenide/selenide/pull/1618">PR 1618</a>.</p>
<p><br /></p>
<h1 id="selenide-selenoid-201">selenide-selenoid 2.0.1</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.0.0"><code class="language-plaintext highlighter-rouge">selenide-selenoid:2.0.0</code></a> с обновлением на Selenide 6.0.1</p>
<p><br /></p>
<h1 id="selenide-appium">selenide-appium</h1>
<p>Кажется, пока что Appium не поддерживает Selenium 4, так что <code class="language-plaintext highlighter-rouge">selenide-appium</code> пока обновить не можем. Будет следить за ситуацией.</p>
<p><br /></p>
<h1 id="upd-selenide-602">UPD Selenide 6.0.2</h1>
<p>Обнаружилась проблемка с проектами на TestNG, по-быстрому выпустили хотфикс 6.0.2.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1623">issue 1623</a></p>
<p><br /></p>
<h1 id="upd-selenide-603">UPD Selenide 6.0.3</h1>
<p>Оказалось, что Maven может подтягивать старый <code class="language-plaintext highlighter-rouge">selenium-api-3*.jar</code>, если он найден в дереве зависимостей
(обычно у BrowserUpProxy или Allure).</p>
<p>Мне давно кажется, что тут Maven мог бы быть чуточку поумнее и выбирать всё-таки более свежую версию.<br />
Но поскольку люди довольно часто наступают на эти грабли, мы решили запилить костыль в селениде.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1625">костыль 1625</a></p>
<p><br /></p>
<h1 id="что-почитать-про-selenium-4">Что почитать про Selenium 4</h1>
<ul>
<li><a href="https://applitools.com/blog/selenium-4/">What’s New In Selenium 4?</a> by Applitools</li>
<li><a href="https://www.browserstack.com/guide/selenium-4-features">Selenium 4: Understanding Key Features</a> by BrowserStack</li>
<li><a href="https://saucelabs.com/selenium-4">A comprehensive guide to Selenium 4</a> by SauceLabs</li>
<li><a href="https://www.lambdatest.com/blog/what-is-deprecated-in-selenium4/">What Is New In Selenium 4 And What Is Deprecated In It?</a> by LambdaTest</li>
<li><a href="https://www.selenium.dev/documentation/">Обновлённый сайт Selenium</a></li>
</ul>
<p><br /></p>
<h1 id="другие-новости">Другие новости</h1>
<ul>
<li>внезапный <a href="https://youtu.be/mK-6-k5EwQM">выпуск Heisenbug Show</a>, посвящённый 10-летию Селенида.</li>
<li>Первый <a href="https://www.eventbrite.com/e/propeller-testops-hackathon-registration-194333114577">TestOps Hackathon</a> от PropellerAds и Qameta Software - с 12 по 16 ноября. Регистрируйтесь!</li>
<li>Ребята из Aerokube выпустили <a href="https://github.com/aerokube/lightning-java">альтернативный клиент WebDriver</a>. Теоретически теперь селенид может работать на нём вместо Selenium Webdriver. Звучит заманчиво?</li>
<li><a href="https://www.youtube.com/watch?v=ch5sxsQJzW4">Koncerns: Почему я не тороплюсь на Котлин</a></li>
<li>Я пока не разбирался, но люди вроде хвалят: <a href="https://github.com/markhobson/docker-maven-chrome">какие-то образы Docker для Selenium</a></li>
</ul>
<p><br /></p>
<h1 id="статистика-использования-селенида">Статистика использования Селенида</h1>
<center>
<img src="/images/2021/10/selenide.downloads.png" width="800" />
</center>
<p><br />
Перевалили за <strong>255 тысяч скачиваний</strong> в месяц.</p>
<p>Что нас ждёт впереди?</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.25.02021-09-28T00:00:00+00:00http://ru.selenide.org/2021/09/28/selenide-5.25.0
<p>Добрый вечер!</p>
<p>Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/131?closed=1">Selenide 5.25.0</a>.</p>
<blockquote>
<p><strong>Очень советую обновиться</strong>,<br />
потому что за этим релизом грядут ещё большие изменения, включая полноценный релиз Selenium 4.<br />
Если не обновитесь сейчас, потом будет намного сложнее разгребать завалы!</p>
</blockquote>
<p>Это прямо большой релиз с кучей изменений, так что запаситесь попкорном и заварите чаю. Погнали!</p>
<p><br /></p>
<h1 id="поддержка-selenium-webdriver-400-rc1">Поддержка Selenium Webdriver 4.0.0 RC1</h1>
<p>Мы выпустили две сборки Selenide 5.25.0: обычную и хипстерскую.<br />
Можете сами выбрать:</p>
<table>
<tbody>
<tr>
<td>либо</td>
<td><code class="language-plaintext highlighter-rouge">com.codeborne:selenide:5.25.0</code></td>
<td>(с <code class="language-plaintext highlighter-rouge">Selenium 3.x</code>),</td>
</tr>
<tr>
<td>либо</td>
<td><code class="language-plaintext highlighter-rouge">com.codeborne:selenide:5.25.0-selenium-4.0.0-rc-1</code></td>
<td>(с <code class="language-plaintext highlighter-rouge">Selenium 4.0.0 RC1</code>).</td>
</tr>
</tbody>
</table>
<p><br /></p>
<h1 id="добавили-поддержку-opentest4j">Добавили поддержку OpenTest4j</h1>
<p>Если вы ещё не слышали, <a href="https://github.com/ota4j-team/opentest4j">OpenTest4j</a> - это маленькая библиотека для assertion errors, созданная по инициативе ребят из JUnit 5. Идея в том, чтобы все тестовые фреймворки кидали именно эти ошибки, а все IDE их поддерживали. Её уже давно поддерживают крупные игроки типа JUnit, TestNG, AssertJ, IDEA и Eclipse, а вот теперь ещё и Selenide.</p>
<p><strong>Что изменится лично для вас:</strong><br />
Когда ваш UI тест падает (ну, <a href="https://github.com/selenide/selenide/issues/1589">почти всегда</a>), IDEA теперь красивенько показывает внизу ссылочку <code class="language-plaintext highlighter-rouge"><Click to see difference></code>. Вы можете её кликнуть и увидеть красивый идеевский DIFF в отдельном диалоге. Очень удобно при отладке.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/969">issue 969</a> и <a href="https://github.com/selenide/selenide/pull/1545">PR 1545</a>.</p>
<p>NB! Если вы в своих проектах использовать напрямую селенидовские классы ошибок (наследовали или кидали), то придётся чуть подпилить код, потому что мы чутка изменили сигнатуры их конструкторов:</p>
<ul>
<li>поменяли местами аргументы “expected” и “actual”</li>
<li>убрали ненужный параметр “driver” из большинства конструкторов</li>
</ul>
<p><br /></p>
<center>
<img src="/images/2021/09/idea-see-diff.png" width="400" style="margin-right: 20px;" />
<img src="/images/2021/09/idea-diff.png" width="300" />
</center>
<p><br /></p>
<h1 id="показываем-стектрейс-под-каждой-ошибкой-в-softasserts">Показываем стектрейс под каждой ошибкой в SoftAsserts</h1>
<p>До сих пор селенидовские софт ассерты показывали только один общий стектрейс после всех ошибок. А удобнее видеть стектрейс под каждой, тогда легко кликнуть на нужную строчку и попасть сразу в нужное место в коде.</p>
<p>Теперь стектрейс будет под каждой ошибкой. Правда, теперь общее сообщение об ошибке стало гораздо длиннее, но ведь их не должно быть часто и много, правда? ;)</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1543">issue 1543</a> и <a href="https://github.com/selenide/selenide/pull/1545">PR 1545</a></p>
<p><br /></p>
<h1 id="добавили-метод-shadowroot">Добавили метод <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></h1>
<p>Раньше можно было только искать элементы внутри shadow root, а теперь можно получить и сам shadow root. Правда, пользы от этого пока немного, т.к. поиск внутри этого элемента не поддерживается (браузерами или вебдрайверами, я уж не знаю - запутался).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1515">issue 1515</a> и <a href="https://github.com/selenide/selenide/pull/1517">PR 1517</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-ancestor">Добавили метод <code class="language-plaintext highlighter-rouge">$.ancestor()</code></h1>
<p>По сути это просто синоним к существующему методу <code class="language-plaintext highlighter-rouge">$.closest()</code>. Но только название <code class="language-plaintext highlighter-rouge">closest</code> было родом из JQuery (олды помнят!), а <code class="language-plaintext highlighter-rouge">ancestor</code> должно быть более понятным, т.к. это общеизвестный термин из XPath.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1556">issue 1556</a>.<br />
Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1567">PR 1567</a></p>
<p><br /></p>
<h1 id="обогатили-методы-closest-и-ancestor">Обогатили методы <code class="language-plaintext highlighter-rouge">$.closest()</code> и <code class="language-plaintext highlighter-rouge">$.ancestor()</code></h1>
<p>Раньше этот метод умел искать элемент только по тэгу или классу:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$.ancestor("table").shouldBe(visible)</code></li>
<li><code class="language-plaintext highlighter-rouge">$.ancestor(".form").shouldBe(visible)</code></li>
</ul>
<p>То теперь добавился поиск по атрибуту:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$.ancestor("[argument-name]");</code></li>
<li><code class="language-plaintext highlighter-rouge">$.ancestor("[argument-name=argument-value]");</code></li>
</ul>
<p>Спасибо <a href="https://github.com/plagov">Vitali Plagov</a> за <a href="https://github.com/selenide/selenide/pull/1554">PR 1554</a></p>
<p><br /></p>
<h1 id="починили-метод-screenshot-на-маках">Починили метод <code class="language-plaintext highlighter-rouge">$.screenshot()</code> на маках</h1>
<p>В селениде уже давно есть метод <code class="language-plaintext highlighter-rouge">$.screenshot()</code>, который позволяет сделать снимок не всего экрана, а только указанного элемента.
Оказалось, что этот метод неправильно работал на MacBook: вырезал не ту область экрана (из-за всей этой чехарды с пикселями на дисплее Retina).</p>
<p>К счастью, мы обнаружили, что соответствующий функционал уже давно реализован в селениуме, поэтому мы вырезали костыльный код из селенида и использовали стандартный метод вебдрайвера. Работает как минимум на Chrome, Firefox, Edge.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1571">issue 1571</a> и <a href="https://github.com/selenide/selenide/pull/1576">PR 1576</a>.</p>
<p><br /></p>
<h1 id="при-падении-текстовых-проверок-селенид-выдаёт-точное-значение-на-момент-падения">При падении текстовых проверок селенид выдаёт точное значение на момент падения</h1>
<p>Ничего себе, какие старые болячки мы иногда чиним! Этот тикет был зарегистрирован аж <em>6 сентября 2015</em>!</p>
<p><strong>В общем, история такая.</strong></p>
<p>Допустим, в вашем тесте есть проверка</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">));</span>
</code></pre></div></div>
<p>И стандартный таймаут в 4 секунды. И вот:</p>
<ol>
<li>в течение 4 секунд селенид проверяет-проверяет текст, а он всё не тот и не тот (допустим, “Goodbye”).</li>
<li>таймаут истекает,</li>
<li>селенид решает кинуть ошибку,</li>
<li>начинает составлять сообщение и добавляет к нему текущий текст элемента - а он как раз в этот момент взял и да поменялся! Допустим, на “Hello”.</li>
</ol>
<p>Такое случается очень редко, но когда случается, может вызвать недопонимание.
Вы видите ошибку:</p>
<blockquote>
<p>Текст не совпал. Ожидался: Hello, а был: Hello.</p>
</blockquote>
<p>и чувствуете, как мозг начинает подгорать…</p>
<p>Теперь Селенид запоминает <strong>именно тот текст</strong>, который был на момент проверки, и именно его добавляет в сообщение об ошибке. Да здравствуют неподгоревшие мозги!</p>
<p>P.S. В будущем мы планируем этот механизм ещё больше усовершенствовать, и выводить всю историю изменений текста в течение этих несчастных 4 секунд. Больше отчётов богу отчётов!</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/217">issue 217</a> и <a href="https://github.com/selenide/selenide/pull/1566">PR 1566</a>.<br />
Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1313">PR 1313</a>.</p>
<p><br /></p>
<h1 id="запретили-пустой-аргумент-в-методе-matchtext">Запретили пустой аргумент в методе <code class="language-plaintext highlighter-rouge">$.matchText("")</code></h1>
<p>В селениде есть метод для проверки текста элемента регулярным выражением:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#child_div1"</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">matchText</span><span class="o">(</span><span class="s">"Таллин{1,2}"</span><span class="o">)));</span>
</code></pre></div></div>
<p>И тут мы обнаружили, что метод <code class="language-plaintext highlighter-rouge">matchText</code> позволяет передать ему пустую строку, что можем привести к ложно-зелёным тестам. Теперь же при попытке вызвать <code class="language-plaintext highlighter-rouge">$.should(matchText("")))</code> вы сразу увидите такую ошибку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">IllegalArgumentException</span><span class="o">:</span> <span class="nc">Argument</span> <span class="n">must</span> <span class="n">not</span> <span class="n">be</span> <span class="kc">null</span> <span class="n">or</span> <span class="n">empty</span> <span class="n">string</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/pull/1566/commits/3f6421226c">PR 1566</a></p>
<p><br /></p>
<h1 id="добавили-проверку-webdrivershouldhavetitle">Добавили проверку <code class="language-plaintext highlighter-rouge">webdriver().shouldHave(title(...))</code></h1>
<p>Как обычно, есть варианты со стандартным таймаутом и кастомным:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">title</span><span class="o">(</span><span class="s">"Login page"</span><span class="o">));</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">title</span><span class="o">(</span><span class="s">"Login page"</span><span class="o">),</span> <span class="n">ofMillis</span><span class="o">(</span><span class="mi">10</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1573">issue 1573</a>.<br />
Спасибо <a href="https://github.com/ervuks">Ervīns Patmalnieks</a> за <a href="https://github.com/selenide/selenide/pull/1579">PR 1579</a>.</p>
<p><br /></p>
<h1 id="selenide-selenoid-120">selenide-selenoid 1.2.0</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.2.0"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.2.0</code></a> с обновлением на Selenide 5.25.0</p>
<p><br /></p>
<h1 id="selenide-appium-170">selenide-appium 1.7.0</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.7.0"><code class="language-plaintext highlighter-rouge">selenide-appium:1.7.0</code></a> с обновлением на Selenide 5.25.0</p>
<p><br />
<br /></p>
<h1 id="ссылки">Ссылки</h1>
<ul>
<li>Вышло моё <a href="https://www.youtube.com/watch?v=o_jAKaVzjyc">интервью с Лёшей Маршалом</a> на ютуб канале “Тестирование ПО”</li>
<li>5-7 октября снова будет Гейзенбаг - <a href="https://heisenbug-moscow.ru/schedule/">советую!</a></li>
<li><a href="https://youtube.com/playlist?list=PLsVTVVvrKX9td9Zm_4nF6Ywlz6gC5_e7K">Доклады с весеннего Гейзенбага</a>
<ul>
<li>В т.ч. мой доклад <a href="https://www.youtube.com/watch?v=LDjDtR6kd2c">Flaky tests. Метод</a></li>
<li>В т.ч. мой мастер-класс <a href="https://heisenbug-piter.ru/2021/spb/talks/3tg2q9jq8nvldssitjuzvr/">Как начать проект автоматизации. Продолжение.</a> - <a href="https://www.youtube.com/watch?v=h254Tccxgq4&list=PLsVTVVvrKX9td9Zm_4nF6Ywlz6gC5_e7K&index=15&ab_channel=Heisenbug">часть 1</a>, <a href="https://www.youtube.com/watch?v=WETyt87o_R4&list=PLsVTVVvrKX9td9Zm_4nF6Ywlz6gC5_e7K&index=16&t=3s&ab_channel=Heisenbug">часть 2</a>.</li>
</ul>
</li>
<li>Лекция <a href="https://www.youtube.com/watch?v=xknQcm6H87M">Автоматизация для QA: Selenide</a></li>
<li>Пост <a href="https://www.indellient.com/blog/automated-testing-using-selenide-testng/">Using Selenide & TestNG</a></li>
</ul>
<p><br /></p>
<h1 id="статистика-использования-селенида">Статистика использования Селенида</h1>
<center>
<img src="/images/2021/09/selenide.downloads.png" width="800" />
</center>
<p><br />
Знаковый рубеж пройден: перевалили за <strong>250 тысяч скачиваний</strong> в месяц.</p>
<p><br /></p>
<h2 id="юбилей">Юбилей</h2>
<p>Кстати, мы вплотную подошли к ещё одному знаковому рубежу: в октябре Селениду исполняется… 10 лет!<br />
В это сложно поверить, но <a href="https://github.com/selenide/selenide/commit/3716078fc7fda8c5da01d871882d513cbd97cd0e">первый коммит</a> в репозитории был сделан аж 25 октября 2011 года. Вы только посмотрите, какой он был нелепый! :)</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.24.02021-08-29T00:00:00+00:00http://ru.selenide.org/2021/08/29/selenide-5.24.0
<p>Добрый вечер!</p>
<p>29 августа 1997 года компьютерная система Скайнет вышла из-под контроля и нанесла ядерный удар по России.
Так началась война между терминаторами и человечеством.</p>
<p>С тех прошло 24 года, с ума сойти! Мы живём в будущем! И пока ещё мы управляем компьютерами.</p>
<p>Я каждый год отмечаю этот день, и сегодня в честь праздника мы зарелизили <a href="https://github.com/selenide/selenide/milestone/130?closed=1">Selenide 5.24.0</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-executecommand-duration">Добавили метод <code class="language-plaintext highlighter-rouge">$.execute(Command, Duration)</code></h1>
<p>для запуска самопальных команд с заданным таймаутом.</p>
<p>В <a href="https://ru.selenide.org/2019/09/02/selenide-5.3.0/">Selenide 5.3.0</a> мы добавили возможность легко запускать самопальные команды с помощью метода <code class="language-plaintext highlighter-rouge">$.execute()</code>. Но тогда не было возможности задать кастомный таймаут. Теперь можно.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1525">issue 1525</a>.<br />
Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1531">PR 1531</a></p>
<p><br /></p>
<h1 id="методы-executecommand-и-executecommand-duration-больше-не-передают-параметры-заданной-команде">Методы <code class="language-plaintext highlighter-rouge">$.execute(Command)</code> и <code class="language-plaintext highlighter-rouge">$.execute(Command, Duration)</code> больше не передают параметры заданной команде</h1>
<p>Это мелочь, но теоретически может сломать ваши самопальные команды, если они у вас есть. Будьте начеку.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1527">issue 1527</a>.<br />
Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1535">PR 1535</a></p>
<p><br /></p>
<h1 id="исправили-условия-or-и-and-при-работе-с-несуществующими-элементами">Исправили условия <code class="language-plaintext highlighter-rouge">Or</code> и <code class="language-plaintext highlighter-rouge">And</code> при работе с несуществующими элементами</h1>
<p>См. <a href="https://github.com/selenide/selenide/issues/1534">issue 1534</a>.<br />
Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1539">PR 1539</a></p>
<p><br /></p>
<h1 id="теперь-условия-or-и-and-не-позволяют-передать-пустой-список-условий">Теперь условия <code class="language-plaintext highlighter-rouge">Or</code> и <code class="language-plaintext highlighter-rouge">And</code> не позволяют передать пустой список условий</h1>
<p>Надеюсь, вы и не пытались, потому что это лишено смысла. Но если раньше Селенид это позволял и тесты могли быть ложно
зелёными, то теперь вы словите ошибку в рантайме. <br />
Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1542">PR 1542</a></p>
<p><br /></p>
<h1 id="переименовали-методы-conditionapplynull-и-collectionconditionapplynull">Переименовали методы <code class="language-plaintext highlighter-rouge">Condition.applyNull()</code> и <code class="language-plaintext highlighter-rouge">CollectionCondition.applyNull()</code></h1>
<p>Когда-то по молодости я назвал этот метод <code class="language-plaintext highlighter-rouge">applyNull</code>, но это название вводит в заблуждение.
Этот метод говорит о том, выполняется ли условие, если искомый элемент вообще не найден, и теперь он называется
<code class="language-plaintext highlighter-rouge">missingElementSatisfiesCondition()</code> - длинновато, зато точно.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1541">issue 1541</a>.<br />
Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1544">PR 1544</a></p>
<p><br /></p>
<h1 id="убрали-из-логов-длинные-бесполезные-стектрейсы-при-закрытии-вебдрайвера">Убрали из логов длинные бесполезные стектрейсы при закрытии вебдрайвера</h1>
<p>Как вы знаете, Селенид сам закрывает вебдрайвер в тот момент, когда он больше не нужен.<br />
Для этого Селенид запускает всякие хитрые потоки, которые в фоновом режиме следят со состоянием всех открытых вебдрайверов.<br />
И когда наступает момент, закрывают ненужные.</p>
<p>Но так бывает, что к моменту закрытия вебдрайвера его уже успел закрыть какой-то другой поток. И тогда Селенид мог
плеваться в лог длинными страшными стектрейсами. Это не опасно, но никто ведь не любит спам.</p>
<p>В общем, теперь Селенид немного сократил объём спама. :)</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1467">issue 1467</a> и
<a href="https://github.com/selenide/selenide/pull/1540">PR 1540</a></p>
<p><br /></p>
<h1 id="исправили-поиск-shadow-root-внутри-веб-элементов">Исправили поиск shadow root внутри веб-элементов</h1>
<p>См. <a href="https://github.com/selenide/selenide/issues/1532">issue 1532</a> и
<a href="https://github.com/selenide/selenide/pull/1536">PR 1536</a></p>
<p><br /></p>
<h1 id="selenide-selenoid-115">selenide-selenoid 1.1.5</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.5"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.5</code></a> с обновлением на Selenide 5.24.0</p>
<p><br /></p>
<h1 id="selenide-appium-168">selenide-appium 1.6.8</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.6.8"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.8</code></a> с обновлением на Selenide 5.24.0</p>
<p><br />
<br /></p>
<h1 id="upd-selenide-5241">UPD: Selenide 5.24.1</h1>
<p>WebDriverManager - это библиотека (которую Селенид использует под капотом) для скачивания бинарников вебдрайвера.
Недавно они выпустили большое обновление 5.0.0 с кучей новых фич. Возможно, мы захотим использовать какие-то из этих
фич в Селениде, но пока что мы по-быстрому выпустили <strong>Selenide 5.24.1</strong> с обновлением на WebDriverManager 5.0.1.</p>
<p>Спасибо <a href="https://github.com/anilreddy">Anil Kumar Reddy Gaddam</a> за <a href="https://github.com/selenide/selenide/pull/1547">PR 1531</a>.</p>
<p><br /></p>
<h1 id="upd-selenide-5242">UPD: Selenide 5.24.2</h1>
<p>Оказалось, что зависимость <code class="language-plaintext highlighter-rouge">commons-lang3</code> (которую Селенид используем под капотом) больше прилетает транзитивно из
WDM 5.x. А раньше прилетала из WDM 4.x.
Поэтому нам пришлось по-быстрому добавить явную зависимость <code class="language-plaintext highlighter-rouge">commons-lang3</code> и зарелизить <strong>Selenide 5.24.2</strong>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1551">issue 1551</a>.</p>
<p><br /></p>
<h1 id="upd-selenide-5243">UPD: Selenide 5.24.3</h1>
<p>Обновились на WebDriverManager 5.0.2, теперь смогли исключить <code class="language-plaintext highlighter-rouge">docker-java</code> и некоторые другие лишние зависимости.</p>
<p><br /></p>
<h1 id="upd-selenide-5244">UPD: Selenide 5.24.4</h1>
<p>Обновились на WebDriverManager 5.0.3, в котором вышел срочный фикс для нового Firefox 92.0 на маке. И кое-что ещё по мелочам.</p>
<p><br />
<br /></p>
<h1 id="ссылки">Ссылки</h1>
<ul>
<li>Новое шоу <a href="https://www.youtube.com/channel/UCHmuu4tJjx54fOWzoIVqmaA">Айтишники</a> от Артёма и Севы</li>
<li>Оказывается, на Селениде можно писать тесты не только для веб и мобилок, но и для Swing приложений! Вот <a href="https://github.com/framebassman/fest-selenide">пример</a>. Под капотом он использует <a href="https://github.com/jalian-systems/marathonv5">реализацию вебдрайвера для swing</a>.</li>
<li>Сильно спорный доклад, но есть полезные идеи: <a href="https://www.youtube.com/watch?v=BzM-VAf8C-c">SPA. React. Selenium vs. Selenide</a></li>
<li>5-7 октября снова будет Гейзенбаг - <a href="https://heisenbug-moscow.ru/callforpapers/">подавайте заявки!</a></li>
</ul>
<p><br /></p>
<h1 id="статистика-использования-селенида">Статистика использования Селенида</h1>
<center>
<img src="/images/2021/08/selenide.downloads.png" width="800" />
</center>
<p>249+ тысяч скачиваний в месяц. Чуть-чуть не хватило до 250K.</p>
<h3 id="аста-ли-виста-бэйби">Аста ли виста, бэйби!</h3>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.23.02021-07-16T00:00:00+00:00http://ru.selenide.org/2021/07/16/selenide-5.23.0
<p>Добрый вечер!</p>
<p>16 июля вышел релиз
<a href="https://github.com/selenide/selenide/milestone/125?closed=1">Selenide 5.23.0</a>.</p>
<p>В нём появилось кое-что существенно новое для селенида.</p>
<h1 id="проверки-нового-поколения">Проверки нового поколения</h1>
<p>Теперь в селениде есть встроенные проверки не только для веб-элементов, но и
для некоторых других штук. С автоматическими ожиданиями, понятными сообщениями об ошибках, попаданием в отчёт и т.д. Всё как вы любите.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1442">issue 1442</a>.
Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за запуск всей этой эпопеи в <a href="https://github.com/selenide/selenide/pull/1478">PR 1478</a>.</p>
<p><br />
Давайте посмотрим, что же это за проверки.</p>
<h2 id="проверки-для-url">Проверки для URL</h2>
<p>До сих пор в селениде было только два метода, позволяющие получить URL текущей страницы или текущего фрейма (в большинстве случаев это одно и то же).</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">String</span> <span class="n">url1</span> <span class="o">=</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">url</span><span class="o">();</span>
<span class="nc">String</span> <span class="n">url2</span> <span class="o">=</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">currentFrameUrl</span><span class="o">();</span>
</code></pre></div></div>
<p>Как их проверять, было непонятно. И ещё непонятнее, как дождаться, что нужный урл загрузился.</p>
<p>Теперь такие проверки есть:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">webdriver</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">WebDriverConditions</span><span class="o">.*;</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">url</span><span class="o">(</span><span class="s">"https://auth.google.com"</span><span class="o">));</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">url</span><span class="o">(</span><span class="s">"https://mastercard.ee"</span><span class="o">),</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">42</span><span class="o">));</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldNotHave</span><span class="o">(</span><span class="n">url</span><span class="o">(</span><span class="s">"http://yandex.ru"</span><span class="o">);</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldNotHave</span><span class="o">(</span><span class="n">urlStartingWith</span><span class="o">(</span><span class="s">"ftp://"</span><span class="o">));</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">currentFrameUrl</span><span class="o">(</span><span class="n">baseUrl</span> <span class="o">+</span> <span class="s">"/login.html"</span><span class="o">));</span>
<span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">currentFrameUrlStartingWith</span><span class="o">(</span><span class="n">baseUrl</span> <span class="o">+</span> <span class="s">"/logout.html"</span><span class="o">));</span>
</code></pre></div></div>
<p>Каждая из этих проверок:</p>
<ol>
<li>если надо, подождёт (по умолчанию до 4 секунд)</li>
<li>добавится в отчёт (текстовой или аллюр)</li>
<li>если урл по истечении 4 секунд не такой - сделает скриншот и кинет ошибку <code class="language-plaintext highlighter-rouge">ConditionNotMetException</code></li>
</ol>
<p>В лучших традициях селенида можно</p>
<ul>
<li>переопределять дефалтовый таймаут (4 секунды) на любой другой: <code class="language-plaintext highlighter-rouge">Configuration.timeout = 8000;</code></li>
<li>задавать кастомный таймаут (вторым параметром типа <code class="language-plaintext highlighter-rouge">Duration.ofSeconds(42)</code>)</li>
<li>создавать свои кастомные проверки (см. <a href="https://github.com/selenide/selenide/blob/c045579f243fb3a5abb99033e440cf8f12caa99c/statics/src/test/java/integration/WebDriverConditionsTest.java#L127">пример</a>)</li>
</ul>
<h2 id="проверки-для-буфера-обмена">Проверки для буфера обмена</h2>
<p>Начиная с версии 5.20.0, в селениде есть метод для доступа к буферу обмена:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Clipboard</span> <span class="n">clipboard</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">clipboard</span><span class="o">();</span>
</code></pre></div></div>
<p>Но если раньше можно было только засунуть и высунуть текст из буфера:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">clipboard</span><span class="o">().</span><span class="na">getText</span><span class="o">();</span>
<span class="n">clipboard</span><span class="o">().</span><span class="na">setText</span><span class="o">(</span><span class="s">"bar"</span><span class="o">);</span>
</code></pre></div></div>
<p>То теперь его можно проверить - с дефалтовым и кастомным таймаутом, скриншотами и т.д.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">clipboard</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">content</span><span class="o">(</span><span class="s">"Hello fast World"</span><span class="o">));</span>
<span class="n">clipboard</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">content</span><span class="o">(</span><span class="s">"Hello slow World"</span><span class="o">),</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofMillis</span><span class="o">(</span><span class="mi">1500</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/pull/1507">PR 1507</a></p>
<h2 id="проверки-для-localstorage">Проверки для <code class="language-plaintext highlighter-rouge">localStorage</code></h2>
<p>Начиная с версии 5.15.0, в селениде есть метод <code class="language-plaintext highlighter-rouge">localStorage()</code>, возвращающий объект <code class="language-plaintext highlighter-rouge">LocalStorage</code>.<br />
Но у него были только методы <code class="language-plaintext highlighter-rouge">getItem</code> и <code class="language-plaintext highlighter-rouge">setItem</code>. Опять же, как их проверять и как дождаться нужного значения, было неясно.</p>
<p>Теперь ясно:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">localStorage().shouldHave(item("cat”));</code></li>
<li><code class="language-plaintext highlighter-rouge">localStorage().shouldHave(itemWithValue("mouse", "Jerry”));</code></li>
</ul>
<p>Кстати, заодно появился метод <code class="language-plaintext highlighter-rouge">localStorage.getItems()</code>, возвращающий всё содержимое в виде мапы.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1502">PR 1502</a></p>
<h2 id="проверки-для-sessionstorage">Проверки для <code class="language-plaintext highlighter-rouge">sessionStorage</code></h2>
<p>Всё то же самое, что и про <code class="language-plaintext highlighter-rouge">localStorage</code> - добавились методы</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">sessionStorage().shouldHave(item("cat”));</code></li>
<li><code class="language-plaintext highlighter-rouge">sessionStorage().shouldHave(itemWithValue("mouse", "Jerry”));</code></li>
<li><code class="language-plaintext highlighter-rouge">Map<String, String> items = sessionStorage.getItems();</code></li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide/pull/1502">PR 1502</a></p>
<p><br /></p>
<h1 id="небольшой-рефакторинг">Небольшой рефакторинг</h1>
<p>Мы сделали классы <code class="language-plaintext highlighter-rouge">StaticConfig</code> и <code class="language-plaintext highlighter-rouge">StaticDriver</code> непубличными.<br />
Нам кажется, они никому не должны быть нужны вне селенида.
А в ваших проектах они используются? Пишите нам, если у вас развалилась компиляция. :)</p>
<h1 id="и-небольшой-багфикс">И небольшой багфикс:</h1>
<p>Теперь метод <code class="language-plaintext highlighter-rouge">Selenide.screenshot("filename")</code> снова делает скриншот, даже если настройка <code class="language-plaintext highlighter-rouge">Configuration.screenshots</code> выставлена в <code class="language-plaintext highlighter-rouge">false</code>.</p>
<p>Давайте проясним.</p>
<ol>
<li>Настройку <code class="language-plaintext highlighter-rouge">Configuration.screenshots</code> стоит прописать в <code class="language-plaintext highlighter-rouge">false</code>, если вам не хочется, чтобы селенид автоматически создавал скриншоты при падении тестов. Често говоря, не понимаю, в каких ситуациях это может быть полезно.</li>
<li>А вот метод <code class="language-plaintext highlighter-rouge">Selenide.screenshot("filename")</code> можно вызвать в любой момент теста, чтобы явно сделать скриншот, независимо от того, упал тест или не упал. Тоже не очень понимаю, в каких ситуациях это может быть полезно.</li>
</ol>
<p>Но суть в том, что второй метод работает независимо от первой настройки. Теперь можно выключить автоматические
скриншоты, но дёргать их руками только там, где надо.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1477">issue 1477</a> и <a href="https://github.com/selenide/selenide/pull/1506">PR 1506</a></p>
<p><br /></p>
<h1 id="selenide-selenoid-114">selenide-selenoid 1.1.4</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.4"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.4</code></a> с обновлением на Selenide 5.23.0</p>
<p><br /></p>
<h1 id="selenide-appium-167">selenide-appium 1.6.7</h1>
<p>Мы выпустили <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.6.7"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.7</code></a> с обновлением на Selenide 5.23.0</p>
<p><br /></p>
<h1 id="ссылки">Ссылки</h1>
<ul>
<li>Статья <a href="https://www.appliedtech.ru/vyibor-instrumentov-dlya-ui-testirovaniya-selenium-ili-selenide.html">Selenium или Selenide?</a> в корпоративном блоге “Прикладные Технологии”</li>
<li>Статья <a href="https://anilkulkarni.com/2020/03/selenide-ui-tests-in-minutes/">Selenide – UI tests in minutes</a> от Anil Kulkarni</li>
</ul>
<p><br /></p>
<h1 id="статистика">Статистика</h1>
<center>
<img src="/images/2021/07/selenide.downloads.png" width="800" />
</center>
<p>232+ тысяч скачиваний в месяц.</p>
<h3 id="беззаботного-лета">Беззаботного лета!</h3>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.22.32021-07-05T00:00:00+00:00http://ru.selenide.org/2021/07/05/selenide-5.22.3
<p>Добрый вечер!</p>
<p>На этом месте был пресс-релиз, который мог больно задеть чувства большого числа наших пользователей и коммитеров. Мы сожалеем, что так получилось, и считаем эту публикацию своей ошибкой, ставшей проявлением непрофессионализма отдельных мейнтейнеров. Никоим образом мы не хотели стать источником раздора и ненависти.</p>
<p>Просто обновляйтесь на <a href="https://github.com/selenide/selenide/milestone/126?closed=1">Selenide 5.22.3</a> с самыми свежими и вкусными багфиксами.</p>
<h3 id="5223-released-05072021">5.22.3 (released 05.07.2021)</h3>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1474">#1474</a> add workaround for NPE in RemoteWebElement.isDisplayed() – see <a href="https://github.com/selenide/selenide/pull/1498">PR #1498</a></li>
</ul>
<h3 id="5222-released-30062021">5.22.2 (released 30.06.2021)</h3>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1493">#1493</a> support uploading files from inside of JAR files – see <a href="https://github.com/selenide/selenide/pull/1494">PR #1494</a></li>
<li>fix command <code class="language-plaintext highlighter-rouge">./gradlew</code> - now it installs jars to a local maven repo – see <a href="https://github.com/selenide/selenide/pull/1489">PR #1489</a></li>
<li>add support for okhttp 4.9.1 – see <a href="https://github.com/selenide/selenide/pull/1488">PR #1488</a></li>
</ul>
<h3 id="5221-released-18062021">5.22.1 (released 18.06.2021)</h3>
<ul>
<li>Add mime type “binary/octet-stream” to download binary files in FireFox</li>
</ul>
<p><br /></p>
<center>
<a href="https://www.spletnik.ru/buzz/chronicle/102227-skandal-vokrug-vkusvill-s-lesbiyskoy-paroy-khronologiya-reaktciya.html">
<img src="/images/2021/07/selenide-5.22.3.png" width="800" />
</a>
</center>
<h3 id="беззаботного-лета">Беззаботного лета!</h3>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.22.02021-06-08T00:00:00+00:00http://ru.selenide.org/2021/06/08/selenide-5.22.0
<p>Добрый вечер!</p>
<p>Лучший подарок - сделанный своими коммитами. <br />
В мой день 40 рождения мы выпустили юбилейный релиз <a href="https://github.com/selenide/selenide/milestone/124?closed=1">Selenide 5.22.0</a>.</p>
<p>Развернём упаковку?</p>
<p><br /></p>
<h1 id="теперь-можно-закрыть-алерт-перед-скачиванием-файла">Теперь можно закрыть алерт перед скачиванием файла</h1>
<p>В Селениде есть метод <code class="language-plaintext highlighter-rouge">$.download()</code>, который работает по простому принципу:</p>
<ol>
<li>Кликни.</li>
<li>Подожди, пока в папке появится нужный файл.</li>
</ol>
<p>Проблема в том, что на некоторых сайтах после клика появляется алерт, который нужно закрыть, чтобы началось скачивание.
Ну или вообще, нужно совершить ещё какое-то действие после или вместо клика, чтобы запустить скачивание файла.</p>
<p>Теперь можно будет <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadToFolderTest.java">закрыть алерт</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">downloadedFile</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Download me with alert"</span><span class="o">)).</span><span class="na">download</span><span class="o">(</span>
<span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">).</span><span class="na">withAction</span><span class="o">(</span>
<span class="n">clickAndConfirm</span><span class="o">(</span><span class="s">"Are you sure to download it?"</span><span class="o">)</span>
<span class="o">)</span>
<span class="o">);</span>
</code></pre></div></div>
<p>или вообще совершить <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadViaProxyTest.java">любое нужное действие</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">downloadedFile</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Download me with alert"</span><span class="o">)).</span><span class="na">download</span><span class="o">(</span>
<span class="n">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">).</span><span class="na">withAction</span><span class="o">((</span><span class="n">driver</span><span class="o">,</span> <span class="n">link</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="c1">// add cookies</span>
<span class="n">link</span><span class="o">.</span><span class="na">click</span><span class="o">();</span>
<span class="c1">// driver.switchTo().window();</span>
<span class="c1">// alert.dismiss();</span>
<span class="c1">// send http request</span>
<span class="c1">// call api</span>
<span class="o">}));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1479">issue 1479</a> и <a href="https://github.com/selenide/selenide/pull/1481">PR 1481</a>.</p>
<p><br />
<br /></p>
<h1 id="доработали-проверку-conditiontextcasesensitive">Доработали проверку <code class="language-plaintext highlighter-rouge">Condition.textCaseSensitive</code></h1>
<p>… чтобы она поддерживала выбранные опции в <code class="language-plaintext highlighter-rouge"><select></code> - по аналогии с <code class="language-plaintext highlighter-rouge">Condition.text</code>.</p>
<p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1482">PR 1482</a>.</p>
<p><br />
<br /></p>
<h1 id="добавили-селекторы-bytextcaseinsensitive-и-withtextcaseinsensitive">Добавили селекторы <code class="language-plaintext highlighter-rouge">byTextCaseInsensitive</code> и <code class="language-plaintext highlighter-rouge">withTextCaseInsensitive</code></h1>
<p>… для поиска элемента по тексту, игнорируя регистр.</p>
<h3 id="было">Было</h3>
<p>В селениде давно есть методы для поиска по тексту.</p>
<ul>
<li>по полному тексту: <code class="language-plaintext highlighter-rouge">$(byText("Wake up we have a tsar again")</code></li>
<li>по подстроке: <code class="language-plaintext highlighter-rouge">$(withText("we have a tsar")</code></li>
</ul>
<p>Эти селекторы чувствительны к регистру (и внутри используют XPath 1.0).</p>
<h3 id="стало">Стало</h3>
<p>Теперь же мы добавили аналогичные методы, но нечувствительные к регистру:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$(byTextCaseInsensitive("wake UP we have a TSAR again")</code></li>
<li><code class="language-plaintext highlighter-rouge">$(withTextCaseInsensitive("TSAR agAiN")</code></li>
</ul>
<p>Эти селекторы позволяют искать элементы, игнорируя большие-маленькие буквы.</p>
<h3 id="технический-нюанс">Технический нюанс</h3>
<p>этот функционал не получилось реализовать с помощью XPath, потому что строковые функции типа
<code class="language-plaintext highlighter-rouge">lower-case</code> и <code class="language-plaintext highlighter-rouge">match</code> появились лишь с версии XPath 2.0, а все современные браузеры поддерживают только XPath 1.0.
Поэтому пришлось наколбасить <a href="https://github.com/selenide/selenide/blob/master/src/main/resources/find-elements-by-text-case-insensitive.js">хитрый JS код</a> для обхода всего дерева элементов.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1380">issue 1380</a> и <a href="https://github.com/selenide/selenide/pull/1381">PR 1381</a>.</p>
<p><br />
<br /></p>
<h1 id="добавили-метод-drivergetsessionid">Добавили метод <code class="language-plaintext highlighter-rouge">Driver.getSessionId()</code></h1>
<p>По сути он нужен для интеграции с Selenoid.</p>
<p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1483">PR 1483</a>.</p>
<p><br /></p>
<h1 id="уменьшили-селенимовские-таймауты">Уменьшили селенимовские таймауты</h1>
<p>Внутри селениума прошиты два таймаута, о которых вы, возможно, не догадывались. Проблема в том, что они анормально
большие, и их невозможно поменять.</p>
<ul>
<li>Соединение с вебдрайвером: <code class="language-plaintext highlighter-rouge">connectTimeout</code> = 120000 ms = 2 минуты</li>
<li>Запрос к вебдрайверу: <code class="language-plaintext highlighter-rouge">readTimeout</code> = 10800000 ms = 3 часа (!)</li>
</ul>
<p>Если какая-то операция с вебдрайвером подвисает, то ваш тест будет висеть 3 часа. Ну это же ненормально!<br />
Например, так происходит, когда тестируется приложение на Electron, и оно уходит в трей, а тест пытается сделать
скриншот - и надолго зависает.</p>
<p>В Selenide 5.22.0 мы добавили возможность <em>менять этот таймаут</em> (под капотом используется жёсткий рефлекшин).<br />
И да, мы сделали дефалтовые значения чуть поменьше (хоть и по-прежнему слишком консервативные):</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">connectTimeout</code> = 1 минута</li>
<li><code class="language-plaintext highlighter-rouge">readTimeout</code> = 2 минуты</li>
</ul>
<p>Давайте восприниматься это как костыль и надеяться, что в Selenium 4 появится более правильное решение.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1433">PR 1433</a>.</p>
<p><br /></p>
<h1 id="selenide-selenoid-113">selenide-selenoid 1.1.3</h1>
<p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-selenoid/milestone/4?closed=1"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.3</code></a>, в котором исправили <code class="language-plaintext highlighter-rouge">ClassCastException</code> в некоторых случаях.</p>
<p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide-selenoid/pull/10">PR 10</a>.</p>
<p><br /></p>
<h1 id="selenide-appium-166">selenide-appium 1.6.6</h1>
<p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.6.6"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.6</code></a>, в котором обновились на Selenide 5.22.0</p>
<p><br /></p>
<h1 id="ссылки">Ссылки</h1>
<p>Ого-го! Сразу несколько известных ребят выложили свои видосики про Селенид.</p>
<ul>
<li>Java champion Sebastian Daschner: <a href="https://www.youtube.com/watch?v=O0-1RhspjAk">Why I switched to using Selenide for UI tests</a></li>
<li>JetBrains feat. DJ Юрий Артамонов: <a href="https://www.youtube.com/watch?v=P-vureOnDWY&t=2758s">Modern UI Test Automation with Selenium Libraries</a></li>
<li>Статья от JetBrains <a href="https://blog.jetbrains.com/idea/2021/06/live-stream-modern-ui-test-automation-with-selenium-libraries/">про этот стрим</a></li>
</ul>
<p>И ещё всплыла парочка старых материалов:</p>
<ul>
<li>Сергей Брит <a href="https://www.youtube.com/watch?v=5WxlKf_EFII">Selenide. Tips and tricks</a>, <a href="https://drive.google.com/file/d/14AUphaV_diRFSE-uUlT9Z_iAtd2uERSu/view">slides</a></li>
<li><a href="https://www.slideshare.net/Provectus/selenide-review-and-how-to-start-using-it-in-legacy-selenium-tests">Selenide review</a> by Provectus</li>
</ul>
<p><br />
И на десерт - молодое поколение учит Селенид:</p>
<center>
<img src="/images/2021/06/selenide-taffel.png" width="800" />
</center>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.21.02021-05-15T00:00:00+00:00http://ru.selenide.org/2021/05/15/selenide-5.21.0
<p>Добрый вечер!</p>
<p>За окошком месяц май, а на экране релиз <a href="https://github.com/selenide/selenide/milestone/123?closed=1">Selenide 5.21.0</a>.</p>
<p><br /></p>
<h1 id="убрали-повторные-скриншоты-и-шаги-в-отчёте-для-цепочи-локаторов">Убрали повторные скриншоты и шаги в отчёте для цепочи локаторов</h1>
<p>Большинство методов Селенида устроены так, что их можно вызвать несколько штук подряд, в одну строку.
Это позволяет писать лаконичные тесты.</p>
<p>Что-то вроде</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"table#id"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"tbody"</span><span class="o">,</span> <span class="mi">2</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"tr.active"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"td"</span><span class="o">,</span> <span class="mi">5</span><span class="o">)</span>
<span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Ну вот"</span><span class="o">))</span>
<span class="o">.</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<p>Правда, с ними была одна проблема. При падении такой проверки Селенид делал несколько скриншотов, а
в отчёт (в т.ч. числе в аллюровский отчёт) добавлялось несколько шагов (один для <code class="language-plaintext highlighter-rouge">"table#id"</code>, второй для <code class="language-plaintext highlighter-rouge">"tbody"</code> и т.д.),
хотя по сути это один шаг.</p>
<p>Хоть это и некритично, мы эту неприятность убрали. Теперь селенид будет делать один скриншот, а в аллюр отчёте будет одна строка.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1055">issue 1055</a> и <a href="https://github.com/selenide/selenide/pull/1465">PR 1465</a>.</p>
<p>NB! Для этого пришлось сделать приличный рефакторинг, и не исключено даже, что что-то могло сломаться. Сообщайте, будем реагировать!</p>
<p><br /></p>
<h1 id="добавили-browserperteststrategyextension">Добавили BrowserPerTestStrategyExtension</h1>
<p>… для перезапуска браузер после каждого теста.</p>
<p>По умолчанию Селенид переиспользует браузер между тестами (в рамках одного потока). Это сделано для скорости.
Мы предполагаем, что вы сами в начале теста позаботитьесь о том, чтобы обновить страницу, почистить данные и т.п. - ведь это зависит от каждого приложения.</p>
<p>Если же вы очень хотите в каждом тесте использовать новый браузер, есть и такая возможность.
В Селениде из коробки есть поддерджка для JUnit 4, JUnit 5 и TestNG.</p>
<p>Но для JUnit5 у нас был только экстеншн для перезапуска браузера для каждого тестового класса (<code class="language-plaintext highlighter-rouge">@ExtendWith({BrowserStrategyExtension.class}</code>), но не тестового метода.</p>
<p>А теперь появился экстенш, заполнивший эту нишу. Если прописать в ваших тестах <code class="language-plaintext highlighter-rouge">@ExtendWith({BrowserPerTestStrategyExtension.class}</code>, то в каждом тестовом методе будет использоваться новый браузер.</p>
<p>Это сделает ваши тесты значительно медленнее, но иногда по-другому никак.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1448">issue 1448</a>
Спасибо <a href="https://github.com/simple-elf">Anton Aftakhov</a> за <a href="https://github.com/selenide/selenide/pull/1450">PR 1450</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-hover-со-сдвигом">Добавили метод $.hover() со сдвигом</h1>
<p>В селениде давно есть метод <code class="language-plaintext highlighter-rouge">$("div#123").hover()</code> для наведения курсора мыши на нужный элемент.
Этот метод двигает курсор к центру элемента, и на это не было возможности повлиять.</p>
<p>Теперь же у нас появился метод <code class="language-plaintext highlighter-rouge">$.hover()</code> с параметром, позволяющим указать, на сколько пикселей от центра нужно сдвинуть курсор:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"div#123"</span><span class="o">).</span><span class="na">hover</span><span class="o">(</span><span class="n">withOffset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">122</span><span class="o">));</span>
</code></pre></div></div>
<p>P.S. Кажется, этот сдвиг не всегда оказывается точным. У меня иногда курсор оказывался <em>примерно</em> в этой позиции плюс-минус 30 пикселей.
Делитесь, как у вас?</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1447">issue 1447</a> и <a href="https://github.com/selenide/selenide/pull/1461">PR 1461</a>.</p>
<p><br /></p>
<h1 id="обновились-на-webdrivermanager-443">Обновились на WebDriverManager 4.4.3</h1>
<p>Спасибо <a href="https://github.com/anilreddy">Anil Kumar Reddy Gaddam</a>
за <a href="https://github.com/selenide/selenide/pull/1464">PR 1464</a>
и <a href="https://github.com/selenide/selenide/pull/1469">PR 1469</a>.</p>
<p><br /></p>
<h1 id="обновились-javadoc-многих-селенидовских-методов">Обновились javadoc многих селенидовских методов</h1>
<p>… касающийся ленивой загрузки и фразы “not recommended”.</p>
<p>Теперь javadoc ведёт на эти две статьи в вики:</p>
<ul>
<li><a href="https://github.com/selenide/selenide/wiki/Lazy-loading">Lazy loading</a></li>
<li><a href="https://github.com/selenide/selenide/wiki/Do-not-use-getters-in-tests">Not recommended</a></li>
</ul>
<p>Внимание, неоднозначный контент.<br />
Возможно, вы захотите подискутировать. <br />
Так давайте подискутируем!</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1430">PR 1430</a></p>
<p><br /></p>
<h1 id="selenide-selenoid-112">selenide-selenoid 1.1.2</h1>
<p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-selenoid/blob/main/CHANGELOG.md"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.2</code></a>, в котором добавили поддержку BasicAuth при скачивании файлов из контейнера Selenoid. <br />
См. <a href="https://github.com/selenide/selenide-selenoid/issues/8">issue 8</a> и <a href="https://github.com/selenide/selenide-selenoid/pull/9">PR 9</a>.</p>
<p><br /></p>
<h1 id="selenide-appium-165">selenide-appium 1.6.5</h1>
<p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-appium/blob/master/CHANGELOG"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.5</code></a>, в котором улучшили сообщения об ошибках пр падении тестов в iOS.<br />
См. <a href="https://github.com/selenide/selenide-appium/issues/54">issue 54</a>.</p>
<p><br /></p>
<h1 id="ссылки">Ссылки</h1>
<p>Что новенького мы нарыли на просторах сети:</p>
<ul>
<li>Открытые видеокурсы <a href="https://www.youtube.com/channel/UCVE8_r0VkxUYjGd094sKc4g">Дневник тестировщика</a> - там и про селенид есть несколько выпусков.</li>
<li><a href="https://habr.com/ru/company/croc/blog/546430/">Как e2e автотесты на Selenide помогают QA-команде при частых релизах</a></li>
<li>годный тред <a href="https://twitter.com/arthicl/status/1366598485252964360?s=20">как слезть с ЛКФ на Селенид</a> (ЛКФ = локальный кривой фреймворк)</li>
<li>Пост <a href="https://rieckpil.de/write-concise-web-tests-with-selenide-for-java-projects/">Write Concise Web Tests With Selenide for Java Projects</a> за авторством <a href="https://github.com/rieckpil">Philip Riecks</a>.</li>
<li>Видео <a href="https://www.youtube.com/watch?v=T9xns1iMbPI">Введение в Selenide</a> от него же</li>
<li>Маленький видеоурок <a href="https://www.youtube.com/watch?v=XPUPirH1yMs">Create Screenshots With Selenide</a> от него же</li>
<li>Пример <a href="https://github.com/senpay/layered-test-framework-example-serenity-jbehave">Selenide+Serenity+JBehave</a></li>
<li>Пример <a href="https://github.com/sergiomartins8/test-automation-bootstrap/tree/master/ui-tests">Selenide+TestNG+ExtentReports</a></li>
<li>курс <a href="https://slifki.info/threads/sergei-semenov-selenium-i-selenide-dlja-nachinajuschix-automation-qa-qc-na-java-2020.85098/">Selenium и Selenide для начинающих</a> (прикольно же!)</li>
</ul>
<p><br /></p>
<h2 id="немного-статистики">Немного статистики</h2>
<p>Статистика скачиваний Selenide за апрель 2021:</p>
<center>
<img src="/images/2021/05/selenide.downloads.png" width="800" />
</center>
<p>Чуть-чуть не дотянули до 200 тысяч.</p>
<p><br /></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.20.12021-03-23T00:00:00+00:00http://ru.selenide.org/2021/03/23/selenide-5.20.1
<p>Добрый вечер!</p>
<p>Джо Байдена спросили, считает ли он <code class="language-plaintext highlighter-rouge">$$.as</code> киллер-фичей. «Ммм, хмм, да», — ответил Байден.</p>
<p>Это сдвоенный обзор на релиз <a href="https://github.com/selenide/selenide/milestone/118?closed=1">Selenide 5.20.0</a> и
<a href="https://github.com/selenide/selenide/milestone/119?closed=1">Selenide 5.20.1</a>.</p>
<p><br /></p>
<h1 id="добавили-методы-для-операций-с-буфером-обмена">Добавили методы для операций с буфером обмена</h1>
<ul>
<li><code class="language-plaintext highlighter-rouge">Selenide.clipboard().setText("111");</code></li>
<li><code class="language-plaintext highlighter-rouge">assertEquals("Hello World", Selenide.clipboard().getText());</code></li>
</ul>
<p>Имейте в виду, что на линуксе буфер обмена не будет работать без иксов. Либо запускайте xvfb, либо ещё как-то выкручивайтесь.</p>
<p>Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1409">PR 1409</a>.</p>
<p>NB! Эти методы переопределены в плагине <a href="https://github.com/selenide/selenide-selenoid">selenide-selenoid</a>, так что они корректно
работают с селеноидом. Для него мы тоже выпустили версию <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.0">1.1.0</a>.</p>
<p><br /></p>
<h1 id="добавили-режим-headless-для-браузера-microsoft-edge">Добавили режим headless для браузера Microsoft Edge</h1>
<p>Раньше настройка <code class="language-plaintext highlighter-rouge">Configuration.headless</code> срабатывала только для Chrome и Firefox, а теперь ещё и для Edge.</p>
<p>(Браузеры IE, Opera и Safari, насколько я знаю, до сих пор не поддерживают headless режим.)</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1422">issue 1422</a>
и <a href="https://github.com/selenide/selenide/pull/1424">PR 1424</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-as-для-задания-алиаса-коллекциям">Добавили метод $$.as() для задания алиаса коллекциям</h1>
<p>Как вы помните, в Selenide 5.17.0 мы добавили возможность задавать единичным элементам “понятное” имя.
Теперь имя можно задавать и коллекциям:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
<span class="err">$$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login buttons"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">2</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1389">issue 1389</a>
и <a href="https://github.com/selenide/selenide/pull/1431">PR 1431</a>.</p>
<p>NB! Не торопитесь использовать эту возможность. Лично я воспринимаю её как “хак последней надежды”.<br />
В отчёте всегда лучше видеть настоящий локатор, чем кем-то придуманное имя, которое всегда может оказаться:</p>
<ul>
<li>Обманчивым</li>
<li>Устаревшим</li>
<li>Вводящим в заблуждение</li>
</ul>
<p>Лучше инвестируйте свои усилия в то, чтобы использовать читаемые локаторы. Ну и грамотно называть переменные и методы. Вот где сила, брат!</p>
<p><br /></p>
<h1 id="добавили-проверку-для-коллекций-containexacttextscasesensitive">Добавили проверку для коллекций <code class="language-plaintext highlighter-rouge">containExactTextsCaseSensitive</code></h1>
<p>Существующий метод <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("a", "b", "c"))</code> проверяет, что в коллекции ровно эти элементы и никаких больше. А иногда хочется менее строгой проверки. Например, нужно проверить, что в списке торгуемых валют точно
есть RUB, EUR, USD - и какие угодно ещё.</p>
<p>Теперь для этого есть метод:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$$</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">containTexts</span><span class="o">(</span><span class="s">"RUB"</span><span class="o">,</span> <span class="s">"EUR"</span><span class="o">,</span> <span class="s">"USD"</span><span class="o">));</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1426">PR 1426</a>.</p>
<p><strong>UPD</strong> В оперативно вышедшей Selenide 5.20.1 его переименовали в</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$$</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">containExactTextsCaseSensitive</span><span class="o">(</span><span class="s">"RUB"</span><span class="o">,</span> <span class="s">"EUR"</span><span class="o">,</span> <span class="s">"USD"</span><span class="o">));</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1438">PR 1438</a> и <a href="https://github.com/selenide/selenide/pull/1439">PR 1439</a>.</p>
<p><br /></p>
<h1 id="починили-утерянные-опции-firefoxoptions">Починили утерянные опции FirefoxOptions</h1>
<p>В некоторых ситуации некоторые настройки Firefox терялись, теперь не теряются. Опции нашлись.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1436">issue 1436</a>.
Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1437">PR 1437</a>.</p>
<p><br /></p>
<h1 id="убрали-логирование-поисковых-методов">Убрали логирование “поисковых” методов</h1>
<p>В SelenideElement есть несколько методов для поиска других элементов:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$.findAll()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.parent()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.sibling()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.preceding()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.lastChild()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.closest()</code></li>
</ul>
<p>Такие методы оказывались в отчёте дважды: сначала при их вызове, а потом - при вызове любого метода на найденном элементе. Теперь мы это дублирование убрали.</p>
<p>Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1428">PR 1428</a>.</p>
<p><br /></p>
<p><br /></p>
<h3 id="доска-объявлений">Доска объявлений</h3>
<p>Ура, новый сезон конференций открыт!
Буквально через неделю-две начинается:</p>
<ul>
<li>У кого нет денег на Heisenbug и JPoint - приходите 27 марта на бесплатный <a href="https://techtrain.ru/2021/spring/schedule/">фестиваль TechTrain</a>, программа там очень многообещающая.
<ul>
<li>Я, например, планирую послушать “<a href="https://techtrain.ru/2021/spring/talks/4vehn9kwvjg7uzjjbp0nl4/">Что мобильным разработчикам в IT-индустрии неведомо</a>”</li>
<li>и “<a href="https://techtrain.ru/2021/spring/talks/1vtaygqjny9w3folebliqv/">Ловушки коллективного владения кодом</a>”.</li>
<li>И конечно, “<a href="https://techtrain.ru/2021/spring/talks/74akccvzvqirxg2jbh4t3m/">Какие тулы ты бы взял на удаленку</a>” от ведущих “Ошибки выжившего” Севы и Артёма обещает стать хитом.</li>
</ul>
</li>
<li>
<p>А 6-9 апреля грядёт наш любимый <a href="https://heisenbug-piter.ru/2021/spb/schedule/">Heisenbug</a>.</p>
<p>У меня там будет заключительный доклад эпопеи про флейки тесты
“<a href="https://heisenbug-piter.ru/2021/spb/talks/4wcc3dephlxuzc87wgwkk7/">Flaky tests. Метод</a>”
и продолжение мастер-класса “<a href="https://heisenbug-piter.ru/2021/spb/talks/8p4qtsit5rqgga8yuxz5a/">Как начать свой проект автоматизации с нуля</a>”.</p>
<p>А послушать я хочу как минимум мастер-класс гуру юзабилити Виталия Фридмана “<a href="https://heisenbug-piter.ru/2021/spb/talks/5lxiyz5rdanigu1wuaujtt/">От birthday selector до inline validation: Всё, что нужно знать о веб-формах</a>” и много чего ещё вкусненького.</p>
</li>
<li>
<p>А 13-17 апреля пройдёт конференция <a href="https://jpoint.ru/2021/schedule/">JPoint</a></p>
<p>Она скорее для разработчиков, и на ней я тоже успею засветиться в <a href="https://jpoint.ru/2021/talks/5gmcsxserjfhlzgt9dpfq3/">мастер-классе по парному программированию</a>.</p>
</li>
<li>
<p>А ещё 29 мая будет новосибирский <a href="https://11.codefest.ru/">Codefest</a></p>
<p>Там у меня будет доклад про “<a href="https://11.codefest.ru/lecture/1751">Трюки с javascript в автотестах</a>”.</p>
</li>
</ul>
<p>Приходите, всё равно ж делать нечего на карантине!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.19.02021-02-24T00:00:00+00:00http://ru.selenide.org/2021/02/24/selenide-5.19.0
<p>Всем привет!</p>
<p>Вы, наверное, не знали, но сегодня, 24 февраля, в Эстонии чуть ли не главный праздник -
<a href="https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BD%D1%8C_%D0%BD%D0%B5%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8_%D0%AD%D1%81%D1%82%D0%BE%D0%BD%D0%B8%D0%B8">День Независимости</a>.
Ровно 103 года назад Эстония была провозглашена независимым демократическим государством.</p>
<p>А спустя 93 года в Эстонии, в казематах компании <a href="https://codeborne.com/">Codeborne</a> родилась библиотека Selenide. Это ли не чудо?</p>
<p>Дню независимости мы и посвящаем наш новый релиз <a href="https://github.com/selenide/selenide/milestone/116?closed=1">Selenide 5.19.0</a>.</p>
<p><br /></p>
<h1 id="наконец-таки-починили-draganddrop">Наконец-таки починили drag’and’drop</h1>
<p>В селениде уже давно есть метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code>, но он фактически не работает. Под капотом он использует
селениумовский механизм <code class="language-plaintext highlighter-rouge">Actions</code>, и что-то там явно сломано. Ну не тащит элемент и всё.</p>
<p>А теперь мы запилили альтернативную реализацию с помощью JavaScript. И она, похоже, работает во всех браузерах.
Поэтому мы даже включили её по умолчанию.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1">// Рабочий вариант:</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#drag1"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#drag1"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">,</span> <span class="n">usingJavaScript</span><span class="o">());</span>
<span class="c1">// Нерабочий вариант через Actions (ну вдруг он у вас почему-то работает):</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#drag1"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">,</span> <span class="n">usingActions</span><span class="o">());</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1110">issue 1110</a>.</p>
<p>Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1412">PR 1412</a>.</p>
<p><br /></p>
<h1 id="поддержка-appium">Поддержка Appium</h1>
<p>Заодно мы выпустили и обновление библиотеки <code class="language-plaintext highlighter-rouge">selenide-appium:1.6.2</code>, в которой метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code>,
теперь переопределён, так что он работает и на мобильниках.
См. <a href="https://github.com/selenide/selenide-appium/pull/53/files">PR #53</a>.</p>
<p><br /></p>
<h1 id="починили-метод-clickusingjavascript-в-internet-explorer">Починили метод <code class="language-plaintext highlighter-rouge">$.click(usingJavascript())</code> в Internet Explorer</h1>
<p>См. <a href="https://github.com/selenide/selenide/issues/1406">issue 1406</a> и
<a href="https://github.com/selenide/selenide/pull/1419">PR 1419</a>.</p>
<p><br /></p>
<h1 id="улучшили-описание-коллекции-snapshot">Улучшили описание коллекции <code class="language-plaintext highlighter-rouge">$$.snapshot()</code></h1>
<p>У селенидовских коллекций есть один хитрый метод <code class="language-plaintext highlighter-rouge">$$.snapshot()</code>.
Он запоминает текущее состоние коллекции, и при дальнейших обращениях не загружает её заново из браузера.
Это полезно для ускорения тестов, когда коллекция большая, и вы уверены, что её элементы уже точно не изменятся.</p>
<p>С ними была только маленькая проблемка: в отчётах выглядело не слишком красиво. Например, такая строка:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"#root li"</span><span class="o">).</span><span class="na">snapshot</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">))</span>
</code></pre></div></div>
<p>при падении в отчёте выглядела как</p>
<blockquote>
<p>List size mismatch: expected: = 3, actual: 2, collection: $$(2 elements)</p>
</blockquote>
<p>Как видите, в описании нет селектора оригинальной коллекции, только <code class="language-plaintext highlighter-rouge">(2 elements)</code>.</p>
<p>Теперь виден и селектор:</p>
<blockquote>
<p>List size mismatch: expected: = 3, actual: 2, collection: #root li.snapshot(2 elements)</p>
</blockquote>
<p>Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1402">PR 1402</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-getalias">Добавили метод <code class="language-plaintext highlighter-rouge">$.getAlias()</code></h1>
<p>Он возвращает то, что вы сами же задали с помощью <code class="language-plaintext highlighter-rouge">$.as("login button")</code>.
В обычных тестах этот метод не должен быть нужен. Но может понадобиться, если вы пилите какой-то свой хитрый отчёт.
Ну мало ли, убийцу Аллюра…</p>
<p>Спасибо <a href="https://github.com/pavelpp">pavelpp</a> за <a href="https://github.com/selenide/selenide/pull/1415">PR 1415</a>.</p>
<p><br /></p>
<h1 id="добавили-события-refresh-и-тп-в-селенидовский-лог">Добавили события “refresh” и т.п. в селенидовский лог</h1>
<p>В селениде есть целый ряд методов, которые не связаны явно в веб-элементами (<code class="language-plaintext highlighter-rouge">refresh()</code>, <code class="language-plaintext highlighter-rouge">back()</code> и т.п.)
И мы в какой-то момент обнаружили, что некоторые из них не отображались в отчёта Selenide или Allure.
Несмертельно, конечно, но всё-таки зачем-то ведь эти отчёты кому-то нужны…</p>
<p>Теперь мы исправили эту оплошность, и следующие методы будут исправно красоваться в отчётах:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">refresh</code></li>
<li><code class="language-plaintext highlighter-rouge">back</code></li>
<li><code class="language-plaintext highlighter-rouge">forward</code></li>
<li><code class="language-plaintext highlighter-rouge">updateHash</code></li>
<li><code class="language-plaintext highlighter-rouge">confirm</code></li>
<li><code class="language-plaintext highlighter-rouge">dismiss</code></li>
<li><code class="language-plaintext highlighter-rouge">prompt</code></li>
<li><code class="language-plaintext highlighter-rouge">clearCookies</code></li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide/issues/1383">issue 1383</a> и
<a href="https://github.com/selenide/selenide/pull/1404">PR 1404</a>.</p>
<p><br /></p>
<h1 id="добавили-аннотаций-nullable-методам-класса-webdriverrunner">Добавили аннотаций <code class="language-plaintext highlighter-rouge">@Nullable</code> методам класса <code class="language-plaintext highlighter-rouge">WebDriverRunner</code></h1>
<p>Самым важным, по-видимому, тут является метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.getSelenideProxy()</code> - не было очевидно,
что он может возвращать <code class="language-plaintext highlighter-rouge">null</code>, если прокси не запущен.</p>
<p>Теперь такая ошибка будет подсвечиваться в IDE жёлтеньким.</p>
<p>См. <a href="https://github.com/selenide/selenide/commit/9b4723d090442c">коммит</a>.</p>
<p><br /></p>
<h1 id="починили-тесты-селенида-на-не-англоязычных-машинах">Починили тесты селенида на не-англоязычных машинах</h1>
<p>Оказалось, что парочка собственных тестов селенида падали, если их запустить на
машине, на которой системным языком был выбран не английский, а скажем, французкий или испанский.
Оказывается, в этих странах иначе форматируют Duration.</p>
<p>Теперь тесты исправлены, и по идее такое больше не может повториться.</p>
<p>Спасибо <a href="https://github.com/vrossellotravelc">Vicente Rossello Jaume</a> за <a href="https://github.com/selenide/selenide/pull/1408">PR 1408</a>.</p>
<p><br /></p>
<h1 id="статистика">Статистика</h1>
<p>Свежая статистика по скачкам Селенида:</p>
<center>
<img src="/images/2021/02/selenide.downloads.png" width="800" />
</center>
<p><br /></p>
<h1 id="традиции">Традиции</h1>
<p>Ну вот и всё на сегодня.</p>
<p>Вы обновляйтесь, а я пока пойду найду стопки водки и бутерброд с килькой. Традиция, ничего не поделаешь.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.18.12021-02-11T00:00:00+00:00http://ru.selenide.org/2021/02/11/selenide-5.18.1
<p>Всем привет!</p>
<p>Как сказал бы рэпер Гнойный, “релизай, а не накапливай!”<br />
Вот и мы релизнули небольшое обновление <a href="https://github.com/selenide/selenide/milestone/115?closed=1">Selenide 5.18.1</a>.</p>
<p>Давайте заглянем туда с фонариком:</p>
<p><br /></p>
<h1 id="добавили-метод-selenidegetsessionstorage">Добавили метод <code class="language-plaintext highlighter-rouge">Selenide.getSessionStorage()</code></h1>
<p>по аналогии с <code class="language-plaintext highlighter-rouge">Selenide.getLocalStorage()</code>, который появился ранее в версии 5.15.0.</p>
<p>У них одинаковый набор методов: <code class="language-plaintext highlighter-rouge">getItem</code>, <code class="language-plaintext highlighter-rouge">setItem</code>, <code class="language-plaintext highlighter-rouge">removeItem</code>, <code class="language-plaintext highlighter-rouge">clear</code> и т.д.</p>
<blockquote>
<p>Иногда полезно положить <code class="language-plaintext highlighter-rouge">sessionStorage</code> или <code class="language-plaintext highlighter-rouge">localStorage</code>, скажем, хитрый флаг, чтобы эмулировать какое-нибудь
действие пользователя или включить-выключить какие-то фичи или настройки.</p>
</blockquote>
<p>Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1400">PR 1400</a>.</p>
<p>P.S. Для справки, <a href="https://itchief.ru/javascript/localstorage-and-sessionstorage">разница между localStorage и sessionStorage</a>.</p>
<p><br /></p>
<h1 id="исправили-сообщение-об-ошибке-для-filterbyand">Исправили сообщение об ошибке для <code class="language-plaintext highlighter-rouge">$$.filterBy(and(..))</code></h1>
<p>Как вы знаете, селенид предоставляет богатые возможности для фильтрации и проверки коллекций.</p>
<p>Но пользователь <a href="https://github.com/fokinp">Pavel Fokin</a> обнаружил, что сообщение об ошибке может выглядеть
непонятно, когда коллекция фильтруется по <code class="language-plaintext highlighter-rouge">and</code> условию, т.е. комбинации нескольких разных условий:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">".sofa"</span><span class="o">).</span><span class="na">filterBy</span><span class="o">(</span><span class="n">and</span><span class="o">(</span><span class="s">"shining"</span><span class="o">,</span> <span class="n">text</span><span class="o">(</span><span class="s">"Jorshik"</span><span class="o">),</span> <span class="n">text</span><span class="o">(</span><span class="s">"Zoloto"</span><span class="o">))).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">2</span><span class="o">));</span>
</code></pre></div></div>
<p><br /></p>
<p>Результат был не совсем логичный (он содержал лишь последнее проверенное условие):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">sofa</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">text</span> <span class="nc">Jorshik</span><span class="o">)</span>
</code></pre></div></div>
<p>А теперь результат более корректный (он содержит все условия):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">sofa</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="nl">shining:</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Jorshik</span><span class="err">'</span> <span class="n">and</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Zoloto</span><span class="err">'</span><span class="o">)</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1392">issue 1392</a>.<br />
Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1393">PR 1393</a>.</p>
<p><br /></p>
<h1 id="передаём-настройку-noproxy-от-внешнего-прокси-селенидовскому">Передаём настройку “noproxy” от внешнего прокси селенидовскому</h1>
<p>Бывает, что селенид запускается и со своим собственным прокси, и с прокси пользователя.</p>
<p>У прокси есть одна особенная настройка “noproxy”, и туда часто пихают “localhost”. Это значит, что через прокси должны
ходить все запросы, кроме “http://localhost:*”. Так вот, в случае с “двойным” прокси эта настройка терялась, и
селенид не мог выполнить запросы на localhost.</p>
<p><em>Напрямую, а не через прокси.</em></p>
<p><em>Локалхост - нежная штучка!</em></p>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1390">PR 1390</a>.</p>
<p><br /></p>
<h1 id="обновились-на-netty-4159final-и-littleproxy-202">Обновились на Netty 4.1.59.Final и LittleProxy 2.0.2</h1>
<p>Вряд ли вы будете читать, но вот релизноутсы
<a href="https://netty.io/news/2021/02/08/4-1-59-Final.html">Netty 4.1.59.Final</a> и
<a href="https://github.com/mrog/LittleProxy/blob/master/RELEASE_NOTES.md">LittleProxy 2.0.2</a>.
Как минимум там исправили какие-то утечки памяти и дыру в безопасности.</p>
<h2 id="читальня">Читальня</h2>
<p>Организаторы Гейзенбага выложили видосики с осеннего Heisenbug Moscow 2020.<br />
Из того, что я успел заметить:</p>
<ul>
<li><a href="https://www.youtube.com/watch?list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&v=fFe3reCoeBQ&feature=emb_logo&ab_channel=Heisenbug">Flaky tests. Порядок имеет значение</a>
/ Андрей Солнцев</li>
<li>Воркшоп “Как начать свой проект автоматизации с нуля”:
<a href="https://www.youtube.com/watch?v=1aq4gYflEho&feature=youtu.be&utm_campaign=Heisenbug_2020_Piter_Announce_NoParticipants_211220&utm_medium=email&utm_source=newsletter&ab_channel=Heisenbug">часть 1</a>,
<a href="https://www.youtube.com/watch?utm_campaign=Heisenbug_2020_Piter_Announce_NoParticipants_211220&utm_medium=email&utm_source=newsletter&v=pbvJ8rmh7Ws&feature=youtu.be&ab_channel=Heisenbug">часть 2</a>
/ Андрей Солнцев, Юрий Артамонов</li>
<li><a href="https://www.youtube.com/watch?v=4yq37nxkguM&list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&index=12&ab_channel=Heisenbug">Серверный античит: Панацея или рудимент?</a> / Евгений Ченцов, Евгений Крутских</li>
<li><a href="https://www.youtube.com/watch?v=8gOkdFk2JZ8&list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&index=3&ab_channel=Heisenbug">Типы автоматического тестирования в IntelliJ IDEA</a> / Юрий Артамонов</li>
<li><a href="https://www.youtube.com/watch?v=Prm2-c_5mYs&list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&index=18&ab_channel=Heisenbug">Тест-кейсы как код</a> / Артем Ерошенко</li>
</ul>
<p><br /></p>
<p>Вот и всё на сегодня. Обновляйтесь и делитесь впечатлениями.
Заводите баги <a href="https://github.com/selenide/selenide/issues/new">на гитхабе</a>, жалуйтесь <a href="https://softwaretesters.slack.com/messages/selenide_ru">в чатиках</a>,
материтесь <a href="https://twitter.com/selenide">в твиттере</a>.</p>
<p><br />
<em>Опенсорс сильнее багов!</em></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.18.02021-01-23T00:00:00+00:00http://ru.selenide.org/2021/01/23/selenide-5.18.0
<p>Ура, товарищи!</p>
<p>На дворе 23 января.</p>
<p>Несанкционированные релизы сейчас запрещены, поэтому сегодня у нас просто негативное падение номера версии.
Обновляйтесь: <a href="https://github.com/selenide/selenide/milestone/113?closed=1">Selenide 5.18.0</a>.</p>
<p>Приглашаю вас на маленькую виртуальную экскурсию по изменениям в 5.18.0.<br />
Устраивайтесь поудобнее.</p>
<p><br /></p>
<h1 id="выключили-логи-вебдрайвера-по-умолчанию">Выключили логи вебдрайвера по умолчанию</h1>
<p>Начиная с версии 5.13.0, Selenide писал логи вебдрайвера в файлы <code class="language-plaintext highlighter-rouge">build/reports/tests/webdriver.uuid.log</code>.<br />
Тогда это казалось полезным, но оказалось, что логи эти занимают довольно много места и особо никого не интересуют.
Поэтому мы всё-таки решили не включать их по умолчанию.</p>
<p><em>Логи вебдрайвера отправляются в комнату для мусора.</em></p>
<p>Если надо, вы всегда можете включить эти логи настройкой<br />
<code class="language-plaintext highlighter-rouge">Configuration.webdriverLogsEnabled = true</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1365">issue 1365</a> и <a href="https://github.com/selenide/selenide/pull/1379">PR 1379</a>.</p>
<p><br /></p>
<h1 id="заменили-тип-параметра-timeout-с-long-на-duration">Заменили тип параметра “timeout” с Long на Duration</h1>
<p>… в методах коллекций. Теперь вместо</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(...),</span> <span class="mi">5000</span><span class="o">);</span>
</code></pre></div></div>
<p>модно будет писать</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(...),</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">5</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1377">issue 1377</a>.<br />
Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за <a href="https://github.com/selenide/selenide/pull/1377">PR 1377</a>.</p>
<p><br /></p>
<h1 id="ускорили-поиск-вложенных-элементов-в-shadow-dom">Ускорили поиск вложенных элементов в shadow dom</h1>
<p>Одна из фич, которые есть Selenide, но нет в Selenium webdriver - это <a href="/2020/03/18/selenide-5.10.0/"><em>shadow dom</em></a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"p"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">,</span> <span class="s">"#inner-shadow-host"</span><span class="o">))</span>
<span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"The Shadow-DOM inside another shadow tree"</span><span class="o">));</span>
</code></pre></div></div>
<p>В дереве DOM может быть сколько угодно <em>shadow root</em> внутри других <em>shadow root</em>.</p>
<p>Чтобы найти все элементы внутри всех вложенных shadow root, метод <code class="language-plaintext highlighter-rouge">$(shadowCss())</code> вызывал в цикле<br />
хитрый JavaScript код для каждого shadow root по отдельности.
И это может быть медленно, потому что каждый вызов webdriver занимает время.</p>
<p>Теперь <code class="language-plaintext highlighter-rouge">$(shadowCss())</code> дёргает <a href="https://github.com/selenide/selenide/blob/master/src/main/resources/find-in-shadow-roots.js">ещё более хитрый рекурсивный JavaScript</a>,
который находит все элементы во всех shadow dom за один вызов.</p>
<p>См. <a href="https://github.com/selenide/selenide/pull/1373">PR 1373</a>.</p>
<p>Отдельное спасибо <a href="https://github.com/sakamoto66">sakamoto66</a> за
<a href="https://github.com/selenide/selenide/issues/1246">issue 1246</a> и
<a href="https://github.com/selenide/selenide/pull/1233">PR 1233</a>.
К сожалению, он не был влит, но дал нам пищу для размышлений.</p>
<p><br /></p>
<h1 id="исправили-проверки-shouldnotand-и-shouldnotor">Исправили проверки <code class="language-plaintext highlighter-rouge">$.shouldNot(and(...))</code> и <code class="language-plaintext highlighter-rouge">$.shouldNot(or(...))</code></h1>
<p>Пользователь <a href="https://github.com/pavelpp">pavelpp</a> обнаружил в селениде багу, комбинируя <code class="language-plaintext highlighter-rouge">not</code> с условиями <code class="language-plaintext highlighter-rouge">and</code> и <code class="language-plaintext highlighter-rouge">or</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".lolkek"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">visible</span><span class="o">,</span> <span class="n">ofSeconds</span><span class="o">(</span><span class="mi">5</span><span class="o">));</span> <span class="c1">// работает</span>
<span class="err">$</span><span class="o">(</span><span class="s">".lolkek"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">and</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="n">visible</span><span class="o">));</span> <span class="c1">// падает</span>
<span class="err">$</span><span class="o">(</span><span class="s">".lolkek"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">or</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="n">visible</span><span class="o">));</span> <span class="c1">// падает</span>
</code></pre></div></div>
<p>Не отвертишься. Пришлось чинить.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1369">issue 1369</a> и <a href="https://github.com/selenide/selenide/pull/1370">PR 1370</a>.</p>
<p>Кстати, мы запретили <code class="language-plaintext highlighter-rouge">and</code> и <code class="language-plaintext highlighter-rouge">or</code> только с одним условием.
То есть теперь строка <code class="language-plaintext highlighter-rouge">or("foo", visible)</code> не скомпилируется.
Придётся написать как минимум два условия: <code class="language-plaintext highlighter-rouge">or("foo", visible, enabled)</code>.</p>
<p>Согласитесь, это логично. <em>Зачем нам выбор из одного?</em></p>
<p><br /></p>
<h1 id="обнаруживаем-конфликт-в-настройке-browsername">Обнаруживаем конфликт в настройке “browserName”</h1>
<p>Так уж вышло, что для задания браузера есть две разных настройки:</p>
<ol>
<li>Главная - <code class="language-plaintext highlighter-rouge">Configuration.browser</code></li>
<li>Другая - <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities["browserName"]</code> (не знаю, зачем она вообще нужна)</li>
</ol>
<p>И у вас может открыться неожиданный браузер, если вы не задали первую, но задали вторую.
В общем, теперь селенид обнаруживает такой конфликт и сразу кидает ошибку, чтобы всем всё стало понятно:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalArgumentException:</span> <span class="nc">Conflicting</span> <span class="n">browser</span> <span class="nl">name:</span> <span class="err">'</span><span class="n">chrome</span><span class="err">'</span> <span class="n">vs</span><span class="o">.</span> <span class="err">'</span><span class="n">firefox</span><span class="err">'</span>
</code></pre></div></div>
<p>Подчёркиваю: настройки <code class="language-plaintext highlighter-rouge">Configuration.browser</code> должно хватать во всех случаях, поэтому вторую не надо никуда пихать.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1366">issue 1366</a> и <a href="https://github.com/selenide/selenide/pull/1374">PR 1374</a>.</p>
<p><br /></p>
<h1 id="починили-показ-таймаута-в-отчёте">Починили показ таймаута в отчёте</h1>
<p>Недавно мы добавили в методы <code class="language-plaintext highlighter-rouge">$.should*</code> параметр <code class="language-plaintext highlighter-rouge">timeout</code> с типом <code class="language-plaintext highlighter-rouge">Duration</code>.
А потом оказалось, что он непонятно отображался в отчётах.<br />
Например, строка <code class="language-plaintext highlighter-rouge">$("h1").shouldBe(visible, Duration.ofSeconds(1))</code> в отчёте выглядела так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">)</span> <span class="n">should</span> <span class="nf">be</span><span class="o">([</span><span class="n">visible</span><span class="o">,</span> <span class="no">PT1M</span><span class="o">])</span>
</code></pre></div></div>
<p>И хотя эта “PT1M” на самом деле отвечает стандарту ISO и означает “time period 1 minute”, мы всё же заменили её на более
понятные “300 ms”, “1s”, “1.500 s.” и т.п.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1376">issue 1376</a> и <a href="https://github.com/selenide/selenide/pull/1378">PR 1378</a>.</p>
<p><br /></p>
<h1 id="обновились-на-webdrivermanager-431">Обновились на WebDriverManager 4.3.1</h1>
<p>Как обычно, изменения описаны <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">здесь</a>.</p>
<p><br /></p>
<p>Вот и всё на сегодня. Обновляйтесь и делитесь впечатлениями.
Заводите баги <a href="https://github.com/selenide/selenide/issues/new">на гитхабе</a>, жалуйтесь <a href="https://softwaretesters.slack.com/messages/selenide_ru">в чатиках</a>,
материтесь <a href="https://twitter.com/selenide">в твиттере</a>.</p>
<p><br /></p>
<h2 id="читальня">Читальня</h2>
<p>На хабре вышла расшифровка доклада <a href="https://habr.com/ru/company/jugru/blog/537166/">“Как законтрибьютить в опенсорс, чтобы не сгореть со стыда”</a>
со смачной картинкой Мастера Йоды. Этот доклад мы с Артёмом Ерошенко делали на фестивале TechTrain осенью 2020.</p>
<p>По ссылке и текст, и видео: кому что больше нравится.</p>
<center>
<img src="https://habrastorage.org/webt/ry/y2/bn/ryy2bn6_cjot2q7dipqjuq66i6q.png" width="500px" />
</center>
<p>Буду счастлив, если пользователи Селенида его посмотрят. Вы же хотите приобщиться к таинству опен-сорса?</p>
<p><br />
Всем щастья!
<br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.17.22020-12-30T00:00:00+00:00http://ru.selenide.org/2020/12/30/selenide-5.17.2
<p>Доброе утро!<br />
Гномики всё никак не успокоятся. Завтра утром они принесут в ваши носочки последний подарочек:
релиз <a href="https://github.com/selenide/selenide/milestone/110?closed=1">Selenide 5.17.2</a>.</p>
<p><br /></p>
<h1 id="теперь-commands-возвращают-selenideelement-вместо-webelement">Теперь <code class="language-plaintext highlighter-rouge">Commands</code> возвращают <code class="language-plaintext highlighter-rouge">SelenideElement</code> вместо <code class="language-plaintext highlighter-rouge">WebElement</code></h1>
<p>Это даёт возможность <em>чейнить</em> вызовы методов <code class="language-plaintext highlighter-rouge">$.execute(Command)</code> с другими селенидовскими метода, делая ваши тесты
ещё лаконичнее и экспрессивнее:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".lupa"</span><span class="o">).</span><span class="na">execute</span><span class="o">(</span><span class="k">new</span> <span class="nc">ScrollToCenter</span><span class="o">()).</span><span class="err">$</span><span class="o">(</span><span class="s">".pupa"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1355">PR 1355</a>.</p>
<p><br /></p>
<h1 id="починили-метод-setvaluenull">Починили метод <code class="language-plaintext highlighter-rouge">$.setValue(null)</code></h1>
<p>См. <a href="https://github.com/selenide/selenide/issues/1356">issue 1356</a>.
Спасибо <a href="https://github.com/dzem">Dmitriy Zemlyanitsyn</a> за <a href="https://github.com/selenide/selenide/pull/1357">PR 1357</a>.</p>
<p><br /></p>
<h1 id="включили-soft-asserts-в-методах-beforeall-и-afterall-в-junit-5">Включили soft asserts в методах @BeforeAll и @AfterAll (в JUnit 5)</h1>
<p>См. <a href="https://github.com/selenide/selenide/issues/981">issue 981</a>,
<a href="https://github.com/selenide/selenide/issues/1070">issue 1070</a> и
<a href="https://github.com/selenide/selenide/pull/1359">PR 1359</a>.</p>
<p><br /></p>
<h1 id="починили-файл-selenide-5172-javadocjar">Починили файл <a href="https://search.maven.org/remotecontent?filepath=com/codeborne/selenide/5.17.2/selenide-5.17.2-javadoc.jar">selenide-5.17.2-javadoc.jar</a></h1>
<p>теперь он содержит javadoc для всех классов.</p>
<p><br /></p>
<p><br />
Ещё раз с наступающим!
<br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.17.02020-12-26T00:00:00+00:00http://ru.selenide.org/2020/12/26/selenide-5.17.0
<p>Доброй ночи!</p>
<p>Я трепетно отношусь к Католическому Рождеству.<br />
Потому, что именно в Рождество террористы захватили небоскрёб Накатоми, и Брюс замочил Ханса Грубера.
А потом взорвал самолёт зажигалкой.</p>
<p>Поэтому ловите рождественский релиз <a href="https://github.com/selenide/selenide/milestone/108?closed=1">Selenide 5.17.0</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-asname">Добавили метод $.as(“name”)</h1>
<p>Чувствую, что открываю ящик пандоры, но что уж поделаешь…</p>
<p>В общем, мы добавили метод <code class="language-plaintext highlighter-rouge">as</code>, позволяющий давать элементам читаемые имена.</p>
<p>Чтобы продемонстрировать эффект, давайте сравним эти две строки в тесте:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">).</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span>
</code></pre></div></div>
<p>Результат в отчёте будет таким:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> +---------------------------------------+--------------------+----------+----------+
|Element |Subject |Status |ms. |
+---------------------------------------+--------------------+----------+----------+
|By.xpath: /long/ugly/xpath[1][2][3] |should not(exist) |PASS |13 |
|Login button |should not(exist) |PASS |38 |
+---------------------------------------+--------------------+----------+----------+
</code></pre></div></div>
<p>В последней строке вместо длинного нечитабельного xpath мы видим понятное имя “Login button”.</p>
<p>NB! Не торопитесь использовать эту возможность. Лично я воспринимаю её как “хак последней надежды”.<br />
В отчёте всегда лучше видеть настоящий локатор, чем кем-то придуманное имя, которое всегда может оказаться:</p>
<ul>
<li>Обманчивым</li>
<li>Устаревшим</li>
<li>Вводящим в заблуждение</li>
</ul>
<p>Лучше инвестируйте свои усилия в то, чтобы использовать читаемые локаторы. Ну и грамотно называть переменные и методы. Вот где сила, брат!</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1200">issue 1200</a> и <a href="https://github.com/selenide/selenide/pull/1353">PR 1353</a>.</p>
<p><br /></p>
<h1 id="добавили-настроек-нашему-headless-chrome">Добавили настроек нашему headless chrome</h1>
<p>Мы взяли и добавили нашему безголовому хрому те же настройки, <a href="https://github.com/puppeteer/puppeteer/blob/7a2a41f2087b07e8ef1feaf3881bdcc3fd4922ca/src/Launcher.js#L261">что и в puppeteer</a>.<br />
Ребята из puppeteer же не дураки, знают, что делают. Мы как они.</p>
<p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1329">PR 1329</a>.</p>
<p>P.S. Интересно, если ребята из puppeteer решать с крыши спрыгнуть, мы тоже спрыгнем?</p>
<p><br /></p>
<h1 id="починили-byshadowcssfindelements">Починили <code class="language-plaintext highlighter-rouge">ByShadowCss.findElements</code></h1>
<p>Оказалось, в одном хитром случае он возвращал не все элементы: когда в DOM было несколько <em>inner shadow hosts</em>
(как это по-православному, бог ты мой?)</p>
<p>Теперь возвращает все.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1346">issue 1346</a>.
Спасибо <a href="https://github.com/dpeger">Daniel H. Peger</a> за <a href="https://github.com/selenide/selenide/pull/1347">PR 1347</a>.</p>
<p><br /></p>
<h1 id="добавили-метод-should-с-кастомным-таймаутом">Добавили метод $.should* с кастомным таймаутом</h1>
<p>Как вы все знаете, в Селениде есть две основных группы методов:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">$.shouldHave</code> / <code class="language-plaintext highlighter-rouge">$.shouldBe</code> / <code class="language-plaintext highlighter-rouge">$.should</code> – используют таймаут по умолчанию</li>
<li><code class="language-plaintext highlighter-rouge">$.waitUntil</code> / <code class="language-plaintext highlighter-rouge">$.waitWhile</code> – используют заданный таймаут</li>
</ol>
<p>Методы <code class="language-plaintext highlighter-rouge">$.wait*</code> полезны для ожидания явно долгих действий, которые точно длятся дольше обычного таймаута (который 4 секунды по умолчанию).</p>
<p>Проблема с <code class="language-plaintext highlighter-rouge">$.wait*</code> методами чисто грамматическая: встроенные селенидовские проверки не звучат по-английски корректно с глаголом “wait”:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">element should have text</code> - звучит</li>
<li><code class="language-plaintext highlighter-rouge">element wait until text</code> - не звучит</li>
</ol>
<p>Не смертельно, но ухо режет (как говорил Ван Гог, гы-гы).</p>
<p>Поэтому теперь вы можете заменить <code class="language-plaintext highlighter-rouge">$.waitUntil(hasText("bob"), 18_000)</code> на <code class="language-plaintext highlighter-rouge">$.shouldHave(text("bob"), Duration.ofSeconds(18))</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1136">issue 1136</a>,
<a href="https://github.com/selenide/selenide/issues/1338">issue 1338</a> и
<a href="https://github.com/selenide/selenide/pull/1340">PR 1340</a>.</p>
<p><br /></p>
<h1 id="пэдж-обжекты">Пэдж обжекты</h1>
<p>Следующий блок улучшений касается пэдж обжектов.
Как вы знаете, в селениде есть возможность объявлять свои пэдж обжекты,</p>
<ul>
<li>а в них объявлять поля с аннотациями <code class="language-plaintext highlighter-rouge">@FindBy</code>,</li>
<li>а поля эти могут быть как стандартные селениумовские <code class="language-plaintext highlighter-rouge">WebElement</code>,</li>
<li>так и наши <code class="language-plaintext highlighter-rouge">SelenideElement</code>,</li>
<li>и даже переиспользуемые компоненты <code class="language-plaintext highlighter-rouge">ElementsContainer</code>, из которых можно собирать пэдж обжекты, как из кирпичиков.</li>
</ul>
<p>И работает всё это в лучших традициях селенида: ленивая загрузка и перезагрузка элементов, вот это всё.</p>
<p><br /></p>
<p>В общем, в этих <code class="language-plaintext highlighter-rouge">@FindBy</code> уже давно были шероховатости:</p>
<ol>
<li>не работала ленивая загрузка для полей ПО с типом <code class="language-plaintext highlighter-rouge">List<ElementsContainer></code> (см. issue <a href="https://github.com/selenide/selenide/issues/282">282</a> и <a href="https://github.com/selenide/selenide/issues/482">482</a>)</li>
<li>не поддерживались поля ПО с дженериками (см. <a href="https://github.com/selenide/selenide/issues/694">issue 694</a>)</li>
</ol>
<p>И это было сложно исправить. Пришлось погрузиться в этот (старый) код и серьёзно его порефакторить.
Пришлось напрячь мозги и применить все свои остатки былого абстрактного мышления.</p>
<p>Так что эти два пулреквеста - предмет моей гордости:</p>
<ul>
<li><a href="https://github.com/selenide/selenide/pull/1351">support page object fields of generic types</a></li>
<li><a href="https://github.com/selenide/selenide/pull/1354">enable lazy loading for Page Object fields of type <code class="language-plaintext highlighter-rouge">List<ElementsContainer></code></a></li>
</ul>
<p><br /></p>
<h1 id="и-напоследок-пачка-технологических-улучшений">И напоследок пачка технологических улучшений:</h1>
<ul>
<li>разбили проект на подпроекты - см. <a href="https://github.com/selenide/selenide/pull/1348">PR 1348</a></li>
<li>исправили тесты селенида, зависящие от ОС - См. <a href="https://github.com/selenide/selenide/issues/1344">issue 1344</a> и
<a href="https://github.com/selenide/selenide/pull/1345">PR 1345</a>, спасибо <a href="https://github.com/dpeger">Daniel H. Peger</a></li>
<li>подчистили код <code class="language-plaintext highlighter-rouge">Plugins</code> – спасибо <a href="https://github.com/yorlov">Yuri Orlov</a> за <a href="https://github.com/selenide/selenide/pull/1343">PR 1343</a></li>
<li>Обновились до browserup-proxy:2.1.2 и guava:30.1-jre</li>
<li>Добавили поддержку chrome 88, edge 89, opera 73</li>
</ul>
<p><br /></p>
<h3 id="известные-проблемы">Известные проблемы:</h3>
<ul>
<li>файл <a href="https://search.maven.org/remotecontent?filepath=com/codeborne/selenide/5.17.0/selenide-5.17.0-javadoc.jar">selenide-5.17.0-javadoc.jar</a>
получился неполным: он содержит javadoc не для всех классов. Надеемся, никто не обидится. Исправим в версии 5.17.1.</li>
</ul>
<p><br /></p>
<h1 id="итоги">Итоги</h1>
<p>В общем, заканчиваем год на позитивной ноте:<br />
провели серьёзный рефакторинг и исправили несколько старых болячек.<br />
В новый год мы переходим всего с одним экраном в <a href="https://github.com/selenide/selenide/issues">github issues</a> вместо прежних двух.</p>
<p>И количество скачиваний селенида выросло за год с 102 до 167 тысяч.</p>
<center>
<img src="/images/2020/12/selenide.downloads.png" width="800" />
</center>
<p><br />
С наступающим!
<br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.16.22020-11-25T00:00:00+00:00http://ru.selenide.org/2020/11/25/selenide-5.16.2
<p>Всем привет!</p>
<p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/109?closed=1">Selenide 5.16.2</a>.</p>
<p>Нет, вы не подумайте, что в недавнем крупном релизе <a href="/2020/11/20/selenide-5.16.0/">Selenide 5.16.0</a> была куча багов.
Вовсе нет.</p>
<p>Данный релиз 5.16.2 - это исправление кучи старых мелких бажочков, просто сейчас дошли до них руки.</p>
<p>Итак, коротко:</p>
<h2 id="selenide-5.16.2">Релиз <a href="https://github.com/selenide/selenide/milestone/109?closed=1">5.16.2</a> (вышел 25.11.2020)</h2>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1332">#1332</a> return old click(int, int) command logic – thanks to Petro Ovcharenko for PR <a href="https://github.com/selenide/selenide/pull/1333">#1333</a></li>
<li>make SoftAssertsExtension thread-safe – thanks to @dtuchs for PR <a href="https://github.com/selenide/selenide/pull/1334">#1334</a></li>
<li><a href="https://github.com/selenide/selenide/issues/1258">#1258</a> fix soft asserts with ParameterizedTest in jUnit5 – see PR <a href="https://github.com/selenide/selenide/pull/1328">#1328</a></li>
<li><a href="https://github.com/selenide/selenide/issues/1293">#1293</a> don’t report “Element not found” in case of other errors – see PR <a href="https://github.com/selenide/selenide/pull/1326">#1326</a></li>
<li><a href="https://github.com/selenide/selenide/issues/1290">#1290</a> don’t show unused page object fields in report – see PR <a href="https://github.com/selenide/selenide/pull/1327">#1327</a></li>
<li>upgrade to littleproxy:2.0.1 – see PR <a href="https://github.com/selenide/selenide/pull/1325">#1325</a></li>
</ul>
<h2 id="selenide-5.16.1">Релиз <a href="https://github.com/selenide/selenide/milestone/106?closed=1">5.16.1</a> (вышел 23.11.2020)</h2>
<p>Тут было два исправления, чтобы хром можно было запускать с расширениями.</p>
<ul>
<li><a href="https://github.com/selenide/selenide/issues/1314">#1314</a> do not exclude “load-extension” switch if Chrome is opened with extensions – see PR <a href="https://github.com/selenide/selenide/pull/1324">#1324</a></li>
<li><a href="https://github.com/selenide/selenide/issues/1315">#1315</a> support custom DriverFactory for running remote browsers – see PR <a href="https://github.com/selenide/selenide/pull/1324">#1324</a></li>
</ul>
<h2 id="news">Новости</h2>
<p>Раз осталось место, поделюсь новенькими ссылками:</p>
<ul>
<li>Пример от LambdaTest: <a href="https://github.com/LambdaTest/selenide-testng-sample">selenide-testng-sample</a></li>
<li>Видосик про Селенид на португальском: <a href="https://www.youtube.com/watch?v=yOfrqZUsFuU&feature=youtu.be&ab_channel=BluesoftLabs">Testes de Aceitação em Java com Selenide, Adriano Magalhães</a></li>
<li>Курс по Селениду где-то в Бразилии: <a href="https://inoveteste.com.br/automacao-web-descomplicada-com-selenide/">Automação Web Descomplicada Com Selenide</a></li>
<li>Визуальное тестирование: <a href="https://medium.com/automated-visual-testing-with-applitools/getting-started-with-the-applitools-sdk-653f2cd1ad48">Applitools+Selenide</a></li>
<li>Пример проекта: <a href="https://github.com/bmurmistro/applitools">Getting started with applitools</a></li>
</ul>
<p><br /></p>
<p>Selenide 5.17.0 уже не за горами, не переключайтесь!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.16.02020-11-20T00:00:00+00:00http://ru.selenide.org/2020/11/20/selenide-5.16.0
<p>Всем привет!</p>
<p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/105?closed=1">Selenide 5.16.0</a>.</p>
<h2 id="плагины">Плагины</h2>
<p>В начале 2020 года на конференции <a href="https://seleniumcamp.com/materials/">SeleniumCamp</a> я рассказывал
про <a href="https://seleniumcamp.com/talk/bof-glorious-past-and-promising-future-of-selenide/">roadmap селенида</a>.
Одна из ключевых идей на этот год была создать в селениде возможность подключать сторонние плагины.</p>
<p><strong>И вот этот день настал!</strong></p>
<p>На данный момент доступно два плагина:</p>
<ul>
<li><a href="https://github.com/selenide/selenide-appium">selenide-appium 1.5.0</a></li>
<li><a href="https://github.com/selenide/selenide-selenoid">selenide-selenoid 1.0.0</a></li>
</ul>
<p>Расскажем о них в отдельных постах.</p>
<p>Вероятно, дальше стоит оформить в виде плагинов поддержку Allure, JUnit, TestNG, AShot.<br />
<em>Накидывайте ещё идеи!</em></p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1051">issue #1051</a> и
<a href="https://github.com/selenide/selenide/pull/1264">PR #1264</a>,
<a href="https://github.com/selenide/selenide/pull/1317">PR 1317</a> и
<a href="https://github.com/selenide/selenide/pull/1321">PR 1321</a>.</p>
<h2 id="сообщения-об-ошибках">Сообщения об ошибках</h2>
<p>Одна из основных обязанностей селенида - составлять подробные сообщения об ошибках в случае падения тестов.<br />
Иногда в эти сообщения тоже закрадываются неточности в каких-то сложных случаях.<br />
В этом релизе мы запилили целую пачку улучшений сообщений об ошибках:</p>
<h3 id="улучшили-описание-проверок-and-и-not">Улучшили описание проверок AND и NOT</h3>
<p>Нормальный способ устроить негативную проверку в селениде - метод <code class="language-plaintext highlighter-rouge">shouldNotHave</code>.
А проверить несколько условий подряд - просто через запятую:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldNotHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"____"</span><span class="o">),</span> <span class="n">attribute</span><span class="o">(</span><span class="s">"data-masked"</span><span class="o">));</span>
</code></pre></div></div>
<p>Но есть ещё методы <code class="language-plaintext highlighter-rouge">Condition.not</code> и <code class="language-plaintext highlighter-rouge">Condition.and</code> для особых случаев:
например, с их помощью можно определять составные условия, таким образом составляя целый DSL для ваших тестов.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyConditions</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Condition</span> <span class="no">NONADMIN</span> <span class="o">=</span> <span class="n">not</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Condition</span> <span class="no">MASKED</span> <span class="o">=</span> <span class="n">and</span><span class="o">(</span><span class="s">"MASKED"</span><span class="o">,</span> <span class="n">text</span><span class="o">(</span><span class="s">"___"</span><span class="o">),</span> <span class="n">attribute</span><span class="o">(</span><span class="s">"data-masked"</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyTest</span> <span class="o">{</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="no">MASKED</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="no">NONADMIN</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Оказалось, что оба эти методы выводили в отчёте неполную информацию:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">be</span><span class="o">(</span><span class="no">MASKED</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span>
<span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">have</span><span class="o">(</span><span class="n">not</span> <span class="n">text</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span>
</code></pre></div></div>
<p>Проблема в том, что в нём не виден ожидаемый текст (“admin”) и другие условия.</p>
<p>Теперь мы эту проблему исправили, и при падении проверки вы увидите сообщение с текстом:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">be</span><span class="o">(</span><span class="nl">MASKED:</span> <span class="n">text</span> <span class="err">'</span><span class="n">___</span><span class="err">'</span> <span class="n">and</span> <span class="n">attribute</span> <span class="n">data</span><span class="o">-</span><span class="n">masked</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span>
<span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">have</span><span class="o">(</span><span class="n">not</span> <span class="n">text</span> <span class="err">'</span><span class="n">admin</span><span class="err">'</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1306">PR 1306</a>
и <a href="https://github.com/selenide/selenide/pull/1300">PR 1306</a>.</p>
<p><br /></p>
<h3 id="добавили-информацию-о-предках">Добавили информацию о предках</h3>
<p>Селенид позволяет искать элементы внутри других элементов. Например, так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#user-table"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"thead"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"trrrr-chah-chah"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Age"</span><span class="o">));</span>
</code></pre></div></div>
<p>Но если такая проверка падает, в сообщении был виден только локатор дочернего элемента:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="n">trrrr</span><span class="o">-</span><span class="n">chah</span><span class="o">-</span><span class="n">chah</span><span class="o">}</span>
</code></pre></div></div>
<p>А теперь мы также добавили информацию и о родителях:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="err">#</span><span class="n">user</span><span class="o">-</span><span class="n">table</span><span class="o">/</span><span class="n">thead</span><span class="o">/</span><span class="n">trrrr</span><span class="o">-</span><span class="n">chah</span><span class="o">-</span><span class="n">chah</span><span class="o">}</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1312">PR 1312</a>.</p>
<p><br />
<br /></p>
<h3 id="добавили-актуальные-текст-для-проверок-owntext-и-exactowntext">Добавили актуальные текст для проверок <code class="language-plaintext highlighter-rouge">ownText</code> и <code class="language-plaintext highlighter-rouge">exactOwnText</code></h3>
<p>Как вы помните, в Selenide 5.15.0 мы добавили проверки <code class="language-plaintext highlighter-rouge">ownText</code> и <code class="language-plaintext highlighter-rouge">exactOwnText</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#child_div1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">ownText</span><span class="o">(</span><span class="s">"Sonar"</span><span class="o">));</span>
</code></pre></div></div>
<p>Но и тут оказалось, что при падении такой проверки в сообщении не выводился, а какой же “свой текст” был на самом деле.
Выводился только текст элемента с детьми:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">should</span> <span class="n">have</span> <span class="n">own</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Sonar</span><span class="err">'</span> <span class="o">{</span><span class="err">#</span><span class="n">child_div1</span><span class="o">}</span>
<span class="nl">Element:</span> <span class="err">'</span><span class="o"><</span><span class="n">div</span> <span class="n">id</span><span class="o">=</span><span class="s">"child_div1"</span><span class="o">></span><span class="nc">Son</span><span class="o"></</span><span class="n">div</span><span class="o">></span><span class="err">'</span>
</code></pre></div></div>
<p>Теперь мы добавили строчку “Actual value” с собственным текстом элемента:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">should</span> <span class="n">have</span> <span class="n">own</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Sonar</span><span class="err">'</span> <span class="o">{</span><span class="err">#</span><span class="n">child_div1</span><span class="o">}</span>
<span class="nl">Element:</span> <span class="err">'</span><span class="o"><</span><span class="n">div</span> <span class="n">id</span><span class="o">=</span><span class="s">"child_div1"</span><span class="o">></span><span class="nc">Son</span><span class="o"></</span><span class="n">div</span><span class="o">></span><span class="err">'</span>
<span class="nc">Actual</span> <span class="nl">value:</span> <span class="nc">Son</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1261">issue 1261</a> и <a href="https://github.com/selenide/selenide/pull/1294">PR 1294</a>.</p>
<p><br />
<br /></p>
<h3 id="кидаем-правильную-ошибку-при-загрузке-несуществующего-файла">Кидаем правильную ошибку при загрузке несуществующего файла</h3>
<p>Если вы пытались загрузить несуществующий файл:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"input[type='file']"</span><span class="o">).</span><span class="na">uploadFile</span><span class="o">(</span><span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="s">"/foo/bar/xyz.pdf"</span><span class="o">));</span>
</code></pre></div></div>
<p>То получали некорректную ошибку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="n">input</span><span class="o">[</span><span class="n">type</span><span class="o">=</span><span class="err">'</span><span class="n">file</span><span class="err">'</span><span class="o">]}</span>
<span class="o">...</span>
<span class="nc">Caused</span> <span class="nl">by:</span> <span class="nl">InvalidArgumentException:</span> <span class="n">invalid</span> <span class="nl">argument:</span> <span class="nc">File</span> <span class="n">not</span> <span class="n">found</span> <span class="o">:</span> <span class="o">/</span><span class="n">foo</span><span class="o">/</span><span class="n">bar</span><span class="o">/</span><span class="n">xyz</span><span class="o">.</span><span class="na">pdf</span>
</code></pre></div></div>
<p>Казалось бы, мелочь, но иногда это сбивало с толку.</p>
<p>Теперь мы это исправили, и вы получите сразу</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nl">InvalidArgumentException:</span> <span class="n">invalid</span> <span class="nl">argument:</span> <span class="nc">File</span> <span class="n">not</span> <span class="n">found</span> <span class="o">:</span> <span class="o">/</span><span class="n">foo</span><span class="o">/</span><span class="n">bar</span><span class="o">/</span><span class="n">xyz</span><span class="o">.</span><span class="na">pdf</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/987">issue 987</a> и <a href="https://github.com/selenide/selenide/pull/1301">PR 1301</a>.</p>
<p><br /></p>
<blockquote>
<p>Корень проблемы в том, что в селенум не хватает отдельных классов ошибок для всех возможных нестандартных ситуаций, и
простой <code class="language-plaintext highlighter-rouge">WebDriverException</code> может говорить как о ненайденном элементе, так и неверной кодировке в вводимой строке.
См., например, <a href="https://github.com/selenide/selenide/issues/1293">issues 1293</a>.</p>
<p>Плюс некоторые реализации вебдрайвера могут кидать какие-то нелогичные ошибки - например, IEDriver когда-то кидал
<code class="language-plaintext highlighter-rouge">Throwable</code> вместо <code class="language-plaintext highlighter-rouge">ElementNotFound</code>.</p>
<p>Поэтому в своё время в селениде была принята консервативная стратегия: если не удалось определить тип ошибки точнее,
по умолчанию считаем, что элемент не найден. А в “caused by” будут видны подробности.</p>
</blockquote>
<p><br />
<br /></p>
<h3 id="адекватно-показываем-clickoptions-в-отчёте">Адекватно показываем ClickOptions в отчёте</h3>
<p>Как вы помните, в Selenide 5.15.0 мы добавили удобные методы для клика со всевозможными опциями:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span>
</code></pre></div></div>
<p>И снова оказалось, что в отчёте такая строка выглядит некрасиво:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">page</span> <span class="o">|</span><span class="n">click</span><span class="o">(</span><span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">ClickOptions</span><span class="err">@</span><span class="mi">33617539</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span>
</code></pre></div></div>
<p>Мы и это исправили, теперь строчка стала <em>окейнее</em>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">page</span> <span class="o">|</span><span class="n">click</span><span class="o">(</span><span class="nl">method:</span> <span class="no">JS</span><span class="o">,</span> <span class="nl">offsetX:</span> <span class="mi">123</span><span class="o">,</span> <span class="nl">offsetY:</span> <span class="mi">222</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1302">issue 1302</a> и <a href="https://github.com/selenide/selenide/pull/1303">PR 1303</a>.</p>
<p><br /></p>
<h2 id="другое">Другое</h2>
<h3 id="добавили-проверку-shouldhaveexacttextscasesensitiveinanyorder">Добавили проверку <code class="language-plaintext highlighter-rouge">$$.shouldHave(exactTextsCaseSensitiveInAnyOrder(...))</code></h3>
<p>В селениде уже давно есть проверки для коллекций:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(</span><span class="s">"вася"</span><span class="o">,</span> <span class="s">"петя"</span><span class="o">,</span> <span class="s">"катя"</span><span class="o">));</span> <span class="c1">// case-insensitive, substring</span>
<span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">textsInAnyOrder</span><span class="o">(</span><span class="s">"вася"</span><span class="o">,</span> <span class="s">"катя"</span><span class="o">,</span> <span class="s">"петя"</span><span class="o">));</span> <span class="c1">// case-insensitive, substring, any order</span>
<span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTexts</span><span class="o">(</span><span class="s">"вася"</span><span class="o">,</span> <span class="s">"петя"</span><span class="o">,</span> <span class="s">"катя"</span><span class="o">));</span> <span class="c1">// case-insensitive, full string match</span>
</code></pre></div></div>
<p>И теперь к ним добавилась ещё одна - менее толерантная:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// case-sensitive, full string match, any order</span>
<span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTextsCaseSensitiveInAnyOrder</span><span class="o">(</span><span class="s">"Вася"</span><span class="o">,</span> <span class="s">"Петя"</span><span class="o">,</span> <span class="s">"Катя"</span><span class="o">));</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/plagov">Vitali Plagov</a> за <a href="https://github.com/selenide/selenide/pull/1286">PR 1286</a>.</p>
<p><br /></p>
<h3 id="исправили-проверку-href-со-спецсимволами">Исправили проверку <code class="language-plaintext highlighter-rouge">href</code> со спец.символами</h3>
<p>Как вы помните, в Selenide 5.15.0 мы добавили проверку <code class="language-plaintext highlighter-rouge">href</code>.<br />
Оказалось, что она неправильно работала, если в <code class="language-plaintext highlighter-rouge">href</code> затесались экранированные символы, как во второй строке:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">href</span><span class="o">(</span><span class="s">"/foo/bar/details.html"</span><span class="o">));</span> <span class="c1">// works</span>
<span class="err">$</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">href</span><span class="o">(</span><span class="s">"/files/some%20file.pdf"</span><span class="o">));</span> <span class="c1">// fails</span>
</code></pre></div></div>
<p>Проблема исправлена.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1298">issue 1298</a>.<br />
Спасибо <a href="https://github.com/rerednaw">rerednaw</a> за <a href="https://github.com/selenide/selenide/pull/1299">PR 1299</a>.</p>
<p><br /></p>
<h3 id="разрешаем-хрому-скачивать-несколько-файлов">Разрешаем хрому скачивать несколько файлов</h3>
<p>Бывают такие хитрые ссылки, по клику на которые браузер начинает скачивать не один, а сразу два или больше файлов.<br />
Оказалось, что в этом случае браузер Chrome показывает диалог “Уверены, что хотите скачать все файлы?” - и этот диалог
не даёт ничего дальше сделать, пока пользователь не нажмёт кнопку. И ваш тест падает.</p>
<blockquote>
<p>Самое противное в этой проблеме то, что её очень сложно повторить руками: браузер показывает диалог только в первый раз, так что
при локальном запуске скорее всего вы его не увидите, и тест будет зелёным.</p>
</blockquote>
<p>Чтобы вылечить эту проблему, мы добавили при запуске хрома специальный ключик <code class="language-plaintext highlighter-rouge">profile.default_content_setting_values.automatic_downloads=1</code>,
который сразу разрешает хрому качать сколько угодно файлов без всяких диалогов.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1307">issue 1307</a>.<br />
Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/pull/1308">PR 1308</a>.</p>
<p><br /></p>
<h3 id="разрешили-скачивать-файлы-со-слэшем-в-названии">Разрешили скачивать файлы со слэшем в названии</h3>
<p>Метод <code class="language-plaintext highlighter-rouge">download</code> не разрешает скачивать файл, если в его названии есть неразрешённые символы, в числе которых был и слэш:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">File</span> <span class="n">report</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#report"</span><span class="o">).</span><span class="na">download</span><span class="o">();</span>
<span class="o">--></span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"File name cannot contain slash: 11/08/2020_-_day_transactions.pdf"</span><span class="o">)</span>
</code></pre></div></div>
<p>Нам казалось это логичным, ведь такие файлы тупо не разрешает создавать файловая система.<br />
Но оказалось, что иногда слэш вполне логичен - например, как разделитель в датах. И хром вполне скачивает такие файлы,
просто заменяя слэш на подчёркивание.</p>
<p>Теперь и селенид так делает.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1322">issue 1322</a> и <a href="https://github.com/selenide/selenide/pull/1323">PR 1323</a>.</p>
<p><br /></p>
<h3 id="зафиксировали-версию-guava-300-jre">Зафиксировали версию Guava 30.0-jre</h3>
<p>Ох уж эта гуава!</p>
<p>В коде самого селенида Guava не используется, нам она не нужна.<br />
Но с ней вечные проблемы: от неё зависят многие другие библиотеки (Selenium, LittleProxy, BrowserUpProxy, Checkstyle),
и все от разных версий. А ещё ведь у неё есть специальная версия для android…</p>
<p>В общем, слишком часто так случалось, что Maven или Gradle подтягивал не ту версию Guava, и селенид в итоге не работал. <br />
Нам это надоело, и теперь в селениде явно прописана свежая версия Guava <code class="language-plaintext highlighter-rouge">30.0-jre</code>. Надеемся, это поможет избежать всех
этих бесконечных конфликтов завиимостей.</p>
<p><br /></p>
<h3 id="перешли-на-github-actions">Перешли на Github Actions</h3>
<p>Как в любом приличном проекте, в Selenide есть свой набор автотестов (юнит- и интеграционных), и они запускаются
автоматически для всех веток на CI сервере. Раньше мы использовали Travis CI, который предоставляет бесплатный сервис
для опен-сорс проектов. Спасибо им большое за годы совместной работы. :)</p>
<p>Но в этом году гитхаб выкатил свой CI сервис под названием “Github actions”.</p>
<blockquote>
<p>Смотри обзор от Артём Ерошенко и Севы Брекелова в <a href="http://www.youtube.com/watch?v=dxGGZQiuD6Q&t=60m10s">выпуске №3 Шоу “Ошибка выжившего”</a>.</p>
</blockquote>
<p>И казалось логичным использовать его.</p>
<p>Ура, этот день настал! Теперь все билды селенида видны <a href="https://github.com/selenide/selenide/actions">прямо на гитхабе</a>.</p>
<p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1319">PR 1319</a>.</p>
<p><br /></p>
<h2 id="уффф">Уффф.</h2>
<p>Многабукаф, но вы осилили. Все молодцы!</p>
<p>Как обычно - обновляйтесь, пробуйте, смело сообщайте о проблемах и делитесь идеями.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Почему прокси не работает в Selenoid?2020-11-17T00:00:00+00:00http://ru.selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid
<p>Всем привет!</p>
<p>Сегодня мы наконец-то раскроем тайну, почему у многих не работает прокси в Selenoid.</p>
<h3 id="задача-скачать-файл">Задача: скачать файл</h3>
<ul>
<li>Мы запускаем тесты, в которых браузер бежит в контейнере Selenoid (обычно также и на Selenide, но необязательно).</li>
<li>В ходе теста мы хотим скачать файл.</li>
<li>Метод по умолчанию <code class="language-plaintext highlighter-rouge">$.download()</code> не подходит (например, потому, что скачивание происходит не по прямой ссылке).</li>
<li>Поэтому мы хотим <a href="/2019/12/10/advent-calendar-download-files/">скачать файл через прокси</a>.</li>
</ul>
<h3 id="наши-действия">Наши действия</h3>
<ol>
<li>Создаём проект</li>
<li>Добавляем в проект зависимость BrowserUpProxy, как указано в документации Selenide:
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">dependencies</span> <span class="p">{</span>
<span class="nf">testRuntimeOnly</span><span class="p">(</span><span class="s">"com.browserup:browserup-proxy-core:2.1.1"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div> </div>
</li>
<li>Копипастим типичный бойлерплейт для запуска браузера в Selenoid:
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyHost</span> <span class="o">=</span> <span class="s">"192.168.0.10"</span><span class="o">;</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">remote</span> <span class="o">=</span> <span class="s">"http://localhost:4444/wd/hub"</span><span class="o">;</span>
<span class="nc">DesiredCapabilities</span> <span class="n">capabilities</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DesiredCapabilities</span><span class="o">();</span>
<span class="n">capabilities</span><span class="o">.</span><span class="na">setBrowserName</span><span class="o">(</span><span class="s">"chrome"</span><span class="o">);</span>
<span class="n">capabilities</span><span class="o">.</span><span class="na">setVersion</span><span class="o">(</span><span class="s">"85.0"</span><span class="o">);</span>
<span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"enableVNC"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
<span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"enableVideo"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
<span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"enableLog"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">capabilities</span><span class="o">;</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">fileDownload</span> <span class="o">=</span> <span class="nc">FileDownloadMode</span><span class="o">.</span><span class="na">PROXY</span><span class="o">;</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</code></pre></div> </div>
</li>
<li>Ну и пишем тест, что-то вроде
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://the-internet.herokuapp.com/download"</span><span class="o">);</span>
<span class="nc">File</span> <span class="n">file</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"some-file.txt"</span><span class="o">)).</span><span class="na">download</span><span class="o">();</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">file</span><span class="o">.</span><span class="na">getName</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"some-file.txt"</span><span class="o">);</span>
</code></pre></div> </div>
</li>
</ol>
<h3 id="проблема">Проблема</h3>
<p>И получаем ошибку при открытии браузера:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">WebDriverException</span><span class="o">:</span> <span class="n">unknown</span> <span class="nl">error:</span> <span class="nl">net:</span><span class="o">:</span><span class="no">ERR_TUNNEL_CONNECTION_FAILED</span>
<span class="o">...</span>
<span class="n">at</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="nc">Selenide</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">49</span><span class="o">)</span>
<span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">selenoid</span><span class="o">.</span><span class="na">FileDownloadTest</span><span class="o">.</span><span class="na">download</span><span class="o">(</span><span class="nc">FileDownloadTest</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">45</span><span class="o">)</span>
</code></pre></div></div>
<p><br /></p>
<h3 id="ааа-паника">ААА, паника!</h3>
<p>На этом месте большинство людей паникует, перебирает кучу опций браузера и селенидовских настроек
и в конце концов пишет в чатик автоматизаторов.</p>
<p>А ведь всего-то надо было почитать внимательно лог.<br />
В логе чётко видна проблема:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="nc">LittleProxy</span><span class="o">-</span><span class="mi">0</span><span class="o">-</span><span class="nc">ProxyToServerWorker</span><span class="o">-</span><span class="mi">1</span><span class="o">]</span> <span class="no">ERROR</span> <span class="n">org</span><span class="o">.</span><span class="na">littleshoot</span><span class="o">.</span><span class="na">proxy</span><span class="o">.</span><span class="na">impl</span><span class="o">.</span><span class="na">ProxyToServerConnection</span>
<span class="o">-</span> <span class="o">(</span><span class="no">HANDSHAKING</span><span class="o">)</span> <span class="o">[</span><span class="nl">id:</span> <span class="mh">0xc05a41d5</span><span class="o">,</span> <span class="nl">L:</span><span class="o">/</span><span class="mf">10.10</span><span class="o">.</span><span class="mf">10.145</span><span class="o">:</span><span class="mi">56103</span>
<span class="o">-</span> <span class="nl">R:</span><span class="n">the</span><span class="o">-</span><span class="n">internet</span><span class="o">.</span><span class="na">herokuapp</span><span class="o">.</span><span class="na">com</span><span class="o">/</span><span class="mf">52.1</span><span class="o">.</span><span class="mf">16.137</span><span class="o">:</span><span class="mi">443</span><span class="o">]</span>
<span class="o">:</span> <span class="nc">Caught</span> <span class="n">an</span> <span class="n">exception</span> <span class="n">on</span> <span class="nc">ProxyToServerConnection</span>
<span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">NoSuchMethodError</span><span class="o">:</span> <span class="err">'</span><span class="kt">int</span> <span class="n">io</span><span class="o">.</span><span class="na">netty</span><span class="o">.</span><span class="na">buffer</span><span class="o">.</span><span class="na">ByteBuf</span><span class="o">.</span><span class="na">maxFastWritableBytes</span><span class="o">()</span><span class="err">'</span>
<span class="n">at</span> <span class="n">io</span><span class="o">.</span><span class="na">netty</span><span class="o">.</span><span class="na">handler</span><span class="o">.</span><span class="na">codec</span><span class="o">.</span><span class="na">ByteToMessageDecoder</span><span class="err">$</span><span class="mi">1</span><span class="o">.</span><span class="na">cumulate</span><span class="o">(</span><span class="nc">ByteToMessageDecoder</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">86</span><span class="o">)</span>
</code></pre></div></div>
<h3 id="крутим-зависимости">Крутим зависимости</h3>
<p>Ошибка <code class="language-plaintext highlighter-rouge">NoSuchMethodError</code> недвусмысленно намекает на то, что у нас проблема с зависимостями:
в classpath оказались какие-то два JAR’а с несовместимыми версиями.</p>
<p>Против этого уже давно придумали прививку. Удивляюсь, почему так много людей до сих пор не в курсе.</p>
<p>Запускаем команду:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">gradle dependencies</code>, или</li>
<li><code class="language-plaintext highlighter-rouge">mvn dependency:tree</code></li>
</ul>
<p>И вуаля! - чётко видим, какие у нас JAR’ы и каких версий. Ищем там что-то похожее на “netty”.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\--- com.browserup:browserup-proxy-core:2.1.1
+--- io.netty:netty-codec:4.1.44.Final
+--- xyz.rogfam:littleproxy:2.0.0-beta-5
| +--- io.netty:netty-all:4.1.34.Final
</code></pre></div></div>
<p>Как видим, у нас есть два джарника с разными версиями: <code class="language-plaintext highlighter-rouge">netty-codec:4.1.44.Final</code> и <code class="language-plaintext highlighter-rouge">netty-all:4.1.34.Final</code>.</p>
<h3 id="лечим-зависимости">Лечим зависимости</h3>
<p>Чтобы исправить проблему, достаточно явно прописать в <code class="language-plaintext highlighter-rouge">build.gradle</code> или <code class="language-plaintext highlighter-rouge">pom.xml</code> более новую версию <code class="language-plaintext highlighter-rouge">Netty</code>:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">testRuntimeOnly</span><span class="p">(</span><span class="s">"io.netty:netty-all:4.1.54.Final"</span><span class="p">)</span>
<span class="nf">testRuntimeOnly</span><span class="p">(</span><span class="s">"io.netty:netty-codec:4.1.54.Final"</span><span class="p">)</span>
</code></pre></div></div>
<p>(на самом деле достаточно только одной из этих строк. <em>Домашнее задание: какой и почему?</em>)</p>
<p>Команда <code class="language-plaintext highlighter-rouge">gradle dependencies</code> показывает, что теперь версии совпадают:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\--- com.browserup:browserup-proxy-core:2.1.1
+--- io.netty:netty-codec:4.1.44.Final -> 4.1.54.Final
+--- xyz.rogfam:littleproxy:2.0.0-beta-5
| +--- io.netty:netty-all:4.1.34.Final -> 4.1.54.Final
</code></pre></div></div>
<p>Тест запускается, прокси работает, файл скачивается. Всем щастье.</p>
<h3 id="мораль">Мораль</h3>
<p>Будьте внимательнее к логам, братьям нашим меньшим!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.15.02020-09-26T00:00:00+00:00http://ru.selenide.org/2020/09/26/selenide-5.15.0
<p>Всем привет!</p>
<p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/104?closed=1">Selenide 5.15.0</a>.</p>
<h2 id="добавили-настройку-configurationpageloadtimeout--по-умолчанию-30-секунд">Добавили настройку <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code> <br /> (по умолчанию 30 секунд)</h2>
<p>Бывает так, что вебдрайвер надолго подвисает, пытаясь загрузить какую-то страницу, либо картинку на этой странице, или ещё какой-то элемент.
Теряется время, сессия обрывается по таймауту и т.д.</p>
<p>В этом случае хорошо бы прервать тест пораньше, но таймаут для загрузки страницы в Selenium по умолчанию слишком большой: 5 минут.</p>
<p>Поэтому мы добавили в Selenide настройку <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code>, чтобы этот таймаут было легко менять.</p>
<p>Внимание! Значение по умолчанию - 30 секунд. Если ваши тесты начали падать по таймауту - знайте, какую настройку подкрутить.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1268">issue 1268</a> и <a href="https://github.com/selenide/selenide/pull/1269">PR 1269</a>.</p>
<h2 id="добавили-универсальный-метод-клика-с-параметром-clickoptions">Добавили универсальный метод клика<br /> с параметром <code class="language-plaintext highlighter-rouge">ClickOptions</code></h2>
<p>В некотором роде это новое слово в API селенида.</p>
<p>В селениде изначально был метод <code class="language-plaintext highlighter-rouge">$.click()</code>, который просто вызывал обычный селениумовский метод <code class="language-plaintext highlighter-rouge">WebElement.click()</code>. Который кликает (вроде как) в центр элемента.</p>
<p>Со временем начали появляться вариации:</p>
<ul>
<li>Настройка <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code>, чтобы кликать не вызовом <code class="language-plaintext highlighter-rouge">WebElement.click()</code>, а через JavaScript. Теоретически это должно сделать тесты стабильнее (а может, и быстрее) и позволит кликать то, что обычно не получается.</li>
<li>Клик со сдвигом <code class="language-plaintext highlighter-rouge">$.click(offsetX, offsetY)</code>, чтобы кликать не в центр элемента</li>
</ul>
<p>Неудобно в этом то, что невозможно кликать то через JS, то через селениум - настройка глобальная. Пришлось бы её постоянно менять.</p>
<p>Поэтому мы добавили метод, в который можно явно передавать парметр - способ клика.</p>
<p>Теперь кликать можно без изменения глобальной настройки, хоть обкликайся:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">());</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">offsetY</span><span class="o">(</span><span class="mi">222</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">());</span>
</code></pre></div></div>
<p>NB! Мы рекомендуем выставить настройку <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code> в то значение, которое вам подходит в большинстве случаев, и задавать параметр <code class="language-plaintext highlighter-rouge">ClickOptions</code> точечно - только там, где он необходим.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1173">issue 1173</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1226">PR 1226</a>.</p>
<h2 id="добавили-универсальный-метод-скачивания-файла-с-параметром-downloadoptions">Добавили универсальный метод скачивания файла<br /> с параметром <code class="language-plaintext highlighter-rouge">DownloadOptions</code></h2>
<p>Аналогичная история была с методом <code class="language-plaintext highlighter-rouge">$.download()</code>. Изначально он умел скачивать файлы только одним способом - через GET запрос.
Потом селенид научился скачивать файлы через прокси. Недавно мы добавили третий способ - <code class="language-plaintext highlighter-rouge">FOLDER</code>.<br />
До сих пор способ скачивания можно было задать только через глобальную настройку <code class="language-plaintext highlighter-rouge">Configuration.fileDownload</code>.</p>
<p>А теперь способ скачивания можно задавать через параметр в каждый вызов <code class="language-plaintext highlighter-rouge">$.download</code>, не меняя глобальную настройку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">);</span>
<span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span>
<span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">).</span><span class="na">withFilter</span><span class="o">(</span><span class="n">withExtension</span><span class="o">(</span><span class="s">"xls"</span><span class="o">)).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span>
<span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">);</span>
<span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span>
<span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">).</span><span class="na">withFilter</span><span class="o">(</span><span class="n">withExtension</span><span class="o">(</span><span class="s">"pdf"</span><span class="o">)).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span>
</code></pre></div></div>
<p>NB! Мы рекомендуем выставить настройку <code class="language-plaintext highlighter-rouge">Configuration.fileDownload</code> в то значение, которое вам подходит в большинстве случаев, и задавать параметр <code class="language-plaintext highlighter-rouge">DownloadsOptions</code> точечно - только там, где он необходим.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1259">issue 1259</a> и <a href="https://github.com/selenide/selenide/pull/1260">PR 1260</a>.</p>
<h2 id="добавили-методы-для-работы-с-localstorage">Добавили методы для работы с LocalStorage</h2>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">localStorage</span><span class="o">;</span>
<span class="c1">// Очистить всё содержимое:</span>
<span class="n">localStorage</span><span class="o">().</span><span class="na">clear</span><span class="o">();</span>
<span class="c1">// Добавить значение:</span>
<span class="n">localStorage</span><span class="o">().</span><span class="na">setItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">,</span> <span class="s">"john"</span><span class="o">);</span>
<span class="c1">// Проверить значение:</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">localStorage</span><span class="o">().</span><span class="na">getItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">)).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"john"</span><span class="o">);</span>
<span class="c1">// Проверить количество элементов:</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">localStorage</span><span class="o">().</span><span class="na">size</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="c1">// удалить значение:</span>
<span class="n">localStorage</span><span class="o">().</span><span class="na">removeItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">localStorage</span><span class="o">().</span><span class="na">getItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">)).</span><span class="na">isNull</span><span class="o">();</span>
</code></pre></div></div>
<p>Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1274">PR 1274</a>.</p>
<h2 id="добавили-проверки-для-текст-элемента-без-потомков">Добавили проверки для текст элемента без потомков</h2>
<p>Классическая селенидовская проверка <code class="language-plaintext highlighter-rouge">$.shouldHave(text("Hello, world"))</code> включает и текст самого элемента, и тексты всех его потомков.<br />
А иногда хочется проверить только текст самого элемента, без потомков. Для этого мы добавила две новых проверки:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">ownText</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">));</span> <span class="c1">// Элемент должен содержать текст "Hello"</span>
<span class="err">$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactOwnText</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">));</span> <span class="c1">// Текст элемента должен быть "Hello" </span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1261">issue 1261</a> и <a href="https://github.com/selenide/selenide/pull/1262">PR 1262</a>.</p>
<h2 id="ускорили-работу-с-большими-фильтрованными-коллекциями">Ускорили работу с большими фильтрованными коллекциями</h2>
<p>В селениде есть удобные методы для работы с коллекциями элементов: их можно удобно отфильтровать, в них можно искать элементы или разом проверить, скажем, тексты всех элементов.</p>
<p>Но если слишком злоупотреблять, можно написать слишком медленный тест. Например, вот так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">list</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="c1">// На странице 100 элементов <li></span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">list</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Если на странице, предположим, 100 элементов <code class="language-plaintext highlighter-rouge"><li></code>, то такой тест может очень долго работать.</p>
<ul>
<li>Ведь селенид на каждом шагу должен перегрузить элемент - вдруг его состояние изменилось!</li>
<li>А чтобы перегрузить N-ный элемент коллекции, нужно перегрузить всю коллекцию (в селениуме ведь нет метода <code class="language-plaintext highlighter-rouge">WebDriver.findElement(index)</code>).</li>
<li>А чтобы перегрузить фильтрованную коллекцию, нужно пройтись по всем её элементам и применить фильтр для каждого (в данном примере - вызвать <code class="language-plaintext highlighter-rouge">WebElement.isDisplayed()</code>).</li>
</ul>
<p><br />
Для сравнения, такой цикл работает гораздо быстрее:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">unfiltered</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">);</span> <span class="c1">// Нефльтрованная коллекция </span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">unfiltered</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>В этом релизе мы немного ускорили работу с коллекциями. Теперь Селенид фильтрует коллекцию умнее: чтобы получить <code class="language-plaintext highlighter-rouge">list.get(N)</code>, он применяет фильтр <code class="language-plaintext highlighter-rouge">visible</code> не для всех 100 элементов, а только для первых <code class="language-plaintext highlighter-rouge"><N></code> элементов.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1266">issue 1266</a> и <a href="https://github.com/selenide/selenide/pull/1270">PR 1270</a>.</p>
<p><br />
P.S. Напомню, что другой вариант ускорить тесты в таких случаях - использовать метод <code class="language-plaintext highlighter-rouge">snapshot</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">list</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">visible</span><span class="o">).</span><span class="na">snapshot</span><span class="o">();</span> <span class="c1">// snapshot() создаёт "слепок" коллекции. Селенид не будет её перегружать каждый раз. </span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">list</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Но при использовании <code class="language-plaintext highlighter-rouge">snapshot</code> есть риск словить <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code>, если коллекция всё-таки обновилась во время цикла.</p>
<h2 id="добавили-проверку-href">Добавили проверку “href”</h2>
<p>Иногда хочется в тесте проверить, что у элемента <code class="language-plaintext highlighter-rouge"><a></code> правильное значение атрибута <code class="language-plaintext highlighter-rouge">href</code>, т.е. ссылки.<br />
Для этого всегда можно было использовать метод <code class="language-plaintext highlighter-rouge">$("a").shouldHave(attribute("href", "/foo/bar/details.html")</code>.</p>
<p>Проблема в том, что такая проверка очень неочевидно падает, потому что селениум возвращает абсолютный, а не относительный URL.<br />
Подробнее см. в докладе Алексея Баранцева <a href="http://www.youtube.com/watch?v=4dh--iD_zK8&t=57m11s">“Заморочки в Selenium WebDriver”</a> с 57:11.</p>
<p>Чтобы хоть немножко облегчить эту заморочку, мы добавили проверку “should have href” для ссылок:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">`$</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">href</span><span class="o">(</span><span class="s">"/foo/bar/details.html"</span><span class="o">))</span><span class="err">`</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1272">issue 1272</a> и <a href="https://github.com/selenide/selenide/pull/1273">PR 1273</a>.</p>
<h2 id="добавили-опцию-хрома-no-sandbox">Добавили опцию хрома “–no-sandbox”</h2>
<p>Я где-то вычитал, что она сделает тесты на хроме стабильнее. Скрестим пальцы и будем надеяться.</p>
<p>См. <a href="https://github.com/selenide/selenide/commit/3293956d">commit 3293956d</a></p>
<h2 id="селенид-теперь-явно-кидает-ошибку">Селенид теперь явно кидает ошибку</h2>
<p>… если не удалось создать папку для скачивания файлов.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1265">issue 1265</a> и commit 94ece98f](https://github.com/selenide/selenide/commit/94ece98f).</p>
<h2 id="обновились-на-webdrivermanager-422">Обновились на WebDriverManager 4.2.2</h2>
<p>См. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p>
<p><br /></p>
<h2 id="видеообзор">Видеообзор</h2>
<p>Смотрите <a href="https://youtu.be/txozPtQIqiE">видеообзор</a> данного релиза.</p>
<h2 id="новости">Новости</h2>
<p>Приходите 4-7 ноября 2020 на онлайн-конференцию <a href="https://heisenbug-moscow.ru/2020/msk/schedule/">Heisenbug</a>!</p>
<p>Я там тоже буду выступать:</p>
<ul>
<li>Воркшоп “Как начать свой проект автоматизации с нуля (с божьей помощью и Selenide)” - для начинающих</li>
<li>Доклад “Flaky tests. Метод” - для опытных</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.14.02020-08-17T00:00:00+00:00http://ru.selenide.org/2020/08/17/selenide-5.14.0
<p>Всем привет!</p>
<p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/101?closed=1">Selenide 5.14.0</a>.</p>
<h2 id="стабилизировали-новый-способ-скачивания-файлов-folder">Стабилизировали новый способ скачивания файлов <code class="language-plaintext highlighter-rouge">FOLDER</code></h2>
<p>… который появился в <a href="/2020/07/08/selenide-5.13.0/">Selenide 5.13.0</a>.</p>
<p>Вот что поменялось в 5.14.0:</p>
<ol>
<li>
<p>Каждый раз, когда селенид открывает браузер, он создаёт для него уникальную папку для скачиваний.
Это помогает избежать ситуаций, когда параллельные тесты одновременно скачивают файлы в одну и ту же папку, и невозможно понять, где чей файл.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1220">issue 1220</a> и <a href="https://github.com/selenide/selenide/pull/1221">PR 1221</a>.</p>
<ul>
<li>Увы, это не работает для IE и Safari (которые в принципе не позволяют задать папку для скачивания файлов)</li>
<li>Также это работает только для тех браузеров, которые открывает селенид.</li>
<li>Если же вы сами открываете браузер и передаёте его селениду, вам нужно будет создать уникальную папку самостоятельно и передать её селениду:
<ul>
<li>Либо с помощью нового метода <code class="language-plaintext highlighter-rouge">setWebDriver(driver, proxy, downloadsFolder)</code>,</li>
<li>либо конструктора <code class="language-plaintext highlighter-rouge">SelenideDriver(..., downloadsFolder)</code>.</li>
</ul>
</li>
</ul>
</li>
<li>Перед началом каждого скачивания файла селенид очищает папку – см. <a href="https://github.com/selenide/selenide/pull/1252">PR 1252</a></li>
<li>Селенид удаляет все пустые папки для скачиваний в конце тестов – см. <a href="https://github.com/selenide/selenide/pull/1247">PR 1247</a></li>
</ol>
<h2 id="добавили-проверку-shouldhaveitemwithtextany-text">Добавили проверку <code class="language-plaintext highlighter-rouge">$$.shouldHave(itemWithText("any text"))</code></h2>
<p>В отличие от классической <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("text1", "text2"))</code>, она означает, что в коллекции есть хотя бы один элемент с данным текстом.</p>
<p>Спасибо <a href="https://github.com/LuisOsv">Luis Serna</a> за <a href="https://github.com/selenide/selenide/pull/1194">PR 1194</a>.</p>
<p>Кстати, это первый коммит в селенид аж из Боливии!</p>
<h2 id="добавили-поддержку-браузера-safari">Добавили поддержку браузера Safari</h2>
<p>Когда-то селенид поддерживал Safari, но тогда куча всего в нём не работало.<br />
В какой-то момент нам надоело с ним мучаться, и мы поддержку выпилили.<br />
Но сейчас попробовали новый подход. Вроде как завелось (не всё, конечно).</p>
<p>Как обычно, достаточно просто прописать</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">Configuration.browser = "safari";</code> либо</li>
<li><code class="language-plaintext highlighter-rouge">-Dselenide.browser=safari</code></li>
</ol>
<p>Делитесь впечатлениями.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1236">issue 1236</a> и <a href="https://github.com/selenide/selenide/pull/1237">PR 1237</a>.</p>
<h2 id="добавили-метод-selenidedriverscreenshotfilename">Добавили метод <code class="language-plaintext highlighter-rouge">SelenideDriver.screenshot(fileName)</code></h2>
<p>Полезно, если вы создаёте “нестатический” вариант драйвера (<code class="language-plaintext highlighter-rouge">new SelenideDriver()</code>) и хотите снимать скриншоты.<br />
Теперь можно.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1166">issue 1166</a> и <a href="https://github.com/selenide/selenide/pull/1227">PR 1227</a>.</p>
<h2 id="добавили-метод-selenidedriverscreenshotoutputtype">Добавили метод <code class="language-plaintext highlighter-rouge">SelenideDriver.screenshot(OutputType)</code></h2>
<p>Иногда хочется получить скриншот в формате Base64. Например, этот формат хотят некоторые инструменты для сравниения скриншотов.</p>
<p>Теперь их можно получить таким вызовом:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">screenshot</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">screenshot</span><span class="o">(</span><span class="nc">OutputType</span><span class="o">.</span><span class="na">BASE64</span><span class="o">);</span>
<span class="kt">byte</span><span class="o">[]</span> <span class="n">decoded</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">getDecoder</span><span class="o">().</span><span class="na">decode</span><span class="o">(</span><span class="n">screenshot</span><span class="o">);</span>
<span class="nc">BufferedImage</span> <span class="n">img</span> <span class="o">=</span> <span class="nc">ImageIO</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="k">new</span> <span class="nc">ByteArrayInputStream</span><span class="o">(</span><span class="n">decoded</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1224">issue 1224</a> и <a href="https://github.com/selenide/selenide/pull/1231">PR 1231</a>.</p>
<h2 id="теперь-селенид-снимает-скриншот-в-случае-падения-switchto">Теперь селенид снимает скриншот в случае падения <code class="language-plaintext highlighter-rouge">switchTo()</code></h2>
<p>Как вы знаете, Селенид автоматически делает скриншот в случае падения тестов.<br />
Но мы обнаружили, что Селенид НЕ делал скриншот, если упал один из этих методов:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">switchTo(frame)</code></li>
<li><code class="language-plaintext highlighter-rouge">switchTo(window)</code></li>
<li><code class="language-plaintext highlighter-rouge">switchTo(alert)</code></li>
</ul>
<p>Теперь мы исправили эту досадную оплошность.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1190">issue 1190</a> и <a href="https://github.com/selenide/selenide/pull/1240">PR 1240</a>.</p>
<h2 id="добавили-хрому-опцию---disable-dev-shm-usage">Добавили хрому опцию <code class="language-plaintext highlighter-rouge">--disable-dev-shm-usage</code></h2>
<p>Мы тут вычитали, что без этой опции Chrome может крэшиться из-за out of memory error.</p>
<ol>
<li>Почему никто из вас на это не жаловался?</li>
<li>Стало ли лучше после добавления это опции?</li>
</ol>
<p>P.S. Позже люди жаловались и на наличие этой опции. <a href="https://github.com/selenide/selenide/issues/1559">Эпопея</a> пока не окончена.</p>
<h2 id="исправили-работу-sizzle-селекторов-на-страницах-с-dojojs-troopjs-и-тп">Исправили работу Sizzle селекторов на страницах с Dojo.js, troop.js и т.п.</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/434">issue 434</a> и <a href="https://github.com/selenide/selenide/pull/1242">PR 1242</a>.</p>
<h2 id="сделали-метод-tostring-безопаснее">Сделали метод <code class="language-plaintext highlighter-rouge">$.toString()</code> безопаснее</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/1241">issue 1241</a> и <a href="https://github.com/selenide/selenide/pull/1245">PR 1245</a>.</p>
<h2 id="улучшили-сообщение-об-ошибке-если-элемент-внезапно-пропал">Улучшили сообщение об ошибке, если элемент внезапно пропал</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/1013">issue 1013</a> и <a href="https://github.com/selenide/selenide/pull/1239">PR 1239</a>.</p>
<h2 id="обновились-на-webdrivermanager-410">Обновились на WebDriverManager 4.1.0</h2>
<p>См. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p>
<p><br /></p>
<h2 id="видеообзор">Видеообзор</h2>
<p>Смотрите <a href="https://youtu.be/x0KWgnjxsl4">видеообзор</a> данного релиза.</p>
<h2 id="новости">Новости</h2>
<ul>
<li>Ничоси! Курс на Udemy “<a href="https://www.udemy.com/course/selenium-selenide-test-automation-engineer/">Selenium и Selenide для начинающих Automation QA, QC на Java</a>”</li>
<li>Про селенид <a href="https://www.youtube.com/watch?v=WNzTuYFd8oI">на немецком</a></li>
<li>Пример проекта <a href="https://github.com/d3m0/automation">на Selenide+Selenoid+Docker</a> от <a href="https://github.com/d3m0">d3m0</a></li>
<li>Ещё пример: <a href="https://github.com/Crushpowerx/JavaMavenSelenideAllureScreenDiffExample">сравнение скриншотов с Selenide+Allure+Ashot+Screen Diff Plugin</a> от <a href="https://github.com/Crushpowerx/">Evgeniy Asovin</a></li>
<li>Ещё пример: <a href="https://github.com/qaschevychelov/giphyTest">Selenide + Appium + Allure + TestNG</a> от <a href="https://github.com/qaschevychelov/">qaschevychelov</a></li>
<li>Сравнение Selenide и Selenium 2019 года: <a href="https://www.appliedtech.ru/en/web-tools-for-ui-testing-selenium-or-selenide.html">Choosing tools for UI testing: Selenium or Selenide?</a> - 17.09.2019</li>
<li>Статья Jakub Skibiński в блоге компании Sonalake: <a href="https://sonalake.com/latest/selenide-a-powerful-testing-framework/">Selenide: A Powerful Testing Framework</a> - 19.06.2020</li>
<li>Статья “<a href="https://habr.com/ru/company/maxilect/blog/499810/">Почему мы перешли на Selenide, попутно написав более 200 новых автотестов</a>” - 30.04.2020</li>
<li><a href="https://medium.com/@maxilect_pr/selenide-our-experience-11240f9ce10c">Switch from Serenity to Selenide</a> - 22.05.2020 от <a href="https://medium.com/@maxilect_pr">Yuri Kudryavtsev</a> (Maxilect company)</li>
</ul>
<p>И целая серия статей от <a href="https://medium.com/@alexspush">Alexander Pushkarev</a>:</p>
<ul>
<li><a href="https://medium.com/@alexspush/test-automation-framework-architecture-part-2-1-layered-architecture-example-62a0011d3329">Test automation framework architecture — Layered architecture example with vanilla JUnit + Selenide</a></li>
<li><a href="https://medium.com/@alexspush/ui-automation-for-mortal-elegant-page-objects-with-java-and-selenide-3122b17dc473">UI Automation for mortals: elegant Page Objects with Java and Selenide</a></li>
<li><a href="https://medium.com/@alexspush/an-alternative-to-ubiquitous-ui-level-checking-subcutaneous-tests-8d29e8883fc2">Effective test automation: subcutaneous tests as a faster alternative to Selenium-driven testing</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.13.02020-07-08T00:00:00+00:00http://ru.selenide.org/2020/07/08/selenide-5.13.0
<p>Всем привет!</p>
<p>Лето не повод расслабляться! Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/98?closed=1">Selenide 5.13.0</a>.</p>
<p>Будьте осторожны, он может сломать ваши тесты (если они были неаккуратно сделаны).</p>
<h2 id="метод-shouldhavetext-кидает-ошибку">Метод <code class="language-plaintext highlighter-rouge">$.shouldHave(text(""))</code> кидает ошибку</h2>
<p>Возможно, самый популярный метод селенида - это <code class="language-plaintext highlighter-rouge">$.shouldHave(text("что-то"))</code>. Но даже если вы используете его каждый день,
возможно, вы не подозреваете, что он проверяет <em>подстроку</em>. Т.е. проверка <code class="language-plaintext highlighter-rouge">$("h1").shouldHave(text("ello"))</code> сработает и для
элемента <code class="language-plaintext highlighter-rouge"><h1>Hello World</h1></code>.</p>
<p>Частный случай такой проверки - <code class="language-plaintext highlighter-rouge">$("h1").shouldHave(text(""))</code>. Такую проверку пройдёт <strong>любой элемент</strong>, ведь любая
строка содержит пустую подстроку. То есть такая проверка просто <em>бессмысленна</em>.</p>
<blockquote>
<p>Если вам нужно убедиться, что текст
пустой, используйте <code class="language-plaintext highlighter-rouge">$("h1").shouldHave(exactText(""))</code> или <code class="language-plaintext highlighter-rouge">$("h1").shouldBe(empty)</code>.</p>
</blockquote>
<p>Мы хотим вам помочь избежать этой типичной проблемы и избавиться от бессмысленных проверок. Для этого теперь селенид
будет бросать ошибку, если вы попытаетесь передать в метод <code class="language-plaintext highlighter-rouge">text("")</code> пустую строку или null.</p>
<blockquote>
<p>Я попробовал Selenide 5.13.0 на своём рабочем проекте и внезапно нашёл тестов 20-30, которые падали с этой ошибкой.
И это всё были логические ошибки в тестах! Вот какое полезное изменение вас ждёт. :)</p>
</blockquote>
<p>См. <a href="https://github.com/selenide/selenide/issues/1156">issue 1156</a>.<br />
Спасибо <a href="https://github.com/eaxdev">Roman S.A.</a> за <a href="https://github.com/selenide/selenide/pull/1186">PR 1186</a>.</p>
<h2 id="убрали-лишние-логи-из-аллюра">Убрали лишние логи из аллюра</h2>
<p>Внимательные аллюровцы заметили, что селенид логирует одно и то же действие дважды. Например, при вызове такой строки:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="n">bySelector</span><span class="o">).</span><span class="na">findAll</span><span class="o">(</span><span class="nc">BySelector</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">condtion</span><span class="o">);</span>
</code></pre></div></div>
<p>(на самом деле не только в аллюре, но и в обычном селенидовском <code class="language-plaintext highlighter-rouge">TextReport</code>)</p>
<p>Теперь мы убрали эти двойные логи. Пришло кое-чего порефакторить, некоторые сообщения об ошибках изменились.<br />
Но вроде бы всё стало лучше. Если заметили ухудшение - смело сообщайте.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/997">issue 997</a> и <a href="https://github.com/selenide/selenide/pull/1193">PR 1193</a>.</p>
<h2 id="улучшили-сообщение-об-ошибках-для-коллекций">Улучшили сообщение об ошибках для коллекций</h2>
<p>Ещё одна похожая история, где мы немного изменили формат ошибок при поиске элементов внутри коллекций.<br />
По идее теперь должно быть понятнее, что внутри чего не удалось найти.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/967">issue 967</a> и <a href="https://github.com/selenide/selenide/pull/1189">PR 1189</a>.</p>
<h2 id="починили-загрузку-файлов-вне-тэга-form">Починили загрузку файлов вне тэга <code class="language-plaintext highlighter-rouge"><form></code></h2>
<p>Как вы знаете, селенид позволяет загрузить одной командой несколько файлов:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">uploadFile</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">File</span><span class="o">(</span><span class="s">"a.txt"</span><span class="o">),</span>
<span class="k">new</span> <span class="nf">File</span><span class="o">(</span><span class="s">"b.txt"</span><span class="o">),</span>
<span class="k">new</span> <span class="nf">File</span><span class="o">(</span><span class="s">"c.txt"</span><span class="o">)</span>
<span class="o">);</span>
</code></pre></div></div>
<p>Для этой загрузки мы в своё время сделали в селениде хитрый JS хак.
Оказалось, что этот хак предполагал, что <code class="language-plaintext highlighter-rouge"><input></code> для загрузки файла должен быть внутри тэга <code class="language-plaintext highlighter-rouge"><form></code>. Что как бы логично.<br />
Но оказалось, что не у всех так.</p>
<p>В общем, теперь мы упростили хак и убрали зависимость от тэга <code class="language-plaintext highlighter-rouge"><form></code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/943">issue 943</a> и <a href="https://github.com/selenide/selenide/pull/1188">PR 1188</a>.</p>
<h2 id="научились-скачивать-файлы-с-кавычками-в-имени">Научились скачивать файлы с кавычками в имени</h2>
<p>… и другими нехорошими символами.</p>
<p>Оказывается, есть и такие чудаки, которые генерируют файлы, в имени которых есть кавычки. Линукс и Мак вполне умеют
сохранять такие файлы, а вот винда никак. Теперь селенид заменяет кавычки и другие нехорошие символы на подчёркивания
(как делают все основные браузеры).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1196">issue 1196</a> и <a href="https://github.com/selenide/selenide/pull/1199">PR 1199</a>.</p>
<h2 id="научили-вебдрайвер-писать-свои-логи-в-файл">Научили вебдрайвер писать свои логи в файл</h2>
<p>До сих пор вебдрайвер, запускаемый селенидом, по умолчанию не писали никуда свои логи.
Вам нужно было включать их явно.</p>
<p>Теперь же селенид включает логи вебдрайвера. Логи пишутся в файлы вида <code class="language-plaintext highlighter-rouge">build/reports/tests/webdriver.ts_pid_tid.log</code>.<br />
Полный путь к файлу вы можете узнать из лога, который пишет селенид каждый раз при открытии браузера:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">INFO</span> <span class="nc">Write</span> <span class="n">webdriver</span> <span class="n">logs</span> <span class="nl">to:</span> <span class="o">/</span><span class="n">andrei</span><span class="o">/</span><span class="n">build</span><span class="o">/</span><span class="n">reports</span><span class="o">/</span><span class="n">tests</span><span class="o">/</span><span class="n">webdriver</span><span class="o">.</span><span class="mi">1594248139109_18125_1</span><span class="o">.</span><span class="na">log</span>
</code></pre></div></div>
<p>Когда в следующий раз будете изучать аномальное поведение вебдрайвера, не забудьте туда заглянуть.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1206">issue 1206</a> и <a href="https://github.com/selenide/selenide/pull/1207">PR 1207</a>.</p>
<h2 id="добавили-новый-способ-скачивания-файлов-folder">Добавили новый способ скачивания файлов <code class="language-plaintext highlighter-rouge">FOLDER</code></h2>
<p>Как вы знаете, до сих пор в селениде было два способа скачивания файлов: <code class="language-plaintext highlighter-rouge">HTTPGET</code> и <code class="language-plaintext highlighter-rouge">PROXY</code>.
См. <a href="/2019/12/10/advent-calendar-download-files/">пост в нашем блоге</a>.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">HTTPGET</code> - самый простой и надёжный. Но умеет скачивать только файлы с ссылки вида <code class="language-plaintext highlighter-rouge"><a href></code>.</li>
<li><code class="language-plaintext highlighter-rouge">PROXY</code> - универсальный и мощный способ. Но при удалённом запуске требует доступа с машины браузера к машине тестов.
Что порой вызывает сложности и поклонников Селеноида и Грида.</li>
</ul>
<p>Теперь появился третий способ: <code class="language-plaintext highlighter-rouge">FOLDER</code>.</p>
<p>Чтобы его включить, просто пропишите в начале тестов:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">fileDownload</span> <span class="o">=</span> <span class="nc">FileDownloadMode</span><span class="o">.</span><span class="na">FOLDER</span><span class="o">;</span>
</code></pre></div></div>
<p>Работает он просто:</p>
<ol>
<li>Кликает элемент</li>
<li>Смотрит, какие новые файлы появились в папке <code class="language-plaintext highlighter-rouge">build/downloads</code></li>
<li>Если таких файлов несколько, пытается угадать, какой из них подходит лучше всего.</li>
</ol>
<p>Рабочий пример всегда можно найти <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadToFolderTest.java">в тестах самого селенида</a>.</p>
<p><br />
P.S. Будем пока считать этот способ <em>экспериментальным</em>, поскольку есть нюансы:</p>
<ol>
<li>Работает надёжно при локальном запуске в один поток</li>
<li>При параллельном запуске одновременные тесты могут скачивать файлы в одну и ту же папку, и тогда селенид не сможет определить, кому какой файл отдать.</li>
<li>При удалённом запуске этот способ вообще пока не работает. Тесты-то здесь, а папка там!</li>
<li>Поддерживаются Chrome, Firefox, Edge, Opera. Не поддерживаются IE и Safari (у в принципе
нет возможности задать папку для скачивания файлов).</li>
</ol>
<p>Мы будем работать над этим, а вы делитесь своими соображениями, как разрулить вышеозначенные проблемы.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1212">issue 1212</a>,
<a href="https://github.com/selenide/selenide/pull/1213">PR 1213</a> и
<a href="https://github.com/selenide/selenide/pull/1215">PR 1215</a>.</p>
<h2 id="метод-getwrappedelement-снова-ждёт-появления-элемента">Метод <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> снова ждёт появления элемента</h2>
<p>Вряд ли вам это интересно, но упомянуть обязан. По сути мы откатили одно недавнее изменение.</p>
<p>Допустим, у вас есть такой код:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideElement</span> <span class="n">button</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"button"</span><span class="o">);</span>
<span class="n">executeJavascript</span><span class="o">(</span><span class="s">"arguments[0].click()"</span><span class="o">,</span> <span class="n">button</span><span class="o">);</span>
</code></pre></div></div>
<p>где <code class="language-plaintext highlighter-rouge">button</code>, допустим, появляется на экране с задержкой.<br />
Всю жизнь этот метод ждал появления кнопки, и лишь тогда кликал. В Selenide 5.11 нас укусила какая-то собака, и мы сделали
так, чтобы не ждал. Никто почему-то не жаловался, а вот в моём рабочем проекте несколько тестов упали.</p>
<p>В общем, теперь мы вернули обратно старое поведение. Теперь снова ждёт.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1191">issue 1191</a> и <a href="https://github.com/selenide/selenide/pull/1203">PR 1203</a>.</p>
<h2 id="обновились-до-browserupproxy-211">Обновились до BrowserUpProxy 2.1.1</h2>
<p>Ну вдруг.</p>
<p>Обновляйтесь, пробуйте, делитесь впечатлениями!</p>
<h2 id="статистика">Статистика</h2>
<p>И моё любимое: статистика скачиваний селенида. Мы пробили потолок в 160 тысяч в месяц!</p>
<center>
<img src="/images/2020/07/selenide.downloads.png" width="800" />
</center>
<p><br />
и 31+ тысяча уникальных айпишников:</p>
<center>
<img src="/images/2020/07/selenide.unique-ips.png" width="800" />
</center>
<p>Жизнь хороша!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.12.22020-05-29T00:00:00+00:00http://ru.selenide.org/2020/05/29/selenide-5.12.2
<p>Всем привет!</p>
<p>Ловите ещё один мини-релиз <a href="https://github.com/selenide/selenide/milestone/99?closed=1">Selenide 5.12.2</a>.</p>
<h2 id="подправили-аннотации-nonnull">Подправили аннотации @Nonnull</h2>
<p>… для некоторых методов <code class="language-plaintext highlighter-rouge">SelenideElement</code>.</p>
<p>После обновления на Selenide 5.12.0 некоторые пользователи котлина начали жаловаться, что их проекты переставил компилироваться.<br />
Всё дело в том, что мы пометили все методы <code class="language-plaintext highlighter-rouge">SelenideElement</code> аннотациями <code class="language-plaintext highlighter-rouge">@Nullable</code>/<code class="language-plaintext highlighter-rouge">@Nonnull</code>, а котлин к ним чуток.</p>
<p>Для следующих методов мы теперь прописали <code class="language-plaintext highlighter-rouge">@Nonnull</code>, потому что дополнительная проверка показала, что они никогда не возвращают null:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$.getText()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.text()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.innerText()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.innerHtml()</code></li>
<li><code class="language-plaintext highlighter-rouge">$.getSelectedText()</code></li>
</ul>
<p>Теперь в котлине их можно по-прежнему пихать в ненулевые переменные (хоть мне и кажутся сомнительными такие конструкции в тестах).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1179">issue 1179</a> и <a href="https://github.com/selenide/selenide/pull/1181">PR 1181</a>.</p>
<h2 id="исправили-работу-настройки-holdbrowseropentrue">Исправили работу настройки <code class="language-plaintext highlighter-rouge">holdBrowserOpen=true</code></h2>
<p>Эта настройка срабатывала не всегда. Уже давно. <br />
И мы давно об этом знали, но забывали, потому что никто нам issue на гитхабе не заводил. :(</p>
<p>В общем, исправили. При заданной настройке <code class="language-plaintext highlighter-rouge">Configuration.holdBrowserOpen=true</code> браузер остаётся открытым после окончания
тестов и вообще всех потоков.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1172">issue 1172</a> и <a href="https://github.com/selenide/selenide/pull/1176">PR 1176</a>.</p>
<h2 id="видосики">Видосики</h2>
<ul>
<li>Видео <a href="https://meetup.jugru.org/qa-heisenbug-breakfast-2">Тяжелое утро с Heisenbug #2</a> со мной. Вопросы, до которых обычно на конференциях не до.</li>
<li>Всеволод Брекелов и Артём Ерошенко в <a href="https://meetup.jugru.org/qa-survival-bias-4">“Ошибке выжившего #4”</a> в прямом эфире пишут экспорт Record&Play в Selenide. Представляете!</li>
<li>Мы опубликовали <a href="https://github.com/selenide/selenide/wiki/Selenide-Roadmap">Selenide Roadmap</a>. Ждём ваших отзывов!</li>
</ul>
<h2 id="новости">Новости</h2>
<p>Ура!</p>
<p>Свершилось!</p>
<p>Мы опубликовали <a href="https://github.com/selenide/selenide-for-selenium-ide">плагин для Selenium IDE</a>,
который умеет <strong>экспортировать код в Selenide</strong>.</p>
<ul>
<li><a href="https://chrome.google.com/webstore/detail/selenide-for-selenium-ide/nlkfobhoffngaakgdbkdnmmjcchibcba">для Chrome</a></li>
<li><a href="https://addons.mozilla.org/ru/firefox/addon/selenide-for-selenium-ide/">для Firefox</a></li>
</ul>
<p>Мы скоро напишем об этом отдельную статью.</p>
<p>Большое спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за этот исторический для селенида момент!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.12.12020-05-25T00:00:00+00:00http://ru.selenide.org/2020/05/25/selenide-5.12.1
<p>Всем привет!</p>
<p>По горячим следам вы выпустили багфикс релиз <a href="https://github.com/selenide/selenide/milestone/97?closed=1">Selenide 5.12.1</a>
с парочкой мелких исправлений для <a href="/2020/05/23/selenide-5.12.0/">Selenide 5.12.0</a>.</p>
<h2 id="исправили-concurrent-modification-exception-при-инициализации-вебдрайвера">Исправили <em>Concurrent modification exception</em> при инициализации вебдрайвера</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/1170">issue 1170</a> и <a href="https://github.com/selenide/selenide/pull/1171">PR 1171</a>.</p>
<h2 id="исправили-мержинг-настройки-excludeswitches-разных-типов">Исправили мержинг настройки “excludeSwitches” разных типов</h2>
<p>Оказывается, настройку <code class="language-plaintext highlighter-rouge">excludeSwitches</code> можно задать и как массив, и как список:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">chromeOptions</span><span class="o">.</span><span class="na">setExperimentalOption</span><span class="o">(</span><span class="s">"excludeSwitches"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">String</span><span class="o">[]{</span><span class="s">"enable-automation"</span><span class="o">,</span> <span class="s">"load-extension"</span><span class="o">});</span>
<span class="n">chromeOptions</span><span class="o">.</span><span class="na">setExperimentalOption</span><span class="o">(</span><span class="s">"excludeSwitches"</span><span class="o">,</span> <span class="n">asList</span><span class="o">(</span><span class="s">"enable-automation"</span><span class="o">,</span> <span class="s">"load-extension"</span><span class="o">));</span>
</code></pre></div></div>
<p>Селенид 5.12.0 ломался, если задать вперемежку и так, и так. Починили.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1169">issue 1169</a> и <a href="https://github.com/selenide/selenide/pull/1174">PR 1174</a>.</p>
<h2 id="новости">Новости</h2>
<ul>
<li>Мы опубликовали <a href="https://github.com/selenide/selenide/wiki/Selenide-Roadmap">Selenide Roadmap</a>. Ждём ваших отзывов!</li>
<li>Видео с митапа <a href="https://www.youtube.com/watch?v=1d-nKyeTH2Y">pro:TEST</a> - Чехия, 28.04.2020</li>
<li>Видео с митапа <a href="https://www.youtube.com/watch?v=aFqZ6dbUJIw&feature=emb_logo">QA meetup</a> - Словакия, 12.05.2020</li>
<li>Всеволод Брекелов и Артём Ерошенко запустили шоу <a href="https://meetup.jugru.org/qa-survival-bias-1">“Ошибка выжившего”</a>. Я посмотрел первые 4 выпуска - годно!</li>
<li>Ребята и девчонки из jug.ru запустили шоу “Тяжелое утро с Heisenbug”.<br />
Ближайший выпуск будет <strong>со мной</strong>, ура-ура! <a href="https://meetup.jugru.org/qa-heisenbug-breakfast-2">27 мая 2020</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.12.02020-05-23T00:00:00+00:00http://ru.selenide.org/2020/05/23/selenide-5.12.0
<p>Всем привет!</p>
<p>Ура, релиз <a href="https://github.com/selenide/selenide/milestone/95?closed=1">Selenide 5.12.0</a>.</p>
<p>Большая часть изменений касается настроек браузеров.</p>
<h2 id="порешали-старинную-проблему-с-configurationbrowsercapabilities">Порешали старинную проблему с <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities</code></h2>
<p>Люди давно уже жаловались на то, что часть настроек в <code class="language-plaintext highlighter-rouge">ChromeOptions</code> теряется (при некоторых условиях).
Вызвана она была <a href="https://github.com/SeleniumHQ/selenium/issues/5279">старой багой в Selenium</a>, которой никто особо не занимается.
И мы не хотели ввязываться.</p>
<p>Но кажется, нам удалось найти простой костыль.</p>
<ul>
<li>Если вам помогло - делитесь.</li>
<li>Если не помогло - тем более делитесь, будем докостыливать дальше.</li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide/issues/676">issue 676</a>, <a href="https://github.com/selenide/selenide/issues/1097">issue 1097</a>
и <a href="https://github.com/selenide/selenide/pull/1155">PR 1155</a>.</p>
<p>Отдельное спасибо за попытки, которые не попали в релиз, но навели нас на итоговое решение:</p>
<ul>
<li>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1103">PR #1103</a></li>
<li>Спасибо <a href="https://github.com/SeleniumTestAB">SeleniumTestAB</a> за <a href="https://github.com/selenide/selenide/pull/1095">PR #1095</a></li>
</ul>
<h2 id="выключили-раздражающий-диалог-save-password">Выключили раздражающий диалог “save password?”</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/1133">issue 1133</a> и <a href="https://github.com/selenide/selenide/pull/1134">PR 1134</a>.</p>
<h2 id="добавили-режим-эмуляции-мобильника-в-гриде">Добавили режим “эмуляции мобильника” в гриде</h2>
<p>Как вы знаете, в Selenide 5.6.1 появилась возможность запускать хром в режиме “эмуляции мобильного браузера”:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span> <span class="o">-</span><span class="nc">Dchromeoptions</span><span class="o">.</span><span class="na">mobileEmulation</span><span class="o">=</span><span class="s">"deviceName=Nexus 5"</span>
</code></pre></div></div>
<p>Но эта опция срабатывала только при локальном запуске хрома, и не передавалась при запуске хрома в гриде.<br />
Теперь мы это починили: в грид передаются все настройки хрома, которые используются и при локальном запуске (кроме “папки для скачивания файлов”).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1109">issue 1109</a> и <a href="https://github.com/selenide/selenide/pull/1163">PR 1163</a>.</p>
<h2 id="упростили-сетап-firefox-теперь-без-профиля">Упростили сетап Firefox: теперь без профиля</h2>
<p>При открытии браузера Firefox селенид создавал ему профиль (<code class="language-plaintext highlighter-rouge">FirefoxProfile</code>) - как минимум, чтобы задать папку для скачивания файлов.<br />
Оказалось, что использование профиля накладывает некоторые ограничения, и вообще не нужно (вроде как когда-то он был нужен для legacy firefox driver).</p>
<p>Теперь мы по умолчанию обходимся без профиля:</p>
<ol>
<li>Папку для скачивания файлов создаём через <code class="language-plaintext highlighter-rouge">firefoxOptions.addPreference("browser.download.dir")</code></li>
<li>Создаём профиль, только если вы специально задали системные свойства, начинающиеся на <code class="language-plaintext highlighter-rouge">"firefoxprofile."</code>.</li>
</ol>
<p>См. <a href="https://github.com/selenide/selenide/issues/1139">issue 1139</a> и <a href="https://github.com/selenide/selenide/pull/1165">PR 1165</a>.</p>
<h2 id="задаём-настройку-accept_insecure_certs-для-версий-edge-построенных-на-движке-chromium">Задаём настройку <code class="language-plaintext highlighter-rouge">"ACCEPT_INSECURE_CERTS"</code> для версий Edge, построенных на движке chromium</h2>
<p>Начиная с какой-то версии, браузеры IE и Edge перестали поддерживать настройку <code class="language-plaintext highlighter-rouge">"ACCEPT_INSECURE_CERTS"</code>
(разрешение самоподписанных SSL сертификатов). И мы выпилили эту настройку для IE и Edge (в Selenide 5.9.0).</p>
<p>Но оказалось, что более поздние версии Edge, которые построены на движке Chromium, снова начали её поддерживать.<br />
Поэтому мы вернули настройку <code class="language-plaintext highlighter-rouge">"ACCEPT_INSECURE_CERTS"</code> для версий Edge 75 и выше.</p>
<p>Важно: версию Edge селенид может узнать, только если EdgeDriver был скачан с помощью WebDriverManager (что в селениде случается по умолчанию).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1093">issue 1093</a> и <a href="https://github.com/selenide/selenide/pull/1167">PR 1167</a>.</p>
<h2 id="обновились-на-webdrivermanager-400">Обновились на WebDriverManager 4.0.0</h2>
<ul>
<li>Поддержка Firefox 76</li>
<li>Поддержка Edge 81, 83, 84</li>
<li>Обновили Apache HttpClient с 4.x на 5.0</li>
</ul>
<p>См. <a href="https://github.com/selenide/selenide/pull/1149">PR 1149</a> и <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p>
<h2 id="обновились-на-browserup-proxy-core210">Обновились на browserup-proxy-core:2.1.0</h2>
<p>Ну мало ли.</p>
<h2 id="исправили-имя-папки-для-скриншота-emptymethod">Исправили имя папки для скриншота ‘emptyMethod’</h2>
<p>Проблема касалась только JUnit 5.</p>
<p>Спасибо <a href="https://github.com/dengayevskiy-sb">Denis Gaievsky</a> за <a href="https://github.com/selenide/selenide/pull/1138">PR 1138</a></p>
<h2 id="расставили-аннотации-nullable-и-nonnull">Расставили аннотации <code class="language-plaintext highlighter-rouge">@Nullable</code> и <code class="language-plaintext highlighter-rouge">@Nonnull</code></h2>
<p>Это поможет IDEA (и надеюсь, другим IDE) лучше подсвечивать косяки в ваших тестах. <br />
А также это поможет пользователям Kotlin правильно использовать nullable/non-nullable типы.</p>
<p>И снова спасибо <a href="https://github.com/jreznot">Yuriy Artamonov</a> за
<a href="https://github.com/selenide/selenide/pull/1140">PR 1140</a> и <a href="https://github.com/selenide/selenide/pull/1144">PR 1144</a>!</p>
<h2 id="новости">Новости</h2>
<ul>
<li>Мы опубликовали <a href="https://github.com/selenide/selenide/wiki/Selenide-Roadmap">Selenide Roadmap</a>. Ждём ваших отзывов!</li>
<li>Видео с митапа <a href="https://www.youtube.com/watch?v=1d-nKyeTH2Y">pro:TEST</a> - Чехия, 28.04.2020</li>
<li>Видео с митапа <a href="https://www.youtube.com/watch?v=aFqZ6dbUJIw&feature=emb_logo">QA meetup</a> - Словакия, 12.05.2020</li>
<li>Всеволод Брекелов и Артём Ерошенко запустили шоу <a href="https://meetup.jugru.org/qa-survival-bias-1">“Ошибка выжившего”</a>. Я посмотрел первые 4 выпуска - годно!</li>
<li>Ребята и девчонки из jug.ru запустили шоу “Тяжелое утро с Heisenbug”.<br />
Ближайший выпуск будет <strong>со мной</strong>, ура-ура! <a href="https://meetup.jugru.org/qa-heisenbug-breakfast-2">27 мая 2020</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.11.12020-04-21T00:00:00+00:00http://ru.selenide.org/2020/04/21/selenide-5.11.1
<p>Всем привет!</p>
<p>Похоже, релиз <a href="https://github.com/selenide/selenide/milestone/94?closed=1">Selenide 5.11.0</a> всё-таки сломал слишком
много устоев, и мы решили сбавить обороты. :)</p>
<p>Меняем ваше возмущение на <a href="https://github.com/selenide/selenide/milestone/96?closed=1">Selenide 5.11.1</a>.</p>
<h2 id="slf4j">SLF4J</h2>
<p>Народные массы возмутил тот факт, что в версии 5.11.0 селенид безусловно стал требовать правильной зависимости slf4j.
Народные массы не хотят настраивать slf4j и в гробу видали наши логи. :)</p>
<p>Для нас это было неожиданно, но мы идём навстречу трудящимся.</p>
<p>Теперь селенид требует slf4j не всегда, а только в тех редких случаях, когда без него точно никак.
А именно, если вы включите <a href="/2016/09/26/selenide-3.10/">фичу <code class="language-plaintext highlighter-rouge">TextReport</code></a>.</p>
<h2 id="because-we-can">because we can!</h2>
<p>В Selenide 5.11.0 мы сделали одну (почти незаметную) багу, связанную с использованием <code class="language-plaintext highlighter-rouge">because</code>. А именно,</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">$("blah").shouldNot(exist)</code> - не падает (это ок)</li>
<li><code class="language-plaintext highlighter-rouge">$("blah").shouldNot(exist.because("we can"))</code> - падает (а вот это не ок)</li>
</ul>
<p>Слово <code class="language-plaintext highlighter-rouge">because</code> оказалось несовместимым с отрицанием. Теперь мы это исправили, и обе строчки не падают.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1130">issue 1130</a> и <a href="https://github.com/selenide/selenide/pull/1131">1131</a>.</p>
<h2 id="сбросили-16-мегабайт">Сбросили 16 мегабайт</h2>
<p>Оказывается, среди зависимостей селенида затесался 16-мегабайтный файл <code class="language-plaintext highlighter-rouge">checker.jar</code>.<br />
Мы с лёгкостью от него избавились.</p>
<p>Спасибо <a href="https://github.com/jreznot">Yuriy Artamonov</a> за <a href="https://github.com/selenide/selenide/pull/1128">PR 1128</a>.</p>
<h2 id="новости">Новости</h2>
<p>Во вторник 28 апреля я буду выступать на чешском митапе <a href="https://www.meetup.com/protest_cz/events/270022839/">[pro:]TEST!</a><br />
Поскольку митап онлайн, участвовать будут все желающие. Можете позвать друзей, которые ещё не знают про селенид!</p>
<ul>
<li>Язык: ломаный английский</li>
<li>Дата: 28.04.2020, 18:00 GMT+2</li>
<li>Уровень: скорее для начинающих</li>
<li>Ссылки: <a href="https://bit.ly/protest84invitation">Анонс</a> / <a href="https://www.youtube.com/watch?v=1d-nKyeTH2Y&feature=youtu.be">Трансляция</a></li>
</ul>
<center>
<iframe width="560" height="315" src="https://www.youtube.com/embed/QcPE0hh9A-Y" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</center>
<p>Добро пожаловать!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.11.02020-04-19T00:00:00+00:00http://ru.selenide.org/2020/04/19/selenide-5.11.0
<p>Всем привет!</p>
<p>Мы выпустили <a href="https://github.com/selenide/selenide/milestone/94?closed=1">Selenide 5.11.0</a>.<br />
Это уже второй карантинный релиз Selenide.
И чтобы вам не засохнуть от скуки, мы сделали парочку существенных изменений.</p>
<h2 id="поменяли-поведение-shouldnot-проверок-для-несуществующего-элемента">Поменяли поведение <code class="language-plaintext highlighter-rouge">shouldNot*</code> проверок для несуществующего элемента</h2>
<p>Просто взгляните на следующую таблицу, чтобы понять, что поменялось.<br />
Предположим, что элемент <code class="language-plaintext highlighter-rouge">h1</code> <strong>не найден</strong> на странице.</p>
<table>
<thead>
<tr>
<th>Проверка</th>
<th>Selenide 5.10-</th>
<th>Selenide 5.11+</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.shouldNot(exist)</code></td>
<td>ok</td>
<td>ok</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.shouldNotBe(visible)</code></td>
<td>ok</td>
<td>ok</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.shouldBe(hidden)</code></td>
<td>ok</td>
<td>ok</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.shouldNotHave(text("foo"))</code></td>
<td>ok</td>
<td>FAIL</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.shouldNotHave(attribute("bar"))</code></td>
<td>ok</td>
<td>FAIL</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.find("h2").shouldNot(exist)</code></td>
<td>ok</td>
<td>ok</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">h1.find("h2").shouldNotHave(text("foo"))</code></td>
<td>ok</td>
<td>FAIL</td>
</tr>
</tbody>
</table>
<p><br /></p>
<h4 id="старая-логика">Старая логика</h4>
<p>Когда-то давно в селениде было принято такое решение: проверка <code class="language-plaintext highlighter-rouge">h1.shouldNotHave(text("foo"))</code> не должна падать:
нет элемента - нет и текста. Значит, условие “should not” выполнено.</p>
<p>Эту логику подкрепляло и такое соображение: проверка <code class="language-plaintext highlighter-rouge">h1.shouldHave(text("foo"))</code> валится, а “shouldNot” - её
отрицание, поэтому она валиться не должна.</p>
<h4 id="новая-логика">Новая логика</h4>
<p>Но победил прагматичный аргумент: старый алгоритм позволял слишком легко ошибиться: случайно написать неправильный
локатор и не заметить, что тест ложно зелёный.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/368">issue 368</a> и <a href="https://github.com/selenide/selenide/pull/1116">PR 1116</a>.</p>
<h2 id="теперь-селенид-ругается-если-slf4j-не-настроен">Теперь Селенид ругается, если SLF4J не настроен</h2>
<p>Если вы увидите такое сообщение:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">IllegalStateException</span><span class="o">:</span> <span class="no">SLF4J</span> <span class="n">is</span> <span class="n">not</span> <span class="n">configured</span><span class="o">.</span> <span class="nc">You</span> <span class="n">will</span> <span class="n">not</span> <span class="n">see</span> <span class="n">any</span> <span class="nc">Selenide</span> <span class="n">logs</span><span class="o">.</span>
<span class="nc">Please</span> <span class="n">add</span> <span class="n">slf4j</span><span class="o">-</span><span class="n">simple</span><span class="o">.</span><span class="na">jar</span><span class="o">,</span> <span class="n">slf4j</span><span class="o">-</span><span class="n">log4j12</span><span class="o">.</span><span class="na">jar</span> <span class="n">or</span> <span class="n">logback</span><span class="o">-</span><span class="n">classic</span><span class="o">.</span><span class="na">jar</span> <span class="n">to</span> <span class="n">your</span> <span class="n">classpath</span><span class="o">.</span>
<span class="nc">See</span> <span class="nl">https:</span><span class="c1">//github.com/selenide/selenide/wiki/slf4j</span>
</code></pre></div></div>
<p>то не пугайтесь - просто сделайте, что там сказано. Это очень просто.</p>
<h4 id="какую-проблему-мы-решали">Какую проблему мы решали?</h4>
<p>Проблема в том, что если у вас в проекте не была подключена никакая реализация SLF4J, то вы могли не увидеть какие-то
важные логи селенида, в т.ч. “текстовой отчёт”. Теперь вам придётся подключить какую-нибудь реализацию SLF4J.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1114">issue 1114</a> и <a href="https://github.com/selenide/selenide/pull/1115">PR 1115</a>.</p>
<h2 id="добавили-метод-для-частичной-проверки-атрибута">Добавили метод для частичной проверки атрибута</h2>
<p>До сих пор в селениде были методы для проверки</p>
<ol>
<li>наличия атрибута, и</li>
<li>точного значения атрибута:</li>
</ol>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#domain-container"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attribute</span><span class="o">(</span><span class="s">"class"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#domain-container"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attribute</span><span class="o">(</span><span class="s">"class"</span><span class="o">,</span> <span class="s">"container"</span><span class="o">));</span>
</code></pre></div></div>
<p>Теперь мы добавили метод <code class="language-plaintext highlighter-rouge">attributeMatching</code> для проверки <em>частичного</em> значения атрибута.<br />
См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/AttributeTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">canVerifyAttributeMatching</span><span class="o">()</span> <span class="o">{</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#domain-container"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attributeMatching</span><span class="o">(</span><span class="s">"class"</span><span class="o">,</span> <span class="s">"contain.*"</span><span class="o">));</span> <span class="c1">// class="container"</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#domain-container"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attributeMatching</span><span class="o">(</span><span class="s">"class"</span><span class="o">,</span> <span class="s">".*tainer"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#domain-container"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attributeMatching</span><span class="o">(</span><span class="s">"class"</span><span class="o">,</span> <span class="s">".+tain.+"</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/996">issue 996</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1100">PR 1100</a>.</p>
<h2 id="добавили-метод-для-получения-последнего-скриншота">Добавили метод для получения последнего скриншота</h2>
<p>Добавили два новых метода:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Screenshots.getLastThreadScreenshot()</code> - возвращает последний скриншот, сделанный селенидом в текущем потоке</li>
<li><code class="language-plaintext highlighter-rouge">Screenshots.getThreadScreenshots()</code> - возвращает все скриншоты, сделанные селенидом в текущем потоке</li>
</ul>
<p>Обычным пользователям <strong>эти методы ни к чему</strong>: селенид и так добавляет информацию о скриншоте к сообщению об ошибке.<br />
Но эти новые методы могут быть полезны тем, кто пишет какие-нибудь свои фреймворки на основе селенида или интегрирует селенид с
фреймворками типа Аллюра.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1029">issue 1029</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1125">PR 1125</a>.</p>
<h2 id="добавили-аннотацию-checkreturnvalue-к-большинству-публичных-методов-селенида">Добавили аннотацию <code class="language-plaintext highlighter-rouge">@CheckReturnValue</code> к большинству публичных методов селенида</h2>
<p>Это позволит IDE (как минимум Intellij IDEA) лучше анализировать код ваших тестов и предупреждать,
если вы вызвали какой-нибудь селенидовский метод, но забыли проверить результат:</p>
<center>
<img src="/images/2020/04/idea-warning.png" width="500" />
</center>
<p>Спасибо <a href="https://github.com/jreznot">Yuriy Artamonov</a> за <a href="https://github.com/selenide/selenide/pull/1106">PR 1106</a>.</p>
<h2 id="добавили-нехватающий-метод-selectorsbytagname">Добавили нехватающий метод <code class="language-plaintext highlighter-rouge">Selectors.byTagName()</code></h2>
<p>Просто чтобы было консистентно с <code class="language-plaintext highlighter-rouge">By</code>.</p>
<p>Спасибо <a href="https://github.com/jreznot">Yuriy Artamonov</a> за <a href="https://github.com/selenide/selenide/pull/1104">PR 1104</a>.</p>
<h2 id="исправили-url-скриншота">Исправили URL скриншота</h2>
<p>… когда проект запускается на дженкинсе, и в имени проекта есть пробел.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1072">issue 1072</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1098">PR 1098</a>.</p>
<h2 id="отключили-предупреждение-о-расширениях-в-chrome">Отключили предупреждение о расширениях в Chrome</h2>
<p>Честно говоря, я так и не понял, при каких условиях это предупреждение появляется - лично я его не видел:</p>
<center>
<img src="/images/2020/04/chrome-warning.png" width="300" />
</center>
<p>Но некоторые товарищи жаловались. Теперь мы его отключили вот такой настройкой:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">options</span><span class="o">.</span><span class="na">setExperimentalOption</span><span class="o">(</span><span class="s">"excludeSwitches"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">String</span><span class="o">[]{</span><span class="s">"enable-automation"</span><span class="o">,</span> <span class="s">"load-extension"</span><span class="o">});</span>
</code></pre></div></div>
<p>Дайте знать, если у вас это вызовет какие-то проблемы.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1119">issue 1119</a> и <a href="https://github.com/selenide/selenide/pull/1120">PR 1120</a>.</p>
<h2 id="теперь-можно-установить-selectormode-и-assertionmode-через-системные-свойства">Теперь можно установить <code class="language-plaintext highlighter-rouge">selectorMode</code> и <code class="language-plaintext highlighter-rouge">assertionMode</code> через системные свойства</h2>
<p>Никто этого не просил - просто для консистентности.</p>
<p>См. <a href="https://github.com/selenide/selenide/commit/231597eb6229e">коммит 231597eb6229e</a>.</p>
<h2 id="метод-getwrappedelement-больше-ничего-не-ждёт">Метод <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> больше ничего не ждёт</h2>
<p>Подозреваю, что большинство из вас не знали о существовании этого метода и тем более не использовали его.<br />
Его идея была в том, чтобы дать автору теста доступ к оригинальному селениумовскому <code class="language-plaintext highlighter-rouge">WebElement</code> без всякой селенидовской
магии (ну мало ли кому-то понадобится). А <a href="https://github.com/yashaka">Iakiv Kramarenko</a> заметил, что без селенидовской
магии не обошлось: метод <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> всё-таки ждал появления элемента.</p>
<p>Теперь он больше ничего не ждёт. Если элемента нет - он сразу кидает селениумовский <code class="language-plaintext highlighter-rouge">org.openqa.selenium.NoSuchElementException</code>.
Ну и легендарный <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code> вы тоже можете отхватить, конечно (ну мало ли кому-то захочется вспомнить молодость).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1015">issue 1015</a> и <a href="https://github.com/selenide/selenide/pull/1124">PR 1124</a>.</p>
<h2 id="статистика">Статистика</h2>
<p>И моё любимое: статистика скачиваний селенида. Мы пробили потолок в 130 тысяч в месяц!</p>
<center>
<img src="/images/2020/04/selenide.downloads.png" width="800" />
</center>
<p><br />
и 23 тысячи уникальных айпишников:</p>
<center>
<img src="/images/2020/04/selenide.unique-ips.png" width="800" />
</center>
<p>Жизнь хороша!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.10.02020-03-18T00:00:00+00:00http://ru.selenide.org/2020/03/18/selenide-5.10.0
<p>Здоровендос!</p>
<p>Шёл третий день карантина.</p>
<p>Чтобы вам не было одиноко, мы выпустили <a href="https://github.com/selenide/selenide/milestone/93?closed=1">Selenide 5.10.0</a> с
кучей улучшений, некоторые из которых даже окажутся капельку обратно несовместимыми. Ну, чтобы вы не скучали в своих берлогах.</p>
<ul class="blogpost-menu">
<li><a href="#add-shadow-dom-support">Добавили поддержку Shadow DOM</a></li>
<li><a href="#exclude-bup-by-default">не тянем BrowserUpProxy по умолчанию</a></li>
<li><a href="#replace-guava">Поменяли Guava API на Java API</a></li>
<li><a href="#add-quotes-to-report">Селенидовский отчёт в Allure красивее</a></li>
<li><a href="#add-image-condition">Добавили условие shouldBe(image)</a></li>
<li><a href="#video">Видосики</a></li>
</ul>
<h2 id="add-shadow-dom-support">Добавили поддержку Shadow DOM</h2>
<p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ShadowElementTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"#anyButton"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span>
<span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"p"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Inside Shadow-DOM"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"p"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">,</span> <span class="s">"#inner-shadow-host"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"The Shadow-DOM inside another shadow tree"</span><span class="o">));</span>
</code></pre></div></div>
<p><em>Firefox</em>: Вызов <code class="language-plaintext highlighter-rouge">setValue("test")</code> / <code class="language-plaintext highlighter-rouge">val("text")</code> на input элементе выкидывает ошибку “not reachable by keyboard”. <br />
Как временное решение, можно использовать <code class="language-plaintext highlighter-rouge">fastSetValue=true</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">fastSetValue</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
<span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"input"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">)).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"test"</span><span class="o">);</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1014">issue 1014</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1090">PR 1090</a>.</p>
<h2 id="exclude-bup-by-default">Selenide больше не тянет BrowserUpProxy по умолчанию</h2>
<p>Изначально это предложил Алексей Баранцев, так что бейте его, если что. :)</p>
<p>Мы сопоставили вместе два факта:</p>
<ol>
<li>Селенид по умолчанию тянет за собой BrowserUpProxy и его зависимости - всего ~17 мегабайт.</li>
<li>Большинство пользователей (наверное) не использует селенидовский прокси.</li>
</ol>
<p>и решили обрадовать Грету Тумберг и не качать эти 17 мегабайт по умолчанию.</p>
<h3 id="зависимость">Зависимость</h3>
<p>Те из вас, кто использует прокси, просто должны добавить в свой проект ещё одну зависимость:</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">testRuntime</span> <span class="s1">'com.browserup:browserup-proxy-core:2.0.1'</span>
</code></pre></div></div>
<p>(у многих из вас она и так уже есть).</p>
<p>Если вы забудете добавить зависимость - не беспокойтесь, вы увидите понятное сообщение:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">IllegalStateException</span><span class="o">:</span> <span class="nc">Cannot</span> <span class="n">initialize</span> <span class="n">proxy</span><span class="o">.</span> <span class="nc">Probably</span> <span class="n">you</span> <span class="n">should</span> <span class="n">add</span> <span class="nc">BrowserUpProxy</span> <span class="n">dependency</span> <span class="n">to</span> <span class="n">your</span> <span class="n">project</span><span class="o">.</span>
<span class="n">at</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">drivercommands</span><span class="o">.</span><span class="na">CreateDriverCommand</span><span class="o">.</span><span class="na">createDriver</span><span class="o">(</span><span class="nc">CreateDriverCommand</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">44</span><span class="o">)</span>
<span class="o">...</span>
<span class="n">at</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="nc">Selenide</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">41</span><span class="o">)</span>
<span class="n">caused</span> <span class="nl">by:</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">NoClassDefFoundError</span><span class="o">:</span> <span class="n">com</span><span class="o">/</span><span class="n">browserup</span><span class="o">/</span><span class="n">bup</span><span class="o">/</span><span class="nc">BrowserUpProxy</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1021">issue 1021</a> и <a href="https://github.com/selenide/selenide/pull/1094">PR 1094</a>.</p>
<p><strong>UPD 12.03.2023</strong>
В наше время больше не нужно добавлять ту зависимость <code class="language-plaintext highlighter-rouge">browserup-proxy-core</code>.
Просто используйте <code class="language-plaintext highlighter-rouge">com.codeborne:selenide-proxy</code> вместо <code class="language-plaintext highlighter-rouge">com.codeborne:selenide</code>, и все нужные зависимости подтянутся автоматически.</p>
<h3 id="просто-из-интереса">Просто из интереса</h3>
<p>Кто же там сжирает эти 17 мегабайт, спросите вы? А вот кто.<br />
Вот полный список файлов, которые должны у вас пропасть из проекта. Список впечатляет, правда?</p>
<ul>
<li>animal-sniffer-annotations-1.17.jar</li>
<li>barchart-udt-bundle-2.3.0.jar</li>
<li>bcpkix-jdk15on-1.62.jar</li>
<li>bcprov-jdk15on-1.62.jar</li>
<li>browserup-proxy-core-2.0.1.jar</li>
<li>browserup-proxy-mitm-2.0.1.jar</li>
<li>checker-qual-2.5.2.jar</li>
<li>dec-0.1.2.jar</li>
<li>dnsjava-2.1.9.jar</li>
<li>error_prone_annotations-2.2.0.jar</li>
<li>failureaccess-1.0.1.jar</li>
<li>guava-27.1-jre.jar</li>
<li>jackson-annotations-2.9.9.jar</li>
<li>jackson-core-2.9.9.jar</li>
<li>jackson-databind-2.9.9.1.jar</li>
<li>javassist-3.25.0-GA.jar</li>
<li>javax.activation-api-1.2.0.jar</li>
<li>jaxb-api-2.3.1.jar</li>
<li>jcl-over-slf4j-1.7.28.jar</li>
<li>jsr305-3.0.2.jar</li>
<li>jzlib-1.1.3.jar</li>
<li>listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar</li>
<li>littleproxy-2.0.0-beta-5.jar</li>
<li>netty-all-4.1.39.Final.jar</li>
</ul>
<p>Кто придумал название “animal-sniffer”? Это что вообще такое - <em>нюхальщик животных</em>?
<br /></p>
<h2 id="replace-guava">Поменяли Guava API на соответствующие Java API</h2>
<p>Мы просто поменяли</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">com.google.common.base.Predicate</code> из Guava</li>
<li>на <code class="language-plaintext highlighter-rouge">java.util.function.Predicate</code> из Java 8</li>
</ul>
<p>и выкинули Guava. Guava, ты была хороша и сделала много полезного (пока не вышла Java 8). Покойся с миром.</p>
<p>Если вы реализовали свои <code class="language-plaintext highlighter-rouge">CollectionCondition</code>, вам придётся метод <code class="language-plaintext highlighter-rouge">apply</code> переименовать в <code class="language-plaintext highlighter-rouge">test</code>. Это должно быть легко.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1091">issue 1091</a>.<br />
Спасибо <a href="https://github.com/wlsc">Wladimir Schmidt</a> за <a href="https://github.com/selenide/selenide/pull/1091">PR 1091</a>.</p>
<p><br /></p>
<h2 id="add-quotes-to-report">Сделали селенидовский отчёт в Allure чуточку красивее</h2>
<p>На самом деле просто добавили кавычки вокруг селекторов.<br />
Не представляю, зачем это может понадобиться, но теперь можно копировать селекторы из аллюровского отчёта и вставлять в
developer console браузера, и они будут работать.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1032">issue 1032</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1092">PR 1092</a>.</p>
<p><br /></p>
<h2 id="add-image-condition">Добавили условие <code class="language-plaintext highlighter-rouge">$("img").shouldBe(image)</code></h2>
<p>Позволяет проверить, что картинка есть, она загрузилась и всё в порядке.</p>
<p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ImageTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#valid-image img"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">image</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"#valid-image"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">image</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">image</span><span class="o">);</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1069">issue 1069</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1086">PR 1086</a>.</p>
<p><br /></p>
<h2 id="fix-search-by-attribute-with-quotes">Исправили поиск элементов по атрибуту, который содержит кавычки</h2>
<p>Я не знаю, каким надо быть извращенцем, чтобы в html атрибут запихать кавычки, но такие нашлись.
А селенид оказался к этому не готов и генерировал невалидный CSS локатор. Теперь это в прошлом.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1060">issue 1060</a>.<br />
Спасибо <a href="https://github.com/denysLystopadskyy">Denys Lystopadskyy</a> за <a href="https://github.com/selenide/selenide/pull/1062">PR 1062</a>.</p>
<p><br /></p>
<h2 id="video">Видосики</h2>
<p>Немногие задумывались об этом, но возможно, SeleniumCamp 2020 - Последняя Конференция Человечества.</p>
<p>Но они ещё и последние альтруисты человечества, потому что из-за карантина они досрочно выложили
<a href="https://www.youtube.com/playlist?list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe">все видео с последней конференции</a> в открытый доступ.<br />
Лечитесь, айтишники, и просвещайтесь!</p>
<p>Там есть три моих доклада:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=6MfMtky-0q4&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=35">Flaky tests: The method</a></li>
<li><a href="https://www.youtube.com/watch?v=RmaTYY3B-Wg&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=41">BOF: прошлое и будущее селенида</a></li>
<li><a href="https://www.youtube.com/watch?v=4vI4Z6sE7OA&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=16">Тройничок: Selenide для Web, Android и iOS</a> – “Толерантные локаторы”!</li>
</ul>
<p>И ещё из того, что я успел заметить:</p>
<ul>
<li>Aleksei Tiurin - <a href="https://www.youtube.com/watch?v=uCAva5bi7IY&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=32">Solving the problems of Espresso Android autotests</a></li>
<li>Michael Bodnarchuk - <a href="https://www.youtube.com/watch?v=yETWaC91t3w&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=4">Puppeteer is a new WebDriver? Secrets of flawless testing.</a></li>
<li>Oleksandr Khotemskyi - <a href="https://www.youtube.com/watch?v=UzdUu9QllK0&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=2">WebdriverIO + Puppeteer. Double gun – double fun</a></li>
<li>Sergey Pirogov - <a href="https://www.youtube.com/watch?v=lMD82Pj3Llk&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe">Test coverage myth busted</a></li>
<li>Mikalai Alimenkou - <a href="https://www.youtube.com/watch?v=O0-vAiqGrVk&list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&index=14">Static analysis tools as the best friend of QA</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.9.02020-03-10T00:00:00+00:00http://ru.selenide.org/2020/03/10/selenide-5.9.0
<p>Здоровендос!</p>
<p>По всему миру объявлена пандемия <a href="https://www.youtube.com/watch?v=jLG3RXECQU8">flaky тестов</a>, и мы ищем новые лекарства для борьбы с ними.</p>
<p>Сегодня мы выпустили <a href="https://github.com/selenide/selenide/milestone/92?closed=1">Selenide 5.9.0</a> с одной фичей, которая может помочь справиться с моргающими тестами.</p>
<h2 id="добавили-фильтр-для-скачивания-файлов-downloadfilefilter">Добавили фильтр для скачивания файлов: <code class="language-plaintext highlighter-rouge">$.download(FileFilter)</code></h2>
<h4 id="проблема">Проблема</h4>
<p>При скачивании файлов <em>через прокси</em> селенид может иногда скачать не тот файл.<br />
Селенид ведь как скачивает файлы: кликает на кнопку “Скачать” и перехватывает ответ сервера браузеру.</p>
<p>Но иногда в этот момент между браузером и сервером могут лететь какие-нибудь левые запросы, никак не связанные со скачиванием.
Например, хром решает проверить обновления. Или ваше приложение шлёт запросы в google analytics. Или просто какие-то фоновые запросы.
Это создаёт плодотворную почву для появления <a href="https://www.youtube.com/watch?v=jLG3RXECQU8">flaky тестов</a>, которые у вас на машине работают, а на дженкинсе иногда падают.</p>
<h4 id="решение">Решение</h4>
<p>Чтобы избежать таких коллизий, теперь вы можете явно указать, какой файл вы ждёте.<br />
Из коробки есть фильтры по имени и расширению файла, но вы можете создавать свои объекты <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/files/FileFilter.java"><code class="language-plaintext highlighter-rouge">FileFilter</code></a> с какими угодно критериями.<br />
И тогда из всех ответов сервера браузеру селенид выберет тот, который подходит под ваш фильтр.</p>
<p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadViaProxyTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">f1</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#downloadMe"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">withName</span><span class="o">(</span><span class="s">"hello_world.txt"</span><span class="o">));</span>
<span class="nc">File</span> <span class="n">f2</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#downloadMe"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">withNameMatching</span><span class="o">(</span><span class="s">"hello_.\\w+\\.txt"</span><span class="o">));</span>
<span class="nc">File</span> <span class="n">f3</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#downloadMe"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">timeout</span><span class="o">,</span> <span class="n">withExtension</span><span class="o">(</span><span class="s">"txt"</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1065">issue 1065</a> и <a href="https://github.com/selenide/selenide/pull/1080">PR 1080</a>.</p>
<h2 id="исправили-ошибку-при-старте-ie-3150">Исправили ошибку при старте IE 3.150</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/1061">issue 1061</a>.<br />
Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1075">PR 1075</a>.</p>
<h2 id="исправили-ошибку-при-старте-microsoft-edge">Исправили ошибку при старте Microsoft Edge</h2>
<p>См. <a href="https://github.com/selenide/selenide/issues/1039">issue 1039</a>.<br />
Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1084">PR 1084</a>.</p>
<h2 id="новости">Новости</h2>
<ul>
<li>
<p>Забавный <a href="https://twitter.com/titusfortner/status/1234862932036608001">диалог</a> получился в твиттере:
контрибьютор проектов Selenium и Watir Titus Fortner признался, что видел <a href="/2015/09/23/selenide-on-seleniumconf/">мой доклад про селенид на SeleniumConf</a> и спёр оттуда несколько
идей для Watir, а я признался, что некоторые вещи в селениде изначально были спёрты из Watir.</p>
</li>
<li>Хорошая статья <a href="https://phauer.com/2019/modern-best-practices-testing-java/">Modern Best Practices for Testing in Java</a>. Масса правильных мыслей.</li>
<li>Статья <a href="https://hackernoon.com/selenide-in-test-automation-through-selenoid-in-the-docker-container-ttw320f">Selenide Test Automation: Using Selenoid in the Docker Container</a></li>
<li>Статья <a href="https://medium.com/@neznajuskas/parametrized-ui-testing-with-selenide-and-junit-5-9aca75a8d62f">Parametrized UI testing with Selenide and Junit 5</a></li>
<li>Некий базовый проект <a href="https://github.com/romsper/qa-automation-base/tree/kotlin-junit5-appium">qa-automation-base</a>, в котором намешаны Kotlin + Selenide/Appium + JUnit 5 + Allure + Allure EE + TestRail.
<br /></li>
</ul>
<h2 id="статистика">Статистика</h2>
<p>Ну и на десерт - статистика скачиваний селенида. Растём!</p>
<center>
<img src="/images/2020/03/selenide.downloads.png" width="800" />
</center>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.8.02020-02-28T00:00:00+00:00http://ru.selenide.org/2020/02/28/selenide-5.8.0
<p>Приветос!</p>
<p>Мы подбили ещё пачку пуллреквестов и выпустили <a href="https://github.com/selenide/selenide/milestone/90?closed=1">Selenide 5.8.0</a>.</p>
<p>Какие же обновления нас ждут?</p>
<h2 id="упростили-создание-своих-условий-с-помощью-лямбд">Упростили создание своих условий с помощью лямбд</h2>
<p>В классе <code class="language-plaintext highlighter-rouge">Condition</code> появился новый метод <code class="language-plaintext highlighter-rouge">match</code>, который позволяет добавлять свои проверки, не создавая подклассов <code class="language-plaintext highlighter-rouge">Condition</code>.
Ему надо просто скормить лямбду.</p>
<p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ConditionsTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#multirowTable"</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">match</span><span class="o">(</span><span class="s">"border=1"</span><span class="o">,</span> <span class="n">el</span> <span class="o">-></span> <span class="n">el</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="s">"border"</span><span class="o">).</span><span class="na">equals</span><span class="o">(</span><span class="s">"1"</span><span class="o">)));</span>
</code></pre></div></div>
<p>Также появились похожие методы для коллекций <code class="language-plaintext highlighter-rouge">anyMatch</code>, <code class="language-plaintext highlighter-rouge">allMatch</code> и <code class="language-plaintext highlighter-rouge">noneMatch</code>.
См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/CollectionMethodsTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">anyMatch</span><span class="o">(</span><span class="s">"value==dog"</span><span class="o">,</span> <span class="n">el</span> <span class="o">-></span> <span class="n">el</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="s">"value"</span><span class="o">).</span><span class="na">equals</span><span class="o">(</span><span class="s">"dog"</span><span class="o">)));</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">allMatch</span><span class="o">(</span><span class="s">"value==cat"</span><span class="o">,</span> <span class="n">el</span> <span class="o">-></span> <span class="n">el</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="s">"value"</span><span class="o">).</span><span class="na">equals</span><span class="o">(</span><span class="s">"cat"</span><span class="o">)));</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">noneMatch</span><span class="o">(</span><span class="s">"value==bird"</span><span class="o">,</span> <span class="n">el</span> <span class="o">-></span> <span class="n">el</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="s">"value"</span><span class="o">).</span><span class="na">equals</span><span class="o">(</span><span class="s">"bird"</span><span class="o">)));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/662">issue 662</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1059">PR 1059</a>.</p>
<p><br /></p>
<h2 id="добавили-методы-sibling-и-preceding">Добавили методы <code class="language-plaintext highlighter-rouge">$.sibling()</code> и <code class="language-plaintext highlighter-rouge">$.preceding()</code></h2>
<p>… которые позволяют найти предшественников и последователей на том же уровне DOM.
Бывает нужно, когда удобных локаторов нет, а навигировать по дому хочется.</p>
<p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/SiblingTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#multirowTableFirstRow"</span><span class="o">).</span><span class="na">sibling</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">id</span><span class="o">(</span><span class="s">"multirowTableSecondRow"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">".second_row"</span><span class="o">).</span><span class="na">parent</span><span class="o">().</span><span class="na">preceding</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="s">"td"</span><span class="o">,</span> <span class="mi">0</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"first_row"</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/845">issue 845</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1064">PR 1064</a>.</p>
<p><br /></p>
<h2 id="запилили-поддержку-псевдо-элементов">Запилили поддержку псевдо-элементов</h2>
<p>Как многие знают, в HTML есть такие штуковины как псевдо-элементы: “:before”, “:after”, “:first-letter”, “:first-line”, “:selection”.
Они могут содержать важный текст и стили, которые иногда важно протестировать. Теперь вы можете это сделать.</p>
<p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/PseudoTest.java">в тестах</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">pseudo</span><span class="o">(</span><span class="s">":first-letter"</span><span class="o">,</span> <span class="s">"color"</span><span class="o">,</span> <span class="s">"rgb(255, 0, 0)"</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"abbr"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">pseudo</span><span class="o">(</span><span class="s">":before"</span><span class="o">,</span> <span class="s">"content"</span><span class="o">,</span> <span class="s">"\"beforeContent\""</span><span class="o">));</span>
<span class="err">$</span><span class="o">(</span><span class="s">"abbr"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">pseudo</span><span class="o">(</span><span class="s">":before"</span><span class="o">,</span> <span class="s">"\"beforeContent\""</span><span class="o">));</span>
</code></pre></div></div>
<p>А также можно спросить и <code class="language-plaintext highlighter-rouge">SelenideElement</code> значение псевдо-элемента (но мы такой способ не приветствуем):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">assertThat</span><span class="o">(</span><span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">pseudo</span><span class="o">(</span><span class="s">":first-letter"</span><span class="o">,</span> <span class="s">"color"</span><span class="o">)).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"rgb(255, 0, 0)"</span><span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="err">$</span><span class="o">(</span><span class="s">"abbr"</span><span class="o">).</span><span class="na">pseudo</span><span class="o">(</span><span class="s">":before"</span><span class="o">)).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"\"beforeContent\""</span><span class="o">);</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/994">issue 994</a>.<br />
Спасибо <a href="https://github.com/Denysss">Denys Shynkarenko</a> за <a href="https://github.com/selenide/selenide/pull/1045">PR 1045</a>.</p>
<p><br /></p>
<h2 id="исправили-softassertionsextension-для-junit5">Исправили SoftAssertionsExtension для JUnit5</h2>
<p>Если один из тестов падал, он помечал и все последующие тесты красным. Упс.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1071">issue 1071</a>.<br />
Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/commit/e92b250337a36a7225d6fcbdffecbf102f4592da">исправление</a>.</p>
<p><br /></p>
<h2 id="теперь-click-кликает-всегда-в-центр-элемента">Теперь <code class="language-plaintext highlighter-rouge">$.click()</code> кликает всегда в ЦЕНТР элемента</h2>
<p>В общем, история такая. Метод <code class="language-plaintext highlighter-rouge">$.click()</code> обычно кликал в центр элемента, НО
если у вас проставлена настройка <code class="language-plaintext highlighter-rouge">Configuration.clickViaJS=true</code>, он кликал в левый верхний угол.
Не то чтобы это было принципиально важно, но мало ли… Теперь он всегда кликает по центру.
На всякий случай. Чтобы всегда всё вело себя одинаково.</p>
<p>См. <a href="https://github.com/selenide/selenide/commit/106c53941c7188c5a19677ad45fbdea910960c73">коммит 106c53941c718</a>.</p>
<p><br /></p>
<h2 id="новости">Новости</h2>
<ul>
<li>
<p>Представляете, доклад Алексея Виноградова <a href="https://youtu.be/3J6mX98TSjk">Selenide: Брандашмыг — интерактивное путешествие по дорогам библиотеки</a>
занял 3 место в <a href="https://habr.com/ru/company/jugru/blog/489310/">ТОП-10 докладов Heisenbug 2019 Moscow</a>. Это успех!</p>
</li>
<li>Ничего себе! John C. Pratt создал <a href="https://chrome.google.com/webstore/detail/selenide-exporter-for-kat/mkbfcgpbkcaieiajhllpdocjfnfcbmlm">экспортер в Selenide для Katalon Recorder</a>
(работает с <a href="https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid">Katalon Recorder</a>). Это дикий успех!</li>
<li>Обнаружен <a href="https://github.com/razielsd/phpSelenide">phpSelenide</a> - порт Селенида на PHP. Это усPHPех!</li>
<li>И если кто ещё не видел, <a href="https://github.com/automician/selenejs">SeleneJS</a> - порт Селенида на JS. Это уех!</li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.7.02020-02-07T00:00:00+00:00http://ru.selenide.org/2020/02/07/selenide-5.7.0
<p>Приветос!</p>
<p>Неожиданно нас завалили пуллреквестами с кучей полезных изменений. В этом сила опенсорса!</p>
<p>Мы подбили всё это в кучу и выпустили <a href="https://github.com/selenide/selenide/milestone/89?closed=1">Selenide 5.7.0</a>.</p>
<h2 id="мы-добавили-новую-настройку-configurationdownloadsfolder">Мы добавили новую настройку <code class="language-plaintext highlighter-rouge">Configuration.downloadsFolder</code></h2>
<p>Раньше файлы скачивались в папку <code class="language-plaintext highlighter-rouge">build/reports</code> - в ту самую, где генерируются отчёты о прохождении тестов.<br />
А людям иногда хочется разделять (и властвовать?).<br />
Для них мы сделали отдельную настройку <code class="language-plaintext highlighter-rouge">Configuration.downloadsFolder</code> - именно туда теперь будут сохраняться файлы.</p>
<p>По умолчанию это папка <code class="language-plaintext highlighter-rouge">build/downloads</code>.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1025">issue 1025</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1041">PR 1041</a>.</p>
<p><br /></p>
<h3 id="скачиваем-файлы-в-configurationdownloadsfolder-вместо-downloads">Скачиваем файлы в <code class="language-plaintext highlighter-rouge">Configuration.downloadsFolder</code> вместо <code class="language-plaintext highlighter-rouge">~/Downloads</code></h3>
<p>Со скачиваниями файлов через прокси (<code class="language-plaintext highlighter-rouge">Configuration.fileDownload=PROXY</code>) есть ещё одна особенность.<br />
Селенид-то свои файлы скачивает в <code class="language-plaintext highlighter-rouge">build/reports</code> (а теперь в <code class="language-plaintext highlighter-rouge">build/downloads</code>), но сам-то браузер тоже скачивает свою
копию файла в папку <code class="language-plaintext highlighter-rouge">~/Downloads</code> (или что там у него по умолчанию). Во-первых, тратится лишнее место на диске, а во-вторых,
оттуда эти файлы никто автоматически не подчищает.</p>
<p>Теперь селенид изначально открывает браузер с такими настройками, чтобы он сразу скачивал файлы в папку <code class="language-plaintext highlighter-rouge">build/downloads</code>.</p>
<ol>
<li>Правда, пока только Chrome и Firefox.</li>
<li>И только в случае, когда селенид сам открывает браузер.</li>
</ol>
<p>См. <a href="https://github.com/selenide/selenide/issues/1057">issue 1057</a>.
Спасибо <a href="https://github.com/dkorobtsov">Dmitri Korobtsov</a> за ревью <a href="https://github.com/selenide/selenide/pull/1058">PR 1058</a>.</p>
<p><br /></p>
<h3 id="добавили-метод-для-переключения-между-окнами-с-кастомным-таймаутом">Добавили метод для переключения между окнами с кастомным таймаутом</h3>
<p>Как вы знаете, в селениде давно есть методы для переключения между вкладками/окнами:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">switchTo</span><span class="o">().</span><span class="na">window</span><span class="o">(</span><span class="mi">3</span><span class="o">);</span>
</code></pre></div></div>
<p>И этот метод даже настолько умный, что ждёт, пока окно появится. Но для него невозможно было задать таймаут:
пресловутые 4 секунды использовались и здесь.</p>
<p>Теперь мы добавили новый метод, в котором вторым аргументом можно задать таймаут для загрузки нового окна:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">switchTo</span><span class="o">().</span><span class="na">window</span><span class="o">(</span><span class="mi">3</span><span class="o">,</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">42</span><span class="o">));</span>
<span class="n">switchTo</span><span class="o">().</span><span class="na">window</span><span class="o">(</span><span class="mi">3</span><span class="o">,</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofMillis</span><span class="o">(</span><span class="mi">16000</span><span class="o">));</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/399">issue 399</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1054">PR 1054</a>.</p>
<p><br /></p>
<h3 id="добавили-логирование-атрибута-readonly">Добавили логирование атрибута “readonly”</h3>
<p>См. <a href="https://github.com/selenide/selenide/issues/990">issue 990</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1042">PR 1042</a>.</p>
<p><br /></p>
<h3 id="исправили-ошибку-indexoutofboundsexception">Исправили ошибку IndexOutOfBoundsException</h3>
<p>… при поиске первого/последнего элемента пустой коллекции</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/991">issue 991</a>.<br />
Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1043">PR 1043</a>.</p>
<p><br /></p>
<h3 id="и-целая-пачка-улучшений-скриншотов">И целая пачка улучшений скриншотов</h3>
<h4 id="1-вернули-потерянные-скриншоты-в-screenshotsgetlastscreenshot">1. Вернули потерянные скриншоты в <code class="language-plaintext highlighter-rouge">Screenshots.getLastScreenshot()</code></h4>
<p>См. <a href="https://github.com/selenide/selenide/issues/814">issue 814</a> и <a href="https://github.com/selenide/selenide/issues/880">issue 880</a>. <br />
Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1052">PR 1052</a>.</p>
<p><br /></p>
<h4 id="2-исправили-ссылки-на-скриншоты-в-jenkins">2. Исправили ссылки на скриншоты в Jenkins</h4>
<p>Теперь селенид умеет читать переменную среды (env variable) <code class="language-plaintext highlighter-rouge">BUILD_URL</code>, и вам больше не нужно прописывать <code class="language-plaintext highlighter-rouge">BUILD_URL</code> в system properties в ваших билд-скриптах.</p>
<p>Спасибо <a href="https://github.com/GongYi">GongYi</a> за <a href="https://github.com/selenide/selenide/pull/1049">PR 1049</a>.</p>
<p><br /></p>
<h4 id="3-исправили-ссылки-на-скриншоты-в-jenkins-для-мультимодульных-проектов-maven">3. Исправили ссылки на скриншоты в Jenkins для мультимодульных проектов Maven</h4>
<p>Спасибо <a href="https://github.com/GongYi">GongYi</a> за <a href="https://github.com/selenide/selenide/pull/1049">PR 1049</a>.</p>
<p><br /></p>
<h3 id="обновились-на-webdrivermanager-381">Обновились на WebDriverManager 3.8.1</h3>
<p>См. <a href="https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-3.8.1...master">changelog</a> (в т.ч. поддержка Edge 80).</p>
<p><br />
<br /></p>
<h1 id="мероприятия">Мероприятия</h1>
<h3 id="seleniumcamp-2020">SeleniumCamp 2020</h3>
<p>Приезжайте в Киев 21-22 февраля на конференцию <a href="https://seleniumcamp.com/program/">SeleniumCamp</a>! <br />
Я буду выступать с двумя докладами:</p>
<ul>
<li><a href="https://seleniumcamp.com/talk/flaky-tests-method/">Flaky tests: МЕТОД</a></li>
<li><a href="https://seleniumcamp.com/talk/selenide-for-web-android-and-ios/">Тройничок: Selenide для Web, Android и iOS</a></li>
</ul>
<p>и ещё будет неформальная сессия BOF <a href="https://seleniumcamp.com/talk/bof-glorious-past-and-promising-future-of-selenide/">про дальнейшие планы развития Селенида</a>.</p>
<h3 id="митап-про-селенид-в-германии">Митап про Селенид в Германии</h3>
<p>Какие-то черти запилили <a href="https://stugrm.de/stugrm-meetups/">митап про Селенид</a> в Германии 12 февраля.<br />
Приятно, чо.</p>
<h3 id="статистика">Статистика</h3>
<p>Количество скачиваний селенида за год выросло в 2.5 раза с 40 тысяч до 110 тысяч.</p>
<center>
<img src="/images/2020/02/selenide.downloads.png" width="800" />
</center>
<p><br /></p>
<p>А количество уникальных айпишников перевалило за 20 тысяч:</p>
<center>
<img src="/images/2020/02/selenide.unique-ips.png" width="800" />
</center>
<p><br /></p>
<p>Мы растём!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.6.12020-01-14T00:00:00+00:00http://ru.selenide.org/2020/01/14/selenide-5.6.1
<p>Всех с Новым Годом!</p>
<p>Новый год - новый релиз. Встречайте <a href="https://github.com/selenide/selenide/milestone/88?closed=1">Selenide 5.6.1</a>.</p>
<h1 id="добавили-метод-selenideexecuteasyncscript">Добавили метод <code class="language-plaintext highlighter-rouge">Selenide.executeAsyncScript()</code></h1>
<p>Нет такого человека, который ни разу не запускал бы метод <code class="language-plaintext highlighter-rouge">Selenide.executeJavaScript()</code>.
JavaScript позволяет выйти <a href="/2019/12/24/advent-calendar-javascript-tricks/">на новый уровень сумрака</a> в автоматизации.</p>
<p>А теперь мы добавили ещё и метод <code class="language-plaintext highlighter-rouge">Selenide.executeAsyncScript()</code>. Правда, я плохо представляю, в каких случаях он
может понадобится, но если кому надо - теперь он есть.</p>
<p>Обратите внимание, его использование сложнее, чем обычного <code class="language-plaintext highlighter-rouge">executeJavaScript()</code>.
После исполнения асинхронного JS кода нужно вызвать callback с результатом. А callback нужно получить из <em>последнего</em> аргумента:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">long</span> <span class="n">value</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Long</span><span class="o">)</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">executeAsyncJavaScript</span><span class="o">(</span>
<span class="s">"var callback = arguments[arguments.length - 1]; "</span> <span class="o">+</span>
<span class="s">"setTimeout(function() { "</span> <span class="o">+</span>
<span class="s">" // Вот тут любая асинхронная чертовщина: "</span> <span class="o">+</span>
<span class="s">" ... "</span> <span class="o">+</span>
<span class="s">" // и в конце возврат в селениум: "</span> <span class="o">+</span>
<span class="s">" callback(10);"</span> <span class="o">+</span>
<span class="s">"}, 5000);"</span>
<span class="o">);</span>
<span class="n">assertThat</span><span class="o">(</span><span class="n">value</span><span class="o">).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span>
</code></pre></div></div>
<p>См. <a href="https://github.com/selenide/selenide/issues/1030">issue 1030</a>.<br />
Спасибо <a href="https://github.com/tyge68">Thierry Ygé</a> за <a href="https://github.com/selenide/selenide/pull/1031">PR 1031</a>.</p>
<p><br /></p>
<h1 id="научили-selenide-скачивать-через-прокси-файлы-без-заголовка-content-disposition">Научили Selenide скачивать через прокси файлы без заголовка <code class="language-plaintext highlighter-rouge">Content-Disposition</code></h1>
<p>Как вы знаете, Selenide умеет скачивать файлы через свой прокси.
Но при скачивании он перехватывал только те ответы сервера, в которых присутствует заголовок <code class="language-plaintext highlighter-rouge">Content-Disposition</code>
(чтобы узнать оттуда имя скачиваемого файлы).</p>
<p>Как выяснилось, этот заголовок необязателен. Файлы могут скачиваться и без него.</p>
<p>Теперь селенидовский прокси стал умнее.</p>
<ol>
<li>Прежде чем скачать файл, он ждёт, пока закончатся все предыдущие запросы-ответы между браузером и сервером.</li>
<li>Кликает кнопку скачивания</li>
<li>Перехватывает ВСЕ запросы-ответы между браузером и сервером (вне зависимости от заголовков).</li>
<li>И пытается понять, какой из них больше всего похож на скачивание файла.</li>
</ol>
<p>А имя файла (в случае ответа без заголовка <code class="language-plaintext highlighter-rouge">Content-Disposition</code>) берётся просто из URL.</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1034">issue 1034</a> и <a href="https://github.com/selenide/selenide/pull/1035">PR 1035</a>.</p>
<p><br /></p>
<h1 id="исправили-метод-webdriverrunnerusing">Исправили метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.using()</code></h1>
<p>В октябре мы <a href="/2019/10/16/selenide-5.4.0/">добавили метод <code class="language-plaintext highlighter-rouge">using</code></a>.<br />
Судя по всему, вы ещё не успели его попробовать, потому что никто не пожаловался на багу: этот метод закрывал
вебдрайвер после использования (хотя не должен). Ну вот, эту багу мы исправили.</p>
<p>См. <a href="https://github.com/selenide/selenide/commit/4d1b19972d">коммит 4d1b19972d</a>.</p>
<p><br /></p>
<h1 id="обновились-на-webdrivermanager-380">Обновились на WebDriverManager 3.8.0</h1>
<p>там было исправлено несколько ошибок, в т.ч. моего авторства :)</p>
<p>См. <a href="https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-3.8.0...master">changelog</a>.<br />
В частности, WDM теперь должен корректно работать без доступа в интернет.</p>
<p><br /></p>
<h2 id="новости">Новости</h2>
<ul>
<li>Огонь! <a href="https://vitalyzinevich.visualstudio.com/_git/Selenious">Selenious</a> - порт селенида на .NET<br />
Ребята обещали, что проект рабочий, они его в реальном проекте используют.</li>
<li>Статья от LambdaTest: <a href="https://www.lambdatest.com/support/docs/selenide-tests-with-lambdatest-online-selenium-grid-for-automated-cross-browser-testing/">Selenide Tests With LambdaTest – Online Selenium Grid For Automated Cross Browser Testing</a></li>
<li>Моё видео с октябрьской конференции Cyprus Quality Conference <a href="https://youtu.be/Y04rU7qV7Vg">Threesome: Selenide for Web, Android and iOS</a>.
Не переживайте, в феврале я его буду рассказывать по-русски на SeleniumCamp.</li>
<li>Если кто пропустил, серия постов <a href="/blog">Selenide Advent Calendar</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Вышла Selenide 5.6.02019-12-26T00:00:00+00:00http://ru.selenide.org/2019/12/26/selenide-5.6.0
<p>Всем привет!</p>
<p>Под конец года мы выпустили <a href="https://github.com/selenide/selenide/milestone/87?closed=1">Selenide 5.6.0</a> с одним обновлением.</p>
<p>Мы поменяли <code class="language-plaintext highlighter-rouge">BrowserMobProxy</code> (который больше не поддерживается) на его форк <code class="language-plaintext highlighter-rouge">BrowserUpProxy</code> (текущая версия 2.0.1).</p>
<p>См. <a href="https://github.com/selenide/selenide/issues/1019">issue 1019</a>.<br />
Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1020">PR 1020</a>.</p>
<h2 id="что-хорошего-в-этом-browserupproxy">Что хорошего в этом <code class="language-plaintext highlighter-rouge">BrowserUpProxy</code>?</h2>
<p>Он</p>
<ul>
<li>Поддерживает Brotli Compression (а не только gzip)</li>
<li>Поддерживает HTTP/2</li>
<li>Основан на поддерживаемом форке <a href="https://github.com/mrog/LittleProxy">LittleProxy</a></li>
<li>Использует какой-то улучшенный <a href="https://github.com/sdstoehr/har-reader">HAR reader</a></li>
<li>Умеет фильтровать записи в HAR</li>
<li>Поддерживает версионированные заголовки для JSON типа <code class="language-plaintext highlighter-rouge">Content-Type=application/something-v1+json</code></li>
<li>Имеет встроенные ассерты для сетевых запросов (что это вообще?)</li>
</ul>
<p>Полный список изменений <code class="language-plaintext highlighter-rouge">BrowserUpProxy</code> (по сравнению с BrowserMobProxy) есть <a href="https://github.com/browserup/browserup-proxy/blob/master/CHANGELOG.md">тут</a>.</p>
<h2 id="как-нам-обновиться">Как нам обновиться?</h2>
<p>В большинстве случаев вам не придётся ничего менять. Всё работает как работало. <br />
Изменения потребуются только в двух случаях:</p>
<h5 id="1-если-вы-явно-импортировали-bmp-то-вам-нужно-поменять-зависимость">1. Если вы явно импортировали BMP, то вам нужно поменять зависимость</h5>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">net</span><span class="o">.</span><span class="na">lightbody</span><span class="o">.</span><span class="na">bmp</span><span class="o">:</span><span class="n">browsermob</span><span class="o">-</span><span class="nl">core:</span><span class="mf">2.1</span><span class="o">.</span><span class="mi">5</span>
</code></pre></div></div>
<p>на</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">com</span><span class="o">.</span><span class="na">browserup</span><span class="o">:</span><span class="n">browserup</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="nl">core:</span><span class="mf">2.0</span><span class="o">.</span><span class="mi">1</span>
</code></pre></div></div>
<h5 id="2-если-вы-определяли-requestfilter-или-responsefilter">2. Если вы определяли <code class="language-plaintext highlighter-rouge">RequestFilter</code> или <code class="language-plaintext highlighter-rouge">ResponseFilter</code>,</h5>
<p>то вам придётся поменять импорт</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">net.lightbody.bmp.*</span><span class="o">;</span>
</code></pre></div></div>
<p>на</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">com.browserup.bup.*</span><span class="o">;</span>
</code></pre></div></div>
<p>И всё.</p>
<p><br /></p>
<h2 id="новости">Новости</h2>
<p>Завезли видео с сентябрьской конференции QA Fest:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=be_cTwayRQc">Андрей Солнцев. Selenide для профи</a></li>
<li><a href="https://www.youtube.com/watch?v=pln38fIbYqA&t=226s">Андрей Солнцев. Десять причин моей ненависти</a></li>
<li>И остальные <a href="https://www.youtube.com/playlist?list=PLuOBDBq7MW70q24thB9tidD2-2Tysf8FS">видео QA Fest 2019</a></li>
<li>Гугловская статья про <a href="https://testing.googleblog.com/2019/12/testing-on-toilet-tests-too-dry-make.html">принципы DAMP и DRY</a></li>
<li>Ещё один фреймворк на базе Selenide: <a href="https://www.justtestlah.qa/">JustTestLah! (JTL)</a> - Помесь BDD, Selenide, Appium для Android, iOS и Web</li>
<li>Если кто пропустил, серия постов <a href="/blog">Selenide Advent Calendar</a></li>
</ul>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Трюки с JavaScript2019-12-24T00:00:00+00:00http://ru.selenide.org/2019/12/24/advent-calendar-javascript-tricks
<p>Привет!</p>
<p>На дворе 24 декабря, католическое рождество. А это значит, что Advent Calendar подошёл к концу.</p>
<p>И напоследок мы поиграемся с JavaScript.</p>
<p>Как язык JavaScript, конечно, дно, но он даёт большие возможности при написании автотестов.<br />
Он позволяет залезть в такие дыры, куда с обычным вебдрайвером и не снилось.</p>
<p>Приведу несколько примеров из реальных проектов.</p>
<h2 id="выбрать-дату">Выбрать дату</h2>
<p>Есть масса всевозможных элементов для выбора даты - т.н. “date picker”. И выбрать в них нужную дату - это вечная головная боль.</p>
<p>Если реализовывать такой метод в лоб:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="n">setDateByName</span><span class="o">(</span><span class="s">"recurrent.startDate"</span><span class="o">,</span> <span class="s">"16.01.2009"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Придётся сделать примерно следующие шаги:</p>
<ol>
<li>Тыкнуть иконку “календарик”</li>
<li>Тыкнуть год</li>
<li>Тыкнуть стрелку “месяц назад” (сколько раз?)</li>
<li>Тыкнуть день</li>
<li>Ой, фсё, сегодня 29 февраля. Тест упал.</li>
</ol>
<p><strong>Долго, сложно, ненадёжно.</strong></p>
<p><br /></p>
<h4 id="а-вот-как-этот-метод-можно-реализовать-с-помощью-js">А вот как этот метод можно реализовать с помощью JS:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">setDateByName</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">,</span> <span class="nc">String</span> <span class="n">date</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executeJavaScript</span><span class="o">(</span>
<span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"$('[name=\"%s\"]').val('%s')"</span><span class="o">,</span>
<span class="n">name</span><span class="o">,</span> <span class="n">date</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>Быстро и надёжно.</strong></p>
<p>Возможно, вам смутит, что это не совсем “честный” способ. Но об этом мы поговорим в конце. Не переключайтесь.</p>
<h2 id="спрятать-календарик">Спрятать календарик</h2>
<p>Допустим, календарик всё же открылся. Как его спрятать?
Решение в лоб - тыкнуть крестик в углу. Опять же, это медленно и ненадёжно:</p>
<ul>
<li>крестик вечно располагается в разных углах</li>
<li>расположение крестика часто меняется в зависимости от дизайна, размера страницы и т.п.</li>
<li>иногда календарик открывается не сразу - нужно добавить ожидание на открытие, чтобы тут же закрыть.</li>
</ul>
<p>Идиотская ситуация.</p>
<p><br /></p>
<h4 id="а-вот-как-этот-метод-можно-реализовать-с-помощью-js-1">А вот как этот метод можно реализовать с помощью JS:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">executeJavaScript</span><span class="o">(</span>
<span class="s">"$('.datepicker').hide();"</span>
<span class="o">);</span>
</code></pre></div></div>
<p><strong>Быстро и надёжно.</strong></p>
<h2 id="перелистнуть-перелистывалку">Перелистнуть перелистывалку</h2>
<p>Представим себе страницу, на которой есть “перелистывалка” карт. Нужно выбрать нужную карту, двигая наманикюренным пальчиком.<br />
<em>Как сэмулировать это селениумом?</em><br />
Можно, конечно. Всякие там Drag’n’Drop, Actions. Нажал, потянул, отпустил. Но всё это <strong>медленно и нестабильно</strong>.<br />
Это легко может сломаться от малейших изменений дизайна, изменения размерна окна браузера, потери фокуса и т.д.</p>
<h4 id="а-вот-как-можно-с-помощью-js">А вот как можно с помощью JS:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">selectAccount</span><span class="o">(</span><span class="nc">String</span> <span class="n">accountId</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executeJavaScript</span><span class="o">(</span>
<span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"$('[data-account-id=\"%s\"]').attr('data-card-account', 'true')"</span><span class="o">,</span> <span class="n">accountId</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Это нетривиально. Пришлось поизучать код этой “перелистывалки” и понять, какой JS код она дёргает при перелистывании.<br />
И дёрнуть из теста аналогичный JS код. Придётся поработать головой, но зато это <strong>быстро и надёжно.</strong></p>
<h2 id="выбрать-опцию-в-bootstrap-select">Выбрать опцию в bootstrap select</h2>
<p>Многие UI фреймворки заменяют стандартный <code class="language-plaintext highlighter-rouge"><select></code> на какие-то свои самодельные супер-пупер красивые/динамичные/удобные элементы,
сварганенные из нагромождения <code class="language-plaintext highlighter-rouge"><div></code>, <code class="language-plaintext highlighter-rouge"><span></code>, <code class="language-plaintext highlighter-rouge"><li></code> и т.п. с кучей разных CSS-классов и стилей. Для автоматизатора это всегда боль.</p>
<p>Один из таких фреймворков - Bootstrap. И в нём тоже есть свой <code class="language-plaintext highlighter-rouge"><select></code>. Мы долго пытались научиться выбирать из него значения:
тыкали на один div, ждали появления следующего span, тыкали на него, искали нужный div с правильными атрибутами… Всё это работало долго и
часто падало на дженкинсе. Причин падения так до конца и не удалось понять. Хотя мы честно пытались.</p>
<p>В итоге мы реализовали метод</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="n">selectBootstrap</span><span class="o">(</span><span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">name</span><span class="o">(</span><span class="s">"operationCode"</span><span class="o">)),</span> <span class="s">"11100"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>с помощью JavaScript:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">selectBootstrap</span><span class="o">(</span><span class="nc">WebElement</span> <span class="n">select</span><span class="o">,</span> <span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executeJavaScript</span><span class="o">(</span>
<span class="s">"$(arguments[0]).val(arguments[1]).trigger('change')"</span><span class="o">,</span>
<span class="n">select</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p><strong>Быстро и надёжно.</strong></p>
<p>А с помощью <a href="https://ru.selenide.org/2019/09/02/selenide-5.3.0/">метода <code class="language-plaintext highlighter-rouge">execute</code></a> это можно написать ещё красивее:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">name</span><span class="o">(</span><span class="s">"operationCode"</span><span class="o">)).</span><span class="na">execute</span><span class="o">(</span><span class="n">selectBootstrap</span><span class="o">(</span><span class="s">"11100"</span><span class="o">));</span>
</code></pre></div></div>
<h2 id="слайдер">Слайдер</h2>
<p>На странице слайдер. Можно таскать ползунок туда-сюда от 0 до 100.<br />
Как это сделать в тесте?</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="n">setMaxYearlyFee</span><span class="o">(</span><span class="mi">100</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Тут даже классический Drag’n’Drop не поможет, потому что нет целевого элемента, куда перетаскивать.<br />
Снова Actions, снова нажал-потянул-отпустил (по координатам?), снова <strong>медленно и нестабильно</strong>.</p>
<h4 id="а-можно-с-помощью-js">А можно с помощью JS:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">setMaxYearlyFee</span><span class="o">(</span><span class="kt">int</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executeJavaScript</span><span class="o">(</span>
<span class="s">"$('#sld').data('slider').value[0] = arguments[0];"</span> <span class="o">+</span>
<span class="s">"$('#sld').triggerHandler('slide');"</span><span class="o">,</span>
<span class="n">value</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Снова пришлось поковыряться в коде, чтобы узнать, какой JS код дёргает слайдер. Зато работает. Зато <strong>быстро и надёжно.</strong></p>
<h2 id="заигнорить-чёртов-confirm">Заигнорить чёртов confirm</h2>
<p>В Selenide есть удобные методы, чтобы нажать “Ok” или “Cancel” в модальном диалоге <code class="language-plaintext highlighter-rouge">alert</code>, <code class="language-plaintext highlighter-rouge">prompt</code> или <code class="language-plaintext highlighter-rouge">confirm</code>.<br />
Но с этими модальными окошками периодически возникает проблемы. Иногда они почему-то не закрываются, и после этого
вебдрайвер не может ничего сделать с браузером, в том числе снять скриншот. Подробнее об этом я рассказывал в видео
<a href="https://www.youtube.com/watch?v=jLG3RXECQU8">Flaky tests</a>.</p>
<h4 id="и-снова-на-помощь-приходит-javascript">И снова на помощь приходит JavaScript!</h4>
<p>Вот так можно “типа нажать Ok” в любом <code class="language-plaintext highlighter-rouge">confirm</code> диалоге:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">mockConfirm</span><span class="o">()</span> <span class="o">{</span>
<span class="n">executeJavaScript</span><span class="o">(</span>
<span class="s">"window.confirm = function() {return true;};"</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="плагины-cordova">Плагины Cordova</h2>
<p>В одном проекте мы писали гибридное мобильное приложение с помощью фреймворка Cordova.<br />
Он создаёт такое приложение, в котором очень маленькая нативная часть запускает браузер (т.н. WebView), а в нём уже открывает
веб-приложение. А доступ к нативным функциями мобильника (контакты, телефон, геолокация и пр.) предоставляет в виде JavaScript-плагинов.</p>
<p>Это очень удобно для тестирования. Для запуска автотестов не нужны реальные мобильники, достаточно открыть урл в обычном
браузере (можно ещё и в режиме <a href="https://ru.selenide.org/2019/11/29/selenide-5.5.1/">эмуляции мобильника</a> - вообще не отличишь).</p>
<p>Но вот как тестировать функционал, требующий доступа к нативным функциям мобильника?</p>
<p>И снова на помощь приходит JS. Мы можем эмулировать плагины Cordova, управляя из теста их поведением:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="n">mockContactsAPI</span><span class="o">(</span><span class="s">"+79110080075"</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">mockContactsAPI</span><span class="o">(</span><span class="nc">String</span> <span class="n">number</span><span class="o">)</span> <span class="o">{</span>
<span class="n">executeJavaScript</span><span class="o">(</span>
<span class="s">"window.plugins = {"</span> <span class="o">+</span>
<span class="s">" contactNumberPicker: { "</span> <span class="o">+</span>
<span class="s">" pick: function(callback) {"</span> <span class="o">+</span>
<span class="s">" callback({"</span> <span class="o">+</span>
<span class="s">" phoneNumber:\""</span> <span class="o">+</span> <span class="n">number</span> <span class="o">+</span> <span class="s">"\""</span>
<span class="s">" });}}}"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Выражение “управлять поведением” кажется диким?
Тогда посмотрите видео <a href="https://www.youtube.com/watch?v=ePvrXUCeAr8">Arrange, mazafaka!</a> с конференции SeleniumCamp 2018.</p>
<p>А мы имеем функцию <code class="language-plaintext highlighter-rouge">mockContactsAPI</code>, которая позволяет “типа выбрать” нужный номер из “типа адресной книги” “типа телефона”.<br />
Собственно, это единственный способ покрыть тестами все возможные случаи: и когда номер найден, и когда не найден, и когда
это дубликат, и с 100500 китайскими символами и бог знает какие ещё. <strong>Быстро и надёжно.</strong></p>
<h2 id="но-это-же-не-по-настоящему">Но это же не по-настоящему?</h2>
<p>Я знаю, у многих из вас полыхает мысль: “Это же фейк, обман, это не по-настоящему! Тесты должны делать так же, как реальный
пользователь - иначе есть риск, что тест не обнаружит реальную ошибку.”</p>
<p>Понимаю.</p>
<p>Но возражу.</p>
<blockquote>
<p>Быстрые и надёжные тесты
лучше, чем
“реалистичные” (но медленные и нестабильные) тесты.</p>
</blockquote>
<ul>
<li>Если при каждом запуске треть ваших тестов падает.</li>
<li>Если каждый раз вы тратите полдня на разбор упавших тестов.</li>
<li>Если заставляете ручников прогонять вручную “автоматические” сценарии, чтобы убедиться, что функционал не сломан (и это было просто случайное падение).</li>
<li>Если вы заполняете эксельку с результатами упавших-не-по-настоящему тестов.</li>
<li>Если в компании нет доверия к тестам.</li>
</ul>
<p><em>То какой к чёрту прок от ваших “настоящих” тестов?</em><br />
Один вред.</p>
<ul>
<li>Лучше пусть мои тесты будут быстрыми и стабильными.</li>
<li>И с лёгкостью проверять разные сложные случаи, которые “по-настоящему” повторить нереально.</li>
<li>А я спокойно буду жить с пониманием того, что целью автоматизации никогда и не было <em>автоматизировать абсолютно всё</em>.</li>
</ul>
<p><br /></p>
<p>Вы всё ещё хотите возразить:</p>
<h3 id="и-всё-таки-моей-душе-спокойнее-когда-всё-по-настоящему">И всё-таки моей душе спокойнее, когда всё по-настоящему.</h3>
<p>Спешу вас огорчить.</p>
<ul>
<li>Ваши “реалистичные” тесты на Selenium WebDriver по-любому <em>не настоящие</em>. Они работают не так, как реальный пользователь.
Вебдрайвер формирует http-запрос на каждую вашу команду и даже - вот сюрприз! - запускает некую логику на JavaScript, чтобы определить видимость элементов и т.п.</li>
<li>О да, в некотором смысле вызов действия через JavaScript даже “реалистичнее”, чем через WebDriver. Это ближе к тому, что на самом деле делает браузер.</li>
<li>И даже ваши ручные тестировщики, прокликивающие ваши сценарии - <em>не настоящие!</em> Они работают не так, как реальный пользователь.</li>
</ul>
<p>Живите теперь с этим. :)</p>
<h2 id="что-теперь">Что теперь?</h2>
<p>А всё. Это был последний пост рождественского календаря. Уффф!</p>
<p>Скажу честно: я всё это затеял, чтобы набить руку и приучиться много писать.
Надеюсь, это поможет в <a href="https://selenide.org/selenide-site-ng/">обновлении сайта</a> и написании документации.
А там, глядишь, и до книги дойдёт. :)</p>
<p>Вот такие планы на следующий год.</p>
<p>С наступающим!</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Defaŭlta lingvo2019-12-22T00:00:00+00:00http://ru.selenide.org/2019/12/22/advent-calendar-defaulta-lingvo
<h1 id="defaŭlta-lingvo">Defaŭlta lingvo</h1>
<p>Название сегодняшней темы пришло из языка Эсперанто и означает “язык по умолчанию”.</p>
<p>Вы могли заметить, что некоторые веб-приложения и сайты автоматически меняют свой язык в зависимости от настроек вашего браузера или вашего местоположения.</p>
<h2 id="проблема">Проблема</h2>
<p>В том случае, когда у вас в команде интернационал разработчиков, которые пишут и запускают тесты на разных компьютерах, вы могли обратить внимание, что иногда одни и те же тесты начинают падать из-за того, приложение запустилось не на том языке, для которого писались тесты.</p>
<p>Если приложение выбирает язык в зависимости от местоположения пользователя, то писать стабильные тесты запускающиеся в разных странах будет непросто. Зато, если приложение всего лишь смотрит в браузере на язык предпочитаемый пользователем по умолчанию, задача сильно упрощается.</p>
<h2 id="решение">Решение</h2>
<p>Итак, допустим у вас есть тесты, написанные для языка, другого чем тот, который является языком вашего браузера по умолчанию. Например - ваши тесты ожидают <em><strong>немецкий</strong></em>.</p>
<p>Теперь у вас есть следующие опции:</p>
<ul>
<li>Поменяйте язык по умолчанию вашей системы. Теперь большинство ваших программ на компьютере заговорят по-немецки. <em><strong>Ordnung muss sein!</strong></em></li>
<li>Поменяйте в вашем браузере порядок языков так, чтобы самым предпочитаемым стал немецкий. Сохраните профиль браузера. С помощью гугла, напильника и удачи сконфигурируйте тесты так, чтобы профиль загружался перед запуском каждого теста. Да, не забудьте удалить немецкий из топа в списке предпочитаемых языков, иначе, ну вы поняли - <em><strong>Ordnung….</strong></em></li>
<li>Ну или - просто воспользуйтесь Chrome preference “intl.accept_languages” установив её значение на “de” (немецкий язык).</li>
</ul>
<p>Разумеется, вы можете легко сделать это в Selenide.
Установите значение системной переменной <code class="language-plaintext highlighter-rouge">chromeoptions.prefs=intl.accept_languages=de</code> или в коде:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">System</span><span class="o">.</span><span class="na">setProperty</span><span class="o">(</span><span class="s">"chromeoptions.prefs"</span><span class="o">,</span><span class="s">"intl.accept_languages=de"</span><span class="o">);</span>
</code></pre></div></div>
<p>или, лучше, в конфигурационных файлах Maven или Gradle</p>
<h3 id="maven">Maven</h3>
<p>maven <code class="language-plaintext highlighter-rouge">pom.xml</code></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
<span class="nt"><plugin></span>
<span class="nt"><artifactId></span>maven-surefire-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.xx.yy<span class="nt"></version></span>
<span class="nt"><configuration></span>
<span class="nt"><systemPropertyVariables></span>
...
<span class="nt"><chromeoptions.prefs></span>intl.accept_languages=de<span class="nt"></chromeoptions.prefs></span>
<span class="nt"></systemPropertyVariables></span>
<span class="nt"></configuration></span>
<span class="nt"></plugin></span>
...
</code></pre></div></div>
<h3 id="gradle">Gradle</h3>
<p>аналогично для Gradle в <code class="language-plaintext highlighter-rouge">gradle.properties</code> (вам придётся еще добавить строчку-другую в <code class="language-plaintext highlighter-rouge">build.gradle</code> чтобы передать эти параметры в test task грэдла, но про это - в другой раз)</p>
<div class="language-properties highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">systemProp.chromeoptions.prefs</span><span class="p">=</span><span class="s">intl.accept_languages=de</span>
</code></pre></div></div>
<h3 id="командная-строка">Командная строка</h3>
<p>Вы можете переопределять значения при запуске <code class="language-plaintext highlighter-rouge">mvn test</code> или <code class="language-plaintext highlighter-rouge">gradle test</code> определяя новое значение в <code class="language-plaintext highlighter-rouge">-Dchromeoptions.prefs=intl.accept_languages=ru</code></p>
<h2 id="пример">Пример</h2>
<p>Просто запускайте этот маленький тест с различными параметрами языка и понаблюдайте за результатом.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"http://wikipedia.org"</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">"[data-jsl10n=slogan]"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactText</span><span class="o">(</span><span class="s">"Die freie Enzyklopädie"</span><span class="o">));</span>
</code></pre></div></div>
<p>Я желаю всем вам <em><strong>Fröhliche Weihnachten</strong></em> и <em><strong>Guten Rutsch</strong></em>!</p>
<p><strong>Alexei Vinogradov</strong></p>
Теория большого вейта2019-12-20T00:00:00+00:00http://ru.selenide.org/2019/12/20/advent-calendar-big-wait-theory
<h1 id="теория-большого-вейта">Теория большого вейта</h1>
<p>Тема ожиданий вызывает много обсуждений и споров.<br />
Современные веб-сайты создают проблемы для <em>написателей</em> автотестов. Возникает много ситуаций, в которых стандартные методы Selenium неэффективны.</p>
<p>Если вы читали документацию Selenide, вы уже знаете, что классические явные ожидания типа</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">element</span> <span class="o">=</span> <span class="o">(</span><span class="k">new</span> <span class="nc">WebDriverWait</span><span class="o">(</span><span class="n">driver</span><span class="o">,</span> <span class="o"><</span><span class="n">timeOutForElement</span><span class="o">>))</span>
<span class="o">.</span><span class="na">until</span><span class="o">(</span><span class="nc">ExpectedConditions</span><span class="o">.</span><span class="na">presenceOfElementLocated</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">cssSelector</span><span class="o">(<</span><span class="n">cssSelector</span><span class="o">>)));</span>
</code></pre></div></div>
<p>или</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">element</span> <span class="o">=</span> <span class="o">(</span><span class="k">new</span> <span class="nc">WebDriverWait</span><span class="o">(</span><span class="n">driver</span><span class="o">,</span> <span class="o"><</span><span class="n">timeOutForElement</span><span class="o">>))</span>
<span class="o">.</span><span class="na">until</span><span class="o">(</span><span class="nc">ExpectedConditions</span><span class="o">.</span><span class="na">visibilityOfElementLocated</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">cssSelector</span><span class="o">(<</span><span class="n">cssSelector</span><span class="o">>)));</span>
</code></pre></div></div>
<p>были заменены в Selenide более коротким конструкциями типа</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">element</span> <span class="o">=</span> <span class="err">$</span><span class="o">(<</span><span class="n">cssSelector</span><span class="o">>).</span><span class="na">should</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span>
<span class="n">element</span> <span class="o">=</span> <span class="err">$</span><span class="o">(<</span><span class="n">cssSelector</span><span class="o">>).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
</code></pre></div></div>
<p><strong>Как известно, ассерты в Selenide - это новая версия явных ожиданий, что хорошо описано в <a href="https://selenide.org/documentation.html">документации</a>.</strong></p>
<p>Сегодня мы не будем рассматривать ожидания и ассерты с технической точки зрения, а подумаем, для чего можно использовать
ассерты в различных ситуациях.</p>
<h2 id="современные-проблемы-требуют-современных-решений">Современные проблемы требуют современных решений</h2>
<h3 id="1-threadsleep">1. <code class="language-plaintext highlighter-rouge">Thread.sleep()</code></h3>
<p>Это самое ужасное, что может случиться с нашими тестами на Selenium.</p>
<p>В некоторых ситуациях мы были вынуждены использовать слипы. У нас просто не было другого решения, чтобы обойти проблему и двигаться дальше.
Например, слипы используют, чтобы дождаться окончания загрузки страницы. Иногда - чтобы дождаться какого-то элемента,
когда другие ожидания не помогли. Увы, таким образом мы можем терять много времени при запуске теста.</p>
<p>Если поставить один слип - это ещё ничего. Вы потеряете, скажем, 4 секунды - не смертельно.<br />
Но если вы используете слип в 150 тестах, время их выполнения увеличится заметно.<br />
Нет смысла объяснять, почему это плохо.</p>
<p>Хотя команда <code class="language-plaintext highlighter-rouge">sleep()</code> есть и в Selenide, вышеупомянутые “умные ожидания” делают слипы почти ненужными для ожидания появления чего-то либо на странице.<br />
Смотри следующие пункты.</p>
<h3 id="2-как-дождаться-окончания-загрузки-страницы">2. Как дождаться окончания загрузки страницы?</h3>
<p>Самый простой способ - выбрать какой-то элемент на странице, который редко меняется (скажем, заголовок), и использовать метод Selenide:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">cssSelector</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
</code></pre></div></div>
<p>Selenide сначала попытается найти элемент, а потом проверить, что он видимый.<br />
Если это не удалось за 4 секунды, вы можете сделать вывод, что страница не загрузилась.</p>
<p>Ещё вы можете выбрать какой-то элемент на <em>предыдущей</em> странице и дождаться, пока он исчезнет:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">element</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span>
</code></pre></div></div>
<p>Таким образом мы создаём двойную проверку, что мы перешли с одной страницы на другую.
И это будет работать, даже если загрузка страницы занимает значительное время. Как видите, обошлись без всяких <code class="language-plaintext highlighter-rouge">Thread.sleep()</code>.</p>
<h3 id="3-изменение-состояния-элемента">3. Изменение состояния элемента</h3>
<p>Иногда нам нужно проверить, что состояние элемента поменялось в результате действий пользователя.<br />
Например, элемент может содержать текст, сигнализирующий об успешной или неуспешной загрузке файла.<br />
Допустим, загрузка файла занимает какое-то время, потому что файл большой, или сервер должен запустить какую-то сложную обработку этого файла.</p>
<p>Обычно мы в тесте загружаем файл и проверяем состояние элемента (скажем, текст “файл загружен”).<br />
Но как узнать, <em>когда</em> именно состояние элемента должно поменяться? Загрузка-то происходит не мгновенно.<br />
В этой ситуации многие используют <code class="language-plaintext highlighter-rouge">Thread.sleep()</code>.</p>
<p>А в Selenide у нас есть умный инструмент для “отложенной” проверки состояния элемента:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">cssSelector</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactText</span><span class="o">(<</span><span class="n">expectedText</span><span class="o">>));</span>
</code></pre></div></div>
<p>В этом случае Selenide сам дождётся, пока состояние элемента изменится, и в нём появится нужный текст.<br />
Нам не нужно писать лишних строк для ожиданий, и мы можем быть уверены, что Selenide точно дождётся.</p>
<h3 id="что-теперь">Что теперь?</h3>
<p>Мы рассмотрели всего лишь несколько простых идей, как можно “ждать” с помощью Selenide.<br />
В реальности ситуаций намного больше. И здорово, что теперь у нас есть хороший инструмент, позволяющий нам обходится без слипов и не терять драгоценное время.<br />
Используйте умные инструменты и не теряйте время - время ценно. :)</p>
<p>Maciej Grymuza (figrym@gmail.com)</p>
Как получить сетевые запросы с помощью прокси2019-12-18T00:00:00+00:00http://ru.selenide.org/2019/12/18/advent-calendar-network-logs-with-proxy
<p>Привет!</p>
<p>В предыдущих постах нашего рождественского календаря мы рассмотрели два способа получить сетевые запросы между браузером и приложением.<br />
Оба нас расстроили тем, что не позволяют прочитать тело запроса/ответа.</p>
<p>Наконец, дошла очередь до третьего способа - через встроенный прокси-сервер.</p>
<h3 id="перед-тестом">Перед тестом</h3>
<p>Как вы знаете, в селениде уже есть встроенный прокси-сервер, надо его всего лишь включить:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</code></pre></div></div>
<p>И ещё нужно сказать прокси-серверу, чтобы он начал отслеживать запросы:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">BrowserMobProxy</span> <span class="n">bmp</span> <span class="o">=</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">getSelenideProxy</span><span class="o">().</span><span class="na">getProxy</span><span class="o">();</span>
<span class="c1">// запоминать тело запросов (по умолчанию тело не запоминается, ибо может быть большим)</span>
<span class="n">bmp</span><span class="o">.</span><span class="na">setHarCaptureTypes</span><span class="o">(</span><span class="nc">CaptureType</span><span class="o">.</span><span class="na">getAllContentCaptureTypes</span><span class="o">());</span>
<span class="c1">// запоминать как запросы, так и ответы</span>
<span class="n">bmp</span><span class="o">.</span><span class="na">enableHarCaptureTypes</span><span class="o">(</span><span class="nc">CaptureType</span><span class="o">.</span><span class="na">REQUEST_CONTENT</span><span class="o">,</span> <span class="nc">CaptureType</span><span class="o">.</span><span class="na">RESPONSE_CONTENT</span><span class="o">);</span>
<span class="c1">// начинай запись!</span>
<span class="n">bmp</span><span class="o">.</span><span class="na">newHar</span><span class="o">(</span><span class="s">"pofig"</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="после-теста">После теста</h3>
<p>Теперь нужно получить HAR и анализировать все записи в нём:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">List</span><span class="o"><</span><span class="nc">HarEntry</span><span class="o">></span> <span class="n">requests</span> <span class="o">=</span> <span class="n">bmp</span><span class="o">.</span><span class="na">getHar</span><span class="o">().</span><span class="na">getLog</span><span class="o">().</span><span class="na">getEntries</span><span class="o">();</span>
</code></pre></div></div>
<p>HAR (HTTP Archive) - это типа такой “архив” со всеми сетевыми запросами и ответами (“entries”).</p>
<p>Каждая запись - это и есть запрос от тестируемого приложения к серверу.<br />
Внутри есть все данные: URL, request, response, их http status и body.<br />
Всё, о чём мы так давно мечтали.</p>
<p><img src="/images/2019/12/har.entries.png" alt="HAR entries" /></p>
<h3 id="плюсы">Плюсы:</h3>
<ul>
<li>Есть все данные, которые нам нужны</li>
<li>Легко анализировать программно</li>
<li>Работает во всех браузерах</li>
</ul>
<h3 id="минусы">Минусы:</h3>
<p>Минус только один: иногда у людей возникают сложности с запуском прокси, когда браузер и тесты бегут на разных
машинах, и с “браузерной” машины нет доступа к “тестовой” машине.
Хотя я никогда не понимал, зачем такие сложности. Запускайте тесты и браузеры на одной и той же машине - ВСЁ будет в стотыщ раз ПРОЩЕ.<br />
Надо параллелить - параллельте тесты.
Нужен кластер - запускайте ТЕСТЫ на разных нодах кластера (а с ними запустятся и браузеры). Зачем всё усложнять?</p>
<h2 id="что-теперь">Что теперь?</h2>
<p>Теперь мы умеем читать сетевые запросы при прогоне тестов.<br />
Но я вообще-то надеюсь, что обычно это вам не должно быть нужно. Ну может, в каких-то очень уж запутанных случаях.<br />
Обычно должно быть достаточно почитать логи приложения, чтобы понять, какие запросы к нему прилетали. Будьте проще.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Как получить логи браузера через JavaScript2019-12-17T00:00:00+00:00http://ru.selenide.org/2019/12/17/advent-calendar-browser-logs-with-js
<p>Привет!</p>
<p>В прошлом посте нашего рождественского календаря мы пробовали получить логи хрома с помощью <em>капабилити</em> “goog:loggingPrefs”.<br />
А сегодня попробуем другой способ - с помощью JavaScript.</p>
<p>Итак, надо всего лишь в конце теста дёрнуть такой вот JavaScript:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">js</span> <span class="o">=</span>
<span class="s">"var performance = window.performance || window.mozPerformance"</span> <span class="o">+</span>
<span class="s">" || window.msPerformance || window.webkitPerformance || {};"</span> <span class="o">+</span>
<span class="s">" return performance.getEntries() || {};"</span><span class="o">;</span>
<span class="nc">String</span> <span class="n">netData</span> <span class="o">=</span> <span class="n">executeJavaScript</span><span class="o">(</span><span class="n">js</span><span class="o">).</span><span class="na">toString</span><span class="o">();</span>
<span class="n">logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Network traffic: {}"</span><span class="o">,</span> <span class="n">netData</span><span class="o">);</span>
</code></pre></div></div>
<p>Результат получается примерно такой:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">Network</span><span class="w"> </span><span class="err">traffic:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="err">name=https://selenide.org/quick-start.html</span><span class="p">,</span><span class="w"> </span><span class="err">connectEnd=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">connectStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">decodedBodySize=</span><span class="mi">32582</span><span class="p">,</span><span class="w"> </span><span class="err">domComplete=</span><span class="mi">724</span><span class="p">,</span><span class="w"> </span><span class="err">domContentLoadedEventEnd=</span><span class="mi">119</span><span class="p">,</span><span class="w"> </span><span class="err">domContentLoadedEventStart=</span><span class="mi">115</span><span class="p">,</span><span class="w"> </span><span class="err">domInteractive=</span><span class="mi">104</span><span class="p">,</span><span class="w"> </span><span class="err">domainLookupEnd=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">domainLookupStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">duration=</span><span class="mi">724</span><span class="p">,</span><span class="w"> </span><span class="err">encodedBodySize=</span><span class="mi">32582</span><span class="p">,</span><span class="w"> </span><span class="err">entryType=navigation</span><span class="p">,</span><span class="w"> </span><span class="err">fetchStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">initiatorType=navigation</span><span class="p">,</span><span class="w"> </span><span class="err">loadEventEnd=</span><span class="mi">724</span><span class="p">,</span><span class="w"> </span><span class="err">loadEventStart=</span><span class="mi">724</span><span class="p">,</span><span class="w"> </span><span class="err">nextHopProtocol=http/</span><span class="mf">1.1</span><span class="p">,</span><span class="w"> </span><span class="err">redirectCount=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">redirectEnd=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">redirectStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">requestStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">responseEnd=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">responseStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">secureConnectionStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">serverTiming=</span><span class="p">[],</span><span class="w"> </span><span class="err">startTime=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">transferSize=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">type=navigate</span><span class="p">,</span><span class="w"> </span><span class="err">unloadEventEnd=</span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="err">unloadEventStart=</span><span class="mi">9</span><span class="p">,</span><span class="w"> </span><span class="err">workerStart=</span><span class="mi">0</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="err">name=https://selenide.org/assets/themes/ingmar/css/styles.css?</span><span class="mi">001</span><span class="p">,</span><span class="w"> </span><span class="err">connectEnd=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">connectStart=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">decodedBodySize=</span><span class="mi">8177</span><span class="p">,</span><span class="w"> </span><span class="err">domainLookupEnd=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">domainLookupStart=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">duration=</span><span class="mi">29</span><span class="p">,</span><span class="w"> </span><span class="err">encodedBodySize=</span><span class="mi">8177</span><span class="p">,</span><span class="w"> </span><span class="err">entryType=resource</span><span class="p">,</span><span class="w"> </span><span class="err">fetchStart=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">initiatorType=link</span><span class="p">,</span><span class="w"> </span><span class="err">nextHopProtocol=http/</span><span class="mf">1.1</span><span class="p">,</span><span class="w"> </span><span class="err">redirectEnd=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">redirectStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">requestStart=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">responseEnd=</span><span class="mi">41</span><span class="p">,</span><span class="w"> </span><span class="err">responseStart=</span><span class="mi">21</span><span class="p">,</span><span class="w"> </span><span class="err">secureConnectionStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">serverTiming=</span><span class="p">[],</span><span class="w"> </span><span class="err">startTime=</span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="err">transferSize=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">workerStart=</span><span class="mi">0</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="err">name=https://ajax.googleapis.com/ajax/libs/jquery/</span><span class="mf">2.1</span><span class="err">.</span><span class="mi">1</span><span class="err">/jquery.min.js</span><span class="p">,</span><span class="w"> </span><span class="err">connectEnd=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">connectStart=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">decodedBodySize=</span><span class="mi">84245</span><span class="p">,</span><span class="w"> </span><span class="err">domainLookupEnd=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">domainLookupStart=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">duration=</span><span class="mi">28</span><span class="p">,</span><span class="w"> </span><span class="err">encodedBodySize=</span><span class="mi">84245</span><span class="p">,</span><span class="w"> </span><span class="err">entryType=resource</span><span class="p">,</span><span class="w"> </span><span class="err">fetchStart=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">initiatorType=script</span><span class="p">,</span><span class="w"> </span><span class="err">nextHopProtocol=http/</span><span class="mf">1.1</span><span class="p">,</span><span class="w"> </span><span class="err">redirectEnd=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">redirectStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">requestStart=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">responseEnd=</span><span class="mi">41</span><span class="p">,</span><span class="w"> </span><span class="err">responseStart=</span><span class="mi">21</span><span class="p">,</span><span class="w"> </span><span class="err">secureConnectionStart=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">serverTiming=</span><span class="p">[],</span><span class="w"> </span><span class="err">startTime=</span><span class="mi">13</span><span class="p">,</span><span class="w"> </span><span class="err">transferSize=</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="err">workerStart=</span><span class="mi">0</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>
<h3 id="плюсы">Плюсы:</h3>
<ul>
<li>Не нужно никак настраивать браузер. Оно работает из коробки.</li>
<li>Работает во всех браузерах (кажется?)</li>
</ul>
<h3 id="минусы">Минусы:</h3>
<ul>
<li>Здесь всё ещё нет тела запроса.</li>
<li>Это невалидный JSON. Распарсить его стандартным парсером не получится. Придётся придумывать какой-то свой обработчик.</li>
</ul>
<p>Но этот способ вполне подходит, чтобы просто посмотреть глазами и прикинуть, что происходит.</p>
<h2 id="что-теперь">Что теперь?</h2>
<p>Наша последняя надежда - получить запросы и ответы с помощью встроенного прокси-сервера. Этот способ мы рассмотрим в следующем посте.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Как получить логи браузера2019-12-16T00:00:00+00:00http://ru.selenide.org/2019/12/16/advent-calendar-browser-logs
<p>Привет!</p>
<p>Мы продолжаем наш рождественский календарь.<br />
На сей раз мы посмотрим, как можно взглянуть хрому под вкладку “developer tools”.<br />
Это на случай, если вы хотите понять, какие ошибки писались и какие сетевые запросы летели из тестируемого приложения во время прогона тестов.</p>
<p>Chromedriver предлагает следующий рецепт.</p>
<h3 id="1-добавить-щепотку-строк-при-открытии-браузера">1. Добавить щепотку строк при открытии браузера:</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">LoggingPreferences</span> <span class="n">logPrefs</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LoggingPreferences</span><span class="o">();</span>
<span class="n">logPrefs</span><span class="o">.</span><span class="na">enable</span><span class="o">(</span><span class="nc">LogType</span><span class="o">.</span><span class="na">BROWSER</span><span class="o">,</span> <span class="nc">Level</span><span class="o">.</span><span class="na">ALL</span><span class="o">);</span>
<span class="n">logPrefs</span><span class="o">.</span><span class="na">enable</span><span class="o">(</span><span class="nc">LogType</span><span class="o">.</span><span class="na">PERFORMANCE</span><span class="o">,</span> <span class="nc">Level</span><span class="o">.</span><span class="na">ALL</span><span class="o">);</span>
<span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"goog:loggingPrefs"</span><span class="o">,</span> <span class="n">logPrefs</span><span class="o">);</span>
</code></pre></div></div>
<p>До какой-то версии эта <em>капабилитя</em> называлась “loggingPrefs”, потом переименовали в “goog:loggingPrefs”.<br />
Не знаю, как в других браузерах.</p>
<p>Кстати, помимо <code class="language-plaintext highlighter-rouge">BROWSER</code> и <code class="language-plaintext highlighter-rouge">PERFORMANCE</code>, есть и другие типы логов, но у меня они как-то нестабильно работали, да я
и пользы в них не увидел. Знаете больше? Делитесь!</p>
<h3 id="2-в-конце-теста-снять-пенку-с-логов">2. В конце теста снять пенку с логов:</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Logs</span> <span class="n">logs</span> <span class="o">=</span> <span class="n">getWebDriver</span><span class="o">().</span><span class="na">manage</span><span class="o">().</span><span class="na">logs</span><span class="o">();</span>
<span class="n">printLog</span><span class="o">(</span><span class="n">logs</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">LogType</span><span class="o">.</span><span class="na">BROWSER</span><span class="o">));</span>
<span class="kt">void</span> <span class="nf">printLog</span><span class="o">(</span><span class="nc">LogEntries</span> <span class="n">entries</span><span class="o">)</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"{} log entries found"</span><span class="o">,</span> <span class="n">entries</span><span class="o">.</span><span class="na">getAll</span><span class="o">().</span><span class="na">size</span><span class="o">());</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">LogEntry</span> <span class="n">entry</span> <span class="o">:</span> <span class="n">entries</span><span class="o">)</span> <span class="o">{</span>
<span class="n">logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"{} {} {}"</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">Date</span><span class="o">(</span><span class="n">entry</span><span class="o">.</span><span class="na">getTimestamp</span><span class="o">()),</span> <span class="n">entry</span><span class="o">.</span><span class="na">getLevel</span><span class="o">(),</span> <span class="n">entry</span><span class="o">.</span><span class="na">getMessage</span><span class="o">()</span>
<span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="3-блюдо-подаётся-к-отчёту-примерно-в-таком-виде">3. Блюдо подаётся к отчёту примерно в таком виде:</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">BROWSER</span> <span class="nl">logs:</span>
<span class="nc">Mon</span> <span class="nc">Dec</span> <span class="mi">16</span> <span class="mi">19</span><span class="o">:</span><span class="mi">29</span><span class="o">:</span><span class="mi">42</span> <span class="no">EET</span> <span class="mi">2019</span> <span class="no">SEVERE</span> <span class="nl">http:</span><span class="c1">//localhost:9126/page/image/payment-promo-campaign-ozon.png - Failed to load resource: the server responded with a status of 404 (Not Found)</span>
<span class="nc">Mon</span> <span class="nc">Dec</span> <span class="mi">16</span> <span class="mi">19</span><span class="o">:</span><span class="mi">49</span><span class="o">:</span><span class="mi">14</span> <span class="no">EET</span> <span class="mi">2019</span> <span class="no">INFO</span> <span class="n">console</span><span class="o">-</span><span class="n">api</span> <span class="mi">19</span><span class="o">:</span><span class="mi">16</span> <span class="s">"start loading loans"</span>
<span class="nc">Mon</span> <span class="nc">Dec</span> <span class="mi">16</span> <span class="mi">19</span><span class="o">:</span><span class="mi">49</span><span class="o">:</span><span class="mi">14</span> <span class="no">EET</span> <span class="mi">2019</span> <span class="no">INFO</span> <span class="n">console</span><span class="o">-</span><span class="n">api</span> <span class="mi">21</span><span class="o">:</span><span class="mi">18</span> <span class="s">"loaded loans"</span>
</code></pre></div></div>
<p>Здесь видны все логи, что есть обычно в Developer Tools -> Console. В том числе сообщения <code class="language-plaintext highlighter-rouge">console.log</code> и ошибки JavaScript.</p>
<h3 id="4-для-гурманов-можно-подать-десерт">4. Для гурманов можно подать десерт</h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">PERFORMANCE</span><span class="w"> </span><span class="err">logs:</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Network.loadingFinished"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{</span><span class="nl">"encodedDataLength"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="nl">"requestId"</span><span class="p">:</span><span class="s2">"2C9E49BC49DCD3CA6EA9644255E34DE5"</span><span class="p">,</span><span class="nl">"shouldReportCorbBlocking"</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span><span class="nl">"timestamp"</span><span class="p">:</span><span class="mf">141439.076528</span><span class="p">}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Page.loadEventFired"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{</span><span class="nl">"timestamp"</span><span class="p">:</span><span class="mf">141439.234207</span><span class="p">}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Page.frameStoppedLoading"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{</span><span class="nl">"frameId"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Page.domContentEventFired"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{</span><span class="nl">"timestamp"</span><span class="p">:</span><span class="mf">141439.234834</span><span class="p">}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Page.frameResized"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span><span class="err">...</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Network.dataReceived"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{</span><span class="nl">"dataLength"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="nl">"encodedDataLength"</span><span class="p">:</span><span class="mi">327</span><span class="p">,</span><span class="nl">"requestId"</span><span class="p">:</span><span class="s2">"58583.71"</span><span class="p">,</span><span class="nl">"timestamp"</span><span class="p">:</span><span class="mf">141474.021635</span><span class="p">}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"message"</span><span class="p">:{</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Network.loadingFinished"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:{</span><span class="nl">"encodedDataLength"</span><span class="p">:</span><span class="mi">586</span><span class="p">,</span><span class="nl">"requestId"</span><span class="p">:</span><span class="s2">"58583.71"</span><span class="p">,</span><span class="nl">"shouldReportCorbBlocking"</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span><span class="nl">"timestamp"</span><span class="p">:</span><span class="mf">141473.994219</span><span class="p">}},</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h3 id="плюс">Плюс</h3>
<p>Каждая запись - это валидный JSON, его вполне можно парсить и анализировать прямо в тестах.</p>
<p>Вот так выглядит отформатированная первая запись:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:{</span><span class="w">
</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"Network.loadingFinished"</span><span class="p">,</span><span class="w">
</span><span class="nl">"params"</span><span class="p">:{</span><span class="w">
</span><span class="nl">"encodedDataLength"</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"requestId"</span><span class="p">:</span><span class="s2">"2C9E49BC49DCD3CA6EA9644255E34DE5"</span><span class="p">,</span><span class="w">
</span><span class="nl">"shouldReportCorbBlocking"</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"timestamp"</span><span class="p">:</span><span class="mf">141439.076528</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"webview"</span><span class="p">:</span><span class="s2">"FF1A4E4EAAD7143749CD3740DF9BB95F"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h3 id="минус">Минус:</h3>
<ul>
<li>Что-то понять из этих логов сложно. Нужно строить поверх какие-то анализаторы.</li>
<li>Здесь нет тела запроса.</li>
</ul>
<h2 id="что-теперь">Что теперь?</h2>
<p>В следующий раз вы изучим другие возможности получить логи - со статусами и телами запросов.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Drag and Drop2019-12-15T00:00:00+00:00http://ru.selenide.org/2019/12/15/advent-calendar-drag-and-drop
<p>Привет!</p>
<p>В сегодняшнем выпуске рождественского календаря мы посмотрим короткое, но весёлое видео о том, как перетаскивать элементы в Selenide.</p>
<h3 id="селенид-умеет-перетаскивать-элементы">Селенид умеет перетаскивать элементы?</h3>
<p>Да, в селениде есть метод Drag’n’Drop. Вот скучное описание из блога селенида:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#from"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#to"</span><span class="o">)</span>
</code></pre></div></div>
<p>А вот весёленькое описание от Martin Škarbala:</p>
<iframe width="800" height="490" src="https://www.youtube.com/embed/OSnwiosrMq0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>Вот это подача!</p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Actions2019-12-12T00:00:00+00:00http://ru.selenide.org/2019/12/12/advent-calendar-actions
<p>Привет!</p>
<p>В сегодняшнем выпуске рождественского календаря мы рассмотрим, как можно использовать “действия” (Actions) в Selenide.</p>
<p>Иногда при написании автотестов мы сталкиваемся со странными проблемами. Уверен на 100%, каждый из нас испытывал или
будет испытывать необычные проблемы, которые блокируют нашу работу.
Например, у нас часто не получается кликнуть на какой-то элемент, и стандартная селениумовская/селенидовская команда типа</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">element</span><span class="o">.</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<p>не работает. Причин, по которым клик может не срабатывать - множество. Но мы не можем сдаться просто так, мы должны
найти какое-то решение. В Selenium для таких случаев есть класс <code class="language-plaintext highlighter-rouge">Actions</code>, который позволяет выполнить клик иначе:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">WebElement</span> <span class="n">element</span> <span class="o">=</span> <span class="o"><</span><span class="nc">Some</span> <span class="n">selector</span><span class="o">>;</span>
<span class="nc">Actions</span> <span class="n">actions</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Actions</span><span class="o">(</span><span class="n">driver</span><span class="o">);</span>
<span class="n">actions</span><span class="o">.</span><span class="na">moveToElement</span><span class="o">(</span><span class="n">element</span><span class="o">);</span>
<span class="n">actions</span><span class="o">.</span><span class="na">click</span><span class="o">();</span>
<span class="n">actions</span><span class="o">.</span><span class="na">build</span><span class="o">().</span><span class="na">perform</span><span class="o">();</span>
</code></pre></div></div>
<p>Этот вариант иногда срабатывает там, где обычный клик бессилен.</p>
<h4 id="но-как-сделать-это-в-selenide">Но как сделать это в Selenide?</h4>
<p>Оказывается, в Selenide это ещё проще, чем в Selenium (как, собственно, и всё в Selenide :)). <br />
В Selenide тоже есть <code class="language-plaintext highlighter-rouge">Actions</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">SelenideElement</span> <span class="n">element</span> <span class="o">=</span> <span class="err">$</span><span class="o">(<</span><span class="n">some</span> <span class="n">selector</span><span class="o">>);</span>
<span class="n">actions</span><span class="o">().</span><span class="na">moveToElement</span><span class="o">(</span><span class="n">element</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">element</span><span class="o">).</span><span class="na">perform</span><span class="o">();</span>
</code></pre></div></div>
<p>Здесь <code class="language-plaintext highlighter-rouge">actions()</code> - это один из тех методов, которые вы можете магически подключить волшебным импортом:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.*;</span>
</code></pre></div></div>
<p>Заметить, чтобы использовать <code class="language-plaintext highlighter-rouge">actions()</code>, не нужен webdriver!</p>
<h5 id="чумачечий-drag-and-drop">Чумачечий drag and drop</h5>
<p>Если вы читали документацию, вы знаете, что в Selenide по умолчанию есть два типа операций “drag and drop”:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">dragAndDropTo(java.lang.String targetCssSelector);</code></li>
<li><code class="language-plaintext highlighter-rouge">dragAndDropTo(org.openqa.selenium.WebElement target);</code></li>
</ol>
<p>Первый метод перетаскивает элемент в цель по CSS локатору. Второй - в другой WebElement.</p>
<p>Но что, если мы не знаем точно, в какой элемент нужно перетащить?<br />
Допустим, у нас есть просто пустая страница, и мы хотим перетащить несколько объектов в разные места на этой странице.<br />
И тут снова на помощь приходят <code class="language-plaintext highlighter-rouge">Actions</code>. В Selenium мы бы сделали это примерно так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">WebElement</span> <span class="n">element</span> <span class="o">=</span> <span class="n">driver</span><span class="o">.</span><span class="na">findElement</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">some</span><span class="o">);</span>
<span class="nc">Actions</span> <span class="n">actions</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Actions</span><span class="o">(</span><span class="n">driver</span><span class="o">);</span>
<span class="n">actions</span><span class="o">.</span><span class="na">dragAndDropBy</span><span class="o">(</span><span class="n">element</span><span class="o">,</span> <span class="n">xOffset</span><span class="o">,</span> <span class="n">yOffset</span><span class="o">).</span><span class="na">perform</span><span class="o">();</span>
</code></pre></div></div>
<p>Где <code class="language-plaintext highlighter-rouge">xOffset</code> и <code class="language-plaintext highlighter-rouge">yOffset</code> - сдвиг по горизонтали и вертикали.</p>
<p>В Selenide это выглядит чуть короче:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">SelenideElement</span> <span class="n">element</span> <span class="o">=</span> <span class="o"><</span><span class="nc">Some</span> <span class="n">selector</span><span class="o">>;</span>
<span class="n">actions</span><span class="o">().</span><span class="na">dragAndDropBy</span><span class="o">(</span><span class="n">element</span><span class="o">,</span> <span class="n">xOffset</span><span class="o">,</span> <span class="n">yOffset</span><span class="o">).</span><span class="na">perform</span><span class="o">();</span>
</code></pre></div></div>
<p>Таким образом мы можем перетащить элемент в любую точку, даже не зная локатора цели.</p>
<h2 id="что-дальше">Что дальше?</h2>
<p>Конечно же, это только пара примеров использования <code class="language-plaintext highlighter-rouge">actions()</code> в Selenide, и вы можете экспериментировать и находить другие варианты.</p>
<p>Наслаждайтесь <code class="language-plaintext highlighter-rouge">actions()</code>!</p>
<p>Maciej Grymuza (figrym@gmail.com)</p>
Как скачать файл с помощью Selenide2019-12-10T00:00:00+00:00http://ru.selenide.org/2019/12/10/advent-calendar-download-files
<p>Добрый вечер!</p>
<p>На дворе декабрь, и в сегодняшнем посте рождественского календаря Selenide мы поговорим о том, какие возможности
для скачивания файлов есть в Selenide.</p>
<h1 id="как-я-могу-скачать-файл-в-моём-тесте">Как я могу скачать файл в моём тесте?</h1>
<p>В какой-то момент нашей карьеры каждый из нас сталкивается с необходимость скачать какой-то файл в тесте.</p>
<p>Как мы помним, в Selenium это было непросто, потому что для разных браузеров требуются разные настройки.<br />
Например, вот так выглядит создание профиля Firefox с нужными настройками:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.download.dir"</span><span class="o">,</span> <span class="n">downloadPath</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.download.folderList"</span><span class="o">,</span> <span class="mi">2</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.download.manager.showWhenStarting"</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.helperApps.alwaysAsk.force"</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.helperApps.neverAsk.saveToDisk"</span><span class="o">,</span> <span class="n">mimeTypes</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.download.manager.focusWhenStarting"</span><span class="o">,</span><span class="kc">false</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.download.manager.useWindow"</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"browser.download.manager.showAlertOnComplete"</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
<span class="n">profile</span><span class="o">.</span><span class="na">setPreference</span><span class="o">(</span><span class="s">"pdfjs.disabled"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="а-в-selenide">А в Selenide</h3>
<p>проблема решается гораздо проще - методом <code class="language-plaintext highlighter-rouge">$.download()</code>.</p>
<p>Чтобы скачать файл, в Selenide достаточно просто вызвать метод:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">report</span> <span class="o">=</span> <span class="n">element</span><span class="o">.</span><span class="na">download</span><span class="o">();</span>
</code></pre></div></div>
<p>и Selenide автоматически сделает всё, что надо. Вам не придётся возиться со всплывающим окошком, которое спрашивает,
куда сохранить файл, и потом закрывать его.</p>
<p>Selenide сохранит скачанный файл в папку <code class="language-plaintext highlighter-rouge">build/reports/tests</code>. Это та же папка, где Gradle генерирует результаты прогона тестов,
так что их как раз удобно видеть вместе.</p>
<p>Конечно, поменять эту папку тоже можно:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">downloadsFolder</span> <span class="o">=</span> <span class="o"><</span><span class="n">desired</span> <span class="n">location</span> <span class="k">for</span> <span class="n">downloaded</span> <span class="n">files</span><span class="o">>;</span>
</code></pre></div></div>
<h3 id="но">НО:</h3>
<p>Таким образом можно скачивать файлы только со ссылкой с атрибутом “href”.</p>
<p>Но что, если у меня ссылки с атрибутом “href”? Так бывает, например, когда файл скачивается в результате сабмита формы.
В этом случае можно скачивать файлы с помощью встроенного в селенид прокси-сервера.</p>
<p>Для начала нам нужно включить его (т.к. он по умолчанию выключен):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
<span class="nc">Configuration</span><span class="o">.</span><span class="na">fileDownload</span> <span class="o">=</span> <span class="no">PROXY</span><span class="o">;</span>
</code></pre></div></div>
<p>После этого мы снова можем вызывать метод <code class="language-plaintext highlighter-rouge">$.download()</code>, но теперь он стал более могущественным и не требует наличия атрибута “href”:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">report</span> <span class="o">=</span> <span class="n">element</span><span class="o">.</span><span class="na">download</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="хозяйке-на-заметку">Хозяйке на заметку:</h3>
<p>Не забудьте увеличить таймаут, если собираетесь скачивать файл большого размера.</p>
<p>Файл будет скачан в папку по умолчанию (что-то типа <code class="language-plaintext highlighter-rouge">C:\downloads and settings\downloads</code>). <br />
Таким образом, скачанный файл окажется в двух местах: <code class="language-plaintext highlighter-rouge">c:\downloads...</code> и <code class="language-plaintext highlighter-rouge">build/reports/tests</code>.</p>
<p>Если это для вас проблема, можете в конце теста удалить ненужную папку, чтобы очистить место на диске:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">FileUtils</span><span class="o">.</span><span class="na">deleteDirectory</span><span class="o">(</span><span class="k">new</span> <span class="nc">File</span><span class="o">(<</span><span class="n">папка</span><span class="o">,</span> <span class="n">подлежащая</span> <span class="n">удалению</span><span class="o">>));</span>
</code></pre></div></div>
<p>Узнать подробнее про механизмы скачивания файлов можно <a href="https://ru.selenide.org/2016/08/27/selenide-3.9.1/">тут</a>.</p>
<p>Maciej Grymuza (figrym@gmail.com)</p>
Почему статики запретили, а потом разрешили?2019-12-09T00:00:00+00:00http://ru.selenide.org/2019/12/09/advent-calendar-statics
<p>Привет!</p>
<p>9 день рождественского календаря Selenide.<br />
Расскажу о том, что сильнее всего волнует общественность.</p>
<h1 id="почему-статики-запретили-в-500-а-потом-снова-разрешили">Почему статики запретили в 5.0.0, а потом снова разрешили?</h1>
<p>Короткий ответ: запретили случайно. Но правильно сделали. </p>
<p>А теперь подробнее. </p>
<h3 id="коротко-о-хранении-вебдрайверов-в-селениде">Коротко о хранении вебдрайверов в селениде</h3>
<p>Селенид изначально хранил вебдрайверы в ThreadLocal.<br />
Это позволяет запускать тесты параллельно: в разных потоках - разные вебдрайверы. Код выглядит примерно так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">WebDriverRunner</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o"><</span><span class="nc">WebDriver</span><span class="o">></span> <span class="n">webdriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o"><>();</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o"><</span><span class="nc">SelenideProxyServer</span><span class="o">></span> <span class="n">proxy</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o"><>();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Скажем, метод <code class="language-plaintext highlighter-rouge">$("a").click()</code> работает примерно так:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">webdriver</span><span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">findElement</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span>
</code></pre></div></div>
<h2 id="коротко-про-selenidedriver">Коротко про <code class="language-plaintext highlighter-rouge">SelenideDriver</code></h2>
<p>Но ThreadLocal накладывает ограничение: ты не можешь использовать в одном потоке два вебдрайвера
(а также в двух потоках - один и тот же вебдрайвер, но до этого мы пока не дошли).</p>
<p>Это мы и собирались решить в Selenide 5.0.0. Мы сделали специальный класс <code class="language-plaintext highlighter-rouge">SelenideDriver</code>, позволяющий использовать два
вебдрайвера в одном тесте:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideDriver</span> <span class="n">browser1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SelenideDriver</span><span class="o">();</span>
<span class="nc">SelenideDriver</span> <span class="n">browser2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SelenideDriver</span><span class="o">();</span>
<span class="n">browser1</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"https://google.com"</span><span class="o">);</span>
<span class="n">browser2</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"http://yandex.ru"</span><span class="o">);</span>
<span class="n">browser1</span><span class="o">.</span><span class="err">$</span><span class="o">(</span><span class="n">h1</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Google"</span><span class="o">));</span>
<span class="n">browser2</span><span class="o">.</span><span class="err">$</span><span class="o">(</span><span class="n">h1</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Yandeks"</span><span class="o">));</span>
</code></pre></div></div>
<p>Для этого пришлось сделать большой рефакторинг, так чтобы внутри самого селенида нигде не использовались старые добрые
статические методы <code class="language-plaintext highlighter-rouge">open()</code>, <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code>. Везде, где нужен вебдрайвер, селенид теперь использовал параметр <code class="language-plaintext highlighter-rouge">SelenideDriver</code>.
Да-да, пришлось его прокидывать во все методы как параметр.</p>
<h2 id="и-вот-тут-главный-вопрос">И вот тут главный вопрос</h2>
<p>А что делать со старыми добрыми статическими методами <code class="language-plaintext highlighter-rouge">open()</code>, <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code>? Они должны откуда-то взять инстанс <code class="language-plaintext highlighter-rouge">SelenideDriver</code> и передать
его дальше в кишки селенида. Откуда его взять?</p>
<h2 id="selenide-500-статикам-наносят-удар">Selenide 5.0.0: Статикам наносят удар</h2>
<p>На тот момент этот вопрос решили так (как оказалось, это решение сильно повлияло на ход дел). <br />
В класс <code class="language-plaintext highlighter-rouge">WebDriverRunner</code> добавили третий ThreadLocal:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o"><</span><span class="nc">SelenideDriver</span><span class="o">></span> <span class="n">selenideDriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o"><>();</span>
</code></pre></div></div>
<p>Сам <code class="language-plaintext highlighter-rouge">SelenideDriver</code> - это, грубо говоря, очень простой класс с двумя полями <code class="language-plaintext highlighter-rouge">WebDriver</code> и <code class="language-plaintext highlighter-rouge">SelenideProxyServer</code>.<br />
И в целом всё хорошо работало и решало поставленную задачу.</p>
<p>Чего я не мог предусмотреть на тот момент - это того, что многие люди определяли веб-элементы в <strong>статических</strong> полях:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyPageObject</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="nc">SelenideElement</span> <span class="n">fname</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#fname"</span><span class="o">);</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="nc">SelenideElement</span> <span class="n">lname</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#lname"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Да ещё и переоткрывали браузер между тестами.</p>
<h3 id="важный-нюанс">Важный нюанс:</h3>
<p>в Selenide 5.0.0 мы сделали ещё одно улучшение.<br />
Раньше селенид автоматически открывал новый браузер, если его ещё нет, что часто вызывало недоумение, ибо браузер
иногда открывался в очень неожиданные моменты (например, при попытке залогировать ошибку, произошедшую из-за закрэшившегося браузера).</p>
<p>Конечно, это происходило из-за плохого кода тестов, но <em>мы же создали селенид, чтобы помогать людям, верно?</em><br />
Поэтому с версии 5.0.0 селенид стал чётко говорить: “Браузера нет, работать не могу. Вызови сначала метод <code class="language-plaintext highlighter-rouge">open()</code>, тогда поговорим.”</p>
<h3 id="и-что-же-свалилось">И что же свалилось?</h3>
<p>Совпадение этих двух факторов привело к следующей проблеме:</p>
<ol>
<li>Человек создаёт статическую переменную <code class="language-plaintext highlighter-rouge">static SelenideElement fname = $("#fname")</code>.</li>
<li>Этот <code class="language-plaintext highlighter-rouge">fname</code> запоминает, с каким инстансом <code class="language-plaintext highlighter-rouge">SelenideDriver</code> он был создан.</li>
<li>Человек в конце теста закрывает браузер</li>
<li>В следующем тесте человек открывает новый браузер и снова обращается к статическому полю <code class="language-plaintext highlighter-rouge">fname</code>.</li>
<li>А <code class="language-plaintext highlighter-rouge">fname</code> обращается к своему <code class="language-plaintext highlighter-rouge">SelenideDriver</code>, с которым он был изначально создан.</li>
<li>И кидает ошибку, потому что тот браузер уже закрылся.</li>
</ol>
<p>Больше года - с сентября 2018 до октября 2019 - я пытался объяснить всем, что статические переменные - это зло, не надо их использовать. <br />
Сделал даже специально доклад <a href="https://www.youtube.com/watch?v=4JJNccWtdNI">“Антистатик”</a>.</p>
<p>Но в итоге сдался из тех соображений, что слишком много людей, которым пришлось бы переписывать свои тесты.
Даже если они согласны насчёт злостности статических переменных, переписать всё - это зачастую неподъёмная задача.<br />
<em>Мы же создали селенид, чтобы помогать людям, верно?</em></p>
<h2 id="selenide-540-статики-победили">Selenide 5.4.0: Статики победили</h2>
<p>Как в итоге решили эту проблему?</p>
<p>Довольно просто. Мы заменили <code class="language-plaintext highlighter-rouge">ThreadLocal<SelenideDriver></code> на статическую переменную (да, вы не ослышались :))</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">SelenideDriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocalSelenideDriver</span><span class="o">();</span>
</code></pre></div></div>
<p>Теперь наш “статический” <code class="language-plaintext highlighter-rouge">SelenideDriver</code> - один на всех. Он никогда не закрывается. Все статические поля, созданные с
ним, будут жить вечно. Но он и не хранит в себе поля <code class="language-plaintext highlighter-rouge">WebDriver</code> и <code class="language-plaintext highlighter-rouge">SelenideProxyServer</code>. Он каждый берёт их из
тредлокалов <code class="language-plaintext highlighter-rouge">WebDriverRunner</code>.</p>
<h3 id="ps">P.S.</h3>
<p>Поэтому в версии 5.4.0 пропал метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.getSelenideDriver()</code>.</p>
<p>Я очень удивился, что многие люди уже успели его использовать. Люди, я вас не понимаю! <em>Как вы умудряетесь всё так неправильно использовать?</em><br />
Ок, я не подумав, сделал его публичным. Моя ошибка. Но он не упомянут ни в каких пресс-релизах, ни в какой документации.
Как могла кому-то прийти в голову идея его использовать? Как эта магия вообще происходит?</p>
<h2 id="что-теперь">Что теперь?</h2>
<p>Мы снова вернули возможность объявлять <code class="language-plaintext highlighter-rouge">SelenideElement</code> в статических полях.<br />
Но пожалуйста, не злоупотребляйте этим. <br />
Я всё ещё это не одобряю. :)</p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Как протестировать защиту от CSRF атаки2019-12-07T00:00:00+00:00http://ru.selenide.org/2019/12/07/advent-calendar-csrf-protection
<p>Привет!</p>
<p>Сегодня 7 день рождественского календаря Selenide.<br />
И сегодня мы поговорим о тестировании безопасности.</p>
<h1 id="что-такое-csrf">Что такое CSRF?</h1>
<p>Одна из самых распространённых атак - это CSRF (Cross-Site Request Forgery), или подделка межсайтовых запросов.
Подробно о ней я рассказывал в видосике <a href="https://www.youtube.com/watch?v=z-aEjd22BGU">WTF Security</a>, а сейчас для нас
важно то, что защиту от этой атаки легко протестировать вашими обычными автотестами.</p>
<p>Для того, чтобы веб-приложение было защищено от CSRF-атак, с каждым его POST-запросом должен посылаться один хитрый
параметр. Он обычно называется <code class="language-plaintext highlighter-rouge">authenticityToken</code> (хотя и не обязательно). Когда вы заходите в одной вкладке, скажем,
в свой интернет-банк, а в другой вкладке на сайт с котиками, зловредный код на этом сайте может послать POST-запрос вашему
банку для совершения платежа на счёт хакера. Хакер может послать счёт и сумму, а также все cookies из вашей вкладки, но
он не сможет послать <code class="language-plaintext highlighter-rouge">authenticityToken</code> (потому, что он уникальный для каждой сессии и не хранится в cookies).</p>
<p>А типичная ошибка такая: веб-приложение либо не посылает <code class="language-plaintext highlighter-rouge">authenticityToken</code> на сервер с каким-то POST-запросом,
либо на сервере не проверяет пришедший токен.</p>
<p><br /></p>
<h3 id="короче-как-проверить-защищённость">Короче, как проверить защищённость?</h3>
<p>У вас уже есть куча автотестов, покрывающих весь критичный функционал вашего веб-приложения.<br />
Мы убьём двух зайцев разом: во время запуска этих тестов мы будем перехватывать каждый POST-запрос и посылать точно такой
же, но с изменённым <code class="language-plaintext highlighter-rouge">authenticityToken</code>. И будем проверять, что сервер вернул ошибку. Обычно это ошибка 403 Forbidden.</p>
<p><br /></p>
<h3 id="звучит-сложно-как-это-закодить">Звучит сложно. Как это закодить?</h3>
<p>Не так уж сложно. <br />
Как вы знаете, селенид может запускать свой встроенный прокси-сервер. Изначально он использовался для скачивания файлов,
но к нему можно добавлять и свои “листенеры”, которые могут перехватывать все запросы между браузером и тестируемым приложением.<br />
Это мы и сделаем.</p>
<p><br /></p>
<h4 id="шаг-1-включаем-селенидовский-прокси-сервер">Шаг 1. Включаем селенидовский прокси-сервер</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</code></pre></div></div>
<p>(это нужно сделать ДО открытия браузера)</p>
<p><br /></p>
<h4 id="шаг-2-добавляем-листенер-для-прокси-сервера">Шаг 2. Добавляем листенер для прокси-сервера</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">BaseTest</span> <span class="o">{</span>
<span class="kd">private</span> <span class="nc">AuthenticityTokenChecker</span> <span class="n">authenticityTokenChecker</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AuthenticityTokenChecker</span><span class="o">();</span>
<span class="c1">// и где-то сразу после open("http://..."):</span>
<span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">getProxy</span><span class="o">().</span><span class="na">addRequestFilter</span><span class="o">(</span><span class="n">authenticityTokenChecker</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>На данный момент это можно сделать только ПОСЛЕ открытия браузера, что иногда не очень удобно.<br />
Я надеюсь, в следующей версии селенида мы сможем сделать так, чтобы листенеры для прокси-сервера можно было добавлять в любой момент.</p>
<p><br /></p>
<h4 id="шаг-3-реализуем-authenticitytokenchecker">Шаг 3. Реализуем AuthenticityTokenChecker</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">com.codeborne.selenide.Configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.netty.handler.codec.http.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">net.lightbody.bmp.filters.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">net.lightbody.bmp.util.*</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">AuthenticityTokenChecker</span> <span class="kd">implements</span> <span class="nc">RequestFilter</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">HttpClient</span> <span class="n">httpClient</span> <span class="o">=</span> <span class="nc">HttpClient</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">build</span><span class="o">();</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">unprotectedUrls</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o"><>(</span><span class="mi">1</span><span class="o">);</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">reset</span><span class="o">()</span> <span class="o">{</span>
<span class="n">unprotectedUrls</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="nf">getUnprotectedUrls</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">unprotectedUrls</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">HttpResponse</span> <span class="nf">filterRequest</span><span class="o">(</span><span class="nc">HttpRequest</span> <span class="n">httpRequest</span><span class="o">,</span> <span class="nc">HttpMessageContents</span> <span class="n">contents</span><span class="o">,</span> <span class="nc">HttpMessageInfo</span> <span class="n">httpMessageInfo</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">httpRequest</span><span class="o">.</span><span class="na">getMethod</span><span class="o">()</span> <span class="o">!=</span> <span class="nc">HttpMethod</span><span class="o">.</span><span class="na">POST</span><span class="o">)</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// игнорируем не-POST запросы</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="nc">Configuration</span><span class="o">.</span><span class="na">baseUrl</span><span class="o">))</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// игнорируем запросы хрома к google.com и подобным ресурсами</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">Этому</span> <span class="n">урлу</span> <span class="n">разрешено</span> <span class="n">и</span> <span class="n">без</span> <span class="n">токена</span><span class="o">)</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// некоторым post-запросам не требуется защита</span>
<span class="nc">String</span> <span class="n">body</span> <span class="o">=</span> <span class="n">contents</span><span class="o">.</span><span class="na">getTextContents</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">body</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"authenticityToken="</span><span class="o">))</span> <span class="o">{</span>
<span class="n">unprotectedUrls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"No 'authenticityToken=' found for "</span> <span class="o">+</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">()</span> <span class="o">+</span> <span class="s">" in "</span> <span class="o">+</span> <span class="n">body</span><span class="o">);</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">sendHackedPostRequest</span><span class="o">(</span><span class="n">httpRequest</span><span class="o">,</span> <span class="n">contents</span><span class="o">);</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Обратите внимание: <code class="language-plaintext highlighter-rouge">return null;</code> значит “не изменяй запрос”. То есть браузер пошлёт изначальный запрос на сервер без изменений,
и нормальное течение вашего теста не будет нарушено.</p>
<p><br /></p>
<h4 id="шаг-4-посылаем-хакнутый-post-запрос">Шаг 4. Посылаем хакнутый POST-запрос</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">sendHackedPostRequest</span><span class="o">(</span><span class="nc">HttpRequest</span> <span class="n">httpRequest</span><span class="o">,</span> <span class="nc">HttpMessageContents</span> <span class="n">contents</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span>
<span class="c1">// Над этой строчкой придётся поработать. </span>
<span class="c1">// Формат запроса (и даже имя параметра "authenticityToken") может зависеть от вашего приложения.</span>
<span class="c1">// Обратите внимание, что параметров "authenticityToken" может быть несколько (сразу кидайте ошибку, если они разные).</span>
<span class="c1">// Если в POST-запросе сабмитится форма, да ещё и с файлами, параметр "authenticityToken" придётся выцепить немножко по-другому. </span>
<span class="nc">String</span> <span class="n">hackedBody</span> <span class="o">=</span> <span class="n">contents</span><span class="o">.</span><span class="na">getTextContents</span><span class="o">()</span>
<span class="o">.</span><span class="na">replace</span><span class="o">(</span><span class="s">"authenticityToken=1234567890"</span><span class="o">).</span><span class="na">на</span><span class="o">(</span><span class="s">"authenticityToken=hack-me-if-you-can"</span><span class="o">);</span>
<span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span><span class="o">.</span><span class="na">Builder</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">()</span>
<span class="o">.</span><span class="na">uri</span><span class="o">(</span><span class="no">URI</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">()))</span>
<span class="o">.</span><span class="na">timeout</span><span class="o">(</span><span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">Map</span><span class="o">.</span><span class="na">Entry</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">></span> <span class="n">header</span> <span class="o">:</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">headers</span><span class="o">())</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">restrictedHeaders</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">header</span><span class="o">.</span><span class="na">getKey</span><span class="o">().</span><span class="na">toLowerCase</span><span class="o">()))</span> <span class="o">{</span>
<span class="n">builder</span><span class="o">.</span><span class="na">header</span><span class="o">(</span><span class="n">header</span><span class="o">.</span><span class="na">getKey</span><span class="o">(),</span> <span class="n">header</span><span class="o">.</span><span class="na">getValue</span><span class="o">());</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span> <span class="n">request</span> <span class="o">=</span> <span class="n">builder</span>
<span class="o">.</span><span class="na">POST</span><span class="o">(</span><span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span><span class="o">.</span><span class="na">BodyPublishers</span><span class="o">.</span><span class="na">ofString</span><span class="o">(</span><span class="n">hackedBody</span><span class="o">))</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Sending hacked request to {}"</span><span class="o">,</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span>
<span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpResponse</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">httpResponse</span> <span class="o">=</span> <span class="n">httpClient</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpResponse</span><span class="o">.</span><span class="na">BodyHandlers</span><span class="o">.</span><span class="na">ofString</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">httpResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">()</span> <span class="o">==</span> <span class="mi">403</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Hacked request was rejected: {} {}"</span><span class="o">,</span> <span class="n">httpResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">(),</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"HACK SUCCEEDED {} {}"</span><span class="o">,</span> <span class="n">httpResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">(),</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span>
<span class="n">unprotectedUrls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"Detected URL without authenticity token check: "</span> <span class="o">+</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Set</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">restrictedHeaders</span> <span class="o">=</span> <span class="nc">Set</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"connection"</span><span class="o">,</span> <span class="s">"content-length"</span><span class="o">,</span>
<span class="s">"date"</span><span class="o">,</span> <span class="s">"expect"</span><span class="o">,</span> <span class="s">"from"</span><span class="o">,</span> <span class="s">"host"</span><span class="o">,</span> <span class="s">"upgrade"</span><span class="o">,</span> <span class="s">"via"</span><span class="o">,</span> <span class="s">"warning"</span><span class="o">);</span>
</code></pre></div></div>
<p>Конкретно эта реализация использует <code class="language-plaintext highlighter-rouge">HttpClient</code> из Java 11, но если вы из тех бедолаг, что до сих пор сидят на Java 8,
вы можете заменить его на OkHttp, Apache Http Client или что-то подобное.</p>
<p><br /></p>
<h4 id="шаг-5-валим-тест-если-нашлись-незащищённые-запросы">Шаг 5. Валим тест, если нашлись незащищённые запросы</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">BaseTest</span> <span class="o">{</span>
<span class="nd">@Before</span> <span class="kt">void</span> <span class="nf">resetChecker</span><span class="o">()</span> <span class="o">{</span>
<span class="n">authenticityTokenChecker</span><span class="o">.</span><span class="na">reset</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@After</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">verifyThatAllPostRequestsAreProtectedWithAuthenticityToken</span><span class="o">()</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">authenticityTokenChecker</span><span class="o">.</span><span class="na">getUnprotectedUrls</span><span class="o">().</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
<span class="n">fail</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">authenticityTokenChecker</span><span class="o">.</span><span class="na">getUnprotectedUrls</span><span class="o">()));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><br /></p>
<h2 id="что-теперь">Что теперь?</h2>
<p>Мы убили двух зайцев и научились автоматически проверять защиту от CSRF-атак при запуске наших обычных автотестов.<br />
Это не фантазия, мы реально так сделали на одном проекте и нашли две серьёзных уязвимости в настоящем интернет-банке.</p>
<p>Хорошо, но этого мало. На свете ещё куча атак.</p>
<p>Пересмотрите <a href="https://www.youtube.com/watch?v=z-aEjd22BGU">WTF Security</a>, почитывайте <a href="https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project">OWASP 10</a>
и мыслите креативно, а как ещё можно убить третьего и четвёртого зайца вашими автотестами.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Как визуализировать клик2019-12-06T00:00:00+00:00http://ru.selenide.org/2019/12/06/advent-calendar-visualize-click
<p>Привет!</p>
<p>На дворе 6 декабря, мы продолжаем рождественский календарь Selenide.<br />
И сегодня я покажу один простой способ поймать некоторые нестабильные тесты.</p>
<h1 id="что-не-так-с-кликами">Что не так с кликами?</h1>
<p>Вечная проблема - флейки тесты (моргающие, нестабильные).<br />
Одна из типичных причин - клик не срабатывает.<br />
Как я подробно рассказывал <a href="https://www.youtube.com/watch?v=ibx8nVvt-Js">в этом видео</a> на DelEx 2019, клик в селениуме
часто не срабатывает потому, что элемент в этот момент двигался, растягивался, сжимался, или внезапно появился какой-то
другой элемент и перекрыл наш. И клик попал в него.</p>
<h3 id="почему-селенид-это-уже-не-решил">Почему селенид это уже не решил?</h3>
<p>Глобальное решение могло бы быть такое: метод <code class="language-plaintext highlighter-rouge">$.click()</code>, перед тем, как кликнуть, сначала ждёт окончания любых движений и анимаций.
Но пока в селениде такого нет: я боюсь, что сделать абсолютно универсальную “ожидалку” на все случаи жизни просто невозможно.
Проекты разные, UI разный, фреймворки разные, анимации разные. Если у вас есть идеи, как это можно было бы реализовать -
обращайтесь, запилим.</p>
<p>А пока нам остаётся делать какие-то свои “ожидалки” в каждом конкретном проекте.</p>
<p>А я хочу показать способ, как узнать, куда клик попал на самом деле. Это не решит проблему полностью, но хотя бы
поможет её локализовать.</p>
<h3 id="как-визулизировать-клик">Как визулизировать клик?</h3>
<p>Можно добавить такой вот JS код в своё приложение. Он слушает любые клики на странице, и подсвечивает зелёной рамкой тот
элемент, которые реально был кликнут. Это поможет вам на скриншоте упавшего теста понять, что клик попал не в тот
элемент, который вам был нужен.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">#</span><span class="o">{</span><span class="k">if</span> <span class="n">test</span><span class="o">}</span>
<span class="o"><</span><span class="n">script</span><span class="o">></span>
<span class="n">function</span> <span class="nf">onClick</span><span class="o">(</span><span class="n">event</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">var</span> <span class="n">e</span> <span class="o">=</span> <span class="n">event</span> <span class="o">||</span> <span class="n">window</span><span class="o">.</span><span class="na">event</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">target</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">target</span> <span class="o">||</span> <span class="n">e</span><span class="o">.</span><span class="na">srcElement</span><span class="o">;</span>
<span class="n">target</span><span class="o">.</span><span class="na">style</span><span class="o">[</span><span class="err">'</span><span class="n">box</span><span class="o">-</span><span class="n">sizing</span><span class="err">'</span><span class="o">]</span> <span class="o">=</span> <span class="err">'</span><span class="n">border</span><span class="o">-</span><span class="n">box</span><span class="err">'</span><span class="o">;</span>
<span class="n">target</span><span class="o">.</span><span class="na">style</span><span class="o">[</span><span class="err">'</span><span class="n">border</span><span class="err">'</span><span class="o">]</span> <span class="o">=</span> <span class="err">'</span><span class="mi">2</span><span class="n">px</span> <span class="n">solid</span> <span class="n">green</span><span class="err">'</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">document</span><span class="o">.</span><span class="na">addEventListener</span><span class="o">(</span><span class="err">'</span><span class="n">click</span><span class="err">'</span><span class="o">,</span> <span class="n">onClick</span><span class="o">);</span>
<span class="o"></</span><span class="n">script</span><span class="o">></span>
<span class="err">#</span><span class="o">{/</span><span class="k">if</span><span class="o">}</span>
</code></pre></div></div>
<p>Естественно, приведённый код зависит от вашего фреймворка, приложения, проекта и т.д. <br />
Цвет и толщина рамки - от вашего дизайна. Возможно даже, это должна быть не рамка, а что-то другое.<br />
Главное, чтобы было видно, какой элемент был реально кликнут.</p>
<p>Здесь можно посмотреть один из примеров реализации: <a href="https://github.com/selenide-examples/gmail/blob/master/test/org/selenide/examples/gmail/Highlighter.java">Highlighter</a>.</p>
<h2 id="что-теперь">Что теперь?</h2>
<p>Если у вас есть мигающие тесты (а они есть у всех) - начните с этого шага, и вы хотя бы увидите, куда попал клик.
А дальше уже будем разбираться.</p>
<p><br /></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Как быстро проверить размер?2019-12-04T00:00:00+00:00http://ru.selenide.org/2019/12/04/advent-calendar-effective-size-check
<p>Драсьте!</p>
<p>На дворе 4 декабря, перед вами открыт рождественский календарь Selenide, и в сегодняшнем посте я покажу один простой трюк, как сделать некоторые проверки быстрее.</p>
<h1 id="как-быстро-проверить-размер-коллекции">Как быстро проверить размер коллекции?</h1>
<p>В Selenide есть удобные методы для проверки коллекций. Например, вот так можно проверить, что в некотором списке книг ровно три книги:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"#books .book"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>А если страница реализована так, что в HTML книг больше, а ненужные книги просто прячутся, то можно отфильтровать коллекцию перед проверкой:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"#books .book"</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">visible</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="проблема">Проблема</h2>
<p>Если таких книг на странице много (скажем, несколько десятков и больше), то такая фильтрация может быть медленной
(скажем, от нескольких секунд и выше). Причина в том, что метод <code class="language-plaintext highlighter-rouge">filter(visible)</code> должен вызвать метод
<code class="language-plaintext highlighter-rouge">WebElement.isDisplayed()</code> в цикле для каждого элемента коллекции по отдельности. А каждый вызов - это отдельное обращение
к вебдрайверу, отдельный http запрос и т.д. В сумме накапливается.</p>
<p>Может быть, в вашем проекте тесты бегут трое суток, и несколько секунд погоды не делает.
Тогда можете дальше не читать.</p>
<p>Но если вы заботитесь об эффективности ваших тестов, если работаете над их ускорением - мой пост для вас.</p>
<h2 id="первая-попытка">Первая попытка</h2>
<p>Как можно ускорить эту проверку? Напрашивается первое решение: фильтровать видимые элементы на уровне селектора:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="err">$$</span><span class="o">(</span><span class="s">"#books .book:visible"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Но так не работает.</p>
<p>Проблема в том, что CSS-селектора <code class="language-plaintext highlighter-rouge">:visible</code> не существует. Такую штуку умеет делать только JQuery.</p>
<h2 id="вторая-попытка">Вторая попытка</h2>
<p>Что же нас спасёт?</p>
<p>Вера Брежнева ошибалась: мир спасёт не красота, мир спасёт уродство. И имя ему - JavaScript.</p>
<p>Вы можете создать вспомогательный метод, который с помощью JavaScript и JQuery вернёт количество элементов:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">int</span> <span class="nf">sizeOf</span><span class="o">(</span><span class="nc">String</span> <span class="n">cssSelector</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Number</span> <span class="n">count</span> <span class="o">=</span> <span class="n">executeJavaScript</span><span class="o">(</span><span class="s">"return $(arguments[0]).length"</span><span class="o">,</span> <span class="n">cssSelector</span><span class="o">);</span>
<span class="k">return</span> <span class="n">count</span><span class="o">.</span><span class="na">intValue</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>(этот код работает только при наличии JQuery на тестируемой странице. Если JQuery нет, наверняка можно написать
аналогичный код с использованием любой другой библиотеки.)</p>
<p>И теперь эта проверка будет быстрой, потому что она обратится к браузеру всего один раз. А браузер внутри себя выполняет любые скрипты очень быстро.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span>
<span class="n">assertEquals</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">sizeOf</span><span class="o">(</span><span class="s">"#books .book:visible"</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="что-теперь">Что теперь?</h2>
<p>Познайте всю мощь JavaScript. <br />
Он позволяет ещё много разных трюков, которые помогут сделать ваши тесты быстрее и надёжнее.</p>
<p><br /></p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
setWebDriver или WebDriverProvider?2019-12-03T00:00:00+00:00http://ru.selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider
<p>Добрый вечер!</p>
<p>На дворе 3 декабря, перед вами открыт рождественский календарь Selenide, и в сегодняшнем посте я отвечу на один простой вопрос.</p>
<h1 id="что-выбрать-setwebdriver-или-webdriverprovider">Что выбрать, <code class="language-plaintext highlighter-rouge">setWebDriver()</code> или <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>?</h1>
<p>Как вы знаете, Selenide по умолчанию сам открывает и закрывает браузер. Вам для этого ничего не надо делать.</p>
<p>Но иногда вам хочется открыть браузер с какими-то специфичными настройками.
Для этого в селениде предусмотрены два разных метода. Так уж сложилось, что я никогда нигде не писал, почему понадобилось два, и как выбрать один из них.</p>
<p>Итак,</p>
<h4 id="вариант-1">Вариант 1:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browser</span> <span class="o">=</span> <span class="nc">MyWebdriverProvider</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span>
</code></pre></div></div>
<p>Этот вариант предпочтительнее в большинстве случаев. Он хорош тем, что ваш класс <code class="language-plaintext highlighter-rouge">MyWebdriverProvider</code> отвечает только за то, КАК открыть браузер
(какие ключики ему передать, где найти бинарник, вот это всё). Но не отвечает за то, КОГДА открыть и закрыть браузер.
Об этом заботится селенид.</p>
<h4 id="вариант-2">Вариант 2:</h4>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Before</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUp</span><span class="o">()</span> <span class="o">{</span>
<span class="kt">var</span> <span class="n">yandexBrowser</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">YandexDriver</span><span class="o">(...);</span>
<span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">setWebDriver</span><span class="o">(</span><span class="n">yandexBrowser</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Этот вариант хуже тем, что вам придётся самому не только открывать, но и закрывать браузер. Это часто вызывает недопонимание.
Селенид не может его закрыть, ведь он не знает, где ещё вы можете его использовать. Сам открыл - сам закрывай.</p>
<p>Отсюда назревает вопрос:</p>
<h3 id="а-зачем-он-вообще-нужен">А зачем он вообще нужен?</h3>
<p>Честно говоря, я сам долгое время не отдавал себе в этом отчёт, и лишь недавно назрело понимание. :)</p>
<p>Изначально метод <code class="language-plaintext highlighter-rouge">setWebDriver()</code> был добавлен для того, чтобы людям было легко перевести на селенид существующие
проекты (не переписывая весь проект).</p>
<p>Представьте, что в компании X уже есть стотыщ автотестов на селениуме (или HtmlElements, Thucydides, Serenity или чём-то ещё).
И они хотят новые тесты писать на селениде, а старые не трогать. Работают и работают.</p>
<p>И у них где-то в тестах уже есть код, которые открывает и закрывает браузер. И хотелось бы как-то сказать селениду,
чтобы он тоже этот браузер использовал. Вот для этого и подходит метод <code class="language-plaintext highlighter-rouge">setWebDriver()</code>. Он позволяет запускать и
старый, и новый код вперемежку, так чтобы они работали с одним и тем же браузером и не мешали друг дружке.</p>
<p>То есть применение у метода <code class="language-plaintext highlighter-rouge">setWebDriver()</code>, как вы видите, довольно узкое. Я вообще не уверен, использовали ли его вообще
хоть когда-нибудь в таком ключе.</p>
<h2 id="что-теперь">Что теперь?</h2>
<p>А если вы пишите новый проект, используйте <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>. SRP.</p>
<p><br /></p>
<h1 id="driver-factory">UPD</h1>
<p>Позже в <a href="https://ru.selenide.org/2020/11/25/selenide-5.16.2/#selenide-5.16.2">Selenide 5.16.1</a> появилась более новая возможность - <code class="language-plaintext highlighter-rouge">DriverFactory</code>.
Теперь это рекомендованный способ создавать кастомный вебдрайвер.</p>
<p>По сути она очень похожа на <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>, но даёт больше контроля над происходящим.
Вам нужно</p>
<ol>
<li>либо реализовать интерфейс <code class="language-plaintext highlighter-rouge">DriverFactory</code>,</li>
<li>либо создать подкласс <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/webdriver/ChromeDriverFactory.java">ChromeDriverFactory</a>,
<a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/webdriver/FirefoxDriverFactory.java">FirefoxDriverFactory</a>
или другого похожего класса (которые есть в составе Selenide и реализуют интерфейс <code class="language-plaintext highlighter-rouge">DriverFactory</code>) и переопределить лишь нужные методы.
Все методы в них специально сделаны маленькие и объявлены как protected.</li>
</ol>
<p>Примеры есть в <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/ChromeProfileByFactoryTest.java">тестах самого селенида</a>.</p>
<p>См. также этот <a href="https://mbbaig.blog/selenide-webdriverfactory/">пост от Boris Bay</a>.</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Как надругаться над Селенидом2019-12-02T00:00:00+00:00http://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide
<p>Всем привет!</p>
<p>На дворе 2 декабря, и мы продолжаем рождественский календарь постом про злоупотребление селенидом.</p>
<h1 id="как-надругаться-над-селенидом">Как надругаться над Селенидом</h1>
<h3 id="и-почему-это-у-многих-вызывает-трудности">(и почему это у многих вызывает трудности)</h3>
<p>Я часто получаю жалобу, что у людей не ловится ошибка вот в таком коде:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">try</span> <span class="o">{</span>
<span class="err">$</span><span class="o">(</span><span class="s">".banner"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span>
<span class="err">$</span><span class="o">(</span><span class="s">".banner .close"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span>
<span class="o">}</span>
<span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// System.out.println("Элемент не найден - значит, баннер в этот раз не появился");</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Этот тест хочет проверить, появился ли рекламный баннер. Но не сразу, а немножко подождать: вдруг баннер появится с задержкой.
И если появился, надо его закрыть.</p>
<p><br /></p>
<h1 id="почему-это-плохой-тест">Почему это плохой тест?</h1>
<p>Потому, что он <strong>медленный</strong>.</p>
<p>В большинстве случае баннер не появится, и тест будет ждать 4 секунды (или какой у вас таймаут).</p>
<p>Это ужасно. Вы профессионалы или кто? Ваша цель - сделать тесты быстрыми и надёжными. Иначе чем вы отличаетесь от обезьяны?</p>
<p><br /></p>
<h1 id="почему-это-очень-плохой-тест">Почему это очень плохой тест?</h1>
<p>Потому, что он <strong>нестабильный</strong>.</p>
<p>Иногда баннер будет появляться как раз на истёке 4 секунд.
Тест решит, что баннера нет, и пойдёт дальше, а тут оп! - и выскочит баннер. И вы получаете “моргающий” тест.</p>
<p><br /></p>
<h1 id="почему-это-очень-очень-плохой-тест">Почему это очень-очень плохой тест?</h1>
<p>Потому, что он <strong>не тестирует сам баннер</strong>.</p>
<ol>
<li>
<p>Представьте себе, что за время прогона тестов баннер так ни разу и не появился. Все тесты зелёные.
А у реальных пользователей баннер появляется, и как раз тогда случается ошибка. И всё крэшится.
Пользователи жалуются и обрушивают рейтинг вашего приложения. <br /> <br />
<em>А ваши тесты зелёные.</em></p>
</li>
<li>
<p>Или наоборот, во время прогона тестов баннер появляется постоянно. Ну вот так сложилось в тестовой среде.
А у реальных пользователей баннер появляется редко, и как раз без баннера случается ошибка. Всё крэшится.
Пользователи жалуются и обрушивают рейтинг вашего приложения. <br /> <br />
<em>А ваши тесты зелёные.</em></p>
</li>
</ol>
<p><br /></p>
<h1 id="что-же-делать">Что же делать?</h1>
<p>Очевидно, что у вас должно быть как минимум два теста:</p>
<ol>
<li>Случай, когда баннер выскакивает, и</li>
<li>Случай, когда баннер не выскакивает.</li>
</ol>
<p>Значит, у вас должна быть возможность как-то управлять показом баннера.
Либо через “ручку” (api) тестируемого приложения, либо через какую-то админку, либо запуском SQL в базе. Как угодно.
Если такой ручки нет, надо пойти договориться с разработчиками.</p>
<p>Только не надо мне говорить, что <a href="https://asolntsev.livejournal.com/77819.html">договориться не получится</a>. Вы профессионалы или кто?</p>
<p>Если не получится, то стирайте весь этот тест. Не получилось так не получилось.</p>
<p>Подробнее про ручки и управление тестовой средой см. в докладе <a href="https://www.youtube.com/watch?v=ePvrXUCeAr8">Arrange, mazafaka!</a></p>
<p><br /></p>
<p>И наконец, на десерт:</p>
<h1 id="почему-ошибка-не-ловится">Почему ошибка не ловится?</h1>
<p>Вы уже догадались, почему ошибка не ловится?</p>
<p>Всё дело в иерархии исключений в Java. Базовый класс не <code class="language-plaintext highlighter-rouge">Exception</code>, а <code class="language-plaintext highlighter-rouge">Throwable</code>.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Throwable</code>
<ul>
<li><code class="language-plaintext highlighter-rouge">Exception</code>
<ul>
<li><code class="language-plaintext highlighter-rouge">RuntimeException</code>
<ul>
<li><code class="language-plaintext highlighter-rouge">WebDriverException</code></li>
</ul>
</li>
</ul>
</li>
<li><code class="language-plaintext highlighter-rouge">Error</code>
<ul>
<li><code class="language-plaintext highlighter-rouge">AssertionError</code>
<ul>
<li><code class="language-plaintext highlighter-rouge">UIAssertionError</code>
<ul>
<li><code class="language-plaintext highlighter-rouge">ElementShould</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Именно поэтому, когда вы ловите <code class="language-plaintext highlighter-rouge">catch (Exception e)</code>, мимо вас пролетают все подклассы <code class="language-plaintext highlighter-rouge">Error</code>, в том числе все селенидовские ошибки.</p>
<p>“Ну отлично, буду ловить <code class="language-plaintext highlighter-rouge">catch (Error e)</code>” - решите вы. НЕТ! Прошу вас, НЕТ!</p>
<p>Читаем javadoc к классу <code class="language-plaintext highlighter-rouge">java.lang.Error</code>:</p>
<blockquote>
<p>An Error is a subclass of Throwable that indicates serious problems that a reasonable application <strong>should not try to catch</strong>.
Most such errors are abnormal conditions.</p>
</blockquote>
<h2 id="что-теперь">Что теперь?</h2>
<p>Я надеюсь, вы перестанете ловить ошибки в тестах. Тесты для того и созданы, чтобы сообщать об ошибках, а не ловить их.</p>
<p>Надеюсь, вы посмотрите <a href="https://www.youtube.com/watch?v=ePvrXUCeAr8">Arrange, mazafaka!</a> и возьмёте управление тестовой средой под свой контроль.</p>
<p>И решите для себя, тварь вы дрожащая, или право имеете?</p>
<p><br /></p>
<p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p>
<p>ru.selenide.org</p>
Главный Алгоритм2019-12-01T00:00:00+00:00http://ru.selenide.org/2019/12/01/advent-calendar-main-algorithm
<p>Всем привет!</p>
<p>В западном мире начался период “Адвент”, или ожидание Рождества.</p>
<p>Дети распаковывают свои “рождественские календари”, в которых открывают каждый день по окошечку (и находят под ним шоколадку).
В Java мире, например, ведут <a href="https://www.javaadvent.com/">Java Advent Calendar</a>, в котором разные авторы каждый день публикуют по статье.
Я тоже в нём участвовал пару раз (<a href="https://www.javaadvent.com/2017/12/flaky-tests.html">2017</a> и
<a href="https: