Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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 |
(...skipping 10 matching lines...) Expand all Loading... | |
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 extractZip = require("extract-zip"); |
30 const remoteInterface = require("chrome-remote-interface"); | 30 const remoteInterface = require("chrome-remote-interface"); |
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.
| |
33 | 31 |
34 const CHROMIUM_REVISION = 467222; | 32 const CHROMIUM_REVISION = 467222; |
35 | 33 |
36 function rmdir(dirPath) | 34 function rmdir(dirPath) |
37 { | 35 { |
38 for (let file of fs.readdirSync(dirPath)) | 36 for (let file of fs.readdirSync(dirPath)) |
39 { | 37 { |
40 let filePath = path.join(dirPath, file); | 38 let filePath = path.join(dirPath, file); |
41 try | 39 try |
42 { | 40 { |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 return new Promise((resolve, reject) => | 241 return new Promise((resolve, reject) => |
244 { | 242 { |
245 setTimeout(() => | 243 setTimeout(() => |
246 { | 244 { |
247 connectRemoteInterface(attempt + 1).then(resolve).catch(reject); | 245 connectRemoteInterface(attempt + 1).then(resolve).catch(reject); |
248 }, 200); | 246 }, 200); |
249 }); | 247 }); |
250 }); | 248 }); |
251 } | 249 } |
252 | 250 |
253 function webpackInMemory(bundleFilename, options) | 251 function runScript(script, scriptName, scriptArgs) |
254 { | 252 { |
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 | |
299 return connectRemoteInterface().then(async client => | 253 return connectRemoteInterface().then(async client => |
300 { | 254 { |
301 try | 255 try |
302 { | 256 { |
303 let {Runtime, Log, Console, Page} = client; | 257 let {Runtime, Log, Console} = client; |
304 | 258 |
305 await Log.enable(); | 259 await Log.enable(); |
306 Log.entryAdded(({entry}) => | 260 Log.entryAdded(({entry}) => |
307 { | 261 { |
308 reportMessage(entry.text, entry.level); | 262 reportMessage(entry.text, entry.level); |
309 }); | 263 }); |
310 | 264 |
311 await Console.enable(); | 265 await Console.enable(); |
312 Console.messageAdded(({message}) => | 266 Console.messageAdded(({message}) => |
313 { | 267 { |
314 reportMessage(message.text, message.level); | 268 reportMessage(message.text, message.level); |
315 }); | 269 }); |
316 | 270 |
317 await Page.navigate({url: initialPage}); | |
318 | |
319 await Runtime.enable(); | 271 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 | |
343 let compileResult = await Runtime.compileScript({ | 272 let compileResult = await Runtime.compileScript({ |
344 expression: bundle, | 273 expression: script, |
345 sourceURL: bundleFilename, | 274 sourceURL: scriptName, |
346 persistScript: true | 275 persistScript: true |
347 }); | 276 }); |
348 if (compileResult.exceptionDetails) | 277 if (compileResult.exceptionDetails) |
349 throwException(compileResult.exceptionDetails, bootstrapPath); | 278 throwException(compileResult.exceptionDetails, scriptName); |
350 | 279 |
351 let runResult = await Runtime.runScript({ | 280 let runResult = await Runtime.runScript({ |
352 scriptId: compileResult.scriptId | 281 scriptId: compileResult.scriptId |
353 }); | 282 }); |
354 if (runResult.exceptionDetails) | 283 if (runResult.exceptionDetails) |
355 throwException(runResult.exceptionDetails, bootstrapPath); | 284 throwException(runResult.exceptionDetails, scriptName); |
356 | 285 |
357 let callResult = await Runtime.callFunctionOn({ | 286 let callResult = await Runtime.callFunctionOn({ |
358 objectId: runResult.result.objectId, | 287 objectId: runResult.result.objectId, |
359 functionDeclaration: "function(...modules) {return this(modules);}", | 288 functionDeclaration: "function(...args) { return this(...args); }", |
360 arguments: browserTestModules.map(module => ({value: module})) | 289 arguments: scriptArgs.map(arg => ({value: arg})) |
361 }); | 290 }); |
362 if (callResult.exceptionDetails) | 291 if (callResult.exceptionDetails) |
363 throwException(callResult.exceptionDetails, bootstrapPath); | 292 throwException(callResult.exceptionDetails, scriptName); |
364 | 293 |
365 let promiseResult = await Runtime.awaitPromise({ | 294 let promiseResult = await Runtime.awaitPromise({ |
366 promiseObjectId: callResult.result.objectId | 295 promiseObjectId: callResult.result.objectId |
367 }); | 296 }); |
368 if (promiseResult.exceptionDetails) | 297 if (promiseResult.exceptionDetails) |
369 throwException(promiseResult.exceptionDetails, bootstrapPath); | 298 throwException(promiseResult.exceptionDetails, scriptName); |
370 } | 299 } |
371 finally | 300 finally |
372 { | 301 { |
373 client.close(); | 302 client.close(); |
374 } | 303 } |
375 }); | 304 }); |
376 } | 305 } |
377 | 306 |
378 module.exports = function(browserTestFiles) | 307 module.exports = function(script, scriptName, ...scriptArgs) |
379 { | 308 { |
380 return ensureChromium().then(chromiumPath => | 309 return ensureChromium().then(chromiumPath => |
381 { | 310 { |
382 let child = startChromium(chromiumPath); | 311 let child = startChromium(chromiumPath); |
383 return Promise.race([ | 312 return Promise.race([ |
384 child.done, | 313 child.done, |
385 runBrowserTests( | 314 runScript(script, scriptName, scriptArgs) |
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 ) | |
390 ]).then(result => | 315 ]).then(result => |
391 { | 316 { |
392 child.kill(); | 317 child.kill(); |
393 return result; | 318 return result; |
394 }).catch(error => | 319 }).catch(error => |
395 { | 320 { |
396 child.kill(); | 321 child.kill(); |
397 throw error; | 322 throw error; |
398 }); | 323 }); |
399 }); | 324 }); |
400 }; | 325 }; |
LEFT | RIGHT |