-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaccessibility-test.js
More file actions
161 lines (134 loc) · 4.99 KB
/
Copy pathaccessibility-test.js
File metadata and controls
161 lines (134 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env node
/**
* Accessibility Test using Static Analysis
* Tests built output for WCAG compliance without requiring browser automation
*/
const fs = require('fs');
const path = require('path');
// Check if the build output exists
const outDir = path.join(__dirname, 'out');
if (!fs.existsSync(outDir)) {
console.log('📦 Build output not found. Please run "npm run build" first.');
process.exit(1);
}
console.log('🔍 Static Accessibility Analysis');
console.log('================================\n');
// Read and analyze HTML files
function analyzeHTML(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const issues = [];
// Check for missing lang attribute
if (!/<html[^>]*\slang=/i.test(content)) {
issues.push('Missing lang attribute on <html> element');
}
// Check for images without alt attributes
const imgMatches = content.match(/<img[^>]*>/gi) || [];
imgMatches.forEach((img, index) => {
if (!img.includes('alt=')) {
issues.push(`Image ${index + 1} missing alt attribute: ${img.substring(0, 50)}...`);
}
});
// Check for form inputs without labels
const inputMatches = content.match(/<input[^>]*>/gi) || [];
inputMatches.forEach((input, index) => {
if (input.includes('type="text"') || input.includes('type="email"')) {
// Simple check - this is basic, real tools would be more sophisticated
const hasAriaLabel = input.includes('aria-label=');
const hasId = input.match(/id="([^"]+)"/);
let hasLabel = false;
if (hasId) {
const id = hasId[1];
hasLabel = content.includes(`for="${id}"`);
}
if (!hasAriaLabel && !hasLabel) {
issues.push(`Input ${index + 1} missing accessible label: ${input.substring(0, 50)}...`);
}
}
});
// Check for headings hierarchy
const headingMatches = content.match(/<h[1-6][^>]*>/gi) || [];
const headingLevels = headingMatches.map(h => parseInt(h.match(/h([1-6])/i)?.[1] || '1'));
let previousLevel = 0;
headingLevels.forEach((level, index) => {
if (index > 0 && level > previousLevel + 1) {
issues.push(`Heading hierarchy skip: H${previousLevel} followed by H${level}`);
}
previousLevel = level;
});
// Check for buttons/links with meaningful text
const buttonMatches = content.match(/<button[^>]*>.*?<\/button>/gi) || [];
buttonMatches.forEach((button, index) => {
const text = button.replace(/<[^>]*>/g, '').trim();
if (!text || text.length < 2) {
const hasAriaLabel = button.includes('aria-label=');
if (!hasAriaLabel) {
issues.push(`Button ${index + 1} has no accessible text`);
}
}
});
const linkMatches = content.match(/<a[^>]*>.*?<\/a>/gi) || [];
linkMatches.forEach((link, index) => {
const text = link.replace(/<[^>]*>/g, '').trim();
if (!text || text.length < 2) {
const hasAriaLabel = link.includes('aria-label=');
if (!hasAriaLabel) {
issues.push(`Link ${index + 1} has no accessible text`);
}
}
});
return issues;
}
// Function to scan all HTML files
function scanDirectory(dir, baseDir = dir) {
const results = [];
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
results.push(...scanDirectory(fullPath, baseDir));
} else if (item.endsWith('.html')) {
const relativePath = path.relative(baseDir, fullPath);
const issues = analyzeHTML(fullPath);
results.push({ file: relativePath, issues });
}
}
return results;
}
// Perform the scan
console.log('Analyzing HTML files for accessibility issues...\n');
const results = scanDirectory(outDir);
let totalIssues = 0;
results.forEach(({ file, issues }) => {
console.log(`📄 ${file}`);
if (issues.length === 0) {
console.log(' ✅ No accessibility issues detected');
} else {
issues.forEach(issue => {
console.log(` ❌ ${issue}`);
totalIssues++;
});
}
console.log();
});
// Summary
console.log('📊 Summary');
console.log('==========');
console.log(`Files analyzed: ${results.length}`);
console.log(`Total issues found: ${totalIssues}`);
if (totalIssues === 0) {
console.log('🎉 Static analysis passed! No obvious accessibility issues detected.');
console.log('\n💡 Note: This is a basic static analysis. For comprehensive testing, use:');
console.log(' - axe DevTools browser extension');
console.log(' - WAVE Web Accessibility Evaluator');
console.log(' - Manual keyboard navigation testing');
console.log(' - Screen reader testing');
} else {
console.log('\n🚨 Issues detected. Please review and fix the accessibility problems listed above.');
console.log('\n💡 Additional testing recommendations:');
console.log(' - Test with keyboard navigation');
console.log(' - Test with screen readers');
console.log(' - Verify color contrast in browser');
console.log(' - Use axe DevTools for comprehensive analysis');
}
process.exit(totalIssues > 0 ? 1 : 0);