-
Notifications
You must be signed in to change notification settings - Fork 0
fix(ops): live checks assert the deployed footer tap-target rule (WCAG 2.2, PR #22 follow-up) #110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6df1690
a1cd94f
d858791
691b775
384e4d7
8366a99
9bba2d3
dc01f97
3749993
15fe152
9587672
06e28ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,8 @@ | |
| // JSON-LD on only 4 of 12 pages). | ||
| // Proof 2b covers the 2026-08-08 dogfood finding page: | ||
| // 2b. /contact/ renders H2 after H1 (the heading-hierarchy repair, PR #18). | ||
| // 5. the deployed stylesheet keeps the WCAG 2.2 24px footer tap-target | ||
| // rule (PR #22) so mobile footer links stay >= 24px on every page. | ||
| import { join } from "node:path" | ||
| import { fileURLToPath } from "node:url" | ||
| import { dirname } from "node:path" | ||
|
|
@@ -151,6 +153,26 @@ try { | |
| const blocks = (body.match(/<script\s+type="application\/ld\+json"[^>]*>/gi) || []).length | ||
| ok(blocks === 1, `${path} carries exactly one application/ld+json block (got ${blocks})`) | ||
| } | ||
|
|
||
| console.log("G. the deployed stylesheet keeps the WCAG 2.2 24px footer tap-target rule (PR #22)") | ||
| { | ||
| const { status, body } = await get("/styles.css") | ||
| ok(status === 200, `GET /styles.css returns 200 (got ${status})`) | ||
| const footerRule = body.match(/\.footer-links\s*a\s*\{([^}]*)\}/) | ||
| ok(footerRule !== null, "deployed stylesheet has a .footer-links a rule") | ||
| if (footerRule) { | ||
| const rule = footerRule[1] | ||
| ok(/display:\s*(inline-block|inline-flex|block)/.test(rule), ".footer-links a is a block-level box (hit area covers the line box)") | ||
| ok(!/display:\s*inline\s*;/.test(rule), ".footer-links a is not a plain inline box") | ||
| ok(/min-height:\s*24px/.test(rule), ".footer-links a declares min-height: 24px") | ||
| const padding = rule.match(/padding:\s*([^;]+)/) | ||
| ok(padding !== null, ".footer-links a declares vertical padding") | ||
| if (padding) { | ||
| const vertical = parseFloat(padding[1].trim().split(/\s+/)[0]) | ||
| ok(vertical >= 4, `.footer-links a vertical padding is at least 4px (${vertical}px), so 16px text + padding >= 24px`) | ||
|
Comment on lines
+161
to
+172
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Use one cascade-aware validator for both live checks. Both implementations inspect only the first matching text block, so later declarations or conditional rules can invalidate the effective footer styles without failing the checks.
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| } | ||
| } catch (error) { | ||
| failures++ | ||
| console.error(` FAIL live request error: ${error.message}`) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,13 @@ | ||
| // Guard the LIVE public site against soft-404s: an unknown URL on | ||
| // tinystudio.in must return HTTP 404 with the real 404 page, never HTTP 200 | ||
| // with the homepage. | ||
| // Guard the LIVE public site against soft-404s and a stale a11y bundle: an | ||
| // unknown URL on tinystudio.in must return HTTP 404 with the real 404 page, | ||
| // never HTTP 200 with the homepage, and the deployed stylesheet must keep the | ||
| // WCAG 2.2 24px footer tap-target rule (PR #22) so mobile footer links are | ||
| // tappable on every live page. | ||
| // | ||
| // The static test (test-public-soft-404.mjs) only proves public/404.html | ||
| // exists in the repo; it cannot catch a stale or misconfigured deployment. | ||
| // This check hits the deployed site so a regression is detected the moment it | ||
| // ships. It runs as `npm run site:check-live`, from the nightly | ||
| // The static tests (test-public-soft-404.mjs, test-public-link-targets.mjs) | ||
| // only prove the repo state; they cannot catch a stale or misconfigured | ||
| // deployment. This check hits the deployed site so a regression is detected | ||
| // the moment it ships. It runs as `npm run site:check-live`, from the nightly | ||
| // live-site-check workflow, and as part of the deploy lane's post-deploy | ||
| // verification. | ||
| // | ||
|
|
@@ -73,6 +75,7 @@ try { | |
| unknown: await fetchWithRetry(`${SITE}${UNKNOWN_PATH}`), | ||
| notFoundAsset: await fetchWithRetry(`${SITE}/404.html`), | ||
| realPage: await fetchWithRetry(`${SITE}/promptly/`), | ||
| css: await fetchWithRetry(`${SITE}/styles.css`), | ||
| } | ||
| } catch (err) { | ||
| console.error(` FAIL could not reach ${SITE}: ${err.message}`) | ||
|
|
@@ -84,6 +87,7 @@ const { res: homeRes, body: homeBody } = results.home | |
| const { res: unknownRes, body: unknownBody } = results.unknown | ||
| const { res: notFoundRes, body: notFoundBody } = results.notFoundAsset | ||
| const { res: realRes } = results.realPage | ||
| const { res: cssRes, body: cssBody } = results.css | ||
|
|
||
| console.log("A. the homepage is reachable and intact") | ||
| ok(homeRes.status === 200, `GET / returns HTTP ${homeRes.status}`) | ||
|
|
@@ -102,8 +106,27 @@ ok(!notFoundBody.includes(HOME_TITLE), "/404.html body is not the homepage") | |
| console.log("D. a real page still serves") | ||
| ok(realRes.status === 200, `GET /promptly/ returns HTTP ${realRes.status}`) | ||
|
|
||
| // Mirrors section B of test-public-link-targets.mjs against the DEPLOYED | ||
| // stylesheet, so a stale bundle that loses the PR #22 rule fails loudly. | ||
| console.log("E. the deployed stylesheet keeps the WCAG 2.2 24px footer tap-target rule (PR #22)") | ||
| ok(cssRes.status === 200, `GET /styles.css returns HTTP ${cssRes.status}`) | ||
| const footerRule = cssBody.match(/\.footer-links\s*a\s*\{([^}]*)\}/) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Both new live checks use Useful? React with 👍 / 👎. |
||
| ok(footerRule !== null, "deployed stylesheet has a .footer-links a rule") | ||
| if (footerRule) { | ||
| const rule = footerRule[1] | ||
| ok(/display:\s*(inline-block|inline-flex|block)/.test(rule), ".footer-links a is a block-level box (hit area covers the line box)") | ||
| ok(!/display:\s*inline\s*;/.test(rule), ".footer-links a is not a plain inline box") | ||
| ok(/min-height:\s*24px/.test(rule), ".footer-links a declares min-height: 24px") | ||
|
Comment on lines
+116
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the footer declarations are commented out—for example, Useful? React with 👍 / 👎. |
||
| const padding = rule.match(/padding:\s*([^;]+)/) | ||
| ok(padding !== null, ".footer-links a declares vertical padding") | ||
| if (padding) { | ||
| const vertical = parseFloat(padding[1].trim().split(/\s+/)[0]) | ||
|
Comment on lines
+119
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If a deployed rule contains malformed values such as Useful? React with 👍 / 👎. |
||
| ok(vertical >= 4, `.footer-links a vertical padding is at least 4px (${vertical}px), so 16px text + padding >= 24px`) | ||
| } | ||
| } | ||
|
|
||
| console.log(`\n${checks} checks, ${failures} failures`) | ||
| if (failures > 0) { | ||
| console.error("\nThe live site is soft-404ing or serving a stale bundle. Re-deploy the public site from origin/main and re-run this check.") | ||
| console.error("\nThe live site is soft-404ing, serving a stale bundle, or missing the footer tap-target rule. Re-deploy the public site from origin/main and re-run this check.") | ||
| } | ||
| process.exit(failures === 0 ? 0 : 1) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the deployed stylesheet scopes this declaration beneath another selector, such as
.homepage .footer-links a { ... }, the unanchored regex starts matching at.footer-linksand every assertion passes. Footer links on pages outside that ancestor remain undersized even though this acceptance proof claims coverage on every page; validate the complete selector list and require an unscoped.footer-links aselector.Useful? React with 👍 / 👎.