Spaces:
Sleeping
Sleeping
| const DEFAULT_REPORTING = { | |
| max_html_issues: 50, | |
| moderate_issue_threshold_percent: 5, | |
| include_key_page_issues: true, | |
| }; | |
| const SEVERITY_RANK = { Critical: 0, High: 1, Medium: 2, Low: 3 }; | |
| export function normalizeAccessibilityReporting(reporting = {}) { | |
| return { | |
| ...DEFAULT_REPORTING, | |
| ...reporting, | |
| max_html_issues: Math.max(0, Number(reporting.max_html_issues ?? DEFAULT_REPORTING.max_html_issues) || 0), | |
| moderate_issue_threshold_percent: Math.max( | |
| 0, | |
| Number(reporting.moderate_issue_threshold_percent ?? DEFAULT_REPORTING.moderate_issue_threshold_percent) || 0 | |
| ), | |
| include_key_page_issues: reporting.include_key_page_issues ?? DEFAULT_REPORTING.include_key_page_issues, | |
| }; | |
| } | |
| export function prioritizeAccessibilityBugs(summary, bugs, { keyPages = [], reporting = {} } = {}) { | |
| const cfg = normalizeAccessibilityReporting(reporting); | |
| const keyPageSet = cfg.include_key_page_issues ? new Set(keyPages) : new Set(); | |
| const totalPages = summary.pagesScanned || bugs[0]?.frequency.total_pages_scanned || 0; | |
| const threshold = cfg.moderate_issue_threshold_percent; | |
| const decorated = bugs.map((bug) => { | |
| const pagesAffected = bug.frequency?.pages_affected ?? 0; | |
| const prevalencePercent = totalPages > 0 ? (100 * pagesAffected) / totalPages : 0; | |
| const keyPageHit = keyPageSet.size > 0 && bugHasKeyPageHit(summary, bug, keyPageSet); | |
| const tier = priorityTier(bug, prevalencePercent, keyPageHit, threshold, cfg.include_key_page_issues); | |
| return { | |
| ...bug, | |
| priority_tier: tier, | |
| priority_key_page: keyPageHit, | |
| priority_prevalence_percent: Math.round(prevalencePercent * 100) / 100, | |
| }; | |
| }); | |
| decorated.sort((a, b) => | |
| a.priority_tier - b.priority_tier || | |
| (b.frequency?.pages_affected ?? 0) - (a.frequency?.pages_affected ?? 0) || | |
| (SEVERITY_RANK[a.severity] ?? 99) - (SEVERITY_RANK[b.severity] ?? 99) || | |
| (b.frequency?.instances ?? 0) - (a.frequency?.instances ?? 0) || | |
| String(a.summary ?? '').localeCompare(String(b.summary ?? '')) | |
| ); | |
| const visible = []; | |
| for (const bug of decorated) { | |
| if (bug.priority_tier <= 1) visible.push(bug); | |
| } | |
| for (const bug of decorated) { | |
| if (bug.priority_tier <= 1) continue; | |
| if (cfg.max_html_issues > 0 && visible.length >= cfg.max_html_issues) break; | |
| visible.push(bug); | |
| } | |
| const visibleSet = new Set(visible.map((b) => b.instance_id)); | |
| return { | |
| bugs: decorated.map((bug) => ({ | |
| ...bug, | |
| default_visible: visibleSet.has(bug.instance_id), | |
| })), | |
| visibleCount: visibleSet.size, | |
| totalCount: bugs.length, | |
| reporting: cfg, | |
| }; | |
| } | |
| function priorityTier(bug, prevalencePercent, keyPageHit, thresholdPercent, includeKeyPages) { | |
| if (bug.severity === 'Critical' || bug.severity === 'High') return 0; | |
| if (includeKeyPages && keyPageHit && prevalencePercent > thresholdPercent) return 1; | |
| if (isWcagAorAa(bug.wcag_category)) return 2; | |
| if (bug.wcag_category === 'Best Practice') return 3; | |
| if (bug.wcag_category === 'WCAG 2.x AAA') return 4; | |
| return 5; | |
| } | |
| function isWcagAorAa(category) { | |
| return /^WCAG \d\.\d [A]{1,2}$/.test(category ?? ''); | |
| } | |
| function bugHasKeyPageHit(summary, bug, keyPageSet) { | |
| const rule = ruleForBug(summary, bug); | |
| const affected = rule?.affectedPages?.map((p) => p.url).filter(Boolean) ?? bug.affected_pages ?? []; | |
| return affected.some((url) => keyPageSet.has(url)); | |
| } | |
| function ruleForBug(summary, bug) { | |
| const rules = { | |
| 'axe-core': summary.axe?.rules, | |
| alfa: summary.alfa?.rules, | |
| 'deprecated-html': summary.deprecatedHtml?.rules, | |
| }[bug.engine_key]; | |
| return rules?.[bug.rule_id] ?? null; | |
| } |