diff options
Diffstat (limited to '')
-rw-r--r-- | tests/contrast/test_contrasts.py | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/tests/contrast/test_contrasts.py b/tests/contrast/test_contrasts.py new file mode 100644 index 0000000..517b34e --- /dev/null +++ b/tests/contrast/test_contrasts.py @@ -0,0 +1,101 @@ +""" + Contrast Tests + ~~~~~~~~~~ + + Pygments styles should be accessible to people with suboptimal vision. + This test ensures that the minimum contrast of styles does not degrade, + and that every rule of a new style fulfills the WCAG AA standard.[1] + + [1]: https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html +""" + +import json +import os +import sys + +import pygments.styles +import pygments.token +import wcag_contrast_ratio + +JSON_FILENAME = os.path.join(os.path.dirname(__file__), "min_contrasts.json") +WCAG_AA_CONTRAST = 4.5 + + +def hex2rgb(hexstr): + hexstr = hexstr.lstrip("#") + r = int(hexstr[:2], 16) / 255 + g = int(hexstr[2:4], 16) / 255 + b = int(hexstr[4:], 16) / 255 + return (r, g, b) + + +def get_style_contrasts(style_cls): + return [ + ( + round( + wcag_contrast_ratio.rgb( + hex2rgb(style["bgcolor"] or style_cls.background_color), + hex2rgb(style["color"] or "#000000") + # we default to black because browsers also do + ), + 1, + ), + ttype, + ) + for ttype, style in style_cls.list_styles() + if ttype != pygments.token.Whitespace + ] + + +def builtin_styles(): + for style_name in pygments.styles.STYLE_MAP: + yield (style_name, pygments.styles.get_style_by_name(style_name)) + + +def min_contrasts(): + return { + name: min(x[0] for x in get_style_contrasts(style)) + for name, style in builtin_styles() + } + + +def update_json(): + with open(JSON_FILENAME, "w") as f: + json.dump( + min_contrasts(), + f, + indent=2, + ) + + +def test_contrasts(fail_if_improved=True): + with open(JSON_FILENAME) as f: + previous_contrasts = json.load(f) + + for style_name in pygments.styles.STYLE_MAP: + style = pygments.styles.get_style_by_name(style_name) + contrasts = get_style_contrasts(style) + min_contrast = min([x[0] for x in contrasts]) + + bar = previous_contrasts.get(style_name, WCAG_AA_CONTRAST) + + assert not min_contrast < bar, ( + "contrast degradation for style '{}'\n" + "The following rules have a contrast lower than the required {}:\n\n" + "{}\n" + ).format( + style_name, + bar, + "\n".join( + [ + "* {:.2f} {}".format(contrast, ttype) + for contrast, ttype in contrasts + if contrast < bar + ] + ), + ) + + if fail_if_improved: + assert ( + not min_contrast > bar + ), "congrats, you improved a contrast! please run ./scripts/update_contrasts.py" |