Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
CHANGELOG
^^^^^^^^^^^^^^

version 2.0.0
**************
- All functionalities that are not part of axe-core have been moved into a separate package, ``pytest-axe``. This includes:
- ``run_axe`` helper method
- ``get_rules`` Axe class method
- ``run`` Axe class method
- ``impact_included`` Axe class method
- ``analyze`` Axe class method.

The purpose of this change is to separate implementations that are specific to the Mozilla Firefox Test Engineering team, and leave the base ``axe-selenium-python`` package for a more broad use case. This package was modeled off of Deque's Java package, axe-selenium-java, and will now more closely mirror it.

All functionalities can still be utilized when using ``axe-selenium-python`` in conjunction with ``pytest-axe``.

version 1.2.3
**************
- Added the analyze method to the Axe class. This method runs accessibility checks, and writes the JSON results to file based on the page URL and the timestamp.
Expand Down
66 changes: 22 additions & 44 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ axe-selenium-python integrates aXe and selenium to enable automated web accessib
.. image:: https://img.shields.io/pypi/wheel/axe-selenium-python.svg?style=for-the-badge
:target: https://pypi.org/project/axe-selenium-python/
:alt: wheel
.. .. image:: https://img.shields.io/travis/kimberlythegeek/axe-selenium-python.svg?style=for-the-badge
.. :target: https://travis-ci.org/kimberlythegeek/axe-selenium-python/
.. :alt: Travis
.. image:: https://img.shields.io/github/issues-raw/kimberlythegeek/axe-selenium-python.svg?style=for-the-badge
:target: https://github.com/kimberlythegeek/axe-selenium-python/issues
:alt: Issues
Expand All @@ -27,14 +24,10 @@ Requirements

You will need the following prerequisites in order to use axe-selenium-python:

- selenium >= 3.0.0
- Python 2.7 or 3.6
- pytest-selenium >= 3.0.0
- `geckodriver <https://github.com/mozilla/geckodriver/releases>`_ downloaded and `added to your PATH <https://stackoverflow.com/questions/40208051/selenium-using-python-geckodriver-executable-needs-to-be-in-path#answer-40208762>`_

Optional
^^^^^^^^
- tox

Installation
------------

Expand All @@ -44,48 +37,33 @@ To install axe-selenium-python:

$ pip install axe-selenium-python

To install pytest-axe:

.. code-block:: bash

$ pip install pytest-axe

Usage
------
``test_accessibility.py``

.. code-block:: python

import pytest

@pytest.mark.nondestructive
def test_header_accessibility(axe):
violations = axe.run('header', None, 'critical')
assert len(violations) == 0, axe.report(violations)



The above example runs aXe against only the content within the *<header>* tag, and filters for violations labeled ``critical``.

The method ``axe.run()`` accepts three parameters: ``context``, ``options``, and
``impact``.

For more information on ``context`` and ``options``, view the `aXe
documentation here <https://github.com/dequelabs/axe-core/blob/master/doc/API.md#parameters-axerun>`_.

The third parameter, ``impact``, allows you to filter violations by their impact
level. The options are ``'critical'``, ``'serious'`` and ``'minor'``, with the
default value set to ``None``.

This will filter violations for the impact level specified, and **all violations with a higher impact level**.

To run the above test you will need to specify the browser instance to be invoked, and the **base_url**.

.. code-block:: bash

$ pytest --base-url http://www.mozilla.com --driver Firefox test_accessibility.py


import pytest
from selenium import webdriver
from axe_selenium_python import Axe

def test_google():
driver = webdriver.Firefox()
driver.get("http://www.google.com")
axe = Axe(driver)
# Inject axe-core javascript into page.
axe.inject()
# Run axe accessibility checks.
results = axe.execute()
# Write results to file
axe.write_results('a11y.json', results)
driver.close()
# Assert no violations are found
assert len(results["violations"]) == 0, axe.report(results["violations"])

The method ``axe.execute()`` accepts two parameters: ``context`` and ``options``.

For more information on ``context`` and ``options``, view the `aXe documentation here <https://github.com/dequelabs/axe-core/blob/master/doc/API.md#parameters-axerun>`_.

Resources
---------
Expand Down
71 changes: 7 additions & 64 deletions axe_selenium_python/axe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,12 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from os import environ, path
from os import path
import json
import time
import re

_DEFAULT_SCRIPT = path.join(path.dirname(__file__), 'src', 'axe.min.js')


def run_axe(page):
axe = Axe(page.selenium)
axe.analyze()


class Axe(object):

def __init__(self, selenium, script_url=_DEFAULT_SCRIPT):
Expand All @@ -31,11 +24,6 @@ def inject(self):
with open(self.script_url) as f:
self.selenium.execute_script(f.read())

