Selenide Selenide - Andrei Solntsev http://ru.selenide.org/rss http://ru.selenide.org 2022-11-29T14:08:59+00:00 2022-11-29T14:08:59+00:00 1800 Вышла 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&amp;ab_channel=DEVCLUB.EU&amp;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">&lt;select&gt;</code> через JavaScript</h3> <p>Это должно сделать работу с селектами быстрее. И теперь селенид выкидывает более подробную ошибку, если <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> (или <code class="language-plaintext highlighter-rouge">&lt;option&gt;</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">&lt;select</span> <span class="na">id=</span><span class="s">"region"</span><span class="nt">&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"belgorod"</span><span class="nt">&gt;</span>Belgorod<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"kherson"</span> <span class="na">disabled</span><span class="nt">&gt;</span>Kherson<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"zaporozhia"</span> <span class="na">disabled</span><span class="nt">&gt;</span>Zaporozhia<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;/select&gt;</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&amp;ab_channel=DEVCLUB.EU">Как законтрибьютить в опенсорс, чтобы не сгореть со стыда</a></li> <li><a href="https://www.youtube.com/watch?v=JKxzELiwO_o&amp;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&amp;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> http://ru.selenide.org/2022/11/21/selenide-6.10.0/ http://ru.selenide.org/2022/11/21/selenide-6.10.0 2022-11-21T00:00:00+00:00 Вышла 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">-&gt;</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> -&gt; <code class="language-plaintext highlighter-rouge">192.168.0.18</code></li> <li><code class="language-plaintext highlighter-rouge">ClientUtil.getConnectableAddress()</code> -&gt; <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&amp;ab_channel=SQAANALYSTSECR">Selenide + Playwright Java = объединяй и властвуй</a> - конференция SQA Days EA, 01.10.2022</li> <li>Доклад Алексея Виноградова <a href="https://www.youtube.com/watch?v=MLxf9q9qXu4&amp;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&amp;ab_channel=FoilArmsandHog">по ирландским комикам</a>!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/10/07/selenide-6.9.0/ http://ru.selenide.org/2022/10/07/selenide-6.9.0 2022-10-07T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2022/09/27/selenide-6.8.1/ http://ru.selenide.org/2022/09/27/selenide-6.8.1 2022-09-27T00:00:00+00:00 Вышла 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&amp;ab_channel=MasyanyaKuvaeva">Масяню!</a></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/09/24/selenide-6.8.0/ http://ru.selenide.org/2022/09/24/selenide-6.8.0 2022-09-24T00:00:00+00:00 Вышла 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&amp;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&amp;list=PL6AdzyjjD5HC4NJuc083bzFq86JekmASF&amp;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> http://ru.selenide.org/2022/09/05/selenide-6.7.4/ http://ru.selenide.org/2022/09/05/selenide-6.7.4 2022-09-05T00:00:00+00:00 Вышла 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">&lt;select&gt;</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> http://ru.selenide.org/2022/08/27/selenide-6.7.3/ http://ru.selenide.org/2022/08/27/selenide-6.7.3 2022-08-27T00:00:00+00:00 Selenide puzzler <p><br /></p> <h1 id="привет">Привет!</h1> <p>Вы любите пазлеры? Соскучались по пазлерам? В последний раз мы показывали пазлеры аж в 2017 году, кто не смотрел - <a href="https://www.youtube.com/watch?v=y-ZyxTWHH08&amp;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> http://ru.selenide.org/2022/08/22/selenide-puzzler/ http://ru.selenide.org/2022/08/22/selenide-puzzler 2022-08-22T00:00:00+00:00 Вышла 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">&lt;</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&amp;t=318s&amp;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> http://ru.selenide.org/2022/08/14/selenide-6.7.2/ http://ru.selenide.org/2022/08/14/selenide-6.7.2 2022-08-14T00:00:00+00:00 Вышла 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">&lt;div</span> <span class="na">id=</span><span class="s">"freedom-to"</span><span class="nt">&gt;</span>Britney Spears<span class="nt">&lt;/div&gt;</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&amp;ab_channel=KVHigh-TechClub">Пацан накодил - пацан протестил</a> - Career Day, Минск, 04.06.2022</li> <li><a href="https://www.youtube.com/watch?v=-KGtZoFVzr8&amp;list=PL9Z-JgiTsOYRfoG_mcRBlTUIFPIknhQ6S">Extending open-source libraries: Selenide &amp; Selenium</a> - Selenium Conf, 30.07.2022</li> <li><a href="https://www.youtube.com/watch?v=SqiYAJfpQwY&amp;list=PLULFH3b6unlcUrwT9hJycTvAWPFqXZO4m&amp;ab_channel=QAClubLviv">Автоматизація з Selenide, ввідне заняття</a> by Роман Маринский</li> <li><a href="https://www.youtube.com/watch?v=-c5XT2v5gRY&amp;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> http://ru.selenide.org/2022/08/04/selenide-6.7.0/ http://ru.selenide.org/2022/08/04/selenide-6.7.0 2022-08-04T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2022/07/01/selenide-6.6.6/ http://ru.selenide.org/2022/07/01/selenide-6.6.6 2022-07-01T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2022/06/20/selenide-6.6.4/ http://ru.selenide.org/2022/06/20/selenide-6.6.4 2022-06-20T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2022/06/12/selenide-6.6.3/ http://ru.selenide.org/2022/06/12/selenide-6.6.3 2022-06-12T00:00:00+00:00 Вышла 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 -&gt; 4.2.1</a>.</li> <li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">5.1.1 -&gt; 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> http://ru.selenide.org/2022/06/08/selenide-6.6.0/ http://ru.selenide.org/2022/06/08/selenide-6.6.0 2022-06-08T00:00:00+00:00 Вышла 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">&lt;input type=date&gt;</code></h3> <p>Оказалось, что выбрать дату в селениуме не так-то просто.<br /> Если в вашем приложении есть элемент вида <code class="language-plaintext highlighter-rouge">&lt;input type=date&gt;</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">&lt;input&gt;</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 -&gt; 4.1.4</a>.</li> <li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">5.1.0 -&gt; 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 -&gt; 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 -&gt; 4.1.77.Final</a></li> <li>LittleProxy <a href="https://github.com/LittleProxy/LittleProxy/releases">2.0.7 -&gt; 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 -&gt; Delete” вместо “Home -&gt; Shift+A -&gt; 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&amp;loop=true&amp;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> http://ru.selenide.org/2022/05/17/selenide-6.5.0/ http://ru.selenide.org/2022/05/17/selenide-6.5.0 2022-05-17T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2022/04/07/selenide-6.4.0/ http://ru.selenide.org/2022/04/07/selenide-6.4.0 2022-04-07T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2022/02/07/selenide-6.3.0/ http://ru.selenide.org/2022/02/07/selenide-6.3.0 2022-02-07T00:00:00+00:00 Вышла 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-для-большинства-селенидовских-ошибок">Добавили ссылку “&lt;Click to see difference&gt;” для большинства селенидовских ошибок</h1> <p>Как вы помните, в <a href="/2021/09/28/selenide-5.25.0/">Selenide 5.25.0</a> мы добавили поддержку OpenTest4J, в результате чего у селенидовских ошибок в IDE появилась ссылочка “&lt;Click to see difference&gt;”, позволяющая удобно посмотреть различия между ожидаемым и актуальным значением.</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(&lt;условие&gt;)</code>. Хочешь проверить коллекцию элементов на соответствие какому-то условию - передай нужное условие. Не нашёл готового условия - напиши своё, благо это легко.</p> <h3 id="the-ошибка">The ошибка</h3> <p>Но в те давние времена случилось так, что я наследовал класс <code class="language-plaintext highlighter-rouge">ElementsCollection</code> от <code class="language-plaintext highlighter-rouge">AbstractList&lt;SelenideElement&gt;</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> http://ru.selenide.org/2022/01/10/selenide-6.2.0/ http://ru.selenide.org/2022/01/10/selenide-6.2.0 2022-01-10T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/11/24/selenide-6.1.1/ http://ru.selenide.org/2021/11/24/selenide-6.1.1 2021-11-24T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/11/23/selenide-6.1.0/ http://ru.selenide.org/2021/11/23/selenide-6.1.0 2021-11-23T00:00:00+00:00 Вышла 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> -&gt; <code class="language-plaintext highlighter-rouge">ElementsCollection.shouldHave(size())</code></li> <li><code class="language-plaintext highlighter-rouge">$.waitUntil(_, timeout)</code> -&gt; <code class="language-plaintext highlighter-rouge">$.should(_, Duration.ofMillis(timeout))</code></li> <li><code class="language-plaintext highlighter-rouge">$.waitWhile(_, timeout)</code> -&gt; <code class="language-plaintext highlighter-rouge">$.shouldNot(_, Duration.ofMillis(timeout))</code></li> <li><code class="language-plaintext highlighter-rouge">Condition.disappears</code> -&gt; <code class="language-plaintext highlighter-rouge">Condition.hidden</code></li> <li><code class="language-plaintext highlighter-rouge">Condition.matchesText</code> -&gt; <code class="language-plaintext highlighter-rouge">Condition.matchText</code></li> <li><code class="language-plaintext highlighter-rouge">Selenide.close</code> -&gt; <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">&lt;select&gt;</code> и <code class="language-plaintext highlighter-rouge">&lt;input type=radio&gt;</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> http://ru.selenide.org/2021/10/25/selenide-6.0.1/ http://ru.selenide.org/2021/10/25/selenide-6.0.1 2021-10-25T00:00:00+00:00 Вышла 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">&lt;Click to see difference&gt;</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&amp;list=PLsVTVVvrKX9td9Zm_4nF6Ywlz6gC5_e7K&amp;index=15&amp;ab_channel=Heisenbug">часть 1</a>, <a href="https://www.youtube.com/watch?v=WETyt87o_R4&amp;list=PLsVTVVvrKX9td9Zm_4nF6Ywlz6gC5_e7K&amp;index=16&amp;t=3s&amp;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 &amp; 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> http://ru.selenide.org/2021/09/28/selenide-5.25.0/ http://ru.selenide.org/2021/09/28/selenide-5.25.0 2021-09-28T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/08/29/selenide-5.24.0/ http://ru.selenide.org/2021/08/29/selenide-5.24.0 2021-08-29T00:00:00+00:00 Вышла 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&lt;String, String&gt; 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> http://ru.selenide.org/2021/07/16/selenide-5.23.0/ http://ru.selenide.org/2021/07/16/selenide-5.23.0 2021-07-16T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/07/05/selenide-5.22.3/ http://ru.selenide.org/2021/07/05/selenide-5.22.3 2021-07-05T00:00:00+00:00 Вышла 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">-&gt;</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">&lt;select&gt;</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&amp;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> http://ru.selenide.org/2021/06/08/selenide-5.22.0/ http://ru.selenide.org/2021/06/08/selenide-5.22.0 2021-06-08T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/05/15/selenide-5.21.0/ http://ru.selenide.org/2021/05/15/selenide-5.21.0 2021-05-15T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/03/23/selenide-5.20.1/ http://ru.selenide.org/2021/03/23/selenide-5.20.1 2021-03-23T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/02/24/selenide-5.19.0/ http://ru.selenide.org/2021/02/24/selenide-5.19.0 2021-02-24T00:00:00+00:00 Вышла 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&amp;v=fFe3reCoeBQ&amp;feature=emb_logo&amp;ab_channel=Heisenbug">Flaky tests. Порядок имеет значение</a> / Андрей Солнцев</li> <li>Воркшоп “Как начать свой проект автоматизации с нуля”: <a href="https://www.youtube.com/watch?v=1aq4gYflEho&amp;feature=youtu.be&amp;utm_campaign=Heisenbug_2020_Piter_Announce_NoParticipants_211220&amp;utm_medium=email&amp;utm_source=newsletter&amp;ab_channel=Heisenbug">часть 1</a>, <a href="https://www.youtube.com/watch?utm_campaign=Heisenbug_2020_Piter_Announce_NoParticipants_211220&amp;utm_medium=email&amp;utm_source=newsletter&amp;v=pbvJ8rmh7Ws&amp;feature=youtu.be&amp;ab_channel=Heisenbug">часть 2</a> / Андрей Солнцев, Юрий Артамонов</li> <li><a href="https://www.youtube.com/watch?v=4yq37nxkguM&amp;list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;index=12&amp;ab_channel=Heisenbug">Серверный античит: Панацея или рудимент?</a> / Евгений Ченцов, Евгений Крутских</li> <li><a href="https://www.youtube.com/watch?v=8gOkdFk2JZ8&amp;list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;index=3&amp;ab_channel=Heisenbug">Типы автоматического тестирования в IntelliJ IDEA</a> / Юрий Артамонов</li> <li><a href="https://www.youtube.com/watch?v=Prm2-c_5mYs&amp;list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;index=18&amp;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> http://ru.selenide.org/2021/02/11/selenide-5.18.1/ http://ru.selenide.org/2021/02/11/selenide-5.18.1 2021-02-11T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2021/01/23/selenide-5.18.0/ http://ru.selenide.org/2021/01/23/selenide-5.18.0 2021-01-23T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2020/12/30/selenide-5.17.2/ http://ru.selenide.org/2020/12/30/selenide-5.17.2 2020-12-30T00:00:00+00:00 Вышла 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&lt;ElementsContainer&gt;</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&lt;ElementsContainer&gt;</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> http://ru.selenide.org/2020/12/26/selenide-5.17.0/ http://ru.selenide.org/2020/12/26/selenide-5.17.0 2020-12-26T00:00:00+00:00 Вышла 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&amp;feature=youtu.be&amp;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> http://ru.selenide.org/2020/11/25/selenide-5.16.2/ http://ru.selenide.org/2020/11/25/selenide-5.16.2 2020-11-25T00:00:00+00:00 Вышла 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">&lt;</span><span class="n">div</span> <span class="n">id</span><span class="o">=</span><span class="s">"child_div1"</span><span class="o">&gt;</span><span class="nc">Son</span><span class="o">&lt;/</span><span class="n">div</span><span class="o">&gt;</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">&lt;</span><span class="n">div</span> <span class="n">id</span><span class="o">=</span><span class="s">"child_div1"</span><span class="o">&gt;</span><span class="nc">Son</span><span class="o">&lt;/</span><span class="n">div</span><span class="o">&gt;</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">--&gt;</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&amp;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> http://ru.selenide.org/2020/11/20/selenide-5.16.0/ http://ru.selenide.org/2020/11/20/selenide-5.16.0 2020-11-20T00:00:00+00:00 Почему прокси не работает в 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 -&gt; 4.1.54.Final +--- xyz.rogfam:littleproxy:2.0.0-beta-5 | +--- io.netty:netty-all:4.1.34.Final -&gt; 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> http://ru.selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid/ http://ru.selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid 2020-11-17T00:00:00+00:00 Вышла 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 элементов &lt;li&gt;</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">&lt;</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">&lt;li&gt;</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">&lt;</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">&lt;N&gt;</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">&lt;</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">&lt;a&gt;</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&amp;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> http://ru.selenide.org/2020/09/26/selenide-5.15.0/ http://ru.selenide.org/2020/09/26/selenide-5.15.0 2020-09-26T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2020/08/17/selenide-5.14.0/ http://ru.selenide.org/2020/08/17/selenide-5.14.0 2020-08-17T00:00:00+00:00 Вышла 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">&lt;h1&gt;Hello World&lt;/h1&gt;</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">&lt;form&gt;</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">&lt;input&gt;</code> для загрузки файла должен быть внутри тэга <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code>. Что как бы логично.<br /> Но оказалось, что не у всех так.</p> <p>В общем, теперь мы упростили хак и убрали зависимость от тэга <code class="language-plaintext highlighter-rouge">&lt;form&gt;</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">&lt;a href&gt;</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> http://ru.selenide.org/2020/07/08/selenide-5.13.0/ http://ru.selenide.org/2020/07/08/selenide-5.13.0 2020-07-08T00:00:00+00:00 Вышла 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&amp;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> http://ru.selenide.org/2020/05/29/selenide-5.12.2/ http://ru.selenide.org/2020/05/29/selenide-5.12.2 2020-05-29T00:00:00+00:00 Вышла 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&amp;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> http://ru.selenide.org/2020/05/25/selenide-5.12.1/ http://ru.selenide.org/2020/05/25/selenide-5.12.1 2020-05-25T00:00:00+00:00 Вышла 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&amp;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> http://ru.selenide.org/2020/05/23/selenide-5.12.0/ http://ru.selenide.org/2020/05/23/selenide-5.12.0 2020-05-23T00:00:00+00:00 Вышла 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&amp;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> http://ru.selenide.org/2020/04/21/selenide-5.11.1/ http://ru.selenide.org/2020/04/21/selenide-5.11.1 2020-04-21T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2020/04/19/selenide-5.11.0/ http://ru.selenide.org/2020/04/19/selenide-5.11.0 2020-04-19T00:00:00+00:00 Вышла 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> <h2 id="добавили-поддержку-shadow-dom">Добавили поддержку 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="selenide-больше-не-тянет-browserupproxy-по-умолчанию">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> <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="поменяли-guava-api-на-соответствующие-java-api">Поменяли 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="сделали-селенидовский-отчёт-в-allure-чуточку-красивее">Сделали селенидовский отчёт в 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="добавили-условие-imgshouldbeimage">Добавили условие <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="исправили-поиск-элементов-по-атрибуту-который-содержит-кавычки">Исправили поиск элементов по атрибуту, который содержит кавычки</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="видосики">Видосики</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&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;index=35">Flaky tests: The method</a></li> <li><a href="https://www.youtube.com/watch?v=RmaTYY3B-Wg&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;index=41">BOF: прошлое и будущее селенида</a></li> <li><a href="https://www.youtube.com/watch?v=4vI4Z6sE7OA&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;index=16">Тройничок: Selenide для Web, Android и iOS</a> – “Толерантные локаторы”!</li> </ul> <p>И ещё из того, что я успел заметить:</p> <ul> <li>Aleksei Tiurin - <a href="https://www.youtube.com/watch?v=uCAva5bi7IY&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;index=32">Solving the problems of Espresso Android autotests</a></li> <li>Michael Bodnarchuk - <a href="https://www.youtube.com/watch?v=yETWaC91t3w&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;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&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;index=2">WebdriverIO + Puppeteer. Double gun – double fun</a></li> <li>Sergey Pirogov - <a href="https://www.youtube.com/watch?v=lMD82Pj3Llk&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe">Test coverage myth busted</a></li> <li>Mikalai Alimenkou - <a href="https://www.youtube.com/watch?v=O0-vAiqGrVk&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;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> http://ru.selenide.org/2020/03/18/selenide-5.10.0/ http://ru.selenide.org/2020/03/18/selenide-5.10.0 2020-03-18T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2020/03/10/selenide-5.9.0/ http://ru.selenide.org/2020/03/10/selenide-5.9.0 2020-03-10T00:00:00+00:00 Вышла 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">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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> http://ru.selenide.org/2020/02/28/selenide-5.8.0/ http://ru.selenide.org/2020/02/28/selenide-5.8.0 2020-02-28T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2020/02/07/selenide-5.7.0/ http://ru.selenide.org/2020/02/07/selenide-5.7.0 2020-02-07T00:00:00+00:00 Вышла 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> http://ru.selenide.org/2020/01/14/selenide-5.6.1/ http://ru.selenide.org/2020/01/14/selenide-5.6.1 2020-01-14T00:00:00+00:00 Вышла 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&amp;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> http://ru.selenide.org/2019/12/26/selenide-5.6.0/ http://ru.selenide.org/2019/12/26/selenide-5.6.0 2019-12-26T00:00:00+00:00 Трюки с JavaScript <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">&lt;select&gt;</code> на какие-то свои самодельные супер-пупер красивые/динамичные/удобные элементы, сварганенные из нагромождения <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;span&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;li&gt;</code> и т.п. с кучей разных CSS-классов и стилей. Для автоматизатора это всегда боль.</p> <p>Один из таких фреймворков - Bootstrap. И в нём тоже есть свой <code class="language-plaintext highlighter-rouge">&lt;select&gt;</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> http://ru.selenide.org/2019/12/24/advent-calendar-javascript-tricks/ http://ru.selenide.org/2019/12/24/advent-calendar-javascript-tricks 2019-12-24T00:00:00+00:00 Defaŭlta 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">&lt;plugin&gt;</span> <span class="nt">&lt;artifactId&gt;</span>maven-surefire-plugin<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>2.xx.yy<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;configuration&gt;</span> <span class="nt">&lt;systemPropertyVariables&gt;</span> ... <span class="nt">&lt;chromeoptions.prefs&gt;</span>intl.accept_languages=de<span class="nt">&lt;/chromeoptions.prefs&gt;</span> <span class="nt">&lt;/systemPropertyVariables&gt;</span> <span class="nt">&lt;/configuration&gt;</span> <span class="nt">&lt;/plugin&gt;</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> http://ru.selenide.org/2019/12/22/advent-calendar-defaulta-lingvo/ http://ru.selenide.org/2019/12/22/advent-calendar-defaulta-lingvo 2019-12-22T00:00:00+00:00 Теория большого вейта <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">&lt;</span><span class="n">timeOutForElement</span><span class="o">&gt;))</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">(&lt;</span><span class="n">cssSelector</span><span class="o">&gt;)));</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">&lt;</span><span class="n">timeOutForElement</span><span class="o">&gt;))</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">(&lt;</span><span class="n">cssSelector</span><span class="o">&gt;)));</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">(&lt;</span><span class="n">cssSelector</span><span class="o">&gt;).</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">(&lt;</span><span class="n">cssSelector</span><span class="o">&gt;).</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">(&lt;</span><span class="n">expectedText</span><span class="o">&gt;));</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> http://ru.selenide.org/2019/12/20/advent-calendar-big-wait-theory/ http://ru.selenide.org/2019/12/20/advent-calendar-big-wait-theory 2019-12-20T00:00:00+00:00 Как получить сетевые запросы с помощью прокси <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">&lt;</span><span class="nc">HarEntry</span><span class="o">&gt;</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> http://ru.selenide.org/2019/12/18/advent-calendar-network-logs-with-proxy/ http://ru.selenide.org/2019/12/18/advent-calendar-network-logs-with-proxy 2019-12-18T00:00:00+00:00 Как получить логи браузера через JavaScript <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> http://ru.selenide.org/2019/12/17/advent-calendar-browser-logs-with-js/ http://ru.selenide.org/2019/12/17/advent-calendar-browser-logs-with-js 2019-12-17T00:00:00+00:00 Как получить логи браузера <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 -&gt; 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> http://ru.selenide.org/2019/12/16/advent-calendar-browser-logs/ http://ru.selenide.org/2019/12/16/advent-calendar-browser-logs 2019-12-16T00:00:00+00:00 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> http://ru.selenide.org/2019/12/15/advent-calendar-drag-and-drop/ http://ru.selenide.org/2019/12/15/advent-calendar-drag-and-drop 2019-12-15T00:00:00+00:00 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">&lt;</span><span class="nc">Some</span> <span class="n">selector</span><span class="o">&gt;;</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">(&lt;</span><span class="n">some</span> <span class="n">selector</span><span class="o">&gt;);</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">&lt;</span><span class="nc">Some</span> <span class="n">selector</span><span class="o">&gt;;</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> http://ru.selenide.org/2019/12/12/advent-calendar-actions/ http://ru.selenide.org/2019/12/12/advent-calendar-actions 2019-12-12T00:00:00+00:00 Как скачать файл с помощью Selenide <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">&lt;</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">&gt;;</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">(&lt;</span><span class="n">папка</span><span class="o">,</span> <span class="n">подлежащая</span> <span class="n">удалению</span><span class="o">&gt;));</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> http://ru.selenide.org/2019/12/10/advent-calendar-download-files/ http://ru.selenide.org/2019/12/10/advent-calendar-download-files 2019-12-10T00:00:00+00:00 Почему статики запретили, а потом разрешили? <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">&lt;</span><span class="nc">WebDriver</span><span class="o">&gt;</span> <span class="n">webdriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">&lt;&gt;();</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o">&lt;</span><span class="nc">SelenideProxyServer</span><span class="o">&gt;</span> <span class="n">proxy</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">&lt;&gt;();</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">&lt;</span><span class="nc">SelenideDriver</span><span class="o">&gt;</span> <span class="n">selenideDriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">&lt;&gt;();</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&lt;SelenideDriver&gt;</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> http://ru.selenide.org/2019/12/09/advent-calendar-statics/ http://ru.selenide.org/2019/12/09/advent-calendar-statics 2019-12-09T00:00:00+00:00 Как протестировать защиту от CSRF атаки <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">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">unprotectedUrls</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;(</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">&lt;</span><span class="nc">String</span><span class="o">&gt;</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">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</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">&lt;</span><span class="nc">String</span><span class="o">&gt;</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">&lt;</span><span class="nc">String</span><span class="o">&gt;</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> http://ru.selenide.org/2019/12/07/advent-calendar-csrf-protection/ http://ru.selenide.org/2019/12/07/advent-calendar-csrf-protection 2019-12-07T00:00:00+00:00 Как визуализировать клик <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">&lt;</span><span class="n">script</span><span class="o">&gt;</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">&lt;/</span><span class="n">script</span><span class="o">&gt;</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> http://ru.selenide.org/2019/12/06/advent-calendar-visualize-click/ http://ru.selenide.org/2019/12/06/advent-calendar-visualize-click 2019-12-06T00:00:00+00:00 Как быстро проверить размер? <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> http://ru.selenide.org/2019/12/04/advent-calendar-effective-size-check/ http://ru.selenide.org/2019/12/04/advent-calendar-effective-size-check 2019-12-04T00:00:00+00:00 setWebDriver или WebDriverProvider? <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> http://ru.selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider/ http://ru.selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider 2019-12-03T00:00:00+00:00 Как надругаться над Селенидом <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> http://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide/ http://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide 2019-12-02T00:00:00+00:00 Главный Алгоритм <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://www.javaadvent.com/2018/12/wtf-connection-pools.html">2018</a>).</p> <p>Я тоже решил не отставать и замутить Selenide Advent Calendar. Каждый день по статье. С 1 по 25 декабря. Начну я, а вы можете присоединиться - сообщите о себе любым способом.</p> <p>Сегодняшняя тема будет такая:</p> <h1 id="самый-главный-алгоритм">Самый Главный Алгоритм</h1> <p>Главная фишка селенида, как многие считают - это автоматические ожидания. Вам не нужно писать лишний код типа <code class="language-plaintext highlighter-rouge">new WebDriverWait(driver, 30).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[contains(text(),'COMPOSE')]")))</code>, чтобы дождаться наступления какого-то события. Вам не нужно тратить время и силы, чтобы вообще думать о том, а где нужны ожидания. Вам не нужно раз за разом спотыкаться о моргнувший тест и думать, куда бы ещё добавить ожидание.</p> <p>В селениде всё проще: любая строчка типа <code class="language-plaintext highlighter-rouge">$(byText("COMPOSE")).shouldBe(visible)</code> <strong>автоматически ждёт</strong>, если надо.</p> <h3 id="а-не-замедлит-ли-это-тест">А не замедлит ли это тест?</h3> <p>Нет.</p> <p>Если элемент уже видимый, селенид сразу пойдёт дальше. Если нет - подождёт 100 мс и проверит снова. Если снова нет - подождёт ещё 100 мс и проверит ещё раз. И так до 4 секунд. А вот если по истечении 4 секунд элемент всё ещё невидим, тогда тест упадёт.</p> <p>Естественно, эти 4с и 100 мс настраиваются. Вы можете задать любой таймаут под ваш проект.</p> <h3 id="как-это-работает">Как это работает?</h3> <p>Вот мы и переходим к Самому Главному Алгоритму. На самом деле он очень прост.</p> <p>Вот как работает, например, метод <code class="language-plaintext highlighter-rouge">$(".btn").shouldBe(visible)</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">shouldBeVisible</span><span class="o">()</span> <span class="o">{</span> <span class="k">do</span> <span class="o">{</span> <span class="k">try</span> <span class="o">{</span> <span class="k">assert</span> <span class="n">webdriver</span><span class="o">.</span><span class="na">findElement</span><span class="o">().</span><span class="na">isDisplayed</span><span class="o">()</span> <span class="o">==</span> <span class="kc">true</span><span class="o">;</span> <span class="k">return</span> <span class="n">ok</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="n">sleep</span><span class="o">(</span><span class="mi">100</span> <span class="n">мс</span><span class="o">)</span> <span class="o">}</span> <span class="o">}</span> <span class="k">while</span> <span class="o">(</span><span class="n">прошло</span> <span class="n">меньше</span> <span class="no">N</span> <span class="n">секунд</span><span class="o">);</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ElementShouldBeVisible</span><span class="o">(</span><span class="s">"Ожидали: то-то, на самом деле: то-то"</span><span class="o">);</span> <span class="c1">// тут ещё сделать скриншот.</span> <span class="o">}</span> </code></pre></div></div> <p>В общем-то всё просто.</p> <p>Этот же код в реальности выглядит <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/impl/WebElementSource.java#L44">чуть сложнее</a>. А всё потому, что он “универсальный” - не только для <code class="language-plaintext highlighter-rouge">shouldBe(visible)</code>, но и для других операций и условий.</p> <p>Как известно,</p> <h3 id="дьявол-кроется-в-деталях">дьявол кроется в деталях</h3> <p>В приведённом выше коде есть масса вопросов:</p> <ol> <li>Какие именно ошибки ловить? <ul> <li><code class="language-plaintext highlighter-rouge">Exception</code>, <code class="language-plaintext highlighter-rouge">Error</code>, <code class="language-plaintext highlighter-rouge">Throwable</code>, <code class="language-plaintext highlighter-rouge">AssertionError</code>, <code class="language-plaintext highlighter-rouge">WebDriverException</code>?</li> </ul> </li> <li>В случае каких ошибок надо вылететь сразу, не продолжая ожидание? <ul> <li>например, если XPath невалидный, то он и через 4 секунды будет невалидный.</li> </ul> </li> <li>Результат хороший или плохой? <ul> <li>например, если элемент не найден - это может быть ок для условия <code class="language-plaintext highlighter-rouge">$.shouldNot(exist)</code>.</li> </ul> </li> <li>В какой момент делать скриншот?</li> <li>Можно ли всё это кастомизировать? <ul> <li>Например, при использовании Аллюра, возможно, не стоит снимать скриншот, ведь Аллюр сам снимет скриншот.</li> <li>Или наоборот, стоит снять скриншот и как-то передать его Аллюру?</li> <li>(сейчас и Селенид, и Аллюр делают каждый по скриншоту, так что они дублируются)</li> </ul> </li> <li>Что делать, если мы дошли до последней строки, а вот в ней-то как раз прилетел зло#учий <code class="language-plaintext highlighter-rouge">StaleElementException</code>?</li> </ol> <p>Ответы на эти вопросы менялись с течением времени. И наверное, будут ещё меняться. Но суть остаётся.</p> <h2 id="что-теперь">Что теперь?</h2> <p>Теперь, я надеюсь, вы не будете относиться к селенидовским ожиданиям как магии, а будете понимать, как оно работает. Возможно, это поможет при изучении различных странных падений тестов и прочей мистики.</p> <p>А может, и посоветуете, как улучшить или упростить <s>ядро скайнета</s> наш Главный Алгоритм.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/01/advent-calendar-main-algorithm/ http://ru.selenide.org/2019/12/01/advent-calendar-main-algorithm 2019-12-01T00:00:00+00:00 Вышла Selenide 5.5.1 <p>Всем привет!</p> <p>Соскучились?</p> <p>Чтобы вы не расслаблялись, мы выпустили <a href="https://github.com/selenide/selenide/milestone/86?closed=1">Selenide 5.5.1</a> с парочкой новых фич.</p> <p><br /></p> <h1 id="добавили-эмуляцию-мобильного-браузера">Добавили эмуляцию мобильного браузера</h1> <p>Иногда хочется запустить тесты не на обычном хроме, а “как будто в мобилке”.</p> <p>Как будто этот хром открыли бы на мобильном телефоне или планшете.</p> <p>(Например, сайт selenide.org открывается на мобилке очень криво. :( Мы работаем над этим.)</p> <p>Теперь это легко сделать, добавив такую вот System property перед запуском вебдрайвера:</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>или прямо в коде:</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.mobileEmulation"</span><span class="o">,</span> <span class="s">"deviceName=Nexus 5"</span><span class="o">);</span> </code></pre></div></div> <p>NB! Речь идёт только об одной опции - имени устройства. Этого достаточно в большинстве случаев. Если вам нужна более тонкая настройка мобильного браузера, го реализовывать <code class="language-plaintext highlighter-rouge">WebDriverProvider</code> с аргументами и опциями.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1008">1008</a> и <a href="https://github.com/selenide/selenide/pull/1011">PR 1011</a>.</p> <p><br /></p> <h1 id="пометили-метод-selenideclose-как-устаревший">Пометили метод <code class="language-plaintext highlighter-rouge">Selenide.close()</code> как устаревший</h1> <p>В селениде издревле был метод <code class="language-plaintext highlighter-rouge">Selenide.close()</code>. Но он вводил в заблуждение, т.к. он закрывал весь браузер, а не только активную вкладку. В то же время в Selenium есть метод <code class="language-plaintext highlighter-rouge">close()</code>, который закрывает только текущую вкладку (и только если она была единственной - весь браузер). А для закрытия всего браузера в Selenium есть метод <code class="language-plaintext highlighter-rouge">quit()</code>.</p> <p>Я, конечно, виноват, что сделал когда-то метод <code class="language-plaintext highlighter-rouge">close()</code>, не почитав документацию к Selenium. Я был молод и не верил в javadoc. Но и селениумовские названия <code class="language-plaintext highlighter-rouge">close</code> vs <code class="language-plaintext highlighter-rouge">quit</code> тоже, честно говоря, не очень удачные. Поди запомни, кто из них что закрывает.</p> <p>В общем, чтобы облегчить твою жизнь, дорогой пользователь, теперь мы Селенид предлагает тебе на выбор два метода с говорящими названиями.</p> <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.closeWindow()</code> - закрывает текущее окно (или вкладку, что для селениума одно и то же)</li> <li><code class="language-plaintext highlighter-rouge">Selenide.closeWebDriver()</code> - закрывает весь браузер</li> </ul> <p>Запоминать больше ничего не придётся.</p> <p>А метод <code class="language-plaintext highlighter-rouge">Selenide.close()</code> мы пока пометили как <code class="language-plaintext highlighter-rouge">@Deprecated</code>. Удалим в Selenide 6.0.0</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1016">1016</a> и <a href="https://github.com/selenide/selenide/pull/1017">PR 1017</a>.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Огонь-огонь! Новый плагин IDEA для Selenide/Selenium: <a href="https://plugins.jetbrains.com/plugin/13267-qa-lithium">QA Lithium</a>. Спасибо <a href="https://strangeway.org/">Yuriy Artamonov</a>!</li> <li>Ещё один фреймворк на базе Selenide: <a href="https://github.com/cuba-platform/masquerade">Masquerade</a> - <em>CUBA Platform UI Testing Library</em></li> <li>Статья <a href="https://medium.com/swlh/selenide-in-testing-process-automatisation-through-selenoid-in-the-docker-container-48e659d2ee72">про Selenide и Selenoid</a></li> <li>И ещё <a href="https://www.intexsoft.com/blog/post/selenide-docker.html">про Selenide и Selenoid</a></li> </ul> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/11/29/selenide-5.5.1/ http://ru.selenide.org/2019/11/29/selenide-5.5.1 2019-11-29T00:00:00+00:00 Вышла Selenide 5.5.0 <p>Всем привет!</p> <p>А как вы празднуете Хэллоуин?</p> <p>Мы вот выпустили <a href="https://github.com/selenide/selenide/milestone/85?closed=1">Selenide 5.5.0</a> с несколькими <em>обратно несовместимым изменениями</em>.</p> <p>СТРАШНО?</p> <p><br /></p> <h1 id="перешли-с-jul-на-slf4j">Перешли с JUL на SLF4J</h1> <p>Иногда селениду нужно записать в лог какие-то вспомогательные сообщения: “я нашёл бинарник браузера там-то”, “я не смог сохранить скриншот туда-то” и т.д.</p> <p>До сих пор Селенид писал свои логи через стандартный Java механизм <code class="language-plaintext highlighter-rouge">java.util.logging</code> (т.н. JUL). Всем известно, что у него есть недостатки, и поэтому в Java мире давно появилась целая куча библиотек, в которых сложно разобраться: Log4J, Slf4j, Logback, JCL.</p> <p>В общем, теперь будет так.</p> <ul> <li>Slf4J - это “фасад”, что-то типа “интерфейса”. Селенид будет писать логи через него.</li> <li>А Log4j, Logback, JCL - это реализации этого “интерфейса”. Наверное, есть и другие.</li> </ul> <p>Вы можете сами решать, какую реализацию Slf4J вы используете в проекте. Наверняка в вашем проекте это уже давно решено, ведь как минимум WebDriverManager изначально писал свои логи через Slf4J.</p> <p>Чтобы подключить одну из реализаций Slf4J, вы должны добавить в проект одну из зависимостей:</p> <ul> <li>slf4j-log4j12-*.jar</li> <li>logback-classic-*.jar</li> <li>slf4j-simple-*.jar</li> <li>slf4j-jdk14-*.jar</li> <li>slf4j-jcl-*.jar</li> <li>slf4j-nop-*.jar (ТОЛЬКО НЕ ЭТО! Это самый плохой вариант. Он будет игнорировать логи.)</li> </ul> <p>NB! Подчёркиваю, что скорее всего такая зависимость у вас уже есть, и поэтому делать ничего не надо. Оно само заработает.</p> <p>Если же зависимости всё-таки нет, вы увидите такое сообщение при запуске тестов (и не увидите логов селенида):</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. </code></pre></div></div> <p>Если вы его увидите, самое простое решение - это добавить такую строчку в ваш build.gradle:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.28' </code></pre></div></div> <p>или в pom.xml:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>org.slf4j<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>slf4j-simple<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>1.7.28<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span> <span class="nt">&lt;/dependency&gt;</span> </code></pre></div></div> <p>NB! Если не разобрались с Slf4j - не паникуйте, логи селенида не очень-то важны. Ошибки при падении тестов никуда не денутся. Речь идёт только про служебные INFO и WARNING логи типа <code class="language-plaintext highlighter-rouge">INFO Using browser binary: /var/lib/opera.exe</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/923">923</a>.</p> <p>Спасибо <a href="https://github.com/gschukin">Gleb Schukin</a> за <a href="https://github.com/selenide/selenide/pull/926">PR 926</a>.</p> <p><br /></p> <h1 id="удалили-зависимости-htmlunit-и-phantomjs">Удалили зависимости HtmlUnit и PhantomJS</h1> <p>Не переживайте, тесты должны по-прежнему работать на HtmlUnit и PhantomJS, просто селенид больше не тянет эти зависимости автоматически (ведь большинство людей давно перешли на <em>headless chrome</em> и <em>headless firefox</em>).</p> <p>Если вы упорно хотите использовать HtmlUnit или PhantomJS, вам просто нужно будет добавить соответствующие зависимости в проект.</p> <p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1003">PR 1003</a> и <a href="https://github.com/selenide/selenide/pull/998">PR 998</a>.</p> <p><br /></p> <h1 id="удалили-встроенную-поддержку-браузера-safari">Удалили встроенную поддержку браузера Safari</h1> <p>Но не волнуйтесь, вы всё ещё можете использовать Safari! См. <a href="https://github.com/selenide/selenide/wiki/Safari">инструкцию</a></p> <p><br /></p> <h1 id="сделали-метод-executecommand-более-универсальным">Сделали метод <code class="language-plaintext highlighter-rouge">$.execute(command)</code> более универсальным</h1> <p>Раньше метод <code class="language-plaintext highlighter-rouge">$.execute</code> умел работать только с <code class="language-plaintext highlighter-rouge">Command&lt;SelenideElement&gt;</code>, а теперь - с любыми командами.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1000">Issue 1000</a> и <a href="https://github.com/selenide/selenide/pull/1001">PR 1001</a>.</p> <p><br /></p> <h1 id="починили-настройку-holdbrowseropen">Починили настройку <code class="language-plaintext highlighter-rouge">holdBrowserOpen</code></h1> <p>.. которую мы сломали в Selenide 5.4.1. Теперь браузер снова остаётся открытым, и вы можете дебажить упавший тест.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/999">999</a> и <a href="https://github.com/selenide/selenide/pull/1005">PR 1005</a>.</p> <p><br /></p> <h1 id="снимаем-скриншот-в-случае-ошибки-dialogtextmismatch">Снимаем скриншот в случае ошибки <code class="language-plaintext highlighter-rouge">DialogTextMismatch</code></h1> <p>Как вы знаете, в селениде есть функция для проверки текста диалогового окна (<code class="language-plaintext highlighter-rouge">alert</code> или <code class="language-plaintext highlighter-rouge">confirm</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="s">"Are you sure?"</span><span class="o">);</span> </code></pre></div></div> <p>Но при падении этой проверке селенид забывал сделать скриншот. Теперь делает.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/907">907</a>. Спасибо <a href="https://github.com/nwholloway">Nick Holloway</a> за <a href="https://github.com/selenide/selenide/pull/986">PR 986</a>.</p> <p><br /></p> <h1 id="рефакторинг-screenshotlaboratory">Рефакторинг ScreenShotLaboratory</h1> <p>Хотя это всего лишь рефакторинг, т.е. на вас он не повлияет, я не могу не отметить инициативу <a href="https://github.com/SeleniumTestAB">SeleniumTestAB</a>, который решил улучшить код и запилил <a href="https://github.com/selenide/selenide/pull/1004">PR 1004</a> и <a href="https://github.com/selenide/selenide/pull/1006">PR 1006</a>.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Статья на хабре: <a href="https://habr.com/ru/post/473454/">Автоматизация тестирования с использованием Selenide через Selenoid в Docker контейнере</a></li> <li>Доклад про Selenide на конференции EclipseCon 2019: <a href="https://www.eclipsecon.org/europe2019/sessions/complete-selenium-techstack-conception-evaluation-open-source-software">A complete Selenium Techstack</a></li> <li>Мой доклад для Cyprus Quality Conference <a href="https://docs.google.com/presentation/d/1hSCmjwvLCY4bKqSncffZfMOi1NmXIooJu5LIjxYN6hg/edit">Тройничок: Selenide для Web, Android и iOS”</a>. Пока только слайды.</li> </ul> <h2 id="и-главное">И главное</h2> <p>И главное: 25 октября <strong>Селениду ИСПОЛНИЛОСЬ 8 ЛЕТ!!!</strong></p> <p>25.10.2011 был сделан первый коммит в репозиторий селенида. После 8 лет мы имеем:</p> <ul> <li>89 тыщ скачиваний с 14 тыщ уникальных айпишников (в месяц)</li> <li>1 тыща звёзд на гитхабе</li> <li>55 контрибьютеров</li> </ul> <p>Спасибо всем контрибьютерам, пользователям, критикам, идеологам и просто сочувствующим.</p> <p>Это только начало!</p> <p><br /></p> <p>Статистика скачиваний Selenide за октябрь 2019:</p> <center> <img src="/images/2019/10/selenide.downloads.png" width="800" /> </center> <p><br /></p> <center> <img src="/images/2019/10/selenide.unique-ips.png" width="800" /> </center> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/10/31/selenide-5.5.0/ http://ru.selenide.org/2019/10/31/selenide-5.5.0 2019-10-31T00:00:00+00:00 Вышла Selenide 5.4.1 <p>Всем привет!</p> <p>Нет ничего унылее октября. Давайте же скрашивать его хорошими новостями!</p> <p>Сегодня мы выпустили Selenide 5.4.0 с парочкой существенных изменений, <em>о которых многие из вас давно просили</em>.</p> <p><strong>UPD</strong> В 5.4.0 была ошибка, так что обновляйтесь сразу на <strong>5.4.1</strong></p> <p><strong>UPD 2</strong> В 5.4.1 тоже нашлась малюсенькая ошибочка: опция <code class="language-plaintext highlighter-rouge">Configuration.holdBrowserOpen</code> не работает. Исправим в следующей версии. Надеюсь, вы не используете её часто.</p> <p><br /></p> <h1 id="fix-illegal-state-exception">1. Исправили ошибку “IllegalStateException WebDriver has been closed”</h1> <p>Начиная с Selenide 5.0.0 (т.е. примерно год назад), многие пользователи стали жаловаться на ошибку <code class="language-plaintext highlighter-rouge">IIlegalStateException WebDriver has been closed...</code>.</p> <p>Строго говоря, это не ошибка, и я долгое время отказывался её исправлять. Я убеждён, что в грамотно написанных тестах такая ошибка случиться не может. Собственно, селенид её специально кидает, чтобы стимулировать пользователей писать тесты правильно.</p> <p>Но жалоб было слишком много, поэтому с тяжёлым сердцем я всё-таки решил убрать эту ошибку.</p> <blockquote> <p>В конце концов, каждый имеет право набивать свои шишки.</p> </blockquote> <p>Если вкратце, ошибка эта случается, когда <code class="language-plaintext highlighter-rouge">SelenideElement</code> переиспользуют с разными инстансами <code class="language-plaintext highlighter-rouge">SelenideDriver</code>. Обычно так бывает, когда <code class="language-plaintext highlighter-rouge">SelenideElement</code> объявляют как статическое поле (в пэдж обжекте или в тесте, неважно). И инициализируют его раньше, чем открывается браузер. Либо перезапускают браузер между тестами. Ещё это бывает, когда используют Cucumber, который пытается инициализировать степы и пэдж обжекты до открытия браузеров. Или зачем-то пихают в тесты Guice или Spring. В тесты, Карл!</p> <p>Так вот, всё это переусложнение и в тестах не нужно. Статические поля - зло. Подробнее об этом я рассказывал в докладе <a href="https://www.youtube.com/watch?v=4JJNccWtdNI">Антистатик</a>.</p> <p>Но раз уж людям так впёрлось - ладно. Ловите Selenide 5.4.0</p> <p>Теперь вот такой код будет работать:</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">SomeClass</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="nc">SelenideElement</span> <span class="n">body</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"body"</span><span class="o">);</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://mail.ru"</span><span class="o">);</span> <span class="n">body</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="nc">Selenide</span><span class="o">.</span><span class="na">close</span><span class="o">();</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://mail.ru"</span><span class="o">);</span> <span class="n">body</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="c1">// А раньше отсюда летел "IllegalStateException WebDriver has been closed"</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Больше примеров и жалоб можно найти в issue <a href="https://github.com/selenide/selenide/issues/862">862</a>, <a href="https://github.com/selenide/selenide/issues/902">902</a>, <a href="https://github.com/selenide/selenide/issues/954">954</a>, <a href="https://github.com/selenide/selenide/issues/922">922</a>, <a href="https://github.com/selenide/selenide/issues/873">873</a>.</p> <p>Как мы позволили статикам победить, можно отследить в <a href="https://github.com/selenide/selenide/pull/989">PR 989</a>.</p> <p><br /></p> <h1 id="close-always-closes-browser">2. Теперь <code class="language-plaintext highlighter-rouge">SelenideDriver.close()</code> всегда закрывает браузер</h1> <p>Опять же, начиная с Selenide 5.0.0, люди стали жаловаться на то, что метод <code class="language-plaintext highlighter-rouge">close()</code> не закрывает браузер. Конечно, не всегда, а только в одном специфическом случае - когда пользователь сам же браузер и открыл.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideDriver</span> <span class="n">browser</span><span class="o">=</span><span class="k">new</span> <span class="nc">SelenideDriver</span><span class="o">(</span><span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">());</span> <span class="n">browser</span><span class="o">.</span><span class="na">close</span><span class="o">();</span> <span class="c1">// вот так браузер НЕ закрывался</span> <span class="n">browser</span><span class="o">.</span><span class="na">getWebDriver</span><span class="o">().</span><span class="na">close</span><span class="o">();</span> <span class="c1">// а так закрывался</span> </code></pre></div></div> <p>Это тоже не совсем бага, потому что изначально в Селениде была такая философия: браузер, открытый пользователем, селенид не закрывает. Потому что поди знай, вдруг его ещё где-нибудь используют помимо Селенида.</p> <p>Как говорят в Сбертехе,</p> <blockquote> <p>где браузер открывали - там его и закрывайте. :)</p> </blockquote> <p>Но опять же, жалобы были. У людей вызывало непонимание, как так: метод <code class="language-plaintext highlighter-rouge">close()</code> есть, отрабатывает без ошибок - но не закрывает.</p> <p>Теперь мы это тоже исправили. Метод <code class="language-plaintext highlighter-rouge">close()</code> закрывает браузер.</p> <p>P.S. Если у вас вдруг браузер начнёт закрываться в ненужный момент - обращайтесь, будем изучать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/896">issue 896</a> и <a href="https://github.com/selenide/selenide/pull/989">PR 989</a></p> <p><br /></p> <h1 id="restore-short-error-messages">3. Вернули короткие сообщения об ошибках</h1> <p>Как мы могли заметить, в Selenide 5.3.1 прилетела одна некритичная регрессия. Сообщения об ошибках стали слишком длинными:</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">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">ex</span><span class="o">.</span><span class="na">ElementNotFound</span><span class="o">:</span> <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">customerDashboardButton</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">customerDashboardButton</span><span class="o">}</span> </code></pre></div></div> <p>P.S. У бедных пользователей <code class="language-plaintext highlighter-rouge">maven-surefire-plugin 2.22.2</code> всё ещё будут длинные сообщения, <a href="/2019/09/07/selenide-5.3.1/">подробности тут</a>. Сочувствую.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/993">PR 993</a></p> <p><br /></p> <h1 id="add-method-using">4. Добавили метод <code class="language-plaintext highlighter-rouge">using</code> для лёгкого переключения несколькими вебдрайверами</h1> <p><a href="/2018/10/10/selenide-5.0.0/">Повторюсь</a>, использование нескольких браузеров в одном тесте в подавляющем большинстве случаев я считаю плохой практикой.</p> <p>Но теперь набивать шишки таким образом стало чуть удобнее.</p> <p>Если раньше приходилось постоянно дёргать метод <code class="language-plaintext highlighter-rouge">setWebDriver()</code> и не забывать, какой браузер активный в данный момент:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">browser1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="kt">var</span> <span class="n">browser2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="n">setWebDriver</span><span class="o">(</span><span class="n">browser1</span><span class="o">);</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://google.com"</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">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Released Selenide 5.4.0"</span><span class="o">));</span> <span class="n">setWebDriver</span><span class="o">(</span><span class="n">browser2</span><span class="o">);</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://yandex.ru"</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">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Вышла Selenide 5.4.0"</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="kt">var</span> <span class="n">browser1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="kt">var</span> <span class="n">browser2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="n">using</span><span class="o">(</span><span class="n">browser1</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://google.com"</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">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Released Selenide 5.4.0"</span><span class="o">));</span> <span class="o">});</span> <span class="n">using</span><span class="o">(</span><span class="n">browser2</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://yandex.ru"</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">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Вышла Selenide 5.4.0"</span><span class="o">));</span> <span class="o">});</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/976">PR 976</a> и примеры кода в <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/CustomWebdriverTest.java">CustomWebdriverTest.java</a>.</p> <p><br /></p> <h1 id="add-xpath-sanity-check">5. Добавили защиту от одной типичной ошибки с xpath</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">".parent"</span><span class="o">).</span><span class="na">find</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">"/child"</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>Дело в том, что XPath, начинающийся на слэш (“/”), ищет элемент <strong>от корня проекта</strong> (а не от элемента <code class="language-plaintext highlighter-rouge">.parent</code>, как, очевидно, хотел автор этого кода).</p> <p>Слишком многие наступали на эти грабли. Поэтому мы добавили защиту от этой типичной ошибки.</p> <p>Теперь показанный выше код кинет ошибку, которая чётко объяснит проблему и сэкономит вам время:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalArgumentException:</span> <span class="nc">XPath</span> <span class="n">starting</span> <span class="n">from</span> <span class="o">/</span> <span class="n">searches</span> <span class="n">from</span> <span class="n">root</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">".parent"</span><span class="o">).</span><span class="na">find</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">"./child"</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">".parent"</span><span class="o">).</span><span class="na">find</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">".//child"</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">".parent"</span><span class="o">).</span><span class="na">find</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">"child"</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>См. <a href="https://github.com/selenide/selenide/pull/963">PR 963</a> и <a href="https://github.com/selenide/selenide/pull/975">PR 975</a></p> <p><br /></p> <h1 id="update-dependencies">6. Обновили зависимости</h1> <ul> <li>upgrade to webdrivermanager:3.7.1</li> <li>exclude old Guava dependency coming from net.lightbody.bmp:browsermob-core:2.1.5</li> </ul> <p>Внимание, в WDM обнаружена ошибка: когда вы хотите скачать chromedriver версии 77, он иногда может скачать “75.0.3770.90”. Да-да, потому, что эта версия содержит подстроку “77”. <a href="https://github.com/bonigarcia/webdrivermanager/issues/391">Смешно, да?</a> :)</p> <p><br /></p> <h2 id="news">Новости</h2> <ul> <li>Новая утилита <a href="https://github.com/bsideup/jabel">Jabel</a> от Сергея Егорова:<br /> теперь можно писать на Java13, а компилировать в Java8. <em>А что, так можно было???</em></li> <li><a href="https://www.youtube.com/watch?v=zQAGh_o7Dqs">Сергей Егоров - Как сделать OSS проект успешным</a> – devclub.eu, Таллинн, 24.09.2019 <br /> Близкая мне тема. Вот примерно всем этим я и занимаюсь с Селенидом.</li> <li><a href="https://www.youtube.com/watch?v=WNzTuYFd8oI">Видосик про Selenide на немецком</a> - со школы не смотрел видео на немецком с таким интересом :)</li> <li>Англоязычный мир <a href="https://testguild.com/selenide/">открывает для себя Selenide</a>. Там же ссылка на подкаст про Selenide.</li> <li>Martin Škarbala из Словакии добавил к своему замечательному видео про Selenide английские субтитры: <br /> <a href="https://www.youtube.com/watch?v=y9WTRTOTOsc">Selenide - stručné UI testy</a>.</li> <li><a href="https://www.youtube.com/watch?v=U6z2dK7MwmI&amp;t=10s">10 причин моей ненависти</a> – devclub.eu, Таллинн, 24.09.2019</li> <li>Отличный доклад “<a href="https://www.youtube.com/watch?v=C5eD-K8AO3o">Атомарный рефакторинг</a>” от Тагира Валеева! Жёстко рекомендую.</li> <li>Ещё один доклад с JPoint: <a href="https://www.youtube.com/watch?v=qd9N9eU1Qn0">Миру нужны фулстеки</a></li> </ul> <p><br /></p> <h2 id="conferences">Конференции</h2> <p>Друзья, в унылый пасмурный октябрьский денёк очень советую пойти на конференцию Joker. <a href="https://habr.com/ru/company/jugru/blog/470555/">Круче этой программы ничего не может быть</a>!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/10/16/selenide-5.4.0/ http://ru.selenide.org/2019/10/16/selenide-5.4.0 2019-10-16T00:00:00+00:00 Вышла Selenide 5.3.1 <p>Всем привет!</p> <p>На днях случилась сенсация.</p> <h4 id="презентация-нового-айфона-прошла-незамеченной">Презентация нового айфона прошла незамеченной!</h4> <p>Потому, что мы выпустили Selenide 5.3.1. Тоже с улучшенными снимками :)</p> <p>В этой версии мы исправили ровно одну проблему, зато какую!</p> <p><br /></p> <h1 id="maven-где-мои-скриншоты">Maven, где мои скриншоты?</h1> <p>Недавно мы обнаружили, что пользователи мавена и свежего <code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.22.2</code> были лишены одной из самых важных фич селенида: имя скриншота не добавлялось к сообщению об ошибке при падении теста.</p> <p>Вот это нежданчик!</p> <p><strong>Как вы вообще жили без этого, бедняги?</strong></p> <p><em>Может, поэтому вы так упарываетесь по BDD, Серенити и Аллюру?</em></p> <p><br /></p> <h1 id="для-сравнения">Для сравнения</h1> <p>Вот как выглядит сообщение об ошибке при падении теста в Maven и последнем maven-surefire-plugin:2.22.2:</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">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">ex</span><span class="o">.</span><span class="na">ListSizeMismatch</span><span class="o">:</span> <span class="o">:</span> <span class="nl">expected:</span> <span class="o">=</span> <span class="mi">11</span><span class="o">,</span> <span class="nl">actual:</span> <span class="mi">0</span><span class="o">,</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">results</span> <span class="nl">Elements:</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">GoogleTest</span><span class="o">.</span><span class="na">openGoogle</span><span class="o">(</span><span class="nc">GoogleTest</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">42</span><span class="o">)</span> </code></pre></div></div> <p>Вы видите скриншот? И я не вижу. А он есть.</p> <p>А вот как выглядит ошибка в Gradle, Ant, любой IDE и дефалтовом maven-surefire-plugin:2.12.4:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span> <span class="n">size</span> <span class="n">mismatch</span><span class="o">.</span> <span class="nl">Expected:</span> <span class="o">=</span> <span class="mi">11</span><span class="o">,</span> <span class="nl">actual:</span> <span class="mi">0</span><span class="o">,</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">results</span> <span class="nl">Elements:</span> <span class="o">[]</span> <span class="nl">Screenshot:</span> <span class="nl">file:</span><span class="o">/</span><span class="nc">Users</span><span class="o">/</span><span class="n">andrei</span><span class="o">/</span><span class="n">projects</span><span class="o">/</span><span class="n">selenide</span><span class="o">-</span><span class="n">examples</span><span class="o">/</span><span class="n">sandbox</span><span class="o">-</span><span class="n">selenide</span><span class="o">-</span><span class="n">junit5</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="mf">1567803355181.0</span><span class="o">.</span><span class="na">png</span> <span class="nc">Page</span> <span class="nl">source:</span> <span class="nl">file:</span><span class="o">/</span><span class="nc">Users</span><span class="o">/</span><span class="n">andrei</span><span class="o">/</span><span class="n">projects</span><span class="o">/</span><span class="n">selenide</span><span class="o">-</span><span class="n">examples</span><span class="o">/</span><span class="n">sandbox</span><span class="o">-</span><span class="n">selenide</span><span class="o">-</span><span class="n">junit5</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="mf">1567803355181.0</span><span class="o">.</span><span class="na">html</span> <span class="nl">Timeout:</span> <span class="mi">4</span> <span class="n">s</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">GoogleTest</span><span class="o">.</span><span class="na">openGoogle</span><span class="o">(</span><span class="nc">GoogleTest</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">42</span><span class="o">)</span> </code></pre></div></div> <p>Теперь при падении теста вы сразу видите скриншот и код странички. Этого достаточно, чтобы изучить причину падения. Не нужны больше никакие “красивые” отчёты и прочая усложнятина.</p> <p><em>Жизнь заиграла совсем другими красками, правда?</em></p> <p><br /></p> <h1 id="расследование">Расследование</h1> <p>Но почему так получилось?</p> <ol> <li>В Java есть класс <code class="language-plaintext highlighter-rouge">Throwable</code>, от которого наследуются все ошибки.</li> <li>В нём есть два метода: <code class="language-plaintext highlighter-rouge">getMessage()</code> и <code class="language-plaintext highlighter-rouge">toString()</code>.</li> <li>По умолчанию <code class="language-plaintext highlighter-rouge">toString()</code> = <code class="language-plaintext highlighter-rouge">имя класса</code> + <code class="language-plaintext highlighter-rouge">getMessage()</code></li> </ol> <p>Например, такой код:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Throwable</span> <span class="n">e</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"низя"</span><span class="o">);</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">toString</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">низя</span> <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="n">низя</span> </code></pre></div></div> <p>Фокус в том, что изначально Selenide добавлял имя скриншота именно в методе <code class="language-plaintext highlighter-rouge">UIAssertionError.toString()</code>. И это прекрасно работало в Gradle, Ant, всех IDE. Да и в Maven тоже - в дефалтовом <code class="language-plaintext highlighter-rouge">maven-surefire-plugin</code>.</p> <p><br /></p> <h3 id="что-же-обновилось-в-maven">Что же обновилось в Maven?</h3> <p>В Maven вообще сидят весёлые ребята.</p> <ol> <li>В Maven для тестов зачем-то два плагина, а не один.</li> <li>Называются они почему-то “surefire”, а не какой-нибудь “test”, что было бы всем понятно.</li> <li>И по умолчанию последний Maven 3.6.2 почему-то использует <em>разные версии</em> этих плагинов: <ul> <li><code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.12.4</code> - выпущена <strong>7 лет назад</strong> (например, не поддерживает Java 11)</li> <li>и <em>внезапно</em> <br /><code class="language-plaintext highlighter-rouge">maven-surefire-report-plugin:3.0.0-M3</code> - типа <strong>бета-версия</strong>, как бы ещё сырая</li> </ul> </li> </ol> <p><br /></p> <p>Но вот если прописать в своём pom.xml более новую версию плагина:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">&lt;plugin&gt;</span> <span class="nt&qu