Skip to content
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

core(gather-runner): covert assertPageLoaded into soft failure #4048

Merged
merged 2 commits into from
Dec 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions lighthouse-core/gather/gather-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,12 @@ class GatherRunner {
}

/**
* Throws an error if the original network request failed or wasn't found.
* Returns an error if the original network request failed or wasn't found.
* @param {string} url The URL of the original requested page.
* @param {{online: boolean}} driver
* @param {!Array<WebInspector.NetworkRequest>} networkRecords
* @return {?Error}
*/
static assertPageLoaded(url, driver, networkRecords) {
if (!driver.online) return;

static getPageLoadError(url, networkRecords) {
const mainRecord = networkRecords.find(record => {
// record.url is actual request url, so needs to be compared without any URL fragment.
return URL.equalWithExcludedFragments(record.url, url);
Expand All @@ -162,7 +160,7 @@ class GatherRunner {
log.error('GatherRunner', errorMessage, url);
const error = new Error(`Unable to load page: ${errorMessage}`);
error.code = 'PAGE_LOAD_ERROR';
throw error;
return error;
}
}

Expand Down Expand Up @@ -260,6 +258,7 @@ class GatherRunner {
const passData = {};

let pass = Promise.resolve();
let pageLoadError;

if (config.recordTrace) {
pass = pass.then(_ => {
Expand All @@ -280,9 +279,18 @@ class GatherRunner {
log.log('status', status);
const devtoolsLog = driver.endDevtoolsLog();
const networkRecords = NetworkRecorder.recordsFromLogs(devtoolsLog);
GatherRunner.assertPageLoaded(options.url, driver, networkRecords);
log.verbose('statusEnd', status);

pageLoadError = GatherRunner.getPageLoadError(options.url, networkRecords);
// If the driver was offline, a page load error is expected, so do not save it.
if (!driver.online) pageLoadError = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we do this check within getPageLoadError? seems reasonable.

Copy link
Collaborator Author

@patrickhulce patrickhulce Dec 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh, I explicitly moved it out of there since it felt like it was in the wrong place over there :)

In fact I'd eventually want to remove this special check entirely to make the passes on similar footing. Is it primarily the extra line here that you don't like?


if (pageLoadError) {
gathererResults.LighthouseRunWarnings.push('Lighthouse was unable to reliably load the ' +
'page you requested. Make sure you are testing the correct URL and that the server is ' +
'properly responding to all requests.');
}

// Expose devtoolsLog and networkRecords to gatherers
passData.devtoolsLog = devtoolsLog;
passData.networkRecords = networkRecords;
Expand All @@ -295,7 +303,9 @@ class GatherRunner {
const status = `Retrieving: ${gatherer.name}`;
return chain.then(_ => {
log.log('status', status);
const artifactPromise = Promise.resolve().then(_ => gatherer.afterPass(options, passData));
const artifactPromise = pageLoadError ?
Promise.reject(pageLoadError) :
Promise.resolve().then(_ => gatherer.afterPass(options, passData));
gathererResults[gatherer.name].push(artifactPromise);
return GatherRunner.recoverOrThrow(artifactPromise);
}).then(_ => {
Expand All @@ -319,8 +329,10 @@ class GatherRunner {
const artifacts = {};

// Nest LighthouseRunWarnings, if any, so they will be collected into artifact.
gathererResults.LighthouseRunWarnings = [gathererResults.LighthouseRunWarnings];
const uniqueWarnings = Array.from(new Set(gathererResults.LighthouseRunWarnings));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be worthwhile to change LighthouseRunWarnings to be a set instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh, it's not clear when we would do the flip back to array needed for JSON.stringify, so I'm not necessarily sold. since we were already manipulating it here and aggregating across multiple passes, seems reasonable to de-dupe but let audits keep track of it on their own

gathererResults.LighthouseRunWarnings = [uniqueWarnings];

const pageLoadFailures = [];
return Object.keys(gathererResults).reduce((chain, gathererName) => {
return chain.then(_ => {
const phaseResultsPromises = gathererResults[gathererName];
Expand All @@ -336,9 +348,16 @@ class GatherRunner {
// To reach this point, all errors are non-fatal, so return err to
// runner to handle turning it into an error audit.
artifacts[gathererName] = err;
// Track page load errors separately, so we can fail loudly if needed.
if (err.code === 'PAGE_LOAD_ERROR') pageLoadFailures.push(err);
});
});
}, Promise.resolve()).then(_ => {
// Fail the run if more than 50% of all artifacts failed due to page load failure.
if (pageLoadFailures.length > Object.keys(artifacts).length * .5) {
throw pageLoadFailures[0];
}

return artifacts;
});
}
Expand Down
27 changes: 11 additions & 16 deletions lighthouse-core/test/gather/gather-runner-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -621,33 +621,31 @@ describe('GatherRunner', function() {
/afterPass\(\) method/);
});

describe('#assertPageLoaded', () => {
describe('#getPageLoadError', () => {
it('passes when the page is loaded', () => {
const url = 'http://the-page.com';
const records = [{url}];
GatherRunner.assertPageLoaded(url, {online: true}, records);
assert.ok(!GatherRunner.getPageLoadError(url, records));
});

it('passes when the page is loaded, ignoring any fragment', () => {
const url = 'http://example.com/#/page/list';
const records = [{url: 'http://example.com'}];
GatherRunner.assertPageLoaded(url, {online: true}, records);
assert.ok(!GatherRunner.getPageLoadError(url, records));
});

it('throws when page fails to load', () => {
const url = 'http://the-page.com';
const records = [{url, failed: true, localizedFailDescription: 'foobar'}];
assert.throws(() => {
GatherRunner.assertPageLoaded(url, {online: true}, records);
}, /Unable.*foobar/);
const error = GatherRunner.getPageLoadError(url, records);
assert.ok(error && /Unable.*foobar/.test(error.message));
});

it('throws when page times out', () => {
const url = 'http://the-page.com';
const records = [];
assert.throws(() => {
GatherRunner.assertPageLoaded(url, {online: true}, records);
}, /Unable.*no document request/);
const error = GatherRunner.getPageLoadError(url, records);
assert.ok(error && /Unable.*no document request/.test(error.message));
});
});

Expand Down Expand Up @@ -901,13 +899,10 @@ describe('GatherRunner', function() {
url,
flags: {},
config: new Config({}),
})
.then(_ => {
assert.ok(false);
}, error => {
assert.ok(true);
assert.ok(/net::ERR_NAME_NOT_RESOLVED/.test(error.message));
});
}).then(artifacts => {
assert.equal(artifacts.LighthouseRunWarnings.length, 1);
assert.ok(/unable.*load the page/.test(artifacts.LighthouseRunWarnings[0]));
});
});

it('resolves when domain name can\'t be resolved but is offline', () => {
Expand Down