Released Selenide 7.17.0

Released Selenide 7.17.0

Role play
12.07.26

Buenos hellos!

Take a second break from the football and check out what release just rolled in:
Selenide 7.17.0!


We finally shipped the byRole locator!


byRole locator β€” find an element by ARIA role

Lately, “accessibility locators” have become all the rage.

I love the thought that these were originally invented to help blind and visually impaired people and so on. And testers went like: sweet, who cares about the blind, we’ll use this in our tests so we don’t have to hunt for IDs!

It’s often handy in tests to search for elements by role. Like, “find the button” β€” without caring how that button is actually implemented under the hood.

Now the byRole method lets you find an element by role, or by role and text.

$(byRole("button")).click();
$(byRole("button", "Start refueling")).click();
  • And the role isn’t only matched via the explicit role="button" attribute, but also via a tag’s implicit role β€” if you’ve got an honest-to-goodness <button> or <a href="...">, Selenide will find it too. The list of supported roles is decent: button, link, heading, checkbox, radio, textbox, searchbox, combobox, listbox, option, img, list, listitem, table, row, cell, navigation, dialog, form, and about a dozen more.

  • And the text (i.e. accessibleName β€” the element’s “accessible name”) is computed in a specific order: first aria-labelledby, then aria-label, then a linked <label>, then alt, then the inner text, and finally title.


ElementLocator
<button>Refuel</button>$(byRole("button")).click();
<h1>No fuel<h2>,
<h6>Station down</h6>
$$(byRole("heading"))
<div role="alert">No 95 octane!</div>$(byRole("alert"))
<button aria-label="We don't fill canisters">Γ—</button>$(byRole("button", "We don't fill canisters"))
<label>Complaint
<input type="text"></label>
$(byRole("textbox", "Complaint"))
<img src="qr.png"
alt="QR code for refueling"/>
$(byRole("img", "QR code for refueling"))


You can refine the text search too: “full text” or “substring”, “case-sensitive” or “case-insensitive”; “ignore whitespace” or “respect whitespace” β€” same as in byPlaceholder, byTitle, byAltText:

import static com.codeborne.selenide.TextMatchOptions.*;
import static com.codeborne.selenide.Selectors.byRole;

$(byRole("img", "QR code", partialText()));
$(byRole("img", "qr code for REFUELING", fullText().caseInsensitive()));
$(byRole("img", "QR     code    for    refueling", partialText().ignoreWhitespaces()));

This isn’t a full implementation of the W3C AccName algorithm yet (recursive subtree traversal and hidden elements aren’t there yet), but for 95% of real-world pages it’ll be more than enough.

See issue 3359 and PR 3336.


More MCP tools for your AI slave!

Remember we recently shipped the Selenide MCP server? At first it only had a limited set of tools β€” just enough to try it out and play around. Now we’ve vibe-coded it nine more tools:

  • tab management β€” browser_tab_list, browser_tab_select, browser_tab_new, browser_tab_close
  • browser_resize β€” resize the browser window
  • browser_fill_form β€” fill a whole batch of fields in one call
  • browser_wait_for β€” wait for text, for text to disappear, for an element’s state, or just for a plain timeout
  • browser_network_requests / browser_network_request β€” see what the browser’s actually been asking the backend for (requires --proxy-enabled at startup)

Now your AI can see all the tabs on the field, track network requests, and even fill out forms with a single kick instead of poking at every field one by one.

> claude
> /mcp enable selenide

> Dear agent,
> resize the window to mobile size and find out which gas stations have 95-octane fuel.
> Open each gas station in a separate tab.
> Grab the session cookies from all the responses and retry every 5 minutes.
> Don't give up!

See PR 3335.


Downloading multiple files at once

Selenide has long had the $.download method with a whole bunch of options, to click a button and download a file.

But here’s the catch: sometimes one click triggers downloading several files at once, and the test wants to check all of them. And sometimes downloading them one at a time just isn’t an option β€” say, if the link is single-use.

Now there’s a $.downloadFiles method, which clicks and returns several files at once:

List<File> reports = $("#reports").downloadFiles(
    files(5).withExtension("zip")
);
assertThat(reports).hasSizeGreaterThanOrEqualTo(5);

Handy if one click in the UI triggers downloading a whole batch of files (reports, archives, whatever), and you need to check that they all arrived and are the right format.

See issue 3261 and PR 3334.


SelenideAppiumElement now honestly returns SelenideAppiumElement

An old thorn in mobile tests’ side, going on two years now: if you called find()/$()/$x() on a SelenideAppiumElement, it used to hand you back a plain SelenideElement β€” and all the mobile-specific methods became unavailable. Like calling up a striker for the national team, and then sending a midfielder onto the pitch instead.

SelenideAppiumElement parent = SelenideAppium.$(By.xpath("//android.view.ViewGroup"));
SelenideAppiumElement child = SelenideAppium.$(By.xpath("//android.widget.TextView"));

  // Compilation error: child is `SelenideElement`, not `SelenideAppiumElement`.

Now find(), $(), $x(), findAll(), $$(), $$x() on SelenideAppiumElement return proper Appium elements β€” throughout the whole call chain, not just at the first level.

Thanks to Stanislav Vasenkov for starting to dig into this in PR 3362.

See issue 2761 and PR 3373.


Selenide.cookies() β€” a new API for cookies

Selenide has long had a nice, symmetric API for storages:

sessionStorage().clear();
localStorage().clear();

But cookies never got that same beauty β€” just the old clunky Selenide.clearBrowserCookies() and checks via webdriver().shouldHave(cookie("name")).

We fixed that injustice β€” now cookies live in the same lineup:

cookies().clear();
cookies().add("mouse", "Jerry");

cookies().shouldHave(cookie("cat"));
cookies().shouldHave(cookie("mouse", "Jerry"));

While we were at it, we deprecated the old clearBrowserLocalStorage() and clearBrowserCookies() methods β€” use

  • localStorage().clear(),
  • sessionStorage().clear() and
  • cookies().clear().

Thanks to Stanislav Vasenkov for PR 3361.

See PR 3375 and issue 3357.


element()/elements() for Kotlin developers in Appium

If you write Appium tests in Kotlin, you’ve probably already cursed the $ method β€” in Kotlin you have to escape it with backticks, and it looks more like a magic spell than code:

To cool down the burning Kotlin backsides, we added element()/elements() aliases β€” just like in Selenide.

// before
private val button = SelenideAppium.`$`(CombinedBy.android(...).ios(...))

// after
private val button = SelenideAppium.element(CombinedBy.android(...).ios(...))

Thanks to Gabriel Baldez for the PR!

See issue 3348 and PR 3350.


Upgraded Selenium from 4.44.0 to 4.46.0

And bumped the CDP version from v148 to v150.

Notable changes for us:

import org.openqa.selenium.devtools.v148.browser.Browser;

and forever bumping it across all your tests, you can just write “latest” and forget about it forever:

import org.openqa.selenium.devtools.latest.browser.Browser;

See PR 3347, PR 3374 and the Selenium changelog.


Don’t delay,

update!

We’ll be vibecoding even more soon…



Andrei Solntsev

selenide.org

12.07.26