-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Speed up testing, docs. Start exporting fixtures. (#84)
* update pytest to introduce smoke testing. * rname selectors cause of subprocess conflict * make a reusable axe pytest fixture * get axe from npm in ci * fluent api for axe results object in fixture * drop convert script and build docs * remove old docs build and prefer the new system * name the test workflow * export utility functiuons for axe test * add tests for a11y dialogs * rname test_playwirght
- Loading branch information
Showing
11 changed files
with
411 additions
and
223 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# requires node and axe | ||
# requires playwright | ||
import dataclasses | ||
from functools import lru_cache | ||
from itertools import chain | ||
from json import dumps, loads | ||
from os import environ | ||
from shlex import quote, split | ||
from subprocess import CalledProcessError, check_output | ||
from sys import argv | ||
from pathlib import Path | ||
from typing import Any | ||
from attr import dataclass | ||
import exceptiongroup | ||
from pytest import fixture, mark, param | ||
import pytest | ||
|
||
NBCONVERT_HTML5_DYNAMIC_TEST = "NBCONVERT_HTML5_DYNAMIC_TEST" | ||
|
||
axe_config_aa = { | ||
"runOnly": ["act", "best-practice", "experimental", "wcag21a", "wcag21aa", "wcag22aa"], | ||
"allowedOrigins": ["<same_origin>"], | ||
} | ||
|
||
axe_config_aaa = { | ||
"runOnly": [ | ||
"act", | ||
"best-practice", | ||
"experimental", | ||
"wcag21a", | ||
"wcag21aa", | ||
"wcag22aa", | ||
"wcag2aaa", | ||
], | ||
"allowedOrigins": ["<same_origin>"], | ||
} | ||
|
||
MATHJAX = "[id^=MathJax]" | ||
tests_axe = {"exclude": [MATHJAX]} | ||
|
||
|
||
def get_npm_directory(package, data=False): | ||
try: | ||
info = loads(check_output(split(f"npm ls --long --depth 0 --json {quote(package)}"))) | ||
except CalledProcessError: | ||
return | ||
if data: | ||
return info | ||
return Path(info.get("dependencies").get(package).get("path")) | ||
|
||
|
||
@dataclass | ||
class AxeResults: | ||
data: Any | ||
|
||
def raises(self): | ||
if self.data["violations"]: | ||
raise AxeException.from_violations(self.data) | ||
return self | ||
|
||
def dump(self, file: Path): | ||
if file.is_dir(): | ||
file /= "axe-results.json" | ||
file.parent.mkdir(exist_ok=True, parents=True) | ||
file.write_text(dumps(self.data)) | ||
return self | ||
|
||
|
||
@dataclasses.dataclass | ||
class AxeException(Exception): | ||
message: str | ||
target: list | ||
data: dict = dataclasses.field(repr=False) | ||
|
||
types = {} | ||
|
||
@classmethod | ||
def new(cls, id, impact, message, data, target, **kwargs): | ||
if id in cls.types: | ||
cls = cls.types.get(id) | ||
else: | ||
cls = cls.types.setdefault( | ||
id, | ||
type( | ||
f"{impact.capitalize()}{''.join(map(str.capitalize, id.split('-')))}Exception", | ||
(cls,), | ||
dict(), | ||
), | ||
) | ||
return cls(message, target, data) | ||
|
||
@classmethod | ||
def from_violations(cls, data): | ||
out = [] | ||
for violation in (violations := data.get("violations")): | ||
for node in violation["nodes"]: | ||
for exc in node["any"]: | ||
out.append(cls.new(**exc, target=["target"])) | ||
return exceptiongroup.ExceptionGroup(f"{len(violations)} accessibility violations", out) | ||
|
||
|
||
@mark.parametrize("package", ["axe-core", param("axe-core-doesnt-ship-this", marks=mark.xfail)]) | ||
def test_non_package(package): | ||
assert get_npm_directory(package), "package not found." | ||
|
||
|
||
@lru_cache(1) | ||
def get_axe(): | ||
return (get_npm_directory("axe-core") / "axe.js").read_text() | ||
|
||
|
||
def inject_axe(page): | ||
page.evaluate(get_axe()) | ||
|
||
|
||
def run_axe_test(page, tests_config=None, axe_config=None): | ||
return AxeResults( | ||
page.evaluate( | ||
f"window.axe.run({tests_config and dumps(tests_config) or 'document'}, {dumps(axe_config or {})})" | ||
) | ||
) | ||
|
||
|
||
@fixture | ||
def axe(page): | ||
def go(url, tests=tests_axe, axe_config=axe_config_aa): | ||
page.goto(url) | ||
inject_axe(page) | ||
return run_axe_test(page, tests, axe_config) | ||
|
||
yield go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,5 @@ dependencies: | |
- beautifulsoup4 | ||
- scipy | ||
- doit | ||
- mkdocs-material | ||
- mkdocstrings[python] |
Oops, something went wrong.