import { promises as fs } from 'fs';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { AccessibilityTestResult, ViolationSummary } from '../types';
export class FileOutputManager {
private outputDir: string;
constructor(outputDir: string = 'outputs') {
this.outputDir = path.resolve(outputDir);
this.ensureOutputDirectory();
}
private async ensureOutputDirectory(): Promise<void> {
try {
await fs.mkdir(this.outputDir, { recursive: true });
} catch (error) {
console.error('Failed to create output directory:', error);
}
}
async saveTestResult(result: AccessibilityTestResult): Promise<string> {
const fileName = `accessibility-test-${result.id}-${Date.now()}.txt`;
const filePath = path.join(this.outputDir, fileName);
const content = this.formatTestResult(result);
try {
await fs.writeFile(filePath, content, 'utf-8');
console.log(`Test result saved to: ${filePath}`);
return filePath;
} catch (error) {
console.error('Failed to save test result:', error);
throw new Error(`Failed to save test result: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
private formatTestResult(result: AccessibilityTestResult): string {
const lines: string[] = [];
lines.push('ACCESSIBILITY TEST REPORT');
lines.push('='.repeat(50));
lines.push('');
lines.push(`Test ID: ${result.id}`);
lines.push(`Timestamp: ${result.timestamp.toISOString()}`);
lines.push(`URL Tested: ${result.url}`);
lines.push(`WCAG Level: ${result.options.wcagLevel || 'AA'}`);
lines.push(`WCAG Version: ${result.options.wcagVersion || '2.1'}`);
lines.push(`Browser: ${result.options.browser || 'chromium'}`);
lines.push('');
if (result.error) {
lines.push('ERROR OCCURRED:');
lines.push('-'.repeat(20));
lines.push(result.error);
lines.push('');
return lines.join('\n');
}
// Summary
lines.push('SUMMARY:');
lines.push('-'.repeat(20));
lines.push(`Violations: ${result.summary.violations}`);
lines.push(`Passes: ${result.summary.passes}`);
lines.push(`Incomplete: ${result.summary.incomplete}`);
lines.push(`Inapplicable: ${result.summary.inapplicable}`);
lines.push('');
// Violations Details
if (result.axeResults.violations.length > 0) {
lines.push('ACCESSIBILITY VIOLATIONS:');
lines.push('='.repeat(30));
lines.push('');
result.axeResults.violations.forEach((violation, index) => {
lines.push(`${index + 1}. ${violation.help}`);
lines.push(` Rule ID: ${violation.id}`);
lines.push(` Impact: ${violation.impact || 'unknown'}`);
lines.push(` Description: ${violation.description}`);
lines.push(` Help URL: ${violation.helpUrl}`);
lines.push(` Tags: ${violation.tags.join(', ')}`);
lines.push('');
lines.push(' Affected Elements:');
violation.nodes.forEach((node, nodeIndex) => {
lines.push(` ${nodeIndex + 1}. Target: ${Array.isArray(node.target) ? node.target.join(', ') : node.target}`);
lines.push(` HTML: ${node.html}`);
if (node.failureSummary) {
lines.push(` Issue: ${node.failureSummary}`);
}
lines.push('');
});
lines.push('-'.repeat(40));
lines.push('');
});
} else {
lines.push('✅ NO ACCESSIBILITY VIOLATIONS FOUND!');
lines.push('');
}
// Passes summary
if (result.axeResults.passes.length > 0) {
lines.push('SUCCESSFUL ACCESSIBILITY CHECKS:');
lines.push('='.repeat(35));
lines.push('');
result.axeResults.passes.forEach((pass, index) => {
lines.push(`${index + 1}. ${pass.help} (${pass.id})`);
});
lines.push('');
}
// Incomplete tests
if (result.axeResults.incomplete.length > 0) {
lines.push('INCOMPLETE ACCESSIBILITY CHECKS:');
lines.push('='.repeat(35));
lines.push('(These require manual verification)');
lines.push('');
result.axeResults.incomplete.forEach((incomplete, index) => {
lines.push(`${index + 1}. ${incomplete.help} (${incomplete.id})`);
lines.push(` Description: ${incomplete.description}`);
lines.push('');
});
}
// Additional info
lines.push('ADDITIONAL INFORMATION:');
lines.push('-'.repeat(25));
lines.push(`Test completed at: ${result.timestamp.toLocaleString()}`);
lines.push(`Total rules evaluated: ${result.axeResults.violations.length + result.axeResults.passes.length + result.axeResults.incomplete.length + result.axeResults.inapplicable.length}`);
if (result.screenshotPath) {
lines.push(`Screenshot saved to: ${result.screenshotPath}`);
}
lines.push('');
lines.push('Generated by Accessibility MCP Server');
lines.push('For more information, visit: https://www.w3.org/WAI/WCAG21/quickref/');
return lines.join('\n');
}
async getTestResult(fileName: string): Promise<string | null> {
const filePath = path.join(this.outputDir, fileName);
try {
const content = await fs.readFile(filePath, 'utf-8');
return content;
} catch (error) {
console.error('Failed to read test result:', error);
return null;
}
}
async listTestResults(): Promise<string[]> {
try {
const files = await fs.readdir(this.outputDir);
return files.filter(file => file.startsWith('accessibility-test-') && file.endsWith('.txt'));
} catch (error) {
console.error('Failed to list test results:', error);
return [];
}
}
async saveRawResults(result: AccessibilityTestResult): Promise<string> {
const fileName = `raw-results-${result.id}-${Date.now()}.json`;
const filePath = path.join(this.outputDir, fileName);
try {
await fs.writeFile(filePath, JSON.stringify(result, null, 2), 'utf-8');
return filePath;
} catch (error) {
console.error('Failed to save raw results:', error);
throw new Error(`Failed to save raw results: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
public getScreenshotPath(): string {
const uniqueId = uuidv4();
const fileName = `screenshot-${uniqueId}.png`;
return path.join(this.outputDir, fileName);
}
}