Accessibility Testing With Selenium Webdriver with Code Example

This Article Explains Website Accessibility Testing Using Selenium With Simple Code Examples. It Covers The Two Main Solutions For Writing Web Accessibility Tests:

What Is Website Accessibility?

The Internet is designed for different types of people from different countries, with different languages and different abilities. People in the world have a very diverse range of movement, hearing, sight and cognitive abilities.

If a web developer creates an interesting page on the internet, he/she selects the proper language which covers most of the potential readers, but what can he/she do to help people with different disabilities to use the said web application?

Accessibility Testing Using Selenium

People with a slightly low level of sight abilities have to zoom in the web applications very close. Some of them with an even higher level of disability need to use Screen Readers to use the application properly.

There are other similar solutions available for most of the disabilities. They need to use a variety of tools or techniques to experience the Internet.

Web Content Accessibility Guidelines (WCAG)

Fortunately, web developers are not alone in trying to help people with different levels of abilities to explore various web applications. Many such resources exist to assist them.

One of them is the Web Content Accessibility Guidelines (WCAG). This is a set of documents, that contains the recommendations related to web accessibility. These documents are also the best method to verify if the application is accessibility friendly.

How To Perform Website Accessibility Testing?

There are several methods to test the web accessibility. Most of them consist of checking the WCAG recommendations. This is a good path, but how can these tests be performed? The situation in this area is the same as for any other type of testing.

Firstly, all the recommendations can be tested manually.

Manual Web Accessibility Testing

For many recommendations, Manual Testing works well because one can perform extensive checks to automate metrics (E.g. logical order of elements on the page, good text explanation of the images presented on the page, etc.). However, manual testing is a time consuming and error-prone process.

Moreover, when we want to get feedback about the current state of the web application accessibility level, the tester would need to repeat his work, again and again. Every time the developers change the code, we would want to check the compliance level.

That’s why it is a good idea to automate as much web accessibility testing as we can.

Automated Accessibility Testing

When we decide to automate the testing strategy, we have to decide which method we want to use i.e. the easier one or the more adjustable ones.

The first one does almost everything automatically, without we having control of what we want to check. The second one gives us full control over the rules executed during the test and the level of accepted compliance.

Suggested Read => Quickly Learn Selenium With Real-Time Examples

Fastest Accessibility Testing Method

If you don’t want to have full control over the testing process and if you can trust the WCAG guidelines completely, the best option would be to use a special library for this purpose. The most comprehensive library for this is Axe. Let’s see how to include it in our automated testing code.

Let’s assume that we already have a .NET testing code in C#. We use an NUnit library as a testing framework and a Selenium for testing automation.

The template code for this purpose can look like this:

[TestFixture]
public class Tests
{
    [Test]
    public void Test()
    {
        var driver = new ChromeDriver();
        driver.Navigate().GoToUrl("https://www.google.pl");
        driver.Close();
    }
}

You probably know that this code opens the web browser and navigates to the Google main page. It also closes the browser at the end to clean up the used memory. This is a perfect template to build a more complicated test. We will integrate an Axe library with our testing code.

At first, we need to add an Axe library to our project. We can do it using the NuGet Manager. Right-click on the project and select “Manage NuGet Packages”.

Add Axe library to project

Then we need to search for the “Globant.Selenium.Axe” library, select a single result on the list and click the “Install” button on the right of the screen. Now, we are ready to write some code.

Search for Globant.Selenium Axe

To test the Accessibility of any site, we need to execute only one command.

[TestFixture]
public class Tests
{
    [Test]
    public void Test()
    {
        var driver = new ChromeDriver();
        driver.Navigate().GoToUrl("https://www.google.com");
            AxeResult results = driver.Analyze();
            driver.Close();
    }
}

When we run this test, we will notice that the test still passes even if the web page contains some accessibility problems. To make the test sensitive to the detected problems we have to write the assertions on our own.

Assert.IsEmpty(results.Violations);

This simple assertion line ensures that we don’t pass any accessibility problem in our tests.

Comprehensive Accessibility Testing Method

Another way to perform a deep accessibility testing of a website is to create our own set of rules that need to be fulfilled during the testing. We can find many WCAG rule sets on the internet. Example of WCAG Rules.

We can simply implement them one by one with the help of that kind of page. Creating our own rules will give us the ability to decide what level of conformity we accept in the tested application.

