-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds support for custom audits and gatherers #593
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
|
||
const log = require('../lib/log.js'); | ||
const Audit = require('../audits/audit'); | ||
const path = require('path'); | ||
|
||
/** | ||
* Class that drives browser to load the page and runs gatherer lifecycle hooks. | ||
|
@@ -189,7 +190,29 @@ class GatherRunner { | |
const driver = options.driver; | ||
const tracingData = {traces: {}}; | ||
|
||
passes = GatherRunner.instantiateGatherers(passes); | ||
if (typeof options.url !== 'string' || options.url.length === 0) { | ||
return Promise.reject(new Error('You must provide a url to the driver')); | ||
} | ||
|
||
if (typeof options.flags === 'undefined') { | ||
options.flags = {}; | ||
} | ||
|
||
if (typeof options.config === 'undefined') { | ||
return Promise.reject(new Error('You must provide a config')); | ||
} | ||
|
||
// Default mobile emulation and page loading to true. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This set of checks duplicates what's already in runner. : https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/runner.js#L29-L44 I think we should only check where neccessary and not end up with all the duplication. If we can't keep straight if we need a safety check, then we probably need to untangle a bit. (I'm thinking of our refactor for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. This looks like an artifact of my refactoring perhaps, rather than something that should be there. |
||
// The extension will switch these off initially. | ||
if (typeof options.flags.mobile === 'undefined') { | ||
options.flags.mobile = true; | ||
} | ||
|
||
if (typeof options.flags.loadPage === 'undefined') { | ||
options.flags.loadPage = true; | ||
} | ||
|
||
passes = this.instantiateGatherers(passes, options.config.configDir); | ||
|
||
return driver.connect() | ||
.then(_ => GatherRunner.setupDriver(driver, options)) | ||
|
@@ -222,26 +245,88 @@ class GatherRunner { | |
const artifacts = Object.assign({}, tracingData); | ||
passes.forEach(pass => { | ||
pass.gatherers.forEach(gatherer => { | ||
if (typeof gatherer.artifact === 'undefined') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think I want a gatherer to return undefined as it's easily mistakable for a failure to set |
||
throw new Error(`${gatherer.constructor.name} failed to provide an artifact.`); | ||
} | ||
|
||
artifacts[gatherer.name] = gatherer.artifact; | ||
}); | ||
}); | ||
return artifacts; | ||
}); | ||
} | ||
|
||
static getGathererClass(gatherer) { | ||
return require(`./gatherers/${gatherer}`); | ||
static getGathererClass(gatherer, rootPath) { | ||
const Runner = require('../runner'); | ||
const list = Runner.getGathererList(); | ||
const coreGatherer = list.find(a => a === `${gatherer}.js`); | ||
|
||
// Assume it's a core gatherer first. | ||
let requirePath = path.resolve(__dirname, `./gatherers/${gatherer}`); | ||
let GathererClass; | ||
|
||
// If not, see if it can be found another way. | ||
if (!coreGatherer) { | ||
// Firstly try and see if the gatherer resolves naturally through the usual means. | ||
try { | ||
require.resolve(gatherer); | ||
|
||
// If the above works, update the path to the absolute value provided. | ||
requirePath = gatherer; | ||
} catch (requireError) { | ||
// If that fails, try and find it relative to any config path provided. | ||
if (rootPath) { | ||
requirePath = path.resolve(rootPath, gatherer); | ||
} | ||
} | ||
} | ||
|
||
// Now try and require it in. If this fails then the audit file isn't where we expected it. | ||
try { | ||
GathererClass = require(requirePath); | ||
} catch (requireError) { | ||
GathererClass = null; | ||
} | ||
|
||
if (!GathererClass) { | ||
throw new Error(`Unable to locate gatherer: ${gatherer} (tried at: ${requirePath})`); | ||
} | ||
|
||
// Confirm that the gatherer appears valid. | ||
this.assertValidGatherer(gatherer, GathererClass); | ||
|
||
return GathererClass; | ||
} | ||
|
||
static assertValidGatherer(gatherer, GathererDefinition) { | ||
const gathererInstance = new GathererDefinition(); | ||
|
||
if (typeof gathererInstance.beforePass !== 'function') { | ||
throw new Error(`${gatherer} has no beforePass() method.`); | ||
} | ||
|
||
if (typeof gathererInstance.pass !== 'function') { | ||
throw new Error(`${gatherer} has no pass() method.`); | ||
} | ||
|
||
if (typeof gathererInstance.afterPass !== 'function') { | ||
throw new Error(`${gatherer} has no afterPass() method.`); | ||
} | ||
|
||
if (typeof gathererInstance.artifact !== 'object') { | ||
throw new Error(`${gatherer} has no artifact property.`); | ||
} | ||
} | ||
|
||
static instantiateGatherers(passes) { | ||
static instantiateGatherers(passes, rootPath) { | ||
return passes.map(pass => { | ||
pass.gatherers = pass.gatherers.map(gatherer => { | ||
// If this is already instantiated, don't do anything else. | ||
if (typeof gatherer !== 'string') { | ||
return gatherer; | ||
} | ||
|
||
const GathererClass = GatherRunner.getGathererClass(gatherer); | ||
const GathererClass = GatherRunner.getGathererClass(gatherer, rootPath); | ||
return new GathererClass(); | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this check should be in
Runner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? The Gatherer needs the config, it should be the one to assert that it exists.