@casoon/auditmysite
Version:
A comprehensive command-line tool for automated accessibility, security, performance, and SEO testing using Playwright and pa11y, based on sitemap URLs
185 lines (157 loc) • 6.06 kB
text/typescript
import { Command } from "commander";
import chalk from "chalk";
import ora from "ora";
import { SitemapParser } from "./parsers";
import { AccessibilityChecker } from "./core";
import { OutputGenerator } from "./generators";
import { TestOptions, TestSummary } from "./types";
const program = new Command();
program
.name("a11y-test")
.description(
"CLI-Tool für automatische Accessibility-Tests basierend auf Sitemap",
)
.version("1.0.0");
program
.argument("<sitemap-url>", "URL zur sitemap.xml")
.option(
"-m, --max-pages <number>",
"Maximale Anzahl zu testender Seiten",
"20",
)
.option("-t, --timeout <number>", "Timeout in Millisekunden", "10000")
.option(
"-w, --wait-until <string>",
"Warten bis (domcontentloaded|load|networkidle)",
"domcontentloaded",
)
.option(
"-f, --filter <patterns>",
"Auszuschließende URL-Muster (kommagetrennt)",
"[...slug],[category],/demo/",
)
.option(
"-i, --include <patterns>",
"Einzuschließende URL-Muster (kommagetrennt)",
)
.option("-v, --verbose", "Detaillierte Ausgabe")
.option(
"--standard <standard>",
"Accessibility Standard (WCAG2A|WCAG2AA|WCAG2AAA|Section508)",
"WCAG2AA"
)
.option("--include-details", "Detaillierte Informationen in Output-Datei")
.option("--include-pa11y", "pa11y-Issues in Output-Datei einschließen")
.option("--summary-only", "Nur Zusammenfassung ohne Seiten-Details")
.action(async (sitemapUrl: string, options: any) => {
const spinner = ora("Initialisiere Accessibility-Tests...").start();
try {
// Parser initialisieren
const parser = new SitemapParser();
spinner.text = "Lade Sitemap...";
// Sitemap parsen
const urls = await parser.parseSitemap(sitemapUrl);
spinner.text = `Sitemap geladen: ${urls.length} URLs gefunden`;
// URLs filtern
const filterPatterns = options.filter
? options.filter.split(",")
: ["[...slug]", "[category]", "/demo/"];
const includePatterns = options.include
? options.include.split(",")
: undefined;
const filteredUrls = parser.filterUrls(urls, {
filterPatterns,
includePatterns,
});
spinner.text = `URLs gefiltert: ${filteredUrls.length} URLs zum Testen`;
// URLs zu lokalen URLs konvertieren (falls nötig)
const baseUrl = new URL(sitemapUrl).origin;
const localUrls = parser.convertToLocalUrls(filteredUrls, baseUrl);
// Accessibility-Checker initialisieren
const checker = new AccessibilityChecker();
await checker.initialize();
spinner.text = "Führe Accessibility-Tests aus...";
// Tests ausführen
const testOptions: TestOptions = {
maxPages: parseInt(options.maxPages),
timeout: parseInt(options.timeout),
waitUntil: options.waitUntil,
verbose: options.verbose,
pa11yStandard: options.standard,
};
const results = await checker.testMultiplePages(
localUrls.map((url) => url.loc),
testOptions,
);
// Zusammenfassung erstellen
const summary: TestSummary = {
totalPages: localUrls.length,
testedPages: results.length,
passedPages: results.filter((r) => r.passed).length,
failedPages: results.filter((r) => !r.passed).length,
totalErrors: results.reduce((sum, r) => sum + r.errors.length, 0),
totalWarnings: results.reduce((sum, r) => sum + r.warnings.length, 0),
totalDuration: results.reduce((sum, r) => sum + r.duration, 0),
results,
};
await checker.cleanup();
// Ergebnisse ausgeben
spinner.succeed("Tests abgeschlossen!");
// Output-Datei generieren falls gewünscht
if (options.output && options.output !== 'console') {
spinner.text = 'Generiere Output-Datei...';
const outputGenerator = new OutputGenerator();
const outputOptions = {
format: options.output as 'json' | 'csv' | 'markdown' | 'html',
outputFile: options.outputFile,
includeDetails: options.includeDetails,
includePa11yIssues: options.includePa11y,
summaryOnly: options.summaryOnly
};
try {
const outputPath = await outputGenerator.generateOutput(summary, outputOptions);
spinner.succeed(`Output-Datei erstellt: ${outputPath}`);
} catch (error) {
spinner.warn(`Fehler beim Erstellen der Output-Datei: ${error}`);
}
}
displayResults(summary, options);
} catch (error) {
spinner.fail(`Fehler: ${error}`);
process.exit(1);
}
});
function displayResults(summary: TestSummary, options: any): void {
console.log("\n" + chalk.bold.blue("🎯 Accessibility Test Zusammenfassung"));
console.log(chalk.gray("─".repeat(50)));
console.log(`📄 Gesamtseiten: ${summary.totalPages}`);
console.log(`🧪 Getestete Seiten: ${summary.testedPages}`);
console.log(`✅ Bestanden: ${chalk.green(summary.passedPages)}`);
console.log(`❌ Fehlgeschlagen: ${chalk.red(summary.failedPages)}`);
console.log(`⚠️ Warnungen: ${chalk.yellow(summary.totalWarnings)}`);
console.log(`⏱️ Gesamtdauer: ${summary.totalDuration}ms`);
if (options.verbose) {
console.log("\n" + chalk.bold("📋 Detaillierte Ergebnisse:"));
summary.results.forEach((result) => {
const status = result.passed ? chalk.green("✅") : chalk.red("❌");
console.log(`${status} ${result.url}`);
console.log(` Titel: ${result.title}`);
console.log(` Dauer: ${result.duration}ms`);
if (result.warnings.length > 0) {
result.warnings.forEach((warning) => {
console.log(` ⚠️ ${warning}`);
});
}
if (result.errors.length > 0) {
result.errors.forEach((error) => {
console.log(` ❌ ${error}`);
});
}
});
}
if (summary.failedPages > 0) {
process.exit(1);
}
}
program.parse();