168 lines
5.0 KiB
JavaScript
168 lines
5.0 KiB
JavaScript
/**
|
|
* WCAG 2.1 AAA compliance test using axe-core
|
|
*
|
|
* This test runs axe-core against all pages of the website to check for WCAG 2.1 AAA compliance.
|
|
* It tests for issues related to:
|
|
* - Color contrast (7:1 ratio for AAA)
|
|
* - Text spacing
|
|
* - Heading structure
|
|
* - ARIA attributes
|
|
* - And many other WCAG 2.1 AAA criteria
|
|
*/
|
|
|
|
const { chromium } = require('playwright');
|
|
const axeCore = require('axe-core');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// URLs to test
|
|
const BASE_URL = process.argv[2] || 'http://localhost:8080'\;
|
|
const PAGES = [
|
|
'/',
|
|
'/stories/',
|
|
'/stories/open-source-success.html',
|
|
'/stories/viperwire.html',
|
|
'/one-pager-tools/csv-tool.html'
|
|
];
|
|
|
|
// Create reports directory if it doesn't exist
|
|
const reportsDir = path.join(__dirname, '../reports');
|
|
if (!fs.existsSync(reportsDir)) {
|
|
fs.mkdirSync(reportsDir, { recursive: true });
|
|
}
|
|
|
|
async function runAxe(page, pagePath) {
|
|
// Inject axe-core into the page
|
|
await page.evaluate(() => {
|
|
if (!window.axe) {
|
|
// This would normally be done by injecting the axe-core script
|
|
// but for this example, we'll assume axe-core is already available
|
|
console.log('Warning: axe-core not available in the page');
|
|
}
|
|
});
|
|
|
|
// Run axe with WCAG 2.1 AAA rules
|
|
const results = await page.evaluate(() => {
|
|
return new Promise(resolve => {
|
|
if (!window.axe) {
|
|
resolve({ error: 'axe-core not available' });
|
|
return;
|
|
}
|
|
|
|
axe.run(document, {
|
|
runOnly: {
|
|
type: 'tag',
|
|
values: ['wcag2aaa']
|
|
},
|
|
resultTypes: ['violations', 'incomplete', 'inapplicable'],
|
|
rules: {
|
|
'color-contrast': { enabled: true, options: { noScroll: true } }
|
|
}
|
|
})
|
|
.then(results => resolve(results))
|
|
.catch(err => resolve({ error: err.toString() }));
|
|
});
|
|
});
|
|
|
|
return results;
|
|
}
|
|
|
|
async function testPage(browser, pageUrl) {
|
|
const page = await browser.newPage();
|
|
console.log(`Testing ${pageUrl}...`);
|
|
|
|
try {
|
|
// Navigate to the page
|
|
await page.goto(pageUrl, { waitUntil: 'networkidle' });
|
|
|
|
// Run axe-core tests
|
|
const results = await runAxe(page, pageUrl);
|
|
|
|
// Save results to file
|
|
const fileName = pageUrl === BASE_URL ? 'index' : pageUrl.replace(BASE_URL, '').replace(/\//g, '-').replace(/^-/, '');
|
|
const reportPath = path.join(reportsDir, `axe-${fileName || 'index'}.json`);
|
|
fs.writeFileSync(reportPath, JSON.stringify(results, null, 2));
|
|
|
|
// Log results summary
|
|
if (results.error) {
|
|
console.error(`Error running axe-core on ${pageUrl}:`, results.error);
|
|
return { url: pageUrl, success: false, error: results.error };
|
|
}
|
|
|
|
const { violations, incomplete, passes, inapplicable } = results;
|
|
console.log(`Results for ${pageUrl}:`);
|
|
console.log(`- Violations: ${violations.length}`);
|
|
console.log(`- Incomplete: ${incomplete.length}`);
|
|
console.log(`- Passes: ${passes.length}`);
|
|
console.log(`- Inapplicable: ${inapplicable.length}`);
|
|
|
|
// Print violations
|
|
if (violations.length > 0) {
|
|
console.log('\nViolations:');
|
|
violations.forEach((violation, i) => {
|
|
console.log(`${i + 1}. ${violation.id} - ${violation.help} (Impact: ${violation.impact})`);
|
|
console.log(` ${violation.description}`);
|
|
console.log(` WCAG: ${violation.tags.filter(t => t.startsWith('wcag')).join(', ')}`);
|
|
console.log(` Elements: ${violation.nodes.length}`);
|
|
});
|
|
}
|
|
|
|
return {
|
|
url: pageUrl,
|
|
success: violations.length === 0,
|
|
violations: violations.length,
|
|
incomplete: incomplete.length,
|
|
passes: passes.length
|
|
};
|
|
} catch (error) {
|
|
console.error(`Error testing ${pageUrl}:`, error);
|
|
return { url: pageUrl, success: false, error: error.toString() };
|
|
} finally {
|
|
await page.close();
|
|
}
|
|
}
|
|
|
|
async function runTests() {
|
|
const browser = await chromium.launch();
|
|
const results = [];
|
|
|
|
try {
|
|
// Test each page
|
|
for (const pagePath of PAGES) {
|
|
const pageUrl = `${BASE_URL}${pagePath}`;
|
|
const result = await testPage(browser, pageUrl);
|
|
results.push(result);
|
|
}
|
|
|
|
// Save overall results
|
|
const overallReport = {
|
|
timestamp: new Date().toISOString(),
|
|
baseUrl: BASE_URL,
|
|
pages: results,
|
|
summary: {
|
|
total: results.length,
|
|
passed: results.filter(r => r.success).length,
|
|
failed: results.filter(r => !r.success).length
|
|
}
|
|
};
|
|
|
|
fs.writeFileSync(path.join(reportsDir, 'axe-summary.json'), JSON.stringify(overallReport, null, 2));
|
|
|
|
// Print overall summary
|
|
console.log('\n=== Overall Summary ===');
|
|
console.log(`Total pages tested: ${overallReport.summary.total}`);
|
|
console.log(`Pages passed: ${overallReport.summary.passed}`);
|
|
console.log(`Pages failed: ${overallReport.summary.failed}`);
|
|
|
|
// Exit with appropriate code
|
|
process.exit(overallReport.summary.failed > 0 ? 1 : 0);
|
|
} catch (error) {
|
|
console.error('Error running tests:', error);
|
|
process.exit(1);
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
}
|
|
|
|
runTests();
|