Skip to content

Commit d55fe50

Browse files
feat: PW Axe test updates
1 parent 9a8a504 commit d55fe50

File tree

7 files changed

+214
-130
lines changed

7 files changed

+214
-130
lines changed

package-lock.json

Lines changed: 43 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@eslint/eslintrc": "^3.1.0",
3939
"@eslint/js": "^9.13.0",
4040
"@nabla/vite-plugin-eslint": "^2.0.4",
41-
"@playwright/test": "^1.48.2",
41+
"@playwright/test": "^1.50.1",
4242
"@tailwindcss/forms": "^0.5.7",
4343
"@tailwindcss/typography": "^0.5.10",
4444
"@types/body-scroll-lock": "^3.1.2",
@@ -56,13 +56,13 @@
5656
"axe-html-reporter": "^2.2.11",
5757
"cypress": "^13.6.2",
5858
"cypress-axe": "^1.5.0",
59-
"dotenv": "^16.3.1",
59+
"dotenv": "^16.4.7",
6060
"dotenv-flow": "^4.1.0",
6161
"eslint": "^9.13.0",
6262
"eslint-config-prettier": "^9.1.0",
6363
"eslint-plugin-prettier": "^5.2.1",
6464
"eslint-plugin-vue": "^9.29.1",
65-
"html-validate": "^9.1.3",
65+
"html-validate": "^9.5.3",
6666
"kolorist": "^1.8.0",
6767
"postcss": "^8.4.47",
6868
"postcss-import": "^16.1.0",
@@ -98,4 +98,4 @@
9898
"optionalDependencies": {
9999
"@rollup/rollup-linux-x64-gnu": "^4.21.3"
100100
}
101-
}
101+
}

tests/axe-test.spec.ts

Lines changed: 94 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,89 @@
1-
import { test, expect } from '@playwright/test';
1+
import { test } from '@playwright/test';
22
import AxeBuilder from '@axe-core/playwright';
3-
import dotenv from 'dotenv';
4-
import { parseStringPromise } from 'xml2js';
53
import { createHtmlReport } from 'axe-html-reporter';
6-
import fs from 'fs';
74
import path from 'path';
8-
9-
// Load environment variables from .env file
10-
dotenv.config();
5+
import fs from 'fs';
6+
import { processSitemap } from './utils/process-sitemap';
117

