Selenide Selenide - Andrei Solntsev https://selenide.org/rss https://selenide.org 2023-05-30T06:39:16+00:00 2023-05-30T06:39:16+00:00 1800 Released Selenide 6.15.0 <p>Good night!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/184?closed=1">Selenide 6.15.0</a> with quite serious internal changes.</p> <ul> <li><a href="#merge-selenide-selenoid-to-selenide">Merged project <code class="language-plaintext highlighter-rouge">selenide-selenoid</code> into <code class="language-plaintext highlighter-rouge">selenide</code></a></li> <li><a href="#merge-selenide-appium-to-selenide">Merged project <code class="language-plaintext highlighter-rouge">selenide-appium</code> into <code class="language-plaintext highlighter-rouge">selenide</code></a></li> <li><a href="#clicking-disable-element-fails">Click checks that element is not disabled</a></li> <li><a href="#escape-newlines-in-report">Escape newlines in text report</a></li> <li><a href="#combined-selectors-for-mobile-apps">Combined selectors for mobile apps</a></li> <li><a href="#conditions-for-mobile-apps">New checks for mobile apps</a></li> <li><a href="#new-selectors-for-mobile-apps">New selectors for mobile apps</a></li> <li><a href="#switch-context-in-mobile-apps">Switch context in mobile apps</a></li> <li><a href="#refactor-collection-conditions">Refactoring of CollectionCondition</a></li> <li><a href="#news">News</a> <p><br /></p> </li> </ul> <h3 id="merge-selenide-selenoid-to-selenide">Merged project <code class="language-plaintext highlighter-rouge">selenide-selenoid</code> into <code class="language-plaintext highlighter-rouge">selenide</code></h3> <p>This allows us to easily change and release these projects. For you, the dependency declaration will change.</p> <p>Instead of</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>You will need to use:</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>See <a href="https://github.com/selenide/selenide/pull/2292">PR 2292</a>.</p> <p><br /></p> <h3 id="merge-selenide-appium-to-selenide">Merged project <code class="language-plaintext highlighter-rouge">selenide-appium</code> into <code class="language-plaintext highlighter-rouge">selenide</code></h3> <p>This allows us to easily change and release these projects. For you, just the dependency version will change.</p> <p>You will need to update <code class="language-plaintext highlighter-rouge">selenide-appium</code> from <code class="language-plaintext highlighter-rouge">2.8.1</code> to <code class="language-plaintext highlighter-rouge">6.15.0</code>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/2291">PR 2291</a>.</p> <p><br /></p> <h3 id="clicking-disable-element-fails">Now Click checks that the element is not disabled</h3> <p>As you know, method <code class="language-plaintext highlighter-rouge">$.click()</code> first checks that the element is visible (more specifically, the element should be either visible or have css property <code class="language-plaintext highlighter-rouge">opacity: 0</code>).</p> <p>BUT until now, Selenide didn’t check that the element is <em>enabled</em>. In other words,</p> <blockquote> <p>Selenide did allow to click <code class="language-plaintext highlighter-rouge">disabled</code> elements.</p> </blockquote> <ol> <li>Argument <strong>in favor</strong> of this decision: what if test author does want to click the disabled element and make sure that nothing happens?</li> <li>Argument <strong>against</strong> this decision: this behavior can cause flaky tests: <ul> <li>the test clicks a button without waiting until the buttons gets enabled;</li> <li>de-facto click didn’t happen (but no errors were thrown);</li> <li>and later the test fails because the click didn’t happen (and it will be very hard to understand why the test failed).</li> <li>or even worse: the test may remain false positive.</li> </ul> </li> </ol> <p>So now <strong>argument #2 has won</strong>. Now method <code class="language-plaintext highlighter-rouge">$.click()</code> will wait until the element gets enabled. <br /> If the element is not enabled, the click will fail.</p> <p>Now you can shorten your tests even more:</p> <blockquote> <p>Replace <code class="language-plaintext highlighter-rouge">$.shouldBe(enabled).click()</code> by just <code class="language-plaintext highlighter-rouge">$.click()</code>.</p> </blockquote> <p>P.S. If you’re in the #1 camp, you can use <code class="language-plaintext highlighter-rouge">$.shouldBe(disabled)</code> instead of clicking.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2288">issue 2288</a>. Thanks to <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> for <a href="https://github.com/selenide/selenide/pull/2290">PR 2290</a>.</p> <p><br /></p> <h3 id="escape-newlines-in-report">Escape newlines in text report</h3> <p>If you tried using a multi-line string in your test:</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\n multi\n line\n world"</span><span class="o">);</span> </code></pre></div></div> <p>Then you might have noticed that in the report this line looked clumsy and broke the entire table.</p> <p>Now it will look good.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2283">issue 2283</a>. Thanks to <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> for <a href="https://github.com/selenide/selenide/pull/2284">PR 2284</a>.</p> <p><br /></p> <h3 id="combined-selectors-for-mobile-apps">Combined selectors for mobile apps</h3> <p>When testing mobile apps, it would be convenient to use two selectors in a check: one for Android, and the other for iOS.<br /> Previously, you had to come up with helper methods or use IFs in tests.</p> <p>Now you can use a single selector using <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">"Hello, Android user!"</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">"Hello, iOS user!"</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">"Hello, Android user!"</span><span class="o">));</span> </code></pre></div></div> <p><br /></p> <h3 id="conditions-for-mobile-apps">New checks for mobile apps</h3> <p>A similar solution for checks: now you can declare a single check for Android, and iOS using class <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">// A single element check:</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">"To do or not to do"</span> <span class="o">));</span> <span class="c1">// A collection check:</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">"First"</span><span class="o">,</span> <span class="s">"Second"</span><span class="o">,</span> <span class="s">"Third"</span> <span class="o">));</span> </code></pre></div></div> <p><br /></p> <h3 id="new-selectors-for-mobile-apps">New selectors for mobile apps</h3> <p>We also added a bunch of selectors to find mobile elements by tag, text, substring etc.:</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">"Good"</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">"bye"</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">"ugly xpath world"</span><span class="o">)).</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/2300">issue 2300</a>. Thanks to <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> for <a href="https://github.com/selenide/selenide-appium/pull/135">PR 135</a>. See also <a href="https://github.com/selenide/selenide/pull/2315">PR 2315</a>.</p> <p><br /></p> <h3 id="switch-context-in-mobile-apps">Switch context in mobile apps</h3> <p>Mobile apps often open some content in an embedded web browser (WebView). Sometimes mobile apps work entire in WebView. When testing such apps, you often need to switch between “native” and “web” context.</p> <p>Now we have convenient methods to switch between contexts:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Here we are in the "native" context</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">// Oops, and we're already in the "web" context:</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>See <a href="https://github.com/selenide/selenide/pull/2308">PR 2308</a>.<br /> Thanks to <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> for <a href="https://github.com/selenide/selenide-appium/pull/149">PR 149</a>.</p> <p><br /></p> <h3 id="refactor-collection-conditions">Refactoring of CollectionCondition</h3> <p>We have changed the internal structure of collection conditions (class <code class="language-plaintext highlighter-rouge">CollectionCondition</code>).</p> <p>If you wanted to create a <a href="https://github.com/selenide/selenide/blob/main/src/test/java/integration/collections/CustomCollectionConditionTest.java">custom collection condition</a>, You had to implement method <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">".moron"</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>This <code class="language-plaintext highlighter-rouge">test</code> method will stay for some time (for backward compatibility), but the <strong>recommended method</strong> now will be <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">".moron"</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="what-does-it-give-us">What does it give us?</h3> <p>In the future, it will open up opportunities for us to improve collection checks:</p> <ol> <li>It allows us to greatly speed up the checks: for example, quickly get the texts of all elements with just one JS call.</li> <li>In the error message, we can write “actual texts at the moment of last check” instead of “actual texts at the moment of error message generation”. The same problem for a single element was <a href="/2021/09/28/selenide-5.25.0/#actual-value-at-the-moment-of-last-check">described here</a>. Now it’s the same for collections.</li> </ol> <p>See <a href="https://github.com/selenide/selenide/pull/2312">PR 2312</a> and <a href="https://github.com/selenide/selenide/pull/2307">PR 2307</a>.</p> <p><br /></p> <h3 id="news">News</h3> <ul> <li>Video <a href="https://www.youtube.com/watch?v=M--_ziTZPQQ&amp;ab_channel=kanezi">Testing Spring with Selenide</a> by kanezi</li> <li>Video series on Testing Mini Bytes: <a href="https://www.youtube.com/watch?v=UQWQJ3dhgVI&amp;ab_channel=TestingMiniBytes">Selenide + Allure = Beautiful HTML Reports</a></li> <li>Post <a href="https://creatingvalue.substack.com/p/why-we-chose-selenide-over-selenium">Why we chose Selenide over Selenium</a> by Amuthan Sakthivel</li> <li>A historical moment: <a href="https://pikabu.ru/story/selenium_selenide_i_selenoid_8376039">the first meme about Selenide</a></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/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/05/29/selenide-6.15.0/ https://selenide.org/2023/05/29/selenide-6.15.0 2023-05-29T00:00:00+00:00 Released Selenide 6.14.0 <p>Good evening!</p> <p>Good news: we have released <a href="https://github.com/selenide/selenide/milestone/181?closed=1">Selenide 6.14.0</a>.</p> <p>It contains some changes that look minor, but might break something in your tests. <em>That’s why I highly recommend to <strong>upgrade now</strong></em></p> <ul> <li><a href="#speedup-debug">Sped up debugging</a></li> <li><a href="#remove-flag--no-sandbox">Removed flag <code class="language-plaintext highlighter-rouge">--no-sandbox</code></a></li> <li><a href="#add-step-method">Added method <code class="language-plaintext highlighter-rouge">step</code> for reports</a></li> <li><a href="#drag-and-drop-with-options">Added method <code class="language-plaintext highlighter-rouge">$.dragAndDrop(DragAndDropOptions)</code></a></li> <li><a href="#allow-negative-browser-position">Browser out of screen</a></li> <li><a href="#update-dependencies">Updated dependencies</a></li> <li><a href="#news">News</a></li> <li><a href="#statistics">Statistics</a> <p><br /></p> </li> </ul> <h3 id="speedup-debug">Speed up debugging</h3> <p>When you are debugging a test and stop at a breakpoint, sometimes IDE is rendering very slowly page object fields. In this state IDE can hang for quite a long time:</p> <p><img src="/images/2023/05/selenide-debug.slow.png" alt="Selenide. debug. slow." width="600px" /></p> <p>The reason is that Selenide makes multiple webdriver calls trying to get elements’ tag names, texts and attributes. As a result, you will see the most actual detailed information after some time:</p> <p><img src="/images/2023/05/selenide-debug.slow.result.png" alt="Selenide. debug. slow. result." width="600px" /></p> <p>Now this process will be significantly faster. Selenide will not call webdriver anymore, but just show locator:</p> <p><img src="/images/2023/05/selenide-debug.fast.png" alt="Selenide. debug. slow. result." width="600px" /></p> <p>But be informed that format of <code class="language-plaintext highlighter-rouge">$.toString()</code> / <code class="language-plaintext highlighter-rouge">$$.toString()</code> will change. Who knows, probably it might break your tests.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2253">issue 2253</a>. and <a href="https://github.com/selenide/selenide/pull/2269">PR 2269</a>.</p> <blockquote> <p>But don’t worry, it’s all only about debugging. In case of test failure, you will still receive the full element information with blackjack and screenshooters.</p> </blockquote> <p><br /></p> <h3 id="remove-flag--no-sandbox">Removed Chrome argument <code class="language-plaintext highlighter-rouge">--no-sandbox</code></h3> <p>I personally don’t see any difference, whether I run tests with or without this argument.</p> <p>But</p> <ol> <li>Some users complain that Chrome cannot start in Docker <em>without</em> argument <code class="language-plaintext highlighter-rouge">--no-sandbox</code>;</li> <li>Other users complain that Chrome, when started <em>with</em> argument <code class="language-plaintext highlighter-rouge">--no-sandbox</code>, doesn’t stop - and chrome process hangs forever in background and eats CPU. But it seems to happen only on Windows (?)</li> </ol> <p>After years of hesitations, we decided to remove this argument <code class="language-plaintext highlighter-rouge">--no-sandbox</code> by default. Users of type #1</p> <ul> <li>can easily detect the problem (chrome wont start)</li> <li>can easily add this argument in their tests: <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>See <a href="https://github.com/selenide/selenide/issues/2270">issue 2270</a> and <a href="https://github.com/selenide/selenide/pull/2271">PR 2271</a>.</p> <p><br /></p> <h3 id="add-step-method">Added method <code class="language-plaintext highlighter-rouge">step</code> for reports</h3> <p>If you use Selenide built-in report (<code class="language-plaintext highlighter-rouge">TextReport</code>), now you can group actions using new method <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>These “steps” will be visually distinguished in the report:</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>By the way, those steps can be embedded into each other.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2172">issue 2172</a>. Thanks to <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> for <a href="https://github.com/selenide/selenide/pull/2250">PR 2250</a>.</p> <p><br /></p> <h3 id="drag-and-drop-with-options">Added method <code class="language-plaintext highlighter-rouge">$.dragAndDrop(DragAndDropOptions)</code> with options</h3> <p>Now method <code class="language-plaintext highlighter-rouge">$.dragAndDrop</code> acquired a more logical sound: first “what”, then “where” and finally, “how”.</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>Thanks to <a href="https://github.com/Au6ojlut">Maksim @Au6ojlut</a> for <a href="https://github.com/selenide/selenide/pull/2245">PR 2245</a>.</p> <p><br /></p> <h3 id="allow-negative-browser-position">Allow to set browser position out of screen</h3> <p>We fixed a minor bug that didn’t allow setting negative values to browser position:</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>I have no idea why this might be useful, but now it’s possible.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2258">issue 2258</a> and <a href="https://github.com/selenide/selenide/pull/2259">PR 2259</a>.</p> <p><br /></p> <h3 id="support-edge-binary-location">Allow setting path to Edge browser binary</h3> <p>Selenide has setting <code class="language-plaintext highlighter-rouge">Configuration.browserBinary</code> that allows you set path to browser binary (just in case: in most cases, you <em>don’t need to set it</em>).</p> <p>Previously, this setting was not supported for Edge browser. Now it’s supported.</p> <p>Thanks to <a href="https://github.com/vlad8x8">Vladislav Velichko</a> for <a href="https://github.com/selenide/selenide/pull/2267">PR 2267</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated dependencies</h3> <ul> <li><a href="https://github.com/selenide/selenide/pull/2277">Selenium 4.9.1</a> - see <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">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">News</h3> <ul> <li>Series of tutorials <a href="https://www.youtube.com/playlist?list=PLFGzDEkV3ACu0i-Gd_whXsspqDlmP3ymP">Selenide Java with Cucumber</a> by <a href="https://www.youtube.com/@TechProEducationUS">TechPro Education</a></li> </ul> <h3 id="statistics">Statistics</h3> <p>Number of monthly downloads of Selenide exceeded 600 thousands!</p> <center> <img src="/images/2023/04/selenide.downloads.jpg" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/05/08/selenide-6.14.0/ https://selenide.org/2023/05/08/selenide-6.14.0 2023-05-08T00:00:00+00:00 Released Selenide 6.13.0 <p>Good night!</p> <p>Today we have a big major release <a href="https://github.com/selenide/selenide/milestone/177?closed=1">Selenide 6.13.0</a>.</p> <ul> <li><a href="#add-method-cached">Added method <code class="language-plaintext highlighter-rouge">$.cached()</code></a></li> <li><a href="#mock-response-with-http-status">Added http status for server response mock</a></li> <li><a href="#add-method-in-new-browser">Added method <code class="language-plaintext highlighter-rouge">inNewBrowser</code> for running code in a new browser</a></li> <li><a href="#add-method-doubleclick-with-options">Added method <code class="language-plaintext highlighter-rouge">$.doubleClick(options)</code></a></li> <li><a href="#add-condition-inner-text">Added condition <code class="language-plaintext highlighter-rouge">$.shouldHave(innerText())</code></a></li> <li><a href="#add-condition-attributes">Added collection condition <code class="language-plaintext highlighter-rouge">$$.shouldHave(attributes(...))</code></a></li> <li><a href="#clear-error-message-in-select">Clear error message in methods <code class="language-plaintext highlighter-rouge">$.select*()</code></a></li> <li><a href="#fix-method-sublist">Fixed bug in <code class="language-plaintext highlighter-rouge">$$.subList()</code></a></li> <li><a href="#update-dependencies">Updated dependencies</a></li> <li><a href="#news">News</a> <p><br /></p> </li> </ul> <h3 id="add-method-cached">Added method <code class="language-plaintext highlighter-rouge">$.cached()</code></h3> <p>Sometimes you need to write quite complex code for finding an element.<br /> And it might work <strong>slowly</strong>, especially if you find it from a long collection applying specific filtering.<br /> Something like this:</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>Every call to <code class="language-plaintext highlighter-rouge">target</code> is apparently slow.</p> <p>Now you can speed up such tests by caching the <code class="language-plaintext highlighter-rouge">target</code> element:</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>Obviously, you can cache it only if you are sure that it’s not updated/reloaded during the test. <br /> If it still gets reloaded, you will get the famous <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2171">issue 2171</a>, <a href="https://github.com/selenide/selenide/issues/1927">issue 1927</a> and <a href="https://github.com/selenide/selenide/pull/2189">PR 2189</a>.</p> <p><br /></p> <h3 id="mock-response-with-http-status">Added http status to server response mock</h3> <p>Selenide has method for mocking a server response:</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>It allowed to mock only response body, response status was always 200 (“OK”).<br /> Now you can set another status too:</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>See <a href="https://github.com/selenide/selenide/issues/2227">issue 2227</a> and <a href="https://github.com/selenide/selenide/pull/2234">PR 2234</a>.</p> <p><br /></p> <h3 id="add-method-in-new-browser">Added method <code class="language-plaintext highlighter-rouge">inNewBrowser</code> for running a code snippet in a new browser</h3> <p>Sometimes you want to run a code block in a new browser.</p> <blockquote> <p>Usually I say it’s a bad practice. Actions like data preparation etc. should not be performed via UI (we are testing UI -&gt; we cannot trust UI). Instead, you should use some reliable way: using API, query to database etc.</p> </blockquote> <p>Still, for this purpose we have <a href="/2019/10/16/selenide-5.4.0/#add-method-using">method <code class="language-plaintext highlighter-rouge">using</code></a>. But to use it, you need to open your custom webdriver with some specific settings. But what if you don’t specific settings? What if you need a standard webdriver with usual Selenide settings - just a new instance of browser?</p> <p>Now you can use method <code class="language-plaintext highlighter-rouge">inNewBrowser</code> for that:</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>See <a href="https://github.com/selenide/selenide/issues/2213">issue 2213</a> and <a href="https://github.com/selenide/selenide/pull/2236">PR 2236</a>.</p> <p><br /></p> <h3 id="add-method-doubleclick-with-options">Added method <code class="language-plaintext highlighter-rouge">$.doubleClick(options)</code></h3> <p>We already had method <code class="language-plaintext highlighter-rouge">$.doubleClick()</code> which could only click the center of given element. Now it got an advanced bro with <code class="language-plaintext highlighter-rouge">options</code> parameter. The options are the same as for usual <code class="language-plaintext highlighter-rouge">$.click</code> method:</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>See <a href="https://github.com/selenide/selenide/issues/2133">issue 2133</a>. Thanks to <a href="https://github.com/aakachurin">aakachurin</a> for <a href="https://github.com/selenide/selenide/pull/2135">PR 2135</a>.</p> <p><br /></p> <h3 id="add-condition-inner-text">Added condition <code class="language-plaintext highlighter-rouge">$.shouldHave(innerText())</code></h3> <p>It allows to check texts of hidden elements.</p> <blockquote> <p>Probably it’s a bad idea: if real users cannot see the element then you don’t need to test it.</p> </blockquote> <p>But if you really want, you can now use condition <code class="language-plaintext highlighter-rouge">innerText</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">"#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">"Can you see the hidden text?"</span><span class="o">));</span> <span class="c1">// Usual $("#theHiddenElement").text() returns an empty string here: "";</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/2220">issue 2220</a> and <a href="https://github.com/selenide/selenide/pull/2223">PR 2223</a>.</p> <p><br /></p> <h3 id="add-condition-attributes">Added collection condition <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>See <a href="https://github.com/selenide/selenide/issues/2091">issue 2091</a>. Thanks to <a href="https://github.com/AlexLAA">Alexey Lakovych</a> for <a href="https://github.com/selenide/selenide/pull/2091">PR 2091</a>. See also <a href="https://github.com/selenide/selenide/pull/2230">PR 2230</a>.</p> <p><br /></p> <h3 id="clear-error-message-in-select">Added a clear error message in methods <code class="language-plaintext highlighter-rouge">$.select*()</code></h3> <p>Selenide has convenient methods for working with dropdowns:</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>Recently we <a href="/2022/11/21/selenide-6.10.0/#select-options-using-javascript">changed their implementation to JavaScript</a> - it’s faster and stable.</p> <p>But it turned out that if you mistakenly call such a method for a non-<code class="language-plaintext highlighter-rouge">&lt;select&gt;</code>, a very incomprehensible error took off:</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>Now this error message will be clear:</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>See <a href="https://github.com/selenide/selenide/issues/2231">issue 2231</a> and <a href="https://github.com/selenide/selenide/pull/2233">PR 2233</a>.</p> <h3 id="fix-method-sublist">Fixed bug in method <code class="language-plaintext highlighter-rouge">$$.subList()</code></h3> <p>As you know, Selenide has methods for checking the whole collection of elements with one line:</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>Method <code class="language-plaintext highlighter-rouge">$$</code> returns an object <code class="language-plaintext highlighter-rouge">ElementsCollection</code>, which (unfortunately) is inherited from <code class="language-plaintext highlighter-rouge">AbstractList&lt;SelenideElement&gt;</code>. Thus, it inherits few unexpected methods that we initially didn’t think about.</p> <p>One of such methods is <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>This iterator returns two, not three elements. It truncates the last element. Oops… <code class="language-plaintext highlighter-rouge">¯\_(ツ)_/¯</code></p> <p>Now this bug is fixed. Iterator will return all elements.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2239">issue 2239</a> and <a href="https://github.com/selenide/selenide/pull/2240">PR 2240</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated dependencies</h3> <ul> <li>bump Selenium from 4.8.1 to 4.8.3 – see <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">News</h3> <p>Today we have few videos about Selenide</p> <ul> <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=wN45Qla66-o&amp;list=PLFGoYjJG_fqrvWt1FfHqKoREQmSPxazBq&amp;ab_channel=NaveenAutomationLabs">Selenide channel</a> by NaveenAutomationLabs</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/04/04/selenide-6.13.0/ https://selenide.org/2023/04/04/selenide-6.13.0 2023-04-04T00:00:00+00:00 Released Selenide 6.13.0 <p>Hi all!</p> <p>Wow, today we released <strong>Selenide 6.13.0</strong></p> <ul> <li><a href="#banners-support">Banners support</a></li> <li><a href="#news">News</a> <p><br /></p> </li> </ul> <h3 id="banners-support">Banners support</h3> <h4 id="the-problem">The problem</h4> <p>People often asked me: “What can I do if during a test execution, a banner can appear on the screen (or any other unexpected element)?”</p> <p>Such a banner can cover other elements, can hide some important button - and your test might occasionally get broken.</p> <blockquote> <p><a href="https://ru.selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide/">I’ve always scolded these questions</a> and insisted that you should control your testing environment. You should dictate when the banner should appear, and when it should not.</p> </blockquote> <p>But this question was too popular. In the end, we decided to suggest a solution in Selenide.</p> <p>Now you can all new method <code class="language-plaintext highlighter-rouge">Selenide.onBanner()</code> in the beginning of your test:</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="how-it-works">How it works</h4> <p>By default, this method adds a listener to webdriver. Before every click or check, this listener will ask browser if there exists an element with given selector <code class="language-plaintext highlighter-rouge">"#pizzaAds"</code>. If it exists, Selenide will try to close it by clicking button <code class="language-plaintext highlighter-rouge">".btn-close"</code>.</p> <p>We called this method <code class="language-plaintext highlighter-rouge">POLL</code>. It’s very simple and stable, but it’s also relatively slow. Because Selenide needs to ask from browser many-many times if the banner has appeared. Every request takes some time.</p> <p><br /></p> <h4 id="alternative-methods">Alternative methods</h4> <p>That’s why we added 2 alternative methods for checking if the banner exists. You can enable them by a global setting:</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">// the default mode</span> <span class="c1">// Listens DOM change events via 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">// Works only in Chromium-based browsers</span> <span class="c1">// Sends current page html to ChatGPT and asks if it contains a banner</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>Or you can set mode as a parameter using <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="custom-logic">Custom logic</h4> <p>There is a more generic option if you need to implement some custom logic for handling banner. For example, you might need to find some info inside the banner before closing it:</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">"Your password has been changed."</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>Feel free to try, experiment, register issues on GitHub. Feel free to suggest your algorithms for banner detection.</p> <blockquote> <p>Let’s unite against banners!</p> </blockquote> <h3 id="news">News</h3> <p>Today we have few video about Selenide:</p> <ul> <li><a href="https://www.youtube.com/watch?v=GwHG550moGc">ChatGPT: How to download file in Selenide</a></li> <li>Channel <a href="https://www.youtube.com/watch?v=SohZfPKicZQ&amp;ab_channel=OlehPendrak">Selenide - UI Automation Tool</a> by Naveen AutomationLabs</li> <li><a href="https://www.youtube.com/watch?v=18J2_4a4Cl4&amp;ab_channel=Jfokus">Flaky tests</a> - JFokus conference, Stockholm, 8.02.2023</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/04/01/selenide-6.13.0/ https://selenide.org/2023/04/01/selenide-6.13.0 2023-04-01T00:00:00+00:00 Released Selenide 6.12.4 <p>Good evening!</p> <p>We released <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">Workaround for Chromedriver</a></li> <li><a href="#support-jdk-http-client">Support for jdk-http-client</a></li> <li><a href="#restore-proxy-after-using">Fixed $.download(PROXY) after “using”</a></li> <li><a href="#fix-clear-when-element-disappears">Fixed $.clear() when element disappears</a></li> <li><a href="#update-dependencies">Updated dependencies</a></li> <li><a href="#release-selenide-appium">Released selenide-appium 2.7.0</a></li> <li><a href="#release-selenide-selenoid">Released selenide-selenoid 2.3.6</a></li> <li><a href="#news">News</a> <p><br /></p> </li> </ul> <h3 id="workaround-for-chromedriver-bug">Workaround for Chromedriver</h3> <p>Reminder: if you still are looking for a solution for</p> <blockquote> <p>Invalid Status code=403 text=Forbidden</p> </blockquote> <p>then it was released in <a href="/2023/03/09/selenide-6.12.2/">Selenide 6.12.2</a>.</p> <p><br /></p> <h3 id="support-jdk-http-client">Added support for <code class="language-plaintext highlighter-rouge">jdk-http-client</code> as a replacement for <code class="language-plaintext highlighter-rouge">netty-client</code></h3> <p>By default, Selenide still uses old good <code class="language-plaintext highlighter-rouge">netty-client</code>, but if you wish to<br /> <a href="https://www.selenium.dev/blog/2022/using-java11-httpclient/">upgrade to jdk-http-client</a>, now you can easily do that:</p> <ol> <li>Add dependency:<br /> <code class="language-plaintext highlighter-rouge">org.seleniumhq.selenium:selenium-http-jdk-client:$seleniumVersion</code></li> <li>Add system property before opening a browser: <br /> <code class="language-plaintext highlighter-rouge">System.setProperty("webdriver.http.factory", "jdk-http-client");</code></li> </ol> <p>See <a href="https://github.com/selenide/selenide/issues/2215">issue 2215</a> and <a href="https://github.com/selenide/selenide/pull/2216">PR 2216</a>.</p> <p><br /></p> <h3 id="restore-proxy-after-using">Restored proxy after using <code class="language-plaintext highlighter-rouge">using</code></h3> <p>It’s almost the same problem as <a href="https://github.com/selenide/selenide/issues/2202">issue 2202</a>, but with Selenide built-in proxy.</p> <p>If you enabled a proxy in your tests, once you used method <code class="language-plaintext highlighter-rouge">using</code>, the proxy disappeared. The following downloading of files (or any other featured you used proxy for) didn’t work anymore.</p> <p>Now <code class="language-plaintext highlighter-rouge">using</code> method has been fixed. See more details about this <a href="/2019/10/16/selenide-5.4.0/#add-method-using">very convenient method <code class="language-plaintext highlighter-rouge">using</code></a>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/2208">PR 2208</a> and <a href="https://github.com/selenide/selenide/pull/2209">PR 2209</a>.</p> <p><br /></p> <h3 id="fix-clear-when-element-disappears">Fixed <code class="language-plaintext highlighter-rouge">$.clear()</code> when element disappears</h3> <p>As you know, Selenide has method <code class="language-plaintext highlighter-rouge">$("input").clear()</code> that clears given input field.<br /> But it could fail in one specific situation: when the input disappears as a result of clearing.<br /> At this moment, method <code class="language-plaintext highlighter-rouge">$.clear()</code> tried to trigger <code class="language-plaintext highlighter-rouge">change</code> event on this input - and failed.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2207">issue 2207</a> and <a href="https://github.com/selenide/selenide/pull/2221">PR 2221</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated 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">Released <code class="language-plaintext highlighter-rouge">selenide-appium:2.7.0</code></h3> <ul> <li>Added scroll up/down for mobile apps (#139)</li> <li>Fixed method <code class="language-plaintext highlighter-rouge">terminateApp</code> (#146)</li> <li>Upgraded to Selenide 6.12.4 (#143)</li> </ul> <p>See <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">Released <code class="language-plaintext highlighter-rouge">selenide-selenoid:2.3.6</code></h3> <ul> <li>Upgraded to Selenide 6.12.4</li> </ul> <p>See <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>Finally, I have a video about <a href="https://www.youtube.com/watch?v=18J2_4a4Cl4&amp;ab_channel=Jfokus">Flaky tests in English</a> - JFokus conference, Stockholm, 8.02.2023</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/03/23/selenide-6.12.4/ https://selenide.org/2023/03/23/selenide-6.12.4 2023-03-23T00:00:00+00:00 Released Selenide 6.12.2 <p>Hi all!</p> <p>Urgently update to <a href="https://github.com/selenide/selenide/milestone/178?closed=1">Selenide 6.12.2</a>!</p> <p>It has one important workaround for Chromedriver 111 issue:</p> <blockquote> <p>Invalid Status code=403 text=Forbidden</p> </blockquote> <h3 id="whats-the-problem">What’s the problem?</h3> <p>Recently a new version of Chrome and Chromedriver was released: 111.</p> <p>People who did upgrade their browser unexpectedly started getting test failures.<br /> Test tried to open the browser. The browser window appeared, but webdriver received an exception, and could not do any following actions. In the end, test could not even close that browser!</p> <p>In logs, you could read only messages like this:</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>Although it’s a <a href="https://bugs.chromium.org/p/chromedriver/issues/detail?id=4361">bug in chromedriver</a>, it had a simple workaround, and we decided to add it to Selenide to avoid a bigger hassle in QA world.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2192">issue 2192</a> and <a href="https://github.com/selenide/selenide/pull/2194">PR 2194</a>.</p> <blockquote> <p>We recommend you upgrading to <a href="/2023/03/22/selenide-6.12.4/#support-jdk-http-client">HttpClient</a>. Yes, it requires Java11+, but has better support etc.</p> </blockquote> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/03/09/selenide-6.12.2/ https://selenide.org/2023/03/09/selenide-6.12.2 2023-03-09T00:00:00+00:00 Released Selenide 6.12.0 <p>Hello!</p> <p>On this tragic and festive day, we released <a href="https://github.com/selenide/selenide/milestone/173?closed=1">Selenide 6.12.0</a>.</p> <p>It doesn’t contain any new features, but it introduces new headless mode that might affect your tests.</p> <ul class="blogpost-menu"> <li><a href="#new-headless-mode">New headless mode</a></li> <li><a href="#improve-logs-when-downloading-file">Improve logging when downloading files</a></li> <li><a href="#improve-download-in-edge">Improved downloading files in Edge on Windows</a></li> <li><a href="#update-dependencies">Updated dependencies</a></li> <li><a href="#rename-master-to-main">Renamed master to main</a></li> </ul> <p><br /></p> <h3 id="new-headless-mode">New headless mode</h3> <p>In Chromium browsers, a “new headless” mode has been recently introduced. Read more details <a href="https://developer.chrome.com/articles/new-headless/">in their blog</a>.</p> <p>Shortly said, it’s a better headless mode that should fix all problems of the “old headless”.</p> <p>In theory, you can still switch between the old and new headless modes. But in practice, you will have to switch to a new one. Because after last Chrome and Edge update, downloading files in the old headless mode stopped working.</p> <p>Starting from Selenide 6.12.0, the new headless mode will be enabled by default.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2104">issue 2104</a>. Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/2105">PR 2105</a> and <a href="https://github.com/selenide/selenide/pull/2169">PR 2169</a>.</p> <p><br /></p> <h3 id="improve-logs-when-downloading-file">Improve logging when downloading files</h3> <p>If file downloading failed (especially using method <code class="language-plaintext highlighter-rouge">FOLDER</code>), it’s sometimes hard to understand why it failed.<br /> Now we improved logging inside of method <code class="language-plaintext highlighter-rouge">$.download()</code>, so the debugging should become easier.</p> <p>See <a href="https://github.com/selenide/selenide/pull/2167">PR 2167</a>.</p> <p><br /></p> <h3 id="improve-download-in-edge">Improved downloading files in Edge on Windows</h3> <p>When downloading a file in Edge browser, Selenide tracks temporary files “<em>.crdownload”. They are created by Chromium engine during the download process. Recently we realized that on Windows, Edge also creates temporary files “</em>.tmp”. Now Selenide tracks them too.</p> <p>See <a href="https://github.com/selenide/selenide/pull/2167">PR 2167</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated dependencies</h3> <ul> <li>bump Selenium from 4.8.0 to 4.8.1, see <a href="https://github.com/selenide/selenide/pull/2161">PR 2161</a> and <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">Selenium release notes</a>.</li> <li>Bump nettyVersion from 4.1.87.Final to 4.1.89.Final, see <a href="https://github.com/selenide/selenide/pull/2158">PR 2158</a>.</li> </ul> <p><br /></p> <h3 id="rename-master-to-main">Renamed <code class="language-plaintext highlighter-rouge">master</code> branch to <code class="language-plaintext highlighter-rouge">main</code></h3> <p>Yes, these are echoes of that same BLM. One smart person convinced me that it’s worth to rename the branch. :)</p> <p><br /></p> <h3 id="news">News</h3> <ul> <li>Post <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>Post <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>My video from 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> </ul> <p><br /> Well, why did I call this day <em>festive</em>? It’s because February, 24 is actually the Independence Day of Estonia. 105 years ago, Estonian troops expelled the invaders from the country and proclaimed a new independent state.</p> <center> <img src="/images/2023/02/independence-day-estonia.png" width="300" /> </center> <blockquote> <p>Without that brave guys on the armored train, there would be no Selenide now. :)</p> </blockquote> <p><br /></p> <h1 id="upd-released-selenide-6121">UPD Released Selenide 6.12.1</h1> <p>Fixed an old bug in method <code class="language-plaintext highlighter-rouge">using</code>.</p> <p>See <a href="https://github.com/selenide/selenide/milestone/176?closed=1">changelog</a></p> <p><br /></p> <h1 id="upd-released-selenide-6122">UPD Released Selenide 6.12.2</h1> <p>Added a quick workaround for Chromedriver 111 issue:<br /> <code class="language-plaintext highlighter-rouge">Invalid Status code=403 text=Forbidden</code>.</p> <p>See <a href="https://github.com/selenide/selenide/milestone/178?closed=1">changelog</a></p> <p><br /></p> <h1 id="upd-released-selenide-6123">UPD Released Selenide 6.12.3</h1> <p>We fixed one old bug in method <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code> (if it’s called after <code class="language-plaintext highlighter-rouge">using</code>).</p> <p>See <a href="https://github.com/selenide/selenide/milestone/179?closed=1">changelog</a></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/02/24/selenide-6.12.0/ https://selenide.org/2023/02/24/selenide-6.12.0 2023-02-24T00:00:00+00:00 Released Selenide 6.11.1 <p>Good jan!</p> <p>We release a minor update <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">Truncate only WebDriverException</a></li> <li><a href="#fix-download-to-folder">Tuned $.download(FOLDER)</a></li> <li><a href="#update-dependencies">Updated dependencies</a></li> <li><a href="#statistics">Statistics</a></li> </ul> <h3 id="truncate-webdriver-exception-message">Truncate only <code class="language-plaintext highlighter-rouge">WebDriverException</code> messages</h3> <p>In a very rare situation - if you</p> <ul> <li>declare a custom command,</li> <li>throw some assertion error from it,</li> <li>and this error has multi-line message,</li> </ul> <p>then Selenide truncates this error message, leaving only the first line.</p> <p>Initially, our intention was only to truncate error message of <code class="language-plaintext highlighter-rouge">WebDriverException</code> because it contains multiline garbage:</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>But here the mistake comes: Selenide truncated message not only for <code class="language-plaintext highlighter-rouge">WebDriverException</code>, but for all errors. Now we fixed it, and you will see your multiline goodness entirely.</p> <p>See <a href="https://github.com/selenide/selenide/pull/2131">PR 2131</a>.</p> <p><br /></p> <h3 id="fix-download-to-folder">Fixed <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code> a bit</h3> <p>This is a very rare case, so you probably didn’t even notice it.</p> <p>But our tests flaked sometimes, so I had to start investigation. And found couple of rare bugs in <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code>. Sometimes it could report that the file is not downloaded (though it was) in two cases:</p> <ul> <li> <p><a href="https://github.com/selenide/selenide/pull/2116">#2116</a> If the file’s modification timestamp appeared in the previous second of click (it may happen because different file systems may return file modification time with error up to 1 second. And it can be even in the past.)</p> </li> <li> <p><a href="https://github.com/selenide/selenide/pull/2119">#2119</a> If the file’s modification timestamp was “0” (according to the spec, it may happen if for some reason file system decided that the path is invalid)</p> </li> </ul> <p>Now it should continue downloading in both cases.</p> <p><br /></p> <h3 id="update-dependencies">Updated 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, see <a href="https://github.com/selenide/selenide/pull/2126">PR 2126</a>.</li> </ul> <p><br /></p> <h3 id="news">News</h3> <ul> <li>Post by Amuthan Sakthivel <a href="https://creatingvalue.substack.com/p/why-we-chose-selenide-over-selenium">Why we chose Selenide over Selenium for our Test Automation Needs</a></li> </ul> <p><br /></p> <h3 id="statistics">Statistics</h3> <p>Number of monthly downloads of Selenide exceeded 490 thousands!</p> <center> <img src="/images/2023/01/selenide.downloads.png" width="800" /> </center> <p>To be continued…</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/01/20/selenide-6.11.1/ https://selenide.org/2023/01/20/selenide-6.11.1 2023-01-20T00:00:00+00:00 Released Selenide 6.11.0 <center> <img src="/images/2023/01/selenide-6.11.0.png" width="700" /> </center> <p><br /></p> <h1 id="happy-new-year-dear-friends">Happy New Year, dear friends!</h1> <p>Santa Claus brought us a new release <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">Copy-paste methods</a></li> <li><a href="#fix-download-with-credentials">Download file behind BasicAuth</a></li> <li><a href="#download-large-files-via-proxy">Download large files via proxy</a></li> <li><a href="#can-handle-unexpected-alerts">Unexpected alerts</a></li> <li><a href="#fix-screenshot-file-permission">Screenshot file permissions</a></li> <li><a href="#support-as-annotation">Annotation @As for fields without @FindBy</a></li> <li><a href="#last-page-source">Last page source</a></li> <li><a href="#page-url-in-error-message">Page URL in error message</a></li> </ul> <h3 id="added-copy-paste-methods">Added methods to copy and paste text</h3> <p>We added two methods:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.copy()</code> for copying currently selected text to clipboard, and</li> <li><code class="language-plaintext highlighter-rouge">$.paste()</code> to paste clipboard content into input field.</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">"Life is highly overrated"</span><span class="o">));</span> </code></pre></div></div> <p>Known restrictions: these methods need a graphics environment. So they would not work on server linux. But simple running <code class="language-plaintext highlighter-rouge">xvfb</code> should help.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1817">issue 1817</a>.<br /> Thanks to <a href="https://github.com/evpl">Evgenii Plugatar</a> for <a href="https://github.com/selenide/selenide/pull/2027">PR 2027</a>.</p> <p><br /></p> <h3 id="fix-download-with-credentials">Fixed method <code class="language-plaintext highlighter-rouge">Selenide.download(url)</code></h3> <p>…in case if url contains login/password (the resource is protected by BasicAuth).</p> <p>For example, this code worked few weeks ago, but stopped working after upgrading Apache Http client (internally used by Selenide):</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>In the latest update, Apache Http client enforced URL validation, and this part with login/password is not allowed anymore (it’s kinda unsafe deprecated way of passing credentials).</p> <p>Now Selenide removes credentials from url and sends them with http header <code class="language-plaintext highlighter-rouge">Authorization</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2037">issue 2037</a> and <a href="https://github.com/selenide/selenide/pull/2102">PR 2102</a>.</p> <p><br /></p> <h3 id="download-large-files-via-proxy">Now Selenide allows downloading large files via proxy</h3> <p>If you use <code class="language-plaintext highlighter-rouge">PROXY</code> method for downloading files, you might know that file size could not exceed 64 megabytes.<br /> Initially, it seemed a reasonable limit: why should anyone need to load a test bench by downloading giant files? (even 64 mb was quite big: BrowserUpProxy by default sets limit to 2 megabytes; we increased it to 64 in Selenide)</p> <p>But it turned out that sometimes people want to test downloading large files. After a discussion, we decided to simply remove this limit. You decide if you want to load your test environment.</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. Be aware that proxy might not quickly download files that are too large. And BrowserUpProxy still has built-in technical restriction of 2 gigabytes (because of Integer type used for file size limit).</p> <p>But at least now you can download, say, 200 megabyte files.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2082">issue 2082</a> and <a href="https://github.com/selenide/selenide/pull/2098">PR 2098</a>.</p> <p><br /></p> <h3 id="can-handle-unexpected-alerts">Now you can handle unexpected alerts</h3> <p>By default, Selenide ignores unexpected alerts in browser. (More specifically, Selenide runs webdriver with option <code class="language-plaintext highlighter-rouge">capabilities.setCapability(UNHANDLED_PROMPT_BEHAVIOUR, ACCEPT)</code>).</p> <p>On the one hand, this is convenient so that tests do not break due to sudden pop-up ads and other useless messages. On the other hand, sometimes these alerts can contain useful information - in particular, a valuable error message.</p> <p>If this is your case, now you can override this option:</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>Then you will get an exception saying what message that alert contained:</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">Oops</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="nl">Oops:</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">Oops</span><span class="o">}</span> <span class="c1">// edge</span> <span class="nl">UnhandledAlertException:</span> <span class="o">:</span> <span class="nc">Oops</span> <span class="c1">// safari</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/2054">issue 2054</a> and <a href="https://github.com/selenide/selenide/pull/2095">PR 2095</a>.</p> <p><br /></p> <h3 id="fix-screenshot-file-permission">Fixed permissions for screenshot file</h3> <p>When your test fails, Selenide automatically takes a snapshot of current page, thus creating two files: : <code class="language-plaintext highlighter-rouge">*.png</code> and <code class="language-plaintext highlighter-rouge">*.html</code>. It turned out that these files had different permissions:</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>And it caused difficulties for some advanced devops how wanted to run different step of build pipeline by different users. <em>Whatever devops amuses… :)</em></p> <p>Now both files have the same permissions: <code class="language-plaintext highlighter-rouge">-rw-r--r--</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2081">issue 2081</a> and <a href="https://github.com/selenide/selenide/pull/2084">PR 2084</a>.</p> <p><br /></p> <h3 id="support-as-annotation">Support <code class="language-plaintext highlighter-rouge">@As</code> annotation for fields without <code class="language-plaintext highlighter-rouge">@FindBy</code></h3> <p>In Selenide, you can give readable names (aka “aliases”) for page object fields with method <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>But there was an idea that it would be convenient to put the name at the beginning of the line. Otherwise, it would be visually lost on the right of the long selector.</p> <p>Now you can give alias with annotation <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>But of course, the annotation will work only if you initialize the page object with method <code class="language-plaintext highlighter-rouge">Selenide.page()</code> or <code class="language-plaintext highlighter-rouge">Selenide.open()</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2087">issue 2087</a> and <a href="https://github.com/selenide/selenide/pull/2088">PR 2088</a>.</p> <p><br /></p> <h3 id="last-page-source">Added methods to get last saved page source</h3> <p>It’s rarely needed, so don’t bother. Essentially, we added few methods to class <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>Thanks to <a href="https://github.com/armanayvazyan">Arman Ayvazyan</a> for <a href="https://github.com/selenide/selenide/pull/2065">PR 2065</a>.</p> <p><br /></p> <h3 id="page-url-in-error-message">Now you can add page URL to error message</h3> <p>Sometimes it might be useful, sometimes harmful. After a discussion, we decided to make this feature optional (in a form of a plugin).</p> <p>Let it stay in experimental status for now. We will probably play with error message formats in the future.</p> <p>See <a href="https://github.com/selenide/selenide/issues/980">issue 980</a> and <a href="https://github.com/selenide/selenide/pull/2097">PR 2097</a>.</p> <p><br /></p> <h3 id="year-summary">Year summary</h3> <p>Let’s sum up year 2022? During this year, Selenide was mentioned in several ratings:</p> <ul> <li><a href="https://qameta.io/blog/5-testing-automation-tools/">5 Testing Automation Tools</a> in Qameta Software blog</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>and finally,</p> <ul> <li>Selenide got mentioned on <a href="https://www.selenium.dev/ecosystem/">Selenium official site</a> in category “Ecosystem”.</li> </ul> <p>Selenide monthly downloads statistics <strong>grew by half</strong>:</p> <blockquote> <p>from 302 thousands in January<br /> to 469 thousands in November.</p> </blockquote> <h3 id="new-year-wishes">And still</h3> <p>It’s been a terrible year.</p> <p>I hope that in the coming year the war will end, the guilty will be punished, the citizens of the oppressed countries will finally overthrow their tyrants.</p> <p>And the beautiful free country of Ukraine will be rebuilt and will bloom more than ever.</p> <blockquote> <p>And after a year, we will see you in Kyiv at the wonderful <a href="https://seleniumcamp.com/">SeleniumCamp conference</a>.</p> </blockquote> <p>Glory to the heroes!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2023/01/03/selenide-6.11.0/ https://selenide.org/2023/01/03/selenide-6.11.0 2023-01-03T00:00:00+00:00 Released Selenide 6.10.2 <p><br /></p> <h1 id="good-evening">Good evening!</h1> <p>We got mini-release <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">Added method $.press()</a></li> <li><a href="#trigger-change-events-by-select-methods">Trigger change event in $.select*</a></li> <li><a href="#friendly-select-option-in-reports">selectOption in reports</a></li> <li><a href="#friendly-local-storage-in-reports">localStorage in reports</a></li> <li><a href="#news">News</a></li> </ul> <h3 id="added-method-press">Added method <code class="language-plaintext highlighter-rouge">$.press()</code></h3> <p>It’s essentially the same ss <code class="language-plaintext highlighter-rouge">sendKeys()</code>, but it’s not <code class="language-plaintext highlighter-rouge">void</code>. You can <em>chain</em> it with other methods:</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>Thanks to <a href="https://github.com/amuthansakthivel">Amuthan Sakthivel</a> for <a href="https://github.com/selenide/selenide/pull/2032">PR 2032</a>.</p> <p><br /></p> <h3 id="trigger-change-events-by-select-methods">Trigger <code class="language-plaintext highlighter-rouge">change</code> event by method <code class="language-plaintext highlighter-rouge">$.select*</code></h3> <p>As you remember, in previous release we <a href="/2022/11/21/selenide-6.10.0/#select-options-using-javascript">reworked working with selects using JavaScript</a>. But we forgot to trigger <code class="language-plaintext highlighter-rouge">focus</code>, <code class="language-plaintext highlighter-rouge">click</code> and <code class="language-plaintext highlighter-rouge">change</code> events. Now we trigger them.</p> <p>See <a href="https://github.com/selenide/selenide/issues/2050">issue 2050</a>. Thanks to <a href="https://github.com/cocorossello">Vicente Rossello Jaume</a> for <a href="https://github.com/selenide/selenide/pull/2051">PR 2051</a>.</p> <p>UPD. Fixed once again in <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">Show <code class="language-plaintext highlighter-rouge">$.selectOption()</code> friendly in reports</h3> <p>Another minor issue caused by the refactoring of selects: these operations look ugly in reports.<br /> That’s because Java arrays don’t have standard method <code class="language-plaintext highlighter-rouge">toString()</code>, and developers have to re-invent the wheel.</p> <p>Before:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| #blockChannel | select option([TV Rain, [Ljava.lang.String;@6732726]) | PASS | 487 | </code></pre></div></div> <p>After:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| #blockChannel | select option(TV Rain) | PASS | 487 | </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/2047">issue 2047</a> and <a href="https://github.com/selenide/selenide/pull/2052">PR 2052</a>.</p> <p><br /></p> <h3 id="friendly-local-storage-in-reports">Show <code class="language-plaintext highlighter-rouge">localStorage</code> friendly in reports</h3> <p>Similar problem with <code class="language-plaintext highlighter-rouge">sessionStorage</code> and <code class="language-plaintext highlighter-rouge">localStorage</code>: they look unreadable in reports.</p> <p>Before:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| com.codeborne.selenide.LocalStorage@138a952f | set item(['Bout', 9125]) | | com.codeborne.selenide.SessionStorage@549w123gg | set item(['Griner', 3285]) | </code></pre></div></div> <p>After:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>| localStorage | set item(['Bout', 9125]) | | sessionStorage | set item(['Griner', 3285]) | </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/2045">issue 2045</a> and <a href="https://github.com/selenide/selenide/pull/2046">PR 2046</a>.</p> <p><br /></p> <h3 id="update-dependencies">Upgraded 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="subprojects">Subprojects</h3> <p>We also released our subprojects:</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">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> on Testing Mini Bytes channel</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> by Oleksandr Podoliako</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/12/08/selenide-6.10.2/ https://selenide.org/2022/12/08/selenide-6.10.2 2022-12-08T00:00:00+00:00 Released Selenide 6.10.0 <p><br /></p> <h1 id="evil-evening">Evil evening!</h1> <p>We released <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">Improved file download algorithm</a></li> <li><a href="#fail-download-early">Fail download early</a></li> <li><a href="#select-options-using-javascript">Select options using JavaScript</a></li> <li><a href="#make-click-chainable">Made click chainable</a></li> <li><a href="#fix-size-for-new-tabs">Fixed size of new tabs</a></li> <li><a href="#encode-basic-auth-credentials-in-url">BasicAuth with special characters</a></li> <li><a href="#news">News</a></li> </ul> <h3 id="slow-download-in-firefox">Improved file download algorithm</h3> <p>Selenide contains several algorithms for downloading files: <code class="language-plaintext highlighter-rouge">HTTPGET</code>, <code class="language-plaintext highlighter-rouge">PROXY</code> and <code class="language-plaintext highlighter-rouge">FOLDER</code>. Let’s talk about <code class="language-plaintext highlighter-rouge">FOLDER</code>. To download a file, it clicks the button and waits for new files in downloads folder.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">File</span> <span class="n">racoons</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">racoons</span><span class="o">).</span><span class="na">hasName</span><span class="o">(</span><span class="s">"racoons.xls"</span><span class="o">);</span> </code></pre></div></div> <p>The problem is that it didn’t work well in Firefox in case of very slow download. We realized that Firefox creates immediately two files: “racoons.xls” and “racoons.xls.part” - both are empty. And only then starts filling “racoons.xls.part”.</p> <p><br /> Now the <code class="language-plaintext highlighter-rouge">FOLDER</code> algorithm is smarter. It</p> <ol> <li>waits until some suitable file appears in the downloads folder;</li> <li>waits until all <code class="language-plaintext highlighter-rouge">*part</code> files disappear (in Firefox);</li> <li>waits until all <code class="language-plaintext highlighter-rouge">*crdownload</code> (in Chrome);</li> <li>waits until none of files has been modified for 1+ seconds (in other browsers).</li> </ol> <p>This approach should provide more reliable file downloads.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1989">issue 1989</a> and <a href="https://github.com/selenide/selenide/pull/2003">PR 2003</a>.</p> <p><br /></p> <h3 id="fail-download-early">Fail fast the downloading process if no progress observed</h3> <p>People often set a long timeout for downloading files. Especially when downloading a large file:</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">"puten mobshiza.MP4"</span><span class="o">));</span> </code></pre></div></div> <p>But it doesn’t make sense so long if the downloading process has not even started. For example, if the click missed the button (how it can happen, I showed in video <a href="https://www.youtube.com/watch?v=-c5XT2v5gRY&amp;ab_channel=DEVCLUB.EE&amp;t=37m31s">“Flaky tests”</a>).</p> <p>To fail earlier, you can set another parameter “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">"puten mobshiza.MP4"</span><span class="o">));</span> </code></pre></div></div> <p>In this case, the general download timeout is 99 seconds, BUT it during 2 seconds none of files is modified, then the method will throw an exception immediately.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1990">issue 1990</a> and <a href="https://github.com/selenide/selenide/pull/2023">PR 2023</a>.</p> <p><br /></p> <h3 id="select-options-using-javascript">Select <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> options using JavaScript</h3> <p>It should made work with <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code>s faster. As a bonus, now Selenide throws a more detailed exception if <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> (or <code class="language-plaintext highlighter-rouge">&lt;option&gt;</code>) was <code class="language-plaintext highlighter-rouge">disabled</code>.</p> <p>Given a <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> with some disabled options:</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>Trying to select a <code class="language-plaintext highlighter-rouge">disabled</code> option:</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>will throw a clear error message:</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>Before this release, the error was not so detailed:</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>(and even earlier, such a test did not fall at all, but did not select the option either)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1553">issue 1553</a> and <a href="https://github.com/selenide/selenide/pull/1876">PR 1876</a>.</p> <blockquote> <p>Specials thanks to <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> for <a href="https://github.com/selenide/selenide/pull/1553">PR 1553</a>. Though it was not merged, it triggered a discussion in Selenide and even in Selenium team: what is a “just framework” and “opinionated framework”.</p> </blockquote> <p><br /></p> <h3 id="make-click-chainable">Made <code class="language-plaintext highlighter-rouge">$.click(options)</code> chainable</h3> <p>People often complain that method <code class="language-plaintext highlighter-rouge">$.click()</code> has return type <code class="language-plaintext highlighter-rouge">void</code>. You cannot chain it like this:</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>Alas, we cannot fix it, since class <code class="language-plaintext highlighter-rouge">SelenideElement</code> inherits method <code class="language-plaintext highlighter-rouge">void click()</code> from Selenium’s <code class="language-plaintext highlighter-rouge">WebElement</code>.</p> <p>But we made chainable another (overloaded) <code class="language-plaintext highlighter-rouge">click</code> method (the one with parameters).<br /> At least this one can now be chained:</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>See <a href="https://github.com/selenide/selenide/issues/2007">issue 2007</a> and <a href="https://github.com/selenide/selenide/pull/2008">PR 2008</a>.</p> <p><br /></p> <h3 id="fix-size-for-new-tabs">Fixed window size of new tabs</h3> <p>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/2017">PR 2017</a>.</p> <p>P.S. Fixed once again in <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">Support BasicAuth login/password with special characters</h3> <p>Selenide can open sites protected by 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>This login/password is added either to http header <code class="language-plaintext highlighter-rouge">Authorization</code> (if proxy is enabled) or URL (if proxy is disabled).</p> <p>Recently I discovered that the the second way doesn’t work correctly if login or password contains special characters. When adding to URL, these characters were not escaped, causing an invalid 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>browser could not open such a link.</p> <blockquote> <p>I am curious, why nobody every complained about that? Do you all use just <code class="language-plaintext highlighter-rouge">scott</code>/<code class="language-plaintext highlighter-rouge">tiger</code> credentials? :)</p> </blockquote> <p>Now Selenide encodes such characters and generates a valid 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>See <a href="https://github.com/selenide/selenide/issues/2020">issue 2020</a> and <a href="https://github.com/selenide/selenide/pull/2021">PR 2021</a>.</p> <p><br /></p> <h3 id="update-dependencies">Upgraded dependencies</h3> <ul> <li>Selenium from 4.5.0 to 4.6.0, see <a href="https://www.selenium.dev/blog/2022/selenium-4-6-0-released/">changelog</a></li> <li>WebDriverManager from 5.3.0 to 5.3.1, see <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md#531---2022-11-04">changelog</a></li> <li>BrowserUpProxy from 2.2.3 to 2.2.5, see <a href="https://github.com/valfirst/browserup-proxy/blob/master/CHANGELOG.md">changelog</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, see <a href="https://github.com/LittleProxy/LittleProxy/milestone/19?closed=1">changelog</a></li> <li>#2014 Bump httpclient5 from 5.1.3 to 5.2, see <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, see <a href="https://github.com/selenide/selenide/pull/2025">PR 2025</a></li> </ul> <p><br /></p> <h3 id="child-projects">Child projects</h3> <p>We also released our child projects:</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">News</h3> <ul> <li>JetBrains released AQUA - new <a href="https://www.jetbrains.com/aqua/">IDE for automated tests</a>! Nice logo!</li> <li>Selenium introduced its own built-in <a href="https://www.selenium.dev/blog/2022/introducing-selenium-manager/">WebDriverManager analogue</a>. Someone already tried it?</li> <li>New framework <a href="https://gitlab.com/brewcode/selenide-pages">Selenide Pages</a> based on Selenide from Maxim Kochetkov. Nice logo!</li> </ul> <h3 id="videos">Videos and posts</h3> <ul> <li>Post <a href="https://www.linkedin.com/pulse/all-selenide-muhammad-naeem/">All About Selenide</a> by Muhammad Naeem</li> <li>Video <a href="https://morioh.com/p/018678871de9">Working with web elements using Selenide</a> by Automation Bro</li> <li>Post <a href="https://dev.to/automationbro/page-object-model-selenide-tutorial-series-g3g">Page Object Model</a> from “Selenide Tutorial Series” by Automation Bro</li> </ul> <p><br /></p> <h3 id="statistics">Statistics</h3> <ul> <li>Our <a href="https://www.linkedin.com/groups/9154550/">Selenide user group in LinkedIn</a> has already 176 members. Join!</li> <li>We got a fresh downloads statistics for October. We passed the 470 thousands line!</li> </ul> <center> <img src="/images/2022/11/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/11/21/selenide-6.10.0/ https://selenide.org/2022/11/21/selenide-6.10.0 2022-11-21T00:00:00+00:00 Released Selenide 6.9.0 <p><br /></p> <h1 id="yo-seleniders">Yo Seleniders!</h1> <p>Let me break your doomscrolling with some good news.</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/166?closed=1">Selenide 6.9.0</a>! Basically, they pumped proxies and updated selenium.</p> <ul class="blogpost-menu"> <li><a href="#proxy-mock-response">Fake server response in proxy</a></li> <li><a href="#secure-authorization-header">Authorization header</a></li> <li><a href="#resolve-proxy-hostname">Improve proxy host name</a></li> <li><a href="#upgrade-to-selenium-4.5.0">Upgrade to Selenium 4.5.0</a></li> <li><a href="#remove-opera-support">Drop Opera support</a></li> <li><a href="#news">News</a></li> </ul> <h3 id="proxy-mock-response">Now Selenide proxy can mock server response</h3> <p>As you know, Selenide can run its own embedded proxy server. It listens requests between browser and other world. We mostly used it in read-only mode (logging, downloading files). But now you can also change the traffic. Namely, you can replace server response. It may be useful to mock the response of some service.</p> <p>Let’s look at some simple abstract example.</p> <h4 id="example">Example</h4> <p>Let’s say you’re testing a site showing the results of a referendum. It’s a html page on address, say, https://referendum.ru, which calls service https://cik.ru to fetch json with voting results.</p> <p>Since the results are not known in advance, but the site needs to be tested here and now, we want to check how it will look in different corner cases.</p> <h4 id="test">Test</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">"not only satisfy but also surprised"</span><span class="o">));</span> </code></pre></div></div> <h4 id="explanation">Explanation</h4> <p>Look at the first parameter <code class="language-plaintext highlighter-rouge">cik-mock</code>. This is <em>name</em> of the mock.<br /> You can use it later to cancel the mock (to avoid occasional effects on the following tests):</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>Or you can just cancel all the mocks for safety:</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="constraints">Constraints</h4> <p>If you site and mocked service run on same domain, there is no constraints.<br /> But if the site calls a service on another domain, mock will work only if</p> <ul> <li>both site and mock run on https (not http)</li> <li>service is alive and accessible from proxy</li> </ul> <h4 id="in-sum">In sum</h4> <p>Using mocks is a powerful technique letting us verify different corner cases that are hard or even impossible to reproduce otherwise <br /> (“146% votes for”, “no one showed up to vote”, “service is unavailable” etc.)</p> <blockquote> <p>Do not be shy about your desire to correct, clean up, add something. It will work as you want.</p> </blockquote> <p>See <a href="https://github.com/selenide/selenide/issues/1254">issue 1254</a> and <a href="https://github.com/selenide/selenide/pull/1978">PR 1978</a>.</p> <p><br /></p> <h3 id="secure-authorization-header">Send authorization header only to specified domain</h3> <p>Some site you need to test are protected by BasicAuth. Selenide allows to open such sites with overloaded method <code class="language-plaintext highlighter-rouge">open</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">"saver-of-the-world"</span><span class="o">,</span> <span class="s">"gojda!!!"</span><span class="o">));</span> </code></pre></div></div> <p>This method can overcome BasicAuth:</p> <ul> <li>if proxy is not enabled - by adding login/password to URL,</li> <li>and if proxy is enabled - by adding http header <code class="language-plaintext highlighter-rouge">Authorization</code> to requests from browser to server.</li> </ul> <p>But here’s the problem. Recently we realized that Selenide sent <code class="language-plaintext highlighter-rouge">Authorization</code> header not only to the app under test, but also to all other services (e.g. S3 or Google authentication).</p> <p>Now Selenide will send <code class="language-plaintext highlighter-rouge">Authorization</code> only to needed domain. But you will also need to specify the domain in constructor:</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">"saver-of-the-world"</span><span class="o">,</span> <span class="s">"gojda!!!"</span><span class="o">));</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/1974">issue 1974</a> and <a href="https://github.com/selenide/selenide/pull/1975">PR 1975</a>.</p> <p><br /></p> <h3 id="resolve-proxy-hostname">Improved resolving proxy host name</h3> <p>When you enable proxy in Selenide (e.g. by setting <code class="language-plaintext highlighter-rouge">Configuration.proxyEnabled = true</code>), it will run embedded proxy server on random port. And open a browser with instructions to use proxy <code class="language-plaintext highlighter-rouge">HOST:PORT</code>. The question is, which HOST to use?</p> <p>Until now, we used expression <code class="language-plaintext highlighter-rouge">ClientUtil.getConnectableAddress()</code> to resolve the host name (it’s default behaviour of BrowserUpProxy). But from this release, we will use <code class="language-plaintext highlighter-rouge">new NetworkUtils().getNonLoopbackAddressOfThisMachine()</code> (it’s Selenium internal method). I’m too lazy to figure out what their specifics are, but they may return different results on my machine:</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>The first one is definitely better when the browser runs on different machine or in a container. The proxy will just not be accessible by address <code class="language-plaintext highlighter-rouge">127.0.0.1</code> from other machine.</p> <blockquote> <p>If none expression works properly for you, you can always specify host name explicitly by setting <code class="language-plaintext highlighter-rouge">Configuration.proxyHost = "my.comp.eu";</code></p> </blockquote> <p>See <a href="https://github.com/selenide/selenide/pull/1970">PR 1970</a>.</p> <p><br /></p> <h3 id="upgrade-to-selenium-4.5.0">Upgraded to Selenium 4.5.0</h3> <p>Notable changes:</p> <ul> <li>dropped support for Opera browser</li> <li>added alternative implementation of webdriver transport using JDK 11 HTTP client instead of Netty client</li> <li>added checks for <code class="language-plaintext highlighter-rouge">disabled</code> in class <code class="language-plaintext highlighter-rouge">Select</code> (now you cannot select a disabled options anymore)</li> <li>removed host name from Selenium exceptions <blockquote> <p>arrr, I asked for this change <a href="https://github.com/SeleniumHQ/selenium/issues/489">since 2015</a>!!!</p> </blockquote> </li> </ul> <p>See <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">changelog</a> and <a href="https://github.com/selenide/selenide/pull/1967">PR 1967</a>.</p> <p><br /></p> <h3 id="remove-opera-support">Removed support for Opera browser</h3> <p>As a consequence of the Selenium upgrade, we also had to drop Opera support. I guess the reason is that Opera uses Chrome engine and is not very different from Chrome. If you still need to run tests in Opera - it’s possible, just use chromedriver.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1967">PR 1967</a>.</p> <p><br /></p> <h3 id="do-not-log-get-alias">Removed <code class="language-plaintext highlighter-rouge">getAlias</code> from reports</h3> <p>Thanks to <a href="https://github.com/reserved-word">Reserved Word</a> for <a href="https://github.com/selenide/selenide/pull/1971">PR 1971</a>.</p> <p><br /></p> <h3 id="restore-setting-connection-timeout">Restored setting “connection timeout”</h3> <p>Few people need this, so feel free to skip it.</p> <p class="small">We used to have two settings for the webdriver http client: “connection timeout” and “read timeout”. The first one had to be removed when upgrading to Selenium 4, because it was cut out there. Now it was resuscitated in Selenium 4.5.0, well, so we resuscitated also.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1977">PR 1977</a>.</p> <p><br /></p> <h3 id="update-dependencies">Upgraded 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> <p>Not only IT people suffer from <a href="https://www.youtube.com/watch?v=laIGavOMcw8&amp;ab_channel=FoilArmsandHog">constantly changing requirements</a>!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/10/07/selenide-6.9.0/ https://selenide.org/2022/10/07/selenide-6.9.0 2022-10-07T00:00:00+00:00 Released Selenide 6.8.1 <p><br /></p> <h1 id="gooda">Gooda!</h1> <p>Catch min-bugfix <a href="https://github.com/selenide/selenide/milestone/165?closed=1">Selenide 6.8.1</a>.</p> <p>This only applies to those who directly call in their tests</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>and encountered <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code> after upgrading to <a href="/2022/09/24/selenide-6.8.0/">Selenide 6.8.0</a>.</p> <p><br /></p> <h3 id="pre-history">Prehistory</h3> <p>What is OpenTelemetry? This is some kind of thing that was added to Selenium 4 for some reason, but nobody really knows why it’s needed and how to use it. :) In fact, no one cares, no one uses it. :)</p> <p>In Selenide, we also removed it in <a href="https://github.com/selenide/selenide/pull/1763">PR 1763</a>.</p> <blockquote> <p>OpenTelemetry dependency in Selenium is like Special Military Operation: nobody knows why it’s needed.</p> </blockquote> <h3 id="the-problem">The problem</h3> <p>So, most Selenide users don’t have such a problem.</p> <p>BUT you might into trouble if you call constructor <code class="language-plaintext highlighter-rouge">new RemoteWebDriver(url, options)</code> in your tests. Unfortunately, it requires OpenTelemetry dependency. And after upgrading to <a href="/2022/09/24/selenide-6.8.0/">Selenide 6.8.0</a> these users might get <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code>.</p> <p><br /></p> <h3 id="temporary-solution">Temporary solution</h3> <p>Add parameter <code class="language-plaintext highlighter-rouge">false</code> to the constructor: <code class="language-plaintext highlighter-rouge">new RemoteWebDriver(url, options, false)</code>. This <code class="language-plaintext highlighter-rouge">false</code> says “please don’t user telemetry”.</p> <p><br /></p> <h3 id="restored-opentelemetry">Demobilized OpenTelemetry dependency</h3> <p>In short, we quickly released Selenide 6.8.1, which brought back OpenTelemetry.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1965">issue 1965</a> and <a href="https://github.com/selenide/selenide/pull/1966">PR 1966</a>.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/09/27/selenide-6.8.1/ https://selenide.org/2022/09/27/selenide-6.8.1 2022-09-27T00:00:00+00:00 Released Selenide 6.8.0 <p><br /></p> <h1 id="bad-morning">Bad morning!</h1> <p>While the world is going crazy, we are releasing <a href="https://github.com/selenide/selenide/milestone/161?closed=1">Selenide 6.8.0</a>.<br /> Not so many features, rather dependency updates.</p> <ul class="blogpost-menu"> <li><a href="#deep-shadow-selectors">Recursive search in shadow dom</a></li> <li><a href="#add-page-without-class">Method page() without parameters</a></li> <li><a href="#as-annotation">Annotation @As</a></li> <li><a href="#news">News</a></li> </ul> <h3 id="deep-shadow-selectors">Recursive search in shadow dom</h3> <p>As you probably know, Selenide has <a href="/2020/03/18/selenide-5.10.0">methods</a> to find elements inside shadow dom. And even in multiple embedded shadow doms:</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>But you still need to waste time to investigate these doms and find proper locators for all those shadow roots.</p> <p>Now you can save time with the help of new method <code class="language-plaintext highlighter-rouge">shadowDeepCss</code> - it searches the element recursively in all shadow roots all over the 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>Naturally, there is still a risk that such “too generic” search can find a wrong element which probably resides in another shadow dom, but occasionally matches your locator.<br /> In this case you will need to return to the old good “exact” search.</p> <blockquote> <p>As in the case of databases and sausages, “it’s better not to look inside” - there is a bunch of tricky javascript for tree search.</p> </blockquote> <p>See <a href="https://github.com/selenide/selenide/issues/1946">issue 1946</a>.</p> <p>Thanks to</p> <ol> <li><a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1947">PR 1947</a>, and</li> <li><a href="https://github.com/Georgegriff">Georgegriff</a> for project <a href="https://github.com/Georgegriff/query-selector-shadow-dom">query-selector-shadow-dom</a>, from where we stole this code. :)</li> </ol> <p><br /></p> <h3 id="add-page-without-class">Added method <code class="language-plaintext highlighter-rouge">page()</code> not requiring <code class="language-plaintext highlighter-rouge">Class</code> argument</h3> <p>Dear Page Object fans, take a seat, this is for you. There is a turning point in your life.</p> <p>Now you don’t need to pass <code class="language-plaintext highlighter-rouge">Class</code> argument to method <code class="language-plaintext highlighter-rouge">page(Class pageObjectClass)</code>. Wow!</p> <p>Instead of</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>you can just write</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>I used to think Java couldn’t do that, but then I found <a href="https://twitter.com/tagir_valeev/status/1262763570904719361">a hack</a>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1961">PR 1961</a>, and thanks to <a href="https://twitter.com/tagir_valeev">Tagir Valeev</a> for the hack.</p> <p><br /></p> <h3 id="as-annotation">Added annotation <code class="language-plaintext highlighter-rouge">@As</code></h3> <p>… for giving human-readable aliases for page object fields.</p> <h4 id="1-without-aliases">1. Without aliases</h4> <p>Assuming you have a page object with <code class="language-plaintext highlighter-rouge">@FindBy</code> annotations:</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>and a test like <code class="language-plaintext highlighter-rouge">page.header.shouldBe(visible)</code>.</p> <p>In reports, it looks quite long and unreadable:</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>And when the test fails, it’s quite hard to understand which element was not found:</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>People often want to give some human-readable names to elements to avoid seeing long ugly xpath’s in reports.</p> <h4 id="2-resistance">2. Resistance</h4> <p>I have always been against it. I believe that not only reports, but also the tests themselves should be readable. I stand for clean code. If you don’t like the ugly xpath - well, just change it, don’t hide it!</p> <p>But still decided to meet people’s need. In <a href="/2020/12/26/selenide-5.17.0/">Selenide 5.17.0</a>, we added method <code class="language-plaintext highlighter-rouge">$.as("alias")</code>.</p> <h4 id="3-with-aliases">3. With aliases</h4> <p>And in this release you can also add annotation <code class="language-plaintext highlighter-rouge">@As</code> to page object fields.</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>It will look readable in reports:</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>And when the test fails, it’s easier to understand which element was not found:</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>See <a href="https://github.com/selenide/selenide/issues/1903">issue 1903</a> and <a href="https://github.com/selenide/selenide/pull/1956">PR 1956</a>.</p> <blockquote> <p>Don’t forget: everyone lies.</p> <p>Documentation lies, comments lie, users lie. Sooner or later, the alias will also lie. Someone changed the locator, but forgot the alias. Or the element was copied, but the alias was forgotten to be changed. Etc.</p> <p>Reports are good, but in the end, only the code can be trusted.</p> </blockquote> <p><br /></p> <h3 id="update-dependencies">Updated 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 – see <a href="https://junit.org/junit5/docs/5.9.1/release-notes/">release notes</a></li> </ul> <blockquote> <p>Announced partial mobilization of dependencies!</p> </blockquote> <p><br /></p> <h3 id="news">News</h3> <ul> <li>The series from Dilpreet Johal continues - now <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> by Tomasz Buga</li> <li>Java 19 has been released.</li> </ul> <h3 id="statistics">Statistics</h3> <p>Latest Selenide download statistics only makes us happy. We passed the 400 thousands line!</p> <center> <img src="/images/2022/09/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/09/24/selenide-6.8.0/ https://selenide.org/2022/09/24/selenide-6.8.0 2022-09-24T00:00:00+00:00 Released Selenide 6.7.4 <p><br /></p> <h1 id="hey-there">Hey there!</h1> <p><br /> We did yet another nano-release <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">Added setting <code class="language-plaintext highlighter-rouge">remoteReadTimeout</code></h3> <p>I recently <a href="https://www.youtube.com/watch?v=-KGtZoFVzr8&amp;list=PL9Z-JgiTsOYRfoG_mcRBlTUIFPIknhQ6S">complained about</a> too many settings in Selenide.</p> <p>Nevertheless, we added yet another setting <code class="language-plaintext highlighter-rouge">Configuration.remoteReadTimeout</code> (a.k.a. <code class="language-plaintext highlighter-rouge">-Dselenide.remoteReadTimeout</code>).</p> <p>It may be useful if you run browser on a remote server, but the number if browser is limited on that server, and your tests need to wait in a queue for a free browser. In this case the default timeout for 1.5 minutes might not be enough, and you need to increase the timeout.</p> <blockquote> <p>Personally, I call it HELL on the project, but who are we to stop people from voluntarily frying in a pan …</p> </blockquote> <p>Now you can set any timeout from build scripts, command line etc.</p> <p>Thanks to <a href="https://github.com/rodion-goritskov">Rodion Goritskov</a> for <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">Fixed mini-bug in “Dead threads watchdog”</h3> <p>Have you ever seen <code class="language-plaintext highlighter-rouge">java.lang.IllegalStateException: Shutdown in progress</code> in logs?</p> <p>It means you hasn’t upgraded to <a href="/2022/08/14/selenide-6.7.2/">Selenide 6.7.2</a> :)</p> <p>Now I recommend to upgrade to 6.7.4 - and you get fixed both memory leaks and IllegalStateException.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1942">issue 1942</a> and <a href="https://github.com/selenide/selenide/pull/1943">PR 1943</a>.</p> <p><br /></p> <h3 id="update-dependencies">Upgraded 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">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/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/09/05/selenide-6.7.4/ https://selenide.org/2022/09/05/selenide-6.7.4 2022-09-05T00:00:00+00:00 Released Selenide 6.7.3 <p><br /></p> <h1 id="good-saturday">Good saturday!</h1> <p><br /> You are reading release notes for <a href="https://github.com/selenide/selenide/milestone/163?closed=1">Selenide 6.7.3</a>.</p> <h3 id="condition-partial-value">Added condition <code class="language-plaintext highlighter-rouge">partialValue</code></h3> <p>Similarly to <code class="language-plaintext highlighter-rouge">$.shouldHave(partialText("Good Co"))</code>, now you have <code class="language-plaintext highlighter-rouge">$.shouldHave(partialValue("Cola"))</code>.</p> <p>It’s needed if you set <code class="language-plaintext highlighter-rouge">Configuration.textCheck = FULL_TEXT</code>, but need to check a value of some input or textarea <em>partially</em>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1923">issue 1923</a> and <a href="https://github.com/selenide/selenide/pull/1924">PR 1924</a>.</p> <p><br /></p> <h3 id="condition-tag-name">Added condition <code class="language-plaintext highlighter-rouge">tagName</code></h3> <p>You probably don’t need it often. Because we often find element by the tag, and only then verify some its attributes.</p> <p>Anyway, now you can verify the tag name of an element:</p> <p><code class="language-plaintext highlighter-rouge">$(".btn-primary").shouldHave(tagName("button"));</code></p> <p>Or filter a collection by tag name:</p> <p><code class="language-plaintext highlighter-rouge">$$(byText("Submit!")).filterBy(tagName("button"));</code></p> <p>See <a href="https://github.com/selenide/selenide/issues/1928">issue 1928</a> and <a href="https://github.com/selenide/selenide/pull/1929">PR 1929</a>.</p> <p><br /></p> <h3 id="check-select-tag">Now we check that element is <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code></h3> <p>… in methods <code class="language-plaintext highlighter-rouge">$.getSelectedText()</code> and <code class="language-plaintext highlighter-rouge">getSelectedValue()</code>.</p> <p>The initial intention of these methods was to find a selected <code class="language-plaintext highlighter-rouge">&lt;option&gt;</code> in a <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> and return its text or value.</p> <p>But</p> <ol> <li>it’s not obvious from the name (and that’s why we renamed methods to <code class="language-plaintext highlighter-rouge">$.getSelectedOptionText()</code> and <code class="language-plaintext highlighter-rouge">getSelectedOptionValue()</code>)</li> <li>you could call these methods on any other element but <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> - and get an unpredictable behavior.</li> </ol> <p>Now call to <code class="language-plaintext highlighter-rouge">$("div").getSelectedOptionText()</code> will throw <code class="language-plaintext highlighter-rouge">IllegalArgumentException</code>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1934">PR 1934</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated 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">News</h3> <ul> <li>Selenide puzzler <a href="/2022/08/22/selenide-puzzler/">Logical AND or OR?</a></li> <li>Selenide got to <a href="https://aglowiditsolutions.com/blog/top-java-frameworks/">Top Java Frameworks to Use in 2022</a></li> </ul> <p>And finally, <strong>the main achievement</strong>:</p> <ul> <li>Selenide got mentioned on <a href="https://www.selenium.dev/ecosystem/">official Selenium site</a> in category “Ecosystem”!</li> </ul> <p>Impossible!</p> <p>It hasn’t even been 10 years!</p> <p>Wait, it’s been…</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/08/27/selenide-6.7.3/ https://selenide.org/2022/08/27/selenide-6.7.3 2022-08-27T00:00:00+00:00 Selenide puzzler <p><br /></p> <h1 id="good-night">Good night!</h1> <p>Do you love puzzlers? Did you miss puzzlers?</p> <p>We recently published a new <a href="https://twitter.com/selenide/status/1560192824536088580">puzzler in Twitter</a>:</p> <blockquote> <p>Do these lines work equally? Or is there some difference?</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="answer-options">Answer options</h3> <p>You gave us many answers:</p> <ul> <li>both lines work equally (the most popular answer)</li> <li>the first case is “logical or”, and the second is “logical and”</li> <li>the second runs %timeout% seconds longer</li> <li>the first runs with one timeout, the second run with two timeouts</li> <li>the first fails if one of conditions is false, the second fails if the element is not visible, but is enabled.</li> </ul> <h3 id="correct-answer">Correct answer</h3> <p>In most cases both variants work equally.</p> <p>Both variants is “logical AND”. In both cases Selenide will check that the element matches both conditions.</p> <table> <tbody> <tr> <td> </td> <td><strong>enabled</strong></td> <td><strong>disabled</strong></td> </tr> <tr> <td><strong>visible</strong></td> <td>ok</td> <td>nok</td> </tr> <tr> <td><strong>invisible</strong></td> <td>nok</td> <td>nok</td> </tr> </tbody> </table> <p><br /></p> <p>But there is a nuance.</p> <h3 id="the-nuance">The nuance</h3> <p>The difference will appear if the element meets the conditions not immediately, but after some time. Moreover, the first condition should be met in less than 4 seconds, and the second - in more than 4 seconds. (Say, it gets visible after 3.5 seconds, and enabled - after more 3.5 seconds).</p> <blockquote> <p>The thing is that the method <code class="language-plaintext highlighter-rouge">shouldHave</code> has the timeout (by default 4 seconds), while conditions (<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>) don’t have any timeouts - they can just check “matches” or “doesn’t match”.</p> </blockquote> <h3 id="example">Example</h3> <p>For example, let’s open <a href="https://selenide.org/traffic-light.html">Traffic light</a>. The light gets green after 3.5 seconds, and after next 3.5 seconds a text “Go!” appears on it.</p> <p>The first variant checks if the light gets both color and text during 4 seconds:</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">// timeout 4 seconds</span> </code></pre></div></div> <p>This variant fails because the light doesn’t get both color and text in 4 seconds (it gets the text “Go” after 7 seconds).</p> <p>The second variant will not fail:</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">// timeout 4 seconds</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">// another timeout 4 seconds</span> </code></pre></div></div> <p>The first <code class="language-plaintext highlighter-rouge">shouldHave</code> waits until the element gets green (and it gets during 4 seconds). Then the second <code class="language-plaintext highlighter-rouge">shouldHave</code> gets started which waits until the element gets text “Go!” (and it gets it during next 4 seconds).</p> <p>Apparently, the closest answer was “the first runs with one timeout, the second run with two timeouts”.</p> <h3 id="your-puzzlers">Your puzzlers?</h3> <p>Do you have your own puzzlers? Share with us! Send us! Send’em all! Let’s guess them together!</p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/08/22/selenide-puzzler/ https://selenide.org/2022/08/22/selenide-puzzler 2022-08-22T00:00:00+00:00 Released Selenide 6.7.2 <p><br /></p> <h1 id="good-sunday">Good Sunday!</h1> <p><br /> We made a mini-release <a href="https://github.com/selenide/selenide/milestone/162?closed=1">Selenide 6.7.2</a> fixing a few memory leaks.</p> <p>But don’t worry, they are non-critical. It is unlikely that any of you even noticed them.</p> <h3 id="fix-selenide-memory-leak">Fixed a memory leak with shutdown hooks in Selenide</h3> <p>If you open and close a browser multiple times in a test</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>then memory consumption starts growing:</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>It’s caused by shutdown hooks that Selenide adds for every opened browser. And never removes them until the end of JVM.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1917">issue 1917</a> and <a href="https://github.com/selenide/selenide/pull/1919">PR 1919</a>.</p> <p>After upgrading to version 6.7.2, memory consumption doesn’t grow anymore:</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><br /></p> <h3 id="fix-little-proxy-memory-leak">Fixed a memory leak in LittleProxy</h3> <p>We upgraded to LittleProxy 2.0.11 which also contains a memory leak fix.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1918">PR 1918</a> and <a href="https://github.com/LittleProxy/LittleProxy/pull/141">PR 141</a></p> <p><br /></p> <h3 id="upgrade-to-selenium-4.4.0">Upgraded to Selenium 4.4.0</h3> <p>which even contains one my personal fix! Which, by the way, helped us fix the next problem.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1913">PR 1913</a> and <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">Selenium changelog</a></p> <p><br /></p> <h3 id="fix-full-size-screenshot">Fixed <code class="language-plaintext highlighter-rouge">full-size-screenshot</code> plugin</h3> <p>Our new plugin <code class="language-plaintext highlighter-rouge">full-size-screenshot</code> had one known problem (caused by a known bug in Selenium): if you run a browser remotely and open multiple tabs or windows, Selenide might take screenshot from a wrong window.</p> <p>This bug was fixed in Selenium 4.4.0, and now in Selenide too.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1920">PR 1920</a> and <a href="https://github.com/SeleniumHQ/selenium/pull/10811">PR 10811</a>.</p> <p><br /></p> <h3 id="news">Links</h3> <ul> <li>Post <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>Video <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>Post <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> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/08/14/selenide-6.7.2/ https://selenide.org/2022/08/14/selenide-6.7.2 2022-08-14T00:00:00+00:00 Released Selenide 6.7.0 <p><br /></p> <h1 id="good-day">Good day!</h1> <p><br /> We’ve got a big release <a href="https://github.com/selenide/selenide/milestone/154?closed=1">Selenide 6.7.0</a></p> <h3 id="holy-whole-string">Now <code class="language-plaintext highlighter-rouge">$.shouldHave(text)</code> can verify the whole text</h3> <p>From the very beginning, command <code class="language-plaintext highlighter-rouge">$.shouldHave(text)</code> verified the text <em>partially</em>, not the whole text:</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>Initially, this seemed like a good idea, because a web page often contains all sorts of inconsiderable/invisible characters. (newlines, tab characters, non-portable spaces, multiple spaces, etc.).</p> <p>But now it seems that it was still a bad idea, because beginners assume by default that the text is verified entirely, and snatch their portion of WTF moments when they discover that all their checks were checking a little wrong. :)</p> <p>And Selenide learned to ignore differences in the inconsiderable characters (for example, multiple subsequent spaces/tabs/newlines are considered as a single space).</p> <p>NB! Since this change is too drastic and will probably break a bunch of your tests, we left the previous behaviour as a default. And the new one can be enabled with this setting:</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>And when you need to check a substring, you can use the new check <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>Perhaps we will enable this mode by default in the next major version of Selenide 7.0.0 - until then <strong>you can play around and give us feedback</strong>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1780">issue 1780</a> and <a href="https://github.com/selenide/selenide/pull/1783">PR 1783</a>.</p> <p><br /></p> <h3 id="full-size-screenshots">Full-size screenshots</h3> <p>Finally!</p> <p>Now Selenide can take full-size screenshots.</p> <p>When a test fails, Selenide automatically takes a screenshot to help you understand why the test failed. By default, the webdriver captures only the visible part, not the entire browser window. But the most important things are often located outside the screen. The test failure is often caused by some disabled button at the bottom of the screen or a pop-up notification somewhere in the corner.</p> <p>Now you can apply a plugin that takes a screenshot with the full content of a web page in one line:</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. This plugin works only in browser supporting CDP (Chrome, Edge etc.) and Firefox. In other browsers, Selenide will take standard screenshots as usually.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1799">issue 1799</a> and <a href="https://github.com/selenide/selenide/pull/1858">PR 1858</a>. Thanks to <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> for <a href="https://github.com/selenide/selenide/pull/1800">PR 1800</a>.</p> <p><br /></p> <h3 id="cache-lookup-annotation">Caching of page object elements</h3> <p>As you know, Selenide supports “standard” Selenium page objects with annotations like <code class="language-plaintext highlighter-rouge">@FindBy</code>. But one specific annotation <code class="language-plaintext highlighter-rouge">@CacheLookup</code> was not supported by Selenide. It seemed an unneeded optimization because during test run, most of the time is usually spent on other things.</p> <p>Now we support the caching, why not. Who knows, probably has so much optimized project that the search of web elements became a bottleneck. I would like to see such a project. :)</p> <p>Thanks to <a href="https://github.com/groov1kk">Ilya Koshaleu</a> for <a href="https://github.com/selenide/selenide/pull/1894">PR 1894</a>.</p> <p>P.S. The question arises, should we also add caching for regular <code class="language-plaintext highlighter-rouge">$</code> and <code class="language-plaintext highlighter-rouge">$$</code> as well. What should be the api: a parameter, a setting? New method?</p> <p><strong>Suggest your ideas!</strong></p> <p><br /></p> <h3 id="cancel-report-testng-annotation">Cancelled TestNG annotation <code class="language-plaintext highlighter-rouge">@Report</code></h3> <p>Now the cancel culture has reached the annotations!</p> <p>If you use TestNG and Selenide text report, this announce is for you. Until now, you had to add 2 annotations to your tests:</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>Why we did this is a long story about the clumsy principles of how listeners work in TestNG (in short: adding a listener annotation to one class suddenly affects all other classes). But now we realized that the system can be simplified: the <code class="language-plaintext highlighter-rouge">@Report</code> annotation is no longer needed.</p> <p>Now Selenide report will be generated for all tests annotated with <code class="language-plaintext highlighter-rouge">@Listeners({TextReport.class}</code> <strong>and their child classes</strong>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1891">issue 1891</a> and <a href="https://github.com/selenide/selenide/pull/1909">PR 1909</a>.</p> <p><br /></p> <h3 id="decode-downloaded-file-name">Decode downloaded file name</h3> <p>It turned out that when downloading a file, the file name in the server response is sometimes encoded in Base64. And Selenide was reading this encoded file name as is. Now Selenide decrypts the file name.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1886">issue 1886</a> and <a href="https://github.com/selenide/selenide/pull/1889">PR 1889</a>.</p> <p><br /></p> <h3 id="restore-ie-support-set-value">Fixed <code class="language-plaintext highlighter-rouge">$.setValue()</code> in IE</h3> <p>It turns out we recently broke the <code class="language-plaintext highlighter-rouge">$.setValue()</code> method in Internet Explorer. We assumed people don’t use IE anymore: IE has officially given up. But there were complaints, and we restored IE support.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1907">PR 1907</a>.</p> <p><br /></p> <h3 id="made-http-client-timeouts-public">Made <code class="language-plaintext highlighter-rouge">HttpClientTimeouts</code> public</h3> <p>See <a href="https://github.com/selenide/selenide/pull/1902">PR 1902</a>.</p> <p><br /></p> <h3 id="restore-set-value-string">Returned parameter type to <code class="language-plaintext highlighter-rouge">String</code> for method <code class="language-plaintext highlighter-rouge">$.setValue()</code></h3> <p>Personally, it burns me from the fact that such a problem arose at all.</p> <p>See details in <a href="https://github.com/selenide/selenide/issues/1885">issue 1885</a> and <a href="https://github.com/selenide/selenide/pull/1888">PR 1888</a>.</p> <p><br /></p> <h3 id="validate-file-extension">Added a warning about invalid file extension parameter</h3> <p>See <a href="https://github.com/selenide/selenide/pull/1887">PR 1887</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated 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>restored parameter <code class="language-plaintext highlighter-rouge">Driver</code> in method <code class="language-plaintext highlighter-rouge">SelenidePageFactory.findSelector()</code> - it’s used by <code class="language-plaintext highlighter-rouge">selenide-appium</code> plugin.</li> </ul> <p><br /></p> <h3 id="news">News</h3> <ul> <li><a href="https://github.com/iSYS-Software/SelenideReporter">Opinionated reporting framework for Selenide</a> by Ulrich Mayring</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=-c5XT2v5gRY&amp;ab_channel=DEVCLUB.EE">Flaky Tests</a> - devclub.ee, Tallinn, 05.07.2022</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/08/04/selenide-6.7.0/ https://selenide.org/2022/08/04/selenide-6.7.0 2022-08-04T00:00:00+00:00 Released Selenide 6.6.6 <p><br /></p> <h1 id="good-evening">Good evening!</h1> <p><br /> We made another release <a href="https://github.com/selenide/selenide/milestone/160?closed=1">Selenide 6.6.6</a></p> <h3 id="remove-deprecated-capabilities">Removed deprecated capabilities</h3> <p>Some webdriver options (which Selenide has been setting for years) were later marked as deprecated.<br /> Starting from some release, Selenium started logging warnings about them.</p> <p>Now Selenide doesn’t set them anymore: <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>.</p> <p>We will not forget you, friends! You have served us faithfully for over 10 years.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1862">issue 1862</a>, <a href="https://github.com/selenide/selenide/issues/1866">issue 1866</a> and <a href="https://github.com/selenide/selenide/pull/1870">PR 1870</a>.</p> <p><br /></p> <h3 id="fix-clear-with-shortcut">Fixed ClearWithShortcut</h3> <p>… when working with driver wrapped into listeners. It’s rare, don’t worry.</p> <p>Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <a href="https://github.com/selenide/selenide/pull/1856">PR 1856</a>.</p> <p><br /></p> <h3 id="shorter-syntax-for-click">Added shorter syntax for <code class="language-plaintext highlighter-rouge">$.click</code></h3> <p>If you wished to click with an offset or custom timeout, you had to write quite a long line:</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>Now you can write a shorter line:</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>See <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">Added support for mobile apps</h3> <p>… when checking if webdriver is still alive.</p> <p>This is a minor fix for <code class="language-plaintext highlighter-rouge">selenide-appium</code>.</p> <p>When you reuse the same webdriver for multiple tests, Selenide needs to regularly check if the webdriver is still alive. To check it, Selenide calls <code class="language-plaintext highlighter-rouge">WebDriver.getTitle()</code>.</p> <p>Recently we realised that method <code class="language-plaintext highlighter-rouge">getTitle()</code> is not supported in Appium. Now we handle this situation properly, and the browser health check works in Appium too.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1878">issue 1878</a> and <a href="https://github.com/selenide/selenide/pull/1879">PR 1879</a>.</p> <p><br /></p> <h3 id="fix-reopen-browser-on-fail">Fixed logic of <code class="language-plaintext highlighter-rouge">reopenBrowserOnFail</code> setting</h3> <p>Another improvement for testing mobile apps.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1880">issue 1880</a> and <a href="https://github.com/selenide/selenide/pull/1881">PR 1881</a>.</p> <p><br /></p> <h3 id="update-dependencies">Upgraded dependencies</h3> <ul> <li>WebDriverManager <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">from 5.2.0 to 5.2.1</a>.</li> <li>byteBuddyVersion <a href="https://github.com/selenide/selenide/pull/1872">from 1.12.11 to 1.12.12</a>.</li> </ul> <p><br /></p> <h3 id="release-selenide-appium-2.1.0">Released selenide-appium 2.1.0</h3> <p>This version contains improved support for running tests for iOS applications.</p> <p>See <a href="https://github.com/selenide/selenide-appium/blob/master/CHANGELOG">selenide-appium</a>.</p> <p><br /></p> <p><em>With this release, we decided to demonstrate to the world community that we do not interfere with the creation of automated tests for mobile applications.</em></p> <p><em>Now it’s up to the automators.</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/07/01/selenide-6.6.6/ https://selenide.org/2022/07/01/selenide-6.6.6 2022-07-01T00:00:00+00:00 Released Selenide 6.6.4 <p><br /></p> <h1 id="gootas-morningas">Gootas morningas!</h1> <p><br /> We made another mini-released <a href="https://github.com/selenide/selenide/milestone/158?closed=1">Selenide 6.6.4</a>.</p> <h3 id="exact-texts-case-sensitive">Added condition <code class="language-plaintext highlighter-rouge">exactTextsCaseSensitive</code> for collections</h3> <p>Selenide has several built-in checks for collections texts:</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">// etc.</span> </code></pre></div></div> <p>Now there is one more check available:</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>Thanks to <a href="https://github.com/ben-nc2">Ben Heap</a> for <a href="https://github.com/selenide/selenide/pull/1861">PR 1861</a>.</p> <p><br /></p> <h3 id="selected-option-lazy-loaded">Made method <code class="language-plaintext highlighter-rouge">$.getSelectedOption()</code> lazy</h3> <p>By design, (almost) all Selenide methods <a href="https://github.com/selenide/selenide/wiki/Lazy-loading">should be lazy</a>.</p> <p>For example, call to <code class="language-plaintext highlighter-rouge">$("#nope")</code> does NOT fail even if the element doesn’t exist. It allows writing negative checks, e.g.</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>We realized we method <code class="language-plaintext highlighter-rouge">$("select").getSelectedOption()</code> was not lazy. It failed immediately if the select was not loaded yet etc.<br /> So you could not write such a negative check:</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">// failed on this line</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>Now we have corrected this issue. <em>The laziness won!</em> See <a href="https://github.com/selenide/selenide/issues/1581">issue 1581</a> and <a href="https://github.com/selenide/selenide/pull/1864">PR 1864</a>.</p> <p><br /></p> <h3 id="update-dependencies">Updated dependencies</h3> <ul> <li>Netty <a href="https://github.com/selenide/selenide/pull/1857">from 4.1.77.Final to 4.1.78.Final</a>.</li> <li>BrowserUp proxy <a href="https://github.com/selenide/selenide/pull/1860">from 2.1.5 to 2.2.0</a>.</li> </ul> <p><br /></p> <h3 id="release-selenide-6.6.5">UPD Released Selenide 6.6.5</h3> <p>One more mini-release <a href="https://github.com/selenide/selenide/milestone/159?closed=1">Selenide 6.6.5</a>.</p> <p>Upgraded Selenium from 4.2.2 to 4.3.0.</p> <p><br /> <br /></p> <p><em>This is our frank response to your frank request.</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/06/20/selenide-6.6.4/ https://selenide.org/2022/06/20/selenide-6.6.4 2022-06-20T00:00:00+00:00 Released Selenide 6.6.3 <p><br /></p> <h1 id="good-evening-fellows">Good evening fellows!</h1> <p><br /> We made a mini-release <a href="https://github.com/selenide/selenide/milestone/157?closed=1">Selenide 6.6.3</a>.</p> <h3 id="improve-click-timeout">Improved timeout for <code class="language-plaintext highlighter-rouge">$.click()</code></h3> <p>In version 6.6.0, we added an optional parameter <code class="language-plaintext highlighter-rouge">timeout</code> for method <code class="language-plaintext highlighter-rouge">$.click()</code>.<br /> But it appeared that it <a href="/2022/06/08/selenide-6.6.0/#click-timeout">only partially</a> solved the problem.</p> <p>Now we improved it: method</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>waits 8 seconds for both the element itself and the following page loading.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1572">issue 1572</a> and <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 day before, we released <a href="https://github.com/selenide/selenide/milestone/156?closed=1">Selenide 6.6.2</a> with upgrade to Selenium 4.2.2</p> <p>See <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>Before this, we released <a href="https://github.com/selenide/selenide/milestone/155?closed=1">Selenide 6.6.1</a> with a tiny fix for an old <a href="https://github.com/selenide/selenide/issues/1850">issue 1850</a>. We restore <code class="language-plaintext highlighter-rouge">byte-buddy</code> dependency which was needed for adding listeners to webdriver.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/06/12/selenide-6.6.3/ https://selenide.org/2022/06/12/selenide-6.6.3 2022-06-12T00:00:00+00:00 Released Selenide 6.6.0 <p><br /></p> <h1 id="good-morning-friends">Good morning, friends!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/152?closed=1">Selenide 6.6.0</a>.</p> <h3 id="selenide-clear-with-shortcut">Added new Selenide plugin <code class="language-plaintext highlighter-rouge">selenide-clear-with-shortcut</code></h3> <p>In Selenide 6.5.0, we changed the implementation of <code class="language-plaintext highlighter-rouge">$.clear()</code> method from a standard WebDriver to a shortcut (“Select all” -&gt; “Delete”).<br /> Later we realized this shortcut didn’t work stably in all browsers, we had to rework it. It got more stable, but slower. And since it’s not really needed for all, we decided to return the old good <code class="language-plaintext highlighter-rouge">WebElement.clear()</code>.</p> <p>And if you need the <em>smart</em> <code class="language-plaintext highlighter-rouge">$.clean()</code> method with shortcuts - you can add the brand new Selenide plugin:</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>Dependency <code class="language-plaintext highlighter-rouge">com.codeborne:selenide-clear-with-shortcut</code> will override method <code class="language-plaintext highlighter-rouge">$.clear()</code> - and <code class="language-plaintext highlighter-rouge">$.setValue()</code> correspondingly.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1497">issue 1497</a>, <a href="https://github.com/selenide/selenide/pull/1847">PR 1847</a> and <a href="https://github.com/selenide/selenide/pull/1838">PR 1838</a>.</p> <p><br /></p> <h3 id="fix-clear-in-safari">Fix method <code class="language-plaintext highlighter-rouge">$.clear()</code> in Safari</h3> <p>We found that new <code class="language-plaintext highlighter-rouge">$.clear()</code> didn’t work in Safari browser (who uses it nowadays?). <br /> I hope we fixed it. :)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1819">issue 1819</a> and <a href="https://github.com/selenide/selenide/pull/1820">PR 1820</a>.</p> <p><br /></p> <h3 id="new-own-test-checks">New checks for element own text</h3> <p>Added two new checks for element’s 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>Thanks to <a href="https://github.com/kachurinaa">Kachurin Alexandr</a> for <a href="https://github.com/selenide/selenide/pull/1811">PR 1811</a> and <a href="https://github.com/selenide/selenide/pull/1812">PR 1812</a>.</p> <p><br /></p> <h3 id="click-timeout">Added method <code class="language-plaintext highlighter-rouge">$.click()</code> with timeout</h3> <p>By default, method <code class="language-plaintext highlighter-rouge">$.click()</code> has the standard Selenide timeout (which is initially 4 seconds).<br /> Sometimes it’s not enough - for example:</p> <ol> <li>if clicking a link opens a new page which loads longer than 4 seconds.</li> <li>if the element which we are going to click is not visible yet, and appears after more than 4 seconds.</li> </ol> <p>In this release, we added timeout parameter to method <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>Though, it solves only the first problem. In next release we will also solve the second. :)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1572">issue 1572</a> and <a href="https://github.com/selenide/selenide/pull/1845">PR 1845</a>.</p> <p><br /></p> <h3 id="modal-timeout">Added timeout to methods <code class="language-plaintext highlighter-rouge">confirm()</code>, <code class="language-plaintext highlighter-rouge">dismiss()</code> and <code class="language-plaintext highlighter-rouge">prompt()</code></h3> <p>These methods existed from the very beginning of Selenide era. They allow working with JavaScript modal dialogs (<code class="language-plaintext highlighter-rouge">alert</code>, <code class="language-plaintext highlighter-rouge">confirm</code>, <code class="language-plaintext highlighter-rouge">prompt</code>). For example:</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>But they could not have any other timeout except the default one. Now you can pass a timeout parameter:</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>See <a href="https://github.com/selenide/selenide/issues/1721">issue 1721</a> and <a href="https://github.com/selenide/selenide/pull/1846">PR 1846</a>.</p> <p><br /></p> <h3 id="fix-execute-javascript">Fixed method <code class="language-plaintext highlighter-rouge">Driver.executeJavaScript()</code></h3> <p>…to support a wrapped webdriver (i.e. when you have added some listeners to the webdriver).</p> <p>See <a href="https://github.com/selenide/selenide/pull/1848">PR 1848</a>.</p> <p><br /></p> <h3 id="fix-checks-wording">Fixed phrasing of some checks</h3> <p>… to sound correctly in English. Namely:</p> <table> <thead> <tr> <th>In a test</th> <th>Was in report</th> <th>Now in report</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>See <a href="https://github.com/selenide/selenide/pull/1840">PR 1840</a>.</p> <p><br /></p> <h3 id="restore-safari-options">Restored Safari options</h3> <p>We realized that after upgrading to Selenium 4, we lost most of Safari options.<br /> It was easy to fix, yet… why nobody did complain on it?</p> <p>Looks like nobody is using Safari… :)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1836">issue 1836</a> and <a href="https://github.com/selenide/selenide/pull/1841">PR 1841</a>.</p> <p><br /></p> <h3 id="fix-testng-soft-asserts">Fixed soft asserts in TestNG</h3> <p>After upgrading to TestNG 7.5, Selenide soft asserts didn’t work anymore. If some check failed, your tests still remained green.</p> <p>We had to downgrade to TestNG 7.4.0 and hope for the fix on the dark side. :)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1834">issue 1834</a> and <a href="https://github.com/selenide/selenide/pull/1843">PR 1843</a>.</p> <p><br /></p> <h3 id="update-dependencies">Upgraded 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">Statistics</h3> <p>We haven’t published Selenide download statistics for a long time:</p> <center> <img src="/images/2022/06/selenide.downloads.png" width="800" /> </center> <p>We’re almost back to our all-time high of 324k downloads in May.</p> <p><br /></p> <p>Stay tuned!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/06/08/selenide-6.6.0/ https://selenide.org/2022/06/08/selenide-6.6.0 2022-06-08T00:00:00+00:00 Released Selenide 6.5.0 <p><br /></p> <h1 id="good-night">Good night!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/151?closed=1">Selenide 6.5.0</a>.</p> <h3 id="now-you-can-mask-passwords-in-reports">Now you can mask passwords in reports</h3> <p>Sometimes you need to type passwords and other sensitive data into input fields. Sometimes people don’t want this data to be visible in test reports (e.g. Allure or TextReport). And ask to hide this sensitive data somehow.</p> <p>I personally never understood why it’s a problem.<br /> Why the password from test environment should be a problem?<br /> You don’t use the same password in production, right?<br /> You don’t run tests in production, right?</p> <p>Anyway, we made it possible to mask sensitive data in test reports:</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">// replaces by "********"</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">// replaces by "J* username"</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/1768">issue 1768</a> and <a href="https://github.com/selenide/selenide/pull/1770">PR 1770</a>.</p> <p><br /></p> <h3 id="now-you-can-easily-pick-a-date-in-input-typedate">Now you can easily pick a date in <code class="language-plaintext highlighter-rouge">&lt;input type=date&gt;</code></h3> <p>It’s not a trivial task to pick a date in Selenium.</p> <p>When your application has element like <code class="language-plaintext highlighter-rouge">&lt;input type=date&gt;</code>, and your test tries to click it, different browsers open different calendars and use different date formats. In theory, they should use format based on user settings, but in reality each browser plays its own game.</p> <p>This causes flaky tests that might work on your machine, but occasionally fail on others.</p> <p>As a result of my investigation, I found that the only stable way to pick a date is setting a value in format <code class="language-plaintext highlighter-rouge">yyyy-mm-dd</code> using JavaScript snippet. It seems to work in all OSs and browsers.</p> <p>Now you can easily do it with new method <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>See <a href="https://github.com/selenide/selenide/issues/1753">issue 1753</a> and <a href="https://github.com/selenide/selenide/pull/1770">PR 1770</a>.</p> <p><br /></p> <h3 id="now-method-setvalue-supports-react-vuejs-etc">Now method <code class="language-plaintext highlighter-rouge">$.setValue("")</code> supports React, Vue.js etc.</h3> <p>Standard Selenium method <code class="language-plaintext highlighter-rouge">WebElement.clean()</code> (e.g. it’s used in Selenide method <code class="language-plaintext highlighter-rouge">$.setValue()</code>) doesn’t always work when the application uses some tricky constructions (generated by React/Vue/…) instead of standard <code class="language-plaintext highlighter-rouge">&lt;input&gt;</code>.</p> <p>Hopefully, we have fixed it. Waiting for your feedback.</p> <p>Now the following 2 methods abandoned using <code class="language-plaintext highlighter-rouge">WebElement.clear()</code>. They use key combinations instead:</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>This new way seems to work in all OSs and browsers.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1497">issue 1497</a> and <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p> <p>Thanks to <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> for the research in <a href="https://github.com/selenide/selenide/pull/1499">PR 1499</a>.</p> <p><br /></p> <h3 id="removed-triggering-duplicate-blur-and-change-events">Removed triggering duplicate <code class="language-plaintext highlighter-rouge">blur</code> and <code class="language-plaintext highlighter-rouge">change</code> events</h3> <p>As a result of previous change, method <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> triggers <code class="language-plaintext highlighter-rouge">blur</code> and <code class="language-plaintext highlighter-rouge">change</code> events only once.</p> <p>Before this release, method <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> worked in two steps and therefore triggered events twice:</p> <ul> <li>Step 1. <code class="language-plaintext highlighter-rouge">$.clear()</code> // triggered <code class="language-plaintext highlighter-rouge">blur</code> and <code class="language-plaintext highlighter-rouge">change</code></li> <li>Step 2. <code class="language-plaintext highlighter-rouge">$.sendKeys("blah")</code> // triggered <code class="language-plaintext highlighter-rouge">blur</code> and <code class="language-plaintext highlighter-rouge">change</code> once more</li> </ul> <p>The events triggered on the first step could cause unexpected effects, including disappearing of the field itself.<br /> See <a href="https://github.com/selenide/selenide/issues/960">issue 960</a>.</p> <p><br /></p> <h3 id="trigger-blur-on-a-previous-element">Trigger <code class="language-plaintext highlighter-rouge">blur</code> on a previous element</h3> <p>During working on the previous change, we improved method <code class="language-plaintext highlighter-rouge">$.setValue("blah")</code> so that it triggers <code class="language-plaintext highlighter-rouge">blur</code> event on a previous active element. At least it helped to fix some of our flaky tests. Hopefully, it can improve your tests as well.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1784">issue 1784</a> and <a href="https://github.com/selenide/selenide/commit/593e6fc900500d9">commit 593e6fc9005</a>.</p> <p><br /></p> <h3 id="now-method-setvalue-and-append-check-that-element-is-not-disabled-or-readonly">Now method <code class="language-plaintext highlighter-rouge">$.setValue()</code> and <code class="language-plaintext highlighter-rouge">$.append()</code> check that element is not disabled or readonly</h3> <p>See <a href="https://github.com/selenide/selenide/issues/1523">issue 1523</a> and <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p> <p><br /></p> <h3 id="added-conditions-interactable-and-editable">Added conditions “interactable” and “editable”</h3> <p>These conditions existed inside of Selenide, but were not available to end-users. <br /> Now you can also use them:</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>See <a href="https://github.com/selenide/selenide/issues/1523">issue 1523</a> and <a href="https://github.com/selenide/selenide/pull/1787">PR 1787</a>.</p> <p><br /></p> <h3 id="method-downloadfolder-waits-for-full-download-completion">Method <code class="language-plaintext highlighter-rouge">$.download(FOLDER)</code> waits for full download completion</h3> <p>Sometimes test might be flaky when it tries to download a file which is being downloaded very slowly. Selenide might detect and copy the file in <code class="language-plaintext highlighter-rouge">C:\Downloads</code> folder which is not fully downloaded yet.</p> <p>Now Selenide waits until <code class="language-plaintext highlighter-rouge">C:\Downloads</code> doesn’t contain any files “<em>.crdownload” (for Chrome) or “</em>.part” (for Firefox). This is a temporary file created by the browser for a short period of time while the file is beging downloaded.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1779">issue 1779</a>, <a href="https://github.com/selenide/selenide/pull/1804">PR 1804</a> and <a href="https://github.com/selenide/selenide/pull/1769">PR 1769</a>.</p> <p><br /></p> <h3 id="added-method-stream-for-collections">Added method <code class="language-plaintext highlighter-rouge">stream()</code> for collections</h3> <p>Actually, method <code class="language-plaintext highlighter-rouge">$$.stream()</code> already existed, but was deprecated in version <a href="/2022/01/10/selenide-6.2.0/">6.2.0</a>. Now you can pick one of two non-deprecated replacements:</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>See <a href="https://github.com/selenide/selenide/issues/1773">issue 1773</a> and <a href="https://github.com/selenide/selenide/pull/1774">PR 1774</a>.</p> <p><br /></p> <h3 id="remove-selenium-built-in-opentelemetry">Remove Selenium built-in OpenTelemetry</h3> <p>If I am not mistaken, this “telemetry” was added Selenium 4. I have no idea why it’s needed, but it could interfere with someone who already had their own telemetry. <br /> It was simpler for us to remove it than support both. :)</p> <p>Thanks to <a href="https://github.com/zzz">Petro Ovcharenko</a> and <a href="https://github.com/zzz">Aliaksandr Rasolka</a> for <a href="https://github.com/selenide/selenide/pull/1763">PR 1763</a>.</p> <p><br /></p> <h3 id="upgraded-dependencies">Upgraded dependencies</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>By the way LittleProxy projects moved to a dedicated <a href="https://github.com/LittleProxy/LittleProxy">github organisation</a>, and I became the main maintainer of the project. I was never a big fan of proxies, but LittleProxy is used inside of Selenide, and somebody needs to maintain it…</p> <p><br /></p> <h1 id="upd-selenide-651">UPD Selenide 6.5.1</h1> <p>A small update <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> Fixed <code class="language-plaintext highlighter-rouge">$.clear()</code> so that it doesn’t press <code class="language-plaintext highlighter-rouge">tab</code> anymore – see <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> Bump browserup-proxy-core 2.1.4 to 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> Fixed method <code class="language-plaintext highlighter-rouge">$.clear()</code> on a fresh Firefox: now it presses “Ctrl+A -&gt; Delete” instead of “Home -&gt; Shift+A -&gt; Delete”. See <a href="https://github.com/selenide/selenide/pull/1838">PR #1838</a>.</p> <p><br /></p> <p>Stay tuned!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/05/17/selenide-6.5.0/ https://selenide.org/2022/05/17/selenide-6.5.0 2022-05-17T00:00:00+00:00 Released Selenide 6.4.0 <p><br /></p> <h1 id="good-evening-friends">Good evening, friends!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/145?closed=1">Selenide 6.4.0</a>.</p> <p><br /></p> <h2 id="now-we-show-both-alias-and-locator-on-test-failure">Now we show both alias and locator on test failure</h2> <p>In Selenide, you can use method <code class="language-plaintext highlighter-rouge">as</code> to give web elements readable names.<br /> It may be useful when the element has no good ID/class, and you have no choice but write long ugly unreadable xpath. It’s hard to read such selectors in reports.</p> <p>Sample usage of <code class="language-plaintext highlighter-rouge">as</code>:</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>We always had a dilemma: should Selenide</p> <ol> <li>show only alias (pros: easily readable, cons: locator is not visible - but it might be helpful)</li> <li>show both alias and locator (pros: locator is visible, cons: hard to read).</li> </ol> <p><br /></p> <p>Finally, we realized what is the right way.</p> <p>Starting from version 6.4.0, Selenide will:</p> <ol> <li>Show only alias in reports (@TextReport, Allure report etc.)</li> <li>Show both alias and locator in error message in case of test failure</li> </ol> <p>For example, the code mentioned above will look like this in report:</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>And in error message:</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>See <a href="https://github.com/selenide/selenide/issues/1765">issue 1765</a> and <a href="https://github.com/selenide/selenide/pull/1766">PR 1766</a>.</p> <p><br /></p> <h2 id="added-spaces-in-selenide-report">Added spaces in Selenide report</h2> <p>The report mentioned above looked like this until now:</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>The problem is that the long URL is wrapped with <code class="language-plaintext highlighter-rouge">|</code> characters on the left and right. It makes it impossible to easily select it with double click. Now a space will be added to the left and right of the URL and other values in the report.</p> <p>And the report looks now nicer, right?</p> <p>See <a href="https://github.com/selenide/selenide/issues/1764">issue 1764</a> and <a href="https://github.com/selenide/selenide/pull/1767">PR 1767</a>.</p> <p><br /></p> <h2 id="upgraded-to-selenium-413">Upgraded to Selenium 4.1.3</h2> <p>See <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">Selenium changelog</a> and <a href="https://github.com/selenide/selenide/pull/1759">PR 1759</a>.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/04/07/selenide-6.4.0/ https://selenide.org/2022/04/07/selenide-6.4.0 2022-04-07T00:00:00+00:00 Released Selenide 6.3.0 <p><br /></p> <h1 id="hello-people">Hello, people!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/143?closed=1">Selenide 6.3.0</a>.</p> <p>Let’s review what’s changed?</p> <p><br /></p> <h1 id="added-methods-switchtoframetimeout-with-custom-timeout">Added methods <code class="language-plaintext highlighter-rouge">switchTo().frame(timeout)</code> with custom timeout</h1> <p>Selenide has methods <code class="language-plaintext highlighter-rouge">switchTo().frame(name)</code> for switching between frames. As usually, with built-in waiting and other bonuses.<br /> But what if the frame is being loaded longer than the default timeout (4 seconds)?</p> <p>Now you can pass an additional parameter <code class="language-plaintext highlighter-rouge">Duration</code> - the timeout for switching into the frame:</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">// waits up to default timeout (4 seconds)</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">// waits up to 6 seconds</span> </code></pre></div></div> <p>Thanks to <a href="https://github.com/donesvad">@donesvad</a> for <a href="https://github.com/selenide/selenide/pull/1722">PR 1722</a>.</p> <p><br /></p> <h1 id="added-selectors-bytagandtext-and-withtagandtext">Added selectors <code class="language-plaintext highlighter-rouge">byTagAndText</code> and <code class="language-plaintext highlighter-rouge">withTagAndText</code></h1> <p>Selenide has method for finding elements by text:</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>But sometimes it’s not enough: if there are multiple elements with given text, and you need to pick one of them by tag.</p> <p>Now Selenide has methods for find by tag and text:</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>Thanks to <a href="https://github.com/maurizio-lattuada">Maurizio Lattuada</a> for <a href="https://github.com/selenide/selenide/issues/1650">issue 1650</a> and <a href="https://github.com/selenide/selenide/pull/1651">PR 1651</a>.</p> <p><br /></p> <h1 id="fixed-bug-in-bytextcaseinsensitive">Fixed bug in <code class="language-plaintext highlighter-rouge">byTextCaseInsensitive</code></h1> <p>Now this selector ignores leading/trailing spaces and newlines in the text (like the canonical <code class="language-plaintext highlighter-rouge">byText</code>).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1723">issue 1723</a> and <a href="https://github.com/selenide/selenide/pull/1724">PR 1724</a>.</p> <p><br /></p> <h1 id="added-records-webdriver-create-and-webdriver-close-to-report">Added records “webdriver create” and “webdriver close” to report</h1> <p>If you use Selenide’s <code class="language-plaintext highlighter-rouge">TextReport</code> or plugin <code class="language-plaintext highlighter-rouge">AllureSelenide</code>, you are used to see the report in the end of every test.<br /> The report shows all the performed steps: open a page - find an element - click etc.</p> <p>Now the report also shows moments when the webdriver was created or closed. This information may be useful when debugging some problems with tests.</p> <p>Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <a href="https://github.com/selenide/selenide/pull/1715">PR 1715</a>.</p> <p><br /></p> <h1 id="fixed-overriding-of-selenium-timeout">Fixed overriding of Selenium timeout</h1> <p>In <a href="/2021/06/08/selenide-5.22.0/">Selenide 5.22.0</a> we introduced a hack for overriding Selenium webdriver timeout from (unreasonably long) 3 hours to 2 minutes.</p> <p>But this hack was broken after upgrade to Selenium 4 (as it always happens to all hacks). Now we reanimated it.</p> <p>Let me remind that Selenide now has the following timeouts in communication between tests and webdriver:</p> <ul> <li>connect timeout - 10 seconds</li> <li>read timeout - 1.5 minutes</li> </ul> <p>See <a href="https://github.com/selenide/selenide/commit/cf02da5">commit cf02da5</a>.</p> <p><br /></p> <h1 id="removed-duplicate-wrapping-of-element-not-found">Removed duplicate wrapping of “Element not found”</h1> <p>See <a href="https://github.com/selenide/selenide/issues/1705">issue 1705</a> and <a href="https://github.com/selenide/selenide/pull/1706">PR 1706</a>.</p> <p><br /></p> <h1 id="added-support-for-authentication-type-bearer">Added support for authentication type <code class="language-plaintext highlighter-rouge">BEARER</code></h1> <p>Selenide has several types of authentication:</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>but only <code class="language-plaintext highlighter-rouge">BASIC</code> did really work. Nobody ever tested the other types. :)</p> <p>And we found that <code class="language-plaintext highlighter-rouge">BEARER</code> type didn’t work. Now we fixed it.</p> <p>This is how you can use it:</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>See <a href="https://github.com/selenide/selenide/pull/1714">PR 1714</a>.</p> <p><br /></p> <h1 id="now-selenide-treats-empty-settings-like-selenideremote-as-undefined">Now Selenide treats empty settings like <code class="language-plaintext highlighter-rouge">selenide.remote</code> as undefined</h1> <p>This change should make devops’ life easier. When configuring CI pipelines and jobs, you often use variables to set Selenide settings:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dselenide.remote=${env.GRID_URL} </code></pre></div></div> <p>If <code class="language-plaintext highlighter-rouge">GRID_URL</code> variable is not set in some environment, Selenide failed because it tried to parse empty url to set <code class="language-plaintext highlighter-rouge">selenide.remote</code>.</p> <p>Now Selenide will treat empty setting like <code class="language-plaintext highlighter-rouge">selenide.remote</code> just undefined and continue work as usually (like <code class="language-plaintext highlighter-rouge">selenide.remote</code> was never set).</p> <p>See <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> for <a href="https://github.com/selenide/selenide/issues/1656">issue 1656</a> and <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1663">PR 1663</a>.</p> <p><br /></p> <h1 id="upgraded-to-selenium-412">Upgraded to Selenium 4.1.2</h1> <p>A heard some users experienced problems with Guava version after upgrading to Selenium 4.1.2. Selenide defined Guava version explicitly, so generally you should not experience such a problem. If you still get something like <code class="language-plaintext highlighter-rouge">NoSuchMethodError</code>, please verify the guava version: pribably it’s overridden by some Gradle or Maven plugin. Please refer the old good <a href="/2020/11/17/why-proxy-does-not-work-in-selenoid/">post about resolving dependencies</a> if needed.</p> <p>See <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG">Selenium changelog</a> and <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>Minor update <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> we restored possibility to use soft asserts in TestNG in methods <code class="language-plaintext highlighter-rouge">@Before*</code> and <code class="language-plaintext highlighter-rouge">@After*</code> (we occasionally forbidden them it Selenide 6.2.0) - see <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> Upgraded from Netty 4.1.73.Final to 4.1.74.Final</li> </ul> <p><br /></p> <h1 id="upd-selenide-632">UPD Selenide 6.3.2</h1> <p>Another update <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> Added a workaround for Selenium bug <a href="https://github.com/SeleniumHQ/selenium/issues/10345">10345</a>, which caused failure of method <code class="language-plaintext highlighter-rouge">FirefoxDriver.close()</code> after upgrading to Firefox 97.</li> <li><a href="https://github.com/selenide/selenide/pull/1736">#1736</a> Upgraded from BrowserUpProxy 2.1.3 to 2.1.4</li> <li><a href="https://github.com/selenide/selenide/pull/1611">#1611</a> Upgraded Java version from 8 to 17.</li> </ul> <p>Let me explain the Java update. Now Selenide project is built with Java 17, but the generated binaries <code class="language-plaintext highlighter-rouge">selenide-*.jar</code> are targeted to Java 8. This allows</p> <ul> <li>Selenide developers use all the latest Java features, and</li> <li>Selenide users still run their tests on Java 8 (though we recommend upgrading Java, of course).</li> </ul> <p>It became possible thanks to the tool <a href="https://github.com/bsideup/jabel">Jabel</a> and personally <a href="https://github.com/bsideup"> Sergei Egorov</a> who came up with this elegant hack (and also involved in <a href="https://github.com/testcontainers/testcontainers-java">TestContainers</a> and <a href="https://www.atomicjar.com/">AtomicJar</a>).</p> <p><br /></p> <h1 id="upd-selenide-633">UPD Selenide 6.3.3</h1> <p>One more minor update <a href="https://github.com/selenide/selenide/milestone/148?closed=1">Selenide 6.3.3</a>:</p> <ul> <li>#1737 allow overriding Firefox preferences for downloading files</li> <li>#1740 upgraded to WebDriverManager 5.1.0</li> </ul> <p><br /></p> <h1 id="upd-selenide-634">UPD Selenide 6.3.4</h1> <p>Another update <a href="https://github.com/selenide/selenide/milestone/149?closed=1">Selenide 6.3.4</a>:</p> <ul> <li>#1746 show the expected attribute when <code class="language-plaintext highlighter-rouge">$.shouldHave(attribute(...))</code> fails.</li> <li>#1748 fixed module name in generated Selenide binaries</li> </ul> <p><br /></p> <h1 id="upd-selenide-635">UPD Selenide 6.3.5</h1> <p>Another update <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> fix file download via proxy in case of encoded response - see <a href="https://github.com/selenide/selenide/pull/1756">PR #1756</a></li> </ul> <p><br /></p> <h1 id="news">News</h1> <ul> <li>We created a group in LinkedIn: <a href="https://www.linkedin.com/groups/9154550/">Selenide User Group</a>!</li> <li>Post by Miklós Szeles <a href="https://www.linkedin.com/pulse/selenium-selenide-mikl%25C3%25B3s-szeles/">Selenium or Selenide?</a></li> <li>Selenide got to compilation <a href="https://qameta.io/blog/5-testing-automation-tools/">5 Testing Automation Tools</a> in Qameta Software blog (Allure Report maintainers)</li> <li>Selenide got to compilation <a href="https://hackernoon.com/top-java-libraries-for-automation-testing-in-2022">Top Java Libraries for Automation Testing in 2022</a></li> <li>Post <a href="https://blog.knoldus.com/selenide-concise-ui-test-in-java/">about Selenide</a> in Knoldus blog</li> <li>Post <a href="https://medium.com/@gaveen0513/selenide-the-software-that-doesnt-need-documentation-cda8535cb7e6">The software that doesn’t need documentation</a> by Gaveen Nayanajith</li> <li>Post <a href="https://mszeles.com/selenide-i-think-this-is-the-beginning-of-a-beautiful-friendship">the beginning of a beautiful friendship</a> by Miklós Szeles</li> </ul> <p><br /></p> <h4 id="i-think-this-is-the-beginning-of-a-beautiful-friendship">I think this is the beginning of a beautiful friendship!</h4> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/02/07/selenide-6.3.0/ https://selenide.org/2022/02/07/selenide-6.3.0 2022-02-07T00:00:00+00:00 Released Selenide 6.2.0 <p><br /></p> <h1 id="happy-new-year-people">Happy New Year, people!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/140?closed=1">Selenide 6.2.0</a>.</p> <p>Let’s look into this year’s first release.</p> <p>Pour some tea and let’s go!</p> <p><br /></p> <h1 id="added-link-click-to-see-difference-for-most-of-selenide-assertion-errors">Added link “&lt;Click to see difference&gt;” for most of Selenide assertion errors</h1> <p>As you remember, in <a href="/2021/09/28/selenide-5.25.0/">Selenide 5.25.0</a> we added Test4J support. As a result, Selenide assertion errors got a link “&lt;Click to see difference&gt;” in IDE which allows to easily compare expected and actual values in case of test failure.</p> <p>At that moment we didn’t have enough time to refactor all the assertion errors, and some of them didn’t get the link. <br /> Now we added the link to all the errors.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1589">issue 1589</a> and <a href="https://github.com/selenide/selenide/pull/1676">PR 1676</a>.</p> <p><br /></p> <h1 id="replaced-iterator-by-asdynamiciterable-and-asfixediterable">Replaced <code class="language-plaintext highlighter-rouge">$$.iterator()</code> by <code class="language-plaintext highlighter-rouge">$$.asDynamicIterable()</code> and <code class="language-plaintext highlighter-rouge">$$.asFixedIterable()</code></h1> <blockquote> <p><em>Wow, what an old sore we have fixed!</em></p> </blockquote> <p>Selenide has method <code class="language-plaintext highlighter-rouge">$$</code> for a collection of web elements. It returns instance of class <code class="language-plaintext highlighter-rouge">ElementsCollection</code>.<br /> Initially, it should have only one method <code class="language-plaintext highlighter-rouge">$$.shouldHave(&lt;condition&gt;)</code>. When you want to verify the collection - pass it the needed condition. If you didn’t find a needed condition - create your own, it’s easy.</p> <h3 id="the-mistake">The mistake</h3> <p>But I did a mistake at that moment. I inherited class <code class="language-plaintext highlighter-rouge">ElementsCollection</code> from <code class="language-plaintext highlighter-rouge">AbstractList&lt;SelenideElement&gt;</code>. I later regretted this decision many times.</p> <p>What happened, you may ask? People started abusing the collections.</p> <ul> <li>For example, they wanted to iterate the collection elements (and it <em>occasionally</em> was possible because of extending <code class="language-plaintext highlighter-rouge">AbstractList</code>).</li> <li>they expected that the collection elements will be reloaded during the iteration (because Selenide had been updating all other web elements during checks &amp; re-tries).</li> <li>In addition to the questionable test design, this also caused performance problems, because at every iteration step Selenide must reload the entire collection. And it can be slow, especially if there are many elements in the collection, or the collection is being filtered: <code class="language-plaintext highlighter-rouge">$$("div").filter(visible).filterBy(text("Hello"))</code>.</li> </ul> <h3 id="the-dilemma">The dilemma</h3> <p>The dilemma turned out:</p> <ul> <li>some people wanted Selenide to reload collection elements during the iteration (because it helps to avoid <code class="language-plaintext highlighter-rouge">StaleElementException</code> etc when the elements appear and disappear during the iteration);</li> <li>other people didn’t want Selenide to reload the elements because it makes tests faster on big collections.</li> </ul> <p>Whichever option we choose, we won’t please everyone.</p> <h3 id="the-solution">The solution</h3> <p>Finally, we figured out how to solve this dilemma. We <strong>deprecated</strong> all these methods like <code class="language-plaintext highlighter-rouge">$$.iterator()</code> which were occasionally inherited from <code class="language-plaintext highlighter-rouge">AbstractList</code>. As a replacement, we suggest two new methods. You can choose one of them depending on your needs:</p> <ul> <li><code class="language-plaintext highlighter-rouge">$$.asDynamicIterable()</code> - reload the elements during the iteration. Might be slow on big collections.</li> <li><code class="language-plaintext highlighter-rouge">$$.asFixedIterable()</code> - doesn’t reload the elements. May be faster, but will not get updates if elements appear/disappear during the iteration.</li> </ul> <h3 id="the-recommendation">The recommendation</h3> <p>Which one I recommend to you, you may ask?</p> <p>NONE!</p> <p>In the end, let me remind that well-designed test should not have loops, ifs etc. You must know how many elements are expected, and which properties they are expected to have. <em>Just verify those properties.</em></p> <p>Instead of iteration, it’s always better to create your <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/CustomCollectionConditionTest.java">custom <code class="language-plaintext highlighter-rouge">CollectionCondition</code></a>. It’s easy.</p> <p>See <a href="https://github.com/selenide/selenide/issues/797">issue 797</a> and <a href="https://github.com/selenide/selenide/pull/1688">PR 1688</a>.</p> <p><br /></p> <h1 id="fixed-softassert-to-avoid-failing-a-test">Fixed SoftAssert to avoid failing a test</h1> <p>in a rather rare situation:</p> <ol> <li>when soft assert listener/rule/extension is added to a test, but</li> <li>soft asserts are <strong>disabled</strong>, and</li> <li>there was an assertion error raised and caught during the test execution (try/catch).</li> </ol> <p>It sounds like a design smell. Probably such test should be fixed. :(</p> <p>But still, we fixed the Selenide soft assert listener. Now it doesn’t fail the test if soft assertions are disabled.</p> <blockquote> <p><em>Uh, well, you sometimes throw up unexpected problems, dear users … :)</em></p> </blockquote> <p>See <a href="https://github.com/selenide/selenide/issues/1646">issue 1646</a> and <a href="https://github.com/selenide/selenide/pull/1680">PR 1680</a>.</p> <p><br /></p> <h1 id="we-fixed-soft-asserts-to-include-all-the-errors">We fixed soft asserts to include all the errors</h1> <p>There was one more rare problem with soft asserts. Image the situation:</p> <ol> <li>You have soft asserts enabled;</li> <li>During the test execution, some Selenide assertion errors were raised (and caught by the listener);</li> <li>But there was also some other assertion error (like <code class="language-plaintext highlighter-rouge">NPE</code> or <code class="language-plaintext highlighter-rouge">assertEquals(2, 3)</code>).</li> </ol> <p>In this case, Selenide soft assert listener failed the test (which is correct), but showed only Selenide assertion errors (#2) and lost the “other” error (#3).</p> <p>Now the listener got smarter: it merges all the assertion errors.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1661">issue 1661</a> and <a href="https://github.com/selenide/selenide/pull/1679">PR 1679</a>.</p> <p><br /></p> <h1 id="we-added-locator-to-some-of-selenide-assertion-errors">We added locator to some of Selenide assertion errors</h1> <p>Another small improvement.</p> <p>We added a locator to some of Selenide assertion errors.<br /> For example, if previously you could see an error like this:</p> <blockquote> <p>“Invalid element state: Cannot change invisible element”</p> </blockquote> <p>then now you will see the improved error message:</p> <blockquote> <p>“Invalid element state [.btn.btn-primary]: Cannot change invisible element”</p> </blockquote> <p><br /></p> <h1 id="upgraded-browserupproxy-from-212-to-213">Upgraded BrowserUpProxy from 2.1.2 to 2.1.3</h1> <p>Note that version 2.1.3 is a <strong>fork</strong> of the original BrowserUpProxy. The authors announced the end of support, and the volunteers took over and released version 2.1.3 <a href="https://github.com/browserup/browserup-proxy/issues/388#issuecomment-1004097733">from the fork</a>.</p> <p>We will monitor the situation. Ideally, it would be great to switch from BrowserUpProxy to mitmproxy. Are there volunteers?</p> <p>See <a href="https://github.com/selenide/selenide/pull/1678">PR 1678</a>.</p> <p><br /></p> <h1 id="upgraded-testng-from-740-to-75">Upgraded TestNG from 7.4.0 to 7.5</h1> <p>Changelist is quite <a href="https://github.com/cbeust/testng/blob/7.5/CHANGES.txt">impressive</a>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1682">PR 1682</a>.</p> <p><br /></p> <p>Happy New Year, my friends!<br /> I wish you stable tests and beautiful reports!</p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2022/01/10/selenide-6.2.0/ https://selenide.org/2022/01/10/selenide-6.2.0 2022-01-10T00:00:00+00:00 Released Selenide 6.1.1 <p><br /></p> <h1 id="tere-hommikust">TERE HOMMIKUST!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/141?closed=1">Selenide 6.1.1</a>.</p> <p>In this tiny release we fixed a bunch of problems with browser settings.<br /> These problems arose after upgrading to Selenium 4 where <code class="language-plaintext highlighter-rouge">ChromeOptions</code> and other <code class="language-plaintext highlighter-rouge">Capabilities</code> were refactored.</p> <p>Now we plunged into the topic and fixed all the problems at once.</p> <p><br /></p> <h1 id="early-conflicts-detection">Early conflict detection</h1> <p>If you try to open a Chrome with Firefox settings:</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>then Selenide 5.x did show a clear error message, but Selenide 6.0.x didn’t.<br /> Now we have restored the swearing, and you will see the old good</p> <blockquote> <p>IllegalArgumentException: Conflicting browser name: ‘chrome’ vs. ‘firefox’</p> </blockquote> <p>See <a href="https://github.com/selenide/selenide/issues/1591">issue 1591</a> and <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p> <p><br /></p> <h1 id="merging-chrome-arguments">Merging of chrome arguments</h1> <p>Say, if you want to run Chrome in full screen mode or setup its language:</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>then these settings were lost in Selenide 6.0.x<br /> Now we have restored them.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1626">issue 1626</a>, <a href="https://github.com/selenide/selenide/issues/1630">issue 1630</a> and <a href="https://github.com/selenide/selenide/issues/1631">issue 1631</a>.</p> <p>The fix was in <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p> <p><br /></p> <h1 id="wrapping-browser-capabilities">IMPORTANT</h1> <p>If you used <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities</code>, then with a high probability you wrapped it into <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>Now you have to <strong>simplify your code</strong>, to avoid loosing of your settings:</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">We changed the parameter type of <code class="language-plaintext highlighter-rouge">WebDriverProvider</code></h1> <p>… from <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> to just <code class="language-plaintext highlighter-rouge">Capabilities</code>.</p> <p>For you, almost nothing changes. If you use <code class="language-plaintext highlighter-rouge">WebDriverProvider</code> in your tests, simply change <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> to<code class="language-plaintext highlighter-rouge"> Capabilities</code> and everything will work as previously.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1642">PR 1642</a>.</p> <p><br /></p> <p><em>More capabilities for the Capabilities God!</em></p> <p><br /></p> <h1 id="upd-selenide-6.1.2">UPD Selenide 6.1.2</h1> <p>We released Selenide 6.1.2 with upgrade to Selenium 4.1.1. It has several remarkable bugfixes.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/11/24/selenide-6.1.1/ https://selenide.org/2021/11/24/selenide-6.1.1 2021-11-24T00:00:00+00:00 Released Selenide 6.1.0 <p><br /></p> <h1 id="tere">TERE!</h1> <p><br /> We released <a href="https://github.com/selenide/selenide/milestone/137?closed=1">Selenide 6.1.0</a>.</p> <p><br /></p> <h1 id="added-support-for-selenideproperties">Added support for <code class="language-plaintext highlighter-rouge">selenide.properties</code></h1> <p>Now Selenide can read its settings from file <code class="language-plaintext highlighter-rouge">selenide.properties</code> if it’s found in classpath.</p> <p>NB! I personally still don’t find it really useful. It’s easier to just set settings</p> <ol> <li>right in code: <code class="language-plaintext highlighter-rouge">Configuration.timeout = 8000;</code></li> <li>or via system properties: <code class="language-plaintext highlighter-rouge">-Dselenide.timeout=8000</code>.</li> </ol> <p>Please, don’t rush generating these files. Use <code class="language-plaintext highlighter-rouge">selenide.properties</code> only if you have a good reason, and not simply because it is now fashionable or “beautiful”.</p> <p>Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <a href="https://github.com/selenide/selenide/pull/1601">PR 1601</a>.</p> <p><br /></p> <h1 id="added-possibility-for-fine-tuning-the-proxy">Added possibility for fine tuning the proxy</h1> <p>As you know, Selenide can run its embedded proxy server. It makes possible downloading of files, listening/intercepting requests etc. But capabilities of configuring the proxy were quite limited by now: only <code class="language-plaintext highlighter-rouge">Configuration.proxyHost</code> and <code class="language-plaintext highlighter-rouge">Configuration.proxyPort</code>.</p> <p>Now you can get the instance of BrowserModProxy and set it up as you wish before opening a browser.</p> <p>NB! Please don’t overuse it. It’s very easy to shoot yourself in the foot via re-configuring the proxy.</p> <p>And don’t forget: if your settings really helped you, then maybe they will help others as well. Tell us what you set up there - maybe we should make these proxy settings default in Selenide?</p> <p>See <a href="https://github.com/selenide/selenide/issues/1561">issue 1561</a>. Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1620">PR 1620</a>.</p> <p><br /></p> <h1 id="added-a-workaround-against-random-noclassdeffounderror-in-webdriverexception">Added a workaround against random <code class="language-plaintext highlighter-rouge">NoClassDefFoundError</code> in <code class="language-plaintext highlighter-rouge">WebDriverException</code>.</h1> <p>There is <a href="https://github.com/SeleniumHQ/selenium/issues/9784">a bug in Selenium</a> which is not entirely fixed yet. But you will not see it anymore because we added a workaround in Selenide code. :)</p> <p>See <a href="https://github.com/selenide/selenide/commit/2eff0307e3a">the workaround</a>.</p> <p><br /></p> <h1 id="changed-parameter-type-for-method-selenideconfigbrowsercapabilities">Changed parameter type for method <code class="language-plaintext highlighter-rouge">SelenideConfig.browserCapabilities()</code></h1> <p>… from <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> to <code class="language-plaintext highlighter-rouge">MutableCapabilities</code>.</p> <p>It allows to simplify your code. You don’t need to wrap <code class="language-plaintext highlighter-rouge">ChromeOptions</code> into <code class="language-plaintext highlighter-rouge">DesiredCapabilities</code> anymore. More about simplification of capabilities will be in the next release <code class="language-plaintext highlighter-rouge">Selenide 6.1.1</code>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1637">PR 1637</a>.</p> <p><br /></p> <h1 id="upgraded-to-selenium-webdriver-410">Upgraded to Selenium Webdriver 4.1.0</h1> <p>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1638">PR 1638</a>.</p> <p><br /></p> <h1 id="removed-method-shadowroot">Removed method <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></h1> <p>This method got broken after upgrading to Chrome 96, and probably will be broken in other browsers soon.<br /> Taking into account that</p> <ol> <li>It’s hard to fix this method, and</li> <li>This method <a href="https://github.com/selenide/selenide/issues/1515#issuecomment-894476289">is not really useful</a> since there are <a href="/2020/03/18/selenide-5.10.0/">better ways</a> to find elements in Shadow DOM. we decided to just delete this method. Easy! :)</li> </ol> <p>See <a href="https://github.com/selenide/selenide/issues/1640">issue 1640</a> and <a href="https://github.com/selenide/selenide/pull/1641">PR 1641</a>.</p> <p><br /></p> <h1 id="news">News</h1> <ul> <li>A <a href="https://www.linkedin.com/posts/qametasoftware_propeller-testops-hackathon-activity-6869263215520550912-m2_-/">QA/TestOps Hackaton</a> by PropellerAds and Qameta Software. Register!</li> <li><a href="https://www.lambdatest.com/selenium-automation-testing-with-selenide-framework">How to use Selenide with Lambdatest service</a></li> <li>A post <a href="https://mbbaig.blog/selenide-webdriverfactory/">Selenide - Create a Custom WebDriver</a> by Boris Bay</li> <li>Wow! <a href="https://www.linkedin.com/feed/update/urn:li:activity:6867477909766979584/">LinkedIn runs course on Selenide</a> and even gives nice diplomas! :)</li> </ul> <p><br /></p> <h1 id="selenide-downloads-statistics">Selenide downloads statistics</h1> <center> <img src="/images/2021/11/selenide.downloads.png" width="800" /> </center> <p><br /> In October we made a big leap and exceeded <strong>280 thousand downloads</strong> per month.</p> <p><em>More downloads for the Downloads God!</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/11/23/selenide-6.1.0/ https://selenide.org/2021/11/23/selenide-6.1.0 2021-11-23T00:00:00+00:00 Released Selenide 6.0.1 <p><br /></p> <h1 id="happy-birthday-selenide">Happy Birthday, Selenide!</h1> <p>Wow! It’s hard to believe, but today Selenide got … 10 years old!</p> <p>This day 10 years ago was created <a href="https://github.com/selenide/selenide/commit/3716078fc7fda8c5da01d871882d513cbd97cd0e">the first commit</a> to Selenide github repository.</p> <p>Many thanks to everyone who participated in the project, committed, reported bugs, suggested ideas, answered questions in the forums, criticized and talked about selenide at conferences and meetups. And who just decided to use Selenide in their projects. You are all part of this movement.</p> <p><br /> In honor of the anniversary, we released major version <a href="https://github.com/selenide/selenide/milestone/136?closed=1">Selenide 6.0.1</a>.</p> <p><br /></p> <h1 id="upgraded-to-selenium-webdriver-400">Upgraded to Selenium Webdriver 4.0.0</h1> <p>Nothing really should change for you until you are using Selenium Grid. Everything more or less works as before. Well, some classes were renamed or deprecated - it seems not scary.</p> <p>Selenium 4 also brings <a href="https://www.browserstack.com/guide/selenium-4-features">new features</a> (like CDP and Relative locators), but haven’t introduced any special support for them yet. Can work on it in next versions if needed.</p> <p>See <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> and <a href="https://github.com/selenide/selenide/pull/1617">PR 1617</a>.</p> <p><br /></p> <h1 id="splitted-selenide-to-several-artifacts">Splitted Selenide to several artifacts</h1> <p>Before now, the whole Selenide code was published as a single artifact: <code class="language-plaintext highlighter-rouge">selenide-5.25.0.jar</code>.<br /> And you had to add other dependencies like JUnit or TestNG, and probably BrowserMobProxy.</p> <p>Now we made it easier for you (especially with proxy).</p> <ol> <li>For JUnit5 users the dependency will not change: <br /><code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide:6.0.1)</code></li> <li>JUnit4 users should use <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-junit4:6.0.1)</code></li> <li>TestNG users should use <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-testng:6.0.1)</code></li> <li>If you want to use proxy, it’s enough to add dependency <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-proxy:6.0.1)</code> - and forget about all these BrowserUpProxy, Netty etc.</li> <li>And if you happen to be a radical hater of static methods, now you can also be happy by declaring <br /> <code class="language-plaintext highlighter-rouge">testImplementation("com.codeborne:selenide-core:6.0.1)</code> - and then you will only have <code class="language-plaintext highlighter-rouge">SelenideDriver</code> without <code class="language-plaintext highlighter-rouge">Selenide.*</code> static methods.</li> </ol> <p>See <a href="https://github.com/selenide/selenide/pull/1612">PR 1612</a>.</p> <p><br /></p> <h1 id="removed-lots-of-deprecated-code">Removed lots of deprecated code</h1> <p><a href="https://github.com/selenide/selenide/pull/1607/files">Here</a> you can find what exactly was removed and how can you replaced it. <br /> In particular,</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> or <code class="language-plaintext highlighter-rouge">Selenide.closeWindow()</code> <br /></li> </ul> <p>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1607/files">PR 1607</a> and <a href="https://github.com/selenide/selenide/pull/1609">PR 1609</a>.</p> <p><br /></p> <h1 id="remove-support-for-browser-legacy_firefox">Remove support for browser “legacy_firefox”</h1> <p>It was an old webdriver that worked with Firefox 52 and earlier. I guess it’s not used anymore.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1610">PR 1610</a>.</p> <p><br /></p> <h1 id="removed-some-old-settings">Removed some old settings</h1> <h3 id="configurationstartmaximized"><code class="language-plaintext highlighter-rouge">Configuration.startMaximized</code></h3> <p>We think this is bad practice, because the size of the browser window depends on the current environment, which can lead to flaky tests. We recommend to use <code class="language-plaintext highlighter-rouge">Configuration.browserSize</code> instead (default value is <code class="language-plaintext highlighter-rouge">1366x768</code>).</p> <h3 id="configurationversatilesetvalue"><code class="language-plaintext highlighter-rouge">Configuration.versatileSetValue</code></h3> <p>Most probably you didn’t use it because it was <code class="language-plaintext highlighter-rouge">false</code> by default. <br /> Now for selecting a value from <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> or <code class="language-plaintext highlighter-rouge">&lt;input type=radio&gt;</code> you can use plain old methods <code class="language-plaintext highlighter-rouge">$.selectOptionByValue()</code> and <code class="language-plaintext highlighter-rouge">$.selectRadio()</code>.</p> <p>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1619">PR 1619</a>.</p> <p><br /></p> <h1 id="fixed-method-selenidesleepn">Fixed method <code class="language-plaintext highlighter-rouge">Selenide.sleep(N)</code></h1> <p>It turns out that the standard Java method <code class="language-plaintext highlighter-rouge">Thread.sleep (N)</code> does not necessarily sleep exactly N ms, it can wake up earlier. And this can cause flaky tests if your expected a certain pause.</p> <p>Now method <code class="language-plaintext highlighter-rouge">Selenide.sleep(N)</code> is guaranteed to wait at least given number of milliseconds.</p> <p>See <a href="https://github.com/selenide/selenide/blob/b05d53dfb794ee02e795587867c6ec8022171040/statics/src/main/java/com/codeborne/selenide/Selenide.java#L258">implementation</a>.</p> <p><br /></p> <h1 id="added-methods-for-addingremoving-webdriverlistener">Added methods for adding/removing <code class="language-plaintext highlighter-rouge">WebDriverListener</code></h1> <p>Selenide has method for adding <code class="language-plaintext highlighter-rouge">WebDriverEventListener</code>, but this class was replaced by <code class="language-plaintext highlighter-rouge">WebDriverListener</code> in Selenium 4. <br /> So we have to support both.</p> <p><br /></p> <p>See <a href="https://github.com/selenide/selenide/issues/1615">issue 1615</a> and <a href="https://github.com/selenide/selenide/pull/1616">PR 1616</a>.</p> <p><br /></p> <h1 id="changed-signature-of-method-conditionapply">Changed signature of method <code class="language-plaintext highlighter-rouge">Condition.apply</code></h1> <p>It doesn’t concern you until you created your custom Conditions.</p> <p>Before now, class <code class="language-plaintext highlighter-rouge">Condition</code> had method <code class="language-plaintext highlighter-rouge">apply</code> which returned <code class="language-plaintext highlighter-rouge">boolean</code>:</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>Now you need to rename it to <code class="language-plaintext highlighter-rouge">check</code> and return <code class="language-plaintext highlighter-rouge">CheckResult</code> instead of <code class="language-plaintext highlighter-rouge">boolean</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>This <code class="language-plaintext highlighter-rouge">CheckResult</code> contains not only flag “condition met / not met”, but also a actual value at that moment. <br /> It allows Selenide to generate a better error message in case of test failure.</p> <p>P.S. Though, old method <code class="language-plaintext highlighter-rouge">apply</code> is not removed, but just marked as deprecated. So you can postpone this refactoring for some time.</p> <p>See <a href="https://github.com/selenide/selenide/issues/217">issue 217</a>, <a href="https://github.com/selenide/selenide/pull/1586">PR 1586</a> and <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>We released <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v2.0.1"><code class="language-plaintext highlighter-rouge">selenide-selenoid:2.0.1</code></a> with upgrade to Selenide 6.0.1</p> <p><br /></p> <h1 id="selenide-appium">selenide-appium</h1> <p>It seems that Appium doesn’t support Selenium 4 yet, so we cannot upgrade <code class="language-plaintext highlighter-rouge">selenide-appium</code> yet. Stay tuned.</p> <p><br /></p> <h1 id="upd-selenide-602">UPD Selenide 6.0.2</h1> <p>We found a problem with projects using TestNG, released a hotfix 6.0.2.</p> <p>See <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>It appears that Maven can fetch an older version of <code class="language-plaintext highlighter-rouge">selenium-api-3*.jar</code> if it’s found in a dependency tree (usually in BrowserUpProxy or Allure transitive dependencies).</p> <p>I personally find this Maven behaviour misleading and just wrong. Maven could pick up a newer version.<br /> But since people often experience this problem, we decided to add a workaround on Selenide side.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1625">workaround 1625</a></p> <p><br /></p> <h1 id="what-to-read-about-selenium-4">What to read about 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/">Updated Selenium website</a></li> </ul> <p><br /></p> <h1 id="other-news">Other news</h1> <ul> <li>Aerokube released <a href="https://github.com/aerokube/lightning-java">alternative WebDriver client</a>. In theory, Selenide could now use it under the hood instead of Selenium Webdriver. Sounds tempting?</li> <li>I haven’t figured it out yet, but people seem to be praising it: <a href="https://github.com/markhobson/docker-maven-chrome">some Docker images for Selenium</a></li> </ul> <p><br /></p> <h1 id="selenide-download-statistics">Selenide download statistics</h1> <center> <img src="/images/2021/10/selenide.downloads.png" width="800" /> </center> <p><br /> We crossed <strong>255K downloads</strong> per months!</p> <p>What is waiting us in next 10 years?</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/10/25/selenide-6.0.1/ https://selenide.org/2021/10/25/selenide-6.0.1 2021-10-25T00:00:00+00:00 Released Selenide 5.25.0 <p>Good evening!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/131?closed=1">Selenide 5.25.0</a>.</p> <blockquote> <p><strong>I strongly recommend you to upgrade</strong>,<br /> because there are even more changes coming with next releases, including the full release of Selenium 4.<br /> If you don’t upgrade now, it will be much more difficult to clear the rubble later!</p> </blockquote> <p>This is quite a big release with a bunch of changes, so grab some popcorn and make some tea. Let’s go!</p> <ul> <li><a href="#support-webdriver-4.0.0.RC1">Support for Selenium Webdriver 4.0.0 RC1</a></li> <li><a href="#opentest4j">Support for OpenTest4j</a></li> <li><a href="#stacktrace-in-soft-asserts">Stacktrace below every error in soft asserts</a></li> <li><a href="#get-shadow-root">Added method <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></a></li> <li><a href="#get-ancestor">Added method <code class="language-plaintext highlighter-rouge">$.ancestor()</code></a></li> <li><a href="#enrich-ancestor">Enriched methods <code class="language-plaintext highlighter-rouge">$.closest()</code> and <code class="language-plaintext highlighter-rouge">$.ancestor()</code></a></li> <li><a href="#fixed-element-screenshot">Fixed method <code class="language-plaintext highlighter-rouge">$.screenshot()</code> on Mac</a></li> <li><a href="#actual-value-at-the-moment-of-last-check">Now Selenide reports an exact text at the moment of test failure</a></li> <li><a href="#empty-text-not-allowed">We now forbid an empty argument in method <code class="language-plaintext highlighter-rouge">$.matchText("")</code></a></li> <li><a href="#check-webdriver-title">Check <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="#statistics">Selenide downloads statistics</a></li> <li><a href="#selenide-anniversary">Selenide anniversary</a></li> </ul> <p><br /></p> <h1 id="support-webdriver-4.0.0.RC1">Support for Selenium Webdriver 4.0.0 RC1</h1> <p>We released two builds of Selenide 5.25.0: regular and hipster one. <br /> You can pick:</p> <table> <tbody> <tr> <td>either</td> <td><code class="language-plaintext highlighter-rouge">com.codeborne:selenide:5.25.0</code></td> <td>(with <code class="language-plaintext highlighter-rouge">Selenium 3.x</code>),</td> </tr> <tr> <td>or</td> <td><code class="language-plaintext highlighter-rouge">com.codeborne:selenide:5.25.0-selenium-4.0.0-rc-1</code></td> <td>(with <code class="language-plaintext highlighter-rouge">Selenium 4.0.0 RC1</code>).</td> </tr> </tbody> </table> <p><br /></p> <h1 id="opentest4j">We added support for OpenTest4j</h1> <p>If you haven’t heard yet, <a href="https://github.com/ota4j-team/opentest4j">OpenTest4j</a> is a small library for assertion errors created by JUnit 5 team. The idea is that:</p> <ul> <li>all testing frameworks should throw these errors, and</li> <li>all IDEs should support them.</li> </ul> <p>Yet another standard, like webdriver. :) All major players like JUnit, TestNG, AssertJ, IDEA and Eclipse already supported opentest4j, and now does Selenide too.</p> <p><strong>What will change for you personally:</strong><br /> When your UI fails (well, <a href="https://github.com/selenide/selenide/issues/1589">almost always</a>), IDEA will show a nice link <code class="language-plaintext highlighter-rouge">&lt;Click to see difference&gt;</code> below. By clicking it you can view a nice DIFF in a separate IDEA dialog. Very convenient for debugging.</p> <p>See <a href="https://github.com/selenide/selenide/issues/969">issue 969</a> and <a href="https://github.com/selenide/selenide/pull/1545">PR 1545</a>.</p> <p>NB! If you use directly some of Selenide assertion errors (inherit or throw), you will need to update your code a bit.<br /> We slightly change signature of their constructors:</p> <ul> <li>flipped “expected” and “actual” arguments</li> <li>removed unneeded “driver” argument from most constructors</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">Now we show stacktrace below every error in soft asserts</h1> <p>Until now, Selenide soft asserts showed only one stack trace in the end of all errors. It allows to keep the total error message (relatively) short.<br /> But in practice it’s convenient to see a separate stacktrace for every error. It allows to quickly click the needed line and get to the right place in the code.</p> <p>Now you will see separate under every error. I personally don’t like that the error message has become much longer, but usually there shouldn’t be too many of errors, right? ;)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1543">issue 1543</a> and <a href="https://github.com/selenide/selenide/pull/1545">PR 1545</a></p> <p><br /></p> <h1 id="get-shadow-root">We added method <code class="language-plaintext highlighter-rouge">$.shadowRoot()</code></h1> <p>Until now, you could only search elements <em>inside of</em> shadow root, and now you can get the shadow root itself.<br /> Though, there is still little benefit from this because search within this element is not supported (by browsers or web drivers, I am not sure).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1515">issue 1515</a> and <a href="https://github.com/selenide/selenide/pull/1517">PR 1517</a>.</p> <p><br /></p> <h1 id="get-ancestor">We added method <code class="language-plaintext highlighter-rouge">$.ancestor()</code></h1> <p>It’s an alias for existing method <code class="language-plaintext highlighter-rouge">$.closest()</code>. The name <code class="language-plaintext highlighter-rouge">closest</code> was originally from JQuery (which is old!), while <code class="language-plaintext highlighter-rouge">ancestor</code> should be more clear because it’s a standard XPath term.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1556">issue 1556</a>.<br /> Thanks to <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> for <a href="https://github.com/selenide/selenide/pull/1567">PR 1567</a>.</p> <p><br /></p> <h1 id="enrich-ancestor">We enriched methods <code class="language-plaintext highlighter-rouge">$.closest()</code> and <code class="language-plaintext highlighter-rouge">$.ancestor()</code></h1> <p>Until now, this method could only search element by tag or class name:</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>Now it can search by attributes:</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>Thanks to <a href="https://github.com/plagov">Vitali Plagov</a> for <a href="https://github.com/selenide/selenide/pull/1554">PR 1554</a></p> <p><br /></p> <h1 id="fixed-element-screenshot">We fixed method <code class="language-plaintext highlighter-rouge">$.screenshot()</code> on Mac</h1> <p>For a long time, Selenide has method <code class="language-plaintext highlighter-rouge">$.screenshot()</code> to take screenshot of some element (instead of a full screen).<br /> We found that this method worked incorrectly on MacBooks because of Retina display.</p> <p>Now we remove the incorrect custom code from Selenide and started using a standard webdriver method that was created meanwhile. We checked it works in at least Chrome, Firefox and Edge.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1571">issue 1571</a> and <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">Now Selenide reports an exact text at the moment of test failure</h1> <p>Wow, what old issues we sometimes fix! This ticket was registered already on <em>September 6, 2015</em>!</p> <p><strong>In general, the story is like this.</strong></p> <p>Let’s assume you have a check in test:</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>And a standard timeout 4 seconds. Here is one possible scenario:</p> <ol> <li>during 4 seconds, Selenide checks the element text, but it’s wrong all the time (say, “Goodbye”).</li> <li>timeout expires,</li> <li>selenide decides to throw an assertion error,</li> <li>composes an error message, including actual element text.</li> <li>at this moment the element text changes. Say, to “Hello”.</li> </ol> <p>It happens very rarely, but when it happens, it may confuse a lot.<br /> You are seeing an error message and feel totally confused:</p> <blockquote> <p>Text didn’t match. Expected text: “Hello”, actual text: “Hello”.</p> </blockquote> <p>Now Selenide remembers <strong>exactly the text</strong> at the moment of last check and appends it to the error message.</p> <p>P.S. In next releases, we are going to improve this mechanism even more. We will show the history of element texts during those unfortunate 4 seconds. It may help debugging some tricky cases with flaky tests etc.</p> <p>See <a href="https://github.com/selenide/selenide/issues/217">issue 217</a> and <a href="https://github.com/selenide/selenide/pull/1566">PR 1566</a>.<br /> Thanks to <a href="https://github.com/fokinp">Pavel Fokin</a> for <a href="https://github.com/selenide/selenide/pull/1313">PR 1313</a>.</p> <p><br /></p> <h1 id="empty-text-not-allowed">We now forbid an empty argument in method <code class="language-plaintext highlighter-rouge">$.matchText("")</code></h1> <p>There is a method for checking element text with a regular expression:</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">"Tallin{1,2}"</span><span class="o">)));</span> </code></pre></div></div> <p>We found that it was possible to occasionally pass an empty string to <code class="language-plaintext highlighter-rouge">matchText</code> which might cause false-positive tests.<br /> Now you will see an exception when trying to execute <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>See <a href="https://github.com/selenide/selenide/pull/1566/commits/3f6421226c">PR 1566</a></p> <p><br /></p> <h1 id="check-webdriver-title">We added check <code class="language-plaintext highlighter-rouge">webdriver().shouldHave(title(...))</code></h1> <p>As usually, you can use it with default or custom timeout:</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>See <a href="https://github.com/selenide/selenide/issues/1573">issue 1573</a>.<br /> Thanks to <a href="https://github.com/ervuks">Ervīns Patmalnieks</a> for <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>We released <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> with upgrade to Selenide 5.25.0.</p> <p><br /></p> <h1 id="release-selenide-appium-1.7.0">selenide-appium 1.7.0</h1> <p>We released <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> with upgrade to Selenide 5.25.0.</p> <p><br /></p> <h1 id="statistics">Selenide downloads statistics</h1> <center> <img src="/images/2021/09/selenide.downloads.png" width="800" /> </center> <p><br /> The milestone has been passed: we exceeded <strong>250 thousand downloads</strong> per month.</p> <p><br /></p> <h2 id="selenide-anniversary">Selenide anniversary</h2> <p>By the way, we have come close to another significant milestone: in October, Selenide will be … 10 years old!<br /> It’s hard to believe, but <a href="https://github.com/selenide/selenide/commit/3716078fc7fda8c5da01d871882d513cbd97cd0e">the first commit</a> was made in the repository on October 25, 2011. Just look how ridiculous it was! :)</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/09/28/selenide-5.25.0/ https://selenide.org/2021/09/28/selenide-5.25.0 2021-09-28T00:00:00+00:00 Released Selenide 5.24.0 <p>On August 29, 1997 a computer system Skynet became self-aware and launched nuclear missiles at Russia. So the war between humans and terminators began.</p> <p>It happened .. oh, my god! - 24 years ago. We live in the Future, and we are still alive. And we still rule the machines.</p> <p>I I celebrate this day every year, and today, in honor of the holiday, we released <a href="https://github.com/selenide/selenide/milestone/130?closed=1">Selenide 5.24.0</a>.</p> <p><br /></p> <h1 id="we-added-method-executecommand-duration">We added method <code class="language-plaintext highlighter-rouge">$.execute(Command, Duration)</code></h1> <p>to run custom commands with a custom timeout.</p> <p>In <a href="https://ru.selenide.org/2019/09/02/selenide-5.3.0/">Selenide 5.3.0</a> we added method <code class="language-plaintext highlighter-rouge">$.execute()</code> for running custom commands. But it could only use a default timeout. Now you can use custom timeout for any custom command.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1525">issue 1525</a>.<br /> Thanks to <a href="https://github.com/evpl">Evgenii Plugatar</a> for <a href="https://github.com/selenide/selenide/pull/1531">PR 1531</a>.</p> <p><br /></p> <h1 id="methods-executecommand-and-executecommand-duration-dont-pass-arguments-to-the-command">Methods <code class="language-plaintext highlighter-rouge">$.execute(Command)</code> and <code class="language-plaintext highlighter-rouge">$.execute(Command, Duration)</code> don’t pass arguments to the command</h1> <p>It’s a small change, but it might break your custom commands if you have some. Be prepared.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1527">issue 1527</a>.<br /> Thanks to <a href="https://github.com/evpl">Evgenii Plugatar</a> for <a href="https://github.com/selenide/selenide/pull/1535">PR 1535</a>.</p> <p><br /></p> <h1 id="we-fixed-or-and-and-conditions-in-case-if-element-is-not-found">We fixed <code class="language-plaintext highlighter-rouge">Or</code> and <code class="language-plaintext highlighter-rouge">And</code> conditions in case if element is not found</h1> <p>See <a href="https://github.com/selenide/selenide/issues/1534">issue 1534</a>.<br /> Thanks to <a href="https://github.com/evpl">Evgenii Plugatar</a> for <a href="https://github.com/selenide/selenide/pull/1539">PR 1539</a>.</p> <p><br /></p> <h1 id="now-or-and-and-dont-accept-empty-conditions-list">Now <code class="language-plaintext highlighter-rouge">Or</code> and <code class="language-plaintext highlighter-rouge">And</code> don’t accept empty conditions list</h1> <p>I hope you never tried to use <code class="language-plaintext highlighter-rouge">Or</code> and <code class="language-plaintext highlighter-rouge">And</code> with an empty conditions list because it makes no sense. Anyway, now you will get a runtime exception if you try to. <br /> Thanks to <a href="https://github.com/evpl">Evgenii Plugatar</a> for <a href="https://github.com/selenide/selenide/pull/1542">PR 1542</a>.</p> <p><br /></p> <h1 id="we-renamed-methods-conditionapplynull-and-collectionconditionapplynull">We renamed methods <code class="language-plaintext highlighter-rouge">Condition.applyNull()</code> and <code class="language-plaintext highlighter-rouge">CollectionCondition.applyNull()</code></h1> <p>Many years ago I named it <code class="language-plaintext highlighter-rouge">applyNull</code>, but this name is very misleading.<br /> This method checks if the condition is met if the element is not found at all. Now it’s named <code class="language-plaintext highlighter-rouge">missingElementSatisfiesCondition()</code> - a rather long, but accurate.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1541">issue 1541</a>.<br /> Thanks to <a href="https://github.com/evpl">Evgenii Plugatar</a> for <a href="https://github.com/selenide/selenide/pull/1544">PR 1544</a>.</p> <p><br /></p> <h1 id="we-removed-useless-stacktraces-when-closing-the-webdriver">We removed useless stacktraces when closing the webdriver</h1> <p>As you know, Selenide automatically closes the webdriver when it’s not needed anymore. To make it possible, Selenide runs a few background threads which monitor the webdriver status. And sometimes these threads compete with each other and try to close the webdriver which has been already closed by another thread. You might see such stacktraces in your logs.</p> <p>Now Selenide detects such situation better and doesn’t print long useless stacktraces.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1467">issue 1467</a> and <a href="https://github.com/selenide/selenide/pull/1540">PR 1540</a></p> <p><br /></p> <h1 id="we-fixed-searching-shadow-roots-inside-of-web-elements">We fixed searching shadow roots inside of web elements</h1> <p>See <a href="https://github.com/selenide/selenide/issues/1532">issue 1532</a> and <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>We released <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> with upgrade to Selenide 5.24.0</p> <p><br /></p> <h1 id="selenide-appium-168">selenide-appium 1.6.8</h1> <p>We released <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> with upgrade to Selenide 5.24.0</p> <p><br /> <br /></p> <h1 id="upd-selenide-5241">UPD: Selenide 5.24.1</h1> <p>WebDriverManager is a library (used by Selenide) for downloading webdriver binaries. They recently released a major update 5.0.0 with lots of new features. We haven’t incorporated these new features in Selenide yet, but at least we released <strong>Selenide 5.24.1</strong> with upgrade to WebDriverManager 5.0.1.</p> <p>Thanks to <a href="https://github.com/anilreddy">Anil Kumar Reddy Gaddam</a> for <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>We found that <code class="language-plaintext highlighter-rouge">commons-lang3</code> dependency (used by Selenide) doesn’t come transitively from WDM 5.x anymore. We had to add it explicitly and release <strong>Selenide 5.24.2</strong>.</p> <p>See <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>We upgraded to WebDriverManager 5.0.2, now we could exclude <code class="language-plaintext highlighter-rouge">docker-java</code> and few other dependencies.</p> <p><br /></p> <h1 id="upd-selenide-5244">UPD: Selenide 5.24.4</h1> <p>We upgraded to WebDriverManager 5.0.3 which had a quickfix for the latest Firefox 92.0 on MacOS.</p> <p><br /> <br /></p> <h1 id="news">News</h1> <p>The World doesn’t consist of only Web and Mobile!<br /> You can also use Selenide for testing Swing applications! Here is <a href="https://github.com/framebassman/fest-selenide">an example</a>.<br /> Under the hood it uses <a href="https://github.com/jalian-systems/marathonv5">webdriver implementation for swing</a>.</p> <p><br /></p> <h1 id="selenide-usage-statistics">Selenide usage statistics</h1> <center> <img src="/images/2021/08/selenide.downloads.png" width="800" /> </center> <p>249+ thousands of downloads per month. We almost got 250K!</p> <h3 id="hasta-la-vista-baby">Hasta la vista, baby!</h3> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/08/29/selenide-5.24.0/ https://selenide.org/2021/08/29/selenide-5.24.0 2021-08-29T00:00:00+00:00 Released Selenide 5.23.0 <p>Good summer day!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/125?closed=1">Selenide 5.23.0</a> on July, 16.</p> <p>It introduces a fundamentally new feature in Selenide:</p> <h1 id="the-new-generation-checks">The new generation checks</h1> <p>Now Selenide has checks not only for <em>web</em> elements, but for some other elements too. You can apply those checks with built-in waitings, readable error messages, reports etc. Everything you like.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1442">issue 1442</a>. Thanks to <a href="https://github.com/dbudim">Dmitriy Budim</a> for launching this whole epic in <a href="https://github.com/selenide/selenide/pull/1478">PR 1478</a>.</p> <p><br /> Let’s look at these checks.</p> <h2 id="checks-for-url">Checks for URL</h2> <p>Until now, there were 2 methods in Selenide to get an URL of a current page or frame (in most cases it’s the same value).</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>Though, it was not clear how to check them or wait for a right url.</p> <p>Now we have such checks:</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>Every of these checks:</p> <ol> <li>waits up to 4 seconds (by default) if needed</li> <li>appears in a report (text report or Allure)</li> <li>takes a screenshot and throws a readable <code class="language-plaintext highlighter-rouge">ConditionNotMetException</code> if the url is not as expected after 4 seconds.</li> </ol> <p>In the best traditions of selenide, you can</p> <ul> <li>override the default timeout (4 seconds) to any other number, e.g. <code class="language-plaintext highlighter-rouge">Configuration.timeout = 8000;</code></li> <li>set a custom timeout for every check (as a second parameter like <code class="language-plaintext highlighter-rouge">Duration.ofSeconds(42)</code>)</li> <li>create custom checks (see <a href="https://github.com/selenide/selenide/blob/c045579f243fb3a5abb99033e440cf8f12caa99c/statics/src/test/java/integration/WebDriverConditionsTest.java#L127">example</a>)</li> </ul> <h2 id="checks-for-clipboard">Checks for clipboard</h2> <p>Starting from version 5.20.0, Selenide has a method for accessing the clipboard:</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>But you could only get or set text to the clipboard:</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>Now you can also check the clipboard content. Again, with automated screenshots, reports, etc.</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>See <a href="https://github.com/selenide/selenide/pull/1507">PR 1507</a></p> <h2 id="checks-for-localstorage">Checks for <code class="language-plaintext highlighter-rouge">localStorage</code></h2> <p>Starting from version 5.15.0, Selenide has method <code class="language-plaintext highlighter-rouge">localStorage()</code> which return JavaScript object <code class="language-plaintext highlighter-rouge">LocalStorage</code>.<br /> But it had only methods <code class="language-plaintext highlighter-rouge">getItem</code> and <code class="language-plaintext highlighter-rouge">setItem</code>. Again, it was not clear how to check them or wait for an item in the local storage.</p> <p>Now it’s clear:</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>By the way, we also added method <code class="language-plaintext highlighter-rouge">localStorage.getItems()</code> returning all the content of <code class="language-plaintext highlighter-rouge">localStorage</code> as a map.</p> <p>See <a href="https://github.com/selenide/selenide/blob/c045579f243fb3a5abb99033e440cf8f12caa99c/statics/src/test/java/integration/LocalStorageTest.java#L157">example of custom condition</a> in Selenide tests.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1502">PR 1502</a></p> <h2 id="checks-for-sessionstorage">Checks for <code class="language-plaintext highlighter-rouge">sessionStorage</code></h2> <p>The same as for <code class="language-plaintext highlighter-rouge">localStorage</code> - we added methods</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>See <a href="https://github.com/selenide/selenide/pull/1502">PR 1502</a></p> <p><br /></p> <h1 id="a-small-refactoring">A small refactoring</h1> <p>We made classes <code class="language-plaintext highlighter-rouge">StaticConfig</code> and <code class="language-plaintext highlighter-rouge">StaticDriver</code> non-public.<br /> It seems they shouldn’t be needed outside of Selenide. Do you occasionally use them in your project? Contact us if you are sure you need them.</p> <h1 id="and-a-small-bugfix">And a small bugfix:</h1> <p>Now method <code class="language-plaintext highlighter-rouge">Selenide.screenshot("filename")</code> (again) takes a screenshot even if setting <code class="language-plaintext highlighter-rouge">Configuration.screenshots</code> is <code class="language-plaintext highlighter-rouge">false</code>.</p> <p>Let me clarify.</p> <ol> <li>You can set <code class="language-plaintext highlighter-rouge">Configuration.screenshots</code> to <code class="language-plaintext highlighter-rouge">false</code> if you need to disable <em>automated</em> screenshots in case of test failures (actually, I don’t really see any reasons to do that).</li> <li>You can use method <code class="language-plaintext highlighter-rouge">Selenide.screenshot("filename")</code> to take a screenshot at any moment you wish. Independently if the test status (I also don’t really see any reasons to do that:))</li> </ol> <p>The point is that method #2 works independently of setting #1. Now you can disable the automated screenshots and only take screenshots explicitly where needed (though, I still don’t see any reasons why it should be a good idea).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1477">issue 1477</a> and <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>We released <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> with upgrade to Selenide 5.23.0</p> <p><br /></p> <h1 id="selenide-appium-167">selenide-appium 1.6.7</h1> <p>We released <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> with upgrade to Selenide 5.23.0</p> <p><br /></p> <h1 id="new-links">New links</h1> <ul> <li>Post <a href="https://anilkulkarni.com/2020/03/selenide-ui-tests-in-minutes/">Selenide – UI tests in minutes</a> by Anil Kulkarni</li> </ul> <p><br /></p> <h1 id="statistics">Statistics</h1> <center> <img src="/images/2021/07/selenide.downloads.png" width="800" /> </center> <p>We crossed the line 232+ thousands of downloads per month!</p> <h3 id="have-a-carefree-summer">Have a carefree summer!</h3> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/07/16/selenide-5.23.0/ https://selenide.org/2021/07/16/selenide-5.23.0 2021-07-16T00:00:00+00:00 Released Selenide 5.22.3 <p>Good evening!</p> <p>There was a press release here that might hurt the feelings of many of our users and committers. We regret that this has happened, and we consider this publication to be our mistake, a manifestation of the unprofessionalism of certain maintainers.</p> <p>So just upgrade to <a href="https://github.com/selenide/selenide/milestone/126?closed=1">Selenide 5.22.3</a> with the most fresh and delicious bugfixes.</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.bbc.com/news/world-europe-57720890"> <img src="/images/2021/07/selenide-5.22.3.png" width="800" /> </a> </center> <h3 id="have-a-carefree-summer">Have a carefree summer!</h3> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/07/05/selenide-5.22.3/ https://selenide.org/2021/07/05/selenide-5.22.3 2021-07-05T00:00:00+00:00 Released Selenide 5.22.0 <p>Good morning!</p> <p>For my birthday, we made an anniversary release <a href="https://github.com/selenide/selenide/milestone/124?closed=1">Selenide 5.22.0</a>.</p> <p>Let’s unbox the gift!</p> <p><br /></p> <h1 id="now-you-can-close-an-alert-to-download-a-file">Now you can close an alert to download a file</h1> <p>Selenide has method <code class="language-plaintext highlighter-rouge">$.download()</code> which works by a very simple principle:</p> <ol> <li>Click</li> <li>Wait for a new file in a downloads folder</li> </ol> <p>The problem is that some websites show an alert or some other dialog, and you need to close it to start downloading the file. Generally, you might need to perform some action to start the download process.</p> <p>Now you can <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadToFolderTest.java">close the alert</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>or perform <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadViaProxyTest.java">any other action</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>See <a href="https://github.com/selenide/selenide/issues/1479">issue 1479</a> and <a href="https://github.com/selenide/selenide/pull/1481">PR 1481</a>.</p> <p><br /> <br /></p> <h1 id="we-improved-condition-textcasesensitive">We improved condition <code class="language-plaintext highlighter-rouge">textCaseSensitive</code></h1> <p>… to support selected options in a <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> element - similar to <code class="language-plaintext highlighter-rouge">Condition.text</code>.</p> <p>Thanks to <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> for <a href="https://github.com/selenide/selenide/pull/1482">PR 1482</a>.</p> <p><br /> <br /></p> <h1 id="we-added-selectors-bytextcaseinsensitive-and-withtextcaseinsensitive">We added selectors <code class="language-plaintext highlighter-rouge">byTextCaseInsensitive</code> and <code class="language-plaintext highlighter-rouge">withTextCaseInsensitive</code></h1> <p>… for finding elements by text ignoring case.</p> <h3 id="before">Before</h3> <p>Selenide has methods for finding elements by text (case sensitive) for a long time:</p> <ul> <li>by the whole text: <code class="language-plaintext highlighter-rouge">$(byText("Wake up we have a tsar again")</code></li> <li>by substring: <code class="language-plaintext highlighter-rouge">$(withText("we have a tsar")</code></li> </ul> <p>These selectors are case sensitive (and use XPath 1.0 inside).</p> <h3 id="after">After</h3> <p>Now we have added similar methods, but case insensitive:</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>These selectors can find element by text ignoring the case.</p> <h3 id="technical-nuance">Technical nuance</h3> <p>we could not implement this feature by XPath because string functions like <code class="language-plaintext highlighter-rouge">lower-case</code> and <code class="language-plaintext highlighter-rouge">match</code> were added in XPath 2.0, but all popular browsers support only XPath 1.0. That’s why we had to write <a href="https://github.com/selenide/selenide/blob/master/src/main/resources/find-elements-by-text-case-insensitive.js">a tricky JS code</a> to walk through the DOM tree.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1380">issue 1380</a> and <a href="https://github.com/selenide/selenide/pull/1381">PR 1381</a>.</p> <p><br /> <br /></p> <h1 id="we-added-method-drivergetsessionid">We added method <code class="language-plaintext highlighter-rouge">Driver.getSessionId()</code></h1> <p>Basically it’s needed in Selenide-Selenoid integration.</p> <p>Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <a href="https://github.com/selenide/selenide/pull/1483">PR 1483</a>.</p> <p><br /></p> <h1 id="we-shortened-default-webdriver-timeouts">We shortened default webdriver timeouts</h1> <p>There are two very strange timeouts inside of Selenium webdriver. I think it’s a problem that they are abnormally large. And you cannot change them.</p> <p>Here are these default timeouts:</p> <ul> <li>connect to a webdriver: <code class="language-plaintext highlighter-rouge">connectTimeout</code> = 120000 ms = 2 minutes</li> <li>any webdriver request: <code class="language-plaintext highlighter-rouge">readTimeout</code> = 10800000 ms = 3 hours (!)</li> </ul> <p>If some webdriver command hangs for whatever reason, your test will wait for 3 hours. For example, it happens when we test an Electron application and it goes to the system tray. The test tries to take a screenshot and hangs for a long time. I really don’t understand, is there anybody who thinks it’s reasonable?</p> <p>In Selenide 5.22.0 we added possibility to <em>change these default timeouts</em> (it uses complex reflection under the hood).<br /> And we made these timeouts fewer (though they are still too conservative in my mind):</p> <ul> <li><code class="language-plaintext highlighter-rouge">connectTimeout</code> = 1 minute</li> <li><code class="language-plaintext highlighter-rouge">readTimeout</code> = 2 minutes</li> </ul> <p>Let’s consider it as a workaround and hope Selenium 4 brings a proper solution.</p> <p>See <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>We released an update <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> which fixes <code class="language-plaintext highlighter-rouge">ClassCastException</code> in some cases.</p> <p>Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <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>We released an update <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> which upgrades to Selenide 5.22.0</p> <p><br /></p> <h1 id="news">News</h1> <p>Wow! Several famous guys posted their videos about Selenide.</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 Yuriy Artamonov: <a href="https://www.youtube.com/watch?v=P-vureOnDWY&amp;t=2758s">Modern UI Test Automation with Selenium Libraries</a></li> <li>JetBrains post <a href="https://blog.jetbrains.com/idea/2021/06/live-stream-modern-ui-test-automation-with-selenium-libraries/">about this stream</a></li> </ul> <p>And a couple of older materials:</p> <ul> <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 /> And for dessert, the younger generation teaches Selenide:</p> <center> <img src="/images/2021/06/selenide-taffel.png" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/06/08/selenide-5.22.0/ https://selenide.org/2021/06/08/selenide-5.22.0 2021-06-08T00:00:00+00:00 Released Selenide 5.21.0 <p>Good evening!</p> <p>We have released <a href="https://github.com/selenide/selenide/milestone/123?closed=1">Selenide 5.21.0</a>.</p> <p>What’s new there?</p> <p><br /></p> <h1 id="we-removed-duplicate-screenshots-for-chained-locators">We removed duplicate screenshots for chained locators</h1> <p>Most of Selenide methods are <em>chainable</em> (aka <em>fluent</em>), so that you can call multiple methods in a line. Thus way you can write concise tests.</p> <p>Something like</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">"Foo"</span><span class="o">))</span> <span class="o">.</span><span class="na">click</span><span class="o">();</span> </code></pre></div></div> <p>But there has been a problem with chained calls. If such a check failed, Selenide took multiple screenshots and added multiple steps to the report (incl. Allure report). One screenshot for <code class="language-plaintext highlighter-rouge">"table#id"</code>, another for <code class="language-plaintext highlighter-rouge">"tbody"</code> etc.<br /> Though it’s essentially a single step.</p> <p>Though it was not critical, we have fixed this unpleasantness. Now Selenide will take one screenshot, and Allure report will contain one line.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1055">issue 1055</a> and <a href="https://github.com/selenide/selenide/pull/1465">PR 1465</a>.</p> <p>NB! We had to do quite a major refactoring, so something might go wrong. Feel free to share your feedback, let’s fix it quickly!</p> <p><br /></p> <h1 id="we-added-browserperteststrategyextension">We added BrowserPerTestStrategyExtension</h1> <p>… for reopening a browser on every test.</p> <p>By default Selenide reuses browser between tests (in a single thread). It’s a good idea for performance: opening a new browser takes time.<br /> We assume you take care of the AUT state: clear cache, reset data, reload a page etc. in the beginning of every test. The exact clearing logic depends on your application anyway.</p> <p>But if you really want to open a new browser in every test, Selenide provides built-in mechanism for JUnit 4, JUnit 5 and TestNG.</p> <p>But we recently realized one missing feature for JUnit5. Selenide had only an extension for re-opening a browser before every <em>test class</em> (<code class="language-plaintext highlighter-rouge">@ExtendWith({BrowserStrategyExtension.class}</code>), but not <em>test method</em>.</p> <p>Now we added such an extension. If you add <code class="language-plaintext highlighter-rouge">@ExtendWith({BrowserPerTestStrategyExtension.class}</code>, Selenide will open a new browser before every <em>test method</em>.</p> <p><em>Don’t forget it makes your tests slower!</em></p> <p>See <a href="https://github.com/selenide/selenide/issues/1448">issue 1448</a> Thanks to <a href="https://github.com/simple-elf">Anton Aftakhov</a> for <a href="https://github.com/selenide/selenide/pull/1450">PR 1450</a>.</p> <p><br /></p> <h1 id="we-added-method-hover-with-offset">We added method <code class="language-plaintext highlighter-rouge">$.hover()</code> with offset</h1> <p>Selenide has method <code class="language-plaintext highlighter-rouge">$("div#123").hover()</code> for emulating moving cursor onto given element. But this method puts cursor in the center point of the element, and you could not influence that.</p> <p>Now we have overloaded method <code class="language-plaintext highlighter-rouge">$.hover()</code> with offset parameter. It allows you to specify how many pixels for the center point should be the cursor located:</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. It seems the offset does not always work precisely. In my tests, it was located <em>near</em> this position plus-minus 30 pixels. Feel free to share your experience with hover!</p> <p>See <a href="https://github.com/selenide/selenide/issues/1447">issue 1447</a> and <a href="https://github.com/selenide/selenide/pull/1461">PR 1461</a>.</p> <p><br /></p> <h1 id="upgraded-to-webdrivermanager-443">Upgraded to WebDriverManager 4.4.3</h1> <p>Thanks to <a href="https://github.com/anilreddy">Anil Kumar Reddy Gaddam</a> for <a href="https://github.com/selenide/selenide/pull/1464">PR 1464</a> and <a href="https://github.com/selenide/selenide/pull/1469">PR 1469</a>.</p> <p><br /></p> <h1 id="updated-javadoc-of-many-selenide-methods">Updated javadoc of many Selenide methods</h1> <p>… regarding lazy loading and “not recommended” phrase.</p> <p>Now javadoc has links to these wiki articles:</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>You will probably want to discuss these topics. Great, let’s discuss them!</p> <p>See <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>We released update <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> with support for BasicAuth when downloading files from Selenoid containers. <br /> See <a href="https://github.com/selenide/selenide-selenoid/issues/8">issue 8</a> and <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>We released update <a href="https://github.com/selenide/selenide-appium/blob/master/CHANGELOG"><code class="language-plaintext highlighter-rouge">selenide-appium:1.6.5</code></a> with improved error message in iOS tests.<br /> See <a href="https://github.com/selenide/selenide-appium/issues/54">issue 54</a>.</p> <p><br /></p> <h1 id="links">Links</h1> <p>Here are some links about Selenide we recently found:</p> <ul> <li>Post <a href="https://rieckpil.de/write-concise-web-tests-with-selenide-for-java-projects/">Write Concise Web Tests With Selenide for Java Projects</a> by <a href="https://github.com/rieckpil">Philip Riecks</a>.</li> <li>Video <a href="https://www.youtube.com/watch?v=T9xns1iMbPI">Introduction to Selenide</a> from him</li> <li>Small video tutorial <a href="https://www.youtube.com/watch?v=XPUPirH1yMs">Create Screenshots With Selenide</a> from him</li> <li>Sample <a href="https://github.com/senpay/layered-test-framework-example-serenity-jbehave">Selenide+Serenity+JBehave</a></li> <li>Sample <a href="https://github.com/sergiomartins8/test-automation-bootstrap/tree/master/ui-tests">Selenide+TestNG+ExtentReports</a></li> </ul> <p><br /></p> <h2 id="statistics">Statistics</h2> <p>It’s a Selenide download statistics for April, 2021:</p> <center> <img src="/images/2021/05/selenide.downloads.png" width="800" /> </center> <p>We almost achieved 200 K downloads per months!</p> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/05/15/selenide-5.21.0/ https://selenide.org/2021/05/15/selenide-5.21.0 2021-05-15T00:00:00+00:00 Released Selenide 5.20.1 <p>Good night!</p> <p>Joe Biden was asked if he thinks <code class="language-plaintext highlighter-rouge">$$.as</code> is a killer feature. «Mmm hmm, I do», — Biden answered.</p> <p>This is a double release notes for <a href="https://github.com/selenide/selenide/milestone/118?closed=1">Selenide 5.20.0</a> and <a href="https://github.com/selenide/selenide/milestone/119?closed=1">Selenide 5.20.1</a>.</p> <p><br /></p> <h1 id="we-added-operations-with-clipboard">We added operations with clipboard</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>Note that clipboard doesn’t work on Linux without graphical environment.</p> <p>Thanks to <a href="https://github.com/dbudim">Dmitriy Budim</a> for <a href="https://github.com/selenide/selenide/pull/1409">PR 1409</a>.</p> <p>NB!These methods are overridden in <a href="https://github.com/selenide/selenide-selenoid">selenide-selenoid</a> plugin to work correctly in Selenoid. We also released version <a href="https://github.com/selenide/selenide-selenoid/releases/tag/v1.1.0">1.1.0</a> of the plugin.</p> <p><br /></p> <h1 id="we-added-headless-mode-for-microsoft-edge">We added <code class="language-plaintext highlighter-rouge">headless</code> mode for Microsoft Edge</h1> <p>The setting <code class="language-plaintext highlighter-rouge">Configuration.headless</code> worked only for Chrome and Firefox, now it also works for Edge.</p> <p>(Browsers IE, Opera and Safari afaik still don’t support headless mode)</p> <p>See <a href="https://github.com/selenide/selenide/issues/1422">issue 1422</a> and <a href="https://github.com/selenide/selenide/pull/1424">PR 1424</a>.</p> <p><br /></p> <h1 id="we-added-method-as-for-giving-collection-an-alias">We added method $$.as() for giving collection an alias</h1> <p>As you probably remember, we added method <code class="language-plaintext highlighter-rouge">$.as()</code> in Selenide 5.17.0. It allows to give an alias (human-readable name) to any <code class="language-plaintext highlighter-rouge">SelenideElement</code>.</p> <p>Now you can also given readable names to collections:</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>See <a href="https://github.com/selenide/selenide/issues/1389">issue 1389</a> and <a href="https://github.com/selenide/selenide/pull/1431">PR 1431</a>.</p> <p>NB! Don’t rush with using this feature. I personally take it as a “last sort hack”.<br /> It’s always better to see the real locator instead of a name which always may happen to be:</p> <ul> <li>Deceiving</li> <li>Deprecated</li> <li>Misleading</li> </ul> <p>It’s always better to invest your time in readable locator, method names, well-organized tests etc. This is where the real power lies.</p> <p><br /></p> <h1 id="we-added-collection-condition-containexacttextscasesensitive">We added collection condition <code class="language-plaintext highlighter-rouge">containExactTextsCaseSensitive</code></h1> <p>Existing method <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("a", "b", "c"))</code> checks that the collection contains exactly these elements, and not any others. But sometimes we want a less strict check. For example, you need to check that the traded currencies list contains EUR and USD - and any others.</p> <p>Now we have a method for such check:</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">"EUR"</span><span class="o">,</span> <span class="s">"USD"</span><span class="o">));</span> </code></pre></div></div> <p>Thanks to <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> for <a href="https://github.com/selenide/selenide/pull/1426">PR 1426</a>.</p> <p><strong>UPD</strong> In release Selenide 5.20.1 it was renamed to</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>Thanks to <a href="https://github.com/bereg2k">Oleg Berezhnoy</a> for <a href="https://github.com/selenide/selenide/pull/1438">PR 1438</a> and <a href="https://github.com/selenide/selenide/pull/1439">PR 1439</a>.</p> <p><br /></p> <h1 id="fixed-the-lost-firefoxoptions">Fixed the lost FirefoxOptions</h1> <p>In some situations some of Firefox options could be lost. Now they are not lost anymore.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1436">issue 1436</a>. Thanks to <a href="https://github.com/dbudim">Dmitriy Budim</a> for <a href="https://github.com/selenide/selenide/pull/1437">PR 1437</a>.</p> <p><br /></p> <h1 id="removed-duplicate-logging-of-navigation-methods">Removed duplicate logging of “navigation” methods</h1> <p><code class="language-plaintext highlighter-rouge">SelenideElement</code> has several methods to find other elements:</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>Such methods were logged twice: first when you call them, and second when you call another method of the the found element. Now we removed this duplication.</p> <p>Thanks to <a href="https://github.com/fokinp">Pavel Fokin</a> for <a href="https://github.com/selenide/selenide/pull/1428">PR 1428</a>.</p> <p><br /></p> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/03/23/selenide-5.20.1/ https://selenide.org/2021/03/23/selenide-5.20.1 2021-03-23T00:00:00+00:00 Released Selenide 5.19.0 <p>Good morning!</p> <p>You probably didn’t know, but today, February 24, is the main holiday in Estonia - <a href="https://en.wikipedia.org/wiki/Estonian_Declaration_of_Independence">Independence Day</a>. Exactly 103 years ago, Estonia was proclaimed an independent democratic republic.</p> <p>And 93 years later in Estonia, in the casemates of <a href="https://codeborne.com/">Codeborne</a> company, Selenide library was born. Isn’t that a miracle?</p> <p>So we dedicate our new release <a href="https://github.com/selenide/selenide/milestone/116?closed=1">Selenide 5.19.0</a> to Independence Day.</p> <p><br /></p> <h1 id="we-fixed-draganddrop-finally">We fixed drag’and’drop, finally!</h1> <p>Selenide has had method <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code> for years, but de-factor it doesn’t work. Under the hood it uses Selenium mechanism <code class="language-plaintext highlighter-rouge">Actions</code>, and something was broken in it. It just doesn’t drag the element.</p> <p>Now we added an alternative implementation using tricky JavaScript snippet. And it seems to work in all browsers. We even made it the default implementation.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// The working solution:</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">// Non-working solution using Actions (in case if you still want to give it a chance):</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>See <a href="https://github.com/selenide/selenide/issues/1110">issue 1110</a>.</p> <p>Thanks to <a href="https://github.com/dbudim">Dmitriy Budim</a> for <a href="https://github.com/selenide/selenide/pull/1412">PR 1412</a>.</p> <p><br /></p> <h1 id="appium-support">Appium support</h1> <p>We also released a <code class="language-plaintext highlighter-rouge">selenide-appium:1.6.2</code> update which has method <code class="language-plaintext highlighter-rouge">$.dragAndDropTo()</code> overridden, so that it works on mobile devices too. See <a href="https://github.com/selenide/selenide-appium/pull/53/files">PR #53</a>.</p> <p><br /></p> <h1 id="we-fixed-method-clickusingjavascript-in-internet-explorer">We fixed method <code class="language-plaintext highlighter-rouge">$.click(usingJavascript())</code> in Internet Explorer</h1> <p>See <a href="https://github.com/selenide/selenide/issues/1406">issue 1406</a> and <a href="https://github.com/selenide/selenide/pull/1419">PR 1419</a>.</p> <p><br /></p> <h1 id="we-improved-description-of-snapshot-collections">We improved description of <code class="language-plaintext highlighter-rouge">$$.snapshot()</code> collections</h1> <p>There is one tricky method in Selenide collections: <code class="language-plaintext highlighter-rouge">$$.snapshot()</code>.<br /> It takes a “snapshot” of the collection (fetches its elements only once), and doesn’t reload them from browser anymore. It may be useful to speed up your tests in case of big collections. But only if you are sure that the collection elements will not change anymore.</p> <p>There was one tiny problem: such a snapshot didn’t look nice in reports. For example, this line:</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>when failed, looked in the report like this:</p> <blockquote> <p>List size mismatch: expected: = 3, actual: 2, collection: $$(2 elements)</p> </blockquote> <p>The description doesn’t contain selector of the original collection, only <code class="language-plaintext highlighter-rouge">(2 elements)</code>.</p> <p>Now it also contains the selector:</p> <blockquote> <p>List size mismatch: expected: = 3, actual: 2, collection: #root li.snapshot(2 elements)</p> </blockquote> <p>Thanks to <a href="https://github.com/fokinp">Pavel Fokin</a> for <a href="https://github.com/selenide/selenide/pull/1402">PR 1402</a>.</p> <p><br /></p> <h1 id="added-method-getalias">Added method <code class="language-plaintext highlighter-rouge">$.getAlias()</code></h1> <p>It returns the same value that you set with method <code class="language-plaintext highlighter-rouge">$.as("login button")</code>.<br /> Normally you don’t need this method. But it may ne useful for those who generate their own reports. Who knows, probably some of you is developing an Allure killer…</p> <p>Thanks to <a href="https://github.com/pavelpp">pavelpp</a> for <a href="https://github.com/selenide/selenide/pull/1415">PR 1415</a>.</p> <p><br /></p> <h1 id="added-refresh-and-other-events-to-selenide-log">Added “refresh” and other events to Selenide log</h1> <p>There is a bunch of method in Selenide that are not related to web elements (like <code class="language-plaintext highlighter-rouge">refresh()</code>, <code class="language-plaintext highlighter-rouge">back()</code> etc.) And we recently found that some of these methods were not shown in Selenide/Allure reports. Not fatal, of course, but still, someone needs these reports for some reason…</p> <p>Now we fixed it, and the following actions will be added to reports:</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>See <a href="https://github.com/selenide/selenide/issues/1383">issue 1383</a> and <a href="https://github.com/selenide/selenide/pull/1404">PR 1404</a>.</p> <p><br /></p> <h1 id="added-nullable-annotations-to-webdriverrunner-methods">Added <code class="language-plaintext highlighter-rouge">@Nullable</code> annotations to <code class="language-plaintext highlighter-rouge">WebDriverRunner</code> methods</h1> <p>Probably the most import is <code class="language-plaintext highlighter-rouge">@Nullable</code> annotation for method <code class="language-plaintext highlighter-rouge">WebDriverRunner.getSelenideProxy()</code>: it was not obvious that this method can return <code class="language-plaintext highlighter-rouge">null</code> if the proxy is not started.</p> <p>Now such an error will be highlighted in your IDE.</p> <p>See <a href="https://github.com/selenide/selenide/commit/9b4723d090442c">commit</a>.</p> <p><br /></p> <h1 id="fixed-selenide-own-tests-on-non-en-machines">Fixed Selenide own tests on non-EN machines</h1> <p>Selenide user <a href="(https://github.com/vrossellotravelc)">@vrossellotravelc</a> tried to build selenide project on a machine which has a default language other than English (was it French or Spanish?)</p> <p>We found that a couple of Selenide own tests contained hard-coded formatting of <code class="language-plaintext highlighter-rouge">Duration</code>, which can differ in other locales.</p> <p>Now those tests are fixed so that it cannot happen anymore. How? Welcome to the PR!</p> <p>Thanks to <a href="https://github.com/vrossellotravelc">Vicente Rossello Jaume</a> for <a href="https://github.com/selenide/selenide/pull/1408">PR 1408</a>.</p> <p><br /></p> <h1 id="statistics">Statistics</h1> <p>The latest Selenide downloads statistics:</p> <center> <img src="/images/2021/02/selenide.downloads.png" width="800" /> </center> <p><br /></p> <h1 id="traditions">Traditions</h1> <p>That’s it for today.</p> <p>While you are updating, I’m going to find a glass of vodka and a sprat sandwich.<br /> It’s a tradition in Estonia for independence day, you know.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/02/24/selenide-5.19.0/ https://selenide.org/2021/02/24/selenide-5.19.0 2021-02-24T00:00:00+00:00 Released Selenide 5.18.1 <p>Hi all!</p> <p>We have release a small update <a href="https://github.com/selenide/selenide/milestone/115?closed=1">Selenide 5.18.1</a>.</p> <p>Let’s look at the changes:</p> <p><br /></p> <h1 id="added-method-selenidegetsessionstorage">Added method <code class="language-plaintext highlighter-rouge">Selenide.getSessionStorage()</code></h1> <p>… similar to <code class="language-plaintext highlighter-rouge">Selenide.getLocalStorage()</code> added in 5.15.0.</p> <p>They both have the same set of methods: <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> etc.</p> <blockquote> <p>Sometimes it’s useful to set some flag to <code class="language-plaintext highlighter-rouge">sessionStorage</code> to <code class="language-plaintext highlighter-rouge">localStorage</code> to emulate some user action or toggle some features or settings.</p> </blockquote> <p>Thanks to <a href="https://github.com/dbudim">Dmitriy Budim</a> for <a href="https://github.com/selenide/selenide/pull/1400">PR 1400</a>.</p> <p>P.S. FYI <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage">difference between localStorage and sessionStorage</a>.</p> <p><br /></p> <h1 id="fixed-error-message-for-filterbyand">Fixed error message for <code class="language-plaintext highlighter-rouge">$$.filterBy(and(..))</code></h1> <p>As you know, Selenide provides powerful API for filtering and validating collections.</p> <p>User <a href="https://github.com/fokinp">Pavel Fokin</a> found that the error message can look misleading when the collection is filtered with <code class="language-plaintext highlighter-rouge">and</code> condition (a combination of other conditions):</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>The output was a bit misleading (because it showed only the last checked condition):</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>Now the output contains all conditions:</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>See <a href="https://github.com/selenide/selenide/issues/1392">issue 1392</a>.<br /> Thanks to <a href="https://github.com/fokinp">Pavel Fokin</a> for <a href="https://github.com/selenide/selenide/pull/1393">PR 1393</a>.</p> <p><br /></p> <h1 id="pass-noproxy-option-from-user-provided-proxy-to-selenide-proxy">Pass “noproxy” option from user-provided proxy to Selenide proxy</h1> <p>As you know, Selenide can run its own embedded proxy server (used to download files, intercept requests etc.) It’s also possible to provide Selenide your own proxy. Then Selenide uses both proxies, one through the other.</p> <p>Every proxy can have a setting “noproxy” which often has value “localhost”. It mean that all requests <em>except “http://localhost:*“</em> should be proxied. And this setting was lost when running Selenide with two proxies, Thus Selenide could not run requests to localhost.</p> <p>Now Selenide passes “noproxy” setting from your proxy to its own proxy.</p> <p><em>Localhost is a delicate thing!</em></p> <p>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1390">PR 1390</a>.</p> <p><br /></p> <h1 id="upgraded-to-netty-4159final-and-littleproxy-202">Upgraded to Netty 4.1.59.Final and LittleProxy 2.0.2</h1> <p>It is unlikely that you will read them, but here are the release notes for <a href="https://netty.io/news/2021/02/08/4-1-59-Final.html">Netty 4.1.59.Final</a> and <a href="https://github.com/mrog/LittleProxy/blob/master/RELEASE_NOTES.md">LittleProxy 2.0.2</a>. At lease they fixed some security issue and a memory leak.</p> <p><br /></p> <p>It’s all for today. Feel free to update and share your feedback. <br /> Create issues <a href="https://github.com/selenide/selenide/issues/new">in github</a>, complain <a href="https://gitter.im/codeborne/selenide">in chats</a>, swear <a href="https://twitter.com/selenide">in twitter</a>.</p> <p><br /> <em>Github is stronger than bugs!</em></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/02/11/selenide-5.18.1/ https://selenide.org/2021/02/11/selenide-5.18.1 2021-02-11T00:00:00+00:00 Released Selenide 5.18.0 <p>Good evening!</p> <p>Today is January, 23.</p> <p>Let’s meet the new update: <a href="https://github.com/selenide/selenide/milestone/113?closed=1">Selenide 5.18.0</a>.</p> <p>Let me invite you to take a small virtual tour of the changes in 5.18.0. <br /> Make yourself comfortable.</p> <p><br /></p> <h1 id="we-disabled-webdriver-logs-by-default">We disabled webdriver logs by default</h1> <p>Starting from version 5.13.0, Selenide wrote webdriver logs to files <code class="language-plaintext highlighter-rouge">build/reports/tests/webdriver.uuid.log</code>.<br /> It seemed to be useful at that moment. But later we realized that these logs take quite a lot of disk space, and nobody really needs to read them in most cases. That’s why we decided to disable them by default.</p> <p>If you need those webdriver logs, you can still enable them with setting <code class="language-plaintext highlighter-rouge">Configuration.webdriverLogsEnabled = true</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1365">issue 1365</a> and <a href="https://github.com/selenide/selenide/pull/1379">PR 1379</a>.</p> <p><br /></p> <h1 id="changed-timeout-parameter-type-from-long-to-duration">Changed “timeout” parameter type from Long to Duration</h1> <p>… in collection methods. Now instead of</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>it is fashionable to write</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>See <a href="https://github.com/selenide/selenide/issues/1377">issue 1377</a>.<br /> Thanks to <a href="https://github.com/ostap-oleksyn">Ostap Oleksyn</a> for <a href="https://github.com/selenide/selenide/pull/1377">PR 1377</a>.</p> <p><br /></p> <h1 id="speed-up-search-of-inner-shadow-dom-elements">Speed up search of inner shadow dom elements</h1> <p>One of features that do exist in Selenide, but not in Selenium webdriver is <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>There might be multiple shadow roots inside of other shadow roots.<br /> To find all elements inside inner shadow roots, method <code class="language-plaintext highlighter-rouge">$(shadowCss())</code> performed a JavaScript snippet in loop for every shadow root separately. It might be slow because every call to webdriver takes time.</p> <p>Now <code class="language-plaintext highlighter-rouge">$(shadowCss())</code> calls one <a href="https://github.com/selenide/selenide/blob/master/src/main/resources/find-in-shadow-roots.js">even more trickier recursive JavaScript</a> which finds all elements in all inner shadow roots with one call.</p> <p>See <a href="https://github.com/selenide/selenide/pull/1373">PR 1373</a>.</p> <p>A separate “thank you” goes to <a href="https://github.com/sakamoto66">sakamoto66</a> for <a href="https://github.com/selenide/selenide/issues/1246">issue 1246</a> and <a href="https://github.com/selenide/selenide/pull/1233">PR 1233</a> which unfortunately was not merged, but inspired us for this optimization.</p> <p><br /></p> <h1 id="fixed-checks-shouldnotand-and-shouldnotor">Fixed checks <code class="language-plaintext highlighter-rouge">$.shouldNot(and(...))</code> and <code class="language-plaintext highlighter-rouge">$.shouldNot(or(...))</code></h1> <p>Selenide user <a href="https://github.com/pavelpp">pavelpp</a> detected a bug in Selenide when used <code class="language-plaintext highlighter-rouge">not</code> in combination with <code class="language-plaintext highlighter-rouge">and</code> and <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">// works</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">// fails</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">// fails</span> </code></pre></div></div> <p>Now we fixed it. All three lines don’t throw errors anymore.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1369">issue 1369</a> and <a href="https://github.com/selenide/selenide/pull/1370">PR 1370</a>.</p> <p>By the way, we now forbid using <code class="language-plaintext highlighter-rouge">and</code> and <code class="language-plaintext highlighter-rouge">or</code> with only one condition. In other words, line <code class="language-plaintext highlighter-rouge">or("foo", visible)</code> doesn’t compile anymore. You have to use at least two condition, like <code class="language-plaintext highlighter-rouge">or("foo", visible, enabled)</code>.</p> <p>Agree, this is logical.</p> <p><br /></p> <h1 id="we-detect-conflict-in-browsername-capability">We detect conflict in “browserName” capability</h1> <p>We recently realized that there are two settings for specifying browser:</p> <ol> <li>The primary - <code class="language-plaintext highlighter-rouge">Configuration.browser</code></li> <li>Another - <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities["browserName"]</code> (I don’t really know why it’s needed)</li> </ol> <p>And you might get an unexpected browser opened if you haven’t set the first one, but set the second one. Now Selenide detects such a conflict and throws an exception:</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>Let me repeat: setting <code class="language-plaintext highlighter-rouge">Configuration.browser</code> is enough in all cases. As far as I know, the second setting is never needed.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1366">issue 1366</a> and <a href="https://github.com/selenide/selenide/pull/1374">PR 1374</a>.</p> <p><br /></p> <h1 id="fixed-displaying-timeout-parameter-in-reports">Fixed displaying timeout parameter in reports</h1> <p>Recently we added to <code class="language-plaintext highlighter-rouge">$.should*</code> methods parameter <code class="language-plaintext highlighter-rouge">timeout</code> of type <code class="language-plaintext highlighter-rouge">Duration</code>.<br /> Then we realized it was displayed quite unclearly in Selenide/Allure reports.</p> <p>For example, line <code class="language-plaintext highlighter-rouge">$("h1").shouldBe(visible, Duration.ofSeconds(1))</code> looked like this in a report:</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>Though this “PT1M” is an ISO-compatible representation and means “time period 1 minute”, we decided to replace it by more familiar “300 ms”, “1s”, “1.500 s.” etc.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1376">issue 1376</a> and <a href="https://github.com/selenide/selenide/pull/1378">PR 1378</a>.</p> <p><br /></p> <h1 id="upgraded-to-webdrivermanager-431">Upgraded to WebDriverManager 4.3.1</h1> <p>As usually, see their changelog <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">here</a>.</p> <p><br /></p> <p>It’s the end of our excursion. Feel free to update and share your feedback. <br /> Create issues <a href="https://github.com/selenide/selenide/issues/new">in github</a>, complain <a href="https://gitter.im/codeborne/selenide">in chats</a>, swear <a href="https://twitter.com/selenide">in twitter</a>.</p> <h2 id="news">News</h2> <p>Here is an final downloads statistics of Selenide for year 2020. Not bad!</p> <center> <img src="/images/2021/01/selenide.downloads.png" width="800" /> </center> <p><br /> Happy current year! <br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2021/01/23/selenide-5.18.0/ https://selenide.org/2021/01/23/selenide-5.18.0 2021-01-23T00:00:00+00:00 Released Selenide 5.17.2 <p>Good morning!</p> <p>The gnomes still cannot calm down. Tomorrow morning they will bring you the last present in your socks: release <a href="https://github.com/selenide/selenide/milestone/110?closed=1">Selenide 5.17.2</a>.</p> <p><br /></p> <h1 id="make-commands-return-selenideelement-instead-webelement">Make <code class="language-plaintext highlighter-rouge">Commands</code> return <code class="language-plaintext highlighter-rouge">SelenideElement</code> instead <code class="language-plaintext highlighter-rouge">WebElement</code></h1> <p>It allows you chaining <code class="language-plaintext highlighter-rouge">$.execute(Command)</code> with other Selenide methods, thus making your tests even more concise and expressive:</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>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1355">PR 1355</a>.</p> <p><br /></p> <h1 id="fixed-method-setvaluenull">Fixed method <code class="language-plaintext highlighter-rouge">$.setValue(null)</code></h1> <p>See <a href="https://github.com/selenide/selenide/issues/1356">issue 1356</a>. Thanks to <a href="https://github.com/dzem">Dmitriy Zemlyanitsyn</a> for <a href="https://github.com/selenide/selenide/pull/1357">PR 1357</a>.</p> <p><br /></p> <h1 id="enabled-soft-asserts-in-beforeall-and-afterall-methods-in-junit-5">Enabled soft asserts in @BeforeAll and @AfterAll methods (in JUnit 5)</h1> <p>See <a href="https://github.com/selenide/selenide/issues/981">issue 981</a>, <a href="https://github.com/selenide/selenide/issues/1070">issue 1070</a> and <a href="https://github.com/selenide/selenide/pull/1359">PR 1359</a>.</p> <p><br /></p> <h1 id="fixed-file-selenide-5172-javadocjar">Fixed file <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>now it contains javadoc for all classes.</p> <p><br /></p> <p><br /> Once again, Happy New Year! <br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/12/30/selenide-5.17.2/ https://selenide.org/2020/12/30/selenide-5.17.2 2020-12-30T00:00:00+00:00 Released 5.17.0 <p>Good night!</p> <p>I am passionate about Catholic Christmas. Because it was on Christmas that the terrorists took over the Nakatomi plaza and Bruce killed Hans Gruber. And then he blew up the plane with a lighter.</p> <p>So catch the Christmas release <a href="https://github.com/selenide/selenide/milestone/108?closed=1">Selenide 5.17.0</a>.</p> <p><br /></p> <h1 id="added-method-asname">Added method $.as(“name”)</h1> <p>We added method <code class="language-plaintext highlighter-rouge">as</code>, so that you can give elements readable names.</p> <p>To feel the difference, let’s compare the following two lines in test:</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>The test result looks like this:</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>In the last line, we see a readable name “Login button” instead of long unreadable xpath.</p> <p>NB! Don’t rush with using this feature. I personally take it as a “last sort hack”.<br /> It’s always better to see the real locator instead of a name which always may happen to be:</p> <ul> <li>Deceiving</li> <li>Deprecated</li> <li>Misleading</li> </ul> <p>It’s always better to invest your time in readable locator, method names, well-organized tests etc. This is where the real power lies.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1200">issue 1200</a> and <a href="https://github.com/selenide/selenide/pull/1353">PR 1353</a>.</p> <p><br /></p> <h1 id="added-bunch-of-default-options-to-headless-chrome">Added bunch of default options to headless chrome</h1> <p>We added the same default options to headless chrome <a href="https://github.com/puppeteer/puppeteer/blob/7a2a41f2087b07e8ef1feaf3881bdcc3fd4922ca/src/Launcher.js#L261">as in Puppeteer</a>.<br /> We assume that Puppeteer guys are smart, so we do like them. :)</p> <p>Thanks to <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> for <a href="https://github.com/selenide/selenide/pull/1329">PR 1329</a>.</p> <p><br /></p> <h1 id="fixed-byshadowcssfindelements">Fixed <code class="language-plaintext highlighter-rouge">ByShadowCss.findElements</code></h1> <p>It returned only subset of elements in a particular case: when DOM contained multiple <em>inner shadow hosts</em>.</p> <p>Now it returns all the elements.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1346">issue 1346</a>. Thanks to <a href="https://github.com/dpeger">Daniel H. Peger</a> for <a href="https://github.com/selenide/selenide/pull/1347">PR 1347</a>.</p> <p><br /></p> <h1 id="added-should-methods-with-custom-timeout">Added $.should* methods with custom timeout</h1> <p>As you know, there are two major groups of methods in Selenide:</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> – use default timeout</li> <li><code class="language-plaintext highlighter-rouge">$.waitUntil</code> / <code class="language-plaintext highlighter-rouge">$.waitWhile</code> – use given timeout</li> </ol> <p><code class="language-plaintext highlighter-rouge">$.wait*</code> are useful for “long” actions that last longer than the default timeout (which is 4 seconds by default).</p> <p><br /></p> <p>The problem with <code class="language-plaintext highlighter-rouge">$.wait*</code> methods is purely grammatical: Selenide built-in conditions don’t sound correctly with verb “wait”:</p> <ol> <li><code class="language-plaintext highlighter-rouge">element should have text</code> - sounds right</li> <li><code class="language-plaintext highlighter-rouge">element wait until text</code> - doesn’t sound right</li> </ol> <p>Now you can replace <code class="language-plaintext highlighter-rouge">$.waitUntil(hasText("bob"), 18_000)</code> by <code class="language-plaintext highlighter-rouge">$.shouldHave(text("bob"), Duration.ofSeconds(18))</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1136">issue 1136</a>, <a href="https://github.com/selenide/selenide/issues/1338">issue 1338</a> and <a href="https://github.com/selenide/selenide/pull/1340">PR 1340</a>.</p> <p><br /></p> <h1 id="page-objects">Page objects</h1> <p>The next block of improvements concerns the Page Objects.</p> <p>As you know, in Selenide it’s possible to create page objects</p> <ul> <li>with fields annotated with <code class="language-plaintext highlighter-rouge">@FindBy</code>,</li> <li>which can be standard selenium <code class="language-plaintext highlighter-rouge">WebElement</code>,</li> <li>or our <code class="language-plaintext highlighter-rouge">SelenideElement</code>,</li> <li>or even reusable components <code class="language-plaintext highlighter-rouge">ElementsContainer</code> which can be used to compose more complex page objects.</li> </ul> <p>And it all works in the best traditions of selenide: lazy loading and reloading of elements etc.</p> <p><br /></p> <p>There were some issues with those <code class="language-plaintext highlighter-rouge">@FindBy</code> fields:</p> <ol> <li>lazy loading didn’t work for PO fields of type <code class="language-plaintext highlighter-rouge">List&lt;ElementsContainer&gt;</code> (see issue <a href="https://github.com/selenide/selenide/issues/282">282</a> and <a href="https://github.com/selenide/selenide/issues/482">482</a>)</li> <li>PO fields of generic types were not supported (see <a href="https://github.com/selenide/selenide/issues/694">issue 694</a>)</li> </ol> <p>It was hard to fix. I had to dive into (quite old) code and refactor it. I had to strain my brains and apply all my remnants of the former abstract thinking. :)</p> <p>That’s why these two pull requests are my pride:</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="and-finally-a-couple-of-technical-improvements">And finally, a couple of technical improvements:</h1> <ul> <li>split Selenide project to subprojects - see <a href="https://github.com/selenide/selenide/pull/1348">PR 1348</a></li> <li>fixed Selenide tests that depended on OS - see <a href="https://github.com/selenide/selenide/issues/1344">issue 1344</a> and <a href="https://github.com/selenide/selenide/pull/1345">PR 1345</a>, thanks to <a href="https://github.com/dpeger">Daniel H. Peger</a></li> <li>cleaned up <code class="language-plaintext highlighter-rouge">Plugins</code> code – thanks to <a href="https://github.com/yorlov">Yuri Orlov</a> for <a href="https://github.com/selenide/selenide/pull/1343">PR 1343</a></li> <li>upgraded to browserup-proxy:2.1.2 and guava:30.1-jre</li> <li>added support for chrome 88, edge 89, opera 73</li> </ul> <p><br /></p> <h3 id="known-issues">Known issues:</h3> <ul> <li>file <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> happened to be incomplete: it doesn’t contain javadoc for some of Selenide classes. Will be fixed in 5.17.1.</li> </ul> <p><br /></p> <h1 id="summarizing">Summarizing</h1> <p>In general, we end the year on a positive note:<br /> performed a non-trivial refactoring and fixed several old pains. <br /> We are entering the New Year with only one screen in <a href="https://github.com/selenide/selenide/issues">github issues</a> instead of two.</p> <p>And the number of downloads of selenide has grown over the year from 102 to 167 thousand.</p> <center> <img src="/images/2020/12/selenide.downloads.png" width="800" /> </center> <p><br /> Happy New Year! <br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/12/26/selenide-5.17.0/ https://selenide.org/2020/12/26/selenide-5.17.0 2020-12-26T00:00:00+00:00 Released Selenide 5.16.2 <p>Good morning!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/109?closed=1">Selenide 5.16.2</a>.</p> <p>Please don’t think that recent release <a href="/2020/11/20/selenide-5.16.0/">Selenide 5.16.0</a> had a lot of bugs.<br /> Not at all.</p> <p>This release 5.16.2 is a bunch of fixes for old small issues.</p> <p>Briefly:</p> <h2 id="selenide-5.16.2">Release <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">Release <a href="https://github.com/selenide/selenide/milestone/106?closed=1">5.16.1</a> (23.11.2020)</h2> <p>Here we had two fixes to run Chrome with extensions.</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">News</h2> <p>Once there is room left, I will share brand new links:</p> <ul> <li>Example from LambdaTest: <a href="https://github.com/LambdaTest/selenide-testng-sample">selenide-testng-sample</a></li> <li>Video about Selenide on Portugal: <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>Selenide course somewhere in Brazil: <a href="https://inoveteste.com.br/automacao-web-descomplicada-com-selenide/">Automação Web Descomplicada Com Selenide</a></li> <li>Visual testing: <a href="https://medium.com/automated-visual-testing-with-applitools/getting-started-with-the-applitools-sdk-653f2cd1ad48">Applitools+Selenide</a></li> <li>Project sample: <a href="https://github.com/bmurmistro/applitools">Getting started with applitools</a></li> </ul> <p><br /></p> <p>Stay tuned for Selenide 5.17.0!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/11/25/selenide-5.16.2/ https://selenide.org/2020/11/25/selenide-5.16.2 2020-11-25T00:00:00+00:00 Released Selenide 5.16.0 <p>Good night!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/105?closed=1">Selenide 5.16.0</a>.</p> <h2 id="plugin-system">Plugin system</h2> <p>One of the major plans for this year in <a href="https://github.com/selenide/selenide/wiki/Selenide-Roadmap">Selenide roadmap</a> was to create a plugins system. It took time, but now</p> <p><strong>the day has come!</strong></p> <p>Currently there are two Selenide plugins available:</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>We will describe them in following posts.</p> <p>Apparently, we are going to create next plugins for Allure, JUnit, TestNG, AShot.<br /> <em>Please share your ideas too!</em></p> <p>See <a href="https://github.com/selenide/selenide/issues/1051">issue #1051</a> and <a href="https://github.com/selenide/selenide/pull/1264">PR #1264</a>, <a href="https://github.com/selenide/selenide/pull/1317">PR 1317</a> and <a href="https://github.com/selenide/selenide/pull/1321">PR 1321</a>.</p> <h2 id="error-messages">Error messages</h2> <p>One of core Selenide functions is forming error messages in case of test failures.<br /> Sometimes these error messages can also be incomplete or unclear, especially in some corner cases.<br /> In this release, we did a whole bunch of fixes for error messages.</p> <h3 id="improved-error-messages-for-and-and-not-checks">Improved error messages for AND and NOT checks</h3> <p>A canonical way to perform a negative check in Selenide is method <code class="language-plaintext highlighter-rouge">shouldNotHave</code>.<br /> And if you want to perform multiple checks - just write them in a line using commas:</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>But there are alternative methods <code class="language-plaintext highlighter-rouge">Condition.not</code> and <code class="language-plaintext highlighter-rouge">Condition.and</code> for specific cases. For example, you can declare composite conditions, thus creating DSL for your tests.</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>We found that these methods showed incomplete information in test reports:</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>The problem is that we don’t see the expected text (“admin”) and other conditions.</p> <p>Now we fixed this problem, and you will see the expected text:</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>Thanks to <a href="https://github.com/fokinp">Pavel Fokin</a> for <a href="https://github.com/selenide/selenide/pull/1306">PR 1306</a> and <a href="https://github.com/selenide/selenide/pull/1300">PR 1306</a>.</p> <p><br /></p> <h3 id="added-information-about-parents">Added information about parents</h3> <p>In Selenide, you can find elements inside other elements. Like this:</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>But if such a check failed, you could see only the child element’s locator in the error message:</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>Now we added information about its parents too:</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>Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <a href="https://github.com/selenide/selenide/pull/1312">PR 1312</a>.</p> <p><br /> <br /></p> <h3 id="we-added-actual-texts-for-owntext-and-exactowntext-checks">We added actual texts for <code class="language-plaintext highlighter-rouge">ownText</code> and <code class="language-plaintext highlighter-rouge">exactOwnText</code> checks</h3> <p>As you remember, in Selenide 5.15.0 we added checks <code class="language-plaintext highlighter-rouge">ownText</code> and <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>But if such check failed, you didn’t see the actual “own text”, but only the whole element’s text with its children:</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>Now we added “Actual value” line with element’s own text:</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>See <a href="https://github.com/selenide/selenide/issues/1261">issue 1261</a> and <a href="https://github.com/selenide/selenide/pull/1294">PR 1294</a>.</p> <p><br /> <br /></p> <h3 id="we-throw-the-right-exception-if-you-try-to-upload-a-wrong-file">We throw the right exception if you try to upload a wrong file</h3> <p>If you tried to upload a missing file:</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>you got an incorrect exception:</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>Not a big deal, but sometimes is might be misleading.</p> <p>Now we throw the right exception:</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>See <a href="https://github.com/selenide/selenide/issues/987">issue 987</a> and <a href="https://github.com/selenide/selenide/pull/1301">PR 1301</a>.</p> <p><br /></p> <blockquote> <p>The root problem that Selenium doesn’t have dedicated exception classes for all possible corner cases. See <a href="https://github.com/selenide/selenide/issues/1293">issues 1293</a> for example. Also some webdriver implementations can throw non-reasonable exceptions - for example, IEDriver threw <code class="language-plaintext highlighter-rouge">Throwable</code> instead of <code class="language-plaintext highlighter-rouge">ElementNotFound</code> when I checked it last time.</p> <p>That’s why we decided to use a conservative strategy in Selenide: if we couldn’t detect the problem precisely, we assume that element not found, and you will see the real problem in the “caused by” block anyway.</p> </blockquote> <p><br /> <br /></p> <h3 id="show-clickoptions-in-the-report">Show ClickOptions in the report</h3> <p>As you remember, in Selenide 5.15.0 we added generic methods for clicking with all kinds of options:</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>And again, we found that such a line was not readably in report:</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>Now we made it readable:</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>See <a href="https://github.com/selenide/selenide/issues/1302">issue 1302</a> and <a href="https://github.com/selenide/selenide/pull/1303">PR 1303</a>.</p> <p><br /></p> <h2 id="other-changes">Other changes</h2> <h3 id="we-added-check-shouldhaveexacttextscasesensitiveinanyorder">We added check <code class="language-plaintext highlighter-rouge">$$.shouldHave(exactTextsCaseSensitiveInAnyOrder(...))</code></h3> <p>There are multiple collection checks in 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">".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">"john"</span><span class="o">,</span> <span class="s">"bob"</span><span class="o">,</span> <span class="s">"katie"</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">"john"</span><span class="o">,</span> <span class="s">"katie"</span><span class="o">,</span> <span class="s">"bob"</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">"john"</span><span class="o">,</span> <span class="s">"bob"</span><span class="o">,</span> <span class="s">"katie"</span><span class="o">));</span> <span class="c1">// case-insensitive, full string match</span> </code></pre></div></div> <p>Now we got one more:</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">"John"</span><span class="o">,</span> <span class="s">"Bob"</span><span class="o">,</span> <span class="s">"Katie"</span><span class="o">));</span> </code></pre></div></div> <p>Thanks to <a href="https://github.com/plagov">Vitali Plagov</a> and <a href="https://github.com/selenide/selenide/pull/1286">PR 1286</a>.</p> <p><br /></p> <h3 id="we-support-hrefs-with-special-symbols">We support <code class="language-plaintext highlighter-rouge">href</code>s with special symbols</h3> <p>As you remember, in Selenide 5.15.0 we added check <code class="language-plaintext highlighter-rouge">href</code>.<br /> But it didn’t support <code class="language-plaintext highlighter-rouge">href</code> values with escaped characters, like in the second line:</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>Now it’s fixed.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1298">issue 1298</a>.<br /> Thanks to <a href="https://github.com/rerednaw">rerednaw</a> and <a href="https://github.com/selenide/selenide/pull/1299">PR 1299</a>.</p> <p><br /></p> <h3 id="allow-chrome-to-download-multiple-files">Allow Chrome to download multiple files</h3> <p>There are such tricky links that cause downloading of two or more files.<br /> We found that Chrome shows a dialog “Are you sure you want all these files?” in this case. And this dialog blocks your test until user clicks “ok”.</p> <blockquote> <p>The worst thing about this problem is that it is very difficult to repeat it by hand: the browser shows the dialog only the first time, so when running locally, most likely you will not see it, and the test will be green.</p> </blockquote> <p>To fix this problem, we added an option <code class="language-plaintext highlighter-rouge">profile.default_content_setting_values.automatic_downloads=1</code> when running Chrome browser. This option allows Chrome to download multiple files without any dialogs.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1307">issue 1307</a>.<br /> Thanks to <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> and <a href="https://github.com/selenide/selenide/pull/1308">PR 1308</a>.</p> <p><br /></p> <h3 id="we-allow-downloading-files-with-slashes-in-name">We allow downloading files with slashes in name</h3> <p>Until now, method <code class="language-plaintext highlighter-rouge">download</code> didn’t allow a file which has slash in name:</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>It seemed logical to us, because no file system allows creating of such files. <br /> But it turned out that sometimes the slash is quite logical - for example, as a separator in dates. And browsers can download such files. For example, Chrome simply replaces the slashes with underscores.</p> <p>Now Selenide also does it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1322">issue 1322</a> and <a href="https://github.com/selenide/selenide/pull/1323">PR 1323</a>.</p> <p><br /></p> <h3 id="fixed-guava-version-300-jre">Fixed Guava version 30.0-jre</h3> <p>Oh, Guava!</p> <p>In Selenide own code, Guava is not used. We don’t directly need it. <br /> But many other libraries still use <em>different versions of Guava</em>: Selenium, LittleProxy, BrowserUpProxy, Checkstyle…</p> <p>Selenide users periodically experienced problems when Maven or Gradle fetched transitively a wrong Guava version, and Selenide didn’t properly work. <br /> We are tired of it. Now Selenide explicitly declares a dependency on latest Guava version <code class="language-plaintext highlighter-rouge">30.0-jre</code>. We hope it will fix all those endless dependency issues.</p> <p><br /></p> <h3 id="we-migrated-to-github-actions">We migrated to Github Actions</h3> <p>As in any decent project, Selenide has its own set of automated tests (unit and integration), and they run automatically for all branches on a CI server. We used to use Travis CI which provides a free service for open-source projects. Thank them very much for the years of joint work. :)</p> <p>But this year, Github started their own CI service “Github actions”. It seemed reasonable to migrate to it.</p> <p>Now all Selenide builds can be found <a href="https://github.com/selenide/selenide/actions">right on github</a>.</p> <p>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> and <a href="https://github.com/selenide/selenide/pull/1319">PR 1319</a>.</p> <p><br /></p> <h2 id="the-ned">The ned</h2> <p>It was a long text, but you mastered it. All good fellows!</p> <p>As usually, update, try, experiment and feel free to report problems and share ideas.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/11/20/selenide-5.16.0/ https://selenide.org/2020/11/20/selenide-5.16.0 2020-11-20T00:00:00+00:00 Why proxy does not work in Selenoid? <p>Good night!</p> <p>Today we will finally reveal the secret of why proxy does not often work in Selenoid.</p> <h3 id="the-goal-download-a-file">The goal: download a file</h3> <ul> <li>We run tests which open browsers in Selenoid containers (usually with Selenide, but it’s not important in this case).</li> <li>We want to download a file during the test.</li> <li>Default method <code class="language-plaintext highlighter-rouge">$.download()</code> doesn’t work (for example, because the download starts by submitting a form - there is no direct link to the file).</li> <li>That’s why we want to <a href="https://selenide.org/2019/12/10/advent-calendar-download-files/">download file via proxy</a>.</li> </ul> <h3 id="our-plan">Our plan</h3> <ol> <li>Create a project</li> <li>Add BrowserUpProxy dependency as shown in Selenide documentation: <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>Copy-paste a typical boilerplate to run browsers in 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>And write a test, something like this: <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="the-problem">The problem</h3> <p>And we get an error when opening a browser:</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="aaa-panic-mode">AAA, panic mode!</h3> <p>At this point, most people panic, go through a bunch of browser options and Selenide settings and in the end write to one of the QA chats.</p> <p>But all you had to do was read the log carefully.</p> <p>The problem is clearly visible in the log:</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="tune-the-dependencies">Tune the dependencies</h3> <p>Exception <code class="language-plaintext highlighter-rouge">NoSuchMethodError</code> clearly says that we have a problem with dependencies: apparently, there are two JAR’s with incompatible versions in classpath.</p> <p>The humanity invented a vaccine against this a long time ago. I wonder why so many people still don’t know it.</p> <p>Let’s run command</p> <ul> <li><code class="language-plaintext highlighter-rouge">gradle dependencies</code>, or</li> <li><code class="language-plaintext highlighter-rouge">mvn dependency:tree</code></li> </ul> <p>And we clearly see which JAR’s have which versions. Let’s look for something similar to “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>We have two jars with different versions: <code class="language-plaintext highlighter-rouge">netty-codec:4.1.44.Final</code> and <code class="language-plaintext highlighter-rouge">netty-all:4.1.34.Final</code>.</p> <h3 id="treating-dependencies">Treating dependencies</h3> <p>There are many ways to fix the problem. Probably the easiest one is to declare Netty versions explicitly in <code class="language-plaintext highlighter-rouge">build.gradle</code> or <code class="language-plaintext highlighter-rouge">pom.xml</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>(actually it’s enough to declare just one of them. <em>Homework for you: which one and why?</em>)</p> <p>Now command <code class="language-plaintext highlighter-rouge">gradle dependencies</code> shows that Netty versions are matching:</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>The test runs, proxy works, file is being downloaded. Everyone is happy.</p> <h3 id="moral">Moral</h3> <p>Pay attention to the logs, our smaller brothers!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid/ https://selenide.org/2020/11/17/why-proxy-does-not-work-in-selenoid 2020-11-17T00:00:00+00:00 Released Selenide 5.15.0 <p>Hi all!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/104?closed=1">Selenide 5.15.0</a>.</p> <p>What’s new?</p> <h2 id="we-added-setting-configurationpageloadtimeout--default-value-is-30-seconds">We added setting <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code> <br /> (default value is 30 seconds)</h2> <p>Sometimes your browser hands trying to load some heavy page, or a huge image or something else.<br /> You loose your time, your build lasts for a long; sessions get expired by timeout etc.</p> <p>It would be reasonable to fail test fast in this case. But the default page loading timeout in Selenium is unreasonable big: the whole 5 minutes.</p> <p>That’s why we added setting <code class="language-plaintext highlighter-rouge">Configuration.pageLoadTimeout</code>, so that you could easily change this timeout.</p> <p>NB! The default is 30 seconds. It might be too small for your application. Be aware.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1268">issue 1268</a> and <a href="https://github.com/selenide/selenide/pull/1269">PR 1269</a>.</p> <h2 id="we-added-generic-click-method-with-clickoptions-parameter">We added generic click method<br /> with <code class="language-plaintext highlighter-rouge">ClickOptions</code> parameter</h2> <p>In a way, this is a new word in Selenide API.</p> <p>Initially, Selenide has method <code class="language-plaintext highlighter-rouge">$.click()</code> which just called standard Selenium method <code class="language-plaintext highlighter-rouge">WebElement.click()</code>. Which clicks to the center of web element (I guess).</p> <p>Over time, variations began to appear:</p> <ul> <li>we added setting <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code> to click using JS code instead of <code class="language-plaintext highlighter-rouge">WebElement.click()</code>. In theory, it should make your tests more stable, and maybe even faster.</li> <li>we added click with offset <code class="language-plaintext highlighter-rouge">$.click(offsetX, offsetY)</code> to click some other point but the center of element.</li> </ul> <p>But if you ever had need to click “both ways” (standard and via JS), you had to switch the global setting <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code> every time. It might be inconvenient.</p> <p>That’s why we added method <code class="language-plaintext highlighter-rouge">click</code> which accepts a “way to click” as an explicit parameter.</p> <p>Now you can click in different ways without changing the global setting:</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! We recommend to set <code class="language-plaintext highlighter-rouge">Configuration.clickViaJs</code> to a value which is reasonable in most cases, and pass <code class="language-plaintext highlighter-rouge">ClickOptions</code> parameter only in exceptional cases.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1173">issue 1173</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1226">PR 1226</a>.</p> <h2 id="we-added-generic-method-download-method-with-downloadoptions-parameter">We added generic method download method<br /> with <code class="language-plaintext highlighter-rouge">DownloadOptions</code> parameter</h2> <p>A similar story is about method <code class="language-plaintext highlighter-rouge">$.download()</code>. Initially it could only download files with <code class="language-plaintext highlighter-rouge">GET</code> request.<br /> Later Selenide added possibility to download files via <code class="language-plaintext highlighter-rouge">PROXY</code> server. And recently we added a third way - <code class="language-plaintext highlighter-rouge">FOLDER</code>.<br /> Until now, you could only select the downloading method global, via global setting <code class="language-plaintext highlighter-rouge">Configuration.fileDownload</code>.</p> <p>Now you can explicitly set downloading mode to every call of <code class="language-plaintext highlighter-rouge">$.download</code> method:</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! We recommend to set <code class="language-plaintext highlighter-rouge">Configuration.fileDownload</code> to a value which is reasonable in most cases, and pass <code class="language-plaintext highlighter-rouge">DownloadsOptions</code> parameter only in exceptional cases.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1259">issue 1259</a> and <a href="https://github.com/selenide/selenide/pull/1260">PR 1260</a>.</p> <h2 id="we-added-methods-to-work-with-localstorage">We added methods to work with 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">// Delete all items from local storage:</span> <span class="n">localStorage</span><span class="o">().</span><span class="na">clear</span><span class="o">();</span> <span class="c1">// Add an item to local storage:</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">// Check item value:</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">// Check items count:</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">// Remove an item from local storage:</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>Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1274">PR 1274</a>.</p> <h2 id="we-added-checks-for-element-own-text-without-descendants">We added checks for element own text (without descendants)</h2> <p>The classical selenide check <code class="language-plaintext highlighter-rouge">$.shouldHave(text("Hello, world"))</code> includes text of element itself and all its children (descendants).<br /> But sometimes you want to check only the element itself, without descendants.</p> <p>Now you can use <code class="language-plaintext highlighter-rouge">ownText</code> check for this:</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">// We expect element text to contain "Hello" world</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">// We expect element have text "Hello"</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/1261">issue 1261</a> and <a href="https://github.com/selenide/selenide/pull/1262">PR 1262</a>.</p> <h2 id="we-improved-performance-of-big-filtered-collections">We improved performance of big filtered collections</h2> <p>Selenide has convenient methods for working with collections. You can search and filter elements, you can checks texts or properties of multiple elements with just one line.</p> <p>But if you overuse this, you might end up with too slow tests. Like this:</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">// Page has 100 &lt;li&gt; elements</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>Assume that there is 100 <code class="language-plaintext highlighter-rouge">&lt;li&gt;</code> elements on the page. Then this test can work too slowly, because:</p> <ul> <li>on each step, Selenide needs to reload the element (its state might have changed)</li> <li>To reload Nth element of a collection, Selenide needs to reload the whole collection (Selenium does not have method like <code class="language-plaintext highlighter-rouge">WebDriver.findElement(index)</code>).</li> <li>To reload a filtered collection, Selenide needs to apply the filter to all its elements (in this case, call <code class="language-plaintext highlighter-rouge">WebElement.isDisplayed()</code> for all 100 elements).</li> </ul> <p><br /> For comparison, this code works much faster:</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">// Unfiltered collection </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>In this release, we improved working we collections. Now Selenide filter for collections is a bit smarter: to get <code class="language-plaintext highlighter-rouge">list.get(N)</code>, it applies <code class="language-plaintext highlighter-rouge">visible</code> filter only to first <code class="language-plaintext highlighter-rouge">&lt;N&gt;</code> elements, not to all 100 elements.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1266">issue 1266</a> and <a href="https://github.com/selenide/selenide/pull/1270">PR 1270</a>.</p> <p><br /> P.S. Let me remind you another way to speed up working with collections - method <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() creates a "dump" of the collection. Selenide will not reload it </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>Method <code class="language-plaintext highlighter-rouge">$$.snapshot()</code> creates a “dump” of the collection. Selenide will not reload it every time.<br /> It makes your test much faster, but there is a risk to get <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code>, if the collection still changes during the iteration.</p> <h2 id="we-added-check-href">We added check “href”</h2> <p>Sometimes you want to check that element <code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> has a correct <code class="language-plaintext highlighter-rouge">href</code> attribute.<br /> Selenide has a method for attribute check: <code class="language-plaintext highlighter-rouge">$("a").shouldHave(attribute("href", "/foo/bar/details.html")</code>.</p> <p>But there is a problem with links. Such a check may unexpectedly fail because Selenium returns an absolute, not a relative URL.</p> <p>To address this issue, we added a special check for “href” attribute:</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>See <a href="https://github.com/selenide/selenide/issues/1272">issue 1272</a> and <a href="https://github.com/selenide/selenide/pull/1273">PR 1273</a>.</p> <h2 id="we-added-chrome-option-no-sandbox">We added chrome option “–no-sandbox”</h2> <p>We found that this option should make Chrome tests more stable. Let’s see if it helps.</p> <p>See <a href="https://github.com/selenide/selenide/commit/3293956d">commit 3293956d</a></p> <h2 id="now-selenide-throws-an-explicit-error">now Selenide throws an explicit error</h2> <p>… if it failed to create downloads folder.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1265">issue 1265</a> and commit 94ece98f](https://github.com/selenide/selenide/commit/94ece98f).</p> <h2 id="upgraded-to-webdrivermanager-422">Upgraded to WebDriverManager 4.2.2</h2> <p>See <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p> <p><br /></p> <h2 id="news">News</h2> <p>Exactly at the moment when I was releasing Selenide 5.15.0, Hima Bindu Peteti presented Selenide on meetup:</p> <ul> <li><a href="https://www.youtube.com/watch?v=-FtWITInoCc&amp;ab_channel=JoeColantonio">Test Automation Made Easy Using Selenide with Hima Bindu Peteti</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/09/26/selenide-5.15.0/ https://selenide.org/2020/09/26/selenide-5.15.0 2020-09-26T00:00:00+00:00 Released Selenide 5.14.0 <p>Good night!</p> <p>We have released <a href="https://github.com/selenide/selenide/milestone/101?closed=1">Selenide 5.14.0</a>.</p> <h2 id="stabilized-new-file-downloading-mode-folder">Stabilized new file downloading mode <code class="language-plaintext highlighter-rouge">FOLDER</code></h2> <p>… which was introduced in <a href="/2020/07/08/selenide-5.13.0/">Selenide 5.13.0</a>.</p> <p>What has been changed in 5.14.0:</p> <ol> <li> <p>Every time when Selenide opens a browser it creates a unique downloads folder for it.<br /> It helps to avoid situation when parallel tests are downloading multiple files to the same folder at the same time. And it’s impossible to understand what file is downloaded by which test.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1220">issue 1220</a> and <a href="https://github.com/selenide/selenide/pull/1221">PR 1221</a>.</p> <ul> <li>Unfortunately, it doesn’t work in IE and Safari (which don’t allow to customize downloads folder).</li> <li>It works only for browsers opened by Selenide itself.</li> <li>If you create browser by yourself, you will need to create the downloads folder and pass it to Selenide: <ul> <li>either using new method <code class="language-plaintext highlighter-rouge">setWebDriver(driver, proxy, downloadsFolder)</code>.</li> <li>or constructor <code class="language-plaintext highlighter-rouge">SelenideDriver(..., downloadsFolder)</code>.</li> </ul> </li> </ul> </li> <li>Before downloading next file, Selenide clears the downloads folder – see <a href="https://github.com/selenide/selenide/pull/1252">PR 1252</a></li> <li>In the end of tests run, Selenide deletes all empty downloads folders – see <a href="https://github.com/selenide/selenide/pull/1247">PR 1247</a></li> </ol> <h2 id="added-check-shouldhaveitemwithtextany-text">Added check <code class="language-plaintext highlighter-rouge">$$.shouldHave(itemWithText("any text"))</code></h2> <p>Unlike the classic check <code class="language-plaintext highlighter-rouge">$$.shouldHave(texts("text1", "text2"))</code>, it verifies that collection contains <strong>at least one element with given text</strong>.</p> <p>Thanks to <a href="https://github.com/LuisOsv">Luis Serna</a> for <a href="https://github.com/selenide/selenide/pull/1194">PR 1194</a>.</p> <p>By the way, it’s the first commit to Selenide from Bolivia! :)</p> <h2 id="added-support-for-safari-browser">Added support for Safari browser</h2> <p>Few years ago Selenide already had Safari support, but it seemed to work badly at that time.<br /> At some moment, we removed the Safari support. <br /> Now it’s the next try. This time Safari tests seem to work more or less.</p> <p>As usually, you need to add just one line to run tests in Safari:</p> <ol> <li><code class="language-plaintext highlighter-rouge">Configuration.browser = "safari";</code> or</li> <li><code class="language-plaintext highlighter-rouge">-Dselenide.browser=safari</code></li> </ol> <p>Please share your feedback.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1236">issue 1236</a> and <a href="https://github.com/selenide/selenide/pull/1237">PR 1237</a>.</p> <h2 id="added-method-selenidedriverscreenshotfilename">Added method <code class="language-plaintext highlighter-rouge">SelenideDriver.screenshot(fileName)</code></h2> <p>It may be useful when you create “non-static” variant of driver (<code class="language-plaintext highlighter-rouge">new SelenideDriver()</code>) and want to take screenshots. <br /> Now you can do it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1166">issue 1166</a> and <a href="https://github.com/selenide/selenide/pull/1227">PR 1227</a>.</p> <h2 id="added-method-selenidedriverscreenshotoutputtype">Added method <code class="language-plaintext highlighter-rouge">SelenideDriver.screenshot(OutputType)</code></h2> <p>Sometimes you need to get screenshot in Base64 format. For example, some visual comparison tools want it.</p> <p>Now you can get screenshot as Base64 (or File or bytes) with new method <code class="language-plaintext highlighter-rouge">screenshot(OutputType)</code>:</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>See <a href="https://github.com/selenide/selenide/issues/1224">issue 1224</a> and <a href="https://github.com/selenide/selenide/pull/1231">PR 1231</a>.</p> <h2 id="now-selenide-takes-a-screenshot-when-method-switchto-fails">Now Selenide takes a screenshot when method <code class="language-plaintext highlighter-rouge">switchTo()</code> fails</h2> <p>As you know, Selenide automatically takes a screenshot in case of test failure. <br /> But we found that Selenide didn’t take a screenshot if one of these methods failed:</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>Now we fixed it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1190">issue 1190</a> and <a href="https://github.com/selenide/selenide/pull/1240">PR 1240</a>.</p> <h2 id="added-chrome-option---disable-dev-shm-usage">Added chrome option <code class="language-plaintext highlighter-rouge">--disable-dev-shm-usage</code></h2> <p>We found that without this option, Chrome might sometimes crash (because of out of memory error).</p> <ol> <li>Why nobody complained about this?</li> <li>Are thing better now after adding this option? Please share your feedback.</li> </ol> <p>P.S. Later some people <a href="https://github.com/selenide/selenide/issues/1559">complained</a> about this option. We are still investigating it.</p> <h2 id="fixed-sizzle-selectors-on-web-pages-containing-dojojs-troopjs-etc">Fixed Sizzle selectors on web pages containing Dojo.js, troop.js etc.</h2> <p>See <a href="https://github.com/selenide/selenide/issues/434">issue 434</a> and <a href="https://github.com/selenide/selenide/pull/1242">PR 1242</a>.</p> <h2 id="made-method-tostring-safer">Made method <code class="language-plaintext highlighter-rouge">$.toString()</code> safer</h2> <p>See <a href="https://github.com/selenide/selenide/issues/1241">issue 1241</a> and <a href="https://github.com/selenide/selenide/pull/1245">PR 1245</a>.</p> <h2 id="improved-error-message-when-web-element-has-disappeared">Improved error message when web element has disappeared</h2> <p>See <a href="https://github.com/selenide/selenide/issues/1013">issue 1013</a> and <a href="https://github.com/selenide/selenide/pull/1239">PR 1239</a>.</p> <h2 id="upgraded-to-webdrivermanager-410">Upgraded to WebDriverManager 4.1.0</h2> <p>See <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Wow! Udemy course including Selenide: “<a href="https://www.udemy.com/course/what-a-java-software-developer-must-know-about-testing/">What every java developer must know about software testing</a>”</li> <li>A video about Selenide <a href="https://www.youtube.com/watch?v=WNzTuYFd8oI">in german</a></li> <li>A project example <a href="https://github.com/d3m0/automation">using Selenide+Selenoid+Docker</a> by <a href="https://github.com/d3m0">d3m0</a></li> <li>Another example: <a href="https://github.com/Crushpowerx/JavaMavenSelenideAllureScreenDiffExample">screenshot comparison using Selenide+Allure+Ashot+Screen Diff Plugin</a> by <a href="https://github.com/Crushpowerx/">Evgeniy Asovin</a></li> <li>One more example: <a href="https://github.com/qaschevychelov/giphyTest">Selenide + Appium + Allure + TestNG</a> by <a href="https://github.com/qaschevychelov/">qaschevychelov</a></li> <li>Selenide vs Selenium comparison: <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>Post by Jakub Skibiński in Sonalake company blog: <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://medium.com/@maxilect_pr/selenide-our-experience-11240f9ce10c">Switch from Serenity to Selenide</a> - 22.05.2020 by <a href="https://medium.com/@maxilect_pr">Yuri Kudryavtsev</a> (Maxilect company)</li> </ul> <p>And a bunch of posts by <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/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/08/17/selenide-5.14.0/ https://selenide.org/2020/08/17/selenide-5.14.0 2020-08-17T00:00:00+00:00 Released Selenide 5.13.0 <p>Good morning!</p> <p>Good summer!</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/98?closed=1">Selenide 5.13.0</a>.</p> <p>Be prepared: it might break your tests (it they were not accurate).</p> <ul class="blogpost-menu"> <li><a href="#should-have-empty-text">Forbidden <code class="language-plaintext highlighter-rouge">shouldHave(text(""))</code></a></li> <li><a href="#remove-unneeded-allure-logs">Removed unneeded Allure logs</a></li> <li><a href="#improve-collection-error-messages">Error messages for collections</a></li> <li><a href="#fix-upload-without-form">Download files without <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code></a></li> <li><a href="#download-files-with-quotes">Download files with quotes in name</a></li> <li><a href="#write-webdriver-logs-to-file">Write webdriver logs to file</a></li> <li><a href="#new-file-download-mode-folder">New downloading algorithm <code class="language-plaintext highlighter-rouge">FOLDER</code></a></li> <li><a href="#get-wrapped-element-waits-for-element">Method <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> waits for the element</a></li> <li><a href="#statistics">Statistics</a></li> </ul> <h2 id="should-have-empty-text">Method <code class="language-plaintext highlighter-rouge">$.shouldHave(text(""))</code> now throws an exception</h2> <p>Probably most popular Selenide method is <code class="language-plaintext highlighter-rouge">$.shouldHave(text("something"))</code>. But if you even use it daily, you might not realize that it checks a <em>substring</em>, not the whole text.</p> <p>So, a check <code class="language-plaintext highlighter-rouge">$("h1").shouldHave(text("ello"))</code> works even for element <code class="language-plaintext highlighter-rouge">&lt;h1&gt;Hello World&lt;/h1&gt;</code>.</p> <p>A special case of this check is empty text: <code class="language-plaintext highlighter-rouge">$("h1").shouldHave(text(""))</code>. This check is <em>useless</em> becase any string contains an empty substring.</p> <blockquote> <p>If you need to check that text is empty, you can use either <code class="language-plaintext highlighter-rouge">$("h1").shouldHave(exactText(""))</code> or <code class="language-plaintext highlighter-rouge">$("h1").shouldBe(empty)</code>.</p> </blockquote> <p>We want to help you overcome this typical problem and avoid useless checks. Now Selenide will throw an exception if you pass null or empty string to method <code class="language-plaintext highlighter-rouge">text("")</code>.</p> <blockquote> <p>I tried Selenide 5.13.0 on my current project and found 20-30 tests that failed because of this new exception.<br /> All of this were logical errors in tests. You see what a useful change waits for you. :)</p> </blockquote> <p>See <a href="https://github.com/selenide/selenide/issues/1156">issue 1156</a>.<br /> Thanks to <a href="https://github.com/eaxdev">Roman S.A.</a> for <a href="https://github.com/selenide/selenide/pull/1186">PR 1186</a>.</p> <h2 id="remove-unneeded-allure-logs">Removed duplicated allure logs</h2> <p>You might see that Selenide may logs the same action twice in some cases. For example, when executing this:</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>(actually this problem is not only in Allure reports, but also in usual Selenide <code class="language-plaintext highlighter-rouge">TextReport</code>)</p> <p>Now we removed those duplicated logs. We had to refactor some code and change some other error messages, so be prepared.</p> <p>See <a href="https://github.com/selenide/selenide/issues/997">issue 997</a> and <a href="https://github.com/selenide/selenide/pull/1193">PR 1193</a>.</p> <h2 id="improve-collection-error-messages">Improved error messages for collections</h2> <p>Another story where we have changed error message format in case of search failure inside a collection. I hope it should be clearer now what exactly Selenide failed to find.</p> <p>See <a href="https://github.com/selenide/selenide/issues/967">issue 967</a> and <a href="https://github.com/selenide/selenide/pull/1189">PR 1189</a>.</p> <h2 id="fix-upload-without-form">Fixed uploading of files without <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code> tag</h2> <p>As you probably know, Selenide allows uploading multiple files with one command:</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>To enable it, we once did a JS trick in Selenide.<br /> Now it appeared that this hack required <code class="language-plaintext highlighter-rouge">&lt;input&gt;</code> to be inside of <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code>. This is reasonable, but for some reason there are inputs that exist outside of <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code>. Our <code class="language-plaintext highlighter-rouge">$.uploadFile()</code> didn’t work for them.</p> <p>Now we simplified upload implementation. Now it works for any inputs and doesn’t use JS tricks.</p> <p>See <a href="https://github.com/selenide/selenide/issues/943">issue 943</a> and <a href="https://github.com/selenide/selenide/pull/1188">PR 1188</a>.</p> <h2 id="download-files-with-quotes">Fixed downloading files with quotation marks in the name</h2> <p>… and other “bad” symbols.</p> <p>It sounds strange, but sometimes people want to download files that have bad symbols in name (quotation marks, apostrophes etc). Linux and Mac can save such files, but Windows cannot. Selenide method <code class="language-plaintext highlighter-rouge">$.download()</code> failed on Windows for such files.</p> <p>Now method <code class="language-plaintext highlighter-rouge">$.download()</code> replaces quotation marks and other “bad” symbols in file name by underscore, as all major browsers do.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1196">issue 1196</a> and <a href="https://github.com/selenide/selenide/pull/1199">PR 1199</a>.</p> <h2 id="write-webdriver-logs-to-file">Configured webdriver to write its logs to a file</h2> <p>Until now, webdrivers started by Selenide didn’t write their logs to anywhere. You had to enable it explicitly if you needed them.</p> <p>Now Selenide enables webdriver logs by default. They are written to file like <code class="language-plaintext highlighter-rouge">build/reports/tests/webdriver.ts_pid_tid.log</code>.<br /> You can get full file name from Selenide logs like this:</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>Read these logs next time when you need to debug some weird webdriver behaviour.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1206">issue 1206</a> and <a href="https://github.com/selenide/selenide/pull/1207">PR 1207</a>.</p> <h2 id="new-file-download-mode-folder">Added new way of downloading files: <code class="language-plaintext highlighter-rouge">FOLDER</code></h2> <p>As you probably know, Selenide has 2 options for downloading files: <code class="language-plaintext highlighter-rouge">HTTPGET</code> and <code class="language-plaintext highlighter-rouge">PROXY</code>.<br /> See <a href="/2019/12/10/advent-calendar-download-files/">post in our blog</a>.</p> <ul> <li><code class="language-plaintext highlighter-rouge">HTTPGET</code> - simple and stable. But it can only download files using links like <code class="language-plaintext highlighter-rouge">&lt;a href&gt;</code>.</li> <li><code class="language-plaintext highlighter-rouge">PROXY</code> - generic and powerful. But it may cause problems if you run browser remotely because it requires access from “browser machine” to “tests machine”. It often causes troubles for Selenoid and Grid users.</li> </ul> <p>Now we have a third optionL: <code class="language-plaintext highlighter-rouge">FOLDER</code>.</p> <p>To use it, just add this line in the beginning of tests:</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>It’s working mechanism is very simple:</p> <ol> <li>It clicks the element,</li> <li>Watches for new files in folder <code class="language-plaintext highlighter-rouge">build/downloads</code></li> <li>If there are several new files, tries to guess which one if them suites better.</li> </ol> <p>As always, you can find a working example <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadToFolderTest.java">in Selenide own tests</a>.</p> <p><br /> P.S. We consider this option <em>experimental</em> by now because there are some nuances:</p> <ol> <li>It works well on local run with single browser, but</li> <li>During parallel run, multiple browsers might download their files into the same folder, and then Selenide might pick a wrong file.</li> <li>During remote run, this option doesn’t work at all. Tests are on machine A, but browser and files are on machine B.</li> <li>Currently only Chrome, Firefox, Edge, Opera are supported. But IE and Safari are not: they don’t allow to configure the downloads folder.</li> </ol> <p>We will work on those nuances. Please share your thoughts on how can we solve them.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1212">issue 1212</a>, <a href="https://github.com/selenide/selenide/pull/1213">PR 1213</a> and <a href="https://github.com/selenide/selenide/pull/1215">PR 1215</a>.</p> <p>UPD Later we improved method <code class="language-plaintext highlighter-rouge">FOLDER</code>, and now you can safely use it in your projects.</p> <h2 id="get-wrapped-element-waits-for-element">Method <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> waits for the element. Again.</h2> <p>Probably it doesn’t affect you, but I have to mention it. Essentially, we reverted one recent change (made in Selenide 5.11).</p> <p>Assuming you have the following code:</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>where <code class="language-plaintext highlighter-rouge">button</code> is shown <em>with a delay</em>.</p> <p>Selenide always waits for this <code class="language-plaintext highlighter-rouge">button</code> to appear, and only then executed the JS code.<br /> In Selenide 5.11 we did a change, so that Selenide didn’t wait for the <code class="language-plaintext highlighter-rouge">button</code> anymore.<br /> It could break some tests which occasionally relied on that behaviour. <br /> Now we restored that old behaviour: Selenide waits for the <code class="language-plaintext highlighter-rouge">button</code> again before executing JS code.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1191">issue 1191</a> and <a href="https://github.com/selenide/selenide/pull/1203">PR 1203</a>.</p> <h2 id="upgrade-to-bup-2.1.1">Upgraded to BrowserUpProxy 2.1.1</h2> <p>Just in case</p> <p>Upgrade. Try. Share you feedback.</p> <h2 id="statistics">Statistics</h2> <p>And here is my favorite part: latest Selenide downloads statistics.<br /> We hit the line <strong>160 thousands per months</strong>!</p> <center> <img src="/images/2020/07/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p>and <strong>31+ thousands of unique IPs</strong>:</p> <center> <img src="/images/2020/07/selenide.unique-ips.png" width="800" /> </center> <p>Life is good!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/07/08/selenide-5.13.0/ https://selenide.org/2020/07/08/selenide-5.13.0 2020-07-08T00:00:00+00:00 Released Selenide 5.12.2 <p>Hi all!</p> <p>We have published one more mini-release <a href="https://github.com/selenide/selenide/milestone/99?closed=1">Selenide 5.12.2</a>.</p> <h2 id="fixed-annotations-nonnull">Fixed annotations @Nonnull</h2> <p>… for some of <code class="language-plaintext highlighter-rouge">SelenideElement</code> methods.</p> <p>After upgrading to Selenide 5.12.0 some Kotlin users started getting compilation errors in their projects. <br /> It’s because we marked all methods of <code class="language-plaintext highlighter-rouge">SelenideElement</code> with annotations <code class="language-plaintext highlighter-rouge">@Nullable</code>/<code class="language-plaintext highlighter-rouge">@Nonnull</code>, and Kotlin is sensible to them.</p> <p>For the following methods we now changed <code class="language-plaintext highlighter-rouge">@Nullable</code> annotation to <code class="language-plaintext highlighter-rouge">@Nonnull</code> (because they never return 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>Now you can again put results of those methods to non-nullable variables (though such constructs seem to me a bit strange in tests).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1179">issue 1179</a> and <a href="https://github.com/selenide/selenide/pull/1181">PR 1181</a>.</p> <h2 id="fixed-setting-holdbrowseropentrue">Fixed setting <code class="language-plaintext highlighter-rouge">holdBrowserOpen=true</code></h2> <p>This setting didn’t work sometimes.<br /> No we fixed it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1172">issue 1172</a> and <a href="https://github.com/selenide/selenide/pull/1176">PR 1176</a>.</p> <h2 id="news">News</h2> <p>Wow!</p> <p>It happened!!!</p> <p>We published <a href="https://github.com/selenide/selenide-for-selenium-ide">Selenium IDE plugin</a> which can <strong>export code to Selenide</strong>.</p> <ul> <li><a href="https://chrome.google.com/webstore/detail/selenide-for-selenium-ide/nlkfobhoffngaakgdbkdnmmjcchibcba">for Chrome</a></li> <li><a href="https://addons.mozilla.org/ru/firefox/addon/selenide-for-selenium-ide/">for Firefox</a></li> </ul> <p>We will publish a separate post soon on this topic.</p> <p>Huge thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for this historical moment for Selenide!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/05/29/selenide-5.12.2/ https://selenide.org/2020/05/29/selenide-5.12.2 2020-05-29T00:00:00+00:00 Released Selenide 5.12.1 <p>Good night!</p> <p>This is a quickfix release <a href="https://github.com/selenide/selenide/milestone/97?closed=1">Selenide 5.12.1</a> with a couple of minor fixes for <a href="/2020/05/23/selenide-5.12.0/">Selenide 5.12.0</a></p> <h2 id="fixed-concurrent-modification-exception-during-webdriver-initialization">Fixed <em>Concurrent modification exception</em> during webdriver initialization</h2> <p>See <a href="https://github.com/selenide/selenide/issues/1170">issue 1170</a> and <a href="https://github.com/selenide/selenide/pull/1171">PR 1171</a>.</p> <h2 id="fixed-merging-of-excludeswitches-settings-of-different-types">Fixed merging of “excludeSwitches” settings of different types</h2> <p>It appeared that setting <code class="language-plaintext highlighter-rouge">excludeSwitches</code> can have either Array or List type:</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>Selenide 5.12.0 crashed if both of types were used. Now we fixed it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1169">issue 1169</a> and <a href="https://github.com/selenide/selenide/pull/1174">PR 1174</a>.</p> <h2 id="news">News</h2> <ul> <li>We published <a href="https://github.com/selenide/selenide/wiki/Selenide-Roadmap">Selenide Roadmap</a>. Feel free to share your feedback!</li> <li>Vide from <a href="https://www.youtube.com/watch?v=1d-nKyeTH2Y">pro:TEST meetup</a> - Czech, 28.04.2020</li> <li>Video from <a href="https://www.youtube.com/watch?v=aFqZ6dbUJIw&amp;feature=emb_logo">QA meetup</a> - Slovak, 12.05.2020</li> </ul> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/05/25/selenide-5.12.1/ https://selenide.org/2020/05/25/selenide-5.12.1 2020-05-25T00:00:00+00:00 Released Selenide 5.12.0 <p>Good day!</p> <p>Yes! We released <a href="https://github.com/selenide/selenide/milestone/95?closed=1">Selenide 5.12.0</a>.</p> <p>Most of the changes are about browser capabilities.</p> <h2 id="we-fixed-an-old-problem-with-configurationbrowsercapabilities">We fixed an old problem with <code class="language-plaintext highlighter-rouge">Configuration.browserCapabilities</code></h2> <p>People had been complaining for years about <code class="language-plaintext highlighter-rouge">ChromeOptions</code>: some of these settings could be lost in some circumstances. <br /> It was caused by <a href="https://github.com/SeleniumHQ/selenium/issues/5279">an old bug in Selenium webdriver</a>, which seems to be lost and forgotten.</p> <p>But it seems that we have found a simple workaround for this problem.</p> <ul> <li>If it helped you - please share you feedback.</li> <li>If it didn’t help - please register a github issue. We will work out another hack. :)</li> </ul> <p>See <a href="https://github.com/selenide/selenide/issues/676">issue 676</a>, <a href="https://github.com/selenide/selenide/issues/1097">issue 1097</a> and <a href="https://github.com/selenide/selenide/pull/1155">PR 1155</a>.</p> <p>Special thanks to those experiments that didn’t get to the release, but inspired it:</p> <ul> <li>Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1103">PR #1103</a></li> <li>Thanks to <a href="https://github.com/SeleniumTestAB">SeleniumTestAB</a> for <a href="https://github.com/selenide/selenide/pull/1095">PR #1095</a></li> </ul> <h2 id="we-disabled-to-annoying-dialog-save-password">We disabled to annoying dialog “save password?”</h2> <p>See <a href="https://github.com/selenide/selenide/issues/1133">issue 1133</a> and <a href="https://github.com/selenide/selenide/pull/1134">PR 1134</a>.</p> <h2 id="we-adeed-mobile-emulation-mode-in-grid">We adeed “mobile emulation” mode in grid</h2> <p>Since Selenide 5.6.1, you can run Chrome in “mobile emulation” mode:</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>Recently we found that this option only works when running Chrome locally, and was not passed to the remote Chrome.<br /> Now we fixed it: when running Chrome in Grid, we pass all options that are used for local Chrome (except “downloads folder”).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1109">issue 1109</a> and <a href="https://github.com/selenide/selenide/pull/1163">PR 1163</a>.</p> <h2 id="we-simplified-firefox-setup-now-without-profile">We simplified Firefox setup: now without profile</h2> <p>When opening a Firefox browser, Selenide created a new profile (<code class="language-plaintext highlighter-rouge">FirefoxProfile</code>) - at least to set a “downloads folder” setting.<br /> Recently we found that using profile causes some limitations, and generally is not needed (I guess profile was needed for the legacy firefox driver).</p> <p>Now we don’t create a profile by default.</p> <ol> <li>Instead, we set downloads folder via <code class="language-plaintext highlighter-rouge">firefoxOptions.addPreference("browser.download.dir")</code>.</li> <li>We only create profile if you set some system settings starting with <code class="language-plaintext highlighter-rouge">"firefoxprofile."</code>.</li> </ol> <p>See <a href="https://github.com/selenide/selenide/issues/1139">issue 1139</a> and <a href="https://github.com/selenide/selenide/pull/1165">PR 1165</a>.</p> <h2 id="we-set-accept_insecure_certs-setting-for-chromium-based-edge-versions">We set <code class="language-plaintext highlighter-rouge">"ACCEPT_INSECURE_CERTS"</code> setting for chromium-based Edge versions</h2> <p>Starting from some version, IE and Edge browser stopped supporting <code class="language-plaintext highlighter-rouge">"ACCEPT_INSECURE_CERTS"</code> setting<br /> (which enables self-signed SSL certificates). So we removed this setting for IE and Edge (starting from Selenide 5.9.0).</p> <p>Recently we found that latest versions of Edge browser (which are built on Chromium engine) do support this setting. <br /> So we set <code class="language-plaintext highlighter-rouge">"ACCEPT_INSECURE_CERTS"</code> again for Edge 75 and newer.</p> <p>NB! Selenide can know Edge version only if EdgeDriver was downloaded with WebDriverManager (which is enabled by default in Selenide).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1093">issue 1093</a> and <a href="https://github.com/selenide/selenide/pull/1167">PR 1167</a>.</p> <h2 id="we-upgraded-to-webdrivermanager-400">We upgraded to WebDriverManager 4.0.0</h2> <ul> <li>Support for Firefox 76</li> <li>Support for Edge 81, 83, 84</li> <li>Upgraded Apache HttpClient from 4.x to 5.0</li> </ul> <p>See <a href="https://github.com/selenide/selenide/pull/1149">PR 1149</a> and <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">WDM Changelog</a>.</p> <h2 id="we-upgraded-to-browserup-proxy-core210">We upgraded to browserup-proxy-core:2.1.0</h2> <p>Just in case.</p> <h2 id="we-fixed-name-of-screenshot-folder-emptymethod">We fixed name of screenshot folder ‘emptyMethod’</h2> <p>This issue was specific to JUnit 5.</p> <p>Thanks to <a href="https://github.com/dengayevskiy-sb">Denis Gaievsky</a> for <a href="https://github.com/selenide/selenide/pull/1138">PR 1138</a>.</p> <h2 id="annotated-most-of-the-public-selenide-methods-with-nullable-and-nonnull">Annotated most of the public Selenide methods with <code class="language-plaintext highlighter-rouge">@Nullable</code> and <code class="language-plaintext highlighter-rouge">@Nonnull</code></h2> <p>It will help IDEA (and hopefully other IDEs) to better highlight issues in your tests. <br /> It will also help Kotlin users to use nullable/non-nullable types.</p> <p>Again, thanks to <a href="https://github.com/jreznot">Yuriy Artamonov</a> for <a href="https://github.com/selenide/selenide/pull/1140">PR 1140</a> and <a href="https://github.com/selenide/selenide/pull/1144">PR 1144</a>!</p> <h2 id="news">News</h2> <ul> <li>We published <a href="https://github.com/selenide/selenide/wiki/Selenide-Roadmap">Selenide Roadmap</a>. Feel free to share your feedback!</li> <li>Vide from <a href="https://www.youtube.com/watch?v=1d-nKyeTH2Y">pro:TEST meetup</a> - Czech, 28.04.2020</li> <li>Video from <a href="https://www.youtube.com/watch?v=aFqZ6dbUJIw&amp;feature=emb_logo">QA meetup</a> - Slovak, 12.05.2020</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/05/23/selenide-5.12.0/ https://selenide.org/2020/05/23/selenide-5.12.0 2020-05-23T00:00:00+00:00 Released Selenide 5.11.1 <p>Good evening!</p> <p>It seems that <a href="https://github.com/selenide/selenide/milestone/94?closed=1">Selenide 5.11.0</a> still broke too many rules, and we decided to slow down the horses.</p> <p>If you are affected, you can upgrade to <a href="https://github.com/selenide/selenide/milestone/96?closed=1">Selenide 5.11.1</a>.</p> <h2 id="slf4j">SLF4J</h2> <p>Some folks didn’t like the fact that Selenide 5.11.0 requires a valid slf4j dependency. Folks don’t want to bother with slf4j. Folks don’t want to read our logs. :)</p> <p>It was unexpected for us, but well, we found a compromise.</p> <p>Now Selenide doesn’t require slf4j always, but only when it’s needed for sure. Namely, when you use <a href="/2016/09/26/selenide-3.10/"><code class="language-plaintext highlighter-rouge">TextReport</code> feature</a>.</p> <h2 id="because-we-can">because we can!</h2> <p>In Selenide 5.11.0 we introduced one small issue. Namely,</p> <ul> <li><code class="language-plaintext highlighter-rouge">$("blah").shouldNot(exist)</code> - passes (it’s ok)</li> <li><code class="language-plaintext highlighter-rouge">$("blah").shouldNot(exist.because("we can"))</code> - fails (it’s not ok)</li> </ul> <p>Word <code class="language-plaintext highlighter-rouge">because</code> happened to be incompatible with negation. Now we fixed it, and both lines are passing.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1130">issue 1130</a> and <a href="https://github.com/selenide/selenide/pull/1131">1131</a>.</p> <h2 id="dropped-16-megabytes">Dropped 16 megabytes</h2> <p>It appears that Selenide had one huge 16 megabyte sized file <code class="language-plaintext highlighter-rouge">checker.jar</code> among its dependencies.<br /> Now we removed it.</p> <p>Thanks to <a href="https://github.com/jreznot">Yuriy Artamonov</a> for <a href="https://github.com/selenide/selenide/pull/1128">PR 1128</a>.</p> <h2 id="news">News</h2> <p>I will present Selenide on Czech meetup <a href="https://www.meetup.com/protest_cz/events/270022839/">[pro:]TEST!</a> next Tuesday, 28.04.2020.<br /> This is an online event, so everybody can participate for free. You can call your friends who is not familiar with Selenide yet!</p> <ul> <li>Language: English</li> <li>Date: 28.04.2020, 18:00 GMT+2</li> <li>Level: rather for beginners</li> <li>Links: <a href="https://bit.ly/protest84invitation">Announcement</a> / <a href="https://www.youtube.com/watch?v=1d-nKyeTH2Y&amp;feature=youtu.be">Live stream</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>See you online!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/04/21/selenide-5.11.1/ https://selenide.org/2020/04/21/selenide-5.11.1 2020-04-21T00:00:00+00:00 Released Selenide 5.11.0 <p>Good night!</p> <p>We have released <a href="https://github.com/selenide/selenide/milestone/94?closed=1">Selenide 5.11.0</a>.<br /> This is the second quarantine release of Selenide in which we made a couple of breaking changes.</p> <h2 id="changed-the-behaviour-of-shouldnot-methods-for-unexisting-elements">Changed the behaviour of <code class="language-plaintext highlighter-rouge">shouldNot*</code> methods for unexisting elements</h2> <p>Just look at the table below to understand what changed.<br /> Assuming element <code class="language-plaintext highlighter-rouge">h1</code> <strong>does not exist</strong> on a page:</p> <table> <thead> <tr> <th>Check</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> <h3 id="previous-logic">Previous logic</h3> <p>A long time ago it was decided in Selenide: the check <code class="language-plaintext highlighter-rouge">h1.shouldNotHave(text("foo"))</code> should not fail. If the element <code class="language-plaintext highlighter-rouge">&lt;h1&gt;</code> does not exist, then it doesn’t have text “foo”. So, the condition “should not have text” is true.</p> <p>Additionally, it seemed to be reasonable because the check <code class="language-plaintext highlighter-rouge">h1.shouldNotHave(text("foo"))</code> is the opposite to <code class="language-plaintext highlighter-rouge">h1.shouldHave(text("foo"))</code> which fails for an unexisting element.</p> <h3 id="new-logic">New logic</h3> <p>But we realized that there was a problem with the previous approach: it allowed to make a mistake too easily. Namely, you could accidentally write a wrong locator, thus making a false-positive test. Test that was green, but didn’t check anything.</p> <p>Now both <code class="language-plaintext highlighter-rouge">h1.shouldHave(text("foo"))</code> and <code class="language-plaintext highlighter-rouge">h1.shouldNotHave(text("foo"))</code> fail if <code class="language-plaintext highlighter-rouge">&lt;h1&gt;</code> is not found.</p> <p>See <a href="https://github.com/selenide/selenide/issues/368">issue 368</a> and <a href="https://github.com/selenide/selenide/pull/1116">PR 1116</a>.</p> <h2 id="now-selenide-fails-if-slf4j-is-not-configured">Now Selenide fails if SLF4J is not configured</h2> <p>If you get such an error:</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>don’t be afraid: just do what it says. It’s easy.</p> <h4 id="what-problem-we-tried-to-solve">What problem we tried to solve?</h4> <p>The problem is: if you don’t have any SLF4J implementations in your project, you could miss some important Selenide logs, including “text report”. Now you are forced to include some of SLF4J implementations.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1114">issue 1114</a> and <a href="https://github.com/selenide/selenide/pull/1115">PR 1115</a>.</p> <h2 id="added-method-for-partially-checking-the-attribute-value">Added method for <em>partially</em> checking the attribute value</h2> <p>Until now, Selenide had 2 methods for checking attributes:</p> <ol> <li>presence of attribute, and</li> <li>exact value of attribute:</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>Now we added method <code class="language-plaintext highlighter-rouge">attributeMatching</code> for <em>partially</em> checking attribute value.<br /> See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/AttributeTest.java">in tests</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>See <a href="https://github.com/selenide/selenide/issues/996">issue 996</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1100">PR 1100</a>.</p> <h2 id="added-method-for-getting-the-last-screenshot">Added method for getting the last screenshot</h2> <p>We added 2 new methods:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Screenshots.getLastThreadScreenshot()</code> - returns the last screenshot taken in current thread</li> <li><code class="language-plaintext highlighter-rouge">Screenshots.getThreadScreenshots()</code> - returns all screenshots taken in current thread</li> </ul> <p>Most of the users <strong>do not need</strong> those methods: Selenide adds screenshot details to error message anyway. <br /> But those methods can be useful for those who write their own frameworks on top of Selenide or integrate Selenide with frameworks like Allure.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1029">issue 1029</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1125">PR 1125</a>.</p> <h2 id="added-annotation-checkreturnvalue-to-most-of-selenide-methods">Added annotation <code class="language-plaintext highlighter-rouge">@CheckReturnValue</code> to most of Selenide methods</h2> <p>It allows IDE (at least Intellij IDEA) to better analyze your tests and show warnings if you called some Selenide method, but forgot to check results:</p> <center> <img src="/images/2020/04/idea-warning.png" width="500" /> </center> <p>Thanks to <a href="https://github.com/jreznot">Yuriy Artamonov</a> for <a href="https://github.com/selenide/selenide/pull/1106">PR 1106</a>.</p> <h2 id="added-missing-method-selectorsbytagname">Added missing method <code class="language-plaintext highlighter-rouge">Selectors.byTagName()</code></h2> <p>Just for consistency with <code class="language-plaintext highlighter-rouge">By</code> methods.</p> <p>Thanks to <a href="https://github.com/jreznot">Yuriy Artamonov</a> for <a href="https://github.com/selenide/selenide/pull/1104">PR 1104</a>.</p> <h2 id="fixed-screenshot-url">Fixed screenshot URL</h2> <p>… in case when project is run in Jenkins, and the project path contains spaces.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1072">issue 1072</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1098">PR 1098</a>.</p> <h2 id="disabled-annoying-warnings-about-chrome-extensions">Disabled annoying warnings about Chrome extensions</h2> <p>Some folks saw those annoying warnings in Chrome:</p> <center> <img src="/images/2020/04/chrome-warning.png" width="300" /> </center> <p>Now we disabled them with this setting:</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>Please let us know if it causes some problems in your projects.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1119">issue 1119</a> and <a href="https://github.com/selenide/selenide/pull/1120">PR 1120</a>.</p> <h2 id="now-you-can-set-selectormode-and-assertionmode-via-system-properties">Now you can set <code class="language-plaintext highlighter-rouge">selectorMode</code> and <code class="language-plaintext highlighter-rouge">assertionMode</code> via system properties</h2> <p>… Just for consistency with other Selenide settings.</p> <p>See <a href="https://github.com/selenide/selenide/commit/231597eb6229e">commit 231597eb6229e</a>.</p> <h2 id="method-getwrappedelement-doesnt-wait-anymore">Method <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> doesn’t wait anymore</h2> <p>I guess most of you didn’t even know about this method.<br /> A long time ago, it was added to give access to the original Selenium <code class="language-plaintext highlighter-rouge">WebElement</code> without any Selenide magic (just in case, if somebody should need it).<br /> But <a href="https://github.com/yashaka">Iakiv Kramarenko</a> noticed that it still applies Selenide magic: method <code class="language-plaintext highlighter-rouge">$.getWrappedElement()</code> waited for presence of the <code class="language-plaintext highlighter-rouge">WebElement</code>.</p> <p>Not it doesn’t wait anymore. If the element doesn’t exist - you will get <code class="language-plaintext highlighter-rouge">org.openqa.selenium.NoSuchElementException</code>.<br /> And yes, you can also get the legendary <code class="language-plaintext highlighter-rouge">StaleElementReferenceException</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1015">issue 1015</a> and <a href="https://github.com/selenide/selenide/pull/1124">PR 1124</a>.</p> <h2 id="statistics">Statistics</h2> <p>And here is my favorite part: latest Selenide downloads statistics. We hit the line 130 thousands per months!</p> <center> <img src="/images/2020/04/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p>and 23 thousands of unique IPs:</p> <center> <img src="/images/2020/04/selenide.unique-ips.png" width="800" /> </center> <p>Life is good!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/04/19/selenide-5.11.0/ https://selenide.org/2020/04/19/selenide-5.11.0 2020-04-19T00:00:00+00:00 Released Selenide 5.10.0 <p>Good quarantine!</p> <p>It was the third day of quarantine.</p> <p>To make you feel not so lonely, we released <a href="https://github.com/selenide/selenide/milestone/93?closed=1">Selenide 5.10.0</a> with a bunch of improvements. Some of them are even slightly backward incompatible. Well, so you do not get bored in your dens.</p> <ul> <li><a href="#add-shadow-dom-support">Added support for Shadow DOM</a></li> <li><a href="#exclude-bup-by-default">Don’t fetch BrowserUpProxy by default</a></li> <li><a href="#replace-guava">Replace Guava APIs by Java APIs</a></li> <li><a href="#add-quotes-to-report">Made Selenide Allure report nicer</a></li> <li><a href="#add-image-condition">Added condition shouldBe(image)</a></li> <li><a href="#video">Videos</a></li> </ul> <h2 id="add-shadow-dom-support">We added support for Shadow DOM</h2> <p>See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ShadowElementTest.java">in tests</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>: Calling <code class="language-plaintext highlighter-rouge">setValue("text")</code> / <code class="language-plaintext highlighter-rouge">val("text")</code> on an input field will result in an error, saying that the element is “not reachable by keyboard”. <br /> Temporary workaround for now is to use <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>See <a href="https://github.com/selenide/selenide/issues/1014">issue 1014</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1090">PR 1090</a>.</p> <h2 id="exclude-bup-by-default">Selenide does not fetch BrowserUpProxy by default</h2> <p>Taking those two facts into account:</p> <ol> <li>Selenide fetches BrowserUpProxy and all its transitive dependencies - it’s about ~17MB in total.</li> <li>Most of Selenide users (I guess) do not use Selenide proxy.</li> </ol> <p>we decided to avoid fetching those 17 megabytes by default.</p> <h3 id="the-dependency">The dependency</h3> <p>Those of you who wants to use proxy just need to add a dependency to your project:</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>(many of you already have it anyway).</p> <p>Don’t worry if you forget to add the dependency - you will see a clear error message like this:</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>See <a href="https://github.com/selenide/selenide/issues/1021">issue 1021</a> and <a href="https://github.com/selenide/selenide/pull/1094">PR 1094</a>.</p> <p><strong>UPD 12.03.2023</strong> Nowadays we don’t need to add that dependency anymore. Just use <code class="language-plaintext highlighter-rouge">com.codeborne:selenide-proxy</code> instead of <code class="language-plaintext highlighter-rouge">com.codeborne:selenide</code>. It will add all needed dependencies automatically.</p> <h3 id="just-in-case">Just in case</h3> <p>Probably you feel curious who eats those 17 megabytes? Here they are. <br /> This is the list of files that should disappear from your project. The list is impressive, right?</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><br /></p> <h2 id="replace-guava">Migrated Guava API to corresponding Java API</h2> <p>We just changed</p> <ul> <li><code class="language-plaintext highlighter-rouge">com.google.common.base.Predicate</code> from Guava</li> <li>to <code class="language-plaintext highlighter-rouge">java.util.function.Predicate</code> from Java 8</li> </ul> <p>and removed Guava dependency. It was great (until Java 8 was released), rip.</p> <p>If you occasionally implemented your own <code class="language-plaintext highlighter-rouge">CollectionCondition</code>, you will need to rename <code class="language-plaintext highlighter-rouge">apply</code> to <code class="language-plaintext highlighter-rouge">test</code>. It must be easy.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1091">issue 1091</a>.<br /> Thanks to <a href="https://github.com/wlsc">Wladimir Schmidt</a> for <a href="https://github.com/selenide/selenide/pull/1091">PR 1091</a>.</p> <p><br /></p> <h2 id="add-quotes-to-report">We made Selenide Allure report a little nicer</h2> <p>We actually just added quotes around selectors in the Allure report.<br /> Now you can copy selectors from the report and paste into browser’s developer console (though, I cannot imagine why somebody should do it).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1032">issue 1032</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1092">PR 1092</a>.</p> <p><br /></p> <h2 id="add-image-condition">We added condition <code class="language-plaintext highlighter-rouge">$("img").shouldBe(image)</code></h2> <p>You can use it to check that image was successfully loaded.</p> <p>See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ImageTest.java">in tests</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>See <a href="https://github.com/selenide/selenide/issues/1069">issue 1069</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1086">PR 1086</a>.</p> <p><br /></p> <h2 id="fix-search-by-attribute-with-quotes">We fixed find element by attribute which contains quites</h2> <p>For some reason, people sometimes put quotes inside attributes.<br /> When trying to find an element by such an attribute, Selenide generated an invalid CSS locator.<br /> Now we fixed it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1060">issue 1060</a>.<br /> Thanks to <a href="https://github.com/denysLystopadskyy">Denys Lystopadskyy</a> for <a href="https://github.com/selenide/selenide/pull/1062">PR 1062</a>.</p> <p><br /></p> <h2 id="video">Videos</h2> <p>Did you think that probably SeleniumCamp 2020 is The Last Conference on Earth?</p> <p>Because of quarantine, they have published all <a href="https://www.youtube.com/playlist?list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe">SeleniumCamp 2020 videos</a>.</p> <p>Among others, this playlist contains my 3 videos (sorry, they are in Russian):</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: Glorious past and promising future of Selenide</a></li> <li><a href="https://www.youtube.com/watch?v=4vI4Z6sE7OA&amp;list=PLa7q-VITePQWDxFmiDrwlBZ1E9k_nnqLe&amp;index=16">Threesome: Selenide for Web, Android and iOS</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/03/18/selenide-5.10.0/ https://selenide.org/2020/03/18/selenide-5.10.0 2020-03-18T00:00:00+00:00 Released Selenide 5.9.0 <p>Buenos Marchos!</p> <p>In Selenide, we strive to cure flaky tests all the time.</p> <p>Today we released <a href="https://github.com/selenide/selenide/milestone/92?closed=1">Selenide 5.9.0</a> with one feature that can help to overcome some of your flaky tests.</p> <h2 id="we-added-a-filter-for-downloading-files-downloadfilefilter">We added a filter for downloading files: <code class="language-plaintext highlighter-rouge">$.download(FileFilter)</code></h2> <h3 id="problem">Problem</h3> <p>When downloading files <em>via proxy</em>, Selenide can sometimes download a wrong file. <br /> This is how it works: Selenide clicks the “download” button and intercepts all responses from server to browser.</p> <p>Exactly at this moment there could happen some unrelated requests between browser and server.<br /> Say, chrome decides to check for updates at this moment. Or your AUT sends request to google analytics. Or just some background requests. <br /> This creates fertile ground for emergence of <em>flaky tests</em> which work on your machine but sometimes fail on jenkins.</p> <h3 id="solution">Solution</h3> <p>To avoid such collisions, you can now explicitly specify which file you expect. Out of the box, there are filters by file name or extension. And you can create your own <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> implementations with any filters you need.<br /> Given such a filter, Selenide will select the right response matching your criteria.</p> <p>See examples <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/FileDownloadViaProxyTest.java">in tests</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>See <a href="https://github.com/selenide/selenide/issues/1065">issue 1065</a> and <a href="https://github.com/selenide/selenide/pull/1080">PR 1080</a>.</p> <h2 id="we-fixed-error-when-starting-ie-3150">We fixed error when starting IE 3.150</h2> <p>See <a href="https://github.com/selenide/selenide/issues/1061">issue 1061</a>.<br /> Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1075">PR 1075</a>.</p> <h2 id="we-fixed-error-when-starting-microsoft-edge">We fixed error when starting Microsoft Edge</h2> <p>See <a href="https://github.com/selenide/selenide/issues/1039">issue 1039</a>.<br /> Thanks to <a href="https://github.com/BorisOsipov">Boris Osipov</a> for <a href="https://github.com/selenide/selenide/pull/1084">PR 1084</a>.</p> <h2 id="news">News</h2> <ul> <li> <p>A funny <a href="https://twitter.com/titusfortner/status/1234862932036608001">dialog</a> in Twitter: contributor of Selenium and Watir projects Titus Fortner admitted that he watched <a href="/2015/09/23/selenide-on-seleniumconf/">my presentation about Selenide on SeleniumConf</a>, and even stole some ideas for Watir. And I admitted that some of Selenide ideas were initially stolen from Watir. :)</p> </li> <li>A good article <a href="https://phauer.com/2019/modern-best-practices-testing-java/">Modern Best Practices for Testing in Java</a>. A plenty of right ideas.</li> <li>A post <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 post <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>Some base project <a href="https://github.com/romsper/qa-automation-base/tree/kotlin-junit5-appium">qa-automation-base</a> which has mixed Kotlin + Selenide/Appium + JUnit 5 + Allure + Allure EE + TestRail. <br /></li> </ul> <h2 id="statistics">Statistics</h2> <p>And for dessert - Selenide download statistics. We are growing!</p> <center> <img src="/images/2020/03/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/03/10/selenide-5.9.0/ https://selenide.org/2020/03/10/selenide-5.9.0 2020-03-10T00:00:00+00:00 Released Selenide 5.8.0 <p>Buenos noches!</p> <p>We have merged next pack of pull requests and released <a href="https://github.com/selenide/selenide/milestone/90?closed=1">Selenide 5.8.0</a>.</p> <p>What updates will you see there?</p> <h2 id="simplified-creation-of-custom-conditions-using-lambdas">Simplified creation of custom conditions using lambdas</h2> <p>In class <code class="language-plaintext highlighter-rouge">Condition</code> there is a new method <code class="language-plaintext highlighter-rouge">match</code> which allows you to create custom conditions without need to subclass <code class="language-plaintext highlighter-rouge">Condition</code>. You just need to pass it a lambda.</p> <p>See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/ConditionsTest.java">in tests</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>We also added similar methods for collections <code class="language-plaintext highlighter-rouge">anyMatch</code>, <code class="language-plaintext highlighter-rouge">allMatch</code> and <code class="language-plaintext highlighter-rouge">noneMatch</code>. See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/CollectionMethodsTest.java">in tests</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>See <a href="https://github.com/selenide/selenide/issues/662">issue 662</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1059">PR 1059</a>.</p> <p><br /></p> <h2 id="added-methods-sibling-and-preceding">Added methods <code class="language-plaintext highlighter-rouge">$.sibling()</code> and <code class="language-plaintext highlighter-rouge">$.preceding()</code></h2> <p>… which allow you to find “previous” and “following” elements on the same DOM level. It can be useful when you don’t have good locators, but need to navigate in DOM tree.</p> <p>See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/SiblingTest.java">in tests</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>See <a href="https://github.com/selenide/selenide/issues/845">issue 845</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1064">PR 1064</a>.</p> <p><br /></p> <h2 id="added-support-for-pseudo-elements">Added support for pseudo-elements</h2> <p>As many of you know, there are <em>things</em> in HTML called <em>pseudo-elements</em>: “:before”, “:after”, “:first-letter”, “:first-line”, “:selection”. They can contains important texts and styles that you sometimes want to check. Now it’s possible.</p> <p>See examples <a href="https://github.com/selenide/selenide/blob/master/src/test/java/integration/PseudoTest.java">in tests</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>You can also ask for a value of pseudo-element from <code class="language-plaintext highlighter-rouge">SelenideElement</code> (but we do not welcome this method):</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>See <a href="https://github.com/selenide/selenide/issues/994">issue 994</a>.<br /> Thanks to <a href="https://github.com/Denysss">Denys Shynkarenko</a> for <a href="https://github.com/selenide/selenide/pull/1045">PR 1045</a>.</p> <p><br /></p> <h2 id="fixed-softassertionsextension-for-junit5">Fixed SoftAssertionsExtension for JUnit5</h2> <p>If one of your tests failed, <code class="language-plaintext highlighter-rouge">SoftAssertionsExtension</code> also marked all following tests as failed. Ups.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1071">issue 1071</a>.<br /> Thanks to <a href="https://github.com/vinogradoff">Alexei Vinogradov</a> for <a href="https://github.com/selenide/selenide/commit/e92b250337a36a7225d6fcbdffecbf102f4592da">the fix</a>.</p> <p><br /></p> <h2 id="now-click-always-clicks-in-the-center-of-an-element">Now <code class="language-plaintext highlighter-rouge">$.click()</code> always clicks in the CENTER of an element</h2> <p>Usually method <code class="language-plaintext highlighter-rouge">$.click()</code> did click in the center of the element. But if you set <code class="language-plaintext highlighter-rouge">Configuration.clickViaJS=true</code>, it clicked in the top left corner.<br /> Not that it was critical, but you never know … Now it always clicks in the center. Just in case. Just to keep the same behaviour in all cases.</p> <p>See <a href="https://github.com/selenide/selenide/commit/106c53941c7188c5a19677ad45fbdea910960c73">commit 106c53941c718</a>.</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Wow! John C. Pratt created a <a href="https://chrome.google.com/webstore/detail/selenide-exporter-for-kat/mkbfcgpbkcaieiajhllpdocjfnfcbmlm">Selenide Exporter for Katalon Recorder</a> (works with <a href="https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid">Katalon Recorder</a>).</li> <li>We discovered <a href="https://github.com/razielsd/phpSelenide">phpSelenide</a> - a Selenide port to PHP.</li> <li>And if you didn’t know, <a href="https://github.com/automician/selenejs">SeleneJS</a> - a Selenide port to JavaScript.</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/02/28/selenide-5.8.0/ https://selenide.org/2020/02/28/selenide-5.8.0 2020-02-28T00:00:00+00:00 Released Selenide 5.7.0 <p>Buchahos!</p> <p>Recently we got a lot of pull requests with lot of useful changes. This is the power of open source!</p> <p>We have merged all those changes and released <a href="https://github.com/selenide/selenide/milestone/89?closed=1">Selenide 5.7.0</a>.</p> <h2 id="added-setting-configurationdownloadsfolder">Added setting <code class="language-plaintext highlighter-rouge">Configuration.downloadsFolder</code></h2> <p>Before now, Selenide downloaded files to folder <code class="language-plaintext highlighter-rouge">build/reports</code> - the same folder which contains generated test reports. <br /> But sometimes people want to have reports and screenshots in separate folders.<br /> For those people we added a dedicated setting <code class="language-plaintext highlighter-rouge">Configuration.downloadsFolder</code> - now Selenide will download files there.</p> <p>By default it’s <code class="language-plaintext highlighter-rouge">build/downloads</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1025">issue 1025</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1041">PR 1041</a>.</p> <p><br /></p> <h3 id="download-files-to-configurationdownloadsfolder-instead-of-downloads">Download files to <code class="language-plaintext highlighter-rouge">Configuration.downloadsFolder</code> instead of <code class="language-plaintext highlighter-rouge">~/Downloads</code></h3> <p>We had one more nuance when downloading files via proxy (<code class="language-plaintext highlighter-rouge">Configuration.fileDownload=PROXY</code>). <br /> Selenide downloads files to its own folder <code class="language-plaintext highlighter-rouge">build/reports</code> (now <code class="language-plaintext highlighter-rouge">build/downloads</code>), but browser also downloads the same files to folder <code class="language-plaintext highlighter-rouge">~/Downloads</code> (or what is the system default). It takes disk space, and nobody cleans up those files after test run.</p> <p>Now Selenide configures webdriver to download files to folder <code class="language-plaintext highlighter-rouge">build/downloads</code>.</p> <ol> <li>But only for Chrome and Firefox by now.</li> <li>And only in case of Selenide opens webdriver by itself.</li> </ol> <p>See <a href="https://github.com/selenide/selenide/issues/1057">issue 1057</a>. Thanks to <a href="https://github.com/dkorobtsov">Dmitri Korobtsov</a> for code review of <a href="https://github.com/selenide/selenide/pull/1058">PR 1058</a>.</p> <p><br /></p> <h3 id="added-method-for-switching-between-windows-with-custom-timeout">Added method for switching between windows with custom timeout</h3> <p>As you know, Selenide has methods for switching between tabs/windows:</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>This method is smart enough to wait until window is loaded. But it was impossible to set a timeout: the default 4 seconds was used here.</p> <p>Now we added a overloaded method with timeout parameter:</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>See <a href="https://github.com/selenide/selenide/issues/399">issue 399</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1054">PR 1054</a>.</p> <p><br /></p> <h3 id="added-logging-of-readonly-attribute">Added logging of “readonly” attribute</h3> <p>See <a href="https://github.com/selenide/selenide/issues/990">issue 990</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1042">PR 1042</a>.</p> <p><br /></p> <h3 id="fixed-indexoutofboundsexception">Fixed IndexOutOfBoundsException</h3> <p>… when searching first/last element of an empty collection</p> <p>See <a href="https://github.com/selenide/selenide/issues/991">issue 991</a>.<br /> Thanks to <a href="https://github.com/dstekanov">Dmytro Stekanov</a> for <a href="https://github.com/selenide/selenide/pull/1043">PR 1043</a>.</p> <p><br /></p> <h3 id="and-several-screenshot-improvements">And several screenshot improvements</h3> <h4 id="1-returned-the-lost-screenshots-in-screenshotsgetlastscreenshot">1. Returned the lost screenshots in <code class="language-plaintext highlighter-rouge">Screenshots.getLastScreenshot()</code></h4> <p>See <a href="https://github.com/selenide/selenide/issues/814">issue 814</a> and <a href="https://github.com/selenide/selenide/issues/880">issue 880</a>. <br /> Thanks to <a href="https://github.com/petroOv-PDFfiller">Petro Ovcharenko</a> for <a href="https://github.com/selenide/selenide/pull/1052">PR 1052</a>.</p> <p><br /></p> <h4 id="2-fixed-link-to-screenshots-in-jenkins">2. Fixed link to screenshots in Jenkins</h4> <p>Now Selenide can read environment variable <code class="language-plaintext highlighter-rouge">BUILD_URL</code>, and you don’t need to set system variable <code class="language-plaintext highlighter-rouge">BUILD_URL</code> in your build scripts.</p> <p>Thanks to <a href="https://github.com/GongYi">GongYi</a> for <a href="https://github.com/selenide/selenide/pull/1049">PR 1049</a>.</p> <p><br /></p> <h4 id="3-fixed-paths-to-screenshots-in-multi-module-maven-builds">3. Fixed paths to screenshots in multi-module Maven builds</h4> <p>Thanks to <a href="https://github.com/GongYi">GongYi</a> for <a href="https://github.com/selenide/selenide/pull/1049">PR 1049</a>.</p> <p><br /></p> <h3 id="upgraded-to-webdrivermanager-381">Upgraded to WebDriverManager 3.8.1</h3> <p>See <a href="https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-3.8.1...master">changelog</a> (including support for Edge 80).</p> <p><br /> <br /></p> <h1 id="events">Events</h1> <h3 id="seleniumcamp-2020">SeleniumCamp 2020</h3> <p>Welcome to Kiev in February, 21-22 to <a href="https://seleniumcamp.com/program/">SeleniumCamp</a> conference! <br /> I will present two talks:</p> <ul> <li><a href="https://seleniumcamp.com/talk/flaky-tests-method/">Flaky tests: The Method</a></li> <li><a href="https://seleniumcamp.com/talk/selenide-for-web-android-and-ios/">Threesome: Selenide for Web, Android and iOS</a></li> </ul> <p>and I will also hold an informal BOF session about <a href="https://seleniumcamp.com/talk/bof-glorious-past-and-promising-future-of-selenide/">Selenide roadmap</a>.</p> <h3 id="meetup-about-selenide-in-germany">Meetup about Selenide in Germany</h3> <p>Some folks organize a <a href="https://stugrm.de/stugrm-meetups/">Selenide meetup</a> in Germany in February, 12.<br /> That’s cool, right?</p> <p><br /></p> <h3 id="downloads-statistics">Downloads statistics</h3> <p>The number of Selenide downloads over the year increased 2.5 times from 40 thousand to 110 thousand.</p> <center> <img src="/images/2020/02/selenide.downloads.png" width="800" /> </center> <p><br /></p> <p>And the number of unique IPs exceeded 20 thousand:</p> <center> <img src="/images/2020/02/selenide.unique-ips.png" width="800" /> </center> <p><br /></p> <p>We are growing!</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/02/07/selenide-5.7.0/ https://selenide.org/2020/02/07/selenide-5.7.0 2020-02-07T00:00:00+00:00 Released Selenide 5.6.1 <p>Happy New Year for everyone!</p> <p>New year - new release. Please welcome <a href="https://github.com/selenide/selenide/milestone/88?closed=1">Selenide 5.6.1</a>.</p> <h1 id="added-method-selenideexecuteasyncscript">Added method <code class="language-plaintext highlighter-rouge">Selenide.executeAsyncScript()</code></h1> <p>I guess all of you have used method <code class="language-plaintext highlighter-rouge">Selenide.executeJavaScript()</code>. JavaScript gives us <a href="/2019/12/24/advent-calendar-javascript-tricks/">a big power</a> in test automation.</p> <p>Now we added a method <code class="language-plaintext highlighter-rouge">Selenide.executeAsyncScript()</code>. It can be used for executing asynchronous functions (like <code class="language-plaintext highlighter-rouge">setTimeout</code>, <code class="language-plaintext highlighter-rouge">$.get</code> etc.)</p> <p>Please note that it’s a little bit harder to use than <code class="language-plaintext highlighter-rouge">executeJavaScript()</code>.<br /> After calling asynchronous JS methods need to execute <code class="language-plaintext highlighter-rouge">callback</code> with a return value. You have to get <code class="language-plaintext highlighter-rouge">callback</code> from a <em>last argument</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">" // Here is your asynchronous JS code: "</span> <span class="o">+</span> <span class="s">" ... "</span> <span class="o">+</span> <span class="s">" // and return to Selenium: "</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>See <a href="https://github.com/selenide/selenide/issues/1030">issue 1030</a>.<br /> Thanks to <a href="https://github.com/tyge68">Thierry Ygé</a> for <a href="https://github.com/selenide/selenide/pull/1031">PR 1031</a>.</p> <p><br /></p> <h1 id="now-selenide-can-download-files-without-content-disposition-header-via-proxy">Now Selenide can download files without <code class="language-plaintext highlighter-rouge">Content-Disposition</code> header (via proxy)</h1> <p>As you know, Selenide can download files via its built-in proxy server.<br /> But until now, it intercepted only server responses containing <code class="language-plaintext highlighter-rouge">Content-Disposition</code> header (to get the name of file being downloaded).</p> <p>But it appeared that this header is not always required. Sometimes files can be downloaded without this header.</p> <p>Now Selenide proxy got to be smarter. Starting from Selenide 5.6.1, it works as follows:</p> <ol> <li>Before starting download, it waits until all previous requests/responses between browser and server are completed.</li> <li>It clicks the download button.</li> <li>It intercepts ALL requests between browser and server (no matter which headers they contain).</li> <li>And tries to understand which of them is more like a file download.</li> </ol> <p>If this response doesn’t contain <code class="language-plaintext highlighter-rouge">Content-Disposition</code> header, the name of the file is taken from URL.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1034">issue 1034</a> and <a href="https://github.com/selenide/selenide/pull/1035">PR 1035</a>.</p> <p><br /></p> <h1 id="fix-method-webdriverrunnerusing">Fix method <code class="language-plaintext highlighter-rouge">WebDriverRunner.using()</code></h1> <p>We <a href="/2019/10/16/selenide-5.4.0/#add-method-using">added method <code class="language-plaintext highlighter-rouge">using</code></a> in October, 2019.<br /> We recently found a bug in this method: it closed the webdriver (but should not). Now we fixed this bug.</p> <p>See <a href="https://github.com/selenide/selenide/commit/4d1b19972d">commit 4d1b19972d</a>.</p> <p><br /></p> <h1 id="upgraded-to-webdrivermanager-380">Upgraded to WebDriverManager 3.8.0</h1> <p>It contains lot of bugfixes, including those reported by me :)</p> <p>See <a href="https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-3.8.0...master">changelog</a>. <br /> Among other improvements, WDM can now work without internet access.</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Wow! Welcome <a href="https://vitalyzinevich.visualstudio.com/_git/Selenious">Selenious</a> - a port of Selenide to .NET platform.</li> <li>Article from 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>My video from Cyprus Quality Conference <a href="https://youtu.be/Y04rU7qV7Vg">Threesome: Selenide for Web, Android and iOS</a> in October, 2019.</li> <li>In case you missed it: series of posts <a href="/blog">Selenide Advent Calendar 2019</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2020/01/14/selenide-5.6.1/ https://selenide.org/2020/01/14/selenide-5.6.1 2020-01-14T00:00:00+00:00 Released Selenide 5.6.0 <p>Good morning!</p> <p>In the end of the year, we released <a href="https://github.com/selenide/selenide/milestone/87?closed=1">Selenide 5.6.0</a> with one significant change.</p> <p>We replaced (outdated) <code class="language-plaintext highlighter-rouge">BrowserMobProxy</code> by its fork <code class="language-plaintext highlighter-rouge">BrowserUpProxy</code> (current version 2.0.1).</p> <p>See <a href="https://github.com/selenide/selenide/issues/1019">issue 1019</a>.<br /> Thanks to <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> for <a href="https://github.com/selenide/selenide/pull/1020">PR 1020</a>.</p> <h2 id="what-good-is-there-is-browserupproxy">What good is there is <code class="language-plaintext highlighter-rouge">BrowserUpProxy</code>?</h2> <p>It</p> <ul> <li>supports Brotli Compression (in addition to gzip)</li> <li>supports HTTP/2</li> <li>based on actively maintained fork <a href="https://github.com/mrog/LittleProxy">LittleProxy</a></li> <li>uses a better <a href="https://github.com/sdstoehr/har-reader">HAR reader</a></li> <li>can filter HAR entries</li> <li>supports versioned headers for JSON like <code class="language-plaintext highlighter-rouge">Content-Type=application/something-v1+json</code></li> <li>has built-in asserts for network and pages</li> </ul> <p>Here is the <a href="https://github.com/browserup/browserup-proxy/blob/master/CHANGELOG.md">full changelog</a> of <code class="language-plaintext highlighter-rouge">BrowserUpProxy</code> (compared to BrowserMobProxy).</p> <h2 id="how-to-upgrade">How to upgrade?</h2> <p>In most cases, you don’t need to change anything. Everything just work as before.<br /> Some changes are required only in 2 cases:</p> <h4 id="1-if-you-declared-bmp-dependency-explicitly-you-need-to-replace">1. If you declared BMP dependency explicitly, you need to replace</h4> <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>by</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> <h4 id="2-if-you-implemented-requestfilter-or-responsefilter">2. If you implemented <code class="language-plaintext highlighter-rouge">RequestFilter</code> or <code class="language-plaintext highlighter-rouge">ResponseFilter</code>,</h4> <p>you need to replace import</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>by</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>That’s it. Nothing more.</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Google post about <a href="https://testing.googleblog.com/2019/12/testing-on-toilet-tests-too-dry-make.html">DAMP and DRY principles</a></li> <li>One more framework on top of Selenide: <a href="https://www.justtestlah.qa/">JustTestLah! (JTL)</a> - Hybrid of BDD, Selenide, Appium for Android, iOS and Web</li> <li>In case if you missed it, posts series <a href="/blog">Selenide Advent Calendar</a></li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/26/selenide-5.6.0/ https://selenide.org/2019/12/26/selenide-5.6.0 2019-12-26T00:00:00+00:00 JavaScript tricks <p>Good Christmas!</p> <p>Today is December, 24. It’s Christmas. It’s the last day of our Selenide Advent Calendar.</p> <p>In this last day, we will play JavaScript.</p> <p>As a language, JavaScript is a disaster, but it gives a big power when used in automated tests.<br /> It allows you doing things that are impossible with pure WebDriver.</p> <p>Let me show few examples from real projects.</p> <h2 id="pick-a-date">Pick a date</h2> <p>There is a lot of different “date pickers” out there. It’s always a headache to pick a date using them.</p> <p>How to pick a date in your test?</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>A straightforward way could be like this:</p> <ol> <li>Click a “calendar” icon</li> <li>Click a year number</li> <li>Click “previous month” arrow (how many times?)</li> <li>Click a day</li> <li>Ooops, today is February, 29. The test failed.</li> </ol> <p>This way is <strong>slow and unstable</strong>.</p> <p><br /></p> <h4 id="and-this-is-how-it-can-be-implemented-using-javascript">And this is how it can be implemented using JavaScript:</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>It’s <strong>fast and stable</strong>.</p> <p>You might be confused because it’s not the “real” way. We will discuss it in the end. Hold on.</p> <h2 id="hide-the-calendar">Hide the calendar</h2> <p>Assuming that the calendar was opened, how to close it?</p> <p>A straightforward solution would be to click the “cross” in the corner of calendar modal dialog. Again, it’s slow and unstable:</p> <ul> <li>the “cross” is always located in different corners</li> <li>location of the “cross” can change depending on design, browser size etc.</li> <li>sometimes calendar popup is opened slowly - you need to add explicit wait for the “cross” to immediately close it.</li> </ul> <p>The idiotic situation.</p> <p><br /></p> <h4 id="it-can-be-done-much-easier-with-javascript">It can be done much easier with JavaScript:</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>It’s <strong>fast and stable</strong>.</p> <h2 id="flip-a-flipper">Flip a flipper</h2> <p>Imagine a page with “flipper” for choosing a card. To select a card, user has to make a right finger movement in a right direction.<br /> <em>How to emulate it with Selenium?</em> It’s possible. All those Drag’n’Drop, Actions. Press, hold, move, release. All this is <strong>slow and unstable</strong>.<br /> It can easily be broken because of minor design changes, different browser size, focus lost etc.</p> <h4 id="and-this-a-solution-with-javascript">And this a solution with JavaScript:</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>Yes, it’s not trivial. We had to dive into the flipper’s code and understand what JS code it calls on flipping.<br /> And call similar JS code from test. Yes, you need to apply your head, but this solution is <strong>fast and stable.</strong></p> <h2 id="choose-and-option-in-bootstrap-select">Choose and option in bootstrap select</h2> <p>Many UI frameworks replace the standard <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code> with custom home-made “nice”/”usable” elements, made from bunch of <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code>s, <code class="language-plaintext highlighter-rouge">&lt;span&gt;</code>s, <code class="language-plaintext highlighter-rouge">&lt;li&gt;</code>s etc. with bunch of CSS classes and styles. It’s always a pain for automation.</p> <p>One of such UI frameworks is Bootstrap. It also has its <code class="language-plaintext highlighter-rouge">&lt;select&gt;</code>. We tried hard, but failed to clicks all those <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code>s and <code class="language-plaintext highlighter-rouge">&lt;span&gt;</code>s in the right order to select the right option.</p> <p>At the end, we implement the method:</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>with 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>Fast and stable.</strong></p> <p>By the way, with the help of <a href="https://selenide.org/2019/09/02/selenide-5.3.0/"><code class="language-plaintext highlighter-rouge">execute</code> method</a> you can make it even nicer:</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="slider">Slider</h2> <p>There is a slider on a page. User can drag the slider back and forth from 0 to 100.<br /> How to do it in test?</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>Even classical Drag’n’Drop doesn’t work here because we just haven’t a target element.<br /> Again, we can use Actions: press - hold - drag (by coordinates?) - release. Again, it’s <strong>slow and unstable</strong>.</p> <h4 id="but-we-can-use-javascript">But we can use JavaScript:</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>Again, we had to dive into the slider code. But it works. It’s <strong>fast and stable</strong>.</p> <h2 id="but-its-not-real">But it’s not real?</h2> <p>I know, many of you are frustrated because “it’s not real, it’s fake. Tests must emulate real user’s behaviour. Otherwise tests can miss some real problem.”</p> <p>I understand.</p> <p>But I will argue.</p> <blockquote> <p>Fast and stable tests are much, much better than “realistic” (but slow and unstable) tests.</p> </blockquote> <ul> <li>If 30% of your tests fail during every run,</li> <li>If you spent few hours to analyze every failed build,</li> <li>If you manually click through your “automated” scripts to check that functionality is not broken (and it was just a flaky test),</li> <li>If you fill an excel with failed-but-manually-checked test,</li> <li>If there is <em>no trust for tests</em> in your company,</li> </ul> <p>then <em>what the hell is the benefit of your tests</em>?<br /> Hard, only harm.</p> <ul> <li>I prefer fast and stable tests.</li> <li>I prefer tests that can easily emulate dependencies and check “complex” scenarios that are almost impossible to reproduce in a “realistic” way.</li> <li>I prefer to live in peace with understanding that the goal of automated testing was never to <em>automate everything</em>.</li> </ul> <p><br /></p> <p>You might still say:</p> <h3 id="nevertheless-my-soul-is-calmer-when-everything-is-for-real">Nevertheless, my soul is calmer when everything is “for real”.</h3> <p>Sorry, I have to upset you.</p> <ul> <li>You “realistic” Selenium WebDriver tests will never be <em>real</em>. They work <em>differently</em> (compare to real users). WebDriver sends a http request for each your command, and even - surprise! - applies some logic on JavaScript to check visibility of elements etc.</li> <li>In some sense, actions with JavaScript are even more “realistic” that with WebDriver. It’s closer to what browser actually does.</li> <li>And even your manual testers clicking through you scenarios - are <em>not real</em>! They work <em>differently</em> (compare to real users).</li> </ul> <p>Live now with it. :)</p> <h2 id="whats-next">What’s next?</h2> <p>That’s it. It was the last post of Selenide Advent Calendar 2019. Uhhhh!</p> <p>I will be honest: I wrote it to train my hands and get used to write a lot. I hope it will help me to <a href="https://selenide.org/selenide-site-ng/">update our site</a> and write Selenide documentation. Maybe even book. :)</p> <p>These are the plans for next year.</p> <p>Happy New Year!</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/24/advent-calendar-javascript-tricks/ https://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>The name of today’s topic is Esperanto and translates as “default language”.</p> <p>You may have noticed that some web applications or sites change their language depending on either your browser language setting or on your location.</p> <h2 id="problem">Problem</h2> <p>In case you have international developers in your team all writing and running some tests on different computer, you also may have noticed that the very same tests is failing or passing because the applications was displayed in different languages on different machines.</p> <p>If the application decides about the language based on user location, it can be difficult to write stable tests running everywhere. But if it is just “preferred browser language of the user”, then your task is much easier.</p> <h2 id="solution">Solution</h2> <p>So if you have a test which is supposed to run with language that differs from the default language of your browser, you have following options. Let’s imagine you are writing the tests for <em>German</em> Locale.</p> <ul> <li>Change the default language of you operational system. Poor you. Now the most of the programs will be showing in German. <em><strong>Ordnung muss sein!</strong></em></li> <li>Take your browser and setup it to be German the preferable language. Save away the profile. Google and experiment a lot about loading custom browser profile before starting your tests. Don’t forget to remove German from the top of preferred languages or otherwise, you know already, <em><strong>Ordnung….</strong></em></li> <li>Just make use of Chrome preference “intl.accept_languages” and set it to “de” (for German).</li> </ul> <p>Of course you can very easily do it in Selenide. Setup the system variable <code class="language-plaintext highlighter-rouge">chromeoptions.prefs=intl.accept_languages=de</code></p> <p>You can do it either in 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>or even better in configuration file of Maven or 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>likewise for gradle in <code class="language-plaintext highlighter-rouge">gradle.properties</code> (you additionally need a line or two in <code class="language-plaintext highlighter-rouge">build.gradle</code> to get this parameter transferred in to the test task in gradle)</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="command-line">Command-line</h3> <p>You can then override the setting running <code class="language-plaintext highlighter-rouge">mvn test</code> or <code class="language-plaintext highlighter-rouge">gradle test</code> defining another value in the command line <code class="language-plaintext highlighter-rouge">-Dchromeoptions.prefs=intl.accept_languages=ru</code></p> <h2 id="example">Example</h2> <p>Just run this short test and see the effect of different language settings.</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>I wish you all <em><strong>Fröhliche Weihnachten</strong></em> and <em><strong>Guten Rutsch</strong></em>!</p> <p><strong>Alexei Vinogradov</strong></p> https://selenide.org/2019/12/22/advent-calendar-defaulta-lingvo/ https://selenide.org/2019/12/22/advent-calendar-defaulta-lingvo 2019-12-22T00:00:00+00:00 Big “wait” theory <h1 id="big-wait-theory">Big “wait” theory</h1> <p>The topic of “wait” mechanism is well-known and sometimes can be controversial.<br /> The fact is that modern websites are problematic to be tested by test automation frameworks and generate many situations where standard Selenium methods are ineffective. If you have read Selenide documentation, you already know that classic explicit waits like:</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>or</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>have been replaced by Selenide with (way shorter) variants:</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>As we know assertions in Selenide are new version of explicit wait, which is well-described for example in <a href="https://selenide.org/documentation.html">documentation</a>.</strong></p> <p>Today we won’t be looking at assertions and wait mechanism from the technical point of view, but we will think about good ideas and examples of using particular assertions in different situations.</p> <h2 id="modern-problems-require-modern-solutions">Modern problems require modern solutions</h2> <h3 id="1-threadsleep">1. <code class="language-plaintext highlighter-rouge">Thread.sleep()</code></h3> <p>This is the worst possible thing, that could happen to our tests in Selenium.</p> <p>Some situations just required to use it, and there were no other solutions to move forward. Sometimes QAs try to use it to wait for some page to load, sometimes they try to wait for some element when other waits fail. Unfortunately, this way we can lose a lot of time. If we use it only in one test, it’s not that dangerous cause it will take probably around 4 seconds of our time. But if we use it in 150 tests, it will substantially increase the time of performance. There is no need to explain why it’s a bad thing. Although <code class="language-plaintext highlighter-rouge">sleep()</code> is still available in Selenide, smart assertions mentioned before make it useless in sense of waiting for anything on the page. Check next points.</p> <h3 id="2-how-to-wait-for-page-to-load">2. How to wait for page to load?</h3> <p>The easiest way is to choose some element from the page that we want to load and use Selenide method:</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>Notice that Selenide will try to find the element first, and then it will wait for the element to be visible. If it has not found the element it means that the page hasn’t loaded.</p> <p>Also, we can designate some element from the page that we are leaving and use method:</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>This way we create double-check for moving from one page to another, even if it takes a lot of time and previously we needed <code class="language-plaintext highlighter-rouge">Thread.sleep()</code>.</p> <h3 id="3-changing-state-of-an-element">3. Changing state of an element</h3> <p>It happens that we want to validate the state of an element that changes depending on actions taken by user. For example, element contains text informing us if some file has been uploaded or not. It is possible that we will upload the file but it will take some time to change the state of element, because back-end has to process the task and give back the information.</p> <p>Normally in our test we would like to upload file and check the state of an element to make sure that action succeeded. But how do we know when the element will change its state? The uploading and processing time can be different depending on the file’s size. This is the moment where a lot of people would use <code class="language-plaintext highlighter-rouge">Thread.sleep()</code> if standard Selenium methods failed(it really happens, sometimes Selenium checks the state of some element and thinks that it’s just wrong).</p> <p>With Selenide, we have a very smart tool for this purpose:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">element</span> <span class="o">=</span> <span class="err">$</span><span class="o">(</span><span class="n">cssSelector</span><span class="o">).</span><span class="na">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>This way Selenide will wait for the default timeout period for the element to change its state. We can check it with no additional lines of code and we can be sure that Selenide will really wait for it.</p> <h3 id="whats-next">What’s next?</h3> <p>These are just very basic ideas and examples on how to “wait” with Selenide. There are many more ways of doing it, and the fact is that now we have a lot of powerful tools to write our tests with real and effective waits that don’t need to be fixed with additional “sleeps”. It’s up to you what assertions will be added to your tests. Just make it smart and don’t waste the time, it’s a valuable thing :)</p> <p>Maciej Grymuza (figrym@gmail.com)</p> https://selenide.org/2019/12/20/advent-calendar-big-wait-theory/ https://selenide.org/2019/12/20/advent-calendar-big-wait-theory 2019-12-20T00:00:00+00:00 How to get network data with proxy <p>Good night!</p> <p>In two previous posts of our Selenide Advent Calendar, we tried to find a way to read requests/responses between browser and server. <br /> They both were not ideal because they cannot read BODY of requests and responses.</p> <p>Finally, we try a third way - using a Selenide built-in proxy server.</p> <h3 id="before-test">Before test</h3> <p>As you know, Selenide already has built-in proxy server. You only need to enable it:</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>Now you need to say the proxy to start tracking requests:</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">// remember body of requests (body is not stored by default because it can be large)</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">// remember both requests and responses</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">// start recording!</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="after-test">After test</h3> <p>Now you need to get a HAR and analyze its entries:</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) is like an “archive” with all network requests recorded during the test run.</p> <p>Every entry in it is a network request.<br /> It has everything needed inside: URL, request and response, their http status and body.<br /> Everything that we dreamed about.</p> <p><img src="/images/2019/12/har.entries.png" alt="HAR entries" /></p> <h3 id="pros">Pros:</h3> <ul> <li>It has all data you needed</li> <li>It’s easy to analyze it programmatically</li> <li>Works in all browsers</li> </ul> <h3 id="cons">Cons:</h3> <p>There is only one possible problem. When you run tests and browsers on different machines, and the “tests” machine is not accessible from the “browsers” machine, it’s just technically impossible to use proxy server.<br /> Though I never understood why people make things that complex.</p> <p>People, just run tests and browsers on the same machine. Everything will get MUCH SIMPLER! <br /> If you need to parallelize - just parallelize TESTS.<br /> If you need a cluster (grid) - just run TESTS on different cluster nodes (and they will run their browsers locally).<br /> There is no reasons to make it more complex.</p> <h2 id="whats-now">What’s now?</h2> <p>Now we can read network data during tests run.<br /> But I hope you don’t really use it often. Usually it should not be needed. Maybe in very rare cases. <br /> Usually it should be enough to just read application logs to understand what requests were sent to server.</p> <p>Keep it simple.</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/18/advent-calendar-network-logs-with-proxy/ https://selenide.org/2019/12/18/advent-calendar-network-logs-with-proxy 2019-12-18T00:00:00+00:00 How to get network data with JavaScript <p>Good night!</p> <p>In the yesterday post we tried to get browser logs with “goog:loggingPrefs” capability.<br /> Now we will try to get browser network data with JavaScript.</p> <p>It’s simple. We just need to call this JavaScript in the end of test:</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>The result looks like this:</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="pros">Pros:</h3> <ul> <li>You don’t need to setup the browser. It works out of the box.</li> <li>It works in all browsers (I guess?)</li> </ul> <h3 id="cons">Cons:</h3> <ul> <li>We still don’t see request body here.</li> <li>It’s not a valid JSON. We cannot parse it with a standard parser. Would need to create some custom parser.</li> </ul> <p>But this option is quite ok to look at and understand what happens.</p> <h2 id="whats-next">What’s next?</h2> <p>Our last hope to get request bodies is built-in proxy server. We will look at it next time.</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/17/advent-calendar-browser-logs-with-js/ https://selenide.org/2019/12/17/advent-calendar-browser-logs-with-js 2019-12-17T00:00:00+00:00 How to get browser logs <p>Good evening!</p> <p>We continue our Selenide Advent Calendar.<br /> Today we will try to look into browser “developer tools”.<br /> It may be useful if you want to know what errors happened during test run, or what requests your AUT sent.</p> <p>Chromedriver suggests the following receipt.</p> <h3 id="1-add-a-couple-of-lines-before-opening-a-browser">1. Add a couple of lines before opening a browser:</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>This capability was named “loggingPrefs” until some version of Chrome, and then was renamed to “goog:loggingPrefs”.<br /> Not sure about other browsers.</p> <p>Besides <code class="language-plaintext highlighter-rouge">BROWSER</code> and <code class="language-plaintext highlighter-rouge">PERFORMANCE</code> there are other types too, but I didn’t understand them.</p> <h3 id="2-fetch-the-logs-in-the-end-of-test">2. Fetch the logs in the end of test:</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-this-is-how-browser-logs-look-like">3. This is how BROWSER logs look like:</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>Here you can see all logs usually seen at “Developer Tools” -&gt; “Console” tab. Including <code class="language-plaintext highlighter-rouge">console.log</code> messages and JavaScript errors.</p> <h3 id="4-and-this-is-how-performance-logs-look-like">4. And this is how PERFORMANCE logs look like:</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="pros">Pros</h3> <p>Every record is a valid JSON. You can parse and analyze right in your test.</p> <p>This is how the first record looks formatted:</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="cons">Cons</h3> <ul> <li>It’s hard to understand those logs. Some additional analyzers might be needed.</li> <li>We don’t see request body here.</li> </ul> <h2 id="whats-next">What’s next?</h2> <p>Next time we will try other ways to get browser logs - including http statuses and request bodies.</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/16/advent-calendar-browser-logs/ https://selenide.org/2019/12/16/advent-calendar-browser-logs 2019-12-16T00:00:00+00:00 Drag and Drop <p>Hi all!</p> <p>Today is 15th day of our Selenide Advent Calendar.</p> <p>Today we will watch a short funny video demonstrating how to drag-and-drop elements in Selenide.</p> <h3 id="selenide-can-drag-and-drop-web-elements">Selenide can drag-and-drop web elements?</h3> <p>Yes, Selenide has method Drag’n’Drop. This is a boring description from Selenide blog:</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>And this is a funny demonstration by 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>That’s marvellous!</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/15/advent-calendar-drag-and-drop/ https://selenide.org/2019/12/15/advent-calendar-drag-and-drop 2019-12-15T00:00:00+00:00 Actions <p>Hi!</p> <p>In today’s advent calendar article we will check how to perform special operations in Selenide.</p> <p>Sometimes in our tests we encounter weird problems. It is 100% sure that all of us have experienced, or will experience some unusual problem that will make our work blocked. It happens quite often that for example we cannot click some element on the page, and standard selenium/selenide command like:</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>does not work. There can be many different reasons that will make our click operation ineffective but we cannot give up just like that, and we have to find some solution. In this case, in Selenium we had <code class="language-plaintext highlighter-rouge">Actions</code> class that helped us performing click operation in a bit different way:</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>This way we were able to click the element and solve some of our problems occurring in tests.</p> <h4 id="but-how-can-we-do-it-in-selenide">But how can we do it in Selenide?</h4> <p>As it turns out, it is easier and simpler than it was in Selenium.<br /> Selenide solves many problems that were quite common in Selenium, but if we need to perform some things in a bit different way, actions are still available in Selenide. Here we can check how it goes:</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>As we can see, <code class="language-plaintext highlighter-rouge">actions</code> is just a method available when we have:</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>imported to our project. Notice that no driver is needed to use <code class="language-plaintext highlighter-rouge">actions()</code> method!</p> <h5 id="crazy-drag-and-drop">Crazy drag and drop</h5> <p>If you have read the documentation, you probably already know that there are two types of drag and drop operations available in Selenide by default:</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>First method will let us drag and drop element to CSS selector defining target element.<br /> Second method will just let us drag and drop some element to another one.</p> <p>But what if we want to perform drag and drop action and we have no target element or css selector available?<br /> For example, we have just an empty page and we want to put there some objects in many different places.<br /> In this case, actions method will help us again. In Selenium we would have to code something like this:</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>Where <code class="language-plaintext highlighter-rouge">xOffset</code> is a horizontal move offset and <code class="language-plaintext highlighter-rouge">yOffset</code> is a vertical move offset.</p> <p>In Selenide it should look like this:</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>This way we can drag and drop some object to the desired location without using additional target identifiers.</p> <h2 id="whats-next">What’s next</h2> <p>Of course, these are just two examples of using special methods in Selenide and user can experiment with many more options.</p> <p>Enjoy actions() !</p> <p>Maciej Grymuza (figrym@gmail.com)</p> https://selenide.org/2019/12/12/advent-calendar-actions/ https://selenide.org/2019/12/12/advent-calendar-actions 2019-12-12T00:00:00+00:00 How to download a file with Selenide <p>Good evening!<br /> Today is December - and in this Selenide Advent calendar article we will take a look at the file download options in Selenide.</p> <p><strong>UPD</strong><br /> This article describes only 2 methods for downloading a file: <a href="#HTTPGET"><code class="language-plaintext highlighter-rouge">HTTPGET</code></a> and <a href="#PROXY"><code class="language-plaintext highlighter-rouge">PROXY</code></a>. Later <a href="/2020/07/08/selenide-5.13.0/#new-file-download-mode-folder">we developed a third method <code class="language-plaintext highlighter-rouge">FOLDER</code></a>. Probably you need this method - if your link doesn’t have <code class="language-plaintext highlighter-rouge">href</code> attribute, and you cannot enable proxy for some reason.</p> <p><br /></p> <h1 id="how-can-i-download-some-file-in-my-test">How can I download some file in my test?</h1> <p>At some point of our career all of us will face the problem of downloading the file in the test.</p> <p>As we remember, in Selenium it wasn’t that easy, because different browsers required different configurations like creating new user profile in Firefox and setting preferences:</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">In Selenide,</h3> <p>this problem has been solved by adding <code class="language-plaintext highlighter-rouge">$.download()</code> method.</p> <p>When we want to download some file, we just have to perform:</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>and Selenide will handle with all downloads window popup and just close them when process is finished.</p> <p>Selenide will create new folder containing our file in <code class="language-plaintext highlighter-rouge">build/reports/tests</code>. This is the folder where Gradle generate its rest reports, so that it’s convenient to have them all together.</p> <p>We can edit our download directory by changing default settings:</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">BUT:</h3> <p>This way we will be able to download some file only when its element has “href” attribute.</p> <p>What if my element does not have “href” attribute? For example, if file is generated as a result of form submission.</p> <p>In this case, we have to organize downloading of our files in a bit different way. First of all we have to change settings:</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>After changing these settings, we are able to download files from all kinds of elements, we don’t need a <code class="language-plaintext highlighter-rouge">href</code> attribute anymore - and we just have to use:</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="tip">Tip:</h3> <p>Remember to set the appropriate timeout if you want to download files that have a big size - it will take more time to download them.</p> <p>File will be downloaded to the default folder (something like <code class="language-plaintext highlighter-rouge">C:\downloads and settings\downloads</code>).<br /> We have to remember that in case of some browsers, files are also downloaded to system downloads folder so our file will be present in two locations.</p> <p>In the next steps we can for example delete folder with downloaded file using:</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">folder</span> <span class="n">we</span> <span class="n">want</span> <span class="n">to</span> <span class="n">delete</span><span class="o">&gt;));</span> </code></pre></div></div> <p>For more detailed info about file downloading mechanism read <a href="https://selenide.org/2016/08/27/selenide-3.9.1/">this post</a>.</p> <p>Maciej Grymuza (figrym@gmail.com)</p> https://selenide.org/2019/12/10/advent-calendar-download-files/ https://selenide.org/2019/12/10/advent-calendar-download-files 2019-12-10T00:00:00+00:00 Why we banned statics and then allowed again? <p>Good evening!</p> <p>Here is the 9th day of Selenide Advent Calendar, and<br /> I’ll tell you what excites the public most of all.</p> <h1 id="why-statics-were-banned-in-selenide-500-but-allowed-again-in-540">Why statics were banned in Selenide 5.0.0, but allowed again in 5.4.0?</h1> <p>Short answer: we banned them occasionally. But it was good.</p> <p>Let me explain it in detail :)</p> <h3 id="how-selenide-holds-webdrivers">How Selenide holds WebDrivers</h3> <p>Selenide stores webdriver instances in ThreadLocal.<br /> It allows you to run tests in parallel: different threads get different webdrivers. The code looks like this:</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>Say, method <code class="language-plaintext highlighter-rouge">$("a").click()</code> works like this:</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="briefly-about-selenidedriver">Briefly about SelenideDriver</h2> <p>But ThreadLocal implies one limitation: you cannot use two webdrivers in one thread<br /> (as well as one webdriver in two threads - but aren’t there yet).</p> <p>We planned to solve this problem in Selenide 5.0.0. We created a special class <code class="language-plaintext highlighter-rouge">SelenideDriver</code>, that allowed you to use two webdrivers in one test:</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>It forces us to do a major refactoring: we removed all usages of old good static method <code class="language-plaintext highlighter-rouge">open()</code>, <code class="language-plaintext highlighter-rouge">$</code> and <code class="language-plaintext highlighter-rouge">$$</code> inside Selenide itself. Every piece of Selenide code that needs a webdriver now gets a <code class="language-plaintext highlighter-rouge">SelenideDriver</code> parameter.<br /> Yes, we had to pass this parameter everywhere…</p> <h2 id="here-comes-the-question">Here comes the question</h2> <p>What should we do with old good static methods <code class="language-plaintext highlighter-rouge">open()</code>, <code class="language-plaintext highlighter-rouge">$</code> and <code class="language-plaintext highlighter-rouge">$$</code>? They have to get <code class="language-plaintext highlighter-rouge">SelenideDriver</code> instance somewhere. Where to take it from?</p> <h2 id="selenide-500-statics-got-a-punch">Selenide 5.0.0: Statics got a punch</h2> <p>We added to class <code class="language-plaintext highlighter-rouge">WebDriverRunner</code> a third 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>Briefly, <code class="language-plaintext highlighter-rouge">SelenideDriver</code> is a simple class with two fields <code class="language-plaintext highlighter-rouge">WebDriver</code> and <code class="language-plaintext highlighter-rouge">SelenideProxyServer</code>.<br /> In general, it worked and solved the initial problem.</p> <p>What I could not have envisioned at that moment was that so many folks defined their web elements in static fields:</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>And re-open the webdriver between tests.</p> <h3 id="one-important-detail">One important detail:</h3> <p>We did one more improvement in Selenide 5.0.0.</p> <p>Before 5.0.0, Selenide automatically opened a new browser if it wasn’t opened yet. At any moment. <br /> Sometimes it caused perplexity because browser opened at unexpected moments (for example, when trying to log an error caused by a crashed browser).</p> <p>Of course, it happened because of bad tests. Bug after all, <em>we created Selenide to help people, right?</em></p> <p>That’s why starting from version 5.0.0, Selenide didn’t occasionally open a browser anymore.<br /> Instead, it said: “There is no an opened browser, I cannot do my job. You need to call method <code class="language-plaintext highlighter-rouge">open()</code> first.”</p> <h3 id="and-what-failed-then">And what failed then?</h3> <p>The coincidence of these two factors led to the following problem:</p> <ol> <li>Test creates a static field <code class="language-plaintext highlighter-rouge">static SelenideElement fname = $("#fname")</code>.</li> <li>This <code class="language-plaintext highlighter-rouge">fname</code> remembers a <code class="language-plaintext highlighter-rouge">SelenideDriver</code> it was created with.</li> <li>Test closes the browser in the end.</li> <li>A following test opens a new browser tries to use static field <code class="language-plaintext highlighter-rouge">fname</code>.</li> <li>And <code class="language-plaintext highlighter-rouge">fname</code> calls its own instance of <code class="language-plaintext highlighter-rouge">SelenideDriver</code> it was created with.</li> <li>And fails, because <em>that</em> `SelenideDriver has been closed.</li> </ol> <p>More than a year - from September, 2018 to October, 2019 - I was trying to explain that static variables are evil.<br /> I even created a special talk <a href="https://www.youtube.com/watch?v=dFQSOlOOoXE&amp;list=PLfazdZ9SzB9eDJIugtfH7KeVLLAP1pDLh">“Antistatic”</a> (yes, this time in English:)).</p> <p>But finally I gave up. Because it would be too big refactoring for many folks to rewrite their projects from static variables.</p> <p>After all, <em>we created Selenide to help people, right?</em></p> <h2 id="selenide-540-statics-won">Selenide 5.4.0: Statics won</h2> <p>How we finally solved this problem?</p> <p>The solution is quite simple. We replaced <code class="language-plaintext highlighter-rouge">ThreadLocal&lt;SelenideDriver&gt;</code> by a static variable (yep :))</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>Now this “static” <code class="language-plaintext highlighter-rouge">SelenideDriver</code> is a singleton. It always exists. It’s never closed. All static <code class="language-plaintext highlighter-rouge">SelenideElement</code> variables created with it will live forever. But it also doesn’t hold fields <code class="language-plaintext highlighter-rouge">WebDriver</code> and <code class="language-plaintext highlighter-rouge">SelenideProxyServer</code>. It fetches them from <code class="language-plaintext highlighter-rouge">WebDriverRunner</code>’s ThreadLocals every time.</p> <h3 id="ps">P.S.</h3> <p>That’s why method <code class="language-plaintext highlighter-rouge">WebDriverRunner.getSelenideDriver()</code> disappeared in Selenide 5.4.0.</p> <p>I was surprised that many people have already managed to use it. People, I do not understand you! <em>How do you manage to use everything so wrong?</em><br /> Well, it was my mistake to make this method public. But I never mentioned it in any of my posts nor documentation. I never recommended to use. How could someone decide to use it? How this magic happens?</p> <h2 id="whats-now">What’s now</h2> <p>We returned the possibility to declare your <code class="language-plaintext highlighter-rouge">SelenideElement</code>s in static fields.</p> <p>But please do not abuse it.</p> <p>I still don’t like it :)</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/09/advent-calendar-statics/ https://selenide.org/2019/12/09/advent-calendar-statics 2019-12-09T00:00:00+00:00 How to test CSRF protection <p>Hi all!</p> <p>Today is 7th day of our Selenide Advent Calendar. <br /> Today we will talk about testing security.</p> <h1 id="what-is-csrf">What is CSRF?</h1> <p><a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)">CSRF</a> is one of the most popular security attacks (Cross-Site Request Forgery).</p> <p>To protect itself from CSRF attack, a web-application should add a special parameter to (almost) every POST request. Typically it’s called <code class="language-plaintext highlighter-rouge">authenticityToken</code> (though the name may vary).</p> <p>A typical mistake is when web-application either doesn’t send <code class="language-plaintext highlighter-rouge">authenticityToken</code> to server with some POST request or doesn’t check it on server side.</p> <h3 id="how-to-check-the-protection">How to check the protection?</h3> <p>I assume you have some automated tests covering the most critical functionality of your web application.<br /> We will kill two birds with one stone: during run of your tests, we will intercept every POST request and send exactly the same request, but with modified <code class="language-plaintext highlighter-rouge">authenticityToken</code>. And verify that the server returned an error. Usually it should be “403 Forbidden”.</p> <h3 id="sounds-hard-how-to-program-it">Sounds hard. How to program it?</h3> <p>Now so hard.<br /> As you know, Selenide can run its own embedded proxy server. Initially it was created for downloading files, but it also allows to add your own listeners which can intercept all requests between browser and server. We are going to use it.</p> <p><br /> <br /></p> <h4 id="step-1-enable-selenide-proxy">Step 1. Enable Selenide proxy</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>(you need to do it BEFORE opening a browser)</p> <p><br /> <br /></p> <h4 id="step-2-add-listener-for-the-proxy">Step 2. Add listener for the proxy</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">// somewhere after 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>Currently you can only add listeners AFTER opening a browser, which is sometimes inconvenient. I hope we will implement adding listeners at any moment in next Selenide release.</p> <p><br /></p> <h4 id="step-3-implement-authenticitytokenchecker">Step 3. Implement 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">// ignore non-POST requests</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">// ignore chrome requests to google.com etc.</span> <span class="k">if</span> <span class="o">(</span><span class="k">this</span> <span class="n">url</span> <span class="n">can</span> <span class="n">work</span> <span class="n">without</span> <span class="n">authenticityToken</span><span class="o">)</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="c1">// some post requests don't need csrf protection</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>Note that <code class="language-plaintext highlighter-rouge">return null;</code> means “do not modify the request”, which means that the browser will still send the original request, and the normal flow of your test will not be affected.</p> <p><br /></p> <h4 id="step-4-send-hacked-post-request">Step 4. Send hacked POST request</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">// You need to tune this line. </span> <span class="c1">// The request format (including name of parameter "authenticityToken") may depend on your application.</span> <span class="c1">// Note that the request can contain several "authenticityToken" parameters (immediately throw an error if they are different).</span> <span class="c1">// If the request contains a submitted form, especially with uploaded files, you need to modify "authenticityToken" a little bit differently. </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">to</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>This implementation uses Java 11 built-in <code class="language-plaintext highlighter-rouge">HttpClient</code>. If you are that poor guy that still uses Java 8, you can easily replace it with OkHttp, Apache Http Client or some similar http client.</p> <p><br /></p> <h4 id="step-5-fail-the-test-if-some-hacked-request-hasnt-got-forbidden-error">Step 5. Fail the test if some hacked request hasn’t got “Forbidden” error</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="whats-now">What’s now?</h2> <p>We managed to automatically check if our application is protected against CSRF attacks.<br /> (It’s not theoretical: we actually did it in one project and found two vulnerabilities in a real internet-bank.)</p> <p>It’s good, but it’s not enough. There is a lot of other security attacks.</p> <p>Keep track of <a href="https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project">OWASP 10</a>, think creatively, and try to figure out how you could emulate other attacks with your automated tests.</p> <p><br /></p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/07/advent-calendar-csrf-protection/ https://selenide.org/2019/12/07/advent-calendar-csrf-protection 2019-12-07T00:00:00+00:00 How to visualize click <p>Good evening!</p> <p>Today is December 6th, you are reading the Selenide Advent calendar, and in today’s post I will show one simple technique how to catch some flaky tests.</p> <h1 id="whats-wring-with-clicks">What’s wring with clicks?</h1> <p>It’s an eternal problem: flaky tests. One of typical reasons of flaky tests: click doesn’t work.</p> <p>As i described <a href="https://www.youtube.com/watch?v=ibx8nVvt-Js">in this video</a> on DelEx 2019 conference (sorry, the video is in Russian), click in selenium often doesn’t work if the element was moving or resizing at that moment. Or if it was covered by other element that suddenly appeared and got the click.</p> <h3 id="why-selenide-hasnt-solved-it-already">Why Selenide hasn’t solved it already?</h3> <p>A global solution could be like this: method <code class="language-plaintext highlighter-rouge">$.click()</code> waits for completion of any animations and movements before actually performing the click. Currently Selenide doesn’t have such a solution. I guess it’s impossible to make such waiting universal. All projects are different, with different frameworks and designs. If you have an idea how to implement such a waiting - feel free to drop me a notice.</p> <p>But what we can is to highlight the element that actually got the click. It will not solve the problem entirely, but at least it helps to localize the problem.</p> <h3 id="how-to-highlight-the-element">How to highlight the element?</h3> <p>You can add the following JS code to your application.<br /> It listens for all click events on the page and adds highlights with a green border the element that actually got the click.<br /> It will help you to detect cases where a wrong element got the click.</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>You might need to adjust this code to your needs: it depends on your application, framework, design etc.</p> <p>Here you can see one example of possible solutions: <a href="https://github.com/selenide-examples/gmail/blob/master/test/org/selenide/examples/gmail/Highlighter.java">Highlighter</a>.</p> <h2 id="whats-now">What’s now?</h2> <p>If you have flaky test (as all of us), you can start from this step.<br /> At least you will see which element got the click.</p> <p><br /></p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/06/advent-calendar-visualize-click/ https://selenide.org/2019/12/06/advent-calendar-visualize-click 2019-12-06T00:00:00+00:00 How to check size effectively? <p>Hello!</p> <p>Today is December 4th, it’s the Selenide Advent calendar, and in today’s post I will show how to check a collection size effectively.</p> <h1 id="how-to-effectively-check-a-collection-size">How to effectively check a collection size?</h1> <p>Selenide has convenient methods for checking collections. For example, this is how you can check that a book list contains exactly 3 books:</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>If this page implemented so that it contains much more books, and only some of them are visible, then you can filter the collection:</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="the-problem">The problem</h2> <p>If the page contains a lot of books (say, a few dozens or more), this filtration can be slow (say, few seconds and more).<br /> It’s because method <code class="language-plaintext highlighter-rouge">filter(visible)</code> needs to call <code class="language-plaintext highlighter-rouge">WebElement.isDisplayed()</code> separately for each element in the collection in loop. Every call means a separate call to webdriver, which is a separate http request etc. This all takes times.</p> <p>Probably tests run 3 days in your project, and few seconds don’t really play any role. Then you can skip this post. :)</p> <p>But if you care about effectiveness of your tests, go on.</p> <h2 id="the-first-try">The first try</h2> <p>How to speed up this check? The first idea is to filter elements right in the selector:</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>But this selector doesn’t work.</p> <p>The problem is that there is no CSS selector <code class="language-plaintext highlighter-rouge">:visible</code>. It doesn’t exist. Only JQuery can understand it.</p> <h2 id="the-second-try">The second try</h2> <p>Who saves us? Of course, JavaScript!</p> <p>You can create a helper method which finds a number of elements (with a help of JavaScript and 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>(this code only works if the page contains JQuery library. If not, you can write a similar code using some other JavaScript library.)</p> <p>Now your check will be fast because it calls WebDriver method only once. And browser executes any JavaScript very quickly.</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="whats-now">What’s now?</h2> <p>Learn the power of JavaScript. <br /> It allows many other tricks which can make your tests more fast and stable.</p> <p><br /></p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/04/advent-calendar-effective-size-check/ https://selenide.org/2019/12/04/advent-calendar-effective-size-check 2019-12-04T00:00:00+00:00 setWebDriver or WebDriverProvider? <p>Good evening!</p> <p>Today is December 3rd, you are reading the Selenide Advent calendar, and in today’s post I will answer one simple question.</p> <h1 id="which-one-should-i-choose-setwebdriver-or-webdriverprovider">Which one should I choose, <code class="language-plaintext highlighter-rouge">setWebDriver()</code> or <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>?</h1> <p>As you know, Selenide opens a browser automatically and closes it at the right moment. You don’t need to care about the browser.</p> <p>But sometimes you want to open a “custom” browser with some specific settings. Selenide has 2 methods for this. It happened so that I never wrote anywhere how to choose one of them.</p> <p>So,</p> <h4 id="option-1">Option 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>This option is preferred in most cases. It’s good because class <code class="language-plaintext highlighter-rouge">MyWebdriverProvider</code> is only responsible for HOW to open a browser (which options to pass, where to find binaries etc). But is not responsible for WHEN to open and close the browser. Selenide takes care of it.</p> <h4 id="option-2">Option 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>This option is worse because you are responsible not only for opening, but also for close the browser. This often causes misunderstanding. Selenide cannot close the browser because you might use it somewhere else. You open it - you close it.</p> <p>This begs the question:</p> <h3 id="why-its-needed-at-all">Why it’s needed at all?</h3> <p>Initially method <code class="language-plaintext highlighter-rouge">setWebDriver()</code> was created to enable easy adoption of Selenide in existing projects (without re-writing the existing codebase).</p> <p>Imagine that company X already has a lot of automated tests on Selenium (or HtmlElements, Thucydides, Serenity or anything else). They want to write new tests on Selenide, but leave existing tests untouched.</p> <p>Apparently they already have a code snippet in their codebase which opens and closes the browser. They want to say Selenide to use the same browser. That’s when method <code class="language-plaintext highlighter-rouge">setWebDriver()</code> is useful. It allows you to run the old and the new code all together, so that it works in the same browser.</p> <p>As you see, the use of method <code class="language-plaintext highlighter-rouge">setWebDriver()</code> is rather narrow. I’m not sure at all if it was ever used in that vein. :)</p> <h2 id="whats-now">What’s now?</h2> <p>If you are writing a new project, use <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>. SRP.</p> <p><br /></p> <h1 id="driver-factory">UPD</h1> <p>Later in <a href="https://ru.selenide.org/2020/11/25/selenide-5.16.2/#selenide-5.16.2">Selenide 5.16.1</a> we added one more option <code class="language-plaintext highlighter-rouge">DriverFactory</code>. Now it’s the recommended way to create a customized webdriver.</p> <p>The idea is very similar to <code class="language-plaintext highlighter-rouge">WebDriverProvider</code>, but gives you more control over webdriver options. You need to</p> <ol> <li>either implement interface <code class="language-plaintext highlighter-rouge">DriverFactory</code>,</li> <li>or create a subclass of <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> or similar class (which exist in Selenide and implement interface <code class="language-plaintext highlighter-rouge">DriverFactory</code>) and override only needed methods. All methods in these classes are made small and declared protected.</li> </ol> <p>You can find examples in <a href="https://github.com/selenide/selenide/blob/master/statics/src/test/java/integration/ChromeProfileByFactoryTest.java">Selenide own tests</a>.</p> <p>See also <a href="https://mbbaig.blog/selenide-webdriverfactory/">blog post from Boris Bay</a>.</p> <p><br /> <a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider/ https://selenide.org/2019/12/03/advent-calendar-set-webdriver-vs-webdriver-provider 2019-12-03T00:00:00+00:00 How to abuse Selenide <p>Good year!</p> <p>It’s December, 02, and we continue with our Selenide Advent Calendar. This time will talk about invalid use of Selenide.</p> <h1 id="how-to-abuse-selenide">How to abuse Selenide</h1> <p>I often hear people complaining that the following code doesn’t catch the exception:</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("The element is not found - a banner hasn't appeared this time.");</span> <span class="o">}</span> </code></pre></div></div> <p>The intention of this test is to check if a banner appeared. Not immediately, but wait for the banner a little bit: probably it will appear after some time. If the banner has appeared, the test needs to close it.</p> <p><br /></p> <h1 id="why-this-is-a-bad-test">Why this is a bad test?</h1> <p>Because <strong>it’s slow</strong>.</p> <p>In most cases, banner will not appear, and the test will waste 4 seconds (or what is your timeout).</p> <p>It’s awful. Are you a professional engineer? Your goal is to make your tests fast and stable.</p> <p><br /></p> <h1 id="why-this-test-is-very-bad">Why this test is very bad?</h1> <p>Because <strong>it’s unstable</strong>.</p> <p>Sometimes banner will appear in the end of 4th second. Test decides that banner hasn’t appeared and goes on. At this moment the banner appears. Ups, you get a flaky test.</p> <p><br /></p> <h1 id="why-this-test-is-very-very-bad">Why this test is very-very bad?</h1> <p>Because <strong>it doesn’t test the banner</strong>.</p> <ol> <li> <p>Imagine that banner hasn’t appeared during tests run. All tests are green. But in production, the banner appears for end-users. And when the banner appears, an error happens. The application crashes. Users complain. <br /> <br /> <em>But your tests are green.</em></p> </li> <li> <p>Imagine the opposite. During your tests run, banner appears all the time. Well, that’s what happened in the test environment. But real users see the banner very rarely, and the error happens when they don’t see the banner. The application crashes. Users complain. <br /> <br /> <em>But your tests are green.</em></p> </li> </ol> <p><br /></p> <h1 id="what-can-you-do">What can you do?</h1> <p>Obviously, you should have at least 2 tests:</p> <ol> <li>Case when the banner appears, and</li> <li>Case when the banner doesn’t appear.</li> </ol> <p>To achieve that, you need a possibility to rule the banner. You should be able to show or hide the banner either using some API or admin console or by running SQL in database. Whatever. Every mean is good. If you don’t have such API, you should make a deal with developers and create it.</p> <p>Don’t tell me that you can’t. Are you a professional engineer or what?</p> <p>If you cannot, just delete this test. Cannot means CAN NOT.</p> <p><br /></p> <p>And finally, the initial question:</p> <h1 id="why-that-code-doesnt-catch-the-exception">Why that code doesn’t catch the Exception?</h1> <p>You probably guessed it, right?</p> <p>The trick is with hierarchy of Java exceptions. The parent class of all java exceptions is <code class="language-plaintext highlighter-rouge">Throwable</code>, not <code class="language-plaintext highlighter-rouge">Exception</code>. Here is the hierarchy:</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>This is the reason why you miss all <code class="language-plaintext highlighter-rouge">Error</code> subclasses (including Selenide errors) when you <code class="language-plaintext highlighter-rouge">catch (Exception e)</code>.</p> <p>You might think: “Great, I will <code class="language-plaintext highlighter-rouge">catch (Error e)</code>”. No. NO! Please, NO!</p> <p>Read the <code class="language-plaintext highlighter-rouge">java.lang.Error</code> javadoc:</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="whats-now">What’s now?</h2> <p>I hope you will stop catching exceptions in tests. The goal of tests is to report errors, not catch them.</p> <p>I hope you will take the test environment under your control.</p> <p>You need to decide, either you rule the banner, or the banner rules you.</p> <p>Either you automate tests, or tests automate you.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide/ https://selenide.org/2019/12/02/advent-calendar-how-to-abuse-selenide 2019-12-02T00:00:00+00:00 The Main Algorithm <p>Good December!</p> <p>The Advent has begun.</p> <p>Children are unpacking their advent calendars, opening one small window with a chocolate every day. In Java world, we have a <a href="https://www.javaadvent.com/">Java Advent Calendar</a>, in which many authors write a new article every day. I also participated in it could of times (<a href="https://www.javaadvent.com/2017/12/flaky-tests.html">2017</a> and <a href="https://www.javaadvent.com/2018/12/wtf-connection-pools.html">2018</a>).</p> <p>I also decided to start Selenide Advent Calendar. Every new day a new post. From 1st to 25th December. I am starting, and you can continue - just give me a notice by any possible way.</p> <p>Today topic is:</p> <h1 id="the-main-algorithm">The Main Algorithm</h1> <p>The main feature of Selenide, as many of you say, is <em>automated waiting</em>.<br /> You don’t need to copy-paste boilerplate code like <code class="language-plaintext highlighter-rouge">new WebDriverWait(driver, 30).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[contains(text(),'COMPOSE')]")))</code> to wait for some event. You don’t need to waste your time and effort to decide where a waiting is needed. You don’t need to see a flaky tests and investigate where you should add one more <code class="language-plaintext highlighter-rouge">new WebDriverWait</code>.</p> <p>With Selenide, everything is much more simple. Any line like <code class="language-plaintext highlighter-rouge">$(byText("COMPOSE")).shouldBe(visible)</code> <strong>waits automatically</strong> if needed.</p> <h3 id="doesnt-it-slower-my-tests">Doesn’t it slower my tests?</h3> <p>No.</p> <p>If the element is already visible, Selenide will just go on. If not - Selenide will wait for 100 ms and check again. If still not, wait for another 100 ms and check again. So it will continue up to 4 seconds. And only if the element will not appear after 4 seconds, then Selenide will fail the test.</p> <p>Of course, those numbers 4s and 100ms are configurable.</p> <h3 id="how-it-works">How it works?</h3> <p>We are getting to the Main Algorithm. Actually, it’s very simple.</p> <p>For example, this is how <code class="language-plaintext highlighter-rouge">$(".btn").shouldBe(visible)</code> works:</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">ms</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">less</span> <span class="n">than</span> <span class="no">N</span> <span class="n">seconds</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">"Expected: foo, actually: bar"</span><span class="o">);</span> <span class="c1">// and take a screenshot</span> <span class="o">}</span> </code></pre></div></div> <p>You see: the idea is really simple.</p> <p>In reality, this code <a href="https://github.com/selenide/selenide/blob/master/src/main/java/com/codeborne/selenide/impl/WebElementSource.java#L44">is a bit more complex</a>. It’s because it’s “generic” - not only for <code class="language-plaintext highlighter-rouge">shouldBe(visible)</code>, but for all other methods and conditions.</p> <p>As you know,</p> <h3 id="the-devil-is-in-the-details">the devil is in the details</h3> <p>In the code shown above, there is plenty of questions:</p> <ol> <li>Which exceptions to catch? <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>In case of which exceptions should we stop the waiting and fail the test immediately? <ul> <li>for example, if XPath is invalid, it will remain invalid after 4 seconds too.</li> </ul> </li> <li>The result is ok or nok? <ul> <li>for example, if element is not found - it could be ok for condition <code class="language-plaintext highlighter-rouge">$.shouldNot(exist)</code>.</li> </ul> </li> <li>At what moment would we take screenshot?</li> <li>Can user customize any of these lines? <ul> <li>For example, it’s probably not needed to take a screenshot because Allure will take its own screenshot anyway.</li> <li>Or the opposite, we probably should take screenshot and pass it to Allure somehow?</li> <li>(currently both Selenide and Allure take a screenshot, so they are duplicated)</li> </ul> </li> <li>What if we run until the last line, and during execution of the last line we got a fu#ng <code class="language-plaintext highlighter-rouge">StaleElementException</code>?</li> </ol> <p>Our answers to these questions have been changing in time. And will probably change. But the essence remains.</p> <h2 id="whats-now">What’s now?</h2> <p>I hope you will now understand Selenide waiting instead of perceiving it as a “black magic”. It will probably help you during debugging of some corner cases and mystical test failures.</p> <p>And you can even suggest an idea how to improve or simplify the <s>Skynet core</s> Main Algorithm.</p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/12/01/advent-calendar-main-algorithm/ https://selenide.org/2019/12/01/advent-calendar-main-algorithm 2019-12-01T00:00:00+00:00 Released Selenide 5.5.1 <p>Hi all!</p> <p>At Black Friday, we released <a href="https://github.com/selenide/selenide/milestone/86?closed=1">Selenide 5.5.1</a> with a couple of new features.</p> <p><br /></p> <h1 id="we-added-mobile-browser-emulation">We added mobile browser emulation</h1> <p>Sometimes you want to run browser not on a usual Chrome, but on Chrome “like in mobile”.</p> <p>Like you would open a browser in mobile phone or tablet.</p> <p>(For example, site selenide.org looks ugly on mobile. :( We are working on it.)</p> <p>Now it’s easy to do it by adding a System property before running tests:</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>or right in your 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.mobileEmulation"</span><span class="o">,</span> <span class="s">"deviceName=Nexus 5"</span><span class="o">);</span> </code></pre></div></div> <p>NB! We are talking only about one setting - a device name. It should be enough in most cases. If you need to tune other mobile browser settings, please implement <code class="language-plaintext highlighter-rouge">WebDriverProvider</code> interface.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1008">issue 1008</a> and <a href="https://github.com/selenide/selenide/pull/1011">PR 1011</a>.</p> <p><br /></p> <h1 id="we-deprecated-method-selenideclose">We deprecated method <code class="language-plaintext highlighter-rouge">Selenide.close()</code></h1> <p>Selenide always had method <code class="language-plaintext highlighter-rouge">Selenide.close()</code>. But its name is misleading: it closes the whole browser, not only a current window. The problem is that Selenium Webdriver also has method <code class="language-plaintext highlighter-rouge">close()</code> which only closes a current window/tab (and the whole browser only if it was the only window/tab). And for closing the whole browser Selenium has method <code class="language-plaintext highlighter-rouge">quit()</code>.</p> <p>It was my fault that I created method <code class="language-plaintext highlighter-rouge">close()</code> without reading Selenium documentation. I was young and didn’t believe in Javadoc. :) But part of the problem is that Selenium names <code class="language-plaintext highlighter-rouge">close</code> vs <code class="language-plaintext highlighter-rouge">quit</code> are not intuitive. Go figure which of them closes what.</p> <p>To make your life easier, we now suggest two method with explicit names:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Selenide.closeWindow()</code> - closes a current window (or tab, which is the same)</li> <li><code class="language-plaintext highlighter-rouge">Selenide.closeWebDriver()</code> - closes the whole browser</li> </ul> <p>Now you don’t need to remember anything.</p> <p>The old method <code class="language-plaintext highlighter-rouge">Selenide.close()</code> is now deprecated. Going to delete it in Selenide 6.0.0</p> <p>See <a href="https://github.com/selenide/selenide/issues/1016">issue 1016</a> and <a href="https://github.com/selenide/selenide/pull/1017">PR 1017</a>.</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Wow! A new IDEA plugin for Selenide/Selenium: <a href="https://plugins.jetbrains.com/plugin/13267-qa-lithium">QA Lithium</a>. Thanks to <a href="https://strangeway.org/">Yuriy Artamonov</a>!</li> <li>Another framework on top of Selenide: <a href="https://github.com/cuba-platform/masquerade">Masquerade</a> - <em>CUBA Platform UI Testing Library</em></li> <li>Article <a href="https://medium.com/swlh/selenide-in-testing-process-automatisation-through-selenoid-in-the-docker-container-48e659d2ee72">about Selenide and Selenoid</a></li> <li>Another article <a href="https://www.intexsoft.com/blog/post/selenide-docker.html">about Selenide and Selenoid</a></li> </ul> <p><br /></p> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/11/29/selenide-5.5.1/ https://selenide.org/2019/11/29/selenide-5.5.1 2019-11-29T00:00:00+00:00 Released Selenide 5.5.0 <p>Good evening!</p> <p>How do you celebrate Halloween?</p> <p>We released <a href="https://github.com/selenide/selenide/milestone/85?closed=1">Selenide 5.5.0</a> with some <em>backward incompatible changes</em>.</p> <p>ARE YOU SCARED?</p> <p><br /></p> <h1 id="migrated-from-jul-to-slf4j">Migrated from JUL to SLF4J</h1> <p>Sometimes Selenide needs to write down some diagnostic logs, like “I found browser binary in foo”, “I failed to save screenshot to bar” etc.</p> <p>Until now, Selenide did write its logs using Java built-in mechanism <code class="language-plaintext highlighter-rouge">java.util.logging</code> (aka JUL). It has some known disadvantages, that’s why there is plenty of alternatives in the Java world: Log4J, Slf4j, Logback, JCL.</p> <p>How it works:</p> <ul> <li>Slf4J is a “facade”, something like “interface”. Selenide will write logs using it.</li> <li>And Log4j, Logback, JCL are implementations of this “interface” (probably there are more of them).</li> </ul> <p>You decide which implementation of Slf4J you want to use. Most probably this decision is already done in your project, because at least WebDriverManager always wrote logs using Slf4J.</p> <p>To use one of Slf4J implementations, you need to add corresponding dependency to your project:</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 (NO! Please DON’T! It just ignores all logs.)</li> </ul> <p>NB! Let me remind: most probably you already have one of these dependencies. So you don’t need to do anything.</p> <p>If you still don’t have any of these dependencies, you will see such message when running tests (I will not see Selenide logs):</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>If you see it, probably the easiest solution would be to add one line to your 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>or in 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! If you failed to setup Slf4j - don’t panic. Selenide logs are not so much important. Test failures will not disappear. We are only talking about diagnostic INFO and WARNING logs like <code class="language-plaintext highlighter-rouge">INFO Using browser binary: /var/lib/opera.exe</code>.</p> <p>See <a href="https://github.com/selenide/selenide/issues/923">923</a>.</p> <p>Thanks to <a href="https://github.com/gschukin">Gleb Schukin</a> for <a href="https://github.com/selenide/selenide/pull/926">PR 926</a>.</p> <p><br /></p> <h1 id="removed-htmlunit-and-phantomjs-dependencies">Removed HtmlUnit and PhantomJS dependencies</h1> <p>Don’t be afraid, your tests should still work with HtmlUnit and PhantomJS. Just Selenide will not bring these dependencies transitively (because most of people already migrated to <em>headless chrome</em> and <em>headless firefox</em>).</p> <p>If you still want to use HtmlUnit or PhantomJS, you just need to add corresponding dependencies to your project.</p> <p>Thanks to <a href="https://github.com/rosolko">Aliaksandr Rasolka</a> for <a href="https://github.com/selenide/selenide/pull/1003">PR 1003</a> and <a href="https://github.com/selenide/selenide/pull/998">PR 998</a>.</p> <p><br /></p> <h1 id="removed-built-in-support-for-safari-browser">Removed built-in support for Safari browser</h1> <p>But don’t worry, you can still use Safari! See the <a href="https://github.com/selenide/selenide/wiki/Safari">instruction</a></p> <p><br /></p> <h1 id="made-method-executecommand-generic">Made method <code class="language-plaintext highlighter-rouge">$.execute(command)</code> generic</h1> <p>Before now, method<code class="language-plaintext highlighter-rouge">$.execute</code> accepted only <code class="language-plaintext highlighter-rouge">Command&lt;SelenideElement&gt;</code>, but now it can accept any <code class="language-plaintext highlighter-rouge">Command</code>s.</p> <p>See <a href="https://github.com/selenide/selenide/issues/1000">issue 1000</a> and <a href="https://github.com/selenide/selenide/pull/1001">PR 1001</a>.</p> <p><br /></p> <h1 id="fixed-setting-holdbrowseropen">Fixed setting <code class="language-plaintext highlighter-rouge">holdBrowserOpen</code></h1> <p>.. which we broke in Selenide 5.4.1. Now browser is left opened again, and you can investigate your failed tests.</p> <p>See <a href="https://github.com/selenide/selenide/issues/999">issue 999</a> and <a href="https://github.com/selenide/selenide/pull/1005">PR 1005</a>.</p> <p><br /></p> <h1 id="take-a-screenshot-in-case-of-dialogtextmismatch-error">Take a screenshot in case of <code class="language-plaintext highlighter-rouge">DialogTextMismatch</code> error</h1> <p>As you know, Selenide has a method for verifying text of modal dialog (<code class="language-plaintext highlighter-rouge">alert</code> or <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>But when this check failed, Selenide forgot to take a screenshot. Now it’s fixed.</p> <p>See <a href="https://github.com/selenide/selenide/issues/907">issue 907</a>. Thanks to <a href="https://github.com/nwholloway">Nick Holloway</a> for <a href="https://github.com/selenide/selenide/pull/986">PR 986</a>.</p> <p><br /></p> <h1 id="refactoring-of-screenshotlaboratory">Refactoring of <code class="language-plaintext highlighter-rouge">ScreenShotLaboratory</code></h1> <p>Though it’s just a refactoring, and it will not affect you, I must mention the initiative of <a href="https://github.com/SeleniumTestAB">SeleniumTestAB</a> who decided to improve Selenide code and sent <a href="https://github.com/selenide/selenide/pull/1004">PR 1004</a> and <a href="https://github.com/selenide/selenide/pull/1006">PR 1006</a>.</p> <p>Good job!</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Presentation about Selenide on 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>My presentation for Cyprus Quality Conference <a href="https://docs.google.com/presentation/d/1hSCmjwvLCY4bKqSncffZfMOi1NmXIooJu5LIjxYN6hg/edit">Threesome: Selenide for Web, Android and iOS”</a>. So far, only slides.</li> </ul> <h2 id="and-the-most-important">And the most important!</h2> <p><strong>Selenide got 8 years old</strong>!</p> <p>25.10.2011 a first commit to Selenide was done.</p> <p>After 8 years, we have:</p> <ul> <li>89K downloads from 14K unique IPs (per month)</li> <li>1K stars on github</li> <li>55 contributors</li> </ul> <p>Many thanks to all contributors, users, critics, ideologists and just sympathizers.</p> <p>This is just the beginning :)</p> <p><br /></p> <p>Selenide download statistics for October, 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/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/10/31/selenide-5.5.0/ https://selenide.org/2019/10/31/selenide-5.5.0 2019-10-31T00:00:00+00:00 Released Selenide 5.4.1 <p>Good evening!</p> <p>There is nothing in the World more boring than October. Let’s brighten it up with bright news!</p> <p>Today we released Selenide 5.4.0 with a couple of noticeable changes <em>which many of you have been asking for a while</em>.</p> <p><strong>UPD</strong> There was a bug in 5.4.0, so please upgrade to <strong>5.4.1</strong></p> <p><strong>UPD 2</strong> There is also a minor bug in 5.4.1: <code class="language-plaintext highlighter-rouge">Configuration.holdBrowserOpen</code> doesn’t work. Will fix it in next version. I hope you don’t use it often.</p> <p><br /></p> <h1 id="fix-illegal-state-exception">1. Fixed “IllegalStateException WebDriver has been closed”</h1> <p>Starting from Selenide 5.0.0 (~1 year ago), many users started complaining about <br /> <code class="language-plaintext highlighter-rouge">IIlegalStateException WebDriver has been closed...</code>.</p> <p>Actually it’s not a bug, it was intended behaviour. I didn’t want to fix it for a while because I believe such an exception cannot happen in well-designed tests. Selenide threw in intentionally to stimulate people design their tests better.</p> <p>But we had too many complaints, so I decided to remove this exception - <em>with a heavy heart</em>!</p> <blockquote> <p>In the end, everyone has the right to fill their own bumps.</p> </blockquote> <h3 id="how-this-error-happened">How this error happened?</h3> <p>Shortly speaking, this exception happens when instance of <code class="language-plaintext highlighter-rouge">SelenideElement</code> is reused with multiple instances of <code class="language-plaintext highlighter-rouge">SelenideDriver</code>. It can happen when <code class="language-plaintext highlighter-rouge">SelenideElement</code> is declared as a static field (either of a page object or a test). And is initialized before the browser is opened. Or browser is restarted between tests. It also may happen when people use Cucumber which tries to initialize step objects, page objects before opening a browser. Some folks also put Guice or Spring to their test.</p> <p>All of this is over-engineering. It’s just not needed.</p> <p>People, you don’t need all these things in tests. Tests must be simple. KISS, <em>amen</em>!</p> <p>Watch this presentation to know why <a href="https://www.youtube.com/watch?v=dFQSOlOOoXE&amp;t=1s">Static fields are evil</a>.</p> <h3 id="whats-changed">What’s changed?</h3> <p>But well, people can have different opinions. <em>As a sign of tolerance</em>, let’s meet Selenide 5.4.0</p> <p>Now this code will work without exceptions:</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">// it threw "IllegalStateException WebDriver has been closed" before Selenide 5.4.0</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>You can find more examples and complaints in issues <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>If you are interested in implementation details, welcome to <a href="https://github.com/selenide/selenide/pull/989">PR 989</a>.</p> <p><br /></p> <h1 id="close-always-closes-browser">2. Now method <code class="language-plaintext highlighter-rouge">SelenideDriver.close()</code> always closes the browser</h1> <p>Starting from Selenide 5.0.0, people started complaining that method <code class="language-plaintext highlighter-rouge">close()</code> doesn’t close the browser. It happened in (rare) cases when browser was opened by user code, not Selenide.</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">// it didn't close the browser</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">// it did close the browser</span> </code></pre></div></div> <p>Actually it’s not a bug, it was intended behaviour. Selenide initially has this principle: it closed only the browser opened by Selenide itself. And never closed the browser opened by user: it might be used somewhere else.</p> <p>But again, we had a lot of complaints. It caused a hassle: you see method <code class="language-plaintext highlighter-rouge">close()</code>, you call it, it works without exceptions - but the browser is still open.</p> <p>Now we fixed it. Method <code class="language-plaintext highlighter-rouge">close()</code> always closes the browser.</p> <p>P.S. If your browser is now being unexpectedly closed - feel free to fire a github issue, we will investigate it.</p> <p>See <a href="https://github.com/selenide/selenide/issues/896">issue 896</a> and <a href="https://github.com/selenide/selenide/pull/989">PR 989</a></p> <p><br /></p> <h1 id="restore-short-error-messages">3. We restored short human-readable error messages</h1> <p>You might notice that Selenide 5.3.1 introduced one minor issue. Error messages became longer and hard to read:</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>Now we returned the old good human-readable messages:</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. Poor users of <code class="language-plaintext highlighter-rouge">maven-surefire-plugin 2.22.2</code> will still have long messages, <a href="/2019/09/07/selenide-5.3.1/">see details here</a>.</p> <p>See <a href="https://github.com/selenide/selenide/pull/993">PR 993</a></p> <p><br /></p> <h1 id="add-method-using">4. Added method <code class="language-plaintext highlighter-rouge">using</code> for easier switching between multiple webdrivers</h1> <p>As I already <a href="/2018/10/10/selenide-5.0.0/">said before</a>, using of multiple webdrivers in one test is a bad practice in absolutely most cases.</p> <p>But again, everyone has the right to fill their own bumps. Now it’s a little more easier to fill that bumps with multiple webdrivers.</p> <p>Before Selenide 5.4.0, you had to call <code class="language-plaintext highlighter-rouge">setWebDriver()</code> all the time - and keep in mind which browser is active at the moment:</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">"Released Selenide 5.4.0"</span><span class="o">));</span> </code></pre></div></div> <p>now you can wrap code blocks for different browsers in separate <code class="language-plaintext highlighter-rouge">using</code> calls:</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">"Released Selenide 5.4.0"</span><span class="o">));</span> <span class="o">});</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/pull/976">PR 976</a> and code samples in <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. Added protection against one typical error with xpath</h1> <p>Here is the typical error:</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>The problem is that XPath starting with a slash (“/”) looks for element <strong>from the root of the document</strong> (not from <code class="language-plaintext highlighter-rouge">.parent</code> element).</p> <p>We added a protection. Now this code will throw an exception which clearly states the problem and saves your time:</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>The code above can be fixed in different ways (depending on what you actually need):</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>See <a href="https://github.com/selenide/selenide/pull/963">PR 963</a> and <a href="https://github.com/selenide/selenide/pull/975">PR 975</a>.</p> <p><br /></p> <h1 id="update-dependencies">6. Upgraded dependencies</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>Just in case, we discovered a bug in WDM: when you want to download chromedriver 77, it may download “75.0.3770.90” instead. It’s because this version contains substring “77”. <a href="https://github.com/bonigarcia/webdrivermanager/issues/391">Isn’t it funny?</a> :)</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>A new utility <a href="https://github.com/bsideup/jabel">Jabel</a> from Sergei Egorov: <br />now you can write code in Java13 but compile to Java8. <em>Unbelievable, but it’s possible!</em></li> <li>Video <a href="https://www.youtube.com/watch?v=WNzTuYFd8oI">about Selenide in German</a></li> <li>English-speaking world <a href="https://testguild.com/selenide/">discovers Selenide</a>. The post also contains a link to a podcast about Selenide.</li> <li>Martin Škarbala from Slovakia added english subtitles to his great video about Selenide: <br /> <a href="https://www.youtube.com/watch?v=y9WTRTOTOsc">Selenide - stručné UI testy</a>.</li> <li><a href="https://vimeo.com/363993082">The World Needs Full-Stack Craftsmen</a> - Anton Keks, GeekOut, Tallinn, 26.09.2019</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/10/16/selenide-5.4.0/ https://selenide.org/2019/10/16/selenide-5.4.0 2019-10-16T00:00:00+00:00 Released Selenide 5.3.1 <p>Hi all!</p> <p>There was a sensation:</p> <p><strong>The presentation of the new iPhone went unnoticed!</strong></p> <p>Because we released Selenide 5.3.1. With improved shots as well. :)</p> <p>We fixed only one problem in this version, but what problem is it!</p> <p><br /></p> <h1 id="maven-where-are-my-screenshots">Maven, where are my screenshots?</h1> <p>Recently we discovered that users of Maven and latest <code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.22.2</code> were deprived of one of the most important selenide features: screenshot was not added to the error report.</p> <p>That was unexpected!</p> <p><strong>How did you even live without it, poor fellows?</strong></p> <p><em>Probably that’s why you think you need BDD, Serenity and Allure?</em> Probably you don’t need. :)</p> <p>Read on.</p> <p><br /></p> <h1 id="see-the-difference">See the difference</h1> <p>That’s how error report looks in Maven and latest <code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.22.2</code>:</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>You don’t see a screenshot. Bad luck.</p> <p>And this is how error report looks in Gradle, Ant, any IDE and default <code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.12.4</code>:</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>Now you see a screenshot in case of test failure. It’s enough to investigate the reason of test failure. You just don’t need any “nice” reports, BDD, report portals etc.</p> <p><em>Now we are talking!</em></p> <p><br /></p> <h1 id="investigation">Investigation</h1> <p>But why it happened?</p> <ol> <li>In Java, there is a class <code class="language-plaintext highlighter-rouge">Throwable</code> which is a parent of all errors and exceptions.</li> <li>It has 2 important methods: <code class="language-plaintext highlighter-rouge">getMessage()</code> and <code class="language-plaintext highlighter-rouge">toString()</code>.</li> <li>by default <code class="language-plaintext highlighter-rouge">toString()</code> = <code class="language-plaintext highlighter-rouge">class name</code> + <code class="language-plaintext highlighter-rouge">getMessage()</code></li> </ol> <p>For example, the following code</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">"nope"</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>prints out:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">nope</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">nope</span> </code></pre></div></div> <p>Initially, Selenide added screenshot name in method <code class="language-plaintext highlighter-rouge">UIAssertionError.toString()</code>. And it worked fine in Gradle, Ant, all IDEs. And in Maven too - in case of default <code class="language-plaintext highlighter-rouge">maven-surefire-plugin</code> version.</p> <p><br /></p> <h3 id="so-what-was-updated-in-maven">So what was updated in Maven?</h3> <p>There are funny guys working on Maven.</p> <ol> <li>Maven has 2 plugins for tests, not one.</li> <li>These plugins are named “surefire”, not “test” (which would be intuitive and clear for everyone)</li> <li>The latest Maven 3.6.2 by default uses <em>different versions</em> of those plugins: <ul> <li><code class="language-plaintext highlighter-rouge">maven-surefire-plugin:2.12.4</code> - <strong>7 years old</strong> (e.g. doesn’t support Java 11)</li> <li>and <em>unexpectedly</em> <br /><code class="language-plaintext highlighter-rouge">maven-surefire-report-plugin:3.0.0-M3</code> - <strong>beta version</strong>, not ready for production</li> </ul> </li> </ol> <p><br /></p> <p>Now, if you upgrade to latest <code class="language-plaintext highlighter-rouge">maven-surefire-plugin</code> in your 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>test reports start changing. Instead of <code class="language-plaintext highlighter-rouge">e.toString()</code> it now uses <code class="language-plaintext highlighter-rouge">class name + getMessage()</code>. That’s why screenshots are not appended to error report anymore.</p> <p><br /></p> <h1 id="how-we-fixed-it">How we fixed it?</h1> <p>Simple. We now add screenshot in method <code class="language-plaintext highlighter-rouge">e.getMessage()</code> instead of <code class="language-plaintext highlighter-rouge">e.toString()</code>. Sound simple, but we had to refactor all Selenide exceptions and fix many tests.</p> <p>Probably something went wrong, so feel free to <strong>file issues</strong>, if you wish some error messages to be improved!</p> <p>All the background and code modifications can be found here:</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="news">News</h2> <p>Selenide goes over the world!</p> <ul> <li>Look, what a <a href="https://www.youtube.com/watch?v=y9WTRTOTOsc">great video</a> created Martin Škarbala from Slovakia. In just a 1.5 minutes he demonstrates benefits of Selenide, and how much of boilerplate code you can delete after migration to Selenide.</li> </ul> <p><br /></p> <h2 id="conferences">Conferences</h2> <ul> <li>September, 20-21 - I will give two talks at <a href="http://qafest.com/en/">QA Fest</a> conference - Kyiv, Ukraine</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/09/07/selenide-5.3.1/ https://selenide.org/2019/09/07/selenide-5.3.1 2019-09-07T00:00:00+00:00 Released Selenide 5.3.0 <p>We released Selenide 5.3.0. What’s new?</p> <p><br /></p> <h1 id="968-execute-custom-commands">#968 Execute custom commands</h1> <p>You will like this feature!</p> <p>I believe your code is fill of custom methods for operating web elements. You use some tricky double click, select some non-standard radio button on ReactJS, pick a date from hipster datepicker and upload a file from hidden input.</p> <p>Now we added a convenient way to execute such commands in one line. You just need to call method <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="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> </code></pre></div></div> <p>Thanks to <a href="https://github.com/rosolko">Aleksandr Rasolka</a> for <a href="https://github.com/selenide/selenide/pull/968">pull request 968</a>.</p> <p><br /></p> <h1 id="970-improved-error-message-for-shouldhaveattributehref-">#970 Improved error message for <code class="language-plaintext highlighter-rouge">shouldHave(attribute("href", ..."))</code></h1> <p>Folks reported a strange problem: <code class="language-plaintext highlighter-rouge">href</code> is the same, but test fails:</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>The problem is with one nuance in Selenium: method <code class="language-plaintext highlighter-rouge">WebElement.getAttribute("href")</code> returns absolute URL, even if <code class="language-plaintext highlighter-rouge">href</code> contains a relative URL.<br /></p> <p>Actually we haven’t fixed this nuance, but improved the error message. Now it shows the actual value of the attribute:</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-improved-error-messages-for-or-checks">#469 Improved error messages for OR checks</h1> <p>Probably you know that Selenide allows you to check one of multiple conditions (OR operand). It’s useful when you need to check if first OR second condition is matched (or both).</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#agree"</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">"checked"</span><span class="o">,</span> <span class="n">attribute</span><span class="o">(</span><span class="s">"checked"</span><span class="o">,</span> <span class="s">"true"</span><span class="o">),</span> <span class="n">attribute</span><span class="o">(</span><span class="s">"checked"</span><span class="o">,</span> <span class="s">"on"</span><span class="o">)</span> <span class="o">));</span> </code></pre></div></div> <p><br /></p> <p>The problem was: when such a test failed, it reported an invalid error message. It included only the first condition:</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="err">'</span><span class="n">attribute</span> <span class="n">checked</span><span class="err">'</span> <span class="o">{</span><span class="n">input</span><span class="err">#</span><span class="n">agree</span><span class="o">}</span><span class="err">"</span> </code></pre></div></div> <p><br /></p> <p>We have fixed it. Now it reports all conditions from OR block:</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">checked:</span> <span class="n">attribute</span> <span class="err">'</span><span class="kc">true</span><span class="err">'</span> <span class="n">or</span> <span class="n">attribute</span> <span class="err">'</span><span class="n">on</span><span class="err">'</span> <span class="o">{</span><span class="n">input</span><span class="err">#</span><span class="n">agree</span><span class="o">}</span> </code></pre></div></div> <p>See <a href="https://github.com/selenide/selenide/issues/469">Issue 469</a> and <a href="https://github.com/selenide/selenide/pull/962">PR 962</a>.</p> <p><br /></p> <h1 id="upgraded-to-webdrivermanager362">Upgraded to WebDriverManager:3.6.2</h1> <p>It seems to have mostly fixes for Edge browser. <a href="https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md">Changelog is here</a>.</p> <p><br /></p> <h2 id="news">News</h2> <ul> <li>Boni García (author of WebDriverManager) created <a href="https://bonigarcia.github.io/selenium-jupiter">selenium-jupiter</a> - a wrapper for Selenide (+JUnit5 +Docker). Look ma, <em>a wrapper for a wrapper</em>!</li> <li>Comparision of Selenide and FluentLenium: <a href="https://testcraftsmanship.com/articles/2019/selenide_vs_fluentlenium_part1.html">Part 1</a>, <a href="https://testcraftsmanship.com/articles/2019/selenide_vs_fluentlenium_part2.html">Part 2</a></li> <li>Wow! Sergei Egorov (committer of TestContainers) created a true magic: <a href="https://github.com/bsideup/jabel">Jabel</a>. Now you can write code on Java13 and compile it to Java8 byte code.</li> </ul> <p><br /></p> <h2 id="conferences">Conferences</h2> <ul> <li>September, 20-21 - I will give two talks at <a href="http://qafest.com/en/">QA Fest</a> conference - Kyiv, Ukraine</li> </ul> <p><br /></p> <p><a href="http://asolntsev.github.io/">Andrei Solntsev</a></p> <p>selenide.org</p> https://selenide.org/2019/09/02/selenide-5.3.0/ https://selenide.org/2019/09/02/selenide-5.3.0 2019-09-02T00:00:00+00:00 Released Selenide 5.2.7 <p>Good summer everyone!</p> <p>We recently released several minor Selenide updates: 5.2.5-5.2.7.</p> <p>Let me speak from my heart why we needed them.</p> <p><br /></p> <h1 id="we-fixed-error-messages-when-running-selenideappium">We fixed error messages when running Selenide+Appium</h1> <p>Appium is a webdriver for mobile applications (<code class="language-plaintext highlighter-rouge">AndroidDriver</code>, <code class="language-plaintext highlighter-rouge">IOSDriver</code>). At a first glance, it seems to be a great idea: use the well-known Selenium WebDriver protocol for testing mobile. But many things that were “standard” and “reasonable” in web, work differently in mobile. Or do not work at all.</p> <blockquote> <p>Appium for Selenium is like Lobachevsky’s geometry for Euclidean geometry: everything is round and through the ass.</p> </blockquote> <p>That’s why Selenide could give illegal error messages in case of test failures. When trying to compose an error message, Selenide calls some “standard” methods which always worked in all web browsers: <code class="language-plaintext highlighter-rouge">$.getTagName()</code>, <code class="language-plaintext highlighter-rouge">$.isDisplayed()</code>, <code class="language-plaintext highlighter-rouge">executeJavascript()</code> etc. But which fail in Appium (either Android or iOS or both).</p> <p>To fix this issue, we had to add several <code class="language-plaintext highlighter-rouge">catch (NoSuchElementException | UnsupportedOperationException | UnsupportedCommandException | WebDriverException)</code> blocks which are essentially needed only because of Appium. We needed versions 5.2.6 and 5.2.7 just to add another <code class="language-plaintext highlighter-rouge">catch</code> for another corner case.</p> <p>Now Selenide works better for mobiles. Amen.</p> <p>See <a href="https://github.com/selenide/selenide/issues/496">issue 496 - Fix $.toString() in Appium</a></p> <p>NB! You can find <strong>examples of using Selenide for mobile</strong> <a href="https://github.com/selenide-examples/selenide-appium">on github</a>.</p> <p><br /></p> <h1 id="added-method-open-without-parameters">Added method <code class="language-plaintext highlighter-rouge">open()</code> without parameters</h1> <p>This method just opens an empty browser. Without a URL.</p> <p>It’s also needed for mobile. Selenide 5.+ requires that browser should be opened before any operations with web elements. But the traditional method <code class="language-plaintext highlighter-rouge">open(URL)</code> doesn’t work in Appium - you don’t have any URLs in mobile, you have applications.</p> <p>It’s a stupid situation, but we found an elegant solution, right? :)</p> <p>Thanks to <a href="https://github.com/yaroslav-orel">yaroslav-orel</a> for <a href="https://github.com/selenide/selenide/pull/956">PR 956</a>.</p> <p><br /></p> <h1 id="added-method-because-for-collections">Added method <code class="language-plaintext highlighter-rouge">because</code> for collections</h1> <p>As you know, to make error messages more readable, you can add a description to any condition. You don’t just say that the button is expected to be blue, but you also explain why <em>you think</em> that it should be blue:</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