From ba24ec56528e4458f0a5604a8d511923c943a479 Mon Sep 17 00:00:00 2001 From: Kimberly Date: Tue, 18 Sep 2018 11:01:39 -0600 Subject: [PATCH 1/2] * make it black * update readme to remove docker reference --- .dockerignore | 6 -- .gitignore | 4 +- .travis.yml | 79 ++++++++++++++++++++----- CHANGELOG.rst | 58 ------------------- Dockerfile | 37 ------------ Jenkinsfile | 2 +- MANIFEST.in | 2 +- README.rst | 83 +++++++++++++++++++++------ axe_selenium_python/__init__.py | 2 +- axe_selenium_python/axe.py | 66 +++++++++++---------- axe_selenium_python/package-lock.json | 13 +++++ axe_selenium_python/package.json | 22 +++++++ axe_selenium_python/src/axe.min.js | 1 - axe_selenium_python/tests/conftest.py | 6 +- axe_selenium_python/tests/test_axe.py | 12 ++-- package-lock.json | 5 ++ package.json | 19 ++++++ setup.py | 47 ++++++++------- test.txt | 2 + tox.ini | 6 +- 20 files changed, 270 insertions(+), 202 deletions(-) delete mode 100644 .dockerignore delete mode 100644 CHANGELOG.rst delete mode 100644 Dockerfile create mode 100644 axe_selenium_python/package-lock.json create mode 100644 axe_selenium_python/package.json delete mode 100644 axe_selenium_python/src/axe.min.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 test.txt diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 536e365..0000000 --- a/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -.cache -.tox -**/__pycache__ -**/*.pyc -build -dist diff --git a/.gitignore b/.gitignore index dbc78a6..c84498f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,6 @@ __pycache__ .cache/ .tox *.log -assets/ -*.json +node_modules build/ update.sh -results/ diff --git a/.travis.yml b/.travis.yml index 3372f31..9174c94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,71 @@ sudo: required language: python -services: - - docker + +cache: + directories: + - $HOME/.cache/pip + - $HOME/virtualenv/python2.7.9/lib/python2.7/site-packages + - node_modules + +install: pip install -r axe_selenium_python/tests/requirements/tests.txt +script: skip + jobs: include: - stage: - env: - - TOXENV=flake8 + language: python + python: 3.6 + node_js: stable + addons: + firefox: latest-nightly + env: TOXENV=py36 MOZ_HEADLESS=1 GECKODRIVER=0.22.0 + before_install: + - wget -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER/geckodriver-v$GECKODRIVER-linux64.tar.gz + - mkdir $HOME/geckodriver && tar xvf /tmp/geckodriver.tar.gz -C $HOME/geckodriver + - export PATH=$HOME/geckodriver:$PATH + - firefox --version + - geckodriver --version + install: + - pip install tox + before_script: + - npm install + script: tox - stage: - env: TOXENV=py27 + language: python + python: 2.7 + node_js: stable + addons: + firefox: latest-nightly + env: TOXENV=py27 MOZ_HEADLESS=1 GECKODRIVER=0.22.0 + before_install: + - wget -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER/geckodriver-v$GECKODRIVER-linux64.tar.gz + - mkdir $HOME/geckodriver && tar xvf /tmp/geckodriver.tar.gz -C $HOME/geckodriver + - export PATH=$HOME/geckodriver:$PATH + - firefox --version + - geckodriver --version + install: + - pip install tox + before_script: + - npm install + script: tox - stage: - env: - - TOXENV=py36 -cache: - pip: true -before_install: docker build -t axe-selenium-python . -install: skip -script: - - docker run -e TOXENV=$TOXENV axe-selenium-python tox -notifications: - email: fte-ci@mozilla.com + language: python + env: TOXENV=flake8 + install: + - pip install tox + + script: tox + - stage: deploy + deploy: + provider: pypi + user: kimberlythegeek + password: + secure: LHsL91XR32/M4r5ETAvaN/vUTakYByIfdwdCw6EI59LBvSnwaSant010QIl39+uafuev57yzUC/Y+orefczjkJnG3KdQBNS0Rt/zWIMw0Dr6Fp41Vg66e5URK/FRIwK36WlWzOcd3GkrQLLaDeqnXVzDWVMuXPP9/1ssu6mvriSeLctPsmX5N6m4yZwxNtpFsTLEh+BumXiamCuqjTI0RpyqxYlUVfio0G5LWeY9rkPskrwbSbc8xhq/PMk/ecLtlAxdn8AwgjLYCAt9d6NRfgL4Yp0R+kkfUQsX1Wf8A/pBNRr8Ht8Hy4CNlnEphgao4fgVEY4dc6tZL3FXOU9jQSmbChoANlbPzDhO+nb6d/QW5vLHVDufKyRQqtFxD90XFXlWFc/0gnD6tPZhi+UEFmBMoo9ugWDnYUeBd1T3lbwKT7sOmOQS58WhDVMLxPwr2BfgItGrNsaVHmzx9v0BnxZvD5ilmvNPrad6Rcsa0N8GtNXpnbyaupWzA97bemzuLqNHOjTm6TMZWRol6lLEJsJ1MRs7xWI4DYztXzlWITH7rvf6NNyvXKe9FPV3Lfoei3k1mT8QuEh6bIEFpBKRVV0ObQVSfFQ4M4wbgDBb3CbHVqMqFHgi98mpUo9tNGm4uzZCXw3hfEQpUPugKaH+VHoaylE+MR+fOhsyVlLQluM= + install: skip + script: skip + skip_cleanup: true + on: + tags: true + branch: master + distributions: "sdist bdist_wheel" + skip_cleanup: true \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index f989a50..0000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,58 +0,0 @@ -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. -- Writing results to file can be enabled by setting the environment variable ``ACCESSIBILITY_REPORTING=true``. The files will be written to ``results/`` directory, which must be created if it does not already exist. -- Accessibility checks can be disabled by setting the environment variable ``ACCESSIBILITY_DISABLED=true``. - -version 1.2.1 -************** -- Updated axe.min.js to ``axe-core@2.6.1`` -- Modified impact_included class method to reflect changes to the aXe API: -- There are now only 3 impact levels: 'critical', 'serious', and 'minor' - -version 1.0.0 -************** -- Updated usage examples in README -- Added docstrings to methods lacking documentation -- Removed unused files - -version 0.0.3 -************** -- Added run method to Axe class to simplify the usage in existing test suites -- run method includes the ability to set what impact level to test for: 'minor', 'moderate', 'severe', 'critical' - -version 0.0.276 -**************** -- Added pytest-axe usage example to README - -version 0.0.275 -**************** -- Added usage example to README - -version 0.0.273 -**************** -- Added selenium instance as a class attribute -- Changed file paths to OS independent structure -- Fixed file read operations to use with keyword - - -version 0.0.21 -*************** -- Fixed include of aXe API file and references to it -- Updated README diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 1562c4e..0000000 --- a/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM ubuntu:xenial - -ENV DEBIAN_FRONTEND=noninteractive \ - MOZ_HEADLESS=1 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 - -RUN apt-get update \ - && apt-get install -y software-properties-common \ - && add-apt-repository ppa:deadsnakes/ppa \ - && apt-get update \ - && apt-get install -y bzip2 curl firefox git python2.7 python-dev python3.6 python3-pip \ - && rm -rf /var/lib/apt/lists/* - -ENV FIREFOX_VERSION=61.0 - -RUN curl -fsSLo /tmp/firefox.tar.bz2 https://download-installer.cdn.mozilla.net/pub/firefox/releases/$FIREFOX_VERSION/linux-x86_64/en-US/firefox-$FIREFOX_VERSION.tar.bz2 \ - && apt-get -y purge firefox \ - && rm -rf /opt/firefox \ - && tar -C /opt -xjf /tmp/firefox.tar.bz2 \ - && rm /tmp/firefox.tar.bz2 \ - && mv /opt/firefox /opt/firefox-$FIREFOX_VERSION \ - && ln -fs /opt/firefox-$FIREFOX_VERSION/firefox /usr/bin/firefox - -ENV GECKODRIVER_VERSION=0.21.0 -RUN curl -fsSLo /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER_VERSION/geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz \ - && rm -rf /opt/geckodriver \ - && tar -C /opt -zxf /tmp/geckodriver.tar.gz \ - && rm /tmp/geckodriver.tar.gz \ - && mv /opt/geckodriver /opt/geckodriver-$GECKODRIVER_VERSION \ - && chmod 755 /opt/geckodriver-$GECKODRIVER_VERSION \ - && ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/geckodriver - -ENV TOX_VERSION=3.2.0 -RUN pip3 install tox==$TOX_VERSION - -ADD . /src -WORKDIR /src diff --git a/Jenkinsfile b/Jenkinsfile index aaa5af3..144ffa8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ def branch = env.BRANCH_NAME ?: 'master' /** Desired capabilities */ def capabilities = [ browserName: 'Firefox', - version: '61.0', + version: '62.0', platform: 'Windows 10' ] diff --git a/MANIFEST.in b/MANIFEST.in index 582862c..00ca075 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include README.rst axe_selenium_python/src/axe.min.js +include README.rst axe_selenium_python/node_modules/axe-core/axe.min.js axe_selenium_python/tests/test_page.html recursive-include *.rst *.txt diff --git a/README.rst b/README.rst index cf7a7b9..adbb47a 100644 --- a/README.rst +++ b/README.rst @@ -3,29 +3,23 @@ axe-selenium-python axe-selenium-python integrates aXe and selenium to enable automated web accessibility testing. -**This version of axe-selenium-python is using axe-core@3.0.3.** +**This version of axe-selenium-python is using axe-core@3.1.1.** -.. image:: https://img.shields.io/badge/license-MPL%202.0-blue.svg?style=flat-square +.. image:: https://img.shields.io/badge/license-MPL%202.0-blue.svg :target: https://github.com/mozilla-services/axe-selenium-python/blob/master/LICENSE.txt :alt: License -.. image:: https://img.shields.io/pypi/v/axe-selenium-python.svg?style=flat-square +.. image:: https://img.shields.io/pypi/v/axe-selenium-python.svg :target: https://pypi.org/project/axe-selenium-python/ :alt: PyPI -.. image:: https://img.shields.io/travis/mozilla-services/axe-selenium-python.svg?style=flat-square +.. image:: https://img.shields.io/travis/mozilla-services/axe-selenium-python.svg :target: https://travis-ci.org/mozilla-services/axe-selenium-python :alt: Travis -.. image:: https://img.shields.io/pypi/wheel/axe-selenium-python.svg?style=flat-square - :target: https://pypi.org/project/axe-selenium-python/ - :alt: wheel -.. image:: https://img.shields.io/github/issues-raw/mozilla-services/axe-selenium-python.svg?style=flat-square +.. image:: https://img.shields.io/github/issues-raw/mozilla-services/axe-selenium-python.svg :target: https://github.com/mozilla-services/axe-selenium-python/issues :alt: Issues -.. image:: https://pyup.io/repos/github/mozilla-services/axe-selenium-python/shield.svg?style=flat-square +.. image:: https://pyup.io/repos/github/mozilla-services/axe-selenium-python/shield.svg :target: https://pyup.io/repos/github/mozilla-services/axe-selenium-python/ :alt: Updates -.. image:: https://pyup.io/repos/github/mozilla-services/axe-selenium-python/python-3-shield.svg?style=flat-square - :target: https://pyup.io/repos/github/mozilla-services/axe-selenium-python/ - :alt: Python 3 Requirements @@ -35,7 +29,8 @@ You will need the following prerequisites in order to use axe-selenium-python: - selenium >= 3.0.0 - Python 2.7 or 3.6 -- `geckodriver `_ downloaded and `added to your PATH `_ +- The appropriate driver for the browser you intend to use, downloaded and added to your path, e.g. geckodriver for Firefox: + - `geckodriver `_ downloaded and `added to your PATH `_ Installation ------------ @@ -79,12 +74,11 @@ Contributing Fork the repository and submit PRs with bug fixes and enhancements; contributions are very welcome. You can run the tests using -`Docker `_: +`tox `_: .. code-block:: bash - $ docker build -t axe-selenium-python . - $ docker run -it axe-selenium-python tox + $ tox Resources --------- @@ -92,3 +86,60 @@ Resources - `Issue Tracker `_ - `Code `_ - `pytest-axe `_ + +CHANGELOG +^^^^^^^^^^^^^^ + +version 2.1.0 +************** +- Created package.json file to maintain axe-core dependency +- Replaced unit tests with more meaningful integration tests + - included a sample html file for integration tests + +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. +- Writing results to file can be enabled by setting the environment variable ``ACCESSIBILITY_REPORTING=true``. The files will be written to ``results/`` directory, which must be created if it does not already exist. +- Accessibility checks can be disabled by setting the environment variable ``ACCESSIBILITY_DISABLED=true``. + +version 1.2.1 +************** +- Updated axe to ``axe-core@2.6.1`` +- Modified impact_included class method to reflect changes to the aXe API: +- There are now only 3 impact levels: 'critical', 'serious', and 'minor' + +version 1.0.0 +************** +- Updated usage examples in README +- Added docstrings to methods lacking documentation +- Removed unused files + +version 0.0.3 +************** +- Added run method to Axe class to simplify the usage in existing test suites +- run method includes the ability to set what impact level to test for: 'minor', 'moderate', 'severe', 'critical' + +version 0.0.28 +**************** +- Added selenium instance as a class attribute +- Changed file paths to OS independent structure +- Fixed file read operations to use with keyword + + +version 0.0.21 +*************** +- Fixed include of aXe API file and references to it +- Updated README diff --git a/axe_selenium_python/__init__.py b/axe_selenium_python/__init__.py index 5676c76..b86eab3 100644 --- a/axe_selenium_python/__init__.py +++ b/axe_selenium_python/__init__.py @@ -2,4 +2,4 @@ # 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 .axe import Axe # NOQA +from .axe import Axe # NOQA diff --git a/axe_selenium_python/axe.py b/axe_selenium_python/axe.py index 112808a..f1b6802 100644 --- a/axe_selenium_python/axe.py +++ b/axe_selenium_python/axe.py @@ -6,11 +6,12 @@ from io import open from os import path -_DEFAULT_SCRIPT = path.join(path.dirname(__file__), 'src', 'axe.min.js') +_DEFAULT_SCRIPT = path.join( + path.dirname(__file__), "node_modules", "axe-core", "axe.min.js" +) class Axe(object): - def __init__(self, selenium, script_url=_DEFAULT_SCRIPT): self.script_url = script_url self.selenium = selenium @@ -22,7 +23,7 @@ def inject(self): :param script_url: location of the axe-core script. :type script_url: string """ - with open(self.script_url, 'r', encoding='utf8') as f: + with open(self.script_url, "r", encoding="utf8") as f: self.selenium.execute_script(f.read()) def execute(self, context=None, options=None): @@ -32,18 +33,18 @@ def execute(self, context=None, options=None): :param context: which page part(s) to analyze and/or what to exclude. :param options: dictionary of aXe options. """ - template = 'return axe.run(%s).then(function(result){return result;});' - args = '' + template = "return axe.run(%s).then(function(result){return result;});" + args = "" # If context parameter is passed, add to args if context is not None: - args += '%r' % context + args += "%r" % context # Add comma delimiter only if both parameters are passed if context is not None and options is not None: - args += ',' + args += "," # If options parameter is passed, add to args if options is not None: - args += '%s' % options + args += "%s" % options command = template % args response = self.selenium.execute_script(command) @@ -58,36 +59,43 @@ def report(self, violations): :return report: Readable report of violations. :rtype: string """ - string = '' - string += 'Found ' + str(len(violations)) + ' accessibility violations:' + string = "" + string += "Found " + str(len(violations)) + " accessibility violations:" 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 violation['tags']: - string += ' ' + tag - string += '\n\tElements Affected:' + string += ( + "\n\n\nRule Violated:\n" + + violation["id"] + + " - " + + violation["description"] + + "\n\tURL: " + + violation["helpUrl"] + + "\n\tImpact Level: " + + violation["impact"] + + "\n\tTags:" + ) + for tag in violation["tags"]: + string += " " + tag + string += "\n\tElements Affected:" i = 1 - for node in violation['nodes']: - for target in node['target']: - string += '\n\t' + str(i) + ') Target: ' + target + for node in violation["nodes"]: + for target in node["target"]: + string += "\n\t" + str(i) + ") Target: " + target i += 1 - for item in node['all']: - string += '\n\t\t' + item['message'] - for item in node['any']: - string += '\n\t\t' + item['message'] - for item in node['none']: - string += '\n\t\t' + item['message'] - string += '\n\n\n' + for item in node["all"]: + string += "\n\t\t" + item["message"] + for item in node["any"]: + string += "\n\t\t" + item["message"] + for item in node["none"]: + string += "\n\t\t" + item["message"] + string += "\n\n\n" return string - def write_results(self, data, name='results.json'): + def write_results(self, data, name="results.json"): """ Write JSON to file with the specified name. :param name: Name of file to be written to. :param output: JSON object. """ - with open(name, 'r', encoding='utf8') as f: + with open(name, "r", encoding="utf8") as f: f.write(json.dumps(data, indent=4)) diff --git a/axe_selenium_python/package-lock.json b/axe_selenium_python/package-lock.json new file mode 100644 index 0000000..9d3a574 --- /dev/null +++ b/axe_selenium_python/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "axe-selenium-python", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axe-core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.1.1.tgz", + "integrity": "sha512-5/JPwRqOMDnEqonolatT9/i+aOTYCpTEHR4rqmYjWRn+oiZdRHbYv8MC+HNLEtP0I3mkC3glAF8fRkfKHrUOtw==" + } + } +} diff --git a/axe_selenium_python/package.json b/axe_selenium_python/package.json new file mode 100644 index 0000000..1326a31 --- /dev/null +++ b/axe_selenium_python/package.json @@ -0,0 +1,22 @@ +{ + "name": "axe-selenium-python", + "version": "0.0.1", + "description": "Integrate axe-core with python", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kimberlythegeek/axe-selenium-python.git" + }, + "author": "Kimberly Sereduck", + "license": "MPL-2.0", + "bugs": { + "url": "https://github.com/kimberlythegeek/axe-selenium-python/issues" + }, + "homepage": "https://github.com/kimberlythegeek/axe-selenium-python#readme", + "dependencies": { + "axe-core": ">=3.1.0" + } +} diff --git a/axe_selenium_python/src/axe.min.js b/axe_selenium_python/src/axe.min.js deleted file mode 100644 index d097938..0000000 --- a/axe_selenium_python/src/axe.min.js +++ /dev/null @@ -1 +0,0 @@ -!function e(window){var document=window.document,f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function c(e){this.name="SupportError",this.cause=e.cause,this.message="`"+e.cause+"` - feature unsupported in your environment.",e.ruleId&&(this.ruleId=e.ruleId,this.message+=" Skipping "+this.ruleId+" rule."),this.stack=(new Error).stack}(axe=axe||{}).version="3.0.3","function"==typeof define&&define.amd&&define("axe-core",[],function(){"use strict";return axe}),"object"===("undefined"==typeof module?"undefined":f(module))&&module.exports&&"function"==typeof e.toString&&(axe.source="("+e.toString()+')(typeof window === "object" ? window : this);',module.exports=axe),"function"==typeof window.getComputedStyle&&(window.axe=axe),(c.prototype=Object.create(Error.prototype)).constructor=c;var utils=axe.utils={},o={};f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function t(e,t,n){"use strict";var a,r;for(a=0,r=e.length;at){var n=e.indexOf(">");e=e.substring(0,n+1)}return e}(r||"")),this._element=e}axe.utils.clone=function(e){"use strict";var t,n,a=e;if(null!==e&&"object"===(void 0===e?"undefined":f(e)))if(Array.isArray(e))for(a=[],t=0,n=e.length;t":!0,"?":!0,"@":!0,"[":!0,"\\":!0,"]":!0,"^":!0,"`":!0,"{":!0,"|":!0,"}":!0,"~":!0},o={"\n":"\\n","\r":"\\r","\t":"\\t","\f":"\\f","\v":"\\v"},y={n:"\n",r:"\r",t:"\t",f:"\f","\\":"\\","'":"'"},v={n:"\n",r:"\r",t:"\t",f:"\f","\\":"\\",'"':'"'};function t(s,u,c,d,r,m){var p,f,h,b,g;return b=s.length,p=null,h=function(e,t){var n,a,r;for(r="",u++,p=s.charAt(u);u"),axe.utils.cssParser=n}(axe),O.prototype={get selector(){return this.spec.selector||[axe.utils.getSelector(this.element,this._options)]},get xpath(){return this.spec.xpath||[axe.utils.getXpath(this.element)]},get element(){return this._element},get fromFrame(){return this._fromFrame},toJSON:function(){"use strict";return{selector:this.selector,source:this.source,xpath:this.xpath}}},O.fromFrame=function(e,t,n){return e.selector.unshift(n.selector),e.xpath.unshift(n.xpath),new axe.utils.DqElement(n.element,t,e)},axe.utils.DqElement=O,axe.utils.matchesSelector=function(){"use strict";var n;return function(e,t){return n&&e[n]||(n=function(e){var t,n,a=e.Element.prototype,r=["matches","matchesSelector","mozMatchesSelector","webkitMatchesSelector","msMatchesSelector"],i=r.length;for(t=0;t=e.length/2}(u)?P(u):void 0}};var V,_=axe.utils.escapeSelector,U=void 0,D=["class","style","id","selected","checked","disabled","tabindex","aria-checked","aria-selected","aria-invalid","aria-activedescendant","aria-busy","aria-disabled","aria-expanded","aria-grabbed","aria-pressed","aria-valuenow"],M=31;function H(e,t){var n=t.name,a=void 0;if(-1!==n.indexOf("href")||-1!==n.indexOf("src")){var r=axe.utils.getFriendlyUriEnd(e.getAttribute(n));if(r){var i=encodeURI(r);if(!i)return;a=_(t.name)+'$="'+i+'"'}else a=_(t.name)+'="'+e.getAttribute(n)+'"'}else a=_(n)+'="'+_(t.value)+'"';return a}function B(e,t){return e.count "+i:l,o=o?o.filter(function(e){return axe.utils.matchesSelector(e,i)}):Array.from(n.querySelectorAll(i)),e=e.parentElement}while((1 ")?":root"+i.substring(i.indexOf(" > ")):":root"}axe.utils.getSelectorData=function(e){for(var a={classes:{},tags:{},attributes:{}},r=(e=Array.isArray(e)?e:[e]).slice(),i=[],t=function(){var e=r.pop(),n=e.actualNode;if(n.querySelectorAll){var t=n.nodeName;a.tags[t]?a.tags[t]++:a.tags[t]=1,n.classList&&Array.from(n.classList).forEach(function(e){var t=_(e);a.classes[t]?a.classes[t]++:a.classes[t]=1}),n.attributes&&Array.from(n.attributes).filter(G).forEach(function(e){var t=H(n,e);t&&(a.attributes[t]?a.attributes[t]++:a.attributes[t]=1)})}for(e.children.length&&(i.push(r),r=e.children.slice());!r.length&&i.length;)r=i.pop()};r.length;)t();return a},axe.utils.getSelector=function(e){var t=1>>0,r=arguments[1],i=0;i>>0,a=2<=arguments.length?arguments[1]:void 0,r=0;rthis.length)&&-1!==this.indexOf(e,t)});f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function ne(r,i){"use strict";return function(e){var t=r[e.id]||{},n=t.messages||{},a=Object.assign({},t);delete a.messages,void 0===e.result?"object"===f(n.incomplete)?a.message=function(){return function(t,n){function a(e){return e.incomplete&&e.incomplete.default?e.incomplete.default:o.incompleteFallbackMessage()}if(!t||!t.missingData)return a(n);try{var e=n.incomplete[t.missingData[0].reason];if(!e)throw new Error;return e}catch(e){return"string"==typeof t.missingData?n.incomplete[t.missingData]:a(n)}}(e.data,n)}:a.message=n.incomplete:a.message=e.result===i?n.pass:n.fail,axe.utils.extendMetaData(e,a)}}axe.utils.publishMetaData=function(e){"use strict";var t=axe._audit.data.checks||{},n=axe._audit.data.rules||{},a=axe.utils.findBy(axe._audit.rules,"id",e.id)||{};e.tags=axe.utils.clone(a.tags||[]);var r=ne(t,!0),i=ne(t,!1);e.nodes.forEach(function(e){e.any.forEach(r),e.all.forEach(r),e.none.forEach(i)}),axe.utils.extendMetaData(e,axe.utils.clone(n[e.id]||{}))};var ae=function(){},re=function(){};var ie,oe=(ie=/(?=[\-\[\]{}()*+?.\\\^$|,#\s])/g,function(e){return e.replace(ie,"\\")}),le=/\\/g;function se(e){if(e)return e.map(function(e){var t,n,a=e.name.replace(le,""),r=(e.value||"").replace(le,"");switch(e.operator){case"^=":n=new RegExp("^"+oe(r));break;case"$=":n=new RegExp(oe(r)+"$");break;case"~=":n=new RegExp("(^|\\s)"+oe(r)+"(\\s|$)");break;case"|=":n=new RegExp("^"+oe(r)+"(-|$)");break;case"=":t=function(e){return r===e};break;case"*=":t=function(e){return e&&e.includes(r)};break;case"!=":t=function(e){return r!==e};break;default:t=function(e){return!!e}}return""===r&&/^[*$^]=$/.test(e.operator)&&(t=function(){return!1}),t||(t=function(e){return e&&n.test(e)}),{key:a,value:r,test:t}})}function ue(e){if(e)return e.map(function(e){return{value:e=e.replace(le,""),regexp:new RegExp("(^|\\s)"+oe(e)+"(\\s|$)")}})}function ce(e){if(e)return e.map(function(e){var t;return"not"===e.name&&(t=(t=axe.utils.cssParser.parse(e.value)).selectors?t.selectors:[t],t=ae(t)),{name:e.name,expressions:t,value:e.value}})}function de(e,t,n,a){var r={nodes:e.slice(),anyLevel:t,thisLevel:n,parentShadowId:a};return r.nodes.reverse(),r}function me(e,t){return c=e.actualNode,d=t[0],1===c.nodeType&&("*"===d.tag||c.nodeName.toLowerCase()===d.tag)&&(s=e.actualNode,!(u=t[0]).classes||u.classes.reduce(function(e,t){return e&&s.className&&s.className.match(t.regexp)},!0))&&(o=e.actualNode,!(l=t[0]).attributes||l.attributes.reduce(function(e,t){var n=o.getAttribute(t.key);return e&&null!==n&&(!t.value||t.test(n))},!0))&&(r=e.actualNode,!(i=t[0]).id||r.id===i.id)&&(n=e,!((a=t[0]).pseudos&&!a.pseudos.reduce(function(e,t){if("not"===t.name)return e&&!re([n],t.expressions,!1).length;throw new Error("the pseudo selector "+t.name+" has not yet been implemented")},!0)));var n,a,r,i,o,l,s,u,c,d}ae=function(e){return e.map(function(e){for(var t=[],n=e.rule;n;)t.push({tag:n.tagName?n.tagName.toLowerCase():"*",combinator:n.nestingOperator?n.nestingOperator:" ",id:n.id,attributes:se(n.attrs),classes:ue(n.classNames),pseudos:ce(n.pseudos)}),n=n.rule;return t})},re=function(e,t,n,a){for(var r=[],i=de(Array.isArray(e)?e:[e],t,[],e[0].shadowId),o=[];i.nodes.length;){for(var l=i.nodes.pop(),s=[],u=[],c=i.anyLevel.slice().concat(i.thisLevel),d=!1,m=0;m"].includes(f[0].combinator))throw new Error("axe.utils.querySelectorAll does not support the combinator: "+p[1].combinator);">"===f[0].combinator?s.push(f):u.push(f)}!i.anyLevel.includes(p)||p[0].id&&l.shadowId!==i.parentShadowId||u.push(p)}for(l.children&&l.children.length&&n&&(r.push(i),i=de(l.children,u,s,l.shadowId));!i.nodes.length&&r.length;)i=r.pop()}return o},axe.utils.querySelectorAll=function(e,t){return axe.utils.querySelectorAllFilter(e,t)},axe.utils.querySelectorAllFilter=function(e,t,n){e=Array.isArray(e)?e:[e];var a=axe.utils.cssParser.parse(t);return a=a.selectors?a.selectors:[a],a=ae(a),re(e,a,!0,n)};f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(){"use strict";function m(){}function p(e){if("function"!=typeof e)throw new TypeError("Queue methods require functions as arguments")}axe.utils.queue=function(){var t,a=[],r=0,i=0,n=m,o=!1,l=function(e){t=e,setTimeout(function(){null!=t&&axe.log("Uncaught error (of queue)",t)},1)},s=l;function u(t){return function(e){a[t]=e,(i-=1)||n===m||(o=!0,n(a))}}function c(e){return n=m,s(e),a}var d={defer:function(e){if("object"===(void 0===e?"undefined":f(e))&&e.then&&e.catch){var n=e;e=function(e,t){n.then(e).catch(t)}}if(p(e),void 0===t){if(o)throw new Error("Queue already completed");return a.push(e),++i,function(){for(var e=a.length;re.clientHeight||!a&&e.scrollWidth>e.clientWidth)return{elm:e,top:e.scrollTop,left:e.scrollLeft}}(t);return n&&e.push(n),e.concat(he(t))},[])}function be(e){"use strict";return e.sort(function(e,t){return axe.utils.contains(e,t)?1:-1})[0]}function ge(t,e){"use strict";var n=e.include&&be(e.include.filter(function(e){return axe.utils.contains(e,t)})),a=e.exclude&&be(e.exclude.filter(function(e){return axe.utils.contains(e,t)}));return!!(!a&&n||a&&axe.utils.contains(a,n))}function ye(e,t){"use strict";var n;if(0===e.length)return t;e.length>>((3&t)<<3)&255;return a}}for(var l="function"==typeof e.Buffer?e.Buffer:Array,r=[],i={},s=0;s<256;s++)r[s]=(s+256).toString(16).substr(1),i[r[s]]=s;function p(e,t){var n=t||0,a=r;return a[e[n++]]+a[e[n++]]+a[e[n++]]+a[e[n++]]+"-"+a[e[n++]]+a[e[n++]]+"-"+a[e[n++]]+a[e[n++]]+"-"+a[e[n++]]+a[e[n++]]+"-"+a[e[n++]]+a[e[n++]]+a[e[n++]]+a[e[n++]]+a[e[n++]]+a[e[n++]]}var u=o(),f=[1|u[0],u[1],u[2],u[3],u[4],u[5]],h=16383&(u[6]<<8|u[7]),b=0,g=0;function c(e,t,n){var a=t&&n||0;"string"==typeof e&&(t="binary"==e?new l(16):null,e=null);var r=(e=e||{}).random||(e.rng||o)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t)for(var i=0;i<16;i++)t[a+i]=r[i];return t||p(r)}(pe=c).v1=function(e,t,n){var a=t&&n||0,r=t||[],i=null!=(e=e||{}).clockseq?e.clockseq:h,o=null!=e.msecs?e.msecs:(new Date).getTime(),l=null!=e.nsecs?e.nsecs:g+1,s=o-b+(l-g)/1e4;if(s<0&&null==e.clockseq&&(i=i+1&16383),(s<0||b>>24&255,r[a++]=u>>>16&255,r[a++]=u>>>8&255,r[a++]=255&u;var c=o/4294967296*1e4&268435455;r[a++]=c>>>8&255,r[a++]=255&c,r[a++]=c>>>24&15|16,r[a++]=c>>>16&255,r[a++]=i>>>8|128,r[a++]=255&i;for(var d=e.node||f,m=0;m<6;m++)r[a+m]=d[m];return t||p(r)},pe.v4=c,pe.parse=function(e,t,n){var a=t&&n||0,r=0;for(t=t||[],e.toLowerCase().replace(/[0-9a-f]{2}/g,function(e){r<16&&(t[a+r++]=i[e])});r<16;)t[a+r++]=0;return t},pe.unparse=p,pe.BufferClass=l}(window),axe._load({data:{rules:{accesskeys:{description:"Ensures every accesskey attribute value is unique",help:"accesskey attribute value must be unique"},"area-alt":{description:"Ensures elements of image maps have alternate text",help:"Active elements must have alternate text"},"aria-allowed-attr":{description:"Ensures ARIA attributes are allowed for an element's role",help:"Elements must only use allowed ARIA attributes"},"aria-dpub-role-fallback":{description:"Ensures unsupported DPUB roles are only used on elements with implicit fallback roles",help:"Unsupported DPUB ARIA roles should be used on elements with implicit fallback roles"},"aria-hidden-body":{description:"Ensures aria-hidden='true' is not present on the document body.",help:"aria-hidden='true' must not be present on the document body"},"aria-required-attr":{description:"Ensures elements with ARIA roles have all required ARIA attributes",help:"Required ARIA attributes must be provided"},"aria-required-children":{description:"Ensures elements with an ARIA role that require child roles contain them",help:"Certain ARIA roles must contain particular children"},"aria-required-parent":{description:"Ensures elements with an ARIA role that require parent roles are contained by them",help:"Certain ARIA roles must be contained by particular parents"},"aria-roles":{description:"Ensures all elements with a role attribute use a valid value",help:"ARIA roles used must conform to valid values"},"aria-valid-attr-value":{description:"Ensures all ARIA attributes have valid values",help:"ARIA attributes must conform to valid values"},"aria-valid-attr":{description:"Ensures attributes that begin with aria- are valid ARIA attributes",help:"ARIA attributes must conform to valid names"},"audio-caption":{description:"Ensures