@@ -65,12 +65,12 @@ function createFirefoxManifest(manifest) {
65
65
// Firefox supports SVG icons.
66
66
Object . assign ( manifest , {
67
67
"icons" : {
68
- "16" : "icons/icon.svg" ,
69
- "32" : "icons/icon.svg" ,
70
- "48" : "icons/icon.svg" ,
71
- "64" : "icons/icon.svg" ,
72
- "96" : "icons/icon.svg" ,
73
- "128" : "icons/icon.svg"
68
+ "16" : "icons/icon.svg" ,
69
+ "32" : "icons/icon.svg" ,
70
+ "48" : "icons/icon.svg" ,
71
+ "64" : "icons/icon.svg" ,
72
+ "96" : "icons/icon.svg" ,
73
+ "128" : "icons/icon.svg" ,
74
74
} ,
75
75
} ) ;
76
76
@@ -130,7 +130,10 @@ async function buildStorePackage() {
130
130
const firefoxManifest = createFirefoxManifest ( chromeManifest ) ;
131
131
await writeDistManifest ( firefoxManifest ) ;
132
132
// Exclude PNG icons from the Firefox build, because we use the SVG directly.
133
- await shell ( "bash" , [ "-c" , `${ zipCommand } ../firefox/vimium-firefox-${ version } .zip . -x icons/*.png` ] ) ;
133
+ await shell ( "bash" , [
134
+ "-c" ,
135
+ `${ zipCommand } ../firefox/vimium-firefox-${ version } .zip . -x icons/*.png` ,
136
+ ] ) ;
134
137
135
138
// Build the Chrome Store package.
136
139
await writeDistManifest ( chromeManifest ) ;
@@ -163,49 +166,55 @@ const runUnitTests = async () => {
163
166
return await shoulda . run ( ) ;
164
167
} ;
165
168
166
- const runDomTests = async ( port ) => {
167
- const testUrl = `http://localhost:${ port } /tests/dom_tests/dom_tests.html` ;
168
-
169
- const browser = await puppeteer . launch ( ) ;
170
- const page = await browser . newPage ( ) ;
171
- let receivedErrorOutput = false ;
172
-
169
+ function setupPuppeteerPageForTests ( page ) {
170
+ // The "console" event emitted has arguments which are promises. To obtain the values to be
171
+ // printed, we must resolve those promises. However, if many console messages are emitted at once,
172
+ // resolving the promises often causes the console.log messages to be printed out of order. Here,
173
+ // we use a queue to strictly enforce that the messages appear in the order in which they were
174
+ // logged.
175
+ const messageQueue = [ ] ;
176
+ let processing = false ;
177
+ const processMessageQueue = async ( ) => {
178
+ while ( messageQueue . length > 0 ) {
179
+ const values = await Promise . all ( messageQueue . shift ( ) ) ;
180
+ console . log ( ...values ) ;
181
+ }
182
+ processing = false ;
183
+ } ;
173
184
page . on ( "console" , async ( msg ) => {
174
- const args = await Promise . all ( msg . args ( ) . map ( ( a ) => a . jsonValue ( ) ) ) ;
175
- console . log ( ...args ) ;
185
+ const values = msg . args ( ) . map ( ( a ) => a . jsonValue ( ) ) ;
186
+ messageQueue . push ( values ) ;
187
+ if ( ! processing ) {
188
+ processing = true ;
189
+ processMessageQueue ( ) ;
190
+ }
176
191
} ) ;
192
+
177
193
page . on ( "error" , ( err ) => {
178
- // As far as I can tell, this handler never gets executed.
194
+ // NOTE(philc): As far as I can tell, this handler never gets executed.
179
195
console . error ( err ) ;
180
196
} ) ;
181
197
// pageerror catches the same events that window.onerror would, like JavaScript parsing errors.
182
198
page . on ( "pageerror" , ( error ) => {
183
- receivedErrorOutput = true ;
199
+ // This is an arbitrary field we're writing to the page object.
200
+ page . receivedErrorOutput = true ;
184
201
// Whatever type error is, it requires toString() to print the message.
185
202
console . log ( error . toString ( ) ) ;
186
203
} ) ;
187
- page . on (
188
- "requestfailed" ,
189
- ( request ) => console . log ( console . log ( `${ request . failure ( ) . errorText } ${ request . url ( ) } ` ) ) ,
190
- ) ;
191
-
192
- page . goto ( testUrl ) ;
204
+ page . on ( "requestfailed" , ( request ) => {
205
+ console . log ( `${ request . failure ( ) . errorText } ${ request . url ( ) } ` ) ;
206
+ } ) ;
207
+ }
193
208
209
+ // Navigates the Puppeteer `page` to `url` and invokes shoulda.run().
210
+ async function runPuppeteerTest ( page , url ) {
211
+ page . goto ( url ) ;
194
212
await page . waitForNavigation ( { waitUntil : "load" } ) ;
195
-
196
213
const success = await page . evaluate ( async ( ) => {
197
214
return await shoulda . run ( ) ;
198
215
} ) ;
199
-
200
- // NOTE(philc): At one point in development, I noticed that the output from Deno would suddenly
201
- // pause, prior to the tests fully finishing, so closing the browser here may be racy. If it
202
- // occurs again, we may need to add "await delay(200)".
203
- await browser . close ( ) ;
204
- if ( receivedErrorOutput ) {
205
- throw new Error ( "The tests fail because there was a page-level error." ) ;
206
- }
207
216
return success ;
208
- } ;
217
+ }
209
218
210
219
desc ( "Download and parse list of top-level domains (TLDs)" ) ;
211
220
task ( "fetch-tlds" , [ ] , async ( ) => {
@@ -228,8 +237,7 @@ task("test-unit", [], async () => {
228
237
}
229
238
} ) ;
230
239
231
- desc ( "Run DOM tests" ) ;
232
- task ( "test-dom" , [ ] , async ( ) => {
240
+ async function testDom ( ) {
233
241
const port = await getAvailablePort ( ) ;
234
242
let served404 = false ;
235
243
const httpServer = Deno . serve ( { port } , async ( req ) => {
@@ -247,15 +255,41 @@ task("test-dom", [], async () => {
247
255
}
248
256
} ) ;
249
257
250
- const success = await runDomTests ( port ) ;
251
- if ( served404 ) {
252
- console . log ( "Tests failed because a background or content script requested a missing file." ) ;
258
+ const files = [ "dom_tests.html" , "vomnibar_test.html" ] ;
259
+ const browser = await puppeteer . launch ( ) ;
260
+ let success = true ;
261
+ for ( const file of files ) {
262
+ const page = await browser . newPage ( ) ;
263
+ console . log ( "Running" , file ) ;
264
+ setupPuppeteerPageForTests ( page ) ;
265
+ const url = `http://localhost:${ port } /tests/dom_tests/${ file } ?dom_tests=true` ;
266
+ const result = await runPuppeteerTest ( page , url ) ;
267
+ success = success && result ;
268
+ if ( served404 ) {
269
+ console . log ( `${ file } failed: a background or content script requested a missing file.` ) ;
270
+ }
271
+ if ( page . receivedErrorOutput ) {
272
+ console . log ( `${ file } failed: there was a page level error.` ) ;
273
+ success = false ;
274
+ }
275
+ // If we close the puppeteer page (tab) via page.close(), we can get innocuous but noisy output
276
+ // like this:
277
+ // net::ERR_ABORTED http://localhost:43524/pages/hud.html?dom_tests=true
278
+ // There's probably a way to prevent that, but as a work around, we avoid closing the page.
279
+ // browser.close() will close all of its owned pages.
253
280
}
281
+ // NOTE(philc): At one point in development, I noticed that the output from Deno would suddenly
282
+ // pause, prior to the tests fully finishing, so closing the browser here may be racy. If it
283
+ // occurs again, we may need to add "await delay(200)".
284
+ await browser . close ( ) ;
254
285
await httpServer . shutdown ( ) ;
255
286
if ( served404 || ! success ) {
256
287
abort ( "test-dom failed." ) ;
257
288
}
258
- } ) ;
289
+ }
290
+
291
+ desc ( "Run DOM tests" ) ;
292
+ task ( "test-dom" , [ ] , testDom ) ;
259
293
260
294
desc ( "Run unit and DOM tests" ) ;
261
295
task ( "test" , [ "test-unit" , "test-dom" ] ) ;
0 commit comments