@casoon/auditmysite
Version:
Professional website analysis suite with robust accessibility testing, Core Web Vitals performance monitoring, SEO analysis, and content optimization insights. Features isolated browser contexts, retry mechanisms, and comprehensive API endpoints for profe
288 lines (279 loc) âĸ 10.7 kB
JavaScript
;
/**
* đ Enhanced Accessibility Summary Generator
*
* Generates detailed, actionable summaries of accessibility audits
* with clear metrics, categorization, and severity levels.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateAccessibilitySummary = generateAccessibilitySummary;
exports.formatSummaryAsMarkdown = formatSummaryAsMarkdown;
/**
* Generate enhanced accessibility summary
*/
function generateAccessibilitySummary(pages) {
const totalPages = pages.length;
const testedPages = pages.filter(p => p.accessibility).length;
// Collect all issues
const allIssues = [];
const issuesByCode = new Map();
const issuesByCategory = new Map();
let totalErrors = 0;
let totalWarnings = 0;
let totalNotices = 0;
for (const page of pages) {
if (!page.accessibility)
continue;
const issues = page.accessibility.pa11yIssues || [];
allIssues.push(...issues);
totalErrors += page.accessibility.errors?.length || 0;
totalWarnings += page.accessibility.warnings?.length || 0;
totalNotices += page.accessibility.notices?.length || 0;
// Categorize issues
for (const issue of issues) {
const code = issue.code || 'unknown';
issuesByCode.set(code, (issuesByCode.get(code) || 0) + 1);
const category = categorizeIssue(issue.code);
issuesByCategory.set(category, (issuesByCategory.get(category) || 0) + 1);
}
}
// Calculate severity distribution
const bySeverity = {
critical: allIssues.filter(i => i.impact === 'critical').length,
serious: allIssues.filter(i => i.impact === 'serious').length,
moderate: allIssues.filter(i => i.impact === 'moderate').length,
minor: allIssues.filter(i => i.impact === 'minor' || !i.impact).length
};
// Find top issues
const topIssues = Array.from(issuesByCode.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([code, count]) => ({
code,
count,
severity: getSeverityForCode(code),
description: getDescriptionForCode(code),
impact: getImpactForCode(code)
}));
// Calculate metrics
const pagesWithErrors = pages.filter(p => p.accessibility && p.accessibility.errors && p.accessibility.errors.length > 0).length;
const pagesWithWarnings = pages.filter(p => p.accessibility && p.accessibility.warnings && p.accessibility.warnings.length > 0).length;
const averageIssuesPerPage = totalPages > 0 ? allIssues.length / totalPages : 0;
const successRate = totalPages > 0 ? ((totalPages - pagesWithErrors) / totalPages) * 100 : 0;
// Calculate overall score (improved formula)
const overallScore = calculateOverallScore(totalErrors, totalWarnings, totalNotices, totalPages);
const overallGrade = getGradeFromScore(overallScore);
const wcagLevel = determineWCAGLevel(totalErrors, totalWarnings, averageIssuesPerPage);
// Generate recommendations
const recommendations = generateRecommendations(topIssues, bySeverity, issuesByCategory);
return {
overview: {
totalPages,
testedPages,
overallScore,
overallGrade,
wcagLevel
},
issues: {
total: allIssues.length,
byType: {
errors: totalErrors,
warnings: totalWarnings,
notices: totalNotices
},
bySeverity,
byCategory: Object.fromEntries(issuesByCategory)
},
topIssues,
recommendations,
metrics: {
averageIssuesPerPage: Math.round(averageIssuesPerPage * 10) / 10,
pagesWithErrors,
pagesWithWarnings,
successRate: Math.round(successRate * 10) / 10
}
};
}
/**
* Improved overall score calculation
*/
function calculateOverallScore(errors, warnings, notices, pages) {
if (pages === 0)
return 0;
// Start with perfect score
let score = 100;
// Deduct points per page (normalized)
const errorsPerPage = errors / pages;
const warningsPerPage = warnings / pages;
const noticesPerPage = notices / pages;
// Progressive penalties (diminishing returns for many issues)
score -= Math.min(50, errorsPerPage * 5); // Max 50 points for errors
score -= Math.min(30, warningsPerPage * 2); // Max 30 points for warnings
score -= Math.min(10, noticesPerPage * 1); // Max 10 points for notices
// Additional penalty for widespread issues
if (errorsPerPage > 20)
score -= 10;
if (errorsPerPage > 50)
score -= 10;
return Math.max(0, Math.round(score));
}
/**
* Determine WCAG compliance level
*/
function determineWCAGLevel(errors, warnings, avgIssuesPerPage) {
if (errors === 0 && warnings === 0)
return 'AAA';
if (errors === 0 && avgIssuesPerPage < 5)
return 'AA';
if (avgIssuesPerPage < 10)
return 'A';
if (avgIssuesPerPage < 20)
return 'Partial Compliance';
return 'Non-Compliant';
}
/**
* Get grade from score
*/
function getGradeFromScore(score) {
if (score >= 90)
return 'A';
if (score >= 80)
return 'B';
if (score >= 70)
return 'C';
if (score >= 60)
return 'D';
return 'F';
}
/**
* Categorize issue by code
*/
function categorizeIssue(code) {
const categories = {
'color-contrast': 'Visual Design',
'button-name': 'Interactive Elements',
'link-name': 'Navigation',
'image-alt': 'Images & Media',
'label': 'Forms',
'aria-': 'ARIA & Semantics',
'heading-': 'Document Structure',
'landmark': 'Page Regions',
'html-': 'HTML Validation'
};
for (const [prefix, category] of Object.entries(categories)) {
if (code.includes(prefix))
return category;
}
return 'Other';
}
/**
* Get severity for issue code
*/
function getSeverityForCode(code) {
const criticalCodes = ['color-contrast', 'button-name', 'link-name'];
const seriousCodes = ['image-alt', 'label', 'aria-required'];
if (criticalCodes.some(c => code.includes(c)))
return 'Critical';
if (seriousCodes.some(c => code.includes(c)))
return 'Serious';
return 'Moderate';
}
/**
* Get human-readable description for code
*/
function getDescriptionForCode(code) {
const descriptions = {
'color-contrast': 'Text does not have sufficient color contrast',
'button-name': 'Buttons are missing accessible names',
'link-name': 'Links are missing accessible names',
'image-alt': 'Images are missing alternative text',
'label': 'Form inputs are missing labels',
'aria-required-attr': 'ARIA attributes are missing or incorrect'
};
return descriptions[code] || code;
}
/**
* Get impact description for code
*/
function getImpactForCode(code) {
const impacts = {
'color-contrast': 'Users with low vision cannot read text',
'button-name': 'Screen reader users cannot identify button purpose',
'link-name': 'Screen reader users cannot identify link destination',
'image-alt': 'Screen reader users miss important image content',
'label': 'Form fields are unusable for screen reader users'
};
return impacts[code] || 'Affects accessibility for some users';
}
/**
* Generate actionable recommendations
*/
function generateRecommendations(topIssues, bySeverity, byCategory) {
const recommendations = [];
// Priority 1: Critical issues
if (bySeverity.critical > 0) {
recommendations.push(`đ´ Address ${bySeverity.critical} critical issues immediately - these prevent users from accessing content`);
}
// Priority 2: Most common issues
if (topIssues.length > 0) {
const topIssue = topIssues[0];
if (topIssue.code === 'color-contrast') {
recommendations.push(`đ¨ Fix color contrast issues (${topIssue.count} instances) - use darker colors on light backgrounds (e.g., #1F2937 instead of #374151)`);
}
else {
recommendations.push(`đ§ Fix "${topIssue.description}" (${topIssue.count} instances) - this is your most common issue`);
}
}
// Priority 3: Category-specific
const topCategory = Array.from(byCategory.entries()).sort((a, b) => b[1] - a[1])[0];
if (topCategory && topCategory[1] > 10) {
recommendations.push(`đ Focus on "${topCategory[0]}" category (${topCategory[1]} issues) - consider a comprehensive review`);
}
// Priority 4: Serious issues
if (bySeverity.serious > 5) {
recommendations.push(`â ī¸ ${bySeverity.serious} serious issues need attention - these significantly impact user experience`);
}
// Priority 5: General improvements
if (recommendations.length < 3) {
recommendations.push('â
Consider automated accessibility testing in your CI/CD pipeline', 'đ Review WCAG 2.1 guidelines for comprehensive compliance');
}
return recommendations.slice(0, 5); // Max 5 recommendations
}
/**
* Format summary as markdown
*/
function formatSummaryAsMarkdown(summary) {
return `
# đ Accessibility Audit Summary
## Overview
- **Pages Tested:** ${summary.overview.testedPages} / ${summary.overview.totalPages}
- **Overall Score:** ${summary.overview.overallScore}/100 (Grade: ${summary.overview.overallGrade})
- **WCAG Level:** ${summary.overview.wcagLevel}
- **Success Rate:** ${summary.metrics.successRate}%
## Issues Found
### By Type
- â **Errors:** ${summary.issues.byType.errors}
- â ī¸ **Warnings:** ${summary.issues.byType.warnings}
- âšī¸ **Notices:** ${summary.issues.byType.notices}
### By Severity
- đ´ **Critical:** ${summary.issues.bySeverity.critical}
- đ **Serious:** ${summary.issues.bySeverity.serious}
- đĄ **Moderate:** ${summary.issues.bySeverity.moderate}
- đĸ **Minor:** ${summary.issues.bySeverity.minor}
## Top Issues
${summary.topIssues.map((issue, i) => `
${i + 1}. **${issue.description}**
- Code: \`${issue.code}\`
- Count: ${issue.count} instances
- Severity: ${issue.severity}
- Impact: ${issue.impact}
`).join('')}
## Recommendations
${summary.recommendations.map((rec, i) => `${i + 1}. ${rec}`).join('\n')}
## Metrics
- Average Issues per Page: ${summary.metrics.averageIssuesPerPage}
- Pages with Errors: ${summary.metrics.pagesWithErrors} / ${summary.overview.totalPages}
- Pages with Warnings: ${summary.metrics.pagesWithWarnings} / ${summary.overview.totalPages}
`;
}
//# sourceMappingURL=accessibility-summary.js.map