In order to create a configurable and flexible mechanism to execute our accessibility tests, we need to create the testing framework first. Similar to the previous example, we will use C# code to show each step of framework creation.

Recommended Read => A Complete Step By Step Guide On Accessibility Testing 

Accessibility Testing Framework

In the beginning, we need to create an interface to define a rule. It also defines the action that wants to execute to validate this rule and its description.

We can start with a simple interface with 2 members i.e. Description string and Check method. The method is the essence of the whole testing framework. It will execute the logic implemented in each particular rule.

public interface IAccessibilityRule
{
    IEnumerable<AccessibilityViolation> Check(IWebDriver driver);
    string Description { get; }
}

The Check method takes the driver object i.e. the Selenium object to perform all operations on the web page and it returns the list of violations.

The AccessibilityViolation is shown below:

public class AccessibilityViolation
{
    public IWebElement Element { get; set; }
    public string Violation;
}

It is a very basic description of the violation of the accessibility rules detected on the web application. It only consists of the source element and the description of the validation rule.

With the help of these two classes, we can create an engine to execute all the rules on a particular page. We have to create a separate class with the logic of rule execution and merge all the results violation.

public class AccessiblityEngine
{
    private IWebDriver Driver { get; set; }
    private AccessibilityTestConfiguration Configuration { get; set; }
    public AccessiblityEngine(IWebDriver driver, AccessibilityTestConfiguration configuration)
    {
        this.Driver = driver;
        this.Configuration = configuration;
    }
    public IEnumerable<AccessibilityViolation> Check()
    {
        List<AccessibilityViolation> result = new List<AccessibilityViolation>();
        foreach (var rule in Configuration.Rules)
        {
            result.AddRange(rule.Check(Driver));
        }
        return result;
    }
}

This engine class contains only the constructor and the main checking method. You can notice that the constructor takes a special AccessibilityTestConfiguration object. This is a simple model to pass the list of test rules that we want to include in our testing strategy.

public class AccessibilityTestConfiguration
{
    public IEnumerable<IAccessibilityRule> Rules { get; set; }
}

However, the Check method is a mechanism to go over all the rules and execute them separately in the context of the current web page. It also takes all the violations and merges them into a single list. That’s it – this is a very simple and straightforward solution. Now, it is time to create our own rule.

Sample WCAG Rules

We will go through a few sample WCAG rules to see how to create them, but we won’t create the whole set of rules here, because there are more than a hundred rules available on the standard page.

Let’s start!!

#1) Document Title Rule

One of the most important rules in the whole document in the context of accessibility is that the web page should contain a meaningful title, using the title tag (easy to interpret by browsers). The first recommendation is very hard to check and it should be done manually by testers, but the second one is very easy to check.

We can implement this behavior in our class. It simply checks if the title element exists on the page. Otherwise, it returns the AccessibilityViolation class with a description of the problem.

public class DocumentTitleRule : IAccessibilityRule
{
    public string Description => "Documents must have <title> element to aid in navigation";
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        if (driver.FindElements(By.TagName("title")).Count == 0)
        {
            return new List<AccessibilityViolation>() {
                new AccessibilityViolation()
                {
                    Element = null,
                    Violation = Description
                }
            };
        }
        return Enumerable.Empty<AccessibilityViolation>();
    }
}

#2) Aria Hidden Body Rule

Another interesting WCAG rule is to ensure that the body element is visible on the page and it is not suppressed for the screen readers using the aria-hidden attribute. Aria-hidden attribute tells the screen reader software which elements are visible and which elements should be presented (in audio form) to the user.

The rule checking implementation is presented in the code below. First, we need to find the body element, then we need to check its value. If it is set to true, then we need to raise a violation in the accessibility policy.

public class AriaHiddenBodyRule : IAccessibilityRule
    {
        public string Description => "Ensures aria-hidden='true' is not present on the document body";
        public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
        {
            var element = driver.FindElement(By.TagName("body"));
            
            if (element != null && element.GetAttribute("aria-hidden") == "true")
            {
                return new List<AccessibilityViolation>()
                {
                    new AccessibilityViolation()
                    {
                        Element = element,
                        Violation = Description
                    }
                };
            }
            return Enumerable.Empty<AccessibilityViolation>();
        }
    }

#3) Access Keys Rule

