1
1
'use strict' ;
2
2
3
- const fs = require ( 'fs' ) ;
4
- const path = require ( 'path' ) ;
3
+ /**
4
+ * @typedef {import('playwright').Page } Page
5
+ * @typedef {import('playwright').PageScreenshotOptions } PageScreenshotOptions
6
+ */
7
+
8
+ const fs = require ( 'node:fs/promises' ) ;
5
9
const http = require ( 'http' ) ;
6
10
const os = require ( 'os' ) ;
11
+ const path = require ( 'path' ) ;
12
+ const pixelmatch = require ( 'pixelmatch' ) ;
7
13
const { chromium } = require ( 'playwright' ) ;
8
14
const { PNG } = require ( 'pngjs' ) ;
9
- const pixelmatch = require ( 'pixelmatch' ) ;
10
15
const { optimize } = require ( '../lib/svgo.js' ) ;
11
16
12
- const runTests = async ( { list } ) => {
13
- let skipped = 0 ;
17
+ const width = 960 ;
18
+ const height = 720 ;
19
+
20
+ /** @type {PageScreenshotOptions } */
21
+ const screenshotOptions = {
22
+ omitBackground : true ,
23
+ clip : { x : 0 , y : 0 , width, height } ,
24
+ animations : 'disabled' ,
25
+ } ;
26
+
27
+ /**
28
+ * @param {string[] } list
29
+ * @returns {Promise<boolean> }
30
+ */
31
+ const runTests = async ( list ) => {
14
32
let mismatched = 0 ;
15
33
let passed = 0 ;
16
- list . reverse ( ) ;
17
- console . info ( 'Start browser...' ) ;
34
+ console . info ( 'Start browser…' ) ;
35
+ /**
36
+ * @param {Page } page
37
+ * @param {string } name
38
+ */
18
39
const processFile = async ( page , name ) => {
19
- if (
20
- // animated
21
- name . startsWith ( 'w3c-svg-11-test-suite/svg/animate-' ) ||
22
- name === 'w3c-svg-11-test-suite/svg/filters-light-04-f.svg' ||
23
- name === 'w3c-svg-11-test-suite/svg/filters-composite-05-f.svg' ||
24
- // messed gradients
25
- name === 'w3c-svg-11-test-suite/svg/pservers-grad-18-b.svg' ||
26
- // removing wrapping <g> breaks :first-child pseudo-class
27
- name === 'w3c-svg-11-test-suite/svg/styling-pres-04-f.svg' ||
28
- // rect is converted to path which matches wrong styles
29
- name === 'w3c-svg-11-test-suite/svg/styling-css-08-f.svg' ||
30
- // complex selectors are messed because of converting shapes to paths
31
- name === 'w3c-svg-11-test-suite/svg/struct-use-10-f.svg' ||
32
- name === 'w3c-svg-11-test-suite/svg/struct-use-11-f.svg' ||
33
- name === 'w3c-svg-11-test-suite/svg/styling-css-01-b.svg' ||
34
- name === 'w3c-svg-11-test-suite/svg/styling-css-04-f.svg' ||
35
- // strange artifact breaks inconsistently breaks regression tests
36
- name === 'w3c-svg-11-test-suite/svg/filters-conv-05-f.svg'
37
- ) {
38
- console . info ( `${ name } is skipped` ) ;
39
- skipped += 1 ;
40
- return ;
41
- }
42
40
await page . goto ( `http://localhost:5000/original/${ name } ` ) ;
43
- await page . setViewportSize ( { width, height } ) ;
44
- const originalBuffer = await page . screenshot ( {
45
- omitBackground : true ,
46
- clip : { x : 0 , y : 0 , width, height } ,
47
- } ) ;
41
+ const originalBuffer = await page . screenshot ( screenshotOptions ) ;
48
42
await page . goto ( `http://localhost:5000/optimized/${ name } ` ) ;
49
- const optimizedBuffer = await page . screenshot ( {
50
- omitBackground : true ,
51
- clip : { x : 0 , y : 0 , width, height } ,
52
- } ) ;
43
+ const optimizedBufferPromise = page . screenshot ( screenshotOptions ) ;
44
+
53
45
const originalPng = PNG . sync . read ( originalBuffer ) ;
54
- const optimizedPng = PNG . sync . read ( optimizedBuffer ) ;
46
+ const optimizedPng = PNG . sync . read ( await optimizedBufferPromise ) ;
55
47
const diff = new PNG ( { width, height } ) ;
56
48
const matched = pixelmatch (
57
49
originalPng . data ,
@@ -63,24 +55,25 @@ const runTests = async ({ list }) => {
63
55
// ignore small aliasing issues
64
56
if ( matched <= 4 ) {
65
57
console . info ( `${ name } is passed` ) ;
66
- passed += 1 ;
58
+ passed ++ ;
67
59
} else {
68
- mismatched += 1 ;
60
+ mismatched ++ ;
69
61
console . error ( `${ name } is mismatched` ) ;
70
62
if ( process . env . NO_DIFF == null ) {
71
63
const file = path . join (
72
64
__dirname ,
73
65
'regression-diffs' ,
74
66
`${ name } .diff.png` ,
75
67
) ;
76
- await fs . promises . mkdir ( path . dirname ( file ) , { recursive : true } ) ;
77
- await fs . promises . writeFile ( file , PNG . sync . write ( diff ) ) ;
68
+ await fs . mkdir ( path . dirname ( file ) , { recursive : true } ) ;
69
+ await fs . writeFile ( file , PNG . sync . write ( diff ) ) ;
78
70
}
79
71
}
80
72
} ;
81
73
const worker = async ( ) => {
82
74
let item ;
83
75
const page = await context . newPage ( ) ;
76
+ await page . setViewportSize ( { width, height } ) ;
84
77
while ( ( item = list . pop ( ) ) ) {
85
78
await processFile ( page , item ) ;
86
79
}
@@ -93,44 +86,21 @@ const runTests = async ({ list }) => {
93
86
Array . from ( new Array ( os . cpus ( ) . length * 2 ) , ( ) => worker ( ) ) ,
94
87
) ;
95
88
await browser . close ( ) ;
96
- console . info ( `Skipped: ${ skipped } ` ) ;
97
89
console . info ( `Mismatched: ${ mismatched } ` ) ;
98
90
console . info ( `Passed: ${ passed } ` ) ;
99
91
return mismatched === 0 ;
100
92
} ;
101
93
102
- const readdirRecursive = async ( absolute , relative = '' ) => {
103
- let result = [ ] ;
104
- const list = await fs . promises . readdir ( absolute , { withFileTypes : true } ) ;
105
- for ( const item of list ) {
106
- const itemAbsolute = path . join ( absolute , item . name ) ;
107
- const itemRelative = path . join ( relative , item . name ) ;
108
- if ( item . isDirectory ( ) ) {
109
- const itemList = await readdirRecursive ( itemAbsolute , itemRelative ) ;
110
- result = [ ...result , ...itemList ] ;
111
- } else if ( item . name . endsWith ( '.svg' ) ) {
112
- result = [ ...result , itemRelative ] ;
113
- }
114
- }
115
- return result ;
116
- } ;
117
-
118
- const width = 960 ;
119
- const height = 720 ;
120
94
( async ( ) => {
121
95
try {
122
96
const start = process . hrtime . bigint ( ) ;
123
97
const fixturesDir = path . join ( __dirname , 'regression-fixtures' ) ;
124
- const list = await readdirRecursive ( fixturesDir ) ;
125
- // setup server
98
+ const filesPromise = fs . readdir ( fixturesDir , { recursive : true } ) ;
126
99
const server = http . createServer ( async ( req , res ) => {
127
100
const name = req . url . slice ( req . url . indexOf ( '/' , 1 ) ) ;
128
101
let file ;
129
102
try {
130
- file = await fs . promises . readFile (
131
- path . join ( fixturesDir , name ) ,
132
- 'utf-8' ,
133
- ) ;
103
+ file = await fs . readFile ( path . join ( fixturesDir , name ) , 'utf-8' ) ;
134
104
} catch ( error ) {
135
105
res . statusCode = 404 ;
136
106
res . end ( ) ;
@@ -144,14 +114,8 @@ const height = 720;
144
114
}
145
115
if ( req . url . startsWith ( '/optimized/' ) ) {
146
116
const optimized = optimize ( file , {
147
- path : name ,
148
117
floatPrecision : 4 ,
149
118
} ) ;
150
- if ( optimized . error ) {
151
- throw new Error ( `Failed to optimize ${ name } ` , {
152
- cause : optimized . error ,
153
- } ) ;
154
- }
155
119
res . setHeader ( 'Content-Type' , 'image/svg+xml' ) ;
156
120
res . end ( optimized . data ) ;
157
121
return ;
@@ -161,9 +125,9 @@ const height = 720;
161
125
await new Promise ( ( resolve ) => {
162
126
server . listen ( 5000 , resolve ) ;
163
127
} ) ;
164
- const passed = await runTests ( { list } ) ;
128
+ const list = ( await filesPromise ) . filter ( ( name ) => name . endsWith ( '.svg' ) ) ;
129
+ const passed = await runTests ( list ) ;
165
130
server . close ( ) ;
166
- // compute time
167
131
const end = process . hrtime . bigint ( ) ;
168
132
const diff = ( end - start ) / BigInt ( 1e6 ) ;
169
133
if ( passed ) {
0 commit comments