<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mark Noonan</title>
    <description>The latest articles on Forem by Mark Noonan (@marktnoonan).</description>
    <link>https://forem.com/marktnoonan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F113592%2F9e2c0347-20a8-406e-a7f4-031d4ce6e98d.jpeg</url>
      <title>Forem: Mark Noonan</title>
      <link>https://forem.com/marktnoonan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/marktnoonan"/>
    <language>en</language>
    <item>
      <title>Open Source Accessibility Plugins in Cypress</title>
      <dc:creator>Mark Noonan</dc:creator>
      <pubDate>Wed, 19 Feb 2025 16:02:45 +0000</pubDate>
      <link>https://forem.com/cypress/open-source-accessibility-plugins-in-cypress-23da</link>
      <guid>https://forem.com/cypress/open-source-accessibility-plugins-in-cypress-23da</guid>
      <description>&lt;p&gt;Last year saw the GA launch of Cypress's commercial &lt;a href="https://on.cypress.io/accessibility-trial?utm_medium=intro&amp;amp;utm_source=dev-to&amp;amp;utm_campaign=dev-to-syndication&amp;amp;utm_term=&amp;amp;utm_content=accessibility-testing-solution" rel="noopener noreferrer"&gt;accessibility testing solution&lt;/a&gt; built into Cypress Cloud. We're delighted to see this helping teams speed up and scale their accessibility efforts.&lt;/p&gt;

&lt;p&gt;But accessibility testing itself is &lt;strong&gt;nothing new&lt;/strong&gt; in Cypress, and there are many different ways to account for accessibility in test automation which all complement each other. We’ve blogged in the past about considering &lt;a href="https://www.cypress.io/blog/maintaining-accessibility-through-user-focused-testing?utm_medium=intro&amp;amp;utm_source=dev-to&amp;amp;utm_campaign=dev-to-syndication&amp;amp;utm_term=&amp;amp;utm_content=accessibility-in-component-tests" rel="noopener noreferrer"&gt;accessibility in component tests&lt;/a&gt;, and recently added an &lt;a href="https://docs.cypress.io/app/guides/accessibility-testing?utm_medium=intro&amp;amp;utm_source=dev-to&amp;amp;utm_campaign=dev-to-syndication&amp;amp;utm_term=&amp;amp;utm_content=accessibility-testing-guide" rel="noopener noreferrer"&gt;accessibility testing guide&lt;/a&gt; to the open-source Cypress App docs.&lt;/p&gt;

&lt;p&gt;When drafting that guide I found I wanted to go a bit deeper into the community plugin side of accessibility and Cypress, as I realized that in 2024 alone, there were multiple accessibility related plugins released, some directly related to testing, and others more geared towards customization of Cypress itself.&lt;/p&gt;

&lt;p&gt;In this post we’ll take a look at three recently-released accessibility-related Cypress Plugins: Wick A11y, Cypress Runner Themes, and Cypress Voice Plugin, and hear from their creators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cypress Voice Plugin
&lt;/h2&gt;

