summaryrefslogtreecommitdiffstats
path: root/tests/contrast/test_contrasts.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/contrast/test_contrasts.py')
-rw-r--r--tests/contrast/test_contrasts.py101
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"