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(driver): add run warning on page load timeout #10538

Merged
merged 6 commits into from
Apr 8, 2020
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
27 changes: 20 additions & 7 deletions lighthouse-core/gather/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ class Driver {
* @param {number} cpuQuietThresholdMs
* @param {number} maxWaitForLoadedMs
* @param {number=} maxWaitForFcpMs
* @return {Promise<void>}
* @return {Promise<{timedOut: boolean}>}
* @private
*/
async _waitForFullyLoaded(pauseAfterFcpMs, pauseAfterLoadMs, networkQuietThresholdMs,
Expand All @@ -939,6 +939,7 @@ class Driver {

// Wait for all initial load promises. Resolves on cleanup function the clears load
// timeout timer.
/** @type {Promise<() => Promise<{timedOut: boolean}>>} */
const loadPromise = Promise.all([
waitForFcp.promise,
waitForLoadEvent.promise,
Expand All @@ -947,9 +948,13 @@ class Driver {
waitForCPUIdle = this._waitForCPUIdle(cpuQuietThresholdMs);
return waitForCPUIdle.promise;
}).then(() => {
return function() {
/** @return {Promise<{timedOut: boolean}>} */
const cleanupFn = async function() {
log.verbose('Driver', 'loadEventFired and network considered idle');
return {timedOut: false};
};

return cleanupFn;
}).catch(err => {
// Throw the error in the cleanupFn so we still cleanup all our handlers.
return function() {
Expand All @@ -959,6 +964,7 @@ class Driver {

// Last resort timeout. Resolves maxWaitForLoadedMs ms from now on
// cleanup function that removes loadEvent and network idle listeners.
/** @type {Promise<() => Promise<{timedOut: boolean}>>} */
const maxTimeoutPromise = new Promise((resolve, reject) => {
maxTimeoutHandle = setTimeout(resolve, maxWaitForLoadedMs);
}).then(_ => {
Expand All @@ -970,6 +976,8 @@ class Driver {
await this.sendCommand('Runtime.terminateExecution');
throw new LHError(LHError.errors.PAGE_HUNG);
}

return {timedOut: true};
};
});

Expand All @@ -985,7 +993,7 @@ class Driver {
waitForNetworkIdle.cancel();
waitForCPUIdle.cancel();

await cleanupFn();
return cleanupFn();
}

/**
Expand Down Expand Up @@ -1071,7 +1079,7 @@ class Driver {
* Resolves on the url of the loaded page, taking into account any redirects.
* @param {string} url
* @param {{waitForFcp?: boolean, waitForLoad?: boolean, waitForNavigated?: boolean, passContext?: LH.Gatherer.PassContext}} options
* @return {Promise<string>}
* @return {Promise<{finalUrl: string, timedOut: boolean}>}
*/
async gotoURL(url, options = {}) {
const waitForFcp = options.waitForFcp || false;
Expand Down Expand Up @@ -1101,6 +1109,7 @@ class Driver {
// No timeout needed for Page.navigate. See https://github.com/GoogleChrome/lighthouse/pull/6413.
const waitforPageNavigateCmd = this._innerSendCommand('Page.navigate', undefined, {url});

let timedOut = false;
if (waitForNavigated) {
await this._waitForFrameNavigated();
} else if (waitForLoad) {
Expand All @@ -1120,14 +1129,18 @@ class Driver {
/* eslint-enable max-len */

if (!waitForFcp) maxFCPMs = undefined;
await this._waitForFullyLoaded(pauseAfterFcpMs, pauseAfterLoadMs, networkQuietThresholdMs,
cpuQuietThresholdMs, maxWaitMs, maxFCPMs);
const loadResult = await this._waitForFullyLoaded(pauseAfterFcpMs, pauseAfterLoadMs,
networkQuietThresholdMs, cpuQuietThresholdMs, maxWaitMs, maxFCPMs);
timedOut = loadResult.timedOut;
}

// Bring `Page.navigate` errors back into the promise chain. See https://github.com/GoogleChrome/lighthouse/pull/6739.
await waitforPageNavigateCmd;

return this._endNetworkStatusMonitoring();
return {
finalUrl: this._endNetworkStatusMonitoring(),
timedOut,
};
}

/**
Expand Down
8 changes: 7 additions & 1 deletion lighthouse-core/gather/gather-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const UIStrings = {
warningRedirected: 'The page may not be loading as expected because your test URL ' +
`({requested}) was redirected to {final}. ` +
'Try testing the second URL directly.',
/**
* @description Warning that Lighthouse timed out while waiting for the page to load.
*/
warningTimeout: 'The page loaded too slowly to finish within the time limit. ' +
'Results may be incomplete.',
};

const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
Expand Down Expand Up @@ -76,12 +81,13 @@ class GatherRunner {
};
log.time(status);
try {
const finalUrl = await driver.gotoURL(passContext.url, {
const {finalUrl, timedOut} = await driver.gotoURL(passContext.url, {
waitForFcp: passContext.passConfig.recordTrace,
waitForLoad: true,
passContext,
});
passContext.url = finalUrl;
if (timedOut) passContext.LighthouseRunWarnings.push(str_(UIStrings.warningTimeout));
} catch (err) {
// If it's one of our loading-based LHErrors, we'll treat it as a page load error.
if (err.code === 'NO_FCP' || err.code === 'PAGE_HUNG') {
Expand Down
3 changes: 3 additions & 0 deletions lighthouse-core/lib/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,9 @@
"lighthouse-core/gather/gather-runner.js | warningRedirected": {
"message": "The page may not be loading as expected because your test URL ({requested}) was redirected to {final}. Try testing the second URL directly."
},
"lighthouse-core/gather/gather-runner.js | warningTimeout": {
"message": "The page loaded too slowly to finish within the time limit. Results may be incomplete."
},
"lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": {
"message": "Cache TTL"
},
Expand Down
3 changes: 3 additions & 0 deletions lighthouse-core/lib/i18n/locales/en-XL.json
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,9 @@
"lighthouse-core/gather/gather-runner.js | warningRedirected": {
"message": "T̂h́ê ṕâǵê ḿâý n̂ót̂ b́ê ĺôád̂ín̂ǵ âś êx́p̂éĉt́êd́ b̂éĉáûśê ýôúr̂ t́êśt̂ ÚR̂Ĺ ({requested}) ŵáŝ ŕêd́îŕêćt̂éd̂ t́ô {final}. T́r̂ý t̂éŝt́îńĝ t́ĥé ŝéĉón̂d́ ÛŔL̂ d́îŕêćt̂ĺŷ."
},
"lighthouse-core/gather/gather-runner.js | warningTimeout": {
"message": "T̂h́ê ṕâǵê ĺôád̂éd̂ t́ôó ŝĺôẃl̂ý t̂ó f̂ín̂íŝh́ ŵít̂h́îń t̂h́ê t́îḿê ĺîḿît́. R̂éŝúl̂t́ŝ ḿâý b̂é îńĉóm̂ṕl̂ét̂é."
},
"lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": {
"message": "Ĉáĉh́ê T́T̂Ĺ"
},
Expand Down
8 changes: 4 additions & 4 deletions lighthouse-core/test/gather/driver-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,7 @@ describe('.gotoURL', () => {
const loadPromise = driver.gotoURL(startUrl, loadOptions);

await flushAllTimersAndMicrotasks();
const loadedUrl = await loadPromise;
expect(loadedUrl).toEqual(finalUrl);
expect(await loadPromise).toEqual({finalUrl, timedOut: false});
});

describe('when waitForNavigated', () => {
Expand Down Expand Up @@ -577,7 +576,7 @@ describe('.gotoURL', () => {
waitForResult.mockResolve();
await flushAllTimersAndMicrotasks();
expect(loadPromise).toBeDone(`Did not resolve on ${name}`);
await loadPromise;
expect(await loadPromise).toMatchObject({timedOut: false});
});
});

Expand Down Expand Up @@ -607,7 +606,7 @@ describe('.gotoURL', () => {
driver._waitForCPUIdle.mockResolve();
await flushAllTimersAndMicrotasks();
expect(loadPromise).toBeDone(`Did not resolve on CPU idle`);
await loadPromise;
expect(await loadPromise).toMatchObject({timedOut: false});
});

it('should timeout when not resolved fast enough', async () => {
Expand Down Expand Up @@ -638,6 +637,7 @@ describe('.gotoURL', () => {
expect(driver._waitForLoadEvent.getMockCancelFn()).toHaveBeenCalled();
expect(driver._waitForNetworkIdle.getMockCancelFn()).toHaveBeenCalled();
expect(driver._waitForCPUIdle.getMockCancelFn()).toHaveBeenCalled();
expect(await loadPromise).toMatchObject({timedOut: true});
});

it('should cleanup listeners even when waits reject', async () => {
Expand Down
2 changes: 1 addition & 1 deletion lighthouse-core/test/gather/fake-driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function makeFakeDriver({protocolGetVersionResponse}) {
},
/** @param {string} url */
gotoURL(url) {
return Promise.resolve(url);
return Promise.resolve({finalUrl: url, timedOut: false});
},
beginEmulation() {
return Promise.resolve();
Expand Down
46 changes: 37 additions & 9 deletions lighthouse-core/test/gather/gather-runner-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ describe('GatherRunner', function() {
const url2 = 'https://example.com/interstitial';
const driver = {
gotoURL() {
return Promise.resolve(url2);
return Promise.resolve({finalUrl: url2, timedOut: false});
},
};

Expand Down Expand Up @@ -216,7 +216,7 @@ describe('GatherRunner', function() {
const finalUrl = 'https://example.com/interstitial';
const driver = Object.assign({}, fakeDriver, {
gotoURL() {
return Promise.resolve(finalUrl);
return Promise.resolve({finalUrl, timedOut: false});
},
});
const config = makeConfig({passes: [{}]});
Expand Down Expand Up @@ -427,7 +427,7 @@ describe('GatherRunner', function() {
const driver = {
beginDevtoolsLog: asyncFunc,
beginTrace: asyncFunc,
gotoURL: asyncFunc,
gotoURL: async () => ({}),
cleanBrowserCaches: createCheck('calledCleanBrowserCaches'),
setThrottling: asyncFunc,
blockUrlPatterns: asyncFunc,
Expand Down Expand Up @@ -534,9 +534,9 @@ describe('GatherRunner', function() {
// NO_FCP should be ignored because it's a warn pass.
const navigationError = new LHError(LHError.errors.NO_FCP);

const gotoUrlForAboutBlank = jest.fn().mockResolvedValue(null);
const gotoUrlForAboutBlank = jest.fn().mockResolvedValue({});
const gotoUrlForRealUrl = jest.fn()
.mockResolvedValueOnce(requestedUrl)
.mockResolvedValueOnce({finalUrl: requestedUrl, timedOut: false})
.mockRejectedValueOnce(navigationError);
const driver = Object.assign({}, fakeDriver, {
online: true,
Expand Down Expand Up @@ -709,7 +709,7 @@ describe('GatherRunner', function() {
return Promise.resolve();
},
gotoURL() {
return Promise.resolve();
return Promise.resolve({finalUrl: '', timedOut: false});
},
};

Expand Down Expand Up @@ -895,7 +895,7 @@ describe('GatherRunner', function() {
if (url.includes('blank')) return null;
if (firstLoad) {
firstLoad = false;
return requestedUrl;
return {finalUrl: requestedUrl, timedOut: false};
} else {
throw new LHError(LHError.errors.NO_FCP);
}
Expand Down Expand Up @@ -1584,7 +1584,7 @@ describe('GatherRunner', function() {
const unresolvedDriver = Object.assign({}, fakeDriver, {
online: true,
gotoURL() {
return Promise.resolve(requestedUrl);
return Promise.resolve({finalUrl: requestedUrl, timedOut: false});
},
endDevtoolsLog() {
return unresolvedPerfLog;
Expand All @@ -1602,6 +1602,34 @@ describe('GatherRunner', function() {
});
});

it('resolves but warns when page times out', () => {
const config = makeConfig({
passes: [{
recordTrace: true,
passName: 'firstPass',
gatherers: [],
}],
});

const requestedUrl = 'http://www.slow-loading-page.com/';
const timedoutDriver = Object.assign({}, fakeDriver, {
online: true,
gotoURL() {
return Promise.resolve({finalUrl: requestedUrl, timedOut: true});
},
});

return GatherRunner.run(config.passes, {
driver: timedoutDriver,
requestedUrl,
settings: config.settings,
}).then(artifacts => {
assert.equal(artifacts.LighthouseRunWarnings.length, 1);
expect(artifacts.LighthouseRunWarnings[0])
.toBeDisplayString(/too slow/);
});
});

it('resolves when domain name can\'t be resolved but is offline', () => {
const config = makeConfig({
passes: [{
Expand All @@ -1616,7 +1644,7 @@ describe('GatherRunner', function() {
const unresolvedDriver = Object.assign({}, fakeDriver, {
online: false,
gotoURL() {
return Promise.resolve(requestedUrl);
return Promise.resolve({finalUrl: requestedUrl, timedOut: false});
},
endDevtoolsLog() {
return unresolvedPerfLog;
Expand Down
4 changes: 2 additions & 2 deletions lighthouse-core/test/runner-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,10 @@ describe('Runner', () => {
online: true,
// Loads the page successfully in the first pass, fails with PAGE_HUNG in the second.
async gotoURL(url) {
if (url.includes('blank')) return null;
if (url.includes('blank')) return {finalUrl: '', timedOut: false};
if (firstLoad) {
firstLoad = false;
return url;
return {finalUrl: url, timedOut: false};
} else {
throw new LHError(LHError.errors.PAGE_HUNG);
}
Expand Down
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ yarn build-all
```

#### installing protobuf
If changing audit output, you'll need to have v3.7.1 of the protocol-buffer/protobuf compiler installed. (v3.7.1 is known to be compatible, and 3.11.x is known to be **not** compatible.).
If changing audit output, you'll need to have v3.7.1 of the protocol-buffer/protobuf compiler installed. (v3.7.1 is known to be compatible, and 3.11.x is known to be **not** compatible.).

Homebrew should be able to install it correctly: `brew install [email protected].1`
Homebrew should be able to install it correctly: `brew install [email protected]`
Copy link
Collaborator

Choose a reason for hiding this comment

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

were these changes on purpose?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes drive by fix as I needed to run this on my new machine and brew install [email protected] doesn't work, it only supports @3.7

Copy link
Member

Choose a reason for hiding this comment

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

yes drive by fix as I needed to run this on my new machine and brew install [email protected] doesn't work, it only supports @3.7

Looks like @3.7 is the official way to refer to the older version now

(our tests not working as desired on the lastest protobuf release it going to come around to bite us sooner or later...:)


But if you want to do it manually, these steps that have worked well for us:

Expand Down