|
| 1 | +/** |
| 2 | + * Details Output Demo |
| 3 | + * |
| 4 | + * Runs scanPage against intentionally non-compliant test pages to capture the |
| 5 | + * enriched Details messages for: color-contrast, color-contrast-enhanced, |
| 6 | + * target-size, valid-lang, and oobee-grading-text-contents. |
| 7 | + * |
| 8 | + * Usage: node examples/details-runner.js |
| 9 | + */ |
| 10 | +import { chromium } from 'playwright'; |
| 11 | +import { scanPage } from '../dist/npmIndex.js'; |
| 12 | +import { gradeReadability } from '../dist/crawlers/custom/gradeReadability.js'; |
| 13 | + |
| 14 | +// --- Test HTML pages --- |
| 15 | + |
| 16 | +const colorContrastHTML = ` |
| 17 | +<!DOCTYPE html> |
| 18 | +<html lang="en"> |
| 19 | +<head><title>Color Contrast Test</title></head> |
| 20 | +<body style="background-color: #ffffff;"> |
| 21 | + <h1>Color Contrast Violations</h1> |
| 22 | + <p style="color: #999999; font-size: 14px;">This light gray text on white background fails AA contrast</p> |
| 23 | + <p style="color: #aaaaaa; font-size: 14px; background-color: #f0f0f0;">Very light gray on light gray</p> |
| 24 | + <button style="background-color: #55aa99; color: #e8ffe8; font-size: 12px;">Low contrast button</button> |
| 25 | +</body> |
| 26 | +</html> |
| 27 | +`; |
| 28 | + |
| 29 | +const colorContrastEnhancedHTML = ` |
| 30 | +<!DOCTYPE html> |
| 31 | +<html lang="en"> |
| 32 | +<head><title>Color Contrast Enhanced Test</title></head> |
| 33 | +<body style="background-color: #ffffff;"> |
| 34 | + <h1>Color Contrast Enhanced AAA Violations</h1> |
| 35 | + <p style="color: #757575; font-size: 14px;">This text passes AA but fails AAA needs 7 to 1</p> |
| 36 | + <p style="color: #6b6b6b; font-size: 12px;">Small text needs 7 to 1 for AAA</p> |
| 37 | +</body> |
| 38 | +</html> |
| 39 | +`; |
| 40 | + |
| 41 | +const targetSizeHTML = ` |
| 42 | +<!DOCTYPE html> |
| 43 | +<html lang="en"> |
| 44 | +<head><title>Target Size Test</title> |
| 45 | +<style> |
| 46 | +body { font-family: sans-serif; padding: 40px; } |
| 47 | +.icon-link { |
| 48 | + display: inline-block; |
| 49 | + width: 16px; |
| 50 | + height: 16px; |
| 51 | + font-size: 10px; |
| 52 | + line-height: 16px; |
| 53 | + text-align: center; |
| 54 | + text-decoration: none; |
| 55 | + color: #333; |
| 56 | + overflow: hidden; |
| 57 | +} |
| 58 | +</style> |
| 59 | +</head> |
| 60 | +<body> |
| 61 | +<main> |
| 62 | +<h1>Icon-sized interactive targets</h1> |
| 63 | +<a href="/a" class="icon-link" style="width: 16px; height: 16px;">A</a> |
| 64 | +<a href="/b" class="icon-link" style="width: 16px; height: 16px;">B</a> |
| 65 | +<a href="/c" class="icon-link" style="width: 16px; height: 16px;">C</a> |
| 66 | +</main> |
| 67 | +</body> |
| 68 | +</html> |
| 69 | +`; |
| 70 | + |
| 71 | +const validLangHTML = ` |
| 72 | +<!DOCTYPE html> |
| 73 | +<html lang="x-sindarin"> |
| 74 | +<head><title>Valid Lang Test</title></head> |
| 75 | +<body> |
| 76 | + <main> |
| 77 | + <h1>Valid Lang Violation</h1> |
| 78 | + <p>This page uses a private-use language subtag that is not valid according to BCP 47.</p> |
| 79 | + <div lang="x-klingon">This section also has an invalid private-use lang tag with some sample text content for context.</div> |
| 80 | + </main> |
| 81 | +</body> |
| 82 | +</html> |
| 83 | +`; |
| 84 | + |
| 85 | +const readabilityHTML = ` |
| 86 | +<!DOCTYPE html> |
| 87 | +<html lang="en"> |
| 88 | +<head><title>Readability Test</title></head> |
| 89 | +<body> |
| 90 | + <main> |
| 91 | + <h1>Building Safety Standards</h1> |
| 92 | + <p>The committee reviewed the proposed changes to the building safety standards last Thursday. Members noted that the current regulations do not address modern construction materials adequately. Several technical amendments were suggested to improve clarity for contractors and inspectors. The revised standards will require additional testing for fire resistance in commercial properties. Public consultation on these proposed changes will remain open until the end of next quarter. Building owners should review the draft guidelines to understand potential compliance requirements.</p> |
| 93 | + </main> |
| 94 | +</body> |
| 95 | +</html> |
| 96 | +`; |
| 97 | + |
| 98 | +// --- Helpers --- |
| 99 | + |
| 100 | +function extractMessages(result, ruleId) { |
| 101 | + const messages = []; |
| 102 | + for (const category of ['mustFix', 'goodToFix', 'needsReview']) { |
| 103 | + const rules = result?.[category]?.rules; |
| 104 | + if (rules && rules[ruleId]) { |
| 105 | + const rule = rules[ruleId]; |
| 106 | + messages.push({ |
| 107 | + category, |
| 108 | + rule: rule.rule || ruleId, |
| 109 | + description: rule.description, |
| 110 | + totalItems: rule.totalItems, |
| 111 | + items: rule.items?.map(item => ({ |
| 112 | + html: item.html || item.element, |
| 113 | + message: item.message, |
| 114 | + })), |
| 115 | + }); |
| 116 | + } |
| 117 | + } |
| 118 | + return messages; |
| 119 | +} |
| 120 | + |
| 121 | +// --- Main --- |
| 122 | + |
| 123 | +(async () => { |
| 124 | + console.log("Launching browser..."); |
| 125 | + const browser = await chromium.launch({ headless: true }); |
| 126 | + const output = {}; |
| 127 | + |
| 128 | + // 1. Color Contrast (AA) |
| 129 | + console.log("Scanning: color-contrast..."); |
| 130 | + try { |
| 131 | + const page = await browser.newPage(); |
| 132 | + await page.setContent(colorContrastHTML); |
| 133 | + const result = await scanPage(page, { |
| 134 | + name: "Test", email: "test@test.com", pageTitle: "Color Contrast Test", |
| 135 | + }); |
| 136 | + output['color-contrast'] = extractMessages(result, 'color-contrast'); |
| 137 | + await page.close(); |
| 138 | + } catch (e) { console.error("color-contrast error:", e.message); } |
| 139 | + |
| 140 | + // 2. Color Contrast Enhanced (AAA) |
| 141 | + console.log("Scanning: color-contrast-enhanced..."); |
| 142 | + try { |
| 143 | + const page = await browser.newPage(); |
| 144 | + await page.setContent(colorContrastEnhancedHTML); |
| 145 | + const result = await scanPage(page, { |
| 146 | + name: "Test", email: "test@test.com", pageTitle: "Color Contrast Enhanced Test", |
| 147 | + ruleset: ['default', 'enable-wcag-aaa'], |
| 148 | + }); |
| 149 | + output['color-contrast-enhanced'] = extractMessages(result, 'color-contrast-enhanced'); |
| 150 | + await page.close(); |
| 151 | + } catch (e) { console.error("color-contrast-enhanced error:", e.message); } |
| 152 | + |
| 153 | + // 3. Target Size |
| 154 | + console.log("Scanning: target-size..."); |
| 155 | + try { |
| 156 | + const page = await browser.newPage(); |
| 157 | + await page.setContent(targetSizeHTML); |
| 158 | + const result = await scanPage(page, { |
| 159 | + name: "Test", email: "test@test.com", pageTitle: "Target Size Test", |
| 160 | + }); |
| 161 | + output['target-size'] = extractMessages(result, 'target-size'); |
| 162 | + await page.close(); |
| 163 | + } catch (e) { console.error("target-size error:", e.message); } |
| 164 | + |
| 165 | + // 4. Valid Lang |
| 166 | + console.log("Scanning: valid-lang..."); |
| 167 | + try { |
| 168 | + const page = await browser.newPage(); |
| 169 | + await page.setContent(validLangHTML); |
| 170 | + const result = await scanPage(page, { |
| 171 | + name: "Test", email: "test@test.com", pageTitle: "Valid Lang Test", |
| 172 | + }); |
| 173 | + output['valid-lang'] = extractMessages(result, 'valid-lang'); |
| 174 | + await page.close(); |
| 175 | + } catch (e) { console.error("valid-lang error:", e.message); } |
| 176 | + |
| 177 | + // 5. Readability (oobee-grading-text-contents) |
| 178 | + console.log("Scanning: oobee-grading-text-contents..."); |
| 179 | + try { |
| 180 | + const page = await browser.newPage(); |
| 181 | + await page.setContent(readabilityHTML); |
| 182 | + |
| 183 | + // Simulate what the crawler does: extract text, grade readability |
| 184 | + const textContent = readabilityHTML.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); |
| 185 | + const sentences = textContent.split(/(?<=[.!?])\s+/).filter(s => s.trim().length > 0); |
| 186 | + const flag = gradeReadability(sentences); |
| 187 | + console.log(` Readability flag: "${flag}"`); |
| 188 | + |
| 189 | + if (flag) { |
| 190 | + const score = parseFloat(flag); |
| 191 | + let interpretation = ''; |
| 192 | + if (score > 30) interpretation = 'It is targeted for junior college (JC) level comprehension and above.'; |
| 193 | + else interpretation = 'It is targeted for university graduate level comprehension and above.'; |
| 194 | + |
| 195 | + output['oobee-grading-text-contents'] = [{ |
| 196 | + category: 'needsReview', |
| 197 | + rule: 'oobee-grading-text-contents', |
| 198 | + description: 'Page content must use clear, plain language', |
| 199 | + items: [{ |
| 200 | + html: '<html lang="en">...</html>', |
| 201 | + message: `Text content is potentially difficult to read. It scored ${flag} out of 50 on the Flesch-Kincaid Readability Test. ${interpretation}`, |
| 202 | + }], |
| 203 | + }]; |
| 204 | + } else { |
| 205 | + console.log(" Score filtered out (<=0 or >50). No violation triggered."); |
| 206 | + } |
| 207 | + await page.close(); |
| 208 | + } catch (e) { console.error("readability error:", e.message); } |
| 209 | + |
| 210 | + await browser.close(); |
| 211 | + |
| 212 | + // Print results |
| 213 | + console.log("\n" + JSON.stringify(output, null, 2)); |
| 214 | +})(); |
0 commit comments