UNPKG

arda-site-scan

Version:

A standalone CLI tool for comprehensive website analysis including screenshots, SEO, and accessibility testing using Playwright

211 lines 10.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AccessibilityTester = void 0; const chalk_1 = __importDefault(require("chalk")); const session_manager_js_1 = require("../utils/session-manager.js"); const test_output_handler_js_1 = require("../utils/test-output-handler.js"); class AccessibilityTester { sessionManager; constructor() { this.sessionManager = new session_manager_js_1.SessionManager(); } async runAccessibilityScan(page, pageUrl, sessionId) { const pageName = test_output_handler_js_1.StandardTestOutputHandler.getPageNameFromUrl(pageUrl); // Create initial test result using standardized system const testResult = this.sessionManager.createStandardTestResult('accessibility', 'pending'); try { console.log(chalk_1.default.gray(` ♿ Running accessibility scan...`)); // Inject axe-core library await this.injectAxeCore(page); // Run accessibility scan const results = await this.runAxeScan(page); // Generate report const report = this.generateAccessibilityReport(results, pageUrl); // Prepare output context for the accessibility scan const context = { url: pageUrl, pageName }; // Save using the standardized output system const saveResult = await this.sessionManager.saveTestOutput(report, sessionId, 'accessibility', context); if (saveResult.success) { testResult.status = 'success'; testResult.outputPath = saveResult.outputPath; } else { throw new Error(saveResult.error || 'Failed to save accessibility report'); } testResult.endTime = new Date(); const violationCount = results.violations.length; if (violationCount > 0) { console.log(chalk_1.default.yellow(` ⚠️ Accessibility scan completed with ${violationCount} violation(s)`)); } else { console.log(chalk_1.default.green(` ✅ Accessibility scan completed with no violations`)); } } catch (error) { testResult.status = 'failed'; testResult.error = error instanceof Error ? error.message : String(error); testResult.endTime = new Date(); console.log(chalk_1.default.red(` ❌ Accessibility scan failed: ${testResult.error}`)); } return testResult; } async injectAxeCore(page) { // Inject axe-core from CDN await page.addScriptTag({ url: 'https://unpkg.com/axe-core@4.8.2/axe.min.js' }); // Wait for axe to be available await page.waitForFunction(() => window.axe !== undefined); } async runAxeScan(page) { return await page.evaluate(async () => { // Run axe scan with default comprehensive ruleset const results = await window.axe.run(document); return { violations: results.violations, passes: results.passes, incomplete: results.incomplete, url: window.location.href, timestamp: new Date().toISOString() }; }); } generateAccessibilityReport(results, pageUrl) { let report = `# Accessibility Scan Report\n\n`; report += `**URL:** ${pageUrl}\n`; report += `**Scan Date:** ${results.timestamp}\n`; report += `**Tool:** axe-core\n\n`; // Summary report += `## Summary\n\n`; report += `- **Violations:** ${results.violations.length}\n`; report += `- **Passes:** ${results.passes.length}\n`; report += `- **Incomplete:** ${results.incomplete.length}\n\n`; // Violations by severity const violationsBySeverity = this.groupBySeverity(results.violations); report += `### Violations by Severity\n`; report += `- **Critical:** ${violationsBySeverity.critical?.length || 0}\n`; report += `- **Serious:** ${violationsBySeverity.serious?.length || 0}\n`; report += `- **Moderate:** ${violationsBySeverity.moderate?.length || 0}\n`; report += `- **Minor:** ${violationsBySeverity.minor?.length || 0}\n\n`; // Detailed violations if (results.violations.length > 0) { report += `## Violations\n\n`; ['critical', 'serious', 'moderate', 'minor'].forEach(severity => { const severityViolations = results.violations.filter(v => v.impact === severity); if (severityViolations.length > 0) { report += `### ${severity.charAt(0).toUpperCase() + severity.slice(1)} Issues\n\n`; severityViolations.forEach((violation, index) => { report += `#### ${index + 1}. ${violation.id}\n`; report += `**Impact:** ${violation.impact}\n`; report += `**Description:** ${violation.description}\n`; report += `**Help:** ${violation.help}\n`; report += `**Learn More:** [${violation.helpUrl}](${violation.helpUrl})\n`; report += `**Elements Affected:** ${violation.nodes.length}\n\n`; if (violation.nodes.length > 0) { report += `**Affected Elements:**\n`; violation.nodes.slice(0, 3).forEach((node, nodeIndex) => { report += `${nodeIndex + 1}. Target: \`${node.target.join(', ')}\`\n`; report += ` HTML: \`${node.html.substring(0, 100)}${node.html.length > 100 ? '...' : ''}\`\n`; }); if (violation.nodes.length > 3) { report += `... and ${violation.nodes.length - 3} more elements\n`; } report += '\n'; } }); } }); } else { report += `## ✅ No Violations Found\n\n`; report += `Great! This page passed all accessibility tests.\n\n`; } // Incomplete tests if (results.incomplete.length > 0) { report += `## Incomplete Tests\n\n`; report += `The following tests could not be completed automatically and may require manual review:\n\n`; results.incomplete.forEach((item, index) => { report += `### ${index + 1}. ${item.id}\n`; report += `**Description:** ${item.description}\n`; report += `**Help:** ${item.help}\n`; report += `**Elements:** ${item.nodes.length}\n\n`; }); } // Recommendations report += `## Recommendations\n\n`; if (results.violations.length === 0) { report += `🎉 **Excellent!** This page has no accessibility violations.\n\n`; report += `**Maintain Accessibility:**\n`; report += `- Continue testing new content and features\n`; report += `- Consider user testing with assistive technologies\n`; report += `- Keep up with WCAG guidelines updates\n\n`; } else { report += `**Priority Actions:**\n`; if (violationsBySeverity.critical?.length > 0) { report += `🚨 **Critical:** Address ${violationsBySeverity.critical.length} critical issue(s) immediately\n`; } if (violationsBySeverity.serious?.length > 0) { report += `⚠️ **Serious:** Fix ${violationsBySeverity.serious.length} serious issue(s) soon\n`; } if (violationsBySeverity.moderate?.length > 0) { report += `📋 **Moderate:** Plan to resolve ${violationsBySeverity.moderate.length} moderate issue(s)\n`; } if (violationsBySeverity.minor?.length > 0) { report += `📝 **Minor:** Consider fixing ${violationsBySeverity.minor.length} minor issue(s)\n`; } report += '\n**General Recommendations:**\n'; report += '- Focus on critical and serious issues first\n'; report += '- Test with screen readers and keyboard navigation\n'; report += '- Validate fixes with automated and manual testing\n'; report += '- Consider consulting with accessibility experts\n\n'; } // WCAG Compliance report += `## WCAG 2.1 Compliance\n\n`; const complianceLevel = this.calculateWCAGCompliance(results.violations); report += `**Estimated Compliance Level:** ${complianceLevel}\n\n`; if (complianceLevel === 'Non-compliant') { report += `❌ This page does not meet WCAG 2.1 standards due to critical or serious accessibility violations.\n`; } else if (complianceLevel === 'AA') { report += `✅ This page appears to meet WCAG 2.1 AA standards based on automated testing.\n`; } else { report += `⚠️ This page may have minor accessibility issues that should be addressed.\n`; } report += `\n**Note:** Automated testing can only detect ~30% of accessibility issues. Manual testing is recommended.\n`; return report; } groupBySeverity(violations) { return violations.reduce((groups, violation) => { const severity = violation.impact; if (!groups[severity]) { groups[severity] = []; } groups[severity].push(violation); return groups; }, {}); } calculateWCAGCompliance(violations) { const hasCritical = violations.some(v => v.impact === 'critical'); const hasSerious = violations.some(v => v.impact === 'serious'); const hasModerate = violations.some(v => v.impact === 'moderate'); if (hasCritical || hasSerious) { return 'Non-compliant'; } else if (hasModerate) { return 'Partial AA'; } else { return 'AA'; } } } exports.AccessibilityTester = AccessibilityTester; //# sourceMappingURL=accessibility-tester.js.map