Repository navigation
Expand file tree
/
Copy pathaxe.py
More file actions
executable file
·113 lines (94 loc) · 3.78 KB
/
Copy pathaxe.py
File metadata and controls
executable file
·113 lines (94 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# This Source Code Form is subject to the terms of the Mozilla Public
# 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/.
"""Selenium Axe Python"""
from __future__ import annotations
import json
import os
from typing import TYPE_CHECKING, Dict, List
if TYPE_CHECKING:
from selenium.webdriver.remote.webdriver import WebDriver # pragma: no cover
from typing_extensions import TypeAlias
_DEFAULT_SCRIPT = os.path.join(os.path.dirname(__file__), "axe-core", "axe.min.js")
T_data: TypeAlias = Dict[str, List[Dict]]
class Axe:
"""Axe"""
def __init__(self, selenium: WebDriver, script_url: str = _DEFAULT_SCRIPT):
"""
script_url: location of the axe-core script.
"""
self.script_url = script_url
self.selenium = selenium
def inject(self):
"""
Recursively inject aXe into all iframes and the top level document.
"""
with open(self.script_url, encoding="utf8") as f:
self.selenium.execute_script(f.read())
def run(self, context: object | None = None, options: Dict | None = None) -> T_data:
"""
Run axe against the current page.
:param context: which page part(s) to analyze and/or what to exclude.
:param options: dictionary of aXe options.
"""
args = ""
# If context parameter is passed, add to args
if context is not None:
args += f"{context}"
if options is not None:
args += ","
# If options parameter is passed, add to args
if options is not None:
args += f"{options}"
command = (
f"var callback = arguments[arguments.length - 1];"
f"axe.run({args}).then(results => callback(results))"
)
return self.selenium.execute_async_script(command)
@staticmethod
def report(violations: List[Dict]) -> str:
"""
Return readable report of accessibility violations found.
:param violations: Dictionary of violations.
:return report: Readable report of violations.
"""
string = ""
string += f"Found {len(violations)} accessibility violations:"
for violation in violations:
string += (
f"\n\n\nRule Violated:"
f'\n{violation["id"]} - {violation["description"]}'
f'\n\tURL: {violation["helpUrl"]}'
f'\n\tImpact Level: {violation["impact"]}'
f"\n\tTags:"
)
for tag in violation["tags"]:
string += f" {tag}"
string += "\n\tElements Affected:"
i = 1
for node in violation["nodes"]:
for target in node["target"]:
string += f"\n\t{i}) Target: {target}"
i += 1
for item in node["all"]:
string += f"\n\t\t{item['message']}"
for item in node["any"]:
string += f"\n\t\t{item['message']}"
for item in node["none"]:
string += f"\n\t\t{item['message']}"
string += "\n\n\n"
return string
@staticmethod
def write_results(data: Dict, name: str | None = None) -> None:
"""
Write JSON to file with the specified name.
:param name: Path to the file to be written to. If no path is passed
a new JSON file "results.json" will be created in the
current working directory.
"""
if name:
filepath = os.path.abspath(name)
else:
filepath = os.path.join(os.getcwd(), "results.json")
with open(filepath, "w", encoding="utf8") as f:
f.write(json.dumps(data, indent=4))