&lt;p&gt;This plugin was created by &lt;a href="https://www.linkedin.com/in/dennis-bergevin/" rel="noopener noreferrer"&gt;Dennis Bergiven&lt;/a&gt;, who told me "The voice plugin was inspired by your chat with Filip Hric, where you chatted over various ideas and topics using Cypress". This was a really fun live stream &lt;a href="https://www.linkedin.com/in/filip-hric/" rel="noopener noreferrer"&gt;Filip&lt;/a&gt; and I did back in 2023, and here's the relevant clip called &lt;a href="https://www.youtube.com/watch?v=-SS4G8qd4EM" rel="noopener noreferrer"&gt;"How to make Cypress talk"&lt;/a&gt;. In it, I show how to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis" rel="noopener noreferrer"&gt;built-in browser Speech Synthesis&lt;/a&gt; to have Cypress verbally announce something at the end of a spec's execution, especially where the spec might contain many tests or do something that takes some time and you have already moved your screen back to the next code change. (In addition to being a great testing educator, Filip is also the developer of the popular &lt;a href="https://www.npmjs.com/package/cypress-plugin-api" rel="noopener noreferrer"&gt;Cypress API testing plugin&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;This was just a proof of concept, as we discussed various ways to hack different functionality into Cypress to customize it for specific testing needs. Dennis described later seeing a practical problem at work that this could help with, saying "The idea came back when some colleagues at work expressed they wanted a faster way to detect a test result when running in the UI."&lt;/p&gt;

&lt;p&gt;The end result is a highly configurable voice announcer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xgayfauful3ixx9z4gq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xgayfauful3ixx9z4gq.png" alt="A zoomed-in view of the Cypress App showing three sliders to control voice rate, voice pitch, and voice volume for the Cypress Voice Plugin" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dennis added that "using native web speech API within the plugin fits right into the Cypress framework as it lives in the browser as well", which made the implementation possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://medium.com/@drbergevin/cypress-voice-plugin-an-auditory-tool-for-the-cypress-test-runner-ui-1726040b693e?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Medium post about Cypress Voice Plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.npmjs.com/package/cypress-voice-plugin?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Cypress Voice Plugin's npm package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cypress Runner Themes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/dingraham01/?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;David Ingraham&lt;/a&gt; recognized two needs in the community and created a plugin to solve both. First he created a more color-blind friendly theme for the Cypress command in the Cypress App, to avoid using red and green to hint at the passing/failing status of tests. Then he rolled this up into a general Cypress theming package to combine and update some existing theming solutions like &lt;a href="https://www.npmjs.com/package/cypress-light-theme?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;cypress-light-theme&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/cypress-dark?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;cypress-dark&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;David said, "I created &lt;code&gt;cypress-runner-themes&lt;/code&gt; after a user on the discord mentioned they were having trouble reading Cypress results due to being colorblind and I noticed there wasn't any existing plugins out there. In combination with the old plugin &lt;code&gt;cypress-dark&lt;/code&gt; being unsupported, I thought it would be a good idea to provide a newer theme plugin with accessibility support."&lt;/p&gt;

&lt;p&gt;Cypress does not have built-in theme support, but David has been able to leverage the fact that Cypress executes plugin code in the browser, and runs in the same environment, to make modifications to the Cypress interface. Plugins are a common way that the community can get out ahead and iterate on behavior that might be desirable in the Cypress App, without waiting for it to be implemented into the core Cypress library. Here's an example of one of the themes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv4ld58h9yg2kjvphmci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv4ld58h9yg2kjvphmci.png" alt="The Cypress App open with a custom yellow color replacing the standard green color for a passing test." width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"It was my first community plugin so I also wanted to learn something new while making a real impact on the community," David told us. We look forward to seeing more in the future!&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://medium.com/@dingraham01/cypress-runner-themes-alternative-styles-for-the-cypress-test-runner-9505881002a0?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Cypress Runner Themes medium post&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.npmjs.com/package/cypress-runner-themes?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Cypress Runner Themes npm package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wick-A11y
&lt;/h2&gt;

&lt;p&gt;Created by &lt;a href="https://www.linkedin.com/in/sebastianclavijosuero/?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Sebastian Clavijo Suero&lt;/a&gt;, and named for its John Wick-style ability to identify and remove accessibility issues with extreme prejudice, this summer blockbuster adds some visual, reporting, and voice-announcing dimensions to the &lt;a href="https://github.com/component-driven/cypress-axe?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;cypress-axe plugin&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Before we hear from Sebastian, it's worth backing up for a second to explain the connection between all these tools.&lt;/p&gt;

&lt;p&gt;At the root is the open source &lt;a href="https://github.com/dequelabs/axe-core?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Axe Core library&lt;/a&gt; by Deque Systems, which was released in 2015. Axe Core has over 1 billion downloads and is used in many accessibility testing systems such as Google's Lighthouse, as well as in &lt;a href="https://on.cypress.io/accessibility-trial?utm_medium=intro&amp;amp;utm_source=dev-to&amp;amp;utm_campaign=dev-to-syndication&amp;amp;utm_term=&amp;amp;utm_content=cypress-accessibility" rel="noopener noreferrer"&gt;Cypress Accessibility&lt;/a&gt;. (Side note: we were delighted to welcome Dylan Barrell, the creator of Axe Core, to Cypress Conf 2023, where he shared his presentation about &lt;a href="https://www.youtube.com/watch?v=MM8u1dbniwQ&amp;amp;ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Agile Accessible Software Development&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;The cypress-axe plugin by Andy Van Slaars was first published in 2018, providing an easy integration in Cypress allowing testers to add Axe Core checks where they chose at different steps of the user journey in their tests. In our docs, we discuss more about these kinds of &lt;a href="https://docs.cypress.io/app/guides/accessibility-testing?utm_medium=intro&amp;amp;utm_source=dev-to&amp;amp;utm_campaign=dev-to-syndication&amp;amp;utm_term=&amp;amp;utm_content=in-test-accessibility-scans" rel="noopener noreferrer"&gt;in-test accessibility scans&lt;/a&gt; work, so I won't repeat that here.&lt;/p&gt;

&lt;p&gt;While there are other accessibility checkers that also have Cypress integrations, cypress-axe has been the go-to for most Cypress users.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;OK, now we're up to date!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Much like the other plugin authors, Sebastian saw an opportunity for extending beyond the current behavior to solve a problem, and shared that solution with others. I send him over a few questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What inspired you to create wick-a11y?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I created Wick-a11y to significantly simplify accessibility testing, which is often neglected despite its crucial role in developing inclusive web applications. Although tools like cypress-axe are easy to set up, they provide raw data that requires additional processing for clear interpretation of the results. Wick-a11y seamlessly integrates with Cypress, delivering an easy-to-use solution to assess accessibility, interact with results directly in the Cypress runner, and effortlessly produce detailed HTML reports with screenshots.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What made Cypress a good platform for this?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cypress is an excellent platform for wick-a11y due to its seamless integration capabilities with tools like axe-core for comprehensive accessibility testing. Cypress's interactive test runner and user-friendly interface allow for real-time interaction with accessibility results directly on the web page, enhancing the identification and resolution of issues. Additionally, Cypress supports detailed logging, providing testers with an efficient way to document and share accessibility findings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Tell us about your recent addition of voice feedback&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I added the voice feedback feature to wick-a11y, acknowledging that many individuals testing website accessibility are affected by disabilities themselves. By providing auditory cues alongside accessibility violations, this feature enhances the testing experience, empowering those committed to inclusivity to engage without barriers and continue championing a more accessible web for everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's an example of a test open in the Cypress App, showing some accessibility issues detected with cypress-axe and highlighted by wick-a11y&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7294kcp7kef1shsvaty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7294kcp7kef1shsvaty.png" alt="The wick a11y plugin running, which adds highlights to elements with failing accessibility checks and lists elements in the command log of the Cypress App" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is much more to see, so check out the links below to learn more about adding these kinds of accessibility assertions to your Cypress tests.&lt;/p&gt;

&lt;p&gt;Links&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://dev.to/sebastianclavijo/wick-a11y-cypress-plugin-your-unstoppable-ally-for-smashing-accessibility-barriers-cool-as-john-wick-280a?ref=cypress-io.ghost.io"&gt;Article for original Wick-A11y release&lt;/a&gt;   &lt;/li&gt;
&lt;li&gt;  &lt;a href="https://dev.to/sebastianclavijo/wick-a11y-v121-voice-the-accessibility-cypress-plugin-that-talks-more-than-john-wick-in-his-movies-8c8?ref=cypress-io.ghost.io"&gt;Article for voice feature release&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.npmjs.com/package/wick-a11y?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Wick-A11y npm package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;The Cypress community has created a useful array of plugins to support accessibility testing and other customized extensions to the app. This is only a recent subset of plugins related to accessibility in Cypress - there are plugins for other accessibility testing packages beyond Axe Core, as well as accessibility-minded test locators from &lt;a href="https://testing-library.com/docs/cypress-testing-library/intro/?ref=cypress-io.ghost.io" rel="noopener noreferrer"&gt;Cypress Testing Library&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;What I like about these three recently released plugins is that they represent extensions and iterations upon existing ideas to solve new needs. They leverage the hackability that's inherent to the browser-based nature of Cypress to great effect, and they set the baseline for future iterations to continue building.&lt;/p&gt;

&lt;p&gt;Do you have a plugin for Cypress? You can &lt;a href="https://github.com/cypress-io/cypress-documentation/blob/main/CONTRIBUTING.md?ref=cypress-io.ghost.io#adding-plugins" rel="noopener noreferrer"&gt;submit it&lt;/a&gt; to our documentation's &lt;a href="https://docs.cypress.io/app/plugins/plugins-list?utm_medium=intro&amp;amp;utm_source=dev-to&amp;amp;utm_campaign=dev-to-syndication&amp;amp;utm_term=&amp;amp;utm_content=accessibility-testing-guide" rel="noopener noreferrer"&gt;plugins list&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cypress</category>
      <category>a11y</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why I rarely use `getByRole`: Testing Library and the first rule of ARIA</title>
      <dc:creator>Mark Noonan</dc:creator>
      <pubDate>Sat, 04 May 2024 00:16:34 +0000</pubDate>
      <link>https://forem.com/marktnoonan/why-i-rarely-use-getbyrole-testing-library-and-the-first-rule-of-aria-4581</link>
      <guid>https://forem.com/marktnoonan/why-i-rarely-use-getbyrole-testing-library-and-the-first-rule-of-aria-4581</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: I've gotten some &lt;a href="https://x.com/matanbobi/status/1787033641177993545"&gt;feedback&lt;/a&gt; from a Testing Library maintainer, Matan Borenkraout, about these ideas. I haven't gotten around to incorporating that feedback yet. He had some good examples where &lt;code&gt;ByText&lt;/code&gt; or even &lt;code&gt;ByLabelText&lt;/code&gt; might not be able to find some elements due to how the label is created or how the implicit role is determined, so what I'm describing here might not handle all cases nicely. I don't think this blows up the premise I'm describing, but I'll be thinking that feedback over and expect I'll be updating the content below in the future.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;To explain the reasons that I mostly stopped using &lt;code&gt;ByRole&lt;/code&gt; locators and started specifying elements, I want to zoom way out and talk about the various pieces of the puzzle. Here's a rough table of contents.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;What problems Testing Library is intended to solve&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;ByRole&lt;/code&gt; is too vague&lt;/li&gt;
&lt;li&gt;What makes something accessible&lt;/li&gt;
&lt;li&gt;What roles even are&lt;/li&gt;
&lt;li&gt;The first rule of ARIA (Accessible Rich Internet Applications)&lt;/li&gt;
&lt;li&gt;Semantic HTML, TypeScript, and using the platform&lt;/li&gt;
&lt;li&gt;What we expect developers and testers to know when writing and testing their code&lt;/li&gt;
&lt;li&gt;Situations where I still use &lt;code&gt;ByRole&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Why I love &lt;code&gt;ByLabelText&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The use of Test IDs&lt;/li&gt;
&lt;li&gt;Overall conclusions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since this argument is all in the details, this is a relatively long post, so I'm skipping some background on the underlying ideas of front-end testing and locating elements. If you're new to these topics, I have a 2022 post on CSS-Tricks called &lt;a href="https://css-tricks.com/front-end-test-element-locators/"&gt;Writing Strong Front-end Test Element Locators&lt;/a&gt; that starts more from the ground up. But for now, I'm going to assume you have some familiarity with how front-end testing works and are interested in best practices for locating elements.&lt;/p&gt;

&lt;p&gt;And of course, feel free to skip ahead to the conclusions if this is all familiar stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/"&gt;Testing Library&lt;/a&gt; provides a great set of accessibility-focused tools to locating elements when writing tests. It was created by Kent C Dodds, one of my longtime favorite educators on front-end and testing topics.  Since the initial release in 2018 as "React Testing Library", it's grown in popularity beyond its React component testing roots. It now has plugins for all major test runners and JavaScript frameworks, and in 2022 some of its patterns were adopted in Playwright's test runner as the recommended way to locate elements.&lt;/p&gt;

&lt;p&gt;I do almost all of my testing in Cypress (and for clarity: I work there). I've added &lt;a href="https://testing-library.com/docs/cypress-testing-library/intro"&gt;Cypress Testing Library&lt;/a&gt; to multiple projects over the years, and it's my go-to for a lot of testing tasks, especially when testing forms, using the &lt;code&gt;ByLabelText&lt;/code&gt; locator.&lt;/p&gt;

&lt;p&gt;One thing I've noticed over time is that I've mostly stopped using the main workhorse of Testing Library, the &lt;code&gt;ByRole&lt;/code&gt; locator. When I wrote some guidance for my team a few years ago, I recommended not using it in most cases. I also mention this as a quick caveat when I teach testing or accessibility topics (for example, it comes up in passing in a &lt;a href="https://www.cypress.io/blog/2023/06/13/maintaining-accessibility-through-user-focused-testing#locating-a-link-using-name-and-expected-location-in-the-accessibility-tree"&gt;blog post about user-focused locators&lt;/a&gt; that I wrote last year). &lt;/p&gt;

&lt;p&gt;I haven't given it much thought over the years, it's just one tool in the Testing Library toolbox and it has its place. My recommendation was driven by what I expect &lt;code&gt;ByRole&lt;/code&gt; to accomplish. But recently I've noticed many strong recommendations to use &lt;code&gt;ByRole&lt;/code&gt; as the &lt;em&gt;best&lt;/em&gt; way to find an element in a test, citing the reason that this helps ensure the underlying application being tested is accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;My argument in this post will be that &lt;code&gt;getByRole&lt;/code&gt; is not strict enough to ensure that a given accessible role is correctly implemented. Locating elements this way can create misleading scenarios where your tests imply that an element is accessible, even if it's not. We'll get into more detail on that below, but first I want to give you the TLDR on what I do instead.&lt;/p&gt;

&lt;p&gt;The recommended Testing Library approach to find and test a button is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/submit/i&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What I prefer to do is locate an element by a combination of two things that, if they are changed, should fail a test: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The element's specific tag name - &lt;code&gt;button&lt;/code&gt; or &lt;code&gt;a&lt;/code&gt;, for example&lt;/li&gt;
&lt;li&gt;The element's accessible name - "Save" or "Home", for example&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to do this in Testing Library, for buttons and links, I would use the &lt;code&gt;ByText&lt;/code&gt; locator, like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get a "Submit" button&lt;/span&gt;
&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// get a "Home" link in the navigation element&lt;/span&gt;
&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nav a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I do something similar using the following format of the &lt;code&gt;contains&lt;/code&gt; command in Cypress. I tend to only look for another locator when this isn't going to get me the element I need:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get a "Submit" button&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// get a "Home" link in the navigation element&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nav a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The long version
&lt;/h2&gt;

&lt;p&gt;The rest of this post is really just me trying to push at this from a few different angles and think everything through. I often find I have some opinion that I think is right, and then when I try to write about it, I realize I don't know the exact source of why I think something, or what things might just be assumptions. So it helps to document all the pieces, and it lets somebody else cross-check the ideas.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Testing Library for?
&lt;/h3&gt;

&lt;p&gt;The basic premise of Testing Library is summed up by this quote from the home page:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The more your tests resemble the way your software is used,&lt;br&gt;
the more confidence they can give you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problems Testing Library solves are described in two ways. The first is about avoiding testing implementation details to have better confidence that test failures matter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You want tests for your UI that avoid including implementation details and rather focus on making your tests give you the confidence for which they are intended.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second is about how not testing implementation details makes refactoring less painful:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You want your tests to be maintainable so refactors (&lt;em&gt;changes to implementation but not functionality&lt;/em&gt;) don't break your tests and slow you and your team down.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The issue of test stability over time, and avoiding testing "implementation details", had previously often been addressed with a Test ID based approach, which maximizes the stability of tests. Testing Library's authors make the argument that using &lt;code&gt;[data-testid="login"]&lt;/code&gt; style locators ignores important user-facing aspects of the elements being tested. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is true.&lt;/strong&gt; Test IDs intentionally and explicitly ignore the nature of the elements being used. We'll talk more about them at the end -- there's no disagreement there. &lt;/p&gt;

&lt;p&gt;My issue with &lt;code&gt;ByRole&lt;/code&gt; locators is the same as the problem with Test IDs, just in a narrower area. If either of these are your &lt;strong&gt;only&lt;/strong&gt; locators and no test ever specifies the specific elements you expect, developers have too much wiggle room to break accessibility while keeping tests green. &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;ByRole&lt;/code&gt; locators are too forgiving for safe refactoring
&lt;/h3&gt;

&lt;p&gt;The Testing Library query documentation says this about the &lt;code&gt;ByRole&lt;/code&gt; locator:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This should be your top preference for just about everything. There's not much you can't get with this (if you can't, it's possible your UI is inaccessible).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Playwright documentation puts it in similar terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We recommend prioritizing role locators to locate elements, as it is the closest way to how users and assistive technology perceive the page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both give examples of doing something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/submit/i&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The underlying idea I take from this that if an element passes the "role check", it's likely to be accessible: locating an element by its role is intended to achieve the goal of safe refactoring - where "changes to implementation but not functionality" do not break your tests.&lt;/p&gt;

&lt;p&gt;However, even though a &lt;code&gt;role&lt;/code&gt; is part of an element's "identity" in an accessibility sense, it's not enough. We could refactor an accessible component in a way that fully breaks all accessibility, but still have a green test that uses &lt;code&gt;getByRole&lt;/code&gt;. Many implementation changes that leave &lt;code&gt;role&lt;/code&gt; intact are actually critical changes in functionality.&lt;/p&gt;

&lt;p&gt;Here's an example of this kind of refactoring:&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/cf363569974643fa8278eaf2d68549ff"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  What makes a button accessible?
&lt;/h3&gt;

&lt;p&gt;HTML elements all have implicit roles that describe the nature of the element to people with disabilities, accessing the page using assistive technology. Buttons have the &lt;code&gt;button&lt;/code&gt; role, anchor tags with &lt;code&gt;href&lt;/code&gt; attributes have the &lt;code&gt;link&lt;/code&gt; role, &lt;code&gt;input&lt;/code&gt; elements with type &lt;code&gt;text&lt;/code&gt; have a &lt;code&gt;textbox&lt;/code&gt; role and so on.&lt;/p&gt;

&lt;p&gt;When it comes to interactive elements, these roles are not usually useful by themselves. What is useful about them is that they represent an overall contract that element will fulfill. To use the &lt;code&gt;button&lt;/code&gt; element as an example, here is at least a partial list of the accessibility "contract" that browser vendors fulfill for a &lt;code&gt;button&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has the implicit role of &lt;code&gt;button&lt;/code&gt; so that users know what it is - a screen reader, for example, would announce it as a button&lt;/li&gt;
&lt;li&gt;It has a default browser button style to visually distinguish it from surrounding content&lt;/li&gt;
&lt;li&gt;It is placed in the tab order of the document and can be focused with the keyboard&lt;/li&gt;
&lt;li&gt;When focused it receives a focus outline&lt;/li&gt;
&lt;li&gt;It can be activated with a mouse click, or the enter key, or the space bar&lt;/li&gt;
&lt;li&gt;If placed inside a form element, activating the button by any of these methods will submit the form (because its default &lt;code&gt;type&lt;/code&gt; is &lt;code&gt;submit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It will receive specific styling, customizable by the user, in Windows High Contrast Mode (used by people with certain visual disabilities)&lt;/li&gt;
&lt;li&gt;It has default browser styles for hover and "pressed" states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of behavior that the browser implements for a humble button. The browser also manages this button's presence in the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree"&gt;Accessibility Tree&lt;/a&gt;, which is how assistive technology like a screen reader can announce the button's existence, what it does, and how the user understands what actions are possible.&lt;/p&gt;

&lt;p&gt;And here's what may be surprising: the browser only does this stuff for real &lt;code&gt;button&lt;/code&gt; elements. Adding &lt;code&gt;role="button"&lt;/code&gt; to a &lt;code&gt;div&lt;/code&gt; or a &lt;code&gt;span&lt;/code&gt; does not trigger any of this in the browser. If &lt;code&gt;role="button"&lt;/code&gt; is used on say, a &lt;code&gt;div&lt;/code&gt; with an &lt;code&gt;onClick&lt;/code&gt; handler, the &lt;strong&gt;entire contract of a button must be implemented by the developer&lt;/strong&gt;, using extra HTML attributes, CSS rules, and JavaScript listeners. And even then, there will still be gaps - especially in &lt;a href="https://www.smashingmagazine.com/2022/06/guide-windows-high-contrast-mode/#use-semantic-html"&gt;Windows High Contrast Mode, which ignores &lt;code&gt;role="button"&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the main reason I avoid &lt;code&gt;ByRole&lt;/code&gt; locators most of the time. Let's look at this locator again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/submit/i&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will absolutely find a true &lt;code&gt;button&lt;/code&gt; element, because of its implicit role, but it will also have no problem finding a &lt;code&gt;div&lt;/code&gt; with &lt;code&gt;role="button"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since these two elements can be &lt;em&gt;very&lt;/em&gt; different from an accessibility point of view, it does not seem at all safe to depend on &lt;code&gt;ByRole&lt;/code&gt; locators during a UI refactor. Accessibility can change radically depending on the underlying HTML, CSS, and JavaScript, even when roles are preserved during what is intended to be passive refactoring.&lt;/p&gt;

&lt;p&gt;So, in the cases where there is a semantic HTML element available, I prefer to just specify &lt;em&gt;that element&lt;/em&gt;, and that way the tests will fail if somebody refactors to a pattern that uses non-semantic elements to recreate existing HTML functionality.&lt;/p&gt;

&lt;p&gt;This element-level approach also nudges developers towards  accessibility success, by forcing them to either follow the first rule of ARIA in their refactoring, or modify the test code if they change the implementation. That means that in a pull request, for example, a reviewer would see unexpected test changes resulting from a refactor (where tests shouldn't change) and hopefully ask why semantic HTML was no longer being used. There's at least an extra layer of protection there.&lt;/p&gt;

&lt;h3&gt;
  
  
  The first rule of ARIA
&lt;/h3&gt;

&lt;p&gt;ARIA stands for &lt;a href="https://www.w3.org/TR/using-aria/#rule1"&gt;Accessible Rich Internet Applications&lt;/a&gt; and it's a specification that teaches us how to create online experiences that are likely to be functional for people with disabilities.&lt;/p&gt;

&lt;p&gt;The first rule of ARIA is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you can use a native HTML element &lt;a href="https://www.w3.org/TR/using-aria/#bib-html51"&gt;HTML51&lt;/a&gt; or attribute with the semantics and behavior you require &lt;strong&gt;already built&lt;/strong&gt; in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, &lt;strong&gt;then do so&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reason for this is to take advantage of what the browser provides, which is highly likely to work correctly with assistive technology, instead of doing a custom implementation of the same functionality.&lt;/p&gt;

&lt;p&gt;One way to think about this is: the &lt;em&gt;absolute best&lt;/em&gt; you can do for users when implementing a custom button, is to faithfully recreate everything the browser already does in the "button" contract, except your version won't support High Contrast Mode. &lt;/p&gt;

&lt;p&gt;There is no value to writing and owning code that reimplements browser functionality - your company doesn't need it, you don't need it, and your users definitely don't need it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Related practices
&lt;/h3&gt;

&lt;p&gt;A lot of Testing Library advocates are also fans of other good practices, like using explicit types with TypeScript. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;ByRole&lt;/code&gt; locator essentially says that there is only one required property of an element, and everything else about its accessibility contract is optional - the test will accept whatever has that one single property. Specifying the correct semantic element, on the other hand, is a way to force the test to only except something where the browser is implementing the full contract. Following the first rule of ARIA in our tests, as well as our code, gives us the most "type safety" in this sense. &lt;/p&gt;

&lt;p&gt;The alternative solution is to assert all of the expected accessibility behaviors in your tests, in addition to locating the element by its role, which is absolutely the way to go if &lt;strong&gt;you&lt;/strong&gt; own the code that implements that functionality.&lt;/p&gt;

&lt;p&gt;Another good practice is in web development is "using the platform", not fighting it. The first rule of ARIA perfectly aligns with this: using correct semantic elements allows browsers and operating systems to meet the needs of disabled users, and provides the best compatibility with assistive technology. When people say the web is "accessible by default", semantic HTML is at the heart of this statement.&lt;/p&gt;

&lt;p&gt;Developers can still override things and break semantic elements of course, but semantic HTML is still a better foundation and more likely to be accessible. So having our tests depend on this aspect of code seems worth it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we expect developers to know
&lt;/h3&gt;

&lt;p&gt;Because of the first rule of ARIA, I don't see any valid use case for &lt;code&gt;role="button"&lt;/code&gt; in code. We should only see the &lt;code&gt;role&lt;/code&gt; attribute being used for custom widgets that &lt;strong&gt;do not have&lt;/strong&gt; native HTML equivalents. &lt;/p&gt;

&lt;p&gt;The Testing Library docs point out something similar to the first rule of ARIA, but don't quite make the same conclusion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that setting a role and/or aria-* attribute that matches the implicit ARIA semantics is unnecessary and is not recommended as these properties are already set by the browser&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This recommendation makes clear that it is expected and desired that developers know they should prefer maintaining the native semantics of HTML. Since they need this knowledge to choose the correct accessible implementation of a given &lt;code&gt;role&lt;/code&gt;, we should take the mystery out of it and just specify the element that should be there in our tests. Then there is no need to hope that future developers know all the same nuances about accessibility. They can learn them when their non-semantic HTML causes a test failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  When I still use &lt;code&gt;ByRole&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I'll take a &lt;code&gt;ByRole&lt;/code&gt; selector any time for roles like &lt;code&gt;menu&lt;/code&gt;, &lt;code&gt;menuitem&lt;/code&gt;, &lt;code&gt;tabpanel&lt;/code&gt;, &lt;code&gt;treegrid&lt;/code&gt;... there are lots of useful accessible roles that are not yet implemented in standard HTML. Selecting them by role is a great idea, especially as it allow you to refactor &lt;em&gt;into semantic HTML&lt;/em&gt; without breaking a test, when elements with those roles do make their way into browsers.&lt;/p&gt;

&lt;p&gt;There are also some rare cases where ARIA roles are needed, even when there is overlap with semantic HTML elements. This might happen when working around some specific browser bug with the native element, for example, where owning the implementation of the accessibility contract is worth it because it lets you do something you can't do otherwise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I love &lt;code&gt;ByLabelText&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;OK, enough about why &lt;code&gt;ByRole&lt;/code&gt; is not my thing. Let's talk about some good stuff. Here's my favorite way to locate a form field in Cypress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myusername&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not built in to Cypress -- I use the Testing Library plugin. It's elegant, readable, concise, and requires form fields to have a specific programmatically associated label present, which is also needed for assistive technology.&lt;/p&gt;

&lt;p&gt;If I wasn't using Testing Library, I might define a custom command that knows how to find labels in my application, executing something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this depends on knowing the exact DOM structure, and here we don't care about that. There are a lot of correct ways to label a form input, it doesn't matter too much which way is used. One good thing is that here we are specifying a real input element, but Testing Library can do that too if we want to, using the &lt;code&gt;selector&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myusername&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This option is explained in the docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it is important that you query a specific element (e.g. an ) you can provide a selector in the options. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is also presumably why &lt;code&gt;ByText&lt;/code&gt; also takes a &lt;code&gt;selector&lt;/code&gt; option. Even though we want to avoid testing implementation details when they don't matter, Testing Library has built-in options so that you can still specify them when they do matter.&lt;/p&gt;

&lt;p&gt;In general the risk of developers creating custom inputs built out of divs and spans is pretty low, so I don't do that very often in practice. But it might be useful to use this pattern at the component test level, to give the developers instant feedback if they decide to get creative and reimplement an &lt;code&gt;input&lt;/code&gt; out of a custom &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So why don't we pass this &lt;code&gt;selector&lt;/code&gt; option to our &lt;code&gt;ByRole&lt;/code&gt; locators, where the risks of using non-semantic elements are higher? Primarily because &lt;code&gt;ByRole&lt;/code&gt; doesn't support a selector option. That's for a pretty good reason as far as I can tell, since &lt;code&gt;ByRole&lt;/code&gt; is specifically intended to &lt;strong&gt;avoid&lt;/strong&gt; specifying the implementation. &lt;/p&gt;

&lt;p&gt;It would be pretty pointless and circular to write code that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/submit/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// ⚠️ not possible, just an example&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above format, the &lt;code&gt;selector&lt;/code&gt; property would just override the role. On the other hand, in the &lt;code&gt;ByLabelText&lt;/code&gt; scenario, since many elements can have similar labels, passing a &lt;code&gt;selector&lt;/code&gt; option to further narrow what is acceptable makes more sense. You could have a Shipping and a Billing form on the same page with the same labels. You could have a &lt;code&gt;select&lt;/code&gt; and an &lt;code&gt;input&lt;/code&gt; with the same label text. &lt;/p&gt;

&lt;p&gt;Or you might want to pass something like a &lt;code&gt;[data-testid="header"] input&lt;/code&gt; selector if there are two instances of similar forms on the same page. &lt;/p&gt;

&lt;h3&gt;
  
  
  Oh yeah, what &lt;strong&gt;about&lt;/strong&gt; &lt;code&gt;data-testid&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;As a big believer in accessibility, and a longtime user of Testing Library, I want to take a second to stick up for the good old Test ID pattern, which still has its place and can be used in conjunction with specifying the element, or the role, or anything else.&lt;/p&gt;

&lt;p&gt;I strongly believe you need to cover the accessibility of your app with automated tests, and that means specifying the parts of the DOM that matter. The HTML we ship to users is &lt;em&gt;our product&lt;/em&gt; and we should all pay attention to the actual code we ship. There is an enormous, expensive, complicated system of servers, databases, and various tubes often involved in sending it from us to our users. We might as well send the right HTML.&lt;/p&gt;

&lt;p&gt;How I feel about Test IDs is this: if you've already specified the correct HTML output in one place, for example in component tests, then it's fine to use Test IDs elsewhere if you want.&lt;/p&gt;

&lt;p&gt;Especially if you have a separate QA team, not as familiar with what the "right" HTML and roles are, we don't need to block them from workflows using generic helper locators, as long as there are tests owned and run by the developers writing code that will fail if HTML accessibility is broken. You must make sure your something in your system will fail if accessibility regressions happen. &lt;/p&gt;

&lt;p&gt;On a personal level I'd be happy if everything went red when accessibility broke, because it's a critical thing for me. But as long as accessibility is specified, tested, and owned, then testing some workflows using Test IDs might actually have some upsides for certain teams.&lt;/p&gt;

&lt;p&gt;It's also the case that many applications are not built in an accessible way where roles, or even elements themselves, are appropriate for their functionality. Those apps still need to be tested, and if it's not easy or possible to remediate things on the fly, a generic Test ID is a better choice than something that, say, specifies the current &lt;em&gt;wrong&lt;/em&gt; role that something happens to have, and then causes confusion. &lt;/p&gt;

&lt;p&gt;Test IDs also come in handy when disambiguating different instances of similar elements, and in a few other situations. It's just important to know what they do and do not cover.&lt;/p&gt;

&lt;p&gt;I'm hoping to make a PR to the Cypress Best Practices documentation, which already endorses both Test IDs &lt;strong&gt;and&lt;/strong&gt; Testing Library, to get into some of this kind of detail about how to choose the approach. But as you can see, what I've got here currently long and unwieldy. I'm hoping writing this post will help me form a much shorter proposed update to the docs.&lt;/p&gt;

&lt;p&gt;If you've ready this far: thanks! And please let me know if you have thoughts or feedback. Am I missing something? Is the &lt;code&gt;ByRole&lt;/code&gt; locator awesome in a way I'm not accounting for?&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Here's the understanding I'm working with at the moment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic HTML, when available, is always preferred over custom &lt;code&gt;role&lt;/code&gt; attributes for accessibility. This is the first rule of ARIA.&lt;/li&gt;
&lt;li&gt;While users do perceive the &lt;code&gt;role&lt;/code&gt; of an element, what they they &lt;em&gt;depend&lt;/em&gt; on is its entire contract, so a ensuring a role exists is not enough to guarantee behavior doesn't change in refactors.&lt;/li&gt;
&lt;li&gt;This means we should &lt;em&gt;not&lt;/em&gt; use a &lt;code&gt;ByRole&lt;/code&gt; locator if the role we are selecting on can be fulfilled by a native HTML element.&lt;/li&gt;
&lt;li&gt;Instead we should specify the HTML element itself in our tests. It will help developers do the right thing, and provide better feedback when refactoring, giving you one extra layer of resistance to accessibility bugs.&lt;/li&gt;
&lt;li&gt;We can do this in Testing Library by passing a &lt;code&gt;selector&lt;/code&gt; option to our &lt;code&gt;ByText&lt;/code&gt; and &lt;code&gt;ByLabelText&lt;/code&gt; locators. And we can likely do something like it in our framework of choice even if Testing Library is not available, such as with &lt;code&gt;cy.contains('selector', 'text')&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing tests this way aligns your tests and your development practices with the first rule of ARIA, which will give you a great foundation for building accessible apps and avoid some common accessibility pitfalls that trap even the most well-intentioned of us.&lt;/p&gt;

&lt;p&gt;Last note: Testing Library is still great and I'll still add it to every project to augment whatever I'm doing.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>cypress</category>
      <category>testing</category>
      <category>react</category>
    </item>
  </channel>
</rss>
