@casoon/auditmysite
Version:
A comprehensive command-line tool for automated accessibility, security, performance, and SEO testing using Playwright and pa11y, based on sitemap URLs
382 lines (379 loc) • 18.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutputGenerator = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const json_generator_1 = require("./json-generator");
const csv_generator_1 = require("./csv-generator");
class OutputGenerator {
/**
* Generiert eine KI-freundliche Output-Datei
*/
async generateOutput(summary, options) {
const timestamp = new Date().toISOString();
const outputData = this.prepareOutputData(summary, timestamp, options);
let content;
let filename;
switch (options.format) {
case 'json':
content = this.generateJSON(outputData, options);
filename = options.outputFile || `accessibility-report-${Date.now()}.json`;
break;
case 'csv':
content = this.generateCSV(outputData, options);
filename = options.outputFile || `accessibility-report-${Date.now()}.csv`;
break;
case 'markdown':
content = this.generateMarkdown(outputData, options);
filename = options.outputFile || `accessibility-report-${Date.now()}.md`;
break;
case 'html':
content = this.generateHTML(outputData, options);
filename = options.outputFile || `accessibility-report-${Date.now()}.html`;
break;
default:
throw new Error(`Unsupported format: ${options.format}`);
}
// Datei schreiben
const outputPath = path.resolve(filename);
fs.writeFileSync(outputPath, content, 'utf8');
return outputPath;
}
prepareOutputData(summary, timestamp, options) {
return {
metadata: {
timestamp,
tool: '@casoon/accessibility-test-cli',
version: '1.0.1',
totalPages: summary.totalPages,
testedPages: summary.testedPages,
passedPages: summary.passedPages,
failedPages: summary.failedPages,
totalErrors: summary.totalErrors,
totalWarnings: summary.totalWarnings,
totalDuration: summary.totalDuration,
successRate: summary.testedPages > 0 ? (summary.passedPages / summary.testedPages * 100).toFixed(2) + '%' : '0%'
},
summary: {
overall: summary.passedPages === summary.testedPages ? 'PASSED' : 'FAILED',
score: summary.testedPages > 0 ? Math.round((summary.passedPages / summary.testedPages) * 100) : 0,
criticalIssues: summary.totalErrors,
warnings: summary.totalWarnings,
averageLoadTime: summary.testedPages > 0 ? Math.round(summary.totalDuration / summary.testedPages) : 0
},
pages: options.summaryOnly ? [] : summary.results.map(result => ({
url: result.url,
title: result.title,
status: result.passed ? 'PASSED' : 'FAILED',
loadTime: result.duration,
errors: result.errors.length,
warnings: result.warnings.length,
issues: options.includeDetails ? {
imagesWithoutAlt: result.imagesWithoutAlt,
buttonsWithoutLabel: result.buttonsWithoutLabel,
headingsCount: result.headingsCount,
pa11yIssues: options.includePa11yIssues ? result.pa11yIssues : undefined,
pa11yScore: result.pa11yScore,
performanceMetrics: result.performanceMetrics,
keyboardNavigation: result.keyboardNavigation,
colorContrastIssues: result.colorContrastIssues,
focusManagementIssues: result.focusManagementIssues,
screenshots: result.screenshots
} : undefined,
errorDetails: options.includeDetails ? result.errors : undefined,
warningDetails: options.includeDetails ? result.warnings : undefined
})),
recommendations: this.generateRecommendations(summary)
};
}
generateJSON(data, options) {
const jsonGenerator = new json_generator_1.JsonGenerator();
return jsonGenerator.generateJson(data, {
includeDetails: options.includeDetails,
pretty: true
});
}
generateCSV(data, options) {
const csvGenerator = new csv_generator_1.CsvGenerator();
return csvGenerator.generateCsv(data, {
includeDetails: options.includeDetails,
includeHeaders: true
});
}
generateMarkdown(data, options) {
const lines = [];
// Header
lines.push('# Accessibility Test Report');
lines.push(`Generated: ${data.metadata.timestamp}`);
lines.push('');
// Summary
lines.push('## Summary');
lines.push(`- **Overall Status**: ${data.summary.overall}`);
lines.push(`- **Success Rate**: ${data.metadata.successRate}`);
lines.push(`- **Score**: ${data.summary.score}/100`);
lines.push(`- **Critical Issues**: ${data.summary.criticalIssues}`);
lines.push(`- **Warnings**: ${data.summary.warnings}`);
lines.push(`- **Average Load Time**: ${data.summary.averageLoadTime}ms`);
// Pa11y Score if available
const pagesWithPa11yScore = data.pages.filter((page) => page.issues?.pa11yScore && page.issues.pa11yScore !== 'N/A');
if (pagesWithPa11yScore.length > 0) {
const avgPa11yScore = pagesWithPa11yScore.reduce((sum, page) => sum + page.issues.pa11yScore, 0) / pagesWithPa11yScore.length;
lines.push(`- **Average Pa11y Score**: ${Math.round(avgPa11yScore)}/100`);
}
// Performance Metrics if available
const pagesWithPerformance = data.pages.filter((page) => page.issues?.performanceMetrics);
if (pagesWithPerformance.length > 0) {
const avgLoadTime = pagesWithPerformance.reduce((sum, page) => sum + page.issues.performanceMetrics.loadTime, 0) / pagesWithPerformance.length;
lines.push(`- **Average Load Time**: ${Math.round(avgLoadTime)}ms`);
}
// Keyboard Navigation if available
const pagesWithKeyboard = data.pages.filter((page) => page.issues?.keyboardNavigation?.length > 0);
if (pagesWithKeyboard.length > 0) {
lines.push(`- **Pages with Keyboard Navigation**: ${pagesWithKeyboard.length}`);
}
// Color Contrast Issues if available
const totalColorIssues = data.pages.reduce((sum, page) => sum + (page.issues?.colorContrastIssues?.length || 0), 0);
if (totalColorIssues > 0) {
lines.push(`- **Total Color Contrast Issues**: ${totalColorIssues}`);
}
// Focus Management Issues if available
const totalFocusIssues = data.pages.reduce((sum, page) => sum + (page.issues?.focusManagementIssues?.length || 0), 0);
if (totalFocusIssues > 0) {
lines.push(`- **Total Focus Management Issues**: ${totalFocusIssues}`);
}
lines.push('');
// Pages
if (data.pages.length > 0) {
lines.push('## Page Results');
lines.push('');
lines.push('| URL | Title | Status | Load Time | Errors | Warnings | Pa11y Score | Performance | Keyboard | Contrast | Focus |');
lines.push('|-----|-------|--------|-----------|--------|----------|-------------|-------------|----------|----------|-------|');
data.pages.forEach((page) => {
const pa11yScore = page.issues?.pa11yScore || 'N/A';
const performanceScore = page.issues?.performanceMetrics?.loadTime ?
Math.max(0, 100 - Math.round(page.issues.performanceMetrics.loadTime / 100)) : 'N/A';
const keyboardCount = page.issues?.keyboardNavigation?.length || 0;
const contrastCount = page.issues?.colorContrastIssues?.length || 0;
const focusCount = page.issues?.focusManagementIssues?.length || 0;
lines.push(`| ${page.url} | ${page.title} | ${page.status} | ${page.loadTime}ms | ${page.errors} | ${page.warnings} | ${pa11yScore} | ${performanceScore} | ${keyboardCount} | ${contrastCount} | ${focusCount} |`);
});
lines.push('');
}
// Detailed Pa11y Issues
const pagesWithPa11yIssues = data.pages.filter((page) => page.issues?.pa11yIssues && page.issues.pa11yIssues.length > 0);
if (pagesWithPa11yIssues.length > 0) {
lines.push('## Detailed Pa11y Issues');
lines.push('');
pagesWithPa11yIssues.forEach((page) => {
lines.push(`### ${page.title} (${page.url})`);
lines.push('');
page.issues.pa11yIssues.forEach((issue) => {
lines.push(`#### ${issue.code}`);
lines.push(`- **Type**: ${issue.type}`);
lines.push(`- **Impact**: ${issue.impact || 'Unknown'}`);
lines.push(`- **Message**: ${issue.message}`);
if (issue.selector) {
lines.push(`- **Element**: \`${issue.selector}\``);
}
if (issue.context) {
lines.push(`- **Context**: \`${issue.context}\``);
}
if (issue.help) {
lines.push(`- **Help**: ${issue.help}`);
}
if (issue.helpUrl) {
lines.push(`- **More Info**: ${issue.helpUrl}`);
}
lines.push('');
});
});
}
// 🆕 Detailed Playwright Test Results
const pagesWithPlaywrightTests = data.pages.filter((page) => page.issues?.keyboardNavigation?.length > 0 ||
page.issues?.colorContrastIssues?.length > 0 ||
page.issues?.focusManagementIssues?.length > 0 ||
page.issues?.performanceMetrics ||
page.issues?.screenshots);
if (pagesWithPlaywrightTests.length > 0) {
lines.push('## Detailed Playwright Test Results');
lines.push('');
pagesWithPlaywrightTests.forEach((page) => {
lines.push(`### ${page.title} (${page.url})`);
lines.push('');
// Performance Metrics
if (page.issues?.performanceMetrics) {
lines.push('#### Performance Metrics');
const metrics = page.issues.performanceMetrics;
lines.push(`- **Load Time**: ${Math.round(metrics.loadTime)}ms`);
lines.push(`- **DOM Content Loaded**: ${Math.round(metrics.domContentLoaded)}ms`);
lines.push(`- **First Paint**: ${Math.round(metrics.firstPaint)}ms`);
lines.push(`- **First Contentful Paint**: ${Math.round(metrics.firstContentfulPaint)}ms`);
lines.push(`- **Largest Contentful Paint**: ${Math.round(metrics.largestContentfulPaint)}ms`);
lines.push('');
}
// Keyboard Navigation
if (page.issues?.keyboardNavigation?.length > 0) {
lines.push('#### Keyboard Navigation Elements');
page.issues.keyboardNavigation.forEach((element, index) => {
lines.push(`${index + 1}. ${element}`);
});
lines.push('');
}
// Color Contrast Issues
if (page.issues?.colorContrastIssues?.length > 0) {
lines.push('#### Color Contrast Issues');
page.issues.colorContrastIssues.forEach((issue, index) => {
lines.push(`${index + 1}. ${issue}`);
});
lines.push('');
}
// Focus Management Issues
if (page.issues?.focusManagementIssues?.length > 0) {
lines.push('#### Focus Management Issues');
page.issues.focusManagementIssues.forEach((issue, index) => {
lines.push(`${index + 1}. ${issue}`);
});
lines.push('');
}
// Screenshots
if (page.issues?.screenshots) {
lines.push('#### Screenshots');
if (page.issues.screenshots.desktop) {
lines.push(`- **Desktop**: ${page.issues.screenshots.desktop}`);
}
if (page.issues.screenshots.mobile) {
lines.push(`- **Mobile**: ${page.issues.screenshots.mobile}`);
}
lines.push('');
}
});
}
// Recommendations
if (data.recommendations.length > 0) {
lines.push('## Recommendations');
lines.push('');
data.recommendations.forEach((rec) => {
lines.push(`- ${rec}`);
});
lines.push('');
}
return lines.join('\n');
}
generateHTML(data, options) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accessibility Test Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.summary { background: #f5f5f5; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
.passed { color: green; }
.failed { color: red; }
.warning { color: orange; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>Accessibility Test Report</h1>
<p>Generated: ${data.metadata.timestamp}</p>
<div class="summary">
<h2>Summary</h2>
<p><strong>Overall Status:</strong> <span class="${data.summary.overall === 'PASSED' ? 'passed' : 'failed'}">${data.summary.overall}</span></p>
<p><strong>Success Rate:</strong> ${data.metadata.successRate}</p>
<p><strong>Score:</strong> ${data.summary.score}/100</p>
<p><strong>Critical Issues:</strong> ${data.summary.criticalIssues}</p>
<p><strong>Warnings:</strong> ${data.summary.warnings}</p>
</div>
${data.pages.length > 0 ? `
<h2>Page Results</h2>
<table>
<tr>
<th>URL</th>
<th>Title</th>
<th>Status</th>
<th>Load Time</th>
<th>Errors</th>
<th>Warnings</th>
</tr>
${data.pages.map((page) => `
<tr>
<td>${page.url}</td>
<td>${page.title}</td>
<td class="${page.status === 'PASSED' ? 'passed' : 'failed'}">${page.status}</td>
<td>${page.loadTime}ms</td>
<td>${page.errors}</td>
<td>${page.warnings}</td>
</tr>
`).join('')}
</table>
` : ''}
${data.recommendations.length > 0 ? `
<h2>Recommendations</h2>
<ul>
${data.recommendations.map((rec) => `<li>${rec}</li>`).join('')}
</ul>
` : ''}
</body>
</html>`;
}
generateRecommendations(summary) {
const recommendations = [];
if (summary.totalErrors > 0) {
recommendations.push(`Fix ${summary.totalErrors} critical accessibility errors to meet WCAG standards.`);
}
if (summary.totalWarnings > 0) {
recommendations.push(`Address ${summary.totalWarnings} accessibility warnings to improve user experience.`);
}
if (summary.failedPages > 0) {
recommendations.push(`Review and fix issues on ${summary.failedPages} pages that failed accessibility tests.`);
}
const successRate = summary.testedPages > 0 ? (summary.passedPages / summary.testedPages) * 100 : 0;
if (successRate < 80) {
recommendations.push(`Improve overall accessibility score (currently ${successRate.toFixed(1)}%) to reach at least 80%.`);
}
if (summary.totalDuration > 30000) {
recommendations.push(`Optimize page load times - average load time is ${Math.round(summary.totalDuration / summary.testedPages)}ms.`);
}
return recommendations;
}
}
exports.OutputGenerator = OutputGenerator;
//# sourceMappingURL=output-generator.js.map