One of the methods to make easy navigation on a page is to enable a keyboard shortcut to focus on a particular element. To achieve it, we can use the accesskey attribute. One of the WCAG rules is to ensure that all Accesskey attributes are unique.

Otherwise, the browser will have a problem with the interpretation of a particular shortcut.

The code for this check is a bit more complex. First, we find all the elements with the accesskey attribute, then we extract its values. Next, we group all the values and check if any group has more than one element. If one accesskey value appears more than once on the web page, then we can raise a violation.

public class AccessKeysRule : IAccessibilityRule
{
    public string Description {  get { return "Every accesskey attribute value is unique"; } }
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        var elements = driver.FindElements(By.CssSelector("*[accesskey]"));
        var invalidTexts = elements.Select(x => x.GetAttribute("accesskey")).GroupBy(n => n).
Where(c => c.Count() > 1).Select(x => x.Key);
        return elements.Where(x => invalidTexts.Contains(x.GetAttribute("accesskey")))
            .Select(x => new AccessibilityViolation()
            {
                Element = x,
                Violation = Description
            });
    }
}

#4) List Item Rule

To make the HTML page consistent and fulfill the standards regarding the lists, we need checklist elements. We need to make sure (following the WCAG rule) that the list elements ul and ol for an unordered list and ordered list have only li elements as the first-level children.

To write this validation, we need to find all the ul and ol elements on the page, find all their first level children and check if all of them are li elements. We used a By.XPath(“*”)) selector to find all the direct children of the chosen element.

public class ListItemRule : IAccessibilityRule
{
    public string Description => "All list items (<li>) must be contained within <ul> or <ol> parent elements.";
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        return driver.FindElements(By.TagName("ul"))
            .Concat(driver.FindElements(By.TagName("ol")))
            .Where(x => x.FindElements(By.XPath("*")).Any(y => y.TagName != "li"))
            .Select(x => new AccessibilityViolation()
            {
                Element = x,
                Violation = Description
            });                
    }
}

#5) Tab Index Rule

Another very simple rule is to make sure that the tabindex attribute is always greater than zero or equal to zero. tabindex is an attribute to define the order of Tab navigation between the different controls on the web page.

If the tabindex is less than zero, then it is not taken into account during the keyboard navigation. This should be avoided in the inaccessible world.

To check that, we need to find all tabindex values, parse it to the number and check if it is less than zero. Then we have to raise an accessibility violation of the invalid value.

public class TabIndexRule : IAccessibilityRule
{
    public string Description => "Elements should not have tabindex greater than zero";
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        return driver.FindElements(By.CssSelector("[tabindex]")).Where(x => int.Parse(x.GetAttribute("tabindex")) < 0)
            .Select(x => new AccessibilityViolation()
            {
                Element = x,
                Violation = Description
            });
    }
}

#6) Valid Lang Rule

The next rule is related to the language of the document. HTML standard defines a specific list of supported languages and language codes that can be used in the lang attribute. This value is very important to the screen reader software to decide which text-to-speech engine should be used.

The longest and most important part of this check is a list of the supported language codes. We only need to check the value of the lang attribute on the body element and validate it with the defined list.

class ValidLangRule : IAccessibilityRule
{
    public string Description => "Ensures lang attributes have valid values";
    private string[] ValidLang = { "", "ab", "aa", "af", "ak", "sq", "am", "ar", "an", "hy", "as", "av", "ae", "ay", "az", "bm", "ba", 
"eu", "be", "bn", "bh", "bi", "bs", "br", "bg", "my", "ca", "ch", "ce", "ny", "zh", "zh-Hans", "zh-Hant", "cv", "kw", "co", "cr", "hr",
 "cs", "da", "dv", "nl", "dz", "en", "eo", "et", "ee", "fo", "fj", "fi", "fr", "ff", "gl", "gd", "gv", "ka", "de", "el", "kl", "gn", "gu", "ht", "ha", 
"he", "hz", "hi", "ho", "hu", "is", "io", "ig", "id", "in", "ia", "ie", "iu", "ik", "ga", "it", "ja", "jv", "kl", "kn", "kr", "ks", "kk", "km", "ki",
 "rw", "rn", "ky", "kv", "kg", "ko", "ku", "kj", "lo", "la", "lv", "li", "ln", "lt", "lu", "lg", "lb", "gv", "mk", "mg", "ms", "ml", "mt", "mi", 
"mr", "mh", "mo", "mn", "na", "nv", "ng", "nd", "ne", "no", "nb", "nn", "ii", "oc", "oj", "cu", "or", "om", "os", "pi", "ps", "fa", "pl",
 "pt", "pa", "qu", "rm", "ro", "ru", "se", "sm", "sg", "sa", "sr", "sh", "st", "tn", "sn", "ii", "sd", "si", "ss", "sk", "sl", "so", "nr", "es",
 "su", "sw", "ss", "sv", "tl", "ty", "tg", "ta", "tt", "te", "th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "ve", "vi", "vo",
 "wa", "cy", "wo", "fy", "xh", "yi", "ji", "yo", "za", "zu" };
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        if (!ValidLang.Contains(driver.FindElement(By.TagName("body")).GetAttribute("lang")))
        {
            return new List<AccessibilityViolation>()
            {
                new AccessibilityViolation()
                {
                    Element = driver.FindElement(By.TagName("body")),
                    Violation = Description
                }
            };
        }
        return Enumerable.Empty<AccessibilityViolation>();
    }
}

