DEV Community

Jordan Brennan
Jordan Brennan

Posted on • Updated on

HTML Attribute for Tests & Tool Integration

Websites, especially SaaS products, often integrate with tools like Glassbox, Adobe Analytics, and Cypress. These tools work by querying or binding to elements in the DOM and are most often configured to use classes and/or ids.

Using classes and ids does work, but there's another approach myself and some coworkers came up with: the data-ref attribute

The data-ref attribute

This special attribute serves as a dedicated selector for tests and integrating third-party tools. It looks like:

<button data-ref="..."></button>
Enter fullscreen mode Exit fullscreen mode

And it works on any HTML element, which makes it just as viable as classes.

Examples
Let's say an end-to-end test needs to verify the presence of the login button before proceeding to the next step. Add a dedicated data-ref attribute instead of a class to the button and test:

const loginBtn = document.querySelector('[data-ref="login"]');
expect(loginBtn).toBeDefined();
Enter fullscreen mode Exit fullscreen mode

Another example could be a product manager wanting to analyze user engagement with a new feature on the Home page, like "How many people scrolled to the new feature and how many clicked the see more link?" Add data-ref attributes to the elements being tracked:

<h2 data-ref="home.new-feature.intro">The New Feature!</h2>
<p>Lorem ipsum...</p>
<a data-ref="home.new-feature.expand" href="...">See more</a>
Enter fullscreen mode Exit fullscreen mode

So there's a couple quick examples of how it gets used. Let's look at the attribute's name and value in a little more detail.

Attribute name
The name includes the familiar "ref" abbreviation used by many frameworks (ref is short for reference). Ref is preferred because it's short and applicable to any tool, whereas "test", "e2e", "track", "session", or "at" (acceptance test) all have too narrow of a meaning.

The standard data- prefix was added to ensure compatibility and prevent interference with frameworks that use ref.

Attribute value
Unlike classes which are mixed together with other classes, attribute values are separate and exclusive. This makes them more reliable and also more readable.

Attribute values also have a lot of flexibility. You can put just about anything in them. This means patterns like {page}.{module}.{element} and {page}.{items}.{id} can be established that help provide useful context to anyone in contact with these attributes:

<p data-ref="home.announcement">You won!</p>

<button data-ref="account.profile.save">Save</button>

<ul>
  <li data-ref="products.shoes.xm4u-020">Yeezy 500 Salt</li>
  <li data-ref="products.shoes.ac3e-001">Yeezy Boost 380</li>
</ul>
Enter fullscreen mode Exit fullscreen mode

Get creative and do whatever suits you!

What's wrong with classes?

Nothing, except they're being used for everything. Ever change a style class and accidentally broke an event binding or test? Many of us have probably done that!

The problem is so widespread in fact that a lot of methodologies (BEM, SMACSS, SUIT, js- prefix, and more) have popped up over the years attempting to help create more manageable class names. In reality, they're just engineering around the design flaws of class overuse.

The solution is no secret: decoupling and separation of concerns

All but one of the requirements solved with classes have better alternatives:

Event bindings They never needed a class. Custom attributes, like Bootstrap's data-target, worked well, but the standard onevent handlers are and always have been a better design choice and frameworks like Vue make them so easy to use now. Goodbye .js-* classes!

UI components They are so much better implemented as custom HTML tags. Take a simple component like Icon or Alert:

<div class="icon icon-user"></div>
<div class="alert alert-success alert-dismissible">...</div>

vs.

<m-icon name="user"></m-icon>
<m-alert type="success" autodismiss>...</m-alert>
Enter fullscreen mode Exit fullscreen mode

Big difference! The familiar tag + attribute design is more robust and readable than the boilerplate div and class.

Seriously, look how bad classes get (and this example is the state-of-the-art when it comes to CSS) compared to the beauty of custom HTML tags :

<div class="mdc-layout-grid">
    <div class="mdc-layout-grid__inner">
        <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-3 mdc-layout-grid__cell--span-6-phone"></div>
        <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-9 mdc-layout-grid__cell--span-6-phone"></div>
    </div>
</div>

vs.

<m-row>
    <m-col span="3 sm-6"></m-col>
    <m-col span="9 sm-6"></m-col>
</m-row>
Enter fullscreen mode Exit fullscreen mode

Hello, genuine API! Goodbye convoluted methodologies!

Test and tool integration A web standard does not exist, so a custom attribute like data-ref decoupled from everything else and focused on specific concerns is the next best thing. No more broken tests or missing analytics because "Sorry, I thought that class was only used for styling!"

Styles Generic style classes are the one requirement best solved with classes.

Configuring tools for data-ref

The number of tools that could use data-ref is huge. There are a ton! They generally fall into three categories:

Test & monitoring
These are used for component, acceptance, end-to-end tests and site monitoring. Examples include:

  • Datadog Synthetics
  • Cypress
  • Selenium
  • Jest

Session replay
These tools record users' sessions, which makes it possible to replay later for tech support situations and user research. Examples include:

  • Glassbox
  • FullStory
  • Tealeaf

User Analytics
These tools collect lots of user data like pages visited, page events, user interactions, device, and time. Effective product orgs use these tools to run tests and collect data for better decision-making. Examples:

  • Adobe Analytics
  • Google Analytics

All of these tools work with data-ref just as well as classes.

[Cool story: When we came up with data-ref, FullStory was our only tool that didn't work with it. We asked them to support it and they cut a new release in less than two weeks. FullStory is awesome!]

Configuration
Configuring these tools to use data-ref is simple. Tests using the class selector just use the attribute selector instead:

// Before
const loginBtn = document.querySelector('.login');
expect(loginBtn).toBeDefined();

// After
const loginBtn = document.querySelector('[data-ref="login"]');
expect(loginBtn).toBeDefined();
Enter fullscreen mode Exit fullscreen mode

Tools with an admin console will have a section where you configure the integration. It varies from tool to tool, but there will be a setting where you set the selector(s) to use. For example, this Adobe Launch configuration tracks all clicks on any element with a data-ref:
Alt Text

Other tools support searching through logs/recordings and a universal "data-ref" query is so much better than having to find all the possible classes and searching for those.

Conclusion

Data-ref's decoupled and focused design make its purpose clear and avoids risks associated with classes. So, in the absence of a web standard*, data-ref might be the next best method for test and tool integration.

*I might propose a new standard. I'm thinking something like ARIA...message me if you're interested in collaborating

What do you think? What other approaches have you tried?

Discussion (0)