diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c9499af..f989a50 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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. diff --git a/README.rst b/README.rst index afb376b..1d7c90c 100644 --- a/README.rst +++ b/README.rst @@ -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 @@ -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 `_ downloaded and `added to your PATH `_ -Optional -^^^^^^^^ -- tox - Installation ------------ @@ -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 *
* 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 `_. - -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 `_. Resources --------- diff --git a/axe_selenium_python/axe.py b/axe_selenium_python/axe.py index d76f4cb..31d6e3f 100644 --- a/axe_selenium_python/axe.py +++ b/axe_selenium_python/axe.py @@ -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): @@ -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. @@ -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. @@ -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 @@ -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): @@ -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) diff --git a/axe_selenium_python/tests/conftest.py b/axe_selenium_python/tests/conftest.py index 567c454..9ab25f8 100644 --- a/axe_selenium_python/tests/conftest.py +++ b/axe_selenium_python/tests/conftest.py @@ -15,7 +15,7 @@ @pytest.fixture def script_url(): - """Return a script URL""" + """Return a script URL.""" return _DEFAULT_SCRIPT diff --git a/axe_selenium_python/tests/requirements/tests.txt b/axe_selenium_python/tests/requirements/tests.txt index 2a6dadd..670c7bb 100644 --- a/axe_selenium_python/tests/requirements/tests.txt +++ b/axe_selenium_python/tests/requirements/tests.txt @@ -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 diff --git a/axe_selenium_python/tests/test_axe.py b/axe_selenium_python/tests/test_axe.py index b555b87..ad99ca8 100644 --- a/axe_selenium_python/tests/test_axe.py +++ b/axe_selenium_python/tests/test_axe.py @@ -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.""" @@ -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 diff --git a/setup.py b/setup.py index 763446f..00eeb7a 100644 --- a/setup.py +++ b/setup.py @@ -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(),