Selenide Selenide - Andrei Solntsev http://ru.selenide.org/rss http://ru.selenide.org 2022-01-14T13:59:37+00:00 2022-01-14T13:59:37+00:00 1800 Вышла 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="раннее-обнаружение-конфликтов">Раннее обнаружение конфликтов</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="слияние-аргументов-хрома">Слияние аргументов хрома</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="важно">ВАЖНО</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="поменяли-тип-параметра-в-webdriverprovider">Поменяли тип параметра в <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-612">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="релиз-5162-вышел-25112020">Релиз <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="релиз-5161-вышел-23112020">Релиз <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="новости">Новости</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> <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="1-исправили-ошибку-illegalstateexception-webdriver-has-been-closed">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="2-теперь-selenidedriverclose-всегда-закрывает-браузер">2. Теперь SelenideDriver.close() всегда закрывает браузер</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="3-вернули-короткие-сообщения-об-ошибках">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="4-добавили-метод-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="5-добавили-защиту-от-одной-типичной-ошибки-с-регуляркой">5. Добавили защиту от одной типичной ошибки с регуляркой</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="6-обновили-зависимости">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="новости">Новости</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="конференции">Конференции</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">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="nt">&lt;/groupId&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.22.2<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;/plugin&gt;</span> </code></pre></div></div> <p>то начинаются чудеса. Вместо <code class="language-plaintext highlighter-rouge">e.toString()</code> он теперь зачем-то использует <code class="language-plaintext highlighter-rouge">имя класса + getMessage()</code>. Вот поэтому скриншоты и не добавляются больше к отчёту.</p> <p><br /></p> <h1 id="как-исправили">Как исправили?</h1> <p>Теперь мы добавляем скриншот в методе <code class="language-plaintext highlighter-rouge">e.getMessage()</code>, а не <code class="language-plaintext highlighter-rouge">e.toString()</code>. Звучит просто, но нам пришлось перелопатить все классы ошибок в Selenide и исправить кучу тестов.</p> <p>Возможно, что-то получилось неидеально, поэтому <strong>смело рапортуйте</strong>, если найдёте шероховатости!</p> <p>Всю предысторию и изменения кода можно найти здесь:</p> <ul> <li><a href="https://github.com/selenide/selenide/issues/234">issue 234</a></li> <li><a href="https://github.com/selenide/selenide/pull/972">PR 972</a></li> </ul> <p><br /></p> <h2 id="новости">Новости</h2> <p>Селенид шагает по планете!</p> <ul> <li> <p>Зацените, какое <a href="https://www.youtube.com/watch?v=y9WTRTOTOsc">классное видео</a> сделал Martin Škarbala из Словакии. Всего за 1.5 минуты он успел показать, чем хорош Selenide и как много кода можно будет выкинуть.</p> </li> <li>Дмитрий Тучс: <a href="https://www.youtube.com/watch?v=Hs8rcmyYV4U">JUnit 5 + DDT: Что такое осень?</a></li> <li>Антон Архипов: <a href="https://www.youtube.com/watch?v=p6nXKii-GEo">Анализ и отладка приложений в IntelliJ IDEA</a></li> </ul> <p><br /></p> <h2 id="конференции">Конференции</h2> <p>Приходите на конференцию <a href="http://qafest.com/en/">QA Fest</a> в Киеве 20-21 сентября!<br /><br /> Говорят, это крупнейшая конференция по тестированию в Украине. 5 потоков! <br /> Много крутых спикеров и спикерш.</p> <p>У меня будет два доклада:</p> <ul> <li>“Селенид для продвинутых” <br /> (параллелизация, статика, листенеры, прокси, трюки с JavaScript, производительность)</li> <li>“10 причин моей ненависти” <br /> (TestNG, репорты, пэдж обжекты, try/catch, ифы в тестах, dependency injection, spring и OverKISS)</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/2019/09/07/selenide-5.3.1/ http://ru.selenide.org/2019/09/07/selenide-5.3.1 2019-09-07T00:00:00+00:00 Вышла Selenide 5.3.0 <div style="text-align: right;"> В Selenide 5.2.8, как в игру,<br /> На холодном ветру<br /> Поиграли с тобой,<br /> Но пришёл сам собой<br /> Selenide 5.3.0 </div> <p><br /></p> <h1 id="968-запускаем-кустарные-команды">#968 Запускаем кустарные команды</h1> <p>Эта фича придётся вам по вкусу.</p> <p>Наверняка ваш код полон самодельных команд для операций над веб-элементами. Какие-нибудь хитрые двойные клики, выбор из радиобаттона на реакте или загрузка файла из спрятанного поля.</p> <p>Так вот, мы добавили удобный способ запускать такие команды, особенно несколько команд в одну строку. Для этого достаточно вызвать метод <code class="language-plaintext highlighter-rouge">$.execute</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">var</span> <span class="n">turnCalendar</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Command</span><span class="o">&lt;</span><span class="nc">SelenideElement</span><span class="o">&gt;()</span> <span class="o">{...};</span> <span class="kt">var</span> <span class="n">pickDate</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Command</span><span class="o">&lt;</span><span class="nc">SelenideElement</span><span class="o">&gt;()</span> <span class="o">{...};</span> <span class="kt">var</span> <span class="n">lookAtPhoto</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Command</span><span class="o">&lt;</span><span class="nc">SelenideElement</span><span class="o">&gt;()</span> <span class="o">{...};</span> <span class="err">$</span><span class="o">(</span><span class="s">"hiddenFileInput"</span><span class="o">).</span><span class="na">execute</span><span class="o">(</span><span class="n">turnCalendar</span><span class="o">).</span><span class="na">execute</span><span class="o">(</span><span class="n">pickDate</span><span class="o">(</span><span class="s">"03.09"</span><span class="o">)).</span><span class="na">execute</span><span class="o">(</span><span class="n">lookAtPhoto</span><span class="o">);</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/rosolko">Aleksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/968">pull request 968</a>.</p> <p><br /></p> <h1 id="970-дополнили-текст-ошибки-для-shouldhaveattributehref-">#970 Дополнили текст ошибки для <code class="language-plaintext highlighter-rouge">shouldHave(attribute("href", ..."))</code></h1> <p>Люди с полей сообщали о странной проблеме: <code class="language-plaintext highlighter-rouge">href</code> вроде бы одинаковый, а тест падает:</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">attribute</span> <span class="n">href</span><span class="o">=</span><span class="s">"/files/hello_world.txt"</span> <span class="o">{</span><span class="n">by</span> <span class="nl">text:</span> <span class="nc">Download</span> <span class="n">me</span><span class="o">}</span> <span class="nl">Element:</span> <span class="err">'</span><span class="o">&lt;</span><span class="n">a</span> <span class="n">href</span><span class="o">=</span><span class="s">"/files/hello_world.txt"</span><span class="o">&gt;</span><span class="nc">Download</span> <span class="n">me</span><span class="o">&lt;/</span><span class="n">a</span><span class="o">&gt;</span><span class="err">'</span> </code></pre></div></div> <p>Оказалось, что дело в одной хитрой особенности Selenium: метод <code class="language-plaintext highlighter-rouge">WebElement.getAttribute("href")</code> возвращает абсолютный URL, а не относительный.<br /> Подробнее об этом Алексей Баранцев рассказывал <a href="https://www.youtube.com/watch?v=4dh--iD_zK8&amp;feature=youtu.be&amp;t=3444">на Гейзенбаге 2018</a>.</p> <p>Собственно, мы эту проблему не исправили, но улучшили сообщение об ошибке. Теперь там видно актуальное значение атрибута:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Actual</span> <span class="nl">value:</span> <span class="n">href</span><span class="o">=</span><span class="s">"http://my-test-env.com:9999/files/hello_world.txt"</span> </code></pre></div></div> <p><br /></p> <h1 id="469-улучшили-текст-ошибки-для-or-условий">#469 Улучшили текст ошибки для OR условий</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">"h1"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">or</span><span class="o">(</span><span class="s">"poetic"</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">text</span><span class="o">(</span><span class="s">"Горят костры рябин"</span><span class="o">)</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="nc">Element</span> <span class="n">should</span> <span class="n">have</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Пролетел</span> <span class="n">жёлтый</span> <span class="n">лист</span> <span class="n">по</span> <span class="n">бульварам</span> <span class="nc">Москвы</span><span class="err">'</span> <span class="o">{</span><span class="n">h1</span><span class="o">}</span> </code></pre></div></div> <p><br /></p> <p>Мы это дело подправили, теперь он рапортует все условия из OR:</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">be</span> <span class="nl">poetic:</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Пролетел</span> <span class="n">жёлтый</span> <span class="n">лист</span> <span class="n">по</span> <span class="n">бульварам</span> <span class="nc">Москвы</span><span class="err">'</span> <span class="n">or</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Горят</span> <span class="n">костры</span> <span class="n">рябин</span><span class="err">'</span> <span class="o">{</span><span class="n">h1</span><span class="o">}</span> <span class="nl">Element:</span> <span class="err">'</span><span class="o">&lt;</span><span class="n">h1</span><span class="o">&gt;</span><span class="mi">3</span> <span class="n">сентября</span><span class="o">&lt;/</span><span class="n">h1</span><span class="o">&gt;</span><span class="err">'</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/469">Issue 469</a> и <a href="https://github.com/selenide/selenide/pull/962">PR 962</a>.</p> <p><br /></p> <h1 id="обновились-до-webdrivermanager362">Обновились до webdrivermanager:3.6.2</h1> <p>Похоже, в основном исправления для Edge. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">Changelog тут</a>.</p> <p><br /></p> <p>Всё то и всё так!</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Автор WebDriverManager Boni García запилил <a href="https://bonigarcia.github.io/selenium-jupiter">selenium-jupiter</a> - обёртку для Selenide (+JUnit5 +Docker). Обёртку для обёртки, прикиньте!</li> <li>Сравнение Selenide и FluentLenium: <a href="https://testcraftsmanship.com/articles/2019/selenide_vs_fluentlenium_part1.html">Часть 1</a>, <a href="https://testcraftsmanship.com/articles/2019/selenide_vs_fluentlenium_part2.html">Часть 2</a></li> <li>Чума! Сергей Егоров (коммитер TestContainers) сотворил волшебство: <a href="https://github.com/bsideup/jabel">Jabel</a>. Теперь вы можете писать на Java13 и компилировать код в байт-код Java8.</li> </ul> <p><br /></p> <h2 id="конференции">Конференции</h2> <p>Приходите на конференцию <a href="http://qafest.com/en/">QA Fest</a> в Киеве 20-21 сентября!<br /><br /> Говорят, это крупнейшая конференция по тестированию в Украине. 5 потоков! <br /> Много крутых спикеров и спикерш.</p> <p>У меня будет два доклада:</p> <ul> <li>“Селенид для продвинутых” <br /> (параллелизация, статика, листенеры, прокси, трюки с JavaScript, производительность)</li> <li>“10 причин моей ненависти” <br /> (TestNG, репорты, пэдж обжекты, try/catch, ифы в тестах, dependency injection, spring и OverKISS)</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/2019/09/02/selenide-5.3.0/ http://ru.selenide.org/2019/09/02/selenide-5.3.0 2019-09-02T00:00:00+00:00 Вышла Selenide 5.2.7 <p>Всем летний привет!</p> <p>Мы выпустили несколько минорных обновлений Selenide: 5.2.5-5.2.7. Давайте расскажу, зачем понадобилось несколько.</p> <p><br /></p> <h1 id="подправили-сообщения-об-ошибках-при-запуске-selenideappium">Подправили сообщения об ошибках при запуске Selenide+Appium</h1> <p>Appium - это вебдрайвер для мобилок (<code class="language-plaintext highlighter-rouge">AndroidDriver</code>, <code class="language-plaintext highlighter-rouge">IOSDriver</code>). Казалось бы, хорошая идея - использовать для тестирования мобилок знакомый протокол Selenium WebDriver. Только вот многие вещи, казавшиеся логичными и стандартными в вебе, в мобилках работают по-другому или вовсе не работают.</p> <blockquote> <p>Appium для Selenium - это как геометрия Лобачевского для геометрии евклидовой: всё круглое и через жопу.</p> </blockquote> <p>Из-за этого, в частности, Селенид выдавал некорректные сообщения об ошибках при падении тестов. Потому, что при попытке сформировать сообщение об ошибке Селенид вызывает некоторые “стандартные” методы, которые до аппиума всегда работали во всех браузерах: <code class="language-plaintext highlighter-rouge">$.getTagName()</code>, <code class="language-plaintext highlighter-rouge">$.isDisplayed()</code>, <code class="language-plaintext highlighter-rouge">executeJavascript()</code>, но которые падают в Appium (или в Android, или в iOS, или везде).</p> <p>В общем, для исправления этой неприятности пришлось нам добавить несколько <code class="language-plaintext highlighter-rouge">catch (NoSuchElementException | UnsupportedOperationException | UnsupportedCommandException | WebDriverException)</code>, по сути нужных только в случае аппиума. В общем-то версии 5.2.6 и 5.2.7 как раз и понадобились для того, чтобы добавить очередной catch для очередного странного кейса.</p> <p>Ну ничего, зато теперь Селенид с мобилками работает стабильнее.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/496">issue 496 - Fix $.toString() in Appium</a></p> <p>NB! <strong>Примеры тестов Selenide для мобилок</strong> можно найти <a href="https://github.com/selenide-examples/selenide-appium">на гитхабе</a>.</p> <p><br /></p> <h1 id="добавили-метод-open-без-параметров">Добавили метод <code class="language-plaintext highlighter-rouge">open()</code> без параметров</h1> <p>Этот метод просто открывает браузер. Пустой, blank. Без ссылки, blank.</p> <p>Это тоже нужно для мобилок - ведь Селенид 5.+ требует, чтобы браузер был открыт перед тем, как совершать какие-либо операции с элементами. А в аппиум традиционный метод <code class="language-plaintext highlighter-rouge">open(URL)</code> не работает - там ведь нет никакого URL.</p> <p>Глупая ситуация, но выход мы нашли элегантный, правда? :)</p> <p>(Люди ведь и раньше просили возможность открыть пустой браузер. Как это ни странно.)</p> <p>Спасибо <a href="https://github.com/yaroslav-orel">yaroslav-orel</a> за <a href="https://github.com/selenide/selenide/pull/956">PR 956</a>.</p> <p><br /></p> <h1 id="добавили-метод-because-для-коллекций">Добавили метод <code class="language-plaintext highlighter-rouge">because</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">"#login"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">disabled</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#login"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">disabled</span><span class="o">.</span><span class="na">because</span><span class="o">(</span><span class="s">"Этот неудачник ввёл неверный пароль 3 раза"</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="s">".cv"</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="err">$$</span><span class="o">(</span><span class="s">".cv"</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="na">because</span><span class="o">(</span><span class="s">"У Пети два CV, а у Ани одно."</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/rkliuha">Roman Kliuha</a> за <a href="https://github.com/selenide/selenide/pull/904">PR 904</a></p> <p><br /></p> <h1 id="исправили-казус-с-open-внутри-open">Исправили казус с open внутри open</h1> <p>А это прямо анекдотичный случай. Вызываешь ты такой метод <code class="language-plaintext highlighter-rouge">open(url)</code>, а он тебе кидает ошибку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalStateException:</span> <span class="nc">No</span> <span class="n">webdriver</span> <span class="n">is</span> <span class="n">bound</span> <span class="n">to</span> <span class="n">current</span> <span class="nl">thread:</span> <span class="mi">19</span><span class="o">.</span> <span class="nc">You</span> <span class="n">need</span> <span class="n">to</span> <span class="n">call</span> <span class="nf">open</span><span class="o">(</span><span class="n">url</span><span class="o">)</span> <span class="n">first</span><span class="o">.</span> </code></pre></div></div> <p>Типа, “вызвать <code class="language-plaintext highlighter-rouge">open</code> нельзя, потому что сначала нужно вызвать <code class="language-plaintext highlighter-rouge">open</code>”. Вызывать <code class="language-plaintext highlighter-rouge">open</code> <em>экономически невыгодно</em>.</p> <p>Дело оказалось в связке Selenide+Allure.</p> <ol> <li>Селенид попытался открыть браузер</li> <li>не смог (например, не нашёл бинарник Chrome в PATH)</li> <li>Аллюр попытался добавить эту ошибку в свой лог</li> <li>Аллюр захотел для этого сделать скриншот</li> <li>обратился к текущему браузеру - получил ошибку.</li> </ol> <p>См. фикс в</p> <ul> <li>Selenide: <a href="https://github.com/selenide/selenide/issues/928">issue 928</a> и <a href="https://github.com/selenide/selenide/pull/958">PR 958</a></li> <li>Allure: <a href="https://github.com/allure-framework/allure-java/issues/379">issue 379</a> и <a href="https://github.com/allure-framework/allure-java/pull/380">PR 380</a></li> </ul> <p><br /></p> <h2 id="новости">Новости</h2> <p>Яков Крамаренко, в своём время активный продвигатель идей селенида, проснулся от зимней спячки и бесплатно опубликовал свои курсы по автоматизации. А также начал писать книгу про Selenide:</p> <ul> <li>Откровение от Якова <a href="https://leanpub.com/selenide-automation-ru">Искусство Автоматизации с Selenide</a></li> <li>Курсы Якова <a href="https://www.youtube.com/playlist?list=PLWKsep_LKQYq_QRa4ROEjLse7jDbiSl-H">Автоматизация с нуля (Java, Selenide, Widgets aka PageObjects)</a></li> <li>Видео <a href="https://www.youtube.com/watch?v=4JJNccWtdNI">Антистатик</a> - Андрей Солнцев в <a href="https://devclub.eu">DevClub</a>, Таллинн, 27.06.2019</li> <li>Видео <a href="https://comaqa.by/2019/07/11/comaqa-spring-2019-video-vistuplenia-a-vinogradova/">«Selenide 2019 — Quo vadis? Новинки и малоизвестные функции библиотеки»</a> - Алексей Виноградов на ComaQA Spring 2019</li> <li>Пост <a href="/2019/07/08/code-simplicity/">Простота кода</a> - блог Selenide, 08.07.2019</li> </ul> <p><br /></p> <h2 id="конференции">Конференции</h2> <ul> <li>Я выступаю в Киеве 20-21 сентября на конференции <a href="http://qafest.com/en/">QA Fest</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/08/01/selenide-5.2.7/ http://ru.selenide.org/2019/08/01/selenide-5.2.7 2019-08-01T00:00:00+00:00 Простота кода <p>Давайте поговорим о простоте кода. Наверное, все согласны с тем, что простой код - хороший код. Только все понимают “простоту” по-разному.</p> <p>Я хочу показать пример, всплывший в комментариях к нашему сайту, чтобы вы могли сравнить, что такое сложно, а что такое просто.</p> <h3 id="задача">Задача</h3> <p>Есть некий список типа “Gmail” с чекбоксами и заголовками. Требуется пэджобжект, который позволил бы найти по заголовку и кликнуть один или несколько чекбоксов.</p> <h3 id="структура-html">Структура HTML</h3> <p>не важна, но схематично выглядит она так:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"boxCheckbox"</span><span class="nt">&gt;</span> <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"checkbox"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/input&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"boxLabel"</span><span class="nt">&gt;</span> Here is checkbox #1 <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">&gt;</span> ... <span class="nt">&lt;/div&gt;</span> ... </code></pre></div></div> <p>т.е. у нас есть несколько <code class="language-plaintext highlighter-rouge">&lt;div class="box"&gt;</code>, внутри каждого пара <code class="language-plaintext highlighter-rouge">&lt;div class="boxCheckbox"&gt;</code> + <code class="language-plaintext highlighter-rouge">&lt;div class="boxLabel"&gt;</code>.</p> <h3 id="сложный-код">Сложный код</h3> <p>Перед вами <strong>типичный</strong> код, который мне приходилось видеть примерно миллион раз.</p> <p>Это олицетворение современной автоматизации тестирования (так мне иногда кажется).</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.openqa.selenium.WebElement</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">org.openqa.selenium.support.FindBy</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">java.util.ArrayList</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">SeleniumPageObject</span> <span class="o">{</span> <span class="c1">// Search for element index in the list by title</span> <span class="kd">public</span> <span class="kd">static</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="nf">findListIndexesByTitle</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">WebElement</span><span class="o">&gt;</span> <span class="n">elementList</span><span class="o">,</span> <span class="nc">String</span> <span class="nc">SearchText</span><span class="o">)</span> <span class="o">{</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">ints</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&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="n">elementList</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">elementList</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">getText</span><span class="o">().</span><span class="na">contains</span><span class="o">(</span><span class="nc">SearchText</span><span class="o">))</span> <span class="o">{</span> <span class="n">ints</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">i</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="k">return</span> <span class="n">ints</span><span class="o">;</span> <span class="o">}</span> <span class="c1">// Buttons in the boxes</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">xpath</span> <span class="o">=</span> <span class="s">"//div[@class='boxCheckbox']"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">WebElement</span><span class="o">&gt;</span> <span class="n">selectCheckBoxesInBoxes</span><span class="o">;</span> <span class="c1">// Titles for boxes</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">xpath</span> <span class="o">=</span> <span class="s">"//div[@class='boxLabel']"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">WebElement</span><span class="o">&gt;</span> <span class="n">listOfTitlesInBoxes</span><span class="o">;</span> <span class="c1">// Select Box by title</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">selectBoxByTitle</span><span class="o">(</span><span class="nc">String</span> <span class="n">searchTitle</span><span class="o">)</span> <span class="o">{</span> <span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="n">findListIndexesByTitle</span><span class="o">(</span><span class="n">listOfTitlesInBoxes</span><span class="o">,</span> <span class="n">searchTitle</span><span class="o">).</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> <span class="n">selectBoxByIndex</span><span class="o">(</span><span class="n">index</span><span class="o">);</span> <span class="o">}</span> <span class="c1">// Select Box by index</span> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">selectBoxByIndex</span><span class="o">(</span><span class="kt">int</span> <span class="n">elPosition</span><span class="o">)</span> <span class="o">{</span> <span class="n">selectCheckBoxesInBoxes</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">elPosition</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> <span class="o">}</span> <span class="c1">// Select All Boxes by title</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">selectAllBoxesByTitle</span><span class="o">(</span><span class="nc">String</span> <span class="n">searchTitle</span><span class="o">)</span> <span class="o">{</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">ints</span> <span class="o">=</span> <span class="n">findListIndexesByTitle</span><span class="o">(</span><span class="n">listOfTitlesInBoxes</span><span class="o">,</span> <span class="n">searchTitle</span><span class="o">);</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="n">ints</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="n">selectBoxByIndex</span><span class="o">(</span><span class="n">ints</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="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Не буду повторяться, <a href="https://asolntsev.github.io/ru/2010/05/02/javadoc/">почему комментарии в коде не нужны</a>, что <a href="https://asolntsev.github.io/ru/2016/07/09/true-page-object/"><code class="language-plaintext highlighter-rouge">@FindBy</code> обычно ничего не упрощает</a> и т.д. Просто сравните эти два куска кода.</p> <h3 id="простой-код">Простой код</h3> <p>А вот во что я смог превратить этот код, переписав на Selenide:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">com.codeborne.selenide.SelenideElement</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">Condition</span><span class="o">.</span><span class="na">text</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">Selenide</span><span class="o">.</span><span class="err">$$</span><span class="o">;</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">SelenidePageObject</span> <span class="o">{</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">selectBoxByTitle</span><span class="o">(</span><span class="nc">String</span> <span class="n">title</span><span class="o">)</span> <span class="o">{</span> <span class="n">selectBox</span><span class="o">(</span><span class="err">$$</span><span class="o">(</span><span class="s">".boxLabel"</span><span class="o">).</span><span class="na">findBy</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="n">title</span><span class="o">)));</span> <span class="o">}</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">selectAllBoxesByTitle</span><span class="o">(</span><span class="nc">String</span> <span class="n">searchTitle</span><span class="o">)</span> <span class="o">{</span> <span class="err">$$</span><span class="o">(</span><span class="s">".boxLabel"</span><span class="o">).</span><span class="na">filterBy</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="n">searchTitle</span><span class="o">)).</span><span class="na">forEach</span><span class="o">(</span><span class="k">this</span><span class="o">::</span><span class="n">selectBox</span><span class="o">);</span> <span class="o">}</span> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">selectBox</span><span class="o">(</span><span class="nc">SelenideElement</span> <span class="n">boxLabel</span><span class="o">)</span> <span class="o">{</span> <span class="n">boxLabel</span><span class="o">.</span><span class="na">closest</span><span class="o">(</span><span class="s">".box"</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="s">".boxCheckbox"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Правда, ПРОСТО?</p> <p>Этот вариант тоже не идеален. Но главное - он гораздо короче и проще. Его проще поддерживать. Его проще понять. Его проще поменять. Его проще переписать с нуля, в конце концов.</p> <p>P.S. Заметили трюк? Я сказал, что переписал код на Селенид, но на самом деле выигрыш получился не столько от Селенида, сколько от упрощения селекторов, выкидывания лишнего, использования лямбд (т.е. более современных средств языка). В общем, вы все с этим справитесь.</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/07/08/code-simplicity/ http://ru.selenide.org/2019/07/08/code-simplicity 2019-07-08T00:00:00+00:00 Вышла Selenide 5.2.4 <p>Всем привет из лета!</p> <p>Пока в Эстонии празднуют Иванову Ночь и День Победы, мы выпускаем Selenide 5.2.4</p> <p>Что нового?</p> <p><br /></p> <h1 id="исправили-работу-прокси-с-приложением-на-localhost">Исправили работу прокси с приложением на localhost</h1> <p>Как вы знаете, Селенид может запускать свой встроенный прокси-сервер для некоторых функций (скачивание файлов, авторизация, логирование и т.п.). Последние версии популярных браузеров (Chrome72+ и Firefox 67+) начали по умолчанию запрещать прокси перехватывать запросы на <code class="language-plaintext highlighter-rouge">localhost</code>. Типа из соображений безопасности.</p> <p>Из-за этого сломалось скачивание файлов у тех ребят, которые запускают AUT на <code class="language-plaintext highlighter-rouge">http://localhost:port</code> (как я).</p> <p>Пришлось поковыряться в настройках и добавить хитрые ключики, которые снова разрешают прокси перехватывать запросы на localhost.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/950">PR 950</a>.</p> <p><br /></p> <h1 id="исправили-screenshooterextension-для-junit5">Исправили ScreenShooterExtension для JUnit5</h1> <p>Как вы знаете, когда Селенид кидает ошибку (как правило, это подкласс <code class="language-plaintext highlighter-rouge">UIAssertionError</code>), он всегда автоматически делает скриншот. Обычно этого достаточно, но иногда люди хотят использовать какие-нибудь свои ассерты (из JUnit, Hamcrest, AssertJ и т.д.) В общем случае для этих ошибок Селенид не может делать скриншот, ведь тогда Селенид должен был бы зависеть от конкретного фреймворка.</p> <p>Но для трёх самых распространённых в Селениде есть поддержка: <a href="https://github.com/selenide/selenide/blob/master/modules/junit4/src/main/java/com/codeborne/selenide/junit/ScreenShooter.java">JUnit4</a>, <a href="https://github.com/selenide/selenide/blob/master/statics/src/main/java/com/codeborne/selenide/junit5/ScreenShooterExtension.java">JUnit5</a>, <a href="https://github.com/selenide/selenide/blob/master/modules/testng/src/main/java/com/codeborne/selenide/testng/ScreenShooter.java">TestNG</a>.</p> <p>Так вот, в <a href="https://github.com/selenide/selenide/blob/master/statics/src/main/java/com/codeborne/selenide/junit5/ScreenShooterExtension.java"><code class="language-plaintext highlighter-rouge">ScreenShooterExtension</code></a> для JUnit5 была ошибка. До сих пор он делал скриншоты <em>только</em> для селенидовских ошибок <code class="language-plaintext highlighter-rouge">UIAssertionError</code>. А надо наоборот - для НЕ селенидовских. Вот это мы и <a href="https://github.com/selenide/selenide/commit/5414bc743469d0624e6f5">исправили</a>.</p> <p><br /></p> <h1 id="улучшили-сообщение-об-ошибке-в-shouldhavetexts1-2-3">Улучшили сообщение об ошибке в <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("1", "2", "3"))</code></h1> <p>Метод <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts())</code> позволяет разом проверить тексты целого ряда элементов. Удобен для проверки, например, целой строки или колонки таблицы. В некоторых (редких) случаях он мог давать не совсем понятное (хоть и корректное) сообщение. Мы его улучшили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/454">issue 454</a>.<br /> Спасибо <a href="https://github.com/xaknem">A.Smashentsev</a> за <a href="https://github.com/selenide/selenide/pull/944">PR 944</a>.</p> <p><br /></p> <h1 id="метод-clickoffsetx-offsety-теперь-считает-координаты-от-центра-элемента">Метод <code class="language-plaintext highlighter-rouge">$.click(offsetX, offsetY)</code> теперь считает координаты от ЦЕНТРА элемента</h1> <p>Не все знают, но у метода <code class="language-plaintext highlighter-rouge">$.click()</code> есть метод-побратим <code class="language-plaintext highlighter-rouge">$.click(offsetX, offsetY)</code> для клика со сдвигом. Классический метод <code class="language-plaintext highlighter-rouge">$.click()</code> кликает в центр элемента. А вот с методом <code class="language-plaintext highlighter-rouge">$.click(offsetX, offsetY)</code> сложнее - в некоторых браузерах сдвиг считался от центра, а в некоторых - от левого верхнего угла. Даже в документации Selenium класса <code class="language-plaintext highlighter-rouge">org.openqa.selenium.interactions.Actions</code> есть противоречивые сведения на эту тему:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cm">/** * Moves the mouse to an offset from the top-left corner of the element. */</span> <span class="kd">public</span> <span class="nc">Actions</span> <span class="nf">moveToElement</span><span class="o">(</span><span class="nc">WebElement</span> <span class="n">target</span><span class="o">,</span> <span class="kt">int</span> <span class="n">xOffset</span><span class="o">,</span> <span class="kt">int</span> <span class="n">yOffset</span><span class="o">)</span> <span class="o">{</span> <span class="o">...</span> <span class="c1">// Of course, this is the offset from the centre of the element. We have no idea what the width</span> <span class="c1">// and height are once we execute this method.</span> <span class="o">...</span> <span class="o">}</span> </code></pre></div></div> <p>Теперь официально координаты считаются от центра элемента.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/950">PR 950</a>.</p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <p>Обновили версии зависимостей:</p> <ul> <li>WebDriverManager 3.6.1</li> </ul> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li><a href="https://www.testdevlab.com/blog/2019/05/testui-ui-test-automation-for-all-platforms-with-appium-and-selenide/">Фреймворк TestUI на базе Appium и Selenide</a></li> <li>Во Львове проводят <a href="https://dou.ua/calendar/27417">воркшоп “Selenide + Selenoid”</a>. Круто!</li> <li>А в Николаеве - <a href="https://www.globallogic.com/ua/news/gl-ui-automation-cookbook-mykolaiv/">UI Automation Cookbook “Selenide + Allure”</a>. Тоже норм!</li> <li>Статья от LambdaTest <a href="https://www.lambdatest.com/blog/selenium-testing-with-selenide-using-intellij-maven/">“Selenide+IntelliJ+Maven”</a></li> <li>Статья на японском <a href="https://qiita.com/tatesuke/items/0bac60172e7cfd12aeb1">“Page Object на Selenide”</a></li> <li>Презентаха на японском <a href="https://speakerdeck.com/shimashima35/example-of-e2e-automation-test-architecture-by-selenide-in-osaka">“Пример разработки инфраструктуры тестирования E2E в стиле DSL на Selenide”</a></li> <li>Ещё статья на японском <a href="https://codezine.jp/article/detail/10335">“Selenoid + Selenide + Page objects”</a></li> <li>Ещё статья на японском про <a href="https://qiita.com/shimashima35/items/0575ac5488edd6942d5a">запись видео во время запуска Selenide</a></li> <li>Амбициозная презентаха <a href="https://www.slideshare.net/Bugraptors/selenide-vs-selenium-the-war-of-technologies">“Selenide vs. Selenium: The War Of Technologies”</a>. Даёшь батл!</li> </ul> <p><br /></p> <h1 id="конференции">Конференции</h1> <ul> <li>27 июня рассказываю в DevClub, <a href="https://www.facebook.com/events/1335258949960597/">чем же плохи статические методы</a></li> <li>Я выступаю в Киеве 20-21 сентября на конференции <a href="http://qafest.com/en/">QA Fest</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/06/20/selenide-5.2.4/ http://ru.selenide.org/2019/06/20/selenide-5.2.4 2019-06-20T00:00:00+00:00 Вышла Selenide 5.2.3 <p>Когда, если не на майских?</p> <p>Мы выпустили Selenide 5.2.3 с небольшими, не обратно несовместимым изменениями.</p> <p>Испугались?</p> <p>Саечку за испуг!</p> <p>Всё не так страшно. Подробности ниже:</p> <p><br /></p> <h1 id="добавили-метод-selenideloggerbeforeevent">Добавили метод <code class="language-plaintext highlighter-rouge">SelenideLogger.beforeEvent()</code></h1> <p>Иногда нужно что-то логировать ДО совершения действия. Например, писать что-то в лог ПЕРЕД любым кликом. У Селенида есть для этого интерфейс <code class="language-plaintext highlighter-rouge">SelenideLogger</code>, но до сих пор в нём было только один метод <code class="language-plaintext highlighter-rouge">onEvent()</code>, срабатывающий ПОСЛЕ действия. Теперь же мы добавили метод <code class="language-plaintext highlighter-rouge">beforeEvent</code>. А <code class="language-plaintext highlighter-rouge">onEvent</code> переименовали в <code class="language-plaintext highlighter-rouge">afterEvent</code>, чтобы было понятнее.</p> <p>Спасибо <a href="https://github.com/pavelpp">pavelpp</a> за <a href="https://github.com/selenide/selenide/pull/927">PR 927</a>!</p> <p>NB! Это изменение обратно несовместимое.</p> <ol> <li>Если вы реализовали <code class="language-plaintext highlighter-rouge">SelenideLogger</code> в своём проекте, придётся переименовать/добавить метод в вашей реализации.</li> <li>Если вы используете библиотеку <code class="language-plaintext highlighter-rouge">selenide-allure</code>, придётся дождаться новой версии <code class="language-plaintext highlighter-rouge">selenide-allure</code> вот <a href="https://github.com/allure-framework/allure-java/pull/351">с этим PR</a>.</li> </ol> <p><br /></p> <h1 id="теперь-можно-открывать-пустую-страницу">Теперь можно открывать пустую страницу</h1> <p>Иногда в тесте нужно открыть пустую страницу - скажем, чтобы остановить все предыдущие ajax-запросы и начать тест с чистого листа.</p> <p>Теперь для этого можно использовать команду <code class="language-plaintext highlighter-rouge">open("about:blank")</code> (раньше селенид пытался добавить <code class="language-plaintext highlighter-rouge">baseUrl</code> в начало адреса).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/914">issue 914</a> и <a href="https://github.com/selenide/selenide/pull/915">PR 915</a>.</p> <p><br /></p> <h1 id="рефакторинг-вынесли-все-condition-в-отдельные-классы">Рефакторинг: вынесли все <code class="language-plaintext highlighter-rouge">Condition</code> в отдельные классы</h1> <p>Обычного пользователя Селенида это не касается. Просто для справки.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/912">PR 912</a>.</p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <ul> <li>раз: WebDriverManager 3.4.0 (в т.ч. поддержка Chrome 72, 73 и 74)</li> <li>два: HtmlUnit 2.34.1</li> <li>три: HtmlUnitDriver 2.34.0</li> </ul> <h2 id="известные-проблемы">Известные проблемы</h2> <p>Chrome и Chromedriver двух последних версий (73 и 74) почему-то не работают с BrowserMobProxy, бегающем на localhost. Из-за этого не работает скачивание файлов и BasicAuth через прокси. У вас тоже так?</p> <p>Мы пока не придумали ничего лучше, чем запускать тесты на Chrome 71. Качаем его <a href="https://www.slimjet.com/chrome/download-chrome.php?file=files%2F71.0.3578.80%2Fgoogle-chrome-stable_current_amd64.deb">отсюда</a>.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Ура, наконец-то Selenide попал в ТОП! <br /><a href="https://dev.to/arnabroychowdhury/top-5-java-test-frameworks-for-automation-in-2019-1528">Top 5 Java Test Frameworks For Automation In 2019</a></li> <li>Доклад Ярослава Святкина на QA Fest - сравнение трёх фреймворков Serenity, Selenide и Geb <br /><a href="https://dou.ua/lenta/articles/autotest-groovy/">Готовые решения для QA: как писать автотесты на Groovy</a></li> <li>Чего <strong>не стоит</strong> делать с помощью Selenium (официально от авторов Selenium):<br /> <a href="https://seleniumhq.github.io/docs/worst.html">Selenium worst practices</a></li> <li>Разрушаем языковые барьеры. Статья про Selenide <strong>на испанском языке</strong>! <br /> <a href="https://folderit.net/itech/selenide-framework-for-testing-automation/">Часть 1</a> и <a href="https://folderit.net/itech/selenide-framework-for-testing-automation-segunda-parte/">часть 2</a>.</li> <li>Статья про Selenide+Allure: <a href="https://www.linkedin.com/pulse/adding-masala-selenide-test-automation-framework-amarasiri-/">Adding Masala to the Selenide Test Automation Framework with Allure</a>.</li> <li>Ещё одно руководство для начинающих от компании LambdaTest: <br /> <a href="https://www.lambdatest.com/blog/selenium-testing-with-selenide-using-intellij-maven/">Selenium Tests Using Selenide, IntelliJ, And Maven</a></li> <li>Статья на японском про Selenide+Allure+Cucumber+Maven: <a href="https://qiita.com/rolengrays/items/02030397fd2542021dd3">GUI test automation and result visualization</a>.</li> </ul> <p><br /></p> <h1 id="конференции">Конференции</h1> <ul> <li>Я выступаю в Киеве 20-21 сентября на конференции <a href="http://qafest.com/en/">QA Fest</a>. Предварительно планируется два доклада. Приходите!</li> <li>Ух ты! Некая Hima Bindu Peteti <a href="https://www.youtube.com/watch?v=xpP_XYWqmQ0&amp;list=PL67l1VPxOnT5PZQ1r60wQoT2UPDk1of4z">рассказывала про Селенид</a> на конференции <a href="https://pbs.twimg.com/media/D480l1rUcAA63-Q.jpg:large">SauceCon</a> в Остине (Техас).</li> <li>Она же будет рассказывать про Селенид в Англии на конференции DevTEST Conference: <br /> <a href="https://www.softwaretestingnews.co.uk/products/devtest-conference-north/speakers/hima-bindu-peteti/">BDD with Selenide</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/05/07/selenide-5.2.3/ http://ru.selenide.org/2019/05/07/selenide-5.2.3 2019-05-07T00:00:00+00:00 Вышла Selenide 5.2.0 <p>Привет!</p> <p>А вот подоспел и пресс-релиз версии Selenide 5.2.0, которая вышла 19.02.2019.</p> <p>Давайте быстренько глянем, что там под капотом, и побежим вперёд к следующим версиям.</p> <p><br /></p> <h1 id="добавили-алиасы-для-котлина">Добавили алиасы для Котлина</h1> <p>Поскольку в Kotlin нельзя использовать <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code> в качестве названия метода, использование Selenide на Kotlin изначально было не таким уж радужным, как на Java. <a href="https://github.com/selenide-examples/kotlin/blob/master/src/test/kotlin/GoogleTest.kt">Здесь</a> можно посмотреть некоторые варианты, как это можно было решить.</p> <p>После долгих споров и изысканий мы остановились на просто и понятном варианте: заменить <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code> методами <code class="language-plaintext highlighter-rouge">element</code> и <code class="language-plaintext highlighter-rouge">elements</code>. Чуть длиннее, чем <code class="language-plaintext highlighter-rouge">$</code>, но зато предложение читается по-человечески:</p> <ul> <li><code class="language-plaintext highlighter-rouge">element(".header").shouldNot(exist)</code></li> <li><code class="language-plaintext highlighter-rouge">elements(".header").shouldHave(size(3))</code></li> </ul> <p>См. <a href="https://github.com/selenide/selenide/issues/865">issue 865</a>.</p> <p>Спасибо <a href="https://github.com/jkromski">Jacek Kromski</a> за <a href="https://github.com/selenide/selenide/pull/870">PR 870</a>!</p> <p><br /></p> <h1 id="добавили-метод-для-проверки-выделенного-текста">Добавили метод для проверки выделенного текста</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">"textarea"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">selectedText</span><span class="o">(</span><span class="s">"oo ba"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"textarea"</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">"foo bar"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/766">issue 766</a>.</p> <p>Спасибо <a href="https://github.com/symonk">symonk</a> за <a href="https://github.com/selenide/selenide/pull/876">PR 876</a>!</p> <p><br /></p> <h1 id="улучшили-поддержку-chromeoptionsargs-и-chromeoptionsprefs">Улучшили поддержку <code class="language-plaintext highlighter-rouge">chromeoptions.args</code> и <code class="language-plaintext highlighter-rouge">chromeoptions.prefs</code></h1> <p>Я вообще сильно сомневаюсь, что это полезная фича. Проще уж реализовать свой <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>. Но фича есть, и её надо поддерживать. Больше подробностей в PR.</p> <p>Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/pull/883">PR 883</a>.</p> <p><br /></p> <h1 id="удалили-старый-хак-для-максимизации-окна-браузера-в-chrome">Удалили старый хак для максимизации окна браузера в Chrome</h1> <p>Раньше хром не умел максимизировать окно браузера, поэтому в Selenide был специальный хак для хрома. Теперь хром научился, и наш хак стал не нужен.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/838">issue 838</a> и <a href="https://github.com/selenide/selenide/pull/901">PR 901</a>.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Сравнительный анализ: <a href="https://speakerdeck.com/poohsunny/selenide-vs-geb">Selenide vs Geb</a></li> <li>Neodymium: новый фреймворк на основе Selenide от германской компании <a href="https://www.xceptance.com/en/company/">Xceptance</a> <br /> <a href="https://dzone.com/articles/neodymium-an-open-source-framework-for-web-testing">Статья 1</a> | <a href="https://blog.xceptance.com/2019/02/26/neodymium-an-open-source-framework-for-web-testing/">Статья 2</a></li> <li>Akita: <a href="https://habr.com/ru/company/alfa/blog/350238/">Фреймворк от Альфа-банка на основе Selenide</a></li> <li>Статья про <a href="https://habr.com/ru/company/alfa/blog/441674/">тестирование вёрстки от Альфа-банка</a> (естественно, с использованием Selenide)</li> <li>Статья про BDD на Selenide: <a href="https://hackernoon.com/bdd-writing-a-test-suite-before-writing-code-6279e4cf4be6">BDD: Writing an Automated Test Suite isn’t Rocket Science</a></li> <li>И ещё про <a href="https://www.linkedin.com/pulse/eureka-integration-selenide-behavior-driven-amarasiri-/">BDD на Selenide+Cucumber</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/02/19/selenide-5.2.0/ http://ru.selenide.org/2019/02/19/selenide-5.2.0 2019-02-19T00:00:00+00:00 Вышла Selenide 5.1.1 <p>Привет!</p> <p>С большим запозданием мы публикуем пресс-релиз Selenide 5.1.1 Давайте быстренько глянем, что там под капотом, и побежим вперёд к следующим версиям.</p> <h1 id="selenide-позволяет-кликать-по-прозрачным-элементам">Selenide позволяет кликать по прозрачным элементам</h1> <p>До сих пор Selenide не позволял кликать по прозрачным элементам (у которых <code class="language-plaintext highlighter-rouge">opacity: 0</code>). Это казалось разумным, ведь реальный пользователь тоже не может кликнуть элемент, который он не видит. <br /> Но выяснилось, что иногда это нужно - например, когда под прозрачным элементом располагается поле для загрузки файла.</p> <p>Теперь мы это не запрещаем. :)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/201">issue 201</a></p> <p>Спасибо <a href="https://github.com/vinogradoff">Алексею Виноградову</a> за <a href="https://github.com/selenide/selenide/pull/874">PR 874</a> и <a href="https://github.com/barancev">Алексею Баранцеву</a> за терпеливые объяснения в чатах.</p> <p><br /></p> <h1 id="исправили-npe-когда-вебдрайвер-кастомный-и-без-прокси-а-прокси-хочется">Исправили NPE, когда вебдрайвер кастомный и без прокси, а прокси хочется</h1> <p>Теперь вместо <code class="language-plaintext highlighter-rouge">NullPointerException</code> будет лететь понятный</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalStateException:</span> <span class="n">config</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">==</span> <span class="kc">true</span> <span class="n">but</span> <span class="n">proxy</span> <span class="n">server</span> <span class="n">is</span> <span class="n">not</span> <span class="n">started</span><span class="o">.</span> </code></pre></div></div> <p>Всё для людей! :)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/878">issue 878</a> и <a href="https://github.com/selenide/selenide/pull/888">PR 888</a></p> <p><br /></p> <h1 id="теперь-можно-сколько-угодно-переключаться-между-двумя-вебдрайверами">Теперь можно сколько угодно переключаться между двумя вебдрайверами</h1> <p>Это работало в Selenide 4.x и раньше, а в версии 5.0.0 сломалось. Теперь починили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/867">issue 867</a> и <a href="https://github.com/selenide/selenide/pull/890">PR 890</a></p> <p><br /></p> <h1 id="селенид-генерирует-уникальную-папку-для-каждого-скачанного-файла">Селенид генерирует уникальную папку для каждого скачанного файла</h1> <p>До сих пор Селенид скачивал все файлы в папку <code class="language-plaintext highlighter-rouge">build/reports/tests</code> с их оригинальными именами. Это могло создать проблему, если два параллельных теста скачивают файлы с одинаковыми именами.</p> <p>Теперь Селенид будет скачивать каждый файл в уникальную папку <code class="language-plaintext highlighter-rouge">build/reports/UUID</code> (но по-прежнему со своим оригинальным именем, ведь имя файла может быть важно для теста).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/892">issue 892</a> и <a href="https://github.com/selenide/selenide/pull/893">PR 893</a>.</p> <p><br /></p> <h1 id="обновились-на-selenium-java-314159">Обновились на selenium-java 3.141.59</h1> <p>Эту версию селениума авторы в шутку назвали “Пи”: 3.141.59 Кажется, это последняя версия в линейке 3.x - за ней будет Selenium 4.0</p> <p><a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Список изменений</a> в selenium 3.141.*</p> <h2 id="новости">Новости</h2> <ul> <li>Три классные статьи от коммитера Селенида <a href="https://github.com/rosolko">Александра Росолко</a> <ul> <li>Кратко и ёмко: <a href="https://medium.com/@rosolko/simple-allure-2-configuration-for-gradle-8cd3810658dd">как настроить проект с Gradle, JUnit5, Allure и Selenide</a>, и код <a href="https://github.com/rosolko/allure-gradle-configuration">на гитхабе</a></li> <li>Как ускорить тесты с помощью <a href="https://medium.com/@rosolko/boost-you-autotests-with-fast-authorization-b3eee52ecc19">мгновенной авторизации</a></li> <li>Как ещё ускорить авторизацию <a href="https://medium.com/@rosolko/fast-authorization-level-local-storage-6c84e9b3cef1">с помощью LocalStorage</a></li> </ul> </li> <li> <p>Найдена старенькая, но хорошая презентация <a href="https://www.slideshare.net/comaqa/page-object-with-selenide">Page object with selenide</a></p> </li> <li>Исторический момент: кто-то защитил магистерскую работу про Selenide:<br /> <a href="https://digi.lib.ttu.ee/i/?10612">“Development of Selenide Page Object class generator”</a></li> </ul> <p><br /> <a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/12/31/selenide-5.1.1/ http://ru.selenide.org/2018/12/31/selenide-5.1.1 2018-12-31T00:00:00+00:00 Вышла Selenide 5.0.1 <p>Привет! Прошёл месяц с выхода Selenide 5.0.0. Пока не слышно о каких-то громких фейлах. Никто по-большому не жалуется.</p> <p>Поэтому громких багфиксов не будет. Мы просто выпустили Selenide 5.0.1 с небольшими исправлениями старых болячек.</p> <h1 id="генерируем-случайное-имя-файла-если-нет-никакого">Генерируем случайное имя файла, если нет никакого</h1> <p>Как вы знаете, в Selenide есть удобный метод для скачивания файла по прямой ссылке:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">image</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">download</span><span class="o">(</span><span class="s">"https://utdirect.utexas.edu/apps/pts/parking/citations/nlogon/images/6584836/"</span><span class="o">);</span> </code></pre></div></div> <p>Он пытается получить имя файла из http заголовка или урла. Но если нет ни того, ни другого - метод падал. А теперь больше не падает, а просто генерирует случайное имя файла. Хотя я бы рассматривал это как багу в приложении.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/840">issue 840</a> и <a href="https://github.com/selenide/selenide/pull/856">PR 856</a></p> <p>Спасибо <a href="https://github.com/fyrewall77">David Phillips</a> за идею решения.</p> <p><br /></p> <h1 id="исправили-indexoutofbounds-когда-родитель-не-найден">Исправили <code class="language-plaintext highlighter-rouge">IndexOutOfBounds</code>, когда родитель не найден</h1> <p>Вот такой вызов раньше выдавал <code class="language-plaintext highlighter-rouge">IndexOutOfBounds</code>, а теперь возвращает <code class="language-plaintext highlighter-rouge">false</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">"not-existing-locator"</span><span class="o">).</span><span class="na">first</span><span class="o">().</span><span class="err">$$</span><span class="o">(</span><span class="s">"locator"</span><span class="o">).</span><span class="na">isEmpty</span><span class="o">();</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/747">issue 747</a></p> <p>Спасибо <a href="https://github.com/Denysss">Denys Shynkarenko</a> за <a href="https://github.com/selenide/selenide/pull/837">PR 837</a></p> <p><br /></p> <h1 id="исправили-classcastexception-в-методе-toarray">Исправили <code class="language-plaintext highlighter-rouge">ClassCastException</code> в методе <code class="language-plaintext highlighter-rouge">$$.toArray()</code></h1> <p>Вот такой код раньше выдавал <code class="language-plaintext highlighter-rouge">ClassCastException</code>:</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">SelenideElement</span><span class="o">&gt;</span> <span class="n">selenideElements</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span> <span class="n">selenideElements</span><span class="o">.</span><span class="na">addAll</span><span class="o">(</span><span class="err">$$</span><span class="o">(</span><span class="s">"#table1 td"</span><span class="o">));</span> </code></pre></div></div> <p>Я даже не хочу знать, зачем писать такой код в своих тестах. Народ, что вы там курите?</p> <p>Но починить надо было, и мы починили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/844">issue 844</a></p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/847">PR 847</a></p> <p><br /></p> <h1 id="добавили-поддержку-chrome-70">Добавили поддержку chrome 70</h1> <p>Начиная с версии 3.0, WebDriverManager стал хрупким. А именно, он содержит файл <code class="language-plaintext highlighter-rouge">versions.properties</code>, который хранит соответствия между версиями вебдрайверов и браузеров. Но сам WDM обновляется гораздо реже, чем браузеры и вебдрайверы. Естественно, каждый раз, когда выходит новая версия Firefox или Chrome, WDM перестаёт работать.</p> <p>Мы пока внесли свою версию <code class="language-plaintext highlighter-rouge">versions.properties</code> в Селенид. Мы-то обновляемся чаще, чем WDM. :)</p> <p>И уже добавили туда поддержку chrome70=2.42 и firefox62. Что делать с этим дальше - посмотрим. Возможно, придётся сделать свой WDM с блэклистом и шлюзами.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/855">PR 855</a></p> <p><br /></p> <h1 id="обновились-на-selenium-java-31415">Обновились на selenium-java 3.141.5</h1> <p><a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Список изменений</a> в selenium 3.141.*</p> <p><br /></p> <h2 id="статистика">Статистика</h2> <p>Количество скачиваний растёт. Мы пробили планку 40800 скачек в месяц.</p> <center> <img src="/images/2018/10/selenide-downloads.png" width="800" /> </center> <p><br /></p> <h2 id="конференции">Конференции</h2> <p>Если вы ещё не бывали, очень советую съездить на конференцию <a href="https://heisenbug-moscow.ru/2018/msk/people/">Гейзенбаг</a>. Это реально крутая конференция по тестированию и не только. Чего стоят только эти имена (из известных мне):</p> <ul> <li>Барух Садогурский - человек не из мира тестирования, но зато <a href="https://www.youtube.com/watch?v=KABC7Fty3x8">живая легенда из мира разработки</a>. Самые крутые Java-разработчики считают за честь сфоткаться с его картонным трафаретом, серьёзно!</li> <li>Артём Ерошенко - человек-Allure и <a href="https://www.youtube.com/watch?v=gwXpYB6ZayE">самый харизматичный спикер</a> на конференциях по тестированию.</li> <li>Алексей Баранцев - <a href="https://www.youtube.com/watch?v=tf_7Drar-NU">человек-селениум</a></li> <li>Кирилл Толкачёв - ещё одна легенда в мире DevOps. <a href="https://www.youtube.com/watch?v=FZ-feRdu6uk">Человек-Spring-Boot</a></li> <li>Антонина Хисаметдинова - человек-юзабилити. <a href="https://www.youtube.com/watch?v=lZ-4r_5UrUM&amp;t=990s">Смешно, интересно, полезно</a></li> </ul> <p><br /> <a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/11/07/selenide-5.0.1/ http://ru.selenide.org/2018/11/07/selenide-5.0.1 2018-11-07T00:00:00+00:00 Вышла Selenide 5.0.0 <p>Ура! Наконец-то это случилось. Мы выпустили мажорную версию Selenide 5.0.0</p> <p>Самое большое изменение в Selenide 5.0.0 - это большой рефакторинг. То есть внутренние изменения. Вас это почти не должно задеть. Кому интересно - детали рефакторинга будут в отдельном посте.</p> <p>Какие же изменения ждут вас в Selenide 5.0.0?</p> <h1 id="возможность-открывать-два-браузера-в-одном-тесте">Возможность открывать два браузера в одном тесте</h1> <p>Прежде всего: <em>не используйте эту возможность</em>!<br /> Открывать два браузера в одном тесте - это в большинстве случаев <strong>плохая практика</strong>.</p> <blockquote> <p>Обычно такое желание возникает для того, чтобы, скажем, одним браузером в админке поменять какую-то настройку и другим браузером проверить, что на страничке пользователя эта настройка отобразилась. Но правильное решение в таком случае - тестировать админку отдельно и пользовательскую страничку отдельно. А подготавливать окружение (в т.ч. менять настройки) можно как угодно (rest запрос, прямой запрос в базу и т.п.), но <strong>только не через UI</strong>. Вы ведь тестируете UI, а значит, априори <em>не должны ему доверять</em>.</p> </blockquote> <p>Если вы всё-таки решите идти грешным путём, это делается примерно так:</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>См. актуальный пример <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/SelenideDriverITest.java">в тестах Селенида</a></p> <p>См. <a href="https://github.com/selenide/selenide/issues/354">issue 354</a> и <a href="https://github.com/selenide/selenide/pull/801">PR 801</a></p> <p>Хочу выразить благодарность людям, которые сыграли важную роль в этом рефакторинге:</p> <ul> <li><a href="https://github.com/yashaka">Iakiv Kramarenko</a> за изначальную идею,</li> <li><a href="https://github.com/barancev">Alexei Barantsev</a> за конструктивные споры и убедительные аргументы,</li> <li><a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за моральную поддержку и генерацию идей</li> </ul> <p>NB! Старые добрые <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">new SelenideDriver()</code>, но правильно его хранят и уничтожают. Вам незачем городить свои велосипеды и решать те же задачи снова и снова.</p> <p>Ещё раз: два браузера в тесте - это наверняка <em>ПЛОХАЯ ПРАКТИКА</em>!</p> <p><br /></p> <h1 id="теперь-селенид-по-умолчанию-использует-chrome">Теперь Селенид по умолчанию использует Chrome</h1> <p>Когда-то давно мы выбрали Firefox по умолчанию, потому что это был единственный браузер, для запуска которого не требовалось установки отдельного вебдрайвера. Этот аргумент давно неактуален, т.к. 1) Firefox теперь тоже требует установки geckodriver, и 2) Селенид умеет устанавливать их автоматически.</p> <p>Зато Chrome, по нашему опыту, быстрее и надёжнее. Поэтому наступила эпоха Хрома.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/811">issue 811</a> – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/812">PR 812</a></p> <p><br /></p> <h1 id="селенид-больше-не-максимизирует-браузер-по-умолчанию">Селенид больше <em>не</em> максимизирует браузер по умолчанию</h1> <p>Когда-то казалось хорошей идеей запускать браузер на весь экран. Казалось, что это должно сделать тесты стабильнее: ведь больше элементов поместится на экран. Это поверье очень популярно и по сей день.</p> <p>На самом деле это делает тесты нестабильными: ведь их результат зависит от размера экрана, а это величина случайная и нам неподконтрольная. Наша новая рекомендация - выставлять окну браузера фиксированный размер. Минимальный, который ваше приложение должно поддерживать. Селенид по умолчанию устанавливает <code class="language-plaintext highlighter-rouge">1366x768</code> как самое популярное в мире на данный момент. Как обычно, вы можете его переопределить как любую другую настройку.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/810">issue 810</a> – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/812">PR 812</a></p> <p><br /></p> <h1 id="селенид-больше-не-открывает-браузер-автоматически">Селенид больше не открывает браузер автоматически</h1> <p>Действия и проверки (например, <code class="language-plaintext highlighter-rouge">$("#name").click()</code> и <code class="language-plaintext highlighter-rouge">$$("#list").shouldHave(size(1))</code>) теперь кидают ошибку, если браузер не открыт (или уже закрыт). Т.е. сначала нужно открыть браузер (вызвать <code class="language-plaintext highlighter-rouge">open(url)</code>), а лишь потом работать с элементами.</p> <p>Раньше подобные действия открывали браузер сами, если он ещё не был открыт. И это поведение было иногда неожиданным. Например, у людей иногда выскакивал Firefox там, где они ожидали Chrome. Или открывалось два браузера вместо одного. Понятно, что это ошибка в их тестах, но теперь Селенид поможет легче её найти.</p> <blockquote> <p>Уже известны случаи, когда тесты начали падать после перехода на Selenide 5.0.0 по двум причинам:</p> <ol> <li>Поля пэдж обжекта были объявлены статическими, а браузер закрывался после каждого теста. Статические поля инициализировались только один раз. Каждый следующий тест не переинициализировал их, а получал <code class="language-plaintext highlighter-rouge">SelenideElement</code> от предыдущего теста с устаревшей ссылкой на вебдрайвер (уже закрытый). Решение: сделать поля нестатическими. Статики - зло.</li> <li>Поля тест-класса (в случае TestNG) инициализировались не в <code class="language-plaintext highlighter-rouge">setUp</code> методе, а прямо в объявлении полей. За это я и люблю JUnit. Счастливые пользователи JUnit могут позволить себе такую роскошь: <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">MyTest</span> <span class="o">{</span> <span class="nc">SelenideElement</span> <span class="n">header</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div> </div> <p>А бедные пользователи TestNG вынуждены выносить инициализацию во всякие <code class="language-plaintext highlighter-rouge">@Before</code> методы:</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">MyTest</span> <span class="o">{</span> <span class="nc">SelenideElement</span> <span class="n">header</span><span class="o">;</span> <span class="nd">@BeforeEach</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="n">header</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div> </div> <p>Ужасно, правда? Мой вам совет: забудьте TestNG как страшный сон. JUnit рулит.</p> </li> </ol> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/issues/809">issue 809</a></p> <p><br /></p> <h1 id="подчистили-старые-настройки-и-фичи">Подчистили старые настройки и фичи</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/806">issue 806</a> – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/812">PR 812</a></p> <p>В том числе убрали поддержку следующих настроек:</p> <ul> <li><code class="language-plaintext highlighter-rouge">browser</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.browser</code>)</li> <li><code class="language-plaintext highlighter-rouge">remote</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.remote</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.browser-size</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.browserSize</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.browser.version</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.browserVersion</code>)</li> <li><code class="language-plaintext highlighter-rouge">browser.version</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.browserVersion</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.start-maximized</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.startMaximized</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.chrome.switches</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.chromeSwitches</code>)</li> <li><code class="language-plaintext highlighter-rouge">chrome.switches</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.chromeSwitches</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.page-load-strategy</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.pageLoadStrategy</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.click-via-js</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.clickViaJs</code>)</li> <li><code class="language-plaintext highlighter-rouge">selenide.reports</code> (используйте <code class="language-plaintext highlighter-rouge">selenide.reportsFolder</code>)</li> </ul> <p><br /></p> <h1 id="убрали-зависимость-от-junit5-api">Убрали зависимость от <code class="language-plaintext highlighter-rouge">junit5-api</code></h1> <p>Раньше Селенид транзитивно тянул за собой <code class="language-plaintext highlighter-rouge">junit5-api</code>, даже если вы не используете JUnit5. Очевидно, вам это не надо. Теперь больше не тянет.</p> <p>Если вы всё же используете JUnit5 и у вас из classpath пропали нужные классы, придётся в своём проекте явно объявить зависимость <code class="language-plaintext highlighter-rouge">"org.junit.jupiter:junit-jupiter-api:5.3.1"</code>.</p> <p><br /></p> <h1 id="ну-и-по-мелочам">Ну и по мелочам:</h1> <ul> <li>Классы <code class="language-plaintext highlighter-rouge">AssertionMode</code>, <code class="language-plaintext highlighter-rouge">SelectorMode</code>, <code class="language-plaintext highlighter-rouge">FileDownloadMode</code> перенесены из класса <code class="language-plaintext highlighter-rouge">Configuration</code> в пакет <code class="language-plaintext highlighter-rouge">com.codeborne.selenide</code></li> <li>Теперь Селенид кидает <code class="language-plaintext highlighter-rouge">ElementIsNotClickableException</code> вместо <code class="language-plaintext highlighter-rouge">ElementNotFoundException</code> (если элемент закрыт другим элементом и поэтому не удалось его кликнуть)</li> <li>Селенид кидает ошибку, если <code class="language-plaintext highlighter-rouge">Configuration.fileDownload == PROXY</code>, но <code class="language-plaintext highlighter-rouge">Configuration.proxyEnabled == false</code>. Вам придётся проставить <code class="language-plaintext highlighter-rouge">Configuration.proxyEnabled=true</code>, если хотите скачивать файлы через прокси.</li> <li><a href="https://github.com/selenide/selenide/issues/817">817</a> fix “FirefoxDriverFactory overwrites Firefox profile provided by Configuration” – спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/821">PR 821</a></li> <li>bugfix: method Selenide.download() should not fail if there is no opened browser yet</li> <li><a href="https://github.com/selenide/selenide/pull/825">825</a> Обновили WebDriverManager до версии 3.0.0 (снова)</li> <li><a href="https://github.com/selenide/selenide/pull/825">825</a> Придумали костыль для WebDriverManager, чтобы он не обращался слишком часто к github (получая при этом ошибку 403)</li> <li><a href="https://github.com/selenide/selenide/pull/832">832</a> Added support for screenshots outside of “user.dir” in CI server – спасибо <a href="https://github.com/admizh">Alex Yu</a></li> </ul> <p><br /></p> <h1 id="и-технические-изменения-которые-вас-скорее-всего-не-затронут">И технические изменения (которые вас, скорее всего, не затронут)</h1> <ul> <li>Обновились до htmlunitdriver 2.33.0</li> <li>Перенесли константы <code class="language-plaintext highlighter-rouge">IE</code>, <code class="language-plaintext highlighter-rouge">FIREFOX</code> и т.д. из класса <code class="language-plaintext highlighter-rouge">WebDriverRunner</code> в его родительский класс <code class="language-plaintext highlighter-rouge">Browsers</code></li> <li>Перенесли класс <code class="language-plaintext highlighter-rouge">Selenide</code>, <code class="language-plaintext highlighter-rouge">WebDriverRunner</code>, <code class="language-plaintext highlighter-rouge">Configuration</code> в подпапку <a href="https://github.com/selenide/selenide/tree/master/statics">statics</a>.</li> <li>Перенесли логику инициализации дефалтовых настроек из <a href="https://github.com/selenide/selenide/blob/master/statics/src/main/java/com/codeborne/selenide/Configuration.java">Configuration</a> в <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/SelenideConfig.java">SelenideConfig</a>.</li> <li>when waiting for a condition, catch explicitly only needed exceptions instead of <code class="language-plaintext highlighter-rouge">Throwable</code> which is too generic. It does not make sense to wait for 4 seconds in case of IllegalStateException, FileNotFoundException etc.</li> </ul> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Селенидовские проекты переехали в специальную “организацию” на гитхабе: <a href="https://github.com/selenide">github.com/selenide</a></li> <li>Напоминаю, что примеры использования селенида с разными фреймворками лежат в организации <a href="https://github.com/selenide-examples">github.com/selenide-examples</a></li> <li>Появился <a href="https://github.com/selenide/selenide-gradle-template">шаблон проекта на Selenide для Грэдла</a></li> <li>Наконец-то подоспело видео моего доклада <a href="https://www.youtube.com/watch?v=o6AEfW39f0Y">Selenide: fall in love after 15 slides</a> с конференции GeeCON 2018 в Кракове</li> <li>Неинтересная статейка <a href="https://www.bugraptors.com/selenide-vs-selenium/">Selenide vs. Selenium: The War Of Technologies!!!</a></li> <li>16 октября, Краков, Agile &amp; Automation Days - <a href="http://aadays.pl/speakers/alexei-vinogradov/">мастер-класс по Селениду</a>, Алексей Виноградов</li> <li>7 ноября, Белград, Test Conference - <a href="https://bg-testconference.rs/agenda#/sessions/rapid-ui-test-automation-with-selenide">Rapid UI-Test automation with Selenide</a>, Алексей Виноградов</li> <li>18 октября, Москва, встреча анонимных тестировщиков - <a href="https://www.meetup.com/%D0%9E%D0%B1%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%BE-%D0%B0%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D1%8B%D1%85-%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D1%89%D0%B8%D0%BA%D0%BE%D0%B2/events/255547063/">Конфиги в тестах, Gradle, Java, Selenide</a>, Борис Осянин</li> <li>Завтра в Америке состоится митап <a href="https://www.meetup.com/NOVA-Software-Quality-Engineering-and-Automation-Meetup/events/254691657/">BDD with Selenide</a> - 16 октября 2018, Рестон, Вирджиния <blockquote> <p>Selenide is an open-source library that can make a <strong>huge impact</strong> on and accelerate software delivery by introducing a concise API, shorter expressions, and many other capabilities.</p> </blockquote> </li> </ul> <h2 id="нам-7-лет">Нам 7 лет!</h2> <p>Время летит незаметно. Не успели оглянуться, а 25 октября селениду исполняется целых 7 лет. Сейчас, конечно, смешно смотреть, какими были первые коммиты.</p> <center> <img src="/images/2018/10/selenide-first-commits.png" width="800" /> </center> <p><br /></p> <h1 id="а-как-у-вас-прошло-обновление">А как у вас прошло обновление?</h1> <p>Делитесь своим опытом, смело заводите <a href="https://github.com/selenide/selenide/issues">тикеты на гитхабе</a>, обсуждайте проблемы в <a href="https://softwaretesters.slack.com/messages/selenide_ru">слаке</a> или <a href="https://gitter.im/codeborne/selenide-ru">гиттере</a>.</p> <p><br /> <a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/10/10/selenide-5.0.0/ http://ru.selenide.org/2018/10/10/selenide-5.0.0 2018-10-10T00:00:00+00:00 Вышла Selenide 4.14.0 <p>29 августа 1997 года компьютерная система Скайнет вышла из-под контроля и нанесла ядерный удар по России. Так началась война между терминаторами и человечеством.</p> <p>С тех прошёл - страшно подумать - 21 год! И раз мы до сих пор живы, мы решили в этот исторический день зарелизить Selenide 4.14.0</p> <p>Изменений немного, и они касаются в основном селенидовского прокси-сервера.</p> <p><br /></p> <h1 id="реализовали-basicauth-через-прокси">Реализовали BasicAuth через прокси</h1> <p>Часто тестовые сервера закрыты паролем (т.н. BasicAuth). (Зачем это нужно - остаётся для меня загадкой, ведь они и так в интранете! Но что поделаешь, есть как есть.)</p> <p>До сих пор Selenide позволял вам авторизоваться, только добавляя <code class="language-plaintext highlighter-rouge">username:password@</code> в URL. Но говорят, это работает не во всех браузерах. Теперь же мы реализовали BasicAuth через прокси-сервер, а не URL. Грубо говоря, селенид сам добавляет в каждый запрос от браузера к приложению заголовок <code class="language-plaintext highlighter-rouge">Authorization: Basic foobar</code> Этот способ точно работает во всех браузерах. Смело включайте прокси (см. следующий пункт).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/784">issue 784</a> – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/785">PR 785</a></p> <p><br /></p> <h1 id="добавили-настройку-для-включениявыключения-прокси-сервера">Добавили настройку для включения/выключения прокси-сервера</h1> <p>Как вы знаете, Селенид умеет запускать свой встроенный прокси-сервер, который отслеживает запросы между браузером и тестируемым приложением. Изначально он использовался только для одной функции - скачивания файлов. Поэтому у прокси-сервера не было какой-то отдельной настройки - он включался настройкой <code class="language-plaintext highlighter-rouge">Configuration.fileDownload == PROXY</code>. Теперь же использование прокси-сервера расширяется (см. предыдущий пункт), и будет расширяться ещё.</p> <p>Поэтому мы добавили отдельную настройку для прокси-сервера:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Configuration.proxyEnabled = true | false</code> (по умолчанию false)</li> </ul> <p>Поясню, почему по умолчанию <code class="language-plaintext highlighter-rouge">false</code>. Лично я советую всем включать прокси-сервер. Он хороший. Он позволяет делать вещи, которые с голым селениумом просто невозможны.</p> <p>Он не сработает только в одном случае: если тесты и браузер запускаются на разных машинах, и машина тестов не видна с машины браузера. Я надеюсь, что у большинства из вас нет такой ерунды. Я вообще не понимаю, зачем разграничивать доступы между <em>тестовыми серверами</em> в <em>интранете</em>, блин. Но говорят, в суровых ынтерпрайзах это бывает. Вот из-за них-то мы пока и не включили <code class="language-plaintext highlighter-rouge">proxyEnabled</code> по умолчанию.</p> <p>Для тонкой настройки прокси-сервера (большинству из вас это не нужно):</p> <ul> <li><code class="language-plaintext highlighter-rouge">Configuration.proxyHost</code> <br />По умолчанию <code class="language-plaintext highlighter-rouge">""</code> (селенид автоматически подставит IP или имя хоста текущей машины)</li> <li><code class="language-plaintext highlighter-rouge">Configuration.proxyPort</code> <br />По умолчанию <code class="language-plaintext highlighter-rouge">0</code> (селенид автоматически выберет случайный свободный порт на текущей машине)</li> </ul> <p>См. <a href="https://github.com/selenide/selenide/issues/788">issue 788</a> и <a href="https://github.com/selenide/selenide/pull/791">PR 791</a></p> <p><br /></p> <h1 id="удалили-старый-хак-для-ie">Удалили старый хак для IE.</h1> <p>Внимательные читатели могли заметить, что когда Селенид запускает тесты в IE, он автоматически добавляет к каждому URL параметр <code class="language-plaintext highlighter-rouge">?timestamp=какиетоциферки</code>. В своё время мы сделали это для того, чтобы заставить IE перегружать страницы. Нам казалось, что в IE есть баг, из-за которого он иногда при клике не перегружает страницу, а достаёт старую страницу из кэша.</p> <p>Прошли годы, и теперь я думаю, что клик тогда не работал по другой причине, и этот хак не нужен. Вот мы его и удалили. Ну вы это, жалуйтесь, если у вас это всё-таки аукнется.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/789">issue 789</a> и <a href="https://github.com/selenide/selenide/pull/790">PR 790</a></p> <h2 id="новости">Новости</h2> <p>Обновилась статистику скачиваний Selenide за июль. А она скакнула! Мы перевалили 37000 скачек в месяц.</p> <center> <img src="/images/2018/08/selenide.downloads.png" width="800" /> </center> <h2 id="а-вы-что-думаете">А вы что думаете?</h2> <p>Делитесь своими идеями, как ещё можно использовать прокси-сервер. Обсудим!</p> <p><br /> <a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/08/29/selenide-4.14.0/ http://ru.selenide.org/2018/08/29/selenide-4.14.0 2018-08-29T00:00:00+00:00 Вышла Selenide 4.13.0 <p>Всем привет!</p> <p>20 августа в Эстонии отмечается уникальный праздник - День Восстановления Независимости. 20.08.1991 Эстония заново обрела независимость. Прямо сейчас у меня за окном многотысячный хор поёт патриотические песни и гремит салют.</p> <p>А я под звуки салюта выкатываю … Selenide 4.13.0</p> <p>Изменений оказалось больше, чем мне казалось. :)</p> <p><br /></p> <h1 id="добавили-метод-lastchild">Добавили метод <code class="language-plaintext highlighter-rouge">$.lastChild()</code></h1> <p>Находит последний дочерний элемент данного элемента. Типа <code class="language-plaintext highlighter-rouge">$("table").lastChild().shouldHave(cssClass("lastRow"));</code></p> <p>См. <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/LastChildTest.java">примеры в тестах селенида</a></p> <p>Спасибо <a href="https://github.com/symonk">SymonK</a> за <a href="https://github.com/selenide/selenide/pull/771">PR 771</a></p> <p><br /></p> <h1 id="добавили-проверки-для-коллекций-с-кастомным-таймаутом">Добавили проверки для коллекций с кастомным таймаутом</h1> <p>Как вы знаете, в Селениде издревле был метод <code class="language-plaintext highlighter-rouge">$.waitUntil(условие, таймаут)</code> для ожидания события с нестандартным таймаутом. Теперь мы добавили аналогичный метод и для коллекций. Правда, мы решили не называть его <code class="language-plaintext highlighter-rouge">waitUntil</code>, потому что это название неудачное в плане английской грамматики - <code class="language-plaintext highlighter-rouge">$$.waitUntil(texts("a", "b", "c"))</code> просто не звучит. В итоге метод называется так же, как и все предыдущие методы:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$$.shouldBe(empty, 9000)</code></li> <li><code class="language-plaintext highlighter-rouge">$$.shouldHave(size(4), 9000)</code></li> </ul> <p>Больше примеров <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/CollectionWaitTest.java">в тестах селенида</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/601">issue 601</a> и <a href="https://github.com/selenide/selenide/pull/781">PR 781</a></p> <p><br /></p> <h1 id="добавили-метод-selenidedownloadurl">Добавили метод <code class="language-plaintext highlighter-rouge">Selenide.download(url)</code></h1> <p>… для скачивания файла по прямой ссылке. Но скачивания, конечно, не просто так, а как будто бы файл открывается в текущем браузере. Технически говоря, Selenide скачивает файл GET-запросом по вашему URL, но добавляет к нему cookies и заголовок <code class="language-plaintext highlighter-rouge">User-Agent</code> от запущенного вебдрайвера.</p> <p>Больше примеров <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/DirectFileDownloadTest.java">в тестах селенида</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/782">PR 782</a></p> <p><br /></p> <h1 id="обновились-до-selenium-3140">Обновились до Selenium 3.14.0</h1> <p>На всякий случай упомяну, что <code class="language-plaintext highlighter-rouge">SelenideElement</code> больше не реализовывает следующие устаревшие интерфейсы:</p> <ul> <li><code class="language-plaintext highlighter-rouge">FindsByLinkText</code></li> <li><code class="language-plaintext highlighter-rouge">FindsById</code></li> <li><code class="language-plaintext highlighter-rouge">FindsByName</code></li> <li><code class="language-plaintext highlighter-rouge">FindsByTagName</code></li> <li><code class="language-plaintext highlighter-rouge">FindsByClassName</code></li> <li><code class="language-plaintext highlighter-rouge">FindsByCssSelector</code></li> <li><code class="language-plaintext highlighter-rouge">FindsByXPath</code></li> <li><code class="language-plaintext highlighter-rouge">HasIdentity</code></li> </ul> <p>По идее это не должно ни на что повлиять, просто на всякий случай сказал.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/773">PR 773</a></p> <p><br /></p> <h1 id="метод-switchtoalert-теперь-кидает-noalertpresentexception">Метод <code class="language-plaintext highlighter-rouge">switchTo().alert()</code> теперь кидает <code class="language-plaintext highlighter-rouge">NoAlertPresentException</code></h1> <p>… вместо <code class="language-plaintext highlighter-rouge">TimeoutException</code></p> <p>См. <a href="https://github.com/selenide/selenide/issues/273">issue 273</a> – спасибо <a href="https://github.com/tsukakei">Keita Tsukamoto</a> за <a href="https://github.com/selenide/selenide/pull/774">PR 774</a></p> <p><br /></p> <h1 id="исправили-сообщение-об-ошибке-из-метода-selectoptionbyvalue">Исправили сообщение об ошибке из метода <code class="language-plaintext highlighter-rouge">$.selectOptionByValue()</code></h1> <p>См. <a href="https://github.com/selenide/selenide/issues/709">issue 709</a> – спасибо <a href="https://github.com/tsukakei">Keita Tsukamoto</a> за <a href="https://github.com/selenide/selenide/pull/780">PR 780</a></p> <p><br /></p> <h1 id="исправили-имя-скачиваемого-файла">Исправили имя скачиваемого файла</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/734">issue 734</a> – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/768">PR 768</a></p> <p><br /></p> <h1 id="обновились-до-webdrivermanager-225">Обновились до WebdriverManager 2.2.5</h1> <p>См. <a href="https://github.com/selenide/selenide/pull/783">PR 783</a> и <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/changelog">changelog</a></p> <p><br /></p> <h1 id="обновились-до-htmlunit-2321">Обновились до HtmlUnit 2.32.1</h1> <p>См. <a href="https://github.com/selenide/selenide/pull/775">PR 775</a></p> <p><br /></p> <h1 id="исправили-все-селенидовские-тесты-для-firefox">Исправили все селенидовские тесты для Firefox</h1> <p>Все вы помните тот исторический момент, когда вебдрайвер для Firefox уже перестал работать, а geckodriver (или marionette) ещё не начал. В то время нам пришлось “временно” выключить запуск селенидовских тестов под Firefox. И вот недавно мы обнаружили, что всё это время они не запускались. Год? Два? Три? Уже никто не помнит.</p> <p>И вот мы наконец их включили обратно и починили. Чинить пришлось прилично. Не исключено, что заодно мы и какие-то баги починили. :)</p> <p>См. <a href="https://github.com/selenide/selenide/pull/778">PR 778</a></p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li> <p>Замечательный обрывок фразы из чата:</p> <blockquote> <p>Юзаю джаву не только за то что она джава, а за наличие кучи готовых плюшек.<br />Таких как selenide, restassured итп.</p> </blockquote> </li> <li>Внезапно появилось сразу два фреймворка для автотестов, построенных на базе Селенида: <ul> <li><a href="https://github.com/sysgears/selenium-automation-bundle">Selenium Automation Bundle</a></li> <li><a href="https://github.com/Xceptance/neodymium-library">Neodymium</a></li> </ul> </li> <li>Мы добавили возможность генерировать базовый проект на Selenide + Maven + JUnit5 одной строкой!</li> </ul> <p>Например так:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn archetype:generate -B -DarchetypeGroupId=org.selenide -DarchetypeArtifactId=selenide-junit5-archetype -DgroupId=com.example -DartifactId=ui-tests -Dpackage=com.example.project.ui </code></pre></div></div> <p>См. <a href="http://qa-blog.alexei-vinogradov.de/2018/08/quick-start-selenide-maven-junit5/">Используем Maven Archetype для генерации Selenide проекта</a></p> <p><br /> Следите за обновлениями!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/08/20/selenide-4.13.0/ http://ru.selenide.org/2018/08/20/selenide-4.13.0 2018-08-20T00:00:00+00:00 Вышла Selenide 4.12.3 <p>Всем привет! Наконец-то чемпионат закончился, и мы можем вернуться к своим любимым опен-сорсным проектам.</p> <p>Сегодня мы выкатили Selenide 4.12.3</p> <p>Пробежимся по изменениям?</p> <p><br /></p> <h1 id="добавили-поддержку-junit-5">Добавили поддержку JUnit 5</h1> <p>Как вы знаете, в Selenide есть специальная поддержка для двух самых распространённых тестовых фреймворков JUnit4 и TestNG. Это специальные “правила” или “Listeners”, которые позволяют периодически переоткрывать браузер, делать скриншоты и т.д.</p> <p>Теперь мы добавили аналогичную поддержку и для JUnit 5:</p> <ul> <li><code class="language-plaintext highlighter-rouge">BrowserStrategyExtension</code></li> <li><code class="language-plaintext highlighter-rouge">ScreenShooterExtension</code></li> <li><code class="language-plaintext highlighter-rouge">SoftAssertsExtension</code></li> <li><code class="language-plaintext highlighter-rouge">TextReportExtension</code></li> </ul> <p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/757">PR 757</a></p> <p>Кстати, мы заодно переписали <a href="https://github.com/selenide/selenide/tree/master/src/test/java">собственные тесты Селенида</a> на JUnit5 и AssertJ. Так что можете оттуда подсматривать примеры на JUnit5, если понадобится.</p> <p><br /></p> <h1 id="таймаут-для-скачивания-файлов">Таймаут для скачивания файлов</h1> <p>Как вы знаете, в Selenide есть метод <code class="language-plaintext highlighter-rouge">$.download()</code> для скачивания файла. Он использует стандартный <code class="language-plaintext highlighter-rouge">Configuration.timeout</code> для ограничения скачивания по времени.</p> <p>Но иногда скачивание некоторых файлов может быть очень долгим (например, генерация какого-то сложного отчёта). Для этих случаев мы добавили новый метод для скачивания с заданным таймаутом:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">hugeReport</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#huge-report"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="mi">100500</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/758">issue 758</a> - спасибо <a href="https://github.com/YuriIvanov">Yuri Ivanov</a> за <a href="https://github.com/selenide/selenide/pull/761">PR 761</a></p> <p><br /></p> <h1 id="обновление-коллекций-при-каждом-обращении">Обновление коллекций при каждом обращении</h1> <p>Это изменение вызвало больше всего споров. Коллекции - это метод <code class="language-plaintext highlighter-rouge">$$</code> - он возвращает список найденных элементов. Иногда список большой. Иногда это может ощутимо замедлить тест.</p> <h3 id="было-раньше">Было раньше</h3> <p>До версии 4.10.1 Selenide всегда загружал весь список заново при вызове любого метода коллекции. Поэтому простое итерирование <strong>большой</strong> коллекции (несколько сотен элементов) могло быть медленным:</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">"span"</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">item</span> <span class="o">-&gt;</span> <span class="n">item</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="n">visible</span><span class="o">));</span> </code></pre></div></div> <h3 id="стало-потом">Стало потом</h3> <p>В версии Selenide 4.10.1 мы стали кэшировать коллекции. Итерирование стало быстрее, но начались жалобы от других товарищей, которые кладут коллекцию один раз в переменную и как раз ожидают, что она будет всё время обновляться:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">movieTitles</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">".film"</span><span class="o">);</span> <span class="n">assertEquals</span><span class="o">(</span><span class="s">"Black Panther"</span><span class="o">,</span> <span class="n">movieTitles</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">getText</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">"sort"</span><span class="o">)).</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"Release Date"</span><span class="o">);</span> <span class="n">assertEquals</span><span class="o">(</span><span class="s">"The Irishman"</span><span class="o">,</span> <span class="n">movieTitles</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">getText</span><span class="o">());</span> </code></pre></div></div> <p>На мой взгляд, такой код скорее неестественный: если уж ты ожидаешь, что страница перегрузится и содержимое коллекции изменится, то естественно было бы вызвать метод <code class="language-plaintext highlighter-rouge">$$</code> заново. Поэтому я некоторое время не соглашался отменять кэширование ради “неправильных” тестов. Но в итоге победил аргумент за “единообразность”: ведь метод <code class="language-plaintext highlighter-rouge">$</code> перегружает элемент при каждом обращении (хоть это и полу-случайно получилось), значит, и <code class="language-plaintext highlighter-rouge">$$</code> тоже должен. Чтобы в Селениде всё вело себя одинаково.</p> <h3 id="что-теперь">Что теперь</h3> <p>В общем, мы выбрали некий компромиссный вариант: <code class="language-plaintext highlighter-rouge">$$</code> загружает коллекцию заново при вызове любых методов, <strong>кроме <code class="language-plaintext highlighter-rouge">$$.iterator()</code> и <code class="language-plaintext highlighter-rouge">$$.listIterator()</code></strong>. Таким образом, и итерирование будет быстрым, и элементы будут перегружаться. Правильное ли это было решение - посмотрим. Ведь и такое решение может вызвать неожиданные эффекты и бурные батхёрты.</p> <h3 id="snapshot">$$.snapshot()</h3> <p>Кстати, на случай если вам захочется настоящего кэширования: в коллекциях есть теперь метод <code class="language-plaintext highlighter-rouge">$$.snapshot()</code>, который возвращает “снимок” коллекции, т.е. объект, отражающий текущее состояние и не перегружающий элементы коллекции. Любые итерации и проверки на нём будут быстрее. Можно использовать для оптимизации тестов, когда вы точно знаете, что содержимое коллекции на этой странице не меняется динамически.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/696">issue 696</a></p> <h2 id="новости">Новости</h2> <ul> <li>Нашли <a href="https://www.slideshare.net/Provectus/selenide-review-and-how-to-start-using-it-in-legacy-selenium-tests">презентацию про Selenide</a> некоего Alexander Bondarev из Determine на каких-то Nerd’s Day at Provectus. Там даже и <a href="https://www.youtube.com/watch?v=ekVSclpEdx0">видео</a> есть.</li> <li>Это случилось! Selenide попал в чей-то <a href="https://image-store.slidesharecdn.com/3f9b2191-f339-4533-8ec4-dd7c6bc771b4-original.png">Technology Radar</a> с пометкой “Selenide as new standard”. См. <a href="https://www.linkedin.com/feed/update/urn:li:activity:6424506901152829440/">тут</a></li> <li>Опубликовали <a href="https://www.youtube.com/playlist?list=PLYinOsby80NlbBlgoCncOLQQl22KtCEDK">видео с митапа DelEx</a> - осторожнее, много годноты!</li> <li>Приезжайте на <a href="http://aadays.pl/speakers/alexei-vinogradov/">Agile Automation Days</a> 15-16 октября - там будет семинар “Jump into the KISS UI-Test automation with Selenide”</li> </ul> <p>Следите за обновлениями!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/07/17/selenide-4.12.3/ http://ru.selenide.org/2018/07/17/selenide-4.12.3 2018-07-17T00:00:00+00:00 Вышла Selenide 4.12.2 <p>Мы выкатили Selenide 4.12.2</p> <p>Пробежимся по изменениям?</p> <p><br /></p> <h1 id="добавили-метод-because-к-коллекциям">Добавили метод <code class="language-plaintext highlighter-rouge">because</code> к коллекциям</h1> <p>Как вы знаете, в Selenide есть метод <code class="language-plaintext highlighter-rouge">because</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">"#login"</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">because</span><span class="o">(</span><span class="s">"After 3 wrong answers user should be logged out"</span><span class="o">));</span> </code></pre></div></div> <p>Теперь метод <code class="language-plaintext highlighter-rouge">because</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">".error"</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="na">because</span><span class="o">(</span><span class="s">"A separate error message per wrong answer"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/440">issue 440</a> - спасибо <a href="https://github.com/sidelnikovmike">Mikhail Sidelnikov</a> за <a href="https://github.com/selenide/selenide/pull/749">PR 749</a></p> <p><br /></p> <h1 id="селенид-больше-не-будет-пытаться-открыть-браузер">Селенид больше не будет пытаться открыть браузер</h1> <p>… при выключенной настройке <code class="language-plaintext highlighter-rouge">Configuration.reopenBrowserOnFail</code>.</p> <p>Не переживайте, для большинства из вас ничего не поменяется - эта настройка по умолчанию <code class="language-plaintext highlighter-rouge">true</code>.</p> <p>Изначально эта настройка была сделана для тех, кто полностью хочет взять на себя заботу о вебдрайвере. Теперь, если настройка выставлена в <code class="language-plaintext highlighter-rouge">false</code>, Селенид не будет ни переоткрывать, ни открывать браузер.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/695">issue 695</a> и <a href="https://github.com/selenide/selenide/pull/754">PR 754</a></p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <ul> <li>selenium 3.13.0</li> <li>webdrivermanager 2.2.3</li> </ul> <p><br /></p> <h2 id="события">События</h2> <p>Напоминаю, что в Минске 7 июля состоится крутой митап “<a href="https://www.eventsme.by/e/delex-chatter-3258476929">DelEx Chatter</a>”. Его устраивают наши друзья, там будут крутые спикеры в два потока. Будет круто.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/06/27/selenide-4.12.2/ http://ru.selenide.org/2018/06/27/selenide-4.12.2 2018-06-27T00:00:00+00:00 Вышла Selenide 4.12.1 <p>С большим опозданием выкладываем описание релиза Selenide 4.12.1</p> <p>Лето, футбол - сами понимаете. Не оторваться.</p> <p>Итак,</p> <p><br /></p> <h1 id="исправили-имя-скачиваемого-файла">Исправили имя скачиваемого файла</h1> <p>В случае, если файл скачивается по ссылке, содержащей <code class="language-plaintext highlighter-rouge">?</code> и параметры (типа <code class="language-plaintext highlighter-rouge">/download/me/selenide-4.11.5.md?sessioncookie=12345</code>), то Selenide пытался создать локальный файл с именем <code class="language-plaintext highlighter-rouge">selenide-4.11.5.md?sessioncookie=12345</code>. Теперь Selenide будет обрезать всё, что после вопросительного знака. В данном случае он создаст файл <code class="language-plaintext highlighter-rouge">selenide-4.11.5.md</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/735">issue 735</a> - спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/735">PR 735</a></p> <p><br /></p> <h1 id="поменяли-реализацию-метода-seleniderefresh">Поменяли реализацию метода <code class="language-plaintext highlighter-rouge">Selenide.refresh()</code></h1> <p>Люди жаловались, что иногда этот метод не обновлял страницу. Мы не знали, как это повторить, но на всякий случай поменяли реализацию на вызов стандартного селениумовского метода <code class="language-plaintext highlighter-rouge">webdriver.navigate().refresh()</code> - и вроде как заработало.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/740">issue 740</a> - спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/741">PR 741</a></p> <p><br /></p> <h1 id="исправили-метод-webdriverrunnerisheadless">Исправили метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.isHeadless()</code></h1> <p>Раньше этот метод возвращал <code class="language-plaintext highlighter-rouge">true</code> только для HtmlUnit и PhantomJS. Теперь же он возвращает <code class="language-plaintext highlighter-rouge">true</code> также и для Chrome/Firefox, запущенных в режиме <code class="language-plaintext highlighter-rouge">headless</code>.</p> <p>Хотя честное слово, я не понимаю, зачем вам вообще нужно использовать этот метод. Не используйте. Неужто вы без селенида не знаете, какой браузер вы запустили?</p> <p>См. <a href="https://github.com/selenide/selenide/issues/750">issue 750</a> - спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/751">PR 751</a></p> <p><br /></p> <h1 id="добавили-id-потока-к-логам-при-закрытии-вебдрайвера">Добавили ID потока к логам при закрытии вебдрайвера</h1> <p>А то раньше не всегда понятно было, какой именно поток закрывает вебдрайвер. Хотя честное слово, я не понимаю, в какой вселенной живут люди, для которых это может быть актуально… :)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/582">issue 582</a> - спасибо <a href="https://github.com/AlexanderPoleschuk">Alexander Poleschuk</a> за <a href="https://github.com/selenide/selenide/pull/737">PR 737</a></p> <p><br /></p> <h1 id="объявили-зависимость-webdrivermanager-как-api">Объявили зависимость <code class="language-plaintext highlighter-rouge">webdrivermanager</code> как <code class="language-plaintext highlighter-rouge">api</code></h1> <p>Т.е. webdrivermanager будет подтягиваться автоматически вместе с селенидом, и вы сможете использовать его в своём коде без объявления дополнительных зависимостей.</p> <p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/736">PR 736</a></p> <p><br /></p> <h1 id="перешли-с-coveralls-на-sonarcloud">Перешли с <code class="language-plaintext highlighter-rouge">coveralls</code> на <code class="language-plaintext highlighter-rouge">sonarcloud</code></h1> <p>для подсчёта покрытия тестами самого селенида. А то с coveralls были определённые проблемы. Вас это напрямую не касается, просто для справки.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/702">issue 702</a> - спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/744">PR 744</a></p> <p><br /></p> <h2 id="новости">Новости</h2> <p>Если будете в Минске 7 июля, приходите на митап “<a href="https://www.eventsme.by/e/delex-chatter-3258476929">DelEx Chatter</a>”. Его устраивают наши друзья, там будут крутые спикеры в два потока. Будет круто.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/06/02/selenide-4.12.1/ http://ru.selenide.org/2018/06/02/selenide-4.12.1 2018-06-02T00:00:00+00:00 Вышла Selenide 4.11.4 <p>Реактивно вышел мини-релиз Selenide 4.11.4</p> <p><br /></p> <h1 id="убрали-спам-в-логах-от-firefox-драйвера">Убрали спам в логах от Firefox драйвера</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/673">issue 673</a> - спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/732">PR 732</a></p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <ul> <li>selenium 3.12.0</li> <li>gson:2.8.4</li> <li>guava:25.0</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/05/09/selenide-4.11.4/ http://ru.selenide.org/2018/05/09/selenide-4.11.4 2018-05-09T00:00:00+00:00 Вышла Selenide 4.11.3 <p>8 мая - День работников Федеральной службы по военно-техническому сотрудничеству России. Специально к этому дню мы приурочили релиз Selenide 4.11.3.</p> <p>И вот какие новинки вас ждут:</p> <p><br /></p> <h1 id="открыли-доступ-к-прокси-серверу">Открыли доступ к прокси-серверу</h1> <p>Как вы знаете, Selenide запускает встроенный прокси-сервер (BrowserMobProxy). Но до сих пор у вас не было к нему доступа, Selenide использовал его только для скачивания файлов.</p> <p>Теперь мы сделали публичный метод, с помощью которого вы можете получить инстанс <code class="language-plaintext highlighter-rouge">BrowserMobProxy</code> и использовать как угодно:</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> </code></pre></div></div> <p>Кстати, расскажите в комментариях, как вы его используете? Возможно, эти фичи надо встроить в Selenide.</p> <p>P.S. Selenide пока ещё запускает прокси-сервер только при включенной настройке <code class="language-plaintext highlighter-rouge">Configuration.fileDownload=PROXY</code>. Наверное, в следующей версии нам стоит развязать эти две настройки.</p> <p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/731">PR 731</a></p> <p><br /></p> <h1 id="добавили-проверку-cssvalue">Добавили проверку <code class="language-plaintext highlighter-rouge">cssValue</code></h1> <p>Теперь можно в тестах проверять CSS свойства элементов:</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">shouldHave</span><span class="o">(</span><span class="n">cssValue</span><span class="o">(</span><span class="s">"font-size"</span><span class="o">,</span> <span class="s">"12"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/628">issue 628</a> - – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/727">PR 727</a></p> <p>Внимание! Я настаиваю, что делать это надо очень осторожно. Подобные проверки могут сделать ваши тесты очень хрупкими. Ведь дизайн может поменяться в любой момент - вы же не хотите, чтобы ваши тесты часто падали?</p> <p>И чтобы вам жизнь слишком лёгкой не казалось, помните, что селениумовский метод <code class="language-plaintext highlighter-rouge">getCssValue</code> возвращает значение так, как его отдаёт браузер. Например, у вас просто так не получится проверить цвет вот так: <code class="language-plaintext highlighter-rouge">$("input").shouldHave(cssValue("color", "#000000"))</code>, потому что</p> <ul> <li>Для хрома вернется <code class="language-plaintext highlighter-rouge">rgb(0, 0, 0)</code></li> <li>Для файерфокса вернется <code class="language-plaintext highlighter-rouge">rgba(0, 0, 0, 0)</code></li> </ul> <p>И никаких тебе хексов.</p> <p><br /></p> <h1 id="подчистили-левую-зависимость-logback-classic">Подчистили левую зависимость <code class="language-plaintext highlighter-rouge">logback-classic</code></h1> <p>Как вы помните, в предыдущей версии Selenide от WebDriverManager прилетела ненужная зависимость, и мы её рекомендовали исключить в вашем билд-скрипте. Теперь можете удалить эти строчки:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">exclude</span> <span class="nl">group:</span> <span class="s2">"ch.qos.logback"</span><span class="o">,</span> <span class="nl">module:</span> <span class="s2">"logback-classic"</span> </code></pre></div></div> <p><br /></p> <h1 id="подчистили-задвоенные-скриншоты">Подчистили задвоенные скриншоты</h1> <p>Начиная с версии 4.11.0, Селенид делал повторные скриншоты в случае некоторых ошибок. Не смертельно, но всё же исправили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/729">issue 729</a> - спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/730">PR 730</a></p> <p><br /></p> <h1 id="починили-browserbinary-в-случае-удалённого-запуска-браузера">Починили <code class="language-plaintext highlighter-rouge">browserBinary</code> в случае удалённого запуска браузера</h1> <p>Начиная с версии 4.9 в Selenide появилась возможность <a href="/2017/12/20/selenide-4.9/">указать путь к бинарнику браузера</a> через настройку <code class="language-plaintext highlighter-rouge">Configuration.browserBinary</code>. Но она использовалась только при локальном запуске браузера, а при удалённом игнорировалась.</p> <p>Теперь Selenide использует эту настройку и при удалённом запуске.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/725">issue 725</a> - Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/pull/726">PR 726</a></p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Какой-то новый <a href="https://www.youtube.com/watch?v=sHPaj1kTgGY&amp;feature=youtu.be">видео-туториал по Selenide</a></li> <li>9-11 мая в Кракове пройдёт <a href="https://2018.geecon.org/speakers/">конференция GeeCon</a>, где я буду рассказывать про Selenide</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/2018/05/08/selenide-4.11.3/ http://ru.selenide.org/2018/05/08/selenide-4.11.3 2018-05-08T00:00:00+00:00 Вышла Selenide 4.11.2 <p>Друзья!</p> <p>Мы выпустили версию Selenide 4.11.2. Не терпится узнать, какие обновления вас ждут?</p> <p><br /></p> <h1 id="теперь-можно-делать-скриншоты-элементов-внутри-iframe">Теперь можно делать скриншоты элементов внутри <code class="language-plaintext highlighter-rouge">iframe</code></h1> <p>За примерами можно сходить в <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/ScreenshotInIframeTest.java">ScreenshotInIframeTest</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideElement</span> <span class="n">iframe</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#iframe_page"</span><span class="o">);</span> <span class="nc">SelenideElement</span> <span class="n">element</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#small_div"</span><span class="o">);</span> <span class="nc">File</span> <span class="n">file</span> <span class="o">=</span> <span class="nc">Screenshots</span><span class="o">.</span><span class="na">takeScreenShot</span><span class="o">(</span><span class="n">iframe</span><span class="o">,</span> <span class="n">element</span><span class="o">);</span> </code></pre></div></div> <p>спасибо <a href="https://github.com/andrejska">Andrejs Kalnačs</a> за <a href="https://github.com/selenide/selenide/pull/705">PR 705</a></p> <p><br /></p> <h1 id="добавили-метод-atbottom">Добавили метод <code class="language-plaintext highlighter-rouge">atBottom()</code></h1> <p>Он позволяет проверить, что страница проскроллена до самого низа:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">assertFalse</span><span class="o">(</span><span class="n">atBottom</span><span class="o">());</span> <span class="n">executeJavaScript</span><span class="o">(</span><span class="s">"return window.scrollTo(0, document.body.scrollHeight);"</span><span class="o">);</span> <span class="n">assertTrue</span><span class="o">(</span><span class="n">atBottom</span><span class="o">());</span> </code></pre></div></div> <p>Примеры в <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/PageAtBottomTest.java">PageAtBottomTest</a></p> <p>спасибо <a href="https://github.com/pavelpp">pavelpp</a> за <a href="https://github.com/selenide/selenide/pull/646">PR 646</a></p> <p><br /></p> <h1 id="добавили-настройку-setvaluechangeevent">Добавили настройку <code class="language-plaintext highlighter-rouge">setValueChangeEvent</code></h1> <p>Как известно, методы <code class="language-plaintext highlighter-rouge">$.append()</code> и <code class="language-plaintext highlighter-rouge">$.setValue()</code> после установки значения генерировали несколько событий, в том числе <code class="language-plaintext highlighter-rouge">change</code>. Недавно мы пришли к выводу, что это неправильно, потому что событие <code class="language-plaintext highlighter-rouge">change</code> должен триггерить браузер в зависимости от действий пользователя. К тому же иногда это вызывало и реальные проблемы - например, когда это искусственное событие <code class="language-plaintext highlighter-rouge">change</code> вызывало переход фокуса к следующему в DOM элементу, который был вне viewPort.</p> <p>Но не волнуйтесь, по умолчанию поведение Selenide не поменялось. По дефолту <code class="language-plaintext highlighter-rouge">setValueChangeEvent=true</code>. Это на всякий случай - вдруг у всех всё сломается. :)</p> <p>Если вы согласны с доводами, выключите генерацию события <code class="language-plaintext highlighter-rouge">change</code> через системное свойство:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.setValueChangeEvent=false </code></pre></div></div> <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">setValueChangeEvent</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> </code></pre></div></div> <p>Ну а если не согласны - смело делитесь соображениями. Устроим дискуссию!</p> <p>спасибо <a href="https://github.com/MikeShysh">MikeShysh</a> за <a href="https://github.com/selenide/selenide/pull/718">PR 718</a></p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <ul> <li>htmlunit:2.30</li> <li>webdrivermanager:2.2.1</li> </ul> <p>Если у вас была кастомная имплементация <code class="language-plaintext highlighter-rouge">SLF4J</code>, и теперь логи заспамлены чем-то типа</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/Users/arasolka/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.11.0/9ba207b78e470fe7765ebee14f1f0336c9cbcc18/log4j-slf4j-impl-2.11.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/Users/arasolka/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/7c4f3c474fb2c041d8028740440937705ebb473a/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] </code></pre></div></div> <p>то стоит заигнорить транзитивную зависимость, прилетевшую из <code class="language-plaintext highlighter-rouge">webdrivermanager</code>. Например, в Gradle это делается так:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">configurations</span><span class="o">.</span><span class="na">all</span> <span class="o">{</span> <span class="n">exclude</span> <span class="nl">group:</span> <span class="s2">"ch.qos.logback"</span><span class="o">,</span> <span class="nl">module:</span> <span class="s2">"logback-classic"</span> <span class="o">}</span> </code></pre></div></div> <p>Мы исправим это в следующей версии Selenide.</p> <h2 id="немного-статистики">Немного статистики</h2> <p>Мы давно не выкладывали статистику скачиваний Selenide. А она растёт! Год назад было 8000, теперь уже 18000 скачек в месяц.</p> <center> <img src="/images/2018/04/selenide.downloads.png" width="800" /> </center> <p>Жизнь ништяк!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/04/25/selenide-4.11.2/ http://ru.selenide.org/2018/04/25/selenide-4.11.2 2018-04-25T00:00:00+00:00 Вышла Selenide 4.11.1 <p>Друзья! С недавних пор мы перешли на “Semantic versioning”. Теперь версии будут называться 4.11.x и выходить чаще.</p> <p>В версии Selenide 4.11.1 было всего одно исправление:</p> <p><br /></p> <h1 id="исправили-проблему-с-зависающим-хромом-на-windows">Исправили проблему с зависающим хромом на Windows</h1> <p>Бедолаги, сидящие на Windows, стали жаловаться, что после вызова метода <code class="language-plaintext highlighter-rouge">Selenide.close()</code> процесс <code class="language-plaintext highlighter-rouge">chrome.exe</code> остаётся висеть в списке задач. И хотя это не ошибка селенида, мы смогли подкрутить настройки вебдрайвера, так чтобы хром закрывался.</p> <p>спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/711">PR 711</a></p> <p><br /></p> <p>На этом пока всё, Selenide 4.11.2 не за горами!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/04/03/selenide-4.11.1/ http://ru.selenide.org/2018/04/03/selenide-4.11.1 2018-04-03T00:00:00+00:00 Вышла Selenide 4.11.0 <p>Привет, друзья!</p> <p>Ой, сколько всего хочется рассказать, аж не терпится!</p> <p>Но начнём с релиза 4.11.0</p> <p><strong>Итак, какие обновления прилетели в Selenide 4.11.0?</strong></p> <p><br /></p> <h1 id="обновились-до-selenium-3110">Обновились до selenium 3.11.0</h1> <p>Главные <a href="https://selenium2.ru/news/199-selenium-311.html">изменения в этом релизе</a> связаны с браузером Internet Explorer и платформой Windows.</p> <p>А также:</p> <ul> <li>удалены методы <code class="language-plaintext highlighter-rouge">startClient</code> и <code class="language-plaintext highlighter-rouge">stopClient</code> в классе <code class="language-plaintext highlighter-rouge">RemoteWebDriver</code>.</li> <li>удалены свойства <code class="language-plaintext highlighter-rouge">SafariOptions.cleanSession</code> и <code class="language-plaintext highlighter-rouge">SafariOptions.port</code></li> </ul> <p><br /></p> <h1 id="добавили-условие-textsinanyorder">Добавили условие <code class="language-plaintext highlighter-rouge">textsInAnyOrder</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="na">shouldHave</span><span class="o">(</span><span class="n">textsInAnyOrder</span><span class="o">(</span><span class="s">"Push"</span><span class="o">,</span> <span class="s">"Image"</span><span class="o">,</span> <span class="s">"Email"</span><span class="o">))</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/478">issue 478</a> – спасибо <a href="https://github.com/hyunil-shin">hyunil-shin</a> за <a href="https://github.com/selenide/selenide/pull/589">PR 589</a></p> <p><br /></p> <h1 id="теперь-можно-указать-местоположение-браузера-на-экране">Теперь можно указать местоположение браузера на экране</h1> <p>Как обычно, либо через системное свойство:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-Dselenide</span>.browserPosition<span class="o">=</span>300x200 </code></pre></div></div> <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">browserPosition</span> <span class="o">=</span> <span class="s">"400x300"</span><span class="o">;</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/687">issue 687</a> – спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/687">PR 687</a></p> <p><br /></p> <h1 id="теперь-selenide-умеет-скачивать-файлы-с-русскими-буквами-в-названии">Теперь Selenide умеет скачивать файлы с русскими буквами в названии</h1> <p>Теперь такой код будет корректно работать:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">File</span> <span class="n">file</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"a"</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">"файл-с-русским-названием.txt"</span><span class="o">);</span> </code></pre></div></div> <p>А раньше Selenide создавал файл с именем <code class="language-plaintext highlighter-rouge">-------.txt</code></p> <p>См. <a href="https://github.com/selenide/selenide/issues/688">ошибку 688</a> и <a href="https://github.com/selenide/selenide/pull/689">PR 689</a></p> <p><br /></p> <h1 id="добавили-возможность-передавать-chromeoptionsprefs">Добавили возможность передавать <code class="language-plaintext highlighter-rouge">chromeoptions.prefs</code></h1> <p>По аналогии с <code class="language-plaintext highlighter-rouge">chromeoptions.args</code>, теперь можно передавать такой параметр в тесты:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-Dchromeoptions</span>.prefs<span class="o">=</span>profile.block_third_party_cookies<span class="o">=</span><span class="nb">false</span>,profile.avatar_index<span class="o">=</span>26 </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/692">PR 692</a> - спасибо <a href="https://github.com/sirdir">Tymur Kubai</a></p> <p><br /></p> <h1 id="теперь-вы-можете-добавлять-в-прокси-сервер-свои-перехватчики-для-запросовответов">Теперь вы можете добавлять в прокси-сервер свои перехватчики для запросов/ответов</h1> <p>Вы давно это просили, и вот мы наконец сделали!</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">addRequestFilter</span><span class="o">(</span><span class="s">"proxy-usages.request"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">RequestFilter</span><span class="o">()</span> <span class="o">{...});</span> <span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">addResponseFilter</span><span class="o">(</span><span class="s">"proxy-usages.response"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">ResponseFilter</span><span class="o">()</span> <span class="o">{...});</span> </code></pre></div></div> <p>В <code class="language-plaintext highlighter-rouge">RequestFilter</code> и <code class="language-plaintext highlighter-rouge">ResponseFilter</code> вы можете делать буквально всё: логировать, засекать время, получать http статусы, менять тело запросов или ответов, внедрять свой JavaScript в страницы. В общем, режим бога. Подробнее о богатых возможностях прокси бог рассказывал <a href="https://habrahabr.ru/post/209752/">тут</a>.</p> <p>Надеюсь, вы используете это во благо и сделаете свои тесты быстрее и надёжнее.</p> <p>Как всегда, пример использования вы можете найти <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/proxy/ProxyServerUsageTest.java">в тестах самого селенида</a></p> <p>NB! Сейчас прокси-сервер запускается только при включенной настройке <code class="language-plaintext highlighter-rouge">Configuration.fileDownload=PROXY</code>.</p> <p>Возможно, стоит развязать эти две настройки. Ждём от вас обратной связи! Смело рассказывайте, получилось ли у вас использовать прокси.</p> <p><br /></p> <h1 id="метод-followlink-помечен-как-deprecated">Метод <code class="language-plaintext highlighter-rouge">$.followLink()</code> помечен как deprecated</h1> <p>Никто уже не помнит, зачем он вообще бы создан 7 лет назад. Сейчас вместо него можно использовать обычный <code class="language-plaintext highlighter-rouge">$.click()</code>.</p> <p><br /></p> <h1 id="исправили-случайный-nullpointerexception-в-selenidereport">Исправили случайный NullPointerException в SelenideReport</h1> <p>Никто не знает, как эту ошибку повторить, но на всякий случай исправили.</p> <p>Спасибо <a href="https://github.com/dkorobtsov">dkorobtsov</a> за <a href="https://github.com/selenide/selenide/pull/686">PR 686</a></p> <p><br /></p> <h1 id="исправили-ошибку-когда-при-софт-ассертах-не-дела