While I began writing my package as a direct, one-to-one translation of the Java package, my package turned out to look very different.
Java and Python are, after all, very different languages. I found some methods to be unnecessary in python, and chose to not use the same design pattern as axe-selenium-java.
I wrote the methods used by the
Builder class to be methods of the
Axe class instead.
getContents() and inject()
As I mentioned in my previous post, I decided that
get_contents() was an unnecessary method for the python package.
The next method I implemented was the
inject() method, to inject the axe-core script into the page.
I was able to translate the
inject() methods into a single line of python code:
def inject(self, selenium, script_url): selenium.execute_script(open(script_url).read())
Recursively Inject into Frames
The next method in the axe-selenium-java class is
injectIntoFrames(), which is a recursive method.
injectIntoFrames() injects the script into the top-level document, as well as any iframes or nested iframes.
Recursive algorithms are a little more difficult to both write and comprehend, and deciphering someone else’s recursive function can be very time-consuming.
As such, I will break down this method in another post, Analyzing and Understanding Recursive Functions.
My mentor and I decided that, at least for the time being, we will not be implementing this method, and instead focus on developing an MVP version of this package.
Reporting the Results
The next method in the Axe class is the
report() method, which creates human-friendly output of the test results.
This result will appear in the pytest traceback, as well in the HTML report, when using pytest-html:
AssertionError: Found 1 accessibility violation: 1) id attribute value must be unique: https://dequeuniversity.com/rules/axe/2.3/duplicate-id?application=axeAPI #masthead > [role="navigation"] > a:nth-child(1) > img[src$="mozillians-logo.png"] Fix any of the following: Document has multiple elements with the same id attribute: mozillians-logo Impact: moderate
This is a very basic results report, and definitely a rough draft version. One of the stretch goals of my internship is to implement reporting that creates an HTML report (that doesn’t scorch the eyeballs).
Though I am not a designer, I do place a high importance on aesthetics, and I’m looking forward to designing more sophisticated reports.
get_rules() and write_results()
I also decided to implement two additional methods in my aXe integration package.
The first is the
get_rules() method. This function simply retrieves a list of accessibility rules from aXe, using axe.getRules().
As of now, this method does not accept parameters, but I plan to modify it to mirror the API method.
getRules() method accepts an array of tag names, e.g. [‘wcag2a’, ‘section508’], and will return a set of rules that includes those tags.
def get_rules(self, selenium): """Return array of accessibility rules.""" response = selenium.execute_script('return axe.getRules();') return response
The second method I added is the
write_results() method, which writes the results to a file.
This method accepts two parameters: a filename, and a JSON object to be written to the file.
To make the data more readable to the human eye, I used the
json python library to format or “pretty-print” the data.
def write_results(self, name, output): file = open(name, 'w+') file.write(json.dumps(output, indent=4)) file.close
Below is my test function for the
It probably isn’t preferable for long URLs, but it does create meaningful and timestamped file names.
@pytest.mark.nondestructive def test_write_results(base_url, selenium, axe): """Write JSON results to file.""" # get string of current python version version = 'v' + str(sys.version_info) + '_' + \ str(sys.version_info) + '_' + str(sys.version_info) # strip protocol and dots filename = re.sub('(http:\/\/|https:\/\/|\.php|\.asp|\.html)', '', base_url) # replace slashes with underscores filename = re.sub('(\/|\.)', '_', filename) # create filename "examplecom-datetime-python-version.json" filename += '-' + time.strftime('%m-%d-%y-%X') + '-' + version + '.json' data = axe.execute(selenium) axe.write_results(filename, data) # check that file exists and is not empty assert os.path.exists(filename) and os.path.getsize(filename) > 0, \ 'Output file not found.'
An example output filename from
Moving forward, my focus is to clean up the package to maximize stability, usability, and to provide well-written documentation.