Skip to main content

Β· 3 min read

Fetching elements within e2e tests can sometimes be very hard. Complex CSS paths or arbitrary test ids make them either less readable or prone to failures. The disappointment we experience when our test fail is by far not comparable to a the bad experience people have when they need to use assistent devices like screen readers on applications build without accessibility in mind.

With the accessibility selector introduced in version v7.24.0 WebdriverIO now provides a powerful way to fetch various of elements containing a certain accessibility name. Rather than applying arbitrary data-testId properties to elements which won't be recognised by assistent devices, developers or QA engineers can now either apply a correct accessibility name to the element themselves or ask the development team to improve the accessibility so that writing tests becomes easier.

WebdriverIO internally uses a chain of xPath selector conditions to fetch the correct element. While the framework has no access to the accessibility tree of the browser, it can only guess the correct name here. As accessibility names are computed based on author supplied names and content names, WebdriverIO fetches an element based in a certain order:

  1. First we try to find an element that has an aria-labelledBy or aria-describedBy property pointing to an element containing a valid id, e.g.:
    <h2 id="social">Social Media</h2>
    <nav aria-labelledBy="social">...</nav>
    So we can fetch a certain link within our navigation via:
    await $('aria/Social Media').$('a=API').click()
  2. Then we look for elements with a certain aria-label, e.g.:
    <button aria-label="close button">X</button>
    Rather than using X to fetch the element or applying a test id property we can just do:
    await $('aria/close button').click()
  3. Well defined HTML forms provide a label to every input element, e.g.:
    <label for="username">Username</label>
    <input id="username" type="text" />
    Setting the value of the input can now be done via:
    await $('aria/Username').setValue('foobar')
  4. Less ideal but still working are placeholder or aria-placeholder properties:
    <input placeholder="Your Username" type="text" />
    Which can now be used to fetch elements as well:
    await $('aria/Your Username').setValue('foobar')
  5. Furthermore if an image tag provides a certain alternative text, this can be used to query that element as well, e.g.:
    <img alt="A warm sommer night" src="..." />
    Such an image can be now fetched via:
    await $('aria/A warm sommer night').getTagName() // outputs "img"
  6. Lastly, if no proper accessibility name can be derived, it is computed by its accumulated text, e.g.:
    Such a heading tag can be now fetched via:
    await $('aria/Welcome!').getTagName() // outputs "h1"

As you can see, there are a variety of ways to define the accessibility name of an element. Many of the browser debugging tools provide handy accessibility features that help you to find the proper name of the element:

Getting Accessibility Name in Chrome DevTools

For more information check out the Chrome DevTools or Firefox Accessibility Inspector docs.

Accessibility is not only a powerful tool to create an inclusive web, it can also help you write stable and readable tests. While you should not go ahead and give every element an aria-label, this new selector can help you build web applications with accessibility in mind so that writing e2e tests for it later on will become much easier.

Thanks for reading!

Β· 5 min read

WebdriverIO is one of the most popular test frameworks and an excellent Web integration tool.

In fact, it's one of my favourites ❀️

But, to write truly great acceptance tests you need more than that:

  • You need business-friendly abstractions that capture the language of your domain and make even the most sophisticated, multi-actor, and cross-system workflows easy to design and adapt as the requirements change.
  • You need in-depth reporting that tells you not only what tests were executed, but also what requirements and business capabilities have (and have not!) been tested.
  • And on top of that, you need to be able to interact with all the interfaces of your system, so that the slower UI-based interactions are just one tool in your testing toolbox, rather than your only option.

Of course, all the above is way outside of the scope of what WebdriverIO is trying to accomplish.

Typically, you and your team would need to figure it out all by yourselves.

But, what if I told you that there was a better way? That there was another framework that's perfectly compatible with WebdriverIO and optimised to help you write world-class, full-stack acceptance tests following the Screenplay Pattern and SOLID design principles, even if not everyone on your team is an experienced test engineer?

What if I told you that this framework also covers business-friendly reporting and helps you write high-quality test code that's easy to understand, maintain, and reuse across projects and teams?

What if I told you that you could add it to your existing WebdriverIO test suites, today?

Please allow me to introduce, Serenity/JS!

About Serenity/JS​

Serenity/JS is an open-source acceptance testing and integration framework, designed to make writing truly great acceptance tests easier, more collaborative, and fun! πŸš€

While you can use Serenity/JS to test systems of any complexity, it works particularly well in complex, workflow-based, multi-actor contexts.

At a high level, Serenity/JS is a modular framework that provides adapters that make it easy to integrate your tests with Web apps, REST APIs, Node.js servers, and pretty much anything a Node.js program can talk to.

Thanks to Serenity/JS Screenplay Pattern, you and your team will also have a simple, consistent, and async-friendly programming model across all those interfaces.

Apart from integrating with your system under test, Serenity/JS can also integrate with popular test runners such as Cucumber, Jasmine, Mocha, Protractor, and now also WebdriverIO!

Better yet, Serenity/JS provides a unique reporting system to help you generate consistent test execution and feature coverage reports across all the interfaces of your system and across all your test suites. Serenity/JS reporting services can work together with your existing WebdriverIO reporters too!

