diff --git a/lighthouse-cli/run.js b/lighthouse-cli/run.js index 6a55fe5f5bb8..52661c3dd7d6 100644 --- a/lighthouse-cli/run.js +++ b/lighthouse-cli/run.js @@ -22,6 +22,7 @@ const opn = require('opn'); const _RUNTIME_ERROR_CODE = 1; const _PROTOCOL_TIMEOUT_EXIT_CODE = 67; +const _PAGE_HUNG_EXIT_CODE = 68; /** * exported for testing @@ -70,6 +71,12 @@ function showProtocolTimeoutError() { process.exit(_PROTOCOL_TIMEOUT_EXIT_CODE); } +/** @param {LH.LighthouseError} err */ +function showPageHungError(err) { + console.error('Page hung:', err.friendlyMessage); + process.exit(_PAGE_HUNG_EXIT_CODE); +} + /** * @param {LH.LighthouseError} err */ @@ -89,6 +96,8 @@ function handleError(err) { showConnectionError(); } else if (err.code === 'CRI_TIMEOUT') { showProtocolTimeoutError(); + } else if (err.code === 'PAGE_HUNG') { + showPageHungError(err); } else { showRuntimeError(err); } diff --git a/lighthouse-cli/test/fixtures/infinite-loop.html b/lighthouse-cli/test/fixtures/infinite-loop.html new file mode 100644 index 000000000000..2b77d7f1cb2a --- /dev/null +++ b/lighthouse-cli/test/fixtures/infinite-loop.html @@ -0,0 +1,22 @@ + + + + +
+ This is the function that never ends + Yes, it just goes on and on my friends. + Some people started computing it, not knowing what it was. + And they'll continue computing it forever just because, + This is the function that never ends. + + + + diff --git a/lighthouse-cli/test/smokehouse/error-config.js b/lighthouse-cli/test/smokehouse/error-config.js new file mode 100644 index 000000000000..771bdf60461e --- /dev/null +++ b/lighthouse-cli/test/smokehouse/error-config.js @@ -0,0 +1,19 @@ +/** + * @license Copyright 2018 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +'use strict'; + +/** + * Config file for sites with various errors, just fail out quickly. + */ +module.exports = { + extends: 'lighthouse:default', + settings: { + maxWaitForLoad: 5000, + onlyAudits: [ + 'first-contentful-paint', + ], + }, +}; diff --git a/lighthouse-cli/test/smokehouse/error-expectations.js b/lighthouse-cli/test/smokehouse/error-expectations.js new file mode 100644 index 000000000000..40d05780607b --- /dev/null +++ b/lighthouse-cli/test/smokehouse/error-expectations.js @@ -0,0 +1,18 @@ +/** + * @license Copyright 2018 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +'use strict'; + +/** + * Expected Lighthouse audit values for sites with various errors. + */ +module.exports = [ + { + requestedUrl: 'http://localhost:10200/infinite-loop.html', + finalUrl: 'http://localhost:10200/infinite-loop.html', + errorCode: 'PAGE_HUNG', + audits: {}, + }, +]; diff --git a/lighthouse-cli/test/smokehouse/run-smoke.js b/lighthouse-cli/test/smokehouse/run-smoke.js index 93e71efaeac9..12f2cb86d7cf 100644 --- a/lighthouse-cli/test/smokehouse/run-smoke.js +++ b/lighthouse-cli/test/smokehouse/run-smoke.js @@ -29,6 +29,11 @@ const SMOKETESTS = [{ config: smokehouseDir + 'a11y/a11y-config.js', expectations: 'a11y/expectations.js', batch: 'parallel-first', +}, { + id: 'errors', + expectations: smokehouseDir + 'error-expectations.js', + config: smokehouseDir + 'error-config.js', + batch: 'errors', }, { id: 'pwa', expectations: smokehouseDir + 'pwa-expectations.js', diff --git a/lighthouse-cli/test/smokehouse/smokehouse.js b/lighthouse-cli/test/smokehouse/smokehouse.js index 8510b4c0bdb1..c3bf337e9535 100755 --- a/lighthouse-cli/test/smokehouse/smokehouse.js +++ b/lighthouse-cli/test/smokehouse/smokehouse.js @@ -15,6 +15,7 @@ const yargs = require('yargs'); const log = require('lighthouse-logger'); const PROTOCOL_TIMEOUT_EXIT_CODE = 67; +const PAGE_HUNG_EXIT_CODE = 68; const RETRIES = 3; const NUMERICAL_EXPECTATION_REGEXP = /^(<=?|>=?)((\d|\.)+)$/; @@ -87,7 +88,7 @@ function runLighthouse(url, configPath, isDebug) { if (runResults.status === PROTOCOL_TIMEOUT_EXIT_CODE) { console.error(`Lighthouse debugger connection timed out ${RETRIES} times. Giving up.`); process.exit(1); - } else if (runResults.status !== 0) { + } else if (runResults.status !== 0 && runResults.status !== PAGE_HUNG_EXIT_CODE) { console.error(`Lighthouse run failed with exit code ${runResults.status}. stderr to follow:`); console.error(runResults.stderr); process.exit(runResults.status); @@ -98,10 +99,14 @@ function runLighthouse(url, configPath, isDebug) { console.error(`STDERR: ${runResults.stderr}`); } + if (runResults.status === PAGE_HUNG_EXIT_CODE) { + return {requestedUrl: url, finalUrl: url, errorCode: 'PAGE_HUNG', audits: {}}; + } + const lhr = fs.readFileSync(outputPath, 'utf8'); if (isDebug) { console.log('LHR output available at: ', outputPath); - } else { + } else if (fs.existsSync(outputPath)) { fs.unlinkSync(outputPath); } diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index 52a0b2ef3973..f5906912306a 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -369,6 +369,7 @@ class Driver { includeCommandLineAPI: true, awaitPromise: true, returnByValue: true, + timeout: 60000, contextId, }; @@ -687,6 +688,25 @@ class Driver { }; } + /** + * Returns whether the page appears to be hung. + * @return {Promise