def get_rules(self):
"""Return array of accessibility rules."""
response = self.selenium.execute_script('return axe.getRules();')
return response

def execute(self, context=None, options=None):
"""
Run axe against the current page.
Expand All @@ -60,32 +48,6 @@ def execute(self, context=None, options=None):
response = self.selenium.execute_script(command)
return response

def run(self, context=None, options=None, impact=None):
"""
Inject aXe, run against current page, and return rules & violations.
"""
self.inject()
data = self.execute(context, options)
violations = dict((rule['id'], rule) for rule in data['violations'] if self.impact_included(rule, impact))

return violations

def impact_included(self, rule, impact):
"""
Function to filter for violations with specified impact level, and all
violations with a higher impact level.
"""
if impact == 'minor' or impact is None:
return True
elif impact == 'serious':
if rule['impact'] != 'minor':
return True
elif impact == 'critical':
if rule['impact'] == 'critical':
return True
else:
return False

def report(self, violations):
"""
Return readable report of accessibility violations found.
Expand All @@ -97,16 +59,16 @@ def report(self, violations):
"""
string = ''
string += 'Found ' + str(len(violations)) + ' accessibility violations:'
for violation, rule in violations.items():
string += '\n\n\nRule Violated:\n' + rule['id'] + ' - ' + rule['description'] + \
'\n\tURL: ' + rule['helpUrl'] + \
'\n\tImpact Level: ' + rule['impact'] + \
for violation in violations:
string += '\n\n\nRule Violated:\n' + violation['id'] + ' - ' + violation['description'] + \
'\n\tURL: ' + violation['helpUrl'] + \
'\n\tImpact Level: ' + violation['impact'] + \
'\n\tTags:'
for tag in rule['tags']:
for tag in violation['tags']:
string += ' ' + tag
string += '\n\tElements Affected:'
i = 1
for node in rule['nodes']:
for node in violation['nodes']:
for target in node['target']:
string += '\n\t' + str(i) + ') Target: ' + target
i += 1
Expand All @@ -117,7 +79,6 @@ def report(self, violations):
for item in node['none']:
string += '\n\t\t' + item['message']
string += '\n\n\n'

return string

def write_results(self, name, output):
Expand All @@ -129,21 +90,3 @@ def write_results(self, name, output):
"""
with open(name, 'w+') as f:
f.write(json.dumps(output, indent=4))

def analyze(self, context=None, options=None, impact=None):
"""Run aXe accessibility checks, and write results to file."""
disabled = environ.get('ACCESSIBILITY_DISABLED')
if not disabled or disabled is None:
violations = self.run(context, options, impact)

# Format file name based on page title and current datetime.
t = time.strftime("%m_%d_%Y_%H:%M:%S")
title = self.selenium.title
title = re.sub('[\s\W]', '-', title)
title = re.sub('(-|_)+', '-', title)

# Output results only if reporting is enabled.
if environ.get('ACCESSIBILITY_REPORTING') == 'true':
# Write JSON results to file if recording enabled
self.write_results('results/%s_%s.json' % (title, t), violations)
assert len(violations) == 0, self.report(violations)
2 changes: 1 addition & 1 deletion axe_selenium_python/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@pytest.fixture
def script_url():
"""Return a script URL"""
"""Return a script URL."""
return _DEFAULT_SCRIPT


Expand Down
3 changes: 2 additions & 1 deletion axe_selenium_python/tests/requirements/tests.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pytest==3.4.2
pytest==3.5.0
selenium==3.11.0
pytest-selenium==1.12.0
pytest-html==1.16.1
pytest_base_url==1.4.1
20 changes: 3 additions & 17 deletions axe_selenium_python/tests/test_axe.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
import pytest


@pytest.mark.nondestructive
def test_get_rules(axe):
"""Assert number of rule tests matches number of available rules."""
axe.inject()
rules = axe.get_rules()
assert len(rules) == 58, len(rules)


@pytest.mark.nondestructive
def test_execute(axe):
"""Run axe against base_url and verify JSON output."""
Expand All @@ -22,18 +14,12 @@ def test_execute(axe):
assert data is not None, data


@pytest.mark.nondestructive
def test_run(base_url, axe):
"""Assert that run method returns results."""
violations = axe.run()
assert violations is not None


@pytest.mark.nondestructive
def test_report(axe):
"""Test that report exists."""
violations = axe.run()

axe.inject()
results = axe.execute()
violations = results["violations"]
report = axe.report(violations)
assert report is not None, report

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def readme():


setup(name='axe-selenium-python',
version='1.2.4',
version='2.0.0',
description='Python library to integrate axe and selenium for web \
accessibility testing.',
long_description=readme(),
Expand Down