1
- import { test , expect } from '@playwright/test' ;
1
+ import { test } from '@playwright/test' ;
2
2
import AxeBuilder from '@axe-core/playwright' ;
3
- import dotenv from 'dotenv' ;
4
- import { parseStringPromise } from 'xml2js' ;
5
3
import { createHtmlReport } from 'axe-html-reporter' ;
6
- import fs from 'fs' ;
7
4
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' ;
11
7
12
8
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 } ) ;
37
27
}
38
28
} ) ;
39
29
40
30
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
+ } ) ;
62
75
}
63
76
} ) ;
64
77
65
78
// After all tests, generate a single HTML report
66
79
test . afterAll ( async ( ) => {
67
80
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
+ }
68
87
69
88
if ( combinedResults . length > 0 ) {
70
89
// Combine all results into one object
@@ -73,12 +92,35 @@ test.describe('Accessibility tests', () => {
73
92
violations : combinedResults . flatMap ( ( result ) => result . violations ) ,
74
93
} ;
75
94
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
+
76
117
// Generate the combined HTML report
77
118
createHtmlReport ( {
78
119
results : allResults ,
79
120
options : {
80
121
outputDir : reportDir ,
81
122
reportFileName : 'combined-accessibility-report.html' ,
123
+ customSummary : screenshotSummary ,
82
124
} ,
83
125
} ) ;
84
126
0 commit comments