Selenide Selenide - Andrei Solntsev http://ru.selenide.org/rss http://ru.selenide.org 2024-04-01T06:13:53+00:00 2024-04-01T06:13:53+00:00 1800 Вышла Selenide 7.2.0 <p>Чёртов февраль.</p> <p>Чтобы хоть как-то отвлечься, попробуйте обновиться на <a href="https://github.com/selenide/selenide/milestone/201?closed=1">Selenide 7.2.0</a>:</p> <ul> <li><a href="#download-files-to-folder-in-selenium-grid">Скачиваем файлы из Selenium Grid</a></li> <li><a href="#download-files-remotely-with-cdp">Скачиваем файлы удалённо с помощью CDP</a></li> <li><a href="#mobile-elements-collections">Коллекции элементов для мобилок</a></li> <li><a href="#disable-chrome-warning-about-stolen-passwords">Выключили предупреждение хрома про покраденные пароли</a></li> <li><a href="#improve-error-message-for-download-without-href">Сообщение об ошибке, если нет href</a></li> <li><a href="#upgraded-to-selenium-4.18.1">Обновили Selenium с 4.17.0 на 4.18.1</a></li> </ul> <p><br /></p> <h3 id="download-files-to-folder-in-selenium-grid">Скачиваем файлы из Selenium Grid</h3> <p>Наконец-то! <br /> Теперь вы можете скачивать файлы методом <code class="language-plaintext highlighter-rouge">FOLDER</code> и из Selenium Grid.<br /> До сих пор <code class="language-plaintext highlighter-rouge">FOLDER</code> работал только при локальном запуске и в Selenoid.</p> <p>Если вы гоняете браузеры на Selenium Grid и хотите в тестах скачивать файлы методом <code class="language-plaintext highlighter-rouge">FOLDER</code>, вам просто нужно будет поменять зависимость</p> <div class="language-groovy 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="nl">selenide:</span><span class="mf">7.1</span><span class="o">.</span><span class="mi">0</span> </code></pre></div></div> <p>на</p> <div class="language-groovy 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="n">selenide</span><span class="o">-</span><span class="nl">grid:</span><span class="mf">7.2</span><span class="o">.</span><span class="mi">0</span> </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">remote</span> <span class="o">=</span> <span class="s">"https://qa.grid.my.com:4444/wd/hub"</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">FOLDER</span><span class="o">;</span> <span class="nc">File</span> <span class="n">quotes</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#quotes"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">withExtension</span><span class="o">(</span><span class="s">"txt"</span><span class="o">));</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">quotes</span><span class="o">.</span><span class="na">content</span><span class="o">()).</span><span class="na">contains</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/selenide/selenide/issues/1687">issue 1687</a>, <a href="https://github.com/selenide/selenide/pull/2659">PR 2659</a> и <a href="https://github.com/selenide/selenide/pull/2660">PR 2660</a>.</p> <p><br /></p> <h3 id="download-files-remotely-with-cdp">Скачиваем файлы удалённо с помощью CDP</h3> <p>В предыдущем релизе мы добавили новый метод скачивания файлов <a href="/2024/02/07/selenide-7.1.0/#download-files-with-cdp">CDP</a>, но работал он только локально.</p> <p>Теперь он также работает с удалёнными браузерами на Selenoid и Selenium Grid.</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">remote</span> <span class="o">=</span> <span class="s">"https://cloud.browsers.my.com:4444/wd/hub"</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">CDP</span><span class="o">;</span> <span class="nc">File</span> <span class="n">places</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#places"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">withExtension</span><span class="o">(</span><span class="s">"csv"</span><span class="o">));</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">places</span><span class="o">.</span><span class="na">content</span><span class="o">()).</span><span class="na">contains</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/selenide/selenide/issues/1687">issue 1687</a>, <a href="https://github.com/selenide/selenide/issues/2550">issue 2550</a> и <a href="https://github.com/selenide/selenide/pull/2661">PR 2661</a>.</p> <p><br /></p> <h3 id="mobile-elements-collections">Коллекции элементов для мобилок</h3> <p>Мы навалили полезняшек для автотестов мобильных приложений. Появились новые методы для коллекций:</p> <ul> <li><code class="language-plaintext highlighter-rouge">SelenideAppium.$$(By)</code></li> <li><code class="language-plaintext highlighter-rouge">SelenideAppium.$$(Collection&lt;WebElement&gt;)</code></li> </ul> <p>Оба возвращают объект <code class="language-plaintext highlighter-rouge">SelenideAppiumCollection</code>. По сути это то же самое, что старый добрый <code class="language-plaintext highlighter-rouge">ElementsCollection</code>, только с объектами <code class="language-plaintext highlighter-rouge">SelenideAppiumElement</code> вместо <code class="language-plaintext highlighter-rouge">SelenideElement</code>.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideAppiumCollection</span> <span class="n">poetry</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">"//android.widget.EditText"</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> <span class="n">poetry</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">scroll</span><span class="o">(</span><span class="n">up</span><span class="o">()).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"и в небо сорвутся тромбы"</span><span class="o">);</span> <span class="n">poetry</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">).</span><span class="na">scroll</span><span class="o">(</span><span class="n">down</span><span class="o">()).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"багровых кремлёвских башен"</span><span class="o">);</span> </code></pre></div></div> <p>Также добавили метод <code class="language-plaintext highlighter-rouge">SelenideAppium.$(WebElement webElement)</code>, но вряд ли это вам пригодится.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2333">issue 2333</a> и <a href="https://github.com/selenide/selenide/pull/2656">PR 2656</a>.</p> <p><br /></p> <h3 id="disable-chrome-warning-about-stolen-passwords">Выключили предупреждение хрома про покраденные пароли</h3> <p>После обновления хрома начал выскакивать этот мешающий диалог “ваши пароли покрадены”. Кажется логичным, что в тестах он никому не нужен.</p> <p>И хотя это выглядит скорее как бага Chrome 122, мы на всякий случай его по-быстрому вырубили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2658">issue 2658</a> и <a href="https://github.com/selenide/selenide/pull/2662">PR 2662</a>.</p> <p><br /></p> <h3 id="improve-error-message-for-download-without-href">Сообщение об ошибке, если нет <code class="language-plaintext highlighter-rouge">href</code></h3> <p>Меня внезапно осенило, почему так много людей мучаются с настройкой прокси и даже не догадываются, что в селениде есть и другие способы скачивания файлов: <code class="language-plaintext highlighter-rouge">FOLDER</code> и <code class="language-plaintext highlighter-rouge">CDP</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">The</span> <span class="n">element</span> <span class="n">does</span> <span class="n">not</span> <span class="n">have</span> <span class="n">href</span> <span class="nl">attribute:</span> <span class="o">&lt;</span><span class="n">a</span> <span class="n">id</span><span class="o">=</span><span class="s">"generate-report"</span><span class="o">&gt;</span> </code></pre></div></div> <p>После этого люди гуглили <a href="https://selenide.org/2019/12/10/advent-calendar-download-files/">первую попавшуюся статью</a> 5-летней давности, в которой, конечно, упоминалась только одна альтернатива - <code class="language-plaintext highlighter-rouge">PROXY</code>. И ломились изучать прокси.</p> <p>Теперь сообщение об ошибке будет подсказывать, что методов несколько:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">The</span> <span class="n">element</span> <span class="n">does</span> <span class="n">not</span> <span class="n">have</span> <span class="s">"href"</span> <span class="nl">attribute:</span> <span class="o">&lt;</span><span class="n">a</span> <span class="n">id</span><span class="o">=</span><span class="s">"generate-report"</span><span class="o">&gt;,</span> <span class="n">so</span> <span class="n">method</span> <span class="no">HTTPGET</span> <span class="n">cannot</span> <span class="n">download</span> <span class="n">the</span> <span class="n">file</span><span class="o">.</span> <span class="nc">Please</span> <span class="k">try</span> <span class="n">another</span> <span class="n">download</span> <span class="nl">method:</span> <span class="no">FOLDER</span><span class="o">,</span> <span class="no">CDP</span> <span class="n">or</span> <span class="no">PROXY</span><span class="o">.</span> <span class="nc">Read</span> <span class="n">more</span> <span class="n">about</span> <span class="n">possible</span> <span class="n">download</span> <span class="nl">methods:</span> <span class="nl">https:</span><span class="c1">//selenide.org/javadoc/current/com/codeborne/selenide/FileDownloadMode.html</span> </code></pre></div></div> <p>Правда, боюсь, это не поможет, потому что кто же их читает-то…</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2653">PR 2653</a>.</p> <p><br /></p> <h3 id="upgraded-to-selenium-4.18.1">Обновили Selenium с 4.17.0 на 4.18.1</h3> <p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a> и <a href="https://github.com/selenide/selenide/pull/2655">PR 2655</a>.</p> <p><br /></p> <p>Будем надеяться на весну.</p> <p><br /></p> <blockquote> <p>Не сдавайтесь.<br /> Не унывайте и не опускайте руки.<br /> Автоматизация тестирования — это не спринт, а долгий тяжёлый марафон.</p> </blockquote> <center> <a href="https://www.youtube.com/watch?v=wp66e-JRlj0">Теперь далеко отсюда</a> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2024/02/27/selenide-7.2.0/ http://ru.selenide.org/2024/02/27/selenide-7.2.0 2024-02-27T00:00:00+00:00 Вышла Selenide 7.1.0 <p>Всем привет! С Новым Годом, с новыми надеждами!</p> <p>Сегодня у нас в меню свеженький блестящий <a href="https://github.com/selenide/selenide/milestone/199?closed=1">Selenide 7.1.0</a>:</p> <ul> <li><a href="#download-files-with-cdp">Скачивание файлов с помощью CDP</a></li> <li><a href="#animated-condition">Условие <code class="language-plaintext highlighter-rouge">animated</code></a></li> <li><a href="#if-with-timeout">IF с таймаутом ¯¯_(ツ)_/¯¯</a></li> <li><a href="#force-click">Можно кликать задизейбленные элементы</a></li> <li><a href="#method-unfocus">Метод <code class="language-plaintext highlighter-rouge">$.unfocus()</code></a></li> <li><a href="#avoid-page-load-timeout-in-mobile">Исправили page load timeout в мобилках</a></li> <li><a href="#can-add-same-proxy-filter-many-times">Можно добавлять прокси фильтр многократно</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#statistics">Статистика</a></li> </ul> <p><br /></p> <h3 id="download-files-with-cdp">Скачивание файлов с помощью CDP</h3> <p>Не прошло и три года, как в Selenium завезли CDP, а мы уже сделали с помощью него скачивание файлов! Как вы знаете, скачивать файлы в селениде можно несколькими способами: <code class="language-plaintext highlighter-rouge">HTTPGET</code>, <code class="language-plaintext highlighter-rouge">FOLDER</code>, <code class="language-plaintext highlighter-rouge">PROXY</code>. Теперь к ним добавился ещё и <code class="language-plaintext highlighter-rouge">CDP</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">fileDownload</span> <span class="o">=</span> <span class="no">CDP</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="na">download</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">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</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">CDP</span><span class="o">));</span> </code></pre></div></div> <p>Работает он примерно как <code class="language-plaintext highlighter-rouge">FOLDER</code>: кликает ссылку и ждёт, пока файл появится в списке скачанных. Только в отличие от <code class="language-plaintext highlighter-rouge">FOLDER</code>, <code class="language-plaintext highlighter-rouge">CDP</code> понимает, что загрузка завершена, не по дате файла, а по событию <code class="language-plaintext highlighter-rouge">Browser.downloadProgress</code> из CDP.</p> <p>Конечно, этот метод скачивания работает только в Chromium-браузерах. И пока только на локальном браузере.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2550">issue 2550</a>. Спасибо <a href="https://github.com/britka">Sergey Brit</a> за <a href="https://github.com/selenide/selenide/pull/2567">PR 2567</a>.</p> <hr /> <p>NB! Как контрибьютер, Сергей получает право рассказать вам о каком-то фонде на его выбор.</p> <p>Слово Сергею:</p> <blockquote> <p>Призываю вас донатить фонду <a href="https://www.angrycorgi.org/en">ANGRY CORGI</a>.</p> <p>Фонд Angry Corgi создан людьми из tech, чтобы усилить компьютерную инфраструктуру частей ПВО и ряду других частей Сил Обороны Украины. <br /> Фонд постоянно принимает технику из разных стран мира, а также собирает деньги на закупку планшетов, телевизоров и другого коммуникационного оборудования.</p> </blockquote> <p>Вот такая у нас теперь будет славная традиция. :)</p> <hr /> <p><br /></p> <h3 id="animated-condition">Условие <code class="language-plaintext highlighter-rouge">animated</code> для окончания анимации</h3> <p>Анимированные элементы могут доставить автоматизаторам немало неприятных моментов. Твой тест пытается кликнуть элемент - а он в этот момент куда-то двигается, сжимается, расширяется - чёрти что. И клик попадает по другому элементу.</p> <blockquote> <p>Подробнее об этом в видосе <a href="https://www.youtube.com/watch?v=zOiSo1hYjF8&amp;t=29m43s">Flaky tests</a>.</p> </blockquote> <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">shouldNotBe</span><span class="o">(</span><span class="n">animated</span><span class="o">);</span> </code></pre></div></div> <p>Надеемся, это поможет сделать ваши тесты более стабильными.</p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/2556/files">PR 2556</a>.</p> <p>Вы спросите, а как же селенид понимает, что анимация закончилась? Загляните в пулреквест, это интересно. Если вкратце - с помощью JS функции <code class="language-plaintext highlighter-rouge">requestAnimationFrame</code> селенид спрашивает у браузера размеры и координаты элемента два раза подряд (в двух разных циклах отрисовки) и проверяет, что они не изменились. Хитро, а?</p> <p><br /></p> <h3 id="if-with-timeout">Добавили IF с таймаутом ¯¯_(ツ)_/¯¯</h3> <p>Много лет мы сопротивлялись, но всё-таки позволили этому случиться. :)</p> <p>Мы добавили метод <code class="language-plaintext highlighter-rouge">$.is(condition, timeout)</code>, который возвращает boolean. Теперь в своих тестах вы сможете писать ифы без try/catch. <a href="/2019/12/02/advent-calendar-how-to-abuse-selenide/">Ворота в ад разверзлись</a>.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</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">is</span><span class="o">(</span><span class="n">visible</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">2</span><span class="o">)))</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> </code></pre></div></div> <p>Логика этого ифа наверняка подпалит немало седалищ, так что будьте внимательны:</p> <ol> <li>Если элемент видимый, он сразу вернёт <code class="language-plaintext highlighter-rouge">true</code>.</li> <li>Если элемент невидимый, метод подождёт какое-то время (но не больше заданного таймаута), и если элемент появился - вернёт <code class="language-plaintext highlighter-rouge">true</code>.</li> <li>А вот если элемент так и не появился, в итоге вернёт <code class="language-plaintext highlighter-rouge">false</code>.</li> </ol> <p>См. <a href="https://github.com/selenide/selenide/issues/2590">issue 2590</a>.<br /> Вечный позор на <a href="https://github.com/asolntsev">мои седины</a> за <a href="https://github.com/selenide/selenide/pull/2640">PR 2640</a>!</p> <blockquote> <p>NB! Новый метод <code class="language-plaintext highlighter-rouge">$.is(.., Duration.ofSeconds(8))</code> <em>может быть медленным</em>! Результат <code class="language-plaintext highlighter-rouge">false</code> он всегда возвращает <em>через 8 секунд</em>! Это же убийственно долго для тестов. Постарайтесь не использовать такие условия.</p> </blockquote> <p><br /></p> <h3 id="force-click">Можно кликать задизейбленные элементы</h3> <p>Начиная с версии 6.15.0, селенид <a href="/2023/05/29/selenide-6.15.0/#clicking-disable-element-fails">не позволяет кликнуть на disabled элемент</a>. Но иногда это бывает надо (например, проверить, что клик по такому элементу ничего не делает). Теперь можно обойти все проверки и тупо кликнуть на какой угодно элемент с помощью параметра <code class="language-plaintext highlighter-rouge">force</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">"#disabledButton"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">force</span><span class="o">());</span> </code></pre></div></div> <p>Не злоупотребляйте.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2635">issue 2635</a> и <a href="https://github.com/selenide/selenide/pull/2636">PR 2636</a>.</p> <p><br /></p> <h3 id="method-unfocus">Метод <code class="language-plaintext highlighter-rouge">$.unfocus()</code></h3> <p>Мы добавили метод <code class="language-plaintext highlighter-rouge">$.unfocus()</code>, который убирает фокус с элемента.</p> <p>В общем-то это можно было сделать и раньше с помощью <code class="language-plaintext highlighter-rouge">$.pressTab()</code>, но у него могут быть нежелательные сайд-эффекты. Например, другой элемент неожиданно окажется в фокусе. А иногда по нажатию на TAB и вовсе сабмитится форма (например, при вводе OTP-кода).</p> <p>А метод <code class="language-plaintext highlighter-rouge">$.unfocus()</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">"#card-number"</span><span class="o">).</span><span class="na">sendKeys</span><span class="o">(</span><span class="s">"1111-222-33-4444"</span><span class="o">).</span><span class="na">unfocus</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="s">"#preview"</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">"Pay to 1111-***-**-***4 ?"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2638">issue 2638</a> и <a href="https://github.com/selenide/selenide/pull/2639">PR 2639</a>.</p> <p><br /></p> <h3 id="avoid-page-load-timeout-in-mobile">Исправили page load timeout в мобилках</h3> <p>Был у нас маленький косячок, из-за которого мы случайно выставляли <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code> в мобильных тестах. А Appium такую настройку не поддерживает. Из-за чего вы могли видеть такие логи:</p> <blockquote> <p>NotImplementedError: Not implemented yet for pageLoad</p> </blockquote> <p>Некритично, это всего лишь warning. Но больше вы его не увидите.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2612">issue 2612</a> и <a href="https://github.com/selenide/selenide/pull/2628">PR 2628</a>.</p> <p><br /></p> <h3 id="can-add-same-proxy-filter-many-times">Можно добавлять прокси фильтр многократно</h3> <p>Стало чуть проще добавлять свои фильтры для селенидовского прокси. Теперь можно не проверять, что такой фильтр уже был добавлен ранее, а просто добавлять его каждый раз заново:</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">RequestFilter</span> <span class="no">REQUEST_LOGGER</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="nd">@BeforeEach</span> <span class="kd">final</span> <span class="kt">void</span> <span class="nf">setUp</span><span class="o">()</span> <span class="o">{</span> <span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">addRequestFilter</span><span class="o">(</span><span class="s">"request-logger"</span><span class="o">,</span> <span class="no">REQUEST_LOGGER</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Раньше селенид ругался, что такой фильтр уже есть. А теперь будет спокойненько работать дальше (но только если это реально тот же самый фильтр).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2617">issue 2617</a> и <a href="https://github.com/selenide/selenide/pull/2630">PR 2630</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>bump JUnit from 5.10.1 to 5.10.2</li> <li>bump TestNG from 7.8.0 to 7.9.0</li> <li>Bump slf4jVersion from 2.0.11 to 2.0.12</li> <li>Bump LittleProxy from 2.1.1 to 2.1.2</li> </ul> <p><br /></p> <h3 id="statistics">Статистика</h3> <p>Количество ежемесячных скачиваний Селенида перевалило за 911 тыщ!</p> <center> <img src="/images/2024/01/selenide.downloads.png" width="600px" /> </center> <p>Вау.</p> <p>И ещё один занимательный график.</p> <p>Есть у Селенида один подпроект <a href="https://github.com/selenide/selenide/tree/main/modules/appium"><code class="language-plaintext highlighter-rouge">selenide-appium</code></a> для написания автотестов для мобилок. Раньше он не особо пользовался спросом (по сравнению с самим Селенидом), но в этом году резко попёр вверх.</p> <center> <img src="/images/2024/01/selenide-appium.downloads.png" width="600px" /> </center> <p>Не знаю, чем это объясняется, но это круто. :)</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2024/02/07/selenide-7.1.0/ http://ru.selenide.org/2024/02/07/selenide-7.1.0 2024-02-07T00:00:00+00:00 Вышла Selenide 7.0.1 <p>Вчера мы выпустили <a href="/2023/10/25/selenide-7.0.0/">Selenide 7.0.0</a> и, честно говоря, я ожидал волны жалоб насчёт Java 17.</p> <p>Но нет. На Java 17 никто пока не жаловался. И это здорово! Все обновили джаву, все молодцы. <br /> Но зато люди очень просили обратно циклы по коллекциям.</p> <p>Так и быть, в сегодняшнем <a href="https://github.com/selenide/selenide/milestone/194?closed=1">Selenide 7.0.1</a> мы кое-что вернули:</p> <ul> <li><a href="#restore-collection-loop">Вернули циклы по коллекциям</a></li> <li><a href="#restore-collection-is-empty">Вернули метод <code class="language-plaintext highlighter-rouge">isEmpty()</code> у коллекций</a></li> <li><a href="#restore-self-in-containers">Вернули поле <code class="language-plaintext highlighter-rouge">self</code> в контейнерах</a></li> <li><a href="#rename-collection-condition">Переименовали <code class="language-plaintext highlighter-rouge">CollectionCondition</code> в <code class="language-plaintext highlighter-rouge">WebElementsCondition</code></a></li> <li><a href="#upgrade-to-selenium-4.15.0">UPD 7.0.2: Обновились на Selenium 4.15.0</a></li> </ul> <p><br /></p> <h3 id="restore-collection-loop">Вернули циклы по коллекциям</h3> <p>Теперь вы можете снова использовать <code class="language-plaintext highlighter-rouge">for</code> по коллекциям:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="nc">SelenideElement</span> <span class="n">element</span> <span class="o">:</span> <span class="err">$$</span><span class="o">(</span><span class="s">".element"</span><span class="o">))</span> <span class="o">{</span> <span class="n">element</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>А я не устану повторять, что</p> <blockquote> <p>НЕ НАДО ТАК ДЕЛАТЬ!</p> </blockquote> <p><a href="https://github.com/selenide/selenide/wiki/do-not-use-getters-in-tests">Не используйте циклы и условия в тестах!</a></p> <p>Если вы хотите собрать в кучу тексты или атрибуты всех элементов, чтобы потом и проверить, то для этого есть т.н. “collection condition”:</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">".errors"</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">"One"</span><span class="o">,</span> <span class="s">"Two"</span><span class="o">,</span> <span class="s">"Three"</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#numbers option"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attributes</span><span class="o">(</span><span class="s">"value"</span><span class="o">,</span> <span class="s">"one"</span><span class="o">,</span> <span class="s">"two"</span><span class="o">,</span> <span class="s">"three"</span><span class="o">,</span> <span class="s">"four"</span><span class="o">,</span> <span class="s">"five"</span><span class="o">));</span> </code></pre></div></div> <p>А если вы не нашли готовой проверки под ваши нужды, легко можете <a href="https://github.com/selenide/selenide/wiki/Custom-collection-conditions">написать свою</a>. Просто создайте класс от <code class="language-plaintext highlighter-rouge">WebElementsCondition</code> и реализуйте в нём метод <code class="language-plaintext highlighter-rouge">check</code>. Ну изи же. И сможете его переиспользовать в куче своих тестов.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2533">PR 2533</a>.</p> <p><br /></p> <h3 id="restore-collection-is-empty">Вернули метод <code class="language-plaintext highlighter-rouge">isEmpty()</code> у коллекций</h3> <p>Мне по-прежнему непонятно - зачем, но зачем-то люди используют этот метод:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">boolean</span> <span class="n">errorsFound</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">".error"</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/pull/2533">PR 2533</a>.</p> <p><br /></p> <h3 id="restore-self-in-containers">Вернули поле <code class="language-plaintext highlighter-rouge">self</code> в контейнерах</h3> <p>В релизе 7.0.0 мы удалили класс <code class="language-plaintext highlighter-rouge">ElementsContainer</code>, а с ним пропал и метод <code class="language-plaintext highlighter-rouge">getSelf()</code>. Я всё ещё нахожу его странным, но почему-то люди его используют.</p> <p>Если это про вас, то вы можете заменить метод <code class="language-plaintext highlighter-rouge">getSelf()</code> на поле с аннотацией <code class="language-plaintext highlighter-rouge">@Self</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">class</span> <span class="nc">RadioButton</span> <span class="kd">implements</span> <span class="nc">Container</span> <span class="o">{</span> <span class="nd">@Self</span> <span class="nc">SelenideElement</span> <span class="n">label</span><span class="o">;</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">tagName</span> <span class="o">=</span> <span class="s">"input[type=radio]"</span><span class="o">)</span> <span class="nc">SelenideElement</span> <span class="n">input</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2534">PR 2534</a>.</p> <p><br /></p> <h3 id="rename-collection-condition">Переименовали <code class="language-plaintext highlighter-rouge">CollectionCondition</code> в <code class="language-plaintext highlighter-rouge">WebElementsCondition</code></h3> <p>Вас это коснётся, только если вы писали <a href="https://github.com/selenide/selenide/wiki/Custom-collection-conditions">самодельные проверки для коллекций</a>.</p> <p>Не беда, просто поменяйте <code class="language-plaintext highlighter-rouge">extends CollectionCondition</code> на <code class="language-plaintext highlighter-rouge">WebElementsCondition</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2372">issue 2372</a> и <a href="https://github.com/selenide/selenide/pull/2533">PR 2533</a>.</p> <p><br /></p> <h3 id="upgrade-to-selenium-4.15.0">UPD 7.0.2: Обновились на Selenium 4.15.0</h3> <p>Позже мы выпустили Selenide 7.0.2 с обновлением на последний Selenium 4.15.0</p> <p>Тут <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог селениума</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2540">PR 2540</a>.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/10/26/selenide-7.0.1/ http://ru.selenide.org/2023/10/26/selenide-7.0.1 2023-10-26T00:00:00+00:00 Вышла Selenide 7.0.0 <p>С днём рождения!</p> <p>Да-да, в этот день 12 лет назад был сделан первый коммит в проекте Selenide. Если интересно, здесь <a href="https://www.youtube.com/watch?v=DBkLFL4E5YA&amp;ab_channel=DEVCLUB.EU">видос моего рассказа к 10-летию селенида</a>.</p> <p>К празднику мы выпустили мажорный релиз <a href="https://github.com/selenide/selenide/milestone/191?closed=1">Selenide 7.0.0</a> с обновлением аж сразу на Java 17.</p> <ul> <li><a href="#java17">Обновились на Java 17</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#fix-deadlock-in-static-initialization">Дедлок при инициализации классов</a></li> <li><a href="#report-actual-value-at-the-moment-of-check">Актуальное значение на момент проверки</a></li> <li><a href="#remove-deprecated-condition-methods">Удалили старые методы в <code class="language-plaintext highlighter-rouge">Condition</code></a></li> <li><a href="#remove-deprecated-methods-from-collection-conditions">Удалили старые методы в <code class="language-plaintext highlighter-rouge">CollectionCondition</code></a></li> <li><a href="#remove-deprecated-methods-from-elements-collection">Удалили старые методы в <code class="language-plaintext highlighter-rouge">ElementsCollection</code></a></li> <li><a href="#replace-elements-container-by-container">Заменили <code class="language-plaintext highlighter-rouge">ElementsContainer</code> на <code class="language-plaintext highlighter-rouge">Container</code></a></li> <li><a href="#replace-file-not-found-exception">Заменили <code class="language-plaintext highlighter-rouge">FileNotFoundException</code> на <code class="language-plaintext highlighter-rouge">FileNotDownloadedError</code></a></li> <li><a href="#rename-exceptions-to-errors">Переименовали <code class="language-plaintext highlighter-rouge">*Exception</code> в <code class="language-plaintext highlighter-rouge">*Error</code></a></li> <li><a href="#remove-deprecated-listener">Удалили поддержку устаревшего <code class="language-plaintext highlighter-rouge">WebDriverEventListener</code></a></li> <li><a href="#remove-deprecated-drag-and-drop-methods">Удалили старые методы для Drag’n’drop</a></li> <li><a href="#remove-deprecated-get-selected-value-methods">Удалили устаревшие методы <code class="language-plaintext highlighter-rouge">$.getSelectedValue()</code> и <code class="language-plaintext highlighter-rouge">$.getSelectedText()</code></a></li> <li><a href="#remove-deprecated-testng-annotations">Удалили старые аннотации для TestNG <code class="language-plaintext highlighter-rouge">@Report</code> и <code class="language-plaintext highlighter-rouge">@GlobalTextReport</code></a></li> <li><a href="#remove-other-deprecated-methods">Остальное</a></li> </ul> <p><br /></p> <h3 id="java17">Обновились на Java 17</h3> <p>Чума! Этот день настал!</p> <p>Чтобы использовать Selenide 7.+, вам придётся компилировать и запускать тесты на Java 17+.</p> <p>Как же это круто, да?</p> <p>Я знаю, среди вас найдутся те, кому это решение не понравится. Вы всё ещё вынуждены гонять тесты на Java 8.</p> <p>Но мы не виноваты. Как вы знаете, Selenium 4.14+ перешёл с Java 8 на Java 11. Так что продолжать жить с Java 8 у вас всё равно не получилось бы. Обновиться на Java 11 всё равно пришлось бы. Ну а дальше простое соображение: самое сложное обновление как раз с Java 8 на Java 11. А вот с Java 11 на Java 17 обновиться намного проще. Там и делать-то ничего не надо.</p> <p>Так что обновляйтесь смелее!</p> <blockquote> <p>Жизнь слишком коротка, чтобы продолжать мять титьки с Java 8</p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/pull/2522">PR 2522</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>Обновили Selenium с 4.13.0 на 4.14.1 (<a href="https://github.com/selenide/selenide/pull/2505">PR 2505</a>) <small>- требует Java 11+</small></li> <li>Обновили Appium с 8.6.0 на 9.0.0 (<a href="https://github.com/selenide/selenide/pull/2505">PR 2505</a>) <small>- требует Java 11+</small></li> <li>Обновили TestNG с 7.4.0 на 7.8.0 (<a href="https://github.com/selenide/selenide/pull/2515">PR 2505</a>) <small>- требует Java 11+</small></li> </ul> <p><br /></p> <h3 id="fix-deadlock-in-static-initialization">Исправили дедлок при инициализации классов</h3> <p>Это старая болячка в Selenide: класс <code class="language-plaintext highlighter-rouge">Condition</code> и любой из его дочерних классов зависели друг от друга. Поэтому при определённых обстоятельствах мог случиться дедлок при одновременной загрузке этих классов. Крайне редко, но теоретически возможно.</p> <blockquote> <p>Подробнее про static initialization deadlock можно посмотреть в видосе <a href="https://www.youtube.com/watch?v=2J6iJLYZZUM&amp;t=31m44s">Перформансные войны</a>.</p> </blockquote> <p>При этом нам пришлось переименовать базовый класс для всех проверок из <code class="language-plaintext highlighter-rouge">Condition</code> в <code class="language-plaintext highlighter-rouge">WebElementCondition</code>.</p> <ol> <li>Не пугайтесь, все ваши импорты типа <code class="language-plaintext highlighter-rouge">Condition.visible</code> останутся без изменений.</li> <li>А вот если вы написали свои самодельные проверки, то в них надо будет заменить <code class="language-plaintext highlighter-rouge">extends Condition</code> на <code class="language-plaintext highlighter-rouge">extends WebElementCondition</code>.</li> </ol> <p>См. <a href="https://github.com/selenide/selenide/issues/2372">issue 2372</a> и <a href="https://github.com/selenide/selenide/pull/2453">PR 2453</a>.</p> <p><br /></p> <h3 id="report-actual-value-at-the-moment-of-check">Актуальное значение на момент проверки</h3> <p>Тут идёт речь не о классических проверках веб-элементов, а о <a href="/2021/07/16/selenide-5.23.0/">проверках “нового поколения”</a>: url, localStorage и т.п.</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">url</span><span class="o">(</span><span class="s">"https://some.com/page.html"</span><span class="o">));</span> <span class="n">localStorage</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">item</span><span class="o">(</span><span class="s">"mouse"</span><span class="o">,</span> <span class="s">"Jerry”)); sessionStorage().shouldHave(itemWithValue("</span><span class="n">mouse</span><span class="s">", "</span><span class="nc">Jerry</span><span class="err">”</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">"Лох, Антилохово, Шалава, Мусорка, Пуково, Попки и Бухалово"</span><span class="o">));</span> </code></pre></div></div> <p>В общем, мы сделали сообщение об ошибке более корректным. За деталями можно сгонять в <a href="https://github.com/selenide/selenide/issues/2500">issue 2500</a> и <a href="https://github.com/selenide/selenide/pull/2501">PR 2501</a>.</p> <p><br /></p> <h3 id="remove-deprecated-condition-methods">Удалили старые методы в <code class="language-plaintext highlighter-rouge">Condition</code></h3> <p>Удалили методы <code class="language-plaintext highlighter-rouge">apply()</code> и <code class="language-plaintext highlighter-rouge">actualValue()</code> в классе <code class="language-plaintext highlighter-rouge">Condition</code>. Они уже давно были помечены как <code class="language-plaintext highlighter-rouge">@Deprecated</code>.</p> <p>Вас это коснётся, только если вы писали свои самодельные проверки. Теперь вместо двух методов <code class="language-plaintext highlighter-rouge">apply()</code> и <code class="language-plaintext highlighter-rouge">actualValue()</code> вам нужно будет реализовывать лишь один метод <code class="language-plaintext highlighter-rouge">check</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2512">PR 2512</a>.</p> <p><br /></p> <h3 id="remove-deprecated-methods-from-collection-conditions">Удалили старые методы в <code class="language-plaintext highlighter-rouge">CollectionCondition</code></h3> <p>Аналогично предыдущему пункту, если у вас есть самодельные проверки для коллекций (<code class="language-plaintext highlighter-rouge">extends CollectionCondition</code>), то в них вместо методов <code class="language-plaintext highlighter-rouge">test</code> и <code class="language-plaintext highlighter-rouge">fail</code> надо будет реализовать один метод <code class="language-plaintext highlighter-rouge">check</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2520">PR 2520</a>.</p> <p><br /></p> <h3 id="remove-deprecated-methods-from-elements-collection">Удалили старые методы в <code class="language-plaintext highlighter-rouge">ElementsCollection</code></h3> <p>Боюсь, тут будет много чего подгорать. :) В общем, теперь <code class="language-plaintext highlighter-rouge">ElementsCollection</code> больше <strong>не наследует</strong> <code class="language-plaintext highlighter-rouge">java.util.List</code>. Изначально это наследование <em>случайно</em> привнесло в <code class="language-plaintext highlighter-rouge">ElementsCollection</code> кучу ненужных методов (таких, как <code class="language-plaintext highlighter-rouge">remove()</code>, <code class="language-plaintext highlighter-rouge">removeRange</code>, <code class="language-plaintext highlighter-rouge">clear</code>, <code class="language-plaintext highlighter-rouge">subList</code> или <code class="language-plaintext highlighter-rouge">listIterator()</code>), которые никогда не планировалось реализовывать. Но пользователи иногда использовали. И даже иногда жаловались на хабре, что они не так работают.</p> <p>Ну вот, теперь нет методов - нет проблем. <br /> В <code class="language-plaintext highlighter-rouge">$$</code> остались только те методы, которые мы считаем нужными. Аминь.</p> <p>P.S. Если у вас есть цикл по элементам коллекции:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">for</span> <span class="o">(</span><span class="nc">SelenideElement</span> <span class="n">el</span> <span class="o">:</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#selenide-users .user"</span><span class="o">))</span> <span class="o">{</span> <span class="n">el</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>то он перестанет компилироваться.</p> <p><strong>Быстрое решение</strong> - добавить к нему <code class="language-plaintext highlighter-rouge">asFixedIterable()</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">for</span> <span class="o">(</span><span class="nc">SelenideElement</span> <span class="n">el</span> <span class="o">:</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#selenide-users .user"</span><span class="o">).</span><span class="na">asFixedIterable</span><span class="o">())</span> <span class="o">{</span> <span class="n">el</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>Но <strong>правильное решение</strong> - переписать код и избавиться от цикла. Скорее всего вам стоит написать <a href="https://github.com/selenide/selenide/wiki/Custom-collection-conditions">свой collection condition</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2513">PR 2513</a>.</p> <p>UPD <a href="/2023/10/26/selenide-7.0.1/">С версии 7.0.1</a> снова компилируется.</p> <p><br /></p> <h3 id="replace-elements-container-by-container">Заменили <code class="language-plaintext highlighter-rouge">ElementsContainer</code> на <code class="language-plaintext highlighter-rouge">Container</code></h3> <p>Если вы вычленяли куски ваших пэдж обжектов в отдельные переиспользуемые компоненты (или “виджеты”), то они обычно наследовали класс <code class="language-plaintext highlighter-rouge">ElementsContainer</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">WeatherWidget</span> <span class="kd">extends</span> <span class="nc">ElementsContainer</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">id</span> <span class="o">=</span> <span class="s">"air"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">SelenideElement</span> <span class="n">airTemperature</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>Теперь надо будет заменить <code class="language-plaintext highlighter-rouge">ElementsContainer</code> на просто <code class="language-plaintext highlighter-rouge">Container</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">WeatherWidget</span> <span class="kd">implements</span> <span class="nc">Container</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">id</span> <span class="o">=</span> <span class="s">"air"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">SelenideElement</span> <span class="n">airTemperature</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>При этом пропадает метод <code class="language-plaintext highlighter-rouge">getSelf()</code>, который очень давно уже был <code class="language-plaintext highlighter-rouge">@Deprecated</code>. Но если он вам всё ещё нужен, в Selenide 7.0.1 ему изобрели замену в виде аннотации <code class="language-plaintext highlighter-rouge">@Self</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2523">PR 2523</a>.</p> <p><br /></p> <h3 id="replace-file-not-found-exception">Заменили <code class="language-plaintext highlighter-rouge">FileNotFoundException</code> на <code class="language-plaintext highlighter-rouge">FileNotDownloadedError</code></h3> <p>Раньше многие методы типа <code class="language-plaintext highlighter-rouge">$.download()</code> были объявлены как <code class="language-plaintext highlighter-rouge">throws FileNotFoundException</code>. И вам приходилось страдать, добавляя этот бессмысленный <code class="language-plaintext highlighter-rouge">throws FileNotFoundException</code> и в свои тесты. Теперь можно будет подчистить этот хлам и убрать ненужные <code class="language-plaintext highlighter-rouge">catch (FileNotFoundException)</code>, если они у вас были.</p> <p>Теперь методы типа <code class="language-plaintext highlighter-rouge">$.download()</code> будут кидать <code class="language-plaintext highlighter-rouge">FileNotDownloadedError</code>, который является <code class="language-plaintext highlighter-rouge">AssertionError</code>, и его не надо ни объявлять, ни ловить. Если тест не смог скачать файл - тест должен упасть. Никакой дополнительной обработки не требуется.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2526">PR 2526</a>.</p> <p><br /></p> <h3 id="rename-exceptions-to-errors">Переименовали некоторые <code class="language-plaintext highlighter-rouge">*Exception</code> классы в <code class="language-plaintext highlighter-rouge">*Error</code></h3> <p>Лично я не думаю, что это хоть сколько-нибудь важно. <br /> Но религия некоторых пользователей запрещает им называть класс <code class="language-plaintext highlighter-rouge">*Exception</code>, если он не наследует класс <code class="language-plaintext highlighter-rouge">java.lang.Exception</code>.</p> <p>Ну что ж, вы уважаем все религии. Теперь селенидовские классы, наследующие <code class="language-plaintext highlighter-rouge">java.lang.Error</code>, будут называться <code class="language-plaintext highlighter-rouge">*Error</code>.</p> <blockquote> <p>В Украине. Блогерка. Молдова. Беларусь. Таллинн.</p> </blockquote> <p>Нам несложно.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2485">issue 2485</a> и <a href="https://github.com/selenide/selenide/pull/2530">PR 2530</a>.</p> <p><br /></p> <h3 id="remove-deprecated-listener">Удалили поддержку устаревшего <code class="language-plaintext highlighter-rouge">WebDriverEventListener</code></h3> <p>Мы удалили все методы типа <code class="language-plaintext highlighter-rouge">addListener()</code> с параметром типа <code class="language-plaintext highlighter-rouge">org.openqa.selenium.support.events.WebDriverEventListener</code>.</p> <p>Этот класс уже давно был помечен как <code class="language-plaintext highlighter-rouge">@Deprecated</code> в селениуме.</p> <p>Используйте вместо него <code class="language-plaintext highlighter-rouge">addListener()</code> с параметром <code class="language-plaintext highlighter-rouge">org.openqa.selenium.support.events.WebDriverListener</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2516">PR 2516</a>.</p> <p><br /></p> <h3 id="remove-deprecated-drag-and-drop-methods">Удалили старые методы для Drag’n’drop</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">"#what"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="n">where</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">"#what"</span><span class="o">).</span><span class="na">dragAndDrop</span><span class="o">(</span><span class="n">to</span><span class="o">(</span><span class="n">where</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2519">PR 2519</a>.</p> <p><br /></p> <h3 id="remove-deprecated-get-selected-value-methods">Удалили устаревшие методы <code class="language-plaintext highlighter-rouge">$.getSelectedValue()</code> и <code class="language-plaintext highlighter-rouge">$.getSelectedText()</code></h3> <p>И ещё чистка.</p> <ol> <li>Замените <code class="language-plaintext highlighter-rouge">$.getSelectedValue()</code> на <code class="language-plaintext highlighter-rouge">$.getSelectedOptionValue()</code></li> <li>Замените <code class="language-plaintext highlighter-rouge">$.getSelectedText()</code> на <code class="language-plaintext highlighter-rouge">$.getSelectedOptionText()</code></li> </ol> <p>См. <a href="https://github.com/selenide/selenide/pull/2521">PR 2521</a>.</p> <p><br /></p> <h3 id="remove-deprecated-testng-annotations">Удалили старые аннотации для TestNG <code class="language-plaintext highlighter-rouge">@Report</code> и <code class="language-plaintext highlighter-rouge">@GlobalTextReport</code></h3> <p>Эти аннотации были не нужны, начиная с <a href="https://selenide.org/2022/08/04/selenide-6.7.0/#cancel-report-testng-annotation">Selenide 6.7.0</a>. Просто удалите их. Да и TestNG удалите, чего уж там. ;)</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2517">PR 2517</a>.</p> <p><br /></p> <h3 id="remove-other-deprecated-methods">Остальное</h3> <ol> <li>Замените <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nc">BearerTokenCredentials</span><span class="o">(</span><span class="s">"pwd"</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="k">new</span> <span class="nc">BearerTokenCredentials</span><span class="o">(</span><span class="s">"domain"</span><span class="o">,</span> <span class="s">"pwd"</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="err">$</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">appears</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">should</span><span class="o">(</span><span class="n">appear</span><span class="o">);</span> <span class="c1">// или просто $.shouldBe(visible)</span> </code></pre></div> </div> </li> <li>Замените <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="s">"username"</span><span class="o">,</span> <span class="s">"password"</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">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="k">new</span> <span class="nc">BasicAuthCredentials</span><span class="o">(</span><span class="s">"username"</span><span class="o">,</span> <span class="s">"password"</span><span class="o">));</span> </code></pre></div> </div> </li> </ol> <p>См. <a href="https://github.com/selenide/selenide/pull/2518">PR 2518</a>.</p> <p><br /></p> <p>Навстречу прогрессу!</p> <center> <img src="/images/2023/10/12-Product-Image.webp" width="300" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/10/25/selenide-7.0.0/ http://ru.selenide.org/2023/10/25/selenide-7.0.0 2023-10-25T00:00:00+00:00 Вышла Selenide 6.19.1 <p>Всем привет!</p> <p>Мир замер в ожидании, грядут большие изменения!<br /> Скоро будет Selenide 7.0.0 с обновлением на Java 11.</p> <blockquote> <p>Эпоха Java8-лямбдовых куколдов проходит</p> </blockquote> <p>А напоследок мы выкатили <strong>последнюю версию</strong>, которая может ещё бегать на Java 8:</p> <p><a href="https://github.com/selenide/selenide/milestone/193?closed=1">Selenide 6.19.1</a></p> <p>В основном там обновления зависимостей, в первую очередь Appium 8.6.0:</p> <ul> <li>bump Appium from 8.5.1 to 8.6.0 <a href="https://github.com/selenide/selenide/pull/2494">(#2494)</a></li> <li>Bump BrowserUpProxy <a href="https://github.com/selenide/selenide/pull/2510">(#2510)</a></li> <li>Bump Netty from 4.1.98.Final to 4.1.100.Final <a href="https://github.com/selenide/selenide/pull/2484">(#2484)</a> <a href="https://github.com/selenide/selenide/pull/2498">(#2498)</a></li> <li>Bump LittleProxy from 2.0.21 to 2.0.22 <a href="https://github.com/selenide/selenide/pull/2491">(#2491)</a></li> <li>Bump Guava from 32.1.2-jre to 32.1.3-jre <a href="https://github.com/selenide/selenide/pull/2499">(#2499)</a></li> <li>Bump Jackson from 2.15.2 to 2.15.3 <a href="https://github.com/selenide/selenide/pull/2502">(#2502)</a> <a href="https://github.com/selenide/selenide/pull/2503">(#2503)</a></li> <li>Bump commons-io from 2.13.0 to 2.14.0 <a href="https://github.com/selenide/selenide/pull/2486">(#2486)</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/10/18/selenide-6.19.1/ http://ru.selenide.org/2023/10/18/selenide-6.19.1 2023-10-18T00:00:00+00:00 Вышла Selenide 6.19.0 <p>Всем привет! На излёте сентября вы выкатили <a href="https://github.com/selenide/selenide/milestone/192?closed=1">Selenide 6.19.0</a>.</p> <blockquote> <p>Можешь сплетничать, ненавидеть, регистрировать баги, и обсуждать в чате. Но мы же оба понимаем, что при встрече ты будешь мне мило слать пулреквест.</p> </blockquote> <ul class="blogpost-menu"> <li><a href="#add-highlight-method">Новый метод <code class="language-plaintext highlighter-rouge">$.highlight()</code></a></li> <li><a href="#strip-invisible-spaces-in-collections">Убираем невидимые пробелы в текстах коллекций</a></li> <li><a href="#upgrade-to-selenium-4.13.0">Обновились на Selenium 4.13.0</a></li> <li><a href="#remove-hub-url-check">Убрали строгую проверку на “/wd/hub”</a></li> <li><a href="#replace-elements-container-by-container">Заменили <code class="language-plaintext highlighter-rouge">ElementsContainer</code> на <code class="language-plaintext highlighter-rouge">Container</code></a></li> <li><a href="#tap-double-tap">Методы <code class="language-plaintext highlighter-rouge">$.tap()</code>, <code class="language-plaintext highlighter-rouge">$.doubleTap()</code> для мобилок</a></li> <li><a href="#add-selector-by-class-and-index">Селектор по классу и индексу для мобилок</a></li> <li><a href="#news">Новости</a></li> </ul> <p><br /></p> <h3 id="add-highlight-method">Новый метод <code class="language-plaintext highlighter-rouge">$.highlight()</code> для подсветки элемента</h3> <p>Чувствую, открываем очередной ящик Пандоры, но что поделать.</p> <p>В общем, мы добавили метод для подсветки элемента.</p> <h4 id="зачем-это-надо">Зачем это надо?</h4> <p>Честно говоря, незачем. :)</p> <ul> <li>Когда у вас тесты автоматически бегают на CI, никто на них смотреть не должен, и подсвечивать ничего не нужно.</li> <li>Но вот если надо сделать крутое демо на конференции или просто показать коллегам - тут подсветка может пригодиться. Или, скажем, когда вы дебажите какой-то тест, сложный случай, когда непонятно, что куда кликается.</li> </ul> <h4 id="как-именно-он-подсвечивается">Как именно он подсвечивается?</h4> <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">HighlightOptions</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">highlight</span><span class="o">();</span> <span class="c1">// по умолчанию - background()</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">highlight</span><span class="o">(</span><span class="n">background</span><span class="o">());</span> <span class="c1">// цветной фон</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">highlight</span><span class="o">(</span><span class="n">border</span><span class="o">());</span> <span class="c1">// цветная рамка</span> </code></pre></div></div> <p>Фону и рамке можно задать свои стили:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">highlight</span><span class="o">(</span><span class="n">background</span><span class="o">(</span><span class="s">"rgb(85, 180, 250);"</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">highlight</span><span class="o">(</span><span class="n">border</span><span class="o">(</span><span class="s">"3px solid blue"</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">"h1"</span><span class="o">).</span><span class="na">highlight</span><span class="o">(</span><span class="n">style</span><span class="o">(</span><span class="s">"color: white; background-color: red;"</span><span class="o">));</span> </code></pre></div></div> <p><br /></p> <h4 id="дальнейшее-развитие">Дальнейшее развитие</h4> <p>Пока что метод подсвечивает только тот элемент, который вы скажете. В будущем, наверное, стоит добавить автоматическую подсветку каждого элемента, с которым происходит взаимодействие. Как раз такая подсветка позволит и демо эффектное сделать, и плавающую ошибку подебажить.</p> <p>В общем, делитесь идеями, будем думать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1277">issue 1277</a>, <a href="https://github.com/selenide/selenide/issues/2395">issue 2395</a> и <a href="https://github.com/selenide/selenide/pull/2481">PR 2481</a>.</p> <p><br /></p> <h3 id="strip-invisible-spaces-in-collections">Убираем невидимые пробелы в текстах коллекций</h3> <p>Как вы помните, в Selenide 6.16.0 мы <a href="/2023/07/02/selenide-6.16.0/#speedup-collection-checks">ускорили работу с коллекциями</a> с помощью вставок JavaScript кода. Оказалось, что при этом мы чуть изменили поведение для элементов, содержащих невидимые пробелы и прочие хитрые символы.</p> <p>Допустим, у нас на страничке есть такой элемент:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"status"</span><span class="nt">&gt;</span> <span class="nt">&lt;span&gt;</span> Боевой дух<span class="nt">&lt;/span&gt;</span> <span class="nt">&lt;span&gt;</span> ипучая<span class="ni">&amp;nbsp;</span>\u200Bдействительность <span class="nt">&lt;/span&gt;</span> <span class="nt">&lt;/div&gt;</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">"#status span"</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> </code></pre></div></div> <p>падала:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Expected</span> <span class="o">:[</span><span class="nc">Боевой</span> <span class="n">дух</span><span class="o">,</span> <span class="n">ипучая</span> <span class="n">действительность</span><span class="o">]</span> <span class="nc">Actual</span> <span class="o">:[</span><span class="nc">Боевой</span> <span class="n">дух</span><span class="o">,</span> <span class="n">ипучая</span> <span class="n">действительность</span><span class="o">]</span> </code></pre></div></div> <p>Причём выглядит сообщение так, как будто тексты одинаковые. Но в первой строке - обычный пробел, а во второй - невидимый пробел “<code class="language-plaintext highlighter-rouge">​</code>”.</p> <p>Теперь же селенид будет вычищать такие невидимые пробелы. Правильно это или нет, можно спорить, но так велит нам стандарт WebDriver, и так работает Selenium. И мы тоже будем соответствовать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2469">issue 2469</a> и <a href="https://github.com/selenide/selenide/pull/2482">PR 2482</a>.<br /> Спасибо <a href="https://github.com/Au6ojlut">Maksim Au6ojlut</a> за <a href="https://github.com/selenide/selenide/issues/2469#issuecomment-1721584046">подсказку с чёртовыми пробелами</a>.</p> <p><br /></p> <h3 id="upgrade-to-selenium-4.13.0">Обновились на Selenium 4.13.0</h3> <p>Обновили Selenium с 4.12.1 на 4.13.0<br /> Вот тут его <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог</a>.</p> <p>Несколько важных багфиксов, но всё ещё бегает на Java8. Ловите, пока он не стал требовать Java 11+! ;)</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2479">PR 2479</a>.</p> <p><br /></p> <h3 id="remove-hub-url-check">Убрали строгую проверку на “/wd/hub”</h3> <p>Если вы используете Selenoid, то раньше плагин <code class="language-plaintext highlighter-rouge">selenide-selenoid</code> проверял, чтобы URL заканчивался строкой <code class="language-plaintext highlighter-rouge">/wd/hub</code>. Оказалось, что URL бывает и другим, и поэтому мы эту проверку убрали.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2475">issue 2475</a>.<br /> Спасибо <a href="https://github.com/Plodick">Dmitry Plodukhin</a> за <a href="https://github.com/selenide/selenide/pull/2476">PR 2476</a>.</p> <p><br /></p> <h3 id="replace-elements-container-by-container">Заменили класс <code class="language-plaintext highlighter-rouge">ElementsContainer</code> на интерфейс <code class="language-plaintext highlighter-rouge">Container</code></h3> <p>Как вы помните, недавно мы <a href="/2023/09/06/selenide-6.18.0/#no-elements-container-for-page-object">запретили пэдж обжектам</a> наследоваться от <code class="language-plaintext highlighter-rouge">ElementsContainer</code>.</p> <p>Такой вариант больше не работает:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="kd">extends</span> <span class="nc">ElementsContainer</span> <span class="o">{</span> <span class="o">}</span> </code></pre></div></div> <p>Я по-прежнему настаиваю, что это не нужно, но нашлись такие проекты, где это необходимо из-за текущего наследия и невозможности всё переписать. Для таких проектов мы добавили интерфейс <code class="language-plaintext highlighter-rouge">Container</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="kd">implements</span> <span class="nc">Container</span> <span class="o">{</span> <span class="o">}</span> </code></pre></div></div> <p>Для обычных виджетов тоже рекомендуется поменять <code class="language-plaintext highlighter-rouge">extends ElementsContainer</code> на <code class="language-plaintext highlighter-rouge">implements Container</code>.</p> <p>Разница только в том, что это интерфейс, а не абстрактный класс (надо писать “implements” вместо “extends”), и в нём не будет метода <code class="language-plaintext highlighter-rouge">getSelf()</code>. Ну и не надо.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2439">issue 2439</a> и <a href="https://github.com/selenide/selenide/pull/2465">PR 2465</a>.</p> <p><br /></p> <h3 id="tap-double-tap">Добавили методы <code class="language-plaintext highlighter-rouge">$.tap()</code>, <code class="language-plaintext highlighter-rouge">$.doubleTap()</code> для мобилок</h3> <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">appium</span><span class="o">.</span><span class="na">SelenideAppium</span><span class="o">.</span><span class="err">$</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">"ComputeSumButton"</span><span class="o">)).</span><span class="na">tap</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="nc">AppiumBy</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"//android.widget.Button"</span><span class="o">)).</span><span class="na">doubleTap</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="nc">AppiumBy</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">".//*[@text='Лютая арта']"</span><span class="o">)).</span><span class="na">tap</span><span class="o">(</span><span class="n">longPressFor</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">4</span><span class="o">)));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/qwez">qwez</a> за <a href="https://github.com/selenide/selenide/pull/2467">PR 2467</a>.</p> <p><br /></p> <h3 id="add-selector-by-class-and-index">Добавили селектор по классу и индексу для мобилок</h3> <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">appium</span><span class="o">.</span><span class="na">AppiumSelectors</span><span class="o">.</span><span class="na">byClassNameAndIndex</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">appium</span><span class="o">.</span><span class="na">SelenideAppium</span><span class="o">.</span><span class="err">$</span><span class="o">;</span> <span class="err">$</span><span class="o">(</span><span class="n">byClassNameAndIndex</span><span class="o">(</span><span class="s">"android.widget.TextView"</span><span class="o">,</span> <span class="mi">7</span><span class="o">)).</span><span class="na">tap</span><span class="o">();</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide/pull/2440">PR 2440</a>.</p> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li><a href="https://github.com/titusfortner/selenium-logger">Selenium logger</a> - новый проект от Titus Fortner</li> <li><a href="https://www.baeldung.com/selenide">Introduction to Selenide</a> - Исторический момент! Пост на легендарном сайте Baeldung.</li> <li><a href="https://www.youtube.com/watch?v=C8rbEuvvg0I&amp;ab_channel=LambdaTest">Selenide appium</a> - Видос с последней конференции LambdaTest от Amuthan Sakthivel</li> <li><a href="https://intexsoft.com/blog/selenide-test-automation-using-selenoid-in-the-docker-container/">Selenide test automation: using Selenoid in docker</a> by Dora &amp; Margarita</li> <li><a href="https://zenn.dev/ragnar1904/articles/selenide-essentials">Things to keep in mind when using Selenide</a> - пост на японском от nilwurtz</li> <li><a href="https://updates4devs.com/intro-to-selenidebaeldung/?feed_id=28562">Intro to Selenide</a> By Updates4Devs.com</li> <li><a href="https://www.youtube.com/watch?v=FZA5PGxPzR0&amp;t=156s&amp;ab_channel=QAGURU">Non-static Selenide: пробуем жить с selenide-core и что это нам даст</a> - интересная нетривиальная тема от Дмитрий Тучс</li> <li><a href="https://www.linkedin.com/pulse/%2525D0%2525BF%2525D0%2525BE%2525D1%252587%2525D0%2525B5%2525D0%2525BC%2525D1%252583-%2525D0%2525BD%2525D0%2525B0%2525D1%252581%2525D0%2525BB%2525D0%2525B5%2525D0%2525B4%2525D0%2525BE%2525D0%2525B2%2525D0%2525B0%2525D0%2525BD%2525D0%2525B8%2525D0%2525B5-%2525D0%2525B2-%2525D0%2525B0%2525D0%2525B2%2525D1%252582%2525D0%2525BE%2525D1%252582%2525D0%2525B5%2525D1%252581%2525D1%252582%2525D0%2525B0%2525D1%252585-%2525D1%25258D%2525D1%252582%2525D0%2525BE-%2525D0%2525B0%2525D0%2525BD%2525D1%252582%2525D0%2525B8%2525D0%2525BF%2525D0%2525B0%2525D1%252582%2525D1%252582%2525D0%2525B5%2525D1%252580%2525D0%2525BD-ruslan-galliamov%3FtrackingId=klEJOeEYQ2K2mix0G2%252F9mQ%253D%253D/?trackingId=klEJOeEYQ2K2mix0G2%2F9mQ%3D%3D">Почему наследование в автотестах это антипаттерн?</a> - пост (не про Selenide) от Ruslan Galliamov</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/09/28/selenide-6.19.0/ http://ru.selenide.org/2023/09/28/selenide-6.19.0 2023-09-28T00:00:00+00:00 Вышла Selenide 6.18.0 <p>Все уже перевернули календарь?<br /> Тогда при свете костров рябин можете обновиться на <a href="https://github.com/selenide/selenide/milestone/190?closed=1">Selenide 6.18.0</a>.</p> <ul class="blogpost-menu"> <li><a href="#show-actual-texts-in-collections">Показываем актуальные тексты в коллекциях</a></li> <li><a href="#update-to-selenium-4.12.1">Обновили Selenium с 4.11.0 на 4.12.1</a></li> <li><a href="#restore-basic-auth-via-cdp">Вернули BasicAuth через DevTools</a></li> <li><a href="#add-method-get-options">Добавили метод <code class="language-plaintext highlighter-rouge">$$.getOptions()</code></a></li> <li><a href="#get-focused-element-lazy-loaded">Метод <code class="language-plaintext highlighter-rouge">getFocusedElement()</code> обленился</a></li> <li><a href="#support-appium-elements-in-page-objects">Поля <code class="language-plaintext highlighter-rouge">SelenideAppiumElement</code> в пэдж обжектах</a></li> <li><a href="#exact-scroll-in-appium">Точечный скроллинг в мобилках</a></li> <li><a href="#no-elements-container-for-page-object">Не наследовать пэдж обжект от <code class="language-plaintext highlighter-rouge">ElementsContainer</code></a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#statistics">Статистика</a></li> </ul> <p><br /></p> <h3 id="show-actual-texts-in-collections">Показываем актуальные тексты в коллекциях</h3> <p>В релизе 6.16.0 мы <a href="/2023/07/02/selenide-6.16.0/#speedup-collection-checks">ускорили проверки коллекций</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">".troubles"</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="s">"я твой враг"</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">List</span> <span class="n">size</span> <span class="nl">mismatch:</span> <span class="nl">expected:</span> <span class="o">=</span> <span class="mi">4</span><span class="o">,</span> <span class="nl">actual:</span> <span class="mi">3</span> </code></pre></div></div> <p>Но люди стали жаловаться, что хотелось бы увидеть, какие же там всё-таки были тексты. Похоже, это тот случай, когда удобство важнее скорости. А падают ваши тесты не так уж часто, ведь правда? ;)</p> <p>Поэтому с этого релиза будем показывать и размер, и тексты:</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="nf">mismatch</span> <span class="o">(</span><span class="nl">expected:</span> <span class="mi">4</span><span class="o">,</span> <span class="nl">actual:</span> <span class="mi">3</span><span class="o">)</span> <span class="nc">Actual</span> <span class="o">(</span><span class="mi">3</span><span class="o">):</span> <span class="o">[</span><span class="nc">Журавлей</span> <span class="n">белый</span> <span class="n">клин</span><span class="o">,</span> <span class="n">твоя</span> <span class="n">дочь</span><span class="o">,</span> <span class="n">и</span> <span class="n">мой</span> <span class="n">сын</span><span class="o">]</span> <span class="nc">Expected</span> <span class="o">(</span><span class="mi">4</span><span class="o">):</span> <span class="o">[</span><span class="nc">Всё</span> <span class="n">не</span> <span class="n">то</span><span class="o">,</span> <span class="n">всё</span> <span class="n">не</span> <span class="n">так</span><span class="o">,</span> <span class="n">ты</span> <span class="n">мой</span> <span class="n">друг</span><span class="o">,</span> <span class="n">я</span> <span class="n">твой</span> <span class="n">враг</span><span class="o">]</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2434">issue 2434</a> и <a href="https://github.com/selenide/selenide/pull/2456">PR 2456</a>.</p> <p><br /></p> <h3 id="update-to-selenium-4.12.1">Обновили Selenium с 4.11.0 на 4.12.1</h3> <p>Ченджлог <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">здесь</a>. Среди прочего, там исправили злополучный <a href="https://github.com/SeleniumHQ/selenium/issues/12576">дедлок в devtools</a>, из-за которого нам пришлось выпустить предыдущий релиз.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2452">PR 2452</a>.</p> <p><br /></p> <h3 id="restore-basic-auth-via-cdp">Вернули BasicAuth через DevTools</h3> <p>В релизе 6.16.0 мы запилили <a href="/2023/07/02/selenide-6.16.0/#improve-basic-auth-in-chromium-browsers">авторизацию BasicAuth через механизм <code class="language-plaintext highlighter-rouge">HasAuthentication</code> в браузерах семейства Chromium</a>.</p> <p>Потом нам пришлось её откатить из-за вышеупомянутого дедлока в вебдрайвере. Теперь селениум обновили, дедлок исправили, и мы вернули авторизацию взад.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2336">issue 2336</a>, <a href="https://github.com/selenide/selenide/pull/2358">PR 2358</a> и <a href="https://github.com/selenide/selenide/pull/2452">PR 2452</a>.</p> <p><br /></p> <h3 id="add-method-get-options">Добавили метод <code class="language-plaintext highlighter-rouge">$$.getOptions()</code></h3> <p>В селениде уже был метод <code class="language-plaintext highlighter-rouge">$$.getSelectedOptions()</code> - он возвращает <em>выбранные</em> опции элемента <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code>. А теперь мы добавили похожий метод <code class="language-plaintext highlighter-rouge">$$.getOptions()</code>, чтобы можно было проверить <em>все</em> его опции.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"select"</span><span class="o">).</span><span class="na">getOptions</span><span class="o">()</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="err">$</span><span class="o">(</span><span class="s">"select"</span><span class="o">).</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"день прощанья"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"select"</span><span class="o">).</span><span class="na">getSelectedOptions</span><span class="o">()</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> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2445">issue 2445</a> и <a href="https://github.com/selenide/selenide/pull/2446">PR 2446</a>.</p> <p><br /></p> <h3 id="get-focused-element-lazy-loaded">Метод <code class="language-plaintext highlighter-rouge">getFocusedElement()</code> обленился</h3> <p>Теперь метод <code class="language-plaintext highlighter-rouge">getFocusedElement()</code> возвращает <code class="language-plaintext highlighter-rouge">SelenideElement</code>, а не <code class="language-plaintext highlighter-rouge">WebElement</code>.</p> <p>Что автоматически означает ленивую загрузку и прочие плюшки.<br /> А ещё вы можете использовать для него стандартные should-проверки:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Selenide</span><span class="o">.</span><span class="na">getFocusedElement</span><span class="o">()</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">tagName</span><span class="o">(</span><span class="s">"input"</span><span class="o">),</span> <span class="n">id</span><span class="o">(</span><span class="s">"otpCode"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2454">PR 2454</a>.</p> <p><br /></p> <h3 id="support-appium-elements-in-page-objects">Добавили поддержку полей <code class="language-plaintext highlighter-rouge">SelenideAppiumElement</code> в пэдж обжектах</h3> <p>Если вы пишете тесты для мобилок с нашим плагином <code class="language-plaintext highlighter-rouge">selenide-appium</code>, то теперь вы можете объявлять поля пэдж обжекта с типом <code class="language-plaintext highlighter-rouge">SelenideAppiumElement</code>, вместо <code class="language-plaintext highlighter-rouge">SelenideElement</code>.</p> <p>Это даёт дополнительные методы для мобилок: <code class="language-plaintext highlighter-rouge">hideKeyboard()</code>, <code class="language-plaintext highlighter-rouge">swipe()</code> и т.п.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="o">{</span> <span class="nd">@AndroidFindBy</span><span class="o">(</span><span class="n">accessibility</span> <span class="o">=</span> <span class="s">"Username input field"</span><span class="o">)</span> <span class="nc">SelenideAppiumElement</span> <span class="n">login</span><span class="o">;</span> <span class="nd">@AndroidFindBy</span><span class="o">(</span><span class="n">accessibility</span> <span class="o">=</span> <span class="s">"Password input field"</span><span class="o">)</span> <span class="nc">SelenideElement</span> <span class="n">password</span><span class="o">;</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">login</span><span class="o">()</span> <span class="o">{</span> <span class="n">password</span><span class="o">.</span><span class="na">swipeTo</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/issues/2437">issue 2437</a> и <a href="https://github.com/selenide/selenide/pull/2438">PR 2438</a>.</p> <p><br /></p> <h3 id="exact-scroll-in-appium">Точечный скроллинг в мобилках</h3> <p>Продолжая тему мобилок. В плагине <code class="language-plaintext highlighter-rouge">selenide-appium</code> есть метод <code class="language-plaintext highlighter-rouge">scroll</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">appium</span><span class="o">.</span><span class="na">AppiumScrollOptions</span><span class="o">.</span><span class="na">with</span><span class="o">;</span> <span class="err">$</span><span class="o">.</span><span class="na">scroll</span><span class="o">(</span><span class="n">with</span><span class="o">(</span><span class="no">UP</span><span class="o">,</span> <span class="mi">20</span><span class="o">));</span> <span class="c1">// свайпать вверх, не больше 20 раз</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">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">appium</span><span class="o">.</span><span class="na">AppiumScrollOptions</span><span class="o">.</span><span class="na">up</span><span class="o">;</span> <span class="err">$</span><span class="o">.</span><span class="na">scroll</span><span class="o">(</span><span class="n">up</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="mi">80</span><span class="o">));</span> <span class="c1">// свайпать от точки 10% до точки 80%</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/qwez">qwez</a> за <a href="https://github.com/selenide/selenide/pull/2449">PR 2449</a>.</p> <p><br /></p> <h3 id="no-elements-container-for-page-object">Нельзя наследовать пэдж обжект от <code class="language-plaintext highlighter-rouge">ElementsContainer</code></h3> <p>Некоторые пользователи жаловались на такой вот странный ворнинг от селенида:</p> <blockquote> <p>WARN com.codeborne.selenide.impl.SelenidePageFactory - Cannot initialize field private com.codeborne.selenide.SelenideElement com.codeborne.selenide.ElementsContainer.self</p> </blockquote> <p>Мы начали исследовать тему и поняли, что это происходит, если вы наследуете пэдж обжект от <code class="language-plaintext highlighter-rouge">ElementsContainer</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="kd">extends</span> <span class="nc">ElementsContainer</span> <span class="o">{</span> <span class="o">}</span> </code></pre></div></div> <p>Что на самом деле <strong>не нужно</strong>. Собственно, теперь и не получится. :)</p> <p>Изначально <code class="language-plaintext highlighter-rouge">ElementsContainer</code> был задуман для того, чтобы объявлять компоненты <em>внутри</em> пэдж обжекта:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">id</span><span class="o">=</span><span class="s">"weather"</span><span class="o">)</span> <span class="nc">WeatherWidget</span> <span class="n">weatherWidget</span><span class="o">;</span> <span class="o">}</span> <span class="kd">class</span> <span class="nc">WeatherWidget</span> <span class="kd">extends</span> <span class="nc">ElementsContainer</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">id</span><span class="o">=</span><span class="s">"temperature"</span><span class="o">)</span> <span class="nc">SelenideElement</span> <span class="n">temperature</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>Просто уберите <code class="language-plaintext highlighter-rouge">extends ElementsContainer</code> из своего пэдж обжекта, и всё будет прекрасно работать.</p> <p>P.S. Дело в том, что это разные объекты с разной судьбой.</p> <ol> <li>Пэдж обжекты вы можете создавать методом <code class="language-plaintext highlighter-rouge">LoginPage page = Selenide.page();</code>. И для этого им <em>не требуется</em> наследоваться от <code class="language-plaintext highlighter-rouge">ElementsContainer</code>.</li> <li>А вот поля этих пэдж обжектов надо наследовать от <code class="language-plaintext highlighter-rouge">ElementsContainer</code>, чтобы селенид знал, что в них надо тоже искать аннотации <code class="language-plaintext highlighter-rouge">@FindBy</code>.</li> </ol> <p>См. <a href="https://github.com/selenide/selenide/issues/2439">issue 2439</a> и <a href="https://github.com/selenide/selenide/pull/2455">PR 2455</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>bump LittleProxy from 2.0.19 to 2.0.20 (fixes a memory leak in Selenide proxy)</li> <li>update vulnerable jackson dependency - см. <a href="https://github.com/selenide/selenide/pull/2442">PR 2442</a></li> </ul> <p><br /></p> <h3 id="statistics">Статистика</h3> <p>Количество ежемесячных скачиваний Селенида перевалило за 670 тыщ!</p> <center> <img src="/images/2023/09/selenide.downloads.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/2023/09/06/selenide-6.18.0/ http://ru.selenide.org/2023/09/06/selenide-6.18.0 2023-09-06T00:00:00+00:00 Вышла Selenide 6.17.2 <p>Добрий вечір!</p> <p>Сегодня 24 августа - День Независимости Украины.<br /> И в честь этого праздника - маленький релизец <a href="https://github.com/selenide/selenide/milestone/189?closed=1">Selenide 6.17.2</a>.</p> <p><br /></p> <h3 id="отвисли-зависающий-вебдрайвер-ещё-раз">Отвисли зависающий вебдрайвер ещё раз</h3> <p><a href="/2023/08/20/selenide-6.17.1/">В прошлом релизе</a> мы исправили проблему с зависающим вебдрайвером <em>для большинства пользователей</em>.<br /> Но остались несчастные, которые вынуждены открывать тестируемые сайты в Хроме через BasicAuth. И вот для них вебдрайвер по-прежнему зависал.</p> <blockquote> <p>Усилия по установлению потерянной с вебдрайвером связи<br /> не дали результатов.<br /> По предварительным данным, поток “CDP Connection” перешёл на нерасчётную орбиту и остановился, столкнувшись с зашаренным локом.<br /> Стактрейсы зависших потоков не поддаются визуальному опознанию.</p> </blockquote> <p>Что делать - пришлось пока откатить <a href="https://github.com/selenide/selenide/pull/2358">CDP-based authentication</a> до лучших времён, пока не исправится соответствующая <a href="https://github.com/SeleniumHQ/selenium/issues/12576">бага в вебдрайвере</a>.</p> <h3 id="как-повторить-проблему">Как повторить проблему</h3> <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">BasicAuthTest</span> <span class="o">{</span> <span class="nd">@RepeatedTest</span><span class="o">(</span><span class="mi">2</span><span class="o">)</span> <span class="kt">void</span> <span class="n">вот_этот_кусок_зависает_на_второй_попытке</span><span class="o">()</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://the-internet.herokuapp.com/basic_auth"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">"the-internet.herokuapp.com"</span><span class="o">,</span> <span class="s">"admin"</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">"#content"</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">"Congratulations!"</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Первый прогон срабатывает, второй - зависает. На Selenide 6.17.1.</p> <p>А на Selenide 6.17.2 больше не зависает. Ура.</p> <p><br /></p> <center> <img src="/images/2023/08/happy-independence-day-ukraine-vector-21712783.jpg" width="300" /> </center> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/08/24/selenide-6.17.2/ http://ru.selenide.org/2023/08/24/selenide-6.17.2 2023-08-24T00:00:00+00:00 Вышла Selenide 6.17.1 <p>Доброе утро!</p> <p>Сегодня 20 августа - <a href="https://www.visitestonia.com/ru/%D0%BF%D0%BE%D1%87%D0%B5%D0%BC%D1%83-%D1%8D%D1%81%D1%82%D0%BE%D0%BD%D0%B8%D1%8F/%D0%B4%D0%B5%D0%BD%D1%8C-%D0%B2%D0%BE%D1%81%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F-%D1%8D%D1%81%D1%82%D0%BE%D0%BD%D1%81%D0%BA%D0%BE%D0%B9-%D1%80%D0%B5%D1%81%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B8">День Восстановления Независимости Эстонии</a>.</p> <p>В честь этого праздника - небольшой релиз <a href="https://github.com/selenide/selenide/milestone/188?closed=1">Selenide 6.17.1</a>!</p> <ul> <li><a href="#fix-hanging-webdriver">Отвисли зависающий вебдрайвер</a></li> <li><a href="#support-type-in-mobile-apps">Добавили поддержку <code class="language-plaintext highlighter-rouge">$.type()</code> в мобилках</a></li> <li><a href="#fix-type-with-keys">Исправили метод <code class="language-plaintext highlighter-rouge">$.type(Keys.*)</code></a></li> <li><a href="#remove-spaces-from-texts">Убрали пробелы с концов в <code class="language-plaintext highlighter-rouge">$$.texts()</code></a> <p><br /></p> </li> </ul> <h3 id="fix-hanging-webdriver">Отвисли зависающий вебдрайвер</h3> <p>В релизе Selenide 6.16.0 (на самом деле <a href="https://github.com/selenide/selenide/pull/2358">PR 2358</a>), в селениде случайно появилась бага, из-за которой вебдрайвер мог легко зависнуть. А именно, если вызвать два раза подряд <code class="language-plaintext highlighter-rouge">open(url, pageClass)</code>, то вебдрайвер накрепко зависает.</p> <blockquote> <p>По предварительным данным, вебдрайвер перешел на нерасчётную орбиту и разбился, столкнувшись с дедлоком в параллельных потоках devtools.</p> </blockquote> <p>Вот такие ничоси. :)</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="n">вот_этот_кусок_зависает</span><span class="o">()</span> <span class="o">{</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"https://www.google.com/"</span><span class="o">,</span> <span class="nc">GooglePage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"https://www.google.com/"</span><span class="o">,</span> <span class="nc">GooglePage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>На самом деле это оказалась <a href="https://github.com/SeleniumHQ/selenium/issues/12576">бага в вебдрайвере</a>, она <a href="https://github.com/asolntsev/selenium-deadlock">легко повторяется</a> и с чистым селениумом. А изменения в Selenide 6.16.0 просто сделали её повторение более вероятным.</p> <p>В общем, проблему мы исправили для большинства пользователей. Но не для всех (грубо говоря, селенид просто старается не вызывать опасный код, пока для этого нет явной необходимости).<br /> А полного исправления придётся ждать на стороне ведрайвера.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2424">issue 2424</a> и <a href="https://github.com/selenide/selenide/pull/2428">PR 2428</a>.</p> <blockquote> <p>Огромное спасибо <a href="https://github.com/extryd">extryd</a> за чёткое описание проблемы с ультрапростыми инструкциями, как её стабильно повторить. Такое описание бага - неоценимая помощь мейнтейнерам. Все бы тикеты такие были!</p> </blockquote> <p><br /></p> <h3 id="support-type-in-mobile-apps">Добавили поддержку <code class="language-plaintext highlighter-rouge">$.type()</code> в мобилках</h3> <p>В предыдущем релизе появился метод</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">.</span><span class="na">type</span><span class="o">(</span><span class="s">"Биомеханоиды и трансгендеры"</span><span class="o">);</span> </code></pre></div></div> <p>который вбивает текст медленно, с задержками. Полезно для тестирования автозаполнения, поиска и т.п.</p> <p>Но оказалось, что он не работал в мобилках (<a href="https://github.com/selenide/selenide/tree/main/modules/appium">selenide-appium</a>). Теперь будет работать.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2191#issuecomment-1667578383">issue</a> и <a href="https://github.com/selenide/selenide/pull/2408">PR 2408</a>.</p> <p><br /></p> <h3 id="fix-type-with-keys">Исправили метод <code class="language-plaintext highlighter-rouge">$.type(Keys.*)</code></h3> <p>Вышеупомянутый метод <code class="language-plaintext highlighter-rouge">type</code> принимает не только строки, но и <code class="language-plaintext highlighter-rouge">CharSequence</code>. Что означает, что такой вариант тоже должен работать:</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.Keys</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">type</span><span class="o">(</span><span class="nc">Keys</span><span class="o">.</span><span class="na">ENTER</span><span class="o">);</span> </code></pre></div></div> <p>Вот только он ломался, потому что пытался закастить <code class="language-plaintext highlighter-rouge">Keys.ENTER</code> (который вообще-то <code class="language-plaintext highlighter-rouge">CharSequence</code>) к типу <code class="language-plaintext highlighter-rouge">String</code>. В общем, теперь этот вариант тоже будет работать (хотя польза от него неясна).</p> <p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за наблюдательность и <a href="https://github.com/selenide/selenide/issues/2422">issue 2422</a>.<br /> Фикс в <a href="https://github.com/selenide/selenide/pull/2421">PR 2421</a>.</p> <p><br /></p> <h3 id="remove-spaces-from-texts">Убрали пробелы с концов в <code class="language-plaintext highlighter-rouge">$$.texts()</code></h3> <p>В Selenide 6.16.0 мы <a href="/2023/07/02/selenide-6.16.0/#speedup-collection-checks">ускорили работу с коллекциями</a> с помощью вставок JavaScript кода. <br /> Но вечновнимательный <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> заметил, что при этом мы случайно поменяли поведение метода <code class="language-plaintext highlighter-rouge">$$.texts()</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">".burnees"</span><span class="o">).</span><span class="na">texts</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="nc">List</span><span class="o">.</span><span class="na">of</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">"Чонгарский мост"</span><span class="o">,</span> <span class="s">"Гениченский мост"</span> <span class="o">));</span> </code></pre></div></div> <p>А с 6.16.0 стал возвращать тексты элементов вместе с пробелами:</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">".burnees"</span><span class="o">).</span><span class="na">texts</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="nc">List</span><span class="o">.</span><span class="na">of</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">" Чонгарский мост "</span><span class="o">,</span> <span class="s">" Гениченский мост "</span> <span class="o">));</span> </code></pre></div></div> <p>Возможно, это спорный вопрос, как правильно, но во всяком случае так работает стандартный метод <code class="language-plaintext highlighter-rouge">WebElement.getText()</code>. Спорить не стали. Теперь <code class="language-plaintext highlighter-rouge">$$.texts()</code> снова обрезает пробелы в голове и хвосте.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2419">issue 2419</a> и <a href="https://github.com/selenide/selenide/pull/2427">PR 2427</a>.</p> <p><br /></p> <center> <img src="/images/2023/08/restoration-independence-taasiseseisvumispaev-estonia.png" width="300" /> </center> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/08/20/selenide-6.17.1/ http://ru.selenide.org/2023/08/20/selenide-6.17.1 2023-08-20T00:00:00+00:00 Вышла Selenide 6.17.0 <p>Привіт!</p> <p>Погнали оновлюватись на <a href="https://github.com/selenide/selenide/milestone/187?closed=1">Selenide 6.17.0</a>!</p> <ul> <li><a href="#update-to-selenium-4.11.0">Обновились на Selenium 4.11.0</a></li> <li><a href="#replace-webdrivermanager-by-selenium-manager">Заменили WebDriverManager на SeleniumManager</a></li> <li><a href="#support-java8-again">Вернули поддержку Java 8</a></li> <li><a href="#add-method-type">Добавили метод <code class="language-plaintext highlighter-rouge">$.type()</code></a></li> <li><a href="#click-with-long-press">Новый метод для мобилок: клик с долгим нажатием</a></li> <li><a href="#swipe-right-and-left">Новый метод для мобилок: свайп влево и вправо</a></li> <li><a href="#statistics">Статистика</a> <p><br /></p> </li> </ul> <h3 id="update-to-selenium-4.11.0">Обновились на Selenium 4.11.0</h3> <p>Мы обновили версию Selenium с 4.10.0 на 4.11.0.</p> <p>Полный список изменений в Selenium <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">тут</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2393">PR 2393</a>.</p> <p><br /></p> <h3 id="replace-webdrivermanager-by-selenium-manager">Заменили WebDriverManager на SeleniumManager</h3> <p>Начиная с версии 4.6.0, в Селениуме появился встроенный механизм для скачивания вебдрайверов - SeleniumManager. Насколько я понимаю, делает его всё тот же <a href="https://bonigarcia.dev/">Boni Garcia</a>, который создал и WebDriverManager. Только теперь он это делает официально как часть проекта Selenium.</p> <p>Подробнее про SeleniumManager <a href="https://www.selenium.dev/blog/2023/whats-new-in-selenium-manager-with-selenium-4.11.0/">тут</a>.</p> <p><strong>И теперь Selenide использует SeleniumManager вместо WebDriverManager.</strong></p> <ul> <li>Да, при этом нам пришлось удалить настройку <code class="language-plaintext highlighter-rouge">Configuration.driverManagerEnabled</code> и метод <code class="language-plaintext highlighter-rouge">DriverFactory.setupWebdriverBinary</code>. Возможно, у вас сломается компиляция.</li> <li>Да, немножко фигово, что мы сделали это <em>неожиданно</em>. Обычно мы не удаляем функционал сразу, а сначала помечаем его как <code class="language-plaintext highlighter-rouge">@Deprecated</code> и удаляем лишь в следующем мажорном релизе.</li> </ul> <p>Но у нас не осталось выбора.</p> <blockquote> <p>Давайте я покажу,<br /> откуда на WebDriverManager готовилось нападение?</p> </blockquote> <p>Начиная с версии 5.4.0, WDM начал требовать Java11+ и перестал работать с Chrome 115. А SeleniumManager и на Java 8 работает, и Chrome 115 поддерживает. И работает из коробки. <em>Что нам ещё оставалось?</em></p> <p>См. <a href="https://github.com/selenide/selenide/issues/2402">issue 2402</a> и <a href="https://github.com/selenide/selenide/pull/2400">PR 2400</a>.</p> <p><br /></p> <p>P.S. Если вы всё-таки очень хотите использовать WebDriverManager, не проблема: просто добавьте его в свои зависимости и вызывайте явно в начале тестов:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">WebDriverManager</span><span class="o">.</span><span class="na">chromedriver</span><span class="o">().</span><span class="na">setup</span><span class="o">();</span> <span class="nc">WebDriverManager</span><span class="o">.</span><span class="na">firefoxdriver</span><span class="o">().</span><span class="na">setup</span><span class="o">();</span> <span class="nc">WebDriverManager</span><span class="o">.</span><span class="na">edgedriver</span><span class="o">().</span><span class="na">setup</span><span class="o">();</span> <span class="n">etc</span><span class="o">.</span> </code></pre></div></div> <p><br /></p> <h3 id="support-java8-again">Вернули поддержку Java 8</h3> <p>В предыдущем релизе Selenide случайно стал требовать как минимум Java 11. Случилось это как раз из-за обновления WebDriverManager, который внезапно стал требовать Java 11.</p> <p>А теперь: нет WebDriverManager - нет проблемы. :)</p> <p>Олды, можете снова запускать свои тесты на Java 8.</p> <blockquote> <p>Тем не менее очень советую обновляться на Java 11 (а ещё лучше - на Java 17), потому что осенью Selenium перейдёт на Java 11. И тогда у вас точно не останется выбора.</p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/issues/2385">issue 2385</a> и <a href="https://github.com/selenide/selenide/pull/2400">PR 2400</a>.</p> <p><br /></p> <h3 id="add-method-type">Добавили метод <code class="language-plaintext highlighter-rouge">$.type()</code></h3> <p>Эту фичу очень давно просили. И вот наконец!</p> <p><code class="language-plaintext highlighter-rouge">$.type()</code> - это то же, что <code class="language-plaintext highlighter-rouge">$.sendKeys()</code>, но с задержками. Типа эмуляция того, как реальный пользователь вводит текст.<br /> Может быть полезно, чтобы сработали автоматические подсказки, автозамена или ещё что-нибудь подобное.</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">TypeOptions</span><span class="o">.*;</span> <span class="err">$</span><span class="o">(</span><span class="s">"#query"</span><span class="o">).</span><span class="na">type</span><span class="o">(</span><span class="s">"Who the fuck is yevgeny prigozhin"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#query"</span><span class="o">).</span><span class="na">type</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Who the fuck is dmitry utkin"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#query"</span><span class="o">).</span><span class="na">type</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Who the fuck is lvova-belova"</span><span class="o">).</span><span class="na">withDelay</span><span class="o">(</span><span class="n">ofMillis</span><span class="o">(</span><span class="mi">100</span><span class="o">)));</span> </code></pre></div></div> <p>По умолчанию задержка между “нажатиями клавиш” - 200 миллисекунд.</p> <p>Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide/pull/2191">PR 2191</a>.</p> <p><br /></p> <h3 id="click-with-long-press">Новый метод для мобилок: клик с долгим нажатием</h3> <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">appium</span><span class="o">.</span><span class="na">SelenideAppium</span><span class="o">.</span><span class="err">$</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">appium</span><span class="o">.</span><span class="na">AppiumClickOptions</span><span class="o">.*;</span> <span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">longPress</span><span class="o">());</span> <span class="c1">// by default 3 seconds</span> <span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">longPressFor</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">4</span><span class="o">)));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2370">issue 2370</a>. Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide/pull/2381">PR 2381</a>.</p> <p><br /></p> <h3 id="swipe-right-and-left">Новый метод для мобилок: свайп влево и вправо</h3> <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">appium</span><span class="o">.</span><span class="na">SelenideAppium</span><span class="o">.</span><span class="err">$</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">appium</span><span class="o">.</span><span class="na">AppiumSwipeOptions</span><span class="o">.*;</span> <span class="err">$</span><span class="o">.</span><span class="na">swipeTo</span><span class="o">();</span> <span class="c1">// by default swipes to the right</span> <span class="err">$</span><span class="o">.</span><span class="na">swipe</span><span class="o">(</span><span class="n">right</span><span class="o">());</span> <span class="err">$</span><span class="o">.</span><span class="na">swipe</span><span class="o">(</span><span class="n">right</span><span class="o">(</span><span class="mi">20</span><span class="o">));</span> <span class="err">$</span><span class="o">.</span><span class="na">swipe</span><span class="o">(</span><span class="n">left</span><span class="o">());</span> <span class="err">$</span><span class="o">.</span><span class="na">swipe</span><span class="o">(</span><span class="n">left</span><span class="o">(</span><span class="mi">30</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide/pull/2369">PR 2369</a>.</p> <p><br /></p> <p>В общем, обновляйтесь, пробуйте, делитесь:</p> <blockquote> <p>как вам SeleniumManager?<br /> Всё вообще сломалось, или можно жить?</p> </blockquote> <p><br /></p> <h3 id="statistics">Статистика</h3> <p>Количество ежемесячных скачиваний Селенида перевалило за 649 тыщ!</p> <center> <img src="/images/2023/07/selenide.downloads.png" width="800" /> </center> <p>(Предыдущий рекорд был в марте: 602 тыщи)</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/08/02/selenide-6.17.0/ http://ru.selenide.org/2023/08/02/selenide-6.17.0 2023-08-02T00:00:00+00:00 Вышла Selenide 6.16.1 <p>Привет, пипл!</p> <p>Мы выпустили небольшое обновление <a href="https://github.com/selenide/selenide/milestone/186?closed=1">Selenide 6.16.1</a>.</p> <ul> <li><a href="#all-of-and-any-of-conditions">Добавили условия <code class="language-plaintext highlighter-rouge">allOf</code> и <code class="language-plaintext highlighter-rouge">anyOf</code></a></li> <li><a href="#add-checks-for-cookies">Добавили проверки для cookies</a></li> <li><a href="#update-webdrivermanager">Обновили WebDriverManager</a></li> <li><a href="#support-tspan-in-svg">Исправили проверку текстов <code class="language-plaintext highlighter-rouge">&lt;tspan&gt;</code> в SVG</a> <p><br /></p> </li> </ul> <h3 id="all-of-and-any-of-conditions">Добавили условия <code class="language-plaintext highlighter-rouge">allOf</code> и <code class="language-plaintext highlighter-rouge">anyOf</code></h3> <p>Теперь можно проверить одно из нескольких условий (OR):</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">"#president"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">anyOf</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> </code></pre></div></div> <p>(на самом деле для этого и раньше был метод <code class="language-plaintext highlighter-rouge">Condition.or</code>, но <code class="language-plaintext highlighter-rouge">anyOf</code> как будто чуть удобнее).</p> <p>Также можно проверить совпадение всех условий сразу (AND):</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">"#generals"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">allOf</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> </code></pre></div></div> <p>(на самом деле это то же самое, что и <code class="language-plaintext highlighter-rouge">$("#generals").shouldHave(text("бездарней"), text("ничтожней"))</code>, но зато результат <code class="language-plaintext highlighter-rouge">allOf</code> можно вынести в переменную или метод, создав таким образом своё условие).</p> <p>Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/2368">PR 2368</a>.</p> <p><br /></p> <h3 id="add-checks-for-cookies">Добавили проверки для cookies</h3> <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="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cookie</span><span class="o">(</span><span class="s">"session_id"</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">cookie</span><span class="o">(</span><span class="s">"session_id"</span><span class="o">,</span> <span class="s">"1234567890"</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">cookie</span><span class="o">(</span><span class="s">"session_id"</span><span class="o">,</span> <span class="s">"1234567890"</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">10</span><span class="o">)));</span> </code></pre></div></div> <p>С ожиданием результата, кастомным таймаутом и скриншотами при падении - всё, как вы любите.</p> <p>Спасибо <a href="https://github.com/adorne">adorne</a> за <a href="https://github.com/selenide/selenide/pull/2367">PR 2367</a>.</p> <p><br /></p> <h3 id="update-webdrivermanager">Обновили WebDriverManager с 5.4.0 до 5.4.1</h3> <p>Среди прочего, там добавлена поддержка Chrome 115, с которым у многих возникли проблемы.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2374">PR 2374</a>.</p> <p>P.S. На маках Chrome 115 в данный момент не работает. Выскакивает такая ошибка:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SessionNotCreatedException: Could not start a new session. Response code 500. Message: unknown error: cannot find Chrome binary </code></pre></div></div> <p>Но мы не виноваты, это <a href="https://github.com/GoogleChromeLabs/chrome-for-testing/issues/30">бага в chromedriver</a>. Обещали скоро исправить.</p> <p><br /></p> <h3 id="support-tspan-in-svg">Исправили проверку текстов <code class="language-plaintext highlighter-rouge">&lt;tspan&gt;</code> в SVG</h3> <p>Вы когда-нибудь проверяли тексты внутри SVG? Я тоже нет. А они там, оказывается, есть!</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">&lt;svg&gt;</span> <span class="nt">&lt;text</span> <span class="na">x=</span><span class="s">"10"</span> <span class="na">y=</span><span class="s">"30"</span> <span class="na">class=</span><span class="s">"small"</span><span class="nt">&gt;</span> Люди хотят <span class="nt">&lt;tspan&gt;</span>благодарности<span class="nt">&lt;/tspan&gt;</span>. <span class="nt">&lt;/text&gt;</span> <span class="nt">&lt;text</span> <span class="na">x=</span><span class="s">"10"</span> <span class="na">y=</span><span class="s">"60"</span> <span class="na">class=</span><span class="s">"small"</span><span class="nt">&gt;</span> Мы не склад <span class="nt">&lt;tspan&gt;</span>Amazon<span class="nt">&lt;/tspan&gt;</span>. <span class="nt">&lt;/text&gt;</span> <span class="nt">&lt;text</span> <span class="na">x=</span><span class="s">"10"</span> <span class="na">y=</span><span class="s">"90"</span> <span class="na">class=</span><span class="s">"small"</span><span class="nt">&gt;</span> Спасибо тебе, <span class="nt">&lt;tspan&gt;</span>Бен Уоллес<span class="nt">&lt;/tspan&gt;</span>! <span class="nt">&lt;/text&gt;</span> <span class="nt">&lt;/svg&gt;</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">"svg tspan"</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">"Amazon"</span><span class="o">,</span> <span class="s">"Бен Уоллес"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2377">issue 2377</a> и <a href="https://github.com/selenide/selenide/pull/2379">PR 2379</a>.</p> <p><br /> <br /></p> <center> <img src="/images/2023/07/ben-wallace.png" width="600" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/07/24/selenide-6.16.1/ http://ru.selenide.org/2023/07/24/selenide-6.16.1 2023-07-24T00:00:00+00:00 Вышла Selenide 6.16.0 <p>Доброй ночи!</p> <blockquote> <p>У меня к вам предложение, от которого лучше не отказываться.</p> </blockquote> <p>Обновляйтесь на <a href="https://github.com/selenide/selenide/milestone/185?closed=1">Selenide 6.16.0</a>!</p> <ul> <li><a href="#speedup-collection-checks">Ускорили проверки коллекций</a></li> <li><a href="#improve-collections-error-messages">Улучшили сообщение об ошибках в коллекциях</a></li> <li><a href="#new-date-checks">Новые проверки для дат <code class="language-plaintext highlighter-rouge">date(...)</code> и <code class="language-plaintext highlighter-rouge">datetime(...)</code></a></li> <li><a href="#set-date-time-value">Новые методы для установки времени</a></li> <li><a href="#full-stacktrace-in-soft-asserts">Полный стектрейс в софт ассертах</a></li> <li><a href="#improve-basic-auth-in-chromium-browsers">BasicAuth в браузерах Chromium</a></li> <li><a href="#multiple-domains-in-basic-auth">Несколько доменов в BasicAuth</a></li> <li><a href="#fix-double-click-in-appium">Исправили <code class="language-plaintext highlighter-rouge">$.doubleClick()</code> в Appium</a></li> <li><a href="#screenshot-before-after-each">Снимаем скриншот чуть раньше</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#news">Новости</a> <p><br /></p> </li> </ul> <h3 id="speedup-collection-checks">Ускорили проверки коллекций</h3> <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">"option"</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="s">"Шлёпкино"</span><span class="o">))</span> </code></pre></div></div> <p>Однако <strong>такая проверка занимает время</strong>, если коллекция достаточно большая.</p> <p>Например, если на странице 1000 элементов <code class="language-plaintext highlighter-rouge">&lt;option&gt;</code>, на моём компе она занимает почти 3 секунды.<br /> Медленность обусловлена тем, что Селенид спрашивает у браузера текст каждого элемента по отдельности. 1000 вызовов вебдрайвера, даже очень быстрых - это долго.</p> <p>Пришло время ускорить эти проверки.</p> <p>Теперь Селенид запрашивает тексты всех 1000 элементов <strong>за один вызов вебдрайвера</strong> с помощью JavaScript.</p> <blockquote> <p>Теперь та проверка вместо 3 секунд занимает 200 мс.<br /> В 15 раз быстрее.<br /> <em>Ну очуметь же!</em></p> </blockquote> <p>P.S. Для Appium и других возможных вебдрайверов, которые не поддерживают JS, остаётся старый вариант (который запрашивает тексты по одному). Что поделать. Хотя бы не стало хуже.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2362">PR 2362</a>.</p> <p><br /></p> <h3 id="improve-collections-error-messages">Улучшили сообщение об ошибках в коллекциях</h3> <p>В рамках предыдущей переделки проверок для коллекций мы чутка дополнили и сообщения об ошибках. Теперь Селенид точнее говорит, <em>какой именно элемент коллекции не совпал</em>.</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">".direction"</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> </code></pre></div></div> <p>Раньше выдавала просто <code class="language-plaintext highlighter-rouge">"Texts mismatch"</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Texts</span> <span class="n">mismatch</span> <span class="nl">Actual:</span> <span class="o">[</span><span class="nc">На</span> <span class="nc">Ростов</span><span class="o">,</span> <span class="nc">На</span> <span class="nc">Москву</span><span class="o">,</span> <span class="no">В</span> <span class="nc">Беларусь</span><span class="o">]</span> <span class="nl">Expected:</span> <span class="o">[</span><span class="nc">На</span> <span class="nc">Ростов</span><span class="o">,</span> <span class="nc">На</span> <span class="nc">Москву</span><span class="o">,</span> <span class="no">В</span> <span class="n">полевые</span> <span class="n">лагеря</span> <span class="n">согласно</span> <span class="n">плану</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">Text</span> <span class="err">#</span><span class="mi">2</span> <span class="n">mismatch</span> <span class="o">(</span><span class="nl">expected:</span> <span class="s">"В полевые лагеря согласно плану"</span><span class="o">,</span> <span class="nl">actual:</span> <span class="s">"В Беларусь"</span><span class="o">)</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2362">PR 2362</a>.</p> <p><br /></p> <h3 id="new-date-checks">Новые проверки для дат <code class="language-plaintext highlighter-rouge">date(...)</code> и <code class="language-plaintext highlighter-rouge">datetime(...)</code></h3> <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">conditions</span><span class="o">.</span><span class="na">datetime</span><span class="o">.</span><span class="na">DateConditionOptions</span><span class="o">.*;</span> <span class="nc">LocalDate</span> <span class="n">theDay</span> <span class="o">=</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="mi">2023</span><span class="o">,</span> <span class="mi">6</span><span class="o">,</span> <span class="mi">23</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#funParty"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">date</span><span class="o">(</span><span class="n">theDay</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#funParty"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">date</span><span class="o">(</span><span class="n">theDay</span><span class="o">,</span> <span class="s">"yyyy-MM-dd"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#funParty"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">dateBetween</span><span class="o">(</span><span class="n">parse</span><span class="o">(</span><span class="s">"2023-06-22"</span><span class="o">),</span> <span class="n">parse</span><span class="o">(</span><span class="s">"2023-06-24"</span><span class="o">)));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#funParty"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">dateFormat</span><span class="o">(</span><span class="s">"yyyy-MM-dd"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2268">issue 2268</a>. Спасибо <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> за <a href="https://github.com/selenide/selenide/pull/2281">PR 2281</a>.</p> <p><br /></p> <h3 id="set-date-time-value">Новые методы для установки времени</h3> <p>В селениде уже был метод для установки даты (<code class="language-plaintext highlighter-rouge">withDate</code>):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">LocalDate</span> <span class="n">theDay</span> <span class="o">=</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="mi">2023</span><span class="o">,</span> <span class="mi">6</span><span class="o">,</span> <span class="mi">23</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#euphoriaDay"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withDate</span><span class="o">(</span><span class="n">theDay</span><span class="o">));</span> <span class="c1">// для &lt;input type="date"&gt;</span> </code></pre></div></div> <p>Теперь же мы добавили похожие методы для установки даты с временем (<code class="language-plaintext highlighter-rouge">withDateTime</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">"#epicFail"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withDateTime</span><span class="o">(</span><span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"2023-06-23 20:29"</span><span class="o">)));</span> <span class="c1">// для &lt;input type="datetime-local"&gt;</span> </code></pre></div></div> <p>либо только времени (<code class="language-plaintext highlighter-rouge">withTime</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">"#rostov"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withTime</span><span class="o">(</span><span class="nc">LocalTime</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"07:00"</span><span class="o">)));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#voronezh"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withTime</span><span class="o">(</span><span class="nc">LocalTime</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"14:00"</span><span class="o">)));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#lipetsk"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withTime</span><span class="o">(</span><span class="nc">LocalTime</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"16:00"</span><span class="o">)));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#moskva-reka"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withTime</span><span class="o">(</span><span class="nc">LocalTime</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"20:20"</span><span class="o">)));</span> <span class="c1">// для &lt;input type="time"&gt;</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2357">PR 2357</a>.</p> <p><br /></p> <h3 id="full-stacktrace-in-soft-asserts">Полный стектрейс в софт ассертах</h3> <p>В Селениде уже давно есть софт ассерты. Когда вы включаете этот режим, при падении нескольких проверок Селенид кидает ошибку не сразу, а лишь в конце теста. И в этой ошибке перечисляются все Assertion error.</p> <p>Вопрос в том, что для этих ошибок Селенид показывал <strong>неполный стектрейс</strong>, и там не была видна часть “Caused by:”. Теперь будет видна. Будьте готовы к тому, что длина стектрейсов резко вырастет :).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2350">issue 2350</a> и <a href="https://github.com/selenide/selenide/pull/2354">PR 2354</a>.</p> <p><br /></p> <h3 id="improve-basic-auth-in-chromium-browsers">Улучшили BasicAuth в браузерах семейства Chromium</h3> <p>В Селениде есть методы для авторизации на странице через BasicAuth:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="s">"admin"</span><span class="o">,</span> <span class="s">"Казкова діброва"</span><span class="o">);</span> </code></pre></div></div> <p>До сих пор было две реализации BasicAuth.</p> <ol> <li>При включённом прокси: Селенид добавляет к запросу заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> (<code class="language-plaintext highlighter-rouge">Basic YWRtaW460JrQsNC30LrQvtCy0LAg0LTRltCx0YDQvtCy0LA=</code>).</li> <li>При выключенном прокси: Селенид добавляет логин и пароль в URL (<code class="language-plaintext highlighter-rouge">https://admin:Казкова+діброва@localhost:42220/basic-auth/hello</code>).</li> </ol> <p>Теперь к ним добавился третий вариант: Если вебдрайвер реализует интерфейс <code class="language-plaintext highlighter-rouge">org.openqa.selenium.HasAuthentication</code>, Селенид добавляет логин+пароль через него. Фактически это означает браузеры семейства Chromium (Chrome, Chromium, Edge).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2336">issue 2336</a> и <a href="https://github.com/selenide/selenide/pull/2358">PR 2358</a>.</p> <p><br /></p> <h3 id="multiple-domains-in-basic-auth">Несколько доменов в BasicAuth</h3> <p>До сих пор можно было прописать только один домен для BasicAuth:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="s">"mister-cider.ru"</span><span class="o">,</span> <span class="s">"luka"</span><span class="o">,</span> <span class="s">"картофельный спас"</span><span class="o">);</span> </code></pre></div></div> <p>Это не помогает, если в тестируемом приложении задействовано несколько доменов - например, оно открывает фреймы с адресом <code class="language-plaintext highlighter-rouge">auth.mister-cider.ru</code> или там <code class="language-plaintext highlighter-rouge">api.mister-cider.ru</code>. Всегда можно прописать пустую строку вместо домена, но у этого варианта есть <a href="/2022/10/07/selenide-6.9.0/#secure-authorization-header">очевидные минусы</a>.</p> <p>Теперь можно прописать несколько доменов через запятую или “|”:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="s">"mister-cider.ru|auth.mister-cider.ru|api.mister-cider.ru"</span><span class="o">,</span> <span class="s">"luka"</span><span class="o">,</span> <span class="s">"картофельный спас"</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2358">PR 2358</a>.</p> <p>P.S. Естественно, имя домена не используется, когда Селенид добавляет логин+пароль просто в URL.</p> <p><br /></p> <h3 id="fix-double-click-in-appium">Исправили <code class="language-plaintext highlighter-rouge">$.doubleClick()</code> в Appium</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="nc">AppiumBy</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">".//android.widget.CheckBox"</span><span class="o">)).</span><span class="na">doubleClick</span><span class="o">();</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2346">issue 2346</a> и <a href="https://github.com/selenide/selenide/pull/2347">PR 2347</a>.</p> <p><br /></p> <h3 id="screenshot-before-after-each">Снимаем скриншот чуть раньше</h3> <p>Обычно Селенид автоматически делает скриншот в случае падения теста. НО Если вы закрывали браузер в методе <code class="language-plaintext highlighter-rouge">@AfterEach</code>, то Селенид уже не мог сделать скриншот:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@AfterEach</span> <span class="kt">void</span> <span class="nf">tearDown</span><span class="o">()</span> <span class="o">{</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">closeWebDriver</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <p>Теперь Селенид будет делать скриншот чуть раньше - ДО методов <code class="language-plaintext highlighter-rouge">@AfterEach</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2352">issue 2352</a> и <a href="https://github.com/selenide/selenide/pull/2356">PR 2356</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li><a href="https://github.com/selenide/selenide/pull/2318">2319</a> use latest version of geckodriver for FF 102+</li> <li><a href="https://github.com/selenide/selenide/pull/2328">2328</a> bump Selenium from 4.9.1 to 4.10.0</li> <li><a href="https://github.com/selenide/selenide/pull/2324">2324</a> Bump io.appium:java-client from 8.5.0 to 8.5.1</li> <li><a href="https://github.com/selenide/selenide/pull/2349">2349</a> Bump Netty from 4.1.93.Final to 4.1.94.Final</li> <li>bump WebDriverManager from 5.3.3 to 5.4.0</li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Пост <a href="https://www.it-notes.wiki/automation-testing/page-object-simple-example-using-selenide/">Page Object – простой пример с использованием Selenide</a> на IT-notes</li> <li>Видос <a href="https://www.youtube.com/watch?v=zAhCLLX9IOc&amp;ab_channel=TestingMiniBytes">Run Selenide Appium Test in LambdaTest</a> в блоге “Testing Mini Bytes”</li> </ul> <p><br /></p> <blockquote> <p>Ну что, нормальный результат, я считаю.<br /> ВСЕХ ВЗБОДРИЛИ!!!<br /> Счастливо!</p> </blockquote> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/07/02/selenide-6.16.0/ http://ru.selenide.org/2023/07/02/selenide-6.16.0 2023-07-02T00:00:00+00:00 Вышла Selenide 6.15.0 <p>Доброй ночи!</p> <blockquote> <p>Есть люди, которые постоянно деплоят, а есть люди, которые один раз в жизни где-то выучили, что должен быть РЕЛИЗ — и копят, копят, копят фичи для этого релиза. <br /> А счастливый дедушка-мейнтейнер думает, что ему хорошо.<br /> И как использовать опенсорс-проект, если вдруг случайно окажется, что этот дедушка — законченный мудак?</p> </blockquote> <p>Как вы поняли, мы выпустили новую версию <a href="https://github.com/selenide/selenide/milestone/184?closed=1">Selenide 6.15.0</a> с серьёзными такими внутренними переделками.</p> <ul> <li><a href="#merge-selenide-selenoid-to-selenide">Влили проект <code class="language-plaintext highlighter-rouge">selenide-selenoid</code> в <code class="language-plaintext highlighter-rouge">selenide</code></a></li> <li><a href="#merge-selenide-appium-to-selenide">Влили проект <code class="language-plaintext highlighter-rouge">selenide-appium</code> в <code class="language-plaintext highlighter-rouge">selenide</code></a></li> <li><a href="#clicking-disable-element-fails">Клик теперь проверяет, что элемент не disabled</a></li> <li><a href="#escape-newlines-in-report">Маскируем переводы строк в отчёте</a></li> <li><a href="#combined-selectors-for-mobile-apps">Комбинированные селекторы для мобилок</a></li> <li><a href="#conditions-for-mobile-apps">Новые проверки для мобилок</a></li> <li><a href="#new-selectors-for-mobile-apps">Новые селекторы для мобилок</a></li> <li><a href="#switch-context-in-mobile-apps">Переключение контекста в мобилках</a></li> <li><a href="#refactor-collection-conditions">Переколбас CollectionCondition</a></li> <li><a href="#news">Новости</a> <p><br /></p> </li> </ul> <h3 id="merge-selenide-selenoid-to-selenide">Влили проект <code class="language-plaintext highlighter-rouge">selenide-selenoid</code> в <code class="language-plaintext highlighter-rouge">selenide</code></h3> <p>Это позволит нам легче менять и релизить эти проекты. Для вас поменяется объявление зависимости. Вместо</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">testImplementation</span><span class="o">(</span><span class="s2">"org.selenide:selenide-selenoid:2.3.7"</span><span class="o">)</span> </code></pre></div></div> <p>Теперь нужно будет прописать:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">testImplementation</span><span class="o">(</span><span class="s2">"com.codeborne:selenide-selenoid:6.15.0"</span><span class="o">)</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/2292">PR 2292</a>.</p> <p><br /></p> <h3 id="merge-selenide-appium-to-selenide">Влили проект <code class="language-plaintext highlighter-rouge">selenide-appium</code> в <code class="language-plaintext highlighter-rouge">selenide</code></h3> <p>Это позволит нам легче менять и релизить эти проекты. Вам всего лишь нужно будет обновить версию <code class="language-plaintext highlighter-rouge">selenide-appium</code> с <code class="language-plaintext highlighter-rouge">2.8.1</code> на <code class="language-plaintext highlighter-rouge">6.15.0</code></p> <p>См. <a href="https://github.com/selenide/selenide/pull/2291">PR 2291</a>.</p> <p><br /></p> <h3 id="clicking-disable-element-fails">Клик теперь проверяет, что элемент не disabled</h3> <p>Как вы знаете, метод <code class="language-plaintext highlighter-rouge">$.click()</code> требует, чтобы элемент был видимым (более точно: либо элемент должен быть видимый, либо иметь css свойство <code class="language-plaintext highlighter-rouge">opacity: 0</code>).</p> <p>НО до сих пор Селенид не проверял, что элемент enabled. Другими словами,</p> <blockquote> <p>Селенид разрешал кликать на <code class="language-plaintext highlighter-rouge">disabled</code> элементы.</p> </blockquote> <ol> <li><strong>В пользу</strong> этого решения был такой аргумент: а вдруг автор теста и хочет кликнуть на задизейбленый элемент и убедиться, что ничего не произошло?</li> <li><strong>Против</strong> этого решения был другой аргумент: такое поведение может спровоцировать нестабильные тесты: <ul> <li>тест кликает на кнопку, не дожидаясь, пока она станет enabled</li> <li>по факту клик не случится (но и никаких ошибок не возникнет)</li> <li>и позже тест упадёт из-за не случившегося клика (и будет очень сложно понять, что произошло)</li> <li>или ещё хуже: тест может остаться ложно позитивным.</li> </ul> </li> </ol> <p>В общем, теперь <strong>аргумент №2 победил</strong>. Теперь метод <code class="language-plaintext highlighter-rouge">$.click()</code> дождётся, пока элемент станет enabled. А если не станет - клик упадёт.</p> <p>Теперь вы можете смело сократить все свои тесты:</p> <blockquote> <p><code class="language-plaintext highlighter-rouge">$.shouldBe(enabled).click()</code> заменить на <code class="language-plaintext highlighter-rouge">$.click()</code>.</p> </blockquote> <p>P.S. Если вы из лагеря аргумента №1, то можете вместо клика использовать <code class="language-plaintext highlighter-rouge">$.shouldBe(disabled)</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2288">issue 2288</a>. Спасибо <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> за <a href="https://github.com/selenide/selenide/pull/2290">PR 2290</a>.</p> <p><br /></p> <h3 id="escape-newlines-in-report">Маскируем переводы строк в отчёте</h3> <p>Если вы пробовали использовать в тесте многострочную строку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Если\n дедушка\n законченный\n мудак"</span><span class="o">);</span> </code></pre></div></div> <p>То могли заметить, что в отчёте эта строка выглядела коряво и ломала всю таблицу.</p> <p>А теперь будет красиво.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2283">issue 2283</a>. Спасибо <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> за <a href="https://github.com/selenide/selenide/pull/2284">PR 2284</a>.</p> <p><br /></p> <h3 id="combined-selectors-for-mobile-apps">Комбинированные селекторы для мобилок</h3> <p>При тестировании мобильных приложений удобно задать в одной проверке два селектора: отдельно для Android и iOS.<br /> Раньше приходилось придумывать вспомогательные методы или городить ифы.</p> <p>Теперь же можно объявить “комбинированный” селектор с помощью <code class="language-plaintext highlighter-rouge">CombinedBy</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">appium</span><span class="o">.</span><span class="na">selector</span><span class="o">.</span><span class="na">CombinedBy</span><span class="o">.</span><span class="na">android</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">appium</span><span class="o">.</span><span class="na">AppiumSelectors</span><span class="o">.</span><span class="na">byTagAndName</span><span class="o">;</span> <span class="err">$</span><span class="o">(</span><span class="n">android</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Бибер"</span><span class="o">)).</span><span class="na">ios</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Долик"</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> </code></pre></div></div> <p><br /></p> <h3 id="conditions-for-mobile-apps">Новые проверки для мобилок</h3> <p>Аналогичное решение для проверок: теперь можно одной строкой задать “комбинированную” проверку и для Android, и для iOS с помощью класса <code class="language-plaintext highlighter-rouge">CombinedAttribute</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">appium</span><span class="o">.</span><span class="na">conditions</span><span class="o">.</span><span class="na">CombinedAttribute</span><span class="o">.</span><span class="na">android</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">appium</span><span class="o">.</span><span class="na">AppiumCondition</span><span class="o">.</span><span class="na">attribute</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">static</span> <span class="n">io</span><span class="o">.</span><span class="na">appium</span><span class="o">.</span><span class="na">java_client</span><span class="o">.</span><span class="na">AppiumBy</span><span class="o">.</span><span class="na">accessibilityId</span><span class="o">;</span> <span class="c1">// Проверка элемента:</span> <span class="err">$</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="n">android</span><span class="o">(</span><span class="s">"content-desc"</span><span class="o">).</span><span class="na">ios</span><span class="o">(</span><span class="s">"name"</span><span class="o">),</span> <span class="s">"Гойду, Жерасимов, где, сука, ебоприпасы?"</span> <span class="o">));</span> <span class="c1">// Проверка коллекции:</span> <span class="err">$$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attributes</span><span class="o">(</span><span class="n">android</span><span class="o">(</span><span class="s">"text"</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">"Зять-«у*бок»"</span><span class="o">,</span> <span class="s">"Сын-отморозок"</span> <span class="o">));</span> </code></pre></div></div> <p><br /></p> <h3 id="new-selectors-for-mobile-apps">Новые селекторы для мобилок</h3> <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">appium</span><span class="o">.</span><span class="na">AppiumSelectors</span><span class="o">.*;</span> <span class="err">$</span><span class="o">(</span><span class="n">byAttribute</span><span class="o">(</span><span class="s">"content-desc"</span><span class="o">,</span> <span class="s">"Hello"</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">byContentDescription</span><span class="o">(</span><span class="s">"Hello"</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">byTagAndAtttribute</span><span class="o">(</span><span class="s">"android.widget.TextView"</span><span class="o">,</span> <span class="s">"Не"</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">byTagAndName</span><span class="o">(</span><span class="s">"XCUIElementTypeStaticText"</span><span class="o">,</span> <span class="s">"вы е"</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">withTagAndName</span><span class="o">(</span><span class="s">"XCUIElementTypeText"</span><span class="o">,</span> <span class="s">"бываться на красной площади"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2300">issue 2300</a>. Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide-appium/pull/135">PR 135</a>. Также см. <a href="https://github.com/selenide/selenide/pull/2315">PR 2315</a>.</p> <p><br /></p> <h3 id="switch-context-in-mobile-apps">Переключение контекста в мобилках</h3> <p>Зачастую мобильные приложения открывают какой-то контент во встроенном браузере (WebView). А то и полностью построены на WebView. При тестировании таких приложений требуется переключаться между “нативным” и “веб” контекстом.</p> <p>Теперь для переключения есть удобные методы:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Тут мы в "нативном" контексте</span> <span class="n">open</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="n">accessibilityId</span><span class="o">(</span><span class="s">"URL input field"</span><span class="o">)).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"www.google.com"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="n">accessibilityId</span><span class="o">(</span><span class="s">"Go To Site button"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> <span class="c1">// Опа, и мы уже в "веб" контексте:</span> <span class="n">switchTo</span><span class="o">().</span><span class="na">context</span><span class="o">(</span><span class="s">"WEBVIEW_com.saucelabs.mydemoapp.rn"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#i-am-not-a-robot"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">getContextHandles</span><span class="o">()).</span><span class="na">hasSize</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/pull/2308">PR 2308</a>.<br /> Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide-appium/pull/149">PR 149</a>.</p> <p><br /></p> <h3 id="refactor-collection-conditions">Переколбас CollectionCondition</h3> <p>Мы поменяли внутреннее устройство условий для коллекций (класс <code class="language-plaintext highlighter-rouge">CollectionCondition</code>).</p> <p>Раньше, если вы хотели создать <a href="https://github.com/selenide/selenide/blob/main/src/test/java/integration/collections/CustomCollectionConditionTest.java">кастомную проверку для коллекций</a>, вы должны были реализовать метод <code class="language-plaintext highlighter-rouge">test(elements)</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">allTextsStartingWithZ</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CollectionCondition</span><span class="o">()</span> <span class="o">{</span> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">test</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">webElements</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="n">webElements</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">webElement</span> <span class="o">-&gt;</span> <span class="n">webElement</span><span class="o">.</span><span class="na">getText</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="s">"Z"</span><span class="o">))</span> <span class="o">.</span><span class="na">reduce</span><span class="o">(</span><span class="kc">true</span><span class="o">,</span> <span class="o">(</span><span class="n">x</span><span class="o">,</span> <span class="n">y</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">x</span> <span class="o">&amp;&amp;</span> <span class="n">y</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="err">$$</span><span class="o">(</span><span class="s">".mudak"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">allTextsStartingWithZ</span><span class="o">)</span> </code></pre></div></div> <p>Этот метод останется ещё на какое-то время (для сохранения обратной совместимости), но <strong>рекомендуемый метод</strong> теперь будет <code class="language-plaintext highlighter-rouge">check(CollectionSource collection)</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">allTextsStartingWithZ</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CollectionCondition</span><span class="o">()</span> <span class="o">{</span> <span class="kd">public</span> <span class="nc">CheckResult</span> <span class="nf">check</span><span class="o">(</span><span class="nc">CollectionSource</span> <span class="n">collection</span><span class="o">)</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">elements</span> <span class="o">=</span> <span class="n">collection</span><span class="o">.</span><span class="na">getElements</span><span class="o">();</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">actualTexts</span> <span class="o">=</span> <span class="nc">ElementsCollection</span><span class="o">.</span><span class="na">texts</span><span class="o">(</span><span class="n">elements</span><span class="o">);</span> <span class="kt">boolean</span> <span class="n">allMatched</span> <span class="o">=</span> <span class="n">webElements</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">webElement</span> <span class="o">-&gt;</span> <span class="n">webElement</span><span class="o">.</span><span class="na">getText</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="s">"Z"</span><span class="o">))</span> <span class="o">.</span><span class="na">reduce</span><span class="o">(</span><span class="kc">true</span><span class="o">,</span> <span class="o">(</span><span class="n">x</span><span class="o">,</span> <span class="n">y</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">x</span> <span class="o">&amp;&amp;</span> <span class="n">y</span><span class="o">);</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">CheckResult</span><span class="o">(</span><span class="n">allMatched</span><span class="o">,</span> <span class="n">actualTexts</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="err">$$</span><span class="o">(</span><span class="s">".mudak"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">allTextsStartingWithZ</span><span class="o">)</span> </code></pre></div></div> <h3 id="что-нам-это-даёт">Что нам это даёт?</h3> <p>В будущем это откроет нам возможности для улучшений проверок коллекций:</p> <ol> <li>Можно будет сильно ускорить проверки: например, быстро получить тексты всех элементов коллекции за один вызов JavaScript кода.</li> <li>В сообщении об ошибке писать не “актуальные тексты на момент составления сообщения”, а “актуальные тексты на момент последней проверки”. Как в <a href="/2021/09/28/selenide-5.25.0/#actual-value-at-the-moment-of-last-check">этой проблеме</a>, только с коллекциями.</li> </ol> <p>См. <a href="https://github.com/selenide/selenide/pull/2312">PR 2312</a> и <a href="https://github.com/selenide/selenide/pull/2307">PR 2307</a>.</p> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Исторический момент: <a href="https://pikabu.ru/story/selenium_selenide_i_selenoid_8376039">первый мем про селенид</a></li> <li>Моё выступление на новогоднем девклубе: <a href="https://www.youtube.com/watch?v=jyPQzygMpoo&amp;ab_channel=DEVCLUB.EU">Думай как пиарщик</a></li> <li>Видос <a href="https://www.youtube.com/watch?v=M--_ziTZPQQ&amp;ab_channel=kanezi">Тестируем Spring с помощью Selenide</a> от kanezi</li> <li>Серия видосов на Testing Mini Bytes: <a href="https://www.youtube.com/watch?v=UQWQJ3dhgVI&amp;ab_channel=TestingMiniBytes">Selenide + Allure = Beautiful HTML Reports</a></li> <li>Пост <a href="https://creatingvalue.substack.com/p/why-we-chose-selenide-over-selenium">Why we chose Selenide over Selenium</a> от Amuthan Sakthivel</li> </ul> <center> <img src="/images/2023/05/selenide-meme.jpeg" width="600" alt="Selenide meme" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/05/29/selenide-6.15.0/ http://ru.selenide.org/2023/05/29/selenide-6.15.0 2023-05-29T00:00:00+00:00 Вышла Selenide 6.14.0 <p>Привет!</p> <p>Пока не начался контрнаступ, давайте-ка обновимся на <a href="https://github.com/selenide/selenide/milestone/181?closed=1">Selenide 6.14.0</a> - а то потом будет не до него.</p> <p>В нём есть несколько изменений, которые как будто не очень большие, но могут чего-нибудь сломать. <em>Поэтому очень советую не оттягивать и <strong>обновиться сейчас</strong>.</em></p> <ul> <li><a href="#speedup-debug">Ускорили дебаг</a></li> <li><a href="#remove-flag--no-sandbox">Убрали флажок <code class="language-plaintext highlighter-rouge">--no-sandbox</code></a></li> <li><a href="#add-step-method">Добавили метод <code class="language-plaintext highlighter-rouge">step</code> для отчётиков</a></li> <li><a href="#drag-and-drop-with-options">Добавили метод <code class="language-plaintext highlighter-rouge">$.dragAndDrop(DragAndDropOptions)</code></a></li> <li><a href="#allow-negative-browser-position">Браузер за экран</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#news">Новости</a></li> <li><a href="#statistics">Статистика</a> <p><br /></p> </li> </ul> <h3 id="speedup-debug">Ускорили дебаг</h3> <p>Когда дебажишь свой тест и останавливаешься на брейкпойнте, иногда можно заметить, что IDE очень долго отображаешь значения полей в пэджобжекте. Вот в таком состоянии IDE может подвиснуть надолго:</p> <p><img src="/images/2023/05/selenide-debug.slow.png" alt="Selenide. debug. slow." width="600px" /></p> <p>Причина в том, что для отображения каждого поля Selenide обращается к вебдрайверу, чтобы получить имя тэга, актуальный текст и атрибуты элемента. Зато через некоторое время вы могли увидеть актуальную картинку:</p> <p><img src="/images/2023/05/selenide-debug.slow.result.png" alt="Selenide. debug. slow. result." width="600px" /></p> <p>Теперь этот процесс должен сильно ускориться. Селенид больше не будет обращаться к вебдрайверу, а просто покажет локатор элемента:</p> <p><img src="/images/2023/05/selenide-debug.fast.png" alt="Selenide. debug. slow. result." width="600px" /></p> <p>Но и формат метода <code class="language-plaintext highlighter-rouge">$.toString()</code> / <code class="language-plaintext highlighter-rouge">$$.toString()</code> тоже изменился. Будьте готовы.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2253">issue 2253</a>. и <a href="https://github.com/selenide/selenide/pull/2269">PR 2269</a>.</p> <blockquote> <p>Но не переживайте, это всё касается только дебага. При падении теста вы по-прежнему будете получать полную информацию об элементе с блекджектом и скриншотами. <br /></p> </blockquote> <h3 id="remove-flag--no-sandbox">Убрали флажок хрома <code class="language-plaintext highlighter-rouge">--no-sandbox</code></h3> <p>История с этим флажком мутная. Лично я не вижу никакой разницы, запускать хром с ним или без него.</p> <p>Но</p> <ol> <li>Некоторые пользователи говорят, что без флажка <code class="language-plaintext highlighter-rouge">--no-sandbox</code> хром не запускается в докере</li> <li>А другие пользователи говорят, что с флажком <code class="language-plaintext highlighter-rouge">--no-sandbox</code> процесс хрома остаётся висеть в фоне и пожирает весь CPU. Но это вроде только на винде (?)</li> </ol> <p>В общем, мы убрали этот флажок, потому что пользователям №1</p> <ul> <li>легко обнаружить проблему (хром тупо не запускается)</li> <li>легко добавить его самостоятельно: <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">browserCapabilities</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">ChromeOptions</span><span class="o">().</span><span class="na">addArguments</span><span class="o">(</span><span class="s">"--no-sandbox"</span><span class="o">);</span> </code></pre></div> </div> </li> </ul> <p>См. <a href="https://github.com/selenide/selenide/issues/2270">issue 2270</a> и <a href="https://github.com/selenide/selenide/pull/2271">PR 2271</a>.</p> <p><br /></p> <h3 id="add-step-method">Добавили метод <code class="language-plaintext highlighter-rouge">step</code> для отчётиков</h3> <p>Если вы используете селенидовский встроенный отчёт (<code class="language-plaintext highlighter-rouge">TextReport</code>), то теперь можете группировать действия в “шаги” с помощью нового метода <code class="language-plaintext highlighter-rouge">step</code>:</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">authentication</span><span class="o">()</span> <span class="o">{</span> <span class="n">step</span><span class="o">(</span><span class="s">"login"</span><span class="o">,()-&gt;{</span> <span class="n">open</span><span class="o">(</span><span class="s">"/login.asp"</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">val</span><span class="o">(</span><span class="s">"u"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#password"</span><span class="o">).</span><span class="na">val</span><span class="o">(</span><span class="s">"p"</span><span class="o">);</span> <span class="o">});</span> <span class="n">step</span><span class="o">(</span><span class="s">"logout"</span><span class="o">,()-&gt;{</span> <span class="err">$</span><span class="o">(</span><span class="s">"#logout"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="s">"#goodBye"</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">"Good bye looser!"</span><span class="o">));</span> <span class="o">});</span> <span class="o">}</span> </code></pre></div></div> <p>В отчёте шаги будут визуально выделяться.</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------------+-----------------------------------------------+-------+------+ | Element |Subject |Status | ms. | +---------------+-----------------------------------------------+-------+------+ | login | |PASS | 4000 | | open | http://127.0.0.1:8080/my_account/account.html |PASS | 2662 | | #username | val "u" |PASS | 300 | | #password | val "p" |PASS | 400 | | logout | |PASS | 1023 | | #logout | click |PASS | 923 | | #goodBye | should have text "Good bye looser!" |PASS | 100 | +---------------+-----------------------------------------------+-------+------+ </code></pre></div></div> <p>Кстати, шаги могут быть вложенными друг в друга.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2172">issue 2172</a>. Спасибо <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> за <a href="https://github.com/selenide/selenide/pull/2250">PR 2250</a>.</p> <p><br /></p> <h3 id="drag-and-drop-with-options">Добавили метод <code class="language-plaintext highlighter-rouge">$.dragAndDrop(DragAndDropOptions)</code></h3> <p>Теперь метод <code class="language-plaintext highlighter-rouge">$.dragAndDrop</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">"#drag1"</span><span class="o">).</span><span class="na">dragAndDrop</span><span class="o">(</span><span class="n">to</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">dragAndDrop</span><span class="o">(</span><span class="n">to</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">).</span><span class="na">usingJS</span><span class="o">());</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> за <a href="https://github.com/selenide/selenide/pull/2245">PR 2245</a>.</p> <p><br /></p> <h3 id="allow-negative-browser-position">Разрешили двигать браузер за экран</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">browserPosition</span> <span class="o">=</span> <span class="s">"-1900x-450"</span><span class="o">;</span> </code></pre></div></div> <p>Слабо себе представляю, зачем это может быть полезно, но исправили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2258">issue 2258</a> и <a href="https://github.com/selenide/selenide/pull/2259">PR 2259</a>.</p> <p><br /></p> <h3 id="support-edge-binary-location">Путь к бинарнику Edge</h3> <p>В селениде есть настройка <code class="language-plaintext highlighter-rouge">Configuration.browserBinary</code>, позволяющая задать путь к бинарнику браузера. (на всякий случай: в большинстве случаев её <em>не нужно</em> задавать).</p> <p>Раньше эта настройка не поддерживалась для браузера Edge, а теперь поддерживается. Ура.</p> <p>Спасибо <a href="https://github.com/vlad8x8">Vladislav Velichko</a> за <a href="https://github.com/selenide/selenide/pull/2267">PR 2267</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li><a href="https://github.com/selenide/selenide/pull/2277">Selenium 4.9.1</a> - см. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог</a></li> <li><a href="https://github.com/selenide/selenide/pull/2263">Netty 4.1.92.Final</a></li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Серия уроков <a href="https://www.youtube.com/playlist?list=PLFGzDEkV3ACu0i-Gd_whXsspqDlmP3ymP">Selenide Java with Cucumber</a> от <a href="https://www.youtube.com/@TechProEducationUS">TechPro Education</a></li> <li>Обзор <a href="https://www.youtube.com/watch?v=vUTQyr2UF0M&amp;ab_channel=OlehPendrak">Jetbrains Aqua для автоматизации тестирования</a> от <a href="https://www.youtube.com/@threadqa">Oleh Pendrak</a></li> </ul> <p><br /></p> <h3 id="statistics">Статистика</h3> <p>Количество ежемесячных скачиваний Селенида перевалило за 600 тыщ!</p> <center> <img src="/images/2023/04/selenide.downloads.jpg" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/05/08/selenide-6.14.0/ http://ru.selenide.org/2023/05/08/selenide-6.14.0 2023-05-08T00:00:00+00:00 Вышла Selenide 6.13.0 <p>Привет!</p> <p>Сегодня у нас большой мажорный релиз <a href="https://github.com/selenide/selenide/milestone/177?closed=1">Selenide 6.13.0</a>.</p> <ul> <li><a href="#add-method-cached">Добавили метод <code class="language-plaintext highlighter-rouge">$.cached()</code></a></li> <li><a href="#mock-response-with-http-status">Добавили http статус при моке ответа сервера</a></li> <li><a href="#add-method-in-new-browser">Новый метод <code class="language-plaintext highlighter-rouge">inNewBrowser</code> для запуска куска кода в новом браузере</a></li> <li><a href="#add-method-doubleclick-with-options">Добавили метод <code class="language-plaintext highlighter-rouge">$.doubleClick(options)</code></a></li> <li><a href="#add-condition-inner-text">Добавили условие <code class="language-plaintext highlighter-rouge">$.shouldHave(innerText())</code></a></li> <li><a href="#add-condition-attributes">Добавили условие для коллекций <code class="language-plaintext highlighter-rouge">$$.shouldHave(attributes(...))</code></a></li> <li><a href="#clear-error-message-in-select">Ясное сообщение об ошибки в методах <code class="language-plaintext highlighter-rouge">$.select*()</code></a></li> <li><a href="#fix-method-sublist">Исправили ошибку в методе <code class="language-plaintext highlighter-rouge">$$.subList()</code></a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#news">Новости</a> <p><br /></p> </li> </ul> <h3 id="add-method-cached">Добавили метод <code class="language-plaintext highlighter-rouge">$.cached()</code></h3> <p>Иногда приходится воротить довольно сложный код для поиска элемента. И он может работать <strong>медленно</strong>, особенно если элемент ищется из какой-то большой коллекции с применением сложной фильтрации. Что-то вроде такого:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">subscriptions</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"div.subs-expands"</span><span class="o">)</span> <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">childExactText</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">".//div[contains(@class,'data')]//div[contains(@class,'heading')]"</span><span class="o">),</span> <span class="n">orderNumber</span><span class="o">)</span> <span class="o">);</span> <span class="nc">SelenideElement</span> <span class="n">target</span> <span class="o">=</span> <span class="n">subscriptions</span> <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">childAttributeValue</span><span class="o">(...))</span> <span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">childExactText</span><span class="o">(....));</span> </code></pre></div></div> <p>Каждое обращение к <code class="language-plaintext highlighter-rouge">target</code> заметно притормаживает.</p> <p>Теперь такие тесты можно ускорить, закэшировав значение <code class="language-plaintext highlighter-rouge">target</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideElement</span> <span class="n">target</span> <span class="o">=</span> <span class="n">subscriptions</span> <span class="o">.</span><span class="na">filter</span><span class="o">(..)</span> <span class="o">.</span><span class="na">find</span><span class="o">(..)</span> <span class="o">.</span><span class="na">cached</span><span class="o">();</span> </code></pre></div></div> <p>Естественно, это можно сделать только в том случае, если вы уверены, что этот элемент не меняется, не перегружается и т.д.<br /> Если он всё-таки перегрузится, то вы словите тот самый легендарный <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2171">issue 2171</a>, <a href="https://github.com/selenide/selenide/issues/1927">issue 1927</a> и <a href="https://github.com/selenide/selenide/pull/2189">PR 2189</a>.</p> <p><br /></p> <h3 id="mock-response-with-http-status">Добавили http статус при моке ответа сервера</h3> <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">responseMocker</span><span class="o">().</span><span class="na">mockText</span><span class="o">(</span><span class="s">"login-mock"</span><span class="o">,</span> <span class="n">urlContains</span><span class="o">(</span><span class="no">POST</span><span class="o">,</span> <span class="s">"/login"</span><span class="o">),</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="s">"{role: admin}"</span><span class="o">);</span> </code></pre></div></div> <p>Раньше можно было мокать только тело ответа, но статус всегда оставался 200 (“OK”).<br /> Теперь можно задать и другой статус:</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">responseMocker</span><span class="o">().</span><span class="na">mockText</span><span class="o">(</span><span class="s">"login-mock"</span><span class="o">,</span> <span class="n">urlContains</span><span class="o">(</span><span class="no">POST</span><span class="o">,</span> <span class="s">"/login"</span><span class="o">),</span> <span class="mi">403</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="s">"{role: looser}"</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2227">issue 2227</a> и <a href="https://github.com/selenide/selenide/pull/2234">PR 2234</a>.</p> <p><br /></p> <h3 id="add-method-in-new-browser">Новый метод <code class="language-plaintext highlighter-rouge">inNewBrowser</code> для запуска куска кода в новом браузере</h3> <p>Иногда вам хочется посреди теста запустить кусок кода в новом браузере.</p> <blockquote> <p>Если что, я это осуждаю: такие вспомогательные действия надо делать не через UI (который мы тестируем, а потому не доверяем), а каким-то контролируем способом: через API, прямой запрос в базу и т.п.</p> </blockquote> <p>Уже давно для этого есть <a href="/2019/10/16/selenide-5.4.0/#add-method-using">метод <code class="language-plaintext highlighter-rouge">using</code></a>. Но он требует, чтобы вы открыли новый браузер с какими-то своими настройками. А что, если вам подходят обычные селенидовские настройки? То есть вам нужен точно такой же браузер, только новый?</p> <p>Теперь для этого есть метод <code class="language-plaintext highlighter-rouge">inNewBrowser</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://site.com/login/as/user/bob"</span><span class="o">);</span> <span class="n">inNewBrowser</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">"https://site.com/login/as/admin"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="n">by</span><span class="o">(</span><span class="s">"value"</span><span class="o">,</span> <span class="s">"bob"</span><span class="o">)).</span><span class="na">find</span><span class="o">(</span><span class="s">"[name=is_admin]"</span><span class="o">).</span><span class="na">setEnabled</span><span class="o">();</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">"Hello, chat admin!"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2213">issue 2213</a> и <a href="https://github.com/selenide/selenide/pull/2236">PR 2236</a>.</p> <p><br /></p> <h3 id="add-method-doubleclick-with-options">Добавили метод <code class="language-plaintext highlighter-rouge">$.doubleClick(options)</code></h3> <p>Раньше был просто метод <code class="language-plaintext highlighter-rouge">$.doubleClick()</code>, который мог кликать только по центру элемента. Теперь же у него появился продвинутый тёзка с параметром <code class="language-plaintext highlighter-rouge">options</code>. Опции такие же, как и у обычного клика:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">doubleClick</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">());</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">doubleClick</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">".btn"</span><span class="o">).</span><span class="na">doubleClick</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">66</span><span class="o">,</span> <span class="mi">33</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">doubleClick</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">66</span><span class="o">,</span> <span class="mi">33</span><span class="o">).</span><span class="na">withTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">9</span><span class="o">)));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2133">issue 2133</a>. Спасибо <a href="https://github.com/aakachurin">aakachurin</a> за <a href="https://github.com/selenide/selenide/pull/2135">PR 2135</a>.</p> <p><br /></p> <h3 id="add-condition-inner-text">Добавили условие <code class="language-plaintext highlighter-rouge">$.shouldHave(innerText())</code></h3> <p>Оно позволит проверять текст невидимых элементов.</p> <blockquote> <p>Скорее всего это плохая идея: раз пользователь не видит элемента, значит, и проверять его не надо.</p> </blockquote> <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">"#theHiddenElement"</span><span class="o">)</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">innerText</span><span class="o">(</span><span class="s">"Видишь суслика? И я не вижу. А он есть!"</span><span class="o">));</span> <span class="c1">// Обычный $("#theHiddenElement").text() выдаёт здесь пустую строку "";</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2220">issue 2220</a> и <a href="https://github.com/selenide/selenide/pull/2223">PR 2223</a>.</p> <p><br /></p> <h3 id="add-condition-attributes">Добавили условие для коллекций <code class="language-plaintext highlighter-rouge">$$.shouldHave(attributes(...))</code></h3> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"#numbers option"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">attributes</span><span class="o">(</span><span class="s">"value"</span><span class="o">,</span> <span class="s">"one"</span><span class="o">,</span> <span class="s">"two"</span><span class="o">,</span> <span class="s">"three"</span><span class="o">,</span> <span class="s">"four"</span><span class="o">,</span> <span class="s">"five"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2091">issue 2091</a>. Спасибо <a href="https://github.com/AlexLAA">Alexey Lakovych</a> за <a href="https://github.com/selenide/selenide/pull/2091">PR 2091</a>. Также см. <a href="https://github.com/selenide/selenide/pull/2230">PR 2230</a>.</p> <p><br /></p> <h3 id="clear-error-message-in-select">Ясное сообщение об ошибки в методах <code class="language-plaintext highlighter-rouge">$.select*()</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">"select#gender"</span><span class="o">).</span><span class="na">selectOptionContainingText</span><span class="o">(</span><span class="s">"Female"</span><span class="o">);</span> </code></pre></div></div> <p>Недавно мы <a href="/2022/11/21/selenide-6.10.0/#select-options-using-javascript">переделали их реализацию на JavaScript</a> - так быстрее и надёжнее.</p> <p>Но оказалось, что если по ошибке вызвать такой метода для не-селекта, вылетала очень непонятная ошибка:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"ul"</span><span class="o">).</span><span class="na">selectOptionContainingText</span><span class="o">(</span><span class="s">"Kelly Snyder"</span><span class="o">);</span> <span class="o">--&gt;</span> <span class="nl">JavascriptException:</span> <span class="n">javascript</span> <span class="nl">error:</span> <span class="n">undefined</span> <span class="n">is</span> <span class="n">not</span> <span class="nf">iterable</span> <span class="o">(</span><span class="n">cannot</span> <span class="n">read</span> <span class="n">property</span> <span class="nf">Symbol</span><span class="o">(</span><span class="nc">Symbol</span><span class="o">.</span><span class="na">iterator</span><span class="o">))</span> </code></pre></div></div> <p>Теперь сообщение будет понятное:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalArgumentException:</span> <span class="nc">Cannot</span> <span class="n">select</span> <span class="n">option</span> <span class="n">from</span> <span class="n">a</span> <span class="n">non</span><span class="o">-</span><span class="n">select</span> <span class="n">element</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2231">issue 2231</a> и <a href="https://github.com/selenide/selenide/pull/2233">PR 2233</a>.</p> <h3 id="fix-method-sublist">Исправили ошибку в методе <code class="language-plaintext highlighter-rouge">$$.subList()</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">"#numbers"</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">"One"</span><span class="o">,</span> <span class="s">"Two"</span><span class="o">,</span> <span class="s">"Three"</span><span class="o">,</span> <span class="s">"Four"</span><span class="o">,</span> <span class="s">"Five"</span><span class="o">));</span> </code></pre></div></div> <p>Но метод <code class="language-plaintext highlighter-rouge">$$</code> возвращает объект <code class="language-plaintext highlighter-rouge">ElementsCollection</code>, который, к сожалению, наследуется от <code class="language-plaintext highlighter-rouge">AbstractList&lt;SelenideElement&gt;</code>. А потому случайно (так не задумывалось!) наследует от него некоторые методы, на которые мы не рассчитывали.</p> <p>Один из таких методов - <code class="language-plaintext highlighter-rouge">subList</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">".user"</span><span class="o">).</span><span class="na">subList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">4</span><span class="o">).</span><span class="na">iterator</span><span class="o">();</span> </code></pre></div></div> <p>Приведённый выше итератор возвращает не три, а всего два элемента. Обрезает последний. Ошибочка. <code class="language-plaintext highlighter-rouge">¯\_(ツ)_/¯</code></p> <p>Теперь она оперативно исправлена.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2239">issue 2239</a> и <a href="https://github.com/selenide/selenide/pull/2240">PR 2240</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>bump Selenium from 4.8.1 to 4.8.3 – см. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">CHANGELOG</a></li> <li>bump LittleProxy from 2.0.16 to 2.0.17</li> <li>bump BrowserUpProxy from 2.2.8 to 2.2.9</li> <li>bump nettyVersion from 4.1.90.Final to 4.1.91.Final</li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <p>Сегодня у нас подборка видосов про Селенид</p> <ul> <li>на русском: <a href="https://youtu.be/k0A8HFWV_iE">ChatGPT: Как скачать файл в Selenide</a></li> <li>на английском: <a href="https://www.youtube.com/watch?v=GwHG550moGc">ChatGPT: How to download file in Selenide</a></li> <li><a href="https://www.youtube.com/watch?v=18J2_4a4Cl4&amp;ab_channel=Jfokus">Flaky tests на английском</a></li> <li><a href="https://www.youtube.com/watch?v=SohZfPKicZQ&amp;ab_channel=OlehPendrak">Ускоряем UI Автотесты с помощью подстановки Cookies</a> by Oleh Pendrak</li> <li><a href="https://www.youtube.com/watch?v=wN45Qla66-o&amp;list=PLFGoYjJG_fqrvWt1FfHqKoREQmSPxazBq&amp;ab_channel=NaveenAutomationLabs">Канал про селенид</a> от NaveenAutomationLabs</li> <li><a href="https://www.youtube.com/watch?v=9S6xaAvJc9M&amp;t=4226s&amp;ab_channel=HillelITSchool">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/2023/04/04/selenide-6.13.0/ http://ru.selenide.org/2023/04/04/selenide-6.13.0 2023-04-04T00:00:00+00:00 Вышла Selenide 6.13.0 <p>Всем привет!</p> <p>Ура, сегодня у нас новый релиз <strong>Selenide 6.13.0</strong></p> <ul> <li><a href="#banners-support">Поддержка баннеров</a></li> <li><a href="#news">Новости</a> <p><br /></p> </li> </ul> <h3 id="banners-support">Поддержка баннеров</h3> <h4 id="проблема">Проблема</h4> <p>Очень часто люди спрашивают: “А что делать, если посреди теста на экране может выскочить баннер или какой-то ещё неожиданный элемент?”</p> <p>Ведь он может перекрыть другие элементы, закрыть важную кнопку и т.п. - и таким образом сломать тест.</p> <blockquote> <p><a href="https://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide/">Я всегда на это люто ругался</a> и настоятельно советовал взять тестовую среду под контроль и самому из теста регулировать, когда баннеры должны появляться, а когда нет.</p> </blockquote> <p>Алексей Виноградов сделал выступление на эту тему: <a href="https://www.youtube.com/watch?v=MLxf9q9qXu4&amp;ab_channel=%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D0%B8%D0%B2%D0%9A%D0%BE%D0%BD%D1%82%D1%83%D1%80%D0%B5">Spinner-driven-development</a>.</p> <p><br /></p> <h4 id="стадия-принятия">Стадия принятия</h4> <p>Но слишком уж это распространённая проблема. В конце концов, Селенид задуман для того, чтобы помогать людям решать проблемы, а не читать нотации.</p> <p>Мы решили пойти людям навстречу и создать решение для проблемы баннеров. Теперь в начале теста вы можете вызвать метод <code class="language-plaintext highlighter-rouge">Selenide.onBanner()</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">onBanner</span><span class="o">(</span><span class="s">"#pizzaAds"</span><span class="o">,</span> <span class="no">CLOSE</span><span class="o">,</span> <span class="s">".btn-close"</span><span class="o">);</span> </code></pre></div></div> <p><br /></p> <h4 id="как-это-работает">Как это работает</h4> <p>По умолчанию, этот метод добавит вебдрайверу листенер, который будет перед каждым кликом или проверкой проверять, не появился ли элемент с данным селектором <code class="language-plaintext highlighter-rouge">"#pizzaAds"</code>. Если появился, селенид попытается закрыть его, нажав на кнопку закрытия <code class="language-plaintext highlighter-rouge">".btn-close"</code>.</p> <p>Этот метод называется <code class="language-plaintext highlighter-rouge">POLL</code>. Он самый простой и надёжный, но и самый медленный. Ведь селенид должен будет кучу раз спрашивать у браузера, не появился ли баннер. Каждый такой запрос занимает некоторое время.</p> <p><br /></p> <h4 id="альтернативные-методы">Альтернативные методы</h4> <p>Поэтому мы добавили ещё два альтернативных метода, которыми селенид может проверять, а не появился ли баннер. Их можно включить либо глобально настройкой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">bannerCloseMode</span> <span class="o">=</span> <span class="no">POLL</span><span class="o">;</span> <span class="c1">// по умолчанию</span> <span class="c1">// Слушает события по изменению DOM через CDP:</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">bannerCloseMode</span> <span class="o">=</span> <span class="no">CDP</span><span class="o">;</span> <span class="c1">// Работает только в Chromium-браузерах</span> <span class="c1">// Скармливает html текущей страницы ChatGPT и спрашивает, нет ли там баннера:</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">bannerCloseMode</span> <span class="o">=</span> <span class="no">CHATGPT</span><span class="o">;</span> </code></pre></div></div> <p>Либо вы можете задать его параметром с помощью <code class="language-plaintext highlighter-rouge">using</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">onBanner</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">CDP</span><span class="o">)</span> <span class="o">.</span><span class="na">withBannerSelector</span><span class="o">(</span><span class="s">"#pizzaAds"</span><span class="o">)</span> <span class="o">.</span><span class="na">withCloseButtonSelector</span><span class="o">(</span><span class="s">".btn-close"</span><span class="o">)</span> <span class="o">.</span><span class="na">withAction</span><span class="o">(</span><span class="no">CLOSE</span><span class="o">)</span> <span class="o">);</span> </code></pre></div></div> <h4 id="кастомный-обработчик">Кастомный обработчик</h4> <p>Есть также более общий вариант, в котором вы сможете залямбдашить свою специфическую логику обработки баннера. Вы же не всегда хотите сразу закрыть баннер - иногда сначала нужно из него прочитать какую-то информацию.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">onBanner</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">CHATGPT</span><span class="o">)</span> <span class="o">.</span><span class="na">withBannerSelector</span><span class="o">(</span><span class="s">"#pizzaAds"</span><span class="o">)</span> <span class="o">.</span><span class="na">withCloseButtonSelector</span><span class="o">(</span><span class="s">".btn-close"</span><span class="o">)</span> <span class="o">.</span><span class="na">withAction</span><span class="o">((</span><span class="n">banner</span><span class="o">,</span> <span class="n">closeButton</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="n">banner</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">".title"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Ваш пароль изменён."</span><span class="o">));</span> <span class="nc">String</span> <span class="n">newPassword</span> <span class="o">=</span> <span class="n">banner</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">".new-password"</span><span class="o">).</span><span class="na">text</span><span class="o">();</span> <span class="n">closeButton</span><span class="o">.</span><span class="na">doubleClick</span><span class="o">();</span> <span class="o">})</span> <span class="o">);</span> </code></pre></div></div> <p><br /></p> <p>Пробуйте, пишите, заводите тикеты на гитхабе. Предлагайте свои алгоритмы для обнаружения баннеров.</p> <p><em>Хватит примерять на себя белые пальто и ссориться из-за дурацких принципов.</em></p> <blockquote> <p>Давайте объединимся против баннеров!</p> </blockquote> <h3 id="news">News</h3> <p>Сегодня у нас три видоса про Селенид</p> <ul> <li>на русском: <a href="https://youtu.be/k0A8HFWV_iE">ChatGPT: Как скачать файл в Selenide</a></li> <li>на английском: <a href="https://www.youtube.com/watch?v=GwHG550moGc">ChatGPT: How to download file in Selenide</a></li> <li><a href="https://www.youtube.com/watch?v=SohZfPKicZQ&amp;ab_channel=OlehPendrak">Ускоряем UI Автотесты с помощью подстановки Cookies</a> by Oleh Pendrak</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/04/01/selenide-6.13.0/ http://ru.selenide.org/2023/04/01/selenide-6.13.0 2023-04-01T00:00:00+00:00 Вышла Selenide 6.12.4 <p>Привет!</p> <p>А вы следили за новостями 23 марта? Молодцы!</p> <p>Сегодня у нас <em>Точка Отсчёта</em> - релиз <a href="https://github.com/selenide/selenide/milestone/180?closed=1">Selenide 6.12.4</a>.</p> <ul> <li><a href="#workaround-for-chromedriver-bug">Костыль для Хрома</a></li> <li><a href="#support-jdk-http-client">Добавили поддержку jdk-http-client</a></li> <li><a href="#restore-proxy-after-using">Исправили $.download(PROXY) после “using”</a></li> <li><a href="#fix-clear-when-element-disappears">Исправили $.clear() при пропадании элемента</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#release-selenide-appium">Выпустили selenide-appium 2.7.0</a></li> <li><a href="#release-selenide-selenoid">Выпустили selenide-selenoid 2.3.6</a></li> <li><a href="#news">Новости</a> <p><br /></p> </li> </ul> <h3 id="workaround-for-chromedriver-bug">Костыль для Хрома</h3> <p>Напоминание: если вы всё ещё роете интернет в поисках лекарства против</p> <blockquote> <p>Invalid Status code=403 text=Forbidden</p> </blockquote> <p>то лекарство уже было выпущено в <a href="/2023/03/09/selenide-6.12.2/">Selenide 6.12.2</a>.</p> <p><br /></p> <h3 id="support-jdk-http-client">Добавили поддержку <code class="language-plaintext highlighter-rouge">jdk-http-client</code> вместо <code class="language-plaintext highlighter-rouge">netty-client</code></h3> <p>По умолчанию по-прежнему используется <code class="language-plaintext highlighter-rouge">netty-client</code>, но если вы хотите <a href="https://www.selenium.dev/blog/2022/using-java11-httpclient/">перейти на jdk-http-client</a>, то можете это легко сделать:</p> <ol> <li>Добавить зависимость <br /> <code class="language-plaintext highlighter-rouge">org.seleniumhq.selenium:selenium-http-jdk-client:$seleniumVersion</code></li> <li>Добавить пропертю перед открытием браузера: <br /> <code class="language-plaintext highlighter-rouge">System.setProperty("webdriver.http.factory", "jdk-http-client");</code></li> </ol> <p>См. <a href="https://github.com/selenide/selenide/issues/2215">issue 2215</a> и <a href="https://github.com/selenide/selenide/pull/2216">PR 2216</a>.</p> <p><br /></p> <h3 id="restore-proxy-after-using">Исправили работу прокси после метода <code class="language-plaintext highlighter-rouge">using</code></h3> <p>Это почти такая же проблема, как в <a href="https://github.com/selenide/selenide/issues/2202">issue 2202</a>, но только с прокси.</p> <p>В общем, стоило вам разочек использовать метод <code class="language-plaintext highlighter-rouge">using</code>, как у вас сразу пропадал прокси. И дальнейшее скачивание файлов (или для чего ещё вы использовали прокси) больше не работало.</p> <p>Но судя по всему, про этот <a href="/2019/10/16/selenide-5.4.0/#add-method-using">супер-удобный метод <code class="language-plaintext highlighter-rouge">using</code></a> мало кто знает, потому что жалоба была всего одна. :)</p> <p>Ну вот, теперь и <code class="language-plaintext highlighter-rouge">using</code> починили, и про него узнает больше народу. :)</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2208">PR 2208</a> и <a href="https://github.com/selenide/selenide/pull/2209">PR 2209</a>.</p> <p><br /></p> <h3 id="fix-clear-when-element-disappears">Исправили <code class="language-plaintext highlighter-rouge">$.clear()</code> при пропадании элемента</h3> <p>Как вы знаете, в селениде есть метод <code class="language-plaintext highlighter-rouge">$("input").clear()</code>, который очищает поле ввода. Оказалось, что метод мог упасть в одной редкой ситуации. А именно, если в результате очистки элемент пропадает. В этот момент метод <code class="language-plaintext highlighter-rouge">$.clear()</code> пытался сгенерировать событие <code class="language-plaintext highlighter-rouge">on change</code> на этом элементе - и падал.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2207">issue 2207</a> и <a href="https://github.com/selenide/selenide/pull/2221">PR 2221</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>#2210 Bump nettyVersion from 4.1.89.Final to 4.1.90.Final</li> <li>#2218 Bump slf4jVersion from 2.0.6 to 2.0.7</li> </ul> <p><br /></p> <h3 id="release-selenide-appium">Выпустили <code class="language-plaintext highlighter-rouge">selenide-appium:2.7.0</code></h3> <ul> <li>Добавили скролл вверх-вниз для мобильников (#139)</li> <li>Исправили функцию <code class="language-plaintext highlighter-rouge">terminateApp</code> (#146)</li> <li>Обновились на Selenide 6.12.4 (#143)</li> </ul> <p>См. <a href="https://github.com/selenide/selenide-appium/releases/tag/v2.7.0">release notes</a>.</p> <p><br /></p> <h3 id="release-selenide-selenoid">Выпустили <code class="language-plaintext highlighter-rouge">selenide-selenoid:2.3.6</code></h3> <ul> <li>Обновились на Selenide 6.12.4</li> </ul> <p>См. <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.3.6">release notes</a>.</p> <p><br /></p> <h3 id="news">News</h3> <ul> <li>Я запилил первый видос после 9-летнего перерыва :) <a href="https://youtu.be/k0A8HFWV_iE">Как скачать файл в Selenide: спросим у ChatGPT</a></li> <li>Наконец-то у меня появился <a href="https://www.youtube.com/watch?v=18J2_4a4Cl4&amp;ab_channel=Jfokus">доклад про Flaky tests на английском</a> - конференция JFokus, Стокгольм, 8.02.2023</li> <li>Видос <a href="https://www.youtube.com/watch?v=9S6xaAvJc9M&amp;t=4226s&amp;ab_channel=HillelITSchool">Selenide: як тестувати веб-елементи</a> от Hillel IT School</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/03/23/selenide-6.12.4/ http://ru.selenide.org/2023/03/23/selenide-6.12.4 2023-03-23T00:00:00+00:00 Вышла Selenide 6.12.2 <p>Привет!</p> <p>Срочно обновляйтесь на <a href="https://github.com/selenide/selenide/milestone/178?closed=1">Selenide 6.12.2</a>.</p> <p>Он содержит один важный костыль для баги в Chromedriver 111.</p> <blockquote> <p>Invalid Status code=403 text=Forbidden</p> </blockquote> <h3 id="в-чём-проблема">В чём проблема?</h3> <p>Недавно вышла новая версия Chrome и Chromedriver 111. И у всех, кто на неё обновился, резко посыпались тесты. Браузер открывался, но при этом тест получал от вебдрайвера ошибку и дальше не мог ничего сделать, в том числе закрыть этот самый браузер.</p> <p>В логах было видно такое вот красноречивое сообщение:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Starting</span> <span class="nc">ChromeDriver</span> <span class="mf">111.0</span><span class="o">.</span><span class="mf">5563.64</span> <span class="n">on</span> <span class="n">port</span> <span class="mi">31021</span> <span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">remote</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">WebSocket</span><span class="n">$Listener</span> <span class="n">onError</span> <span class="nl">WARNING:</span> <span class="nc">Invalid</span> <span class="nc">Status</span> <span class="n">code</span><span class="o">=</span><span class="mi">403</span> <span class="n">text</span><span class="o">=</span><span class="nc">Forbidden</span> <span class="n">java</span><span class="o">.</span><span class="na">io</span><span class="o">.</span><span class="na">IOException</span><span class="o">:</span> <span class="nc">Invalid</span> <span class="nc">Status</span> <span class="n">code</span><span class="o">=</span><span class="mi">403</span> <span class="n">text</span><span class="o">=</span><span class="nc">Forbidden</span> <span class="o">...</span> </code></pre></div></div> <p>И хотя это <a href="https://bugs.chromium.org/p/chromedriver/issues/detail?id=4361">бага хромдрайвера</a>, для неё нашёлся один простой костыль, и мы его по-быстрому запилили в Селениде.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2192">issue 2192</a> и <a href="https://github.com/selenide/selenide/pull/2194">PR 2194</a>.</p> <blockquote> <p>А вообще, обновляйтесь на <a href="/2023/03/22/selenide-6.12.4/#support-jdk-http-client">HttpClient</a>. Он требует Java11+, но зато таких багов там не будет.</p> </blockquote> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/03/09/selenide-6.12.2/ http://ru.selenide.org/2023/03/09/selenide-6.12.2 2023-03-09T00:00:00+00:00 Вышла Selenide 6.12.0 <p>Привет!</p> <p>В этот трагичный и одновременно праздничный день мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/173?closed=1">Selenide 6.12.0</a>.</p> <p>В нём почти нет фич, но есть новый режим, который может сильно повлиять на ваши тесты.</p> <ul class="blogpost-menu"> <li><a href="#new-headless-mode">Новый безбашенный режим</a></li> <li><a href="#improve-logs-when-downloading-file">Улучшили логирование при скачивании файлов</a></li> <li><a href="#improve-download-in-edge">Улучшили скачивание файлов в Edge под Windows</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#rename-master-to-main">Переименовали master в main</a></li> </ul> <p><br /></p> <h3 id="new-headless-mode">Новый безбашенный режим</h3> <p>В Chromium-браузерах появился новый безголовый (headless) режим. Подробнее о нём <a href="https://developer.chrome.com/articles/new-headless/">в их блоге</a>.</p> <p>Если вкратце, этот новый режим внутри использует тот же код, что и обычный headful, и поэтому должен вылечить все болячки headless режима. А вот старый headless режим по сути был отдельным браузером со своими особенностями и багами.</p> <p>В теории вы всё ещё можете переключаться между старым и новым headless режимом. Но на практике вам придётся переключиться на новый, т.к. при обновлении Chrome и Edge перестало работать скачивание файлов в старом headless режиме.</p> <p>В общем, в Selenide 6.12.0 новый headless режим будет включаться по умолчанию.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2104">issue 2104</a>. Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/2105">PR 2105</a> и <a href="https://github.com/selenide/selenide/pull/2169">PR 2169</a>.</p> <p><br /></p> <h3 id="improve-logs-when-downloading-file">Улучшили логирование при скачивании файлов</h3> <p>Если скачивание файла падает (особенно методом <code class="language-plaintext highlighter-rouge">FOLDER</code>), иногда бывает сложно понять, почему именно. Мы немного доработали логирование внутри метода <code class="language-plaintext highlighter-rouge">$.download()</code>, так что теперь должно стать полегче.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2167">PR 2167</a>.</p> <p><br /></p> <h3 id="improve-download-in-edge">Улучшили скачивание файлов в Edge под Windows</h3> <p>До сих пор селенид отслеживал только временные файлы “<em>.crdownload”, которые хромиум использует при скачивании файлов. Но оказалось, что на Windows браузер Edge создаёт ещё и временные файлы “</em>.tmp”. Теперь селенид отслеживает и их.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2167">PR 2167</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>bump Selenium from 4.8.0 to 4.8.1, см. <a href="https://github.com/selenide/selenide/pull/2161">PR 2161</a> и <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a>.</li> <li>Bump nettyVersion from 4.1.87.Final to 4.1.89.Final, см. <a href="https://github.com/selenide/selenide/pull/2158">PR 2158</a>.</li> </ul> <p><br /></p> <h3 id="rename-master-to-main">Переименовали ветку <code class="language-plaintext highlighter-rouge">master</code> в <code class="language-plaintext highlighter-rouge">main</code></h3> <p>Да-да, это отголоски того самого BLM. Один умный человек убедил меня, что стоит это сделать. :)</p> <p><br /></p> <h3 id="news">News</h3> <ul> <li>Пост <a href="https://oleksandr-podoliako.medium.com/test-automation-framework-for-ui-testing-with-java-fddd1e3fd75b">Test automation framework for UI testing with java</a> by Oleksandr Podoliako</li> <li>Пост <a href="https://pradappandiyan.medium.com/running-test-automation-with-selenide-on-gitlab-fb13c0a0dddf">Running test automation with Selenide on GitLab</a> by Pradap Pandiyan</li> <li>Моё видео с Continuous Testing Meetup: <a href="https://www.youtube.com/watch?v=5qiuRoUcICs&amp;t=48m02s">Selenide UI tests in java</a>, 23.01.2023</li> <li>Видео <a href="https://www.youtube.com/watch?v=G7iYLr_IgGA&amp;ab_channel=FullStackQA">Автотесты с нуля для начинающих Java + Selenide + TestNG + Maven</a> на канале FullStackQA</li> </ul> <p><br /> Ну а почему же я назвал этот день праздничным? Да потому, что 24 февраля вообще-то День Независимости Эстонии. 105 лет назад эстонские войска напихали оркам в панамку и провозгласили новое независимое государство.</p> <center> <img src="/images/2023/02/independence-day-estonia.png" width="300" /> </center> <blockquote> <p>Если бы не те бравые ребята на бронепоезде, не было бы сейчас Селенида. :)</p> </blockquote> <p><br /></p> <h1 id="upd-вышла-selenide-6121">UPD Вышла Selenide 6.12.1</h1> <p>Исправили старинную багу в методе <code class="language-plaintext highlighter-rouge">using</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/milestone/176?closed=1">changelog</a></p> <p><br /></p> <h1 id="upd-вышла-selenide-6122">UPD Вышла Selenide 6.12.2</h1> <p>По-быстрому залепили костыль для баги в Chromedriver 111:<br /> <code class="language-plaintext highlighter-rouge">Invalid Status code=403 text=Forbidden</code></p> <p>См. <a href="https://github.com/selenide/selenide/milestone/178?closed=1">changelog</a></p> <p><br /></p> <h1 id="upd-вышла-selenide-6123">UPD Вышла Selenide 6.12.3</h1> <p>Исправили одну старую багу в методе <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code>, если его вызвать после <code class="language-plaintext highlighter-rouge">using</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/milestone/179?closed=1">changelog</a></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/02/24/selenide-6.12.0/ http://ru.selenide.org/2023/02/24/selenide-6.12.0 2023-02-24T00:00:00+00:00 Вышла Selenide 6.11.1 <p>Хаюшки!</p> <p>Одна моя знакомая из Италии рассказала, что вышел новый релиз <a href="https://github.com/selenide/selenide/milestone/174?closed=1">Selenide 6.11.1</a>.</p> <ul class="blogpost-menu"> <li><a href="#truncate-webdriver-exception-message">Обрезаем только WebDriverException</a></li> <li><a href="#fix-download-to-folder">Чутка подправили $.download(FOLDER)</a></li> <li><a href="#update-dependencies">Обновили зависимости</a></li> <li><a href="#statistics">Статистика</a></li> </ul> <h3 id="truncate-webdriver-exception-message">Обрезаем только сообщения <code class="language-plaintext highlighter-rouge">WebDriverException</code></h3> <p>В довольно редких ситуациях - если вы</p> <ul> <li>используете кастомное действие (custom command),</li> <li>и из него кидаете какую-то свою ошибку (assertion error),</li> <li>и текст этой ошибки содержит несколько строк,</li> </ul> <p>то Селенид обрезает этот текст, оставляя лишь первую строку. Изначально планировалось обрезать только текст <code class="language-plaintext highlighter-rouge">WebDriverException</code>, потому что он объективно содержит многострочный мусор:</p> <blockquote> <p>The element could not be found (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 21 milliseconds For documentation on this error … Build info: version: ‘2.29.1’, … System info: os.name: ‘Linux’, … Session ID: 610138404f5c18… Driver info: org.openqa.selenium.chrome.ChromeDriver</p> </blockquote> <p>Но как-то так вышло, что селенид обрезал текст не только <code class="language-plaintext highlighter-rouge">WebDriverException</code>, а вообще всех ошибок. Теперь подправили, и свой многострочный текст вы увидите целиком.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/2131">PR 2131</a>.</p> <p><br /></p> <h3 id="fix-download-to-folder">Чутка подправили <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code></h3> <p>Это очень редко случай, так что вы наверняка этого не замечали.</p> <p>Но наши тесты изредка моргали, и я покопал-покопал, да и раскопал парочку редких багов в методе <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code>. Иногда он мог кинуть ошибку, что файл не скачался (хоть он на самом деле скачался), в двух случаях:</p> <ul> <li> <p><a href="https://github.com/selenide/selenide/pull/2116">#2116</a> Если время изменения оказалось в предыдущей секунде от начала скачивания<br /> (такое бывает, т.к. разные файловые системы выдают время изменения файла с погрешностью до секунды, и иногда оно может даже оказаться в прошлом)</p> </li> <li> <p><a href="https://github.com/selenide/selenide/pull/2119">#2119</a> Если ОС вернула время последнего изменения файла “0”<br /> (оказываете, и такое бывает - если файловая система почему-то решила, что имя файла некорректное)</p> </li> </ul> <p>Теперь должно качаться как намасленное.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>bump WebdriverManager from 5.3.1 to 5.3.2</li> <li>bump Netty from 4.1.86.Final to 4.1.87.Final, см. <a href="https://github.com/selenide/selenide/pull/2126">PR 2126</a>.</li> </ul> <p><br /></p> <h3 id="news">News</h3> <ul> <li>Пост от Amuthan Sakthivel <a href="https://creatingvalue.substack.com/p/why-we-chose-selenide-over-selenium">Почему мы выбрали Селенид, а не Селениум</a></li> <li>Свежая статья на хабре <a href="https://habr.com/ru/company/rostelecom/blog/707710/">Как написать UI-автотесты, если не умеешь программировать?</a></li> <li>Мой доклад в девклубе <a href="https://www.youtube.com/watch?v=VtX7IpCHMS8&amp;ab_channel=DEVCLUB.EU">Как законтрибьютить в опенсорс, чтобы не сгореть со стыда</a></li> <li>Мой доклад в девклубе <a href="https://www.youtube.com/watch?v=JKxzELiwO_o&amp;ab_channel=DEVCLUB.EU">WTF Thread Pools</a></li> </ul> <p><br /></p> <h3 id="statistics">Статистика</h3> <p>Количество ежемесячных скачиваний Селенида перевалило за 490 тыщ!</p> <center> <img src="/images/2023/01/selenide.downloads.png" width="800" /> </center> <p>Поднажмём…</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/01/20/selenide-6.11.1/ http://ru.selenide.org/2023/01/20/selenide-6.11.1 2023-01-20T00:00:00+00:00 Вышла Selenide 6.11.0 <center> <img src="/images/2023/01/selenide-6.11.0.png" width="700" /> </center> <p><br /></p> <h1 id="с-новым-годом-друзья">С Новым Годом, друзья!</h1> <p>Дедушка Мороз принёс нам новый релиз <a href="https://github.com/selenide/selenide/milestone/169?closed=1">Selenide 6.11.0</a>.</p> <ul class="blogpost-menu"> <li><a href="#added-copy-paste-methods">Методы для копирования текста</a></li> <li><a href="#fix-download-with-credentials">Скачивание файла за BasicAuth</a></li> <li><a href="#download-large-files-via-proxy">Скачиваем большие файлы через прокси</a></li> <li><a href="#can-handle-unexpected-alerts">Неожиданные алерты</a></li> <li><a href="#fix-screenshot-file-permission">Пермиссии файла скриншота</a></li> <li><a href="#support-as-annotation">Аннотация @As для полей без @FindBy</a></li> <li><a href="#last-page-source">Последний исходник страницы</a></li> <li><a href="#page-url-in-error-message">URL страницы в ошибке</a></li> </ul> <h3 id="added-copy-paste-methods">Добавили методы для копирования текста</h3> <p>Мы добавили два новых метода:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.copy()</code> - копирует выделенный текст в буфер обмена, и</li> <li><code class="language-plaintext highlighter-rouge">$.paste()</code> вставляет текст из буфера обмена в поле ввода.</li> </ul> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://best-propaganda-quotes.ru"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#solovjov"</span><span class="o">).</span><span class="na">doubleClick</span><span class="o">();</span> <span class="c1">// select the quote text</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">copy</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="s">"[name=q]"</span><span class="o">).</span><span class="na">paste</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="s">"[name=q]"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Обсираться мелкими алмазами на берегу озера Комо"</span><span class="o">));</span> </code></pre></div></div> <p>Известные ограничения: пока что эти методы не работают на серверных линуксах (без графического окружения). Но запуск xvfb должен помочь.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1817">issue 1817</a>.<br /> Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/2027">PR 2027</a>.</p> <p><br /></p> <h3 id="fix-download-with-credentials">Исправили метод <code class="language-plaintext highlighter-rouge">Selenide.download(url)</code></h3> <p>…для случая, когда url содержит логин/пароль (т.е. ресурс защищён BasicAuth).</p> <p>Например, такой код раньше работал, но сломался после обновления Apache Http client:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">download</span><span class="o">(</span><span class="s">"https://admin:tiger@the-internet.herokuapp.com/basic_auth"</span><span class="o">);</span> </code></pre></div></div> <p>Оказалось, что в Apache Http client усилили валидацию URL, и часть с логином и паролем больше не разрешена (это типа небезопасная практика).</p> <p>Ну а селенид теперь вырезает эту часть и посылает с запросом в заголовке <code class="language-plaintext highlighter-rouge">Authorization</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2037">issue 2037</a> и <a href="https://github.com/selenide/selenide/pull/2102">PR 2102</a>.</p> <p><br /></p> <h3 id="download-large-files-via-proxy">Позволяем скачивать большие файлы через прокси</h3> <p>Если вы скачиваете файлы методом <code class="language-plaintext highlighter-rouge">PROXY</code>, то размер файла не мог превышать 64 мегабайта. Изначально это казалось разумным ограничением: зачем кому-то нужно нагружать тестовый стенд, скачивая гигантские файлы? (64 и то многовато, по умолчанию в BrowserUpProxy вообще было 2 мегабайта; мы увеличили лимит до 64 в селениде)</p> <p>Но оказалось, что иногда люди хотят тестировать именно скачивание больших файлов. После долгих споров мы решили просто убрать это ограничение. Вам виднее, качайте сколько хотите.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">favoriteMovie</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#topMovie"</span><span class="o">).</span><span class="na">download</span><span class="o">();</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">favoriteMovie</span><span class="o">)</span> <span class="o">.</span><span class="na">hasName</span><span class="o">(</span><span class="s">"BadSanta.avi"</span><span class="o">)</span> <span class="o">.</span><span class="na">as</span><span class="o">(</span><span class="s">"2 GB"</span><span class="o">).</span><span class="na">hasSize</span><span class="o">(</span><span class="mi">2147483648L</span><span class="o">);</span> </code></pre></div></div> <p>P.S. Но имейте в виду, со слишком большими файлами, скорее всего, прокси всё равно не справится (либо будет скачивать слишком долго). И BrowserUpProxy по-прежнему имеет встроенное техническое ограничение в 2 гигабайта (потому, что тип данных - Integer).</p> <p>Но хотя бы файлы по 200 мегабайтов вы вполне сможете скачивать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2082">issue 2082</a> и <a href="https://github.com/selenide/selenide/pull/2098">PR 2098</a>.</p> <p><br /></p> <h3 id="can-handle-unexpected-alerts">Можно обрабатывать неожиданные алерты</h3> <p>По умолчанию, селенид игнорирует неожиданные алерты в браузере (точнее, запускает вебдрайвер с опцией <code class="language-plaintext highlighter-rouge">capabilities.setCapability(UNHANDLED_PROMPT_BEHAVIOUR, ACCEPT)</code>).</p> <p>С одной стороны, это удобно, чтобы тесты не ломались от внезапно всплывающих реклам и других бесполезных сообщений. С другой стороны, иногда эти алерты могут содержать полезную информацию - в частности, ценное сообщение об ошибке.</p> <p>Если это ваш случай, теперь вы можете переопределить эту настройку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">remote</span><span class="o">.</span><span class="na">CapabilityType</span><span class="o">.</span><span class="na">UNHANDLED_PROMPT_BEHAVIOUR</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">UnexpectedAlertBehaviour</span><span class="o">.</span><span class="na">ACCEPT_AND_NOTIFY</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="no">UNHANDLED_PROMPT_BEHAVIOUR</span><span class="o">,</span> <span class="no">ACCEPT_AND_NOTIFY</span><span class="o">);</span> </code></pre></div></div> <p>И тогда при возникновении нежданных алертов вы узнаете, какой текст в них был:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">UnhandledAlertException:</span> <span class="n">unexpected</span> <span class="n">alert</span> <span class="nl">open:</span> <span class="o">{</span><span class="nc">Alert</span> <span class="n">text</span> <span class="o">:</span> <span class="nc">Как</span> <span class="n">жопа</span><span class="o">,</span> <span class="n">рогозин</span><span class="o">?}</span> <span class="c1">// chrome</span> <span class="nl">UnhandledAlertException:</span> <span class="nc">Accepted</span> <span class="n">user</span> <span class="n">prompt</span> <span class="nl">dialog:</span> <span class="nc">Маршрут</span> <span class="n">через</span> <span class="n">мост</span> <span class="n">недоступен</span><span class="o">.</span> <span class="nc">Чот</span> <span class="n">бахнуло</span><span class="o">.</span> <span class="c1">// firefox</span> <span class="nl">UnhandledAlertException:</span> <span class="n">unexpected</span> <span class="n">alert</span> <span class="nl">open:</span> <span class="o">{</span><span class="nc">Alert</span> <span class="n">text</span> <span class="o">:</span> <span class="nc">Обнаружены</span> <span class="n">боевые</span> <span class="n">комары</span><span class="o">-</span><span class="n">убийцы</span><span class="o">}</span> <span class="c1">// edge</span> <span class="nl">UnhandledAlertException:</span> <span class="o">:</span> <span class="nc">Обнаружена</span> <span class="n">грязная</span> <span class="n">бомба</span> <span class="c1">// safari</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2054">issue 2054</a> и <a href="https://github.com/selenide/selenide/pull/2095">PR 2095</a>.</p> <p><br /></p> <h3 id="fix-screenshot-file-permission">Исправили пермиссии файла скриншота</h3> <p>Как вы знаете, при падении теста селенид автоматически сохраняет скриншот и создаёт два файла: <code class="language-plaintext highlighter-rouge">*.png</code> и <code class="language-plaintext highlighter-rouge">*.html</code>. Так вот выяснилось, что селенид создаёт эти два файла с разными пермиссиями:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-rw------- 1 root root 300295 Dec 19 10:24 1671441847908.0.png -rw-r--r-- 1 root root 185070 Dec 19 10:24 1671441847908.0.html </code></pre></div></div> <p>И кое-кому это помешало писать сложносочинённые CI скрипты, выполняющие команды из-под разных юзеров. <em>Чем бы девопс не тешился… :)</em></p> <p>В общем, теперь у обоих будет “обычные” пермиссии <code class="language-plaintext highlighter-rouge">-rw-r--r--</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2081">issue 2081</a> и <a href="https://github.com/selenide/selenide/pull/2084">PR 2084</a>.</p> <p><br /></p> <h3 id="support-as-annotation">Поддержка аннотации <code class="language-plaintext highlighter-rouge">@As</code> для полей без <code class="language-plaintext highlighter-rouge">@FindBy</code></h3> <p>В селениде уже давно можно задавать удобные имена элементам пэдж обжекты (т.н. “алиасы”) с помощью метода <code class="language-plaintext highlighter-rouge">as</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="o">{</span> <span class="nc">SelenideElement</span> <span class="n">loginButton</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Но кому-то показалось, что удобно было бы повесить имя элемента в начало строки, а то справа от длинного селектора оно визуально теряется.</p> <p>В общем, теперь алиас можно задать и с помощью аннотации <code class="language-plaintext highlighter-rouge">@As</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">LoginPage</span> <span class="o">{</span> <span class="nd">@As</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">)</span> <span class="nc">SelenideElement</span> <span class="n">loginButton</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[3]"</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <p>Но естественно, аннотация сработает, только если вы инициализируете пэдж обжект с помощью метода <code class="language-plaintext highlighter-rouge">Selenide.page()</code> или <code class="language-plaintext highlighter-rouge">Selenide.open()</code></p> <p>См. <a href="https://github.com/selenide/selenide/issues/2087">issue 2087</a> и <a href="https://github.com/selenide/selenide/pull/2088">PR 2088</a>.</p> <p><br /></p> <h3 id="last-page-source">Метод для последнего сохранённого исходника страницы</h3> <p>Редко когда это нужно, так что не заморачивайтесь. По сути добавили методы <code class="language-plaintext highlighter-rouge">ScreenShotLaboratory</code>: <code class="language-plaintext highlighter-rouge">threadScreenshots()</code>, <code class="language-plaintext highlighter-rouge">contextScreenshots()</code>, <code class="language-plaintext highlighter-rouge">lastThreadScreenshot()</code>, <code class="language-plaintext highlighter-rouge">lastContextScreenshot()</code>.</p> <p>Спасибо <a href="https://github.com/armanayvazyan">Arman Ayvazyan</a> за <a href="https://github.com/selenide/selenide/pull/2065">PR 2065</a>.</p> <p><br /></p> <h3 id="page-url-in-error-message">Теперь можно добавлять URL страницы в сообщение об ошибке</h3> <p>Кому-то это полезно, кому-то вредно. После долгих споров сделали эту возможность опциональной (в виде плагина).</p> <p>Пусть пока побудет в статусе экспериментальной возможности. Вероятно, с форматами сообщений об ошибках мы ещё будем играться.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/980">issue 980</a> и <a href="https://github.com/selenide/selenide/pull/2097">PR 2097</a>.</p> <p><br /></p> <h3 id="year-summary">Итоги года</h3> <p>Подведём итоги 2022 года? За этот год Selenide был упомянут в нескольких топах:</p> <ul> <li><a href="https://qameta.io/blog/5-testing-automation-tools/">5 Testing Automation Tools</a> в блоге компании Qameta Software</li> <li><a href="https://hackernoon.com/top-java-libraries-for-automation-testing-in-2022">Top Java Libraries for Automation Testing in 2022</a></li> <li><a href="https://aglowiditsolutions.com/blog/top-java-frameworks/">Top Java Frameworks to Use in 2022</a></li> </ul> <p>И наконец,</p> <ul> <li>Selenide упомянут на <a href="https://www.selenium.dev/ecosystem/">официальном сайте Selenium</a> в разделе “Экосистема”.</li> </ul> <p>Количество скачиваний селенида выросло аж <strong>в полтора раза</strong>:</p> <blockquote> <p>с 302 тысяч в январе до 469 тысяч в ноябре.</p> </blockquote> <h3 id="new-year-wishes">И всё-таки</h3> <p>Это был ужасный год. Но я рад, что я смог использовать накопленный багаж в виде сайта selenide.org и его аудитории, чтобы распространять антивоенную позицию. Кажется, здесь собрались люди, которые эту позицию разделяют. Чему я тоже очень рад.</p> <p>Надеюсь, в наступившем году война закончится, виновные будут наказаны, граждане угнетённых стран сбросят наконец своих тиранов. А сомневающиеся вытащат наконец голову из жопы.</p> <p>А прекрасная свободная страна Украина отстроится и будет цвести пуще прежнего.</p> <blockquote> <p>И через год мы с вами увидимся в Киеве на замечательной <a href="https://seleniumcamp.com/">конференции SeleniumCamp</a>.</p> </blockquote> <p>Героям слава!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2023/01/03/selenide-6.11.0/ http://ru.selenide.org/2023/01/03/selenide-6.11.0 2023-01-03T00:00:00+00:00 Вышла Selenide 6.10.2 <p><br /></p> <h1 id="всем-привет">Всем привет!</h1> <p>У нас вышел новый мини-релиз <a href="https://github.com/selenide/selenide/milestone/171?closed=1">Selenide 6.10.2</a>.</p> <ul class="blogpost-menu"> <li><a href="#added-method-press">Добавили метод $.press()</a></li> <li><a href="#trigger-change-events-by-select-methods">Генерируем события change в методах $.select*</a></li> <li><a href="#friendly-select-option-in-reports">selectOption в отчётах</a></li> <li><a href="#friendly-local-storage-in-reports">localStorage в отчётах</a></li> <li><a href="#news">Новости</a></li> </ul> <h3 id="added-method-press">Добавили метод <code class="language-plaintext highlighter-rouge">$.press()</code></h3> <p>По сути это то же самое, что <code class="language-plaintext highlighter-rouge">sendKeys()</code>, только он не <code class="language-plaintext highlighter-rouge">void</code>. То есть его можно <em>чейнить</em> с другими методами:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">)</span> <span class="o">.</span><span class="na">press</span><span class="o">(</span><span class="s">"x"</span><span class="o">)</span> <span class="o">.</span><span class="na">press</span><span class="o">(</span><span class="no">TAB</span><span class="o">,</span> <span class="no">CONTROL</span><span class="o">,</span> <span class="no">ALT</span><span class="o">,</span> <span class="no">ENTER</span><span class="o">)</span> <span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> за <a href="https://github.com/selenide/selenide/pull/2032">PR 2032</a>.</p> <p><br /></p> <h3 id="trigger-change-events-by-select-methods">Генерируем события <code class="language-plaintext highlighter-rouge">change</code> в методах <code class="language-plaintext highlighter-rouge">$.select*</code></h3> <p>Как вы помните, в предыдущем релизе мы <a href="/2022/11/21/selenide-6.10.0/#select-options-using-javascript">переделали работу с селектами на JavaScript</a>. Но при этом забыли, что надо ещё и генерировать события <code class="language-plaintext highlighter-rouge">focus</code>, <code class="language-plaintext highlighter-rouge">click</code>, <code class="language-plaintext highlighter-rouge">change</code>. Теперь генерируем.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/2050">issue 2050</a>. Спасибо <a href="https://github.com/cocorossello">Vicente Rossello Jaume</a> за <a href="https://github.com/selenide/selenide/pull/2051">PR 2051</a>.</p> <p>UPD. Исправили ещё раз в <a href="https://github.com/selenide/selenide/milestone/172?closed=1">Selenide 6.10.3</a>.</p> <p><br /></p> <h3 id="friendly-select-option-in-reports">Показываем <code class="language-plaintext highlighter-rouge">$.selectOption()</code> в отчётах по-человечески</h3> <p>Ещё один косячок, вылезший после предыдущего рефакторинга селектов: в отчётах выскочили нечитаемые параметры. Это всё потому, что в Java у массивов нет стандартного метода <code class="language-plaintext highlighter-rouge">toString()</code>, приходится изобретать велосипед.</p> <p>Было:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| #blockChannel | select option([Канал Дождь, [Ljava.lang.String;@6732726]) | PASS | 487 | </code></pre></div></div> <p>Стало:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| #blockChannel | select option(Канал Дождь) | PASS | 487 | </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2047">issue 2047</a> и <a href="https://github.com/selenide/selenide/pull/2052">PR 2052</a>.</p> <p><br /></p> <h3 id="friendly-local-storage-in-reports">Показываем <code class="language-plaintext highlighter-rouge">localStorage</code> в отчётах по-человечески</h3> <p>Почти такая же проблема: операции с <code class="language-plaintext highlighter-rouge">sessionStorage</code> и <code class="language-plaintext highlighter-rouge">localStorage</code> выглядели в отчётах нечитабельно.</p> <p>Было:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| com.codeborne.selenide.LocalStorage@138a952f | set item(['Бут', 9125]) | | com.codeborne.selenide.SessionStorage@549w123gg | set item(['Грайнер', 3285]) | </code></pre></div></div> <p>Стало:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| localStorage | set item(['Бут', 9125]) | | sessionStorage | set item(['Грайнер', 3285]) | </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2045">issue 2045</a> и <a href="https://github.com/selenide/selenide/pull/2046">PR 2046</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>#2044 #2057 bump Selenium from 4.6.0 to 4.7.1</li> <li>#2036 bump browserup-proxy-core from 2.2.5 to 2.2.6</li> <li>#2058 bump httpclient5 from 5.2 to 5.2.1</li> <li>bump slf4j from 2.0.4 to 2.0.5</li> </ul> <p><br /></p> <h3 id="дочерние-проекты">Дочерние проекты</h3> <p>Также зарелизили наши дочерние проекты:</p> <ul> <li><a href="https://github.com/selenide/selenide-appium/releases/tag/v2.4.0">selenide-appium 2.4.0</a></li> <li><a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.3.3">selenide-selenoid 2.3.3</a></li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Selenide Tutorial: <a href="https://www.youtube.com/watch?v=5vrYMfsxkGY&amp;list=PL9ok7C7Yn9A9YyRISFrxHdaxb5qqrxp_i&amp;index=4&amp;ab_channel=TestingMiniBytes">Replacement for Selenium?</a> на канале Testing Mini Bytes</li> <li><a href="https://oleksandr-podoliako.medium.com/test-automation-framework-for-ui-testing-with-java-fddd1e3fd75b">Test automation framework for UI testing with java</a> от Oleksandr Podoliako</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/12/08/selenide-6.10.2/ http://ru.selenide.org/2022/12/08/selenide-6.10.2 2022-12-08T00:00:00+00:00 Вышла Selenide 6.10.0 <p><br /></p> <h1 id="недобрый-вечер">Недобрый вечер!</h1> <p>Вышел новый релиз <a href="https://github.com/selenide/selenide/milestone/167?closed=1">Selenide 6.10.0</a>.</p> <ul class="blogpost-menu"> <li><a href="#slow-download-in-firefox">Улучшили алгоритм скачивания файлов</a></li> <li><a href="#fail-download-early">Ускорили отрицательное скачивание файла</a></li> <li><a href="#select-options-using-javascript">Выбираем опции через JavaScript</a></li> <li><a href="#make-click-chainable">Сделали клик “чейнебл”</a></li> <li><a href="#fix-size-for-new-tabs">Размер окна для новых вкладок</a></li> <li><a href="#encode-basic-auth-credentials-in-url">BasicAuth со спецсимволами</a></li> <li><a href="#news">Новости</a></li> </ul> <h3 id="slow-download-in-firefox">Улучшили алгоритм скачивания файлов</h3> <p>Один из алгоритмов скачивания в Selenide - это FOLDER. Чтобы скачать файл, он кликает нужную кнопку и ждёт, пока в папке “Downloads” появится нужный файл.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">еноты</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#stolenRaccoonsReport"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">));</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">еноты</span><span class="o">).</span><span class="na">hasName</span><span class="o">(</span><span class="s">"еноты.xls"</span><span class="o">);</span> </code></pre></div></div> <p>Проблема в том, что этот алгоритм не очень хорошо работал в Firefox, когда скачивание идёт очень медленно. Оказалось, что Firefox сразу создаёт в папке два файла: “еноты.xls” и “еноты.xls.part”, и оба пустые. И лишь затем начинает их потихоньку заполнять.</p> <p><br /> В общем, наш алгоритм стал умнее. Теперь он</p> <ol> <li>Ждёт, когда в папке появятся хоть какие-нибудь подходящие файлы.</li> <li>Ждёт, когда пропадут все файлы <code class="language-plaintext highlighter-rouge">*part</code> (в Firefox).</li> <li>Ждёт, когда пропадут все файлы <code class="language-plaintext highlighter-rouge">*crdownload</code> (в Chrome).</li> <li>Ждёт, когда все файлы замрут как минимум на секунду (в остальных браузерах).</li> </ol> <p>Такой подход должен обеспечить более надёжное скачивание файлов.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1989">issue 1989</a> и <a href="https://github.com/selenide/selenide/pull/2003">PR 2003</a>.</p> <p><br /></p> <h3 id="fail-download-early">Ускорили отрицательное скачивание файла</h3> <p>Часто люди выставляют большой таймаут на скачивание файла. Особенно если файл большой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">video</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#skaebova"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">)</span> <span class="o">.</span><span class="na">withTimeout</span><span class="o">(</span><span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">99</span><span class="o">))</span> <span class="o">.</span><span class="na">withName</span><span class="o">(</span><span class="s">"пыня мобшиза.MP4"</span><span class="o">));</span> </code></pre></div></div> <p>Но незачем ждать так долго, если скачивание даже не началось. Например, если клик тупо не попал по кнопке (как это бывает, я рассказывал в <a href="https://www.youtube.com/watch?v=elQ2LGGU2bg&amp;ab_channel=DEVCLUB.EU&amp;t=21m32s">видео “Flaky tests”</a>.</p> <p>Для ускорения падения теперь можно задать второй параметр “increment timeout”:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">video</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#skaebova"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">)</span> <span class="o">.</span><span class="na">withTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">99</span><span class="o">))</span> <span class="o">.</span><span class="na">withIncrementTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">2</span><span class="o">))</span> <span class="o">.</span><span class="na">withName</span><span class="o">(</span><span class="s">"пыня мобшиза.MP4"</span><span class="o">));</span> </code></pre></div></div> <p>В этом случае общий таймаут на скачивание 99 секунд, НО если в течение 2 секунд не было никаких изменений в папке для скачивания, то метод выкинет ошибку сразу.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1990">issue 1990</a> и <a href="https://github.com/selenide/selenide/pull/2023">PR 2023</a>.</p> <p><br /></p> <h3 id="select-options-using-javascript">Выбираем опции в <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> через JavaScript</h3> <p>Это должно сделать работу с селектами быстрее. И теперь селенид выкидывает более подробную ошибку, если <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> (или <code class="language-plaintext highlighter-rouge">&lt;option&gt;</code>) оказался <code class="language-plaintext highlighter-rouge">disabled</code>.</p> <p>Например, для такого селекта:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">"region"</span><span class="nt">&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"belgorod"</span><span class="nt">&gt;</span>Belgorod<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"kherson"</span> <span class="na">disabled</span><span class="nt">&gt;</span>Kherson<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"zaporozhia"</span> <span class="na">disabled</span><span class="nt">&gt;</span>Zaporozhia<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;/select&gt;</span> </code></pre></div></div> <p>Попытка выбрать <code class="language-plaintext highlighter-rouge">disabled</code> опцию:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#region"</span><span class="o">).</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"Kherson"</span><span class="o">);</span> </code></pre></div></div> <p>выдаст понятную ошибку с подробным объяснением:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Invalid</span> <span class="n">element</span> <span class="n">state</span> <span class="o">[</span><span class="err">#</span><span class="n">region</span><span class="o">/</span><span class="n">option</span><span class="o">[</span><span class="nl">text:</span><span class="nc">Kherson</span><span class="o">]]:</span> <span class="nc">Cannot</span> <span class="n">select</span> <span class="n">a</span> <span class="n">disabled</span> <span class="n">option</span> </code></pre></div></div> <p>Раньше эта ошибка была не такой подробной:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">UnsupportedOperationException</span><span class="o">:</span> <span class="nc">You</span> <span class="n">may</span> <span class="n">not</span> <span class="n">select</span> <span class="n">a</span> <span class="n">disabled</span> <span class="n">option</span> </code></pre></div></div> <p>(а ещё раньше такой тест вообще не падал, но и опцию не выбирал)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1553">issue 1553</a> и <a href="https://github.com/selenide/selenide/pull/1876">PR 1876</a>.</p> <blockquote> <p>Отдельное спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1553">PR 1553</a>, который хоть и не попал в селенид, но инициировал целую дискуссию и в селениде, и в самом селениуме, в т.ч. о том, за что должен или не должен отвечать селениум, и что такое “просто фреймворк” и “опионейтед фреймворк”.</p> </blockquote> <p><br /></p> <h3 id="make-click-chainable">Сделали <code class="language-plaintext highlighter-rouge">$.click(options)</code> “чейнебл”</h3> <p>Люди часто жалуются, что метод <code class="language-plaintext highlighter-rouge">$.click()</code> имеет тип <code class="language-plaintext highlighter-rouge">void</code>, то есть его нельзя чейнить:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">click</span><span class="o">().</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span> </code></pre></div></div> <p>Увы, это мы исправить не можем, т.к. класс <code class="language-plaintext highlighter-rouge">SelenideElement</code> наследует метод <code class="language-plaintext highlighter-rouge">void click()</code> из селениумовского <code class="language-plaintext highlighter-rouge">WebElement</code>.</p> <p>Но мы сделали “чейнебл” другой <code class="language-plaintext highlighter-rouge">click</code> метод, который с параметрами (т.н. “оверлоадед”). Теперь хотя бы его можно “чейнить”:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">)</span> <span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">())</span> <span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">)</span> <span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">withOffset</span><span class="o">(</span><span class="mi">42</span><span class="o">,</span> <span class="mi">42</span><span class="o">))</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"alert"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2007">issue 2007</a> и <a href="https://github.com/selenide/selenide/pull/2008">PR 2008</a>.</p> <p><br /></p> <h3 id="fix-size-for-new-tabs">Исправили размер окна для новых вкладок</h3> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/2017">PR 2017</a>.</p> <p>P.S. Исправили ещё раз в <a href="https://github.com/selenide/selenide/milestone/170?closed=1">Selenide 6.10.1</a></p> <p><br /></p> <h3 id="encode-basic-auth-credentials-in-url">Поддерживаем логин/пароль BasicAuth со спецсимволами</h3> <p>Selenide умеет открывать сайты, защищённые паролем (т.н. BasicAuth):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">""</span><span class="o">,</span> <span class="s">"Královec"</span><span class="o">,</span> <span class="s">"is Czechia /:)"</span><span class="o">));</span> </code></pre></div></div> <p>Эти логин-пароль добавляются либо в http заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> (если прокси включён), либо в URL (если прокси выключен).</p> <p>Недавно я обнаружил, что второй способ работает некорректно, если логин или пароль содержат спецсимволы. При добавлении в URL эти символы не экранировались, и получался невалидный URL:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://Královec:is Czechia /:)@127.0.0.1:4405/basic-auth/hello </code></pre></div></div> <p>и браузер не мог открыть страницу.</p> <blockquote> <p>Мне по-настоящему неловко, что я как-то не увидел этой страницы. Надеюсь, вы меня простите.</p> </blockquote> <p>В общем, теперь такие хитрые пароли тоже должны работать. Селенид экранирует все спецсимволы и генерирует корректный URL:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://Kr%C3%A1lovec:is+Czechia+%2F%3A%29@127.0.0.1:27663/basic-auth/hello </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/2020">issue 2020</a> и <a href="https://github.com/selenide/selenide/pull/2021">PR 2021</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>Selenium from 4.5.0 to 4.6.0, см. <a href="https://www.selenium.dev/blog/2022/selenium-4-6-0-released/">ченджлог</a></li> <li>WebDriverManager from 5.3.0 to 5.3.1, см. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md#531---2022-11-04">ченджлог</a></li> <li>BrowserUpProxy from 2.2.3 to 2.2.5, см. <a href="https://github.com/valfirst/browserup-proxy/blob/master/CHANGELOG.md">ченджлог</a></li> <li>Netty from 4.1.82.Final to 4.1.85.Final</li> <li>LittleProxy from 2.0.13 to 2.0.14, см. <a href="https://github.com/LittleProxy/LittleProxy/milestone/19?closed=1">ченджлог</a></li> <li>#2014 Bump httpclient5 from 5.1.3 to 5.2, см. <a href="https://github.com/selenide/selenide/pull/2014">PR 2014</a></li> <li>#2025 bump slf4j from 2.0.3 to 2.0.4, см. <a href="https://github.com/selenide/selenide/pull/2025">PR 2025</a></li> </ul> <p><br /></p> <h3 id="дочерние-проекты">Дочерние проекты</h3> <p>Также зарелизили наши дочерние проекты:</p> <ul> <li><a href="https://github.com/selenide/selenide-appium/releases/tag/v2.3.0">selenide-appium 2.3.0</a></li> <li><a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.3.2">selenide-selenoid 2.3.2</a></li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>JetBrains выпустила AQUA - новую <a href="https://www.jetbrains.com/aqua/">IDE для автотестов</a>! Клёвое лого!</li> <li>В Selenium появился свой встроенный <a href="https://www.selenium.dev/blog/2022/introducing-selenium-manager/">аналог WebDriverManager</a>. Кто-то уже попробовал?</li> <li>Новый фреймворк <a href="https://gitlab.com/brewcode/selenide-pages">Selenide Pages</a> на базе Selenide от Maxim Kochetkov. Клёвое лого!</li> </ul> <h3 id="my-videos">Мои свежие видосы</h3> <p>Появилось видео моих осенних докладов из таллиннского девклуба:</p> <ul> <li><a href="https://www.youtube.com/watch?v=VtX7IpCHMS8&amp;ab_channel=DEVCLUB.EU">Как законтрибьютить в опенсорс, чтобы не сгореть со стыда</a></li> <li><a href="https://www.youtube.com/watch?v=JKxzELiwO_o&amp;ab_channel=DEVCLUB.EU">WTF Thread Pools</a> - как накосячить с потоками в Java (с зубодробительными примерами из Selenium)</li> </ul> <h3 id="videos">Видосы и читосы</h3> <ul> <li>Видос <a href="https://www.youtube.com/watch?v=_X0rL1eUMK0&amp;ab_channel=OlehPendrak">Чтение логов из браузера через Selenide</a> от Oleh Pendrak</li> <li>Пост <a href="https://www.linkedin.com/pulse/all-selenide-muhammad-naeem/">All About Selenide</a> от Muhammad Naeem</li> <li>Видос <a href="https://morioh.com/p/018678871de9">Working with web elements using Selenide</a> от Automation Bro</li> <li>Пост <a href="https://dev.to/automationbro/page-object-model-selenide-tutorial-series-g3g">Page Object Model</a> из серии “Selenide Tutorial Series” от Automation Bro</li> <li>Пост <a href="https://habr.com/ru/company/innotech/blog/696140/">Как кастомизировать UI артефакты для Selenide + Selenoid + Allure (with TestOPS)</a> на Хабре</li> </ul> <p><br /></p> <h3 id="statistics">Статистика</h3> <ul> <li>Наша <a href="https://www.linkedin.com/groups/9154550/">группа в LinkedIn</a> насчитывает уже 176 участников. Присоединяйтесь!</li> <li>Подъехала свежая статистика скачиваний Селенида. Перевалили за 470 тыщ!</li> </ul> <center> <img src="/images/2022/11/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/11/21/selenide-6.10.0/ http://ru.selenide.org/2022/11/21/selenide-6.10.0 2022-11-21T00:00:00+00:00 Вышла Selenide 6.9.0 <p><br /></p> <h1 id="привет">Привет!</h1> <p>Позвольте прервать ваш думскроллинг хорошей новостью.</p> <p>Вы выпустили <a href="https://github.com/selenide/selenide/milestone/166?closed=1">Selenide 6.9.0</a>! В основном прокачали прокси и обновили селениум.</p> <ul class="blogpost-menu"> <li><a href="#proxy-mock-response">Подменяем ответ сервера в прокси</a></li> <li><a href="#secure-authorization-header">Заголовок авторизации</a></li> <li><a href="#resolve-proxy-hostname">Подкрутили имя прокси хоста</a></li> <li><a href="#upgrade-to-selenium-4.5.0">Обновились на Selenium 4.5.0</a></li> <li><a href="#remove-opera-support">Выкинули поддержку Opera</a></li> <li><a href="#news">Новости</a></li> </ul> <h3 id="proxy-mock-response">Научили прокси подменять ответ сервера</h3> <p>Как вы знаете, селенид умеет запускать свой встроенный прокси между браузером и тестируемым приложением. До сих пор мы использовали его в основном для отслеживания запросов (логирования, скачивания файлов), но теперь ещё и добавили возможность подменить ответ сервера. Это удобно, например, чтобы замокать ответ какого-то сервиса.</p> <p>Давайте рассмотрим на каком-нибудь простеньком абстрактном примере.</p> <h4 id="пример">Пример</h4> <p>Допустим, вы тестируете сайт, показывающий результаты референдума. Это html-страница по адресу, скажем, https://referendum.ru, которая обращается к сервису https://cik.ru и тянет с него json с результатами голосования.</p> <p>Поскольку результаты заранее неизвестны, а сайт протестировать нужно здесь и сейчас, мы хотим проверить, как он будет выглядеть в разных пограничных случаях (corner cases).</p> <h4 id="тест">Тест</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">();</span> <span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">responseMocker</span><span class="o">().</span><span class="na">mockText</span><span class="o">(</span><span class="s">"cik-mock"</span><span class="o">,</span> <span class="n">urlStartsWith</span><span class="o">(</span><span class="no">GET</span><span class="o">,</span> <span class="s">"https://cik.ru/api/gov/no/referendum"</span><span class="o">),</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="s">"{votes: 2133326, for: 99.23, against: 0.77}"</span><span class="o">);</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://referendum.ru"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#votesFor"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"99.23%"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#h3"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Не только порадовали, но и удивили"</span><span class="o">));</span> </code></pre></div></div> <h4 id="детали">Детали</h4> <p>Остановимся подробнее на первом параметре <code class="language-plaintext highlighter-rouge">cik-mock</code>. Это <em>имя</em> мока. Нужно оно для того, чтобы после теста его отменить (чтобы случайно не повлиять на следующие тесты):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@AfterEach</span> <span class="kt">void</span> <span class="nf">tearDown</span><span class="o">()</span> <span class="o">{</span> <span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">responseMocker</span><span class="o">().</span><span class="na">reset</span><span class="o">(</span><span class="s">"cik-mock"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Ну или для надёжности можно отменить <em>все</em> моки:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@AfterEach</span> <span class="kt">void</span> <span class="nf">tearDown</span><span class="o">()</span> <span class="o">{</span> <span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">responseMocker</span><span class="o">().</span><span class="na">resetAll</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <h4 id="ограничения">Ограничения</h4> <p>Если страница и сервис работают на одном домене, ограничений нет. А вот если страница обращается к сервису на другом домене, то мок сработает только при выполнении условий:</p> <ul> <li>страница и сервис бегают на https (не http)</li> <li>сервис работает и физически доступен из прокси</li> </ul> <h4 id="в-общем">В общем</h4> <p>Использование моков - мощная техника, позволяющая вам проверять разные пограничные условия, которые иначе повторить сложно или невозможно (“146% голосов за”, “никто не явился на выборы”, “сервис недоступен” и т.д.)</p> <blockquote> <p>Не стесняйтесь своего желания что-то подправлять, подчищать, добавлять. Будет так, как вы захотите.</p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/issues/1254">issue 1254</a> и <a href="https://github.com/selenide/selenide/pull/1978">PR 1978</a>.</p> <p><br /></p> <h3 id="secure-authorization-header">Посылаем заголовок авторизации только нужному домену</h3> <p>Некоторые сайты, которые вам приходится тестировать, закрыты от внешнего мира с помощью BasicAuth. Селенид позволяет их открыть с помощью метода open с доп. параметрами:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://referendum.ru/admin"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">"vlastelin"</span><span class="o">,</span> <span class="s">"gojda!!!"</span><span class="o">));</span> </code></pre></div></div> <p>Этот метод умеет обходить BasicAuth:</p> <ul> <li>если прокси выключен - добавлением логина-пароля в URL,</li> <li>а если прокси включен - добавлением заголовка <code class="language-plaintext highlighter-rouge">Authorization</code> в запросы от браузера к серверу.</li> </ul> <p>Но вот незадача. Недавно мы обнаружили, что селенид слал заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> не только тестируемому приложению, но и всем сторонним сервисам, к которым оно могло обращаться (например, S3 или Google authentication).</p> <p>Теперь селенид будет посылать заголовок <code class="language-plaintext highlighter-rouge">Authorization</code> только на нужный домен, но и вам придётся указать этот домен в конструкторе:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://referendum.ru/admin"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="k">new</span> <span class="nf">BasicAuthCredentials</span><span class="o">(</span><span class="s">"referendum.ru"</span><span class="o">,</span> <span class="s">"vlastelin"</span><span class="o">,</span> <span class="s">"gojda!!!"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1974">issue 1974</a> и <a href="https://github.com/selenide/selenide/pull/1975">PR 1975</a>.</p> <p><br /></p> <h3 id="resolve-proxy-hostname">Подкрутили имя прокси хоста</h3> <p>Когда вы включаете прокси в селениде (например, через <code class="language-plaintext highlighter-rouge">Configuration.proxyEnabled = true</code>), селенид запускает встроенный прокси-сервер на случайном порту. И открывает браузер с указанием использовать прокси-сервер <code class="language-plaintext highlighter-rouge">HOST:PORT</code>. Вопрос, какой HOST тут использовать?</p> <p>До сих пор по умолчанию использовался результат команды <code class="language-plaintext highlighter-rouge">ClientUtil.getConnectableAddress()</code> (это поведение по умолчанию в BrowserUpProxy). Но с этого релиза будет использоваться <code class="language-plaintext highlighter-rouge">new NetworkUtils().getNonLoopbackAddressOfThisMachine()</code> (это метод из Selenium). Мне лень разбираться, в чём там их специфика, но на моём компе они иногда выдают разный результат:</p> <ul> <li><code class="language-plaintext highlighter-rouge">new NetworkUtils().getNonLoopbackAddressOfThisMachine()</code> -&gt; <code class="language-plaintext highlighter-rouge">192.168.0.18</code></li> <li><code class="language-plaintext highlighter-rouge">ClientUtil.getConnectableAddress()</code> -&gt; <code class="language-plaintext highlighter-rouge">127.0.0.1</code></li> </ul> <p>И первый однозначно лучше, когда браузер бежит на другой машине или в контейнере. Под адресом <code class="language-plaintext highlighter-rouge">127.0.0.1</code> прокси просто не будет виден с другой машины.</p> <blockquote> <p>Кстати, если этот механизм почему-то вам не помог, вы всегда можете явно задать имя хоста для прокси через настройку <code class="language-plaintext highlighter-rouge">Configuration.proxyHost = "my.comp.eu";</code></p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/pull/1970">PR 1970</a>.</p> <p><br /></p> <h3 id="upgrade-to-selenium-4.5.0">Обновились на Selenium 4.5.0</h3> <p>Из заметных изменений:</p> <ul> <li>удалили поддержку браузера Opera</li> <li>добавили альтернативную реализацию вебдрайвера на JDK 11 HTTP client вместо Netty client</li> <li>Добавили проверки на <code class="language-plaintext highlighter-rouge">disabled</code> в класс <code class="language-plaintext highlighter-rouge">Select</code> (больше не получится выбрать задизейбленный <code class="language-plaintext highlighter-rouge">option</code> в выпадающем списке)</li> <li>Убрали имя хоста из селениумовских эксепшенов <blockquote> <p>цуко, я просил об этом <a href="https://github.com/SeleniumHQ/selenium/issues/489">ещё в 2015 году</a>!!!</p> </blockquote> </li> </ul> <p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог</a> и <a href="https://github.com/selenide/selenide/pull/1967">PR 1967</a>.</p> <p><br /></p> <h3 id="remove-opera-support">Выкинули поддержку Opera</h3> <p>Следствие предыдущего пункта. Селениум выкинул - и мы выкинули. Тестировать в опере не имеет особого смысла, т.к. это по сути тот же Chrome. Если очень хочется - можно, просто используйте chromedriver.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1967">PR 1967</a>.</p> <p><br /></p> <h3 id="do-not-log-get-alias">Убрали <code class="language-plaintext highlighter-rouge">getAlias</code> из отчётов</h3> <p>Спасибо <a href="https://github.com/reserved-word">Reserved Word</a> за <a href="https://github.com/selenide/selenide/pull/1971">PR 1971</a>.</p> <p><br /></p> <h3 id="restore-setting-connection-timeout">Вернули настройку “connection timeout”</h3> <p>Это мало кому нужно, так что смело пропускайте.</p> <p class="small">Когда-то у нас было две настройки для http клиента вебдрайвера: “connection timeout” и “read timeout”. Первую пришлось убрать при обновлении на Selenium 4, потому что её там выпилили. Теперь её реанимировали в Selenium 4.5.0, ну и мы реанимировали.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1977">PR 1977</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>LittleProxy from 2.0.12 to 2.0.13</li> <li>slf4j from 2.0.2 to 2.0.3</li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Доклад Сергея Брита <a href="https://www.youtube.com/watch?v=obsJWnBsYwk&amp;ab_channel=SQAANALYSTSECR">Selenide + Playwright Java = объединяй и властвуй</a> - конференция SQA Days EA, 01.10.2022</li> <li>Доклад Алексея Виноградова <a href="https://www.youtube.com/watch?v=MLxf9q9qXu4&amp;ab_channel=%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D0%B8%D0%B2%D0%9A%D0%BE%D0%BD%D1%82%D1%83%D1%80%D0%B5">Spinner-driven-development</a></li> </ul> <p><br /></p> <p>Айтишники, учите английский <a href="https://www.youtube.com/watch?v=laIGavOMcw8&amp;ab_channel=FoilArmsandHog">по ирландским комикам</a>!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/10/07/selenide-6.9.0/ http://ru.selenide.org/2022/10/07/selenide-6.9.0 2022-10-07T00:00:00+00:00 Вышла Selenide 6.8.1 <p><br /></p> <h1 id="добре">Добре!</h1> <p>Ловите мини-багфикс <a href="https://github.com/selenide/selenide/milestone/165?closed=1">Selenide 6.8.1</a>.</p> <p>Это касается только тех, кто напрямую вызывает в своих тестах</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nc">RemoteWebDriver</span><span class="o">(</span><span class="n">url</span><span class="o">,</span> <span class="n">options</span><span class="o">)</span> </code></pre></div></div> <p>и словил <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code> после обновления на <a href="/2022/09/24/selenide-6.8.0/">Selenide 6.8.0</a>.</p> <p><br /></p> <h3 id="pre-history">Предыстория</h3> <p>Что такое OpenTelemetry? Это какая-то штука, которую зачем-то добавили в Selenium 4, но людям забыли сообщить, зачем это вообще нужно и как этим пользоваться. Ну и по факту всем пофиг, никто не пользуется.</p> <p>Да и в самом селениде мы её явно выпилили в <a href="https://github.com/selenide/selenide/pull/1763">PR 1763</a>.</p> <blockquote> <p>Зависимость OpenTelemetry в Selenium - как специальная военная операция. Никто не знает, зачем она нужна.</p> </blockquote> <h3 id="the-problem">Проблема</h3> <p>В общем, у нормальных пользователей Селенида проблемы и не возникнет.</p> <p>НО если вы в своих тестах явно вызываете конструктор <code class="language-plaintext highlighter-rouge">new RemoteWebDriver(url, options)</code>, то он, к сожалению, требует зависимости OpenTelemetry. И после обновления на <a href="/2022/09/24/selenide-6.8.0/">Selenide 6.8.0</a> такие пользователи начали получать <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code>.</p> <p><br /></p> <h3 id="temporary-solution">Временное решение</h3> <p>Добавьте параметр <code class="language-plaintext highlighter-rouge">false</code> в конструктор: <code class="language-plaintext highlighter-rouge">new RemoteWebDriver(url, options, false)</code>. Этот <code class="language-plaintext highlighter-rouge">false</code> говорит “не используй телеметрию”.</p> <p><br /></p> <h3 id="restored-opentelemetry">Демобилизовали зависимость OpenTelemetry</h3> <p>В общем, мы по-быстрому выпустили Selenide 6.8.1, в котором вернули OpenTelemetry.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1965">issue 1965</a> и <a href="https://github.com/selenide/selenide/pull/1966">PR 1966</a>.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/09/27/selenide-6.8.1/ http://ru.selenide.org/2022/09/27/selenide-6.8.1 2022-09-27T00:00:00+00:00 Вышла Selenide 6.8.0 <p><br /></p> <h1 id="недоброе-утро">Недоброе утро!</h1> <p>Пока в мире творится здипец, мы выпускаем релиз <a href="https://github.com/selenide/selenide/milestone/161?closed=1">Selenide 6.8.0</a>.<br /> Фич немного, больше обновление зависимостей.</p> <ul class="blogpost-menu"> <li><a href="#deep-shadow-selectors">Рекурсивный поиск по shadow dom</a></li> <li><a href="#add-page-without-class">Метод page() без параметров</a></li> <li><a href="#as-annotation">Аннотация @As</a></li> <li><a href="#news">Новости</a></li> </ul> <h3 id="deep-shadow-selectors">Рекурсивный поиск по shadow dom</h3> <p>Как вы знаете, в селениде <a href="/2020/03/18/selenide-5.10.0">есть возможность</a> искать элементы внутри shadow dom. И даже в нескольких вложенных shadow dom:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"#reportButton"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">,</span> <span class="s">"#inner-shadow-host"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>Хорошо, что это возможно, но это по-прежнему требует ваших временных затрат на то, чтобы изучить dom и найти точные локаторы для всех этих теневых элементов.</p> <p>Теперь вы сможете сэкономить время с помощью нового метода <code class="language-plaintext highlighter-rouge">shadowDeepCss</code> - он ищет элемент во всех shadow root по всему dom.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowDeepCss</span><span class="o">(</span><span class="s">"#reportButton"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>Естественно, остаётся риск, что такой “слишком широкий” поиск может найти и не тот элемент, если в ваших теневых областях есть несколько элементов в искомым локатором. В этом случае придётся вернуться к более точечному поиску.</p> <blockquote> <p>Как и в случае с базами данных и сосисками, “внутрь лучше не заглядывать” - там начинка из мудрёного джаваскрипта для поиска по дереву.</p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/issues/1946">issue 1946</a>.</p> <p>Спасибо</p> <ol> <li><a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1947">PR 1947</a>, а также</li> <li><a href="https://github.com/Georgegriff">Georgegriff</a> за проект <a href="https://github.com/Georgegriff/query-selector-shadow-dom">query-selector-shadow-dom</a>, откуда мы этот код и спёрли.</li> </ol> <p><br /></p> <h3 id="add-page-without-class">Добавили метод <code class="language-plaintext highlighter-rouge">page()</code>, не требующий параметр <code class="language-plaintext highlighter-rouge">Class</code></h3> <p>Дорогие фанаты пэдж обжектов, садитесь поудобнее, это для вас. В вашей жизни наступает переломный момент.</p> <p>Теперь в метод <code class="language-plaintext highlighter-rouge">page(Class pageObjectClass)</code> больше не нужно передавать параметр <code class="language-plaintext highlighter-rouge">Class</code>. Да-да! Вместо</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelectsPage</span> <span class="n">page</span> <span class="o">=</span> <span class="n">page</span><span class="o">(</span><span class="nc">SelectsPage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> </code></pre></div></div> <p>вы сможете писать просто</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelectsPage</span> <span class="n">page</span> <span class="o">=</span> <span class="n">page</span><span class="o">();</span> </code></pre></div></div> <p>Раньше я думал, что Java так не умеет, но потом нашёл <a href="https://twitter.com/tagir_valeev/status/1262763570904719361">хак</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1961">PR 1961</a> и спасибо <a href="https://twitter.com/tagir_valeev">Тагиру Валееву</a> за хак.</p> <p><br /></p> <h3 id="as-annotation">Добавили аннотацию <code class="language-plaintext highlighter-rouge">@As</code></h3> <p>… для задания алиасов полям пэдж обжекта.</p> <h4 id="1-без-алиасов">1. Без алиасов</h4> <p>Если у вас есть пэдж обжект с аннотациями типа <code class="language-plaintext highlighter-rouge">@FindBy</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PageObject</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">xpath</span><span class="o">=</span><span class="s">"//table/div[3]/span[4]/h1"</span><span class="o">)</span> <span class="nc">SelenideElement</span> <span class="n">header</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>И тест типа <code class="language-plaintext highlighter-rouge">page.header.shouldBe(visible)</code>,</p> <p>То в отчётах он выглядит громоздко и непонятно:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| open | https://devclub.eu | PASS | | By.xpath: //table/div[3]/span[4]/h1 | should be(visible) | FAIL | </code></pre></div></div> <p>А если тест упадёт, то в сообщении не очень-то понятно, о каком элементе идёт речь:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">:</span> <span class="c1">//table/div[3]/span[4]/h1}</span> </code></pre></div></div> <p>Люди часто хотят дать какое-то человеческое название этому элементу, чтобы не видеть в отчётах длинный непонятный xpath.</p> <h4 id="2-сопротивление">2. Сопротивление</h4> <p>Я всегда был против этого. Я считаю, что не только отчёты, но и сами тесты должны быть читабельными. Я за чистый код. Не нравится уродливый xpath - ну так измени его, а не прячь!</p> <p>Но потом я решил дать заднюю.</p> <blockquote> <p>Ведь если мы не дадим заднюю, то они могут дать заднюю, а вот если мы её дадим, они её не дадут. Заднюю никто не даст больше, кроме нас.</p> </blockquote> <p>Убедительно же! Поэтому в версии <a href="/2020/12/26/selenide-5.17.0/">Selenide 5.17.0</a> мы добавили метод <code class="language-plaintext highlighter-rouge">$.as("alias")</code>.</p> <h4 id="3-с-алиасами">3. С алиасами</h4> <p>А с этого релиза - ещё и можете навесить аннотацию <code class="language-plaintext highlighter-rouge">@As</code> на поля поля пэджобжектов.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PageObject</span> <span class="o">{</span> <span class="nd">@As</span><span class="o">(</span><span class="s">"Large header"</span><span class="o">)</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">tagName</span> <span class="o">=</span> <span class="s">"h1"</span><span class="o">)</span> <span class="nc">SelenideElement</span> <span class="n">header1</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>В отчётах он будет выглядеть короче:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| open | https://devclub.eu | PASS | | title | should be(visible) | FAIL | </code></pre></div></div> <p>А при падении теста будет легче понять, о каком элементе речь:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="s">"title"</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">:</span> <span class="c1">//table/div[3]/span[4]/h1}</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1903">issue 1903</a> и <a href="https://github.com/selenide/selenide/pull/1956">PR 1956</a>.</p> <blockquote> <p>Не забывайте: врут все.</p> <p>Документация врёт, комментарии врут, пользователи врут. Рано или поздно соврёт и алиас. Локаторы поменяли, а алиас забыли. Или элемент скопировали, а алиас забыли поменять. И т.д.</p> <p>Отчёты - это хорошо, но в конечном итоге верить можно только коду.</p> </blockquote> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>BrowserUpProxy from 2.2.2 to 2.2.3</li> <li>LittleProxy from 2.0.11 to 2.0.12</li> <li>Netty from 4.1.80.Final to 4.1.82.Final</li> <li>slf4j from 2.0.0 to 2.0.2</li> <li>JUnit from 5.9.0 to 5.9.1 – см. <a href="https://junit.org/junit5/docs/5.9.1/release-notes/">release notes</a></li> </ul> <blockquote> <p>Объявлена частичная мобилизация зависимостей!</p> </blockquote> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Продолжается серия от Dilpreet Johal - теперь <a href="https://dev.to/automationbro/upload-file-with-selenide-1f2a">Upload file with Selenide</a></li> <li><a href="https://sdet-tomaszbuga.medium.com/test-automation-framework-selenium-with-java-alchemy-or-translating-jira-with-selenide-with-e8831ebfe337">Alchemy or Translating JIRA with Selenide</a> от Tomasz Buga</li> <li>Вышла Java 19.</li> </ul> <h3 id="statistics">Статистика</h3> <p>Свежая статистика скачиваний Селенида нас только радует. Перевалили за 400 тыщ!</p> <center> <img src="/images/2022/09/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p>Смотрите <a href="https://www.youtube.com/watch?v=m6TwkNAmI3A&amp;ab_channel=MasyanyaKuvaeva">Масяню!</a></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/09/24/selenide-6.8.0/ http://ru.selenide.org/2022/09/24/selenide-6.8.0 2022-09-24T00:00:00+00:00 Вышла Selenide 6.7.4 <p><br /></p> <h1 id="привет">Привет!</h1> <p><br /> Я календарь перевернул, и снова релиз <a href="https://github.com/selenide/selenide/milestone/164?closed=1">Selenide 6.7.4</a>.</p> <p><br /></p> <h3 id="add-remote-read-setting">Добавили настройку <code class="language-plaintext highlighter-rouge">remoteReadTimeout</code></h3> <p>Ещё недавно <a href="https://www.youtube.com/watch?v=iIIsZRHya-w&amp;ab_channel=DEVCLUB.EU">я жаловался</a> на то, что в селениде и так слишком много настроек, но нам не оставили выбора.</p> <p>В общем, мы добавили ещё одну настройку <code class="language-plaintext highlighter-rouge">Configuration.remoteReadTimeout</code> (также известную как <code class="language-plaintext highlighter-rouge">-Dselenide.remoteReadTimeout</code>).</p> <p>Она пригодится тем, кто запускает браузер удалённо, но при этом браузеров на сервере не хватает, и приходится ждать в очереди. И поэтому дефалтового таймаута в 1.5 минуты оказывается недостаточно, чтобы элементарно открыть браузер.</p> <blockquote> <p>Лично я называю это адом на проекте, но кто мы такие, чтобы мешать людям добровольно жариться на сковородке…</p> </blockquote> <p>В общем, теперь вы можете указать какой угодно таймаут в билд-скриптах, в командой строке и т.п.</p> <p>Спасибо <a href="https://github.com/rodion-goritskov">Rodion Goritskov</a> за <a href="https://github.com/selenide/selenide/pull/1936">PR 1936</a>.</p> <p><br /></p> <p><br /></p> <h3 id="fix-dead-threads-watchdog">Исправили мини-косяк в “Dead threads watchdog”</h3> <p>Вы видели когда-нибудь <code class="language-plaintext highlighter-rouge">java.lang.IllegalStateException: Shutdown in progress</code> в логах?</p> <p>Значит, не обновились на <a href="/2022/08/14/selenide-6.7.2/">Selenide 6.7.2</a> :)</p> <p>В общем, обновляйтесь сразу на 6.7.4 - и всё будет <em>заерысь</em>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1942">issue 1942</a> и <a href="https://github.com/selenide/selenide/pull/1943">PR 1943</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li><a href="https://github.com/selenide/selenide/pull/1932">PR 1932</a> Bump Netty from 4.1.79 to 4.1.80</li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li><a href="https://blog.devgenius.io/selenide-101-in-5-minutes-2703086ee228">Selenide 101 in 5 minutes</a> by <a href="https://sophieer.medium.com/">Sophie R.</a></li> <li><a href="https://www.youtube.com/watch?v=0vlV8_4EDAg&amp;list=PL6AdzyjjD5HC4NJuc083bzFq86JekmASF&amp;ab_channel=AutomationBro-DilpreetJohal">Selenide Java Tutorial Series</a> by <a href="https://www.youtube.com/c/AutomationBro">Dilpreet Johal</a></li> <li><a href="https://mszeles.com/save-dozens-of-minutes-daily-during-writing-selenide-and-selenium-e2e-automated-tests-using-the-selenideium-element-inspector-chrome-extension">Selenideium Element Inspector</a> by <a href="https://hashnode.com/@mszeles">Miki Szeles</a></li> <li><a href="https://mszeles.com/selenium-javascript-python-c-cypress-testcafe-playwright-squish-selector-generation-has-been-added-to-selenideium-element-inspector-v20">Selenideium Element Inspector 2.0</a> by <a href="https://hashnode.com/@mszeles">Miki Szeles</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/09/05/selenide-6.7.4/ http://ru.selenide.org/2022/09/05/selenide-6.7.4 2022-09-05T00:00:00+00:00 Вышла Selenide 6.7.3 <p><br /></p> <h1 id="привет">Привет!</h1> <p><br /> Вы читаете пресс-релиз <a href="https://github.com/selenide/selenide/milestone/163?closed=1">Selenide 6.7.3</a> в пиратском переводе.</p> <p>А что делать, <em>все хотят кушать</em>…</p> <p><br /></p> <h3 id="condition-partial-value">Добавили условие <code class="language-plaintext highlighter-rouge">partialValue</code></h3> <p>По аналогии с <code class="language-plaintext highlighter-rouge">$.shouldHave(partialText("Добрый ко"))</code> теперь появилась и <code class="language-plaintext highlighter-rouge">$.shouldHave(partialValue("cola"))</code>.</p> <p>Это если вы установили настройку <code class="language-plaintext highlighter-rouge">Configuration.textCheck = FULL_TEXT</code>, но хотите проверить значение какого-то инпута или textarea частично, а не полностью.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1923">issue 1923</a> и <a href="https://github.com/selenide/selenide/pull/1924">PR 1924</a>.</p> <p><br /></p> <h3 id="condition-tag-name">Добавили условие <code class="language-plaintext highlighter-rouge">tagName</code></h3> <p>Наверное, оно нечасто нужно, т.к. мы часто ищем элемент по тэгу, и уж потом проверяем другие атрибуты. Да и вообще, тэг - это внутренности, которые пользователь не видит, поэтому, может, и проверять его не нужно.</p> <p>Тем не менее, теперь вы можете проверять тэг:</p> <p><code class="language-plaintext highlighter-rouge">$(".btn-primary").shouldHave(tagName("button"));</code></p> <p>Или фильтровать коллекцию по тэгу:</p> <p><code class="language-plaintext highlighter-rouge">$$(byText("Submit!")).filterBy(tagName("button"));</code></p> <p>См. <a href="https://github.com/selenide/selenide/issues/1928">issue 1928</a> и <a href="https://github.com/selenide/selenide/pull/1929">PR 1929</a>.</p> <p><br /></p> <h3 id="check-select-tag">Проверяем, что элемент - <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code></h3> <p>… в методах <code class="language-plaintext highlighter-rouge">$.getSelectedText()</code> и <code class="language-plaintext highlighter-rouge">getSelectedValue()</code>.</p> <p>Изначальная задумка этих методов была в том, чтобы найти выбранную опцию в селекте, и вернуть её текст или значение.</p> <p>Но</p> <ol> <li>во-первых, из названия это неочевидно (и поэтому мы переименовали их в <code class="language-plaintext highlighter-rouge">$.getSelectedOptionText()</code> и <code class="language-plaintext highlighter-rouge">getSelectedOptionValue()</code>)</li> <li>а во-вторых, эти методы можно было вызвать на любом другом элементе и получить непредсказуемое поведение.</li> </ol> <p>Теперь попытка вызова <code class="language-plaintext highlighter-rouge">$("div").getSelectedOptionText()</code> выкинет <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1934">PR 1934</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li><a href="https://github.com/selenide/selenide/pull/1932">PR 1932</a> Bump webdrivermanager from 5.2.3 to 5.3.0</li> <li><a href="https://github.com/selenide/selenide/pull/1931">PR 1931</a> Bump slf4jVersion from 1.7.36 to 2.0.0</li> <li><a href="https://github.com/selenide/selenide/pull/1921">PR 1921</a> Bump browserup-proxy-core from 2.2.1 to 2.2.2</li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li>Selenide puzzler <a href="/2022/08/22/selenide-puzzler/">Логическое И или ИЛИ?</a></li> <li>Selenide упомянут в <a href="https://aglowiditsolutions.com/blog/top-java-frameworks/">Top Java Frameworks to Use in 2022</a></li> </ul> <p>И наконец, <strong>невиданное</strong>!</p> <ul> <li>Selenide упомянут на <a href="https://www.selenium.dev/ecosystem/">официальном сайте Selenium</a> в разделе “Экосистема”.</li> </ul> <p>Не может быть!</p> <p>Не прошло и 10 лет!</p> <p>А нет, прошло…</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/08/27/selenide-6.7.3/ http://ru.selenide.org/2022/08/27/selenide-6.7.3 2022-08-27T00:00:00+00:00 Selenide puzzler <p><br /></p> <h1 id="привет">Привет!</h1> <p>Вы любите пазлеры? Соскучались по пазлерам? В последний раз мы показывали пазлеры аж в 2017 году, кто не смотрел - <a href="https://www.youtube.com/watch?v=y-ZyxTWHH08&amp;ab_channel=Heisenbug">смотрим</a>!</p> <p>И вот наконец мы <a href="https://twitter.com/selenide/status/1560192824536088580">опубликовали в твиттере</a> новый пазлер:</p> <blockquote> <p>Одинаково ли работают эти строки? Или есть какая-то разница?</p> </blockquote> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span><span class="o">.</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">,</span> <span class="n">enabled</span><span class="o">);</span> <span class="mi">2</span><span class="o">.</span> <span class="err">$</span><span class="o">(</span><span class="s">".btn"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">enabled</span><span class="o">);</span> </code></pre></div></div> <h3 id="варианты-ответов">Варианты ответов</h3> <p>Люди в твиттере и чатиках накидали разных вариантов:</p> <ul> <li>обе строчки работают одинаково (самый популярный ответ)</li> <li>первый случай: логическое or, второй: логический and</li> <li>Второй работает на %ожидание по умолчанию% дольше.</li> <li>Первый должен отработать с одним тайм-аутом, второй должен с двумя тайм-аутами</li> <li>в первом случае упадёт, если отсутствует любое из условий, во втором - если не visible, но enabled.</li> </ul> <h3 id="правильный-ответ">Правильный ответ</h3> <p>В большинстве случаев оба варианта работают одинаково.</p> <p>Оба варианта - это логическое AND. В обоих вариантах селенид проверит, что элемент соответствует обоим условиям.</p> <table> <tbody> <tr> <td> </td> <td><strong>enabled</strong></td> <td><strong>disabled</strong></td> </tr> <tr> <td><strong>видимый</strong></td> <td>ok</td> <td>nok</td> </tr> <tr> <td><strong>невидимый</strong></td> <td>nok</td> <td>nok</td> </tr> </tbody> </table> <p><br /></p> <p>Но есть нюанс.</p> <h3 id="нюанс">Нюанс</h3> <p>Разница проявится в том случае, если элемент соответствует условиям не сразу, а через какое-то время. Причём первому условию меньше, чем за 4 секунды, а второму - больше. (Допустим, visible он становится через 3.5 секунды, а enabled - ещё через 3.5 секунды).</p> <blockquote> <p>Всё дело в том, что таймаут (по умолчанию 4 секунды) есть у метода <code class="language-plaintext highlighter-rouge">shouldHave</code>, а у проверок (<code class="language-plaintext highlighter-rouge">visible</code>, <code class="language-plaintext highlighter-rouge">enabled</code>, <code class="language-plaintext highlighter-rouge">cssClass</code>, <code class="language-plaintext highlighter-rouge">text</code>) никакого таймаута нет - они просто умеют проверить, да или нет.</p> </blockquote> <h3 id="пример">Пример</h3> <p>Откроем для примера <a href="https://selenide.org/traffic-light.html">Светофор</a>. Светофор становится зелёным через 3.5 секунды, а текст на нём появляется ещё через 3.5 секунды.</p> <p>Первый вариант проверяет, что светофор удовлетворит обоим условиям в течение 4 секунд:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#light"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"green"</span><span class="o">),</span> <span class="n">text</span><span class="o">(</span><span class="s">"GO!"</span><span class="o">));</span> <span class="c1">// таймаут 4 секунды</span> </code></pre></div></div> <p>И эта проверка упадёт, т.к. таймаут на эту проверку - 4 секунды, но элемент не приобретёт и цвет, и текст в течение 4 секунд.</p> <p>А второй вариант не упадёт:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#light"</span><span class="o">)</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"green"</span><span class="o">))</span> <span class="c1">// таймаут 4 секунды</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"GO!"</span><span class="o">));</span> <span class="c1">// и плюс ещё 4 секунды</span> </code></pre></div></div> <p>Первая проверка дождётся, пока элемент станет зелёным (и он станет в течение 4 секунд). После этого запустится вторая проверка и запустит новый таймаут с целью дождаться, пока у элемента появится текст. И он дождётся.</p> <p>Видимо, наиболее близкий к правильному был ответ “Первый должен отработать с одним тайм-аутом, второй должен с двумя тайм-аутами”.</p> <h3 id="ваши-пазлеры">Ваши пазлеры</h3> <p>Есть ли у вас свои пазлеры? Присылайте их нам, давайте будем отгадывать вместе!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/08/22/selenide-puzzler/ http://ru.selenide.org/2022/08/22/selenide-puzzler 2022-08-22T00:00:00+00:00 Вышла Selenide 6.7.2 <p><br /></p> <h1 id="привет">Привет!</h1> <p><br /> Мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/162?closed=1">Selenide 6.7.2</a> с исправлением утечек памяти.</p> <p>Да-да, мы серьёзные ребята, у нас тоже бывают утечки.</p> <p>Но не пугайтесь, они некритические. Вряд ли кто-то из вас их вообще замечал.</p> <h3 id="fix-selenide-memory-leak">Исправили утечки с shutdown hooks в Selenide</h3> <p>Если в тесте много-много раз открывать и закрывать вебдрайвер</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">1000</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"about:blank"</span><span class="o">);</span> <span class="n">closeWebDriver</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <p>то расход памяти потихоньку растёт:</p> <p><img src="https://user-images.githubusercontent.com/279773/184475079-bbf8ee0f-d245-43ec-8e72-a010262e099c.png" style="width: 100%" alt="Memory consumption: before" /></p> <p><em>Красная линия, ты где?</em></p> <p>А вот что случилось на самом деле.</p> <p>Это просто сдетонировало несколько shutdown хуков, которые селенид складировал для каждого открытого вебдрайвера.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1917">issue 1917</a> и <a href="https://github.com/selenide/selenide/pull/1919">PR 1919</a>.</p> <p>После обновления на версию 6.7.2 память больше не растёт:</p> <p><img src="https://user-images.githubusercontent.com/279773/184499249-f6e1e3cb-bc88-4c04-8a7b-a080f3f08887.png" style="width: 100%" alt="Memory consumption: after" /></p> <p><em>Обновляемся на Selenide 6.7.2, выдыхаем и идём на пляж.</em></p> <p><br /></p> <h3 id="fix-little-proxy-memory-leak">Исправили утечку в LittleProxy</h3> <p>Мы обновились на версию LittleProxy 2.0.11, в которой была исправлена ещё одна утечка памяти.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1918">PR 1918</a> и <a href="https://github.com/LittleProxy/LittleProxy/pull/141">PR 141</a></p> <p><br /></p> <h3 id="upgrade-to-selenium-4.4.0">Обновились на Selenium 4.4.0</h3> <p>в котором даже есть одно лично моё исправление, так-то! Которое, кстати, помогло нам исправить и следующую проблему.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1913">PR 1913</a> и <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">Selenium changelog</a></p> <p><br /></p> <h3 id="fix-full-size-screenshot">Исправили плагин <code class="language-plaintext highlighter-rouge">full-size-screenshot</code></h3> <p>В нашем новом плагине <code class="language-plaintext highlighter-rouge">full-size-screenshot</code> была одна известная проблема (на самом деле вызванная багой в селениуме): если вы запускаете браузер удалённо и открываете несколько вкладок или окон, то селенид мог взять скриншот не с того окна.</p> <p>Эта ошибка была исправлена в Selenium 4.4.0, и соответственно, у нас она теперь тоже исправится.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1920">PR 1920</a> и <a href="https://github.com/SeleniumHQ/selenium/pull/10811">PR 10811</a>.</p> <p><br /></p> <h3 id="news">Ссылки</h3> <ul> <li>Статья <a href="https://blogs.perficient.com/2022/06/22/working-efficiently-with-selenide-the-subset-of-selenium-webdriver/">Working Efficiently with Selenide</a> by Zainab Firdos, 22.06.2022</li> <li>Серия руководств <a href="https://dev.to/automationbro/selenide-tutorial-series-58p5">Selenide Tutorial Series</a> by Dilpreet Johal, 13.06.2022</li> <li>Видео-руководство <a href="https://www.youtube.com/watch?v=0vlV8_4EDAg&amp;t=318s&amp;ab_channel=AutomationBro-DilpreetJohal">Selenide Java Tutorial Series</a> by Automation Bro, 13.06.2022</li> <li>Статья <a href="https://hackernoon.com/how-to-start-your-friendship-with-selenide">How to Start Your Friendship with Selenide</a> by Miki Szeles, 30.03.2022</li> <li>Оффтопик: моё выступление на типа открытом микрофоне <a href="https://youtu.be/AOBEqZ0T51c">про поездки на конференции в Киев</a> - “Твой выход”, 23.07.2022, Таллинн</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/08/14/selenide-6.7.2/ http://ru.selenide.org/2022/08/14/selenide-6.7.2 2022-08-14T00:00:00+00:00 Вышла Selenide 6.7.0 <p><br /></p> <h1 id="привет">Привет!</h1> <p><br /> У нас случился толстенький релиз <a href="https://github.com/selenide/selenide/milestone/154?closed=1">Selenide 6.7.0</a></p> <h3 id="holy-whole-string">Теперь <code class="language-plaintext highlighter-rouge">$.shouldHave(text)</code> может проверять текст целиком</h3> <p>Испокон веков команда <code class="language-plaintext highlighter-rouge">$.shouldHave(text)</code> проверяла не текст целиком, а только подстроку:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"freedom-to"</span><span class="nt">&gt;</span>Britney Spears<span class="nt">&lt;/div&gt;</span> </code></pre></div></div> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#freedom-to"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Brit"</span><span class="o">));</span> <span class="c1">// was OK</span> </code></pre></div></div> <p>Когда-то это показалось удачной идеей, потому что веб-страничка часто содержит всякие левые символы (переводы строк, символы табуляции, непереносимые пробелы, множественные пробелы и т.п.).</p> <p>Но похоже, это всё-таки была неудачная идея, т.к. новички по умолчанию предполагают, что текст проверяется целиком, и выхватывают свою порцию WTF, когда обнаруживают, что все их проверки проверяли немного не то. :)</p> <p>А левые символы селенид научился правильно игнорировать (например, несколько пробелов/табов/переводов подряд эквивалентны одному пробелу).</p> <p>NB! Поскольку это изменение слишком уж кардинальное и наверняка сломает кучу ваших тестов, мы пока оставили по умолчанию старое поведение. А новое можно включить такой настройкой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Configuration</span><span class="o">.</span><span class="na">textCheck</span> <span class="o">=</span> <span class="no">FULL_TEXT</span><span class="o">;</span> </code></pre></div></div> <p>А когда вам нужно проверить именно подстроку, можете использовать новую проверку <code class="language-plaintext highlighter-rouge">partialText</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#freedom-to"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">partialText</span><span class="o">(</span><span class="s">"ey Spear"</span><span class="o">))</span> </code></pre></div></div> <p>Возможно, мы включим этот режим по умолчанию в следующей мажорной версии Selenide 7.0.0 - а до тех пор <strong>вы можете поиграться и дать нам обратную связь</strong>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1780">issue 1780</a> и <a href="https://github.com/selenide/selenide/pull/1783">PR 1783</a>.</p> <p><br /></p> <h3 id="full-size-screenshots">Научили Селенид делать большие скриншоты</h3> <p>Ну наконец-то!</p> <p>Как вы знаете, при падении теста селенид автоматически делает снимок экрана (скриншот), чтобы помочь вам понять, почему тест упал. По умолчанию вебдрайвер умеет снимать не всё окно браузера, а только видимую его часть. А самое важно часто оказывается за пределами экрана - тест валит какая-нибудь неактивная кнопка внизу экрана или всплывшее уведомление где-нибудь в углу.</p> <p>Теперь вы можете одной строчкой подключить плагин, который снимает скриншот с полным содержимым веб-странички:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">testImplementation</span> <span class="s2">"com.codeborne:selenide-full-screenshot:6.7.0"</span> </code></pre></div></div> <p>P.S. Фича работает только для браузеров, поддерживающих CDP (Chrome, Edge и т.д.) и Firefox. Для остальных будут по-прежнему обычные скриншоты.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1799">issue 1799</a> и <a href="https://github.com/selenide/selenide/pull/1858">PR 1858</a>. Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1800">PR 1800</a>.</p> <p><br /></p> <h3 id="cache-lookup-annotation">Добавили кэширование элементов в пэдж обжектах</h3> <p>Как вы знаете, селенид поддерживает обычные селениумовские пэдж-обжекты с аннотациями <code class="language-plaintext highlighter-rouge">@FindBy</code>. Но вот аннотацию <code class="language-plaintext highlighter-rouge">@CacheLookup</code> селенид до сих пор не поддерживал - нам это казалось ненужной оптимизацией, ведь в тестах большая часть времени теряется вовсе не на поиск элементов.</p> <p>Но теперь вот поддерживаем, почему бы нет. Вдруг у кого-то всё настолько оптимизировано, что именно поиск элементов стал узким горлышком. Хотел бы я посмотреть на такой проект. :)</p> <p>Спасибо <a href="https://github.com/groov1kk">Ilya Koshaleu</a> за <a href="https://github.com/selenide/selenide/pull/1894">PR 1894</a>.</p> <p>P.S. Встаёт вопрос, а не надо ли добавить кэширование и для обычных <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code>. А как это оформить - параметром, настройкой? Новым методом?</p> <p><strong>Предлагайте ваши идеи!</strong></p> <p><br /></p> <h3 id="cancel-report-testng-annotation">Отменили аннотацию <code class="language-plaintext highlighter-rouge">@Report</code></h3> <p>Вот культура отмены добралась и до аннотаций!</p> <p>Это изменение касается любителей TestNG, которые использовали селенидовский отчёт. Для этого им надо было повесить на тесты две аннотации:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Listeners</span><span class="o">({</span><span class="nc">TextReport</span><span class="o">.</span><span class="na">class</span><span class="o">})</span> <span class="nd">@Report</span> <span class="kd">class</span> <span class="nc">MyTest</span> <span class="o">{...}</span> </code></pre></div></div> <p>Почему так сделали - это долгая история про корявые принципы работы листенеров в TestNG (вкратце: добавление аннотации листенера одному классу внезапно влияет на все остальные классы). Но теперь мы поняли, что систему можно упростить: аннотация <code class="language-plaintext highlighter-rouge">@Report</code> больше не нужна.</p> <p>А отчёт будет генерироваться для всех тестов, помеченных аннотацией <code class="language-plaintext highlighter-rouge">@Listeners({TextReport.class}</code> <strong>и их потомков</strong>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1891">issue 1891</a> и <a href="https://github.com/selenide/selenide/pull/1909">PR 1909</a>.</p> <p><br /></p> <h3 id="decode-downloaded-file-name">Декодируем имя скачанного файла</h3> <p>Оказалось, что при скачивании файла имя файла в ответе сервера иногда бывает закодировано в Base64. А селенид считывал имя файла как есть. Теперь вот умеет расшифровывать и Base64.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1886">issue 1886</a> и <a href="https://github.com/selenide/selenide/pull/1889">PR 1889</a>.</p> <p><br /></p> <h3 id="restore-ie-support-set-value">Починили <code class="language-plaintext highlighter-rouge">$.setValue()</code> в IE</h3> <p>Оказалось, мы недавно сломали метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> в Internet Explorer. Не то чтобы случайно: IE ведь официально отдал концы. Но жалобы были, и мы его вернули.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1907">PR 1907</a>.</p> <p><br /></p> <h3 id="made-http-client-timeouts-public">Сделали публичным <code class="language-plaintext highlighter-rouge">HttpClientTimeouts</code></h3> <p>Но лишний раз туда лучше не лазать.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1902">PR 1902</a>.</p> <p><br /></p> <h3 id="restore-set-value-string">Вернули тип параметра <code class="language-plaintext highlighter-rouge">String</code> для <code class="language-plaintext highlighter-rouge">$.setValue()</code></h3> <p>Лично у меня подгорает от того, что такая проблема вообще возникла.</p> <p>Подробности в <a href="https://github.com/selenide/selenide/issues/1885">issue 1885</a> и <a href="https://github.com/selenide/selenide/pull/1888">PR 1888</a>.</p> <p><br /></p> <h3 id="validate-file-extension">Добавили валидацию расширения файла</h3> <p>См. <a href="https://github.com/selenide/selenide/pull/1887">PR 1887</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>JUnit <a href="https://github.com/selenide/selenide/pull/1900">from 5.8.2 to 5.9.0</a></li> <li>WebDriverManager <a href="https://github.com/selenide/selenide/pull/1901">from 5.2.1 to 5.2.3</a></li> <li>Netty <a href="https://github.com/selenide/selenide/pull/1892">from 4.1.78.Final to 4.1.79.Final</a></li> <li>BrowserUpProxy <a href="https://github.com/selenide/selenide/pull/1895">from 2.2.0 to 2.2.1</a></li> <li>LittleProxy <a href="https://github.com/selenide/selenide/pull/1896">from 2.0.9 to 2.0.10</a></li> <li>ByteBuddy <a href="https://github.com/selenide/selenide/pull/1904">from 1.12.12 to 1.12.13</a></li> </ul> <p><br /></p> <h3 id="selenide-6.7.1">UPD Selenide 6.7.1</h3> <ul> <li>вернули параметр <code class="language-plaintext highlighter-rouge">Driver</code> в метод <code class="language-plaintext highlighter-rouge">SelenidePageFactory.findSelector()</code> - он используется в <code class="language-plaintext highlighter-rouge">selenide-appium</code>.</li> </ul> <p><br /></p> <h3 id="news">Новости</h3> <ul> <li><a href="https://github.com/iSYS-Software/SelenideReporter">Opinionated reporting framework for Selenide</a> от Ulrich Mayring</li> <li>Новая версия <a href="https://www.youtube.com/watch?v=C8MIGAjgVqE&amp;ab_channel=KVHigh-TechClub">Пацан накодил - пацан протестил</a> - Career Day, Минск, 04.06.2022</li> <li><a href="https://www.youtube.com/watch?v=-KGtZoFVzr8&amp;list=PL9Z-JgiTsOYRfoG_mcRBlTUIFPIknhQ6S">Extending open-source libraries: Selenide &amp; Selenium</a> - Selenium Conf, 30.07.2022</li> <li><a href="https://www.youtube.com/watch?v=SqiYAJfpQwY&amp;list=PLULFH3b6unlcUrwT9hJycTvAWPFqXZO4m&amp;ab_channel=QAClubLviv">Автоматизація з Selenide, ввідне заняття</a> by Роман Маринский</li> <li><a href="https://www.youtube.com/watch?v=-c5XT2v5gRY&amp;ab_channel=DEVCLUB.EE">Flaky Tests</a> - devclub.ee, Tallinn, 05.07.2022</li> <li>Оффтопик: моё выступление на типа открытом микрофоне <a href="https://youtu.be/AOBEqZ0T51c">про поездки на конференции в Киев</a> - “Твой выход”, 23.07.2022, Таллинн</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/08/04/selenide-6.7.0/ http://ru.selenide.org/2022/08/04/selenide-6.7.0 2022-08-04T00:00:00+00:00 Вышла Selenide 6.6.6 <p><br /></p> <h1 id="добър-вечер">Добър вечер!</h1> <p><br /> У нас вышел ещё один мини-релиз <a href="https://github.com/selenide/selenide/milestone/160?closed=1">Selenide 6.6.6</a></p> <h3 id="remove-deprecated-capabilities">Удалили старые капабилити</h3> <p>Некоторые настройки вебдрайвера (которые селенид проставлял с испокон веков) были помечены как устаревшие, а с недавних пор селениум начал ругаться на нах в логах.</p> <p>Теперь селенид их больше не проставляет: <code class="language-plaintext highlighter-rouge">acceptSslCerts</code>, <code class="language-plaintext highlighter-rouge">handlesAlerts</code>, <code class="language-plaintext highlighter-rouge">javascriptEnabled</code>, <code class="language-plaintext highlighter-rouge">takesScreenshot</code>. Мы не забудем вас, друзья! Вы служили нам верой и правдой больше 10 лет.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1862">issue 1862</a>, <a href="https://github.com/selenide/selenide/issues/1866">issue 1866</a> и <a href="https://github.com/selenide/selenide/pull/1870">PR 1870</a>.</p> <p><br /></p> <h3 id="fix-clear-with-shortcut">Исправили ClearWithShortcut</h3> <p>… при работе с вебдрайвером, завёрнутым в листенеры. Редкая ситуация, не заморачивайтесь.</p> <p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1856">PR 1856</a>.</p> <p><br /></p> <h3 id="shorter-syntax-for-click">Добавили короткие варианты для вызова <code class="language-plaintext highlighter-rouge">$.click</code></h3> <p>Раньше, чтобы кликнуть со сдвигом или с кастомным таймаутом, приходилось писать довольно длинную строку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withDefaultMethod</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span> <span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withDefaultMethod</span><span class="o">().</span><span class="na">timeout</span><span class="o">(...));</span> </code></pre></div></div> <p>Теперь же можно написать короче:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withOffset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span> <span class="err">$</span><span class="o">.</span><span class="na">click</span><span class="o">(</span><span class="n">withTimeout</span><span class="o">(...));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/1875">PR 1875</a>.</p> <p><br /></p> <h3 id="support-mobile-apps-in-webdriver-health-checker">Добавили поддержку мобилок при проверке, жив ли вебдрайвер</h3> <p>Мелкое исправление для <code class="language-plaintext highlighter-rouge">selenide-appium</code>. Чтобы проверить, а жив ли браузер, селенид периодически дёргает метод <code class="language-plaintext highlighter-rouge">WebDriver.getTitle()</code> (если вы переиспользуете один вебдрайвер между разными тестами).</p> <p>А тут обнаружилось, что метод <code class="language-plaintext highlighter-rouge">getTitle()</code> не поддерживается в Appium. Пришлось учесть.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1878">issue 1878</a> и <a href="https://github.com/selenide/selenide/pull/1879">PR 1879</a>.</p> <p><br /></p> <h3 id="fix-reopen-browser-on-fail">Исправили логику настройки <code class="language-plaintext highlighter-rouge">reopenBrowserOnFail</code></h3> <p>Ещё одно исправление для улучшения поддержки мобилок.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1880">issue 1880</a> и <a href="https://github.com/selenide/selenide/pull/1881">PR 1881</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">с 5.2.0 на 5.2.1</a>.</li> <li>byteBuddyVersion <a href="https://github.com/selenide/selenide/pull/1872">с 1.12.11 на 1.12.12</a>.</li> </ul> <p><br /></p> <h3 id="release-selenide-appium-2.1.0">Выпустили selenide-appium 2.1.0</h3> <p>Немного улучшили там поддержку тестов на iOS.</p> <p>См. <a href="https://github.com/selenide/selenide-appium/blob/master/CHANGELOG">selenide-appium</a>.</p> <p><br /></p> <p><em>Этим релизом мы решили продемонстрировать мировому сообществу, что не препятствуем созданию автоматических тестов для мобильных приложений.</em></p> <p><em>Теперь слово за автоматизаторами.</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/07/01/selenide-6.6.6/ http://ru.selenide.org/2022/07/01/selenide-6.6.6 2022-07-01T00:00:00+00:00 Вышла Selenide 6.6.4 <p><br /></p> <h1 id="приветос">Приветос!</h1> <p><br /> У нас вышел ещё один мини-релиз <a href="https://github.com/selenide/selenide/milestone/158?closed=1">Selenide 6.6.4</a>.</p> <h3 id="exact-texts-case-sensitive">Добавили условие <code class="language-plaintext highlighter-rouge">exactTextsCaseSensitive</code> для коллекций</h3> <p>В селениде есть несколько проверок для текстов коллекций:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">textsInAnyOrder</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTexts</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span> <span class="c1">// и др.</span> </code></pre></div></div> <p>Теперь к ним добавилась ещё одна:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTextsCaseSensitive</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="s">"bar"</span><span class="o">,</span> <span class="s">"baz"</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/ben-nc2">Ben Heap</a> за <a href="https://github.com/selenide/selenide/pull/1861">PR 1861</a>.</p> <p><br /></p> <h3 id="selected-option-lazy-loaded">Сделали метод <code class="language-plaintext highlighter-rouge">$.getSelectedOption()</code> ленивым</h3> <p>По задумке, (почти) все методы Селенида <a href="https://github.com/selenide/selenide/wiki/Lazy-loading">должны быть ленивыми</a>.</p> <p>Например, просто вызов <code class="language-plaintext highlighter-rouge">$("#nope")</code> не должен падать, если элемента нет. Это позволяет писать отрицательные условия:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#nope"</span><span class="o">).</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span> </code></pre></div></div> <p>Оказалось, что метод <code class="language-plaintext highlighter-rouge">$("select").getSelectedOption()</code> не был ленивым и падал сразу, если селект ещё не успел подгрузиться и т.д.<br /> То есть вы в принципе не могли написать такую проверку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">var</span> <span class="n">option</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"select#gender"</span><span class="o">).</span><span class="na">getSelectedOption</span><span class="o">();</span> <span class="c1">// падало на этом шаге</span> <span class="n">option</span><span class="o">.</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span> </code></pre></div></div> <p>Теперь мы исправили это недоразумение. <em>Лень победила!</em> См. <a href="https://github.com/selenide/selenide/issues/1581">issue 1581</a> и <a href="https://github.com/selenide/selenide/pull/1864">PR 1864</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>Netty <a href="https://github.com/selenide/selenide/pull/1857">с 4.1.77.Final на 4.1.78.Final</a>.</li> <li>BrowserUp proxy <a href="https://github.com/selenide/selenide/pull/1860">с 2.1.5 на 2.2.0</a>.</li> </ul> <p><br /></p> <h3 id="release-selenide-6.6.5">UPD Выпустили Selenide 6.6.5</h3> <p>И ещё мини-релиз <a href="https://github.com/selenide/selenide/milestone/159?closed=1">Selenide 6.6.5</a>.</p> <p>Обновили Selenium с 4.2.2 на 4.3.0.</p> <p><br /> <br /></p> <p><em>Это наш откровенный респонс на ваш откровенный реквест.</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/06/20/selenide-6.6.4/ http://ru.selenide.org/2022/06/20/selenide-6.6.4 2022-06-20T00:00:00+00:00 Вышла Selenide 6.6.3 <p><br /></p> <h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1> <p><br /> Мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/157?closed=1">Selenide 6.6.3</a>.</p> <h3 id="improve-click-timeout">Доработали таймаут для клика</h3> <p>В версии 6.6.0 мы добавили опциональный параметр - таймаут для метода <code class="language-plaintext highlighter-rouge">$.click()</code>.<br /> Но оказалось, что тот параметр решал <a href="/2022/06/08/selenide-6.6.0/#click-timeout">лишь половину проблемы</a>.</p> <p>Теперь мы исправили и вторую половину: вызов</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#slow-link"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">8</span><span class="o">)));</span> </code></pre></div></div> <p>ждёт до 8 секунд как появления элемента <code class="language-plaintext highlighter-rouge">#slow-link</code>, так и последующей загрузки страницы.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1572">issue 1572</a> и <a href="https://github.com/selenide/selenide/pull/1853">PR 1853</a>.</p> <p><br /></p> <h3 id="selenide-6.6.2">Selenide 6.6.2</h3> <p>А чуть раньше мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/156?closed=1">Selenide 6.6.2</a> с обновлением на Selenium 4.2.2</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1851">PR 1851</a>.</p> <p><br /></p> <h3 id="selenide-6.6.1">Selenide 6.6.1</h3> <p>А чуть раньше мы выпустили мини-релиз <a href="https://github.com/selenide/selenide/milestone/155?closed=1">Selenide 6.6.1</a> с исправлением старой <a href="https://github.com/selenide/selenide/issues/1850">issue 1850</a>. Там мы вернули зависимость <code class="language-plaintext highlighter-rouge">byte-buddy</code>, без которой не работало добавление листенеров для вебдрайвера.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/06/12/selenide-6.6.3/ http://ru.selenide.org/2022/06/12/selenide-6.6.3 2022-06-12T00:00:00+00:00 Вышла Selenide 6.6.0 <p><br /></p> <h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1> <p><br /> Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/152?closed=1">Selenide 6.6.0</a>.</p> <h3 id="selenide-clear-with-shortcut">Появился новый плагин <code class="language-plaintext highlighter-rouge">selenide-clear-with-shortcut</code></h3> <p>В Selenide 6.5.0 мы поменяли реализацию <code class="language-plaintext highlighter-rouge">$.clear()</code> со стандартной селениумовской на шорткат (“Выделить всё” - Удалить). Оказалось, что этот шорткат не работал достаточно стабильно во всех браузерах, пришлось его доработать. Вроде стабилизировался, но стал медленнее, чем просто <code class="language-plaintext highlighter-rouge">WebElement.clear()</code>. А поскольку это не всем нужно, решили в конце концов в Селениде оставить старую добрую селениумовскую реализацию.</p> <p>А кому нужна очистка шорткатом - может подключить себе наш новый плагин:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">testImplementation</span><span class="o">(</span><span class="s1">'com.codeborne:selenide:6.6.0'</span><span class="o">)</span> <span class="n">testImplementation</span><span class="o">(</span><span class="s1">'com.codeborne:selenide-clear-with-shortcut:6.6.0'</span><span class="o">)</span> </code></pre></div></div> <p>Зависимость <code class="language-plaintext highlighter-rouge">com.codeborne:selenide-clear-with-shortcut</code> переопределит метод <code class="language-plaintext highlighter-rouge">$.clear()</code> - а соответственно, и <code class="language-plaintext highlighter-rouge">$.setValue()</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1497">issue 1497</a>, <a href="https://github.com/selenide/selenide/pull/1847">PR 1847</a> и <a href="https://github.com/selenide/selenide/pull/1838">PR 1838</a>.</p> <p><br /></p> <h3 id="fix-clear-in-safari">Исправили метод <code class="language-plaintext highlighter-rouge">$.clear()</code> в Safari</h3> <p>Внезапно выяснилось, что новый <code class="language-plaintext highlighter-rouge">$.clear()</code> не работал в Safari (кто его вообще использует?). <br /> Вроде починили. Делитесь опытом, как у вас, починилось?</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1819">issue 1819</a>, <a href="https://github.com/selenide/selenide/pull/1820">PR 1820</a>.</p> <p><br /></p> <h3 id="new-own-test-checks">Новые проверки для текста элемента</h3> <p>Добавили две новых проверки для “своего текста” (own text) элемента:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$("#child_div1").shouldHave(ownTextCaseSensitive("Son"));</code></li> <li><code class="language-plaintext highlighter-rouge">$("#child_div1").shouldHave(exactOwnTextCaseSensitive("Son"))</code></li> </ul> <p>Спасибо <a href="https://github.com/kachurinaa">Kachurin Alexandr</a> за <a href="https://github.com/selenide/selenide/pull/1811">PR 1811</a> и <a href="https://github.com/selenide/selenide/pull/1812">PR 1812</a>.</p> <p><br /></p> <h3 id="click-timeout">Добавили метод <code class="language-plaintext highlighter-rouge">$.click()</code> с таймаутом</h3> <p>По умолчанию у метода <code class="language-plaintext highlighter-rouge">$.click()</code> стандартный селенидовский таймаут (который по умолчанию 4 секунды).<br /> Иногда этого недостаточно - например,</p> <ol> <li>Когда клик по ссылке ведёт на следующую страницу, которая грузится дольше 4 секунд.</li> <li>Когда элемент, по которому нужно кликнуть, ещё не появился на экране - и появляется больше, чем через 4 секунды.</li> </ol> <p>В этом релизе мы добавили таймаут методу <code class="language-plaintext highlighter-rouge">$.click()</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#slow-link"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">().</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">8</span><span class="o">)));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#slow-link"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">8</span><span class="o">)));</span> </code></pre></div></div> <p>Правда, этот параметр решает пока только первую проблему, но не вторую. В следующем релизе займёмся и второй. :)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1572">issue 1572</a> и <a href="https://github.com/selenide/selenide/pull/1845">PR 1845</a>.</p> <p><br /></p> <h3 id="modal-timeout">Добавили таймаут методам <code class="language-plaintext highlighter-rouge">confirm()</code>, <code class="language-plaintext highlighter-rouge">dismiss()</code> и <code class="language-plaintext highlighter-rouge">prompt()</code></h3> <p>Издревле эти три метода позволяют работать с модальными джаваскриптовскими окошками (<code class="language-plaintext highlighter-rouge">alert</code>, <code class="language-plaintext highlighter-rouge">confirm</code>, <code class="language-plaintext highlighter-rouge">prompt</code>). Например,</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">confirm</span><span class="o">();</span> <span class="n">confirm</span><span class="o">(</span><span class="s">"Are you sure you want to delete all files?"</span><span class="o">);</span> </code></pre></div></div> <p>Но не было варианта с таймаутом - на тот случай, если алерт появляется не сразу, а больше, чем через 4 секунды. Теперь можно задать таймаут:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">confirm</span><span class="o">(</span><span class="n">withTimeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">2</span><span class="o">)));</span> <span class="n">confirm</span><span class="o">(</span><span class="n">withExpectedText</span><span class="o">(</span><span class="s">"Are you sure?"</span><span class="o">).</span><span class="na">timeout</span><span class="o">(</span><span class="n">ofSeconds</span><span class="o">(</span><span class="mi">2</span><span class="o">)));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1721">issue 1721</a> и <a href="https://github.com/selenide/selenide/pull/1846">PR 1846</a>.</p> <p><br /></p> <h3 id="fix-execute-javascript">Подправили метод <code class="language-plaintext highlighter-rouge">Driver.executeJavaScript()</code></h3> <p>…чтобы он работал и с “завёрнутым” вебдрайвером (т.е. когда на вебдрайвер навешаны листенеры).</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1848">PR 1848</a>.</p> <p><br /></p> <h3 id="fix-checks-wording">Подправили формулировку некоторых проверок</h3> <p>… чтобы он звучали корректно по-английски. А именно:</p> <table> <thead> <tr> <th>В тесте</th> <th>В отчёте было</th> <th>В отчёте стало</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">$.should(appear)</code></td> <td>“Element should visible”</td> <td>“Element should be visible”</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">$.should(disappear)</code></td> <td>“Element should hidden”</td> <td>“Element should be hidden”</td> </tr> </tbody> </table> <p>См. <a href="https://github.com/selenide/selenide/pull/1840">PR 1840</a>.</p> <p><br /></p> <h3 id="restore-safari-options">Добавили опции для Safari</h3> <p>Выяснилось, что при открытии браузера Safari селенид терял почти все его настройки. Сломалось при переходе на Selenium 4. Починить это оказалось легко, но почему никто до сих пор не жаловался?</p> <p>Похоже, Safari всё-таки никто не использует. :)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1836">issue 1836</a> и <a href="https://github.com/selenide/selenide/pull/1841">PR 1841</a>.</p> <p><br /></p> <h3 id="fix-testng-soft-asserts">Починили софт ассерты на TestNG</h3> <p>После обновления на TestNG 7.5 перестали правильно работать селенидовские софт ассерты. Если какая-то проверка падала, тест по-прежнему оставался зелёным.</p> <p>Пришлось откатиться до TestNG 7.4.0 и ждать исправления на той стороне.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1834">issue 1834</a> и <a href="https://github.com/selenide/selenide/pull/1843">PR 1843</a>.</p> <p><br /></p> <h3 id="update-dependencies">Обновили зависимости</h3> <ul> <li>Selenium <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">4.1.4 -&gt; 4.2.1</a>.</li> <li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">5.1.1 -&gt; 5.2.0</a></li> </ul> <p><br /></p> <h3 id="statistics">Статистика</h3> <p>Давно мы не публиковали статистика скачиваний Селенида</p> <center> <img src="/images/2022/06/selenide.downloads.png" width="800" /> </center> <p>Мы почти вернулись к рекордному показателю: 324 тысяч скачек за май.</p> <p><br /></p> <p>Не переключайтесь!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/06/08/selenide-6.6.0/ http://ru.selenide.org/2022/06/08/selenide-6.6.0 2022-06-08T00:00:00+00:00 Вышла Selenide 6.5.0 <p><br /></p> <h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1> <p><br /> Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/151?closed=1">Selenide 6.5.0</a>.</p> <h3 id="теперь-можно-маскировать-пароли-в-отчётах">Теперь можно маскировать пароли в отчётах</h3> <p>Иногда в автотестах приходится вбивать в поля пароли и другие секретные данные. И некоторые люди переживают, что эти данные потом видны в отчётах (Allure или TextReport). И просят их как-нибудь скрыть.</p> <p>Лично я не понимаю, почему кого-то должен волновать пароль от тестовой среды. (Вы же не используете одни и те же пароли во всех средах? Вы же не гоняете тесты на продакшине?)</p> <p>Но мы пошли навстречу пожеланиям переживающих, и теперь вы можете маскировать или заменять секретные данные:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#password"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"admin"</span><span class="o">).</span><span class="na">sensitive</span><span class="o">());</span> <span class="c1">// заменяет на звёздочки</span> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"john"</span><span class="o">).</span><span class="na">withDisplayedText</span><span class="o">(</span><span class="s">"J* username"</span><span class="o">));</span> <span class="c1">// заменяет на "J* username"</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1768">issue 1768</a> и <a href="https://github.com/selenide/selenide/pull/1770">PR 1770</a>.</p> <p><br /></p> <h3 id="теперь-можно-легко-выбрать-дату-в-input-typedate">Теперь можно легко выбрать дату в <code class="language-plaintext highlighter-rouge">&lt;input type=date&gt;</code></h3> <p>Оказалось, что выбрать дату в селениуме не так-то просто.<br /> Если в вашем приложении есть элемент вида <code class="language-plaintext highlighter-rouge">&lt;input type=date&gt;</code>, то при клике в него разные браузеры открывают разные календарики, и при этом используют разные форматы даты. По идее они должны использовать формат из настроек пользователя, но на деле каждый браузер играет в свою игру.</p> <p>Всё это приводят к флейки тестам, которые могут работать на одном компьютере, но внезапно упасть на другом.</p> <p>В результате моего исследования я пришёл к выводу, что единственный надёжный способ выбрать дату - это установить значение в фиксированном формате <code class="language-plaintext highlighter-rouge">yyyy-mm-dd</code> через JavaScript. Похоже, это работает одинаково во всех ОС и браузерах.</p> <p>И теперь Селенид предоставляет такую возможность с помощью нового метода <code class="language-plaintext highlighter-rouge">withDate</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">LocalDate</span> <span class="n">birthday</span> <span class="o">=</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"1979-12-31"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#birthday"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">withDate</span><span class="o">(</span><span class="n">birthday</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1753">issue 1753</a> и <a href="https://github.com/selenide/selenide/pull/1770">PR 1770</a>.</p> <p><br /></p> <h3 id="теперь-метод-setvalue-поддерживает-react-vuejs-и-тп">Теперь метод <code class="language-plaintext highlighter-rouge">$.setValue("")</code> поддерживает React, Vue.js и т.п.</h3> <p>Давно замечено, что стандартный селениумовский метод <code class="language-plaintext highlighter-rouge">WebElement.clean()</code> (в частности, использующийся в селенидовском <code class="language-plaintext highlighter-rouge">$.setValue()</code>) не всегда правильно работает, когда имеют дело не с обычным <code class="language-plaintext highlighter-rouge">&lt;input&gt;</code>, а с обёрнутыми/обвязанными модными конструкциями, генерируемыми всякими современными JS-фреймворками типа React, Vue.js и прочей хипстоты.</p> <p>По идее мы это починили, ждём ваших отзывов.</p> <p>Теперь следующие методы реализованы не через стандартный селениумовский <code class="language-plaintext highlighter-rouge">WebElement.clear()</code>, а через нажатия комбинации клавиш:</p> <h4 id="1-method-clear">1. Method <code class="language-plaintext highlighter-rouge">$.clear()</code>:</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">input</span><span class="o">.</span><span class="na">sendKeys</span><span class="o">(</span><span class="no">HOME</span><span class="o">,</span> <span class="n">chord</span><span class="o">(</span><span class="no">SHIFT</span><span class="o">,</span> <span class="no">END</span><span class="o">),</span> <span class="no">BACK_SPACE</span><span class="o">);</span> </code></pre></div></div> <h4 id="2-method-setvalue">2. Method <code class="language-plaintext highlighter-rouge">$.setValue("")</code>:</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">input</span><span class="o">.</span><span class="na">sendKeys</span><span class="o">(</span><span class="no">HOME</span><span class="o">,</span> <span class="n">chord</span><span class="o">(</span><span class="no">SHIFT</span><span class="o">,</span> <span class="no">END</span><span class="o">),</span> <span class="no">BACK_SPACE</span><span class="o">,</span> <span class="no">TAB</span><span class="o">);</span> </code></pre></div></div> <p>Такой способ, похоже, работает во всех ОС и браузерах.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1497">issue 1497</a> и <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p> <p>Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за исследование проблемы в <a href="https://github.com/selenide/selenide/pull/1499">PR 1499</a>.</p> <p><br /></p> <h3 id="убрали-двойное-событие-blur-и-change">Убрали двойное событие <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code></h3> <p>Из предыдущего изменения автоматически следует, что метод <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> вызывает события <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code> только единожды.</p> <p>А раньше метод <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> работал в два шага и вызывал их дважды:</p> <ul> <li>Шаг 1. <code class="language-plaintext highlighter-rouge">$.clear()</code> // вызывало <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code></li> <li>Шаг 2. <code class="language-plaintext highlighter-rouge">$.sendKeys("blah")</code> // ещё раз вызывало <code class="language-plaintext highlighter-rouge">blur</code> и <code class="language-plaintext highlighter-rouge">change</code></li> </ul> <p>И события на первом шаге иногда приводили к неожиданным эффектам - например, исчезанию самого поля.<br /> См. <a href="https://github.com/selenide/selenide/issues/960">issue 960</a>.</p> <p><br /></p> <h3 id="вызываем-событие-blur-на-предыдущем-элементе">Вызываем событие <code class="language-plaintext highlighter-rouge">blur</code> на предыдущем элементе</h3> <p>В предыдущем пулреквесте заодно сделали так, что метод <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> вызывает событие <code class="language-plaintext highlighter-rouge">blur</code> на предыдущем активном элементе. Как минимум это помогло исправить некоторые наши флейки тесты. Надеемся, поможет и вам.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1784">issue 1784</a> и <a href="https://github.com/selenide/selenide/commit/593e6fc900500d9">commit 593e6fc9005</a>.</p> <p><br /></p> <h3 id="теперь-методы-setvalue-и-append-проверяют-что-элемент-не-выключен">Теперь методы <code class="language-plaintext highlighter-rouge">$.setValue()</code> и <code class="language-plaintext highlighter-rouge">$.append()</code> проверяют, что элемент не выключен</h3> <p>Точнее, что элемент не находится в состоянии “disabled” или “readonly”.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1523">issue 1523</a> и <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p> <p><br /></p> <h3 id="добавили-новые-проверки-interactable-и-editable">Добавили новые проверки “interactable” и “editable”</h3> <p>Эти проверки уже существовали и использовались раньше внутри Селенида, но не торчали наружу. А теперь и вы сможете их использовать:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"input[type=file]"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">interactable</span><span class="o">);</span> <span class="c1">// interactable = visible OR "opacity: 0"</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">editable</span><span class="o">);</span> <span class="c1">// editable = interactable AND enabled AND !readonly</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1523">issue 1523</a> и <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p> <p><br /></p> <h3 id="метод-downloadfolder-дожидается-полной-загрузки-файла">Метод <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code> дожидается полной загрузки файла</h3> <p>Британские учёные обнаружили, что иногда флейки тесты вызваны тем, что файл скачивается довольно медленно, и селенид успевает обнаружить и скопировать к себе файл, который уже появился в <code class="language-plaintext highlighter-rouge">C:\Downloads</code>, но ещё не полностью загружен.</p> <p>Теперь селенид ждёт, пока в <code class="language-plaintext highlighter-rouge">C:\Downloads</code> исчезнут все файлы “<em>.crdownload” (для хрома) и “</em>.part” (для фаерфокса). Это временный файл, создаваемый браузером на короткий период, пока скачивается файл.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1779">issue 1779</a>, <a href="https://github.com/selenide/selenide/pull/1804">PR 1804</a> и <a href="https://github.com/selenide/selenide/pull/1769">PR 1769</a>.</p> <p><br /></p> <h3 id="добавили-метод-stream-для-коллекций">Добавили метод <code class="language-plaintext highlighter-rouge">stream()</code> для коллекций</h3> <p>Точнее, метод <code class="language-plaintext highlighter-rouge">$$.stream()</code> существовал и раньше, но в версии <a href="/2022/01/10/selenide-6.2.0/">6.2.0</a> был помечен как deprecated. Теперь вместо него есть на выбор два не-deprecated метода:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$$.asFixedIterable().stream()</code></li> <li><code class="language-plaintext highlighter-rouge">$$.asDynamicIterable().stream()</code></li> </ul> <p>См. <a href="https://github.com/selenide/selenide/issues/1773">issue 1773</a> и <a href="https://github.com/selenide/selenide/pull/1774">PR 1774</a>.</p> <p><br /></p> <h3 id="удалили-встроенную-селениумовскую-телеметрию-opentelemetry">Удалили встроенную селениумовскую телеметрию (OpenTelemetry)</h3> <p>Я так понимаю, эта телеметрия появилась в Selenium 4. Не знаю, зачем она вообще нужна, но кому-то мешала (потому, что у него уже была какая-то своя телеметрия). Ну и нам было проще её удалить, чем поддерживать обе.</p> <p>Спасибо <a href="https://github.com/zzz">Petro Ovcharenko</a> и <a href="https://github.com/zzz">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1763">PR 1763</a>.</p> <p><br /></p> <h3 id="обновили-зависимости">Обновили зависимости</h3> <ul> <li>Selenium <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">4.1.3 -&gt; 4.1.4</a>.</li> <li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">5.1.0 -&gt; 5.1.1</a></li> <li>Netty <a href="https://netty.io/news/2022/04/12/4-1-76-Final.html">4.1.75.Final -&gt; 4.1.76.Final</a></li> <li>Netty <a href="https://netty.io/news/2022/05/06/2-1-77-Final.html">4.1.76.Final -&gt; 4.1.77.Final</a></li> <li>LittleProxy <a href="https://github.com/LittleProxy/LittleProxy/releases">2.0.7 -&gt; 2.0.9</a></li> </ul> <p>Кстати, проект LittleProxy переехал в отдельную <a href="https://github.com/LittleProxy/LittleProxy">организацию на гитхабе</a>, и теперь я его главный мейнтейнер. Не то, чтобы я был фанатом проксей, но он используется в селениде, и поддерживать его как-то надо…</p> <p><br /></p> <h1 id="upd-selenide-651">UPD Selenide 6.5.1</h1> <p>Небольшое обновление <a href="https://github.com/selenide/selenide/milestone/153?closed=1">Selenide 6.5.1</a>:</p> <ul> <li><a href="https://github.com/selenide/selenide/issues/1808">#1808</a> Починили <code class="language-plaintext highlighter-rouge">$.clear()</code>, чтобы он не жмякал таб - см. <a href="https://github.com/selenide/selenide/pull/1809">PR #1809</a></li> <li><a href="https://github.com/selenide/selenide/pull/1806">#1806</a> Обновились с browserup-proxy-core 2.1.4 на 2.1.5</li> </ul> <p><br /></p> <h1 id="upd-selenide-652">UPD Selenide 6.5.2</h1> <p><a href="https://github.com/selenide/selenide/issues/1497">#1497</a> Починили <code class="language-plaintext highlighter-rouge">$.clear()</code> на свежем фаерфоксе: теперь жмякаем “Ctrl+A -&gt; Delete” вместо “Home -&gt; Shift+A -&gt; Delete”. См. <a href="https://github.com/selenide/selenide/pull/1838">PR #1838</a>.</p> <p><br /></p> <h3 id="новости">Новости</h3> <p>Отдельный привет всем, кто ждал, что же тут будет в конце пресс-релиза. ;)</p> <p>А будет моя презентация с последнего фестиваля TechTrain.</p> <iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRIZ4w6bc7wTjWVkHTfCdevRGMo81VRIgNQ-V5Bzy1FV9ldG3jtexEXnbJ79CMtOTKhao36VHKZlfs6/embed?start=false&amp;loop=true&amp;delayms=3000" frameborder="0" width="960" height="569" allow="fullscreen" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe> <p><br /></p> <p>Видео с TechTrain будет через несколько месяцев, а пока можете посмотреть похожее <a class="video" href="//www.youtube.com/watch?v=iIIsZRHya-w">видео из девклуба</a>. Оно немного другое, ибо ориентировано на другую аудиторию, но общее представление получить можно.</p> <p><br /></p> <p>Не переключайтесь!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/05/17/selenide-6.5.0/ http://ru.selenide.org/2022/05/17/selenide-6.5.0 2022-05-17T00:00:00+00:00 Вышла Selenide 6.4.0 <p><br /></p> <h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1> <p><br /> Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/145?closed=1">Selenide 6.4.0</a>.</p> <h3 id="показываем-и-алиас-и-локатор-при-падении-тестов">Показываем и алиас, и локатор при падении тестов</h3> <p>Как вы знаете, в Селениде с помощью метода <code class="language-plaintext highlighter-rouge">as</code> можно задавать элементам “алиас”, или понятное имя. Это полезно в тех случаях, когда нет хорошего локатора, и приходится писать какой-нибудь длинный сложный xpath, который потом в отчётах сложно читать.</p> <p>Например:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">$x</span><span class="o">(</span><span class="s">"/long/ugly/xpath/div[2]/span[3]/li[4]"</span><span class="o">).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>И у нас всегда была дилемма: нужно ли</p> <ol> <li>показывать только алиас (плюс: легко читается, минус: не видно селектора - вдруг он нужен?)</li> <li>показывать и алиас, и селектор (плюс: видно селектор, минус: трудно читается).</li> </ol> <p><br /></p> <p>И вот наконец мы поняли, как правильно. Начиная с версии 6.4.0, селенид будет:</p> <ol> <li>В отчётах на каждом шаге показывать только алиас (коротко, легко читается)</li> <li>А вот при падении теста в сообщение об ошибке добавлять и алиас, и локатор (длинно, зато будет вся необходимая информация для изучения падения).</li> </ol> <p>Например, вышеупомянутая строка в отчётах будет выглядеть вот так:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------------------+--------------------+------------+------------+ | Element | Subject | Status | ms. | +----------------------+--------------------+------------+------------+ | Login button | click() | FAIL | 206 | +----------------------+--------------------+------------+------------+ </code></pre></div></div> <p>А сообщение об ошибке - так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="s">"Login button"</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">:</span> <span class="o">/</span><span class="kt">long</span><span class="o">/</span><span class="n">ugly</span><span class="o">/</span><span class="n">xpath</span><span class="o">[</span><span class="mi">1</span><span class="o">][</span><span class="mi">2</span><span class="o">][</span><span class="mi">3</span><span class="o">]}</span> <span class="nl">Expected:</span> <span class="n">exist</span> <span class="nl">Screenshot:</span> <span class="o">...</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1765">issue 1765</a> и <a href="https://github.com/selenide/selenide/pull/1766">PR 1766</a>.</p> <p><br /></p> <h3 id="добавили-пробелы-в-селенидовском-отчёте">Добавили пробелы в селенидовском отчёте</h3> <p>Например, вышеупомянутый отчёт раньше выглядел так:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------------------+-------------------+------------+------------+ |Element |Subject | Status | ms. | +---------------------+-------------------+------------+------------+ |open |https://google.com/some-long-url.html?q=selenide|PASS |1285 | |Login button |click() | FAIL | 206 | +---------------------+-------------------+------------+------------+ </code></pre></div></div> <p>Проблема была как минимум в том, что вокруг URL слева и справа вплотную прилегают символы <code class="language-plaintext highlighter-rouge">|</code>, так что его невозможно быстро выделить двойным кликом. Теперь вокруг URL и других значений будут пробелы. Заодно отчёт и покрасивее стал.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1764">issue 1764</a> и <a href="https://github.com/selenide/selenide/pull/1767">PR 1767</a>.</p> <p><br /></p> <h3 id="обновились-на-selenium-413">Обновились на Selenium 4.1.3</h3> <p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a> и <a href="https://github.com/selenide/selenide/pull/1759">PR 1759</a>.</p> <p><br /></p> <p><br /></p> <h1 id="важно">ВАЖНО</h1> <p>Меня зовут Андрей Солнцев, я 10 лет делаю библиотеку Selenide и делюсь опытом на конференциях.<br /> Просто так, бесплатно. Я у вас никогда ничего за это не просил.</p> <p>А теперь я прошу всего лишь услышать меня. Вот как вижу ситуацию я - в целом неглупый человек, мнение которого, надеюсь, стало для многих из вас авторитетным за эти 10 лет.</p> <p><br /></p> <p>Россия начала <strong>войну в Украине</strong>. Бессмысленную и бесчеловечную.</p> <p>Там сейчас под бомбёжками, под обстрелами сидят мирные люди, которые никому ничего плохого не сделали.<br /> В том числе и пользователи селенида.</p> <ul> <li>Те, перед которыми я многократно выступал на конференциях.</li> <li>Те, которых я слушал и учился.</li> <li>Те, которые помогали развивать селенид, и результатами работы которых пользуются люди во всём мире, в том числе и России. Пользуетесь лично вы.</li> </ul> <p>Это тяжело признать, об этом тяжело думать, от этого ужаса хочется спрятаться - но такова реальность.</p> <p><br /> Я не знаю, как это остановить.<br /> Но я прошу вас хотя бы <em>не поддерживать это</em>.<br /> Я призываю вас не верить этой <em>чудовищной лжи</em> про нацистов, расширение НАТО, фейки и т.п.<br /> Я прошу вас оставаться людьми.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/04/07/selenide-6.4.0/ http://ru.selenide.org/2022/04/07/selenide-6.4.0 2022-04-07T00:00:00+00:00 Вышла Selenide 6.3.0 <p><br /></p> <h1 id="здравствуйте-друзья">Здравствуйте, друзья!</h1> <p><br /> Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/143?closed=1">Selenide 6.3.0</a>.</p> <p>Приготовьте бутеры и погнали!</p> <p><br /></p> <h1 id="добавили-метод-switchtoframetimeout-с-кастомным-таймаутом">Добавили метод <code class="language-plaintext highlighter-rouge">switchTo().frame(timeout)</code> с кастомным таймаутом</h1> <p>В селениде есть метод <code class="language-plaintext highlighter-rouge">switchTo().frame(name)</code> для переключения между фреймами. Как всегда, со встроенной ожидалкой и другими плюшками. Но что, если фрейм грузится дольше, чем таймаут по умолчанию (4 секунды)?</p> <p>Теперь вы можете передать дополнительный параметр <code class="language-plaintext highlighter-rouge">Duration</code> - таймаут для переключения во фрейм:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">switchTo</span><span class="o">().</span><span class="na">frame</span><span class="o">(</span><span class="s">"ifrm"</span><span class="o">);</span> <span class="c1">// по умолчанию ждёт до 4 секунд</span> <span class="n">switchTo</span><span class="o">().</span><span class="na">frame</span><span class="o">(</span><span class="s">"ifrm"</span><span class="o">,</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">6</span><span class="o">));</span> <span class="c1">// а вот теперь до 6 секунд</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/donesvad">@donesvad</a> за <a href="https://github.com/selenide/selenide/pull/1722">PR 1722</a>.</p> <p><br /></p> <h1 id="добавили-селекторы-bytagandtext-и-withtagandtext">Добавили селекторы <code class="language-plaintext highlighter-rouge">byTagAndText</code> и <code class="language-plaintext highlighter-rouge">withTagAndText</code></h1> <p>До сих пор в селениде были методы для поиска элементов по тексту:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selectors</span><span class="o">.*;</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Hello world"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span> </code></pre></div></div> <p>Иногда этого мало: когда элементов с таким текстом несколько, и хочется из них выбрать только один - с нужным тэгом.</p> <p>Теперь и для этого есть методы:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selectors</span><span class="o">.*;</span> <span class="err">$</span><span class="o">(</span><span class="n">byTagAndText</span><span class="o">(</span><span class="s">"h1"</span><span class="o">,</span> <span class="s">"Hello world"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="n">withTagAndText</span><span class="o">(</span><span class="s">"h1"</span><span class="o">,</span> <span class="s">"Hello"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/maurizio-lattuada">Maurizio Lattuada</a> за <a href="https://github.com/selenide/selenide/issues/1650">issue 1650</a> и <a href="https://github.com/selenide/selenide/pull/1651">PR 1651</a>.</p> <p><br /></p> <h1 id="исправили-ошибку-в-bytextcaseinsensitive">Исправили ошибку в <code class="language-plaintext highlighter-rouge">byTextCaseInsensitive</code></h1> <p>Теперь этот селектор игнорирует множественные пробелы и переводы строк в начале и конце текста (как и каноничный <code class="language-plaintext highlighter-rouge">byText</code>).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1723">issue 1723</a> и <a href="https://github.com/selenide/selenide/pull/1724">PR 1724</a>.</p> <p><br /></p> <h1 id="добавили-записи-webdriver-create-в-webdriver-close-в-отчёт">Добавили записи “webdriver create” в “webdriver close” в отчёт</h1> <p>Если вы используете селенидовский <code class="language-plaintext highlighter-rouge">TextReport</code> или плагин <code class="language-plaintext highlighter-rouge">AllureSelenide</code>, вы привыкли в конце каждого теста видеть отчёт, в котором видны все действия: открыли страничку - нашли элемент - кликнули и т.д.</p> <p>Теперь там добавилась запись, когда был запущен сам вебдрайвер. И когда закрыт. Иногда эта информация может быть полезна при отладке различных проблем с тестами.</p> <p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1715">PR 1715</a>.</p> <p><br /></p> <h1 id="исправили-переопределение-селениумовского-таймаута">Исправили переопределение селениумовского таймаута</h1> <p>В <a href="/2021/06/08/selenide-5.22.0/">Selenide 5.22.0</a> мы сделали хак, который менял дефалтовый селениумовский таймаут для общения с вебдрайвером с дичайших 3 часов до 2 минут.</p> <p>Однако выяснилось, что при обновлении на Selenium 4 этот хак сломался (как и положено всем хакам). Теперь мы его реанимировали.</p> <p>Напоминаю, теперь при общении между тестами и вебдрайвером таймаут такой:</p> <ul> <li>таймаут на соединение - 10 секунд</li> <li>таймаут на чтение - 1.5 минуты</li> </ul> <p>См. <a href="https://github.com/selenide/selenide/commit/cf02da5">commit cf02da5</a>.</p> <p><br /></p> <h1 id="убрали-двойное-заворачивание-ошибок-element-not-found-друг-в-друга">Убрали двойное заворачивание ошибок “Element not found” друг в друга</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/1705">issue 1705</a> и <a href="https://github.com/selenide/selenide/pull/1706">PR 1706</a>.</p> <p><br /></p> <h1 id="добавили-поддержку-типа-аутентификации-bearer">Добавили поддержку типа аутентификации <code class="language-plaintext highlighter-rouge">BEARER</code></h1> <p>В селениде давно уже есть способ аутентификации с разными типами:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"/basic-auth/hello"</span><span class="o">,</span> <span class="no">BASIC</span><span class="o">,</span> <span class="s">"scott"</span><span class="o">,</span> <span class="s">"tiger"</span><span class="o">);</span> </code></pre></div></div> <p>но точно правильно работал только тип <code class="language-plaintext highlighter-rouge">BASIC</code>. Остальные особо никто не проверял. :)</p> <p>Оказалось, что тип <code class="language-plaintext highlighter-rouge">BEARER</code> не работал. Теперь вот работает (а остальные по-прежнему никто не проверял :)).</p> <p>Использовать так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">open</span><span class="o">(</span><span class="s">"/bearer-token-auth/hello"</span><span class="o">,</span> <span class="no">BEARER</span><span class="o">,</span> <span class="k">new</span> <span class="nc">BearerTokenCredentials</span><span class="o">(</span><span class="s">"token-123"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/1714">PR 1714</a>.</p> <p><br /></p> <h1 id="теперь-пустые-настройки-типа-selenideremote-считаются-незаданными">Теперь пустые настройки типа <code class="language-plaintext highlighter-rouge">selenide.remote</code> считаются незаданными</h1> <p>Это должно чутка упростить жизнь девопсам.<br /> При настройках всяких там джобов и пайплайнов на CI сервере, часто приходится использовать переменные для задания настроек селенида:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.remote=${env.GRID_URL} </code></pre></div></div> <p>И если в какой-нибудь среде переменной <code class="language-plaintext highlighter-rouge">GRID_URL</code> не окажется, то селенид грохнется, т.к. попытается использовать пустой урл для <code class="language-plaintext highlighter-rouge">selenide.remote</code>.</p> <p>Так было раньше. А теперь селенид будет считать, что <code class="language-plaintext highlighter-rouge">selenide.remote</code> просто не задан, и продолжить работать как обычно.</p> <p>См. <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/issues/1656">issue 1656</a> и <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1663">PR 1663</a>.</p> <p><br /></p> <h1 id="обновились-на-selenium-412">Обновились на Selenium 4.1.2</h1> <p>После обновления на Selenium 4.1.2 у многих слетела версия Guava. Селенид явно определяет нужную версию Guava, так что вас эта проблема затронуть не должна. Но если что, убедитесь, что в вашем проекте никакой там градловский или мавеновский плагин не переопределяет версию Guava. Старый добрый <a href="/2020/11/17/why-proxy-does-not-work-in-selenoid/">пост про зависимости</a> в помощь.</p> <p>См. <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">ченджлог Selenium</a> и <a href="https://github.com/selenide/selenide/pull/1719">PR 1719</a>.</p> <p><br /></p> <h1 id="upd-selenide-631">UPD Selenide 6.3.1</h1> <p>Небольшое обновление <a href="https://github.com/selenide/selenide/milestone/146?closed=1">Selenide 6.3.1</a>:</p> <ul> <li><a href="https://github.com/selenide/selenide/issues/1731">#1731</a> вернули возможность использовать софт ассерты в TestNG в методах <code class="language-plaintext highlighter-rouge">@Before*</code> и <code class="language-plaintext highlighter-rouge">@After*</code> (ранее мы их случайно запретили в Selenide 6.2.0) - см. <a href="https://github.com/selenide/selenide/pull/1732">PR #1732</a></li> <li><a href="https://github.com/selenide/selenide/pull/1729">#1729</a> Обновились с Netty 4.1.73.Final на 4.1.74.Final</li> </ul> <p><br /></p> <h1 id="upd-selenide-632">UPD Selenide 6.3.2</h1> <p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/147?closed=1">Selenide 6.3.2</a>:</p> <ul> <li><a href="https://github.com/selenide/selenide/pull/1733">#1733</a> Запилили костыль для баги селениума <a href="https://github.com/SeleniumHQ/selenium/issues/10345">10345</a>, из-за которой метод <code class="language-plaintext highlighter-rouge">FirefoxDriver.close()</code> валится после обновления на Firefox 97.</li> <li><a href="https://github.com/selenide/selenide/pull/1736">#1736</a> Обновились с BrowserUpProxy 2.1.3 на 2.1.4</li> <li><a href="https://github.com/selenide/selenide/pull/1611">#1611</a> Обновили версию Java с 8 до 17.</li> </ul> <p>А вот тут давайте остановимся поподробнее. Теперь проект Selenide собирается на Java 17, но бинарники <code class="language-plaintext highlighter-rouge">selenide-*.jar</code> по-прежнему собираются для Java 8. Поэтому</p> <ul> <li>разработчики селенида могут использовать все последние фичи языка Java, а</li> <li>пользователи селенида могут по-прежнему запускать свои тесты на Java 8 (хоть мы и советуем обновить джавушку, конечно).</li> </ul> <p>А стало это возможным благодаря инструменту <a href="https://github.com/bsideup/jabel">Jabel</a> и лично <a href="https://github.com/bsideup"> Sergei Egorov</a>, придумавшему этот изящный хак (а ещё причастному к <a href="https://github.com/testcontainers/testcontainers-java">TestContainers</a> и <a href="https://www.atomicjar.com/">AtomicJar</a>).</p> <p><br /></p> <h1 id="upd-selenide-633">UPD Selenide 6.3.3</h1> <p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/148?closed=1">Selenide 6.3.3</a>:</p> <ul> <li>#1737 позволили переопределять настройки Firefox для скачивания файлов</li> <li>#1740 обновились на WebDriverManager 5.1.0</li> </ul> <p><br /></p> <h1 id="upd-selenide-634">UPD Selenide 6.3.4</h1> <p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/149?closed=1">Selenide 6.3.4</a>:</p> <ul> <li>#1746 показываем ожидаемый атрибут при падении <code class="language-plaintext highlighter-rouge">$.shouldHave(attribute(...))</code>.</li> <li>#1748 исправили имя модуля в сгенерированных бинарниках селенида</li> </ul> <p><br /></p> <h1 id="upd-selenide-635">UPD Selenide 6.3.5</h1> <p>Ещё обновление <a href="https://github.com/selenide/selenide/milestone/150?closed=1">Selenide 6.3.5</a>:</p> <ul> <li><a href="https://github.com/selenide/selenide/issues/1755">#1755</a> починили скачивание файлов через прокси, если ответ сервера закодирован - см. <a href="https://github.com/selenide/selenide/pull/1756">PR #1756</a></li> </ul> <p><br /></p> <h1 id="новости">Новости</h1> <ul> <li>Мы создали группу в LinkedIn <a href="https://www.linkedin.com/groups/9154550/">Selenide User Group</a>!</li> <li>Пост от Miklós Szeles <a href="https://www.linkedin.com/pulse/selenium-selenide-mikl%25C3%25B3s-szeles/">Selenium or Selenide?</a></li> <li>Селенид попал в подборку <a href="https://qameta.io/blog/5-testing-automation-tools/">5 Testing Automation Tools</a> в блоге компании Qameta Software (это которая Allure Report)</li> <li>Селенид попал в подборку <a href="https://hackernoon.com/top-java-libraries-for-automation-testing-in-2022">Top Java Libraries for Automation Testing in 2022</a></li> <li>Пост <a href="https://blog.knoldus.com/selenide-concise-ui-test-in-java/">про Селенид</a> в блоге компании Knoldus</li> <li>Пост <a href="https://medium.com/@gaveen0513/selenide-the-software-that-doesnt-need-documentation-cda8535cb7e6">The software that doesn’t need documentation</a> от Gaveen Nayanajith</li> <li>Пост <a href="https://mszeles.com/selenide-i-think-this-is-the-beginning-of-a-beautiful-friendship">начало прекрасной дружбы</a> от Miklós Szeles</li> </ul> <p><br /></p> <h4 id="я-думаю-это-начало-прекрасной-дружбы">Я думаю, это начало прекрасной дружбы!</h4> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/02/07/selenide-6.3.0/ http://ru.selenide.org/2022/02/07/selenide-6.3.0 2022-02-07T00:00:00+00:00 Вышла Selenide 6.2.0 <p><br /></p> <h1 id="с-новым-годом">С новым годом!</h1> <p><br /> Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/140?closed=1">Selenide 6.2.0</a>.</p> <p>Давайте посмотрим, что нам приготовил первый релиз в новом году.</p> <p>Наливайте чай и погнали! <br /></p> <h1 id="добавили-ссылку-click-to-see-difference-для-большинства-селенидовских-ошибок">Добавили ссылку “&lt;Click to see difference&gt;” для большинства селенидовских ошибок</h1> <p>Как вы помните, в <a href="/2021/09/28/selenide-5.25.0/">Selenide 5.25.0</a> мы добавили поддержку OpenTest4J, в результате чего у селенидовских ошибок в IDE появилась ссылочка “&lt;Click to see difference&gt;”, позволяющая удобно посмотреть различия между ожидаемым и актуальным значением.</p> <p>Но тогда мы всё порефакторить не успели, и ссылочка появилась не у всех типов ошибок. <br /> А теперь должна появиться у всех.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1589">issue 1589</a> и <a href="https://github.com/selenide/selenide/pull/1676">PR 1676</a>.</p> <p><br /></p> <h1 id="заменили-iterator-на-asdynamiciterable-и-asfixediterable">Заменили <code class="language-plaintext highlighter-rouge">$$.iterator()</code> на <code class="language-plaintext highlighter-rouge">$$.asDynamicIterable()</code> и <code class="language-plaintext highlighter-rouge">$$.asFixedIterable()</code></h1> <blockquote> <p><em>Ух какую старую болячку мы исправили!</em> <em>Ну молодцы же!</em></p> </blockquote> <p>Есть в селениде метод <code class="language-plaintext highlighter-rouge">$$</code> для коллекции элементов. Возвращает он объект класса <code class="language-plaintext highlighter-rouge">ElementsCollection</code>.<br /> Изначально у него должен был быть только один метод <code class="language-plaintext highlighter-rouge">$$.shouldHave(&lt;условие&gt;)</code>. Хочешь проверить коллекцию элементов на соответствие какому-то условию - передай нужное условие. Не нашёл готового условия - напиши своё, благо это легко.</p> <h3 id="the-ошибка">The ошибка</h3> <p>Но в те давние времена случилось так, что я наследовал класс <code class="language-plaintext highlighter-rouge">ElementsCollection</code> от <code class="language-plaintext highlighter-rouge">AbstractList&lt;SelenideElement&gt;</code>. Об этом решении я неоднократно жалел впоследствии.</p> <p>Что же случилось, спросите вы? А случилось то, что люди начали абьюзить коллекции как не в себя.</p> <ul> <li>Например, начали итерировать все элементы в коллекции (это <em>случайно</em> стало возможным благодаря наследованию от <code class="language-plaintext highlighter-rouge">AbstractList</code>).</li> <li>И ещё люди ожидают, что элементы в коллекции будут постоянно обновляться, ведь до сих пор селенид автоматически обновлял любые веб-элементы.</li> <li>Помимо сомнительного тест-дизайна, это вызвало ещё и проблемы с производительностью, ведь на каждом шаге итерации селенид должен загрузить заново всю коллекцию элементов. А это может быть медленно, особенно есть элементов много или коллекция фильтруется: <code class="language-plaintext highlighter-rouge">$$("div").filter(visible).filterBy(text("Hello"))</code>.</li> </ul> <h3 id="the-дилемма">The дилемма</h3> <p>Получилась дилемма:</p> <ul> <li>одним хотелось, чтобы селенид при итерировании перегружал элементы каждый раз, ведь это позволяет не получать <code class="language-plaintext highlighter-rouge">StaleElementException</code>, когда элементы на странице исчезают и появляются;</li> <li>а другим хотелось, наоборот, чтобы селенид не перегружал элементы при итерировании, ведь это ускоряет тест на больших коллекциях.</li> </ul> <p>Какой вариант ни выберешь - всем не угодишь.</p> <h3 id="the-решение">The решение</h3> <p>И наконец мы придумали, как решить эту дилемму. Случайно унаследованные от <code class="language-plaintext highlighter-rouge">AbstractList</code> методы типа <code class="language-plaintext highlighter-rouge">$$.iterator()</code> мы пометили как <code class="language-plaintext highlighter-rouge">@Deprecated</code>. Вместо них предлагаем вам два метода, из которых вы сами сможете выбрать согласно вашим потребностям:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$$.asDynamicIterable()</code> - перегружает элементы коллекции при итерировании. Может быть медленным на больших коллекциях.</li> <li><code class="language-plaintext highlighter-rouge">$$.asFixedIterable()</code> - не перегружает элементы при итерировании. Он быстрее, но не получает обновлений, если элементы удаляются или добавляются во время итерирования.</li> </ul> <h3 id="the-совет">The совет</h3> <p>Какой из них я вам советую?</p> <p>НИКАКОЙ! :)</p> <p>Напоминаю, что в хорошем тесте, скорее всего, не должно быть циклов, условий и т.п.<br /> Вы должны точно знать, сколько элементов ожидается на странице, и какие свойства должны быть у каждого из них! <em>Просто проверьте эти свойства.</em></p> <p>Вместо итерирования всегда лучше написать <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/CustomCollectionConditionTest.java">свой <code class="language-plaintext highlighter-rouge">CollectionCondition</code></a>. Благо это легко.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/797">issue 797</a> и <a href="https://github.com/selenide/selenide/pull/1688">PR 1688</a>.</p> <p><br /></p> <h1 id="исправили-softassert-чтобы-он-не-валил-тест">Исправили SoftAssert, чтобы он не валил тест</h1> <p>в довольно редкой ситуации:</p> <ol> <li>если софт ассерт listener/rule/extension добавлен к тесту, но</li> <li>софт ассерты <strong>выключены</strong>, и</li> <li>внутри теста кидается и тут же ловится ошибка (try/catch).</li> </ol> <p>Вообще в такой ситуации надо исправлять сам тест. :(</p> <p>И всё же. До сих пор селенидовский софт ассерт валил тест, потому что в ходе теста была ошибка, и листенер это заметил. А теперь листенер стал чуть умнее и тест не валит.</p> <blockquote> <p><em>Уф, ну и проблемки вы иногда подкидываете, дорогие пользователи… :)</em></p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/issues/1646">issue 1646</a> и <a href="https://github.com/selenide/selenide/pull/1680">PR 1680</a>.</p> <p><br /></p> <h1 id="исправили-софт-ассерты-чтобы-они-включали-все-падения">Исправили софт ассерты, чтобы они включали все падения</h1> <p>Ещё одна редкая проблема с софт ассертами. Представьте ситуацию:</p> <ol> <li>Софт ассерты включены;</li> <li>В ходе теста случились какие-то селенидовские ошибки (пойманы софт ассерт листенером);</li> <li>А также в ходе теста случилась какая-то другая ошибка (банальный <code class="language-plaintext highlighter-rouge">NPE</code> или <code class="language-plaintext highlighter-rouge">assertEquals(2, 3)</code>).</li> </ol> <p>В этом случае селенидовский софт ассерт листенер валил тест (что правильно), но показывал только селенидовские ошибки (п. 2) и терял “другую” ошибку (п.3).</p> <p>Теперь листенер стал умнее, и показывает все ошибки.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1661">issue 1661</a> и <a href="https://github.com/selenide/selenide/pull/1679">PR 1679</a>.</p> <p><br /></p> <h1 id="добавили-локаторы-к-некоторым-селенидовским-ошибкам">Добавили локаторы к некоторым селенидовским ошибкам</h1> <p>Мелочь, но стоит упомянуть.</p> <p>Мы добавили селектор к некоторым селенидовским ошибкам.<br /> Например, там, где селенид раньше кидал такую ошибку:</p> <blockquote> <p>“Invalid element state: Cannot change invisible element”</p> </blockquote> <p>Теперь он будет кидать такую:</p> <blockquote> <p>“Invalid element state [.btn.btn-primary]: Cannot change invisible element”</p> </blockquote> <p><br /></p> <h1 id="обновили-browserupproxy-с-212-на-213">Обновили BrowserUpProxy с 2.1.2 на 2.1.3</h1> <p>Важно отметить, что версия 2.1.3 - это форк оригинального BrowserUpProxy. Автор объявил о прекращении поддержки, добровольцы переняли инициативу и зарелизили версию 2.1.3 <a href="https://github.com/browserup/browserup-proxy/issues/388#issuecomment-1004097733">из форка</a>.</p> <p>Будем следить за ситуацией. В идеале хорошо бы перейти с BrowserUpProxy на mitmproxy. Есть добровольцы?</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1678">PR 1678</a>.</p> <p><br /></p> <h1 id="обновили-testng-с-740-на-75">Обновили TestNG с 7.4.0 на 7.5</h1> <p>Список изменений <a href="https://github.com/cbeust/testng/blob/7.5/CHANGES.txt">внушительный</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1682">PR 1682</a>.</p> <p><br /></p> <p>С Новым Годом, друзья!<br /> Стабильных тестов вам и красивых отчётиков!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2022/01/10/selenide-6.2.0/ http://ru.selenide.org/2022/01/10/selenide-6.2.0 2022-01-10T00:00:00+00:00 Вышла Selenide 6.1.1 <p><br /></p> <h1 id="tere-hommikust">TERE HOMMIKUST!</h1> <p><br /> Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/141?closed=1">Selenide 6.1.1</a>.</p> <p>В этом маленьком релизе мы исправили сразу пачку проблем с настройками браузеров.<br /> Они все всплыли после обновления на Selenium 4, в котором <code class="language-plaintext highlighter-rouge">ChromeOptions</code> и другие <code class="language-plaintext highlighter-rouge">Capabilities</code> были серьёзно переработаны.</p> <p>Ну вот, теперь мы погрузились в тему и разом все проблемы исправили.</p> <p><br /></p> <h1 id="early-conflicts-detection">Раннее обнаружение конфликтов</h1> <p>Если вы попытаетесь открыть хром с настройками файерфокса:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">browser</span> <span class="o">=</span> <span class="s">"chrome"</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="no">FIREFOX_OPTIONS</span><span class="o">,</span> <span class="k">new</span> <span class="nc">FirefoxOptions</span><span class="o">());</span> </code></pre></div></div> <p>то Селенид версии 5.x ругался, а Селенид 6.0.x перестал ругаться. Теперь мы ругань восстановили, и вы снова увидите старое доброе</p> <blockquote> <p>IllegalArgumentException: Conflicting browser name: ‘chrome’ vs. ‘firefox’</p> </blockquote> <p>См. <a href="https://github.com/selenide/selenide/issues/1591">issue 1591</a> и <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p> <p><br /></p> <h1 id="merging-chrome-arguments">Слияние аргументов хрома</h1> <p>Если вы попытаетесь задать хрому, например, полноэкранный режим или язык:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ChromeOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChromeOptions</span><span class="o">();</span> <span class="n">options</span><span class="o">.</span><span class="na">addArguments</span><span class="o">(</span><span class="s">"--start-fullscreen"</span><span class="o">,</span> <span class="s">"--start-incognito"</span><span class="o">);</span> <span class="n">options</span><span class="o">.</span><span class="na">setExperimentalOption</span><span class="o">(</span><span class="s">"prefs"</span><span class="o">,</span> <span class="nc">ImmutableMap</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"intl.accept_languages"</span><span class="o">,</span> <span class="s">"de_DE"</span><span class="o">));</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">chromeOptions</span><span class="o">;</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://codeborne.com"</span><span class="o">;)</span> </code></pre></div></div> <p>то эти настройки терялись в Selenide 6.0.x Теперь мы их восстановили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1626">issue 1626</a>, <a href="https://github.com/selenide/selenide/issues/1630">issue 1630</a> и <a href="https://github.com/selenide/selenide/issues/1631">issue 1631</a>.</p> <p>Исправление в <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p> <p><br /></p> <h1 id="wrapping-browser-capabilities">ВАЖНО</h1> <p>Если вы использовали <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities</code>, то с большой вероятностью заворачивали их в <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ChromeOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChromeOptions</span><span class="o">();</span> <span class="n">options</span><span class="o">.</span><span class="na">addArguments</span><span class="o">(...);</span> <span class="nc">DesiredCapabilities</span> <span class="n">caps</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DesiredCapabilities</span><span class="o">();</span> <span class="n">caps</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="nc">ChromeOptions</span><span class="o">.</span><span class="na">CAPABILITY</span><span class="o">,</span> <span class="n">options</span><span class="o">);</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">caps</span><span class="o">;</span> </code></pre></div></div> <p>Так вот, теперь этот <strong>код нужно упростить</strong>, чтобы настройки больше не терялись:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ChromeOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChromeOptions</span><span class="o">();</span> <span class="n">options</span><span class="o">.</span><span class="na">addArguments</span><span class="o">(...);</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">options</span><span class="o">;</span> </code></pre></div></div> <p><br /></p> <h1 id="webdriver-provider-parameter-type">Поменяли тип параметра в <code class="language-plaintext highlighter-rouge">WebDriverProvider</code></h1> <p>… с <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> на просто <code class="language-plaintext highlighter-rouge">Capabilities</code>.</p> <p>Для вас почти ничего не меняется. Если вы используете в своих тестах <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>, просто поменяйте <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> на <code class="language-plaintext highlighter-rouge">Capabilities</code>, и всё будет по-старому.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p> <p><br /></p> <p><em>Больше капабилитей богу капабилитей!</em></p> <p><br /></p> <h1 id="upd-selenide-6.1.2">UPD Selenide 6.1.2</h1> <p>Мы выпустили Selenide 6.1.2 с обновлением на Selenium 4.1.1 - эта версия содержит несколько заметных исправлений.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/11/24/selenide-6.1.1/ http://ru.selenide.org/2021/11/24/selenide-6.1.1 2021-11-24T00:00:00+00:00 Вышла Selenide 6.1.0 <p><br /></p> <h1 id="tere">TERE!</h1> <p><br /> Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/137?closed=1">Selenide 6.1.0</a>.</p> <p><br /></p> <h1 id="добавили-поддержку-selenideproperties">Добавили поддержку <code class="language-plaintext highlighter-rouge">selenide.properties</code></h1> <p>Теперь Селенид умеет читать настройки из отдельного файлика <code class="language-plaintext highlighter-rouge">selenide.properties</code>, если таковой найдётся в classpath.</p> <p>NB! Лично я всё ещё не вижу пользы от этого, ведь проще задать настройки</p> <ol> <li>прямо в коде: <code class="language-plaintext highlighter-rouge">Configuration.timeout = 8000;</code></li> <li>или через system properties: <code class="language-plaintext highlighter-rouge">-Dselenide.timeout=8000</code>.</li> </ol> <p>Прошу, не надо резко ломиться генерировать эти файлики. Используйте <code class="language-plaintext highlighter-rouge">selenide.properties</code>, только если у вас есть хорошие причины для этого, а не просто потому, что теперь это модно или “так красиво”.</p> <p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1601">PR 1601</a>.</p> <p><br /></p> <h1 id="добавили-возможность-тонкой-настройки-прокси">Добавили возможность тонкой настройки прокси</h1> <p>Как вы знаете, Селенид умеет запускать свой прокси-сервер, который даёт нам некоторые дополнительные возможности. Но возможности для настройки прокси до сих пор были ограниченные. Только <code class="language-plaintext highlighter-rouge">Configuration.proxyHost</code> и <code class="language-plaintext highlighter-rouge">Configuration.proxyPort</code>.</p> <p>Теперь же можно будет получить инстанс BrowserModProxy и настроить его как угодно до запуска браузера.</p> <p>NB! Пожалуйста, не переусердствуйте с этим. Тут очень легко выстрелить себе в ногу.</p> <p>И если ваши настройки действительно помогли вам, то возможно, они помогут и другим. Расскажите нам, что вы там такого настроили - может, стоит сделать это в селениде по умолчанию?</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1561">issue 1561</a>. Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1620">PR 1620</a>.</p> <p><br /></p> <h1 id="добавили-костыль-для-избежания-случайных-noclassdeffounderror-в-webdriverexception">Добавили костыль для избежания случайных <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code> в <code class="language-plaintext highlighter-rouge">WebDriverException</code>.</h1> <p>В Селениум есть <a href="https://github.com/SeleniumHQ/selenium/issues/9784">бага</a>, которая полностью до сих пор не исправлена. Но вы её больше не увидите, потому что теперь в селениде есть костыль против неё. :)</p> <p>См. <a href="https://github.com/selenide/selenide/commit/2eff0307e3a">костыль</a>.</p> <p><br /></p> <h1 id="поменяли-тип-параметра-selenideconfigbrowsercapabilities">Поменяли тип параметра <code class="language-plaintext highlighter-rouge">SelenideConfig.browserCapabilities()</code></h1> <p>… с <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> на <code class="language-plaintext highlighter-rouge">MutableCapabilities</code>.</p> <p>Это позволяет упростить ваш код и не заворачивать <code class="language-plaintext highlighter-rouge">ChromeOptions</code> в <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code>. Больше об упрощении капабилитей этом будет в следующем релизе <code class="language-plaintext highlighter-rouge">Selenide 6.1.1</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1637">PR 1637</a>.</p> <p><br /></p> <h1 id="обновились-на-selenium-webdriver-410">Обновились на Selenium Webdriver 4.1.0</h1> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1638">PR 1638</a>.</p> <p><br /></p> <h1 id="удалили-метод-shadowroot">Удалили метод <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></h1> <p>Этот метод сломался после обновления на Chrome 96, и вероятно, вскоре сломается и в остальных браузерах. Починить его муторно, и при этом <a href="https://github.com/selenide/selenide/issues/1515#issuecomment-894476289">пользы от этого метода немного</a>, ведь для поиска элементов внутри Shadow DOM есть <a href="/2020/03/18/selenide-5.10.0/">более удобные и быстрые методы</a>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1640">issue 1640</a> и <a href="https://github.com/selenide/selenide/pull/1641">PR 1641</a>.</p> <p><br /></p> <h1 id="новости">Новости</h1> <ul> <li>Первый <a href="https://www.eventbrite.com/e/propeller-testops-hackathon-registration-194333114577">TestOps Hackathon</a> от PropellerAds и Qameta Software перенесли на 1-6 декабря. Регистрируйтесь!</li> <li><a href="https://www.lambdatest.com/selenium-automation-testing-with-selenide-framework">Как использовать Selenide с сервисом Lambdatest</a></li> <li>Пост <a href="https://mbbaig.blog/selenide-webdriverfactory/">Selenide - Create a Custom WebDriver</a> от Boris Bay</li> <li>Оказывается, <a href="https://www.linkedin.com/feed/update/urn:li:activity:6867477909766979584/">LinkedIn проводит курсы по Селениду</a> и даже выдаёт красивенькие дипломы.</li> </ul> <p><br /></p> <h1 id="статистика-использования-селенида">Статистика использования Селенида</h1> <center> <img src="/images/2021/11/selenide.downloads.png" width="800" /> </center> <p><br /> В октябре мы сделали мощный скачок и перевалили за <strong>280 тысяч скачиваний</strong> в месяц. Эгегей!</p> <p><em>Больше скачиваний богу скачиваний!</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/11/23/selenide-6.1.0/ http://ru.selenide.org/2021/11/23/selenide-6.1.0 2021-11-23T00:00:00+00:00 Вышла Selenide 6.0.1 <p><br /></p> <h1 id="день-рождения">День рождения!</h1> <p>Ура! Сложно поверить, но сегодня Селениду исполняется… 10 лет!<br /> Именно в этот день 10 лет назад был сделан <a href="https://github.com/selenide/selenide/commit/3716078fc7fda8c5da01d871882d513cbd97cd0e">первый коммит</a> в репозитории.</p> <p>Огромное спасибо всем, кто участвовал в проекте, коммитил, репортил баги, предлагал идеи, отвечал на вопросы в форумах, критиковал и рассказывал про селенид на конференциях и митапах. Да и просто решился использовать селенид в своих проектах. Вы все - часть этого движения.</p> <p><br /> Ну а мы в честь юбилея зарелизили мажорную версию <a href="https://github.com/selenide/selenide/milestone/136?closed=1">Selenide 6.0.1</a>.</p> <p><br /></p> <h1 id="обновились-на-selenium-webdriver-400">Обновились на Selenium Webdriver 4.0.0</h1> <p>Если вы не используете Grid, то для вас особо ничего и не должно поменяться. Всё более-менее работает как раньше. Ну там, переименовали или задеприкейтили некоторые классы, вроде нестрашно.</p> <p>В Selenium 4 появились и <a href="https://www.browserstack.com/guide/selenium-4-features">новые возможности</a> (такие как CDP и Relative locators), но какой-то отдельной поддержки для них в селениде мы пока не делали. Если понадобится, будем делать в следующих версиях.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1162">issue 1162</a>, <a href="https://github.com/selenide/selenide/pull/1605">PR 1605</a>, <a href="https://github.com/selenide/selenide/pull/1614">PR 1614</a> и <a href="https://github.com/selenide/selenide/pull/1617">PR 1617</a>.</p> <p><br /></p> <h1 id="разбили-selenide-на-несколько-артефактов">Разбили Selenide на несколько артефактов</h1> <p>Раньше весь код селенида поставлялся одним джарником: <code class="language-plaintext highlighter-rouge">selenide-5.25.0.jar</code>.<br /> Предполагалось, что дополнительно к нему вы должны были сами добавить и другие зависимости: JUnit или TestNG, и возможно, BrowserMobProxy.</p> <p>Теперь же мы решили упростить вам задачу (особенно с прокси).</p> <ol> <li>Для пользователей Selenide и JUnit5 ничего не меняется: <br /><code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide:6.0.1)</code></li> <li>Пользователи JUnit4 должны будут использовать <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-junit4:6.0.1)</code></li> <li>Пользователи TestNG должны будут использовать <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-testng:6.0.1)</code></li> <li>Если вы хотите использовать прокси, достаточно добавить в проект зависимость <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-proxy:6.0.1)</code> - и можно больше не заморачиваться с версиями BrowserUpProxy, Netty и т.п.</li> <li>Ну и если среди вас есть радикальные ненавистники статических методов, теперь и вы сможете быть счастливы, используя <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-core:6.0.1)</code> - и тогда у вас в проекте не будет статических методов <code class="language-plaintext highlighter-rouge">Selenide.*</code>, а только чистый <code class="language-plaintext highlighter-rouge">SelenideDriver</code>.</li> </ol> <p>См. <a href="https://github.com/selenide/selenide/pull/1612">PR 1612</a>.</p> <p><br /></p> <h1 id="подчистили-кучу-deprecated-методов">Подчистили кучу deprecated методов</h1> <p>По <a href="https://github.com/selenide/selenide/pull/1607/files">этой ссылке</a> всегда можете найти, что именно удалили и чем это заменить.<br /> В частности,</p> <ul> <li><code class="language-plaintext highlighter-rouge">ElementsCollection.shouldHaveSize()</code> -&gt; <code class="language-plaintext highlighter-rouge">ElementsCollection.shouldHave(size())</code></li> <li><code class="language-plaintext highlighter-rouge">$.waitUntil(_, timeout)</code> -&gt; <code class="language-plaintext highlighter-rouge">$.should(_, Duration.ofMillis(timeout))</code></li> <li><code class="language-plaintext highlighter-rouge">$.waitWhile(_, timeout)</code> -&gt; <code class="language-plaintext highlighter-rouge">$.shouldNot(_, Duration.ofMillis(timeout))</code></li> <li><code class="language-plaintext highlighter-rouge">Condition.disappears</code> -&gt; <code class="language-plaintext highlighter-rouge">Condition.hidden</code></li> <li><code class="language-plaintext highlighter-rouge">Condition.matchesText</code> -&gt; <code class="language-plaintext highlighter-rouge">Condition.matchText</code></li> <li><code class="language-plaintext highlighter-rouge">Selenide.close</code> -&gt; <code class="language-plaintext highlighter-rouge">Selenide.closeWebDriver()</code> или <code class="language-plaintext highlighter-rouge">Selenide.closeWindow()</code> <br /></li> </ul> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1607/files">PR 1607</a> и <a href="https://github.com/selenide/selenide/pull/1609">PR 1609</a>.</p> <p><br /></p> <h1 id="удалили-поддержку-браузера-legacy_firefox">Удалили поддержку браузера “legacy_firefox”</h1> <p>Это был такой старый вебдрайвер, который работал только с Firefox 52 и старше. Полагаю, ему пора на покой.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1610">PR 1610</a>.</p> <p><br /></p> <h1 id="удалили-старые-настройки">Удалили старые настройки</h1> <h3 id="configurationstartmaximized"><code class="language-plaintext highlighter-rouge">Configuration.startMaximized</code></h3> <p>Есть мнение, что это плохая практика, потому что размер окна браузера зависит от текущего окружения, что может привести к моргающим тестам. Рекомендуем использовать <code class="language-plaintext highlighter-rouge">Configuration.browserSize</code> (по умолчанию <code class="language-plaintext highlighter-rouge">1366x768</code>).</p> <h3 id="configurationversatilesetvalue"><code class="language-plaintext highlighter-rouge">Configuration.versatileSetValue</code></h3> <p>Скорее всего вы её и не использовали, ведь она давным-давно была <code class="language-plaintext highlighter-rouge">false</code> по умолчанию.<br /> Для выбора значений в <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> и <code class="language-plaintext highlighter-rouge">&lt;input type=radio&gt;</code> можно использовать старый добрые методы <code class="language-plaintext highlighter-rouge">$.selectOptionByValue()</code> и <code class="language-plaintext highlighter-rouge">$.selectRadio()</code>.</p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1619">PR 1619</a>.</p> <p><br /></p> <h1 id="починили-метод-selenidesleepn">Починили метод <code class="language-plaintext highlighter-rouge">Selenide.sleep(N)</code></h1> <p>Казалось бы, разве такой просто однострочный метод может быть сломан? А вот может. Оказывается, стандартный джавовый метод <code class="language-plaintext highlighter-rouge">Thread.sleep(N)</code> не обязательно спит именно N мс, он может проснуться и раньше. И это может привести к моргающим тестам, если ваш тест рассчитывал именно на определённую паузу. Век живи - век учись.</p> <p>Теперь метод <code class="language-plaintext highlighter-rouge">Selenide.sleep(N)</code> гарантированно спит заданное количество миллисекунд.</p> <p>См. <a href="https://github.com/selenide/selenide/blob/b05d53dfb794ee02e795587867c6ec8022171040/statics/src/main/java/com/codeborne/selenide/Selenide.java#L258">реализацию</a>.</p> <p><br /></p> <h1 id="добавили-метод-для-добавления-и-удаления-webdriverlistener">Добавили метод для добавления и удаления <code class="language-plaintext highlighter-rouge">WebDriverListener</code></h1> <p>Раньше в селениде можно было добавлять <code class="language-plaintext highlighter-rouge">WebDriverEventListener</code>, но этот класс был заменён на <code class="language-plaintext highlighter-rouge">WebDriverListener</code> в Selenium 4. Ну вот, пришлось поддерживать оба.</p> <p><br /></p> <p>См. <a href="https://github.com/selenide/selenide/issues/1615">issue 1615</a> и <a href="https://github.com/selenide/selenide/pull/1616">PR 1616</a>.</p> <p><br /></p> <h1 id="поменяли-сигнатуру-метода-conditionapply">Поменяли сигнатуру метода <code class="language-plaintext highlighter-rouge">Condition.apply</code></h1> <p>Если вы не писали свои Conditions, то вас это не касается.</p> <p>А если успели наваять, то придётся их немножко дополнить.</p> <p>Итак, раньше в классе <code class="language-plaintext highlighter-rouge">Condition</code> был метод <code class="language-plaintext highlighter-rouge">apply</code>, который возвращал boolean:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">apply</span><span class="o">(</span><span class="nc">Driver</span> <span class="n">driver</span><span class="o">,</span> <span class="nc">WebElement</span> <span class="n">element</span><span class="o">)</span> </code></pre></div></div> <p>Теперь его нужно переименовать в <code class="language-plaintext highlighter-rouge">check</code>, и возвращать он должен не просто boolean, а <code class="language-plaintext highlighter-rouge">CheckResult</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">CheckResult</span> <span class="nf">check</span><span class="o">(</span><span class="nc">Driver</span> <span class="n">driver</span><span class="o">,</span> <span class="nc">WebElement</span> <span class="n">element</span><span class="o">)</span> </code></pre></div></div> <p>А этот <code class="language-plaintext highlighter-rouge">CheckResult</code> содержит не только признак “проверка прошла / не прошла”, но и какое значение было у элемента в тот момент. Это позволить формировать более точное сообщение об ошибке в случае падения теста.</p> <p>P.S. Впрочем, старый метод <code class="language-plaintext highlighter-rouge">apply</code> всё ещё остался как deprecated, поэтому какое-то время вы можете сидеть на старых кондишинах. Просто при падении теста строчка “Actual value: “ не будет добавляться к сообщению об ошибке. Но это и не всегда критично.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/217">issue 217</a>, <a href="https://github.com/selenide/selenide/pull/1586">PR 1586</a> и <a href="https://github.com/selenide/selenide/pull/1618">PR 1618</a>.</p> <p><br /></p> <h1 id="selenide-selenoid-201">selenide-selenoid 2.0.1</h1> <p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.0.0"><code class="language-plaintext highlighter-rouge">selenide-selenoid:2.0.0</code></a> с обновлением на Selenide 6.0.1</p> <p><br /></p> <h1 id="selenide-appium">selenide-appium</h1> <p>Кажется, пока что Appium не поддерживает Selenium 4, так что <code class="language-plaintext highlighter-rouge">selenide-appium</code> пока обновить не можем. Будет следить за ситуацией.</p> <p><br /></p> <h1 id="upd-selenide-602">UPD Selenide 6.0.2</h1> <p>Обнаружилась проблемка с проектами на TestNG, по-быстрому выпустили хотфикс 6.0.2.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1623">issue 1623</a></p> <p><br /></p> <h1 id="upd-selenide-603">UPD Selenide 6.0.3</h1> <p>Оказалось, что Maven может подтягивать старый <code class="language-plaintext highlighter-rouge">selenium-api-3*.jar</code>, если он найден в дереве зависимостей (обычно у BrowserUpProxy или Allure).</p> <p>Мне давно кажется, что тут Maven мог бы быть чуточку поумнее и выбирать всё-таки более свежую версию.<br /> Но поскольку люди довольно часто наступают на эти грабли, мы решили запилить костыль в селениде.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1625">костыль 1625</a></p> <p><br /></p> <h1 id="что-почитать-про-selenium-4">Что почитать про Selenium 4</h1> <ul> <li><a href="https://applitools.com/blog/selenium-4/">What’s New In Selenium 4?</a> by Applitools</li> <li><a href="https://www.browserstack.com/guide/selenium-4-features">Selenium 4: Understanding Key Features</a> by BrowserStack</li> <li><a href="https://saucelabs.com/selenium-4">A comprehensive guide to Selenium 4</a> by SauceLabs</li> <li><a href="https://www.lambdatest.com/blog/what-is-deprecated-in-selenium4/">What Is New In Selenium 4 And What Is Deprecated In It?</a> by LambdaTest</li> <li><a href="https://www.selenium.dev/documentation/">Обновлённый сайт Selenium</a></li> </ul> <p><br /></p> <h1 id="другие-новости">Другие новости</h1> <ul> <li>внезапный <a href="https://youtu.be/mK-6-k5EwQM">выпуск Heisenbug Show</a>, посвящённый 10-летию Селенида.</li> <li>Первый <a href="https://www.eventbrite.com/e/propeller-testops-hackathon-registration-194333114577">TestOps Hackathon</a> от PropellerAds и Qameta Software - с 12 по 16 ноября. Регистрируйтесь!</li> <li>Ребята из Aerokube выпустили <a href="https://github.com/aerokube/lightning-java">альтернативный клиент WebDriver</a>. Теоретически теперь селенид может работать на нём вместо Selenium Webdriver. Звучит заманчиво?</li> <li><a href="https://www.youtube.com/watch?v=ch5sxsQJzW4">Koncerns: Почему я не тороплюсь на Котлин</a></li> <li>Я пока не разбирался, но люди вроде хвалят: <a href="https://github.com/markhobson/docker-maven-chrome">какие-то образы Docker для Selenium</a></li> </ul> <p><br /></p> <h1 id="статистика-использования-селенида">Статистика использования Селенида</h1> <center> <img src="/images/2021/10/selenide.downloads.png" width="800" /> </center> <p><br /> Перевалили за <strong>255 тысяч скачиваний</strong> в месяц.</p> <p>Что нас ждёт впереди?</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/10/25/selenide-6.0.1/ http://ru.selenide.org/2021/10/25/selenide-6.0.1 2021-10-25T00:00:00+00:00 Вышла Selenide 5.25.0 <p>Добрый вечер!</p> <p>Мы зарелизили <a href="https://github.com/selenide/selenide/milestone/131?closed=1">Selenide 5.25.0</a>.</p> <blockquote> <p><strong>Очень советую обновиться</strong>,<br /> потому что за этим релизом грядут ещё большие изменения, включая полноценный релиз Selenium 4.<br /> Если не обновитесь сейчас, потом будет намного сложнее разгребать завалы!</p> </blockquote> <p>Это прямо большой релиз с кучей изменений, так что запаситесь попкорном и заварите чаю. Погнали!</p> <ul> <li><a href="#support-webdriver-4.0.0.RC1">Поддержка Selenium Webdriver 4.0.0 RC1</a></li> <li><a href="#opentest4j">Добавили поддержку OpenTest4j</a></li> <li><a href="#stacktrace-in-soft-asserts">Показываем стектрейс под каждой ошибкой в SoftAsserts</a></li> <li><a href="#get-shadow-root">Добавили метод <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></a></li> <li><a href="#get-ancestor">Добавили метод <code class="language-plaintext highlighter-rouge">$.ancestor()</code></a></li> <li><a href="#enrich-ancestor">Обогатили методы <code class="language-plaintext highlighter-rouge">$.closest()</code> и <code class="language-plaintext highlighter-rouge">$.ancestor()</code></a></li> <li><a href="#fixed-element-screenshot">Починили метод <code class="language-plaintext highlighter-rouge">$.screenshot()</code> на маках</a></li> <li><a href="#actual-value-at-the-moment-of-last-check">При падении текстовых проверок селенид выдаёт точное значение на момент падения</a></li> <li><a href="#empty-text-not-allowed">Запретили пустой аргумент в методе <code class="language-plaintext highlighter-rouge">$.matchText("")</code></a></li> <li><a href="#check-webdriver-title">Добавили проверку <code class="language-plaintext highlighter-rouge">webdriver().shouldHave(title(...))</code></a></li> <li><a href="#release-selenide-selenoid-1.2.0">selenide-selenoid 1.2.0</a></li> <li><a href="#release-selenide-appium-1.7.0">selenide-appium 1.7.0</a></li> <li><a href="#news">Ссылки</a></li> <li><a href="#statistics">Статистика использования Селенида</a></li> <li><a href="#selenide-anniversary">Юбилей Selenide</a></li> </ul> <p><br /></p> <h1 id="support-webdriver-4.0.0.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="stacktrace-in-soft-asserts">Показываем стектрейс под каждой ошибкой в 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="get-shadow-root">Добавили метод <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="get-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="enrich-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="fixed-element-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="actual-value-at-the-moment-of-last-check">При падении текстовых проверок селенид выдаёт точное значение на момент падения</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="empty-text-not-allowed">Запретили пустой аргумент в методе <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="check-webdriver-title">Добавили проверку <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="release-selenide-selenoid-1.2.0">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="release-selenide-appium-1.7.0">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="news">Ссылки</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="statistics">Статистика использования Селенида</h1> <center> <img src="/images/2021/09/selenide.downloads.png" width="800" /> </center> <p><br /> Знаковый рубеж пройден: перевалили за <strong>250 тысяч скачиваний</strong> в месяц.</p> <p><br /></p> <h2 id="selenide-anniversary">Юбилей Selenide</h2> <p>Кстати, мы вплотную подошли к ещё одному знаковому рубежу: в октябре Селениду исполняется… 10 лет!<br /> В это сложно поверить, но <a href="https://github.com/selenide/selenide/commit/3716078fc7fda8c5da01d871882d513cbd97cd0e">первый коммит</a> в репозитории был сделан аж 25 октября 2011 года. Вы только посмотрите, какой он был нелепый! :)</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/09/28/selenide-5.25.0/ http://ru.selenide.org/2021/09/28/selenide-5.25.0 2021-09-28T00:00:00+00:00 Вышла Selenide 5.24.0 <p>Добрый вечер!</p> <p>29 августа 1997 года компьютерная система Скайнет вышла из-под контроля и нанесла ядерный удар по России. Так началась война между терминаторами и человечеством.</p> <p>С тех прошло 24 года, с ума сойти! Мы живём в будущем! И пока ещё мы управляем компьютерами.</p> <p>Я каждый год отмечаю этот день, и сегодня в честь праздника мы зарелизили <a href="https://github.com/selenide/selenide/milestone/130?closed=1">Selenide 5.24.0</a>.</p> <p><br /></p> <h1 id="добавили-метод-executecommand-duration">Добавили метод <code class="language-plaintext highlighter-rouge">$.execute(Command, Duration)</code></h1> <p>для запуска самопальных команд с заданным таймаутом.</p> <p>В <a href="https://ru.selenide.org/2019/09/02/selenide-5.3.0/">Selenide 5.3.0</a> мы добавили возможность легко запускать самопальные команды с помощью метода <code class="language-plaintext highlighter-rouge">$.execute()</code>. Но тогда не было возможности задать кастомный таймаут. Теперь можно.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1525">issue 1525</a>.<br /> Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1531">PR 1531</a></p> <p><br /></p> <h1 id="методы-executecommand-и-executecommand-duration-больше-не-передают-параметры-заданной-команде">Методы <code class="language-plaintext highlighter-rouge">$.execute(Command)</code> и <code class="language-plaintext highlighter-rouge">$.execute(Command, Duration)</code> больше не передают параметры заданной команде</h1> <p>Это мелочь, но теоретически может сломать ваши самопальные команды, если они у вас есть. Будьте начеку.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1527">issue 1527</a>.<br /> Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1535">PR 1535</a></p> <p><br /></p> <h1 id="исправили-условия-or-и-and-при-работе-с-несуществующими-элементами">Исправили условия <code class="language-plaintext highlighter-rouge">Or</code> и <code class="language-plaintext highlighter-rouge">And</code> при работе с несуществующими элементами</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/1534">issue 1534</a>.<br /> Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1539">PR 1539</a></p> <p><br /></p> <h1 id="теперь-условия-or-и-and-не-позволяют-передать-пустой-список-условий">Теперь условия <code class="language-plaintext highlighter-rouge">Or</code> и <code class="language-plaintext highlighter-rouge">And</code> не позволяют передать пустой список условий</h1> <p>Надеюсь, вы и не пытались, потому что это лишено смысла. Но если раньше Селенид это позволял и тесты могли быть ложно зелёными, то теперь вы словите ошибку в рантайме. <br /> Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1542">PR 1542</a></p> <p><br /></p> <h1 id="переименовали-методы-conditionapplynull-и-collectionconditionapplynull">Переименовали методы <code class="language-plaintext highlighter-rouge">Condition.applyNull()</code> и <code class="language-plaintext highlighter-rouge">CollectionCondition.applyNull()</code></h1> <p>Когда-то по молодости я назвал этот метод <code class="language-plaintext highlighter-rouge">applyNull</code>, но это название вводит в заблуждение. Этот метод говорит о том, выполняется ли условие, если искомый элемент вообще не найден, и теперь он называется <code class="language-plaintext highlighter-rouge">missingElementSatisfiesCondition()</code> - длинновато, зато точно.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1541">issue 1541</a>.<br /> Спасибо <a href="https://github.com/evpl">Evgenii Plugatar</a> за <a href="https://github.com/selenide/selenide/pull/1544">PR 1544</a></p> <p><br /></p> <h1 id="убрали-из-логов-длинные-бесполезные-стектрейсы-при-закрытии-вебдрайвера">Убрали из логов длинные бесполезные стектрейсы при закрытии вебдрайвера</h1> <p>Как вы знаете, Селенид сам закрывает вебдрайвер в тот момент, когда он больше не нужен.<br /> Для этого Селенид запускает всякие хитрые потоки, которые в фоновом режиме следят со состоянием всех открытых вебдрайверов.<br /> И когда наступает момент, закрывают ненужные.</p> <p>Но так бывает, что к моменту закрытия вебдрайвера его уже успел закрыть какой-то другой поток. И тогда Селенид мог плеваться в лог длинными страшными стектрейсами. Это не опасно, но никто ведь не любит спам.</p> <p>В общем, теперь Селенид немного сократил объём спама. :)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1467">issue 1467</a> и <a href="https://github.com/selenide/selenide/pull/1540">PR 1540</a></p> <p><br /></p> <h1 id="исправили-поиск-shadow-root-внутри-веб-элементов">Исправили поиск shadow root внутри веб-элементов</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/1532">issue 1532</a> и <a href="https://github.com/selenide/selenide/pull/1536">PR 1536</a></p> <p><br /></p> <h1 id="selenide-selenoid-115">selenide-selenoid 1.1.5</h1> <p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.5"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.5</code></a> с обновлением на Selenide 5.24.0</p> <p><br /></p> <h1 id="selenide-appium-168">selenide-appium 1.6.8</h1> <p>Мы выпустили <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.6.8"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.8</code></a> с обновлением на Selenide 5.24.0</p> <p><br /> <br /></p> <h1 id="upd-selenide-5241">UPD: Selenide 5.24.1</h1> <p>WebDriverManager - это библиотека (которую Селенид использует под капотом) для скачивания бинарников вебдрайвера. Недавно они выпустили большое обновление 5.0.0 с кучей новых фич. Возможно, мы захотим использовать какие-то из этих фич в Селениде, но пока что мы по-быстрому выпустили <strong>Selenide 5.24.1</strong> с обновлением на WebDriverManager 5.0.1.</p> <p>Спасибо <a href="https://github.com/anilreddy">Anil Kumar Reddy Gaddam</a> за <a href="https://github.com/selenide/selenide/pull/1547">PR 1531</a>.</p> <p><br /></p> <h1 id="upd-selenide-5242">UPD: Selenide 5.24.2</h1> <p>Оказалось, что зависимость <code class="language-plaintext highlighter-rouge">commons-lang3</code> (которую Селенид используем под капотом) больше прилетает транзитивно из WDM 5.x. А раньше прилетала из WDM 4.x. Поэтому нам пришлось по-быстрому добавить явную зависимость <code class="language-plaintext highlighter-rouge">commons-lang3</code> и зарелизить <strong>Selenide 5.24.2</strong>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1551">issue 1551</a>.</p> <p><br /></p> <h1 id="upd-selenide-5243">UPD: Selenide 5.24.3</h1> <p>Обновились на WebDriverManager 5.0.2, теперь смогли исключить <code class="language-plaintext highlighter-rouge">docker-java</code> и некоторые другие лишние зависимости.</p> <p><br /></p> <h1 id="upd-selenide-5244">UPD: Selenide 5.24.4</h1> <p>Обновились на WebDriverManager 5.0.3, в котором вышел срочный фикс для нового Firefox 92.0 на маке. И кое-что ещё по мелочам.</p> <p><br /> <br /></p> <h1 id="ссылки">Ссылки</h1> <ul> <li>Новое шоу <a href="https://www.youtube.com/channel/UCHmuu4tJjx54fOWzoIVqmaA">Айтишники</a> от Артёма и Севы</li> <li>Оказывается, на Селениде можно писать тесты не только для веб и мобилок, но и для Swing приложений! Вот <a href="https://github.com/framebassman/fest-selenide">пример</a>. Под капотом он использует <a href="https://github.com/jalian-systems/marathonv5">реализацию вебдрайвера для swing</a>.</li> <li>Сильно спорный доклад, но есть полезные идеи: <a href="https://www.youtube.com/watch?v=BzM-VAf8C-c">SPA. React. Selenium vs. Selenide</a></li> <li>5-7 октября снова будет Гейзенбаг - <a href="https://heisenbug-moscow.ru/callforpapers/">подавайте заявки!</a></li> </ul> <p><br /></p> <h1 id="статистика-использования-селенида">Статистика использования Селенида</h1> <center> <img src="/images/2021/08/selenide.downloads.png" width="800" /> </center> <p>249+ тысяч скачиваний в месяц. Чуть-чуть не хватило до 250K.</p> <h3 id="аста-ли-виста-бэйби">Аста ли виста, бэйби!</h3> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/08/29/selenide-5.24.0/ http://ru.selenide.org/2021/08/29/selenide-5.24.0 2021-08-29T00:00:00+00:00 Вышла Selenide 5.23.0 <p>Добрый вечер!</p> <p>16 июля вышел релиз <a href="https://github.com/selenide/selenide/milestone/125?closed=1">Selenide 5.23.0</a>.</p> <p>В нём появилось кое-что существенно новое для селенида.</p> <h1 id="проверки-нового-поколения">Проверки нового поколения</h1> <p>Теперь в селениде есть встроенные проверки не только для веб-элементов, но и для некоторых других штук. С автоматическими ожиданиями, понятными сообщениями об ошибках, попаданием в отчёт и т.д. Всё как вы любите.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1442">issue 1442</a>. Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за запуск всей этой эпопеи в <a href="https://github.com/selenide/selenide/pull/1478">PR 1478</a>.</p> <p><br /> Давайте посмотрим, что же это за проверки.</p> <h2 id="проверки-для-url">Проверки для URL</h2> <p>До сих пор в селениде было только два метода, позволяющие получить URL текущей страницы или текущего фрейма (в большинстве случаев это одно и то же).</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">String</span> <span class="n">url1</span> <span class="o">=</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">url</span><span class="o">();</span> <span class="nc">String</span> <span class="n">url2</span> <span class="o">=</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">currentFrameUrl</span><span class="o">();</span> </code></pre></div></div> <p>Как их проверять, было непонятно. И ещё непонятнее, как дождаться, что нужный урл загрузился.</p> <p>Теперь такие проверки есть:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">webdriver</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">WebDriverConditions</span><span class="o">.*;</span> <span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">url</span><span class="o">(</span><span class="s">"https://auth.google.com"</span><span class="o">));</span> <span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">url</span><span class="o">(</span><span class="s">"https://mastercard.ee"</span><span class="o">),</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">42</span><span class="o">));</span> <span class="n">webdriver</span><span class="o">().</span><span class="na">shouldNotHave</span><span class="o">(</span><span class="n">url</span><span class="o">(</span><span class="s">"http://yandex.ru"</span><span class="o">);</span> <span class="n">webdriver</span><span class="o">().</span><span class="na">shouldNotHave</span><span class="o">(</span><span class="n">urlStartingWith</span><span class="o">(</span><span class="s">"ftp://"</span><span class="o">));</span> <span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">currentFrameUrl</span><span class="o">(</span><span class="n">baseUrl</span> <span class="o">+</span> <span class="s">"/login.html"</span><span class="o">));</span> <span class="n">webdriver</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">currentFrameUrlStartingWith</span><span class="o">(</span><span class="n">baseUrl</span> <span class="o">+</span> <span class="s">"/logout.html"</span><span class="o">));</span> </code></pre></div></div> <p>Каждая из этих проверок:</p> <ol> <li>если надо, подождёт (по умолчанию до 4 секунд)</li> <li>добавится в отчёт (текстовой или аллюр)</li> <li>если урл по истечении 4 секунд не такой - сделает скриншот и кинет ошибку <code class="language-plaintext highlighter-rouge">ConditionNotMetException</code></li> </ol> <p>В лучших традициях селенида можно</p> <ul> <li>переопределять дефалтовый таймаут (4 секунды) на любой другой: <code class="language-plaintext highlighter-rouge">Configuration.timeout = 8000;</code></li> <li>задавать кастомный таймаут (вторым параметром типа <code class="language-plaintext highlighter-rouge">Duration.ofSeconds(42)</code>)</li> <li>создавать свои кастомные проверки (см. <a href="https://github.com/selenide/selenide/blob/c045579f243fb3a5abb99033e440cf8f12caa99c/statics/src/test/java/integration/WebDriverConditionsTest.java#L127">пример</a>)</li> </ul> <h2 id="проверки-для-буфера-обмена">Проверки для буфера обмена</h2> <p>Начиная с версии 5.20.0, в селениде есть метод для доступа к буферу обмена:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Clipboard</span> <span class="n">clipboard</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">clipboard</span><span class="o">();</span> </code></pre></div></div> <p>Но если раньше можно было только засунуть и высунуть текст из буфера:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">clipboard</span><span class="o">().</span><span class="na">getText</span><span class="o">();</span> <span class="n">clipboard</span><span class="o">().</span><span class="na">setText</span><span class="o">(</span><span class="s">"bar"</span><span class="o">);</span> </code></pre></div></div> <p>То теперь его можно проверить - с дефалтовым и кастомным таймаутом, скриншотами и т.д.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">clipboard</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">content</span><span class="o">(</span><span class="s">"Hello fast World"</span><span class="o">));</span> <span class="n">clipboard</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">content</span><span class="o">(</span><span class="s">"Hello slow World"</span><span class="o">),</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofMillis</span><span class="o">(</span><span class="mi">1500</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/1507">PR 1507</a></p> <h2 id="проверки-для-localstorage">Проверки для <code class="language-plaintext highlighter-rouge">localStorage</code></h2> <p>Начиная с версии 5.15.0, в селениде есть метод <code class="language-plaintext highlighter-rouge">localStorage()</code>, возвращающий объект <code class="language-plaintext highlighter-rouge">LocalStorage</code>.<br /> Но у него были только методы <code class="language-plaintext highlighter-rouge">getItem</code> и <code class="language-plaintext highlighter-rouge">setItem</code>. Опять же, как их проверять и как дождаться нужного значения, было неясно.</p> <p>Теперь ясно:</p> <ul> <li><code class="language-plaintext highlighter-rouge">localStorage().shouldHave(item("cat”));</code></li> <li><code class="language-plaintext highlighter-rouge">localStorage().shouldHave(itemWithValue("mouse", "Jerry”));</code></li> </ul> <p>Кстати, заодно появился метод <code class="language-plaintext highlighter-rouge">localStorage.getItems()</code>, возвращающий всё содержимое в виде мапы.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1502">PR 1502</a></p> <h2 id="проверки-для-sessionstorage">Проверки для <code class="language-plaintext highlighter-rouge">sessionStorage</code></h2> <p>Всё то же самое, что и про <code class="language-plaintext highlighter-rouge">localStorage</code> - добавились методы</p> <ul> <li><code class="language-plaintext highlighter-rouge">sessionStorage().shouldHave(item("cat”));</code></li> <li><code class="language-plaintext highlighter-rouge">sessionStorage().shouldHave(itemWithValue("mouse", "Jerry”));</code></li> <li><code class="language-plaintext highlighter-rouge">Map&lt;String, String&gt; items = sessionStorage.getItems();</code></li> </ul> <p>См. <a href="https://github.com/selenide/selenide/pull/1502">PR 1502</a></p> <p><br /></p> <h1 id="небольшой-рефакторинг">Небольшой рефакторинг</h1> <p>Мы сделали классы <code class="language-plaintext highlighter-rouge">StaticConfig</code> и <code class="language-plaintext highlighter-rouge">StaticDriver</code> непубличными.<br /> Нам кажется, они никому не должны быть нужны вне селенида. А в ваших проектах они используются? Пишите нам, если у вас развалилась компиляция. :)</p> <h1 id="и-небольшой-багфикс">И небольшой багфикс:</h1> <p>Теперь метод <code class="language-plaintext highlighter-rouge">Selenide.screenshot("filename")</code> снова делает скриншот, даже если настройка <code class="language-plaintext highlighter-rouge">Configuration.screenshots</code> выставлена в <code class="language-plaintext highlighter-rouge">false</code>.</p> <p>Давайте проясним.</p> <ol> <li>Настройку <code class="language-plaintext highlighter-rouge">Configuration.screenshots</code> стоит прописать в <code class="language-plaintext highlighter-rouge">false</code>, если вам не хочется, чтобы селенид автоматически создавал скриншоты при падении тестов. Често говоря, не понимаю, в каких ситуациях это может быть полезно.</li> <li>А вот метод <code class="language-plaintext highlighter-rouge">Selenide.screenshot("filename")</code> можно вызвать в любой момент теста, чтобы явно сделать скриншот, независимо от того, упал тест или не упал. Тоже не очень понимаю, в каких ситуациях это может быть полезно.</li> </ol> <p>Но суть в том, что второй метод работает независимо от первой настройки. Теперь можно выключить автоматические скриншоты, но дёргать их руками только там, где надо.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1477">issue 1477</a> и <a href="https://github.com/selenide/selenide/pull/1506">PR 1506</a></p> <p><br /></p> <h1 id="selenide-selenoid-114">selenide-selenoid 1.1.4</h1> <p>Мы выпустили <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.4"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.4</code></a> с обновлением на Selenide 5.23.0</p> <p><br /></p> <h1 id="selenide-appium-167">selenide-appium 1.6.7</h1> <p>Мы выпустили <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.6.7"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.7</code></a> с обновлением на Selenide 5.23.0</p> <p><br /></p> <h1 id="ссылки">Ссылки</h1> <ul> <li>Статья <a href="https://www.appliedtech.ru/vyibor-instrumentov-dlya-ui-testirovaniya-selenium-ili-selenide.html">Selenium или Selenide?</a> в корпоративном блоге “Прикладные Технологии”</li> <li>Статья <a href="https://anilkulkarni.com/2020/03/selenide-ui-tests-in-minutes/">Selenide – UI tests in minutes</a> от Anil Kulkarni</li> </ul> <p><br /></p> <h1 id="статистика">Статистика</h1> <center> <img src="/images/2021/07/selenide.downloads.png" width="800" /> </center> <p>232+ тысяч скачиваний в месяц.</p> <h3 id="беззаботного-лета">Беззаботного лета!</h3> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/07/16/selenide-5.23.0/ http://ru.selenide.org/2021/07/16/selenide-5.23.0 2021-07-16T00:00:00+00:00 Вышла Selenide 5.22.3 <p>Добрый вечер!</p> <p>На этом месте был пресс-релиз, который мог больно задеть чувства большого числа наших пользователей и коммитеров. Мы сожалеем, что так получилось, и считаем эту публикацию своей ошибкой, ставшей проявлением непрофессионализма отдельных мейнтейнеров. Никоим образом мы не хотели стать источником раздора и ненависти.</p> <p>Просто обновляйтесь на <a href="https://github.com/selenide/selenide/milestone/126?closed=1">Selenide 5.22.3</a> с самыми свежими и вкусными багфиксами.</p> <h3 id="5223-released-05072021">5.22.3 (released 05.07.2021)</h3> <ul> <li><a href="https://github.com/selenide/selenide/issues/1474">#1474</a> add workaround for NPE in RemoteWebElement.isDisplayed() – see <a href="https://github.com/selenide/selenide/pull/1498">PR #1498</a></li> </ul> <h3 id="5222-released-30062021">5.22.2 (released 30.06.2021)</h3> <ul> <li><a href="https://github.com/selenide/selenide/issues/1493">#1493</a> support uploading files from inside of JAR files – see <a href="https://github.com/selenide/selenide/pull/1494">PR #1494</a></li> <li>fix command <code class="language-plaintext highlighter-rouge">./gradlew</code> - now it installs jars to a local maven repo – see <a href="https://github.com/selenide/selenide/pull/1489">PR #1489</a></li> <li>add support for okhttp 4.9.1 – see <a href="https://github.com/selenide/selenide/pull/1488">PR #1488</a></li> </ul> <h3 id="5221-released-18062021">5.22.1 (released 18.06.2021)</h3> <ul> <li>Add mime type “binary/octet-stream” to download binary files in FireFox</li> </ul> <p><br /></p> <center> <a href="https://www.spletnik.ru/buzz/chronicle/102227-skandal-vokrug-vkusvill-s-lesbiyskoy-paroy-khronologiya-reaktciya.html"> <img src="/images/2021/07/selenide-5.22.3.png" width="800" /> </a> </center> <h3 id="беззаботного-лета">Беззаботного лета!</h3> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/07/05/selenide-5.22.3/ http://ru.selenide.org/2021/07/05/selenide-5.22.3 2021-07-05T00:00:00+00:00 Вышла Selenide 5.22.0 <p>Добрый вечер!</p> <p>Лучший подарок - сделанный своими коммитами. <br /> В мой день 40 рождения мы выпустили юбилейный релиз <a href="https://github.com/selenide/selenide/milestone/124?closed=1">Selenide 5.22.0</a>.</p> <p>Развернём упаковку?</p> <p><br /></p> <h1 id="теперь-можно-закрыть-алерт-перед-скачиванием-файла">Теперь можно закрыть алерт перед скачиванием файла</h1> <p>В Селениде есть метод <code class="language-plaintext highlighter-rouge">$.download()</code>, который работает по простому принципу:</p> <ol> <li>Кликни.</li> <li>Подожди, пока в папке появится нужный файл.</li> </ol> <p>Проблема в том, что на некоторых сайтах после клика появляется алерт, который нужно закрыть, чтобы началось скачивание. Ну или вообще, нужно совершить ещё какое-то действие после или вместо клика, чтобы запустить скачивание файла.</p> <p>Теперь можно будет <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadToFolderTest.java">закрыть алерт</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">downloadedFile</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Download me with alert"</span><span class="o">)).</span><span class="na">download</span><span class="o">(</span> <span class="n">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">).</span><span class="na">withAction</span><span class="o">(</span> <span class="n">clickAndConfirm</span><span class="o">(</span><span class="s">"Are you sure to download it?"</span><span class="o">)</span> <span class="o">)</span> <span class="o">);</span> </code></pre></div></div> <p>или вообще совершить <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadViaProxyTest.java">любое нужное действие</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">downloadedFile</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Download me with alert"</span><span class="o">)).</span><span class="na">download</span><span class="o">(</span> <span class="n">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">).</span><span class="na">withAction</span><span class="o">((</span><span class="n">driver</span><span class="o">,</span> <span class="n">link</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">// add cookies</span> <span class="n">link</span><span class="o">.</span><span class="na">click</span><span class="o">();</span> <span class="c1">// driver.switchTo().window();</span> <span class="c1">// alert.dismiss();</span> <span class="c1">// send http request</span> <span class="c1">// call api</span> <span class="o">}));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1479">issue 1479</a> и <a href="https://github.com/selenide/selenide/pull/1481">PR 1481</a>.</p> <p><br /> <br /></p> <h1 id="доработали-проверку-conditiontextcasesensitive">Доработали проверку <code class="language-plaintext highlighter-rouge">Condition.textCaseSensitive</code></h1> <p>… чтобы она поддерживала выбранные опции в <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> - по аналогии с <code class="language-plaintext highlighter-rouge">Condition.text</code>.</p> <p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1482">PR 1482</a>.</p> <p><br /> <br /></p> <h1 id="добавили-селекторы-bytextcaseinsensitive-и-withtextcaseinsensitive">Добавили селекторы <code class="language-plaintext highlighter-rouge">byTextCaseInsensitive</code> и <code class="language-plaintext highlighter-rouge">withTextCaseInsensitive</code></h1> <p>… для поиска элемента по тексту, игнорируя регистр.</p> <h3 id="было">Было</h3> <p>В селениде давно есть методы для поиска по тексту.</p> <ul> <li>по полному тексту: <code class="language-plaintext highlighter-rouge">$(byText("Wake up we have a tsar again")</code></li> <li>по подстроке: <code class="language-plaintext highlighter-rouge">$(withText("we have a tsar")</code></li> </ul> <p>Эти селекторы чувствительны к регистру (и внутри используют XPath 1.0).</p> <h3 id="стало">Стало</h3> <p>Теперь же мы добавили аналогичные методы, но нечувствительные к регистру:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$(byTextCaseInsensitive("wake UP we have a TSAR again")</code></li> <li><code class="language-plaintext highlighter-rouge">$(withTextCaseInsensitive("TSAR agAiN")</code></li> </ul> <p>Эти селекторы позволяют искать элементы, игнорируя большие-маленькие буквы.</p> <h3 id="технический-нюанс">Технический нюанс</h3> <p>этот функционал не получилось реализовать с помощью XPath, потому что строковые функции типа <code class="language-plaintext highlighter-rouge">lower-case</code> и <code class="language-plaintext highlighter-rouge">match</code> появились лишь с версии XPath 2.0, а все современные браузеры поддерживают только XPath 1.0. Поэтому пришлось наколбасить <a href="https://github.com/selenide/selenide/blob/master/src/main/resources/find-elements-by-text-case-insensitive.js">хитрый JS код</a> для обхода всего дерева элементов.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1380">issue 1380</a> и <a href="https://github.com/selenide/selenide/pull/1381">PR 1381</a>.</p> <p><br /> <br /></p> <h1 id="добавили-метод-drivergetsessionid">Добавили метод <code class="language-plaintext highlighter-rouge">Driver.getSessionId()</code></h1> <p>По сути он нужен для интеграции с Selenoid.</p> <p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1483">PR 1483</a>.</p> <p><br /></p> <h1 id="уменьшили-селенимовские-таймауты">Уменьшили селенимовские таймауты</h1> <p>Внутри селениума прошиты два таймаута, о которых вы, возможно, не догадывались. Проблема в том, что они анормально большие, и их невозможно поменять.</p> <ul> <li>Соединение с вебдрайвером: <code class="language-plaintext highlighter-rouge">connectTimeout</code> = 120000 ms = 2 минуты</li> <li>Запрос к вебдрайверу: <code class="language-plaintext highlighter-rouge">readTimeout</code> = 10800000 ms = 3 часа (!)</li> </ul> <p>Если какая-то операция с вебдрайвером подвисает, то ваш тест будет висеть 3 часа. Ну это же ненормально!<br /> Например, так происходит, когда тестируется приложение на Electron, и оно уходит в трей, а тест пытается сделать скриншот - и надолго зависает.</p> <p>В Selenide 5.22.0 мы добавили возможность <em>менять этот таймаут</em> (под капотом используется жёсткий рефлекшин).<br /> И да, мы сделали дефалтовые значения чуть поменьше (хоть и по-прежнему слишком консервативные):</p> <ul> <li><code class="language-plaintext highlighter-rouge">connectTimeout</code> = 1 минута</li> <li><code class="language-plaintext highlighter-rouge">readTimeout</code> = 2 минуты</li> </ul> <p>Давайте восприниматься это как костыль и надеяться, что в Selenium 4 появится более правильное решение.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1433">PR 1433</a>.</p> <p><br /></p> <h1 id="selenide-selenoid-113">selenide-selenoid 1.1.3</h1> <p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-selenoid/milestone/4?closed=1"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.3</code></a>, в котором исправили <code class="language-plaintext highlighter-rouge">ClassCastException</code> в некоторых случаях.</p> <p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide-selenoid/pull/10">PR 10</a>.</p> <p><br /></p> <h1 id="selenide-appium-166">selenide-appium 1.6.6</h1> <p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-appium/releases/tag/v1.6.6"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.6</code></a>, в котором обновились на Selenide 5.22.0</p> <p><br /></p> <h1 id="ссылки">Ссылки</h1> <p>Ого-го! Сразу несколько известных ребят выложили свои видосики про Селенид.</p> <ul> <li>Java champion Sebastian Daschner: <a href="https://www.youtube.com/watch?v=O0-1RhspjAk">Why I switched to using Selenide for UI tests</a></li> <li>JetBrains feat. DJ Юрий Артамонов: <a href="https://www.youtube.com/watch?v=P-vureOnDWY&amp;t=2758s">Modern UI Test Automation with Selenium Libraries</a></li> <li>Статья от JetBrains <a href="https://blog.jetbrains.com/idea/2021/06/live-stream-modern-ui-test-automation-with-selenium-libraries/">про этот стрим</a></li> </ul> <p>И ещё всплыла парочка старых материалов:</p> <ul> <li>Сергей Брит <a href="https://www.youtube.com/watch?v=5WxlKf_EFII">Selenide. Tips and tricks</a>, <a href="https://drive.google.com/file/d/14AUphaV_diRFSE-uUlT9Z_iAtd2uERSu/view">slides</a></li> <li><a href="https://www.slideshare.net/Provectus/selenide-review-and-how-to-start-using-it-in-legacy-selenium-tests">Selenide review</a> by Provectus</li> </ul> <p><br /> И на десерт - молодое поколение учит Селенид:</p> <center> <img src="/images/2021/06/selenide-taffel.png" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/06/08/selenide-5.22.0/ http://ru.selenide.org/2021/06/08/selenide-5.22.0 2021-06-08T00:00:00+00:00 Вышла Selenide 5.21.0 <p>Добрый вечер!</p> <p>За окошком месяц май, а на экране релиз <a href="https://github.com/selenide/selenide/milestone/123?closed=1">Selenide 5.21.0</a>.</p> <p><br /></p> <h1 id="убрали-повторные-скриншоты-и-шаги-в-отчёте-для-цепочи-локаторов">Убрали повторные скриншоты и шаги в отчёте для цепочи локаторов</h1> <p>Большинство методов Селенида устроены так, что их можно вызвать несколько штук подряд, в одну строку. Это позволяет писать лаконичные тесты.</p> <p>Что-то вроде</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"table#id"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"tbody"</span><span class="o">,</span> <span class="mi">2</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"tr.active"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"td"</span><span class="o">,</span> <span class="mi">5</span><span class="o">)</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Ну вот"</span><span class="o">))</span> <span class="o">.</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>Правда, с ними была одна проблема. При падении такой проверки Селенид делал несколько скриншотов, а в отчёт (в т.ч. числе в аллюровский отчёт) добавлялось несколько шагов (один для <code class="language-plaintext highlighter-rouge">"table#id"</code>, второй для <code class="language-plaintext highlighter-rouge">"tbody"</code> и т.д.), хотя по сути это один шаг.</p> <p>Хоть это и некритично, мы эту неприятность убрали. Теперь селенид будет делать один скриншот, а в аллюр отчёте будет одна строка.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1055">issue 1055</a> и <a href="https://github.com/selenide/selenide/pull/1465">PR 1465</a>.</p> <p>NB! Для этого пришлось сделать приличный рефакторинг, и не исключено даже, что что-то могло сломаться. Сообщайте, будем реагировать!</p> <p><br /></p> <h1 id="добавили-browserperteststrategyextension">Добавили BrowserPerTestStrategyExtension</h1> <p>… для перезапуска браузер после каждого теста.</p> <p>По умолчанию Селенид переиспользует браузер между тестами (в рамках одного потока). Это сделано для скорости. Мы предполагаем, что вы сами в начале теста позаботитьесь о том, чтобы обновить страницу, почистить данные и т.п. - ведь это зависит от каждого приложения.</p> <p>Если же вы очень хотите в каждом тесте использовать новый браузер, есть и такая возможность. В Селениде из коробки есть поддерджка для JUnit 4, JUnit 5 и TestNG.</p> <p>Но для JUnit5 у нас был только экстеншн для перезапуска браузера для каждого тестового класса (<code class="language-plaintext highlighter-rouge">@ExtendWith({BrowserStrategyExtension.class}</code>), но не тестового метода.</p> <p>А теперь появился экстенш, заполнивший эту нишу. Если прописать в ваших тестах <code class="language-plaintext highlighter-rouge">@ExtendWith({BrowserPerTestStrategyExtension.class}</code>, то в каждом тестовом методе будет использоваться новый браузер.</p> <p>Это сделает ваши тесты значительно медленнее, но иногда по-другому никак.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1448">issue 1448</a> Спасибо <a href="https://github.com/simple-elf">Anton Aftakhov</a> за <a href="https://github.com/selenide/selenide/pull/1450">PR 1450</a>.</p> <p><br /></p> <h1 id="добавили-метод-hover-со-сдвигом">Добавили метод $.hover() со сдвигом</h1> <p>В селениде давно есть метод <code class="language-plaintext highlighter-rouge">$("div#123").hover()</code> для наведения курсора мыши на нужный элемент. Этот метод двигает курсор к центру элемента, и на это не было возможности повлиять.</p> <p>Теперь же у нас появился метод <code class="language-plaintext highlighter-rouge">$.hover()</code> с параметром, позволяющим указать, на сколько пикселей от центра нужно сдвинуть курсор:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"div#123"</span><span class="o">).</span><span class="na">hover</span><span class="o">(</span><span class="n">withOffset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">122</span><span class="o">));</span> </code></pre></div></div> <p>P.S. Кажется, этот сдвиг не всегда оказывается точным. У меня иногда курсор оказывался <em>примерно</em> в этой позиции плюс-минус 30 пикселей. Делитесь, как у вас?</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1447">issue 1447</a> и <a href="https://github.com/selenide/selenide/pull/1461">PR 1461</a>.</p> <p><br /></p> <h1 id="обновились-на-webdrivermanager-443">Обновились на WebDriverManager 4.4.3</h1> <p>Спасибо <a href="https://github.com/anilreddy">Anil Kumar Reddy Gaddam</a> за <a href="https://github.com/selenide/selenide/pull/1464">PR 1464</a> и <a href="https://github.com/selenide/selenide/pull/1469">PR 1469</a>.</p> <p><br /></p> <h1 id="обновились-javadoc-многих-селенидовских-методов">Обновились javadoc многих селенидовских методов</h1> <p>… касающийся ленивой загрузки и фразы “not recommended”.</p> <p>Теперь javadoc ведёт на эти две статьи в вики:</p> <ul> <li><a href="https://github.com/selenide/selenide/wiki/Lazy-loading">Lazy loading</a></li> <li><a href="https://github.com/selenide/selenide/wiki/Do-not-use-getters-in-tests">Not recommended</a></li> </ul> <p>Внимание, неоднозначный контент.<br /> Возможно, вы захотите подискутировать. <br /> Так давайте подискутируем!</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1430">PR 1430</a></p> <p><br /></p> <h1 id="selenide-selenoid-112">selenide-selenoid 1.1.2</h1> <p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-selenoid/blob/main/CHANGELOG.md"><code class="language-plaintext highlighter-rouge">selenide-selenoid:1.1.2</code></a>, в котором добавили поддержку BasicAuth при скачивании файлов из контейнера Selenoid. <br /> См. <a href="https://github.com/selenide/selenide-selenoid/issues/8">issue 8</a> и <a href="https://github.com/selenide/selenide-selenoid/pull/9">PR 9</a>.</p> <p><br /></p> <h1 id="selenide-appium-165">selenide-appium 1.6.5</h1> <p>Мы выпустили обновление <a href="https://github.com/selenide/selenide-appium/blob/master/CHANGELOG"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.5</code></a>, в котором улучшили сообщения об ошибках пр падении тестов в iOS.<br /> См. <a href="https://github.com/selenide/selenide-appium/issues/54">issue 54</a>.</p> <p><br /></p> <h1 id="ссылки">Ссылки</h1> <p>Что новенького мы нарыли на просторах сети:</p> <ul> <li>Открытые видеокурсы <a href="https://www.youtube.com/channel/UCVE8_r0VkxUYjGd094sKc4g">Дневник тестировщика</a> - там и про селенид есть несколько выпусков.</li> <li><a href="https://habr.com/ru/company/croc/blog/546430/">Как e2e автотесты на Selenide помогают QA-команде при частых релизах</a></li> <li>годный тред <a href="https://twitter.com/arthicl/status/1366598485252964360?s=20">как слезть с ЛКФ на Селенид</a> (ЛКФ = локальный кривой фреймворк)</li> <li>Пост <a href="https://rieckpil.de/write-concise-web-tests-with-selenide-for-java-projects/">Write Concise Web Tests With Selenide for Java Projects</a> за авторством <a href="https://github.com/rieckpil">Philip Riecks</a>.</li> <li>Видео <a href="https://www.youtube.com/watch?v=T9xns1iMbPI">Введение в Selenide</a> от него же</li> <li>Маленький видеоурок <a href="https://www.youtube.com/watch?v=XPUPirH1yMs">Create Screenshots With Selenide</a> от него же</li> <li>Пример <a href="https://github.com/senpay/layered-test-framework-example-serenity-jbehave">Selenide+Serenity+JBehave</a></li> <li>Пример <a href="https://github.com/sergiomartins8/test-automation-bootstrap/tree/master/ui-tests">Selenide+TestNG+ExtentReports</a></li> <li>курс <a href="https://slifki.info/threads/sergei-semenov-selenium-i-selenide-dlja-nachinajuschix-automation-qa-qc-na-java-2020.85098/">Selenium и Selenide для начинающих</a> (прикольно же!)</li> </ul> <p><br /></p> <h2 id="немного-статистики">Немного статистики</h2> <p>Статистика скачиваний Selenide за апрель 2021:</p> <center> <img src="/images/2021/05/selenide.downloads.png" width="800" /> </center> <p>Чуть-чуть не дотянули до 200 тысяч.</p> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/05/15/selenide-5.21.0/ http://ru.selenide.org/2021/05/15/selenide-5.21.0 2021-05-15T00:00:00+00:00 Вышла Selenide 5.20.1 <p>Добрый вечер!</p> <p>Джо Байдена спросили, считает ли он <code class="language-plaintext highlighter-rouge">$$.as</code> киллер-фичей. «Ммм, хмм, да», — ответил Байден.</p> <p>Это сдвоенный обзор на релиз <a href="https://github.com/selenide/selenide/milestone/118?closed=1">Selenide 5.20.0</a> и <a href="https://github.com/selenide/selenide/milestone/119?closed=1">Selenide 5.20.1</a>.</p> <p><br /></p> <h1 id="добавили-методы-для-операций-с-буфером-обмена">Добавили методы для операций с буфером обмена</h1> <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.clipboard().setText("111");</code></li> <li><code class="language-plaintext highlighter-rouge">assertEquals("Hello World", Selenide.clipboard().getText());</code></li> </ul> <p>Имейте в виду, что на линуксе буфер обмена не будет работать без иксов. Либо запускайте xvfb, либо ещё как-то выкручивайтесь.</p> <p>Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1409">PR 1409</a>.</p> <p>NB! Эти методы переопределены в плагине <a href="https://github.com/selenide/selenide-selenoid">selenide-selenoid</a>, так что они корректно работают с селеноидом. Для него мы тоже выпустили версию <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.0">1.1.0</a>.</p> <p><br /></p> <h1 id="добавили-режим-headless-для-браузера-microsoft-edge">Добавили режим headless для браузера Microsoft Edge</h1> <p>Раньше настройка <code class="language-plaintext highlighter-rouge">Configuration.headless</code> срабатывала только для Chrome и Firefox, а теперь ещё и для Edge.</p> <p>(Браузеры IE, Opera и Safari, насколько я знаю, до сих пор не поддерживают headless режим.)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1422">issue 1422</a> и <a href="https://github.com/selenide/selenide/pull/1424">PR 1424</a>.</p> <p><br /></p> <h1 id="добавили-метод-as-для-задания-алиаса-коллекциям">Добавили метод $$.as() для задания алиаса коллекциям</h1> <p>Как вы помните, в Selenide 5.17.0 мы добавили возможность задавать единичным элементам “понятное” имя. Теперь имя можно задавать и коллекциям:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="err">$$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login buttons"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">2</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1389">issue 1389</a> и <a href="https://github.com/selenide/selenide/pull/1431">PR 1431</a>.</p> <p>NB! Не торопитесь использовать эту возможность. Лично я воспринимаю её как “хак последней надежды”.<br /> В отчёте всегда лучше видеть настоящий локатор, чем кем-то придуманное имя, которое всегда может оказаться:</p> <ul> <li>Обманчивым</li> <li>Устаревшим</li> <li>Вводящим в заблуждение</li> </ul> <p>Лучше инвестируйте свои усилия в то, чтобы использовать читаемые локаторы. Ну и грамотно называть переменные и методы. Вот где сила, брат!</p> <p><br /></p> <h1 id="добавили-проверку-для-коллекций-containexacttextscasesensitive">Добавили проверку для коллекций <code class="language-plaintext highlighter-rouge">containExactTextsCaseSensitive</code></h1> <p>Существующий метод <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("a", "b", "c"))</code> проверяет, что в коллекции ровно эти элементы и никаких больше. А иногда хочется менее строгой проверки. Например, нужно проверить, что в списке торгуемых валют точно есть RUB, EUR, USD - и какие угодно ещё.</p> <p>Теперь для этого есть метод:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$$</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">containTexts</span><span class="o">(</span><span class="s">"RUB"</span><span class="o">,</span> <span class="s">"EUR"</span><span class="o">,</span> <span class="s">"USD"</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1426">PR 1426</a>.</p> <p><strong>UPD</strong> В оперативно вышедшей Selenide 5.20.1 его переименовали в</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$$</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">containExactTextsCaseSensitive</span><span class="o">(</span><span class="s">"RUB"</span><span class="o">,</span> <span class="s">"EUR"</span><span class="o">,</span> <span class="s">"USD"</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> за <a href="https://github.com/selenide/selenide/pull/1438">PR 1438</a> и <a href="https://github.com/selenide/selenide/pull/1439">PR 1439</a>.</p> <p><br /></p> <h1 id="починили-утерянные-опции-firefoxoptions">Починили утерянные опции FirefoxOptions</h1> <p>В некоторых ситуации некоторые настройки Firefox терялись, теперь не теряются. Опции нашлись.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1436">issue 1436</a>. Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1437">PR 1437</a>.</p> <p><br /></p> <h1 id="убрали-логирование-поисковых-методов">Убрали логирование “поисковых” методов</h1> <p>В SelenideElement есть несколько методов для поиска других элементов:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$.findAll()</code></li> <li><code class="language-plaintext highlighter-rouge">$.parent()</code></li> <li><code class="language-plaintext highlighter-rouge">$.sibling()</code></li> <li><code class="language-plaintext highlighter-rouge">$.preceding()</code></li> <li><code class="language-plaintext highlighter-rouge">$.lastChild()</code></li> <li><code class="language-plaintext highlighter-rouge">$.closest()</code></li> </ul> <p>Такие методы оказывались в отчёте дважды: сначала при их вызове, а потом - при вызове любого метода на найденном элементе. Теперь мы это дублирование убрали.</p> <p>Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1428">PR 1428</a>.</p> <p><br /></p> <p><br /></p> <h3 id="доска-объявлений">Доска объявлений</h3> <p>Ура, новый сезон конференций открыт! Буквально через неделю-две начинается:</p> <ul> <li>У кого нет денег на Heisenbug и JPoint - приходите 27 марта на бесплатный <a href="https://techtrain.ru/2021/spring/schedule/">фестиваль TechTrain</a>, программа там очень многообещающая. <ul> <li>Я, например, планирую послушать “<a href="https://techtrain.ru/2021/spring/talks/4vehn9kwvjg7uzjjbp0nl4/">Что мобильным разработчикам в IT-индустрии неведомо</a>”</li> <li>и “<a href="https://techtrain.ru/2021/spring/talks/1vtaygqjny9w3folebliqv/">Ловушки коллективного владения кодом</a>”.</li> <li>И конечно, “<a href="https://techtrain.ru/2021/spring/talks/74akccvzvqirxg2jbh4t3m/">Какие тулы ты бы взял на удаленку</a>” от ведущих “Ошибки выжившего” Севы и Артёма обещает стать хитом.</li> </ul> </li> <li> <p>А 6-9 апреля грядёт наш любимый <a href="https://heisenbug-piter.ru/2021/spb/schedule/">Heisenbug</a>.</p> <p>У меня там будет заключительный доклад эпопеи про флейки тесты “<a href="https://heisenbug-piter.ru/2021/spb/talks/4wcc3dephlxuzc87wgwkk7/">Flaky tests. Метод</a>” и продолжение мастер-класса “<a href="https://heisenbug-piter.ru/2021/spb/talks/8p4qtsit5rqgga8yuxz5a/">Как начать свой проект автоматизации с нуля</a>”.</p> <p>А послушать я хочу как минимум мастер-класс гуру юзабилити Виталия Фридмана “<a href="https://heisenbug-piter.ru/2021/spb/talks/5lxiyz5rdanigu1wuaujtt/">От birthday selector до inline validation: Всё, что нужно знать о веб-формах</a>” и много чего ещё вкусненького.</p> </li> <li> <p>А 13-17 апреля пройдёт конференция <a href="https://jpoint.ru/2021/schedule/">JPoint</a></p> <p>Она скорее для разработчиков, и на ней я тоже успею засветиться в <a href="https://jpoint.ru/2021/talks/5gmcsxserjfhlzgt9dpfq3/">мастер-классе по парному программированию</a>.</p> </li> <li> <p>А ещё 29 мая будет новосибирский <a href="https://11.codefest.ru/">Codefest</a></p> <p>Там у меня будет доклад про “<a href="https://11.codefest.ru/lecture/1751">Трюки с javascript в автотестах</a>”.</p> </li> </ul> <p>Приходите, всё равно ж делать нечего на карантине!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/03/23/selenide-5.20.1/ http://ru.selenide.org/2021/03/23/selenide-5.20.1 2021-03-23T00:00:00+00:00 Вышла Selenide 5.19.0 <p>Всем привет!</p> <p>Вы, наверное, не знали, но сегодня, 24 февраля, в Эстонии чуть ли не главный праздник - <a href="https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BD%D1%8C_%D0%BD%D0%B5%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8_%D0%AD%D1%81%D1%82%D0%BE%D0%BD%D0%B8%D0%B8">День Независимости</a>. Ровно 103 года назад Эстония была провозглашена независимым демократическим государством.</p> <p>А спустя 93 года в Эстонии, в казематах компании <a href="https://codeborne.com/">Codeborne</a> родилась библиотека Selenide. Это ли не чудо?</p> <p>Дню независимости мы и посвящаем наш новый релиз <a href="https://github.com/selenide/selenide/milestone/116?closed=1">Selenide 5.19.0</a>.</p> <p><br /></p> <h1 id="наконец-таки-починили-draganddrop">Наконец-таки починили drag’and’drop</h1> <p>В селениде уже давно есть метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code>, но он фактически не работает. Под капотом он использует селениумовский механизм <code class="language-plaintext highlighter-rouge">Actions</code>, и что-то там явно сломано. Ну не тащит элемент и всё.</p> <p>А теперь мы запилили альтернативную реализацию с помощью JavaScript. И она, похоже, работает во всех браузерах. Поэтому мы даже включили её по умолчанию.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// Рабочий вариант:</span> <span class="err">$</span><span class="o">(</span><span class="s">"#drag1"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#drag1"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">,</span> <span class="n">usingJavaScript</span><span class="o">());</span> <span class="c1">// Нерабочий вариант через Actions (ну вдруг он у вас почему-то работает):</span> <span class="err">$</span><span class="o">(</span><span class="s">"#drag1"</span><span class="o">).</span><span class="na">dragAndDropTo</span><span class="o">(</span><span class="s">"#div2"</span><span class="o">,</span> <span class="n">usingActions</span><span class="o">());</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1110">issue 1110</a>.</p> <p>Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1412">PR 1412</a>.</p> <p><br /></p> <h1 id="поддержка-appium">Поддержка Appium</h1> <p>Заодно мы выпустили и обновление библиотеки <code class="language-plaintext highlighter-rouge">selenide-appium:1.6.2</code>, в которой метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code>, теперь переопределён, так что он работает и на мобильниках. См. <a href="https://github.com/selenide/selenide-appium/pull/53/files">PR #53</a>.</p> <p><br /></p> <h1 id="починили-метод-clickusingjavascript-в-internet-explorer">Починили метод <code class="language-plaintext highlighter-rouge">$.click(usingJavascript())</code> в Internet Explorer</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/1406">issue 1406</a> и <a href="https://github.com/selenide/selenide/pull/1419">PR 1419</a>.</p> <p><br /></p> <h1 id="улучшили-описание-коллекции-snapshot">Улучшили описание коллекции <code class="language-plaintext highlighter-rouge">$$.snapshot()</code></h1> <p>У селенидовских коллекций есть один хитрый метод <code class="language-plaintext highlighter-rouge">$$.snapshot()</code>. Он запоминает текущее состоние коллекции, и при дальнейших обращениях не загружает её заново из браузера. Это полезно для ускорения тестов, когда коллекция большая, и вы уверены, что её элементы уже точно не изменятся.</p> <p>С ними была только маленькая проблемка: в отчётах выглядело не слишком красиво. Например, такая строка:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">"#root li"</span><span class="o">).</span><span class="na">snapshot</span><span class="o">().</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">))</span> </code></pre></div></div> <p>при падении в отчёте выглядела как</p> <blockquote> <p>List size mismatch: expected: = 3, actual: 2, collection: $$(2 elements)</p> </blockquote> <p>Как видите, в описании нет селектора оригинальной коллекции, только <code class="language-plaintext highlighter-rouge">(2 elements)</code>.</p> <p>Теперь виден и селектор:</p> <blockquote> <p>List size mismatch: expected: = 3, actual: 2, collection: #root li.snapshot(2 elements)</p> </blockquote> <p>Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1402">PR 1402</a>.</p> <p><br /></p> <h1 id="добавили-метод-getalias">Добавили метод <code class="language-plaintext highlighter-rouge">$.getAlias()</code></h1> <p>Он возвращает то, что вы сами же задали с помощью <code class="language-plaintext highlighter-rouge">$.as("login button")</code>. В обычных тестах этот метод не должен быть нужен. Но может понадобиться, если вы пилите какой-то свой хитрый отчёт. Ну мало ли, убийцу Аллюра…</p> <p>Спасибо <a href="https://github.com/pavelpp">pavelpp</a> за <a href="https://github.com/selenide/selenide/pull/1415">PR 1415</a>.</p> <p><br /></p> <h1 id="добавили-события-refresh-и-тп-в-селенидовский-лог">Добавили события “refresh” и т.п. в селенидовский лог</h1> <p>В селениде есть целый ряд методов, которые не связаны явно в веб-элементами (<code class="language-plaintext highlighter-rouge">refresh()</code>, <code class="language-plaintext highlighter-rouge">back()</code> и т.п.) И мы в какой-то момент обнаружили, что некоторые из них не отображались в отчёта Selenide или Allure. Несмертельно, конечно, но всё-таки зачем-то ведь эти отчёты кому-то нужны…</p> <p>Теперь мы исправили эту оплошность, и следующие методы будут исправно красоваться в отчётах:</p> <ul> <li><code class="language-plaintext highlighter-rouge">refresh</code></li> <li><code class="language-plaintext highlighter-rouge">back</code></li> <li><code class="language-plaintext highlighter-rouge">forward</code></li> <li><code class="language-plaintext highlighter-rouge">updateHash</code></li> <li><code class="language-plaintext highlighter-rouge">confirm</code></li> <li><code class="language-plaintext highlighter-rouge">dismiss</code></li> <li><code class="language-plaintext highlighter-rouge">prompt</code></li> <li><code class="language-plaintext highlighter-rouge">clearCookies</code></li> </ul> <p>См. <a href="https://github.com/selenide/selenide/issues/1383">issue 1383</a> и <a href="https://github.com/selenide/selenide/pull/1404">PR 1404</a>.</p> <p><br /></p> <h1 id="добавили-аннотаций-nullable-методам-класса-webdriverrunner">Добавили аннотаций <code class="language-plaintext highlighter-rouge">@Nullable</code> методам класса <code class="language-plaintext highlighter-rouge">WebDriverRunner</code></h1> <p>Самым важным, по-видимому, тут является метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.getSelenideProxy()</code> - не было очевидно, что он может возвращать <code class="language-plaintext highlighter-rouge">null</code>, если прокси не запущен.</p> <p>Теперь такая ошибка будет подсвечиваться в IDE жёлтеньким.</p> <p>См. <a href="https://github.com/selenide/selenide/commit/9b4723d090442c">коммит</a>.</p> <p><br /></p> <h1 id="починили-тесты-селенида-на-не-англоязычных-машинах">Починили тесты селенида на не-англоязычных машинах</h1> <p>Оказалось, что парочка собственных тестов селенида падали, если их запустить на машине, на которой системным языком был выбран не английский, а скажем, французкий или испанский. Оказывается, в этих странах иначе форматируют Duration.</p> <p>Теперь тесты исправлены, и по идее такое больше не может повториться.</p> <p>Спасибо <a href="https://github.com/vrossellotravelc">Vicente Rossello Jaume</a> за <a href="https://github.com/selenide/selenide/pull/1408">PR 1408</a>.</p> <p><br /></p> <h1 id="статистика">Статистика</h1> <p>Свежая статистика по скачкам Селенида:</p> <center> <img src="/images/2021/02/selenide.downloads.png" width="800" /> </center> <p><br /></p> <h1 id="традиции">Традиции</h1> <p>Ну вот и всё на сегодня.</p> <p>Вы обновляйтесь, а я пока пойду найду стопки водки и бутерброд с килькой. Традиция, ничего не поделаешь.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/02/24/selenide-5.19.0/ http://ru.selenide.org/2021/02/24/selenide-5.19.0 2021-02-24T00:00:00+00:00 Вышла Selenide 5.18.1 <p>Всем привет!</p> <p>Как сказал бы рэпер Гнойный, “релизай, а не накапливай!”<br /> Вот и мы релизнули небольшое обновление <a href="https://github.com/selenide/selenide/milestone/115?closed=1">Selenide 5.18.1</a>.</p> <p>Давайте заглянем туда с фонариком:</p> <p><br /></p> <h1 id="добавили-метод-selenidegetsessionstorage">Добавили метод <code class="language-plaintext highlighter-rouge">Selenide.getSessionStorage()</code></h1> <p>по аналогии с <code class="language-plaintext highlighter-rouge">Selenide.getLocalStorage()</code>, который появился ранее в версии 5.15.0.</p> <p>У них одинаковый набор методов: <code class="language-plaintext highlighter-rouge">getItem</code>, <code class="language-plaintext highlighter-rouge">setItem</code>, <code class="language-plaintext highlighter-rouge">removeItem</code>, <code class="language-plaintext highlighter-rouge">clear</code> и т.д.</p> <blockquote> <p>Иногда полезно положить <code class="language-plaintext highlighter-rouge">sessionStorage</code> или <code class="language-plaintext highlighter-rouge">localStorage</code>, скажем, хитрый флаг, чтобы эмулировать какое-нибудь действие пользователя или включить-выключить какие-то фичи или настройки.</p> </blockquote> <p>Спасибо <a href="https://github.com/dbudim">Dmitriy Budim</a> за <a href="https://github.com/selenide/selenide/pull/1400">PR 1400</a>.</p> <p>P.S. Для справки, <a href="https://itchief.ru/javascript/localstorage-and-sessionstorage">разница между localStorage и sessionStorage</a>.</p> <p><br /></p> <h1 id="исправили-сообщение-об-ошибке-для-filterbyand">Исправили сообщение об ошибке для <code class="language-plaintext highlighter-rouge">$$.filterBy(and(..))</code></h1> <p>Как вы знаете, селенид предоставляет богатые возможности для фильтрации и проверки коллекций.</p> <p>Но пользователь <a href="https://github.com/fokinp">Pavel Fokin</a> обнаружил, что сообщение об ошибке может выглядеть непонятно, когда коллекция фильтруется по <code class="language-plaintext highlighter-rouge">and</code> условию, т.е. комбинации нескольких разных условий:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">(</span><span class="s">".sofa"</span><span class="o">).</span><span class="na">filterBy</span><span class="o">(</span><span class="n">and</span><span class="o">(</span><span class="s">"shining"</span><span class="o">,</span> <span class="n">text</span><span class="o">(</span><span class="s">"Jorshik"</span><span class="o">),</span> <span class="n">text</span><span class="o">(</span><span class="s">"Zoloto"</span><span class="o">))).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">2</span><span class="o">));</span> </code></pre></div></div> <p><br /></p> <p>Результат был не совсем логичный (он содержал лишь последнее проверенное условие):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">sofa</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">text</span> <span class="nc">Jorshik</span><span class="o">)</span> </code></pre></div></div> <p>А теперь результат более корректный (он содержит все условия):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">sofa</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="nl">shining:</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Jorshik</span><span class="err">'</span> <span class="n">and</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Zoloto</span><span class="err">'</span><span class="o">)</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1392">issue 1392</a>.<br /> Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1393">PR 1393</a>.</p> <p><br /></p> <h1 id="передаём-настройку-noproxy-от-внешнего-прокси-селенидовскому">Передаём настройку “noproxy” от внешнего прокси селенидовскому</h1> <p>Бывает, что селенид запускается и со своим собственным прокси, и с прокси пользователя.</p> <p>У прокси есть одна особенная настройка “noproxy”, и туда часто пихают “localhost”. Это значит, что через прокси должны ходить все запросы, кроме “http://localhost:*”. Так вот, в случае с “двойным” прокси эта настройка терялась, и селенид не мог выполнить запросы на localhost.</p> <p><em>Напрямую, а не через прокси.</em></p> <p><em>Локалхост - нежная штучка!</em></p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1390">PR 1390</a>.</p> <p><br /></p> <h1 id="обновились-на-netty-4159final-и-littleproxy-202">Обновились на Netty 4.1.59.Final и LittleProxy 2.0.2</h1> <p>Вряд ли вы будете читать, но вот релизноутсы <a href="https://netty.io/news/2021/02/08/4-1-59-Final.html">Netty 4.1.59.Final</a> и <a href="https://github.com/mrog/LittleProxy/blob/master/RELEASE_NOTES.md">LittleProxy 2.0.2</a>. Как минимум там исправили какие-то утечки памяти и дыру в безопасности.</p> <h2 id="читальня">Читальня</h2> <p>Организаторы Гейзенбага выложили видосики с осеннего Heisenbug Moscow 2020.<br /> Из того, что я успел заметить:</p> <ul> <li><a href="https://www.youtube.com/watch?list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;v=fFe3reCoeBQ&amp;feature=emb_logo&amp;ab_channel=Heisenbug">Flaky tests. Порядок имеет значение</a> / Андрей Солнцев</li> <li>Воркшоп “Как начать свой проект автоматизации с нуля”: <a href="https://www.youtube.com/watch?v=1aq4gYflEho&amp;feature=youtu.be&amp;utm_campaign=Heisenbug_2020_Piter_Announce_NoParticipants_211220&amp;utm_medium=email&amp;utm_source=newsletter&amp;ab_channel=Heisenbug">часть 1</a>, <a href="https://www.youtube.com/watch?utm_campaign=Heisenbug_2020_Piter_Announce_NoParticipants_211220&amp;utm_medium=email&amp;utm_source=newsletter&amp;v=pbvJ8rmh7Ws&amp;feature=youtu.be&amp;ab_channel=Heisenbug">часть 2</a> / Андрей Солнцев, Юрий Артамонов</li> <li><a href="https://www.youtube.com/watch?v=4yq37nxkguM&amp;list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;index=12&amp;ab_channel=Heisenbug">Серверный античит: Панацея или рудимент?</a> / Евгений Ченцов, Евгений Крутских</li> <li><a href="https://www.youtube.com/watch?v=8gOkdFk2JZ8&amp;list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;index=3&amp;ab_channel=Heisenbug">Типы автоматического тестирования в IntelliJ IDEA</a> / Юрий Артамонов</li> <li><a href="https://www.youtube.com/watch?v=Prm2-c_5mYs&amp;list=PLsVTVVvrKX9tBV0_LSkAoSZge3C8qb0ec&amp;index=18&amp;ab_channel=Heisenbug">Тест-кейсы как код</a> / Артем Ерошенко</li> </ul> <p><br /></p> <p>Вот и всё на сегодня. Обновляйтесь и делитесь впечатлениями. Заводите баги <a href="https://github.com/selenide/selenide/issues/new">на гитхабе</a>, жалуйтесь <a href="https://softwaretesters.slack.com/messages/selenide_ru">в чатиках</a>, материтесь <a href="https://twitter.com/selenide">в твиттере</a>.</p> <p><br /> <em>Опенсорс сильнее багов!</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/02/11/selenide-5.18.1/ http://ru.selenide.org/2021/02/11/selenide-5.18.1 2021-02-11T00:00:00+00:00 Вышла Selenide 5.18.0 <p>Ура, товарищи!</p> <p>На дворе 23 января.</p> <p>Несанкционированные релизы сейчас запрещены, поэтому сегодня у нас просто негативное падение номера версии. Обновляйтесь: <a href="https://github.com/selenide/selenide/milestone/113?closed=1">Selenide 5.18.0</a>.</p> <p>Приглашаю вас на маленькую виртуальную экскурсию по изменениям в 5.18.0.<br /> Устраивайтесь поудобнее.</p> <p><br /></p> <h1 id="выключили-логи-вебдрайвера-по-умолчанию">Выключили логи вебдрайвера по умолчанию</h1> <p>Начиная с версии 5.13.0, Selenide писал логи вебдрайвера в файлы <code class="language-plaintext highlighter-rouge">build/reports/tests/webdriver.uuid.log</code>.<br /> Тогда это казалось полезным, но оказалось, что логи эти занимают довольно много места и особо никого не интересуют. Поэтому мы всё-таки решили не включать их по умолчанию.</p> <p><em>Логи вебдрайвера отправляются в комнату для мусора.</em></p> <p>Если надо, вы всегда можете включить эти логи настройкой<br /> <code class="language-plaintext highlighter-rouge">Configuration.webdriverLogsEnabled = true</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1365">issue 1365</a> и <a href="https://github.com/selenide/selenide/pull/1379">PR 1379</a>.</p> <p><br /></p> <h1 id="заменили-тип-параметра-timeout-с-long-на-duration">Заменили тип параметра “timeout” с Long на Duration</h1> <p>… в методах коллекций. Теперь вместо</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(...),</span> <span class="mi">5000</span><span class="o">);</span> </code></pre></div></div> <p>модно будет писать</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(...),</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">5</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1377">issue 1377</a>.<br /> Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за <a href="https://github.com/selenide/selenide/pull/1377">PR 1377</a>.</p> <p><br /></p> <h1 id="ускорили-поиск-вложенных-элементов-в-shadow-dom">Ускорили поиск вложенных элементов в shadow dom</h1> <p>Одна из фич, которые есть Selenide, но нет в Selenium webdriver - это <a href="/2020/03/18/selenide-5.10.0/"><em>shadow dom</em></a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"p"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">,</span> <span class="s">"#inner-shadow-host"</span><span class="o">))</span> <span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"The Shadow-DOM inside another shadow tree"</span><span class="o">));</span> </code></pre></div></div> <p>В дереве DOM может быть сколько угодно <em>shadow root</em> внутри других <em>shadow root</em>.</p> <p>Чтобы найти все элементы внутри всех вложенных shadow root, метод <code class="language-plaintext highlighter-rouge">$(shadowCss())</code> вызывал в цикле<br /> хитрый JavaScript код для каждого shadow root по отдельности. И это может быть медленно, потому что каждый вызов webdriver занимает время.</p> <p>Теперь <code class="language-plaintext highlighter-rouge">$(shadowCss())</code> дёргает <a href="https://github.com/selenide/selenide/blob/master/src/main/resources/find-in-shadow-roots.js">ещё более хитрый рекурсивный JavaScript</a>, который находит все элементы во всех shadow dom за один вызов.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/1373">PR 1373</a>.</p> <p>Отдельное спасибо <a href="https://github.com/sakamoto66">sakamoto66</a> за <a href="https://github.com/selenide/selenide/issues/1246">issue 1246</a> и <a href="https://github.com/selenide/selenide/pull/1233">PR 1233</a>. К сожалению, он не был влит, но дал нам пищу для размышлений.</p> <p><br /></p> <h1 id="исправили-проверки-shouldnotand-и-shouldnotor">Исправили проверки <code class="language-plaintext highlighter-rouge">$.shouldNot(and(...))</code> и <code class="language-plaintext highlighter-rouge">$.shouldNot(or(...))</code></h1> <p>Пользователь <a href="https://github.com/pavelpp">pavelpp</a> обнаружил в селениде багу, комбинируя <code class="language-plaintext highlighter-rouge">not</code> с условиями <code class="language-plaintext highlighter-rouge">and</code> и <code class="language-plaintext highlighter-rouge">or</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".lolkek"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">visible</span><span class="o">,</span> <span class="n">ofSeconds</span><span class="o">(</span><span class="mi">5</span><span class="o">));</span> <span class="c1">// работает</span> <span class="err">$</span><span class="o">(</span><span class="s">".lolkek"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">and</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="n">visible</span><span class="o">));</span> <span class="c1">// падает</span> <span class="err">$</span><span class="o">(</span><span class="s">".lolkek"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">or</span><span class="o">(</span><span class="s">"foo"</span><span class="o">,</span> <span class="n">visible</span><span class="o">));</span> <span class="c1">// падает</span> </code></pre></div></div> <p>Не отвертишься. Пришлось чинить.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1369">issue 1369</a> и <a href="https://github.com/selenide/selenide/pull/1370">PR 1370</a>.</p> <p>Кстати, мы запретили <code class="language-plaintext highlighter-rouge">and</code> и <code class="language-plaintext highlighter-rouge">or</code> только с одним условием. То есть теперь строка <code class="language-plaintext highlighter-rouge">or("foo", visible)</code> не скомпилируется. Придётся написать как минимум два условия: <code class="language-plaintext highlighter-rouge">or("foo", visible, enabled)</code>.</p> <p>Согласитесь, это логично. <em>Зачем нам выбор из одного?</em></p> <p><br /></p> <h1 id="обнаруживаем-конфликт-в-настройке-browsername">Обнаруживаем конфликт в настройке “browserName”</h1> <p>Так уж вышло, что для задания браузера есть две разных настройки:</p> <ol> <li>Главная - <code class="language-plaintext highlighter-rouge">Configuration.browser</code></li> <li>Другая - <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities["browserName"]</code> (не знаю, зачем она вообще нужна)</li> </ol> <p>И у вас может открыться неожиданный браузер, если вы не задали первую, но задали вторую. В общем, теперь селенид обнаруживает такой конфликт и сразу кидает ошибку, чтобы всем всё стало понятно:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalArgumentException:</span> <span class="nc">Conflicting</span> <span class="n">browser</span> <span class="nl">name:</span> <span class="err">'</span><span class="n">chrome</span><span class="err">'</span> <span class="n">vs</span><span class="o">.</span> <span class="err">'</span><span class="n">firefox</span><span class="err">'</span> </code></pre></div></div> <p>Подчёркиваю: настройки <code class="language-plaintext highlighter-rouge">Configuration.browser</code> должно хватать во всех случаях, поэтому вторую не надо никуда пихать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1366">issue 1366</a> и <a href="https://github.com/selenide/selenide/pull/1374">PR 1374</a>.</p> <p><br /></p> <h1 id="починили-показ-таймаута-в-отчёте">Починили показ таймаута в отчёте</h1> <p>Недавно мы добавили в методы <code class="language-plaintext highlighter-rouge">$.should*</code> параметр <code class="language-plaintext highlighter-rouge">timeout</code> с типом <code class="language-plaintext highlighter-rouge">Duration</code>. А потом оказалось, что он непонятно отображался в отчётах.<br /> Например, строка <code class="language-plaintext highlighter-rouge">$("h1").shouldBe(visible, Duration.ofSeconds(1))</code> в отчёте выглядела так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">)</span> <span class="n">should</span> <span class="nf">be</span><span class="o">([</span><span class="n">visible</span><span class="o">,</span> <span class="no">PT1M</span><span class="o">])</span> </code></pre></div></div> <p>И хотя эта “PT1M” на самом деле отвечает стандарту ISO и означает “time period 1 minute”, мы всё же заменили её на более понятные “300 ms”, “1s”, “1.500 s.” и т.п.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1376">issue 1376</a> и <a href="https://github.com/selenide/selenide/pull/1378">PR 1378</a>.</p> <p><br /></p> <h1 id="обновились-на-webdrivermanager-431">Обновились на WebDriverManager 4.3.1</h1> <p>Как обычно, изменения описаны <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">здесь</a>.</p> <p><br /></p> <p>Вот и всё на сегодня. Обновляйтесь и делитесь впечатлениями. Заводите баги <a href="https://github.com/selenide/selenide/issues/new">на гитхабе</a>, жалуйтесь <a href="https://softwaretesters.slack.com/messages/selenide_ru">в чатиках</a>, материтесь <a href="https://twitter.com/selenide">в твиттере</a>.</p> <p><br /></p> <h2 id="читальня">Читальня</h2> <p>На хабре вышла расшифровка доклада <a href="https://habr.com/ru/company/jugru/blog/537166/">“Как законтрибьютить в опенсорс, чтобы не сгореть со стыда”</a> со смачной картинкой Мастера Йоды. Этот доклад мы с Артёмом Ерошенко делали на фестивале TechTrain осенью 2020.</p> <p>По ссылке и текст, и видео: кому что больше нравится.</p> <center> <img src="https://habrastorage.org/webt/ry/y2/bn/ryy2bn6_cjot2q7dipqjuq66i6q.png" width="500px" /> </center> <p>Буду счастлив, если пользователи Селенида его посмотрят. Вы же хотите приобщиться к таинству опен-сорса?</p> <p><br /> Всем щастья! <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2021/01/23/selenide-5.18.0/ http://ru.selenide.org/2021/01/23/selenide-5.18.0 2021-01-23T00:00:00+00:00 Вышла Selenide 5.17.2 <p>Доброе утро!<br /> Гномики всё никак не успокоятся. Завтра утром они принесут в ваши носочки последний подарочек: релиз <a href="https://github.com/selenide/selenide/milestone/110?closed=1">Selenide 5.17.2</a>.</p> <p><br /></p> <h1 id="теперь-commands-возвращают-selenideelement-вместо-webelement">Теперь <code class="language-plaintext highlighter-rouge">Commands</code> возвращают <code class="language-plaintext highlighter-rouge">SelenideElement</code> вместо <code class="language-plaintext highlighter-rouge">WebElement</code></h1> <p>Это даёт возможность <em>чейнить</em> вызовы методов <code class="language-plaintext highlighter-rouge">$.execute(Command)</code> с другими селенидовскими метода, делая ваши тесты ещё лаконичнее и экспрессивнее:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".lupa"</span><span class="o">).</span><span class="na">execute</span><span class="o">(</span><span class="k">new</span> <span class="nc">ScrollToCenter</span><span class="o">()).</span><span class="err">$</span><span class="o">(</span><span class="s">".pupa"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1355">PR 1355</a>.</p> <p><br /></p> <h1 id="починили-метод-setvaluenull">Починили метод <code class="language-plaintext highlighter-rouge">$.setValue(null)</code></h1> <p>См. <a href="https://github.com/selenide/selenide/issues/1356">issue 1356</a>. Спасибо <a href="https://github.com/dzem">Dmitriy Zemlyanitsyn</a> за <a href="https://github.com/selenide/selenide/pull/1357">PR 1357</a>.</p> <p><br /></p> <h1 id="включили-soft-asserts-в-методах-beforeall-и-afterall-в-junit-5">Включили soft asserts в методах @BeforeAll и @AfterAll (в JUnit 5)</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/981">issue 981</a>, <a href="https://github.com/selenide/selenide/issues/1070">issue 1070</a> и <a href="https://github.com/selenide/selenide/pull/1359">PR 1359</a>.</p> <p><br /></p> <h1 id="починили-файл-selenide-5172-javadocjar">Починили файл <a href="https://search.maven.org/remotecontent?filepath=com/codeborne/selenide/5.17.2/selenide-5.17.2-javadoc.jar">selenide-5.17.2-javadoc.jar</a></h1> <p>теперь он содержит javadoc для всех классов.</p> <p><br /></p> <p><br /> Ещё раз с наступающим! <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/12/30/selenide-5.17.2/ http://ru.selenide.org/2020/12/30/selenide-5.17.2 2020-12-30T00:00:00+00:00 Вышла Selenide 5.17.0 <p>Доброй ночи!</p> <p>Я трепетно отношусь к Католическому Рождеству.<br /> Потому, что именно в Рождество террористы захватили небоскрёб Накатоми, и Брюс замочил Ханса Грубера. А потом взорвал самолёт зажигалкой.</p> <p>Поэтому ловите рождественский релиз <a href="https://github.com/selenide/selenide/milestone/108?closed=1">Selenide 5.17.0</a>.</p> <p><br /></p> <h1 id="добавили-метод-asname">Добавили метод $.as(“name”)</h1> <p>Чувствую, что открываю ящик пандоры, но что уж поделаешь…</p> <p>В общем, мы добавили метод <code class="language-plaintext highlighter-rouge">as</code>, позволяющий давать элементам читаемые имена.</p> <p>Чтобы продемонстрировать эффект, давайте сравним эти две строки в тесте:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/long/ugly/xpath[1][2][3]"</span><span class="o">)).</span><span class="na">as</span><span class="o">(</span><span class="s">"Login button"</span><span class="o">).</span><span class="na">shouldNot</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span> </code></pre></div></div> <p>Результат в отчёте будет таким:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> +---------------------------------------+--------------------+----------+----------+ |Element |Subject |Status |ms. | +---------------------------------------+--------------------+----------+----------+ |By.xpath: /long/ugly/xpath[1][2][3] |should not(exist) |PASS |13 | |Login button |should not(exist) |PASS |38 | +---------------------------------------+--------------------+----------+----------+ </code></pre></div></div> <p>В последней строке вместо длинного нечитабельного xpath мы видим понятное имя “Login button”.</p> <p>NB! Не торопитесь использовать эту возможность. Лично я воспринимаю её как “хак последней надежды”.<br /> В отчёте всегда лучше видеть настоящий локатор, чем кем-то придуманное имя, которое всегда может оказаться:</p> <ul> <li>Обманчивым</li> <li>Устаревшим</li> <li>Вводящим в заблуждение</li> </ul> <p>Лучше инвестируйте свои усилия в то, чтобы использовать читаемые локаторы. Ну и грамотно называть переменные и методы. Вот где сила, брат!</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1200">issue 1200</a> и <a href="https://github.com/selenide/selenide/pull/1353">PR 1353</a>.</p> <p><br /></p> <h1 id="добавили-настроек-нашему-headless-chrome">Добавили настроек нашему headless chrome</h1> <p>Мы взяли и добавили нашему безголовому хрому те же настройки, <a href="https://github.com/puppeteer/puppeteer/blob/7a2a41f2087b07e8ef1feaf3881bdcc3fd4922ca/src/Launcher.js#L261">что и в puppeteer</a>.<br /> Ребята из puppeteer же не дураки, знают, что делают. Мы как они.</p> <p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1329">PR 1329</a>.</p> <p>P.S. Интересно, если ребята из puppeteer решать с крыши спрыгнуть, мы тоже спрыгнем?</p> <p><br /></p> <h1 id="починили-byshadowcssfindelements">Починили <code class="language-plaintext highlighter-rouge">ByShadowCss.findElements</code></h1> <p>Оказалось, в одном хитром случае он возвращал не все элементы: когда в DOM было несколько <em>inner shadow hosts</em> (как это по-православному, бог ты мой?)</p> <p>Теперь возвращает все.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1346">issue 1346</a>. Спасибо <a href="https://github.com/dpeger">Daniel H. Peger</a> за <a href="https://github.com/selenide/selenide/pull/1347">PR 1347</a>.</p> <p><br /></p> <h1 id="добавили-метод-should-с-кастомным-таймаутом">Добавили метод $.should* с кастомным таймаутом</h1> <p>Как вы все знаете, в Селениде есть две основных группы методов:</p> <ol> <li><code class="language-plaintext highlighter-rouge">$.shouldHave</code> / <code class="language-plaintext highlighter-rouge">$.shouldBe</code> / <code class="language-plaintext highlighter-rouge">$.should</code> – используют таймаут по умолчанию</li> <li><code class="language-plaintext highlighter-rouge">$.waitUntil</code> / <code class="language-plaintext highlighter-rouge">$.waitWhile</code> – используют заданный таймаут</li> </ol> <p>Методы <code class="language-plaintext highlighter-rouge">$.wait*</code> полезны для ожидания явно долгих действий, которые точно длятся дольше обычного таймаута (который 4 секунды по умолчанию).</p> <p>Проблема с <code class="language-plaintext highlighter-rouge">$.wait*</code> методами чисто грамматическая: встроенные селенидовские проверки не звучат по-английски корректно с глаголом “wait”:</p> <ol> <li><code class="language-plaintext highlighter-rouge">element should have text</code> - звучит</li> <li><code class="language-plaintext highlighter-rouge">element wait until text</code> - не звучит</li> </ol> <p>Не смертельно, но ухо режет (как говорил Ван Гог, гы-гы).</p> <p>Поэтому теперь вы можете заменить <code class="language-plaintext highlighter-rouge">$.waitUntil(hasText("bob"), 18_000)</code> на <code class="language-plaintext highlighter-rouge">$.shouldHave(text("bob"), Duration.ofSeconds(18))</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1136">issue 1136</a>, <a href="https://github.com/selenide/selenide/issues/1338">issue 1338</a> и <a href="https://github.com/selenide/selenide/pull/1340">PR 1340</a>.</p> <p><br /></p> <h1 id="пэдж-обжекты">Пэдж обжекты</h1> <p>Следующий блок улучшений касается пэдж обжектов. Как вы знаете, в селениде есть возможность объявлять свои пэдж обжекты,</p> <ul> <li>а в них объявлять поля с аннотациями <code class="language-plaintext highlighter-rouge">@FindBy</code>,</li> <li>а поля эти могут быть как стандартные селениумовские <code class="language-plaintext highlighter-rouge">WebElement</code>,</li> <li>так и наши <code class="language-plaintext highlighter-rouge">SelenideElement</code>,</li> <li>и даже переиспользуемые компоненты <code class="language-plaintext highlighter-rouge">ElementsContainer</code>, из которых можно собирать пэдж обжекты, как из кирпичиков.</li> </ul> <p>И работает всё это в лучших традициях селенида: ленивая загрузка и перезагрузка элементов, вот это всё.</p> <p><br /></p> <p>В общем, в этих <code class="language-plaintext highlighter-rouge">@FindBy</code> уже давно были шероховатости:</p> <ol> <li>не работала ленивая загрузка для полей ПО с типом <code class="language-plaintext highlighter-rouge">List&lt;ElementsContainer&gt;</code> (см. issue <a href="https://github.com/selenide/selenide/issues/282">282</a> и <a href="https://github.com/selenide/selenide/issues/482">482</a>)</li> <li>не поддерживались поля ПО с дженериками (см. <a href="https://github.com/selenide/selenide/issues/694">issue 694</a>)</li> </ol> <p>И это было сложно исправить. Пришлось погрузиться в этот (старый) код и серьёзно его порефакторить. Пришлось напрячь мозги и применить все свои остатки былого абстрактного мышления.</p> <p>Так что эти два пулреквеста - предмет моей гордости:</p> <ul> <li><a href="https://github.com/selenide/selenide/pull/1351">support page object fields of generic types</a></li> <li><a href="https://github.com/selenide/selenide/pull/1354">enable lazy loading for Page Object fields of type <code class="language-plaintext highlighter-rouge">List&lt;ElementsContainer&gt;</code></a></li> </ul> <p><br /></p> <h1 id="и-напоследок-пачка-технологических-улучшений">И напоследок пачка технологических улучшений:</h1> <ul> <li>разбили проект на подпроекты - см. <a href="https://github.com/selenide/selenide/pull/1348">PR 1348</a></li> <li>исправили тесты селенида, зависящие от ОС - См. <a href="https://github.com/selenide/selenide/issues/1344">issue 1344</a> и <a href="https://github.com/selenide/selenide/pull/1345">PR 1345</a>, спасибо <a href="https://github.com/dpeger">Daniel H. Peger</a></li> <li>подчистили код <code class="language-plaintext highlighter-rouge">Plugins</code> – спасибо <a href="https://github.com/yorlov">Yuri Orlov</a> за <a href="https://github.com/selenide/selenide/pull/1343">PR 1343</a></li> <li>Обновились до browserup-proxy:2.1.2 и guava:30.1-jre</li> <li>Добавили поддержку chrome 88, edge 89, opera 73</li> </ul> <p><br /></p> <h3 id="известные-проблемы">Известные проблемы:</h3> <ul> <li>файл <a href="https://search.maven.org/remotecontent?filepath=com/codeborne/selenide/5.17.0/selenide-5.17.0-javadoc.jar">selenide-5.17.0-javadoc.jar</a> получился неполным: он содержит javadoc не для всех классов. Надеемся, никто не обидится. Исправим в версии 5.17.1.</li> </ul> <p><br /></p> <h1 id="итоги">Итоги</h1> <p>В общем, заканчиваем год на позитивной ноте:<br /> провели серьёзный рефакторинг и исправили несколько старых болячек.<br /> В новый год мы переходим всего с одним экраном в <a href="https://github.com/selenide/selenide/issues">github issues</a> вместо прежних двух.</p> <p>И количество скачиваний селенида выросло за год с 102 до 167 тысяч.</p> <center> <img src="/images/2020/12/selenide.downloads.png" width="800" /> </center> <p><br /> С наступающим! <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/12/26/selenide-5.17.0/ http://ru.selenide.org/2020/12/26/selenide-5.17.0 2020-12-26T00:00:00+00:00 Вышла Selenide 5.16.2 <p>Всем привет!</p> <p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/109?closed=1">Selenide 5.16.2</a>.</p> <p>Нет, вы не подумайте, что в недавнем крупном релизе <a href="/2020/11/20/selenide-5.16.0/">Selenide 5.16.0</a> была куча багов. Вовсе нет.</p> <p>Данный релиз 5.16.2 - это исправление кучи старых мелких бажочков, просто сейчас дошли до них руки.</p> <p>Итак, коротко:</p> <h2 id="selenide-5.16.2">Релиз <a href="https://github.com/selenide/selenide/milestone/109?closed=1">5.16.2</a> (вышел 25.11.2020)</h2> <ul> <li><a href="https://github.com/selenide/selenide/issues/1332">#1332</a> return old click(int, int) command logic – thanks to Petro Ovcharenko for PR <a href="https://github.com/selenide/selenide/pull/1333">#1333</a></li> <li>make SoftAssertsExtension thread-safe – thanks to @dtuchs for PR <a href="https://github.com/selenide/selenide/pull/1334">#1334</a></li> <li><a href="https://github.com/selenide/selenide/issues/1258">#1258</a> fix soft asserts with ParameterizedTest in jUnit5 – see PR <a href="https://github.com/selenide/selenide/pull/1328">#1328</a></li> <li><a href="https://github.com/selenide/selenide/issues/1293">#1293</a> don’t report “Element not found” in case of other errors – see PR <a href="https://github.com/selenide/selenide/pull/1326">#1326</a></li> <li><a href="https://github.com/selenide/selenide/issues/1290">#1290</a> don’t show unused page object fields in report – see PR <a href="https://github.com/selenide/selenide/pull/1327">#1327</a></li> <li>upgrade to littleproxy:2.0.1 – see PR <a href="https://github.com/selenide/selenide/pull/1325">#1325</a></li> </ul> <h2 id="selenide-5.16.1">Релиз <a href="https://github.com/selenide/selenide/milestone/106?closed=1">5.16.1</a> (вышел 23.11.2020)</h2> <p>Тут было два исправления, чтобы хром можно было запускать с расширениями.</p> <ul> <li><a href="https://github.com/selenide/selenide/issues/1314">#1314</a> do not exclude “load-extension” switch if Chrome is opened with extensions – see PR <a href="https://github.com/selenide/selenide/pull/1324">#1324</a></li> <li><a href="https://github.com/selenide/selenide/issues/1315">#1315</a> support custom DriverFactory for running remote browsers – see PR <a href="https://github.com/selenide/selenide/pull/1324">#1324</a></li> </ul> <h2 id="news">Новости</h2> <p>Раз осталось место, поделюсь новенькими ссылками:</p> <ul> <li>Пример от LambdaTest: <a href="https://github.com/LambdaTest/selenide-testng-sample">selenide-testng-sample</a></li> <li>Видосик про Селенид на португальском: <a href="https://www.youtube.com/watch?v=yOfrqZUsFuU&amp;feature=youtu.be&amp;ab_channel=BluesoftLabs">Testes de Aceitação em Java com Selenide, Adriano Magalhães</a></li> <li>Курс по Селениду где-то в Бразилии: <a href="https://inoveteste.com.br/automacao-web-descomplicada-com-selenide/">Automação Web Descomplicada Com Selenide</a></li> <li>Визуальное тестирование: <a href="https://medium.com/automated-visual-testing-with-applitools/getting-started-with-the-applitools-sdk-653f2cd1ad48">Applitools+Selenide</a></li> <li>Пример проекта: <a href="https://github.com/bmurmistro/applitools">Getting started with applitools</a></li> </ul> <p><br /></p> <p>Selenide 5.17.0 уже не за горами, не переключайтесь!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/11/25/selenide-5.16.2/ http://ru.selenide.org/2020/11/25/selenide-5.16.2 2020-11-25T00:00:00+00:00 Вышла Selenide 5.16.0 <p>Всем привет!</p> <p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/105?closed=1">Selenide 5.16.0</a>.</p> <h2 id="плагины">Плагины</h2> <p>В начале 2020 года на конференции <a href="https://seleniumcamp.com/materials/">SeleniumCamp</a> я рассказывал про <a href="https://seleniumcamp.com/talk/bof-glorious-past-and-promising-future-of-selenide/">roadmap селенида</a>. Одна из ключевых идей на этот год была создать в селениде возможность подключать сторонние плагины.</p> <p><strong>И вот этот день настал!</strong></p> <p>На данный момент доступно два плагина:</p> <ul> <li><a href="https://github.com/selenide/selenide-appium">selenide-appium 1.5.0</a></li> <li><a href="https://github.com/selenide/selenide-selenoid">selenide-selenoid 1.0.0</a></li> </ul> <p>Расскажем о них в отдельных постах.</p> <p>Вероятно, дальше стоит оформить в виде плагинов поддержку Allure, JUnit, TestNG, AShot.<br /> <em>Накидывайте ещё идеи!</em></p> <p>См. <a href="https://github.com/selenide/selenide/issues/1051">issue #1051</a> и <a href="https://github.com/selenide/selenide/pull/1264">PR #1264</a>, <a href="https://github.com/selenide/selenide/pull/1317">PR 1317</a> и <a href="https://github.com/selenide/selenide/pull/1321">PR 1321</a>.</p> <h2 id="сообщения-об-ошибках">Сообщения об ошибках</h2> <p>Одна из основных обязанностей селенида - составлять подробные сообщения об ошибках в случае падения тестов.<br /> Иногда в эти сообщения тоже закрадываются неточности в каких-то сложных случаях.<br /> В этом релизе мы запилили целую пачку улучшений сообщений об ошибках:</p> <h3 id="улучшили-описание-проверок-and-и-not">Улучшили описание проверок AND и NOT</h3> <p>Нормальный способ устроить негативную проверку в селениде - метод <code class="language-plaintext highlighter-rouge">shouldNotHave</code>. А проверить несколько условий подряд - просто через запятую:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldNotHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"____"</span><span class="o">),</span> <span class="n">attribute</span><span class="o">(</span><span class="s">"data-masked"</span><span class="o">));</span> </code></pre></div></div> <p>Но есть ещё методы <code class="language-plaintext highlighter-rouge">Condition.not</code> и <code class="language-plaintext highlighter-rouge">Condition.and</code> для особых случаев: например, с их помощью можно определять составные условия, таким образом составляя целый DSL для ваших тестов.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyConditions</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Condition</span> <span class="no">NONADMIN</span> <span class="o">=</span> <span class="n">not</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Condition</span> <span class="no">MASKED</span> <span class="o">=</span> <span class="n">and</span><span class="o">(</span><span class="s">"MASKED"</span><span class="o">,</span> <span class="n">text</span><span class="o">(</span><span class="s">"___"</span><span class="o">),</span> <span class="n">attribute</span><span class="o">(</span><span class="s">"data-masked"</span><span class="o">));</span> <span class="o">}</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyTest</span> <span class="o">{</span> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="no">MASKED</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="no">NONADMIN</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Оказалось, что оба эти методы выводили в отчёте неполную информацию:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">be</span><span class="o">(</span><span class="no">MASKED</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span> <span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">have</span><span class="o">(</span><span class="n">not</span> <span class="n">text</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span> </code></pre></div></div> <p>Проблема в том, что в нём не виден ожидаемый текст (“admin”) и другие условия.</p> <p>Теперь мы эту проблему исправили, и при падении проверки вы увидите сообщение с текстом:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">be</span><span class="o">(</span><span class="nl">MASKED:</span> <span class="n">text</span> <span class="err">'</span><span class="n">___</span><span class="err">'</span> <span class="n">and</span> <span class="n">attribute</span> <span class="n">data</span><span class="o">-</span><span class="n">masked</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span> <span class="o">|</span> <span class="err">#</span><span class="n">username</span> <span class="o">|</span><span class="n">should</span> <span class="nf">have</span><span class="o">(</span><span class="n">not</span> <span class="n">text</span> <span class="err">'</span><span class="n">admin</span><span class="err">'</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/fokinp">Pavel Fokin</a> за <a href="https://github.com/selenide/selenide/pull/1306">PR 1306</a> и <a href="https://github.com/selenide/selenide/pull/1300">PR 1306</a>.</p> <p><br /></p> <h3 id="добавили-информацию-о-предках">Добавили информацию о предках</h3> <p>Селенид позволяет искать элементы внутри других элементов. Например, так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#user-table"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"thead"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="s">"trrrr-chah-chah"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Age"</span><span class="o">));</span> </code></pre></div></div> <p>Но если такая проверка падает, в сообщении был виден только локатор дочернего элемента:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="n">trrrr</span><span class="o">-</span><span class="n">chah</span><span class="o">-</span><span class="n">chah</span><span class="o">}</span> </code></pre></div></div> <p>А теперь мы также добавили информацию и о родителях:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="err">#</span><span class="n">user</span><span class="o">-</span><span class="n">table</span><span class="o">/</span><span class="n">thead</span><span class="o">/</span><span class="n">trrrr</span><span class="o">-</span><span class="n">chah</span><span class="o">-</span><span class="n">chah</span><span class="o">}</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> за <a href="https://github.com/selenide/selenide/pull/1312">PR 1312</a>.</p> <p><br /> <br /></p> <h3 id="добавили-актуальные-текст-для-проверок-owntext-и-exactowntext">Добавили актуальные текст для проверок <code class="language-plaintext highlighter-rouge">ownText</code> и <code class="language-plaintext highlighter-rouge">exactOwnText</code></h3> <p>Как вы помните, в Selenide 5.15.0 мы добавили проверки <code class="language-plaintext highlighter-rouge">ownText</code> и <code class="language-plaintext highlighter-rouge">exactOwnText</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#child_div1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">ownText</span><span class="o">(</span><span class="s">"Sonar"</span><span class="o">));</span> </code></pre></div></div> <p>Но и тут оказалось, что при падении такой проверки в сообщении не выводился, а какой же “свой текст” был на самом деле. Выводился только текст элемента с детьми:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">should</span> <span class="n">have</span> <span class="n">own</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Sonar</span><span class="err">'</span> <span class="o">{</span><span class="err">#</span><span class="n">child_div1</span><span class="o">}</span> <span class="nl">Element:</span> <span class="err">'</span><span class="o">&lt;</span><span class="n">div</span> <span class="n">id</span><span class="o">=</span><span class="s">"child_div1"</span><span class="o">&gt;</span><span class="nc">Son</span><span class="o">&lt;/</span><span class="n">div</span><span class="o">&gt;</span><span class="err">'</span> </code></pre></div></div> <p>Теперь мы добавили строчку “Actual value” с собственным текстом элемента:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">should</span> <span class="n">have</span> <span class="n">own</span> <span class="n">text</span> <span class="err">'</span><span class="nc">Sonar</span><span class="err">'</span> <span class="o">{</span><span class="err">#</span><span class="n">child_div1</span><span class="o">}</span> <span class="nl">Element:</span> <span class="err">'</span><span class="o">&lt;</span><span class="n">div</span> <span class="n">id</span><span class="o">=</span><span class="s">"child_div1"</span><span class="o">&gt;</span><span class="nc">Son</span><span class="o">&lt;/</span><span class="n">div</span><span class="o">&gt;</span><span class="err">'</span> <span class="nc">Actual</span> <span class="nl">value:</span> <span class="nc">Son</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1261">issue 1261</a> и <a href="https://github.com/selenide/selenide/pull/1294">PR 1294</a>.</p> <p><br /> <br /></p> <h3 id="кидаем-правильную-ошибку-при-загрузке-несуществующего-файла">Кидаем правильную ошибку при загрузке несуществующего файла</h3> <p>Если вы пытались загрузить несуществующий файл:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"input[type='file']"</span><span class="o">).</span><span class="na">uploadFile</span><span class="o">(</span><span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="s">"/foo/bar/xyz.pdf"</span><span class="o">));</span> </code></pre></div></div> <p>То получали некорректную ошибку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="n">input</span><span class="o">[</span><span class="n">type</span><span class="o">=</span><span class="err">'</span><span class="n">file</span><span class="err">'</span><span class="o">]}</span> <span class="o">...</span> <span class="nc">Caused</span> <span class="nl">by:</span> <span class="nl">InvalidArgumentException:</span> <span class="n">invalid</span> <span class="nl">argument:</span> <span class="nc">File</span> <span class="n">not</span> <span class="n">found</span> <span class="o">:</span> <span class="o">/</span><span class="n">foo</span><span class="o">/</span><span class="n">bar</span><span class="o">/</span><span class="n">xyz</span><span class="o">.</span><span class="na">pdf</span> </code></pre></div></div> <p>Казалось бы, мелочь, но иногда это сбивало с толку.</p> <p>Теперь мы это исправили, и вы получите сразу</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nl">InvalidArgumentException:</span> <span class="n">invalid</span> <span class="nl">argument:</span> <span class="nc">File</span> <span class="n">not</span> <span class="n">found</span> <span class="o">:</span> <span class="o">/</span><span class="n">foo</span><span class="o">/</span><span class="n">bar</span><span class="o">/</span><span class="n">xyz</span><span class="o">.</span><span class="na">pdf</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/987">issue 987</a> и <a href="https://github.com/selenide/selenide/pull/1301">PR 1301</a>.</p> <p><br /></p> <blockquote> <p>Корень проблемы в том, что в селенум не хватает отдельных классов ошибок для всех возможных нестандартных ситуаций, и простой <code class="language-plaintext highlighter-rouge">WebDriverException</code> может говорить как о ненайденном элементе, так и неверной кодировке в вводимой строке. См., например, <a href="https://github.com/selenide/selenide/issues/1293">issues 1293</a>.</p> <p>Плюс некоторые реализации вебдрайвера могут кидать какие-то нелогичные ошибки - например, IEDriver когда-то кидал <code class="language-plaintext highlighter-rouge">Throwable</code> вместо <code class="language-plaintext highlighter-rouge">ElementNotFound</code>.</p> <p>Поэтому в своё время в селениде была принята консервативная стратегия: если не удалось определить тип ошибки точнее, по умолчанию считаем, что элемент не найден. А в “caused by” будут видны подробности.</p> </blockquote> <p><br /> <br /></p> <h3 id="адекватно-показываем-clickoptions-в-отчёте">Адекватно показываем ClickOptions в отчёте</h3> <p>Как вы помните, в Selenide 5.15.0 мы добавили удобные методы для клика со всевозможными опциями:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span> </code></pre></div></div> <p>И снова оказалось, что в отчёте такая строка выглядит некрасиво:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">page</span> <span class="o">|</span><span class="n">click</span><span class="o">(</span><span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">ClickOptions</span><span class="err">@</span><span class="mi">33617539</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span> </code></pre></div></div> <p>Мы и это исправили, теперь строчка стала <em>окейнее</em>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">|</span> <span class="err">#</span><span class="n">page</span> <span class="o">|</span><span class="n">click</span><span class="o">(</span><span class="nl">method:</span> <span class="no">JS</span><span class="o">,</span> <span class="nl">offsetX:</span> <span class="mi">123</span><span class="o">,</span> <span class="nl">offsetY:</span> <span class="mi">222</span><span class="o">)</span> <span class="o">|</span><span class="no">PASS</span> <span class="o">|</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1302">issue 1302</a> и <a href="https://github.com/selenide/selenide/pull/1303">PR 1303</a>.</p> <p><br /></p> <h2 id="другое">Другое</h2> <h3 id="добавили-проверку-shouldhaveexacttextscasesensitiveinanyorder">Добавили проверку <code class="language-plaintext highlighter-rouge">$$.shouldHave(exactTextsCaseSensitiveInAnyOrder(...))</code></h3> <p>В селениде уже давно есть проверки для коллекций:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">texts</span><span class="o">(</span><span class="s">"вася"</span><span class="o">,</span> <span class="s">"петя"</span><span class="o">,</span> <span class="s">"катя"</span><span class="o">));</span> <span class="c1">// case-insensitive, substring</span> <span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">textsInAnyOrder</span><span class="o">(</span><span class="s">"вася"</span><span class="o">,</span> <span class="s">"катя"</span><span class="o">,</span> <span class="s">"петя"</span><span class="o">));</span> <span class="c1">// case-insensitive, substring, any order</span> <span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTexts</span><span class="o">(</span><span class="s">"вася"</span><span class="o">,</span> <span class="s">"петя"</span><span class="o">,</span> <span class="s">"катя"</span><span class="o">));</span> <span class="c1">// case-insensitive, full string match</span> </code></pre></div></div> <p>И теперь к ним добавилась ещё одна - менее толерантная:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// case-sensitive, full string match, any order</span> <span class="err">$$</span><span class="o">(</span><span class="s">".employee"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactTextsCaseSensitiveInAnyOrder</span><span class="o">(</span><span class="s">"Вася"</span><span class="o">,</span> <span class="s">"Петя"</span><span class="o">,</span> <span class="s">"Катя"</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/plagov">Vitali Plagov</a> за <a href="https://github.com/selenide/selenide/pull/1286">PR 1286</a>.</p> <p><br /></p> <h3 id="исправили-проверку-href-со-спецсимволами">Исправили проверку <code class="language-plaintext highlighter-rouge">href</code> со спец.символами</h3> <p>Как вы помните, в Selenide 5.15.0 мы добавили проверку <code class="language-plaintext highlighter-rouge">href</code>.<br /> Оказалось, что она неправильно работала, если в <code class="language-plaintext highlighter-rouge">href</code> затесались экранированные символы, как во второй строке:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">href</span><span class="o">(</span><span class="s">"/foo/bar/details.html"</span><span class="o">));</span> <span class="c1">// works</span> <span class="err">$</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">href</span><span class="o">(</span><span class="s">"/files/some%20file.pdf"</span><span class="o">));</span> <span class="c1">// fails</span> </code></pre></div></div> <p>Проблема исправлена.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1298">issue 1298</a>.<br /> Спасибо <a href="https://github.com/rerednaw">rerednaw</a> за <a href="https://github.com/selenide/selenide/pull/1299">PR 1299</a>.</p> <p><br /></p> <h3 id="разрешаем-хрому-скачивать-несколько-файлов">Разрешаем хрому скачивать несколько файлов</h3> <p>Бывают такие хитрые ссылки, по клику на которые браузер начинает скачивать не один, а сразу два или больше файлов.<br /> Оказалось, что в этом случае браузер Chrome показывает диалог “Уверены, что хотите скачать все файлы?” - и этот диалог не даёт ничего дальше сделать, пока пользователь не нажмёт кнопку. И ваш тест падает.</p> <blockquote> <p>Самое противное в этой проблеме то, что её очень сложно повторить руками: браузер показывает диалог только в первый раз, так что при локальном запуске скорее всего вы его не увидите, и тест будет зелёным.</p> </blockquote> <p>Чтобы вылечить эту проблему, мы добавили при запуске хрома специальный ключик <code class="language-plaintext highlighter-rouge">profile.default_content_setting_values.automatic_downloads=1</code>, который сразу разрешает хрому качать сколько угодно файлов без всяких диалогов.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1307">issue 1307</a>.<br /> Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за <a href="https://github.com/selenide/selenide/pull/1308">PR 1308</a>.</p> <p><br /></p> <h3 id="разрешили-скачивать-файлы-со-слэшем-в-названии">Разрешили скачивать файлы со слэшем в названии</h3> <p>Метод <code class="language-plaintext highlighter-rouge">download</code> не разрешает скачивать файл, если в его названии есть неразрешённые символы, в числе которых был и слэш:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">File</span> <span class="n">report</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#report"</span><span class="o">).</span><span class="na">download</span><span class="o">();</span> <span class="o">--&gt;</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"File name cannot contain slash: 11/08/2020_-_day_transactions.pdf"</span><span class="o">)</span> </code></pre></div></div> <p>Нам казалось это логичным, ведь такие файлы тупо не разрешает создавать файловая система.<br /> Но оказалось, что иногда слэш вполне логичен - например, как разделитель в датах. И хром вполне скачивает такие файлы, просто заменяя слэш на подчёркивание.</p> <p>Теперь и селенид так делает.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1322">issue 1322</a> и <a href="https://github.com/selenide/selenide/pull/1323">PR 1323</a>.</p> <p><br /></p> <h3 id="зафиксировали-версию-guava-300-jre">Зафиксировали версию Guava 30.0-jre</h3> <p>Ох уж эта гуава!</p> <p>В коде самого селенида Guava не используется, нам она не нужна.<br /> Но с ней вечные проблемы: от неё зависят многие другие библиотеки (Selenium, LittleProxy, BrowserUpProxy, Checkstyle), и все от разных версий. А ещё ведь у неё есть специальная версия для android…</p> <p>В общем, слишком часто так случалось, что Maven или Gradle подтягивал не ту версию Guava, и селенид в итоге не работал. <br /> Нам это надоело, и теперь в селениде явно прописана свежая версия Guava <code class="language-plaintext highlighter-rouge">30.0-jre</code>. Надеемся, это поможет избежать всех этих бесконечных конфликтов завиимостей.</p> <p><br /></p> <h3 id="перешли-на-github-actions">Перешли на Github Actions</h3> <p>Как в любом приличном проекте, в Selenide есть свой набор автотестов (юнит- и интеграционных), и они запускаются автоматически для всех веток на CI сервере. Раньше мы использовали Travis CI, который предоставляет бесплатный сервис для опен-сорс проектов. Спасибо им большое за годы совместной работы. :)</p> <p>Но в этом году гитхаб выкатил свой CI сервис под названием “Github actions”.</p> <blockquote> <p>Смотри обзор от Артём Ерошенко и Севы Брекелова в <a href="http://www.youtube.com/watch?v=dxGGZQiuD6Q&amp;t=60m10s">выпуске №3 Шоу “Ошибка выжившего”</a>.</p> </blockquote> <p>И казалось логичным использовать его.</p> <p>Ура, этот день настал! Теперь все билды селенида видны <a href="https://github.com/selenide/selenide/actions">прямо на гитхабе</a>.</p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/1319">PR 1319</a>.</p> <p><br /></p> <h2 id="уффф">Уффф.</h2> <p>Многабукаф, но вы осилили. Все молодцы!</p> <p>Как обычно - обновляйтесь, пробуйте, смело сообщайте о проблемах и делитесь идеями.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/11/20/selenide-5.16.0/ http://ru.selenide.org/2020/11/20/selenide-5.16.0 2020-11-20T00:00:00+00:00 Почему прокси не работает в Selenoid? <p>Всем привет!</p> <p>Сегодня мы наконец-то раскроем тайну, почему у многих не работает прокси в Selenoid.</p> <h3 id="задача-скачать-файл">Задача: скачать файл</h3> <ul> <li>Мы запускаем тесты, в которых браузер бежит в контейнере Selenoid (обычно также и на Selenide, но необязательно).</li> <li>В ходе теста мы хотим скачать файл.</li> <li>Метод по умолчанию <code class="language-plaintext highlighter-rouge">$.download()</code> не подходит (например, потому, что скачивание происходит не по прямой ссылке).</li> <li>Поэтому мы хотим <a href="/2019/12/10/advent-calendar-download-files/">скачать файл через прокси</a>.</li> </ul> <h3 id="наши-действия">Наши действия</h3> <ol> <li>Создаём проект</li> <li>Добавляем в проект зависимость BrowserUpProxy, как указано в документации Selenide: <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">dependencies</span> <span class="p">{</span> <span class="nf">testRuntimeOnly</span><span class="p">(</span><span class="s">"com.browserup:browserup-proxy-core:2.1.1"</span><span class="p">)</span> <span class="p">}</span> </code></pre></div> </div> </li> <li>Копипастим типичный бойлерплейт для запуска браузера в Selenoid: <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyHost</span> <span class="o">=</span> <span class="s">"192.168.0.10"</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">remote</span> <span class="o">=</span> <span class="s">"http://localhost:4444/wd/hub"</span><span class="o">;</span> <span class="nc">DesiredCapabilities</span> <span class="n">capabilities</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DesiredCapabilities</span><span class="o">();</span> <span class="n">capabilities</span><span class="o">.</span><span class="na">setBrowserName</span><span class="o">(</span><span class="s">"chrome"</span><span class="o">);</span> <span class="n">capabilities</span><span class="o">.</span><span class="na">setVersion</span><span class="o">(</span><span class="s">"85.0"</span><span class="o">);</span> <span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"enableVNC"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"enableVideo"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"enableLog"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browserCapabilities</span> <span class="o">=</span> <span class="n">capabilities</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">fileDownload</span> <span class="o">=</span> <span class="nc">FileDownloadMode</span><span class="o">.</span><span class="na">PROXY</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> </code></pre></div> </div> </li> <li>Ну и пишем тест, что-то вроде <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">open</span><span class="o">(</span><span class="s">"https://the-internet.herokuapp.com/download"</span><span class="o">);</span> <span class="nc">File</span> <span class="n">file</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"some-file.txt"</span><span class="o">)).</span><span class="na">download</span><span class="o">();</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">file</span><span class="o">.</span><span class="na">getName</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"some-file.txt"</span><span class="o">);</span> </code></pre></div> </div> </li> </ol> <h3 id="проблема">Проблема</h3> <p>И получаем ошибку при открытии браузера:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">org</span><span class="o">.</span><span class="na">openqa</span><span class="o">.</span><span class="na">selenium</span><span class="o">.</span><span class="na">WebDriverException</span><span class="o">:</span> <span class="n">unknown</span> <span class="nl">error:</span> <span class="nl">net:</span><span class="o">:</span><span class="no">ERR_TUNNEL_CONNECTION_FAILED</span> <span class="o">...</span> <span class="n">at</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="nc">Selenide</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">49</span><span class="o">)</span> <span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">selenoid</span><span class="o">.</span><span class="na">FileDownloadTest</span><span class="o">.</span><span class="na">download</span><span class="o">(</span><span class="nc">FileDownloadTest</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">45</span><span class="o">)</span> </code></pre></div></div> <p><br /></p> <h3 id="ааа-паника">ААА, паника!</h3> <p>На этом месте большинство людей паникует, перебирает кучу опций браузера и селенидовских настроек и в конце концов пишет в чатик автоматизаторов.</p> <p>А ведь всего-то надо было почитать внимательно лог.<br /> В логе чётко видна проблема:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="nc">LittleProxy</span><span class="o">-</span><span class="mi">0</span><span class="o">-</span><span class="nc">ProxyToServerWorker</span><span class="o">-</span><span class="mi">1</span><span class="o">]</span> <span class="no">ERROR</span> <span class="n">org</span><span class="o">.</span><span class="na">littleshoot</span><span class="o">.</span><span class="na">proxy</span><span class="o">.</span><span class="na">impl</span><span class="o">.</span><span class="na">ProxyToServerConnection</span> <span class="o">-</span> <span class="o">(</span><span class="no">HANDSHAKING</span><span class="o">)</span> <span class="o">[</span><span class="nl">id:</span> <span class="mh">0xc05a41d5</span><span class="o">,</span> <span class="nl">L:</span><span class="o">/</span><span class="mf">10.10</span><span class="o">.</span><span class="mf">10.145</span><span class="o">:</span><span class="mi">56103</span> <span class="o">-</span> <span class="nl">R:</span><span class="n">the</span><span class="o">-</span><span class="n">internet</span><span class="o">.</span><span class="na">herokuapp</span><span class="o">.</span><span class="na">com</span><span class="o">/</span><span class="mf">52.1</span><span class="o">.</span><span class="mf">16.137</span><span class="o">:</span><span class="mi">443</span><span class="o">]</span> <span class="o">:</span> <span class="nc">Caught</span> <span class="n">an</span> <span class="n">exception</span> <span class="n">on</span> <span class="nc">ProxyToServerConnection</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">NoSuchMethodError</span><span class="o">:</span> <span class="err">'</span><span class="kt">int</span> <span class="n">io</span><span class="o">.</span><span class="na">netty</span><span class="o">.</span><span class="na">buffer</span><span class="o">.</span><span class="na">ByteBuf</span><span class="o">.</span><span class="na">maxFastWritableBytes</span><span class="o">()</span><span class="err">'</span> <span class="n">at</span> <span class="n">io</span><span class="o">.</span><span class="na">netty</span><span class="o">.</span><span class="na">handler</span><span class="o">.</span><span class="na">codec</span><span class="o">.</span><span class="na">ByteToMessageDecoder</span><span class="err">$</span><span class="mi">1</span><span class="o">.</span><span class="na">cumulate</span><span class="o">(</span><span class="nc">ByteToMessageDecoder</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">86</span><span class="o">)</span> </code></pre></div></div> <h3 id="крутим-зависимости">Крутим зависимости</h3> <p>Ошибка <code class="language-plaintext highlighter-rouge">NoSuchMethodError</code> недвусмысленно намекает на то, что у нас проблема с зависимостями: в classpath оказались какие-то два JAR’а с несовместимыми версиями.</p> <p>Против этого уже давно придумали прививку. Удивляюсь, почему так много людей до сих пор не в курсе.</p> <p>Запускаем команду:</p> <ul> <li><code class="language-plaintext highlighter-rouge">gradle dependencies</code>, или</li> <li><code class="language-plaintext highlighter-rouge">mvn dependency:tree</code></li> </ul> <p>И вуаля! - чётко видим, какие у нас JAR’ы и каких версий. Ищем там что-то похожее на “netty”.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\--- com.browserup:browserup-proxy-core:2.1.1 +--- io.netty:netty-codec:4.1.44.Final +--- xyz.rogfam:littleproxy:2.0.0-beta-5 | +--- io.netty:netty-all:4.1.34.Final </code></pre></div></div> <p>Как видим, у нас есть два джарника с разными версиями: <code class="language-plaintext highlighter-rouge">netty-codec:4.1.44.Final</code> и <code class="language-plaintext highlighter-rouge">netty-all:4.1.34.Final</code>.</p> <h3 id="лечим-зависимости">Лечим зависимости</h3> <p>Чтобы исправить проблему, достаточно явно прописать в <code class="language-plaintext highlighter-rouge">build.gradle</code> или <code class="language-plaintext highlighter-rouge">pom.xml</code> более новую версию <code class="language-plaintext highlighter-rouge">Netty</code>:</p> <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">testRuntimeOnly</span><span class="p">(</span><span class="s">"io.netty:netty-all:4.1.54.Final"</span><span class="p">)</span> <span class="nf">testRuntimeOnly</span><span class="p">(</span><span class="s">"io.netty:netty-codec:4.1.54.Final"</span><span class="p">)</span> </code></pre></div></div> <p>(на самом деле достаточно только одной из этих строк. <em>Домашнее задание: какой и почему?</em>)</p> <p>Команда <code class="language-plaintext highlighter-rouge">gradle dependencies</code> показывает, что теперь версии совпадают:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\--- com.browserup:browserup-proxy-core:2.1.1 +--- io.netty:netty-codec:4.1.44.Final -&gt; 4.1.54.Final +--- xyz.rogfam:littleproxy:2.0.0-beta-5 | +--- io.netty:netty-all:4.1.34.Final -&gt; 4.1.54.Final </code></pre></div></div> <p>Тест запускается, прокси работает, файл скачивается. Всем щастье.</p> <h3 id="мораль">Мораль</h3> <p>Будьте внимательнее к логам, братьям нашим меньшим!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid/ http://ru.selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid 2020-11-17T00:00:00+00:00 Вышла Selenide 5.15.0 <p>Всем привет!</p> <p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/104?closed=1">Selenide 5.15.0</a>.</p> <h2 id="добавили-настройку-configurationpageloadtimeout--по-умолчанию-30-секунд">Добавили настройку <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code> <br /> (по умолчанию 30 секунд)</h2> <p>Бывает так, что вебдрайвер надолго подвисает, пытаясь загрузить какую-то страницу, либо картинку на этой странице, или ещё какой-то элемент. Теряется время, сессия обрывается по таймауту и т.д.</p> <p>В этом случае хорошо бы прервать тест пораньше, но таймаут для загрузки страницы в Selenium по умолчанию слишком большой: 5 минут.</p> <p>Поэтому мы добавили в Selenide настройку <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code>, чтобы этот таймаут было легко менять.</p> <p>Внимание! Значение по умолчанию - 30 секунд. Если ваши тесты начали падать по таймауту - знайте, какую настройку подкрутить.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1268">issue 1268</a> и <a href="https://github.com/selenide/selenide/pull/1269">PR 1269</a>.</p> <h2 id="добавили-универсальный-метод-клика-с-параметром-clickoptions">Добавили универсальный метод клика<br /> с параметром <code class="language-plaintext highlighter-rouge">ClickOptions</code></h2> <p>В некотором роде это новое слово в API селенида.</p> <p>В селениде изначально был метод <code class="language-plaintext highlighter-rouge">$.click()</code>, который просто вызывал обычный селениумовский метод <code class="language-plaintext highlighter-rouge">WebElement.click()</code>. Который кликает (вроде как) в центр элемента.</p> <p>Со временем начали появляться вариации:</p> <ul> <li>Настройка <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code>, чтобы кликать не вызовом <code class="language-plaintext highlighter-rouge">WebElement.click()</code>, а через JavaScript. Теоретически это должно сделать тесты стабильнее (а может, и быстрее) и позволит кликать то, что обычно не получается.</li> <li>Клик со сдвигом <code class="language-plaintext highlighter-rouge">$.click(offsetX, offsetY)</code>, чтобы кликать не в центр элемента</li> </ul> <p>Неудобно в этом то, что невозможно кликать то через JS, то через селениум - настройка глобальная. Пришлось бы её постоянно менять.</p> <p>Поэтому мы добавили метод, в который можно явно передавать парметр - способ клика.</p> <p>Теперь кликать можно без изменения глобальной настройки, хоть обкликайся:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">());</span> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">offset</span><span class="o">(</span><span class="mi">123</span><span class="o">,</span> <span class="mi">222</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingJavaScript</span><span class="o">().</span><span class="na">offsetY</span><span class="o">(</span><span class="mi">222</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#page"</span><span class="o">).</span><span class="na">click</span><span class="o">(</span><span class="n">usingDefaultMethod</span><span class="o">());</span> </code></pre></div></div> <p>NB! Мы рекомендуем выставить настройку <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code> в то значение, которое вам подходит в большинстве случаев, и задавать параметр <code class="language-plaintext highlighter-rouge">ClickOptions</code> точечно - только там, где он необходим.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1173">issue 1173</a>.<br /> Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1226">PR 1226</a>.</p> <h2 id="добавили-универсальный-метод-скачивания-файла-с-параметром-downloadoptions">Добавили универсальный метод скачивания файла<br /> с параметром <code class="language-plaintext highlighter-rouge">DownloadOptions</code></h2> <p>Аналогичная история была с методом <code class="language-plaintext highlighter-rouge">$.download()</code>. Изначально он умел скачивать файлы только одним способом - через GET запрос. Потом селенид научился скачивать файлы через прокси. Недавно мы добавили третий способ - <code class="language-plaintext highlighter-rouge">FOLDER</code>.<br /> До сих пор способ скачивания можно было задать только через глобальную настройку <code class="language-plaintext highlighter-rouge">Configuration.fileDownload</code>.</p> <p>А теперь способ скачивания можно задавать через параметр в каждый вызов <code class="language-plaintext highlighter-rouge">$.download</code>, не меняя глобальную настройку:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">);</span> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">PROXY</span><span class="o">).</span><span class="na">withFilter</span><span class="o">(</span><span class="n">withExtension</span><span class="o">(</span><span class="s">"xls"</span><span class="o">)).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">);</span> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span> <span class="nc">File</span> <span class="n">f</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"input"</span><span class="o">).</span><span class="na">download</span><span class="o">(</span><span class="nc">DownloadOptions</span><span class="o">.</span><span class="na">using</span><span class="o">(</span><span class="no">FOLDER</span><span class="o">).</span><span class="na">withFilter</span><span class="o">(</span><span class="n">withExtension</span><span class="o">(</span><span class="s">"pdf"</span><span class="o">)).</span><span class="na">withTimeout</span><span class="o">(</span><span class="mi">9999</span><span class="o">));</span> </code></pre></div></div> <p>NB! Мы рекомендуем выставить настройку <code class="language-plaintext highlighter-rouge">Configuration.fileDownload</code> в то значение, которое вам подходит в большинстве случаев, и задавать параметр <code class="language-plaintext highlighter-rouge">DownloadsOptions</code> точечно - только там, где он необходим.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1259">issue 1259</a> и <a href="https://github.com/selenide/selenide/pull/1260">PR 1260</a>.</p> <h2 id="добавили-методы-для-работы-с-localstorage">Добавили методы для работы с LocalStorage</h2> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">localStorage</span><span class="o">;</span> <span class="c1">// Очистить всё содержимое:</span> <span class="n">localStorage</span><span class="o">().</span><span class="na">clear</span><span class="o">();</span> <span class="c1">// Добавить значение:</span> <span class="n">localStorage</span><span class="o">().</span><span class="na">setItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">,</span> <span class="s">"john"</span><span class="o">);</span> <span class="c1">// Проверить значение:</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">localStorage</span><span class="o">().</span><span class="na">getItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">)).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"john"</span><span class="o">);</span> <span class="c1">// Проверить количество элементов:</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">localStorage</span><span class="o">().</span><span class="na">size</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span> <span class="c1">// удалить значение:</span> <span class="n">localStorage</span><span class="o">().</span><span class="na">removeItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">);</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">localStorage</span><span class="o">().</span><span class="na">getItem</span><span class="o">(</span><span class="s">"username"</span><span class="o">)).</span><span class="na">isNull</span><span class="o">();</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1274">PR 1274</a>.</p> <h2 id="добавили-проверки-для-текст-элемента-без-потомков">Добавили проверки для текст элемента без потомков</h2> <p>Классическая селенидовская проверка <code class="language-plaintext highlighter-rouge">$.shouldHave(text("Hello, world"))</code> включает и текст самого элемента, и тексты всех его потомков.<br /> А иногда хочется проверить только текст самого элемента, без потомков. Для этого мы добавила две новых проверки:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">ownText</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">));</span> <span class="c1">// Элемент должен содержать текст "Hello"</span> <span class="err">$</span><span class="o">.</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">exactOwnText</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">));</span> <span class="c1">// Текст элемента должен быть "Hello" </span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1261">issue 1261</a> и <a href="https://github.com/selenide/selenide/pull/1262">PR 1262</a>.</p> <h2 id="ускорили-работу-с-большими-фильтрованными-коллекциями">Ускорили работу с большими фильтрованными коллекциями</h2> <p>В селениде есть удобные методы для работы с коллекциями элементов: их можно удобно отфильтровать, в них можно искать элементы или разом проверить, скажем, тексты всех элементов.</p> <p>Но если слишком злоупотреблять, можно написать слишком медленный тест. Например, вот так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">list</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="c1">// На странице 100 элементов &lt;li&gt;</span> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="n">list</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Если на странице, предположим, 100 элементов <code class="language-plaintext highlighter-rouge">&lt;li&gt;</code>, то такой тест может очень долго работать.</p> <ul> <li>Ведь селенид на каждом шагу должен перегрузить элемент - вдруг его состояние изменилось!</li> <li>А чтобы перегрузить N-ный элемент коллекции, нужно перегрузить всю коллекцию (в селениуме ведь нет метода <code class="language-plaintext highlighter-rouge">WebDriver.findElement(index)</code>).</li> <li>А чтобы перегрузить фильтрованную коллекцию, нужно пройтись по всем её элементам и применить фильтр для каждого (в данном примере - вызвать <code class="language-plaintext highlighter-rouge">WebElement.isDisplayed()</code>).</li> </ul> <p><br /> Для сравнения, такой цикл работает гораздо быстрее:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">unfiltered</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">);</span> <span class="c1">// Нефльтрованная коллекция </span> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="n">unfiltered</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>В этом релизе мы немного ускорили работу с коллекциями. Теперь Селенид фильтрует коллекцию умнее: чтобы получить <code class="language-plaintext highlighter-rouge">list.get(N)</code>, он применяет фильтр <code class="language-plaintext highlighter-rouge">visible</code> не для всех 100 элементов, а только для первых <code class="language-plaintext highlighter-rouge">&lt;N&gt;</code> элементов.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1266">issue 1266</a> и <a href="https://github.com/selenide/selenide/pull/1270">PR 1270</a>.</p> <p><br /> P.S. Напомню, что другой вариант ускорить тесты в таких случаях - использовать метод <code class="language-plaintext highlighter-rouge">snapshot</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ElementsCollection</span> <span class="n">list</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"li"</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">visible</span><span class="o">).</span><span class="na">snapshot</span><span class="o">();</span> <span class="c1">// snapshot() создаёт "слепок" коллекции. Селенид не будет её перегружать каждый раз. </span> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="n">list</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">i</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Но при использовании <code class="language-plaintext highlighter-rouge">snapshot</code> есть риск словить <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code>, если коллекция всё-таки обновилась во время цикла.</p> <h2 id="добавили-проверку-href">Добавили проверку “href”</h2> <p>Иногда хочется в тесте проверить, что у элемента <code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> правильное значение атрибута <code class="language-plaintext highlighter-rouge">href</code>, т.е. ссылки.<br /> Для этого всегда можно было использовать метод <code class="language-plaintext highlighter-rouge">$("a").shouldHave(attribute("href", "/foo/bar/details.html")</code>.</p> <p>Проблема в том, что такая проверка очень неочевидно падает, потому что селениум возвращает абсолютный, а не относительный URL.<br /> Подробнее см. в докладе Алексея Баранцева <a href="http://www.youtube.com/watch?v=4dh--iD_zK8&amp;t=57m11s">“Заморочки в Selenium WebDriver”</a> с 57:11.</p> <p>Чтобы хоть немножко облегчить эту заморочку, мы добавили проверку “should have href” для ссылок:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="err">`$</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">href</span><span class="o">(</span><span class="s">"/foo/bar/details.html"</span><span class="o">))</span><span class="err">`</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1272">issue 1272</a> и <a href="https://github.com/selenide/selenide/pull/1273">PR 1273</a>.</p> <h2 id="добавили-опцию-хрома-no-sandbox">Добавили опцию хрома “–no-sandbox”</h2> <p>Я где-то вычитал, что она сделает тесты на хроме стабильнее. Скрестим пальцы и будем надеяться.</p> <p>См. <a href="https://github.com/selenide/selenide/commit/3293956d">commit 3293956d</a></p> <h2 id="селенид-теперь-явно-кидает-ошибку">Селенид теперь явно кидает ошибку</h2> <p>… если не удалось создать папку для скачивания файлов.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1265">issue 1265</a> и commit 94ece98f](https://github.com/selenide/selenide/commit/94ece98f).</p> <h2 id="обновились-на-webdrivermanager-422">Обновились на WebDriverManager 4.2.2</h2> <p>См. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p> <p><br /></p> <h2 id="видеообзор">Видеообзор</h2> <p>Смотрите <a href="https://youtu.be/txozPtQIqiE">видеообзор</a> данного релиза.</p> <h2 id="новости">Новости</h2> <p>Приходите 4-7 ноября 2020 на онлайн-конференцию <a href="https://heisenbug-moscow.ru/2020/msk/schedule/">Heisenbug</a>!</p> <p>Я там тоже буду выступать:</p> <ul> <li>Воркшоп “Как начать свой проект автоматизации с нуля (с божьей помощью и Selenide)” - для начинающих</li> <li>Доклад “Flaky tests. Метод” - для опытных</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/09/26/selenide-5.15.0/ http://ru.selenide.org/2020/09/26/selenide-5.15.0 2020-09-26T00:00:00+00:00 Вышла Selenide 5.14.0 <p>Всем привет!</p> <p>Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/101?closed=1">Selenide 5.14.0</a>.</p> <h2 id="стабилизировали-новый-способ-скачивания-файлов-folder">Стабилизировали новый способ скачивания файлов <code class="language-plaintext highlighter-rouge">FOLDER</code></h2> <p>… который появился в <a href="/2020/07/08/selenide-5.13.0/">Selenide 5.13.0</a>.</p> <p>Вот что поменялось в 5.14.0:</p> <ol> <li> <p>Каждый раз, когда селенид открывает браузер, он создаёт для него уникальную папку для скачиваний. Это помогает избежать ситуаций, когда параллельные тесты одновременно скачивают файлы в одну и ту же папку, и невозможно понять, где чей файл.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1220">issue 1220</a> и <a href="https://github.com/selenide/selenide/pull/1221">PR 1221</a>.</p> <ul> <li>Увы, это не работает для IE и Safari (которые в принципе не позволяют задать папку для скачивания файлов)</li> <li>Также это работает только для тех браузеров, которые открывает селенид.</li> <li>Если же вы сами открываете браузер и передаёте его селениду, вам нужно будет создать уникальную папку самостоятельно и передать её селениду: <ul> <li>Либо с помощью нового метода <code class="language-plaintext highlighter-rouge">setWebDriver(driver, proxy, downloadsFolder)</code>,</li> <li>либо конструктора <code class="language-plaintext highlighter-rouge">SelenideDriver(..., downloadsFolder)</code>.</li> </ul> </li> </ul> </li> <li>Перед началом каждого скачивания файла селенид очищает папку – см. <a href="https://github.com/selenide/selenide/pull/1252">PR 1252</a></li> <li>Селенид удаляет все пустые папки для скачиваний в конце тестов – см. <a href="https://github.com/selenide/selenide/pull/1247">PR 1247</a></li> </ol> <h2 id="добавили-проверку-shouldhaveitemwithtextany-text">Добавили проверку <code class="language-plaintext highlighter-rouge">$$.shouldHave(itemWithText("any text"))</code></h2> <p>В отличие от классической <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("text1", "text2"))</code>, она означает, что в коллекции есть хотя бы один элемент с данным текстом.</p> <p>Спасибо <a href="https://github.com/LuisOsv">Luis Serna</a> за <a href="https://github.com/selenide/selenide/pull/1194">PR 1194</a>.</p> <p>Кстати, это первый коммит в селенид аж из Боливии!</p> <h2 id="добавили-поддержку-браузера-safari">Добавили поддержку браузера Safari</h2> <p>Когда-то селенид поддерживал Safari, но тогда куча всего в нём не работало.<br /> В какой-то момент нам надоело с ним мучаться, и мы поддержку выпилили.<br /> Но сейчас попробовали новый подход. Вроде как завелось (не всё, конечно).</p> <p>Как обычно, достаточно просто прописать</p> <ol> <li><code class="language-plaintext highlighter-rouge">Configuration.browser = "safari";</code> либо</li> <li><code class="language-plaintext highlighter-rouge">-Dselenide.browser=safari</code></li> </ol> <p>Делитесь впечатлениями.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1236">issue 1236</a> и <a href="https://github.com/selenide/selenide/pull/1237">PR 1237</a>.</p> <h2 id="добавили-метод-selenidedriverscreenshotfilename">Добавили метод <code class="language-plaintext highlighter-rouge">SelenideDriver.screenshot(fileName)</code></h2> <p>Полезно, если вы создаёте “нестатический” вариант драйвера (<code class="language-plaintext highlighter-rouge">new SelenideDriver()</code>) и хотите снимать скриншоты.<br /> Теперь можно.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1166">issue 1166</a> и <a href="https://github.com/selenide/selenide/pull/1227">PR 1227</a>.</p> <h2 id="добавили-метод-selenidedriverscreenshotoutputtype">Добавили метод <code class="language-plaintext highlighter-rouge">SelenideDriver.screenshot(OutputType)</code></h2> <p>Иногда хочется получить скриншот в формате Base64. Например, этот формат хотят некоторые инструменты для сравниения скриншотов.</p> <p>Теперь их можно получить таким вызовом:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">screenshot</span> <span class="o">=</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">screenshot</span><span class="o">(</span><span class="nc">OutputType</span><span class="o">.</span><span class="na">BASE64</span><span class="o">);</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">decoded</span> <span class="o">=</span> <span class="nc">Base64</span><span class="o">.</span><span class="na">getDecoder</span><span class="o">().</span><span class="na">decode</span><span class="o">(</span><span class="n">screenshot</span><span class="o">);</span> <span class="nc">BufferedImage</span> <span class="n">img</span> <span class="o">=</span> <span class="nc">ImageIO</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="k">new</span> <span class="nc">ByteArrayInputStream</span><span class="o">(</span><span class="n">decoded</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1224">issue 1224</a> и <a href="https://github.com/selenide/selenide/pull/1231">PR 1231</a>.</p> <h2 id="теперь-селенид-снимает-скриншот-в-случае-падения-switchto">Теперь селенид снимает скриншот в случае падения <code class="language-plaintext highlighter-rouge">switchTo()</code></h2> <p>Как вы знаете, Селенид автоматически делает скриншот в случае падения тестов.<br /> Но мы обнаружили, что Селенид НЕ делал скриншот, если упал один из этих методов:</p> <ul> <li><code class="language-plaintext highlighter-rouge">switchTo(frame)</code></li> <li><code class="language-plaintext highlighter-rouge">switchTo(window)</code></li> <li><code class="language-plaintext highlighter-rouge">switchTo(alert)</code></li> </ul> <p>Теперь мы исправили эту досадную оплошность.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1190">issue 1190</a> и <a href="https://github.com/selenide/selenide/pull/1240">PR 1240</a>.</p> <h2 id="добавили-хрому-опцию---disable-dev-shm-usage">Добавили хрому опцию <code class="language-plaintext highlighter-rouge">--disable-dev-shm-usage</code></h2> <p>Мы тут вычитали, что без этой опции Chrome может крэшиться из-за out of memory error.</p> <ol> <li>Почему никто из вас на это не жаловался?</li> <li>Стало ли лучше после добавления это опции?</li> </ol> <p>P.S. Позже люди жаловались и на наличие этой опции. <a href="https://github.com/selenide/selenide/issues/1559">Эпопея</a> пока не окончена.</p> <h2 id="исправили-работу-sizzle-селекторов-на-страницах-с-dojojs-troopjs-и-тп">Исправили работу Sizzle селекторов на страницах с Dojo.js, troop.js и т.п.</h2> <p>См. <a href="https://github.com/selenide/selenide/issues/434">issue 434</a> и <a href="https://github.com/selenide/selenide/pull/1242">PR 1242</a>.</p> <h2 id="сделали-метод-tostring-безопаснее">Сделали метод <code class="language-plaintext highlighter-rouge">$.toString()</code> безопаснее</h2> <p>См. <a href="https://github.com/selenide/selenide/issues/1241">issue 1241</a> и <a href="https://github.com/selenide/selenide/pull/1245">PR 1245</a>.</p> <h2 id="улучшили-сообщение-об-ошибке-если-элемент-внезапно-пропал">Улучшили сообщение об ошибке, если элемент внезапно пропал</h2> <p>См. <a href="https://github.com/selenide/selenide/issues/1013">issue 1013</a> и <a href="https://github.com/selenide/selenide/pull/1239">PR 1239</a>.</p> <h2 id="обновились-на-webdrivermanager-410">Обновились на WebDriverManager 4.1.0</h2> <p>См. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p> <p><br /></p> <h2 id="видеообзор">Видеообзор</h2> <p>Смотрите <a href="https://youtu.be/x0KWgnjxsl4">видеообзор</a> данного релиза.</p> <h2 id="новости">Новости</h2> <ul> <li>Ничоси! Курс на Udemy “<a href="https://www.udemy.com/course/selenium-selenide-test-automation-engineer/">Selenium и Selenide для начинающих Automation QA, QC на Java</a>”</li> <li>Про селенид <a href="https://www.youtube.com/watch?v=WNzTuYFd8oI">на немецком</a></li> <li>Пример проекта <a href="https://github.com/d3m0/automation">на Selenide+Selenoid+Docker</a> от <a href="https://github.com/d3m0">d3m0</a></li> <li>Ещё пример: <a href="https://github.com/Crushpowerx/JavaMavenSelenideAllureScreenDiffExample">сравнение скриншотов с Selenide+Allure+Ashot+Screen Diff Plugin</a> от <a href="https://github.com/Crushpowerx/">Evgeniy Asovin</a></li> <li>Ещё пример: <a href="https://github.com/qaschevychelov/giphyTest">Selenide + Appium + Allure + TestNG</a> от <a href="https://github.com/qaschevychelov/">qaschevychelov</a></li> <li>Сравнение Selenide и Selenium 2019 года: <a href="https://www.appliedtech.ru/en/web-tools-for-ui-testing-selenium-or-selenide.html">Choosing tools for UI testing: Selenium or Selenide?</a> - 17.09.2019</li> <li>Статья Jakub Skibiński в блоге компании Sonalake: <a href="https://sonalake.com/latest/selenide-a-powerful-testing-framework/">Selenide: A Powerful Testing Framework</a> - 19.06.2020</li> <li>Статья “<a href="https://habr.com/ru/company/maxilect/blog/499810/">Почему мы перешли на Selenide, попутно написав более 200 новых автотестов</a>” - 30.04.2020</li> <li><a href="https://medium.com/@maxilect_pr/selenide-our-experience-11240f9ce10c">Switch from Serenity to Selenide</a> - 22.05.2020 от <a href="https://medium.com/@maxilect_pr">Yuri Kudryavtsev</a> (Maxilect company)</li> </ul> <p>И целая серия статей от <a href="https://medium.com/@alexspush">Alexander Pushkarev</a>:</p> <ul> <li><a href="https://medium.com/@alexspush/test-automation-framework-architecture-part-2-1-layered-architecture-example-62a0011d3329">Test automation framework architecture — Layered architecture example with vanilla JUnit + Selenide</a></li> <li><a href="https://medium.com/@alexspush/ui-automation-for-mortal-elegant-page-objects-with-java-and-selenide-3122b17dc473">UI Automation for mortals: elegant Page Objects with Java and Selenide</a></li> <li><a href="https://medium.com/@alexspush/an-alternative-to-ubiquitous-ui-level-checking-subcutaneous-tests-8d29e8883fc2">Effective test automation: subcutaneous tests as a faster alternative to Selenium-driven testing</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2020/08/17/selenide-5.14.0/ http://ru.selenide.org/2020/08/17/selenide-5.14.0 2020-08-17T00:00:00+00:00 Вышла Selenide 5.13.0 <p>Всем привет!</p> <p>Лето не повод расслабляться! Мы выпустили релиз <a href="https://github.com/selenide/selenide/milestone/98?closed=1">Selenide 5.13.0</a>.</p> <p>Будьте осторожны, он может сломать ваши тесты (если они были неаккуратно сделаны).</p> <ul class="blogpost-menu"> <li><a href="#should-have-empty-text">Запретили <code class="language-plaintext highlighter-rouge">shouldHave(text(""))</code></a></li> <li><a href="#remove-unneeded-allure-logs">Убрали лишние логи из аллюра</a></li> <li><a href="#improve-collection-error-messages">Сообщение об ошибках для коллекций</a></li> <li><a href="#fix-upload-without-form">Скачиваем файлы без <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code></a></li> <li><a href="#download-files-with-quotes">Скачиваем файлы с кавычками в имени</a></li> <li><a href="#write-webdriver-logs-to-file">Пишем логи вебдрайвера в файл</a></li> <li><a href="#new-file-download-mode-folder">Новый способ скачивания файлов <code class="language-plaintext highlighter-rouge">FOLDER</code></a></li> <li><a href="#get-wrapped-element-waits-for-element">Метод <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> снова ждёт появления элемента</a></li> <li><a href="#statistics">Статистика</a></li> </ul> <h2 id="should-have-empty-text">Метод <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> <p><strong>UPD</strong><br /> Позже мы добавили <a href="/2022/08/04/selenide-6.7.0/#holy-whole-string">настройку <code class="language-plaintext highlighter-rouge">FULL_TEXT</code></a>, заставляющую Selenide проверять строку целиком, на не подстроку.</p> <h2 id="remove-unneeded-allure-logs">Убрали лишние логи из аллюра</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="improve-collection-error-messages">Улучшили сообщение об ошибках для коллекций</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="fix-upload-without-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="download-files-with-quotes">Научились скачивать файлы с кавычками в имени</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="write-webdriver-logs-to-file">Научили вебдрайвер писать свои логи в файл</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="new-file-download-mode-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> <p>UPD Позже этот метод скачивания был доработан, и теперь его смело можно использовать в проектах.</p> <h2 id="get-wrapped-element-waits-for-element">Метод <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="upgrade-to-bup-2.1.1">Обновились до BrowserUpProxy 2.1.1</h2> <p>Ну вдруг.</p> <p>Обновляйтесь, пробуйте, делитесь впечатлениями!</p> <h2 id="statistics">Статистика</h2> <p>И моё любимое: статистика скачиваний селенида.<br /> Мы пробили потолок <strong>в 160 тысяч в месяц</strong>!</p> <center> <img src="/images/2020/07/selenide.downloads.png" width="800" /> </center> <p><br /> и <strong>31+ тысяча уникальных айпишников</strong>:</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> <ul class="blogpost-menu"> <li><a href="#add-shadow-dom-support">Добавили поддержку Shadow DOM</a></li> <li><a href="#exclude-bup-by-default">не тянем BrowserUpProxy по умолчанию</a></li> <li><a href="#replace-guava">Поменяли Guava API на Java API</a></li> <li><a href="#add-quotes-to-report">Селенидовский отчёт в Allure красивее</a></li> <li><a href="#add-image-condition">Добавили условие shouldBe(image)</a></li> <li><a href="#video">Видосики</a></li> </ul> <h2 id="add-shadow-dom-support">Добавили поддержку Shadow DOM</h2> <p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ShadowElementTest.java">в тестах</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"#anyButton"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"p"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Inside Shadow-DOM"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"p"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">,</span> <span class="s">"#inner-shadow-host"</span><span class="o">)).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"The Shadow-DOM inside another shadow tree"</span><span class="o">));</span> </code></pre></div></div> <p><em>Firefox</em>: Вызов <code class="language-plaintext highlighter-rouge">setValue("test")</code> / <code class="language-plaintext highlighter-rouge">val("text")</code> на input элементе выкидывает ошибку “not reachable by keyboard”. <br /> Как временное решение, можно использовать <code class="language-plaintext highlighter-rouge">fastSetValue=true</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">fastSetValue</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> <span class="err">$</span><span class="o">(</span><span class="n">shadowCss</span><span class="o">(</span><span class="s">"input"</span><span class="o">,</span> <span class="s">"#shadow-host"</span><span class="o">)).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"test"</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1014">issue 1014</a>.<br /> Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1090">PR 1090</a>.</p> <h2 id="exclude-bup-by-default">Selenide больше не тянет BrowserUpProxy по умолчанию</h2> <p>Изначально это предложил Алексей Баранцев, так что бейте его, если что. :)</p> <p>Мы сопоставили вместе два факта:</p> <ol> <li>Селенид по умолчанию тянет за собой BrowserUpProxy и его зависимости - всего ~17 мегабайт.</li> <li>Большинство пользователей (наверное) не использует селенидовский прокси.</li> </ol> <p>и решили обрадовать Грету Тумберг и не качать эти 17 мегабайт по умолчанию.</p> <h3 id="зависимость">Зависимость</h3> <p>Те из вас, кто использует прокси, просто должны добавить в свой проект ещё одну зависимость:</p> <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">testRuntime</span> <span class="s1">'com.browserup:browserup-proxy-core:2.0.1'</span> </code></pre></div></div> <p>(у многих из вас она и так уже есть).</p> <p>Если вы забудете добавить зависимость - не беспокойтесь, вы увидите понятное сообщение:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">IllegalStateException</span><span class="o">:</span> <span class="nc">Cannot</span> <span class="n">initialize</span> <span class="n">proxy</span><span class="o">.</span> <span class="nc">Probably</span> <span class="n">you</span> <span class="n">should</span> <span class="n">add</span> <span class="nc">BrowserUpProxy</span> <span class="n">dependency</span> <span class="n">to</span> <span class="n">your</span> <span class="n">project</span><span class="o">.</span> <span class="n">at</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">drivercommands</span><span class="o">.</span><span class="na">CreateDriverCommand</span><span class="o">.</span><span class="na">createDriver</span><span class="o">(</span><span class="nc">CreateDriverCommand</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">44</span><span class="o">)</span> <span class="o">...</span> <span class="n">at</span> <span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="nc">Selenide</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">41</span><span class="o">)</span> <span class="n">caused</span> <span class="nl">by:</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">NoClassDefFoundError</span><span class="o">:</span> <span class="n">com</span><span class="o">/</span><span class="n">browserup</span><span class="o">/</span><span class="n">bup</span><span class="o">/</span><span class="nc">BrowserUpProxy</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1021">issue 1021</a> и <a href="https://github.com/selenide/selenide/pull/1094">PR 1094</a>.</p> <p><strong>UPD 12.03.2023</strong> В наше время больше не нужно добавлять ту зависимость <code class="language-plaintext highlighter-rouge">browserup-proxy-core</code>. Просто используйте <code class="language-plaintext highlighter-rouge">com.codeborne:selenide-proxy</code> вместо <code class="language-plaintext highlighter-rouge">com.codeborne:selenide</code>, и все нужные зависимости подтянутся автоматически.</p> <h3 id="просто-из-интереса">Просто из интереса</h3> <p>Кто же там сжирает эти 17 мегабайт, спросите вы? А вот кто.<br /> Вот полный список файлов, которые должны у вас пропасть из проекта. Список впечатляет, правда?</p> <ul> <li>animal-sniffer-annotations-1.17.jar</li> <li>barchart-udt-bundle-2.3.0.jar</li> <li>bcpkix-jdk15on-1.62.jar</li> <li>bcprov-jdk15on-1.62.jar</li> <li>browserup-proxy-core-2.0.1.jar</li> <li>browserup-proxy-mitm-2.0.1.jar</li> <li>checker-qual-2.5.2.jar</li> <li>dec-0.1.2.jar</li> <li>dnsjava-2.1.9.jar</li> <li>error_prone_annotations-2.2.0.jar</li> <li>failureaccess-1.0.1.jar</li> <li>guava-27.1-jre.jar</li> <li>jackson-annotations-2.9.9.jar</li> <li>jackson-core-2.9.9.jar</li> <li>jackson-databind-2.9.9.1.jar</li> <li>javassist-3.25.0-GA.jar</li> <li>javax.activation-api-1.2.0.jar</li> <li>jaxb-api-2.3.1.jar</li> <li>jcl-over-slf4j-1.7.28.jar</li> <li>jsr305-3.0.2.jar</li> <li>jzlib-1.1.3.jar</li> <li>listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar</li> <li>littleproxy-2.0.0-beta-5.jar</li> <li>netty-all-4.1.39.Final.jar</li> </ul> <p>Кто придумал название “animal-sniffer”? Это что вообще такое - <em>нюхальщик животных</em>? <br /></p> <h2 id="replace-guava">Поменяли Guava API на соответствующие Java API</h2> <p>Мы просто поменяли</p> <ul> <li><code class="language-plaintext highlighter-rouge">com.google.common.base.Predicate</code> из Guava</li> <li>на <code class="language-plaintext highlighter-rouge">java.util.function.Predicate</code> из Java 8</li> </ul> <p>и выкинули Guava. Guava, ты была хороша и сделала много полезного (пока не вышла Java 8). Покойся с миром.</p> <p>Если вы реализовали свои <code class="language-plaintext highlighter-rouge">CollectionCondition</code>, вам придётся метод <code class="language-plaintext highlighter-rouge">apply</code> переименовать в <code class="language-plaintext highlighter-rouge">test</code>. Это должно быть легко.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1091">issue 1091</a>.<br /> Спасибо <a href="https://github.com/wlsc">Wladimir Schmidt</a> за <a href="https://github.com/selenide/selenide/pull/1091">PR 1091</a>.</p> <p><br /></p> <h2 id="add-quotes-to-report">Сделали селенидовский отчёт в Allure чуточку красивее</h2> <p>На самом деле просто добавили кавычки вокруг селекторов.<br /> Не представляю, зачем это может понадобиться, но теперь можно копировать селекторы из аллюровского отчёта и вставлять в developer console браузера, и они будут работать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1032">issue 1032</a>.<br /> Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1092">PR 1092</a>.</p> <p><br /></p> <h2 id="add-image-condition">Добавили условие <code class="language-plaintext highlighter-rouge">$("img").shouldBe(image)</code></h2> <p>Позволяет проверить, что картинка есть, она загрузилась и всё в порядке.</p> <p>См. примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ImageTest.java">в тестах</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#valid-image img"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">image</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#valid-image"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">image</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldNotBe</span><span class="o">(</span><span class="n">image</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/1069">issue 1069</a>.<br /> Спасибо <a href="https://github.com/dstekanov">Dmytro Stekanov</a> за <a href="https://github.com/selenide/selenide/pull/1086">PR 1086</a>.</p> <p><br /></p> <h2 id="fix-search-by-attribute-with-quotes">Исправили поиск элементов по атрибуту, который содержит кавычки</h2> <p>Я не знаю, каким надо быть извращенцем, чтобы в html атрибут запихать кавычки, но такие нашлись. А селенид оказался к этому не готов и генерировал невалидный CSS локатор. Теперь это в прошлом.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1060">issue 1060</a>.<br /> Спасибо <a href="https://github.com/denysLystopadskyy">Denys Lystopadskyy</a> за <a href="https://github.com/selenide/selenide/pull/1062">PR 1062</a>.</p> <p><br /></p> <h2 id="video">Видосики</h2> <p>Немногие задумывались об этом, но возможно, SeleniumCamp 2020 - Последняя Конференция Человечества.</p> <p>Но они ещё и последние альтруисты человечества, потому что из-за карантина они досрочно выложили <a href="https://www.youtube.com/playlist?list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe">все видео с последней конференции</a> в открытый доступ.<br /> Лечитесь, айтишники, и просвещайтесь!</p> <p>Там есть три моих доклада:</p> <ul> <li><a href="https://www.youtube.com/watch?v=6MfMtky-0q4&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/#add-method-using">добавили метод <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> <p><strong>UPD</strong><br /> Ниже описаны только два способа скачивания - <a href="#HTTPGET"><code class="language-plaintext highlighter-rouge">HTTPGET</code></a> и <a href="#PROXY"><code class="language-plaintext highlighter-rouge">PROXY</code></a>. Позже появились <a href="/2020/07/08/selenide-5.13.0/#new-file-download-mode-folder">третий способ <code class="language-plaintext highlighter-rouge">FOLDER</code></a> и <a href="/2024/02/07/selenide-7.1.0/#download-files-with-cdp">четвёртый способ <code class="language-plaintext highlighter-rouge">CDP</code></a>. Возможно, вам нужен как раз один из них, если у вашей ссылки нет атрибута <code class="language-plaintext highlighter-rouge">href</code>, и прокси у вас не заводится.</p> <p><br /></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="HTTPGET">А в 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="PROXY">НО:</h3> <p>Таким образом можно скачивать файлы только со ссылкой с атрибутом “href”.</p> <p>Но что, если у меня ссылки с атрибутом “href”? Так бывает, например, когда файл скачивается в результате сабмита формы. В этом случае можно скачивать файлы с помощью встроенного в селенид прокси-сервера.</p> <p>Для начала нам нужно включить его (т.к. он по умолчанию выключен):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">fileDownload</span> <span class="o">=</span> <span class="no">PROXY</span><span class="o">;</span> </code></pre></div></div> <p>После этого мы снова можем вызывать метод <code class="language-plaintext highlighter-rouge">$.download()</code>, но теперь он стал более могущественным и не требует наличия атрибута “href”:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">report</span> <span class="o">=</span> <span class="n">element</span><span class="o">.</span><span class="na">download</span><span class="o">();</span> </code></pre></div></div> <h3 id="хозяйке-на-заметку">Хозяйке на заметку:</h3> <p>Не забудьте увеличить таймаут, если собираетесь скачивать файл большого размера.</p> <p>Файл будет скачан в папку по умолчанию (что-то типа <code class="language-plaintext highlighter-rouge">C:\downloads and settings\downloads</code>). <br /> Таким образом, скачанный файл окажется в двух местах: <code class="language-plaintext highlighter-rouge">c:\downloads...</code> и <code class="language-plaintext highlighter-rouge">build/reports/tests</code>.</p> <p>Если это для вас проблема, можете в конце теста удалить ненужную папку, чтобы очистить место на диске:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">FileUtils</span><span class="o">.</span><span class="na">deleteDirectory</span><span class="o">(</span><span class="k">new</span> <span class="nc">File</span><span class="o">(&lt;</span><span class="n">папка</span><span class="o">,</span> <span class="n">подлежащая</span> <span class="n">удалению</span><span class="o">&gt;));</span> </code></pre></div></div> <p>Узнать подробнее про механизмы скачивания файлов можно <a href="https://ru.selenide.org/2016/08/27/selenide-3.9.1/">тут</a>.</p> <p>Maciej Grymuza (figrym@gmail.com)</p> http://ru.selenide.org/2019/12/10/advent-calendar-download-files/ http://ru.selenide.org/2019/12/10/advent-calendar-download-files 2019-12-10T00:00:00+00:00 Почему статики запретили, а потом разрешили? <p>Привет!</p> <p>9 день рождественского календаря Selenide.<br /> Расскажу о том, что сильнее всего волнует общественность.</p> <h1 id="почему-статики-запретили-в-500-а-потом-снова-разрешили">Почему статики запретили в 5.0.0, а потом снова разрешили?</h1> <p>Короткий ответ: запретили случайно. Но правильно сделали. </p> <p>А теперь подробнее. </p> <h3 id="коротко-о-хранении-вебдрайверов-в-селениде">Коротко о хранении вебдрайверов в селениде</h3> <p>Селенид изначально хранил вебдрайверы в ThreadLocal.<br /> Это позволяет запускать тесты параллельно: в разных потоках - разные вебдрайверы. Код выглядит примерно так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">WebDriverRunner</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o">&lt;</span><span class="nc">WebDriver</span><span class="o">&gt;</span> <span class="n">webdriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">&lt;&gt;();</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o">&lt;</span><span class="nc">SelenideProxyServer</span><span class="o">&gt;</span> <span class="n">proxy</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">&lt;&gt;();</span> <span class="o">}</span> </code></pre></div></div> <p>Скажем, метод <code class="language-plaintext highlighter-rouge">$("a").click()</code> работает примерно так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">webdriver</span><span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">findElement</span><span class="o">(</span><span class="s">"a"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <h2 id="коротко-про-selenidedriver">Коротко про <code class="language-plaintext highlighter-rouge">SelenideDriver</code></h2> <p>Но ThreadLocal накладывает ограничение: ты не можешь использовать в одном потоке два вебдрайвера (а также в двух потоках - один и тот же вебдрайвер, но до этого мы пока не дошли).</p> <p>Это мы и собирались решить в Selenide 5.0.0. Мы сделали специальный класс <code class="language-plaintext highlighter-rouge">SelenideDriver</code>, позволяющий использовать два вебдрайвера в одном тесте:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideDriver</span> <span class="n">browser1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SelenideDriver</span><span class="o">();</span> <span class="nc">SelenideDriver</span> <span class="n">browser2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SelenideDriver</span><span class="o">();</span> <span class="n">browser1</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"https://google.com"</span><span class="o">);</span> <span class="n">browser2</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"http://yandex.ru"</span><span class="o">);</span> <span class="n">browser1</span><span class="o">.</span><span class="err">$</span><span class="o">(</span><span class="n">h1</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Google"</span><span class="o">));</span> <span class="n">browser2</span><span class="o">.</span><span class="err">$</span><span class="o">(</span><span class="n">h1</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Yandeks"</span><span class="o">));</span> </code></pre></div></div> <p>Для этого пришлось сделать большой рефакторинг, так чтобы внутри самого селенида нигде не использовались старые добрые статические методы <code class="language-plaintext highlighter-rouge">open()</code>, <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code>. Везде, где нужен вебдрайвер, селенид теперь использовал параметр <code class="language-plaintext highlighter-rouge">SelenideDriver</code>. Да-да, пришлось его прокидывать во все методы как параметр.</p> <h2 id="и-вот-тут-главный-вопрос">И вот тут главный вопрос</h2> <p>А что делать со старыми добрыми статическими методами <code class="language-plaintext highlighter-rouge">open()</code>, <code class="language-plaintext highlighter-rouge">$</code> и <code class="language-plaintext highlighter-rouge">$$</code>? Они должны откуда-то взять инстанс <code class="language-plaintext highlighter-rouge">SelenideDriver</code> и передать его дальше в кишки селенида. Откуда его взять?</p> <h2 id="selenide-500-статикам-наносят-удар">Selenide 5.0.0: Статикам наносят удар</h2> <p>На тот момент этот вопрос решили так (как оказалось, это решение сильно повлияло на ход дел). <br /> В класс <code class="language-plaintext highlighter-rouge">WebDriverRunner</code> добавили третий ThreadLocal:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">ThreadLocal</span><span class="o">&lt;</span><span class="nc">SelenideDriver</span><span class="o">&gt;</span> <span class="n">selenideDriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocal</span><span class="o">&lt;&gt;();</span> </code></pre></div></div> <p>Сам <code class="language-plaintext highlighter-rouge">SelenideDriver</code> - это, грубо говоря, очень простой класс с двумя полями <code class="language-plaintext highlighter-rouge">WebDriver</code> и <code class="language-plaintext highlighter-rouge">SelenideProxyServer</code>.<br /> И в целом всё хорошо работало и решало поставленную задачу.</p> <p>Чего я не мог предусмотреть на тот момент - это того, что многие люди определяли веб-элементы в <strong>статических</strong> полях:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyPageObject</span> <span class="o">{</span> <span class="kd">public</span> <span class="kd">static</span> <span class="nc">SelenideElement</span> <span class="n">fname</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#fname"</span><span class="o">);</span> <span class="kd">public</span> <span class="kd">static</span> <span class="nc">SelenideElement</span> <span class="n">lname</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#lname"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Да ещё и переоткрывали браузер между тестами.</p> <h3 id="важный-нюанс">Важный нюанс:</h3> <p>в Selenide 5.0.0 мы сделали ещё одно улучшение.<br /> Раньше селенид автоматически открывал новый браузер, если его ещё нет, что часто вызывало недоумение, ибо браузер иногда открывался в очень неожиданные моменты (например, при попытке залогировать ошибку, произошедшую из-за закрэшившегося браузера).</p> <p>Конечно, это происходило из-за плохого кода тестов, но <em>мы же создали селенид, чтобы помогать людям, верно?</em><br /> Поэтому с версии 5.0.0 селенид стал чётко говорить: “Браузера нет, работать не могу. Вызови сначала метод <code class="language-plaintext highlighter-rouge">open()</code>, тогда поговорим.”</p> <h3 id="и-что-же-свалилось">И что же свалилось?</h3> <p>Совпадение этих двух факторов привело к следующей проблеме:</p> <ol> <li>Человек создаёт статическую переменную <code class="language-plaintext highlighter-rouge">static SelenideElement fname = $("#fname")</code>.</li> <li>Этот <code class="language-plaintext highlighter-rouge">fname</code> запоминает, с каким инстансом <code class="language-plaintext highlighter-rouge">SelenideDriver</code> он был создан.</li> <li>Человек в конце теста закрывает браузер</li> <li>В следующем тесте человек открывает новый браузер и снова обращается к статическому полю <code class="language-plaintext highlighter-rouge">fname</code>.</li> <li>А <code class="language-plaintext highlighter-rouge">fname</code> обращается к своему <code class="language-plaintext highlighter-rouge">SelenideDriver</code>, с которым он был изначально создан.</li> <li>И кидает ошибку, потому что тот браузер уже закрылся.</li> </ol> <p>Больше года - с сентября 2018 до октября 2019 - я пытался объяснить всем, что статические переменные - это зло, не надо их использовать. <br /> Сделал даже специально доклад <a href="https://www.youtube.com/watch?v=4JJNccWtdNI">“Антистатик”</a>.</p> <p>Но в итоге сдался из тех соображений, что слишком много людей, которым пришлось бы переписывать свои тесты. Даже если они согласны насчёт злостности статических переменных, переписать всё - это зачастую неподъёмная задача.<br /> <em>Мы же создали селенид, чтобы помогать людям, верно?</em></p> <h2 id="selenide-540-статики-победили">Selenide 5.4.0: Статики победили</h2> <p>Как в итоге решили эту проблему?</p> <p>Довольно просто. Мы заменили <code class="language-plaintext highlighter-rouge">ThreadLocal&lt;SelenideDriver&gt;</code> на статическую переменную (да, вы не ослышались :))</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">SelenideDriver</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadLocalSelenideDriver</span><span class="o">();</span> </code></pre></div></div> <p>Теперь наш “статический” <code class="language-plaintext highlighter-rouge">SelenideDriver</code> - один на всех. Он никогда не закрывается. Все статические поля, созданные с ним, будут жить вечно. Но он и не хранит в себе поля <code class="language-plaintext highlighter-rouge">WebDriver</code> и <code class="language-plaintext highlighter-rouge">SelenideProxyServer</code>. Он каждый берёт их из тредлокалов <code class="language-plaintext highlighter-rouge">WebDriverRunner</code>.</p> <h3 id="ps">P.S.</h3> <p>Поэтому в версии 5.4.0 пропал метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.getSelenideDriver()</code>.</p> <p>Я очень удивился, что многие люди уже успели его использовать. Люди, я вас не понимаю! <em>Как вы умудряетесь всё так неправильно использовать?</em><br /> Ок, я не подумав, сделал его публичным. Моя ошибка. Но он не упомянут ни в каких пресс-релизах, ни в какой документации. Как могла кому-то прийти в голову идея его использовать? Как эта магия вообще происходит?</p> <h2 id="что-теперь">Что теперь?</h2> <p>Мы снова вернули возможность объявлять <code class="language-plaintext highlighter-rouge">SelenideElement</code> в статических полях.<br /> Но пожалуйста, не злоупотребляйте этим. <br /> Я всё ещё это не одобряю. :)</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/09/advent-calendar-statics/ http://ru.selenide.org/2019/12/09/advent-calendar-statics 2019-12-09T00:00:00+00:00 Как протестировать защиту от CSRF атаки <p>Привет!</p> <p>Сегодня 7 день рождественского календаря Selenide.<br /> И сегодня мы поговорим о тестировании безопасности.</p> <h1 id="что-такое-csrf">Что такое CSRF?</h1> <p>Одна из самых распространённых атак - это CSRF (Cross-Site Request Forgery), или подделка межсайтовых запросов. Подробно о ней я рассказывал в видосике <a href="https://www.youtube.com/watch?v=z-aEjd22BGU">WTF Security</a>, а сейчас для нас важно то, что защиту от этой атаки легко протестировать вашими обычными автотестами.</p> <p>Для того, чтобы веб-приложение было защищено от CSRF-атак, с каждым его POST-запросом должен посылаться один хитрый параметр. Он обычно называется <code class="language-plaintext highlighter-rouge">authenticityToken</code> (хотя и не обязательно). Когда вы заходите в одной вкладке, скажем, в свой интернет-банк, а в другой вкладке на сайт с котиками, зловредный код на этом сайте может послать POST-запрос вашему банку для совершения платежа на счёт хакера. Хакер может послать счёт и сумму, а также все cookies из вашей вкладки, но он не сможет послать <code class="language-plaintext highlighter-rouge">authenticityToken</code> (потому, что он уникальный для каждой сессии и не хранится в cookies).</p> <p>А типичная ошибка такая: веб-приложение либо не посылает <code class="language-plaintext highlighter-rouge">authenticityToken</code> на сервер с каким-то POST-запросом, либо на сервере не проверяет пришедший токен.</p> <p><br /></p> <h3 id="короче-как-проверить-защищённость">Короче, как проверить защищённость?</h3> <p>У вас уже есть куча автотестов, покрывающих весь критичный функционал вашего веб-приложения.<br /> Мы убьём двух зайцев разом: во время запуска этих тестов мы будем перехватывать каждый POST-запрос и посылать точно такой же, но с изменённым <code class="language-plaintext highlighter-rouge">authenticityToken</code>. И будем проверять, что сервер вернул ошибку. Обычно это ошибка 403 Forbidden.</p> <p><br /></p> <h3 id="звучит-сложно-как-это-закодить">Звучит сложно. Как это закодить?</h3> <p>Не так уж сложно. <br /> Как вы знаете, селенид может запускать свой встроенный прокси-сервер. Изначально он использовался для скачивания файлов, но к нему можно добавлять и свои “листенеры”, которые могут перехватывать все запросы между браузером и тестируемым приложением.<br /> Это мы и сделаем.</p> <p><br /></p> <h4 id="шаг-1-включаем-селенидовский-прокси-сервер">Шаг 1. Включаем селенидовский прокси-сервер</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Configuration</span><span class="o">.</span><span class="na">proxyEnabled</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> </code></pre></div></div> <p>(это нужно сделать ДО открытия браузера)</p> <p><br /></p> <h4 id="шаг-2-добавляем-листенер-для-прокси-сервера">Шаг 2. Добавляем листенер для прокси-сервера</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">BaseTest</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">AuthenticityTokenChecker</span> <span class="n">authenticityTokenChecker</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AuthenticityTokenChecker</span><span class="o">();</span> <span class="c1">// и где-то сразу после open("http://..."):</span> <span class="n">getSelenideProxy</span><span class="o">().</span><span class="na">getProxy</span><span class="o">().</span><span class="na">addRequestFilter</span><span class="o">(</span><span class="n">authenticityTokenChecker</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>На данный момент это можно сделать только ПОСЛЕ открытия браузера, что иногда не очень удобно.<br /> Я надеюсь, в следующей версии селенида мы сможем сделать так, чтобы листенеры для прокси-сервера можно было добавлять в любой момент.</p> <p><br /></p> <h4 id="шаг-3-реализуем-authenticitytokenchecker">Шаг 3. Реализуем AuthenticityTokenChecker</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">com.codeborne.selenide.Configuration</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">io.netty.handler.codec.http.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">net.lightbody.bmp.filters.*</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">net.lightbody.bmp.util.*</span><span class="o">;</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">AuthenticityTokenChecker</span> <span class="kd">implements</span> <span class="nc">RequestFilter</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">HttpClient</span> <span class="n">httpClient</span> <span class="o">=</span> <span class="nc">HttpClient</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">build</span><span class="o">();</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">unprotectedUrls</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;(</span><span class="mi">1</span><span class="o">);</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">reset</span><span class="o">()</span> <span class="o">{</span> <span class="n">unprotectedUrls</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span> <span class="o">}</span> <span class="kd">public</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="nf">getUnprotectedUrls</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">unprotectedUrls</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">HttpResponse</span> <span class="nf">filterRequest</span><span class="o">(</span><span class="nc">HttpRequest</span> <span class="n">httpRequest</span><span class="o">,</span> <span class="nc">HttpMessageContents</span> <span class="n">contents</span><span class="o">,</span> <span class="nc">HttpMessageInfo</span> <span class="n">httpMessageInfo</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">httpRequest</span><span class="o">.</span><span class="na">getMethod</span><span class="o">()</span> <span class="o">!=</span> <span class="nc">HttpMethod</span><span class="o">.</span><span class="na">POST</span><span class="o">)</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// игнорируем не-POST запросы</span> <span class="k">if</span> <span class="o">(!</span><span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="nc">Configuration</span><span class="o">.</span><span class="na">baseUrl</span><span class="o">))</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// игнорируем запросы хрома к google.com и подобным ресурсами</span> <span class="k">if</span> <span class="o">(</span><span class="nc">Этому</span> <span class="n">урлу</span> <span class="n">разрешено</span> <span class="n">и</span> <span class="n">без</span> <span class="n">токена</span><span class="o">)</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// некоторым post-запросам не требуется защита</span> <span class="nc">String</span> <span class="n">body</span> <span class="o">=</span> <span class="n">contents</span><span class="o">.</span><span class="na">getTextContents</span><span class="o">();</span> <span class="k">if</span> <span class="o">(!</span><span class="n">body</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"authenticityToken="</span><span class="o">))</span> <span class="o">{</span> <span class="n">unprotectedUrls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"No 'authenticityToken=' found for "</span> <span class="o">+</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">()</span> <span class="o">+</span> <span class="s">" in "</span> <span class="o">+</span> <span class="n">body</span><span class="o">);</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="n">sendHackedPostRequest</span><span class="o">(</span><span class="n">httpRequest</span><span class="o">,</span> <span class="n">contents</span><span class="o">);</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Обратите внимание: <code class="language-plaintext highlighter-rouge">return null;</code> значит “не изменяй запрос”. То есть браузер пошлёт изначальный запрос на сервер без изменений, и нормальное течение вашего теста не будет нарушено.</p> <p><br /></p> <h4 id="шаг-4-посылаем-хакнутый-post-запрос">Шаг 4. Посылаем хакнутый POST-запрос</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">sendHackedPostRequest</span><span class="o">(</span><span class="nc">HttpRequest</span> <span class="n">httpRequest</span><span class="o">,</span> <span class="nc">HttpMessageContents</span> <span class="n">contents</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">InterruptedException</span> <span class="o">{</span> <span class="c1">// Над этой строчкой придётся поработать. </span> <span class="c1">// Формат запроса (и даже имя параметра "authenticityToken") может зависеть от вашего приложения.</span> <span class="c1">// Обратите внимание, что параметров "authenticityToken" может быть несколько (сразу кидайте ошибку, если они разные).</span> <span class="c1">// Если в POST-запросе сабмитится форма, да ещё и с файлами, параметр "authenticityToken" придётся выцепить немножко по-другому. </span> <span class="nc">String</span> <span class="n">hackedBody</span> <span class="o">=</span> <span class="n">contents</span><span class="o">.</span><span class="na">getTextContents</span><span class="o">()</span> <span class="o">.</span><span class="na">replace</span><span class="o">(</span><span class="s">"authenticityToken=1234567890"</span><span class="o">).</span><span class="na">на</span><span class="o">(</span><span class="s">"authenticityToken=hack-me-if-you-can"</span><span class="o">);</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span><span class="o">.</span><span class="na">Builder</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">()</span> <span class="o">.</span><span class="na">uri</span><span class="o">(</span><span class="no">URI</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">()))</span> <span class="o">.</span><span class="na">timeout</span><span class="o">(</span><span class="nc">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span> <span class="k">for</span> <span class="o">(</span><span class="nc">Map</span><span class="o">.</span><span class="na">Entry</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</span> <span class="n">header</span> <span class="o">:</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">headers</span><span class="o">())</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(!</span><span class="n">restrictedHeaders</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">header</span><span class="o">.</span><span class="na">getKey</span><span class="o">().</span><span class="na">toLowerCase</span><span class="o">()))</span> <span class="o">{</span> <span class="n">builder</span><span class="o">.</span><span class="na">header</span><span class="o">(</span><span class="n">header</span><span class="o">.</span><span class="na">getKey</span><span class="o">(),</span> <span class="n">header</span><span class="o">.</span><span class="na">getValue</span><span class="o">());</span> <span class="o">}</span> <span class="o">}</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span> <span class="n">request</span> <span class="o">=</span> <span class="n">builder</span> <span class="o">.</span><span class="na">POST</span><span class="o">(</span><span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpRequest</span><span class="o">.</span><span class="na">BodyPublishers</span><span class="o">.</span><span class="na">ofString</span><span class="o">(</span><span class="n">hackedBody</span><span class="o">))</span> <span class="o">.</span><span class="na">build</span><span class="o">();</span> <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Sending hacked request to {}"</span><span class="o">,</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpResponse</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">httpResponse</span> <span class="o">=</span> <span class="n">httpClient</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">java</span><span class="o">.</span><span class="na">net</span><span class="o">.</span><span class="na">http</span><span class="o">.</span><span class="na">HttpResponse</span><span class="o">.</span><span class="na">BodyHandlers</span><span class="o">.</span><span class="na">ofString</span><span class="o">());</span> <span class="k">if</span> <span class="o">(</span><span class="n">httpResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">()</span> <span class="o">==</span> <span class="mi">403</span><span class="o">)</span> <span class="o">{</span> <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Hacked request was rejected: {} {}"</span><span class="o">,</span> <span class="n">httpResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">(),</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"HACK SUCCEEDED {} {}"</span><span class="o">,</span> <span class="n">httpResponse</span><span class="o">.</span><span class="na">statusCode</span><span class="o">(),</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span> <span class="n">unprotectedUrls</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"Detected URL without authenticity token check: "</span> <span class="o">+</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getUri</span><span class="o">());</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Set</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">restrictedHeaders</span> <span class="o">=</span> <span class="nc">Set</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"connection"</span><span class="o">,</span> <span class="s">"content-length"</span><span class="o">,</span> <span class="s">"date"</span><span class="o">,</span> <span class="s">"expect"</span><span class="o">,</span> <span class="s">"from"</span><span class="o">,</span> <span class="s">"host"</span><span class="o">,</span> <span class="s">"upgrade"</span><span class="o">,</span> <span class="s">"via"</span><span class="o">,</span> <span class="s">"warning"</span><span class="o">);</span> </code></pre></div></div> <p>Конкретно эта реализация использует <code class="language-plaintext highlighter-rouge">HttpClient</code> из Java 11, но если вы из тех бедолаг, что до сих пор сидят на Java 8, вы можете заменить его на OkHttp, Apache Http Client или что-то подобное.</p> <p><br /></p> <h4 id="шаг-5-валим-тест-если-нашлись-незащищённые-запросы">Шаг 5. Валим тест, если нашлись незащищённые запросы</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">BaseTest</span> <span class="o">{</span> <span class="nd">@Before</span> <span class="kt">void</span> <span class="nf">resetChecker</span><span class="o">()</span> <span class="o">{</span> <span class="n">authenticityTokenChecker</span><span class="o">.</span><span class="na">reset</span><span class="o">();</span> <span class="o">}</span> <span class="nd">@After</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">verifyThatAllPostRequestsAreProtectedWithAuthenticityToken</span><span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(!</span><span class="n">authenticityTokenChecker</span><span class="o">.</span><span class="na">getUnprotectedUrls</span><span class="o">().</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span> <span class="n">fail</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">authenticityTokenChecker</span><span class="o">.</span><span class="na">getUnprotectedUrls</span><span class="o">()));</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p><br /></p> <h2 id="что-теперь">Что теперь?</h2> <p>Мы убили двух зайцев и научились автоматически проверять защиту от CSRF-атак при запуске наших обычных автотестов.<br /> Это не фантазия, мы реально так сделали на одном проекте и нашли две серьёзных уязвимости в настоящем интернет-банке.</p> <p>Хорошо, но этого мало. На свете ещё куча атак.</p> <p>Пересмотрите <a href="https://www.youtube.com/watch?v=z-aEjd22BGU">WTF Security</a>, почитывайте <a href="https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project">OWASP 10</a> и мыслите креативно, а как ещё можно убить третьего и четвёртого зайца вашими автотестами.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/07/advent-calendar-csrf-protection/ http://ru.selenide.org/2019/12/07/advent-calendar-csrf-protection 2019-12-07T00:00:00+00:00 Как визуализировать клик <p>Привет!</p> <p>На дворе 6 декабря, мы продолжаем рождественский календарь Selenide.<br /> И сегодня я покажу один простой способ поймать некоторые нестабильные тесты.</p> <h1 id="что-не-так-с-кликами">Что не так с кликами?</h1> <p>Вечная проблема - флейки тесты (моргающие, нестабильные).<br /> Одна из типичных причин - клик не срабатывает.<br /> Как я подробно рассказывал <a href="https://www.youtube.com/watch?v=ibx8nVvt-Js">в этом видео</a> на DelEx 2019, клик в селениуме часто не срабатывает потому, что элемент в этот момент двигался, растягивался, сжимался, или внезапно появился какой-то другой элемент и перекрыл наш. И клик попал в него.</p> <h3 id="почему-селенид-это-уже-не-решил">Почему селенид это уже не решил?</h3> <p>Глобальное решение могло бы быть такое: метод <code class="language-plaintext highlighter-rouge">$.click()</code>, перед тем, как кликнуть, сначала ждёт окончания любых движений и анимаций. Но пока в селениде такого нет: я боюсь, что сделать абсолютно универсальную “ожидалку” на все случаи жизни просто невозможно. Проекты разные, UI разный, фреймворки разные, анимации разные. Если у вас есть идеи, как это можно было бы реализовать - обращайтесь, запилим.</p> <p>А пока нам остаётся делать какие-то свои “ожидалки” в каждом конкретном проекте.</p> <p>А я хочу показать способ, как узнать, куда клик попал на самом деле. Это не решит проблему полностью, но хотя бы поможет её локализовать.</p> <h3 id="как-визулизировать-клик">Как визулизировать клик?</h3> <p>Можно добавить такой вот JS код в своё приложение. Он слушает любые клики на странице, и подсвечивает зелёной рамкой тот элемент, которые реально был кликнут. Это поможет вам на скриншоте упавшего теста понять, что клик попал не в тот элемент, который вам был нужен.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">#</span><span class="o">{</span><span class="k">if</span> <span class="n">test</span><span class="o">}</span> <span class="o">&lt;</span><span class="n">script</span><span class="o">&gt;</span> <span class="n">function</span> <span class="nf">onClick</span><span class="o">(</span><span class="n">event</span><span class="o">)</span> <span class="o">{</span> <span class="kt">var</span> <span class="n">e</span> <span class="o">=</span> <span class="n">event</span> <span class="o">||</span> <span class="n">window</span><span class="o">.</span><span class="na">event</span><span class="o">;</span> <span class="kt">var</span> <span class="n">target</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">target</span> <span class="o">||</span> <span class="n">e</span><span class="o">.</span><span class="na">srcElement</span><span class="o">;</span> <span class="n">target</span><span class="o">.</span><span class="na">style</span><span class="o">[</span><span class="err">'</span><span class="n">box</span><span class="o">-</span><span class="n">sizing</span><span class="err">'</span><span class="o">]</span> <span class="o">=</span> <span class="err">'</span><span class="n">border</span><span class="o">-</span><span class="n">box</span><span class="err">'</span><span class="o">;</span> <span class="n">target</span><span class="o">.</span><span class="na">style</span><span class="o">[</span><span class="err">'</span><span class="n">border</span><span class="err">'</span><span class="o">]</span> <span class="o">=</span> <span class="err">'</span><span class="mi">2</span><span class="n">px</span> <span class="n">solid</span> <span class="n">green</span><span class="err">'</span><span class="o">;</span> <span class="o">}</span> <span class="n">document</span><span class="o">.</span><span class="na">addEventListener</span><span class="o">(</span><span class="err">'</span><span class="n">click</span><span class="err">'</span><span class="o">,</span> <span class="n">onClick</span><span class="o">);</span> <span class="o">&lt;/</span><span class="n">script</span><span class="o">&gt;</span> <span class="err">#</span><span class="o">{/</span><span class="k">if</span><span class="o">}</span> </code></pre></div></div> <p>Естественно, приведённый код зависит от вашего фреймворка, приложения, проекта и т.д. <br /> Цвет и толщина рамки - от вашего дизайна. Возможно даже, это должна быть не рамка, а что-то другое.<br /> Главное, чтобы было видно, какой элемент был реально кликнут.</p> <p>Здесь можно посмотреть один из примеров реализации: <a href="https://github.com/selenide-examples/gmail/blob/master/test/org/selenide/examples/gmail/Highlighter.java">Highlighter</a>.</p> <h2 id="что-теперь">Что теперь?</h2> <p>Если у вас есть мигающие тесты (а они есть у всех) - начните с этого шага, и вы хотя бы увидите, куда попал клик. А дальше уже будем разбираться.</p> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/06/advent-calendar-visualize-click/ http://ru.selenide.org/2019/12/06/advent-calendar-visualize-click 2019-12-06T00:00:00+00:00 Как быстро проверить размер? <p>Драсьте!</p> <p>На дворе 4 декабря, перед вами открыт рождественский календарь Selenide, и в сегодняшнем посте я покажу один простой трюк, как сделать некоторые проверки быстрее.</p> <h1 id="как-быстро-проверить-размер-коллекции">Как быстро проверить размер коллекции?</h1> <p>В Selenide есть удобные методы для проверки коллекций. Например, вот так можно проверить, что в некотором списке книг ровно три книги:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#books .book"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <p>А если страница реализована так, что в HTML книг больше, а ненужные книги просто прячутся, то можно отфильтровать коллекцию перед проверкой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#books .book"</span><span class="o">).</span><span class="na">filter</span><span class="o">(</span><span class="n">visible</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <h2 id="проблема">Проблема</h2> <p>Если таких книг на странице много (скажем, несколько десятков и больше), то такая фильтрация может быть медленной (скажем, от нескольких секунд и выше). Причина в том, что метод <code class="language-plaintext highlighter-rouge">filter(visible)</code> должен вызвать метод <code class="language-plaintext highlighter-rouge">WebElement.isDisplayed()</code> в цикле для каждого элемента коллекции по отдельности. А каждый вызов - это отдельное обращение к вебдрайверу, отдельный http запрос и т.д. В сумме накапливается.</p> <p>Может быть, в вашем проекте тесты бегут трое суток, и несколько секунд погоды не делает. Тогда можете дальше не читать.</p> <p>Но если вы заботитесь об эффективности ваших тестов, если работаете над их ускорением - мой пост для вас.</p> <h2 id="первая-попытка">Первая попытка</h2> <p>Как можно ускорить эту проверку? Напрашивается первое решение: фильтровать видимые элементы на уровне селектора:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#books .book:visible"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">size</span><span class="o">(</span><span class="mi">3</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <p>Но так не работает.</p> <p>Проблема в том, что CSS-селектора <code class="language-plaintext highlighter-rouge">:visible</code> не существует. Такую штуку умеет делать только JQuery.</p> <h2 id="вторая-попытка">Вторая попытка</h2> <p>Что же нас спасёт?</p> <p>Вера Брежнева ошибалась: мир спасёт не красота, мир спасёт уродство. И имя ему - JavaScript.</p> <p>Вы можете создать вспомогательный метод, который с помощью JavaScript и JQuery вернёт количество элементов:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">int</span> <span class="nf">sizeOf</span><span class="o">(</span><span class="nc">String</span> <span class="n">cssSelector</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Number</span> <span class="n">count</span> <span class="o">=</span> <span class="n">executeJavaScript</span><span class="o">(</span><span class="s">"return $(arguments[0]).length"</span><span class="o">,</span> <span class="n">cssSelector</span><span class="o">);</span> <span class="k">return</span> <span class="n">count</span><span class="o">.</span><span class="na">intValue</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <p>(этот код работает только при наличии JQuery на тестируемой странице. Если JQuery нет, наверняка можно написать аналогичный код с использованием любой другой библиотеки.)</p> <p>И теперь эта проверка будет быстрой, потому что она обратится к браузеру всего один раз. А браузер внутри себя выполняет любые скрипты очень быстро.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="o">{</span> <span class="n">assertEquals</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">sizeOf</span><span class="o">(</span><span class="s">"#books .book:visible"</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <h2 id="что-теперь">Что теперь?</h2> <p>Познайте всю мощь JavaScript. <br /> Он позволяет ещё много разных трюков, которые помогут сделать ваши тесты быстрее и надёжнее.</p> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/04/advent-calendar-effective-size-check/ http://ru.selenide.org/2019/12/04/advent-calendar-effective-size-check 2019-12-04T00:00:00+00:00 setWebDriver или WebDriverProvider? <p>Добрый вечер!</p> <p>На дворе 3 декабря, перед вами открыт рождественский календарь Selenide, и в сегодняшнем посте я отвечу на один простой вопрос.</p> <h1 id="что-выбрать-setwebdriver-или-webdriverprovider">Что выбрать, <code class="language-plaintext highlighter-rouge">setWebDriver()</code> или <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>?</h1> <p>Как вы знаете, Selenide по умолчанию сам открывает и закрывает браузер. Вам для этого ничего не надо делать.</p> <p>Но иногда вам хочется открыть браузер с какими-то специфичными настройками. Для этого в селениде предусмотрены два разных метода. Так уж сложилось, что я никогда нигде не писал, почему понадобилось два, и как выбрать один из них.</p> <p>Итак,</p> <h4 id="вариант-1">Вариант 1:</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Configuration</span><span class="o">.</span><span class="na">browser</span> <span class="o">=</span> <span class="nc">MyWebdriverProvider</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span> </code></pre></div></div> <p>Этот вариант предпочтительнее в большинстве случаев. Он хорош тем, что ваш класс <code class="language-plaintext highlighter-rouge">MyWebdriverProvider</code> отвечает только за то, КАК открыть браузер (какие ключики ему передать, где найти бинарник, вот это всё). Но не отвечает за то, КОГДА открыть и закрыть браузер. Об этом заботится селенид.</p> <h4 id="вариант-2">Вариант 2:</h4> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Before</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUp</span><span class="o">()</span> <span class="o">{</span> <span class="kt">var</span> <span class="n">yandexBrowser</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">YandexDriver</span><span class="o">(...);</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">setWebDriver</span><span class="o">(</span><span class="n">yandexBrowser</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Этот вариант хуже тем, что вам придётся самому не только открывать, но и закрывать браузер. Это часто вызывает недопонимание. Селенид не может его закрыть, ведь он не знает, где ещё вы можете его использовать. Сам открыл - сам закрывай.</p> <p>Отсюда назревает вопрос:</p> <h3 id="а-зачем-он-вообще-нужен">А зачем он вообще нужен?</h3> <p>Честно говоря, я сам долгое время не отдавал себе в этом отчёт, и лишь недавно назрело понимание. :)</p> <p>Изначально метод <code class="language-plaintext highlighter-rouge">setWebDriver()</code> был добавлен для того, чтобы людям было легко перевести на селенид существующие проекты (не переписывая весь проект).</p> <p>Представьте, что в компании X уже есть стотыщ автотестов на селениуме (или HtmlElements, Thucydides, Serenity или чём-то ещё). И они хотят новые тесты писать на селениде, а старые не трогать. Работают и работают.</p> <p>И у них где-то в тестах уже есть код, которые открывает и закрывает браузер. И хотелось бы как-то сказать селениду, чтобы он тоже этот браузер использовал. Вот для этого и подходит метод <code class="language-plaintext highlighter-rouge">setWebDriver()</code>. Он позволяет запускать и старый, и новый код вперемежку, так чтобы они работали с одним и тем же браузером и не мешали друг дружке.</p> <p>То есть применение у метода <code class="language-plaintext highlighter-rouge">setWebDriver()</code>, как вы видите, довольно узкое. Я вообще не уверен, использовали ли его вообще хоть когда-нибудь в таком ключе.</p> <h2 id="что-теперь">Что теперь?</h2> <p>А если вы пишите новый проект, используйте <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>. SRP.</p> <p><br /></p> <h1 id="driver-factory">UPD</h1> <p>Позже в <a href="https://ru.selenide.org/2020/11/25/selenide-5.16.2/#selenide-5.16.2">Selenide 5.16.1</a> появилась более новая возможность - <code class="language-plaintext highlighter-rouge">DriverFactory</code>. Теперь это рекомендованный способ создавать кастомный вебдрайвер.</p> <p>По сути она очень похожа на <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>, но даёт больше контроля над происходящим. Вам нужно</p> <ol> <li>либо реализовать интерфейс <code class="language-plaintext highlighter-rouge">DriverFactory</code>,</li> <li>либо создать подкласс <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/webdriver/ChromeDriverFactory.java">ChromeDriverFactory</a>, <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/webdriver/FirefoxDriverFactory.java">FirefoxDriverFactory</a> или другого похожего класса (которые есть в составе Selenide и реализуют интерфейс <code class="language-plaintext highlighter-rouge">DriverFactory</code>) и переопределить лишь нужные методы. Все методы в них специально сделаны маленькие и объявлены как protected.</li> </ol> <p>Примеры есть в <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/ChromeProfileByFactoryTest.java">тестах самого селенида</a>.</p> <p>См. также этот <a href="https://mbbaig.blog/selenide-webdriverfactory/">пост от Boris Bay</a>.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider/ http://ru.selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider 2019-12-03T00:00:00+00:00 Как надругаться над Селенидом <p>Всем привет!</p> <p>На дворе 2 декабря, и мы продолжаем рождественский календарь постом про злоупотребление селенидом.</p> <h1 id="как-надругаться-над-селенидом">Как надругаться над Селенидом</h1> <h3 id="и-почему-это-у-многих-вызывает-трудности">(и почему это у многих вызывает трудности)</h3> <p>Я часто получаю жалобу, что у людей не ловится ошибка вот в таком коде:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">try</span> <span class="o">{</span> <span class="err">$</span><span class="o">(</span><span class="s">".banner"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">".banner .close"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// System.out.println("Элемент не найден - значит, баннер в этот раз не появился");</span> <span class="o">}</span> </code></pre></div></div> <p>Этот тест хочет проверить, появился ли рекламный баннер. Но не сразу, а немножко подождать: вдруг баннер появится с задержкой. И если появился, надо его закрыть.</p> <p><br /></p> <h1 id="почему-это-плохой-тест">Почему это плохой тест?</h1> <p>Потому, что он <strong>медленный</strong>.</p> <p>В большинстве случае баннер не появится, и тест будет ждать 4 секунды (или какой у вас таймаут).</p> <p>Это ужасно. Вы профессионалы или кто? Ваша цель - сделать тесты быстрыми и надёжными. Иначе чем вы отличаетесь от обезьяны?</p> <p><br /></p> <h1 id="почему-это-очень-плохой-тест">Почему это очень плохой тест?</h1> <p>Потому, что он <strong>нестабильный</strong>.</p> <p>Иногда баннер будет появляться как раз на истёке 4 секунд. Тест решит, что баннера нет, и пойдёт дальше, а тут оп! - и выскочит баннер. И вы получаете “моргающий” тест.</p> <p><br /></p> <h1 id="почему-это-очень-очень-плохой-тест">Почему это очень-очень плохой тест?</h1> <p>Потому, что он <strong>не тестирует сам баннер</strong>.</p> <ol> <li> <p>Представьте себе, что за время прогона тестов баннер так ни разу и не появился. Все тесты зелёные. А у реальных пользователей баннер появляется, и как раз тогда случается ошибка. И всё крэшится. Пользователи жалуются и обрушивают рейтинг вашего приложения. <br /> <br /> <em>А ваши тесты зелёные.</em></p> </li> <li> <p>Или наоборот, во время прогона тестов баннер появляется постоянно. Ну вот так сложилось в тестовой среде. А у реальных пользователей баннер появляется редко, и как раз без баннера случается ошибка. Всё крэшится. Пользователи жалуются и обрушивают рейтинг вашего приложения. <br /> <br /> <em>А ваши тесты зелёные.</em></p> </li> </ol> <p><br /></p> <h1 id="что-же-делать">Что же делать?</h1> <p>Очевидно, что у вас должно быть как минимум два теста:</p> <ol> <li>Случай, когда баннер выскакивает, и</li> <li>Случай, когда баннер не выскакивает.</li> </ol> <p>Значит, у вас должна быть возможность как-то управлять показом баннера. Либо через “ручку” (api) тестируемого приложения, либо через какую-то админку, либо запуском SQL в базе. Как угодно. Если такой ручки нет, надо пойти договориться с разработчиками.</p> <p>Только не надо мне говорить, что <a href="https://asolntsev.livejournal.com/77819.html">договориться не получится</a>. Вы профессионалы или кто?</p> <p>Если не получится, то стирайте весь этот тест. Не получилось так не получилось.</p> <p>Подробнее про ручки и управление тестовой средой см. в докладе <a href="https://www.youtube.com/watch?v=ePvrXUCeAr8">Arrange, mazafaka!</a></p> <p><br /></p> <p>И наконец, на десерт:</p> <h1 id="почему-ошибка-не-ловится">Почему ошибка не ловится?</h1> <p>Вы уже догадались, почему ошибка не ловится?</p> <p>Всё дело в иерархии исключений в Java. Базовый класс не <code class="language-plaintext highlighter-rouge">Exception</code>, а <code class="language-plaintext highlighter-rouge">Throwable</code>.</p> <ul> <li><code class="language-plaintext highlighter-rouge">Throwable</code> <ul> <li><code class="language-plaintext highlighter-rouge">Exception</code> <ul> <li><code class="language-plaintext highlighter-rouge">RuntimeException</code> <ul> <li><code class="language-plaintext highlighter-rouge">WebDriverException</code></li> </ul> </li> </ul> </li> <li><code class="language-plaintext highlighter-rouge">Error</code> <ul> <li><code class="language-plaintext highlighter-rouge">AssertionError</code> <ul> <li><code class="language-plaintext highlighter-rouge">UIAssertionError</code> <ul> <li><code class="language-plaintext highlighter-rouge">ElementShould</code></li> </ul> </li> </ul> </li> </ul> </li> </ul> </li> </ul> <p>Именно поэтому, когда вы ловите <code class="language-plaintext highlighter-rouge">catch (Exception e)</code>, мимо вас пролетают все подклассы <code class="language-plaintext highlighter-rouge">Error</code>, в том числе все селенидовские ошибки.</p> <p>“Ну отлично, буду ловить <code class="language-plaintext highlighter-rouge">catch (Error e)</code>” - решите вы. НЕТ! Прошу вас, НЕТ!</p> <p>Читаем javadoc к классу <code class="language-plaintext highlighter-rouge">java.lang.Error</code>:</p> <blockquote> <p>An Error is a subclass of Throwable that indicates serious problems that a reasonable application <strong>should not try to catch</strong>. Most such errors are abnormal conditions.</p> </blockquote> <h2 id="что-теперь">Что теперь?</h2> <p>Я надеюсь, вы перестанете ловить ошибки в тестах. Тесты для того и созданы, чтобы сообщать об ошибках, а не ловить их.</p> <p>Надеюсь, вы посмотрите <a href="https://www.youtube.com/watch?v=ePvrXUCeAr8">Arrange, mazafaka!</a> и возьмёте управление тестовой средой под свой контроль.</p> <p>И решите для себя, тварь вы дрожащая, или право имеете?</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide/ http://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide 2019-12-02T00:00:00+00:00 Главный Алгоритм <p>Всем привет!</p> <p>В западном мире начался период “Адвент”, или ожидание Рождества.</p> <p>Дети распаковывают свои “рождественские календари”, в которых открывают каждый день по окошечку (и находят под ним шоколадку). В Java мире, например, ведут <a href="https://www.javaadvent.com/">Java Advent Calendar</a>, в котором разные авторы каждый день публикуют по статье. Я тоже в нём участвовал пару раз (<a href="https://www.javaadvent.com/2017/12/flaky-tests.html">2017</a> и <a href="https://www.javaadvent.com/2018/12/wtf-connection-pools.html">2018</a>).</p> <p>Я тоже решил не отставать и замутить Selenide Advent Calendar. Каждый день по статье. С 1 по 25 декабря. Начну я, а вы можете присоединиться - сообщите о себе любым способом.</p> <p>Сегодняшняя тема будет такая:</p> <h1 id="самый-главный-алгоритм">Самый Главный Алгоритм</h1> <p>Главная фишка селенида, как многие считают - это автоматические ожидания. Вам не нужно писать лишний код типа <code class="language-plaintext highlighter-rouge">new WebDriverWait(driver, 30).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[contains(text(),'COMPOSE')]")))</code>, чтобы дождаться наступления какого-то события. Вам не нужно тратить время и силы, чтобы вообще думать о том, а где нужны ожидания. Вам не нужно раз за разом спотыкаться о моргнувший тест и думать, куда бы ещё добавить ожидание.</p> <p>В селениде всё проще: любая строчка типа <code class="language-plaintext highlighter-rouge">$(byText("COMPOSE")).shouldBe(visible)</code> <strong>автоматически ждёт</strong>, если надо.</p> <h3 id="а-не-замедлит-ли-это-тест">А не замедлит ли это тест?</h3> <p>Нет.</p> <p>Если элемент уже видимый, селенид сразу пойдёт дальше. Если нет - подождёт 100 мс и проверит снова. Если снова нет - подождёт ещё 100 мс и проверит ещё раз. И так до 4 секунд. А вот если по истечении 4 секунд элемент всё ещё невидим, тогда тест упадёт.</p> <p>Естественно, эти 4с и 100 мс настраиваются. Вы можете задать любой таймаут под ваш проект.</p> <h3 id="как-это-работает">Как это работает?</h3> <p>Вот мы и переходим к Самому Главному Алгоритму. На самом деле он очень прост.</p> <p>Вот как работает, например, метод <code class="language-plaintext highlighter-rouge">$(".btn").shouldBe(visible)</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">shouldBeVisible</span><span class="o">()</span> <span class="o">{</span> <span class="k">do</span> <span class="o">{</span> <span class="k">try</span> <span class="o">{</span> <span class="k">assert</span> <span class="n">webdriver</span><span class="o">.</span><span class="na">findElement</span><span class="o">().</span><span class="na">isDisplayed</span><span class="o">()</span> <span class="o">==</span> <span class="kc">true</span><span class="o">;</span> <span class="k">return</span> <span class="n">ok</span><span class="o">;</span> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="n">sleep</span><span class="o">(</span><span class="mi">100</span> <span class="n">мс</span><span class="o">)</span> <span class="o">}</span> <span class="o">}</span> <span class="k">while</span> <span class="o">(</span><span class="n">прошло</span> <span class="n">меньше</span> <span class="no">N</span> <span class="n">секунд</span><span class="o">);</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ElementShouldBeVisible</span><span class="o">(</span><span class="s">"Ожидали: то-то, на самом деле: то-то"</span><span class="o">);</span> <span class="c1">// тут ещё сделать скриншот.</span> <span class="o">}</span> </code></pre></div></div> <p>В общем-то всё просто.</p> <p>Этот же код в реальности выглядит <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/impl/WebElementSource.java#L44">чуть сложнее</a>. А всё потому, что он “универсальный” - не только для <code class="language-plaintext highlighter-rouge">shouldBe(visible)</code>, но и для других операций и условий.</p> <p>Как известно,</p> <h3 id="дьявол-кроется-в-деталях">дьявол кроется в деталях</h3> <p>В приведённом выше коде есть масса вопросов:</p> <ol> <li>Какие именно ошибки ловить? <ul> <li><code class="language-plaintext highlighter-rouge">Exception</code>, <code class="language-plaintext highlighter-rouge">Error</code>, <code class="language-plaintext highlighter-rouge">Throwable</code>, <code class="language-plaintext highlighter-rouge">AssertionError</code>, <code class="language-plaintext highlighter-rouge">WebDriverException</code>?</li> </ul> </li> <li>В случае каких ошибок надо вылететь сразу, не продолжая ожидание? <ul> <li>например, если XPath невалидный, то он и через 4 секунды будет невалидный.</li> </ul> </li> <li>Результат хороший или плохой? <ul> <li>например, если элемент не найден - это может быть ок для условия <code class="language-plaintext highlighter-rouge">$.shouldNot(exist)</code>.</li> </ul> </li> <li>В какой момент делать скриншот?</li> <li>Можно ли всё это кастомизировать? <ul> <li>Например, при использовании Аллюра, возможно, не стоит снимать скриншот, ведь Аллюр сам снимет скриншот.</li> <li>Или наоборот, стоит снять скриншот и как-то передать его Аллюру?</li> <li>(сейчас и Селенид, и Аллюр делают каждый по скриншоту, так что они дублируются)</li> </ul> </li> <li>Что делать, если мы дошли до последней строки, а вот в ней-то как раз прилетел зло#учий <code class="language-plaintext highlighter-rouge">StaleElementException</code>?</li> </ol> <p>Ответы на эти вопросы менялись с течением времени. И наверное, будут ещё меняться. Но суть остаётся.</p> <h2 id="что-теперь">Что теперь?</h2> <p>Теперь, я надеюсь, вы не будете относиться к селенидовским ожиданиям как магии, а будете понимать, как оно работает. Возможно, это поможет при изучении различных странных падений тестов и прочей мистики.</p> <p>А может, и посоветуете, как улучшить или упростить <s>ядро скайнета</s> наш Главный Алгоритм.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/12/01/advent-calendar-main-algorithm/ http://ru.selenide.org/2019/12/01/advent-calendar-main-algorithm 2019-12-01T00:00:00+00:00 Вышла Selenide 5.5.1 <p>Всем привет!</p> <p>Соскучились?</p> <p>Чтобы вы не расслаблялись, мы выпустили <a href="https://github.com/selenide/selenide/milestone/86?closed=1">Selenide 5.5.1</a> с парочкой новых фич.</p> <ul> <li><a href="#mobile-browser-emulation">Эмуляция мобильного браузера</a></li> <li><a href="#deprecated-selenide-close">Пометили метод <code class="language-plaintext highlighter-rouge">Selenide.close()</code> как устаревший</a></li> <li><a href="#news">Новости</a></li> </ul> <p><br /></p> <h1 id="mobile-browser-emulation">Добавили эмуляцию мобильного браузера</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="deprecated-selenide-close">Пометили метод <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="news">Новости</h2> <ul> <li>Огонь-огонь! Новый плагин IDEA для Selenide/Selenium: <a href="https://plugins.jetbrains.com/plugin/13267-qa-lithium">QA Lithium</a>. Спасибо <a href="https://strangeway.org/">Yuriy Artamonov</a>!</li> <li>Ещё один фреймворк на базе Selenide: <a href="https://github.com/cuba-platform/masquerade">Masquerade</a> - <em>CUBA Platform UI Testing Library</em></li> <li>Статья <a href="https://medium.com/swlh/selenide-in-testing-process-automatisation-through-selenoid-in-the-docker-container-48e659d2ee72">про Selenide и Selenoid</a></li> <li>И ещё <a href="https://www.intexsoft.com/blog/post/selenide-docker.html">про Selenide и Selenoid</a></li> </ul> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/11/29/selenide-5.5.1/ http://ru.selenide.org/2019/11/29/selenide-5.5.1 2019-11-29T00:00:00+00:00 Вышла Selenide 5.5.0 <p>Всем привет!</p> <p>А как вы празднуете Хэллоуин?</p> <p>Мы вот выпустили <a href="https://github.com/selenide/selenide/milestone/85?closed=1">Selenide 5.5.0</a> с несколькими <em>обратно несовместимым изменениями</em>.</p> <p>СТРАШНО?</p> <p><br /></p> <h1 id="перешли-с-jul-на-slf4j">Перешли с JUL на SLF4J</h1> <p>Иногда селениду нужно записать в лог какие-то вспомогательные сообщения: “я нашёл бинарник браузера там-то”, “я не смог сохранить скриншот туда-то” и т.д.</p> <p>До сих пор Селенид писал свои логи через стандартный Java механизм <code class="language-plaintext highlighter-rouge">java.util.logging</code> (т.н. JUL). Всем известно, что у него есть недостатки, и поэтому в Java мире давно появилась целая куча библиотек, в которых сложно разобраться: Log4J, Slf4j, Logback, JCL.</p> <p>В общем, теперь будет так.</p> <ul> <li>Slf4J - это “фасад”, что-то типа “интерфейса”. Селенид будет писать логи через него.</li> <li>А Log4j, Logback, JCL - это реализации этого “интерфейса”. Наверное, есть и другие.</li> </ul> <p>Вы можете сами решать, какую реализацию Slf4J вы используете в проекте. Наверняка в вашем проекте это уже давно решено, ведь как минимум WebDriverManager изначально писал свои логи через Slf4J.</p> <p>Чтобы подключить одну из реализаций Slf4J, вы должны добавить в проект одну из зависимостей:</p> <ul> <li>slf4j-log4j12-*.jar</li> <li>logback-classic-*.jar</li> <li>slf4j-simple-*.jar</li> <li>slf4j-jdk14-*.jar</li> <li>slf4j-jcl-*.jar</li> <li>slf4j-nop-*.jar (ТОЛЬКО НЕ ЭТО! Это самый плохой вариант. Он будет игнорировать логи.)</li> </ul> <p>NB! Подчёркиваю, что скорее всего такая зависимость у вас уже есть, и поэтому делать ничего не надо. Оно само заработает.</p> <p>Если же зависимости всё-таки нет, вы увидите такое сообщение при запуске тестов (и не увидите логов селенида):</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. </code></pre></div></div> <p>Если вы его увидите, самое простое решение - это добавить такую строчку в ваш build.gradle:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.28' </code></pre></div></div> <p>или в pom.xml:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>org.slf4j<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>slf4j-simple<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>1.7.28<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span> <span class="nt">&lt;/dependency&gt;</span> </code></pre></div></div> <p>NB! Если не разобрались с Slf4j - не паникуйте, логи селенида не очень-то важны. Ошибки при падении тестов никуда не денутся. Речь идёт только про служебные INFO и WARNING логи типа <code class="language-plaintext highlighter-rouge">INFO Using browser binary: /var/lib/opera.exe</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/923">923</a>.</p> <p>Спасибо <a href="https://github.com/gschukin">Gleb Schukin</a> за <a href="https://github.com/selenide/selenide/pull/926">PR 926</a>.</p> <p><br /></p> <h1 id="удалили-зависимости-htmlunit-и-phantomjs">Удалили зависимости HtmlUnit и PhantomJS</h1> <p>Не переживайте, тесты должны по-прежнему работать на HtmlUnit и PhantomJS, просто селенид больше не тянет эти зависимости автоматически (ведь большинство людей давно перешли на <em>headless chrome</em> и <em>headless firefox</em>).</p> <p>Если вы упорно хотите использовать HtmlUnit или PhantomJS, вам просто нужно будет добавить соответствующие зависимости в проект.</p> <p>Спасибо <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/1003">PR 1003</a> и <a href="https://github.com/selenide/selenide/pull/998">PR 998</a>.</p> <p><br /></p> <h1 id="удалили-встроенную-поддержку-браузера-safari">Удалили встроенную поддержку браузера Safari</h1> <p>Но не волнуйтесь, вы всё ещё можете использовать Safari! См. <a href="https://github.com/selenide/selenide/wiki/Safari">инструкцию</a></p> <p><br /></p> <h1 id="сделали-метод-executecommand-более-универсальным">Сделали метод <code class="language-plaintext highlighter-rouge">$.execute(command)</code> более универсальным</h1> <p>Раньше метод <code class="language-plaintext highlighter-rouge">$.execute</code> умел работать только с <code class="language-plaintext highlighter-rouge">Command&lt;SelenideElement&gt;</code>, а теперь - с любыми командами.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/1000">Issue 1000</a> и <a href="https://github.com/selenide/selenide/pull/1001">PR 1001</a>.</p> <p><br /></p> <h1 id="починили-настройку-holdbrowseropen">Починили настройку <code class="language-plaintext highlighter-rouge">holdBrowserOpen</code></h1> <p>.. которую мы сломали в Selenide 5.4.1. Теперь браузер снова остаётся открытым, и вы можете дебажить упавший тест.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/999">999</a> и <a href="https://github.com/selenide/selenide/pull/1005">PR 1005</a>.</p> <p><br /></p> <h1 id="снимаем-скриншот-в-случае-ошибки-dialogtextmismatch">Снимаем скриншот в случае ошибки <code class="language-plaintext highlighter-rouge">DialogTextMismatch</code></h1> <p>Как вы знаете, в селениде есть функция для проверки текста диалогового окна (<code class="language-plaintext highlighter-rouge">alert</code> или <code class="language-plaintext highlighter-rouge">confirm</code>):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">confirm</span><span class="o">(</span><span class="s">"Are you sure?"</span><span class="o">);</span> </code></pre></div></div> <p>Но при падении этой проверке селенид забывал сделать скриншот. Теперь делает.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/907">907</a>. Спасибо <a href="https://github.com/nwholloway">Nick Holloway</a> за <a href="https://github.com/selenide/selenide/pull/986">PR 986</a>.</p> <p><br /></p> <h1 id="рефакторинг-screenshotlaboratory">Рефакторинг ScreenShotLaboratory</h1> <p>Хотя это всего лишь рефакторинг, т.е. на вас он не повлияет, я не могу не отметить инициативу <a href="https://github.com/SeleniumTestAB">SeleniumTestAB</a>, который решил улучшить код и запилил <a href="https://github.com/selenide/selenide/pull/1004">PR 1004</a> и <a href="https://github.com/selenide/selenide/pull/1006">PR 1006</a>.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Статья на хабре: <a href="https://habr.com/ru/post/473454/">Автоматизация тестирования с использованием Selenide через Selenoid в Docker контейнере</a></li> <li>Доклад про Selenide на конференции EclipseCon 2019: <a href="https://www.eclipsecon.org/europe2019/sessions/complete-selenium-techstack-conception-evaluation-open-source-software">A complete Selenium Techstack</a></li> <li>Мой доклад для Cyprus Quality Conference <a href="https://docs.google.com/presentation/d/1hSCmjwvLCY4bKqSncffZfMOi1NmXIooJu5LIjxYN6hg/edit">Тройничок: Selenide для Web, Android и iOS”</a>. Пока только слайды.</li> </ul> <h2 id="и-главное">И главное</h2> <p>И главное: 25 октября <strong>Селениду ИСПОЛНИЛОСЬ 8 ЛЕТ!!!</strong></p> <p>25.10.2011 был сделан первый коммит в репозиторий селенида. После 8 лет мы имеем:</p> <ul> <li>89 тыщ скачиваний с 14 тыщ уникальных айпишников (в месяц)</li> <li>1 тыща звёзд на гитхабе</li> <li>55 контрибьютеров</li> </ul> <p>Спасибо всем контрибьютерам, пользователям, критикам, идеологам и просто сочувствующим.</p> <p>Это только начало!</p> <p><br /></p> <p>Статистика скачиваний Selenide за октябрь 2019:</p> <center> <img src="/images/2019/10/selenide.downloads.png" width="800" /> </center> <p><br /></p> <center> <img src="/images/2019/10/selenide.unique-ips.png" width="800" /> </center> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/10/31/selenide-5.5.0/ http://ru.selenide.org/2019/10/31/selenide-5.5.0 2019-10-31T00:00:00+00:00 Вышла Selenide 5.4.1 <p>Всем привет!</p> <p>Нет ничего унылее октября. Давайте же скрашивать его хорошими новостями!</p> <p>Сегодня мы выпустили Selenide 5.4.0 с парочкой существенных изменений, <em>о которых многие из вас давно просили</em>.</p> <p><strong>UPD</strong> В 5.4.0 была ошибка, так что обновляйтесь сразу на <strong>5.4.1</strong></p> <p><strong>UPD 2</strong> В 5.4.1 тоже нашлась малюсенькая ошибочка: опция <code class="language-plaintext highlighter-rouge">Configuration.holdBrowserOpen</code> не работает. Исправим в следующей версии. Надеюсь, вы не используете её часто.</p> <p><br /></p> <h1 id="fix-illegal-state-exception">1. Исправили ошибку “IllegalStateException WebDriver has been closed”</h1> <p>Начиная с Selenide 5.0.0 (т.е. примерно год назад), многие пользователи стали жаловаться на ошибку <code class="language-plaintext highlighter-rouge">IIlegalStateException WebDriver has been closed...</code>.</p> <p>Строго говоря, это не ошибка, и я долгое время отказывался её исправлять. Я убеждён, что в грамотно написанных тестах такая ошибка случиться не может. Собственно, селенид её специально кидает, чтобы стимулировать пользователей писать тесты правильно.</p> <p>Но жалоб было слишком много, поэтому с тяжёлым сердцем я всё-таки решил убрать эту ошибку.</p> <blockquote> <p>В конце концов, каждый имеет право набивать свои шишки.</p> </blockquote> <p>Если вкратце, ошибка эта случается, когда <code class="language-plaintext highlighter-rouge">SelenideElement</code> переиспользуют с разными инстансами <code class="language-plaintext highlighter-rouge">SelenideDriver</code>. Обычно так бывает, когда <code class="language-plaintext highlighter-rouge">SelenideElement</code> объявляют как статическое поле (в пэдж обжекте или в тесте, неважно). И инициализируют его раньше, чем открывается браузер. Либо перезапускают браузер между тестами. Ещё это бывает, когда используют Cucumber, который пытается инициализировать степы и пэдж обжекты до открытия браузеров. Или зачем-то пихают в тесты Guice или Spring. В тесты, Карл!</p> <p>Так вот, всё это переусложнение и в тестах не нужно. Статические поля - зло. Подробнее об этом я рассказывал в докладе <a href="https://www.youtube.com/watch?v=4JJNccWtdNI">Антистатик</a>.</p> <p>Но раз уж людям так впёрлось - ладно. Ловите Selenide 5.4.0</p> <p>Теперь вот такой код будет работать:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SomeClass</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="nc">SelenideElement</span> <span class="n">body</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"body"</span><span class="o">);</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://mail.ru"</span><span class="o">);</span> <span class="n">body</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span> <span class="nc">Selenide</span><span class="o">.</span><span class="na">close</span><span class="o">();</span> <span class="n">open</span><span class="o">(</span><span class="s">"https://mail.ru"</span><span class="o">);</span> <span class="n">body</span><span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">exist</span><span class="o">);</span> <span class="c1">// А раньше отсюда летел "IllegalStateException WebDriver has been closed"</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Больше примеров и жалоб можно найти в issue <a href="https://github.com/selenide/selenide/issues/862">862</a>, <a href="https://github.com/selenide/selenide/issues/902">902</a>, <a href="https://github.com/selenide/selenide/issues/954">954</a>, <a href="https://github.com/selenide/selenide/issues/922">922</a>, <a href="https://github.com/selenide/selenide/issues/873">873</a>.</p> <p>Как мы позволили статикам победить, можно отследить в <a href="https://github.com/selenide/selenide/pull/989">PR 989</a>.</p> <p><br /></p> <h1 id="close-always-closes-browser">2. Теперь <code class="language-plaintext highlighter-rouge">SelenideDriver.close()</code> всегда закрывает браузер</h1> <p>Опять же, начиная с Selenide 5.0.0, люди стали жаловаться на то, что метод <code class="language-plaintext highlighter-rouge">close()</code> не закрывает браузер. Конечно, не всегда, а только в одном специфическом случае - когда пользователь сам же браузер и открыл.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideDriver</span> <span class="n">browser</span><span class="o">=</span><span class="k">new</span> <span class="nc">SelenideDriver</span><span class="o">(</span><span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">());</span> <span class="n">browser</span><span class="o">.</span><span class="na">close</span><span class="o">();</span> <span class="c1">// вот так браузер НЕ закрывался</span> <span class="n">browser</span><span class="o">.</span><span class="na">getWebDriver</span><span class="o">().</span><span class="na">close</span><span class="o">();</span> <span class="c1">// а так закрывался</span> </code></pre></div></div> <p>Это тоже не совсем бага, потому что изначально в Селениде была такая философия: браузер, открытый пользователем, селенид не закрывает. Потому что поди знай, вдруг его ещё где-нибудь используют помимо Селенида.</p> <p>Как говорят в Сбертехе,</p> <blockquote> <p>где браузер открывали - там его и закрывайте. :)</p> </blockquote> <p>Но опять же, жалобы были. У людей вызывало непонимание, как так: метод <code class="language-plaintext highlighter-rouge">close()</code> есть, отрабатывает без ошибок - но не закрывает.</p> <p>Теперь мы это тоже исправили. Метод <code class="language-plaintext highlighter-rouge">close()</code> закрывает браузер.</p> <p>P.S. Если у вас вдруг браузер начнёт закрываться в ненужный момент - обращайтесь, будем изучать.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/896">issue 896</a> и <a href="https://github.com/selenide/selenide/pull/989">PR 989</a></p> <p><br /></p> <h1 id="restore-short-error-messages">3. Вернули короткие сообщения об ошибках</h1> <p>Как мы могли заметить, в Selenide 5.3.1 прилетела одна некритичная регрессия. Сообщения об ошибках стали слишком длинными:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">ex</span><span class="o">.</span><span class="na">ElementNotFound</span><span class="o">:</span> <span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="err">#</span><span class="n">customerDashboardButton</span><span class="o">}</span> </code></pre></div></div> <p>Теперь мы вернули старые добрые человекочитаемые сообщения:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Element</span> <span class="n">not</span> <span class="n">found</span> <span class="o">{</span><span class="err">#</span><span class="n">customerDashboardButton</span><span class="o">}</span> </code></pre></div></div> <p>P.S. У бедных пользователей <code class="language-plaintext highlighter-rouge">maven-surefire-plugin 2.22.2</code> всё ещё будут длинные сообщения, <a href="/2019/09/07/selenide-5.3.1/">подробности тут</a>. Сочувствую.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/993">PR 993</a></p> <p><br /></p> <h1 id="add-method-using">4. Добавили метод <code class="language-plaintext highlighter-rouge">using</code> для лёгкого переключения несколькими вебдрайверами</h1> <p><a href="/2018/10/10/selenide-5.0.0/">Повторюсь</a>, использование нескольких браузеров в одном тесте в подавляющем большинстве случаев я считаю плохой практикой.</p> <p>Но теперь набивать шишки таким образом стало чуть удобнее.</p> <p>Если раньше приходилось постоянно дёргать метод <code class="language-plaintext highlighter-rouge">setWebDriver()</code> и не забывать, какой браузер активный в данный момент:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">browser1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="kt">var</span> <span class="n">browser2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="n">setWebDriver</span><span class="o">(</span><span class="n">browser1</span><span class="o">);</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://google.com"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Released Selenide 5.4.0"</span><span class="o">));</span> <span class="n">setWebDriver</span><span class="o">(</span><span class="n">browser2</span><span class="o">);</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://yandex.ru"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Вышла Selenide 5.4.0"</span><span class="o">));</span> </code></pre></div></div> <p>то теперь можно завернуть блоки кода для разных браузеров в разные блоки <code class="language-plaintext highlighter-rouge">using</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">browser1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="kt">var</span> <span class="n">browser2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FirefoxDriver</span><span class="o">();</span> <span class="n">using</span><span class="o">(</span><span class="n">browser1</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://google.com"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Released Selenide 5.4.0"</span><span class="o">));</span> <span class="o">});</span> <span class="n">using</span><span class="o">(</span><span class="n">browser2</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://yandex.ru"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"h1"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Вышла Selenide 5.4.0"</span><span class="o">));</span> <span class="o">});</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/976">PR 976</a> и примеры кода в <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/CustomWebdriverTest.java">CustomWebdriverTest.java</a>.</p> <p><br /></p> <h1 id="add-xpath-sanity-check">5. Добавили защиту от одной типичной ошибки с xpath</h1> <p>Типичная ошибка:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".parent"</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"/child"</span><span class="o">)).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> </code></pre></div></div> <p>Дело в том, что XPath, начинающийся на слэш (“/”), ищет элемент <strong>от корня проекта</strong> (а не от элемента <code class="language-plaintext highlighter-rouge">.parent</code>, как, очевидно, хотел автор этого кода).</p> <p>Слишком многие наступали на эти грабли. Поэтому мы добавили защиту от этой типичной ошибки.</p> <p>Теперь показанный выше код кинет ошибку, которая чётко объяснит проблему и сэкономит вам время:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">IllegalArgumentException:</span> <span class="nc">XPath</span> <span class="n">starting</span> <span class="n">from</span> <span class="o">/</span> <span class="n">searches</span> <span class="n">from</span> <span class="n">root</span> </code></pre></div></div> <p>Исправить этот код можно по-разному (смотря что вам требуется):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">".parent"</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"./child"</span><span class="o">)).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">".parent"</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">".//child"</span><span class="o">)).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">".parent"</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"child"</span><span class="o">)).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">visible</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/pull/963">PR 963</a> и <a href="https://github.com/selenide/selenide/pull/975">PR 975</a></p> <p><br /></p> <h1 id="update-dependencies">6. Обновили зависимости</h1> <ul> <li>upgrade to webdrivermanager:3.7.1</li> <li>exclude old Guava dependency coming from net.lightbody.bmp:browsermob-core:2.1.5</li> </ul> <p>Внимание, в WDM обнаружена ошибка: когда вы хотите скачать chromedriver версии 77, он иногда может скачать “75.0.3770.90”. Да-да, потому, что эта версия содержит подстроку “77”. <a href="https://github.com/bonigarcia/webdrivermanager/issues/391">Смешно, да?</a> :)</p> <p><br /></p> <h2 id="news">Новости</h2> <ul> <li>Новая утилита <a href="https://github.com/bsideup/jabel">Jabel</a> от Сергея Егорова:<br /> теперь можно писать на Java13, а компилировать в Java8. <em>А что, так можно было???</em></li> <li><a href="https://www.youtube.com/watch?v=zQAGh_o7Dqs">Сергей Егоров - Как сделать OSS проект успешным</a> – devclub.eu, Таллинн, 24.09.2019 <br /> Близкая мне тема. Вот примерно всем этим я и занимаюсь с Селенидом.</li> <li><a href="https://www.youtube.com/watch?v=WNzTuYFd8oI">Видосик про Selenide на немецком</a> - со школы не смотрел видео на немецком с таким интересом :)</li> <li>Англоязычный мир <a href="https://testguild.com/selenide/">открывает для себя Selenide</a>. Там же ссылка на подкаст про Selenide.</li> <li>Martin Škarbala из Словакии добавил к своему замечательному видео про Selenide английские субтитры: <br /> <a href="https://www.youtube.com/watch?v=y9WTRTOTOsc">Selenide - stručné UI testy</a>.</li> <li><a href="https://www.youtube.com/watch?v=U6z2dK7MwmI&amp;t=10s">10 причин моей ненависти</a> – devclub.eu, Таллинн, 24.09.2019</li> <li>Отличный доклад “<a href="https://www.youtube.com/watch?v=C5eD-K8AO3o">Атомарный рефакторинг</a>” от Тагира Валеева! Жёстко рекомендую.</li> <li>Ещё один доклад с JPoint: <a href="https://www.youtube.com/watch?v=qd9N9eU1Qn0">Миру нужны фулстеки</a></li> </ul> <p><br /></p> <h2 id="conferences">Конференции</h2> <p>Друзья, в унылый пасмурный октябрьский денёк очень советую пойти на конференцию Joker. <a href="https://habr.com/ru/company/jugru/blog/470555/">Круче этой программы ничего не может быть</a>!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2019/10/16/selenide-5.4.0/ http://ru.selenide.org/2019/10/16/selenide-5.4.0 2019-10-16T00:00:00+00:00 Вышла Selenide 5.3.1 <p>Всем привет!</p> <p>На днях случилась сенсация.</p> <h4 id="презентация-нового-айфона-прошла-незамеченной">Презентация нового айфона прошла незамеченной!</h4> <p>Потому, что мы выпустили Selenide 5.3.1. Тоже с улучшенными снимками :)</p> <p>В этой версии мы исправили ровно одну проблему, зато какую!</p> <p><br /></p> <h1 id="maven-где-мои-скриншоты">Maven, где мои скриншоты?</h1> <p>Недавно мы обнаружили, что пользователи мавена и свежего <code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.22.2</code> были лишены одной из самых важных фич селенида: имя скриншота не добавлялось к сообщению об ошибке при падении теста.</p> <p>Вот это нежданчик!</p> <p><strong>Как вы вообще жили без этого, бедняги?</strong></p> <p><em>Может, поэтому вы так упарываетесь по BDD, Серенити и Аллюру?</em></p> <p><br /></p> <h1 id="для-сравнения">Для сравнения</h1> <p>Вот как выглядит сообщение об ошибке при падении теста в Maven и последнем maven-surefire-plugin:2.22.2:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">com</span><span class="o">.</span><span class="na">codeborne</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">ex</span><span class="o">.</span><span class="na">ListSizeMismatch</span><span class="o">:</span> <span class="o">:</span> <span class="nl">expected:</span> <span class="o">=</span> <span class="mi">11</span><span class="o">,</span> <span class="nl">actual:</span> <span class="mi">0</span><span class="o">,</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">results</span> <span class="nl">Elements:</span> <span class="o">[]</span> <span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">GoogleTest</span><span class="o">.</span><span class="na">openGoogle</span><span class="o">(</span><span class="nc">GoogleTest</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">42</span><span class="o">)</span> </code></pre></div></div> <p>Вы видите скриншот? И я не вижу. А он есть.</p> <p>А вот как выглядит ошибка в Gradle, Ant, любой IDE и дефалтовом maven-surefire-plugin:2.12.4:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span> <span class="n">size</span> <span class="n">mismatch</span><span class="o">.</span> <span class="nl">Expected:</span> <span class="o">=</span> <span class="mi">11</span><span class="o">,</span> <span class="nl">actual:</span> <span class="mi">0</span><span class="o">,</span> <span class="nl">collection:</span> <span class="o">.</span><span class="na">results</span> <span class="nl">Elements:</span> <span class="o">[]</span> <span class="nl">Screenshot:</span> <span class="nl">file:</span><span class="o">/</span><span class="nc">Users</span><span class="o">/</span><span class="n">andrei</span><span class="o">/</span><span class="n">projects</span><span class="o">/</span><span class="n">selenide</span><span class="o">-</span><span class="n">examples</span><span class="o">/</span><span class="n">sandbox</span><span class="o">-</span><span class="n">selenide</span><span class="o">-</span><span class="n">junit5</span><span class="o">/</span><span class="n">build</span><span class="o">/</span><span class="n">reports</span><span class="o">/</span><span class="n">tests</span><span class="o">/</span><span class="mf">1567803355181.0</span><span class="o">.</span><span class="na">png</span> <span class="nc">Page</span> <span class="nl">source:</span> <span class="nl">file:</span><span class="o">/</span><span class="nc">Users</span><span class="o">/</span><span class="n">andrei</span><span class="o">/</span><span class="n">projects</span><span class="o">/</span><span class="n">selenide</span><span class="o">-</span><span class="n">examples</span><span class="o">/</span><span class="n">sandbox</span><span class="o">-</span><span class="n">selenide</span><span class="o">-</span><span class="n">junit5</span><span class="o">/</span><span class="n">build</span><span class="o">/</span><span class="n">reports</span><span class="o">/</span><span class="n">tests</span><span class="o">/</span><span class="mf">1567803355181.0</span><span class="o">.</span><span class="na">html</span> <span class="nl">Timeout:</span> <span class="mi">4</span> <span class="n">s</span><span class="o">.</span> <span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="na">selenide</span><span class="o">.</span><span class="na">GoogleTest</span><span class="o">.</span><span class="na">openGoogle</span><span class="o">(</span><span class="nc">GoogleTest</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">42</span><span class="o">)</span> </code></pre></div></div> <p>Теперь при падении теста вы сразу видите скриншот и код странички. Этого достаточно, чтобы изучить причину падения. Не нужны больше никакие “красивые” отчёты и прочая усложнятина.</p> <p><em>Жизнь заиграла совсем другими красками, правда?</em></p> <p><br /></p> <h1 id="расследование">Расследование</h1> <p>Но почему так получилось?</p> <ol> <li>В Java есть класс <code class="language-plaintext highlighter-rouge">Throwable</code>, от которого наследуются все ошибки.</li> <li>В нём есть два метода: <code class="language-plaintext highlighter-rouge">getMessage()</code> и <code class="language-plaintext highlighter-rouge">toString()</code>.</li> <li>По умолчанию <code class="language-plaintext highlighter-rouge">toString()</code> = <code class="language-plaintext highlighter-rouge">имя класса</code> + <code class="language-plaintext highlighter-rouge">getMessage()</code></li> </ol> <p>Например, такой код:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Throwable</span> <span class="n">e</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">IllegalArgumentException</span><span class="o">(</span><span class="s">"низя"</span><span class="o">);</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> </code></pre></div></div> <p>выдаёт:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">низя</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">IllegalArgumentException</span><span class="o">:</span> <span class="n">низя</span> </code></pre></div></div> <p>Фокус в том, что изначально Selenide добавлял имя скриншота именно в методе <code class="language-plaintext highlighter-rouge">UIAssertionError.toString()</code>. И это прекрасно работало в Gradle, Ant, всех IDE. Да и в Maven тоже - в дефалтовом <code class="language-plaintext highlighter-rouge">maven-surefire-plugin</code>.</p> <p><br /></p> <h3 id="что-же-обновилось-в-maven">Что же обновилось в Maven?</h3> <p>В Maven вообще сидят весёлые ребята.</p> <ol> <li>В Maven для тестов зачем-то два плагина, а не один.</li> <li>Называются они почему-то “surefire”, а не какой-нибудь “test”, что было бы всем понятно.</li> <li>И по умолчанию последний Maven 3.6.2 почему-то использует <em>разные версии</em> этих плагинов: <ul> <li><code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.12.4</code> - выпущена <strong>7 лет назад</strong> (например, не поддерживает Java 11)</li> <li>и <em>внезапно</em> <br /><code class="language-plaintext highlighter-rouge">maven-surefire-report-plugin:3.0.0-M3</code> - типа <strong>бета-версия</strong>, как бы ещё сырая</li> </ul> </li> </ol> <p><br /></p> <p>Но вот если прописать в своём pom.xml более новую версию плагина:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">&lt;plugin&gt;</span> <span class="nt">&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="reveal-selenide-proxy-server">Открыли доступ к прокси-серверу</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="add-css-value-check">Добавили проверку <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="remove-wrong-logback-dependency">Подчистили левую зависимость <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="remove-duplicate-screenshots">Подчистили задвоенные скриншоты</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="support-browser-binary-for-remote-browser">Починили <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="news">Новости</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="исправили-ошибку-когда-при-софт-ассертах-не-делался-скриншот">Исправили ошибку, когда при софт ассертах не делался скриншот</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/655">issue 655</a> - спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/659">PR 659</a></p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Вот так новость! <a href="https://docs.qameta.io/allure/#_selenide">Allure запил поддержку Selenide</a></li> <li>Selenide перешёл на т.н. “семантическое версионирование”. То есть теперь новые версии будут называться 4.11.1, 4.11.2, 4.11.3 (и я надеюсь, выходить чаще).</li> <li>У Selenide появилось два новых коммитера. Встречайте героев: <ul> <li><a href="https://github.com/BorisOsipov">Борис Осипов</a> (на самом деле уже с осени)</li> <li><a href="https://github.com/rosolko">Александр Расолка</a></li> </ul> <p>Мы растём, и это вселяет оптимизм. :)</p> </li> </ul> <h2 id="ресурсы">Ресурсы</h2> <ul> <li><a href="https://docviewer.yandex.ee/view/0/?*=8tBKEPLAMQW8t2PRjyqwqnpdneR7InVybCI6InlhLWRpc2stcHVibGljOi8vcFN3SHRMY1QwQi96UCtYNXhSOXI4TTBGNzhxOEo4amFWWW5uNHA2YmNNYz0iLCJ0aXRsZSI6IkFsbHVyZSBpbnRlZ3JhdGlvbnMucGRmIiwidWlkIjoiMCIsInl1IjoiOTExNzE4NDM2MTUxODEyMzY5MCIsIm5vaWZyYW1lIjpmYWxzZSwidHMiOjE1MTgxMjM3NTkwOTF9">Интеграция всего с Allure</a> (в том числе Selenide, конечно)</li> <li>Отличный доклад <a href="https://www.slideshare.net/ysparrow/webdriver-waits">Selenium Waits: deep dive</a> от Ярослава Пернеровского на SeleniumCamp 2018. <br />Главный вывод - огонь!</li> <li><a href="http://www.autotest.org.ua/selenide-quick-start-in-automation-testing/">Selenide- быстрый старт в автоматизации тестирования</a></li> <li>Туториал для начинающих <a href="https://installselenium.weebly.com/">An installer for Selenide</a></li> <li>Selenide упомянут в книге <a href="https://books.google.ee/books?id=nszcDgAAQBAJ&amp;pg=PA258&amp;lpg=PA258&amp;dq=%22selenide%22+java&amp;source=bl&amp;ots=9nGbDkOllN&amp;sig=_Q1Qvlxv7c0W1w7FO7_7yQK0WFc&amp;hl=en&amp;sa=X&amp;ved=0ahUKEwir-IHg0tjZAhUGtRQKHbD3CFM4MhDoAQgzMAI#v=onepage&amp;q=%22selenide%22%20java&amp;f=false">Learning Vue.js 2</a></li> <li>Какие-то черти сделали <a href="http://aadays.pl/wp-content/uploads/2017/10/Presentation_Selenide_AADays.pdf">презентацию про Selenide на польском на AADays</a></li> <li>Мой доклад <a href="https://www.youtube.com/watch?v=zOiSo1hYjF8">Flaky tests</a> с конференции SeleniumCamp 2018</li> <li>Мой доклад <a href="https://www.youtube.com/watch?v=ePvrXUCeAr8">Arrange, mazafaka!</a> с конференции SeleniumCamp 2018</li> </ul> <p><br /> И целая пачка материалов про Selenide на японском языке:</p> <ul> <li><a href="http://backpaper0.github.io/ghosts/try-selenide/">70-страничный туториал</a></li> <li>Ещё один hello world <a href="https://qiita.com/tashxii/items/78e9288f7956a81caac1">с Selenide и сервлетом</a></li> <li>Заметка <a href="https://qiita.com/tenten0213/items/24ab7872ce26bbc47f18">про проблему sendKeys в headless Chrome и её решение с помощью Selenide</a></li> <li>http://www.atmarkit.co.jp/ait/articles/1803/05/news010.html</li> <li>https://qiita.com/shimashima35/items/918b26c4260e764ce90a</li> </ul> <p><br /> Всем тёплого апреля! <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/04/02/selenide-4.11/ http://ru.selenide.org/2018/04/02/selenide-4.11 2018-04-02T00:00:00+00:00 Вышла Selenide 4.10 <p>Категорический привет!</p> <p>Новогодние каникулы оказались очень плодотворными. Мы успели смержить кучу пуллреквестов, которые долго ждали своего часа.</p> <p>Итак, с чем мы встречаем старый новый год?</p> <p><strong>Что вы получите, обновившись на Selenide 4.10</strong>?</p> <p><br /></p> <h1 id="добавили-метод-scrollintoview">Добавили метод <code class="language-plaintext highlighter-rouge">$.scrollIntoView()</code></h1> <p>Его удобно использовать, чтобы прокрутить браузер к нужному месту. Например, это бывает полезно, чтобы обойти баги браузеров, которые иногда не могут кликнуть на элемент, находящийся за областью видимости.</p> <p>Если конкретнее, метод <code class="language-plaintext highlighter-rouge">$.scrollIntoView()</code> даёт команду браузеру проскроллить страницу так, чтобы данный элемент оказался в видимой области экрана.</p> <p>У метода может быть как boolean параметр:</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">"#my-button"</span><span class="o">).</span><span class="na">scrollIntoView</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span> <span class="c1">// верх кнопки будет у верхней границы видимой области</span> <span class="err">$</span><span class="o">(</span><span class="s">"#my-button"</span><span class="o">).</span><span class="na">scrollIntoView</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c1">// низ кнопки будет у нижней границы видимой области</span> </code></pre></div></div> <p>так и более продвинутый строкой параметр:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">"#my-button"</span><span class="o">).</span><span class="na">scrollIntoView</span><span class="o">(</span><span class="s">"{block: \"end\"}"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#my-button"</span><span class="o">).</span><span class="na">scrollIntoView</span><span class="o">(</span><span class="s">"{behavior: \"instant\", block: \"end\", inline: \"nearest\"}"</span><span class="o">);</span> </code></pre></div></div> <p>Подробности читайте в javadoc, там всё есть.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/649">issue 649</a></p> <p>Спасибо <a href="https://github.com/rosolko">Aleksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/656">pull request 656</a>.</p> <p><br /></p> <h1 id="теперь-можно-запускать-браузеры-headless-на-гриде">Теперь можно запускать браузеры <em>headless</em> на гриде</h1> <p>Если вы гоняете тесты на Selenium Grid, вы сможете открывать там браузеры в режиме headless. Для этого надо всего-то навсего пару строк:</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">remote</span> <span class="o">=</span> <span class="s">"http://selenium-grid-host:port/wd/hub"</span><span class="o">;</span> <span class="nc">Configuration</span><span class="o">.</span><span class="na">headless</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/660">issue 660</a></p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/661">pull request 661</a>.</p> <p><br /></p> <h1 id="теперь-можно-устанавливать-кастомные-капабилити-для-chrome-и-firefox">Теперь можно устанавливать “кастомные капабилити” для Chrome и Firefox</h1> <p>Вы можете в настройках Selenide прописать свои “Capabilities”, и Selenide будет их автоматически использовать при открытии браузера:</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">browserCapabilities</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DesiredCapabilities</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">SOME_CAP</span><span class="o">,</span> <span class="s">"SOME_VALUE_FROM_CONFIGURATION"</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/556">issue 556</a></p> <p>Спасибо <a href="https://github.com/SergeyPirogov">Sergey Pirogov</a> за <a href="https://github.com/selenide/selenide/pull/556">pull request 556</a> и <a href="https://github.com/BorisOsipov">Boris Osipov</a> за <a href="https://github.com/selenide/selenide/pull/664">pull request 664</a>.</p> <p><br /></p> <h1 id="мы-ускорили-итерирование-коллекций-">Мы ускорили итерирование коллекций <code class="language-plaintext highlighter-rouge">$$</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">"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> <p>Потому, что на каждом шаге Selenide загружает все элементы заново. См. <a href="https://github.com/selenide/selenide/issues/641">issue 641</a></p> <p>Мы это дело подправили. Теперь этот цикл должен работать гораздо быстрее. Спасибо <a href="https://github.com/CaBocuk">Artem Savosik</a> за <a href="https://github.com/selenide/selenide/pull/653">pull request 653</a>.</p> <p><br /></p> <h1 id="добавили-заголовок-user-agent-при-скачивании-файлов">Добавили заголовок “User-Agent” при скачивании файлов</h1> <p>Как вы знаете, Selenide скачивает файлы с помощью отдельного http-запроса, к которому он добавляет все куки из браузера. Кое-где метод <code class="language-plaintext highlighter-rouge">$().download()</code> не работал, потому что хитрое приложение, кроме кук, проверяло ещё и <code class="language-plaintext highlighter-rouge">User-Agent</code>. См. <a href="https://github.com/selenide/selenide/issues/639">issue 639</a></p> <p>Теперь Selenide копирует из заголовок <code class="language-plaintext highlighter-rouge">User-Agent</code> из браузера в запрос.</p> <p>Спасибо <a href="https://github.com/rosolko">Aleksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/657">pull request 657</a>.</p> <p><br /></p> <h1 id="теперь-методы-bytext-и-withtext-понимают-неразрывные-пробелы">Теперь методы <code class="language-plaintext highlighter-rouge">byText</code> и <code class="language-plaintext highlighter-rouge">withText</code> понимают “неразрывные” пробелы</h1> <p>Представьте, что на странице есть такой элемент:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;span&gt;</span>Hello<span class="ni">&amp;nbsp;</span>world<span class="nt">&lt;/span&gt;</span> </code></pre></div></div> <p>До сих пор команда <code class="language-plaintext highlighter-rouge">$(byText("Hello world"))</code> или <code class="language-plaintext highlighter-rouge">$(withText("Hello world"))</code> его не находила. Теперь будет находить.</p> <p>Мы научили методы <code class="language-plaintext highlighter-rouge">byText</code> и <code class="language-plaintext highlighter-rouge">withText</code> понимать “неразрывные” пробелы, т.е. “non-breakable spaces”, или символы <code class="language-plaintext highlighter-rouge">&amp;nbsp</code> или <code class="language-plaintext highlighter-rouge">\u00A0</code>. Точнее, эти пробелы теперь приравниваются к обычным пробелам.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/597">issue 597</a></p> <p><br /></p> <h1 id="обновились-до-phantomjsdriver-144">Обновились до phantomjsdriver 1.4.4</h1> <p>Который, в свою очередь, отличается от предыдущей версии только тем, что обновлён на selenium 3.8.1. Правда, при этом несколько его собственных тестов сломались, и мы их тупо заигнорили. Так что если вы всё ещё используете PhantomJS - смотрите в оба.</p> <p><br /></p> <h1 id="обновились-до-htmlunit-229-и-guava236-jre">Обновились до htmlunit 2.29 и guava:23.6-jre</h1> <p>… На всякий случай</p> <p><br /></p> <h2 id="новости">Новости</h2> <p>Приглашаю всех в Киев на отличную конференцию <a href="https://seleniumcamp.com/">SeleniumCamp 2-3 марта 2018</a>!</p> <p>2 дня, три трека. 500+ участников. И Киев - прекрасный город.</p> <p>Программа конференции ещё формируется, но <a href="https://seleniumcamp.com/speakers/">список докладчиков</a> уже впечатляет. И я там буду, мёд-пиво пить!</p> <p><br /> Всех с @Deprecated Новым Годом! <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2018/01/12/selenide-4.10/ http://ru.selenide.org/2018/01/12/selenide-4.10 2018-01-12T00:00:00+00:00 Вышла Selenide 4.9 <p>Всем привет!</p> <p>Под звуки салютов 31 декабря мы выпустили новую версию Selenide 4.9.</p> <p>Что под капотом?</p> <h1 id="обновились-до-selenium-381">Обновились до selenium 3.8.1</h1> <p>Там есть <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">обратно-несовместимые изменения</a>, так что будьте внимательны. Ребята из selenium любят удалять существующий функционал :)</p> <p>А здесь есть описание <a href="https://selenium2.ru/news/196-selenium-38.html">по-русски</a>.</p> <p>Спасибо <a href="https://github.com/rosolko">Aleksandr Rasolka</a> за <a href="https://github.com/selenide/selenide/pull/638">pull request 638</a>.</p> <p><br /></p> <h1 id="включили-marionette-по-умолчанию">Включили marionette по умолчанию</h1> <p>Как вы знаете, испокон веков дефалтовым браузером в Selenide был Firefox. Причина проста: это был единственный вебдрайвер, не требующий скачивания отдельного бинарника для управления браузером. Потом ситуация изменилась. FireFoxDriver больше не работает с новыми версиями Firefox, ему на замену пришёл GeckoDriver (aka Marionette). Он требует скачивания бинарника, но теперь Selenide умеет скачивать его автоматически.</p> <p>В общем, если вы не указываете браузер, Selenide теперь по умолчанию использует Marionette, т.е. Firefox (хотя лично я предпочитаю Chrome).</p> <p>Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за <a href="https://github.com/selenide/selenide/pull/621">pull request 621</a>.</p> <p><br /></p> <h1 id="починили-инициализацию-элементов-elementscontainer-без-аннотации-findby">Починили инициализацию элементов ElementsContainer без аннотации @FindBy</h1> <p>См. <a href="https://github.com/selenide/selenide/issues/617">issue 617</a></p> <p>Спасибо <a href="https://github.com/CaBocuk">Artem Savosik</a> за <a href="https://github.com/selenide/selenide/pull/618">pull request 618</a>.</p> <p>Хотя лично я не использую <code class="language-plaintext highlighter-rouge">ElementsContainer</code> и вам не советую. Это пережиток нашей бурной молодости. Сейчас Selenide позволяет писать пэдж обжекты / контейнеры гораздо проще.</p> <p><br /></p> <h1 id="добавили-методы-itemfirst3-и-itemlast3">Добавили методы <code class="language-plaintext highlighter-rouge">$$(“.item”).first(3)</code> и <code class="language-plaintext highlighter-rouge">$$(“.item”).last(3)</code></h1> <p>Методы позволяют получить несколько элементов из начала или конца списка. Полезно, когда список длинный или его длина неизвестна, а важно проверить только некоторые элементы.</p> <p>Впрочем, дискуссии всё ещё продолжается, правильно ли оно реализовано. Мы будем держать вас в курсе.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/623">issue 623</a></p> <p>Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за <a href="https://github.com/selenide/selenide/pull/624">pull request 624</a>.</p> <p><br /></p> <h1 id="возможность-указать-путь-к-бинарнику-браузера">Возможность указать путь к бинарнику браузера</h1> <p>Иногда вы хотите запускать тесты с разными версиями Firefox или там Chrome. На стабильной, на canary, на ночном билде. Ну, знаете, всякие люди хотят всякого :)</p> <p>Теперь вы можете явно указать, где искать бинарник браузера. Либо через системное свойство:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.browserBinary=/usr/lib/chrome.exe </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">browserBinary</span> <span class="o">=</span> <span class="s">"/usr/lib/firefox"</span><span class="o">;</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за <a href="https://github.com/selenide/selenide/pull/627">pull request 627</a>.</p> <p><br /></p> <h1 id="исправили-метод-prompt-для-htmlunit">Исправили метод <code class="language-plaintext highlighter-rouge">prompt()</code> для HtmlUnit</h1> <p>Спасибо <a href="https://github.com/simple-elf">Anton Aftakhov</a> за <a href="https://github.com/selenide/selenide/pull/634">pull request 634</a></p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <ul> <li>webdrivermanager:2.0.1</li> <li>guava:23.5-jre</li> <li>httpcore:4.4.8</li> <li>htmlunit-driver:2.28.2</li> </ul> <h2 id="новости">Новости</h2> <ul> <li>Долгожданное видео с Гейзенбага: <a href="https://www.youtube.com/watch?v=jLG3RXECQU8">Flaky tests</a> (№2 в топ лучших докладов конференции)</li> <li>Как запустить тесты с Selenide на Amazon Lambda <a href="https://aws.amazon.com/blogs/devops/ui-testing-at-scale-with-aws-lambda/">UI Testing at Scale with AWS Lambda</a></li> <li>Моя статья про нестабильные тесты в Java Advent Calendar: <a href="https://www.javaadvent.com/2017/12/flaky-tests.html">Flaky tests</a></li> <li>Статья про Selenide на японском: <a href="https://qiita.com/shimashima35/items/411f99a27b7ec5503532">Using WebDriverManager with Selenide</a></li> <li>Ещё одна: <a href="https://qiita.com/motoki1990/items/abe3b7472097d7e6085f">Toward people who want to utilize Selenide</a></li> <li>Статья из японского “Selenium/Appium Advent Calendar 2017”: <a href="https://qiita.com/shimashima35/items/0575ac5488edd6942d5a">Запись видео с Selenide и Monte recorder</a></li> <li>Статья про <a href="https://www.linkedin.com/pulse/awesome-reporting-vigo-selenide-kushan-shalindra-amarasiri-">тест-репорты с Selenide и Vigo Reporting</a></li> </ul> <p><br /> Всех с Новым Годом! <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2017/12/20/selenide-4.9/ http://ru.selenide.org/2017/12/20/selenide-4.9 2017-12-20T00:00:00+00:00 Вышла Selenide 4.8 <p>Всем привет!</p> <p>Мы выпустили новую версию Selenide 4.8. В основном это обновление Selenium.</p> <h1 id="обновились-до-selenium-36">Обновились до selenium 3.6</h1> <p>В этой версии селениума появились кое-какие обратно несовместимые изменения, поэтому мы решили с сделать отдельный релиз.</p> <p>Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за <a href="https://github.com/selenide/selenide/pull/611">pull request 611</a>.</p> <p>Кстати, заодно мы обновились до <code class="language-plaintext highlighter-rouge">guava 23.0</code> и <code class="language-plaintext highlighter-rouge">org.apache.httpcomponents:httpcore:4.4.7</code>. Мало ли, вдруг кому важно.</p> <h1 id="исправили-ошибку-с-софт-ассертами">Исправили ошибку с софт ассертами</h1> <p>Спасибо <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> за исправление <a href="https://github.com/selenide/selenide/issues/614">issue 614</a></p> <p><br /></p> <p>Вот и всё. В этот раз обновление должно быть лёгким, так что не откладывайте!</p> <h2 id="дружеское-напоминание">Дружеское напоминание</h2> <p>Приходите 8-9 декабря на конференцию <a href="https://heisenbug-moscow.ru/">Гейзенбаг</a> в Москве!</p> <p>Я буду там выступать с двумя докладами:</p> <ul> <li><a href="https://heisenbug-moscow.ru/talks/2017/msk/1su57z0to8qimacswsgksu/">Flaky tests</a> - полезность 80 lvl</li> <li><a href="https://heisenbug-moscow.ru/talks/2017/msk/74qfnsvag4gcsi4sw8gyoi/">Selenide Puzzlers</a> - весёлость 80 lvl</li> </ul> <p><strong>Там и познакомимся!</strong></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2017/10/08/selenide-4.8/ http://ru.selenide.org/2017/10/08/selenide-4.8 2017-10-08T00:00:00+00:00 Вышла Selenide 4.7 <p>Всем привет!</p> <p>Мы выпустили новую версию Selenide 4.7 с самой долгожданной фичей за последний год.</p> <p>Теперь-то уж заживём!</p> <p><br /></p> <h1 id="встроенный-webdrivermanager">Встроенный WebDriverManager</h1> <p>Теперь Selenide поставляется вместе со встроенным <a href="https://github.com/bonigarcia/webdrivermanager">WebDriverManager</a>. Это такая библиотека, которая умеет автоматически скачивать свежий бинарник вебдрайвера. То есть вам больше не нужно заботиться о том, откуда скачать <code class="language-plaintext highlighter-rouge">geckodriver.exe</code> или <code class="language-plaintext highlighter-rouge">chromedriver.exe</code> и в какую папку положить, чтобы он оказался в <code class="language-plaintext highlighter-rouge">PATH</code>. Selenide позаботится об этом.</p> <p>Есть один нюанс. WebDriverManager сработает только в том случае, если:</p> <ol> <li>Вы используете один из поддерживаемых им драйверов: <code class="language-plaintext highlighter-rouge">chrome</code>, <code class="language-plaintext highlighter-rouge">edge</code>, <code class="language-plaintext highlighter-rouge">internet explorer</code>, <code class="language-plaintext highlighter-rouge">opera</code>, <code class="language-plaintext highlighter-rouge">phantomjs</code>, <code class="language-plaintext highlighter-rouge">marionette</code> или <code class="language-plaintext highlighter-rouge">gecko</code>.</li> <li>Какой-нибудь ваш код ещё не успел установить системное свойство <code class="language-plaintext highlighter-rouge">webdriver.chrome.driver</code>, <code class="language-plaintext highlighter-rouge">webdriver.edge.driver</code>, <code class="language-plaintext highlighter-rouge">webdriver.ie.driver</code>, <code class="language-plaintext highlighter-rouge">webdriver.opera.driver</code>, <code class="language-plaintext highlighter-rouge">phantomjs.binary.path</code> или <code class="language-plaintext highlighter-rouge">webdriver.gecko.driver</code>.</li> </ol> <p>Если вы используете какой-нибудь другой вебдрайвер (или кастомный <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>), WebDriverManager просто не запустится, и у вас всё будет по-прежнему.</p> <p>Если у вас ещё будут вопросы, не забывайте, что единственный достоверный источник информации - <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/webdriver">ИСХОДНЫЙ КОД</a>!</p> <p>Спасибо Сергею Пирогову и Борису Осипову за этот <a href="https://github.com/selenide/selenide/pull/610">pull request</a></p> <p>См. также issue <a href="https://github.com/selenide/selenide/issues/133">133</a> и <a href="https://github.com/selenide/selenide/issues/418">418</a></p> <h1 id="добавили-метод-prompt">Добавили метод <code class="language-plaintext highlighter-rouge">prompt()</code></h1> <p>Как вы знаете, в JavaScript есть три нативных диалоговых окошка: <code class="language-plaintext highlighter-rouge">alert</code>, <code class="language-plaintext highlighter-rouge">confirm</code> и <code class="language-plaintext highlighter-rouge">prompt</code>. Поддержка двух первых была в Selenide уже давно, а третьего не было. Конечно, для этого была своя причина: использование этих диалогов в современных веб-приложениях считается моветоном, есть миллион JS-библиотек, которые делают это в тыщу раз красивее.</p> <p>Но всё же пробел исправлен:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">canSubmitPromptDialog</span><span class="o">()</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">click</span><span class="o">();</span> <span class="n">prompt</span><span class="o">(</span><span class="s">"Please input your username"</span><span class="o">,</span> <span class="s">"Aegon Targaryen"</span><span class="o">);</span> <span class="n">prompt</span><span class="o">(</span><span class="s">"Please input your password"</span><span class="o">,</span> <span class="s">"qwerty"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#message"</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, Aegon Targaryen!"</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/simple-elf">Anton Aftakhov</a> за <a href="https://github.com/selenide/selenide/pull/591">PR 591</a></p> <p><br /></p> <h1 id="добавили-путь-к-html-файлу-в-сообщение-об-ошибке">Добавили путь к html-файлу в сообщение об ошибке</h1> <p>Одна из самых удобных фишек Selenide в том, что он автоматически делает скриншот при падении теста и добавляет путь к файлу скриншота к сообщению об ошибке. Очень удобно смотреть отчёты об упавших тестах: один клик - и ты уже видишь скриншот, и для этого не нужно запускать какие-нибудь там Thycidides, Allure, Report Portal и прочие сложные штуки. Простые вещи - самые надёжные.</p> <p>Но есть ещё одна фишка, о который не все знали. Вместе со скриншотом Selenide также автоматически сохраняет html-код странички, на которой произошла ошибка. Но нигде не говорит об этом. Люди видят только путь к скриншоту и не догадываются, что есть ещё и html.</p> <p>Этот пробел и исправил <a href="https://github.com/hyunil-shin">hyunil-shin</a> в <a href="https://github.com/selenide/selenide/pull/590">PR 590</a>. Теперь сообщение об ошибке будет содержать оба файла:</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">Goodbye</span><span class="o">,</span> <span class="nc">Aegon</span> <span class="nc">Targaryen</span><span class="o">!</span><span class="err">'</span> <span class="o">{</span><span class="err">#</span><span class="n">message</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">"message"</span><span class="o">&gt;</span><span class="nc">Hello</span><span class="o">,</span> <span class="nc">Aegon</span> <span class="nc">Targaryen</span><span class="o">!&lt;/</span><span class="n">div</span><span class="o">&gt;</span><span class="err">'</span> <span class="nl">Screenshot:</span> <span class="nl">file:</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">canSubmitPromptDialog</span><span class="o">/</span><span class="mf">1507061177250.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="n">build</span><span class="o">/</span><span class="n">reports</span><span class="o">/.../</span><span class="n">canSubmitPromptDialog</span><span class="o">/</span><span class="mf">1507061177250.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> </code></pre></div></div> <p><br /></p> <h1 id="исправили-путаницу-со-скриншотами-при-параллельном-запуске-тестов">Исправили путаницу со скриншотами при параллельном запуске тестов</h1> <p>Руководство ФБР долгое время пыталось замолчать это, но в Selenide была одна неприятная <a href="https://github.com/selenide/selenide/issues/570">бага</a>. А именно, при параллельном запуске тестов он мог путать скриншоты. Всё из-за злостного использования статической переменной.</p> <p>Хорошо, что теперь с этим покончено. Спасибо <a href="https://github.com/jane-ryabchenko">Jane Riabchenko</a> за <a href="https://github.com/selenide/selenide/issues/595">PR 595</a></p> <p>А статические переменные мы планируем искоренить в большом рефакторинге в версии Selenide 5.0</p> <p><br /></p> <h1 id="обновились-до-selenium-java-353">Обновились до selenium-java 3.5.3</h1> <p>За это время уже успел выйти selenium-java 3.6.0, на него мы обновимся в Selenide 4.8, который выйдет уже совсем скоро.</p> <h1 id="обновились-до-browsermob-core-215-и-littleproxy-110-beta-bmp-17">Обновились до browsermob-core 2.1.5 (и littleproxy 1.1.0-beta-bmp-17)</h1> <p>Это может затронуть только тех, кто скачивает файлы через прокси-сервер. Хотя нет, это вообще никого не затронет. Забудьте.</p> <h1 id="обновили-htmlunit-с-224-до-227">Обновили htmlunit с 2.24 до 2.27</h1> <p>Фиг знает, что там улучшилось, но спасибо <a href="https://github.com/alexander-kotlyar">Alexander Kotlyar</a> за <a href="https://github.com/selenide/selenide/pull/609">PR 609</a>.</p> <h2 id="реклама">Реклама</h2> <p>Приходите 8-9 декабря на конференцию <a href="https://heisenbug-moscow.ru/">Гейзенбаг</a> в Москве!</p> <p>Я буду там выступать с двумя докладами:</p> <ul> <li><a href="https://heisenbug-moscow.ru/talks/2017/msk/1su57z0to8qimacswsgksu/">Flaky tests</a> - полезность 80 lvl</li> <li><a href="https://heisenbug-moscow.ru/talks/2017/msk/74qfnsvag4gcsi4sw8gyoi/">Selenide Puzzlers</a> - весёлость 80 lvl</li> </ul> <p><strong>Там и познакомимся!</strong></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2017/09/30/selenide-4.7/ http://ru.selenide.org/2017/09/30/selenide-4.7 2017-09-30T00:00:00+00:00 Вышла Selenide 4.6 <p>Всем привет!</p> <p>Мы выпустили новую версию Selenide 4.6!</p> <p><br /></p> <h1 id="метод-shouldhaveexactvalue-foo---больше-не-обрезает-пробелы-в-начале-и-конце-строки">Метод <code class="language-plaintext highlighter-rouge">$.shouldHave(exactValue(" foo "))</code> больше не обрезает пробелы в начале и конце строки</h1> <p>Как известно, метод <code class="language-plaintext highlighter-rouge">$.shouldHave(value(" foo "))</code>, который все мы используем в большинстве случаев, обрезает начальные и конечные пробелы в строке. Это удобно, потому что браузеры тоже так делают. Но вот оказалось, что то же самое делал и метод <code class="language-plaintext highlighter-rouge">exactValue()</code>, а это уже нелогично.</p> <p>Предположим, у вас на страничке есть <code class="language-plaintext highlighter-rouge">&lt;textarea value="Regression test description "&gt;&lt;/textarea&gt;</code>. Проверка <code class="language-plaintext highlighter-rouge">$("textarea").shouldHave(exactValue("Regression test description "));</code> раньше валилась, а теперь нет.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/573">issue 573</a></p> <p>Спасибо <a href="https://github.com/mseele">@mseele</a> за <a href="https://github.com/selenide/selenide/pull/578">pull request</a>!</p> <h1 id="добавили-метод-getsearchcriteria">Добавили метод <code class="language-plaintext highlighter-rouge">$.getSearchCriteria()</code></h1> <p>В Selenide всегда был удобный метод <code class="language-plaintext highlighter-rouge">$.toString()</code> - он выводил актуальные детали веб-элемента: тэг, текст, атрибуты. Это очень удобно при отладке и логировании. Но у него есть недостаток: за всеми этими деталями он обращается к вебдрайверу, что занимает немножко времени и иногда может упасть.</p> <p>А мы добавили новый метод <code class="language-plaintext highlighter-rouge">$.getSearchCriteria()</code>, который не обращается к вебдрайверу, а просто выводит локатор, по которому элемент пытались искать.</p> <p>Одним словом,</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">"h2"</span><span class="o">).</span><span class="na">toString</span><span class="o">(),</span> <span class="n">equalTo</span><span class="o">(</span><span class="s">"&lt;h2&gt;Dropdown list&lt;/h2&gt;"</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">"h2"</span><span class="o">).</span><span class="na">getSearchCriteria</span><span class="o">(),</span> <span class="n">equalTo</span><span class="o">(</span><span class="s">"h2"</span><span class="o">));</span> </code></pre></div></div> <p><br /></p> <h1 id="исправили-непотребность-528">Исправили непотребность <a href="https://github.com/selenide/selenide/issues/528">528</a></h1> <p>Спасибо <a href="https://github.com/BorisOsipov">@BorisOsipov</a> за <a href="https://github.com/selenide/selenide/pull/530">pull request</a>!</p> <h1 id="исправили-непотребность-484">Исправили непотребность <a href="https://github.com/selenide/selenide/issues/484">484</a></h1> <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">valid</span><span class="o">-</span><span class="n">id</span><span class="o">.</span><span class="na">findBy</span><span class="o">(</span><span class="n">text</span> <span class="err">'</span><span class="no">INVALID</span><span class="o">-</span><span class="no">TEXT</span><span class="err">'</span><span class="o">)}</span> <span class="nl">Expected:</span> <span class="n">text</span> <span class="err">'</span><span class="mi">9802222</span><span class="o">-</span><span class="mi">99L</span><span class="no">X</span><span class="err">'</span> <span class="nl">Screenshot:</span> <span class="kc">null</span> <span class="nl">Timeout:</span> <span class="mi">0</span> <span class="n">ms</span><span class="o">.</span> </code></pre></div></div> <p>Бывает. Исправили.</p> <p><br /></p> <h1 id="починили-запуск-ie-на-гриде">Починили запуск IE на гриде</h1> <p>(Неужели в 21 веке всё ещё кто-то использует грид? И кто-то использует IE? Бог ты мой, ещё и их вместе?)</p> <p>См. <a href="https://github.com/selenide/selenide/issues/529">issue 529</a></p> <p>Спасибо <a href="https://github.com/BorisOsipov">@BorisOsipov</a> за <a href="https://github.com/selenide/selenide/pull/531">pull request</a>!</p> <h2 id="новости">Новости</h2> <p><br /></p> <ul> <li><a href="http://www.devmedia.com.br/api-selenide-desenvolvimento-de-testes-funcionais-em-java/33680">Статья о Selenide на португальском</a></li> <li><a href="http://www.autotest.org.ua/selenide-quick-start-in-automation-testing/">Selenide- быстрый старт в автоматизации тестирования</a></li> <li><a href="https://sweftt.com/en/2017/02/07/selenide-tests-maven-way/">Тесты на Selenide по-мавеновски</a></li> <li><a href="http://qiita.com/radiocat/items/7440c4f8da2101e13761">Заметки о Selenide на японском</a></li> <li><a href="http://codezine.jp/article/detail/10335">Прям длинный туториал на японском</a></li> <li><a href="http://automation-remarks.com/2017/allure-without-annotations/">Как перестать писать @Step аннотации для Allure</a></li> </ul> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2017/09/09/selenide-4.6/ http://ru.selenide.org/2017/09/09/selenide-4.6 2017-09-09T00:00:00+00:00 Вышла Selenide 4.5 <p>Привет, друзья!</p> <p>Мы выпустили новую версию Selenide 4.5! В основном этот релиз посвящён улучшению производительности самого селенида.</p> <p><br /></p> <h1 id="ускорили-метод-setvalue">Ускорили метод <code class="language-plaintext highlighter-rouge">$.setValue()</code></h1> <p>В Selenide была крутая фича, про которую, как оказалось, никто не знал: метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> был настолько умным, что умел проставлять значение не только для простых текстовых полей, но и для выпадающих списков и радио-кнопок. Идея была в том, что тест должен оставаться стабильным, когда разработчики поменяют тип элемента, скажем, с <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> на <code class="language-plaintext highlighter-rouge">&lt;input type="radio"&gt;</code>.</p> <p>Но эта “умность” была не бесплатной: во время каждого вызова <code class="language-plaintext highlighter-rouge">$.setValue()</code> Selenide пытался определить тип элемента, а это несколько вызовов вебдрайвера. То есть несколько десятков (если не сотен) миллисекунд.</p> <p>Поскольку всё равно никто об этом не знал и не использовал, мы решили отказаться от этой фичи в угоду скорости. Теперь мы сделали это “умное” поведение <strong>выключенным по умолчанию</strong>. Если кому очень надо, можно его включить следующей настройкой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">-</span><span class="nc">Dselenide</span><span class="o">.</span><span class="na">versatileSetValue</span><span class="o">=</span><span class="kc">true</span> </code></pre></div></div> <p>Хотя есть способ проще:</p> <ul> <li>либо включите <code class="language-plaintext highlighter-rouge">-Dselenide.fastSetValue=true</code>,</li> <li>либо используйте методы <code class="language-plaintext highlighter-rouge">$.selectOption()</code> и <code class="language-plaintext highlighter-rouge">$.selectRadio()</code>.</li> </ul> <p>Спасибо <a href="https://github.com/selenide/selenide/pull/518">Alexander Popov</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">"tr"</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">7</span><span class="o">));</span> </code></pre></div></div> <p>ждала ещё сколько-то миллисекунд (по умолчанию 600 мс), даже если элементов сразу было 7. Потому, что “а вдруг сейчас ещё несколько подгрузится, и будет уже не 7”.</p> <p>И от этой фичи мы решили отказаться в угоду производительности.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/549">pull request 549</a></p> <p><br /></p> <h1 id="добавили-методы-x-и-x">Добавили методы <code class="language-plaintext highlighter-rouge">$().$x()</code> и <code class="language-plaintext highlighter-rouge">$().$$x()</code></h1> <p>… для поиска по XPath внутри веб-элемента</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"</span><span class="o">).</span><span class="n">$x</span><span class="o">(</span><span class="s">".//tr"</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">"table"</span><span class="o">).</span><span class="err">$</span><span class="n">$x</span><span class="o">(</span><span class="s">".//tr"</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">5</span><span class="o">));</span> </code></pre></div></div> <p>Не знаю, зачем, но людям вот нравится…</p> <p>Спасибо <a href="https://github.com/selenide/selenide/pull/533">Oleksii Cherevatyi</a>!</p> <p><br /></p> <h1 id="исправили-поддержку-opera-driver">Исправили поддержку Opera driver</h1> <p>Не знаю, зачем кому-то понадобился Opera driver, но кому-то понадобился…</p> <p>Спасибо <a href="https://github.com/selenide/selenide/commit/28233d6a88c9758c453629de1710818f28af6b84">Roman Marinsky</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="nl">INFO:</span> <span class="nc">Close</span> <span class="n">proxy</span> <span class="nl">server:</span> <span class="mi">24</span> <span class="o">-&gt;</span> <span class="kc">null</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/selenide/selenide/commit/03c781a3f644ec34782f04a28c08ec483b40143d">Andrew Zakordonets</a>!</p> <p><br /></p> <h1 id="обновились-до-orglittleshootlittleproxy112">Обновились до org.littleshoot:littleproxy:1.1.2</h1> <p>Это касается только тех, кто использует опцию <code class="language-plaintext highlighter-rouge">Configuration.fileDownload=PROXY</code></p> <p><br /></p> <h1 id="обновились-до-phantomjsdriver-143">Обновились до phantomjsdriver 1.4.3</h1> <p>Я вообще не представляю, кого это касается, но имейте в виду. :)</p> <p><br /></p> <h1 id="добавили-много-юнит-тестов-для-самого-selenide">Добавили много юнит-тестов для самого Selenide</h1> <p>Это мой личный праздник. Andrew Zakordonets решил помочь проекту - взял да и дописал кучу недостающих юнит-тестов для самого Селенида. Вот прямо круто!</p> <p><br /></p> <h2 id="новости">Новости</h2> <p><br /></p> <h1 id="мы-стартанули-проект-selenide-appium">Мы стартанули проект selenide-appium</h1> <p>Мы создали отдельный проект для лучшей поддержки Selenide в Appium framework: <a href="https://github.com/selenide/selenide-appium">github.com/selenide/selenide-appium</a></p> <p>Проект пока в зачаточном состоянии, но вполне рабочий. Есть как минимум один реальный проект, где эта библиотека уже используется.</p> <p><br /></p> <h1 id="мы-добавили-несколько-новых-проектов-в-selenide-examples">Мы добавили несколько новых проектов в Selenide examples</h1> <p>Как вы знаете, на гитхабе есть серия мини-проектов - <a href="https://github.com/selenide-examples">примеров использования Selenide с разными инструментами</a>. Недавно мы добавили туда парочку новых примеров:</p> <ul> <li><a href="https://github.com/selenide-examples/testcontainers">Selenide+TestContainers</a></li> <li><a href="https://github.com/selenide-examples/selenide-appium">Selenide+Appium</a></li> </ul> <p>Мы работаем над новыми примерами. В ближайших - Spring Boot, Allure 2 и бог знает что ещё. Присылайте свои примеры!</p> <p><br /></p> <h2 id="а-ты-помог-selenide">А ты помог Selenide?</h2> <p>Дорогой друг! И ты тоже можешь помочь проекту Selenide. Для этого даже не надо быть семи пядей во лбу или иметь 20 лет опыта в разработке.</p> <p>Вот что ты можешь сделать:</p> <ul> <li>Просмотреть открытые <a href="https://github.com/selenide/selenide/pulls">пуллреквесты</a> и дать свою оценку в комментариях, можно ли мержить эти изменения в Selenide</li> <li>Просмотреть открытые <a href="https://github.com/selenide/selenide/issues">тикеты</a> и дать свою оценку в комментариях: <ul> <li>не фигню ли просят</li> <li>не сделано ли это уже</li> <li>стоит ли это делать</li> </ul> </li> <li>Дописать недостающую документацию</li> <li>Написать блог-пост</li> <li>Дописать недостающие тесты</li> <li>Добавить свои примеры использования Selenide с другими инструментами на <a href="https://github.com/selenide-examples">github.com/selenide-examples</a>.</li> <li>Найти добровольца-дизайнера для редизайна сайта (в первую очередь под мобильники)</li> </ul> <p>Любая помощь приветствуется. Как говорится, нас - рать!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2017/06/26/selenide-4.5/ http://ru.selenide.org/2017/06/26/selenide-4.5 2017-06-26T00:00:00+00:00 Вышла Selenide 4.3 <p>Привет, друзья! Давно не виделись.</p> <p>В феврале у нас вышел релиз Selenide 4.3</p> <p><br /></p> <h1 id="исправили-метод-find">Исправили метод <code class="language-plaintext highlighter-rouge">$$().find()</code></h1> <p>Точнее, он и раньше искал элементы правильно, но некорректно сообщал об ошибке. Сообщение об ошибке могло ввести в заблуждение. Теперь ругается как надо.</p> <p>См. <a href="https://github.com/selenide/selenide/pull/426">pull request 426</a></p> <p>Спасибо <a href="https://github.com/juliaviluhina">Юлии Илюхиной</a> за исправление!</p> <p><br /></p> <h1 id="исправили-поля-по-selenideelement-без-аннотации-findby">Исправили поля ПО <code class="language-plaintext highlighter-rouge">SelenideElement</code> без аннотации <code class="language-plaintext highlighter-rouge">@FindBy</code></h1> <p>Оказалось, все эти годы Selenide вёл себя неожиданно, когда его пытались использовать неожиданно. :)</p> <p>Представим, что вы создали пэдж обжект с полями типа <code class="language-plaintext highlighter-rouge">SelenideElement</code> (без аннотации <code class="language-plaintext highlighter-rouge">@FindBy</code>):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">StartPage</span> <span class="o">{</span> <span class="nc">SelenideElement</span> <span class="n">startPage</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#start-page"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Этот класс - прелесть! Он простой и понятный, он работает сам по себе без всяких фабрик и прочих утилит. Достаточно просто его создать: <code class="language-plaintext highlighter-rouge">StartPage page = new StartPage();</code>, и он работает. Это идеальный пэдж обжект, который возможен только в мире Selenide.</p> <p>Но иногда люди по привычке пытаются его инициализировать через всякие там фактори методы, как то:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">StartPage</span> <span class="n">page</span> <span class="o">=</span> <span class="n">page</span><span class="o">(</span><span class="nc">StartPage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> </code></pre></div></div> <p>и тут начиналась чехарда. См. <a href="https://github.com/selenide/selenide/pull/443">pull request 443</a></p> <p>Но теперь это в прошлом.</p> <p>Спасибо <a href="https://github.com/SergeyPirogov">Сергею Пирогову</a> за исправление!</p> <p><br /></p> <h1 id="привели-в-порядок-названия-настроек">Привели в порядок названия настроек</h1> <p>Как вы знаете, все настройки Selenide можно установить двумя способами: либо через system property, либо прямо в коде. Но не всегда названия этих настроек соответствовали друг другу один-в-один.</p> <p>Мы привели их в порядок, так что теперь любой настройке вида <code class="language-plaintext highlighter-rouge">Configuration.someProp</code> всегда соответствует system property <code class="language-plaintext highlighter-rouge">selenide.someProp</code>. Старые настройки пока тоже сохранены для обратной совместимости.</p> <p><br /></p> <h1 id="задепрекячили-метод-selenideselectradio">Задепрекячили метод <code class="language-plaintext highlighter-rouge">Selenide.selectRadio</code></h1> <p>Раньше выбрать radiobutton можно было таким методом: <code class="language-plaintext highlighter-rouge">selectRadio(By.name("me"), "cat");</code> Теперь он помечен как <code class="language-plaintext highlighter-rouge">@Deprecated</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="nc">By</span><span class="o">.</span><span class="na">name</span><span class="o">(</span><span class="s">"me"</span><span class="o">)).</span><span class="na">selectRadio</span><span class="o">(</span><span class="s">"cat"</span><span class="o">);</span> </code></pre></div></div> <p><br /></p> <h1 id="создали-ссылку-на-свежий-javadoc">Создали ссылку на свежий Javadoc</h1> <p>Теперь актуальный javadoc всегда будет доступен по постоянной ссылке (без циферок):</p> <center> <a href="https://selenide.org/javadoc/current/">selenide.org/javadoc/current/</a> </center> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости</h1> <p>Поскольку Selenium не обновляется уже несколько месяцев, а душа просит свежего мяса, пришлось довольствоваться мелочами:</p> <ul> <li>обновились до browsermob-core:2.1.4</li> <li>обновились до org.apache.httpcomponents:httpcore:4.4.6</li> <li>обновились до guava:21.0</li> </ul> <h2 id="новости">Новости</h2> <ul> <li>Исторический момент! BrowserStack написали туториал, как запускать тесты Selenide в их облаках:<br /> <a href="https://www.browserstack.com/automate/selenide">BrowserStack+Selenide</a></li> <li>Разоблачение Selenide от Сергея Пирогова: <a href="http://automation-remarks.com/2016/selenide-shadow-sides/index.html">Тёмная сторона Selenide</a> <br /> Если кто не понял, данный релиз 4.3 как раз эту проблему и исправляет (см. <a href="https://github.com/selenide/selenide/pull/443">pull request 443</a>)</li> <li>Семинар Якова Крамаренко в Сан-Франциско: <a href="http://www.slideshare.net/yashaka/kiss-pageobjects-012017">Kiss PageObjects</a></li> <li>Новая статья про Selenide на японском: <a href="http://qiita.com/nyakome/items/207daf3050809c269e8e">I tried writing a test code with Selenide</a></li> <li>Сравнение Selenide и GEB на японском: <a href="http://qiita.com/PoohSunny/items/8641f24fa22e5b3beb16">Selenide as seen from Geb usage</a></li> <li>И ещё один туториал на японском: <a href="http://naruto-io.hatenablog.com/entry/2017/01/15/205751">Kotlin+Selenide</a></li> </ul> <h2 id="seleniumcamp">SeleniumCamp!</h2> <p>И внимание! Приглашаю всех на конференцию SeleniumCamp, которая пройдёт уже совсем скоро:</p> <center> <h3><a href="http://seleniumcamp.com/" target="_blank">SeleniumCamp.com</a></h3> <h4>24-25 февраля</h4> <h5>Киев</h5> </center> <p>Киев - прекрасный город, а SeleniumCamp - отличная конференция. Там будут и прекрасно приготовленные базовые знания про <a href="http://seleniumcamp.com/talk/start-writing-good-functional-tests-in-java-with-webdriver/">функциональные тесты</a> и <a href="http://seleniumcamp.com/talk/everything-you-want-to-know-about-page-object-design-pattern/">пэдж обжекты</a> от Николая Алименкова, и про наболевший <a href="http://seleniumcamp.com/talk/protractor-framework-how-to-make-stable-e2e-tests-for-angular-applications/">Angular</a>, и экзотика про <a href="http://seleniumcamp.com/talk/how-java-8-can-simplify-test-automation/">Java 8</a> и даже <a href="http://seleniumcamp.com/talk/excuse-me-sir-do-you-have-a-moment-to-talk-about-tests-in-kotlin/">Kotlin</a>, и батл <a href="http://seleniumcamp.com/talk/testng-vs-junit-5-battle/">TestNG против JUnit</a>, и очередное <a href="http://seleniumcamp.com/talk/better-bullshit-driven-development/">поливание грязью BDD</a> и многое другое.</p> <p>Но главное -</p> <ul> <li>полноценный двухчасовой <a href="http://seleniumcamp.com/talk/selenide101-getting-up-and-running-with-real-web-tests/">семинар по Selenide</a> и</li> <li><a href="http://seleniumcamp.com/talk/qa-livecoding-battle-typed-element-framework-jdi-vs-untyped-elements-framework-selenide/">батл Selenide против JDI</a></li> </ul> <p><br /> <br /></p> <p>Это того стоит, отвечаю!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2017/02/09/selenide-4.3/ http://ru.selenide.org/2017/02/09/selenide-4.3 2017-02-09T00:00:00+00:00 Selenide 4.2.1 released <p><a href="https://selenide.org/2017/01/26/selenide-4.2.1/">На английском языке.</a></p> http://ru.selenide.org/2017/01/26/selenide-4.2.1/ http://ru.selenide.org/2017/01/26/selenide-4.2.1 2017-01-26T00:00:00+00:00 Вышла Selenide 4.2 <p><br /> Привет!</p> <p>И снова эксперименты. Этот анонс написан не Андреем Солнцевым, а другими участниками Selenide комьюнити!</p> <p>Версии 4.х только начинают добавлять полезности для общение Selenide с постоянно меняющимся окружающим миром, но в этой версии нам удалось сделать большой шаг вперед по поддержке последних экспериментов британских учёных над браузерами.</p> <h2 id="поддержка-legacy-firefox-driver-431">Поддержка legacy Firefox driver. <a href="https://github.com/selenide/selenide/issues/431">#431</a></h2> <p>Как известно, Firefox, начиная с версии 48, не поддерживается встроенным драйвером Selenium, и вы должны использовать marionnete/gecko драйвер. Новый драйвер работает также и со старыми версиями Firefox, однако, во многих случаях он недостаточно стабилен. Проекты с большим количеством написанных тестов на данный момент часто используют Firefox 45 ESR, потому что затраты на анализ проблем и исправление тестов с gecko драйвером могут быть крайне высоки. В версии 4.2, мы даем вам возможность выбирать какой драйвер использовать для Firefox версий &lt;=47.</p> <p>Используйте <code class="language-plaintext highlighter-rouge">Configuration.browser="firefox"</code> (или через системное свойство <code class="language-plaintext highlighter-rouge">-Dselenide.browser=firefox</code>) для использования встроенного legacy Firefox Driver (Firefox&lt;=47).</p> <p>Используйте <code class="language-plaintext highlighter-rouge">Configuration.browser="gecko"</code> или <code class="language-plaintext highlighter-rouge">Configuration.browser="marionette"</code> (тоже самое) для использования geckodriver (все версии Firefox). Если сам драйвер у вас не находится в path, то вам так же понадобится установить путь к нему в SystemProperty <code class="language-plaintext highlighter-rouge">webdriver.gecko.driver</code> (например <code class="language-plaintext highlighter-rouge">-Dwebdriver.gecko.driver=/Users/mmeier/Downloads/geckodriver-0.11</code>)</p> <h2 id="поддержка-microsoft-edge-браузера">Поддержка Microsoft Edge браузера</h2> <p>Мы добавили новое значение <code class="language-plaintext highlighter-rouge">Configuration.browser="edge"</code>. Так же не забывайте указывать путь к MicrosoftWebDriver.exe в SystemProperty <code class="language-plaintext highlighter-rouge">webdriver.edge.driver</code>.</p> <h2 id="лучшая-поддержка-https-сайтов-с-невалидными-сертификатами">Лучшая поддержка https сайтов с невалидными сертификатами.</h2> <p>В этой версии мы добавили еще одну Capability, которая в некоторых случаях лучше справляется с проблемами невалидных SSL сертификатов. Для использование вам ничего не надо изменять в тестах, улучшение работает со всеми браузерами автоматически.</p> <h2 id="поддержка-кастомных-capabilities-и-облачных-сервисов-browserstack-saucelabs-и-другие-379">Поддержка кастомных capabilities и облачных сервисов (BrowserStack, SauceLabs и другие) <a href="https://github.com/selenide/selenide/issues/379">#379</a></h2> <p>До сих пор, у пользователей Selenide не было удобной возможности передавать специфические capabilities, чтобы изменять поведение браузера. Начиная с этой версии, SystemProperties с префиксом <code class="language-plaintext highlighter-rouge">capabilities.*</code> автоматически обрабатываются Selenide как DesiredCapabilities. Это позволяет легко запускать ваши тесты популярными облачными сервисами не изменяя ни строчки кода!</p> <p>В качестве примера для запуска теста на платформе BrowserStack (конечно, вы должны сперва зарегистрироваться и получить username и auth_key) под Windows 7 в Firefox 48 c разрешением 1680х1050 при запуске теста передайте параметры :</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.remote=https://&lt;username&gt;:&lt;auth_key&gt;@hub-cloud.browserstack.com/wd/hub -Dcapabilities.os=windows -Dcapabilities.os_version=7 -Dcapabilities.browser=firefox -Dcapabilities.browser_version=48.0 -Dcapabilities.resolution=1680x1050 </code></pre></div></div> <p>Какие именно capabilities поддерживает платформа - вы можете узнать из документации соответствующего сервиса</p> <h2 id="прочие-изменения">Прочие изменения</h2> <ul> <li>Исправлен <a href="https://github.com/selenide/selenide/issues/433">#433</a> bypass spawning local browser</li> <li>Добавлен метод <code class="language-plaintext highlighter-rouge">$.selectOptionContainingText(String)</code> <a href="https://github.com/selenide/selenide/issues/391">#391</a> - который позволяет выбирать select-опцию по подстроке.</li> <li>Исправлена проблема со скриншотами слишком больших WebElement <a href="https://github.com/selenide/selenide/issues/378">#378</a></li> </ul> <p><br /> <br /></p> <p>Обновляйтесь!</p> <p><a href="https://github.com/selenide/selenide">Alexei Vinogradov &amp; Selenide Community</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/12/30/selenide-4.2/ http://ru.selenide.org/2016/12/30/selenide-4.2 2016-12-30T00:00:00+00:00 Вышла Selenide 4.1 <p>Свершилось!</p> <p>Ура! Настал тот день. Вышел релиз Selenide 4.1, в котором всё сделано вашими руками, дорогие читатели. Ваши пуллреквесты.</p> <p><br /></p> <h1 id="428-improve-byattribute-method-for-search-via-css-selectors">#428 Improve byAttribute method for search via css selectors</h1> <p>Метод <code class="language-plaintext highlighter-rouge">byAttribute</code> (или короче <code class="language-plaintext highlighter-rouge">by</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="n">by</span><span class="o">(</span><span class="s">"first-name"</span><span class="o">,</span> <span class="s">"john macclane"</span><span class="o">).</span><span class="na">click</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="n">by</span><span class="o">(</span><span class="s">"first-name*"</span><span class="o">,</span> <span class="s">"hn maccl"</span><span class="o">).</span><span class="na">click</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="n">by</span><span class="o">(</span><span class="s">"first-name^"</span><span class="o">,</span> <span class="s">"joh"</span><span class="o">).</span><span class="na">click</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="n">by</span><span class="o">(</span><span class="s">"first-name$"</span><span class="o">,</span> <span class="s">"clane"</span><span class="o">).</span><span class="na">click</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="n">by</span><span class="o">(</span><span class="s">"first-name~"</span><span class="o">,</span> <span class="s">"john"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/rmarinsky">Роману Маринскому</a> за <a href="https://github.com/selenide/selenide/pull/428">pull request 428</a></p> <p><br /> <br /></p> <h1 id="419-425-added-methods-textslist-and-exacttextslist">#419 #425 Added methods <code class="language-plaintext highlighter-rouge">texts(List)</code> and <code class="language-plaintext highlighter-rouge">exactTexts(List)</code></h1> <p>Раньше можно было проверить тексты в коллекции, передав тексты как varargs параметры:</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">"#row-1 td"</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">"pjotr"</span><span class="o">,</span> <span class="s">"iljich"</span><span class="o">,</span> <span class="s">"chajkovskij"</span><span class="o">)));</span> </code></pre></div></div> <p>То теперь в дополнение к vararg можно использовать и список ожидаемых значений:</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">String</span><span class="o">&gt;</span> <span class="n">expected</span> <span class="o">=</span> <span class="n">asList</span><span class="o">(</span><span class="s">"pjotr"</span><span class="o">,</span> <span class="s">"iljich"</span><span class="o">,</span> <span class="s">"chajkovskij"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#row-1 td"</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="n">expected</span><span class="o">)));</span> </code></pre></div></div> <p>Спасибо</p> <ul> <li><a href="https://github.com/yashaka">Якову Крамаренко</a> за <a href="https://github.com/selenide/selenide/issues/419">предложение</a> и</li> <li><a href="https://github.com/pavelsmolensky">Павлу Смоленскому</a> за <a href="https://github.com/selenide/selenide/pull/425">реализацию</a>.</li> </ul> <p><br /></p> <h1 id="improve-support-for-gecko-marionette-driver">Improve support for gecko (marionette) driver</h1> <p>Теперь вместо</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">new</span> <span class="nc">MarionetteDriver</span><span class="o">(</span><span class="n">capabilities</span><span class="o">);</span> </code></pre></div></div> <p>Selenide инициализирует вебдрайвер так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">DesiredCapabilities</span> <span class="n">capabilities</span> <span class="o">=</span> <span class="n">createFirefoxCapabilities</span><span class="o">(</span><span class="n">proxy</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">"marionette"</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">FirefoxDriver</span><span class="o">(</span><span class="n">capabilities</span><span class="o">);</span> </code></pre></div></div> <p>Я пока сам не понял, хорошо это или плохо, но в следующей версии точно будут ещё улучшения для Marionette driver.</p> <p><br /></p> <h1 id="обновили-зависимости">Обновили зависимости:</h1> <ul> <li>selenium-java 3.0.1</li> <li>htmlunit-driver 2.23.2</li> <li>gson 2.8.0</li> <li>guava 20.0</li> <li>httpcore:4.4.5</li> </ul> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li><a href="http://automation-remarks.com/budtie-ostorozhny-s-testng-lisienierami/index.html">Будьте осторожны с TestNG листенерами</a> by Сергей Пирогов</li> <li>Книга <a href="https://www.gitbook.com/book/comaqa/selenium-webdriver-lectures/details">Selenium Webdriver для Java автоматизаторов</a> (в т.ч. про Selenide) by ComaQA</li> <li>Статья про Selenide <a href="http://qiita.com/shimashima35/items/a437f0ed080a9ba71b72">на японском языке</a></li> <li>Презентация про Selenide <a href="http://backpaper0.github.io/ghosts/try-selenide/index.html#1">на японском языке</a></li> <li><a href="https://github.com/backpaper0/selenide-demo">Демо Selenide</a> с Redmine и Docker</li> </ul> <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">AdminPage</span> <span class="kd">extends</span> <span class="nc">AbstractPage</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"commit"</span><span class="o">)</span> <span class="kd">public</span> <span class="nc">SelenideElement</span> <span class="n">デフォルト設定をロード</span><span class="o">;</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">css</span> <span class="o">=</span> <span class="s">".users"</span><span class="o">)</span> <span class="kd">public</span> <span class="nc">SelenideElement</span> <span class="n">ユーザー</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="kd">public</span> <span class="kt">void</span> <span class="n">添付ファイル数は</span><span class="o">(</span><span class="kt">int</span> <span class="n">size</span><span class="o">)</span> <span class="o">{</span> <span class="n">添付ファイル一覧</span><span class="o">.</span><span class="na">shouldHaveSize</span><span class="o">(</span><span class="n">size</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p><br /> <br /></p> <p>Версия 4.2 будет совсем скоро. Чего-то мы там с Marionette мутим.</p> <p>Обновляйтесь!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/12/01/selenide-4.1/ http://ru.selenide.org/2016/12/01/selenide-4.1 2016-12-01T00:00:00+00:00 Доклад о Selenide на TopConf 2016 <p>Привет!</p> <p>Выложу <a href="http://topconf.com/tallinn-2016/trackevent/selenide-concise-ui-tests-in-java/">презентацию про Selenide</a> на недавней конференции TopConf в Таллинне.</p> <p>Видео доклада:</p> <iframe src="//www.youtube.com/embed/hHwFIONnVRs" width="960" height="569" frameborder="0"></iframe> <p>И презентация:</p> <div class="wrapper-content center"> <iframe src="https://docs.google.com/presentation/d/1kH4tQuZujMYgW_gcHI0-ekNxV7dGspZbNIb4DVUgURs/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="960" height="569" allow="fullscreen" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe> </div> <p><br /></p> <p>Andrei Solntsev</p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/11/16/selenide-on-topconf/ http://ru.selenide.org/2016/11/16/selenide-on-topconf 2016-11-16T00:00:00+00:00 Обёртка или чистый вебдрайвер? <p>Бытует мнение, что для настоящего автоматизатора использовать Selenide - несерьёзно. Настоящий труёвый автоматизатор должен использовать чистый вебдрайвер - чтобы знать, как оно там внутри работает.</p> <p>И вот почему это плохо.</p> <h2 id="вам-шашечки-или-ехать">Вам шашечки или ехать?</h2> <p>Представьте себе: вы очень торопитесь на самолёт. Садитесь в такси, говорите: “Срочно в аэропорт!” А таксист вам говорит: сейчас я поставлю на раму мотор, прикручу колёса, залью топливо - и мы поедем!</p> <p>Ты офигеваешь, но ок, раз в этой индустрии так принято - значит, надо подождать.</p> <h3 id="покрытие">Покрытие</h3> <p>Ждёшь пять минут, нервно посматривая на часы. Говоришь: “Ну что, едем уже?” И слышишь: “Да, я уже половину гаек в моторе прикрутил.” И гордо добавляет: “У меня покрытие гайками 70%!”</p> <h3 id="отчёты">Отчёты</h3> <p>Ещё через пять минут ты снова нервно спрашиваешь и слышишь: “Да-да, всё круто! Сиденья уже почти установлены.” И гордо добавляет: “Можете посмотреть отчёт с картинками о проделанных шагах.”</p> <h3 id="зато-если-что">Зато если что</h3> <p>Ты громко ругаешься матом и говоришь: “Да какого чёрта? Почему бы нам просто не сесть в машину и не поехать?” А водитель говорит: “Пусть джуны ездят на заводских автомобилях. А настоящие водители должны знать, как оно там внутри работает.” И гордо добавляет: “Зато если что, я смогу разобраться, что сломалось.”</p> <h3 id="готовые-решения">Готовые решения</h3> <p>Стоит ли говорить, что через пять минут после начала движения у вас заливает свечи. Водила говорит: “Да, это типичная проблема. Я знаю, что делать. У меня так уже было. Там надо слип поставить в двух местах.”</p> <p>Потом отваливается колесо, и чувак с нескрываемой гордостью говорит, что он и с такой проблемой сталкивался, и у него в гараже есть готовое решение, которое он утащил с предыдущего проекта.</p> <p>Да, ребята, это тот чувак, который предпочитает голый селениум.</p> <h3 id="сел-и-поехал">Сел и поехал</h3> <p>Рано или поздно ты плюёшь, ловишь другую машину, в которой уже всё готово, настроено и заправлено. Которая просто едет. И водила в два счёта довозит тебя до аэропорта.</p> <p>А это автоматизатор, использующий Selenide.</p> <h2 id="так-что-же-не-развиваться">Так что же, не развиваться?</h2> <p>Надеюсь, на этом месте вам смешно. А многим - обидно. И это хорошо! Чем обиднее, тем лучше. Тем больше шансов, что это заставит задуматься о том, куда катится наша индустрия.</p> <p>Когда обида поутихнет, у вас должен возникнуть закономерный вопрос.</p> <p>Так что же, пользоваться готовым и не вникать в подробности? Неужто настоящий водитель не должен разбираться в том, как устроен двигатель и куда заливать масло? Неужто настоящий водитель не должен уметь починить машину, если она вдруг сломается посреди дороги?</p> <p><em>Если все будут ездить на всём готовом, у нас же настоящих спецов скоро не останется!</em></p> <h3 id="развиваться">Развиваться!</h3> <p>Конечно, развиваться надо! Обязательно.</p> <p>Плох тот автоматизатор, который не мечтает написать свой фреймворк.</p> <p><strong>Но не за счёт клиента!</strong></p> <p>Разбирайте двигатель. Копайтесь в железках. Играйтесь с солидолом. Но у себя в гараже. А на работе - будьте добры работать. Когда у вас есть свободное время и силы - совершенствуйтесь. Когда клиенту нужно в аэропорт - везите в аэропорт и используйте для этого наиболее эффективные средства. То есть Selenide, да.</p> <p><br /> <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/10/20/selenide-vs-pure-selenium/ http://ru.selenide.org/2016/10/20/selenide-vs-pure-selenium 2016-10-20T00:00:00+00:00 Вышла Selenide 4.0 <p>Привет!</p> <p>Это свершилось!</p> <p>После трёх лет ожидания вышел Selenium Webdriver 3.0!</p> <p>А мы выпустили Selenide 4.0.</p> <h2 id="что-изменилось">Что изменилось?</h2> <p>Собственно, для вас ничего не должно поменяться - они просто подчистили какой-то старый мусор из селениума (который в Selenide никогда и не использовался).</p> <p>Но что важно - теперь Selenide требует Java 8.</p> <p>Ура, теперь весь QA мир перейдёт наконец на Java 8.</p> <p>Вот мы теперь с лямбдами-то заживём!</p> <h2 id="а-можно-поподробнее">А можно поподробнее?</h2> <p>Конечно. Вот полный <a href="http://selenium2.ru/news/183-selenium-3-0.html">список изменений Selenium WebDriver 3.0.0</a>.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Отличная статья Николая Алименкова: <a href="http://xpinjection.com/articles/technical-debt-as-overweight/">Технический долг как избыточный вес</a></li> <li>Про другой большой апдейт для юнит-тестов: <a href="http://asolntsev.github.io/ru/2016/10/11/mockito-2.1/">Миграция на Mockito 2.1</a></li> <li>Про доброе старое: <a href="http://asolntsev.github.io/ru/2016/10/03/spring-boobs/">Spring boobs</a></li> <li>Отличный <a href="http://testingchallenges.thetestingmap.org/">портал для оттачивания навыков тестирования</a></li> <li>Интересное <a href="https://habrahabr.ru/company/jugru/blog/311254/">интервью с @jbaruch про open-source, стартапы и будни developer advocate</a></li> </ul> <p><br /> <br /></p> <p>Обновляйтесь!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/10/15/selenide-4.0/ http://ru.selenide.org/2016/10/15/selenide-4.0 2016-10-15T00:00:00+00:00 Вышла Selenide 3.11 <p>Привет!</p> <p>Мы выпустили Selenide 3.11! Не то чтобы тут много чего поменялось, скорее это просто исторический релиз. Это последний релиз из серии 3.x - последний, что работает на Java 7 и с Selenium WebDriver 2.x</p> <p>Дальше будет Selenide 4.x: Selenium WebDriver 3.x и Java 8.</p> <p>Итак, что же вошло в последнюю троечку:</p> <h2 id="добавили-условие-checked">Добавили условие <code class="language-plaintext highlighter-rouge">checked</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">"#i-agree"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">checked</span><span class="o">);</span> </code></pre></div></div> <p>Раньше приходилось писать <code class="language-plaintext highlighter-rouge">$.shouldBe(selected)</code>, что неочевидно. Раз есть чекбокс - значит, он должен быть “checked”.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/416">issue 416</a></p> <h2 id="оптимизация-getmessage">Оптимизация <code class="language-plaintext highlighter-rouge">getMessage()</code></h2> <p>В селениуме есть такая странная особенность. Когда вы получаете ошибку из вебдрайвера (то есть любой подкласс <code class="language-plaintext highlighter-rouge">SeleniumException</code>), у него вызывается метод <code class="language-plaintext highlighter-rouge">getMessage()</code>.</p> <p>А вот этот метод <code class="language-plaintext highlighter-rouge">SeleniumException.getMessage()</code> пытается быть слишком умным и добавляет в сообщение об ошибке IP и имя текущего компьютера. А это может занимать очень продолжительное время в зависимости от фазы луны. И оказалось, что Selenide дёргает этот несчастный метод несколько раз подряд.</p> <p>У меня однажды так и было: я оказался в необычной WiFi сетке, и внезапно мои тесты стало дико тормозить. Пара часов ушло на поиск причины, но это ещё полбеды. Беда в том, что исправить это невозможно - этот код зашит в Selenium WebDriver.</p> <p>Я лично считаю, что метод <code class="language-plaintext highlighter-rouge">getMessage()</code> ничего такого делать не должен. Это вообще не его работа. Пусть тот, кто запускает тесты на своих серверах, сам разбирается, на каких айпишниках он их запустил.</p> <p>Но авторы селениума прислушаться желают. И всё, что мы можем сделать - это только оптимизировать вызовы <code class="language-plaintext highlighter-rouge">getMessage()</code> в Selenide, так чтобы этот злосчастный метод не дёргался повторно.</p> <p>Что мы и сделали.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/415">issue 415</a></p> <p><br /> <br /></p> <p>Все в предвкушении Selenium 3.0…</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/10/14/selenide-3.11/ http://ru.selenide.org/2016/10/14/selenide-3.11 2016-10-14T00:00:00+00:00 Вышла Selenide 3.10 <p>Привет!</p> <p>Мы выпустили Selenide 3.10!</p> <h2 id="прокси-сервер-выключен-по-умолчанию">Прокси-сервер выключен по умолчанию</h2> <p>Мне грустно говорить об этом, но эксперимент с прокси-сервером, кажется, пока не удался.</p> <p>Проблема с новым способом скачивания файлов через встроенный в Selenide прокси-сервер. На всех моих проектах он работает, но многие жалуются, что у них не взлетело. Почему - пока неясно. Возможно, не работает у тех, кто использует Selenium Grid.</p> <p>В общем, <a href="https://github.com/selenide/selenide/issues/402">пока вернули</a> старый способ скачивания по умолчанию. Кто хочет - включайте скачивание через прокси с помощью настройки:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.fileDownload=PROXY </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">fileDownload</span> <span class="o">=</span> <span class="no">PROXY</span><span class="o">;</span> </code></pre></div></div> <p>Кстати, мы исправили ошибку <a href="https://github.com/selenide/selenide/issues/393">393</a>: Selenide даже не пытается стартовать прокси-сервер, пока соответствующая настройка не будет включена.</p> <h2 id="добавлена-поддержка-множественных-селектов">Добавлена поддержка множественных селектов</h2> <p>Знаете, бывают такие выпадающие списки, в которых можно выбрать несколько опций (<code class="language-plaintext highlighter-rouge">&lt;select multiple&gt;</code>). Теперь в Selenide можно тыкать и в такие. Несколько опций можно выбрать всего одной командой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">select</span><span class="o">.</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"Маргарита"</span><span class="o">,</span> <span class="s">"Theodor Woland"</span><span class="o">);</span> <span class="c1">// по тексту</span> <span class="n">select</span><span class="o">.</span><span class="na">selectOption</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="o">);</span> <span class="c1">// по индексу</span> <span class="n">select</span><span class="o">.</span><span class="na">selectOptionByValue</span><span class="o">(</span><span class="s">"cat"</span><span class="o">,</span> <span class="s">"woland"</span><span class="o">);</span> <span class="c1">// по значению</span> </code></pre></div></div> <p>А также мы добавили метод для получения списка всех выбранных опций:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">select</span><span class="o">.</span><span class="na">getSelectedOptions</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">"Theodor Woland"</span><span class="o">));</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/400">issue 400</a></p> <h2 id="можно-печатать-отчёт-только-для-упавших-тестов">Можно печатать отчёт только для упавших тестов</h2> <p>В Selenide можно печатать <a href="http://ru.selenide.org/2015/11/30/selenide-2.25">отчёт обо всех пройденных шагах</a>. Раньше он печатался всегда, а теперь появилась возможность явно указать, печатать ли его только для упавших или для успешных тестов.</p> <p>Для JUnit:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Rule</span> <span class="kd">public</span> <span class="nc">TestRule</span> <span class="n">report</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TextReport</span><span class="o">().</span><span class="na">onFailedTest</span><span class="o">(</span><span class="kc">true</span><span class="o">).</span><span class="na">onSucceededTest</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> </code></pre></div></div> <p>Для TestNG:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Listeners</span><span class="o">(</span><span class="nc">TextReport</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">GoogleTestNGTest</span> <span class="o">{</span> <span class="nd">@BeforeMethod</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="nc">TextReport</span><span class="o">.</span><span class="na">onSucceededTest</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="nc">TextReport</span><span class="o">.</span><span class="na">onFailedTest</span> <span class="o">=</span> <span class="kc">true</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/issues/408">issue 408</a></p> <h2 id="исправили-парсинг-заголовка-content-disposition-с-указанием-кодировки">Исправили парсинг заголовка <code class="language-plaintext highlighter-rouge">Content-Disposition</code> с указанием кодировки</h2> <p>Теперь Selenide использует правильное имя файла в случае, если заголовок <code class="language-plaintext highlighter-rouge">Content-Disposition</code> включает кодировку:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Content-Disposition: filename=Prices.csv;charset=UTF-8 </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/398">issue 398</a></p> <h2 id="исправили-багу-401-selenide-swallows-exception-in-some-cases">Исправили багу 401: “Selenide swallows exception in some cases”</h2> <p>Никогда не пишите <code class="language-plaintext highlighter-rouge">catch (Throwable)</code>. Никогда!</p> <p>См. <a href="https://github.com/selenide/selenide/issues/401">issue 401</a></p> <h2 id="метод-openurl-теперь-работает-и-для-url-большими-буквами">Метод <code class="language-plaintext highlighter-rouge">open(url)</code> теперь работает и для URL большими буквами</h2> <p>Это странно, но кому-то понадобилось ввести url большими буквами, и оказалось, что Selenide не умеет открывать такие файлы. УПС. Исправили.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/407">issue 407</a></p> <h2 id="вернули-старый-закомментированный-тест">Вернули старый закомментированный тест</h2> <p>Хотя, кажется, его скоро снова придётся закомментировать, потому что в Selenium 3.0 этот баг вернулся. :(</p> <p>См. <a href="https://github.com/selenide/selenide/issues/379">issue 379</a></p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Интервью Николая Алименкова на хабре: <a href="https://habrahabr.ru/company/jugru/blog/309502/">Сферическое тестирование в вакууме</a></li> <li>Мнение эксперта: <a href="http://xpinjection.com/articles/why-developers-do-not-use-tdd/">Почему разработчики не используют TDD</a></li> <li>Тестировать мобильники - как два пальца об экран! <a href="https://github.com/selenide-examples/selenide-appium">Примеры Selenide+Appium</a></li> <li>Новая опупенная конференция от jug.ru: <a href="https://habrahabr.ru/company/jugru/blog/308612/">Гейзенбаг</a> - горячо рекомендую!</li> </ul> <p><br /> <br /></p> <p>Обновляйтесь!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/09/26/selenide-3.10/ http://ru.selenide.org/2016/09/26/selenide-3.10 2016-09-26T00:00:00+00:00 Вышла Selenide 3.9.2 <p>Привет!</p> <p>Мы выпустили Selenide 3.9.2!</p> <p><br /> <br /></p> <h2 id="скачивать-файлы-можно-по-разному">Скачивать файлы можно по-разному</h2> <p>Как вы знаете, в Selenide 3.9 вы изменили способ скачивания файлов. Отзывы пользователей были кардинально разные: для одних новый способ работал там, где старый не работал, но у других, наоборот, новый способ не работает там, где старый работал.</p> <p>Поэтому мы решили дать пользователю выбор.</p> <p>Теперь вы можете выбирать, каким способом скачивать файлы.</p> <h3 id="новый-способ---proxy">“Новый” способ - PROXY</h3> <p>Это способ включен по умолчанию.</p> <p>Этот способ скачивает файлы с помощью встроенного прокси-сервера.</p> <h4 id="плюсы">Плюсы:</h4> <ul> <li>Этот способ лучше тем, что он более универсальный: он работает не только с <code class="language-plaintext highlighter-rouge">&lt;a href</code>, но и при сабмите форм, да и вообще в любом случае, когда сервер выдаёт ответ с файлом.</li> </ul> <h4 id="минусы">Минусы:</h4> <ul> <li>Если вы запускаете свой вебдрайвер (и добавляете его в Selenide с помощью команды <code class="language-plaintext highlighter-rouge">setWebDriver()</code>), то он не будет ничего знать о селенидовском прокси-сервере, который поэтому не получит доступ к ответам сервера. Мы подумаем, как это можно улучшить, а пока -</li> </ul> <h3 id="старый-способ---httpget">“Старый” способ - HTTPGET</h3> <p>Если вы хотите использовать старый способ, пропишите настройку <code class="language-plaintext highlighter-rouge">-Dselenide.fileDownload=HTTPGET</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">fileDownload</span> <span class="o">=</span> <span class="no">HTTPGET</span><span class="o">;</span> </code></pre></div></div> <h4 id="плюсы-1">Плюсы:</h4> <ul> <li>Работает независимо от наличия прокси-сервера.</li> </ul> <h4 id="минусы-1">Минусы:</h4> <ul> <li>Работает только элементами с <code class="language-plaintext highlighter-rouge">&lt;a href</code> и умеет делать только GET запросы.</li> <li>Может не работать, если с машины, где запускаются тесты, нет доступа к тому серверу, с которого скачиваются файлы.</li> </ul> <p>Теперь вам точно ничто не мешает обновиться на Selenide 3.9.2</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Новый QA дайджест №21: <a href="https://dou.ua/lenta/digests/qa-digest-21/">Немного о Jmeter, о языке автоматизации, полезные расширения для браузера</a></li> </ul> <h2 id="немного-статистики">Немного статистики</h2> <p>Статистика скачиваний Selenide за август 2016:</p> <center> <img src="/images/2016/09/selenide.downloads.png" width="800" /> </center> <p><br /> <br /></p> <p>Обновляйтесь!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/09/03/selenide-3.9.2/ http://ru.selenide.org/2016/09/03/selenide-3.9.2 2016-09-03T00:00:00+00:00 Вышла Selenide 3.9.1 <p>Привет!</p> <p>Мы выпустили Selenide 3.9.1!</p> <p><br /> <br /></p> <h3 id="киллер-фича-selenide-умеет-скачивать-любые-файлы-откуда-угодно">Киллер фича! Selenide умеет скачивать любые файлы! Откуда угодно.</h3> <p>Мы хотели сделать это давно, и вот наконец свершилось! Бесконечные проблемы со скачиванием файлов в селениуме ушли в прошлое. Теперь это легко. Всегда. Один простой метод на все случаи жизни:</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">"input#submit"</span><span class="o">).</span><span class="na">download</span><span class="o">();</span> </code></pre></div></div> <p>В Selenide метод <code class="language-plaintext highlighter-rouge">download</code> существует давно. Но до сих пор он умел скачивать файлы только по ссылке с атрибутом href: <code class="language-plaintext highlighter-rouge">&lt;a href</code>.</p> <p>Это было круто, но в некоторых случаях недостаточно. Например:</p> <ul> <li>Клик по кнопке сабмитит форму, а в результате сабмита начинает скачиваться файл.</li> <li>Клик по кнопке открывает PDF в новом табе (режим “inline” - встроенный просмотрщик PDF в браузере)</li> </ul> <p>Теперь же Selenide умеет скачивать любые файлы. Для этого он использует встроенный proxy-сервер (а именно, <a href="https://github.com/lightbody/browsermob-proxy">BrowserMobProxy</a>)</p> <h4 id="как-именно-работает-скачивание">Как именно работает скачивание:</h4> <ol> <li>Тест вызывает команду <code class="language-plaintext highlighter-rouge">$("selector").download();</code></li> <li>Selenide активизирует прокси-сервер</li> <li>Selenide кликает элемент</li> <li>Прокси сервер запоминает все ответы сервера (response), которые содержат http-заголовок <code class="language-plaintext highlighter-rouge">Content-Disposition</code>. Из него Selenide достаёт имя файла и сохранят файл с таким именем в папке <code class="language-plaintext highlighter-rouge">build/reports/tests</code>.</li> <li>Selenide ждёт (до 4 секунд), пока в списке не появится хотя бы один файл</li> <li>Метод <code class="language-plaintext highlighter-rouge">$.download()</code> возвращает первый скачанный файл.</li> <li>Если за это время появились новые окна, Selenide закрывает всех их. Это нужно для тех случаев, когда PDF открывается в новом окне браузера.</li> </ol> <p>См. <a href="https://github.com/selenide/selenide/issues/196">issue #196</a> и <a href="https://github.com/selenide/selenide/pull/267">pull request #267</a>.</p> <p>Огромное спасибо <a href="https://github.com/dimand58">Дмитрию Демину</a> за вклад в эту фичу, предложения и обсуждения!</p> <h3 id="прокси-сервер">Прокси-сервер</h3> <p>Да, теперь Selenide запускает свой прокси-сервер во время запуска тестов.</p> <p>В будущем мы сможем использовать прокси-сервер для реализации многих других полезных возможностей. Например, проверять http статусы страниц и других ресурсов, внедрять свой код в страницы и пр.</p> <p>Кидайте идеи, давайте побрейнстормим!</p> <h3 id="предупреждение-о-слишком-больших-запросах-и-ответах">Предупреждение о слишком больших запросах и ответах</h3> <p>А вот и первое использование прокси-сервера (помимо скачивания файлов). Теперь Selenide отслеживает размер запросов-ответов и пишет предупреждение в лог, если размер какого-то реквеста или респонса превышает 2 MB.</p> <p>Пока это экспериментальная фича. Расскажите, как она прижилась у вас. Обнаружили что-то полезное? Были ли проблемы?</p> <p>См. <a href="https://github.com/selenide/selenide/issues/383">issue #383</a></p> <h3 id="теперь-листенер-softasserts-нормально-работает-с-testng">Теперь листенер <code class="language-plaintext highlighter-rouge">SoftAsserts</code> нормально работает с TestNG</h3> <p>Оказалось, TestNG конкретная редиска и вообще нехороший фреймворк. Если вы объявляете <code class="language-plaintext highlighter-rouge">@Listeners(SoftAsserts.class)</code> у <strong>одного класса</strong>, TestNG автоматически применяет его ко <strong>всем</strong> вашим классам. Нет, ну это надо было додуматься, а!</p> <p>Мы нашли решение для этой проблемы. Теперь листенер <code class="language-plaintext highlighter-rouge">SoftAsserts</code> сам проверяет, объявлен ли он у текущего тестового класса (или его предка).</p> <p>См. <a href="https://github.com/selenide/selenide/issues/384">issue #384</a></p> <h3 id="теперь-аннотация-softasserts-для-testng-игнорирует-тесты-с-атрибутом-expectedexceptions">Теперь аннотация <code class="language-plaintext highlighter-rouge">SoftAsserts</code> для TestNG игнорирует тесты с атрибутом “expectedExceptions”</h3> <p>Если у вас есть такой тест:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Listeners</span><span class="o">(</span><span class="nc">SoftAsserts</span><span class="o">.</span><span class="na">class</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="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">myCheckA</span><span class="o">()</span> <span class="o">{...}</span> <span class="nd">@Test</span><span class="o">(</span><span class="n">expectedExceptions</span> <span class="o">=</span> <span class="o">...)</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">myCheckB</span><span class="o">()</span> <span class="o">{...}</span> <span class="o">}</span> </code></pre></div></div> <p>то очевидно, что <code class="language-plaintext highlighter-rouge">SoftAsserts</code> должны включаться в методе <code class="language-plaintext highlighter-rouge">myCheckA</code>, но <strong>не</strong> должны в методе <code class="language-plaintext highlighter-rouge">myCheckB</code>. Потому что он ожидает какую-то конкретную ошибку, и если <code class="language-plaintext highlighter-rouge">SoftAsserts</code> её спрячет, испортит всё дело.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/372">issue #372</a></p> <h3 id="исправили-ошибку-с-basic-auth-в-браузере-ie">Исправили ошибку с basic auth в браузере IE</h3> <p>См. <a href="https://github.com/selenide/selenide/issues/366">issue #366</a> и <a href="https://github.com/selenide/selenide/pull/369">pull request #369</a></p> <p>Спасибо <a href="https://github.com/simple-elf">Anton Aftakhov</a> за этот pull request!</p> <h3 id="обновились-до-библиотеки-gson-27">Обновились до библиотеки gson 2.7</h3> <p>Сам Selenium использует довольно старый gson 2.3.1, а в некоторых проектах бывает нужен более новый.</p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li>Моё интервью на хабре: <a href="https://habrahabr.ru/company/jugru/blog/308528/">О Legacy-коде без максимализма</a></li> <li><a href="http://asolntsev.github.io/ru/2016/08/05/why-programmer-cannot-be-true-tester/">Почему разработчики должны быть хорошими тестировщиками</a></li> <li>Вышел <a href="http://automation-remarks.com/videorecorder-java/">video-recorder 1.0.8</a> - и это чумовая вещь! Теперь production ready.</li> <li>Подкаст RadioQA <a href="http://radio-qa.com/28-scrum/">Выпуск 28: Стыд и scrum</a></li> </ul> <p><br /> <br /></p> <p>Обновляйтесь!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/08/27/selenide-3.9.1/ http://ru.selenide.org/2016/08/27/selenide-3.9.1 2016-08-27T00:00:00+00:00 Вышла Selenide 3.8 <p>Привет!</p> <p>Мы выпустили Selenide 3.8! В нём мы подправили много мелких недочётов и сделали некоторые небольшие, но полезные полезности.</p> <p><br /></p> <h3 id="исправлена-загрузка-файлов-upload-на-удалённых-браузерах-в-гриде">Исправлена загрузка файлов (upload) на удалённых браузерах в гриде</h3> <p>Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за исправление грида!</p> <h3 id="теперь-можно-отключать-создание-html-файлов">Теперь можно отключать создание *.html файлов</h3> <p>Когда ваш драгоценный тест падает, по умолчанию Selenide создаёт 2 файла:</p> <ol> <li>Скриншот - файл *.png</li> <li>Исходный код текущий страницы - файл *.html</li> </ol> <p>Оказалось, что не все хотят сохранять *.html-файлы. Например, в случае одностраничных приложений в этот html-файл попадал html-код всех-превсех страниц приложения, то есть файлы получались большими и бессмысленными.</p> <p>Теперь можно отключить сохранение html-файлов - как обычно, либо через системную переменную:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mvn <span class="nb">test</span> <span class="nt">-Dselenide</span>.savePageSource<span class="o">=</span><span class="nb">false</span> </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">savePageSource</span> <span class="o">=</span> <span class="kc">false</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/359">pull request</a>!</p> <h3 id="добавлен-метод-draganddroptowebelement">Добавлен метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo(WebElement)</code></h3> <p>До сих пор в Selenide был только метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo(String)</code>, где строковой параметр - это CSS селектор целевого элемента. А иногда хочется определять целевой элемент и по-другому.</p> <p>Для таких случаев теперь есть метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo(WebElement)</code> позволяет перетащить элемент в другое место. Например:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">SelenideElement</span> <span class="n">target</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">"Drop here"</span><span class="o">));</span> <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="n">target</span><span class="o">);</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/355">issue #355</a></p> <h3 id="теперь-аннотации-simplereport-и-softasserts-для-testng---потокобезопасные">Теперь аннотации <code class="language-plaintext highlighter-rouge">SimpleReport</code> и <code class="language-plaintext highlighter-rouge">SoftAsserts</code> для TestNG - потокобезопасные</h3> <p>Если вы используете аннотации <code class="language-plaintext highlighter-rouge">SimpleReport</code> или <code class="language-plaintext highlighter-rouge">SoftAsserts</code> и фреймворк TestNG (бедняги!), да ещё и запускаете тесты параллельно в несколько потоков, то вы могли замечать, что иногда отчёт о прохождении тестов пустой. А иногда могли ловить <code class="language-plaintext highlighter-rouge">ConcurrentModificationException</code>.</p> <p>Оказалось, TestNG конкретная редиска и вообще нехороший фреймворк. Если вы объявляете <code class="language-plaintext highlighter-rouge">@Listener(SoftAsserts)</code> у одного класса, TestNG создаёт только один инстанс класса <code class="language-plaintext highlighter-rouge">SoftAsserts</code> и использует для всех тестов, даже параллельных.</p> <blockquote> <p>А вот JUnit, умница и молодец, создаёт новый экземпляр каждого тест-класса и каждого правила для каждого следующего теста. Таким образом исключаются всяческие конфликты и случайные зависимости теста от предыдущих тестов.</p> </blockquote> <p>В общем, теперь мы это исправили, и <code class="language-plaintext highlighter-rouge">SoftAsserts</code> умеет работать параллельно в нескольких потоках даже в TestNG.</p> <p>См. issue <a href="https://github.com/selenide/selenide/issues/364">#364</a> и <a href="https://github.com/selenide/selenide/issues/303">#303</a>.</p> <h1 id="добавили-методы-в-класс-selectors-bycssselector-и-byclassname">Добавили методы в класс <code class="language-plaintext highlighter-rouge">Selectors</code>: <code class="language-plaintext highlighter-rouge">byCssSelector()</code> и <code class="language-plaintext highlighter-rouge">byClassName()</code></h1> <p>Я до сих пор не уверен, что это так уж нужно, но народ просил.</p> <p>Теперь в классе <code class="language-plaintext highlighter-rouge">Selectors</code> есть <code class="language-plaintext highlighter-rouge">by*</code> аналоги всех методов из селениумовского <code class="language-plaintext highlighter-rouge">By.*</code>.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/360">#360</a>.</p> <h3 id="исправили-javascript-ошибку-при-запуске-браузера-edge">Исправили JavaScript ошибку при запуске браузера Edge</h3> <p>Теперь Selenide можно использовать и с новым микрософтовским браузером Edge. Говорят, он быстр и свеж, как Тимати.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/339">#339</a>.</p> <h3 id="теперь-метод-screenshot-сохраняет-скриншот-в-правильное-место">Теперь метод <code class="language-plaintext highlighter-rouge">$.screenshot()</code> сохраняет скриншот в правильное место</h3> <p>Есть в Selenide такой метод, который сохраняет скриншот не всей страницы, а одного элемента:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">String</span> <span class="n">screenshotFile</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#footer"</span><span class="o">).</span><span class="na">screenshot</span><span class="o">();</span> </code></pre></div></div> <p>Всё бы хорошо, но он сохранял скриншоты в корневую папку проекта. Теперь мы его исправили, и он складывает файлы в папку <code class="language-plaintext highlighter-rouge">build/reports</code>, как и остальные методы для снятия скриншотов.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/290">#290</a>.</p> <h3 id="добавлена-защита-против-неправильного-использования-soft-asserts">Добавлена защита против неправильного использования soft asserts</h3> <p>Теперь Selenide кинет ошибку, если вы включите режим “Soft asserts”, но забудете добавить соответствующую аннотацию к тест-классу.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/302">#302</a>.</p> <h3 id="в-javadoc-всех-методов-selenideelement-добавлены-ссылки-на-имплементации-этих-методов">В Javadoc всех методов <code class="language-plaintext highlighter-rouge">SelenideElement</code> добавлены ссылки на имплементации этих методов</h3> <p>Например, метод <a href="https://selenide.org/javadoc/3.8/com/codeborne/selenide/SelenideElement.html#setValue-java.lang.String-">$(“input”).setValue(“hello”)</a> реализован в классе <a href="https://selenide.org/javadoc/3.8/com/codeborne/selenide/commands/SetValue.html"><code class="language-plaintext highlighter-rouge">SetValue</code></a></p> <p>См. <a href="https://github.com/selenide/selenide/issues/367">#367</a>.</p> <h3 id="обновились-до-htmlunit-223">Обновились до htmlunit 2.23</h3> <p>См. <a href="http://htmlunit.sourceforge.net/changes-report.html#a2.23">список изменений</a></p> <p><br /></p> <h2 id="новости">Новости</h2> <ul> <li><a href="http://itech.folderit.net/1232/selenide-framework-for-testing-automation/">Статья про Selenide на испанском</a> от Dario Lamy</li> <li><a href="http://asolntsev.github.io/ru/2016/08/05/why-programmer-cannot-be-true-tester/">Почему разработчики должны быть хорошими тестировщиками</a> - мутный чувак, но мысли правильные</li> <li>Новая крутая конференция в Питере от создателей Joker и JPoint: <a href="http://heisenbug.ru/">Гейзенбаг</a> - многообещающе! Планирую там выступить.</li> <li>Новая классная статья от дядюшки Боба: <a href="http://blog.cleancoder.com/uncle-bob/2016/07/27/TheChurn.html">ПЕНА</a></li> <li><a href="http://asolntsev.github.io/ru/2016/07/08/what-is-hardcode/">Что на самом деле нельзя хардкодить</a></li> <li><a href="http://asolntsev.github.io/ru/2016/07/09/true-page-object/">Настоящие пэдж-объекты</a></li> </ul> <h3 id="десерт">Десерт</h3> <p>И на десерт - свежая шутка про Чака Норриса:</p> <blockquote> <p>Чак Норрис не использует Selenide.<br /> Чак Норрис пишет тесты на голом селениуме.<br /> А что, он же бессмертный, у него есть на это время.</p> </blockquote> <p><br /> <br /></p> <p>Обновляйтесь!</p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/08/06/selenide-3.8/ http://ru.selenide.org/2016/08/06/selenide-3.8 2016-08-06T00:00:00+00:00 Вышла Selenide 3.7 <p>Привет!</p> <p>Мы выпустили Selenide 3.7!</p> <h2 id="обновились-на-selenium-2531">Обновились на Selenium 2.53.1</h2> <p>Это должно исправить очередную проблему с Firefox 47.</p> <h2 id="добавили-поддержку-браузера-marionette">Добавили поддержку браузера Marionette</h2> <p>Чтобы запустить свои тесты с браузером Marionette, используйте параметр в командной строке (или билд-скрипте):</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.browser=marionette </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">browser</span> <span class="o">=</span> <span class="s">"marionette"</span><span class="o">;</span> </code></pre></div></div> <p>см. <a href="https://github.com/selenide/selenide/pull/349">pull request #349</a></p> <p>Спасибо <a href="https://github.com/ridiekel">Geroen Dierckx</a> за этот pull request!</p> <h2 id="добавили-поддержку-драйверов-для-не-браузеров">Добавили поддержку драйверов для не-браузеров</h2> <p>В последнее время появились драйверы для не-браузеров, а, например,</p> <ul> <li>нативных Windows приложений (<a href="https://github.com/2gis/Winium">Winium</a>),</li> <li>Java Swing приложений (<a href="https://marathontesting.com/seleniumwebdriver-bindings/">Marathon</a>)</li> <li>и даже мобильных приложений (<a href="http://appium.io/">Appium</a>).</li> </ul> <p>Естественно, такие драйверы не поддерживают JavaScript. Оказалось, что Selenide на это не был рассчитан и без JavaScript отказывался работать.</p> <p>Теперь мы эту проблему устранили, и Selenide должен работать и с не-веб драйверами.</p> <p>см. <a href="https://github.com/selenide/selenide/issues/345">issue #345</a></p> <h2 id="убрали-бесполезное-сообщение-screenshots--если-скриншоты-отключены">Убрали бесполезное сообщение <code class="language-plaintext highlighter-rouge">Screenshots: </code>, если скриншоты отключены</h2> <p>см. <a href="https://github.com/selenide/selenide/pull/357">pull request #357</a></p> <p>Спасибо <a href="https://github.com/BorisOsipov">Boris Osipov</a> за этот pull request!</p> <p><br /></p> <p>Вот такое обновление.</p> <p><br /> <br /></p> <h1 id="новости">Новости</h1> <p>Позвольте вам посоветовать ещё три отличных доклада с <a href="http://seleniumcamp.com/materials/">SeleniumCamp 2016</a>.</p> <ul> <li><a href="http://seleniumcamp.com/talk/good-tests-change-your-application/">Хорошие тесты меняют ваше приложение</a> / Игорь Хрол</li> <li><a href="http://seleniumcamp.com/talk/time-to-mock-back-end/">Время прикрыть заднюю часть</a> / Алексей Буль</li> <li><a href="http://seleniumcamp.com/talk/frameworks-shrameworks-or-how-to-ruin-your-test-automation/">Фреймворки-шмымворки</a> / Микалай Алименков</li> </ul> <h2 id="немного-статистики">Немного статистики</h2> <p>Статистика скачиваний Selenide за июнь 2016:</p> <center> <img src="/images/2016/07/selenide.downloads.png" width="800" /> </center> <p>И статистика уникальных IP:</p> <center> <img src="/images/2016/07/selenide.unique-ips.png" width="800" /> </center> <p>Вот какую мы тему замутили!</p> <p>Больше новостей на сегодня нет, но вы держитесь там! Удачи, хорошего настроения!</p> <p><br /> <br /></p> <p><a href="http://asolntsev.github.io/">Андрей Солнцев</a></p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/07/08/selenide-3.7/ http://ru.selenide.org/2016/07/08/selenide-3.7 2016-07-08T00:00:00+00:00 Вышла Selenide 3.6 <p>Привет вам, рыцари ордена качества!</p> <p>В последний день весны мы всё-таки выпустили Selenide 3.6!</p> <h2 id="вернули-стратегию-загрузки-страницы-обратно-на-normal">Вернули стратегию загрузки страницы обратно на <code class="language-plaintext highlighter-rouge">normal</code></h2> <p>В Selenide 3.5 мы прописали дефалтовую стратегию страницы в <code class="language-plaintext highlighter-rouge">none</code>. Для некоторых пользователей это ускорило тесты, но другие стали жаловаться, что тесты повалились, и они вынуждены были вернуться на Selenide 3.4</p> <p>Теперь мы вернулись обратно на <code class="language-plaintext highlighter-rouge">normal</code>, а кто хочет ускорения тестов, прописывайте</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-Dselenide</span>.page-load-strategy<span class="o">=</span>none </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">pageLoadStrategy</span> <span class="o">=</span> <span class="s">"none"</span><span class="o">;</span> </code></pre></div></div> <p>см. <a href="https://github.com/selenide/selenide/issues/321">issue #321</a></p> <h2 id="теперь-пэдж-обжекты-не-обязательно-должны-быть-публичными">Теперь пэдж обжекты не обязательно должны быть публичными</h2> <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">GoogleSearchTest</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">SearchPage</span> <span class="n">search</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">SearchPage</span> <span class="o">{</span> <span class="kd">public</span> <span class="nc">ResultsPage</span> <span class="nf">searchFor</span><span class="o">(</span><span class="nc">String</span> <span class="n">keyword</span><span class="o">)</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">"q"</span><span class="o">)).</span><span class="na">val</span><span class="o">(</span><span class="n">keyword</span><span class="o">).</span><span class="na">pressEnter</span><span class="o">();</span> <span class="k">return</span> <span class="nf">page</span><span class="o">(</span><span class="nc">ResultsPage</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Сделайте его приватным, удалите ненужные конструкторы! Нечего плодить хлам.</p> <p>см. <a href="https://github.com/selenide/selenide/issues/335">issue #335</a></p> <h2 id="добавлена-поддержка-вебдрайвера-jbrowser">Добавлена поддержка вебдрайвера JBrowser</h2> <p>JBrowser - это очередная попытка создать headless браузер (как HtmlUnit и PhantomJS). Типа чтобы тесты бегали быстро и не мозолили глаза. Правда, пока он кажется ненадёжным: большинство собственных тестов Selenide на нём валится.</p> <p>Но вы можете попробовать сами: <code class="language-plaintext highlighter-rouge">-Dselenide.browser="jbrowser"</code>. И придётся ещё добавить зависимость в проект:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency</span> <span class="na">org=</span><span class="s">"com.machinepublishers"</span> <span class="na">name=</span><span class="s">"jbrowserdriver"</span> <span class="na">rev=</span><span class="s">"[0.13.0, 2.0)"</span> <span class="na">conf=</span><span class="s">"test-default"</span><span class="nt">/&gt;</span> </code></pre></div></div> <p>см. <a href="https://github.com/selenide/selenide/issues/329">issue #329</a></p> <p>Спасибо Anil Kumar Reddy Gaddam за этот pull request!</p> <h2 id="метод-download-теперь-использует-стандартный-селенидовский-таймаут">Метод $().download() теперь использует стандартный селенидовский таймаут</h2> <p>А раньше он мог зависать надолго. Один раз наш билд подвис на два часа на методе $.download()!</p> <p>см. <a href="https://github.com/selenide/selenide/issues/341">issue #341</a></p> <h2 id="добавлена-поддержка-basic-auth-для-основных-браузеров">Добавлена поддержка Basic Auth для основных браузеров</h2> <p>Примеры использования можно подсмотреть <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/BasicAuthTest.java">здесь</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Selenide</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="s">"http://httpbin.org/basic-auth/user/passwd"</span><span class="o">,</span> <span class="s">""</span><span class="o">,</span> <span class="s">"user"</span><span class="o">,</span> <span class="s">"passwd"</span><span class="o">);</span> </code></pre></div></div> <p>см. <a href="https://github.com/selenide/selenide/issues/320">issue #320</a></p> <p>Спасибо <a href="https://github.com/dimand58">dimand58</a> за этот pull request!</p> <h2 id="добавили-метод-screenshotasimage">Добавили метод <code class="language-plaintext highlighter-rouge">$.screenshotAsImage()</code></h2> <p>Он позволяет получить изображение одного отдельно взятого элемента. Полезно для дебага, особенно когда страница большая и всё не помещается.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">BufferedImage</span> <span class="n">elementScreenshot</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">".logo"</span><span class="o">).</span><span class="na">screenshotAsImage</span><span class="o">();</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/Akkuzin">Akkuzin</a> за этот pull request!</p> <h2 id="починили-testng-textreport-listener">Починили TestNG TextReport Listener</h2> <p>теперь отчёт будет генерироваться только для классов, аннотированных <code class="language-plaintext highlighter-rouge">@Report</code>.</p> <p>Если я правильно понял, это вроде как бага TestNG: если листенер объявлялся хотя бы в одном тесте,</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@Listeners(TextReport.class) </code></pre></div></div> <p>то он автоматически распространялся на все тесты. Не бог весть какая проблема, но всё-таки лишний хлам в логах. Вдруг вы хотите генерировать отчёт только с некоторых проблемных тестов.</p> <p>Теперь мы сделали так, что отчёт будет генерироваться не для всех классов, а только для тех, что помечены аннотацией <code class="language-plaintext highlighter-rouge">@Report</code>.</p> <p>Спасибо <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> за работу над несчастными листенерами TestNG!</p> <h2 id="обновили-phantomjsdriver-до-версии-130-совместима-с-selenium-java-2530">Обновили phantomjsdriver до версии 1.3.0 (совместима с selenium-java 2.53.0)</h2> <p>Проект PhantomJS Driver, к сожалению, на долгое время остановился в развитии. Его автор Ivan Marino сначала сообщил, что больше не может активно заниматься проектом, а потом и вовсе пропал. :(</p> <p>Но мы взяли его под свой контроль! Мы создали <a href="https://github.com/codeborne/ghostdriver">форк</a>, который может считаться официальным продолжением, и зарелизили версию phantomjsdriver 1.3.0, которая совместима с последним Selenium Webdriver 2.53.0. И эта версия теперь поставляется вместе с Selenide.</p> <p><br /></p> <p>Вот такая у нас вышла чумачечая весна.</p> <p>Скачивайте и тестируйте в своё удовольствие!</p> <p><br /> <br /></p> <h1 id="новости">Новости</h1> <p>В конце февраля в Киеве состоялась моя любимая (юбилейная пятая) конференция <a href="http://seleniumcamp.com/materials/">SeleniumCamp</a>. Я сам туда попасть не смог по семейным обстоятельствам, но и без меня там было сразу несколько докладов про Selenide. Это же просто праздник какой-то, а?</p> <ul> <li>Это не фантастика и не бред! Как использовать <a href="http://seleniumcamp.com/talk/using-selenide-on-c-net/">Selenide на C# .NET</a> / Олег Володин</li> <li><a href="http://seleniumcamp.com/talk/selenide-alternative-in-practice-implementation-lessons-learned/">Альтернативный Selenide</a> - устройство Selenide или подобного фреймворка разбирается по кишочкам / Яков Крамаренко</li> <li>Ещё одна <a href="http://qiita.com/tatesuke/items/589e30ab9b3dc7037e26">статья про Selenide на японском</a> (у всех же Chrome переводит автоматически?) / tatesuke <br />В Японии нас любят!</li> <li>Статья в нашем блоге: Реальный опыт с Selenide: <a href="http://ru.selenide.org/2016/04/17/airtickets">покупка авиабилетов</a> / Василий Ковальченко</li> <li>Туториал: <a href="https://blog.konstantinpavlov.net/2016/05/12/selenium-tests-with-maven-and-selenide/">Selenium Tests with Maven and Selenide</a> / Константин Павлов</li> <li>Статья на хабре: <a href="https://habrahabr.ru/company/rtl-service/blog/300860/">Ускорение автоматизации тестирования веб-интерфейса за счет применения Python и Selenide</a> / Николай Федоров, Николай Смирнов</li> </ul> <h2 id="немного-статистики">Немного статистики</h2> <p>Статистика скачиваний Selenide за май 2016:</p> <center> <img src="/images/2016/06/selenide.downloads.png" width="800" /> </center> <p>Нас всё больше!</p> <p>Забавно, что популярность Selenide никак не коррелируется с выходом новых фич и релизов. Совпадение?..</p> <p><br /> <br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/06/01/selenide-3.6/ http://ru.selenide.org/2016/06/01/selenide-3.6 2016-06-01T00:00:00+00:00 Реальный опыт с Selenide: покупка авиабилетов <p>Привет!</p> <p>Иногда хочется посмотреть использование фреймворка в реальных боевых условиях, а не только хелловорды на кошечках. А почему бы нет!</p> <p>В серии <a href="https://github.com/selenide-examples">Selenide Examples</a> сегодня мы протестируем совершенно настоящий сайт <a href="http://airtickets.ru">airtickets.ru</a> с настоящими покупками, деньгами и карточками. Всё как в жизни.</p> <p>Для разнообразия попробуем использовать шаблон PageObject, причём в разных вариациях:</p> <ul> <li>PageObject a’la Selenide style, <a href="http://ru.selenide.org/documentation/page-objects.html">предложенный Андреем Солнцевым</a></li> <li>классический PageObject</li> <li>PageObject с блоками (a’la HtmlElements)</li> </ul> <h2 id="что-тестируем">Что тестируем</h2> <p>Тест покупает авиабилеты СПб-Москва туда и обратно, вводя все необходимые значения, и доходит до кнопки “Купить” после заполнения данных кредитной карточки.</p> <p>Вот как это выглядит:</p> <center> <iframe src="//www.youtube.com/embed/xS1KxNYgRsk" height="473" width="630" allowfullscreen="" frameborder="0"></iframe> </center> <p><br /></p> <h2 id="как-это-работает">Как это работает</h2> <p>Давайте посмотрим, как этот тест устроен изнутри.</p> <p><a href="https://github.com/selenide-examples/airtickets.ru">Проект на гитхабе</a></p> <h3 id="что-такое-паттерн-pageobject">Что такое паттерн PageObject</h3> <p>PageObject - это способ работы с Web UI, при котором селекторы(локаторы) элементов веб-страницы задаются в отдельном классе. Делается это для того, чтобы при изменении структуры html достаточно было обновлять селекторы всего в одном месте и не искать их по всему коду.</p> <h3 id="какие-у-нас-страницы">Какие у нас страницы</h3> <p>Как видно из видео, мы работаем с 5-ю страницами.</p> <p>В самом тесте (класс <a href="https://github.com/selenide-examples/airtickets.ru/blob/master/src/test/com/sidenis/vasidzius/TestBuyingTickets.java">TestBuyingTickets</a>) описана логика работы всего теста. Из названия методов понятно, что делает каждая строчка. Посмотрим поподробнее на некоторые из них:</p> <h4 id="первая-строка-теста">первая строка теста</h4> <p>открываем страницу и в этой же строчке “засовываем” в переменную <code class="language-plaintext highlighter-rouge">firstPageDirection</code> все данные об элементах/логике первой страницы.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">FirstPageDirection</span> <span class="n">firstPageDirection</span> <span class="o">=</span> <span class="n">open</span><span class="o">(</span><span class="s">"http://www.airtickets.ru/..."</span><span class="o">,</span> <span class="nc">FirstPageDirection</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> </code></pre></div></div> <h4 id="теперь">Теперь</h4> <p>мы можем выполнить нужный нам метод, благодаря тому, что Selenide будет пытаться находить нужные нам селекторы в браузере:</p> <ul> <li>указать город отправления: <br /><code class="language-plaintext highlighter-rouge">firstPageDirection.addDirectionFrom("Санкт", "LED");</code></li> <li>указать город прибытия: <br /><code class="language-plaintext highlighter-rouge">firstPageDirection.addDirectionTo("Москва", "MOW");</code></li> <li>…</li> </ul> <blockquote> <p>стоит пояснить, зачем нужен цикл while - иногда после выбора рейсов и нажатия кнопки “далее” на второй странице может выпасть редкое сообщение о том, что рейсы не доступны, и предложение выбрать другие рейсы. Этот цикл как раз проверяет, что это сообщение не появилось. Если оно появится, то цикл будет повторять выбор билета, пока сообщение будет появляться.</p> </blockquote> <h4 id="окончанием-теста">Окончанием теста</h4> <p>является проверка, что кнопка “Купить” отображена. В Selenide это делается очень просто благодаря мощному механизму проверки условий. Из самого названия метода понятно, что он проверяет:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">fifthPage</span><span class="o">.</span><span class="na">getButton</span><span class="o">().</span><span class="na">shouldBe</span><span class="o">(</span><span class="nc">Condition</span><span class="o">.</span><span class="na">visible</span><span class="o">);</span> </code></pre></div></div> <h4 id="какие-тут-пэдж-объекты">Какие тут пэдж объекты?</h4> <p>Первые три страницы реализованы в виде “селенидовских” PageObject. Т.е. делаем отдельные классы для каждой страницы (FirstPageDirection, SecondPageVariants, …) и в каждом описываем логику, которая нам нужна, <em>в виде отдельных методов</em>. При этом селекторы необходимых элементов прописываются в методах.</p> <p>Например, добавить пункт отправления на первой странице (класс FirstPageDirection):</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">addDirectionFrom</span><span class="o">(</span><span class="nc">String</span> <span class="n">city</span><span class="o">,</span> <span class="nc">String</span> <span class="n">airport</span><span class="o">)</span> <span class="o">{</span> <span class="err">$</span><span class="o">(</span><span class="s">"#from"</span><span class="o">).</span><span class="na">setValue</span><span class="o">(</span><span class="n">city</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#autocomplete"</span><span class="o">).</span><span class="err">$</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="n">airport</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <h4 id="селекторы">Селекторы</h4> <p>По умолчанию $ (поиск элемента по селектору) принимает как аргумент CSS селектор. Поэтому <code class="language-plaintext highlighter-rouge">$("#from")</code> - означает “найти элемент с ID “from”.</p> <blockquote> <p>Можете убедиться в том, что input “Откуда” имеет среди аттрибутов именно такой ID откройте в любом браузере <a href="http://www.airtickets.ru/bileti-aeroflot?gclid=CNzJ_-DQw8UCFcL3cgodeKwA0w">страницу сервиса</a>, наведите мышку на строку ввода пункта отправления “Откуда” и в контекстном меню (правая кнопка мыши) выберите “Просмотреть код”. Браузер откроет html код страницы и подсветит нужный нам input. Среди атрибутов этого элемента есть id=”from”.</p> </blockquote> <h4 id="методы-пэдж-объекта">Методы пэдж объекта</h4> <p>Далее вносим значение в этот input с помощью метода Selenide.setValue(String). Почему это написано в одну строку? Потому что первая часть <code class="language-plaintext highlighter-rouge">$("#from")</code> означает, что мы получили SelenideElement, у которого есть метод setValue(String). Также Вы можете использовать как аргумент <code class="language-plaintext highlighter-rouge">$()</code> и <code class="language-plaintext highlighter-rouge">xpath</code>, тогда вызов будет выглядеть так: <code class="language-plaintext highlighter-rouge">$(By.xpath(....))</code>.</p> <p>Также в <code class="language-plaintext highlighter-rouge">FirstPageDirection</code> вы можете увидеть вспомогательные методы, которые нужны только для работы с первой страницей - получение текущего месяца из System. Это нужно для проверки, что сервис автоматически подставляет как месяц отправления либо текущий месяц, либо следующий. Проверка осуществляется с помощью assert’ов в тесте. Это, например, строка:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Assert</span><span class="o">.</span><span class="na">assertThat</span><span class="o">(</span><span class="s">"Месяц отправления не равен текущему месяцу или следующему месяцу"</span><span class="o">,</span> <span class="n">firstPageDirection</span><span class="o">.</span><span class="na">getMonthDeparture</span><span class="o">(),</span> <span class="n">anyOf</span><span class="o">(</span><span class="n">is</span><span class="o">(</span><span class="n">firstPageDirection</span><span class="o">.</span><span class="na">getCurrentMonth</span><span class="o">()),</span> <span class="n">is</span><span class="o">(</span><span class="n">firstPageDirection</span><span class="o">.</span><span class="na">getNextMonth</span><span class="o">())));</span> </code></pre></div></div> <blockquote> <p>Кстати, крайне рекомендую освоить <code class="language-plaintext highlighter-rouge">assertThat</code> от <code class="language-plaintext highlighter-rouge">hamcrest</code> (которая уже давно в составе JUnit). Отличие <code class="language-plaintext highlighter-rouge">assertThat</code> от <code class="language-plaintext highlighter-rouge">assertTrue</code>, <code class="language-plaintext highlighter-rouge">assertEquals</code> и др. в читаемости, а также в том, что вы всегда при непрохождении проверки будете получать в логи фразы типа: пришло “то-то”, а ожидали “то-то”. Очень удобно.</p> </blockquote> <h4 id="работа-с-коллекциями">Работа с коллекциями</h4> <p>В PageObject второй страницы (<code class="language-plaintext highlighter-rouge">SecondPageVariants</code>) используется селенидовский метод <code class="language-plaintext highlighter-rouge">$$</code>. Он возвращает массив всех элементов, найденных по заданному селектору.</p> <p>Есть задача случайно выбрать рейс из предлагаемых. Для этого находим на странице все рейсы:</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">radioList</span> <span class="o">=</span> <span class="n">priceTable</span><span class="o">.</span><span class="err">$$</span><span class="o">(</span><span class="s">".radio"</span><span class="o">);</span> </code></pre></div></div> <p>Здесь опять использован CSS-селектор, только в этом случае мы ищем элементы по аттрибуту class. Получаем массив всех существующих на странице .radio и выбираем среди них случайный:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SelenideElement</span> <span class="n">radio</span> <span class="o">=</span> <span class="n">radioList</span><span class="o">.</span><span class="na">get</span><span class="o">((</span><span class="kt">int</span><span class="o">)(</span><span class="nc">Math</span><span class="o">.</span><span class="na">random</span><span class="o">()</span> <span class="o">*</span> <span class="o">(</span><span class="n">radioList</span><span class="o">.</span><span class="na">size</span><span class="o">()-</span><span class="mi">1</span><span class="o">)));</span> </code></pre></div></div> <p>(Немного упростил описание, но разобраться в том, как действительно отрабатывает логика, несложно.)</p> <h4 id="классический-пэдж-объект-с-блоками">Классический пэдж объект с блоками</h4> <p>Четвертая страница (<code class="language-plaintext highlighter-rouge">FourthPagePassengerInfo</code>) реализована классическим PageObject c применением блоков. Блоки - это отдельные группы элементов на странице, которые для простоты обработки можно вынести в отдельный PageObject (использовать инкапсуляцию).</p> <p>На четвертой странице у нас есть два фрейма, для заполнения информации о пассажирах и контактных данных. В начале класса мы говорим, что на странице нас интересуют два фрейма, задаем для них селекторы с Selenium аннотацией FindBy:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">xpath</span> <span class="o">=</span> <span class="s">"(.//*[@class='frame'])[1]"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">FramePassengerInfo</span> <span class="n">passengerInfo</span><span class="o">;</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">xpath</span> <span class="o">=</span> <span class="s">"(.//*[@class='frame'])[2]"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">FrameContactPersonInfo</span> <span class="n">contactPersonInfo</span><span class="o">;</span> </code></pre></div></div> <p>Как видите, тип фреймов у нас не <code class="language-plaintext highlighter-rouge">WebElement</code> и не <code class="language-plaintext highlighter-rouge">SelenideElement</code>, а соответствующий для каждого фрейма блок, который вы можете посмотреть в пакете <code class="language-plaintext highlighter-rouge">ForthPagePassengerInfoElements</code>.</p> <p>Каждый блок описан в классическом PageObject, с аннотацией <code class="language-plaintext highlighter-rouge">@FindBy</code> и своими уникальными методами. Для того, чтобы показать, что каждый фрейм - это блок, необходимо наследовать <code class="language-plaintext highlighter-rouge">ElementsContainer</code> (в объявлении класса : <code class="language-plaintext highlighter-rouge">public class FramePassengerInfo extends ElementsContainer</code>).</p> <h2 id="итоги">Итоги</h2> <ol> <li> <p>Те, кто ранее писал тесты на чистом Selenium, надеюсь, увидели, что Selenide позволяет значительно сократить написание тестов.</p> </li> <li> <p>Паттерн PageObject, как и свойственно любому шаблону, является только идеей подхода написания кода. Вы можете описать селекторы в начале с аннотацией @FindBy (классический вариант), или же вы можете написать PageObject как предлагает Андрей Солнцев - страница содержит методы, внутри которых пишутся селекторы. Вы даже можете создать синглтон, который будет содержать мапу со всеми селекторами вашего проекта. И в описании логики тестов выдергивать селекторы из мапы и вставлять их как аргумент в селенидовский $ (именно такое решение применил у себя на проекте, где очень много элементов с похожими названиями, мало уникальных классов и айдишников). Главное, старайтесь описывать селектор для конкретного элемента в одном месте. Чтобы вы или тот автоматизатор, что придет на ваше место в будущем, могли это место легко отыскать и изменить селектор, если разработчики решат поменять html структуру.</p> </li> </ol> <p>Василий Ковальченко</p> http://ru.selenide.org/2016/04/17/airtickets/ http://ru.selenide.org/2016/04/17/airtickets 2016-04-17T00:00:00+00:00 Вышла Selenide 3.5 <p>Привет!</p> <p>Вышла в свет Selenide 3.5 с поддержкой резиновых коллекций. А именно,</p> <h2 id="добавили-гибкие-условия-для-проверки-размера-коллекций">Добавили гибкие условия для проверки размера коллекций</h2> <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="s">".man.angry"</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">12</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">".man.angry"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">sizeLessThan</span><span class="o">(</span><span class="mi">13</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">".man.angry"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">sizeLessThanOrEqual</span><span class="o">(</span><span class="mi">12</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">".man.angry"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">sizeGreaterThan</span><span class="o">(</span><span class="mi">11</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">".man.angry"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">sizeGreaterThanOrEqual</span><span class="o">(</span><span class="mi">12</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">".man.angry"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">sizeNotEqual</span><span class="o">(</span><span class="mi">42</span><span class="o">));</span> </code></pre></div></div> <p>Спасибо <a href="https://github.com/vasilevichra">vasilevichra</a> за этот pull request!</p> <p>P.S. Вообще-то мне до сих пор это кажется сомнительной идеей. Я думаю, что тест должен подготавливать требуемые пред-условия перед запуском тестируемого кода. То есть тест всегда должен точно знать, сколько и каких объектов должно быть на экране. Но народ просил - мы сделали.</p> <h2 id="ускорение-загрузки-страниц">Ускорение загрузки страниц</h2> <p>По умолчанию Selenium webdriver ждёт, пока все элементы страницы (html, script, style, img) загрузятся. Это может быть долго, если например, на странице есть большие картинки. И это наверняка не нужно, особенно в случае с Selenide, который по любому умеет ждать наступления ожидаемых условий.</p> <p>Поэтому в Selenide 3.5 мы включили стратегию загрузки <code class="language-plaintext highlighter-rouge">none</code>. Это должно сделать ваши тесты быстрее.</p> <p>Если нужно, стратегию можно поменять через настройку:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.page-load-strategy=normal </code></pre></div></div> <p>или прямо в коде:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Configuration.pageLoadStrategy=eager; </code></pre></div></div> <p>Описание всех доступных стратегий <a href="https://w3c.github.io/webdriver/webdriver-spec.html#dfn-page-loading-strategy">есть здесь</a>.</p> <h2 id="исправили-метод-tostring">Исправили метод toString()</h2> <p>Selenide умеет выводить подробную информацию о веб-элементах. То есть когда вы пишите <code class="language-plaintext highlighter-rouge">System.out.println($("option#abc"))</code>, вы увидите в консоли текст и значения всех атрибутов этого веб-элемента:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;option</span> <span class="na">id=</span><span class="s">"abc"</span> <span class="na">value=</span><span class="s">"livemail.ru"</span> <span class="na">selected:true</span><span class="nt">&gt;</span>@livemail.ru<span class="nt">&lt;/option&gt;</span> </code></pre></div></div> <p>Это здоровская возможность, и кстати, селениум этого не умеет.</p> <p>Поэтому нам пришлось использовать грязные JavaScript-трюки. Я потом месяц отмывался!</p> <p>Но оказалось, что этот метод печатает изначальное значение атрибута <code class="language-plaintext highlighter-rouge">value</code>, даже если он был позже изменён динамически. Эту проблему мы исправили в Selenide 3.5. Теперь метод <code class="language-plaintext highlighter-rouge">toString()</code> всегда печатает свежее значение <code class="language-plaintext highlighter-rouge">value</code>.</p> <h2 id="обновились-до-selenium-java-2530">Обновились до selenium-java 2.53.0</h2> <p>Список <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">изменений в selenium 2.53.0</a>.</p> <p><br /> <br /></p> <h1 id="новости">Новости</h1> <ul> <li>Исторический момент: <a href="https://en.wikipedia.org/wiki/List_of_GUI_testing_tools">Selenide упомянут в Wikipedia</a></li> <li>Radio QA - Выпуск 20: <a href="http://radio-qa.com/anons-vypusk-20-luchshe-selenide-v-rukah-chem-selenium-v-nebe/">Лучше Selenide в руках, чем Selenium в небе</a></li> <li>Презентация Якова Крамаренко на конференции SeleniumCamp 2016: <a href="http://www.slideshare.net/yashaka/selenide-alternative-in-practice-implementation-lessons-learned-seleniumcamp-2016">Selenide Alternative in Practice</a></li> <li>Рецепты Selenide: <a href="http://selenide-recipes.blogspot.com.ee/2015/09/mobile-automation-appium-selenide.html">Appium + Selenide</a></li> <li>Пост про автоматизацию полиграфического предприятия Ardecs: <a href="http://www.ardecs.com/blog/27-08-2015/?lang=ru">Selenide предлагает не заморачиваться</a></li> <li>Статья про Selenide в <a href="http://www.softwaretestingmagazine.com/videos/concise-ui-tests-in-java-with-selenide/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+SoftwareTestingMagazine+%28Software+Testing+Magazine%29">Software testing magazine</a></li> <li>Статья про Selenide от португальца Fabricio Galdino: <a href="http://mrbool.com/api-selenide-developing-functional-tests-in-java/33952">API Selenide: Developing Functional Tests in Java</a></li> </ul> <h2 id="немного-статистики">Немного статистики</h2> <p>Статистика скачиваний Selenide за февраль 2016:</p> <center> <img src="/images/2016/03/selenide.downloads.png" width="800" /> </center> <p>И количество уникальных IP:</p> <center> <img src="/images/2016/03/selenide.unique-ips.png" width="800" /> </center> <p>Нас всё больше!</p> <p><br /> <br /></p> <p>Следующий релиз Selenide мы планируем выпустить довольно скоро. И в него мы хотим включить прокси-сервер BrowserMobProxy, чтобы Selenide мог скачивать файлы и делать разную другую магию.</p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/03/31/selenide-3.5/ http://ru.selenide.org/2016/03/31/selenide-3.5 2016-03-31T00:00:00+00:00 Вышла Selenide 3.3 <p>Привет!</p> <p>Вышла в свет Selenide 3.3 с поддержкой коллекций. А именно,</p> <h2 id="улучшена-поддержка-аякс-для-коллекций">Улучшена поддержка аякс для коллекций</h2> <p>Теперь методы для работы с коллекциями (оператор <code class="language-plaintext highlighter-rouge">$$</code>) ждут, пока элементы коллекции загрузятся. Это полезно для тех случаев, когда коллекция подгружается асинхронно каким-нибудь очередным аяксом.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/277">issue #277</a></p> <h2 id="добавился-отдельный-таймаут-для-коллекций">Добавился отдельный таймаут для коллекций</h2> <p>Мы добавили два новых параметра для коллекций:</p> <ul> <li><code class="language-plaintext highlighter-rouge">collectionsTimeout</code> (по умолчанию 6 секунд)</li> <li><code class="language-plaintext highlighter-rouge">collectionsPollingInterval</code> (по умолчанию 0.2 секунды)</li> </ul> <p>Это связано с тем, что в большинстве случаев коллекции грузятся дольше, чем одиночные элементы (ведь в них больше элементов). Поэтому им требуется больший таймаут.</p> <h2 id="обновились-до-selenium-java-2510">Обновились до selenium-java 2.51.0</h2> <p>Список <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">изменений в selenium 2.51.0</a>.</p> <p><br /> <br /></p> <h1 id="новости">Новости</h1> <ul> <li><a href="http://automation-remarks.com/groovy-page/">Лаконичный PageObject с GroovyPage + Selenide</a> от Сергея Пирогова</li> <li><a href="https://habrahabr.ru/post/274071/">Эффективные UI-тесты на Selenide</a> на хабре</li> <li>Статья про Selenide <a href="http://hanmomhanda.github.io/2016/01/27/Selenide-%EA%BF%80%ED%8C%81/">на КОРЕЙСКОМ</a>!</li> <li>Статья про Selenide <a href="http://www.devmedia.com.br/api-selenide-desenvolvimento-de-testes-funcionais-em-java/33680">на португальском</a></li> <li><a href="https://gist.github.com/mkpythonanywhereblog/947633ba1bf0bc239639">Selenide cheat sheet</a></li> <li><a href="http://selenide-recipes.blogspot.kr/2015/08/6-waits.html">Рецепты Selenide</a></li> <li>Radio QA - <a href="http://radio-qa.com/vypusk-16-staryj-kod-borozdy-ne-isportit/">Выпуск 16: Старый код борозды не испортит?</a></li> <li>Новый классный формат - IT Battle. <a href="http://itbattle.ru/pochemu-nuzhno-otkazatsya-ot-avtomatizatsii-testirovaniya/">Батл 1: Почему нужно отказаться от автоматизации тестирования</a></li> <li>Видео из девклуба: <a href="http://blog.devclub.eu/2016/01/02/microservices/">Никита Макаров — Микросервисы для автоматизации тестирования («Одноклассники»)</a></li> </ul> <h1 id="давайте-общаться">Давайте общаться!</h1> <p>У нас появились возможности более тесно общаться и обсуждать новые возможности и старые проблемы Selenide. Заходите!</p> <ul> <li><a href="https://softwaretesters.slack.com/messages/selenide_ru/">Чат на Slack</a></li> <li><a href="https://gitter.im/selenide/selenide-ru">Чат в Gitter</a></li> </ul> <p><br /> <br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/02/11/selenide-3.3/ http://ru.selenide.org/2016/02/11/selenide-3.3 2016-02-11T00:00:00+00:00 Вышла Selenide 3.2 <p>Привет!</p> <p>Вышел в свет Selenide 3.2 - давайте знакомится!</p> <h2 id="теперь-selenide-пишет-в-логе-версию-браузера">Теперь Selenide пишет в логе версию браузера</h2> <p>Добавили INFO лог с версией браузера, Selenium и Selenide:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00:32:45 INFO BrowserName=chrome Version=48.0.2564.109 Platform=MAC 00:32:45 INFO Selenide v. 3.2 00:32:45 INFO Selenium WebDriver v. 2.51.0 build time: 2016-02-05 11:20:57 </code></pre></div></div> <h2 id="улучшен-отчёт">Улучшен отчёт</h2> <p>Мы переименовали FAILED-&gt;FAIL, PASSED-&gt;PASS в отчёте тестов, чтобы не путать с <code class="language-plaintext highlighter-rouge">PASSED</code> и <code class="language-plaintext highlighter-rouge">FAILED</code>, которые обычно пишут Maven и другие тулы. Теперь отчёт Selenide чуть проще анализировать в Jenkins.</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="s">"select#email"</span><span class="o">).</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"@yandex.ru"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"select#email"</span><span class="o">).</span><span class="na">selectOptionByValue</span><span class="o">(</span><span class="s">"98347643"</span><span class="o">);</span> </code></pre></div></div> <p>А теперь можно выбрать и <a href="https://github.com/selenide/selenide/issues/275">по индексу</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">"select#email"</span><span class="o">).</span><span class="na">selectOption</span><span class="o">(</span><span class="mi">4</span><span class="o">);</span> </code></pre></div></div> <p>На мой взгляд, это более рискованно (никогда не знаешь, что под этим индексом спрятано), но если очень надо, то теперь возможно.</p> <h2 id="добавлена-новая-настройка-selenidebrowser-size">Добавлена новая настройка <code class="language-plaintext highlighter-rouge">selenide.browser-size</code></h2> <p>Теперь можно до запуска тестов сразу <a href="https://github.com/selenide/selenide/issues/272">задать размер браузера</a>:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-Dselenide</span>.browser-size<span class="o">=</span>1024x768 </code></pre></div></div> <p>или прямо в коде:</p> <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="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">browserSize</span> <span class="o">=</span> <span class="s">"1024x768"</span><span class="o">;</span> <span class="o">}</span> </code></pre></div></div> <p>Возможно, это хорошая идея - изначально задать (минимальный) размер браузера, на который рассчитано тестируемое приложение. Ведь наши компьютеры, на которых мы разрабатываем и запускаем тесты, как правило, имеют большие мониторы, и мы иногда не замечаем, что на меньших экранах наше приложение может и не работать.</p> <h2 id="обновились-до-selenium-java-2500">Обновились до selenium-java 2.50.0</h2> <p>Список <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">изменений в selenium 2.50.0</a>.</p> <p><br /> <br /></p> <p>До скорых встреч! Selenide 3.3 уже на подходе!</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/01/29/selenide-3.2/ http://ru.selenide.org/2016/01/29/selenide-3.2 2016-01-29T00:00:00+00:00 Вышла Selenide 3.1 <p>Привет!</p> <p>Год обещает быть плодотворным. Не успела страна очнуться после каникул, а мы уже выпустили Selenide 3.1. Посмотрим, что новенького?</p> <h2 id="мы-обновили-документацию">Мы обновили документацию</h2> <p>Мы дополнили и исправили <a href="/documentation.html">документацию</a> на сайте.</p> <p>Спасибо <a href="https://github.com/vinogradoff">Алексею Виноградову</a> и Эрику Халимову за проделанную работу!</p> <h2 id="метод-download-не-любит-невидимые-ссылки">Метод <code class="language-plaintext highlighter-rouge">$().download()</code> не любит невидимые ссылки</h2> <p>Раньше метод <code class="language-plaintext highlighter-rouge">$().download()</code> (по недосмотру) позволял скачивать файлы и с невидимых ссылок. Теперь мы эту оплошность <a href="https://github.com/selenide/selenide/issues/263">ликвидировали</a>. Спасибо <a href="https://github.com/dimand58">@dimand58</a> за <a href="https://github.com/selenide/selenide/pull/264">pull request</a>.</p> <h2 id="методы-switchto-теперь-умеют-ждать">Методы <code class="language-plaintext highlighter-rouge">switchTo(...)</code> теперь умеют ждать</h2> <p>Теперь методы <code class="language-plaintext highlighter-rouge">switchTo(alert())</code>, <code class="language-plaintext highlighter-rouge">switchTo(frame())</code>, <code class="language-plaintext highlighter-rouge">switchTo(window())</code> стали умнее и умеют чуть-чуть подождать, если <a href="https://github.com/selenide/selenide/issues/206">алерт</a>, <a href="https://github.com/selenide/selenide/issues/206">фрейм</a> или <a href="https://github.com/selenide/selenide/issues/271">окно</a> ещё не загрузилось. Как обычно, по умолчанию таймаут 4 секунды.</p> <h2 id="добавлены-методы-byname-byxpath-bylinktext-bypartiallinktext-byid">Добавлены методы <code class="language-plaintext highlighter-rouge">byName</code>, <code class="language-plaintext highlighter-rouge">byXpath</code>, <code class="language-plaintext highlighter-rouge">byLinkText</code>, <code class="language-plaintext highlighter-rouge">byPartialLinkText</code>, <code class="language-plaintext highlighter-rouge">byId</code></h2> <p>Это новые статические методы в классе <code class="language-plaintext highlighter-rouge">Selectors</code> (там же, где до этого были <code class="language-plaintext highlighter-rouge">byText</code> и <code class="language-plaintext highlighter-rouge">withText</code>). Фактически они дублируют селениумовские методы <code class="language-plaintext highlighter-rouge">By.*</code>. Не уверен, что они нужны, но некоторым нравится идея вообще не использовать никаких классов Selenium в своих тестах. И ещё это было полезно для того, чтобы использовать Selenide в тестах на .NET (чудны дела твои, господи!)</p> <h2 id="багфикс-условие-shouldhaveexacttextcasesensitive-теперь-проверяет-полную-строку">Багфикс: условие <code class="language-plaintext highlighter-rouge">$.shouldHave(exactTextCaseSensitive("..."))</code> теперь проверяет полную строку</h2> <p>А раньше (по недосмотру) проверял только подстроку.</p> <h2 id="методы-webelement-selector-и-webelement-selector-помечены-как-устаревшие">Методы <code class="language-plaintext highlighter-rouge">$(WebElement, selector)</code> и <code class="language-plaintext highlighter-rouge">$$(WebElement, selector)</code> помечены как устаревшие</h2> <p>Вместо них лучше использовать метод <code class="language-plaintext highlighter-rouge">find</code>:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$(WebElement).find(selector)</code></li> <li><code class="language-plaintext highlighter-rouge">$$(WebElement).find(selector)</code></li> </ul> <h2 id="добавили-метод-getvalue">Добавили метод <code class="language-plaintext highlighter-rouge">$.getValue()</code></h2> <p>На самом деле это просто синоним для <code class="language-plaintext highlighter-rouge">$.val()</code>.</p> <h2 id="selenide-включает-phantomjsdriver-121-из-коробки">Selenide включает phantomjsdriver 1.2.1 из коробки</h2> <p>Теперь не нужно подключать дополнительные зависимости, чтобы использовать PhantomJS в своих тестах.</p> <h2 id="обновились-до-selenium-java-2490">Обновились до selenium-java 2.49.0</h2> <p>Список <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">изменений в selenium 2.49.0</a>.</p> <p><br /> <br /></p> <p>До скорых встреч! Selenide 3.2 и 3.3 уже на подходе!</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2016/01/17/selenide-3.1/ http://ru.selenide.org/2016/01/17/selenide-3.1 2016-01-17T00:00:00+00:00 Вышла Selenide 3.0 <p>Привет!</p> <p>Наконец-то мы выпустили Selenide 3.0. Уффф. Свершилось.</p> <p>Внимание, версия 3.0 - это полный апгрейдец, то есть кое-что может перестать работать.</p> <p>Не пугайтесь, если вы использовали только публичный API и не использовали @Deprecated методы, то для вас ничего не изменится. А если лезли в кишки, то читайте внимательно.</p> <h2 id="исторический-экскурс">Исторический экскурс</h2> <p>Версия Selenide 1.0 была создана в 2011 году в казематах Codeborne и широко не рекламировалась. Примерно год Selenide мариновался и зрел. Мы пробовали использовать его в разных проектах - на Java, Scala, Groovy. Каждый раз обнаруживали, что API опять неудобен и требует доработок. :)</p> <p>Наконец, в марте 2013 мы поехали в Киев на конференцию SeleniumCamp и к этому событию выпустили версию Selenide 2.0. В ней мы повыкидывали весь хлам и оставили только новый API. Этот момент можно считать днём рождения “публичной” версии Selenide. На тот момент в классе <code class="language-plaintext highlighter-rouge">SelenideElement</code> было всего-то с десяток методов. Кстати, сначала он назывался <code class="language-plaintext highlighter-rouge">ShouldableWebElement</code>. :)</p> <p>С этого момента вышло 25 версий Selenide, в ней появилось множество новых функций и своё legacy, свои @Deprecated методы. Назрели перемены.</p> <p>Поэтому наконец мы собрались с силами и выпустили Selenide 3.0</p> <h2 id="новые-функции-в-selenide-30">Новые функции в Selenide 3.0</h2> <p>Функция всего одна - метод <code class="language-plaintext highlighter-rouge">Selenide.updateHash()</code>. Она полезна при тестировании аяксовских приложений, которые реагируют на изменение “#” в URL. Метод <code class="language-plaintext highlighter-rouge">updateHash()</code> меняет “#bla-bla” в URL, не перезагружая всю страницу. Пример использования <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/UpdateHashTest.java">здесь</a>.</p> <blockquote> <p>Спасибо @fabienbancharel за pull request #254</p> </blockquote> <h2 id="обновили-зависимости">Обновили зависимости</h2> <ul> <li>обновились до Sizzle 2.2.1</li> <li>обновились до Guava 19.0</li> <li>обновились до TestNG 6.9.10</li> </ul> <p>Опять же, это касается только тех, кто использует соответствующие функции и библиотеки.</p> <h2 id="большой-рефакторинг">Большой рефакторинг</h2> <ul> <li>Мы нещадно порефакторили класс <code class="language-plaintext highlighter-rouge">AbstractSelenideElement</code>. Теперь вместо одного большого класса у нас будет много маленьких классов (в пакете <code class="language-plaintext highlighter-rouge">com.codeborne.com.commands</code>), отвечающих каждый за свою задачу.</li> <li>Вы можете легко переопределять любой из методов <code class="language-plaintext highlighter-rouge">SelenideElement</code>.</li> <li>И вы даже можете добавлять свои методы в дополнение к стандартным методам Selenide и Selenium.</li> </ul> <p>Подробнее об этом будет отдельный пост, но пример можно подсмотреть в <a href="https://github.com/selenide/selenide/tree/master/statics/src/test/java/integration/customcommands">тестах самого Selenide</a>.</p> <blockquote> <p>Спасибо Якову Крамаренко за идеи и ночные дискуссии.</p> </blockquote> <h2 id="большая-чистка">Большая чистка</h2> <p>Как мы предупреждали в предыдущем анонсе, мы подчистили старый хлам. А именно:</p> <ul> <li>Удалили устаревшие условия: <ul> <li><code class="language-plaintext highlighter-rouge">notPresent</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">$.shouldNot(exist)</code> или <code class="language-plaintext highlighter-rouge">$.shouldNotBe(present)</code>.</li> <li><code class="language-plaintext highlighter-rouge">hasOptions</code> -&gt; Используйте <code class="language-plaintext highlighter-rouge">$.selectOption()</code> или <code class="language-plaintext highlighter-rouge">$.selectOptionByValue()</code>.</li> <li><code class="language-plaintext highlighter-rouge">options</code> -&gt; Используйте <code class="language-plaintext highlighter-rouge">$.selectOption()</code> или <code class="language-plaintext highlighter-rouge">$.selectOptionByValue()</code>.</li> <li><code class="language-plaintext highlighter-rouge">hasNotClass</code> -&gt; Используйте <code class="language-plaintext highlighter-rouge">$.shouldNotHave(cssClass("abc"))</code></li> </ul> </li> <li>Удалили класс <code class="language-plaintext highlighter-rouge">JQuery</code></li> <li>Удалили класс <code class="language-plaintext highlighter-rouge">PrettyReportCreator</code> (используйте <code class="language-plaintext highlighter-rouge">TextReport</code> для JUnit или TestNG)</li> <li>Удалили устаревшие методы <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.switchToWindow(title)</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">switchTo().window(title)</code></li> <li><code class="language-plaintext highlighter-rouge">Selenide.switchToWindow(index)</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">switchTo().window(index)</code></li> </ul> </li> <li>Удалили устаревшие методы <ul> <li><code class="language-plaintext highlighter-rouge">WebDriverRunner.ie()</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">WebDriverRunner.isIE()</code></li> <li><code class="language-plaintext highlighter-rouge">WebDriverRunner.htmlUnit()</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">WebDriverRunner.isHtmlUnit()</code></li> <li><code class="language-plaintext highlighter-rouge">WebDriverRunner.phantomjs()</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">WebDriverRunner.isPhantomjs()</code></li> <li><code class="language-plaintext highlighter-rouge">WebDriverRunner.takeScreenShot()</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">Screenshots.takeScreenShot()</code></li> </ul> </li> <li>Удалили устаревшие методы <ul> <li><code class="language-plaintext highlighter-rouge">$.should*(String message, Condition condition)</code> -&gt; используйте <code class="language-plaintext highlighter-rouge">$.should*(condition.because(message))</code></li> </ul> </li> <li>Удалили класс class <code class="language-plaintext highlighter-rouge">com.codeborne.selenide.impl.Quotes</code>, потому что он переехал в Selenium Webdriver (теперь это <code class="language-plaintext highlighter-rouge">org.openqa.selenium.support.ui.Quotes</code>)</li> </ul> <p>Смело обращайтесь, если у вас всё-таки что-то сломалось.</p> <p>Будем вместе решать, где исправлять! Мы к этому готовы. <br /> <br /></p> <h1 id="новости">Новости</h1> <ul> <li>Selenide попала в список <a href="http://www.coderbucket.com/best-tools-sites-java/">Лучшие инструменты и сайты про Java 2015</a></li> <li>Статья про Selenide в рождественском календаре Java <a href="http://www.javaadvent.com/2015/12/effective-ui-tests-with-selenide.html">Effective UI tests with Selenide</a></li> <li>Статья про Selenide на японском языке. <a href="http://qiita.com/kazuki-ma/items/d6432fc41c82538a61bd">На японском, Карл!</a> (chrome -&gt; translate в помощь)</li> <li>15ый выпуск подкаста Radio QA <a href="http://radio-qa.com/015-money-or-fun/">Работаем за идею или за деньги?</a>. Говорят, было жарко.</li> <li>Русскоязычный чат тестировщиков на слаке <a href="https://softwaretesters.slack.com/messages/automation/">softwaretesters</a></li> </ul> <p><br /></p> <p>Ну и с наступающим вас!</p> <p>Быстрых и эффективных тестов! Пусть селекторы селектяцца, сервисы мочацца, базы данных инмеморяцца, а разработчики юнит-тестяцца.</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/11/30/selenide-3.0/ http://ru.selenide.org/2015/11/30/selenide-3.0 2015-11-30T00:00:00+00:00 Изменения в Selenide 2.25 <p>Привет!</p> <p>Мы выпустили Selenide 2.25. Это последняя версия в линейке 2.*, даже будет большой рефакторинг, чистка deprecated и релиз Selenide 3.0.</p> <p>Пожалуйста, пробегитесь по своим тестам и убедитесь, что вы не используете @Deprecated методы. Мы планируем их удалить в Selenide 3.0.</p> <p>А пока - новинки в Selenide 2.25:</p> <h2 id="добавили-профайлер-selenide-для-testng">Добавили “Профайлер Selenide” для TestNG</h2> <p>Эта штука, которую вы знали под названием <a href="http://ru.selenide.org/2015/05/05/selenide-2.16-and-2.17/">PrettyReportCreator</a>, прежде была доступна только для JUnit.</p> <p>Кстати, мы переименовали его в <code class="language-plaintext highlighter-rouge">TextReport</code>.</p> <p>Чтобы включить профайлер в JUnit:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Rule</span> <span class="kd">public</span> <span class="nc">TestRule</span> <span class="n">report</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TextReport</span><span class="o">();</span> </code></pre></div></div> <p>Чтобы включить профайлер в TestNG:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Listeners</span><span class="o">(</span><span class="nc">TextReport</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">GoogleTestNGTest</span> <span class="o">{</span> <span class="o">...</span> <span class="o">}</span> </code></pre></div></div> <h2 id="добавили-методы-first-и-last">Добавили методы <code class="language-plaintext highlighter-rouge">$$.first()</code> и <code class="language-plaintext highlighter-rouge">$$.last()</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">"#employees .fired"</span><span class="o">).</span><span class="na">first</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">"Steve Jobs"</span><span class="o">));</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#employees .fired"</span><span class="o">).</span><span class="na">last</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">"Richard Williamson"</span><span class="o">));</span> </code></pre></div></div> <h2 id="добавили-метод-screenshotsgetlastscreenshot">Добавили метод <code class="language-plaintext highlighter-rouge">Screenshots.getLastScreenshot()</code></h2> <p>Этот метод возвращает <em>последний</em> скриншот, снятый Selenide. Он будет полезен тем, кто хочет интегрировать Selenide с какими-то фреймворками для построения отчётов, такими как Allure.</p> <p>Кстати, мы переименовали метод <code class="language-plaintext highlighter-rouge">getScreenShotAsFile()</code> в <code class="language-plaintext highlighter-rouge">takeScreenShotAsFile()</code>, потому что старое название было обманчивым: этот метод снимает скриншот, а не просто возвращает.</p> <h2 id="добавили-метод-selenideconfirm-и-selenidedismiss-без-параметров">Добавили метод <code class="language-plaintext highlighter-rouge">Selenide.confirm()</code> и <code class="language-plaintext highlighter-rouge">Selenide.dismiss()</code> без параметров</h2> <p>До сих пор в Selenide были методы <code class="language-plaintext highlighter-rouge">confirm(String)</code> и <code class="language-plaintext highlighter-rouge">dismiss(String)</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">"Вы точно уверены, что хотите удалить файл?"</span><span class="o">);</span> <span class="n">dismiss</span><span class="o">(</span><span class="s">"Вы точно уверены, что хотите удалить файл?"</span><span class="o">);</span> </code></pre></div></div> <p>Эти методы нажимают “ok” или “cancel” в модальном диалоге “confirm” и проверяют, что текст был правильным.</p> <p>Но иногда вы не хотите проверять текст. Поэтому мы добавили методы <code class="language-plaintext highlighter-rouge">confirm()</code> и <code class="language-plaintext highlighter-rouge">dismiss()</code> без параметров:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="n">confirm</span><span class="o">();</span> <span class="nc">String</span> <span class="n">text</span> <span class="o">=</span> <span class="n">dismiss</span><span class="o">();</span> </code></pre></div></div> <p>они не проверяют, но возвращают текст диалогового окна.</p> <h2 id="добавили-параметр--dselenidereopenbrowseronfail">Добавили параметр <code class="language-plaintext highlighter-rouge">-Dselenide.reopenBrowserOnFail</code></h2> <p>По умолчанию Selenide пытается открыть браузер заново, если он завис. Пусть один тест упадёт, но зато следующие побегут дальше. Иногда вы не хотите открывать браузер заново и предпочитаете, чтобы все последующие тесты упали.</p> <p>В этом случае просто используйте новую настройку: <code class="language-plaintext highlighter-rouge">-Dselenide.reopenBrowserOnFail=false</code></p> <h2 id="обновились-до-htmlunit-219">Обновились до htmlunit 2.19</h2> <p>Многие считают браузер HtmlUnit умершим, а нам он нравится. По крайней мере, тесты самого Selenide на нём стабильно бегают.</p> <h2 id="поменяли-лицензию-selenide-на-mit">Поменяли лицензию Selenide на MIT</h2> <p>Об мы уже писали <a href="http://ru.selenide.org/2015/11/16/selenide-changes-license-to-mit/">отдельно</a>, но если вкратце - лицензия MIT наиболее свободная. Мы надеемся, это облегчит выбор в пользу Selenide для компаний, погрязших в бюрократических ограничениях.</p> <p><br /></p> <h1 id="новости">Новости</h1> <ul> <li>На популярном портале automated-testing.info появился специальный <a href="http://automated-testing.info/c/webdriver/selenide">раздел, посвящённый Selenide</a></li> <li><a href="/2015/11/13/selenide-on-devoxx/">Презентация Selenide</a> на Devoxx 2015 - крупнейшей Java-конференции Европы</li> <li>Скринкаст Якова Крамаренко <a href="https://www.youtube.com/watch?v=TZhbI-JPdG0&amp;feature">о тест-покрытии и Selenide</a> - пробный прогон для конференции SQA Days 2015</li> <li>13ый выпуск подкаста Radio QA <a href="http://radio-qa.com/vypusk-13-kak-natestirovat-na-iphone/">Как натестировать на iPhone</a></li> <li>14ый выпуск подкаста Radio QA <a href="http://radio-qa.com/014-qa-live-or-dead/">QA: Пациент жив или мертв?</a></li> </ul> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/11/30/selenide-2.25/ http://ru.selenide.org/2015/11/30/selenide-2.25 2015-11-30T00:00:00+00:00 Selenide меняет лицензию на MIT <p>Привет!</p> <p>Мы долго сомневались, но всё-таки поменяли лицензию Selenide на MIT.</p> <p>Если вкратце, это значит, что с исходным кодом Selenide можно делать всё, что угодно.</p> <p>MIT - наиболее <strong>свободная лицензия</strong>.</p> <p><a href="http://choosealicense.com/">Вот тут</a> есть иллюстративное сравнение опен-сорсных лицензий MIT, Apache и GPL.</p> <h3 id="чем-не-устраивал-lgpl">Чем не устраивал LGPL?</h3> <p>Вообще-то LGPL нас устраивал. Лицензия LGPL обязывает вас выкладывать свой код в опен-сорс, <strong>если</strong> вы включаете код Selenide в свой продукт. Но поскольку вы используете Selenide только в своих тестах, то вы не включаете Selenide в свой продукт, который вы доставляете пользователям. Следовательно, вы не обязаны выкладывать свой код в опен-сорс.</p> <h3 id="почему-мы-поменяли-лицензию">Почему мы поменяли лицензию?</h3> <p>Оказалось, что многие компании просто боятся GPL. Они не хотят связываться с GPL в принципе, не разбираясь в нюансах. Поэтому мы решили поменять лицензию Selenide на самую свободную и ни к чему не обязывающую - MIT.</p> <p>Если у вас есть знакомые, не использующие Selenide из-за лицензии - сегодня у вас есть для них хорошие новости! :)</p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/11/16/selenide-changes-license-to-mit/ http://ru.selenide.org/2015/11/16/selenide-changes-license-to-mit 2015-11-16T00:00:00+00:00 Презентация Selenide на Devoxx 2015 <p>Теперь - и в Европе!</p> <p>Эта неделя ознаменовалась громким событием в Java-мире - конференцией Devoxx, крупнейшей в Европе.</p> <p>Я сделал доклад про Selenide на этой конференции. И эти ребята уже выложили видео - они просто нереально быстрые:</p> <div class="wrapper-content center"> <iframe width="840" height="473" src="https://www.youtube.com/embed/BjEW08vDUfI" frameborder="0" allowfullscreen=""></iframe> </div> <p>А тут слайды:</p> <div class="wrapper-content center"> <iframe src="https://docs.google.com/presentation/d/1qA_0wC2pV9IQZu3DoLwm8yGQ_9DlM1WdNzUYgAWFGiQ/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="960" height="569" allow="fullscreen" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe> </div> <p><br /></p> <p><a href="https://www.youtube.com/channel/UCCBVCTuk6uJrN3iFV_3vurg">А вот тут</a> есть видео остальных докладов Devoxx 2015.</p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/11/13/selenide-on-devoxx/ http://ru.selenide.org/2015/11/13/selenide-on-devoxx 2015-11-13T00:00:00+00:00 Изменения в Selenide 2.24 <p>Привет!</p> <p>Вышел небольшой релиз Selenide 2.24. По сусекам поскребли, по амбару намели.</p> <h2 id="обновились-до-selenium-java-2482">Обновились до selenium-java 2.48.2</h2> <p>Список <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">изменений в selenium 2.48.2</a>.</p> <h2 id="новый-метод-pressescape">Новый метод <code class="language-plaintext highlighter-rouge">$.pressEscape()</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">pressEnter</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">pressTab</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">"input"</span><span class="o">).</span><span class="na">pressEscape</span><span class="o">();</span> </code></pre></div></div> <h2 id="исправили-работу-мягких-ассертов-в-testng">Исправили работу мягких ассертов в TestNG</h2> <p>Возможно, вы не знали, но в Selenide есть <a href="http://ru.selenide.org/2015/05/05/selenide-2.16-and-2.17/">мягкие ассерты</a> (soft asserts). В какой-то версии они оказались сломанными, если запускать тесты на TestNG. Теперь починились.</p> <h2 id="логику-для-создания-вебдрайвера-выделили-в-отдельный-класс-webdriverfactory">Логику для создания вебдрайвера выделили в отдельный класс WebDriverFactory</h2> <p>Раньше созданием вебдрайвера занимался <code class="language-plaintext highlighter-rouge">WebDriverRunner</code>, но со временем он разросся: помимо создания вебдрайвера, появилась логика для закрытия, проверки, отслеживания зависших браузеров и т.д.</p> <h1 id="новости">Новости</h1> <ul> <li>Конференция Devoxx.be<br /> Я <a href="http://cfp.devoxx.be/2015/talk/QBD-3461/Selenide:_concise_UI_Tests_in_Java._From_developers_for_developers.">буду представлять Selenide</a> на крупнейшей Java-конференции Европы Devoxx в Антверпене<br /> Это же здорово, товарищи!<br /></li> <li>10ый выпуск подкаста Radio QA <a href="http://radio-qa.com/vypusk-10-strategiya-testirovaniya/">Стратегия тестирования</a></li> <li>11ый выпуск подкаста Radio QA <a href="http://radio-qa.com/011-hr/">HR: НЯ или не НЯ</a></li> <li>12ый выпуск подкаста Radio QA <a href="http://radio-qa.com/vypusk-12-obuchi-menya-esli-smozhesh/">Обучи меня, если сможешь</a></li> <li>Обзор конференции <a href="http://automation-remarks.com/qafest-retrospective/">QAFest 2015</a></li> </ul> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/11/08/selenide-2.24/ http://ru.selenide.org/2015/11/08/selenide-2.24 2015-11-08T00:00:00+00:00 Доклад о Selenide на SeleniumConf 2015 <p>Наконец-то!</p> <p>У меня появился шанс представить Selenide на ежегодной конференции SeleniumConf, которая в этом году проходила в Портленде, США.</p> <p>Вот видео моего доклада:</p> <div class="wrapper-content center"> <iframe width="840" height="473" src="https://www.youtube.com/embed/fR8CyLcxBZ0" frameborder="0" allowfullscreen=""></iframe> </div> <p>И слайды:</p> <div class="wrapper-content center"> <iframe src="https://docs.google.com/presentation/d/1ZksjuL2vPN_pkhMuon0HH4gm7KNmjU50pByRRGzgVko/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="960" height="569" allow="fullscreen" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe> </div> <p><br /></p> <p><a href="http://confengine.com/selenium-conf-2015/schedule">А здесь</a> можно посмотреть видео других докладов с SeleniumConf.</p> <p>Наслаждайтесь!</p> <p>Andrei Solntsev</p> <p>selenide.org</p> http://ru.selenide.org/2015/09/23/selenide-on-seleniumconf/ http://ru.selenide.org/2015/09/23/selenide-on-seleniumconf 2015-09-23T00:00:00+00:00 Изменения в Selenide 2.23 <p>Категорически приветствую!</p> <p>Сентябрь ознаменовался выходом Selenide 2.23. Пробежимся по новинкам?</p> <h2 id="новый-метод-selectradio">Новый метод <code class="language-plaintext highlighter-rouge">$.selectRadio()</code></h2> <p>Чтобы выбрать кнопку radio, раньше можно было использовать метод</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Selenide</span><span class="o">.</span><span class="na">selectRadio</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">"gender"</span><span class="o">),</span> <span class="s">"male"</span><span class="o">);</span> </code></pre></div></div> <p>Теперь появился новый метод, похожий на остальные методы Selenide:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="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">"gender"</span><span class="o">)).</span><span class="na">selectRadio</span><span class="o">(</span><span class="s">"male"</span><span class="o">);</span> </code></pre></div></div> <p>И в лучших традициях Selenide, этот метод умеет ждать, если элемент доступен не сразу.</p> <h2 id="метод-setvalue-теперь-понимает-и-кнопки-radio">Метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> теперь понимает и кнопки radio</h2> <p>Мы стремимся делать Selenide максимально универсальным, так чтобы вам приходилось меньше думать о технических деталях веб-элементов. В частности, метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> понимает, какому элементу вы пытаетесь присвоить значение: <code class="language-plaintext highlighter-rouge">input</code>, <code class="language-plaintext highlighter-rouge">select</code> или <code class="language-plaintext highlighter-rouge">textarea</code>, и действует соответственно.</p> <p>Теперь к этому списку добавились и radio buttons.</p> <p>Метод <code class="language-plaintext highlighter-rouge">$.setValue()</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="nc">By</span><span class="o">.</span><span class="na">name</span><span class="o">(</span><span class="s">"gender"</span><span class="o">)).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"male"</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">"gender"</span><span class="o">)).</span><span class="na">val</span><span class="o">(</span><span class="s">"male"</span><span class="o">);</span> </code></pre></div></div> <h2 id="нельзя-менять-значение-в-поле-readonly">Нельзя менять значение в поле readonly</h2> <p>Теперь метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> выкинет исключение, если вы попытаетесь вбить значение в поле с признаком <code class="language-plaintext highlighter-rouge">readonly</code>. Будьте внимательны, это потенциально может сломать некоторые из ваших тестов.</p> <h2 id="метод-setvalue-теперь-учитывает-атрибут-maxlength">Метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> теперь учитывает атрибут <code class="language-plaintext highlighter-rouge">maxlength</code></h2> <p>Если вы пытаетесь вбить слишком длинный текст в элемент с атрибутом <code class="language-plaintext highlighter-rouge">maxlength</code>, Selenide обрежет значение. Будьте внимательны, и это тоже может сломать некоторые из ваших тестов.</p> <h2 id="исправлена-проблема-с-незакрывающимися-браузерами-firefox">Исправлена проблема с незакрывающимися браузерами FireFox</h2> <p>Некоторые пользователи продолжали жаловаться на незакрывающиеся браузеры.</p> <p>Поскольку повторить эти ошибки у нас не получается, в итоге мы вернули в Selenide 2.23 старый надёжный механизм управления браузерами. Новый механизм (с защитой от зависаний вебдрайвера) тоже доступен, и при желании каждый может его подключить в проект. Мы продолжим работу над ним.</p> <!-- Итого, у вас есть две возможности: 1. (режим по умолчанию) Браузер создаётся и закрывается в том же потоке, что и используется * Плюс: браузеры закрываются стабильно * Минус: нет защиты от зависания вебдрайвера при открытии/закрытии 2. Браузер создаётся и закрывается в отдельном потоке с таймаутами * Плюс: это защищает от зависания вебдрайвера при открытии/закрытии. Если вебдрайвер зависает (а у нас это регулярно случается с хромом), то Selenide через 15 секунд обрывает его и пытается снова. * Минус: некоторые пользователи жалуются на незакрывающиеся браузеры Firefox. Проблему изучаем. --> <p><br /> <br /></p> <h1 id="новости">Новости</h1> <p>Сегодня у нас потрясающие новости, позвольте поделиться!</p> <ul> <li>Конференция SeleniumConf 2015<br /><br /> Ура, библиотека Selenide наконец-то была представлена на ежегодной конференции SeleniumConf, которая в этом году проходила в Портленде, США.<br /> Вот <a href="https://www.youtube.com/watch?v=fR8CyLcxBZ0">видео</a> и <a href="https://t.co/Ih8FQ7VJMj">слайды</a> моей презентации.<br /><br /> Кстати, остальные видео с SeleniumConf 2015 доступны <a href="https://www.youtube.com/results?filters=month&amp;lclk=month&amp;search_query=seconf2015">здесь</a>. <br /><br /></li> <li>Исторический момент: вышла книга про Selenide! Точнее, книга называется <a href="http://www.amazon.com/Test-Driven-Java-Development-Viktor-Farcic/dp/1783987421">Test-Driven Java Development</a>, но примеры кода в ней используют именно Selenide в связке с JBehave и Cucumber. <br /> Ну это же просто праздник какой-то! <br /></li> </ul> <center> <img src="/images/2015/09/test-driver-java-development.2015.jpg" /> </center> <p><br /><br /></p> <ul> <li>9ой выпуск подкаста Radio QA <a href="http://radio-qa.com/special-programmers-day/">День программиста</a></li> <li>Эпичная заметка Сергея Пирогова <a href="http://automation-remarks.com/pochemy-svoy-fremawork-ploho/">Почему фразу “Мы будем писать свой тестовый фреймворк” нужно произносить шепотом</a></li> <li>Доклад Андрея Стахиевича с конференции ComaQA.BY <a href="https://comaqa.by/2015/09/11/conf2-selenide/">Page Object паттерн и Selenide</a></li> <li>Доклад Антон Семенченко <a href="https://www.youtube.com/watch?v=7bL-LQpAKEo&amp;feature=youtu.be&amp;a">Appium+Selenide: Cross-Platform Mobile QA Automation</a></li> <li>По следам конференции #socrates15: <a href="http://www.tuicool.com/articles/VbUjYr">Ext JS 5 Tests with Selenide</a></li> </ul> <h2 id="немного-статистики">Немного статистики</h2> <p>Статистика скачиваний Selenide за август:</p> <center> <img src="/images/2015/09/selenide_downloads.png" width="800" /> </center> <p>Нас всё больше! И с сентябре будет ещё больше!</p> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/09/15/selenide-2.23/ http://ru.selenide.org/2015/09/15/selenide-2.23 2015-09-15T00:00:00+00:00 Изменения в Selenide 2.21 и 2.22 <p>Доброй субботы!</p> <p>Недавно мы зарелизили Selenide 2.21 и 2.22. Там были немногочисленные, но кардинальные изменения, о которых вам нужно знать.</p> <h2 id="переход-на-java-7">Переход на Java 7</h2> <p>Начиная с версии 2.21, Selenide работает только на Java 7 или выше.</p> <p>Если вы до сих пор работали на Java 6, пора обновляться - больше ждать невозможно!</p> <h2 id="обновились-на-selenium-2471">Обновились на Selenium 2.47.1</h2> <ul> <li>Она-то и требует Java 7.</li> <li>поддержка нативных событий только до FireFox 31, а для более новых FireFox есть только синтетические события.</li> <li>Добавилась экспериментальная поддержка нового браузера “Microsoft Edge”</li> </ul> <p><a href="https://github.com/SeleniumHQ/selenium/blob/master/dotnet/CHANGELOG">Список изменений 2.47.1</a></p> <h2 id="исправлена-проблема-с-незакрывающимися-браузерами">Исправлена проблема с незакрывающимися браузерами</h2> <p>В версии Selenide 2.20 изменился механизм закрытия браузеров. Появился неприятный эффект, когда некоторые браузеры оставались незакрытыми. Проблему оперативно залечили.</p> <p><br /> <br /></p> <h1 id="спасибо-за-вашу-помощь">Спасибо за вашу помощь!</h1> <p>Спасибо всем, кто проголосовал. Ваши голоса помогли, и мою заявку приняли на конференцию <a href="http://year-2015.seleniumconf.org/">SeleniumConf 2015</a>. Еду 8 сентября в Портланд рассказывать про Selenide американской аудитории.</p> <h1 id="новости">Новости</h1> <p>Хочу поделиться новостями</p> <ul> <li>8ой выпуск подкаста Radio QA <a href="http://radio-qa.com/008-hihicriticals/">Про хихикритикалы</a></li> <li>7ой выпуск подкаста Radio QA <a href="http://radio-qa.com/007-tools-of-tester/">Инструменты тестировщика</a></li> <li>Видео: Алексей Баранцев <a href="http://www.devclub.eu/2015/08/24/video-barancev-aspiration-driven-development/">Aspiration Driven Development</a></li> <li>Видео: Ирина Иванова <a href="https://www.youtube.com/watch?v=ME4HqzRjjJs">Ловушки мышления в тестировании</a></li> <li>Видео: Антон Кекс и Дмитрий Эсс <a href="http://www.devclub.eu/2015/08/24/video-codeborne-tarkvarakool/">Школа программирования Codeborne</a></li> <li><a href="http://testingchallenges.thetestingmap.org/">Testing challenge</a></li> </ul> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/08/29/selenide-2.22/ http://ru.selenide.org/2015/08/29/selenide-2.22 2015-08-29T00:00:00+00:00 Изменения в Selenide 2.20 <p>Здрасьте!</p> <p>Сегодня мы зарелизили Selenide 2.20. Что новенького нас ждёт?</p> <h2 id="защита-от-зависания-вебдрайвера-при-открытиизакрытии-браузера">Защита от зависания вебдрайвера при открытии/закрытии браузера</h2> <p>Иногда при открытии или открытии браузера вебдрайвер зависает. Видимо, какие-то баги в вебдрайвере. Просто этот поток бесконечно висит в одном и том же состоянии: “Forwarding newSession on session null to remote”</p> <p>Мы добавили защиту против этой проблемы. Теперь Selenide запускаете открытие/закрытие браузера в отдельном потоке и ждёт ограниченное время (не дольше 15 секунд при открытии и не 5 секунд при закрытии). При неудаче пробует снова (до 3 раз) и лишь потом вылетает с ошибкой.</p> <p>Спасибо <a href="https://github.com/admizh">@admizh</a> за это предложение!</p> <p>См. <a href="https://github.com/selenide/selenide/issues/199">Issue 199</a> и <a href="https://github.com/selenide/selenide/issues/204">Issue 204</a></p> <h2 id="логирование-переведено-на-javautillogging">Логирование переведено на java.util.logging</h2> <p>До сих пор Selenide выводил свои сообщения через <code class="language-plaintext highlighter-rouge">System.out</code> и <code class="language-plaintext highlighter-rouge">System.err</code></p> <p>Вообще-то я думаю, что для тестов этого вполне достаточно: просто и надёжно. Но идя навстречу пожеланиям трудящихся, мы перевели логирование Selenide на “стандартный” механизм <code class="language-plaintext highlighter-rouge">java.util.logging</code> (JUL).</p> <blockquote> <p>Честно говоря, log4j и slf4j явно лучше JUL, но JUL поставляется вместе с Java и используется в Selenium, так что нам не было смысла добавлять в Selenide ещё одну зависимость.</p> </blockquote> <p>Хорошая новость в том, что вам ничего не надо делать, чтобы перейти на новое логирование. Оно само заработает, и по умолчанию будет выводить логи в консоль. Но будет не очень-то красивым:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Jul</span> <span class="mi">25</span><span class="o">,</span> <span class="mi">2015</span> <span class="mi">10</span><span class="o">:</span><span class="mi">48</span><span class="o">:</span><span class="mi">21</span> <span class="no">PM</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">impl</span><span class="o">.</span><span class="na">WebDriverThreadLocalContainer</span> <span class="n">createDriver</span> <span class="nl">INFO:</span> <span class="nc">Create</span> <span class="nl">webdriver:</span> <span class="mi">1</span> <span class="o">-&gt;</span> <span class="nl">FirefoxDriver:</span> <span class="n">firefox</span> <span class="n">on</span> <span class="nf">MAC</span> <span class="o">(</span><span class="mi">3</span><span class="n">e54e3de</span><span class="o">-</span><span class="n">b212</span><span class="o">-</span><span class="mi">2</span><span class="n">a45</span><span class="o">-</span><span class="mi">93</span><span class="n">ad</span><span class="o">-</span><span class="mi">712</span><span class="n">aae6ee853</span><span class="o">)</span> </code></pre></div></div> <p>Чтобы сделать логирование чуть удобнее (если у вас в проекте вдруг ещё не настроен JUL), можно добавить такую строчку в начало тестов:</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">"java.util.logging.SimpleFormatter.format"</span><span class="o">,</span> <span class="s">"%1$tT %4$s %5$s%6$s%n"</span><span class="o">);</span> </code></pre></div></div> <p>Или если вы предпочитаете slf4j, то добавьте зависимость <code class="language-plaintext highlighter-rouge">org.slf4j:jul-to-slf4j:1.7.12</code> в проект и такую строчку в начало тестов:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">SLF4JBridgeHandler</span><span class="o">.</span><span class="na">removeHandlersForRootLogger</span><span class="o">();</span> <span class="nc">SLF4JBridgeHandler</span><span class="o">.</span><span class="na">install</span><span class="o">();</span> </code></pre></div></div> <p>Теперь логи Selenide будут выглядеть так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">22</span><span class="o">:</span><span class="mi">48</span><span class="o">:</span><span class="mi">15</span> <span class="no">INFO</span> <span class="nl">INFO:</span> <span class="nc">Create</span> <span class="nl">webdriver:</span> <span class="mi">1</span> <span class="o">-&gt;</span> <span class="nl">FirefoxDriver:</span> <span class="n">firefox</span> <span class="n">on</span> <span class="nf">MAC</span> <span class="o">(</span><span class="mi">3</span><span class="n">e54e3de</span><span class="o">-</span><span class="n">b212</span><span class="o">-</span><span class="mi">2</span><span class="n">a45</span><span class="o">-</span><span class="mi">93</span><span class="n">ad</span><span class="o">-</span><span class="mi">712</span><span class="n">aae6ee853</span><span class="o">)</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/195">Issue 195</a></p> <h2 id="исправили-использование-cookies-при-скачивании-файла">Исправили использование Cookies при скачивании файла</h2> <p>Спасибо <a href="https://github.com/philipp-kolesnikov">@philipp-kolesnikov</a> за этот <a href="https://github.com/selenide/selenide/pull/191">pull request</a>!</p> <h2 id="добавили-поддержку-коллекций-элементов-в-page-objects">Добавили поддержку коллекций элементов в Page Objects</h2> <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">SearchResultsPage</span> <span class="o">{</span> <span class="nd">@FindBy</span><span class="o">(</span><span class="n">css</span> <span class="o">=</span> <span class="s">"#ires li.g"</span><span class="o">)</span> <span class="kd">private</span> <span class="nc">ElementsCollection</span> <span class="n">results</span><span class="o">;</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/186">Issue 186</a></p> <p>Спасибо <a href="https://github.com/rishaselfing">@rishaselfing</a> за подсказку!</p> <p>P.S. Хотя я по-прежнему думаю, что гораздо короче и проще писать пэдж обжекты без использования аннотаций:</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">SearchResultsPage</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">ElementsCollection</span> <span class="n">results</span> <span class="o">=</span> <span class="err">$$</span><span class="o">(</span><span class="s">"#ires li.g"</span><span class="o">);</span> </code></pre></div></div> <h2 id="метод-selectshouldhavetext-теперь-проверяет-выбранную-опцию">Метод $(“select”).shouldHave(text(“…”)) теперь проверяет выбранную опцию</h2> <p>Раньше метод <code class="language-plaintext highlighter-rouge">$("select").shouldHave(text("..."))</code> фактически был бесполезным, т.к. учитывал тексты всех <code class="language-plaintext highlighter-rouge">&lt;option&gt;</code>, а не только выбранных. Теперь же он проверяет только текст выбранных элементов. А также метод <code class="language-plaintext highlighter-rouge">$("select").getText()</code> возвращает только текст выбранных опций.</p> <p>См. <a href="https://github.com/selenide/selenide/issues/134">Issue 134</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">File</span> <span class="n">screenshot</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">"#some-div"</span><span class="o">).</span><span class="na">screenshot</span><span class="o">();</span> </code></pre></div></div> <p>См. <a href="https://github.com/selenide/selenide/issues/66">Issue 66</a></p> <h2 id="все-методы-string--теперь-deprecated">Все методы <code class="language-plaintext highlighter-rouge">$(String, ...)</code> теперь deprecated</h2> <p>Пожалуйста, убедитесь в том, что вы не используйте deprecated методы. Мы собираемся их удалить в одной из следующих версий Selenide (вероятно, 3.0).</p> <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">"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">"Some wrong test"</span><span class="o">).</span><span class="na">because</span><span class="o">(</span><span class="s">"it's wrong text"</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">waitUntil</span><span class="o">(</span><span class="n">hidden</span><span class="o">.</span><span class="na">because</span><span class="o">(</span><span class="s">"it's sensitive information"</span><span class="o">),</span> <span class="mi">100</span><span class="o">);</span> </code></pre></div></div> <h2 id="удалили-зависимость-cglib-nodep-из-зависимостей-selenide">Удалили зависимость cglib-nodep из зависимостей Selenide</h2> <p>Мы обнаружили, что зависимость <code class="language-plaintext highlighter-rouge">cglib-nodep</code> старая и ненужная, и иногда она может создавать конфликты с зависимостями в текущих проектах. К тому же она, кажется, не поддерживает Java 8 и уже лет 5 как не развивается.</p> <p>Если вам всё же по каким-то причинам нужна cglib-nodep, просто добавьте эту зависимость в проект: <code class="language-plaintext highlighter-rouge">cglib:cglib-nodep:jar:3.1</code></p> <p><br /></p> <h1 id="мне-нужна-ваша-помощь">Мне нужна ваша помощь!</h1> <p>Проголосуйте з амою заявку Selenide на конференцию SeleniumConf 2015 <a href="http://confengine.com/selenium-conf-2015/proposal/1294/selenide-concise-ui-tests-in-java">здесь</a>!</p> <h1 id="новости">Новости</h1> <p>Хочу поделиться новостями</p> <ul> <li>Слушайте новый подкаст про тестирование <a href="http://radio-qa.com/">Radio QA</a></li> <li>Читайте гениальные презентации про тестирование от Капитана Хаоса: <a href="http://www.slideshare.net/orgeirIngvarsson/pptx14">Смертные грехи и постыдные слабости автоматизаторов</a></li> <li>Посмотрите, <a href="http://comaqa.by/2015/07/26/appium-selenide/">как скрестить Appium и Selenide</a> для тестирования мобильных приложений.</li> <li>И улыбайтесь, господа: <a href="https://pbs.twimg.com/media/CIlCKqBUcAARucH.jpg">Developer tools непонятный</a></li> </ul> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> <p>Андрей Солнцев</p> <p>ru.selenide.org</p> http://ru.selenide.org/2015/07/27/selenide-2.20/ http://ru.selenide.org/2015/07/27/selenide-2.20 2015-07-27T00:00:00+00:00 Изменения в Selenide 2.19 <p>Категорически приветствую!</p> <p>Вышел Selenide 2.19.</p> <p>Исправлено несколько мелких проблем, в основном касающихся работы с фреймами и борьбой с самоподписанными сертификатами.</p> <h2 id="работа-с-вложенными-фреймами">Работа с вложенными фреймами</h2> <p>Дьявол отлично поработал в начале девяностых. Венец его творчества - это <strong>фреймы</strong>. На дворе 2015 год - биты, бандиты, рэкетиры и бригады остались в прошлом, а фреймы живы и здоровы и до сих пор больно бьют.</p> <p>Selenium Webdriver умеет худо-бедно переключаться во фрейм, но оказывается, фреймы бывают вложенные! И тут вебдрайвер сдаётся и заставляет пользователей последовательно переключаться сначала в один фрейм, потом во фрейм внутри него и т.д. по цепочке.</p> <p>В Selenide 2.19 мы добавили удобный метод для переключения сразу в нужный вложенный фрейм одной командой:</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="n">switchTo</span><span class="o">().</span><span class="na">innerFrame</span><span class="o">(</span><span class="s">"parentFrame"</span><span class="o">,</span> <span class="s">"childFrame_2"</span><span class="o">,</span> <span class="s">"childFrame_2_1"</span><span class="o">);</span> </code></pre></div></div> <p>Кстати, поиск фрейма ещё и работает быстрее, чем в Selenium, из-за более оптимального использования селекторов.</p> <blockquote> <p>Спасибо @dimand58 за этот pull request!</p> </blockquote> <h2 id="phantomjs-теперь-умеет-работать-с-самоподписанными-сертификатами">PhantomJS теперь умеет работать с самоподписанными сертификатами</h2> <p>Ещё одна находка дьявола - это самоподписанные сертификаты. Это когда администраторы в вашей конторе очень хотят секьюрити и поэтому запускают тестовое приложение на https (типа https://test.company.ru), но на настоящий SSL сертификат деньги жмотят, и поэтому используют в тесте SSL сертификат, который сами же и подписали. В итоге секьюрити никакой не получается, а бедные тестировщики-автоматизаторы получают себе вечный геморрой “браузер плюётся ворнингами”, “файлы не качаются”, “в IE сайт не открывается”. Дьявол довольно потирает копытца.</p> <p>Производители вебдрайверов тоже подкидывают углей в топку. Ведь есть же стандартная настройка <code class="language-plaintext highlighter-rouge">"acceptSslCerts"</code>, которую понимают Chrome, FF и даже htmlunit. Так нет же, PhantomJS её не понимает, требует какую-то свою настройку! А IE так вообще не понимает никаких настроек.</p> <p>В общем, мы докрутили что надо, теперь PhantomJS открывает “https://test.company.ru” без ругательств.</p> <p>С IE пока всё глухо. Кто знает рецепт, поделитесь.</p> <h2 id="метод-download-больше-не-ругается-на-самоподписанные-сертификаты">Метод <code class="language-plaintext highlighter-rouge">$.download()</code> больше не ругается на самоподписанные сертификаты</h2> <p>Если кто не знал, в Selenide есть аццки удобный метод для скачивания файлов:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">cv</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">download</span><span class="o">();</span> </code></pre></div></div> <p>Теперь и он не ругается при попытке скачать файл с “https://test.company.ru”.</p> <h2 id="метод-setvalue-также-генерирует-событие-focus">Метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> также генерирует событие “focus”</h2> <p>Раньше метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> (в режиме <code class="language-plaintext highlighter-rouge">fastSetValue=true</code>) генерировал события “keydown”, “keypress”, “input”, “keyup”, “change”. Теперь добавилось ещё “focus”. Я на самом деле не уверен, нужно ли это. Сделал на всякий случай.</p> <h2 id="исправили-багу-со-скриншотами-из-selenide-218">Исправили багу со скриншотами из Selenide 2.18</h2> <p>В Selenide 2.18 появилась одна забавная бага: Selenide начал делать слишком много скриншотов. Даже когда тесты не падали. Это не вызывало падения тестов, просто занимало больше место на диске.</p> <h2 id="обновились-до-selenium-webdriver-2460">Обновились до Selenium Webdriver 2.46.0</h2> <p><a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Список изменений в 2.46.0</a> внушителен.</p> <ul> <li>Появилась бета-версия вебдрайвера Marionette (какой-то новый замут с Firefox)</li> <li>В каких-то случаях обещали 10-кратное ускорение старта селениум сервера</li> <li>Смерть нативных событий в Firefox 33+</li> <li>Смерть старой Оперы (Presto-based)</li> <li>Ну и много всего другого</li> </ul> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2015/06/21/selenide-2.19/ http://ru.selenide.org/2015/06/21/selenide-2.19 2015-06-21T00:00:00+00:00 Изменения в Selenide 2.18 <p>Здрасьте!</p> <p>В конце апреля мы наконец-то выпустили Selenide 2.18. Это был непростой релиз. Мы полностью переписали алгоритм умных ожиданий, так что теперь-то уж наверняка он отлавливает все StaleElementException.</p> <p>Давайте я расскажу всё подробно.</p> <h2 id="механизм-ожиданий">Механизм ожиданий</h2> <p>Selenide всегда боролся с StaleElementException и другими проблемами, вызванными ajax и таймаутами. Но всё-таки в некоторых редких ситуациях StaleElementException всё ещё можно было получить в предыдущих версиях Selenide.</p> <p>В Selenide 2.18 мы переписали механизм ожиданий, так что теперь StaleElementException стал почти совсем невозможен.</p> <p>Раньше механизм был такой:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">подождатьЭлемент</span><span class="o">().</span><span class="na">click</span><span class="o">()</span> </code></pre></div></div> <p>Selenide сначала ждал, пока элемент станет видимым, а потом делал с ним необходимое действие. То есть действие с элементом было “двухфазным”. И иногда (особенно в SPA-приложениях) могло случиться, что первая фаза отработала корректно, то есть элемент был виден на экране, а во время второй фазы (в данном примере click) элемент пропадал.</p> <p>Теперь алгоритм стал таким:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">пока</span> <span class="o">(</span><span class="n">ошибки</span> <span class="n">и</span> <span class="o">&lt;</span><span class="mi">4</span> <span class="n">секунд</span><span class="o">)</span> <span class="o">{</span> <span class="n">элемент</span><span class="o">.</span><span class="na">click</span><span class="o">()</span> <span class="o">}</span> </code></pre></div></div> <p>Такой алгоритм, во-первых, чуть быстрее, потому что для большинства элементов (которые не пропадают и не появляются) он делает всего одно действие вместо двух. А во-вторых, вероятность получить StaleElementException тут меньше. Хотя, увы, всё ещё ненулевая. :(</p> <h2 id="скриншоты">Скриншоты</h2> <p>Теперь Selenide делает скриншоты после любых ошибок. А раньше он не делал скриншоты, например, в случае селениумовских ошибок (скажем, если метод <code class="language-plaintext highlighter-rouge">click()</code> ругнулся, что элемент некликабельный).</p> <h2 id="метод-shouldhavevaluejohn-игнорирует-невидимые-символы-пробелы-табуляции-переводы-строк">Метод <code class="language-plaintext highlighter-rouge">$.shouldHave(value("john"))</code> игнорирует невидимые символы (пробелы, табуляции, переводы строк).</h2> <p>Это полезно, чтобы тесты не валились из-за того, что в тестовых данных число отформатировано с пробелом, а на страничке символом nbsp (non-breakable space). Визуально разницы нет, пользователь ничего не заметит - значит, и тест не должен делать различий.</p> <p>А если вам всё-таки очень проверить точное значение, мы добавили специальную проверку <code class="language-plaintext highlighter-rouge">$.shouldHave(exactValue(" john "))</code>.</p> <h2 id="режим-кликов-через-javascript">Режим кликов через JavaScript</h2> <p>Теперь можно включить особый режим, при котором клик будет производиться на средствами браузера (нативно), а с помощью JavaScript. <a href="https://github.com/selenide/selenide/pull/174">Говорят</a>, это полезно для запуска тестов в IE, потому что он всегда стабильно кликает.</p> <p>Включить новый режим можно через свойство <code class="language-plaintext highlighter-rouge">-Dselenide.click-via-js=true</code></p> <blockquote> <p>Спасибо @dimand58 за этот pull request!</p> </blockquote> <h2 id="добавили-метод-doubleclick">Добавили метод <code class="language-plaintext highlighter-rouge">$.doubleClick()</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">".modal-popup"</span><span class="o">).</span><span class="na">doubleClick</span><span class="o">();</span><span class="err">`</span> </code></pre></div></div> <h2 id="методы-hover-contextclick-draganddropto-теперь-можно-чейнить">Методы <code class="language-plaintext highlighter-rouge">$.hover()</code>, <code class="language-plaintext highlighter-rouge">$.contextClick()</code>, <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code> теперь можно <em>чейнить</em></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">"#username"</span><span class="o">).</span><span class="na">hover</span><span class="o">().</span><span class="na">contextClick</span><span class="o">().</span><span class="na">doubleClick</span><span class="o">();</span> </code></pre></div></div> <h2 id="исправлена-работа-sizzle-селекторов-на-страничках-без-jquery">Исправлена работа Sizzle селекторов на страничках без jQuery</h2> <blockquote> <p>Спасибо @Gert за этот pull request!</p> </blockquote> <h2 id="добавлен-метод-webdriverrunnerhaswebdriverstarted">Добавлен метод <code class="language-plaintext highlighter-rouge">WebDriverRunner.hasWebDriverStarted()</code></h2> <p>Это может быть полезно, например, если вы хотите делать свои скриншоты или какие-то другие действия с вебдрайвером в своих тестах.</p> <blockquote> <p>Спасибо @dimand58 за этот pull request!</p> </blockquote> <h2 id="метод-setvalue-в-режиме-fastsetvalue-теперь-генерирует-событие-input">Метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> в режиме <code class="language-plaintext highlighter-rouge">fastSetValue</code> теперь генерирует событие <code class="language-plaintext highlighter-rouge">input</code></h2> <p>Теперь <code class="language-plaintext highlighter-rouge">$.setValue()</code> в режиме <code class="language-plaintext highlighter-rouge">fastSetValue</code> генерирует следующую последовательность событий:</p> <ul> <li><code class="language-plaintext highlighter-rouge">keydown</code></li> <li><code class="language-plaintext highlighter-rouge">keypress</code></li> <li><code class="language-plaintext highlighter-rouge">input</code></li> <li><code class="language-plaintext highlighter-rouge">keyup</code></li> <li><code class="language-plaintext highlighter-rouge">change</code></li> </ul> <p>Это исправило автозаполнение в некоторых приложениях, а также заполнение textarea. Надеюсь, теперь <code class="language-plaintext highlighter-rouge">$.sendKeys()</code> можно заменить на быстрый метод <code class="language-plaintext highlighter-rouge">$.setValue()</code> практически везде.</p> <p>А у вас получилось?</p> <h2 id="теперь-selenide-зависит-от-более-последней-версии-commons-codec-110">Теперь Selenide зависит от более последней версии commons-codec 1.10</h2> <p>Раньше Selenide подгружал (транзитивно от selenium-java) старую версию библиотеки commons-codec 1.6 Это могло создавало трудности для приложений, которые хотят использовать более новую commons-codec.</p> <h2 id="разрешите-похвастаться">Разрешите похвастаться</h2> <p>И на десерт - апрельская статистика скачиваний Selenide из центрального репозитория. Нас становится всё больше!</p> <p>Количество уникальных IP:</p> <center> <img src="/images/2015/05/selenide_downloads_unique_ips.png" width="800" /> </center> <p><br /></p> <p>И количество непосредственно скачиваний:</p> <center> <img src="/images/2015/05/selenide_downloads.png" width="800" /> </center> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2015/05/06/selenide-2.18/ http://ru.selenide.org/2015/05/06/selenide-2.18 2015-05-06T00:00:00+00:00 Изменения в Selenide 2.16 и 2.17 <p>Здрасьте!</p> <p>Мы долго не писали в блог. За это время вышло аж три версии Selenide. Сегодня расскажу про изменениях в версиях 2.16 и 2.17</p> <h3 id="soft-asserts">Soft asserts</h3> <p>Мы наконец-то добавили SoftAsserts в Selenide. Я до сих пор не уверен, что это хорошая идея, но уж очень много просили. :)</p> <p>По сути, режим SoftAssert означает, что все проверки типа <code class="language-plaintext highlighter-rouge">$.shouldHave(text("xxx"))</code> будут валиться не сразу, а только в конце теста. Это позволяет разом накопить все фейлы, разом их исправить и один раз перезапустить тест. Наверное, это удобно, если каждый запуск тестов обходится вам слишком дорого.</p> <p>Чтобы включить Soft asserts в JUnit (см. <a href="https://selenide.org/javadoc/current/com/codeborne/selenide/junit/SoftAsserts.html">документацию</a>):</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">SoftAssertJUnitTest</span> <span class="o">{</span> <span class="nd">@Rule</span> <span class="kd">public</span> <span class="nc">SoftAsserts</span> <span class="n">softAsserts</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SoftAsserts</span><span class="o">();</span> </code></pre></div></div> <p>Чтобы включить Soft asserts в TestNG (см. <a href="https://selenide.org/javadoc/current/com/codeborne/selenide/testng/SoftAsserts.html">документацию</a>):</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Listeners</span><span class="o">(</span><span class="nc">SoftAsserts</span><span class="o">.</span><span class="na">class</span><span class="o">)</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">SoftAssertTestNGTest</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="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">AssertionError</span><span class="o">:</span> <span class="nc">Test</span> <span class="nf">userCanUseSoftAssertWithJUnit</span><span class="o">(</span><span class="n">integration</span><span class="o">.</span><span class="na">SoftAssertJUnitTest</span><span class="o">)</span> <span class="n">failed</span><span class="o">.</span> <span class="mi">3</span> <span class="n">checks</span> <span class="n">failed</span> <span class="no">FAIL</span> <span class="err">#</span><span class="mi">1</span><span class="o">:</span> <span class="nc">Element</span> <span class="n">should</span> <span class="n">have</span> <span class="n">attribute</span> <span class="n">value</span><span class="o">=</span><span class="mi">777</span> <span class="o">{</span><span class="err">#</span><span class="n">radioButtons</span> <span class="n">input</span><span class="o">}</span> <span class="nl">Element:</span> <span class="err">'</span><span class="o">&lt;</span><span class="n">input</span> <span class="n">name</span><span class="o">=</span><span class="s">"me"</span> <span class="n">type</span><span class="o">=</span><span class="s">"radio"</span> <span class="n">value</span><span class="o">=</span><span class="s">"master"</span><span class="o">&gt;&lt;/</span><span class="n">input</span><span class="o">&gt;</span><span class="err">'</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">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">integration</span><span class="o">/</span><span class="nc">SoftAssertJUnitTest</span><span class="o">/</span><span class="n">userCanUseSoftAssertWithJUnit</span><span class="o">/</span><span class="mf">1425503251321.0</span><span class="o">.</span><span class="na">png</span> <span class="nl">Timeout:</span> <span class="mi">0</span> <span class="n">ms</span><span class="o">.</span> <span class="no">FAIL</span> <span class="err">#</span><span class="mi">2</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">radioButtons</span> <span class="n">select</span><span class="o">}</span> <span class="nl">Expected:</span> <span class="n">visible</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">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">integration</span><span class="o">/</span><span class="nc">SoftAssertJUnitTest</span><span class="o">/</span><span class="n">userCanUseSoftAssertWithJUnit</span><span class="o">/</span><span class="mf">1425503252361.1</span><span class="o">.</span><span class="na">png</span> <span class="nl">Timeout:</span> <span class="mi">0</span> <span class="n">ms</span><span class="o">.</span> <span class="nc">Caused</span> <span class="nl">by:</span> <span class="nl">NoSuchElementException:</span> <span class="nc">Unable</span> <span class="n">to</span> <span class="n">locate</span> <span class="nl">element:</span> <span class="o">{</span><span class="s">"method"</span><span class="o">:</span><span class="s">"css selector"</span><span class="o">,</span><span class="s">"selector"</span><span class="o">:</span><span class="s">"#radioButtons select"</span><span class="o">}</span> <span class="no">FAIL</span> <span class="err">#</span><span class="mi">3</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">xxx</span><span class="o">}</span> <span class="nl">Expected:</span> <span class="n">visible</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">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">integration</span><span class="o">/</span><span class="nc">SoftAssertJUnitTest</span><span class="o">/</span><span class="n">userCanUseSoftAssertWithJUnit</span><span class="o">/</span><span class="mf">1425503252697.2</span><span class="o">.</span><span class="na">png</span> <span class="nl">Timeout:</span> <span class="mi">0</span> <span class="n">ms</span><span class="o">.</span> <span class="nc">Caused</span> <span class="nl">by:</span> <span class="nl">NoSuchElementException:</span> <span class="nc">Unable</span> <span class="n">to</span> <span class="n">locate</span> <span class="nl">element:</span> <span class="o">{</span><span class="s">"method"</span><span class="o">:</span><span class="s">"css selector"</span><span class="o">,</span><span class="s">"selector"</span><span class="o">:</span><span class="s">"#xxx"</span><span class="o">}</span> <span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="na">junit</span><span class="o">.</span><span class="na">Assert</span><span class="o">.</span><span class="na">fail</span><span class="o">(</span><span class="nc">Assert</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">88</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">junit</span><span class="o">.</span><span class="na">SoftAsserts</span><span class="o">.</span><span class="na">after</span><span class="o">(</span><span class="nc">SoftAsserts</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">54</span><span class="o">)</span> <span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="na">junit</span><span class="o">.</span><span class="na">rules</span><span class="o">.</span><span class="na">ExternalResource</span><span class="err">$</span><span class="mi">1</span><span class="o">.</span><span class="na">evaluate</span><span class="o">(</span><span class="nc">ExternalResource</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">50</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">intellij</span><span class="o">.</span><span class="na">rt</span><span class="o">.</span><span class="na">execution</span><span class="o">.</span><span class="na">application</span><span class="o">.</span><span class="na">AppMain</span><span class="o">.</span><span class="na">main</span><span class="o">(</span><span class="nc">AppMain</span><span class="o">.</span><span class="na">java</span><span class="o">:</span><span class="mi">134</span><span class="o">)</span> </code></pre></div></div> <h3 id="профайлер">Профайлер</h3> <p>Теперь Selenide умеет составлять отчёт обо всех действиях, совершённых в ходе теста. В том числе логируется время выполнения каждой команды. Это позволит вам точнее понять, что происходит в тестах, на что уходит время и не делает ли тест чего лишнего.</p> <p>Пример отчёта:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--------------------+--------------------------------------------+----------+----------+ |Element |Subject |Status |ms. | +--------------------+--------------------------------------------+----------+----------+ |open |http://0.0.0.0:23762/page_jquery.html |PASSED |131 | |#multirowTable |find elements(by text: Chack) |PASSED |100 | |#multirowTable tr |find elements(by text: Chack) |PASSED |100 | |#multirowTable tr |find(by text: Chack) |PASSED |0 | |by text: Chack |get attribute(class) |PASSED |139 | +--------------------+--------------------------------------------+----------+----------+ </code></pre></div></div> <p>Чтобы включить этот отчёт, нужно добавить такое правило в ваши тесты:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Rule</span> <span class="kd">public</span> <span class="nc">TestRule</span> <span class="n">prettyReportCreator</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PrettyReportCreator</span><span class="o">();</span> </code></pre></div></div> <p>Эта функциональность экспериментальная, пока поддерживается только JUnit.</p> <p>Попробуете - расскажите, как получилось.</p> <blockquote> <p>Спасибо <a href="https://github.com/kumarunster">kumarunster</a> за этот pull request!</p> </blockquote> <p><br /></p> <h3 id="исправлена-функция-closest">Исправлена функция <code class="language-plaintext highlighter-rouge">$.closest()</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="nc">By</span><span class="o">.</span><span class="na">name</span><span class="o">(</span><span class="s">"firstName"</span><span class="o">)).</span><span class="na">closest</span><span class="o">(</span><span class="s">".active"</span><span class="o">);</span> </code></pre></div></div> <p><br /></p> <h3 id="метод-tostring-теперь-логирует-все-атрибуты">Метод <code class="language-plaintext highlighter-rouge">$.toString()</code> теперь логирует все атрибуты</h3> <p>Раньше метод логировал только несколько конкретных атрибутов: <code class="language-plaintext highlighter-rouge">id</code>, <code class="language-plaintext highlighter-rouge">class</code>, <code class="language-plaintext highlighter-rouge">type</code> и т.д. Проблема в том, что Selenium webdriver не предоставляет метода для получения всех атрибутов.</p> <p>Но мы нашли способ! Теперь метод <code class="language-plaintext highlighter-rouge">$.toString()</code>, используя хитрый JavaScript, достаёт все атрибуты:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">assertEquals</span><span class="o">(</span><span class="s">"&lt;div class=\"invisible-with-multiple-attributes\" "</span> <span class="o">+</span> <span class="s">"data-animal-id=\"111\" id=\"gopher\" ng-class=\"widget\" ng-click=\"none\" "</span> <span class="o">+</span> <span class="s">"onchange=\"console.log(this);\" onclick=\"void(0);\" placeholder=\"Животное\" "</span> <span class="o">+</span> <span class="s">"displayed:false&gt;&lt;/div&gt;"</span><span class="o">,</span> <span class="err">$</span><span class="o">(</span><span class="s">"#gopher"</span><span class="o">).</span><span class="na">toString</span><span class="o">());</span> </code></pre></div></div> <h3 id="обновились-до-selenium-2450">Обновились до <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Selenium 2.45.0</a>.</h3> <p>Внимание, с этим релизом отключились нативные события в Firefox.</p> <p>Только не спрашивайте: “И чо?” Я и сам хотел бы знать - и чо?</p> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2015/05/05/selenide-2.16-and-2.17/ http://ru.selenide.org/2015/05/05/selenide-2.16-and-2.17 2015-05-05T00:00:00+00:00 Как тестировать GMail <p>Привет!</p> <p>Вы когда-нибудь задумывались, а как бы вы стали тестировать GMail, если б были разработчиком Google? Это отличное упражнение для тестировщика, непременно попробуйте!</p> <p>Тестирование GMail нетривиально, т.к. в нём на каждый чих используется Ajax, грузится долго, всё генерируется динамически и нет разумных селекторов/айдишек. Замучаешься везде проставлять wait!</p> <h3 id="проект-gmail-test">Проект GMail test</h3> <p>Но счастье возможно! В серии <a href="https://github.com/selenide-examples">Selenide Examples</a> мы представляем <a href="https://github.com/selenide-examples/gmail">проект на гитхабе</a>, в котором написан тест для GMail. Он проверяет содержимое инбокса и пишет новое письмо. Причём ещё и делает “Undo”, редактирует и снова посылает. И в конце ждёт, пока кнопка “Undo” пропадёт.</p> <h3 id="видео">Видео</h3> <p>И тут даже есть короткое видео, демонстрирующее, как это работает:</p> <iframe src="//player.vimeo.com/video/115448433" width="800" height="526" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe> <h2 id="вскрытие-покажет">Вскрытие покажет</h2> <p>Вот сам проект на гитхабе: https://github.com/selenide-examples/gmail</p> <p>Давайте рассмотрим некоторые моменты подробнее.</p> <h3 id="настройка">Настройка</h3> <p>Перед запуском тестов надо увеличить таймаут, т.к. в GMail часто элементы грузятся дольше 4 секунд, которые прописаны в Selenide по умолчанию. Поставим 10 секунд. Хотя на некоторых конференциях, где интернет слабенький, мне и этого не хватало.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@BeforeClass</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">openInbox</span><span class="o">()</span> <span class="o">{</span> <span class="n">timeout</span> <span class="o">=</span> <span class="mi">10000</span><span class="o">;</span> <span class="n">baseUrl</span> <span class="o">=</span> <span class="s">"http://gmail.com"</span><span class="o">;</span> <span class="n">open</span><span class="o">(</span><span class="s">"/"</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">"Loading"</span><span class="o">)).</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span> <span class="n">login</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <p>Обратили внимание, как мы ждём окончания загрузки страницы? В этом вся мощь Selenide: элемент легко найти по тексту, и легко дождаться, пока он пропадёт.</p> <h3 id="логин">Логин</h3> <p>Логин происходит очень просто:</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="kt">void</span> <span class="nf">login</span><span class="o">()</span> <span class="o">{</span> <span class="err">$</span><span class="o">(</span><span class="s">"#Email"</span><span class="o">).</span><span class="na">val</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="s">"gmail.username"</span><span class="o">,</span> <span class="s">"enter-your-gmail-username"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#Passwd"</span><span class="o">).</span><span class="na">val</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="s">"gmail.password"</span><span class="o">,</span> <span class="s">"enter-your-gmail-password"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">"#signIn"</span><span class="o">).</span><span class="na">click</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="s">".error-msg"</span><span class="o">).</span><span class="na">waitUntil</span><span class="o">(</span><span class="n">disappears</span><span class="o">,</span> <span class="mi">2000</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Последняя строчка нужна для того, чтобы тест быстро упал, если вы ввели неверный пароль.</p> <h3 id="количество-непрочитанных-писем">Количество непрочитанных писем</h3> <p>В реальной жизни я бы запускал тесты с определённым набором тестовых данных со, скажем, 4 непрочитанными письмами. И тогда я бы в тесте проверил наличие надписи “Inbox (4)”:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">showsNumberOfUnreadMessages</span><span class="o">()</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">"//div[@role='navigation']"</span><span class="o">)).</span><span class="na">find</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"Inbox (4)"</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>(Но тут у нас случай сложнее, нам приходится тестировать с реальными данными, которые непостоянны. Поэтому ограничимся просто проверкой наличия слова “Inbox”.)</p> <h3 id="проверка-содержимого-инбокса">Проверка содержимого инбокса</h3> <p>Увы, у GMail все локаторы тщательно заобфускированы. Использовать ID или другие селекторы не представляется возможным. Поэтому ограничимся просто проверкой наличия на странице определённых текстов - мы знаем, что такие письма точно должны быть в моём инбоксе:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">inboxShowsUnreadMessages</span><span class="o">()</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">"Gmail Team"</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">1</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">"LastPass"</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="err">$$</span><span class="o">(</span><span class="n">byText</span><span class="o">(</span><span class="s">"Pivotal Tracker"</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> <h3 id="обновление-инбокса">Обновление инбокса</h3> <p>В интерфейсе GMail есть кнопка обновления “Refresh”. Ищем её по атрибуту <code class="language-plaintext highlighter-rouge">title</code>=<code class="language-plaintext highlighter-rouge">Refresh</code> - другого способа я не нашёл.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">userCanRefreshMessages</span><span class="o">()</span> <span class="o">{</span> <span class="c1">// В реальной жизни: INSERT INTO messages ...</span> <span class="err">$</span><span class="o">(</span><span class="n">by</span><span class="o">(</span><span class="s">"title"</span><span class="o">,</span> <span class="s">"Refresh"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> <span class="c1">// В реальной жизни: проверить, что новое письмо появилось в инбоксе</span> <span class="o">}</span> </code></pre></div></div> <p>В настоящих тестах я до нажатия добавил бы новое письмо в базу данных, и после нажатия “Refresh” проверил бы, что оно появилось в инбоксе.</p> <h3 id="новое-письмо">Новое письмо</h3> <p>Для составления нового письма нажимаем кнопку “COMPOSE”:</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">byText</span><span class="o">(</span><span class="s">"COMPOSE"</span><span class="o">)).</span><span class="na">click</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="nc">By</span><span class="o">.</span><span class="na">name</span><span class="o">(</span><span class="s">"to"</span><span class="o">)).</span><span class="na">val</span><span class="o">(</span><span class="s">"andrei.solntsev@gmail.com"</span><span class="o">).</span><span class="na">pressTab</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="n">by</span><span class="o">(</span><span class="s">"placeholder"</span><span class="o">,</span> <span class="s">"Subject"</span><span class="o">)).</span><span class="na">val</span><span class="o">(</span><span class="s">"ConfetQA demo!"</span><span class="o">).</span><span class="na">pressTab</span><span class="o">();</span> <span class="err">$</span><span class="o">(</span><span class="s">".editable"</span><span class="o">).</span><span class="na">val</span><span class="o">(</span><span class="s">"Hello braza!"</span><span class="o">).</span><span class="na">pressEnter</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">"Send"</span><span class="o">)).</span><span class="na">click</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="err">$</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"Your message has been sent."</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> <h3 id="undo---redo">Undo - redo</h3> <p>В моём почтовом ящике включена экспериментальная фича - “Undo”. После отсылки письма в течение 10 секунд горит кнопка “Undo”, нажав на которую, я могу отменить отсылку последнего письма и вернуться к редактированию. Очень удобно, когда, нажав “Send”, обнаруживаешь, что случайно послал письмо не тому человеку.</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="n">byText</span><span class="o">(</span><span class="s">"Undo"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> <span class="n">highlight</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">"Sending has been undone."</span><span class="o">)).</span><span class="na">should</span><span class="o">(</span><span class="n">appear</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">".editable"</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">appear</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="s">"Hello from ConfetQA Selen"</span><span class="o">).</span><span class="na">pressEnter</span><span class="o">().</span><span class="na">pressEnter</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">"Send"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>И ждём 10 секунд, пока кнопка “Undo” пропадёт:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">highlight</span><span class="o">(</span><span class="err">$</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"Your message has been sent."</span><span class="o">)).</span><span class="na">should</span><span class="o">(</span><span class="n">appear</span><span class="o">));</span> <span class="n">highlight</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">"Undo"</span><span class="o">)).</span><span class="na">should</span><span class="o">(</span><span class="n">appear</span><span class="o">)).</span><span class="na">waitUntil</span><span class="o">(</span><span class="n">disappears</span><span class="o">,</span> <span class="mi">12000</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">"Sent Mail"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>Письмо окончательно отослано адресату.</p> <p>Вот теперь GMail протестирован, гугл может спать спокойно.</p> <h2 id="выводы">Выводы</h2> <p>Как видите, интерфейсы без хороших ID тоже можно тестировать. Долгие запросы и ajax не являются помехой. Динамический контент не препятствие. Вот простые рецепты, позволяющие бороться с этими трудностями:</p> <ul> <li><em>Поиск элементов по тексту.</em> <br /> Это максимально приближено к поведению пользователя и минимально зависит от реализации веб-приложения.</li> <li><em>Тестировать только важное</em> <br /> В приложении GMail ещё куча вещей осталась непротестированными. В данных условиях их, кажется, и невозможно протестировать (невозможность подсунуть тестовые данные, отсутствие статических локаторов). Но это не так уж и важно - главное, что критичные функции мы протестировали: показ писем в инбоксе и отсылка нового письма. Всегда начинайте с тестирования критичной функциональности, а остальное успеете оценить позже.</li> <li><em>Используйте правильные инструменты</em> <br /> Selenide автоматически решает проблемы с таймаутами, аяксом, динамическим контентом, поиском по тексту. Тесты не загрязнены длинными страшными XPath. Тесты содержат только логику. Тесты хорошо читаемые.</li> </ul> <p><br /> Простых вам тестов!</p> <div class="author"> Андрей Солнцев<br /> <a href="https://ru.selenide.org">ru.selenide.org</a> </div> <p><br /></p> http://ru.selenide.org/2014/12/28/how-to-test-gmail/ http://ru.selenide.org/2014/12/28/how-to-test-gmail 2014-12-28T00:00:00+00:00 Где взять время на рефакторинг <p>Один из самых животрепещущих вопросов начинающих идеалистов - где взять время на рефакторинг?</p> <p>Он возникает и у разработчиков, и у тестировщиков. И у тех, и у других очень быстро возникает куча грязного кода и куча запутанных, медленных, нечитаемых тестов. Жить с ними всё сложнее. Разработка/тестирование всё замедляется, мотивация всё падает. Иногда удаётся что-то подчистить в нерабочее время, но… это же не по-человечески?</p> <ul> <li>Где справедливость?</li> <li>Это что, мне одному надо?</li> <li>Почему начальство не даёт времени на рефакторинг?</li> </ul> <p>Я знаю. Я тоже через это прошёл.</p> <h3 id="а-нигде">А нигде!</h3> <p>А теперь взглянем правде в глаза. Если кто-то придёт ко мне и попросит неделю на рефакторинг, я ему откажу.</p> <p>И вот почему. Потому, что рефакторить можно бесконечно, а кто-то должен и работу делать.</p> <p>10 лет назад мне было обидно такое слышать. Теперь я сам это говорю. Более того, я говорю, что исправлять код по ночам - это не несправедливо, это - непрофессионально.</p> <p>Так что же, я разуверился в своих идеалах?</p> <h3 id="вовсе-нет">Вовсе нет.</h3> <p>Ровно наоборот. Теперь я знаю совершенно точно, что рефакторинг абсолютно необходим. Код нужно постоянно улучшать во что бы то ни стало. Просто делать это надо немножко по-другому.</p> <p>Не надо ни у кого просить выделить время на рефакторинг. Рефакторинг не является каким-то отдельным от кодирования/тестирования процессом, на который надо отдельно выделять время. Рефакторинг - часть кодирования. Такая же естественная и необходимая часть, как набор, компиляция, запуск.</p> <p>Вот мой рецепт:</p> <h2 id="маленькими-шагами"><strong>Маленькими шагами</strong>!</h2> <p>Вспомните TDD. Ваш рабочий процесс должен выглядеть буквально так:</p> <ul> <li>Написал тест (красный) - 1 час</li> <li>Написал код (чтобы тест позеленел) - 1 час</li> <li>Зарефакторил код (чтобы код стал проще и читаемее) - 1 час</li> <li>Налил чаю, вернулся к шагу 1.</li> </ul> <p>Это постоянный процесс. Маленькие шаги. Постоянный прогресс. Горячий чай.</p> <p>(В зависимости от языка программирования, проекта и людей место 1 часа может быть и 5 минут.)</p> <h3 id="а-как-начальство-на-это-посмотрит">А как начальство на это посмотрит?</h3> <p>Никто вас не обвинит в том, что вы тратите лишнее время, <strong>если</strong> вы будете постоянно показывать <strong>прогресс</strong>. Если с каждым днём вы будете делать новые фичи. В случае тестировщиков - новые тесты.</p> <blockquote> <p>Никого и никогда ещё не уволили за то, что он делал очередную фичу 6 часов, а не 5.</p> </blockquote> <p>Вдумайтесь, на самом деле начальство ведь не знает, сколько её надо делать. Стало быть, никто никогда не сможет вам доказательно предъявить, что вы зря потратили время на написание тестов или рефакторинг. Да, вы не можете доказать, что рефакторинг ускоряет процесс разработки. Но и начальство не может доказать обратное. Стало быть - делайте так, как считаете нужным.</p> <p>Только не поймите меня неправильно, я вовсе не призываю саботировать сроки и делать работу дольше, чем нужно. Наоборот, этот простой рецепт позволит делать работу быстрее:</p> <blockquote> <p>Маленькие шаги: Тест. Код. Рефакторинг.</p> </blockquote> <h3 id="и-что-даже-разрешения-не-надо-спрашивать">И что, даже разрешения не надо спрашивать?</h3> <p>Если кто-то придёт ко мне и попросит неделю на рефакторинг, я ему откажу - по той простой причине, что он не знает мантру TDD. Он почти наверняка профигакторит эту неделю, но лучше ничего не станет.</p> <p>Хороший врач не спрашивает ни у кого разрешения на то, чтобы мыть руки перед операцией. Хороший музыкант не стесняется тратить время перед (и даже во время) концерта, чтобы настроить инструмент. Почему-то все понимают, что без этого они не вылечат и не сыграют. Так чем вы хуже, дорогие мои <em>профессиональные</em> разработчики и тестировщики?</p> <p>Но не забывайте, что врач успевает за сутки хоть кого-нибудь вылечить, а музыкант - слабать хоть пару медлячков. Поэтому рефакторить неделю или не рефакторить совсем - одинаково плохо.</p> <p>Маленькие шаги. Постоянный прогресс. Чай с имбирём.</p> <p>И печенькой.</p> <p><br /></p> http://ru.selenide.org/2014/12/17/time-for-refactoring/ http://ru.selenide.org/2014/12/17/time-for-refactoring 2014-12-17T00:00:00+00:00 The fast and the continuous @ SQA Days 16 <p>Видео моего доклада <a href="http://sqadays.com/ru/talk/25882">“The fast and the continuous”</a> на недавней конференции SQA Days 16 в Санкт-Петербурге.</p> <blockquote> <ul> <li>Вы всё ещё гоняете все свои тесты на Selenium?</li> <li>Вы всё ещё считаете, что TestNG круче, чем JUnit?</li> <li>Вы всё ещё пытаетесь распараллелить свои тесты?</li> <li>Вы всё ещё мечтаете иметь собственный Selenium Grid с 30 машинами и прогонять тесты всего лишь за пару часов?</li> </ul> <p>Тогда мы идём к вам!</p> <p>Я поделюсь нашим опытом автоматизации тестирования, расскажу, как нам удаётся командой из нескольких человек писать и код, и тесты, и прогонять полный цикл автотестов всего лишь за 5 минут без гридов и прочих монстров.</p> <p>Тестирование - это не должно быть сложно!</p> </blockquote> <iframe src="//player.vimeo.com/video/114339032" width="800" height="256" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe> <p><a href="https://vimeo.com/114339032">The fast and the continuous</a> from <a href="https://vimeo.com/orlikov">Vlad Orlikov</a> on <a href="https://vimeo.com">Vimeo</a>.</p> <p>Видео доклада Андрея Солнцева на SQA Days-16<br /> 14-15 ноября 2014, Санкт-Петербург, Россия<br /> www.sqadays.com</p> <p>Местами слайды отстают от звука, и кое-где это напрягает. :( Но в целом я очень доволен выступлением. Подумать только, у меня было 57 слайдов, и я уложился в 30 минут! Это прям уже хорошо, я считаю.</p> <h2 id="антитроллинг">Антитроллинг</h2> <p>Хочу сразу прокомментировать два момента, дабы пресечь ненужный троллинг.</p> <h3 id="пирамида-тестирования">Пирамида тестирования</h3> <p>Картинка взята из гугла, первая попавшаяся. И вообще я её выбирал по цвету. А то первым делом начали спорить, что типы тестирования в пирамиде не те. Это неважно! Всё, о чём я говорил, применимо на всех уровнях, со всеми типами. Мне надо было их вообще закрасить.</p> <h3 id="зачем-провокации">Зачем провокации</h3> <p>Я намеренно начал с провокаций. Я знаю точно, что если просто рассказать “как правильно”, то все покивают головами и разойдутся по домам. И нигде ничего не изменится. А вот если привести конкретные примеры, задать конкретные вопросы - вот тогда-то и выяснится, что на практике понимание теории “как правильно” у всех совершенно разное, вплоть до противоположного. Я знаю, что после моих “провокаций” в зале у большинства людей что-нибудь из того, что я говорю, непременно не сойдётся с их практикой, и в результате недовольных будет много. Но я намеренно иду на этот шаг, потому что только так можно спровоцировать диалог.</p> <p><br /> Приятного просмотра, и <br /> смелее делитесь мнениями, хорошими и разными.</p> <p><br /></p> http://ru.selenide.org/2014/12/13/the-fast-and-the-continuous/ http://ru.selenide.org/2014/12/13/the-fast-and-the-continuous 2014-12-13T00:00:00+00:00 Вы находите не те ошибки <p>На недавней конференции SQA Days в Санкт-Петербурге произошёл один инцидент, который поначалу остался незамеченным. Я не нашёлся сразу, что ответить, лишь улыбнулся в ответ. Но какое-то зерно сомнения проронилось в мой мозг и проросло лишь через несколько дней.</p> <h3 id="в-общем">В общем,</h3> <p>после того, как я оттарабанил <a href="http://sqadays.com/ru/talk/25882">свой доклад</a> о том, каким я вижу по-настоящему эффективное тестирование софта, один товарищ взял микрофон и начала пулять вопросами. После нескольких моих ответов он покачал головой и бросил, садясь:</p> <blockquote> <p>Вы находите не те ошибки.</p> </blockquote> <p>Я лишь улыбнулся.</p> <p>Лишь позже я понял, что именно здесь в корне неправильное.</p> <p>Нет, я не хочу сказать, что мы находим правильные ошибки. Нет.</p> <div class="center"> <b>МЫ ВООБЩЕ НЕ ИЩЕМ ОШИБКИ.</b> </div> <p><br /></p> <h3 id="что-такое-quality-assurance">Что такое Quality Assurance</h3> <p>Это чертовски важный момент. Это, чёрт побери, все QA-инженеры должны знать как отче наш.</p> <p>QA расшифровывается как “Quality Assurance”, то есть - “уверенность в качестве”. Наша первоочерёдная задача <u>не найти все баги</u>. Наша первоочерёдная задача - обеспечить, чтобы <em>оно работало</em>.</p> <h3 id="например">Например</h3> <p>Если мы делаем интернет-магазин для продажи телевизоров, задача QA в первую очередь - убедиться, что пользователь может найти телевизор, нажать “Купить” и заплатить деньги. Это - в первую очередь.</p> <p>То, что при этом баннер съехал, кнопка “Распечатать PDF” открывается в новом окне, кнопка “login” пропадает через раз - это, конечно, ошибки, но они уже второстепенные. Если они есть, будет хреново, конечно, но бизнес будет продолжать работать. А вот если кнопка “Купить” не кликается - пиши пропало, бизнес встал. Это большая разница!</p> <p>Ну и совсем уж третьесортные “ошибки” - это когда пользователь вбивает в поле “логин” тысячу символов и получает бяку. Да ни один нормальный пользователь никогда так не сделает! А если кто-то и сделает, то пусть и получает свою бяку. Ибо нефиг!</p> <p>Я не говорю, что все эти второсортные и третьесортные ошибки не надо искать и исправлять. Надо. Но только после того, как вы убедились, что бизнес работает.</p> <h3 id="отсюда-главный-вывод">Отсюда главный вывод:</h3> <ol> <li> <p>Ваш <strong>первый тест</strong> - “<strong>зелёный путь</strong>”. То есть типичный случай, который приносит прибыль:<br /> Найти телек - “Купить” - заплатить. Неважно, пишите ли вы автоматические тесты или тестируете вручную. Первый тест - зелёный путь.</p> <p>С ним есть одна маленькая проблемка. Это самый скучный тест. Это гарантированный способ находить меньше всех багов в отделе. Это гарантированный способ создать у начальства впечатление, что вы нифига полезного не делаете. Но именно это - первоочерёдная задача QA.</p> </li> <li> <p>Все эти <strong>дебильные тесты</strong> про “<strong>1000 символов</strong> в поле логин” - делать надо, только если всё остальное хорошо. И реально больше некуда девать свободное время. То есть практически никогда. Но именно их тестировщики фигачат в каждом релизе в диких количествах, генерируя тонны никому-не-нужных, не приносящих пользы баг-репортов. Эти баг-репорты сжирают ваши ресурсы и отвлекают внимание от по-настоящему серьёзных проблем, которые проскальзывают незамеченными.</p> </li> </ol> <p>Зато это отличный способ находить по 50 багов в релиз и стать лучшим работником месяца. Это называется “локальная оптимизация”, и это лучшее изобретение дьявола.</p> <p>Не надо гнаться за багами. Всех ошибок не поймаешь. Это как с прыщиками: выдавишь один - появятся три других. Надо не следить за прыщиками, а поднимать общее состояние организма. Спорт, питание, гигиена - и прыщи пройдут сами. Надо писать важные тесты. Надо делать их быстрыми. Надо делать их стабильными. Пусть лучше тесты будут постоянно зелёными, и их покраснение вызовет дикую тревогу, чем если тесты будут постоянно красными, и настоящей эпидемии никто не заметит.</p> <p>Поэтому я говорю:</p> <h3 id="мы-не-ищем-баги">Мы не ищем баги</h3> <p>Мы убеждаемся, что софт работает. Мы пишем автотест на то, что телевизор покупается. Это позволяет нам не тратить время на поддержку массы бесполезных баг-репортов, массы навязчивых тестов. Это освобождает наши ресурсы для того, чтобы оперативно исправить проблемы, которые так или иначе периодически возникают у реальных клиентов, несмотря на наличие QA. Это позволяет нам разрабатывать и поддерживать сложный софт маленькой командой. Маленькой, но очень эффективной командой.</p> <p><em>In testo veritas!</em></p> <p><br /></p> http://ru.selenide.org/2014/12/09/you-find-wrong-bugs/ http://ru.selenide.org/2014/12/09/you-find-wrong-bugs 2014-12-09T00:00:00+00:00 Изменения в Selenide 2.15 <p>Здрасьте!</p> <p>Хорошие новости: вышла новая версия Selenide 2.15. На сей раз нас ждёт много нового. Садитесь поудобнее, начинаем обзор новинок!</p> <h3 id="sizzle-селекторы-css3">Sizzle селекторы (CSS3)</h3> <p>Теперь в Selenide можно использовать <a href="http://sizzlejs.com/">CSS3 селекторы</a> для поиска элементов!</p> <p>Например:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span><span class="o">(</span><span class="s">":contains('Tere Martin!')"</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">":not(a#id)"</span><span class="o">);</span> <span class="err">$$</span><span class="o">(</span><span class="s">":not(div,p)"</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">"[value!='johnny']"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"div:has(span)"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":input"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":text"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":checkbox"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":file"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":password"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":image"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":header"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":first"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":last"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">":even"</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">Configuration</span><span class="o">.</span><span class="na">selectorMode</span> <span class="o">=</span> <span class="nc">Sizzle</span><span class="o">;</span> </code></pre></div></div> <p>Попробуете - расскажите, как получилось.</p> <h3 id="загрузка-многих-файлов-multifile-upload">Загрузка многих файлов (multifile upload)</h3> <p>Теперь функции <code class="language-plaintext highlighter-rouge">$.uploadFile()</code> и <code class="language-plaintext highlighter-rouge">$.uploadFileFromClasspath()</code> могут принимать несколько аргументов. То есть с помощью Selenide можно загружать несколько файлов! В стандартном Selenium webdriver этой функции нет и в ближайшее время не предвидится, поэтому нам пришлось реализовать эту возможность с помощью чёрной магии и напильника. Попробуйте!</p> <h3 id="поддержка-browsermob-proxy">Поддержка BrowserMob proxy</h3> <p>Мы добавили в Selenide удобную возможность добавлять в вебдрайвер Proxy сервер. В первую очередь это было сделано, конечно, ради BrowserMob proxy - популярного среди тестировщиков инструмента. Он позволяет перехватывать http-запросы между веб-приложением и браузером, что даёт массу дополнительных возможностей, недоступных в чистом Selenium webdriver:</p> <ul> <li>загрузка файлов с сервера,</li> <li>работа с сайтами, требующими авторизации,</li> <li>проверка кодов ответа на HTTP-запросы,</li> <li>и т.д.</li> </ul> <p>Подробнее о BrowserMob Proxy можно узнать в <a href="http://habrahabr.ru/post/209752/">замечательном докладе Алексея Баранцева</a>.</p> <blockquote> <p>Спасибо <a href="https://github.com/proton72">Владимиру Денисову</a> за этот pull request!</p> </blockquote> <p>Пример использования BrowserMob Proxy в связке с Selenide можно найти в тесте <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/proxy/ProxyServerUsageTest.java">ProxyServerUsageTest.java</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ProxyServer</span> <span class="n">proxyServer</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ProxyServer</span><span class="o">(</span><span class="n">findFreePort</span><span class="o">());</span> <span class="n">proxyServer</span><span class="o">.</span><span class="na">start</span><span class="o">();</span> <span class="n">proxyServer</span><span class="o">.</span><span class="na">newHar</span><span class="o">(</span><span class="s">"google-test"</span><span class="o">);</span> <span class="nc">WebDriverRunner</span><span class="o">.</span><span class="na">setProxy</span><span class="o">(</span><span class="n">proxyServer</span><span class="o">.</span><span class="na">seleniumProxy</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="nc">List</span><span class="o">&lt;</span><span class="nc">HarEntry</span><span class="o">&gt;</span> <span class="n">harEntries</span> <span class="o">=</span> <span class="n">proxyServer</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> <span class="n">assertTrue</span><span class="o">(</span><span class="n">harEntries</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">getRequest</span><span class="o">().</span><span class="na">getUrl</span><span class="o">().</span><span class="na">equals</span><span class="o">(</span><span class="s">"http://google.com"</span><span class="o">));</span> </code></pre></div></div> <p><br /></p> <h3 id="теперь-можно-зумить-страницу">Теперь можно зумить страницу!</h3> <p>Примеры использования, опять же, смотрим <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/SelenideMethodsTest.java#LC504">в тестах</a>:</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="n">zoom</span><span class="o">(</span><span class="mf">2.0</span><span class="o">);</span> <span class="c1">// Увеличить зум вдвое</span> <span class="n">zoom</span><span class="o">(</span><span class="mf">0.5</span><span class="o">);</span> <span class="c1">// Уменьшить зум вдвое</span> </code></pre></div></div> <p>Иногда полезно для тестирования responsive design.</p> <h3 id="поддержка-json-и-ical">Поддержка JSON и iCal</h3> <p>Теперь можно открывать не-html страницы.</p> <p>Раньше Selenide грохался при попытке открыть не-HTML страницу. Теперь работает.</p> <p>Это может быть полезно для тестирования json, iCal и т.п. ресурсов. Другой вопрос, что может, такие вещи лучше тестировать другими средствами, но иногда удобно.</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="n">source</span><span class="o">(),</span> <span class="n">containsString</span><span class="o">(</span><span class="s">"DTSTART:20140409T090000"</span><span class="o">));</span> </code></pre></div></div> <h3 id="поддержка-табов-и-фреймов">Поддержка табов и фреймов</h3> <p>Теперь Selenide позволяет легко переключаться между вкладками и фреймами не только по заголовку (title), но и по индексу (0, 1, 2, …).</p> <p>Примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/FramesTest.java">работы с фреймами</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">switchTo</span><span class="o">().</span><span class="na">frame</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> <span class="n">assertTrue</span><span class="o">(</span><span class="n">source</span><span class="o">().</span><span class="na">contains</span><span class="o">(</span><span class="s">"Hello, WinRar!"</span><span class="o">));</span> <span class="n">switchTo</span><span class="o">().</span><span class="na">defaultContent</span><span class="o">();</span> <span class="n">switchTo</span><span class="o">().</span><span class="na">frame</span><span class="o">(</span><span class="mi">1</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">"Page with dynamic select"</span><span class="o">));</span> </code></pre></div></div> <p>Примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/TabsTest.java">работы со вкладками</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">switchToWindow</span><span class="o">(</span><span class="mi">1</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">"Page with JQuery"</span><span class="o">));</span> <span class="n">switchToWindow</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">"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">"File uploads"</span><span class="o">));</span> <span class="n">switchToWindow</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">"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">"Page with alerts"</span><span class="o">));</span> <span class="n">switchToWindow</span><span class="o">(</span><span class="mi">0</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">"Tabs"</span><span class="o">));</span> </code></pre></div></div> <h3 id="поддержка-allure-framework">Поддержка Allure Framework</h3> <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">Screenshots</span><span class="o">.</span><span class="na">getScreenShotAsFile</span><span class="o">;</span> <span class="nc">File</span> <span class="n">screenshot</span> <span class="o">=</span> <span class="n">getScreenShotAsFile</span><span class="o">();</span> </code></pre></div></div> <p>Эта функция понадобится для интеграции с <a href="http://habrahabr.ru/company/yandex/blog/232697/">Allure Framework</a>.</p> <blockquote> <p>Спасибо <a href="https://github.com/proton72">Владимиру Денисову</a> за этот pull request!</p> </blockquote> <h3 id="обновились-до-selenium-2440">Обновились до <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Selenium 2.44.0</a>.</h3> <ul> <li>Внимание, вместе с этим обновилась и версия библиотеки Google Guava до 18.0.</li> <li>С этой версией перестал работать PhantomJS :( Я уже сделал <a href="https://github.com/detro/ghostdriver/pull/399">Pull Request в ghostdriver</a>, теперь ждём, когда его зарелизят.</li> </ul> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2014/11/03/selenide-2.15/ http://ru.selenide.org/2014/11/03/selenide-2.15 2014-11-03T00:00:00+00:00 Доклад про Selenide на конференции ConfeT&QA <p>Всем привет!</p> <p>В ноябре 2013 прошла онлайн-конференция по тестированию ConfeT&amp;QA.</p> <p>Я чрезвычайно горд тем, что мне удалось там поучаствовать, и более того, занять первое место по результатам зрительского голосования.</p> <h3 id="selenide-лаконичные-и-стабильные-ui-тесты-на-java"><a href="http://software-testing.ru/library/testing/general-testing/1880-selenide----ui---java">Selenide: лаконичные и стабильные UI тесты на Java</a></h3> <iframe width="800" height="415" src="//www.youtube.com/embed/MTHhflyh8Ck" frameborder="0" allowfullscreen=""></iframe> <p>Конечно, стоит упомянуть, что на ConfeT&amp;QA 2013 было и много других <a href="http://www.youtube.com/results?search_query=+ConfeT%26QA+2013">очень полезных докладов</a>. Спасибо организаторам за организацию и участникам за участие! Надеюсь, не в последний раз.</p> <p>Вообще оказалось, что онлайн-конференция - жутко удобная вещь. Не надо никуда ехать, не надо тратиться на самолёт и гостиницу, и даже почти не надо тратить время. Всего-то три дня подряд заканчиваешь работу на два часа раньше и надеваешь наушники. Это здорово помогает отвлечься от работы, получить массу новых идей и с понедельника выйти на работу с желанием всё перевернуть.</p> <p>Надеюсь, не в последний раз.</p> <p>Андрей Солнцев <br /> ru.selenide.org</p> <p><br /></p> http://ru.selenide.org/2014/10/12/selenide-at-confetqa/ http://ru.selenide.org/2014/10/12/selenide-at-confetqa 2014-10-12T00:00:00+00:00 Изменения в Selenide 2.13 и 2.14 <p>Всем привет!</p> <p>В сентябре мы успели зарелизить сразу две версии: Selenide 2.13 и 2.14. Функций там немного, но есть одна принципиально важная, с неё и начнём.<br /> <br /></p> <h3 id="быстрый-метод-setvalue">Быстрый метод $.setValue</h3> <p>Мы в Codeborne анализировали производительность тестов на реальных проектах и заметили, что самая медленная операция - это не поиск по XPath, как многие думают, а заполнение текстовых полей. То есть функция <code class="language-plaintext highlighter-rouge">$.setValue</code> (она же <code class="language-plaintext highlighter-rouge">WebElement.sendKeys</code>). Дело в том, что по умолчанию Selenium при заполнении поля эмулирует нажатия каждой отдельной клавиши со всеми сопутствующими событиями: keyDown, keyUp и пр. - что делает метод <code class="language-plaintext highlighter-rouge">sendKeys</code> относительно медленным.</p> <p>Можно отключить генерацию событий, но тогда перестанет работать та функциональность, которая на них рассчитывает: автозаполнение, автопоиск и т.д.</p> <p>Мы придумали следующее решение проблемы. Пусть в Selenide будет два разных метода: <code class="language-plaintext highlighter-rouge">$.sendKeys</code> и <code class="language-plaintext highlighter-rouge">$.setValue</code>. Первый будет работать как Selenium по умолчанию, то есть медленно и с генерацией событий. Второй будет работать быстро, устанавливая значение поля через JavaScript. Тогда автор теста сам сможет решить, какой из методов нужен в каждом отдельном случае. В большинстве случаев можно использовать метод <code class="language-plaintext highlighter-rouge">$.setValue</code>, ибо автопоисков не так-то много в сравнении с массой “обычных” полей.</p> <p>На наших проектах использование быстрого метода <code class="language-plaintext highlighter-rouge">$.setValue</code> дало заметную экономию примерно в 10%.</p> <p>NB! Эта фича пока экспериментальная и включается флагом <code class="language-plaintext highlighter-rouge">-Dselenide.fastSetValue=true</code>. По умолчанию она выключена, и <code class="language-plaintext highlighter-rouge">$.setValue</code> работает так же, как и <code class="language-plaintext highlighter-rouge">$.sendKeys</code>.</p> <h3 id="добавлен-метод-для-проверки-картинок-imgisimage">Добавлен метод для проверки картинок <code class="language-plaintext highlighter-rouge">$("img").isImage()</code></h3> <p>Метод <code class="language-plaintext highlighter-rouge">$("img").isImage()</code> возвращает <code class="language-plaintext highlighter-rouge">true</code>, если картинка корректно загрузилась. Вызывать метод можно только для элементов <code class="language-plaintext highlighter-rouge">&lt;img&gt;</code>.</p> <p>Пример использования можно подсмотреть в тесте <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ImageTest.java">ImageTest</a>.</p> <p>NB! Функция почему-то не работает в HtmlUnit.</p> <h3 id="добавилась-возможность-писать-поясняющее-сообщение-к-проверкам-как-в-ассертах-junit-и-testng">Добавилась возможность писать поясняющее сообщение к проверкам (как в ассертах JUnit и TestNG)</h3> <p>Теперь ко всем should-методам можно приписать сообщение:</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">"#kpp"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="s">"У частных лиц КПП всегда пустой"</span><span class="o">,</span> <span class="n">empty</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">be</span> <span class="n">empty</span> <span class="o">{</span><span class="nc">By</span><span class="o">.</span><span class="na">id</span><span class="o">:</span> <span class="n">kpp</span><span class="o">}</span> <span class="n">because</span> <span class="no">У</span> <span class="n">частных</span> <span class="n">лиц</span> <span class="no">КПП</span> <span class="n">всегда</span> <span class="n">пустой</span> <span class="nl">Element:</span> <span class="err">'</span><span class="o">&lt;</span><span class="n">input</span> <span class="n">id</span><span class="o">=</span><span class="s">"kpp"</span><span class="o">&gt;</span><span class="mo">012</span><span class="mi">973891263</span><span class="o">&lt;/</span><span class="n">input</span><span class="o">&gt;</span><span class="err">'</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">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">integration</span><span class="o">/</span><span class="nc">SelenideMethodsTest</span><span class="o">/</span><span class="n">waitUntilMethodMayContainOptionalMessageThatIsPartOfErrorMessage</span><span class="o">/</span><span class="mf">1411898416054.0</span><span class="o">.</span><span class="na">png</span> <span class="nl">Timeout:</span> <span class="mi">4</span> <span class="n">s</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="err">$</span><span class="o">(</span><span class="s">"#kpp"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">empty</span><span class="o">.</span><span class="na">because</span><span class="o">(</span><span class="s">"У частных лиц КПП всегда пустой"</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="err">$</span><span class="o">(</span><span class="s">"#kpp"</span><span class="o">).</span><span class="na">because</span><span class="o">(</span><span class="s">"У частных лиц КПП всегда пустой"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">empty</span><span class="o">);</span> </code></pre></div></div> <p>Ну, и меня смущает ещё слово “because” - ведь в разных ситуациях было бы удобнее вместо “because” использовать другие слова.</p> <p>А что вы думаете?</p> <h3 id="добавились-врапперы-be-и-have">Добавились врапперы <code class="language-plaintext highlighter-rouge">be</code> и <code class="language-plaintext highlighter-rouge">have</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">"#username"</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">have</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Ivan"</span><span class="o">)));</span> <span class="err">$</span><span class="o">(</span><span class="s">"br"</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">be</span><span class="o">(</span><span class="n">empty</span><span class="o">));</span> </code></pre></div></div> <p>Это может быть полезно при использовании Selenide с фреймворками типа EasyB, которые тоже используют слова <code class="language-plaintext highlighter-rouge">shouldBe</code> или <code class="language-plaintext highlighter-rouge">shouldHave</code> для своих целей.</p> <p><br /></p> <h3 id="обновились-до-selenium-2431">Обновились до <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Selenium 2.43.1</a>.</h3> <p>Этого релиза Selenium WebDriver все очень ждали. Во-первых, из-за поддержки FireFox 32, а во-вторых, из-за нового драйвера для InternetExplorer.</p> <p>Проблем с новым Selenium 2.43.1 замечено не было.</p> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2014/09/28/selenide-2.13-and-2.14/ http://ru.selenide.org/2014/09/28/selenide-2.13-and-2.14 2014-09-28T00:00:00+00:00 Как начать писать UI тесты за 10 минут <p>Привет!</p> <p>Очень часто я слышу этот вопрос: как научиться писать тесты? С чего начать? Как написать первый тест на Selenium?</p> <p>И вот наконец у нас есть ответ!</p> <p>В этом видео-уроке показано, как создать проект на Maven и написать тест, который открывает Google в браузере, вбивает слово для поиска и проверяет результаты.</p> <center> <iframe src="//player.vimeo.com/video/106867878" width="800" height="450" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe> <p><a href="https://vimeo.com/106867878">Как написать UI тест за 10 минут</a> from <a href="https://vimeo.com/user20427140">Selenide</a> on <a href="https://vimeo.com">Vimeo</a>.</p> <p>Туториал по Selenide</p> </center> <p><a href="/users.html#contact">Конструктивная критика</a> приветствуется.</p> <p><br /></p> http://ru.selenide.org/2014/09/25/how-to-start-writing-ui-tests/ http://ru.selenide.org/2014/09/25/how-to-start-writing-ui-tests 2014-09-25T00:00:00+00:00 Заметки об использовании Selenide: IE, TestNG, Bootstrap dropdown <p>Привет!</p> <p>Меня зовут Сергей Шимкив, и я хочу поделиться с вами своими заметками об использовании Selenide/Selenium. <br /> <br /></p> <h3 id="заметки-об-ie">Заметки об IE</h3> <p>IE 11 x32/x64. В некоторых случаях после действий с HTML элементами (например, <code class="language-plaintext highlighter-rouge">click()</code>) вы можете получить ошибку. <br /> Проблема в использовании синтетических событиях. Решение - использовать нативные события для IE:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">capabilities</span><span class="o">.</span><span class="na">setCapability</span><span class="o">(</span><span class="s">"nativeEvents"</span><span class="o">,</span> <span class="s">"true"</span><span class="o">);</span> </code></pre></div></div> <p>См. https://code.google.com/p/selenium/wiki/InternetExplorerDriver</p> <p><br /></p> <h3 id="один-способ-добавить-скриншоты-selenide-в-testng-html-репорт">Один способ добавить скриншоты Selenide в TestNG HTML репорт</h3> <p>Иногда было бы неплохо добавить скриншоты Selenide от упавших тестов в ваш TestNG HTML отчёт.</p> <p>К примеру: Вы написали два теста (Test1 и Test2), которые должны быть связаны (Test2 зависит от результатов Test1). Но вы знаете, что Test1 точно упадёт из-за ошибки приложения.</p> <p><br /></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="kd">public</span> <span class="kt">void</span> <span class="nf">Test1</span><span class="o">()</span> <span class="o">{</span> <span class="o">...</span> <span class="k">try</span> <span class="o">{</span> <span class="c1">// Кусок теста с известным багом</span> <span class="o">}</span> <span class="k">catch</span><span class="o">(...)</span> <span class="o">{</span> <span class="c1">// Некоторые действия</span> <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> <span class="c1">// Действия, чтобы обеспечить корректные условия для запуска Test2</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Допустим, вы используете свой тест листенер, который наследует <code class="language-plaintext highlighter-rouge">TestListenerAdapter</code>. И вы переопределили метод <code class="language-plaintext highlighter-rouge">onTestFailure(ITestResult result)</code>, чтобы собирать дополнительную информацию в HTML отчёт - например, ваши собственные скриншоты. В этом случае может статься, что ваш код будет собирать неправильную информацию о скриншотах, т.к. блок <code class="language-plaintext highlighter-rouge">finally</code> может быть выполнен ДО того, как Selenide сделает скриншот.</p> <p><br /> Какой же выход? <br /> <br /></p> <p>Просто помните, что имя файла скриншота, который делает Selenide (после любой ошибки), доступно из <code class="language-plaintext highlighter-rouge">result.getThrowable().getMessage();</code> <br /> <br /></p> <h3 id="один-способ-работать-с-кастомизированными-bootstrap-html-элементами">Один способ работать с кастомизированными Bootstrap HTML элементами</h3> <p>Тесты резко усложняются, когда приходится иметь дело с нетривиальными контролами.</p> <p>К примеру, бутстраповский выпадающий список (dropdown) реализован как набор HTML элементов. Обычно это выглядит как-то так:</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">"dropdown"</span><span class="nt">&gt;</span> <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">"btn btn-default dropdown-toggle"</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">id=</span><span class="s">"dropdownMenu1"</span> <span class="na">data-toggle=</span><span class="s">"dropdown"</span><span class="nt">&gt;</span> Dropdown <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"caret"</span><span class="nt">&gt;&lt;/span&gt;</span> <span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"dropdown-menu"</span> <span class="na">role=</span><span class="s">"menu"</span> <span class="na">aria-labelledby=</span><span class="s">"dropdownMenu1"</span><span class="nt">&gt;</span> <span class="nt">&lt;li</span> <span class="na">role=</span><span class="s">"presentation"</span><span class="nt">&gt;&lt;a</span> <span class="na">role=</span><span class="s">"menuitem"</span> <span class="na">tabindex=</span><span class="s">"-1"</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">&gt;</span>Действие<span class="nt">&lt;/a&gt;&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">role=</span><span class="s">"presentation"</span><span class="nt">&gt;&lt;a</span> <span class="na">role=</span><span class="s">"menuitem"</span> <span class="na">tabindex=</span><span class="s">"-1"</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">&gt;</span>Другое действие<span class="nt">&lt;/a&gt;&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">role=</span><span class="s">"presentation"</span><span class="nt">&gt;&lt;a</span> <span class="na">role=</span><span class="s">"menuitem"</span> <span class="na">tabindex=</span><span class="s">"-1"</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">&gt;</span>Что-то ещё<span class="nt">&lt;/a&gt;&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">role=</span><span class="s">"presentation"</span> <span class="na">class=</span><span class="s">"divider"</span><span class="nt">&gt;&lt;/li&gt;</span> <span class="nt">&lt;li</span> <span class="na">role=</span><span class="s">"presentation"</span><span class="nt">&gt;&lt;a</span> <span class="na">role=</span><span class="s">"menuitem"</span> <span class="na">tabindex=</span><span class="s">"-1"</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">&gt;</span>Отдельная ссылка<span class="nt">&lt;/a&gt;&lt;/li&gt;</span> <span class="nt">&lt;/ul&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre></div></div> <p>Проблема в том, что элемент <code class="language-plaintext highlighter-rouge">&lt;ul&gt;</code> изначально невидимый. Selenium (а значит, и Selenide) его не видит и поэтому не может кликнуть. Чтобы выбрать элемент выпадающего списка, вам нужно:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">SelenideElement</span> <span class="n">parentDiv</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="s">".dropdown"</span><span class="o">);</span> <span class="c1">// Найти элемент `&lt;button&gt;` и `click()` его</span> <span class="n">parentDiv</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">"button"</span><span class="o">).</span><span class="na">scrollTo</span><span class="o">().</span><span class="na">click</span><span class="o">();</span> <span class="c1">// Теперь можете найти нужный элемент выпадающего списка по тексту</span> <span class="n">parentDiv</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">".dropdown-menu"</span><span class="o">).</span><span class="na">find</span><span class="o">(</span><span class="n">withText</span><span class="o">(</span><span class="s">"Действие"</span><span class="o">)).</span><span class="na">parent</span><span class="o">().</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p><br /> В следующем посте я поделюсь опытом автоматической инсталляции а тестовую среду (основанной на Grid2). <br /><br /> Спасибо авторам Selenide за отличный тул ;) <br /> <br /></p> <h3 id="сергей-шимкив">Сергей Шимкив</h3> <p><br /></p> http://ru.selenide.org/2014/08/25/selenide-usage-notes/ http://ru.selenide.org/2014/08/25/selenide-usage-notes 2014-08-25T00:00:00+00:00 Укороти свой код! <p>Всем привет!</p> <p>Вашему вниманию представляется <em>Prezi</em> презентация про Selenide от <a href="http://prezi.com/user/-yv8ll42j1_i/">Ashwin Dalvi</a>.</p> <p><br /> <br /></p> <h3 id="плюсы-минусы-и-мифы">Плюсы, Минусы и Мифы:</h3> <iframe src="http://prezi.com/embed/d18jggopjyaj/?bgcolor=ffffff&amp;lock_to_path=0&amp;autoplay=0&amp;autohide_ctrls=0&amp;features=undefined&amp;disabled_features=undefined&amp;html5=1" width="800" height="600" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe> <p><br /> Selenide: Укороти свой код!</p> <p><br /></p> http://ru.selenide.org/2014/08/08/shorten-your-code/ http://ru.selenide.org/2014/08/08/shorten-your-code 2014-08-08T00:00:00+00:00 Изменения в Selenide 2.12 <p>Всем привет!</p> <p>Хорошие новости: мы зарелизили Selenide 2.12. Давайте посмотрим, что там новенького. <br /> <br /></p> <h3 id="добавилась-функция-uploadfilefile">Добавилась функция $.uploadFile(File)</h3> <p>Новая функция позволяет загрузить файл.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">File</span> <span class="n">cv</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="s">"src/test/resources/cv.pdf"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#cvFileUpload"</span><span class="o">).</span><span class="na">uploadFile</span><span class="o">(</span><span class="n">cv</span><span class="o">);</span> </code></pre></div></div> <p>Раньше в Selenide уже была похожая функция <code class="language-plaintext highlighter-rouge">$.uploadFromClasspath(String fileName)</code>. Она искала файл для загрузки в classpath. Нам всегда казалось, что держать тестовые файлы вместе с кодом самих тестов - это правильно (а значит, при сборке проектов эти файлы будут попадать в classpath). Но оказалось, что иногда удобно и загрузить какой-то файл из другого места.</p> <p><br /></p> <h3 id="исправили-функцию-uploadfromclasspath">Исправили функцию $.uploadFromClasspath</h3> <p>Выяснилось, что вебдрайвер ругается, если попытаться загрузить файл вида <code class="language-plaintext highlighter-rouge">c:/src/test/java/../resources/cv.pdf</code>. Мы дополнили функцию <code class="language-plaintext highlighter-rouge">$.uploadFromClasspath</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">"#cvFileUpload"</span><span class="o">).</span><span class="na">uploadFromClasspath</span><span class="o">(</span><span class="s">"cv.pdf"</span><span class="o">);</span> </code></pre></div></div> <p><br /></p> <h3 id="функция-valджонни-работает-и-для-селектов">Функция $.val(“джонни”) работает и для селектов</h3> <p>Функция <code class="language-plaintext highlighter-rouge">$.val</code> стала немножко умнее. Если её вызвать для поля <code class="language-plaintext highlighter-rouge">SELECT</code>, то теперь она выбирает нужный <code class="language-plaintext highlighter-rouge">OPTION</code> с соответствующим <code class="language-plaintext highlighter-rouge">value</code>. Это ещё один шаг в том направлении, чтобы программист не должен был думать о реализации веб-элемента и мог сконцентрироваться на бизнес-логике. Программист просто пишет <code class="language-plaintext highlighter-rouge">$("#sex").val("female")</code> и не думает, что это: <code class="language-plaintext highlighter-rouge">INPUT</code> или <code class="language-plaintext highlighter-rouge">SELECT</code> - Selenide сама поймёт и сделает нужное действие.</p> <p>Технически, <code class="language-plaintext highlighter-rouge">$("select").val("yes")</code> - это синоним для <code class="language-plaintext highlighter-rouge">$("select").selectOptionByValue("yes")</code>.</p> <p><br /></p> <h3 id="добавилась-функция-getwebdriverlogs">Добавилась функция <code class="language-plaintext highlighter-rouge">getWebDriverLogs()</code>.</h3> <p>Функция позволяет прочитать логи браузера. Технически - <code class="language-plaintext highlighter-rouge">webdriver.manager().logs()</code>. По идее, эти логи могут содержать очень полезную информацию об ошибках браузера или javascript, проблемах производительности и пр. Но фактически функция поддерживается не всеми вебдрайверами и работает непонятно. Если вы знаете об этом больше - поделитесь с нами!</p> <p><br /></p> <h3 id="обновились-до-selenium-2422-и-htmlunit-215">Обновились до <a href="https://raw.githubusercontent.com/SeleniumHQ/selenium/master/java/CHANGELOG">Selenium 2.42.2</a> и <a href="http://htmlunit.sourceforge.net/changes-report.html#a2.15">HtmlUnit 2.15</a></h3> <p>Проблем с новым селениумом замечено не было. Работает как часы.</p> <p>Спасибо авторам Selenium за чудесный продукт!</p> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2014/07/05/selenide-2.12/ http://ru.selenide.org/2014/07/05/selenide-2.12 2014-07-05T00:00:00+00:00 Изменения в Selenide 2.11 <p>Всем привет!</p> <p>Мы выпустили версию Selenide 2.11. В ней почти нет новых функций, зато удалены старые неиспользуемые функции, которые иногда мешали нам жить и затрудняли дальнейшее развитие. <br /> <br /></p> <p>Пожалуйста, попробуйте запустить свои тесты с Selenide 2.11 и дайте знать, если что-то сломалось. <br /> <br /></p> <h2 id="что-мы-подчистили">Что мы подчистили</h2> <h3 id="убрали-зависимость-от-jquery">Убрали зависимость от jQuery</h3> <p>Раньше методы $.selectOption() и $.selectOptionByValue() пытались с помощью jQuery сгенерировать событие “change”. Это было сделано когда-то давно для борьбы с тестами, падающими под Windows в IE. Теперь это уже неактуально - во всяком случае, все тесты в наших проектах проходят и так. <br /> <br /></p> <p>И вообще, весь класс <code class="language-plaintext highlighter-rouge">com.codeborne.selenide.JQuery</code> теперь помечен как <code class="language-plaintext highlighter-rouge">deprecated</code>. Проверьте, что вы не используете его в своих тестах.</p> <p><br /></p> <h3 id="подчистили-надоедливые-логи">Подчистили надоедливые логи</h3> <p>Раньше при попытке закрыть Firefox в лог писалось такое бесполезное раздражающее сообщение:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">“</span><span class="nl">UnreachableBrowserException:</span> <span class="nc">Error</span> <span class="n">communicating</span> <span class="n">with</span> <span class="n">the</span> <span class="n">remote</span> <span class="n">browser</span><span class="o">.</span> <span class="nc">It</span> <span class="n">may</span> <span class="n">have</span> <span class="n">died</span><span class="o">.</span><span class="err">”</span> </code></pre></div></div> <p><br /> <br /></p> <p>Мы присмотрелись и поняли, что это бага (или особенность) в Selenium Firefox webdriver. При вызове метода <code class="language-plaintext highlighter-rouge">webdriver.close()</code> он выкидывает ошибку <code class="language-plaintext highlighter-rouge">UnreachableBrowserException</code>, но при этом браузер всё-таки закрывает (во всяком случае, в Linux и Mac OS). <br /> <br /></p> <p>В общем, мы убрали этот лог. <br /> <br /></p> <h2 id="и-что-обновили">И что обновили</h2> <h3 id="обновились-до-phantomjs-120">Обновились до PhantomJS 1.2.0</h3> <p>Как и раньше, все тесты самого Selenide пробегают на PhantomJS, но некоторые тесты на реальных проектах валятся. Так что использовать его полноценно в работе пока не можем. Будем следить за обновлениями фантома.</p> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2014/05/23/selenide-2.11/ http://ru.selenide.org/2014/05/23/selenide-2.11 2014-05-23T00:00:00+00:00 Изменения в Selenide 2.10 <p>Всем привет!</p> <p>Недавно вышла версия Selenide 2.10. Что новенького нас ждёт?</p> <h2 id="новые-функции">Новые функции</h2> <h3 id="работа-с-вкладками-и-окнами">Работа с вкладками и окнами</h3> <p>В Selenium WebDriver неудобно работать с окнами. Selenium предлагает только метод getWindowHandles(), который возвращает множество (Set) строк - идентификаторов открытых окон. Найти среди них нужное - нетривиальная задача. <br /> <br /> Поскольку задача Selenide - освободить голову программиста от технических вопросов, мы хотим сделать работу с окнами/вкладками максимально простой и интуитивной. Начали мы с простого: добавили метод</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Selenide</span><span class="o">.</span><span class="na">switchToWindow</span><span class="o">(</span><span class="nc">String</span> <span class="n">title</span><span class="o">)</span> </code></pre></div></div> <p>Пример использования можно подсмотреть <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/TabsTest.java#LC45">здесь</a>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">canSwitchToWindowByTitle</span><span class="o">()</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">"Page2: alerts"</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">byText</span><span class="o">(</span><span class="s">"Page1: uploads"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> <span class="n">switchToWindow</span><span class="o">(</span><span class="s">"Test::alerts"</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">"Page with alerts"</span><span class="o">));</span> <span class="n">switchToWindow</span><span class="o">(</span><span class="s">"Test::uploads"</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">"File uploads"</span><span class="o">));</span> <span class="o">}</span> </code></pre></div></div> <p>У нас в планах продолжать работу в этом направлении. Возможно, с использованием новых фич из Java 8.</p> <p>А как вам было бы удобнее с окнами и фреймами? Делитесь своими идеями! <br /> <br /></p> <h3 id="новые-методы-для-динамики-hover-и-draganddroptotarget">Новые методы для динамики <code class="language-plaintext highlighter-rouge">$.hover()</code> и <code class="language-plaintext highlighter-rouge">$.dragAndDropTo(target)</code></h3> <p><br /> Метод <code class="language-plaintext highlighter-rouge">$.hover()</code> эмулирует наведение мышкой на элемент (без клика). Примеры <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/HoverTest.java">здесь</a>. <br /> <br /> А метод <code class="language-plaintext highlighter-rouge">$.dragAndDropTo(target)</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">"#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><br /></p> <h3 id="новые-методы-для-поиска-родительских-элементов-parent-и-closest">Новые методы для поиска родительских элементов $.parent() и $.closest()</h3> <p>Часто в тестах бывает удобно найти родительский элемент. Или даже не просто родительский, а ближайший потомок, соответствующий какому-то критерию. Например, я часто ищу по тексту DIV, находящийся внутри таблицы, и затем хочу получить ряд таблицы, в котором этот DIV находится. Раньше это иначе как через XPath сделать было невозможно. <br /> <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="n">byText</span><span class="o">(</span><span class="s">"Row 1"</span><span class="o">)).</span><span class="na">parent</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">"Row 2"</span><span class="o">)).</span><span class="na">closest</span><span class="o">(</span><span class="s">"tr"</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">"firstName"</span><span class="o">)).</span><span class="na">closest</span><span class="o">(</span><span class="s">".form"</span><span class="o">);</span> </code></pre></div></div> <p>Пример <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ParentTest.java">здесь</a> <br /> <br /></p> <h3 id="ловля-javascript-ошибок">Ловля JavaScript ошибок</h3> <p>Теперь Selenide пытается автоматически отлавливать JavaScript ошибки, случающиеся на странице. <br /> <br /> Что мы с ними делаем? <br /> <br /> Прежде всего, когда ваш тест падает, Selenide автоматически <strong>добавляет в сообщение об ошибке список JS ошибок</strong> (если они были). В принципе этого уже достаточно. Но если вы хотите большего контроля, есть два специальных метода:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.getJavascriptErrors()</code> - возвращает список всех ошибок на текущей странице.</li> <li><code class="language-plaintext highlighter-rouge">Selenide.assertNoJavascriptErrors()</code> - валит тест, если были какие-то ошибки.</li> </ul> <p><br /> P.S. Пока что это делается довольно просто (через функцию <code class="language-plaintext highlighter-rouge">window.onerror</code>). Она вроде как не во всех браузерах работает, и не все ошибки отлавливает, так что если у вас есть соображения, как это можно делать лучше - смело делитесь.</p> <p><br /></p> <h2 id="багфиксы-и-улучшения">Багфиксы и улучшения</h2> <h3 id="закрытие-вебдрайверов">Закрытие вебдрайверов</h3> <p>Механизм автоматического закрытия вебдрайверов был улучшен для случаев, когда вебдрайверов несколько, и один из них завис. Спасибо Александру Гавриленко за это - совсем нетривиальное - исправление!</p> <h3 id="selenide-больше-не-ждёт-появления-body">Selenide больше не ждёт появления &lt;body&gt;</h3> <p>Метод <code class="language-plaintext highlighter-rouge">open(url)</code> больше не ждёт, пока в теле страницы появится тэг &lt;body&gt;. Этот атавизм остался с тех давних пор, когда Selenide ещё умел ожидать наступления событий.</p> <h3 id="метод-savepagesourcetofile-пытается-закрыть-все-alert">Метод savePageSourceToFile пытается закрыть все <code class="language-plaintext highlighter-rouge">alert</code>.</h3> <p>Когда в ходе выполнения теста на странице выскакивает неожиданный alert, грохается не только сам тест, но и попытка сделать скриншот, и даже попытка сохранить исходный код страницы.</p> <p>Теперь Selenide после падения теста пытается закрыть все алерты, чтобы хоть как-то сохранить код страницы и скриншот. Ну чтобы хоть какая-то информация осталась.</p> <h3 id="обновились-до-selenium-2410">Обновились до Selenium 2.41.0</h3> <p>Как обычно, мы обновились до последней версии Selenium.</p> <p>Каких-то проблем с новым селениумом пока не замечено. Всё работает как часы.</p> <h2 id="другие-новости">Другие новости</h2> <p>Мартовская статистика скачиваний Selenide из центрального репозитория несказанно нас порадовала.</p> <p>Нас резко стало почти вдвое больше!</p> <center> <img src="/images/2014/04/selenide_downloads.2014-03.png" width="800" /> </center> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2014/04/27/selenide-2.10/ http://ru.selenide.org/2014/04/27/selenide-2.10 2014-04-27T00:00:00+00:00 Изменения в Selenide 2.9 <p>Всем привет!</p> <p>Сегодня вышла версия Selenide 2.9. Давайте посмотрим, что хорошего несёт с собой новая версия.</p> <h2 id="что-новенького-нас-ждёт">Что новенького нас ждёт</h2> <h3 id="добавлена-поддержка-браузера-safari">Добавлена поддержка браузера Safari</h3> <p>Но сам Safari всё ещё кажется нестабильным. Во всяком случае, не все тесты самого Selenide проходят на Safari.</p> <p>И ещё он не поддерживает <code class="language-plaintext highlighter-rouge">alert</code>‘ы и не умеет открывать локальные файла типа <code class="language-plaintext highlighter-rouge">file://c:/tmp.txt</code>. А какой у вас опыт работы с вебдрайвером Safari?</p> <h3 id="добавлен-condition-or">Добавлен condition <code class="language-plaintext highlighter-rouge">or</code></h3> <p>Теперь можно писать так:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nc">Condition</span> <span class="n">completed</span> <span class="o">=</span> <span class="n">or</span><span class="o">(</span><span class="s">"completed"</span><span class="o">,</span> <span class="n">started</span><span class="o">,</span> <span class="n">inProgress</span><span class="o">,</span> <span class="n">loading</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">"div#page"</span><span class="o">).</span><span class="na">shouldBe</span><span class="o">(</span><span class="n">completed</span><span class="o">);</span> </code></pre></div></div> <p>Я совсем не уверен, что это хороша идея. На мой взгляд, тест всегда точно должен знать, в каком состоянии объект. Но просьбам трудящихся сделал.</p> <h3 id="добавлен-метод-screenshootertofolder">Добавлен метод <code class="language-plaintext highlighter-rouge">ScreenShooter.to(folder)</code></h3> <p>Теперь в JUnit можно писать так:</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="nd">@Rule</span> <span class="kd">public</span> <span class="nc">ScreenShooter</span> <span class="n">photographer</span> <span class="o">=</span> <span class="n">failedTests</span><span class="o">().</span><span class="na">to</span><span class="o">(</span><span class="s">"test-results/reports"</span><span class="o">);</span> <span class="o">}</span> </code></pre></div></div> <p>Опять же, не уверен, что это нужно, ведь путь к скриншотам можно задать глобально через системное свойство: <code class="language-plaintext highlighter-rouge">-Dselenide.reports=test-results/reports</code> или программно: <code class="language-plaintext highlighter-rouge">Configuration.reportsFolder=test-results/reports</code>. Так что польза от нового метода будет только в том случае, если вам нужно складывать скриншоты от разных тестов в разные места.</p> <h2 id="багфиксы-и-улучшения">Багфиксы и улучшения</h2> <h3 id="метод-download-кидает-filenotfoundexception-если-сервер-вернул-статус-40x-или-runtimeexception-для-статуса-50x">Метод <code class="language-plaintext highlighter-rouge">$.download()</code> кидает <code class="language-plaintext highlighter-rouge">FileNotFoundException</code> (если сервер вернул статус 40x) или <code class="language-plaintext highlighter-rouge">RuntimeException</code> (для статуса 50x)</h3> <p>До этого метод <code class="language-plaintext highlighter-rouge">$.download()</code> возвращал пустой файл в случае серверной ошибки. Конечно, тестировать это было неудобно.</p> <h3 id="окно-хрома-тоже-максимизируется">Окно хрома тоже максимизируется</h3> <p>Из-за баги в ChromeDriver окно браузера Chrome не открывалось на весь экран. Теперь открывается. Правда, для этого пришлось применить чёрную магию из java.awt.</p> <h3 id="метод-shouldhavetext-игнорирует-также-и-символ-u00a0-тн-non-breaking-space">Метод <code class="language-plaintext highlighter-rouge">$.shouldHave(text())</code> игнорирует также и символ \u00a0 (т.н. “non-breaking space”)</h3> <p>До этого проверка <code class="language-plaintext highlighter-rouge">$(".error").shouldHave(text("Hello, Johny!"))</code> игнорировала все пробелы. Но вот про символ \u00a0 мы забыли. А он часто используется в вебе, например, при форматировании сумм или дат. Например, такая проверка могла упасть:</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">".amount"</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">"123.45 EUR"</span><span class="o">));</span> </code></pre></div></div> <p>А теперь работает.</p> <h3 id="улучшение-закрытие-браузеров-при-многопоточном-запуске-тестов-в-testng">Улучшение закрытие браузеров при многопоточном запуске тестов в TestNG</h3> <p>До сих пор Selenide открывал и закрывал браузер в одном и том же потоке. Обычно этого хватает. <br /><br /> Но выяснилось, что TestNG при многопоточном запуске тестов запускает сами тесты и метод @AfterSuite в разных потоках. Пришлось этот код переписать. Теперь Selenide сам автоматически закрывает все неиспользуемые браузеры сразу, как только соответствующий поток завершает свою работу. <br /><br /> Напоминаю на всякий случай, что при использовании Selenide <em>не требуется</em> вручную закрывать вебдрайвер, т.к. Selenide сам делает это автоматически.</p> <h3 id="включение-ошибки-вебдрайвера">Включение ошибки вебдрайвера</h3> <p>Когда проверка типа <code class="language-plaintext highlighter-rouge">$.shouldBe(visible)</code> падает из-за ошибки вебдрайвера, эта ошибка больше не игнорируется, а добавляется к стандартной селенидовской ошибке <code class="language-plaintext highlighter-rouge">Element should be visible</code>. Теоретически это может быть полезно, если браузер повёл себя неадекватно. Например, у людей были случаи, когда <code class="language-plaintext highlighter-rouge">IE</code> время от времени ругался на валидный xpath.</p> <h3 id="если-не-получается-сделать-скриншот-selenide-пишет-ошибку-со-стектрейсом">Если не получается сделать скриншот, Selenide пишет ошибку со стектрейсом.</h3> <p>Я вообще-то не уверен , что от этого будет какая-то польза, но по просьбе трудящихся сделал. Но на всякий случай, чтобы не спамить логи, стэктрейс пишется только один раз.</p> <h3 id="selenide-сохраняет-исходный-код-страницы-в-правильной-кодировке-utf-8">Selenide сохраняет исходный код страницы в правильной кодировке (UTF-8)</h3> <p>До этого Selenide могла сохранять HTML в неправильной кодировке, в зависимости от настроек оп. системы.</p> <h3 id="убраны-info-сообщения-насчёт-reportsurl-и-build_url">Убраны INFO-сообщения насчёт “reportsUrl” и “BUILD_URL”</h3> <p>Как оказалось, они многих сбивали с толку.</p> <p>Но вообще-то автоматические скриншоты в Jenkins - это очень крутая фича, которая до сих пор остаётся неанонсированной. Об этом скоро будет отдельный большой пост с картинками.</p> <h3 id="использование-слова-jquery-вместо-">Использование слова “<code class="language-plaintext highlighter-rouge">jQuery</code>” вместо “<code class="language-plaintext highlighter-rouge">$</code>”</h3> <p>Кое-где Selenide использовал символ “<code class="language-plaintext highlighter-rouge">$</code>”, чтобы поддерживать события javascript. Это могло вызывать проблемы в случае приложений, использующих и <code class="language-plaintext highlighter-rouge">jQuery</code>, и <code class="language-plaintext highlighter-rouge">Prototype</code> или какой-нибудь другой JS фреймворк.</p> <h3 id="обновились-до-selenium-2400-htmlunit-214-и-testng-688">Обновились до Selenium 2.40.0, HtmlUnit 2.14 и TestNG 6.8.8</h3> <p>Как обычно, мы обновились до последних версий Selenium и TestNG.</p> <p>Внимание, в версии 6.8.8 из TestNG выпилили поддержку junit. Так что если у вас почему-то в проекте используется и TestNG, и JUnit, и последний подтягивается транзитивно, то он больше не подтянется.</p> <p><br /> Так что жизнь бьёт ключом!</p> <p><br /></p> <p>А что у вас новенького?</p> <p><br /></p> http://ru.selenide.org/2014/03/15/selenide-2.9/ http://ru.selenide.org/2014/03/15/selenide-2.9 2014-03-15T00:00:00+00:00 Selenide downloads: 200 уникальных IP <p>Не все знают, но Central Maven Repo раз в месяц публикует статистику скачиваний разных артефактов.</p> <p><br /></p> <p>Сегодня опубликована статистика скачиваний Selenide за февраль. На картинке - количество уникальных IP-адресов, с которых скачивался selenide.jar из центрального репозитория.</p> <p><br /></p> <p>Похоже, мы сделали большой скачок! Ура!</p> <p><br /></p> <center> <img src="/images/2014/03/selenide_downloads.unique_ips.png" width="800" /> </center> <p><br /></p> http://ru.selenide.org/2014/03/12/selenide-downloads/ http://ru.selenide.org/2014/03/12/selenide-downloads 2014-03-12T00:00:00+00:00 Selenide за пять минут от Glen Smith <p>Ух ты!</p> <p>Glen Smith презентовал библиотеку Selenide на JUG в Канберре.</p> <p><br /></p> <p>Это классная 5-минутная живая демонстрация Selenide. За пять минут Глен успел и рассказать, и написать, и запустить полноценный рабочий тест, причём не для чего-нибудь, а для гуглового сервиса поиска картинок. А это, знаете, не так просто: там ведь аякс и всё такое.</p> <p><br /></p> <p>Наслаждайтесь!</p> <p><br /></p> <center> <iframe src="//www.youtube.com/embed/6LW4h5y6Iw4" height="473" width="630" allowfullscreen="" frameborder="0"></iframe> <br /> Оригинальный пост: [Selenide in Five Minutes: A lightning talk](http://blogs.bytecode.com.au/glen/2014/02/13/selenide-in-five-minutes-a-lightning-talk.html) </center> <p><br /></p> <p><a href="http://blogs.bytecode.com.au/glen/about">Глен Смит</a> - инженер из Австралии, соавтор книги “Grails in Action”.</p> <p>Больше информации о нём можно найти в его блоге <a href="http://blogs.bytecode.com.au/glen">blog</a>.</p> <p>Отличная работа, <a href="https://twitter.com/glen_a_smith">@glen_a_smith</a>!</p> <p><img src="http://blogs.bytecode.com.au/glen/wp-content/uploads/2011/06/author.jpg" align="center" /></p> http://ru.selenide.org/2014/03/11/selenide-in-5-minutes/ http://ru.selenide.org/2014/03/11/selenide-in-5-minutes 2014-03-11T00:00:00+00:00 Футболка Selenide <p>Привет!</p> <p>Как вы уже успели заметить, у Selenide появилось новое лого и новый дизайн.</p> <p>И даже свои футболки. Всё по-взрослому!</p> <p>Три такие замечательные футболки мы разыграем уже на следующей неделе на конференции <a href="http://xpdays.com.ua/program/">XP Days</a> в Киеве.</p> <p><a href="/images/2013/10/selenide_front.jpg"><img src="/images/2013/10/selenide_front.thumb.jpg" alt="front" /></a> <a href="/images/2013/10/selenide_back.jpg"><img src="/images/2013/10/selenide_back.thumb.jpg" alt="back" /></a></p> http://ru.selenide.org/2013/10/04/selenide-tshirt/ http://ru.selenide.org/2013/10/04/selenide-tshirt 2013-10-04T00:00:00+00:00 Selenide Harlem Shake <p>Привет!</p> <p>Это видео, демонстрирующее, насколько легко начать писать автоматические тесты с Selenide.</p> <p>Мы хотели создать скринкаст, который был бы коротким, весёлым и в то же время информативным. И кстати, это реальный проект, а не какой-нибудь там Hello Word.</p> <p>Смотреть со звуком!</p> <iframe src="//player.vimeo.com/video/73128965" width="500" height="281" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""> </iframe> <p> <a href="https://vimeo.com/73128965">Selenide Harlem Shake</a> from <a href="https://vimeo.com/user20427140">Selenide</a> on <a href="https://vimeo.com">Vimeo</a>. </p> http://ru.selenide.org/2013/08/29/selenide-harlem-shake/ http://ru.selenide.org/2013/08/29/selenide-harlem-shake 2013-08-29T00:00:00+00:00 Может ли PhantomJS делать скриншоты? <p>Многие думают, что PhantomJS как headless-браузер не умеет делать снимки экрана.</p> <p>А вот и неправда!</p> <p>Согласно <a href="https://github.com/ariya/phantomjs/wiki/Screen-Capture">документации PhantomJS</a>, он умеет делать скриншоты.</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="kd">public</span> <span class="kd">class</span> <span class="nc">TestPhantomScreenshot</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="nc">System</span><span class="o">.</span><span class="na">setProperty</span><span class="o">(</span><span class="s">"browser"</span><span class="o">,</span> <span class="s">"phantomjs"</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="n">screenshot</span><span class="o">(</span><span class="s">"google-com-screenshot"</span><span class="o">);</span> <span class="n">close</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Это работает!</p> <p>Впрочем, работает неидеально. Скриншоты <a href="/images/2013/07/google-com-screenshot.png">google.com</a>, <a href="/images/2013/07/habrahabr-ru-screenshot.png">habrahabr.ru</a> и <a href="/images/2013/07/skype-com-screenshot.png">skype.com</a> выглядят неплохо, но вот скриншот <a href="/images/2013/07/selenide-org-screenshot.png">selenide.org</a> смотрится весьма странно.</p> <p>Значит ли это, что PhantomJS ещё недостаточно зрелый продукт? Не знаю. Но всяком случае теперь мы знаем точно, что PhantomJS умеет делать скриншоты.</p> <p><a href="/images/2013/07/google-com-screenshot.png"><img src="/images/2013/07/google-com-screenshot.thumb.png" alt="google.com" /></a> <a href="/images/2013/07/habrahabr-ru-screenshot.png"><img src="/images/2013/07/habrahabr-ru-screenshot.thumb.png" alt="habrahabr.ru" /></a> <a href="/images/2013/07/skype-com-screenshot.png"><img src="/images/2013/07/skype-com-screenshot.thumb.png" alt="skype.com" /></a> <a href="/images/2013/07/selenide-org-screenshot.png"><img src="/images/2013/07/selenide-org-screenshot.thumb.png" alt="selenide.org" /></a></p> http://ru.selenide.org/2013/07/15/can-phantomjs-take-screenshots/ http://ru.selenide.org/2013/07/15/can-phantomjs-take-screenshots 2013-07-15T00:00:00+00:00 Видео: Selenide на SeleniumCamp 2013 <p>Наш доклад про Selenide на конференции <a href="https://seleniumcamp.com/archive/selenium-camp-2013/materials/">SeleniumCamp 2013</a> в Киеве</p> <p>Часть первая, введение в Selenide:</p> <iframe src="//www.youtube.com/embed/4TegXkNWbqw" width="800" height="490" frameborder="0"></iframe> <p>Часть вторая, живая демонстрация Selenide:</p> <ul> <li>парное программирование</li> <li>ping-pong programming</li> <li>программируем настоящий интернет-банк</li> </ul> <iframe src="//www.youtube.com/embed/x3osTlsU82g" width="800" height="490" frameborder="0"></iframe> http://ru.selenide.org/2013/05/09/video-selenide-on-seleniumcamp/ http://ru.selenide.org/2013/05/09/video-selenide-on-seleniumcamp 2013-05-09T00:00:00+00:00 Что такое Selenide <p>Многие слышали про <a href="https://github.com/seleniumhq/selenium/">Selenium WebDriver</a> - один из самых популярных инструментов для написания приёмочных/интеграционных тестов.</p> <p>Написание UI тестов - непростая задача. Есть ряд проблем, с которыми сталкиваются все без исключения - это Ajax-запросы, динамические страницы и, чёрт их подери, таймауты. Главная цель Selenide - решить эти проблемы.</p> <p><img src="/images/selenide-logo-100x100.png" alt="right" /></p> <h3 id="что-такое-selenide">Что такое Selenide</h3> <p><a href="https://selenide.org">Selenide</a> - это обёртка вокруг Selenium WebDriver, позволяющая быстро и просто его использовать при написании тестов, сосредоточившись на логике, а не суете с браузером.</p> <p>Вот пример теста. Как видите, код минимален. Вызвал метод <code class="language-plaintext highlighter-rouge">open</code> - и браузер открылся.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">testLogin</span><span class="o">()</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"/login"</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">"user.name"</span><span class="o">)).</span><span class="na">sendKeys</span><span class="o">(</span><span class="s">"johny"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#submitButton"</span><span class="o">).</span><span class="na">click</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">"Hello, Johny!"</span><span class="o">));</span> <span class="err">$</span><span class="o">(</span><span class="s">".error"</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="o">}</span> </code></pre></div></div> <p>При вызове метода open Selenide сам запускает браузер и открывает страницу <code class="language-plaintext highlighter-rouge">http://localhost:8080/login</code> (порт и хост конфигурируется, естественно). А также заботится о том, чтобы в конце браузер закрылся.</p> <h3 id="ключевые-особенности-selenide">Ключевые особенности Selenide</h3> <p>Если вкратце, вот главные особенности Selenide</p> <ul> <li>Лаконичный синтаксис в духе jQuery</li> <li>Автоматическое решение большинства проблем с Ajax, ожиданием и таймаутами.</li> <li>Управление жизнедеятельностью браузера</li> <li>Автоматическое создание скриншотов</li> </ul> <p>Одним словом, цель Selenide - сосредоточиться на бизнес-логике и не заниматься вечными надоедливыми мелкими проблемами.</p> <h3 id="дополнительные-вкусности-selenide">Дополнительные вкусности Selenide</h3> <p>Selenide предоставляет дополнительные методы для действий, которые невозможно сделать одной командой Selenium WebDriver. Это выбор радио-кнопки, выбор элемента из выпадающего списка, создание снимка экрана, очистка кэша браузера и т.п.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">canFillComplexForm</span><span class="o">()</span> <span class="o">{</span> <span class="n">open</span><span class="o">(</span><span class="s">"/client/registration"</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">"user.name"</span><span class="o">)).</span><span class="na">setValue</span><span class="o">(</span><span class="s">"johny"</span><span class="o">);</span> <span class="n">selectRadio</span><span class="o">(</span><span class="s">"user.gender"</span><span class="o">,</span> <span class="s">"male"</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">"user.securityQuestion"</span><span class="o">)).</span><span class="na">selectOption</span><span class="o">(</span><span class="s">"What is my first car?"</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">"user.preferredLayout"</span><span class="o">)).</span><span class="na">selectOptionByValue</span><span class="o">(</span><span class="s">"plain"</span><span class="o">);</span> <span class="err">$</span><span class="o">(</span><span class="s">"#submit"</span><span class="o">).</span><span class="na">followLink</span><span class="o">();</span> <span class="n">takeScreenShot</span><span class="o">(</span><span class="s">"complex-form.png"</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Before</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">clearCache</span><span class="o">()</span> <span class="o">{</span> <span class="n">clearBrowserCache</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <h3 id="как-побороть-ajax">Как побороть Ajax?</h3> <p>Ajax - это засада для UI тестировщиков. При тестировании приложений, использующих Ajax, приходится изобретать код, который чего-то ждёт (когда кнопка станет зелёной). Selenide решает эту проблему как никто другой. В то время как Selenium предлагает богатый API для ожидания разного рода событий <a href="https://xpinjection.com/2013/04/04/waits-and-timeouts-in-webdriver/">Например</a>, Selenide просто предлагает вам не заморачиваться. Если вы хотите поверить, что кнопка зелёная, а она пока что не зелёная, Selenide просто подождёт, пока она станет зелёной. Конечно, таймаут конфигурируется (по умолчанию 4 секунды). Это уникальное решение - простое и надёжное.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">pageUsingAjax</span><span class="o">()</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="n">visible</span><span class="o">);</span> <span class="c1">// ждёт, пока элемент появится</span> <span class="err">$</span><span class="o">(</span><span class="s">"#username"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">text</span><span class="o">(</span><span class="s">"Hello, Johny!"</span><span class="o">));</span> <span class="c1">// ждёт, пока текст элемента изменится на "Hello, Johny!"</span> <span class="err">$</span><span class="o">(</span><span class="s">"#login-button"</span><span class="o">).</span><span class="na">shouldHave</span><span class="o">(</span><span class="n">cssClass</span><span class="o">(</span><span class="s">"green-button"</span><span class="o">));</span> <span class="c1">// ждёт, пока кнопка станет зелёной</span> <span class="err">$</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">disabled</span><span class="o">);</span> <span class="c1">// ждёт, пока кнопка станет неактивной</span> <span class="err">$</span><span class="o">(</span><span class="s">".error"</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="c1">// ждёт, пока элемент исчезнет</span> <span class="err">$</span><span class="o">(</span><span class="s">".error"</span><span class="o">).</span><span class="na">should</span><span class="o">(</span><span class="n">disappear</span><span class="o">);</span> <span class="c1">// попробуйте-ка сделать это с Selenium в одну строчку!</span> <span class="o">}</span> </code></pre></div></div> <h3 id="как-автоматически-делать-скриншоты">Как автоматически делать скриншоты?</h3> <p>Легко! Если вы используете JUnit, просто добавьте эту строчку в свой тестовый класс:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Rule</span> <span class="kd">public</span> <span class="nc">ScreenShooter</span> <span class="n">makeScreenshotOnFailure</span> <span class="o">=</span> <span class="nc">ScreenShooter</span><span class="o">.</span><span class="na">failedTests</span><span class="o">();</span> </code></pre></div></div> <p>Тогда после каждого упавшего теста будет автоматически создаваться скриншот (на самом деле два файла, .PNG и .HTML).</p> <p>А если вы хотите снимать вообще все тесты, а не только упавшие, тогда подойдёт такая строчка:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Rule</span> <span class="kd">public</span> <span class="nc">ScreenShooter</span> <span class="n">makeScreenshotOnEveryTest</span> <span class="o">=</span> <span class="nc">ScreenShooter</span><span class="o">.</span><span class="na">failedTests</span><span class="o">().</span><span class="na">succeededTests</span><span class="o">();</span> </code></pre></div></div> <p>Если вы используете TestNG, просто добавьте следующую аннотацию к своему тестовому классу:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Listeners</span><span class="o">({</span> <span class="nc">ScreenShooter</span><span class="o">.</span><span class="na">class</span><span class="o">})</span> </code></pre></div></div> <h3 id="я-хочу-попробовать-с-чего-начать">Я хочу попробовать, с чего начать?</h3> <p>Добавь в свой проект зависимость Selenide:</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>com.codeborne<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>selenide<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>7.2.3<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;/dependency&gt;</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">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>И готово! Пиши тесты, едрён-батон!</p> <h3 id="зачем-ещё-один-велосипед">Зачем ещё один велосипед?</h3> <p>Нам приходилось использовать Selenium в разных проектах, и мы быстро заметили, что нам раз от раза приходится писать один и тот же код, чтобы инициализировать браузер в начале, закрыть его в конце, делать скриншоты после каждого упавшего теста и т.д. В интернете можно найти множество статей типа “Как в Selenium сделать то или это” с кучей кода, который приходится копировать к себе в проект. <a href="https://habrahabr.ru/post/114145/">Например</a>.</p> <p>И мы подумали: почему создание UI-тестов должно быть таким сложным? Поэтому мы решили выделить этот повторяющийся код в отдельную библиотеку. Так на свет появился <a href="https://selenide.org">Selenide</a>.</p> <h3 id="кто-нибудь-это-реально-использует">Кто-нибудь это реально использует?</h3> <p>Да, мы <a href="https://codeborne.com/ru/" target="_blank">в фирме Codeborne</a> используем Selenide в нескольких реальных проектах:</p> <ul> <li>Интернет-банки</li> <li>Порталы самообслуживания</li> <li>и т.д.</li> </ul> <p>с разными языками и тестовыми фреймворками:</p> <ul> <li>Java + ANT + JUnit</li> <li>Java + Gradle + JUnit</li> <li>Scala + ANT + ScalaTest</li> <li>Groovy + ANT</li> <li>и т.д.</li> </ul> <p>Так что можете быть уверены, проект не сырой, реально используется и поддерживается.</p> <h3 id="покажите-мне-работающий-пример">Покажите мне работающий пример!</h3> <p>Есть небольшой эталонный open-source проект, в котором используется Selenide: <a href="https://github.com/asolntsev/hangman">игра Виселица</a>.</p> <p>А также мы создали проект <a href="https://github.com/selenide-examples">Selenide examples</a>, где мы храним примеры использования Selenide для тестирования <a href="https://github.com/selenide-examples/gmail">Gmail</a>, <a href="https://github.com/selenide-examples/google">Google</a> и других классических примеров.</p> <h3 id="откуда-такое-название---selenide">Откуда такое название - Selenide?</h3> <p>Библиотека Selenide взяла своё название от химического элемента (Селен). А селениды - это соединения селена с другими элементами.</p> <p>Вот и у нас:</p> <ul> <li>Selenide = Selenium + JUnit</li> <li>Selenide = Selenium + TestNG</li> <li>Selenide = Selenium + ScalaTest</li> <li>Selenide = Selenium + что угодно</li> </ul> <p>Химичьте на здоровье!</p> <h3 id="поделитесь-с-нами-опытом">Поделитесь с нами опытом!</h3> <p>Нам было бы очень интересно услышать ваши отзывы - что пробовали, что получилось, с чем испытывали проблемы. Напишите нам в <a href="mailto:selenide-ru@googlegroups.com">гуглогруппу</a> или <a href="mailto:andrei тчк solntsev сбк gmail тчк com">лично</a>!</p> http://ru.selenide.org/2013/04/23/what-is-selenide/ http://ru.selenide.org/2013/04/23/what-is-selenide 2013-04-23T00:00:00+00:00