Answer stability round 2: 0 🔴, 2 🟡 (0 misread); make the order check able to fail, measure the page button, brand mark and forced dark

This commit is contained in:
2026-10-31 01:34:42 +01:00
parent 48655c0ad2
commit c12fd2742f
2 changed files with 23 additions and 17 deletions
+14 -9
View File
@@ -174,13 +174,8 @@ test("the public landing at / is ungated and links to sign in + register", async
await shot(page, "live-05-public-landing"); await shot(page, "live-05-public-landing");
}); });
// Measured on the rendered button, so a theme block that drops --on-accent fails here. // Measured on the rendered elements, so a theme block that drops --on-accent fails here.
for (const colorScheme of ["light", "dark"] as const) { const contrastOf = (el: Element): number => {
test(`the primary button's text reaches 4.5:1 in ${colorScheme} mode`, async ({ page, context }) => {
await context.clearCookies();
await page.emulateMedia({ colorScheme });
await page.goto("/");
const ratio = await page.locator(".landing .btn-primary").first().evaluate((el) => {
const luminance = (rgb: string): number => { const luminance = (rgb: string): number => {
const [r = 0, g = 0, b = 0] = (rgb.match(/[\d.]+/g) ?? []).map((c) => { const [r = 0, g = 0, b = 0] = (rgb.match(/[\d.]+/g) ?? []).map((c) => {
const v = Number(c) / 255; const v = Number(c) / 255;
@@ -191,8 +186,18 @@ for (const colorScheme of ["light", "dark"] as const) {
const style = getComputedStyle(el); const style = getComputedStyle(el);
const [hi = 0, lo = 0] = [luminance(style.color), luminance(style.backgroundColor)].sort((x, y) => y - x); const [hi = 0, lo = 0] = [luminance(style.color), luminance(style.backgroundColor)].sort((x, y) => y - x);
return (hi + 0.05) / (lo + 0.05); return (hi + 0.05) / (lo + 0.05);
}); };
expect(ratio).toBeGreaterThanOrEqual(4.5); for (const [name, colorScheme, forceDark] of [["light", "light", false], ["dark", "dark", false], ["forced dark", "light", true]] as const) {
test(`text on the accent reaches 4.5:1 in ${name} mode`, async ({ page, context }) => {
await context.clearCookies();
await page.emulateMedia({ colorScheme });
await page.goto("/");
if (forceDark) await page.locator("#theme-dark").check({ force: true });
// No page of this stack has a second page of results, so add the current-page button.
await page.locator("#main-content").evaluate((el) => el.insertAdjacentHTML("beforeend", '<a class="page-btn" aria-current="page" href="#">2</a>'));
for (const selector of [".landing .btn-primary", ".brand-mark", ".page-btn"]) {
await expect.poll(() => page.locator(selector).first().evaluate(contrastOf), { message: selector }).toBeGreaterThanOrEqual(4.5); // polls past the colour transition
}
}); });
} }
+2 -1
View File
@@ -38,7 +38,8 @@ test("styles.css folds .nav-children only inside @supports selector(:has(a))", (
test("styles.css hides the theme switch and the nav toggles where :has() is missing, after their base rules", () => { test("styles.css hides the theme switch and the nav toggles where :has() is missing, after their base rules", () => {
const guard = stylesheet.indexOf("@supports not selector(:has(a)) {"); const guard = stylesheet.indexOf("@supports not selector(:has(a)) {");
assert.ok(guard >= 0, "no @supports not selector(:has(a)) block"); assert.ok(guard >= 0, "no @supports not selector(:has(a)) block");
assert.ok(guard > stylesheet.lastIndexOf(".theme-switch {", guard), "the guard precedes the base .theme-switch rule"); const base = stylesheet.search(/^\.theme-switch\s*\{/m);
assert.ok(base >= 0 && base < guard, "the guard precedes the base .theme-switch rule");
const block = stylesheet.slice(guard, stylesheet.indexOf("\n}", guard)); const block = stylesheet.slice(guard, stylesheet.indexOf("\n}", guard));
assert.match(block, /\.theme-switch\s*\{\s*display:\s*none/); assert.match(block, /\.theme-switch\s*\{\s*display:\s*none/);
assert.match(block, /\.nav-disc\s*\{\s*visibility:\s*hidden/); assert.match(block, /\.nav-disc\s*\{\s*visibility:\s*hidden/);