35 lines
1.9 KiB
JavaScript
35 lines
1.9 KiB
JavaScript
const assert = require("node:assert/strict");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
const { test } = require("node:test");
|
|
|
|
function luminance(hex) {
|
|
const rgb = hex.slice(1).match(/../g).map(value => parseInt(value, 16) / 255)
|
|
.map(value => value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4);
|
|
return rgb[0] * .2126 + rgb[1] * .7152 + rgb[2] * .0722;
|
|
}
|
|
function contrast(a, b) {
|
|
const values = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
return (values[0] + .05) / (values[1] + .05);
|
|
}
|
|
for (const feature of ["install-base", "service-requests"]) {
|
|
test(`${feature}: explicit dark text colors cover cells, headings, counts and errors with readable contrast`, () => {
|
|
const css = fs.readFileSync(path.join(__dirname, `../src/features/${feature}/styles.css`), "utf8");
|
|
const rules = [...css.matchAll(/([^{}]+)\{([^{}]+)\}/g)];
|
|
const prefix = `.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-${feature}`;
|
|
function color(selector) {
|
|
const matches = rules.filter(([, selectors, body]) => selectors.split(",").map(value => value.trim()).includes(selector)
|
|
&& /(?:^|;)\s*color\s*:/.test(body));
|
|
assert.ok(matches.length, `Missing explicit color for ${selector}`);
|
|
return matches.at(-1)[2].match(/(?:^|;)\s*color\s*:\s*(#[a-f0-9]{6})/i)[1];
|
|
}
|
|
for (const suffix of ["-content", "-table", "-table th", "-table td", "-message", '-message[role="alert"]']) {
|
|
const foreground = color(prefix + suffix);
|
|
const background = suffix.endsWith(" th") ? "#353331" : "#292827";
|
|
assert.ok(contrast(foreground, background) >= 4.5, `${suffix} needs at least 4.5:1 contrast`);
|
|
}
|
|
assert.match(css, /color: #17693a; background: #e1f1e5/, "light status colors remain unchanged");
|
|
assert.match(css, /color: #b8e4c6; background: #214d34/, "dark status colors remain unchanged");
|
|
});
|
|
}
|