Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: chromium_process.js

Issue 29517687: Issue 5079, 5516 - Use webpack for browser tests, modules for content scripts (Closed)
Patch Set: Created Aug. 16, 2017, 3:40 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /* eslint-env node */ 18 /* eslint-env node */
19 /* eslint no-console: "off" */ 19 /* eslint no-console: "off" */
20 20
21 "use strict"; 21 "use strict";
22 22
23 const childProcess = require("child_process"); 23 const childProcess = require("child_process");
24 const fs = require("fs"); 24 const fs = require("fs");
25 const https = require("https"); 25 const https = require("https");
26 const os = require("os"); 26 const os = require("os");
27 const path = require("path"); 27 const path = require("path");
28 28
29 const extractZip = require("extract-zip");
29 const remoteInterface = require("chrome-remote-interface"); 30 const remoteInterface = require("chrome-remote-interface");
30 const extractZip = require("extract-zip"); 31 const webpack = require("webpack");
32 const MemoryFS = require("memory-fs");
Wladimir Palant 2017/08/17 10:05:38 I assume that this module exists because WebPack i
kzar 2017/08/17 12:40:21 Done.
31 33
32 const CHROMIUM_REVISION = 467222; 34 const CHROMIUM_REVISION = 467222;
33 35
34 function rmdir(dirPath) 36 function rmdir(dirPath)
35 { 37 {
36 for (let file of fs.readdirSync(dirPath)) 38 for (let file of fs.readdirSync(dirPath))
37 { 39 {
38 let filePath = path.join(dirPath, file); 40 let filePath = path.join(dirPath, file);
39 try 41 try
40 { 42 {
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 return new Promise((resolve, reject) => 243 return new Promise((resolve, reject) =>
242 { 244 {
243 setTimeout(() => 245 setTimeout(() =>
244 { 246 {
245 connectRemoteInterface(attempt + 1).then(resolve).catch(reject); 247 connectRemoteInterface(attempt + 1).then(resolve).catch(reject);
246 }, 200); 248 }, 200);
247 }); 249 });
248 }); 250 });
249 } 251 }
250 252
251 function runBootstrap(initialPage, bootstrapPath, bootstrapArgs) 253 function webpackInMemory(bundleFilename, options)
252 { 254 {
255 // Based on this example https://webpack.js.org/api/node/#custom-file-systems
256 let memoryFS = new MemoryFS();
257
258 options.output = {filename: bundleFilename, path: "/"};
259 let webpackCompiler = webpack(options);
260 webpackCompiler.outputFileSystem = memoryFS;
Wladimir Palant 2017/08/17 10:05:37 Nit: It's better to put all the statements above i
kzar 2017/08/17 12:40:21 Done.
261 return new Promise((resolve, reject) =>
262 {
263 webpackCompiler.run((err, stats) =>
264 {
265 // Error handling is based on this example
266 // https://webpack.js.org/api/node/#error-handling
267 if (err)
268 {
269 let reason = err.stack || err;
270 if (err.details)
271 reason += "\n" + err.details;
272 reject(reason);
273 }
274 else if (stats.hasErrors())
275 reject(stats.toJson().errors);
276 else
277 {
278 let bundle = memoryFS.readFileSync("/" + bundleFilename, "utf-8");
279 memoryFS.unlink("/" + bundleFilename, () => { resolve(bundle); });
Wladimir Palant 2017/08/17 10:05:38 Nit: Use memoryFS.unlinkSync() here to make this m
kzar 2017/08/17 12:40:21 Done.
280 }
281 });
282 });
283 }
284
285 function runBrowserTests(browserTestModules)
286 {
287 // We need to navigate to this directory because about:blank won't be allowed
288 // to load file:/// URLs.
289 let initialPage = require("url").format({
290 protocol: "file",
291 slashes: "true",
292 pathname: path.resolve(process.cwd(), __dirname).split(path.sep).join("/")
293 });
Wladimir Palant 2017/08/17 10:07:30 Actually, this shouldn't be necessary any more. Ru
kzar 2017/08/17 12:40:21 Done.
294
295 let bootstrapPath = path.join(__dirname, "test", "browser", "_bootstrap.js");
296 let nodeunitPath = path.join(__dirname, "node_modules", "nodeunit",
297 "examples", "browser", "nodeunit.js");
298
253 return connectRemoteInterface().then(async client => 299 return connectRemoteInterface().then(async client =>
254 { 300 {
255 try 301 try
256 { 302 {
257 let {Runtime, Log, Console, Page} = client; 303 let {Runtime, Log, Console, Page} = client;
258 304
259 await Log.enable(); 305 await Log.enable();
260 Log.entryAdded(({entry}) => 306 Log.entryAdded(({entry}) =>
261 { 307 {
262 reportMessage(entry.text, entry.level); 308 reportMessage(entry.text, entry.level);
263 }); 309 });
264 310
265 await Console.enable(); 311 await Console.enable();
266 Console.messageAdded(({message}) => 312 Console.messageAdded(({message}) =>
267 { 313 {
268 reportMessage(message.text, message.level); 314 reportMessage(message.text, message.level);
269 }); 315 });
270 316
271 await Page.navigate({url: initialPage}); 317 await Page.navigate({url: initialPage});
272 318
273 await Runtime.enable(); 319 await Runtime.enable();
320
321 let bundleFilename = "bundle.js";
322 let bundle = await webpackInMemory(bundleFilename, {
323 entry: bootstrapPath,
324 module: {
325 rules: [{
326 resource: nodeunitPath,
327 // I would have rathered use exports-loader here, to avoid treating
Wladimir Palant 2017/08/17 10:05:37 "rather used"?
kzar 2017/08/17 12:40:21 Done.
328 // nodeunit as a global. Unfortunately the nodeunit browser example
329 // script is quite slopily put together, if exports isn't falsey it
330 // breaks! As a workaround we need to use script-loader, which means
331 // that exports is falsey for that script as a side-effect.
332 use: ["script-loader"]
333 }]
334 },
335 resolve: {
336 alias: {
337 nodeunit$: nodeunitPath
338 },
339 modules: [path.resolve(__dirname, "lib")]
340 }
341 });
342
274 let compileResult = await Runtime.compileScript({ 343 let compileResult = await Runtime.compileScript({
275 expression: fs.readFileSync(bootstrapPath, "utf-8"), 344 expression: bundle,
276 sourceURL: bootstrapPath, 345 sourceURL: bundleFilename,
277 persistScript: true 346 persistScript: true
278 }); 347 });
279 if (compileResult.exceptionDetails) 348 if (compileResult.exceptionDetails)
280 throwException(compileResult.exceptionDetails, bootstrapPath); 349 throwException(compileResult.exceptionDetails, bootstrapPath);
281 350
282 let runResult = await Runtime.runScript({ 351 let runResult = await Runtime.runScript({
283 scriptId: compileResult.scriptId 352 scriptId: compileResult.scriptId
284 }); 353 });
285 if (runResult.exceptionDetails) 354 if (runResult.exceptionDetails)
286 throwException(runResult.exceptionDetails, bootstrapPath); 355 throwException(runResult.exceptionDetails, bootstrapPath);
287 356
288 let callResult = await Runtime.callFunctionOn({ 357 let callResult = await Runtime.callFunctionOn({
289 objectId: runResult.result.objectId, 358 objectId: runResult.result.objectId,
290 functionDeclaration: "function(...args) {return this(...args);}", 359 functionDeclaration: "function(...modules) {return this(modules);}",
291 arguments: bootstrapArgs.map(url => ({value: url})) 360 arguments: browserTestModules.map(module => ({value: module}))
292 }); 361 });
293 if (callResult.exceptionDetails) 362 if (callResult.exceptionDetails)
294 throwException(callResult.exceptionDetails, bootstrapPath); 363 throwException(callResult.exceptionDetails, bootstrapPath);
295 364
296 let promiseResult = await Runtime.awaitPromise({ 365 let promiseResult = await Runtime.awaitPromise({
297 promiseObjectId: callResult.result.objectId 366 promiseObjectId: callResult.result.objectId
298 }); 367 });
299 if (promiseResult.exceptionDetails) 368 if (promiseResult.exceptionDetails)
300 throwException(promiseResult.exceptionDetails, bootstrapPath); 369 throwException(promiseResult.exceptionDetails, bootstrapPath);
301 } 370 }
302 finally 371 finally
303 { 372 {
304 client.close(); 373 client.close();
305 } 374 }
306 }); 375 });
307 } 376 }
308 377
309 module.exports = function(initialPage, bootstrapPath, bootstrapArgs) 378 module.exports = function(browserTestFiles)
310 { 379 {
311 return ensureChromium().then(chromiumPath => 380 return ensureChromium().then(chromiumPath =>
312 { 381 {
313 let child = startChromium(chromiumPath); 382 let child = startChromium(chromiumPath);
314 return Promise.race([ 383 return Promise.race([
315 child.done, 384 child.done,
316 runBootstrap(initialPage, bootstrapPath, bootstrapArgs) 385 runBrowserTests(
386 browserTestFiles.map(
387 m => "." + path.sep + path.relative(path.join("test", "browser"), m)
Wladimir Palant 2017/08/17 10:05:37 The point here is generating a list of module name
kzar 2017/08/17 12:40:21 Done.
388 )
389 )
317 ]).then(result => 390 ]).then(result =>
318 { 391 {
319 child.kill(); 392 child.kill();
320 return result; 393 return result;
321 }).catch(error => 394 }).catch(error =>
322 { 395 {
323 child.kill(); 396 child.kill();
324 throw error; 397 throw error;
325 }); 398 });
326 }); 399 });
327 }; 400 };
OLDNEW

Powered by Google App Engine
This is Rietveld