|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +const assert = require('assert').strict; |
| 4 | +const {colorutils} = require('../../../static/js/colorutils'); |
| 5 | + |
| 6 | +// Unit coverage for the WCAG helpers added in #7377. |
| 7 | +// Kept backend-side so it runs in plain mocha without a browser; colorutils |
| 8 | +// is pure and has no DOM deps. |
| 9 | +describe(__filename, function () { |
| 10 | + describe('relativeLuminance', function () { |
| 11 | + it('returns 0 for pure black and 1 for pure white', function () { |
| 12 | + assert.strictEqual(colorutils.relativeLuminance([0, 0, 0]), 0); |
| 13 | + assert.strictEqual(colorutils.relativeLuminance([1, 1, 1]), 1); |
| 14 | + }); |
| 15 | + |
| 16 | + it('matches the WCAG 2.1 reference values (within 1e-3)', function () { |
| 17 | + // Spot-check against published examples from the WCAG spec: |
| 18 | + // #808080 (mid grey) → ~0.2159 |
| 19 | + // #ff0000 (pure red) → ~0.2126 (red coefficient) |
| 20 | + const grey = colorutils.relativeLuminance([0x80 / 255, 0x80 / 255, 0x80 / 255]); |
| 21 | + const red = colorutils.relativeLuminance([1, 0, 0]); |
| 22 | + assert.ok(Math.abs(grey - 0.2159) < 1e-3, `grey luminance: ${grey}`); |
| 23 | + assert.ok(Math.abs(red - 0.2126) < 1e-3, `red luminance: ${red}`); |
| 24 | + }); |
| 25 | + }); |
| 26 | + |
| 27 | + describe('contrastRatio', function () { |
| 28 | + it('is 21 between black and white', function () { |
| 29 | + assert.strictEqual(colorutils.contrastRatio([0, 0, 0], [1, 1, 1]), 21); |
| 30 | + }); |
| 31 | + |
| 32 | + it('is 1 between identical colors', function () { |
| 33 | + assert.strictEqual(colorutils.contrastRatio([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]), 1); |
| 34 | + }); |
| 35 | + |
| 36 | + it('fails WCAG AA for mid-tone red on black (<4.5)', function () { |
| 37 | + // #cc0000-ish — a common "author color" range. |
| 38 | + const ratio = colorutils.contrastRatio([0.8, 0, 0], [0, 0, 0]); |
| 39 | + assert.ok(ratio < 4.5, `expected <4.5, got ${ratio}`); |
| 40 | + }); |
| 41 | + }); |
| 42 | + |
| 43 | + describe('ensureReadableBackground', function () { |
| 44 | + it('leaves light enough backgrounds unchanged', function () { |
| 45 | + // Pastel blue: already has adequate contrast with black text. |
| 46 | + const light = '#aaccff'; |
| 47 | + assert.strictEqual( |
| 48 | + colorutils.ensureReadableBackground(light), light, |
| 49 | + 'a bg that already satisfies 4.5:1 must be returned verbatim'); |
| 50 | + }); |
| 51 | + |
| 52 | + it('leaves very dark backgrounds unchanged (white text handles it)', function () { |
| 53 | + // Near-black bg pairs with white text for contrast >> 4.5 — leave it. |
| 54 | + const dark = '#111111'; |
| 55 | + assert.strictEqual( |
| 56 | + colorutils.ensureReadableBackground(dark), dark, |
| 57 | + 'a bg that works with white text must be returned verbatim'); |
| 58 | + }); |
| 59 | + |
| 60 | + it('lightens mid-tone backgrounds until they pass WCAG AA with black text', function () { |
| 61 | + // #cc0000 is the exact failure case from the issue screenshot — dark |
| 62 | + // enough that black text is hard to read, but not dark enough for |
| 63 | + // white text to hit 4.5:1 either. |
| 64 | + const result = colorutils.ensureReadableBackground('#cc0000'); |
| 65 | + assert.notStrictEqual(result, '#cc0000', 'expected the bg to change'); |
| 66 | + const triple = colorutils.css2triple(result); |
| 67 | + const ratio = colorutils.contrastRatio(triple, [0, 0, 0]); |
| 68 | + assert.ok(ratio >= 4.5, `post-clamp contrast must be >=4.5, got ${ratio}`); |
| 69 | + }); |
| 70 | + |
| 71 | + it('respects a custom minContrast target', function () { |
| 72 | + const result = colorutils.ensureReadableBackground('#888888', 7.0); |
| 73 | + const triple = colorutils.css2triple(result); |
| 74 | + const ratio = colorutils.contrastRatio(triple, [0, 0, 0]); |
| 75 | + assert.ok(ratio >= 7.0, `AAA contrast target not met: ${ratio}`); |
| 76 | + }); |
| 77 | + }); |
| 78 | +}); |
0 commit comments