#7) Video Description Rule

If we use a video on our web page, then we should ensure that it provides a description for all of them, to be sure that people without the ability to see them, can still use the application like the regular users.

This check is very simple. We only need to check if the video element has at least one track child on any level.

public class VideoDescriptionRule : IAccessibilityRule
{
    public string Description => "Ensures <video> elements have audio descriptions";
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        return driver.FindElements(By.TagName("video")).Where(x => x.FindElements(By.TagName("track")).Count != 0)
            .Select(x => new AccessibilityViolation()
            {
                Element = x,
                Violation = Description
            });
    }
}

#8) Button Rule

WCAG defines, that all buttons in HTML code should have an aria-label attribute defined. It specifies the description of the button and its purpose.

The most complicated part of the implementation code is selecting all the elements which are buttons. We defined 5 types of button elements. Then we only need to check if the aria-label attribute is set.

class ButtonRule : IAccessibilityRule
{
    public string Description => throw new NotImplementedException();
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        return driver.FindElements(By.TagName("button"))
            .Concat(driver.FindElements(By.CssSelector("[role='button']")))
            .Concat(driver.FindElements(By.CssSelector("input[type='button']")))
            .Concat(driver.FindElements(By.CssSelector("input[type='submit']")))
            .Concat(driver.FindElements(By.CssSelector("input[type='reset']")))
            .Where(x => x.GetAttribute("aria-label") == "")
            .Select(x => new AccessibilityViolation()
            {
                Element = x,
                Violation = Description
            });
    }
}

#9) Blink Rule

As the blink element is deprecated by the WCAG guidelines, we should ensure that they do not exist on the page. If any of them would be found, then we have an accessibility problem.

public class BlinkRule : IAccessibilityRule
{
    public string Description => "Ensures <blink> elements are not used";
    public IEnumerable<AccessibilityViolation> Check(IWebDriver driver)
    {
        return driver.FindElements(By.TagName("blink"))
            .Select(x => new AccessibilityViolation()
            {
                Element = x,
                Violation = Description
            });
    }
}

Test Execution

As you can see, the definition of a single rule is very easy and straightforward. Here, we show only a very small subset of all possible WCAG rules, but we can implement the rest of them independently.

The main advantage of this solution is that we have full control over the scope of testing and the acceptance criteria for our tests.

The below code shows how to write a test using our accessibility test engine. After the definition of all included rules in the AccessibilityTestConfiguration object, we can simply execute the Check() method and let our automated test do the work.

[Test]
public void TestRules()
{
    var driver = new ChromeDriver();
    driver.Navigate().GoToUrl("https://www.google.com");
    var engine = new AccessiblityEngine(driver, new AccessibilityTestConfiguration()
    {
        Rules = new List<IAccessibilityRule>()
        {
            new AccessKeysRule(),
            new AriaHiddenBodyRule(),
            new BlinkRule(),
            new ButtonRule(),
            new DocumentTitleRule(),
            new ListItemRule(),
            new TabIndexRule(),
            new ValidLangRule(),
            new VideoDescriptionRule()
        }
    });    var result = engine.Check();
    driver.Close();
}

Conclusion

In this article, we learned the two main solutions to write accessibility tests. The easy one and the comprehensive one.

It is your decision, to select and use and the most beneficial for you.

We hope you learned about Website Accessibility Testing Using Selenium from this article!!