128
test.describe('Accessibility tests', () => {
13-
let urls = [];
14-
const combinedResults = [];
15-
16-
const baseUrl = process.env.VITE_ASSET_URL;
17-
18-
test.beforeAll(async () => {
19-
const sitemapUrl = baseUrl + '/page-sitemap.xml';
20-
21-
// Fetch the sitemap content
22-
const response = await fetch(sitemapUrl);
23-
24-
if (!response.ok) {
25-
throw new Error(`Failed to fetch sitemap: ${response.status}`);
26-
}
27-
28-
// Read the text content of the sitemap
29-
const xmlContent = await response.text();
30-
31-
// Parse the XML content to extract URLs
32-
const parsedSitemap = await parseStringPromise(xmlContent);
33-
urls = parsedSitemap.urlset.url.map((entry: any) => entry.loc[0]);
34-
35-
if (!urls.length) {
36-
throw new Error('No URLs found in the sitemap.');
9+
let urls: string[] = [];
10+
let totalPages = 0;
11+
const combinedResults: Array<{
12+
url: string;
13+
violations: any[];
14+
screenshot?: string;
15+
}> = [];
16+
17+
test.beforeAll(async ({ baseURL }) => {
18+
const sitemapUrl = `${baseURL}/sitemap_index.xml`;
19+
// const sitemapUrl = `${baseURL}/page-sitemap.xml`;
20+
// const sitemapUrl = `${baseURL}/post-sitemap.xml`;
21+
urls = await processSitemap(sitemapUrl);
22+
totalPages = urls.length;
23+
24+
const screenshotsDir = path.resolve('./test-results/screenshots');
25+
if (!fs.existsSync(screenshotsDir)) {
26+
fs.mkdirSync(screenshotsDir, { recursive: true });
3727
}
3828
});
3929

4030
test('Should not have accessibility issues', async ({ page }) => {
41-
for (const url of urls) {
42-
await page.goto(url);
43-
44-
const results = await new AxeBuilder({ page })
45-
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
46-
.analyze();
47-
48-
console.log('URL is: ' + url);
49-
50-
if (results.violations.length > 0) {
51-
console.log('I found violations!');
52-
53-
console.log(results.violations);
54-
55-
combinedResults.push({
56-
url,
57-
violations: results.violations,
58-
});
59-
} else {
60-
console.log('Nothing found.');
61-
}
31+
const viewports = [
32+
{ name: 'mobile', width: 375, height: 667 },
33+
{ name: 'tablet', width: 768, height: 1024 },
34+
{ name: 'desktop', width: 1280, height: 720 },
35+
];
36+
37+
for (const viewport of viewports) {
38+
await test.step(`Viewport: ${viewport.name}`, async () => {
39+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
40+
41+
for (const url of urls) {
42+
await test.step(`${viewport.name} - ${url}`, async () => {
43+
await page.goto(url, { waitUntil: 'networkidle' });
44+
45+
const results = await new AxeBuilder({ page })
46+
// Level A, and Level AA
47+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
48+
49+
// Level A only
50+
// .withTags(['wcag2a', 'wcag21a'])
51+
.analyze();
52+
53+
console.log('URL is: ' + url);
54+
55+
if (results.violations.length > 0) {
56+
// Capture screenshot
57+
const screenshotPath = path.resolve(
58+
'./test-results/screenshots',
59+
`axe-violation-screenshot-${Date.now()}.png`
60+
);
61+
62+
await page.screenshot({ path: screenshotPath, fullPage: true });
63+
64+
combinedResults.push({
65+
url,
66+
violations: results.violations,
67+
screenshot: screenshotPath,
68+
});
69+
} else {
70+
console.log('Nothing found.');
71+
}
72+
});
73+
}
74+
});
6275
}
6376
});
6477

6578
// After all tests, generate a single HTML report
6679
test.afterAll(async () => {
6780
const reportDir = 'accessibility-reports';
81+
const reportDirPath = path.resolve(process.cwd(), reportDir);
82+
83+
// make sure the output folder exists
84+
if (!fs.existsSync(reportDirPath)) {
85+
fs.mkdirSync(reportDirPath, { recursive: true });
86+
}
6887

6988
if (combinedResults.length > 0) {
7089
// Combine all results into one object
@@ -73,12 +92,35 @@ test.describe('Accessibility tests', () => {
7392
violations: combinedResults.flatMap((result) => result.violations),
7493
};
7594

95+
let screenshotSummary = '';
96+
97+
// Add total pages scanned
98+
screenshotSummary += `<p>Scanned <strong>${totalPages}</strong> URLs</p>\n`;
99+
100+
// Open <ul>
101+
screenshotSummary += '<ul>\n';
102+
103+
for (let i = 0; i < combinedResults.length; i++) {
104+
const result = combinedResults[i];
105+
if (result.screenshot) {
106+
const relPath = path.relative(reportDirPath, result.screenshot);
107+
const name = path.basename(result.screenshot);
108+
109+
// Add li
110+
screenshotSummary += `<li><a href="${relPath}">${i + 1}. ${name}</a></li>\n`;
111+
}
112+
}
113+
114+
// Close </ul>
115+
screenshotSummary += '</ul>\n';
116+
76117
// Generate the combined HTML report
77118
createHtmlReport({
78119
results: allResults,
79120
options: {
80121
outputDir: reportDir,
81122
reportFileName: 'combined-accessibility-report.html',
123+
customSummary: screenshotSummary,
82124
},
83125
});
84126

0 commit comments

Comments
 (0)