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();
 |