Thinking in Serenity/JS​

The best way to get started with Serenity/JS is to follow our brand-new series of tutorials, where you'll learn how to build full-stack test automation frameworks from scratch.

Check out "Thinking in Serenity/JS" πŸ€“

Examples and project templates​

If you prefer to kick the tires and jump straight into the code, you'll find over a dozen example projects in the Serenity/JS GitHub repository and plenty of code samples in our API docs.

We've also created WebdriverIO + Serenity/JS project templates to help you get started:

All the above templates are configured to produce Serenity BDD HTML reports, automatically capture screenshots upon test failure, and run in a Continuous Integration environment.

Adding Serenity/JS to an existing project​

If you're using WebdriverIO with Mocha, run the following command in your computer terminal to add the relevant Serenity/JS modules to your project:

npm install --save-dev @serenity-js/{code,console-reporter,mocha,webdriverio}

If you're using Cucumber or Jasmine instead, replace mocha with the name of your preferred test runner, so cucumber or jasmine, respectively.

Next, tell WebdriverIO to use Serenity/JS instead of the default framework adapter:

import { ConsoleReporter } from '@serenity-js/console-reporter';
import { WebdriverIOConfig } from '@serenity-js/webdriverio';

export const config: WebdriverIOConfig = {

// Enable Serenity/JS framework adapter
// see:
framework: '@serenity-js/webdriverio',

serenity: {
// Use Serenity/JS test runner adapter for Mocha
runner: 'mocha', // see:
// runner: 'jasmine', // see:
// runner: 'cucumber', // see:

// Configure reporting services
// see:
crew: [

// ... other WebdriverIO configuration

And that's it!

The above configuration enables Serenity/JS Console Reporter, which produces output similar to the below and plays nicely with any existing WebdriverIO reporters you might have already configured:

Serenity/JS Console Reporter output

To enable Serenity BDD HTML Reporter, please follow the instructions.

To learn about Serenity/JS Screenplay Pattern, follow the tutorial.

Questions? Feedback? Ideas?​

If you have questions about Serenity/JS or need guidance in getting started, join our friendly Serenity/JS Community Chat channel.

For project news and updates, follow @SerenityJS on Twitter.

And if you like Serenity/JS and would like to see more articles on how to use it with WebdriverIO, remember to give us a ⭐ on Serenity/JS GitHub! 😊

Enjoy Serenity!


Β· 3 min read

For many years one of the selling features of the WebdriverIO framework was its synchronous API. Especially for folks coming from more synchronous oriented languages such as Java or Ruby, it has helped to avoid race conditions when executing commands. But also people that are more familiar with Promises tend to prefer synchronous execution as it made the code easier to read and handle.

Running asynchronous commands in a synchronous way was possible with the help of the @wdio/sync package. If installed, WebdriverIO would automatically wrap the commands with utility methods that were using the fibers package to create a synchronous execution environment. It uses some internal V8 APIs to allow to jump between multiple call stacks from within a single thread. This approach also has been popular among other projects, e.g. Meteor, where most of the code is written using asynchronous APIs which causes developers to constantly start the line of code with await.

Last year the author of the Fibers package announced that he would no longer continue to maintain the project anymore. He built Fibers when JavaScript did not have any proper mechanism to handle asynchronous code other than using callbacks. With JavaScript evolving and adding APIs like Promises or Generators there is technically no reason anymore for Fibers to exist other than preference of code style. Now with the release of Node.js v16 and the update to V8 v9 Fibers stopped working due to a change in V8 that would remove some internal interfaces Fibers was using. Given that a fix for this is non trivial and the maintainer already stepped down from the project it is unlikely that we will see support for Fibers in Node.js v16 and on.

After the WebdriverIO team discovered this we immediately took action and evaluated our options. We opened an RFC to discuss with the community in which direction the project should go. I would like to thank everyone who chimed in and provided their opinion. We experimented with some alternative options, e.g. using Babel to transpile synchronous code into asynchronous but they all failed due to various reasons. The ultimate decision was made to accept the fact that synchronous command execution won't be possible moving on and rather embrace asynchronicity.

With the release of WebdriverIO v7.9 we are happy to announce that we improved our asynchronous API and matched it with the synchronous one. When chaining element command calls users had to write aweful code like this before:

await (await (await $('#foo')).$$('.bar'))[42].click()

now this got simiplified to this:

await $('#foo').$$('.bar')[42].click()

Thanks to the enormous power of the Proxy Object the API is now much more streamlined and less verbose. This will also help users migrating a project that uses the synchronous API to become asynchronous. The team will work on a codemod to help make this process as automated and easy as possible.

At this point the WebdriverIO team wants to thank Marcel Laverdet (@laverdet on GitHub) for building Fibers and maintaining it for so many years. It is time to move on and embrace all the great JavaScript language feature many people have worked hard on. While we have updated the code examples in our docs, @wdio/sync will continue to be supported until we drop support for Node.js v14 and earlier which won't happen before April 2023. You will have enough time to slowly migrate your tests using async/await.

If you have any questions on this or the migration from a framework writing with synchronous commands to asynchronous code, feel free to drop us a line in our discussion forum or on Gitter.