Skip to content
Go back

Test Automation - How To Perform Automated Accessibility Checks Using Playwright Python And Axe

Published:

Introduction

In today’s digital landscape, ensuring web accessibility is not just a moral obligation but a crucial aspect of delivering inclusive experiences to users with diverse needs. Playwright, a powerful browser automation tool, coupled with the Axe accessibility testing library, provides a robust solution for automating accessibility checks during your testing pipeline. By integrating these two powerful tools, you can proactively identify and address accessibility issues.

The solution presented in this article is exemplified in my Playwright Python example project, developed in collaboration with Elias Shourosh.

Implementing the Solution

The AxeHelper class contains the check_accessibility the method that runs Axe on a given Playwright page, checks for accessibility violations based on the specified maximum allowed violations per impact level, and provides detailed reporting.

The solution code can be found here.

import json
from collections import Counter
from typing import Dict

import allure
from axe_playwright_python.sync_playwright import Axe
from playwright.sync_api import Page

class AxeHelper:

    def __init__(self, axe: Axe):
        self.axe = axe

    def check_accessibility(
        self, page: Page, maximum_allowed_violation_by_impact: Dict[str, int] = None
    ) -> None:
        """Checks accessibility of the page using playwright axe.

        :param page: Playwright Page object
        :param maximum_allowed_violations_by_impact: A dictionary
            specifying the maximum allowed violations for each impact
            level (e.g., {'minor': 2, 'moderate': 1, 'serious': 0, 'critical': 0}). If None, no violations will be allowed for
            any impact level.
        """
        if maximum_allowed_violation_by_impact is None:
            maximum_allowed_violation_by_impact = {
                "minor": 0,
                "moderate": 0,
                "serious": 0,
                "critical": 0,
            }
        results = self.axe.run(page)
        violations_count = dict(
            Counter(
                [violation["impact"] for violation in results.response["violations"]]
            )
        )
        if violations_exceeded := {
            impact_level: violation_count
            for impact_level, violation_count in violations_count.items()
            if violation_count
            > maximum_allowed_violation_by_impact.get(impact_level, 0)
        }:
            allure.attach(
                body=json.dumps(results.response["violations"], indent=4),
                name="Accessibility Violation Results",
                attachment_type=allure.attachment_type.JSON,
            )
            assert not violations_exceeded, (
                f"Found accessibility violations exceeding the maximum allowed: "
                f"{violations_exceeded}"
            )

The conftest file defines a pytest fixture that creates an instance of AxeHelper with Axe initialized. This fixture has a session scope, meaning it’s created once per test session and shared across all tests.

import pytest
from axe_playwright_python.sync_playwright import Axe

from utilities.axe_helper import AxeHelper

@pytest.fixture(scope="session")
def axe_playwright():
    """Fixture to provide an instance of AxeHelper with Axe initialized.

    This fixture has a session scope, meaning it will be created once per test session
    and shared across all tests.

    Returns:
        AxeHelper: An instance of AxeHelper with Axe initialized.
    """
    yield AxeHelper(Axe())

Test Usage

The test usage can be found here.

import allure

class TestAccessibility:

    @allure.title("Test Accessibility with Default Counts")
    def test_accessibility_default_counts(self, axe_playwright, page):
        axe_playwright.check_accessibility(page)

    @allure.title("Test Accessibility with Custom Counts")
    def test_accessibility_custom_counts(self, axe_playwright, page):
        axe_playwright.check_accessibility(
            page,
            maximum_allowed_violation_by_impact={
                "minor": 2,
                "moderate": 5,
                "serious": 0,
                "critical": 0,
            },
        )

The first test, test_accessibility_default_counts, invokes the check_accessibility method of the axe_playwright fixture, which scans the given page for accessibility violations, allowing no violations of any impact level. The second test, test_accessibility_custom_counts, demonstrates how to customize the maximum allowed violations per impact level using the maximum_allowed_violations_by_impact parameter.

In conclusion

Integrating Axe with Playwright empowers automation engineers to incorporate accessibility testing into their workflows seamlessly.

Happy testing!


Suggest Changes

Ready to build your quality roadmap? Start Here


Previous Post
Test Automation - How to Bypass Re-Login With Playwright Python And Pytest
Next Post
Test Automation - How To Link Playwright Traces and Videos to Allure Report using GitHub Actions