Journeying to zero accessibility issues with Angular Testing Library
Flora Soussand7 min read
I truly believe that web accessibility is crucial for creating products for everyone. I want to improve the accessibility of my project because I want to ensure equal access for everyone.
To improve accessibility, we address some of the issues upon detection. Yet, we face a real challenge for reaching “0 accessibility issues”. Indeed, developers sometimes introduce new issues without realizing it. Because of this, a few months ago, I started a kaizen (continuous improvement process). My goal was to help developers identifying and rectifying accessibility issues as they code.
Solutions I envisioned
I explored several solutions to tackle our problem. Those solutions are sumed up here and detailed below. Colors in the header figure our estimation of how long and difficult it would have been to implement this solution.
Issue | Using an external library 🔴 High Cost | ESLint Rules 🟠 Average cost for each problem | Using testing library 🟢 Low cost |
---|---|---|---|
Problem in the HTML structure | 🟢 | 🟢 | 🟠 |
Bad usage of alt texts | 🟠 | 🟠 | 🟠 |
Some zones are not reachable with the keyboard | 🔴 | 🔴 | 🟠 |
Keyboard navigation is not in the right order | 🔴 | 🔴 | 🟠 |
The cursor gets “lost” at some point in the keyboard navigation | 🔴 | 🔴 | 🟠 |
Colors inside the table indicate how well each solution tackles the issue
🔴 Does not tackle at all 🟠 Tackles partially 🟢 Tackles well
External library
I thought about using a library like jasmine-axe or ember-a11y-testing. We would have had to install this new library on our project (average cost). Then, we would only have been able to test some of the issues we detected (low gain). At Theodo, we highly value pragmatism: creating maximum value by playing with existing constraints. It seemed to me that this solution was not the best one in this regard, so I went looking elsewhere.
ESLint rules
I also envisioned to implement some new ESLint rules. Yet, I chose not to use this solution because it wouldn’t have enabled us to detect all issues.
For example, let’s look at different snippets of code. The goal is to implement a button for redirection to the previous page.
Requirement : This button needs to be accessible for people who use a screen reader. Those people need to be provided with a clear description of what happens if they click on the button.
If you want more information about who is the user that might use a screen reader, you can look at this article about a mean to connect with all your users and understand their needs.
Snippets of code:
❌ 1) This code is incorrect : the user that uses a screen reader will not be able to know what happens if he selects the button
<button><arrow-icon /></button>
❌ 2) This code is also incorrect because if the screen reader user selects this button, the screen reader will read “arrow”: setting an aria-label is not enough, it should be descriptive and precise.
<button aria-label="arrow"><arrow-icon /></button>
✅ 3) This code is correct: it provides the user with a clear description of what happens if he selects the button
<button aria-label="Back to previous page" ><arrow-icon /></button>
✅ 4) This code is also correct, as the meaning is being conveyed through text rather than an image, screen reader user will receive the information he needs
<button>Back to previous page</button>
Those 4 snippets illustrate why we could not have used an ESLint rule in this use case
- We could not force the user to always implement an aria-label because it is sometimes useless
- ESLint rules did not enable us to verify whether the aria-label that was set was correct. The developer could implement an aria-label quickly without realizing the description is not clear for a screen reader user
This example shows that ESLint rules are not adapted to all test cases. Thus, I wanted to find a solution that enabled me to test issues not detectable by ESLint and that was pragmatic to implement.
Using testing library
The third solution was to learn how to use testing library to verify whether our components are accessible. Testing Library was already installed on our project, so here the issue was to learn how to use it precisely. This solution was relatively quick to implement and enabled us to tackle all the issues we had identified. This is the solution we chose to implement.
If you want to learn more about testing library and its upsides, I recommend this article.
My learnings about unit tests
I investigated the possibilities of testing library to build a standard that enables each developer to test accessibility on their components. Below is what I learned through three examples
A button has a clear description for screen reader
We can start with the example of the back button we explored earlier. How can we assess that the screen reader user is provided with a clear description of the redirection button ?
I discovered that the following snippet of code meets our specifications.
it('should display a return button with the right name', () => {
expect(screen.queryByRole('button', { name: 'Back to previous page' })).toBeInTheDocument();
});
The name attribute is the text that will be read out loud to users with a screen reader. The above test will fail if your DOM presents no button with this label. We thus have a test that will cover the two following possibilities.
- The button contains some text (see example 4 in the ESLint section)
- The developer sets an aria-label (see example 3 in the ESLint section). In this case, the developper will have to use the aria-label he set in the html in his test: if it is not well-fitted, the developper might realize it using the tests.
Thus, I advise you to test all your buttons using this syntax, it might help you realize the name of your button is perfectible
An input text has a description available for screen readers
Let’s continue our journey into angular component and look at text inputs. It might be tempting to give information through the placeholder of your input. The result will be fitted for most users, but those with a screen reader will be utterly unable to access the information. To prevent this problem, think about adding a title to your input.
Below would be the correct code snippet to tackle this issue
<input
type="text"
[placeholder]="e-mail"
[title]="e-mail"
/>
And below would be one test to evaluate the accessibility of the input.
it('should display an input component with the right name', async () => {
const inputComponent = await screen.queryByRole('textbox', { name: 'e-mail' });
expect(inputComponent).toBeInTheDocument();
});
Here again, the name attribute is the text that will be read out loud to users with a screen reader. Writing such a test should enable you to realize you forgot to set a title : without it, you will have no name to access your component
A link is navigable using keyboard
The two examples above conveyed information about screen reading. Another crucial feature for accessibility is keyboard navigating. It enables users to access your content, even without a map.
If you want to test that a component is accessible with keyboard, it is possible to implement it using angular testing library
Imagine that we want to test the accessibility of a link with the following code:
<a role="link" href="redirect-link">Redirect</a>
In the unit test, we want to test the following steps:
- Get the link in the DOM if it exists
- Focus on the link. If the link is not reachable with a keyboard, you won’t be able to focus it.
- Activate your button using “enter”
- Verify that the actions behind the button were activated.
it('should navigate to service URL with keyboard', async () => {
const link = screen.getByRole('link', { name: 'Redirect' });
link.focus();
expect(link).toHaveFocus();
await userEvent.keyboard('{enter}');
expect(routerMock.navigate).toHaveBeenCalled();
});
Those steps will indeed enable you to verify whether your link is accessible for people without a mouse.
Conclusion about testing library
Thus, I discovered that it is easy to use Angular Testing Library to tackle accessibility issues and its use case are wide enough to tackle all the issues we had identified
Conclusion
In the pursuit of improving the accessibility of our Angular application, we explored different solutions. While external libraries and ESLint rules were considered, using Angular Testing Library (ATL) emerged as a powerful solution.
We explored three examples that underscore ATL’s efficacy in addressing accessibility concerns, offering a context-aware testing approach.