diff --git a/clients/devtools-entry.js b/clients/devtools-entry.js index 0b434bcfe522..305e76220f3e 100644 --- a/clients/devtools-entry.js +++ b/clients/devtools-entry.js @@ -9,7 +9,7 @@ const lighthouse = require('../lighthouse-core/index.js'); const RawProtocol = require('../lighthouse-core/gather/connections/raw.js'); const log = require('lighthouse-logger'); const {registerLocaleData, lookupLocale} = require('../lighthouse-core/lib/i18n/i18n.js'); -const desktopDense4G = require('../lighthouse-core/config/constants.js').throttling.desktopDense4G; +const constants = require('../lighthouse-core/config/constants.js'); /** @typedef {import('../lighthouse-core/gather/connections/connection.js')} Connection */ @@ -28,9 +28,15 @@ function createConfig(categoryIDs, device) { /** @type {LH.SharedFlagsSettings} */ const settings = { onlyCategories: categoryIDs, + // In DevTools, emulation is applied _before_ Lighthouse starts (to deal with viewport emulation bugs). go/xcnjf + // As a result, we don't double-apply viewport emulation. + screenEmulation: {disabled: true}, }; if (device === 'desktop') { - settings.throttling = desktopDense4G; + settings.throttling = constants.throttling.desktopDense4G; + // UA emulation, however, is lost in the protocol handover from devtools frontend to the lighthouse_worker. So it's always applied. + settings.emulatedUserAgent = constants.userAgents.desktop; + settings.formFactor = 'desktop'; } return { diff --git a/clients/test/lightrider-entry-test.js b/clients/test/lightrider-entry-test.js index c7e6c09fdd43..7ddd9e544f95 100644 --- a/clients/test/lightrider-entry-test.js +++ b/clients/test/lightrider-entry-test.js @@ -80,7 +80,7 @@ describe('lightrider-entry', () => { const lrDevice = 'desktop'; await lhBackground.runLighthouseInLR(mockConnection, url, {}, {lrDevice}); const config = runStub.mock.calls[0][1].config; - assert.equal(config.settings.emulatedFormFactor, 'desktop'); + assert.equal(config.settings.formFactor, 'desktop'); runStub.mockRestore(); }); @@ -94,7 +94,7 @@ describe('lightrider-entry', () => { const lrDevice = 'mobile'; await lhBackground.runLighthouseInLR(mockConnection, url, {}, {lrDevice}); const config = runStub.mock.calls[0][1].config; - assert.equal(config.settings.emulatedFormFactor, 'mobile'); + assert.equal(config.settings.formFactor, 'mobile'); runStub.mockRestore(); }); diff --git a/docs/emulation.md b/docs/emulation.md new file mode 100644 index 000000000000..d47277d042a1 --- /dev/null +++ b/docs/emulation.md @@ -0,0 +1,32 @@ + +# Emulation in Lighthouse + +In Lighthouse, "Emulation" refers to the screen/viewport emulation and UserAgent string spoofing. +["Throttling"](./throttling.md) covers the similar topics around network and CPU throttling/simulation. + +With the default configuration, Lighthouse emulates a mobile device. There's [a `desktop` configuration](../lighthouse-core/config/desktop-config.js), available to CLI users with `--preset=desktop`, which applies a consistent desktop environment and scoring calibration. This is recommended as a replacement for `--emulated-form-factor=desktop`. + +### Advanced emulation setups + +Some products use Lighthouse in scenarios where emulation is applied outside of Lighthouse (e.g. by Puppeteer) or running against Chrome on real mobile devices. + +You must always set `formFactor`. It doesn't control emulation, but it determines how Lighthouse should interpret the run in regards to scoring performance metrics and skipping mobile-only tests in desktop. + +You can choose how `screenEmulation` is applied. It can accept an object of `{width: number, height: number, deviceScaleRatio: number, mobile: boolean, disabled: false}` to apply that screen emulation or an object of `{disabled: true}` if Lighthouse should avoid applying screen emulation. It's typically set to disabled if either emulation is applied outside of Lighthouse, or it's being run on a mobile device. The `mobile` boolean applies overlay scrollbars and a few other mobile-specific screen emulation characteristics. + +You can choose how to handle userAgent emulation. The `emulatedUserAgent` property accepts either a `string` to apply the provided userAgent or a `boolean` -- `true` if the default UA spoofing should be applied (default) or `false` if no UA spoofing should be applied. Typically `false` is used if UA spoofing is applied outside of Lighthouse or on a mobile device. You can also redundantly apply userAgent emulation with no risk. + +If you're using Lighthouse on a mobile device, you want to set `--screenEmulation.disabled` and `--throttling.cpuSlowdownMultiplier=1`. (`--formFactor=mobile` is the default already). + +### Changes made in v7 + +In Lighthouse v7, most of the configuration regarding emulation changed to be more intuitive and clear. The [tracking issue](https://github.com/GoogleChrome/lighthouse/issues/10910 +) captures additional motivations. + +* Removed: The `emulatedFormFactor` property (which determined how emulation is applied). +* Removed: The `TestedAsMobileDevice` artifact. Instead of being inferred, the explicit `formFactor` property is used. +* Removed: The `internalDisableDeviceScreenEmulation` property. It's equivalent to the new `--screenEmulation.disabled=true`. +* Added: The `formFactor` property. +* Added: The `screenEmulation` property. +* Added: The `emulatedUserAgent` property. +* (`throttling` and `throttlingMethod` remain unchanged) diff --git a/docs/plugins.md b/docs/plugins.md index e108ec15af37..06942b7ae875 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -223,7 +223,6 @@ The following artifacts are available for use in the audits of Lighthouse plugin - `RuntimeExceptions` - `ScriptElements` - `Stacks` -- `TestedAsMobileDevice` - `Timing` - `URL` - `ViewportDimensions` @@ -371,6 +370,6 @@ Most artifacts will try to represent as truthfully as possible what was observed - [Field Performance](https://github.com/treosh/lighthouse-plugin-field-performance) - A plugin to gather and display Chrome UX Report field data - [AMP Plugin](https://github.com/ampproject/amp-toolbox/tree/main/packages/lighthouse-plugin-amp) - Runs pages through the AMP validator. - [Publisher Ads Audits](https://github.com/googleads/pub-ads-lighthouse-plugin) - a well-written, but complex, plugin -- [Green Web Foundation](https://github.com/thegreenwebfoundation/lighthouse-plugin-greenhouse) - A plugin to see which domains run on renewable power. +- [Green Web Foundation](https://github.com/thegreenwebfoundation/lighthouse-plugin-greenhouse) - A plugin to see which domains run on renewable power. - [requests-content-md5](https://www.npmjs.com/package/lighthouse-plugin-md5) - Generates MD5 hashes from the content of network requests.. diff --git a/docs/readme.md b/docs/readme.md index 682c8e0fa11b..a82952ce86e8 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -116,7 +116,7 @@ Alternatively, you can instruct Chrome to ignore the invalid certificate by addi Lighthouse can run against a real mobile device. You can follow the [Remote Debugging on Android (Legacy Workflow)](https://developer.chrome.com/devtools/docs/remote-debugging-legacy) up through step 3.3, but the TL;DR is install & run adb, enable USB debugging, then port forward 9222 from the device to the machine with Lighthouse. -You'll likely want to use the CLI flags `--emulated-form-factor=none --throttling.cpuSlowdownMultiplier=1` to disable any additional emulation. +You'll likely want to use the CLI flags `--screenEmulation.disabled --throttling.cpuSlowdownMultiplier=1` to disable any additional emulation. ```sh $ adb kill-server @@ -128,7 +128,7 @@ $ adb devices -l $ adb forward tcp:9222 localabstract:chrome_devtools_remote -$ lighthouse --port=9222 --emulated-form-factor=none --throttling.cpuSlowdownMultiplier=1 https://example.com +$ lighthouse --port=9222 --screenEmulation.disabled --throttling.cpuSlowdownMultiplier=1 https://example.com ``` ## Lighthouse as trace processor diff --git a/docs/understanding-results.md b/docs/understanding-results.md index 7d795f455912..fa399d2c0046 100644 --- a/docs/understanding-results.md +++ b/docs/understanding-results.md @@ -121,7 +121,7 @@ An object containing information about the configuration used by Lighthouse. }, "gatherMode": false, "disableStorageReset": false, - "emulatedFormFactor": "mobile", + "formFactor": "mobile", "blockedUrlPatterns": null, "additionalTraceCategories": null, "extraHeaders": null, diff --git a/lighthouse-cli/bin.js b/lighthouse-cli/bin.js index d0610bc69c2b..fb0784bb3acd 100644 --- a/lighthouse-cli/bin.js +++ b/lighthouse-cli/bin.js @@ -96,12 +96,6 @@ async function begin() { cliFlags.outputPath = 'stdout'; } - // @ts-expect-error - deprecation message for removed disableDeviceEmulation; can remove warning in v6. - if (cliFlags.disableDeviceEmulation) { - log.warn('config', 'The "--disable-device-emulation" has been removed in v5.' + - ' Please use "--emulated-form-factor=none" instead.'); - } - if (cliFlags.precomputedLanternDataPath) { const lanternDataStr = fs.readFileSync(cliFlags.precomputedLanternDataPath, 'utf8'); /** @type {LH.PrecomputedLanternData} */ diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 0180e6dc742e..c12180988d87 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -32,8 +32,8 @@ function getFlags(manualArgv) { 'lighthouse --output=json --output-path=./report.json --save-assets', 'Save trace, screenshots, and named JSON report.') .example( - 'lighthouse --emulated-form-factor=none --throttling-method=provided', - 'Disable device emulation and all throttling') + 'lighthouse --screenEmulation.disabled --throttling-method=provided --no-emulated-user-agent', + 'Disable emulation and all throttling') .example( 'lighthouse --chrome-flags="--window-size=412,660"', 'Launch Chrome with a specific window size') @@ -137,9 +137,18 @@ function getFlags(manualArgv) { default: 'localhost', describe: 'The hostname to use for the debugging protocol.', }, - 'emulated-form-factor': { + 'form-factor': { type: 'string', - describe: 'Controls the emulated device form factor (mobile vs. desktop) if not disabled', + describe: 'Determines how performance metrics are scored and if mobile-only audits are skipped. For desktop, --preset=desktop instead.', + }, + 'screenEmulation': { + describe: 'Sets screen emulation parameters. See also --preset. Use --screenEmulation.disabled to disable. Otherwise set these 4 parameters individually: --screenEmulation.mobile --screenEmulation.width=360 --screenEmulation.height=640 --screenEmulation.deviceScaleFactor=2', + coerce: coerceScreenEmulation, + }, + 'emulatedUserAgent': { + type: 'string', + coerce: coerceOptionalStringBoolean, + describe: 'Sets useragent emulation', }, 'max-wait-for-load': { type: 'number', @@ -185,7 +194,7 @@ function getFlags(manualArgv) { }) .group([ 'save-assets', 'list-all-audits', 'list-trace-categories', 'print-config', 'additional-trace-categories', - 'config-path', 'preset', 'chrome-flags', 'port', 'hostname', 'emulated-form-factor', + 'config-path', 'preset', 'chrome-flags', 'port', 'hostname', 'form-factor', 'screenEmulation', 'emulatedUserAgent', 'max-wait-for-load', 'enable-error-reporting', 'gather-mode', 'audit-mode', 'only-audits', 'only-categories', 'skip-audits', 'budget-path', ], 'Configuration:') @@ -278,9 +287,9 @@ function getFlags(manualArgv) { }) // Choices added outside of `options()` and cast so tsc picks them up. - .choices('emulated-form-factor', /** @type {['mobile', 'desktop', 'none']} */ (['mobile', 'desktop', 'none'])) + .choices('form-factor', /** @type {['mobile', 'desktop']} */ (['mobile', 'desktop'])) .choices('throttling-method', /** @type {['devtools', 'provided', 'simulate']} */ (['devtools', 'provided', 'simulate'])) - .choices('preset', /** @type {['perf', 'experimental']} */ (['perf', 'experimental'])) + .choices('preset', /** @type {['perf', 'experimental', 'desktop']} */ (['perf', 'experimental', 'desktop'])) .check(argv => { // Lighthouse doesn't need a URL if... @@ -418,6 +427,48 @@ function coerceThrottling(value) { return throttlingSettings; } +/** + * Take yarg's unchecked object value and ensure it is a proper LH.screenEmulationSettings. + * @param {unknown} value + * @return {Partial} + */ +function coerceScreenEmulation(value) { + if (!isObjectOfUnknownValues(value)) { + throw new Error(`Invalid value: Argument 'screenEmulation' must be an object, specified per-property ('screenEmulation.width', 'screenEmulation.deviceScaleFactor', etc)`); + } + + /** @type {Array} */ + const keys = ['width', 'height', 'deviceScaleFactor', 'mobile', 'disabled']; + /** @type {Partial} */ + const screenEmulationSettings = {}; + + for (const key of keys) { + const possibleSetting = value[key]; + switch (key) { + case 'width': + case 'height': + case 'deviceScaleFactor': + if (possibleSetting !== undefined && typeof possibleSetting !== 'number') { + throw new Error(`Invalid value: 'screenEmulation.${key}' must be a number`); + } + screenEmulationSettings[key] = possibleSetting; + + break; + case 'mobile': + case 'disabled': + if (possibleSetting !== undefined && typeof possibleSetting !== 'boolean') { + throw new Error(`Invalid value: 'screenEmulation.${key}' must be a boolean`); + } + screenEmulationSettings[key] = possibleSetting; + break; + default: + throw new Error(`Unrecognized screenEmulation option: ${key}`); + } + } + + return screenEmulationSettings; +} + module.exports = { getFlags, }; diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap index 5be735b52920..c16c1d07bec0 100644 --- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap +++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap @@ -1461,10 +1461,10 @@ Object { "budgets": null, "channel": "cli", "disableStorageReset": false, - "emulatedFormFactor": "mobile", + "emulatedUserAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Mobile Safari/537.36 Chrome-Lighthouse", "extraHeaders": null, + "formFactor": "mobile", "gatherMode": false, - "internalDisableDeviceScreenEmulation": false, "locale": "en-US", "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, @@ -1474,6 +1474,13 @@ Object { "html", ], "precomputedLanternData": null, + "screenEmulation": Object { + "deviceScaleFactor": 2.625, + "disabled": false, + "height": 640, + "mobile": true, + "width": 360, + }, "skipAudits": null, "throttling": Object { "cpuSlowdownMultiplier": 4, @@ -1607,10 +1614,10 @@ Object { "budgets": null, "channel": "cli", "disableStorageReset": false, - "emulatedFormFactor": "mobile", + "emulatedUserAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Mobile Safari/537.36 Chrome-Lighthouse", "extraHeaders": null, + "formFactor": "mobile", "gatherMode": false, - "internalDisableDeviceScreenEmulation": false, "locale": "en-US", "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, @@ -1622,6 +1629,13 @@ Object { "json", ], "precomputedLanternData": null, + "screenEmulation": Object { + "deviceScaleFactor": 2.625, + "disabled": false, + "height": 640, + "mobile": true, + "width": 360, + }, "skipAudits": null, "throttling": Object { "cpuSlowdownMultiplier": 4, diff --git a/lighthouse-cli/test/cli/index-test.js b/lighthouse-cli/test/cli/index-test.js index 1b002095ed79..dd0b495c9569 100644 --- a/lighthouse-cli/test/cli/index-test.js +++ b/lighthouse-cli/test/cli/index-test.js @@ -95,4 +95,43 @@ describe('CLI Tests', function() { expect(config).toMatchSnapshot(); }); }); + + describe('preset', () => { + it('desktop should set appropriate config', () => { + const ret = spawnSync('node', [indexPath, '--print-config', '--preset=desktop'], { + encoding: 'utf8', + }); + + const config = JSON.parse(ret.stdout); + const {emulatedUserAgent, formFactor, screenEmulation, throttling, throttlingMethod} = + config.settings; + const emulationSettings = + {emulatedUserAgent, formFactor, screenEmulation, throttling, throttlingMethod}; + + /* eslint-disable max-len */ + expect(emulationSettings).toMatchInlineSnapshot(` + Object { + "emulatedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Safari/537.36 Chrome-Lighthouse", + "formFactor": "desktop", + "screenEmulation": Object { + "deviceScaleFactor": 1, + "disabled": false, + "height": 940, + "mobile": false, + "width": 1350, + }, + "throttling": Object { + "cpuSlowdownMultiplier": 1, + "downloadThroughputKbps": 0, + "requestLatencyMs": 0, + "rttMs": 40, + "throughputKbps": 10240, + "uploadThroughputKbps": 0, + }, + "throttlingMethod": "simulate", + } + `); + /* eslint-enable max-len */ + }); + }); }); diff --git a/lighthouse-cli/test/smokehouse/test-definitions/dobetterweb/dbw-expectations.js b/lighthouse-cli/test/smokehouse/test-definitions/dobetterweb/dbw-expectations.js index 6e96b1865186..aed69b9e8fe8 100644 --- a/lighthouse-cli/test/smokehouse/test-definitions/dobetterweb/dbw-expectations.js +++ b/lighthouse-cli/test/smokehouse/test-definitions/dobetterweb/dbw-expectations.js @@ -13,7 +13,6 @@ const expectations = [ { artifacts: { HostFormFactor: 'desktop', - TestedAsMobileDevice: true, Stacks: [{ id: 'jquery', }, { diff --git a/lighthouse-cli/test/smokehouse/test-definitions/screenshot/screenshot-config.js b/lighthouse-cli/test/smokehouse/test-definitions/screenshot/screenshot-config.js index 128a42c1dc6c..c90cc351fcb0 100644 --- a/lighthouse-cli/test/smokehouse/test-definitions/screenshot/screenshot-config.js +++ b/lighthouse-cli/test/smokehouse/test-definitions/screenshot/screenshot-config.js @@ -9,6 +9,13 @@ module.exports = { extends: 'lighthouse:default', settings: { - emulatedFormFactor: 'desktop', + formFactor: 'desktop', + screenEmulation: { + width: 1024, + height: 768, + deviceScaleFactor: 1, + mobile: false, + disabled: false, + }, }, }; diff --git a/lighthouse-core/audits/content-width.js b/lighthouse-core/audits/content-width.js index 0c214925eae1..718ae991abf2 100644 --- a/lighthouse-core/audits/content-width.js +++ b/lighthouse-core/audits/content-width.js @@ -38,21 +38,21 @@ class ContentWidth extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - requiredArtifacts: ['ViewportDimensions', 'TestedAsMobileDevice'], + requiredArtifacts: ['ViewportDimensions'], }; } /** * @param {LH.Artifacts} artifacts + * @param {LH.Audit.Context} context * @return {LH.Audit.Product} */ - static audit(artifacts) { - const IsMobile = artifacts.TestedAsMobileDevice; + static audit(artifacts, context) { const viewportWidth = artifacts.ViewportDimensions.innerWidth; const windowWidth = artifacts.ViewportDimensions.outerWidth; const widthsMatch = viewportWidth === windowWidth; - if (!IsMobile) { + if (context.settings.formFactor === 'desktop') { return { score: 1, notApplicable: true, diff --git a/lighthouse-core/audits/metrics/first-contentful-paint.js b/lighthouse-core/audits/metrics/first-contentful-paint.js index cbc0dc48d04a..226c9b73139c 100644 --- a/lighthouse-core/audits/metrics/first-contentful-paint.js +++ b/lighthouse-core/audits/metrics/first-contentful-paint.js @@ -27,7 +27,7 @@ class FirstContentfulPaint extends Audit { title: str_(i18n.UIStrings.firstContentfulPaintMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['traces', 'devtoolsLogs'], }; } @@ -65,8 +65,7 @@ class FirstContentfulPaint extends Audit { const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; const metricComputationData = {trace, devtoolsLog, settings: context.settings}; const metricResult = await ComputedFcp.request(metricComputationData, context); - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/metrics/first-cpu-idle.js b/lighthouse-core/audits/metrics/first-cpu-idle.js index f67de2aa03c2..192c70432fb4 100644 --- a/lighthouse-core/audits/metrics/first-cpu-idle.js +++ b/lighthouse-core/audits/metrics/first-cpu-idle.js @@ -27,7 +27,7 @@ class FirstCPUIdle extends Audit { title: str_(i18n.UIStrings.firstCPUIdleMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['traces', 'devtoolsLogs'], }; } @@ -68,8 +68,8 @@ class FirstCPUIdle extends Audit { const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; const metricComputationData = {trace, devtoolsLog, settings: context.settings}; const metricResult = await ComputedFci.request(metricComputationData, context); - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; + return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/metrics/first-meaningful-paint.js b/lighthouse-core/audits/metrics/first-meaningful-paint.js index e9de3fabc0f1..cf6390cb102e 100644 --- a/lighthouse-core/audits/metrics/first-meaningful-paint.js +++ b/lighthouse-core/audits/metrics/first-meaningful-paint.js @@ -27,7 +27,7 @@ class FirstMeaningfulPaint extends Audit { title: str_(i18n.UIStrings.firstMeaningfulPaintMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['traces', 'devtoolsLogs'], }; } @@ -68,8 +68,8 @@ class FirstMeaningfulPaint extends Audit { const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; const metricComputationData = {trace, devtoolsLog, settings: context.settings}; const metricResult = await ComputedFmp.request(metricComputationData, context); - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; + return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/metrics/interactive.js b/lighthouse-core/audits/metrics/interactive.js index b4435b47735a..ad3f36bb8028 100644 --- a/lighthouse-core/audits/metrics/interactive.js +++ b/lighthouse-core/audits/metrics/interactive.js @@ -33,7 +33,7 @@ class InteractiveMetric extends Audit { title: str_(i18n.UIStrings.interactiveMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['traces', 'devtoolsLogs'], }; } @@ -72,8 +72,8 @@ class InteractiveMetric extends Audit { const metricComputationData = {trace, devtoolsLog, settings: context.settings}; const metricResult = await Interactive.request(metricComputationData, context); const timeInMs = metricResult.timing; - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; + return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/metrics/largest-contentful-paint.js b/lighthouse-core/audits/metrics/largest-contentful-paint.js index a560fe92fa2d..6cf411138f8d 100644 --- a/lighthouse-core/audits/metrics/largest-contentful-paint.js +++ b/lighthouse-core/audits/metrics/largest-contentful-paint.js @@ -28,7 +28,7 @@ class LargestContentfulPaint extends Audit { title: str_(i18n.UIStrings.largestContentfulPaintMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['HostUserAgent', 'traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['HostUserAgent', 'traces', 'devtoolsLogs'], }; } @@ -93,8 +93,8 @@ class LargestContentfulPaint extends Audit { throw err; } - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; + return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/metrics/speed-index.js b/lighthouse-core/audits/metrics/speed-index.js index df5306fcf2d2..c0b191752b8b 100644 --- a/lighthouse-core/audits/metrics/speed-index.js +++ b/lighthouse-core/audits/metrics/speed-index.js @@ -27,7 +27,7 @@ class SpeedIndex extends Audit { title: str_(i18n.UIStrings.speedIndexMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['traces', 'devtoolsLogs'], }; } @@ -67,8 +67,8 @@ class SpeedIndex extends Audit { const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; const metricComputationData = {trace, devtoolsLog, settings: context.settings}; const metricResult = await ComputedSi.request(metricComputationData, context); - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; + return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/metrics/total-blocking-time.js b/lighthouse-core/audits/metrics/total-blocking-time.js index 038535bdc729..43a2126a84cd 100644 --- a/lighthouse-core/audits/metrics/total-blocking-time.js +++ b/lighthouse-core/audits/metrics/total-blocking-time.js @@ -27,7 +27,7 @@ class TotalBlockingTime extends Audit { title: str_(i18n.UIStrings.totalBlockingTimeMetric), description: str_(UIStrings.description), scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, - requiredArtifacts: ['traces', 'devtoolsLogs', 'TestedAsMobileDevice'], + requiredArtifacts: ['traces', 'devtoolsLogs'], }; } @@ -83,8 +83,8 @@ class TotalBlockingTime extends Audit { const metricComputationData = {trace, devtoolsLog, settings: context.settings}; const metricResult = await ComputedTBT.request(metricComputationData, context); - const isDesktop = artifacts.TestedAsMobileDevice === false; - const options = isDesktop ? context.options.desktop : context.options.mobile; + const options = context.options[context.settings.formFactor]; + return { score: Audit.computeLogNormalScore( diff --git a/lighthouse-core/audits/seo/font-size.js b/lighthouse-core/audits/seo/font-size.js index 0484d20eeb4a..20944eabfb27 100644 --- a/lighthouse-core/audits/seo/font-size.js +++ b/lighthouse-core/audits/seo/font-size.js @@ -233,7 +233,7 @@ class FontSize extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - requiredArtifacts: ['FontSize', 'URL', 'MetaElements', 'TestedAsMobileDevice'], + requiredArtifacts: ['FontSize', 'URL', 'MetaElements'], }; } @@ -243,7 +243,7 @@ class FontSize extends Audit { * @return {Promise} */ static async audit(artifacts, context) { - if (!artifacts.TestedAsMobileDevice) { + if (context.settings.formFactor === 'desktop') { // Font size isn't important to desktop SEO return { score: 1, diff --git a/lighthouse-core/audits/seo/tap-targets.js b/lighthouse-core/audits/seo/tap-targets.js index b9bb038e5ee0..3125dde3deac 100644 --- a/lighthouse-core/audits/seo/tap-targets.js +++ b/lighthouse-core/audits/seo/tap-targets.js @@ -268,7 +268,7 @@ class TapTargets extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - requiredArtifacts: ['MetaElements', 'TapTargets', 'TestedAsMobileDevice'], + requiredArtifacts: ['MetaElements', 'TapTargets'], }; } @@ -278,7 +278,7 @@ class TapTargets extends Audit { * @return {Promise} */ static async audit(artifacts, context) { - if (!artifacts.TestedAsMobileDevice) { + if (context.settings.formFactor === 'desktop') { // Tap target sizes aren't important for desktop SEO, so disable the audit there. // On desktop people also tend to have more precise pointing devices than fingers. return { diff --git a/lighthouse-core/config/config.js b/lighthouse-core/config/config.js index a9ae01b88a26..4aa9bd82d216 100644 --- a/lighthouse-core/config/config.js +++ b/lighthouse-core/config/config.js @@ -28,7 +28,6 @@ const {requireAudits, resolveModule} = require('./config-helpers.js'); const BASE_ARTIFACT_BLANKS = { fetchTime: '', LighthouseRunWarnings: '', - TestedAsMobileDevice: '', HostFormFactor: '', HostUserAgent: '', NetworkUserAgent: '', @@ -162,6 +161,48 @@ function assertValidGatherer(gathererInstance, gathererName) { } } + +/** + * Validate the LH.Flags + * @param {LH.Flags} flags + */ +function assertValidFlags(flags) { + // COMPAT: compatibility layer for devtools as it uses the old way and we need tests to pass + // TODO(paulirish): remove this from LH once emulation refactor has rolled into DevTools + // @ts-expect-error Deprecated flag + if (flags.channel === 'devtools' && flags.internalDisableDeviceScreenEmulation) { + // @ts-expect-error Deprecated flag + flags.formFactor = flags.emulatedFormFactor; + // @ts-expect-error Deprecated flag + flags.emulatedFormFactor = flags.internalDisableDeviceScreenEmulation = undefined; + } + + + // @ts-expect-error Checking for removed flags + if (flags.emulatedFormFactor || flags.internalDisableDeviceScreenEmulation) { + throw new Error('Invalid emulation flag. Emulation configuration changed in LH 7.0. See https://github.com/GoogleChrome/lighthouse/blob/master/docs/emulation.md'); + } +} + +/** + * Validate the settings after they've been built + * @param {LH.Config.Settings} settings + */ +function assertValidSettings(settings) { + if (!settings.formFactor) { + throw new Error(`\`settings.formFactor\` must be defined as 'mobile' or 'desktop'. See https://github.com/GoogleChrome/lighthouse/blob/master/docs/emulation.md`); + } + + if (!settings.screenEmulation.disabled) { + // formFactor doesn't control emulation. So we don't want a mismatch: + // Bad mismatch A: user wants mobile emulation but scoring is configured for desktop + // Bad mismtach B: user wants everything desktop and set formFactor, but accidentally not screenEmulation + if (settings.screenEmulation.mobile !== (settings.formFactor === 'mobile')) { + throw new Error(`Screen emulation mobile setting (${settings.screenEmulation.mobile}) does not match formFactor setting (${settings.formFactor}). See https://github.com/GoogleChrome/lighthouse/blob/master/docs/emulation.md`); + } + } +} + /** * Throws if pluginName is invalid or (somehow) collides with a category in the * configJSON being added to. @@ -334,6 +375,9 @@ class Config { // Validate and merge in plugins (if any). configJSON = Config.mergePlugins(configJSON, flags, configDir); + if (flags) { + assertValidFlags(flags); + } const settings = Config.initSettings(configJSON.settings, flags); // Augment passes with necessary defaults and require gatherers. @@ -354,6 +398,7 @@ class Config { Config.filterConfigIfNeeded(this); + assertValidSettings(this.settings); assertValidPasses(this.passes, this.audits); assertValidCategories(this.categories, this.audits, this.groups); @@ -484,6 +529,12 @@ class Config { // Locale is special and comes only from flags/settings/lookupLocale. settingsWithFlags.locale = locale; + // Default constants uses the mobile UA. Explicitly stating to true asks LH to use the associated UA. + // It's a little awkward, but the alternatives are not allowing `true` or a dedicated `disableUAEmulation` setting. + if (settingsWithFlags.emulatedUserAgent === true) { + settingsWithFlags.emulatedUserAgent = constants.userAgents[settingsWithFlags.formFactor]; + } + return settingsWithFlags; } diff --git a/lighthouse-core/config/constants.js b/lighthouse-core/config/constants.js index 55cb8c347746..1d40fa893b19 100644 --- a/lighthouse-core/config/constants.js +++ b/lighthouse-core/config/constants.js @@ -50,18 +50,62 @@ const throttling = { }, }; +/** + * @type {Required} + */ +const MOTOG4_EMULATION_METRICS = { + mobile: true, + width: 360, + height: 640, + // Moto G4 is really 3, but a higher value here works against + // our perf recommendations. + // https://github.com/GoogleChrome/lighthouse/issues/10741#issuecomment-626903508 + deviceScaleFactor: 2.625, + disabled: false, +}; + +/** + * Desktop metrics adapted from emulated_devices/module.json + * @type {Required} + */ +const DESKTOP_EMULATION_METRICS = { + mobile: false, + width: 1350, + height: 940, + deviceScaleFactor: 1, + disabled: false, +}; + +const screenEmulationMetrics = { + mobile: MOTOG4_EMULATION_METRICS, + desktop: DESKTOP_EMULATION_METRICS, +}; + +// eslint-disable-next-line max-len +const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Mobile Safari/537.36 Chrome-Lighthouse'; +// eslint-disable-next-line max-len +const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Safari/537.36 Chrome-Lighthouse'; + +const userAgents = { + mobile: MOTOG4_USERAGENT, + desktop: DESKTOP_USERAGENT, +}; + /** @type {LH.Config.Settings} */ const defaultSettings = { output: 'json', maxWaitForFcp: 30 * 1000, maxWaitForLoad: 45 * 1000, - throttlingMethod: 'simulate', + + formFactor: 'mobile', throttling: throttling.mobileSlow4G, + throttlingMethod: 'simulate', + screenEmulation: screenEmulationMetrics.mobile, + emulatedUserAgent: userAgents.mobile, + auditMode: false, gatherMode: false, disableStorageReset: false, - emulatedFormFactor: 'mobile', - internalDisableDeviceScreenEmulation: false, channel: 'node', // the following settings have no defaults but we still want ensure that `key in settings` @@ -101,6 +145,8 @@ const nonSimulatedPassConfigOverrides = { module.exports = { throttling, + screenEmulationMetrics, + userAgents, defaultSettings, defaultPassConfig, nonSimulatedPassConfigOverrides, diff --git a/lighthouse-core/config/desktop-config.js b/lighthouse-core/config/desktop-config.js new file mode 100644 index 000000000000..2703b4d9b645 --- /dev/null +++ b/lighthouse-core/config/desktop-config.js @@ -0,0 +1,21 @@ +/** + * @license Copyright 2020 The Lighthouse Authors. 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'; + +const constants = require('./constants.js'); + +/** @type {LH.Config.Json} */ +const config = { + extends: 'lighthouse:default', + settings: { + formFactor: 'desktop', + throttling: constants.throttling.desktopDense4G, + screenEmulation: constants.screenEmulationMetrics.desktop, + emulatedUserAgent: constants.userAgents.desktop, + }, +}; + +module.exports = config; diff --git a/lighthouse-core/config/lr-desktop-config.js b/lighthouse-core/config/lr-desktop-config.js index b71384322604..04f164c88d5c 100644 --- a/lighthouse-core/config/lr-desktop-config.js +++ b/lighthouse-core/config/lr-desktop-config.js @@ -5,7 +5,7 @@ */ 'use strict'; -const desktopDense4G = require('./constants.js').throttling.desktopDense4G; +const constants = require('./constants.js'); /** @type {LH.Config.Json} */ const config = { @@ -13,8 +13,10 @@ const config = { settings: { maxWaitForFcp: 15 * 1000, maxWaitForLoad: 35 * 1000, - emulatedFormFactor: 'desktop', - throttling: desktopDense4G, + formFactor: 'desktop', + throttling: constants.throttling.desktopDense4G, + screenEmulation: constants.screenEmulationMetrics.desktop, + emulatedUserAgent: constants.userAgents.desktop, // Skip the h2 audit so it doesn't lie to us. See https://github.com/GoogleChrome/lighthouse/issues/6539 skipAudits: ['uses-http2'], }, diff --git a/lighthouse-core/config/lr-mobile-config.js b/lighthouse-core/config/lr-mobile-config.js index 2689b552b14e..66719f7fa982 100644 --- a/lighthouse-core/config/lr-mobile-config.js +++ b/lighthouse-core/config/lr-mobile-config.js @@ -11,6 +11,7 @@ const config = { settings: { maxWaitForFcp: 15 * 1000, maxWaitForLoad: 35 * 1000, + // lighthouse:default is mobile by default }, // Skip the h2 audit so it doesn't lie to us. See https://github.com/GoogleChrome/lighthouse/issues/6539 skipAudits: ['uses-http2'], diff --git a/lighthouse-core/fraggle-rock/api.js b/lighthouse-core/fraggle-rock/api.js index 184571a8c1f2..5d1947dc5365 100644 --- a/lighthouse-core/fraggle-rock/api.js +++ b/lighthouse-core/fraggle-rock/api.js @@ -38,7 +38,6 @@ async function snapshot(options) { settings: config.settings, // TODO(FR-COMPAT): convert these to regular artifacts HostFormFactor: 'mobile', - TestedAsMobileDevice: true, HostUserAgent: 'unknown', NetworkUserAgent: 'unknown', BenchmarkIndex: 0, diff --git a/lighthouse-core/gather/gather-runner.js b/lighthouse-core/gather/gather-runner.js index d6f17ad1eb40..e5f588742035 100644 --- a/lighthouse-core/gather/gather-runner.js +++ b/lighthouse-core/gather/gather-runner.js @@ -552,17 +552,13 @@ class GatherRunner { static async initializeBaseArtifacts(options) { const hostUserAgent = (await options.driver.getBrowserVersion()).userAgent; - const {emulatedFormFactor} = options.settings; // Whether Lighthouse was run on a mobile device (i.e. not on a desktop machine). const HostFormFactor = hostUserAgent.includes('Android') || hostUserAgent.includes('Mobile') ? 'mobile' : 'desktop'; - const TestedAsMobileDevice = emulatedFormFactor === 'mobile' || - (emulatedFormFactor !== 'desktop' && HostFormFactor === 'mobile'); return { fetchTime: (new Date()).toJSON(), LighthouseRunWarnings: [], - TestedAsMobileDevice, HostFormFactor, HostUserAgent: hostUserAgent, NetworkUserAgent: '', // updated later diff --git a/lighthouse-core/gather/gatherers/full-page-screenshot.js b/lighthouse-core/gather/gatherers/full-page-screenshot.js index 3e41f8c3b2a2..a807965f0651 100644 --- a/lighthouse-core/gather/gatherers/full-page-screenshot.js +++ b/lighthouse-core/gather/gatherers/full-page-screenshot.js @@ -42,7 +42,8 @@ class FullPageScreenshot extends Gatherer { const height = Math.min(metrics.contentSize.height, MAX_SCREENSHOT_HEIGHT); await driver.sendCommand('Emulation.setDeviceMetricsOverride', { - mobile: passContext.baseArtifacts.TestedAsMobileDevice, + // If we're gathering with mobile screenEmulation on (overlay scrollbars, etc), continue to use that for this screenshot. + mobile: passContext.settings.screenEmulation.mobile, height, width, deviceScaleFactor: 1, @@ -116,8 +117,7 @@ class FullPageScreenshot extends Gatherer { // In case some other program is controlling emulation, try to remember what the device looks // like now and reset after gatherer is done. - const lighthouseControlsEmulation = passContext.settings.emulatedFormFactor !== 'none' && - !passContext.settings.internalDisableDeviceScreenEmulation; + const lighthouseControlsEmulation = !passContext.settings.screenEmulation.disabled; try { return { @@ -151,7 +151,7 @@ class FullPageScreenshot extends Gatherer { observedDeviceMetrics.screenOrientation.type = snakeCaseToCamelCase(observedDeviceMetrics.screenOrientation.type); await driver.sendCommand('Emulation.setDeviceMetricsOverride', { - mobile: passContext.baseArtifacts.TestedAsMobileDevice, // could easily be wrong + mobile: passContext.settings.formFactor === 'mobile', ...observedDeviceMetrics, }); } diff --git a/lighthouse-core/lib/emulation.js b/lighthouse-core/lib/emulation.js index 405362e9adf4..70ce2fc0c9c2 100644 --- a/lighthouse-core/lib/emulation.js +++ b/lighthouse-core/lib/emulation.js @@ -7,44 +7,6 @@ /** @typedef {import('../gather/driver.js')} Driver */ -/** - * @type {LH.Crdp.Emulation.SetDeviceMetricsOverrideRequest} - */ -const MOTOG4_EMULATION_METRICS = { - mobile: true, - screenWidth: 360, - screenHeight: 640, - width: 360, - height: 640, - positionX: 0, - positionY: 0, - scale: 1, - // Moto G4 is really 3, but a higher value here works against - // our perf recommendations. - // https://github.com/GoogleChrome/lighthouse/issues/10741#issuecomment-626903508 - deviceScaleFactor: 2.625, - screenOrientation: { - angle: 0, - type: 'portraitPrimary', - }, -}; - -/** - * Desktop metrics adapted from emulated_devices/module.json - * @type {LH.Crdp.Emulation.SetDeviceMetricsOverrideRequest} - */ -const DESKTOP_EMULATION_METRICS = { - mobile: false, - width: 1350, - height: 940, - deviceScaleFactor: 1, -}; - -// eslint-disable-next-line max-len -const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Mobile Safari/537.36 Chrome-Lighthouse'; -// eslint-disable-next-line max-len -const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Safari/537.36 Chrome-Lighthouse'; - const OFFLINE_METRICS = { offline: true, // values of 0 remove any active throttling. crbug.com/456324#c9 @@ -64,19 +26,6 @@ const NO_CPU_THROTTLE_METRICS = { rate: 1, }; -const emulationParams = { - mobile: { - userAgent: MOTOG4_USERAGENT, - metrics: MOTOG4_EMULATION_METRICS, - touchEnabled: true, - }, - desktop: { - userAgent: DESKTOP_USERAGENT, - metrics: DESKTOP_EMULATION_METRICS, - touchEnabled: false, - }, -}; - /** * * @param {Driver} driver @@ -84,20 +33,21 @@ const emulationParams = { * @return {Promise} */ async function emulate(driver, settings) { - if (!settings.emulatedFormFactor || settings.emulatedFormFactor === 'none') return; - const params = emulationParams[settings.emulatedFormFactor]; - - // In DevTools, emulation is applied before Lighthouse starts (to deal with viewport emulation bugs) - // As a result, we don't double-apply viewport emulation (devtools sets `internalDisableDeviceScreenEmulation`). - // UA emulation, however, is lost in the protocol handover from devtools frontend to the audits_worker. So it's always applied. - - // Network.enable must be called for UA overriding to work - await driver.sendCommand('Network.enable'); - await driver.sendCommand('Network.setUserAgentOverride', {userAgent: params.userAgent}); - - if (!settings.internalDisableDeviceScreenEmulation) { - await driver.sendCommand('Emulation.setDeviceMetricsOverride', params.metrics); - await driver.sendCommand('Emulation.setTouchEmulationEnabled', {enabled: params.touchEnabled}); + if (settings.emulatedUserAgent !== false) { + // Network.enable must be called for UA overriding to work + await driver.sendCommand('Network.enable'); + await driver.sendCommand('Network.setUserAgentOverride', { + userAgent: /** @type {string} */ (settings.emulatedUserAgent), + }); + } + // See devtools-entry for one usecase for disabling screenEmulation + if (settings.screenEmulation.disabled !== true) { + const {width, height, deviceScaleFactor, mobile} = settings.screenEmulation; + const params = {width, height, deviceScaleFactor, mobile}; + await driver.sendCommand('Emulation.setDeviceMetricsOverride', params); + await driver.sendCommand('Emulation.setTouchEmulationEnabled', { + enabled: params.mobile, + }); } } @@ -163,6 +113,4 @@ module.exports = { enableCPUThrottling, disableCPUThrottling, goOffline, - MOBILE_USERAGENT: MOTOG4_USERAGENT, - DESKTOP_USERAGENT, }; diff --git a/lighthouse-core/lib/proto-preprocessor.js b/lighthouse-core/lib/proto-preprocessor.js index e7a121051afe..4b7d0a554b5f 100644 --- a/lighthouse-core/lib/proto-preprocessor.js +++ b/lighthouse-core/lib/proto-preprocessor.js @@ -29,10 +29,10 @@ function processForProto(lhr) { // 'ignore unknown fields' in the language of conversion. if (reportJson.configSettings) { // The settings that are in both proto and LHR - const {emulatedFormFactor, locale, onlyCategories, channel} = reportJson.configSettings; + const {formFactor, locale, onlyCategories, channel} = reportJson.configSettings; // @ts-expect-error - intentionally only a subset of settings. - reportJson.configSettings = {emulatedFormFactor, locale, onlyCategories, channel}; + reportJson.configSettings = {formFactor, locale, onlyCategories, channel}; } // Remove runtimeError if it is NO_ERROR diff --git a/lighthouse-core/lib/sentry.js b/lighthouse-core/lib/sentry.js index ae48200c6001..895c79edc4ad 100644 --- a/lighthouse-core/lib/sentry.js +++ b/lighthouse-core/lib/sentry.js @@ -110,7 +110,7 @@ function init(opts) { const context = Object.assign({ url: opts.url, - emulatedFormFactor: opts.flags.emulatedFormFactor, + formFactor: opts.flags.formFactor, throttlingMethod: opts.flags.throttlingMethod, }, opts.flags.throttling); Sentry.mergeContext({extra: Object.assign({}, opts.environmentData.extra, context)}); diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js index c0e8762aa4f9..e6fd233ba9de 100644 --- a/lighthouse-core/report/html/renderer/performance-category-renderer.js +++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js @@ -150,7 +150,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { const paramPairs = [...metricPairs]; if (Util.reportJson) { - paramPairs.push(['device', Util.reportJson.configSettings.emulatedFormFactor]); + paramPairs.push(['device', Util.reportJson.configSettings.formFactor]); paramPairs.push(['version', Util.reportJson.lighthouseVersion]); } diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index 9a80d3fbf80b..3be1e2b78323 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -424,12 +424,10 @@ class Util { networkThrottling = Util.i18n.strings.runtimeUnknown; } - let deviceEmulation = Util.i18n.strings.runtimeNoEmulation; - if (settings.emulatedFormFactor === 'mobile') { - deviceEmulation = Util.i18n.strings.runtimeMobileEmulation; - } else if (settings.emulatedFormFactor === 'desktop') { - deviceEmulation = Util.i18n.strings.runtimeDesktopEmulation; - } + // TODO(paulirish): revise Runtime Settings strings: https://github.com/GoogleChrome/lighthouse/pull/11796 + const deviceEmulation = settings.formFactor === 'mobile' + ? Util.i18n.strings.runtimeMobileEmulation + : Util.i18n.strings.runtimeDesktopEmulation; return { deviceEmulation, diff --git a/lighthouse-core/scripts/build-report-for-autodeployment.js b/lighthouse-core/scripts/build-report-for-autodeployment.js index 47d126f76ac2..752412db14d2 100644 --- a/lighthouse-core/scripts/build-report-for-autodeployment.js +++ b/lighthouse-core/scripts/build-report-for-autodeployment.js @@ -76,7 +76,6 @@ async function generateErrorLHR() { LighthouseRunWarnings: [ `Something went wrong with recording the trace over your page load. Please run Lighthouse again. (NO_FCP)`, // eslint-disable-line max-len ], - TestedAsMobileDevice: true, HostFormFactor: 'desktop', HostUserAgent: 'Mozilla/5.0 ErrorUserAgent Chrome/66', NetworkUserAgent: 'Mozilla/5.0 ErrorUserAgent Chrome/66', diff --git a/lighthouse-core/test/audits/content-width-test.js b/lighthouse-core/test/audits/content-width-test.js index ae88d8211f96..384586d31502 100644 --- a/lighthouse-core/test/audits/content-width-test.js +++ b/lighthouse-core/test/audits/content-width-test.js @@ -6,22 +6,31 @@ 'use strict'; const Audit = require('../../audits/content-width.js'); +const constants = require('../../config/constants.js'); const assert = require('assert').strict; /* eslint-env jest */ +/** @param {LH.SharedFlagsSettings['formFactor']} formFactor */ +const getFakeContext = (formFactor = 'mobile') => ({ + computedCache: new Map(), + settings: { + formFactor: formFactor, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); + describe('Mobile-friendly: content-width audit', () => { it('fails when scroll width differs from viewport width', () => { - const result = Audit.audit({ - TestedAsMobileDevice: true, + const product = Audit.audit({ ViewportDimensions: { innerWidth: 100, outerWidth: 300, }, - }); + }, getFakeContext()); - assert.equal(result.score, 0); - assert.ok(result.explanation); + assert.equal(product.score, 0); + assert.ok(product.explanation); }); it('passes when widths match', () => { @@ -31,16 +40,17 @@ describe('Mobile-friendly: content-width audit', () => { innerWidth: 300, outerWidth: 300, }, - }, {settings: {emulatedFormFactor: 'mobile'}}).score, 1); + }, getFakeContext()).score, 1); }); it('not applicable when run on desktop', () => { - return assert.equal(Audit.audit({ - TestedAsMobileDevice: false, + const product = Audit.audit({ ViewportDimensions: { innerWidth: 300, outerWidth: 450, }, - }).notApplicable, true); + }, getFakeContext('desktop')); + + assert.equal(product.notApplicable, true); }); }); diff --git a/lighthouse-core/test/audits/metrics/first-contentful-paint-test.js b/lighthouse-core/test/audits/metrics/first-contentful-paint-test.js index 34b9bda9552b..9ee471d1a572 100644 --- a/lighthouse-core/test/audits/metrics/first-contentful-paint-test.js +++ b/lighthouse-core/test/audits/metrics/first-contentful-paint-test.js @@ -7,10 +7,28 @@ const Audit = require('../../../audits/metrics/first-contentful-paint.js'); const assert = require('assert').strict; const options = Audit.defaultOptions; +const constants = require('../../../config/constants.js'); const pwaTrace = require('../../fixtures/traces/progressive-app-m60.json'); const pwaDevtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); +/** + * @param {{ + * {LH.SharedFlagsSettings['formFactor']} formFactor + * {LH.SharedFlagsSettings['throttlingMethod']} throttlingMethod + * }} param0 + */ +const getFakeContext = ({formFactor, throttlingMethod}) => ({ + options: options, + computedCache: new Map(), + settings: { + formFactor: formFactor, + throttlingMethod, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); + + /* eslint-env jest */ describe('Performance: first-contentful-paint audit', () => { @@ -24,8 +42,8 @@ describe('Performance: first-contentful-paint audit', () => { }, }; - const settings = {throttlingMethod: 'provided'}; - const result = await Audit.audit(artifacts, {settings, options, computedCache: new Map()}); + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); + const result = await Audit.audit(artifacts, context); assert.equal(result.score, 1); assert.equal(result.numericValue, 498.87); }); diff --git a/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js b/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js index abab64d89e00..fa59ad274e77 100644 --- a/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js +++ b/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js @@ -7,11 +7,28 @@ const FMPAudit = require('../../../audits/metrics/first-meaningful-paint.js'); const Audit = require('../../../audits/audit.js'); +const constants = require('../../../config/constants.js'); const assert = require('assert').strict; const options = FMPAudit.defaultOptions; const trace = require('../../fixtures/traces/progressive-app-m60.json'); const devtoolsLogs = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); +/** + * @param {{ + * {LH.SharedFlagsSettings['formFactor']} formFactor + * {LH.SharedFlagsSettings['throttlingMethod']} throttlingMethod + * }} param0 + */ +const getFakeContext = ({formFactor, throttlingMethod}) => ({ + options: options, + computedCache: new Map(), + settings: { + formFactor: formFactor, + throttlingMethod, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); + /* eslint-env jest */ describe('Performance: first-meaningful-paint audit', () => { it('computes FMP correctly for valid trace', async () => { @@ -19,7 +36,7 @@ describe('Performance: first-meaningful-paint audit', () => { traces: {[Audit.DEFAULT_PASS]: trace}, devtoolsLogs: {[Audit.DEFAULT_PASS]: devtoolsLogs}, }; - const context = {options, settings: {throttlingMethod: 'provided'}, computedCache: new Map()}; + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); const fmpResult = await FMPAudit.audit(artifacts, context); assert.equal(fmpResult.score, 1); @@ -32,7 +49,7 @@ describe('Performance: first-meaningful-paint audit', () => { traces: {[Audit.DEFAULT_PASS]: trace}, devtoolsLogs: {[Audit.DEFAULT_PASS]: devtoolsLogs}, }; - const context = {options, settings: {throttlingMethod: 'simulate'}, computedCache: new Map()}; + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'simulate'}); const fmpResult = await FMPAudit.audit(artifacts, context); expect({ diff --git a/lighthouse-core/test/audits/metrics/interactive-test.js b/lighthouse-core/test/audits/metrics/interactive-test.js index 34d9c143f61b..8abeeca6dace 100644 --- a/lighthouse-core/test/audits/metrics/interactive-test.js +++ b/lighthouse-core/test/audits/metrics/interactive-test.js @@ -8,6 +8,7 @@ const Interactive = require('../../../audits/metrics/interactive.js'); const assert = require('assert').strict; const options = Interactive.defaultOptions; +const constants = require('../../../config/constants.js'); const acceptableTrace = require('../../fixtures/traces/progressive-app-m60.json'); const acceptableDevToolsLog = @@ -16,6 +17,21 @@ const acceptableDevToolsLog = const redirectTrace = require('../../fixtures/traces/site-with-redirect.json'); const redirectDevToolsLog = require('../../fixtures/traces/site-with-redirect.devtools.log.json'); +/** + * @param {{ + * {LH.SharedFlagsSettings['formFactor']} formFactor + * {LH.SharedFlagsSettings['throttlingMethod']} throttlingMethod + * }} param0 + */ +const getFakeContext = ({formFactor, throttlingMethod}) => ({ + options: options, + computedCache: new Map(), + settings: { + formFactor: formFactor, + throttlingMethod, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); /* eslint-env jest */ describe('Performance: interactive audit', () => { @@ -29,7 +45,7 @@ describe('Performance: interactive audit', () => { }, }; - const context = {options, settings: {throttlingMethod: 'provided'}, computedCache: new Map()}; + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); return Interactive.audit(artifacts, context).then(output => { assert.equal(output.score, 1); assert.equal(Math.round(output.numericValue), 1582); @@ -47,7 +63,7 @@ describe('Performance: interactive audit', () => { }, }; - const context = {options, settings: {throttlingMethod: 'provided'}, computedCache: new Map()}; + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); return Interactive.audit(artifacts, context).then(output => { assert.equal(output.score, 0.97); assert.equal(Math.round(output.numericValue), 2712); diff --git a/lighthouse-core/test/audits/metrics/largest-contentful-paint-test.js b/lighthouse-core/test/audits/metrics/largest-contentful-paint-test.js index 3d9bf74ab77f..a35da8e0bc05 100644 --- a/lighthouse-core/test/audits/metrics/largest-contentful-paint-test.js +++ b/lighthouse-core/test/audits/metrics/largest-contentful-paint-test.js @@ -7,6 +7,7 @@ const LCPAudit = require('../../../audits/metrics/largest-contentful-paint.js'); const defaultOptions = LCPAudit.defaultOptions; +const constants = require('../../../config/constants.js'); const trace = require('../../fixtures/traces/lcp-m78.json'); const devtoolsLog = require('../../fixtures/traces/lcp-m78.devtools.log.json'); @@ -14,19 +15,30 @@ const devtoolsLog = require('../../fixtures/traces/lcp-m78.devtools.log.json'); const preLcpTrace = require('../../fixtures/traces/progressive-app-m60.json'); const preLcpDevtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); -function generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice, HostUserAgent}) { +function generateArtifacts({trace, devtoolsLog, HostUserAgent}) { return { traces: {[LCPAudit.DEFAULT_PASS]: trace}, devtoolsLogs: {[LCPAudit.DEFAULT_PASS]: devtoolsLog}, - TestedAsMobileDevice, HostUserAgent, }; } -function generateContext({throttlingMethod}) { - const settings = {throttlingMethod}; - return {options: defaultOptions, settings, computedCache: new Map()}; -} +/** + * @param {{ + * {LH.SharedFlagsSettings['formFactor']} formFactor + * {LH.SharedFlagsSettings['throttlingMethod']} throttlingMethod + * }} param0 + */ +const getFakeContext = ({formFactor, throttlingMethod}) => ({ + options: defaultOptions, + computedCache: new Map(), + settings: { + formFactor: formFactor, + throttlingMethod, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); + /* eslint-env jest */ describe('Performance: largest-contentful-paint audit', () => { @@ -34,12 +46,8 @@ describe('Performance: largest-contentful-paint audit', () => { const artifactsMobile = generateArtifacts({ trace, devtoolsLog, - TestedAsMobileDevice: true, - HostUserAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) ' + - 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 ' + - 'Mobile Safari/537.36 Chrome-Lighthouse', }); - const contextMobile = generateContext({throttlingMethod: 'provided'}); + const contextMobile = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); const outputMobile = await LCPAudit.audit(artifactsMobile, contextMobile); expect(outputMobile.numericValue).toBeCloseTo(1121.711, 1); @@ -49,12 +57,8 @@ describe('Performance: largest-contentful-paint audit', () => { const artifactsDesktop = generateArtifacts({ trace, devtoolsLog, - TestedAsMobileDevice: false, - HostUserAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) ' + - 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 ' + - 'Mobile Safari/537.36 Chrome-Lighthouse', }); - const contextDesktop = generateContext({throttlingMethod: 'provided'}); + const contextDesktop = getFakeContext({formFactor: 'desktop', throttlingMethod: 'provided'}); const outputDesktop = await LCPAudit.audit(artifactsDesktop, contextDesktop); expect(outputDesktop.numericValue).toBeCloseTo(1121.711, 1); @@ -66,12 +70,11 @@ describe('Performance: largest-contentful-paint audit', () => { const artifactsOldChrome = generateArtifacts({ trace: preLcpTrace, devtoolsLog: preLcpDevtoolsLog, - TestedAsMobileDevice: true, HostUserAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 ' + 'Mobile Safari/537.36 Chrome-Lighthouse', }); - const contextOldChrome = generateContext({throttlingMethod: 'provided'}); + const contextOldChrome = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); await expect(LCPAudit.audit(artifactsOldChrome, contextOldChrome)) .rejects.toThrow(/UNSUPPORTED_OLD_CHROME/); @@ -79,12 +82,11 @@ describe('Performance: largest-contentful-paint audit', () => { const artifactsNewChrome = generateArtifacts({ trace: preLcpTrace, devtoolsLog: preLcpDevtoolsLog, - TestedAsMobileDevice: true, HostUserAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 ' + 'Mobile Safari/537.36 Chrome-Lighthouse', }); - const contextNewChrome = generateContext({throttlingMethod: 'provided'}); + const contextNewChrome = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); await expect(LCPAudit.audit(artifactsNewChrome, contextNewChrome)).rejects.toThrow(/NO_LCP/); }); diff --git a/lighthouse-core/test/audits/metrics/speed-index-test.js b/lighthouse-core/test/audits/metrics/speed-index-test.js index afdb1e15c201..8d77fb3d1703 100644 --- a/lighthouse-core/test/audits/metrics/speed-index-test.js +++ b/lighthouse-core/test/audits/metrics/speed-index-test.js @@ -10,10 +10,28 @@ const Audit = require('../../../audits/metrics/speed-index.js'); const assert = require('assert').strict; const options = Audit.defaultOptions; +const constants = require('../../../config/constants.js'); const pwaTrace = require('../../fixtures/traces/progressive-app-m60.json'); const pwaDevtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); +/** + * @param {{ + * {LH.SharedFlagsSettings['formFactor']} formFactor + * {LH.SharedFlagsSettings['throttlingMethod']} throttlingMethod + * }} param0 + */ +const getFakeContext = ({formFactor, throttlingMethod}) => ({ + options: options, + computedCache: new Map(), + settings: { + formFactor: formFactor, + throttlingMethod, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); + + describe('Performance: speed-index audit', () => { it('works on a real trace', () => { const artifacts = { @@ -21,8 +39,8 @@ describe('Performance: speed-index audit', () => { devtoolsLogs: {defaultPass: pwaDevtoolsLog}, }; - const settings = {throttlingMethod: 'provided'}; - return Audit.audit(artifacts, {options, settings, computedCache: new Map()}).then(result => { + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); + return Audit.audit(artifacts, context).then(result => { assert.equal(result.score, 1); assert.equal(result.numericValue, 605); }); diff --git a/lighthouse-core/test/audits/metrics/total-blocking-time-test.js b/lighthouse-core/test/audits/metrics/total-blocking-time-test.js index 1e2718f3a00a..ab351c410f9e 100644 --- a/lighthouse-core/test/audits/metrics/total-blocking-time-test.js +++ b/lighthouse-core/test/audits/metrics/total-blocking-time-test.js @@ -7,6 +7,7 @@ const TBTAudit = require('../../../audits/metrics/total-blocking-time.js'); const defaultOptions = TBTAudit.defaultOptions; +const constants = require('../../../config/constants.js'); const trace = require('../../fixtures/traces/progressive-app-m60.json'); const devtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); @@ -14,24 +15,35 @@ const devtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools. const lcpTrace = require('../../fixtures/traces/lcp-m78.json'); const lcpDevtoolsLog = require('../../fixtures/traces/lcp-m78.devtools.log.json'); -function generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice}) { +function generateArtifacts({trace, devtoolsLog}) { return { traces: {[TBTAudit.DEFAULT_PASS]: trace}, devtoolsLogs: {[TBTAudit.DEFAULT_PASS]: devtoolsLog}, - TestedAsMobileDevice, }; } -function generateContext({throttlingMethod}) { - const settings = {throttlingMethod}; - return {options: defaultOptions, settings, computedCache: new Map()}; -} +/** + * @param {{ + * {LH.SharedFlagsSettings['formFactor']} formFactor + * {LH.SharedFlagsSettings['throttlingMethod']} throttlingMethod + * }} param0 + */ +const getFakeContext = ({formFactor, throttlingMethod}) => ({ + options: defaultOptions, + computedCache: new Map(), + settings: { + formFactor: formFactor, + throttlingMethod, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); + /* eslint-env jest */ describe('Performance: total-blocking-time audit', () => { it('evaluates Total Blocking Time metric properly', async () => { - const artifacts = generateArtifacts({trace, devtoolsLog, TestedAsMobileDevice: true}); - const context = generateContext({throttlingMethod: 'provided'}); + const artifacts = generateArtifacts({trace, devtoolsLog}); + const context = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); const output = await TBTAudit.audit(artifacts, context); expect(output.numericValue).toBeCloseTo(48.3, 1); @@ -41,8 +53,8 @@ describe('Performance: total-blocking-time audit', () => { it('adjusts scoring based on form factor', async () => { const artifactsMobile = generateArtifacts({trace: lcpTrace, - devtoolsLog: lcpDevtoolsLog, TestedAsMobileDevice: true}); - const contextMobile = generateContext({throttlingMethod: 'provided'}); + devtoolsLog: lcpDevtoolsLog}); + const contextMobile = getFakeContext({formFactor: 'mobile', throttlingMethod: 'provided'}); const outputMobile = await TBTAudit.audit(artifactsMobile, contextMobile); expect(outputMobile.numericValue).toBeCloseTo(333, 1); @@ -50,8 +62,8 @@ describe('Performance: total-blocking-time audit', () => { expect(outputMobile.displayValue).toBeDisplayString('330\xa0ms'); const artifactsDesktop = generateArtifacts({trace: lcpTrace, - devtoolsLog: lcpDevtoolsLog, TestedAsMobileDevice: false}); - const contextDesktop = generateContext({throttlingMethod: 'provided'}); + devtoolsLog: lcpDevtoolsLog}); + const contextDesktop = getFakeContext({formFactor: 'desktop', throttlingMethod: 'provided'}); const outputDesktop = await TBTAudit.audit(artifactsDesktop, contextDesktop); expect(outputDesktop.numericValue).toBeCloseTo(333, 1); diff --git a/lighthouse-core/test/audits/seo/font-size-test.js b/lighthouse-core/test/audits/seo/font-size-test.js index 6957b1be6325..15731b3d32ff 100644 --- a/lighthouse-core/test/audits/seo/font-size-test.js +++ b/lighthouse-core/test/audits/seo/font-size-test.js @@ -6,6 +6,7 @@ 'use strict'; const FontSizeAudit = require('../../../audits/seo/font-size.js'); +const constants = require('../../../config/constants.js'); const assert = require('assert').strict; const URL = { @@ -18,14 +19,21 @@ const validViewport = 'width=device-width'; describe('SEO: Font size audit', () => { const makeMetaElements = viewport => [{name: 'viewport', content: viewport}]; - const getFakeContext = () => ({computedCache: new Map()}); + + /** @param {LH.SharedFlagsSettings['formFactor']} formFactor */ + const getFakeContext = (formFactor = 'mobile') => ({ + computedCache: new Map(), + settings: { + formFactor: formFactor, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, + }); it('fails when viewport is not set', async () => { const artifacts = { URL, MetaElements: [], FontSize: [], - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); @@ -48,7 +56,6 @@ describe('SEO: Font size audit', () => { {nodeId: 2, textLength: 31, fontSize: 11, parentNode: {nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); @@ -68,7 +75,6 @@ describe('SEO: Font size audit', () => { {nodeId: 1, textLength: 0, fontSize: 11, parentNode: {nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); @@ -88,7 +94,6 @@ describe('SEO: Font size audit', () => { {nodeId: 2, textLength: 22, fontSize: 11, parentNode: {nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); assert.equal(auditResult.score, 1); @@ -125,7 +130,6 @@ describe('SEO: Font size audit', () => { {nodeId: 3, textLength: 2, fontSize: 10, parentNode: {}, cssRule: style2}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); @@ -147,7 +151,6 @@ describe('SEO: Font size audit', () => { {textLength: 10, fontSize: 10, parentNode: {nodeId: 1, nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); assert.equal(auditResult.score, 0); @@ -170,7 +173,6 @@ describe('SEO: Font size audit', () => { {textLength: 50, fontSize: 10, parentNode: {nodeId: 1, nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); assert.equal(auditResult.score, 0); @@ -190,7 +192,6 @@ describe('SEO: Font size audit', () => { {textLength: 22, fontSize: 11, parentNode: {nodeId: 2, nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); expect(auditResult.displayValue).toBeDisplayString('89.78% legible text'); @@ -209,7 +210,6 @@ describe('SEO: Font size audit', () => { {textLength: 4, fontSize: 11, parentNode: {nodeId: 2, nodeName: 'p', attributes: []}}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); expect(auditResult.displayValue).toBeDisplayString('2.48% legible text'); @@ -220,9 +220,9 @@ describe('SEO: Font size audit', () => { URL, MetaElements: [], FontSize: {}, - TestedAsMobileDevice: false, }; - const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); + const desktopContext = getFakeContext('desktop'); + const auditResult = await FontSizeAudit.audit(artifacts, desktopContext); expect(auditResult.score).toBe(1); expect(auditResult.notApplicable).toBe(true); }); @@ -237,7 +237,6 @@ describe('SEO: Font size audit', () => { {textLength: 1, fontSize: 1, parentNode: {...nodeProperties}, cssRule: style}, ], }, - TestedAsMobileDevice: true, }; const auditResult = await FontSizeAudit.audit(artifacts, getFakeContext()); expect(auditResult.details.items).toHaveLength(1); diff --git a/lighthouse-core/test/audits/seo/tap-targets-test.js b/lighthouse-core/test/audits/seo/tap-targets-test.js index 3570655d14cd..9a9ddf7d5c69 100644 --- a/lighthouse-core/test/audits/seo/tap-targets-test.js +++ b/lighthouse-core/test/audits/seo/tap-targets-test.js @@ -8,21 +8,28 @@ /* eslint-env jest */ const TapTargetsAudit = require('../../../audits/seo/tap-targets.js'); +const constants = require('../../../config/constants.js'); const assert = require('assert').strict; -const getFakeContext = () => ({computedCache: new Map()}); +/** @param {LH.SharedFlagsSettings['formFactor']} formFactor */ +const getFakeContext = (formFactor = 'mobile') => ({ + computedCache: new Map(), + settings: { + formFactor: formFactor, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }, +}); function auditTapTargets(tapTargets, {MetaElements = [{ name: 'viewport', content: 'width=device-width', -}], TestedAsMobileDevice = true} = {}) { +}]} = {}, context = getFakeContext()) { const artifacts = { TapTargets: tapTargets, MetaElements, - TestedAsMobileDevice, }; - return TapTargetsAudit.audit(artifacts, getFakeContext()); + return TapTargetsAudit.audit(artifacts, context); } const tapTargetSize = 10; @@ -219,9 +226,11 @@ describe('SEO: Tap targets audit', () => { }); it('is not applicable on desktop', async () => { + const desktopContext = getFakeContext('desktop'); + const auditResult = await auditTapTargets(getBorderlineTapTargets({ overlapSecondClientRect: true, - }), {TestedAsMobileDevice: false}); + }), undefined, desktopContext); assert.equal(auditResult.score, 1); assert.equal(auditResult.notApplicable, true); }); diff --git a/lighthouse-core/test/computed/metrics/first-contentful-paint-test.js b/lighthouse-core/test/computed/metrics/first-contentful-paint-test.js index 329f8107aba5..4eb38adef1d8 100644 --- a/lighthouse-core/test/computed/metrics/first-contentful-paint-test.js +++ b/lighthouse-core/test/computed/metrics/first-contentful-paint-test.js @@ -31,7 +31,7 @@ describe('Metrics: FCP', () => { }); it('should compute an observed value (desktop)', async () => { - const settings = {throttlingMethod: 'provided'}; + const settings = {throttlingMethod: 'provided', formFactor: 'desktop'}; const context = {settings, computedCache: new Map()}; const result = await FirstContentfulPaint.request({trace, devtoolsLog, settings}, context); @@ -40,10 +40,10 @@ describe('Metrics: FCP', () => { }); it('should compute an observed value (mobile)', async () => { - const settings = {throttlingMethod: 'provided'}; + const settings = {throttlingMethod: 'provided', formFactor: 'mobile'}; const context = {settings, computedCache: new Map()}; const result = await FirstContentfulPaint.request( - {trace, devtoolsLog, settings, TestedAsMobileDevice: true}, context); + {trace, devtoolsLog, settings}, context); assert.equal(Math.round(result.timing), 499); assert.equal(result.timestamp, 225414670885); diff --git a/lighthouse-core/test/computed/metrics/first-meaningful-paint-test.js b/lighthouse-core/test/computed/metrics/first-meaningful-paint-test.js index f5f3a6378dc7..998b99d6c044 100644 --- a/lighthouse-core/test/computed/metrics/first-meaningful-paint-test.js +++ b/lighthouse-core/test/computed/metrics/first-meaningful-paint-test.js @@ -58,7 +58,7 @@ describe('Metrics: FMP', () => { }); it('should compute an observed value (desktop)', async () => { - settings = {throttlingMethod: 'provided'}; + settings = {throttlingMethod: 'provided', formFactor: 'desktop'}; const context = {computedCache: new Map()}; const result = await FirstMeaningfulPaint.request({trace, devtoolsLog, settings}, context); @@ -67,10 +67,9 @@ describe('Metrics: FMP', () => { }); it('should compute an observed value (mobile)', async () => { - settings = {throttlingMethod: 'provided'}; + settings = {throttlingMethod: 'provided', formFactor: 'mobile'}; const context = {computedCache: new Map()}; - const result = await FirstMeaningfulPaint.request( - {trace, devtoolsLog, settings, TestedAsMobileDevice: true}, context); + const result = await FirstMeaningfulPaint.request({trace, devtoolsLog, settings}, context); assert.equal(Math.round(result.timing), 783); assert.equal(result.timestamp, 225414955343); diff --git a/lighthouse-core/test/computed/metrics/interactive-test.js b/lighthouse-core/test/computed/metrics/interactive-test.js index f4bc97638582..6c10cb5d6d46 100644 --- a/lighthouse-core/test/computed/metrics/interactive-test.js +++ b/lighthouse-core/test/computed/metrics/interactive-test.js @@ -45,7 +45,7 @@ describe('Metrics: TTI', () => { }); it('should compute an observed value (desktop)', async () => { - const settings = {throttlingMethod: 'provided'}; + const settings = {throttlingMethod: 'provided', formFactor: 'desktop'}; const context = {settings, computedCache: new Map()}; const result = await Interactive.request({trace, devtoolsLog, settings}, context); @@ -54,10 +54,9 @@ describe('Metrics: TTI', () => { }); it('should compute an observed value (mobile)', async () => { - const settings = {throttlingMethod: 'provided'}; + const settings = {throttlingMethod: 'provided', formFactor: 'mobile'}; const context = {settings, computedCache: new Map()}; - const result = await Interactive.request( - {trace, devtoolsLog, settings, TestedAsMobileDevice: true}, context); + const result = await Interactive.request({trace, devtoolsLog, settings}, context); assert.equal(Math.round(result.timing), 1582); assert.equal(result.timestamp, 225415754204); diff --git a/lighthouse-core/test/computed/metrics/speed-index-test.js b/lighthouse-core/test/computed/metrics/speed-index-test.js index 0b8d2e37f32d..df9e98767127 100644 --- a/lighthouse-core/test/computed/metrics/speed-index-test.js +++ b/lighthouse-core/test/computed/metrics/speed-index-test.js @@ -68,7 +68,7 @@ describe('Metrics: Speed Index', () => { }); it('should compute an observed value (desktop)', async () => { - const settings = {throttlingMethod: 'provided'}; + const settings = {throttlingMethod: 'provided', formFactor: 'desktop'}; const context = {settings, computedCache: new Map()}; const result = await SpeedIndex.request({trace, devtoolsLog, settings}, context); @@ -77,12 +77,9 @@ describe('Metrics: Speed Index', () => { }); it('should compute an observed value (mobile)', async () => { - const settings = {throttlingMethod: 'provided'}; + const settings = {throttlingMethod: 'provided', formFactor: 'mobile'}; const context = {settings, computedCache: new Map()}; - const result = await SpeedIndex.request( - {trace, devtoolsLog, settings, TestedAsMobileDevice: true}, - context - ); + const result = await SpeedIndex.request({trace, devtoolsLog, settings}, context); assert.equal(result.timing, 605); assert.equal(result.timestamp, 225414777015); diff --git a/lighthouse-core/test/config/config-test.js b/lighthouse-core/test/config/config-test.js index 4feaea170284..36c9aa13888c 100644 --- a/lighthouse-core/test/config/config-test.js +++ b/lighthouse-core/test/config/config-test.js @@ -9,6 +9,7 @@ const Config = require('../../config/config.js'); const assert = require('assert').strict; const path = require('path'); const defaultConfig = require('../../config/default-config.js'); +const constants = require('../../config/constants.js'); const log = require('lighthouse-logger'); const Gatherer = require('../../gather/gatherers/gatherer.js'); const Audit = require('../../audits/audit.js'); @@ -789,16 +790,19 @@ describe('Config', () => { extends: 'lighthouse:default', settings: { disableStorageReset: true, - emulatedFormFactor: 'mobile', + formFactor: 'desktop', + throttling: constants.throttling.desktopDense4G, + screenEmulation: constants.screenEmulationMetrics.desktop, + emulatedUserAgent: constants.userAgents.desktop, }, }, - {emulatedFormFactor: 'desktop'} + {formFactor: 'desktop'} ); assert.ok(config, 'failed to generate config'); assert.ok(typeof config.settings.maxWaitForLoad === 'number', 'missing setting from default'); assert.ok(config.settings.disableStorageReset, 'missing setting from extension config'); - assert.ok(config.settings.emulatedFormFactor === 'desktop', 'missing setting from flags'); + assert.ok(config.settings.formFactor === 'desktop', 'missing setting from flags'); }); it('inherits default settings when undefined', () => { @@ -829,6 +833,41 @@ describe('Config', () => { }); }); + describe('emulatedUserAgent', () => { + it('uses the default UA string when emulatedUserAgent is undefined', () => { + const config = new Config({}); + expect(config.settings.emulatedUserAgent).toMatch(/^Mozilla\/5.*Chrome-Lighthouse$/); + }); + + it('uses the default UA string when emulatedUserAgent is true', () => { + const config = new Config({ + settings: { + emulatedUserAgent: true, + }, + }); + expect(config.settings.emulatedUserAgent).toMatch(/^Mozilla\/5.*Chrome-Lighthouse$/); + }); + + it('does not use a UA string when emulatedUserAgent is false', () => { + const config = new Config({ + settings: { + emulatedUserAgent: false, + }, + }); + expect(config.settings.emulatedUserAgent).toEqual(false); + }); + + it('uses the UA string provided if it is a string', () => { + const emulatedUserAgent = 'one weird trick to get a perfect LH score'; + const config = new Config({ + settings: { + emulatedUserAgent, + }, + }); + expect(config.settings.emulatedUserAgent).toEqual(emulatedUserAgent); + }); + }); + it('is idempotent when accepting a canonicalized Config as valid ConfigJson input', () => { const config = new Config(defaultConfig); const configAgain = new Config(config); diff --git a/lighthouse-core/test/config/default-config-test.js b/lighthouse-core/test/config/default-config-test.js index 7c5524a4e600..89a372672af4 100644 --- a/lighthouse-core/test/config/default-config-test.js +++ b/lighthouse-core/test/config/default-config-test.js @@ -15,6 +15,7 @@ describe('Default Config', () => { it('only has opportunity audits that return opportunities details', async () => { const flags = { auditMode: __dirname + '/../results/artifacts/', + formFactor: 'mobile', // sample_v2 was run with these settings, so need to match them. throttlingMethod: 'devtools', diff --git a/lighthouse-core/test/gather/gather-runner-test.js b/lighthouse-core/test/gather/gather-runner-test.js index 82c10cd4c851..8694ea453257 100644 --- a/lighthouse-core/test/gather/gather-runner-test.js +++ b/lighthouse-core/test/gather/gather-runner-test.js @@ -13,6 +13,7 @@ const Gatherer = require('../../gather/gatherers/gatherer.js'); const GatherRunner_ = require('../../gather/gather-runner.js'); const assert = require('assert').strict; const Config = require('../../config/config.js'); +const constants = require('../../config/constants.js'); const unresolvedPerfLog = require('./../fixtures/unresolved-perflog.json'); const NetworkRequest = require('../../lib/network-request.js'); const LHError = require('../../lib/lh-error.js'); @@ -110,7 +111,6 @@ class EmulationDriver extends Driver { } const fakeDriver = require('./fake-driver.js'); -const fakeDriverUsingRealMobileDevice = fakeDriver.fakeDriverUsingRealMobileDevice; /** @type {EmulationDriver} */ let driver; @@ -233,58 +233,6 @@ describe('GatherRunner', function() { }); }); - describe('collects TestedAsMobileDevice as an artifact', () => { - const requestedUrl = 'https://example.com'; - - it('works when running on desktop device without emulation', async () => { - const driver = fakeDriver; - const config = makeConfig({ - passes: [], - settings: {emulatedFormFactor: 'none', internalDisableDeviceScreenEmulation: false}, - }); - const options = {requestedUrl, driver, settings: config.settings}; - - const results = await GatherRunner.run(config.passes, options); - expect(results.TestedAsMobileDevice).toBe(false); - }); - - it('works when running on desktop device with mobile emulation', async () => { - const driver = fakeDriver; - const config = makeConfig({ - passes: [], - settings: {emulatedFormFactor: 'mobile', internalDisableDeviceScreenEmulation: false}, - }); - const options = {requestedUrl, driver, settings: config.settings}; - - const results = await GatherRunner.run(config.passes, options); - expect(results.TestedAsMobileDevice).toBe(true); - }); - - it('works when running on mobile device without emulation', async () => { - const driver = fakeDriverUsingRealMobileDevice; - const config = makeConfig({ - passes: [], - settings: {emulatedFormFactor: 'none', internalDisableDeviceScreenEmulation: false}, - }); - const options = {requestedUrl, driver, settings: config.settings}; - - const results = await GatherRunner.run(config.passes, options); - expect(results.TestedAsMobileDevice).toBe(true); - }); - - it('works when running on mobile device with desktop emulation', async () => { - const driver = fakeDriverUsingRealMobileDevice; - const config = makeConfig({ - passes: [], - settings: {emulatedFormFactor: 'desktop', internalDisableDeviceScreenEmulation: false}, - }); - const options = {requestedUrl, driver, settings: config.settings}; - - const results = await GatherRunner.run(config.passes, options); - expect(results.TestedAsMobileDevice).toBe(false); - }); - }); - describe('collects HostFormFactor as an artifact', () => { const requestedUrl = 'https://example.com'; @@ -322,14 +270,14 @@ describe('GatherRunner', function() { test('works when running on desktop device', DESKTOP_UA, 'desktop'); }); + /** @param {NonNullable}formFactor */ + const getSettings = formFactor => ({ + formFactor: formFactor, + screenEmulation: constants.screenEmulationMetrics[formFactor], + }); + it('sets up the driver to begin emulation when all flags are undefined', async () => { - await GatherRunner.setupDriver(driver, { - settings: { - emulatedFormFactor: 'mobile', - throttlingMethod: 'provided', - internalDisableDeviceScreenEmulation: false, - }, - }); + await GatherRunner.setupDriver(driver, {settings: getSettings('mobile')}); connectionStub.sendCommand.findInvocation('Emulation.setDeviceMetricsOverride'); expect(connectionStub.sendCommand.findInvocation('Network.emulateNetworkConditions')).toEqual({ @@ -339,13 +287,7 @@ describe('GatherRunner', function() { connectionStub.sendCommand.findInvocation('Emulation.setCPUThrottlingRate')).toThrow(); }); - it('applies the correct emulation given a particular emulationFormFactor', async () => { - /** @param {'mobile'|'desktop'|'none'} formFactor */ - const getSettings = formFactor => ({ - emulatedFormFactor: formFactor, - internalDisableDeviceScreenEmulation: false, - }); - + it('applies the correct emulation given a particular formFactor', async () => { await GatherRunner.setupDriver(driver, {settings: getSettings('mobile')}); expect(connectionStub.sendCommand.findInvocation('Emulation.setDeviceMetricsOverride')) .toMatchObject({mobile: true}); @@ -354,17 +296,13 @@ describe('GatherRunner', function() { await GatherRunner.setupDriver(driver, {settings: getSettings('desktop')}); expect(connectionStub.sendCommand.findInvocation('Emulation.setDeviceMetricsOverride')) .toMatchObject({mobile: false}); - - resetDefaultMockResponses(); - await GatherRunner.setupDriver(driver, {settings: getSettings('none')}); - expect(() => - connectionStub.sendCommand.findInvocation('Emulation.setDeviceMetricsOverride')).toThrow(); }); it('sets throttling according to settings', async () => { await GatherRunner.setupDriver(driver, { settings: { - emulatedFormFactor: 'mobile', internalDisableDeviceScreenEmulation: false, + formFactor: 'mobile', + screenEmulation: constants.screenEmulationMetrics.mobile, throttlingMethod: 'devtools', throttling: { requestLatencyMs: 100, diff --git a/lighthouse-core/test/gather/gatherers/full-page-screenshot-test.js b/lighthouse-core/test/gather/gatherers/full-page-screenshot-test.js index c53469afb0b6..12282d72313b 100644 --- a/lighthouse-core/test/gather/gatherers/full-page-screenshot-test.js +++ b/lighthouse-core/test/gather/gatherers/full-page-screenshot-test.js @@ -61,7 +61,11 @@ describe('FullPageScreenshot gatherer', () => { }); const passContext = { settings: { - emulatedFormFactor: 'mobile', + formFactor: 'mobile', + screenEmulation: { + mobile: true, + disabled: false, + }, }, driver, baseArtifacts: {}, @@ -88,7 +92,11 @@ describe('FullPageScreenshot gatherer', () => { }); const passContext = { settings: { - emulatedFormFactor: 'mobile', + formFactor: 'mobile', + screenEmulation: { + mobile: true, + disabled: false, + }, }, driver, baseArtifacts: {}, @@ -96,7 +104,8 @@ describe('FullPageScreenshot gatherer', () => { await fpsGatherer.afterPass(passContext); - expect(driver.beginEmulation).toHaveBeenCalledWith({emulatedFormFactor: 'mobile'}); + const expectedArgs = {formFactor: 'mobile', screenEmulation: {disabled: false, mobile: true}}; + expect(driver.beginEmulation).toHaveBeenCalledWith(expectedArgs); expect(driver.beginEmulation).toHaveBeenCalledTimes(1); }); @@ -115,12 +124,13 @@ describe('FullPageScreenshot gatherer', () => { }); const passContext = { settings: { - emulatedFormFactor: 'none', + screenEmulation: { + mobile: true, + disabled: true, + }, + formFactor: 'mobile', }, driver, - baseArtifacts: { - TestedAsMobileDevice: true, - }, }; await fpsGatherer.afterPass(passContext); @@ -165,7 +175,11 @@ describe('FullPageScreenshot gatherer', () => { }); const passContext = { settings: { - emulatedFormFactor: 'mobile', + formFactor: 'mobile', + screenEmulation: { + mobile: true, + disabled: false, + }, }, driver, baseArtifacts: {}, diff --git a/lighthouse-core/test/index-test.js b/lighthouse-core/test/index-test.js index c0c5a1fc81b7..e05d87aee618 100644 --- a/lighthouse-core/test/index-test.js +++ b/lighthouse-core/test/index-test.js @@ -116,6 +116,7 @@ describe('Module Tests', function() { }, { settings: { auditMode: __dirname + '/fixtures/artifacts/perflog/', + formFactor: 'mobile', }, audits: [ 'viewport', @@ -141,6 +142,7 @@ describe('Module Tests', function() { const results = await lighthouse(exampleUrl, {}, { settings: { auditMode: __dirname + '/fixtures/artifacts/perflog/', + formFactor: 'mobile', }, audits: [], }); @@ -152,6 +154,7 @@ describe('Module Tests', function() { const results = await lighthouse(exampleUrl, {}, { settings: { auditMode: __dirname + '/fixtures/artifacts/perflog/', + formFactor: 'mobile', channel: 'custom', }, audits: [], diff --git a/lighthouse-core/test/lib/emulation-test.js b/lighthouse-core/test/lib/emulation-test.js index a6475b73c13e..0aef39d85fc9 100644 --- a/lighthouse-core/test/lib/emulation-test.js +++ b/lighthouse-core/test/lib/emulation-test.js @@ -7,6 +7,7 @@ const emulation = require('../../lib/emulation.js'); const Driver = require('../../gather/driver.js'); +const constants = require('../../config/constants.js'); const Connection = require('../../gather/connections/connection.js'); const {createMockSendCommandFn} = require('../gather/mock-commands.js'); @@ -31,16 +32,24 @@ describe('emulation', () => { .mockResponse('Emulation.setTouchEmulationEnabled'); }); - const getSettings = (formFactor, disableDeviceScreenEmulation) => ({ - emulatedFormFactor: formFactor, - internalDisableDeviceScreenEmulation: disableDeviceScreenEmulation, + /** + * @param {LH.SharedFlagsSettings['formFactor']} formFactor + * @param {LH.SharedFlagsSettings['screenEmulation']} screenEmulation + * @param {LH.SharedFlagsSettings['emulatedUserAgent']} emulatedUserAgent + */ + const getSettings = (formFactor, screenEmulation, emulatedUserAgent) => ({ + formFactor: formFactor, + screenEmulation, + emulatedUserAgent: emulatedUserAgent === undefined ? constants.userAgents[formFactor] : false, }); - it('handles: emulatedFormFactor: mobile / disableDeviceScreenEmulation: false', async () => { - await emulation.emulate(driver, getSettings('mobile', false)); + const metrics = constants.screenEmulationMetrics; + + it('default: mobile w/ screenEmulation', async () => { + await emulation.emulate(driver, getSettings('mobile', metrics.mobile)); const uaArgs = connectionStub.sendCommand.findInvocation('Network.setUserAgentOverride'); - expect(uaArgs).toMatchObject({userAgent: emulation.MOBILE_USERAGENT}); + expect(uaArgs).toMatchObject({userAgent: constants.userAgents.mobile}); const emulateArgs = connectionStub.sendCommand.findInvocation( 'Emulation.setDeviceMetricsOverride' @@ -48,34 +57,39 @@ describe('emulation', () => { expect(emulateArgs).toMatchObject({mobile: true}); }); - it('handles: emulatedFormFactor: desktop / disableDeviceScreenEmulation: false', async () => { - await emulation.emulate(driver, getSettings('desktop', false)); + it('default desktop: w/ desktop screen emu', async () => { + await emulation.emulate(driver, getSettings('desktop', metrics.desktop)); const uaArgs = connectionStub.sendCommand.findInvocation('Network.setUserAgentOverride'); - expect(uaArgs).toMatchObject({userAgent: emulation.DESKTOP_USERAGENT}); + expect(uaArgs).toMatchObject({userAgent: constants.userAgents.desktop}); const emulateArgs = connectionStub.sendCommand.findInvocation( 'Emulation.setDeviceMetricsOverride' ); + expect(emulateArgs).toMatchObject({ + mobile: metrics.desktop.mobile, + width: metrics.desktop.width, + height: metrics.desktop.height, + deviceScaleFactor: metrics.desktop.deviceScaleFactor, + }); expect(emulateArgs).toMatchObject({mobile: false}); }); - it('handles: emulatedFormFactor: none / disableDeviceScreenEmulation: false', async () => { - await emulation.emulate(driver, getSettings('none', false)); - expect(connectionStub.sendCommand).not.toHaveBeenCalledWith( - 'Network.setUserAgentOverride', - expect.anything() - ); + it('mobile but screenEmu disabled (scenarios: on-device or external emu applied)', async () => { + await emulation.emulate(driver, getSettings('mobile', false)); + const uaArgs = connectionStub.sendCommand.findInvocation('Network.setUserAgentOverride'); + expect(uaArgs).toMatchObject({userAgent: constants.userAgents.mobile}); + expect(connectionStub.sendCommand).not.toHaveBeenCalledWith( 'Emulation.setDeviceMetricsOverride', expect.anything() ); }); - it('handles: emulatedFormFactor: mobile / disableDeviceScreenEmulation: true', async () => { - await emulation.emulate(driver, getSettings('mobile', true)); + it('desktop but screenEmu disabled (scenario: DevTools or external emu applied)', async () => { + await emulation.emulate(driver, getSettings('desktop', false)); const uaArgs = connectionStub.sendCommand.findInvocation('Network.setUserAgentOverride'); - expect(uaArgs).toMatchObject({userAgent: emulation.MOBILE_USERAGENT}); + expect(uaArgs).toMatchObject({userAgent: constants.userAgents.desktop}); expect(connectionStub.sendCommand).not.toHaveBeenCalledWith( 'Emulation.setDeviceMetricsOverride', @@ -83,27 +97,44 @@ describe('emulation', () => { ); }); - it('handles: emulatedFormFactor: desktop / disableDeviceScreenEmulation: true', async () => { - await emulation.emulate(driver, getSettings('desktop', true)); - const uaArgs = connectionStub.sendCommand.findInvocation('Network.setUserAgentOverride'); - expect(uaArgs).toMatchObject({userAgent: emulation.DESKTOP_USERAGENT}); + it('mobile but UA emu disabled', async () => { + await emulation.emulate(driver, getSettings('mobile', metrics.mobile, false)); expect(connectionStub.sendCommand).not.toHaveBeenCalledWith( - 'Emulation.setDeviceMetricsOverride', + 'Network.setUserAgentOverride', expect.anything() ); + + const emulateArgs = connectionStub.sendCommand.findInvocation( + 'Emulation.setDeviceMetricsOverride' + ); + expect(emulateArgs).toMatchObject({ + mobile: metrics.mobile.mobile, + width: metrics.mobile.width, + height: metrics.mobile.height, + deviceScaleFactor: metrics.mobile.deviceScaleFactor, + }); + expect(emulateArgs).toMatchObject({mobile: true}); }); - it('handles: emulatedFormFactor: none / disableDeviceScreenEmulation: true', async () => { - await emulation.emulate(driver, getSettings('none', true)); + it('desktop but UA emu disabled', async () => { + await emulation.emulate(driver, getSettings('desktop', metrics.desktop, false)); + expect(connectionStub.sendCommand).not.toHaveBeenCalledWith( 'Network.setUserAgentOverride', expect.anything() ); - expect(connectionStub.sendCommand).not.toHaveBeenCalledWith( - 'Emulation.setDeviceMetricsOverride', - expect.anything() + + const emulateArgs = connectionStub.sendCommand.findInvocation( + 'Emulation.setDeviceMetricsOverride' ); + expect(emulateArgs).toMatchObject({ + mobile: metrics.desktop.mobile, + width: metrics.desktop.width, + height: metrics.desktop.height, + deviceScaleFactor: metrics.desktop.deviceScaleFactor, + }); + expect(emulateArgs).toMatchObject({mobile: false}); }); }); }); diff --git a/lighthouse-core/test/lib/proto-preprocessor-test.js b/lighthouse-core/test/lib/proto-preprocessor-test.js index 44afc8a20d35..80ab96c6cd0c 100644 --- a/lighthouse-core/test/lib/proto-preprocessor-test.js +++ b/lighthouse-core/test/lib/proto-preprocessor-test.js @@ -34,7 +34,7 @@ describe('processing for proto', () => { }, 'gatherMode': false, 'disableStorageReset': false, - 'emulatedFormFactor': 'mobile', + 'formFactor': 'mobile', 'locale': 'en-US', 'blockedUrlPatterns': null, 'additionalTraceCategories': null, @@ -46,7 +46,7 @@ describe('processing for proto', () => { }; const expectation = { 'configSettings': { - 'emulatedFormFactor': 'mobile', + 'formFactor': 'mobile', 'locale': 'en-US', 'onlyCategories': null, }, diff --git a/lighthouse-core/test/lib/sentry-test.js b/lighthouse-core/test/lib/sentry-test.js index 5e2fa3f5b928..4e452234f81b 100644 --- a/lighthouse-core/test/lib/sentry-test.js +++ b/lighthouse-core/test/lib/sentry-test.js @@ -57,7 +57,7 @@ describe('Sentry', () => { url: 'http://example.com', flags: { enableErrorReporting: true, - emulatedFormFactor: 'desktop', + formFactor: 'desktop', throttlingMethod: 'devtools', }, environmentData: {}, @@ -68,7 +68,7 @@ describe('Sentry', () => { expect(raven.mergeContext.mock.calls[0][0]).toEqual({ extra: { url: 'http://example.com', - emulatedFormFactor: 'desktop', + formFactor: 'desktop', throttlingMethod: 'devtools', }, }); diff --git a/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js b/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js index c3a0e2a351e5..38fa8b37e79d 100644 --- a/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/performance-category-renderer-test.js @@ -263,7 +263,7 @@ describe('PerfCategoryRenderer', () => { it('also appends device and version number', () => { Util.reportJson = { - configSettings: {emulatedFormFactor: 'mobile'}, + configSettings: {formFactor: 'mobile'}, lighthouseVersion: '6.0.0', }; const href = renderer._getScoringCalculatorHref(category.auditRefs); diff --git a/lighthouse-core/test/report/html/renderer/util-test.js b/lighthouse-core/test/report/html/renderer/util-test.js index fa5b0422e14b..0ddce23239ad 100644 --- a/lighthouse-core/test/report/html/renderer/util-test.js +++ b/lighthouse-core/test/report/html/renderer/util-test.js @@ -35,9 +35,8 @@ describe('util helpers', () => { it('builds device emulation string', () => { const get = opts => Util.getEmulationDescriptions(opts).deviceEmulation; - assert.equal(get({emulatedFormFactor: 'none'}), 'No emulation'); - assert.equal(get({emulatedFormFactor: 'mobile'}), 'Emulated Moto G4'); - assert.equal(get({emulatedFormFactor: 'desktop'}), 'Emulated Desktop'); + assert.equal(get({formFactor: 'mobile'}), 'Emulated Moto G4'); + assert.equal(get({formFactor: 'desktop'}), 'Emulated Desktop'); }); it('builds throttling strings when provided', () => { diff --git a/lighthouse-core/test/results/artifacts/artifacts.json b/lighthouse-core/test/results/artifacts/artifacts.json index 9901d6c68fb9..64be7b85b9d7 100644 --- a/lighthouse-core/test/results/artifacts/artifacts.json +++ b/lighthouse-core/test/results/artifacts/artifacts.json @@ -2,7 +2,6 @@ "LighthouseRunWarnings": [], "BenchmarkIndex": 1000, "HostFormFactor": "desktop", - "TestedAsMobileDevice": true, "HostUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4232.0 Safari/537.36", "NetworkUserAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) AppleWebKit/537.36(KHTML, like Gecko) Chrome/66.0.3359.30 Mobile Safari/537.36", "fetchTime": "2018-03-13T00:55:45.840Z", @@ -28,8 +27,15 @@ "auditMode": false, "gatherMode": "lighthouse-core/test/results/artifacts", "disableStorageReset": false, - "emulatedFormFactor": "mobile", - "internalDisableDeviceScreenEmulation": false, + "formFactor": "mobile", + "emulatedUserAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Mobile Safari/537.36 Chrome-Lighthouse", + "screenEmulation": { + "deviceScaleFactor": 2.625, + "height": 640, + "mobile": true, + "width": 360, + "disabled": false + }, "channel": "cli", "budgets": [ { diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index cf297defa243..6f83c37eb875 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -4385,7 +4385,7 @@ ], "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, - "throttlingMethod": "devtools", + "formFactor": "mobile", "throttling": { "rttMs": 150, "throughputKbps": 1638.4, @@ -4394,11 +4394,18 @@ "uploadThroughputKbps": 675, "cpuSlowdownMultiplier": 4 }, + "throttlingMethod": "devtools", + "screenEmulation": { + "mobile": true, + "width": 360, + "height": 640, + "deviceScaleFactor": 2.625, + "disabled": false + }, + "emulatedUserAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Mobile Safari/537.36 Chrome-Lighthouse", "auditMode": true, "gatherMode": false, "disableStorageReset": false, - "emulatedFormFactor": "mobile", - "internalDisableDeviceScreenEmulation": false, "channel": "cli", "budgets": [ { diff --git a/lighthouse-core/test/runner-test.js b/lighthouse-core/test/runner-test.js index 0e6e5cbfdb5f..6e4130efdac7 100644 --- a/lighthouse-core/test/runner-test.js +++ b/lighthouse-core/test/runner-test.js @@ -112,7 +112,8 @@ describe('Runner', () => { }); it('-A throws if the settings change', async () => { - const settings = {auditMode: artifactsPath, emulatedFormFactor: 'desktop'}; + // Change throttlingMethod from its default of 'simulate' + const settings = {auditMode: artifactsPath, throttlingMethod: 'provided'}; const opts = {config: generateConfig(settings), driverMock}; try { await Runner.run(defaultGatherFn, opts); @@ -123,7 +124,7 @@ describe('Runner', () => { }); it('-A throws if the URL changes', async () => { - const settings = {auditMode: artifactsPath, emulatedFormFactor: 'desktop'}; + const settings = {auditMode: artifactsPath}; const opts = {url: 'https://differenturl.com', config: generateConfig(settings), driverMock}; try { await Runner.run(defaultGatherFn, opts); @@ -412,7 +413,6 @@ describe('Runner', () => { const artifacts = { ...baseArtifacts, ViewportDimensions: new Error(errorMessage), - TestedAsMobileDevice: true, }; const artifactsPath = '.tmp/test_artifacts'; const resolvedPath = path.resolve(process.cwd(), artifactsPath); @@ -424,7 +424,7 @@ describe('Runner', () => { auditMode: resolvedPath, }, audits: [ - // requires ViewportDimensions and TestedAsMobileDevice artifacts + // requires ViewportDimensions artifact 'content-width', ], }); diff --git a/lighthouse-treemap/app/debug.json b/lighthouse-treemap/app/debug.json index dde3199c7bb6..7864e00abad4 100644 --- a/lighthouse-treemap/app/debug.json +++ b/lighthouse-treemap/app/debug.json @@ -16857,8 +16857,7 @@ "auditMode": true, "gatherMode": false, "disableStorageReset": false, - "emulatedFormFactor": "mobile", - "internalDisableDeviceScreenEmulation": false, + "formFactor": "mobile", "channel": "cli", "budgets": null, "locale": "en-US", diff --git a/proto/lighthouse-result.proto b/proto/lighthouse-result.proto index c103c44ef87a..a2b7fb88fa20 100644 --- a/proto/lighthouse-result.proto +++ b/proto/lighthouse-result.proto @@ -126,23 +126,18 @@ message LighthouseResult { // Message containing the configuration settings for the LH run message ConfigSettings { + // The possible form factors an audit can be run in - enum EmulatedFormFactor { - // Unknown form factor. This should not be used + enum EmulatedFormFactor { // Deprecated UNKNOWN_FORM_FACTOR = 0; - - // Mobile form factor mobile = 1; - - // Desktop form factor desktop = 2; - - // No emulation used, uses system directly - none = 3; + none = 3 [deprecated = true]; } - // The form factor used in the audit - EmulatedFormFactor emulated_form_factor = 1; + + // The form factor emulation to apply. Do not use. + EmulatedFormFactor emulated_form_factor = 1 [deprecated = true]; // The locale that was active during the audit string locale = 2; @@ -154,6 +149,9 @@ message LighthouseResult { // How Lighthouse was run, e.g. from the Chrome extension or from the npm // module string channel = 4; + + // How Lighthouse should interpret this run in regards to scoring performance metrics and skipping mobile-only tests in desktop. + EmulatedFormFactor form_factor = 5; } // The settings that were used to run this audit diff --git a/readme.md b/readme.md index 40d6e840f217..07a852885d83 100644 --- a/readme.md +++ b/readme.md @@ -67,6 +67,10 @@ By default, Lighthouse writes the report to an HTML file. You can control the ou ### CLI options + + ``` $ lighthouse --help @@ -85,12 +89,14 @@ Configuration: --config-path The path to the config JSON. An example config file: lighthouse-core/config/lr-desktop-config.js [string] --preset Use a built-in configuration. - WARNING: If the --config-path flag is provided, this preset will be ignored. [string] [choices: "perf", "experimental"] + WARNING: If the --config-path flag is provided, this preset will be ignored. [string] [choices: "perf", "experimental", "desktop"] --chrome-flags Custom flags to pass to Chrome (space-delimited). For a full list of flags, see https://bit.ly/chrome-flags Additionally, use the CHROME_PATH environment variable to use a specific Chrome binary. Requires Chromium version 66.0 or later. If omitted, any detected Chrome Canary or Chrome stable will be used. [string] [default: ""] --port The port to use for the debugging protocol. Use 0 for a random port [number] [default: 0] --hostname The hostname to use for the debugging protocol. [string] [default: "localhost"] - --emulated-form-factor Controls the emulated device form factor (mobile vs. desktop) if not disabled [string] [choices: "mobile", "desktop", "none"] + --form-factor Determines how performance metrics are scored and if mobile-only audits are skipped. For desktop, --preset=desktop instead. [string] [choices: "mobile", "desktop"] + --screenEmulation Sets screen emulation parameters. See also --preset. Use --screenEmulation.disabled to disable. Otherwise set these 4 parameters individually: --screenEmulation.mobile --screenEmulation.width=360 --screenEmulation.height=640 --screenEmulation.deviceScaleFactor=2 + --emulatedUserAgent Sets useragent emulation [string] --max-wait-for-load The timeout (in milliseconds) to wait before the page is considered done loading and the run should continue. WARNING: Very high values can lead to large traces and instability [number] --enable-error-reporting Enables error reporting, overriding any saved preference. --no-enable-error-reporting will do the opposite. More: https://git.io/vFFTO [boolean] --gather-mode, -G Collect artifacts from a connected browser and save to disk. (Artifacts folder path may optionally be provided). If audit-mode is not also enabled, the run will quit early. @@ -132,15 +138,15 @@ Options: --chrome-ignore-default-flags [boolean] [default: false] Examples: - lighthouse --view Opens the HTML report in a browser after the run completes - lighthouse --config-path=./myconfig.js Runs Lighthouse with your own configuration: custom audits, report generation, etc. - lighthouse --output=json --output-path=./report.json --save-assets Save trace, screenshots, and named JSON report. - lighthouse --emulated-form-factor=none --throttling-method=provided Disable device emulation and all throttling - lighthouse --chrome-flags="--window-size=412,660" Launch Chrome with a specific window size - lighthouse --quiet --chrome-flags="--headless" Launch Headless Chrome, turn off logging - lighthouse --extra-headers "{\"Cookie\":\"monster=blue\", \"x-men\":\"wolverine\"}" Stringify'd JSON HTTP Header key/value pairs to send in requests - lighthouse --extra-headers=./path/to/file.json Path to JSON file of HTTP Header key/value pairs to send in requests - lighthouse --only-categories=performance,pwa Only run the specified categories. Available categories: accessibility, best-practices, performance, pwa, seo + lighthouse --view Opens the HTML report in a browser after the run completes + lighthouse --config-path=./myconfig.js Runs Lighthouse with your own configuration: custom audits, report generation, etc. + lighthouse --output=json --output-path=./report.json --save-assets Save trace, screenshots, and named JSON report. + lighthouse --screenEmulation.disabled --throttling-method=provided --no-emulatedUserAgent Disable device emulation and all throttling + lighthouse --chrome-flags="--window-size=412,660" Launch Chrome with a specific window size + lighthouse --quiet --chrome-flags="--headless" Launch Headless Chrome, turn off logging + lighthouse --extra-headers "{\"Cookie\":\"monster=blue\", \"x-men\":\"wolverine\"}" Stringify'd JSON HTTP Header key/value pairs to send in requests + lighthouse --extra-headers=./path/to/file.json Path to JSON file of HTTP Header key/value pairs to send in requests + lighthouse --only-categories=performance,pwa Only run the specified categories. Available categories: accessibility, best-practices, performance, pwa, seo For more information on Lighthouse, see https://developers.google.com/web/tools/lighthouse/. ``` diff --git a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run-expected.txt b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run-expected.txt index c5305d63fa80..cfbbf99e9e87 100644 --- a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run-expected.txt +++ b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run-expected.txt @@ -15,14 +15,18 @@ Generate report: enabled visible =============== Lighthouse Results =============== URL: http://127.0.0.1:8000/devtools/lighthouse/resources/lighthouse-emulate-pass.html Version: 6.5.0 -TestedAsMobileDevice: true -ViewportDimensions: { - "innerWidth": 360, - "innerHeight": 640, - "outerWidth": 360, - "outerHeight": 640, - "devicePixelRatio": 3 +formFactor: mobile +screenEmulation: { + "mobile": true, + "width": 360, + "height": 640, + "deviceScaleFactor": 2.625, + "disabled": true } +Mobile network UA?: true +Mobile configured UA?: true +throttlingMethod: simulate +throttling.rttMs: 150 content-width: pass undefined diff --git a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run.js b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run.js index 82d73e7b19f1..7cdf274d5865 100644 --- a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run.js +++ b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-emulate-run.js @@ -16,8 +16,12 @@ TestRunner.addResult('\n=============== Lighthouse Results ==============='); TestRunner.addResult(`URL: ${lhr.finalUrl}`); TestRunner.addResult(`Version: ${lhr.lighthouseVersion}`); - TestRunner.addResult(`TestedAsMobileDevice: ${artifacts.TestedAsMobileDevice}`); - TestRunner.addResult(`ViewportDimensions: ${JSON.stringify(artifacts.ViewportDimensions, null, 2)}`); + TestRunner.addResult(`formFactor: ${lhr.configSettings.formFactor}`); + TestRunner.addResult(`screenEmulation: ${JSON.stringify(lhr.configSettings.screenEmulation, null, 2)}`); + TestRunner.addResult(`Mobile network UA?: ${lhr.environment.networkUserAgent.includes('Mobile')}`); + TestRunner.addResult(`Mobile configured UA?: ${lhr.configSettings.emulatedUserAgent.includes('Mobile')}`); + TestRunner.addResult(`throttlingMethod: ${lhr.configSettings.throttlingMethod}`); + TestRunner.addResult(`throttling.rttMs: ${lhr.configSettings.throttling.rttMs}`); TestRunner.addResult('\n'); const auditName = 'content-width'; diff --git a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run-expected.txt b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run-expected.txt index 04281a8bb36f..0970a6eca503 100644 --- a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run-expected.txt +++ b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run-expected.txt @@ -13,10 +13,9 @@ Tests that lighthouse panel passes flags. Generate report: enabled visible =============== Lighthouse Results =============== -emulatedFormFactor: desktop +formFactor: desktop disableStorageReset: false throttlingMethod: devtools -TestedAsMobileDevice: false View Trace Button Text: "View Trace" View Trace Button Title: "" diff --git a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run.js b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run.js index 2ade417e58ec..17a8f422aaaf 100644 --- a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run.js +++ b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-flags-run.js @@ -21,10 +21,9 @@ const {artifacts, lhr} = await LighthouseTestRunner.waitForResults(); TestRunner.addResult('\n=============== Lighthouse Results ==============='); - TestRunner.addResult(`emulatedFormFactor: ${lhr.configSettings.emulatedFormFactor}`); + TestRunner.addResult(`formFactor: ${lhr.configSettings.formFactor}`); TestRunner.addResult(`disableStorageReset: ${lhr.configSettings.disableStorageReset}`); TestRunner.addResult(`throttlingMethod: ${lhr.configSettings.throttlingMethod}`); - TestRunner.addResult(`TestedAsMobileDevice: ${artifacts.TestedAsMobileDevice}`); const viewTraceButton = LighthouseTestRunner.getResultsElement().querySelector('.view-trace'); TestRunner.addResult(`\nView Trace Button Text: "${viewTraceButton.textContent}"`); diff --git a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run-expected.txt b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run-expected.txt index 96dac5fc3784..4c8e8468e1fb 100644 --- a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run-expected.txt +++ b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run-expected.txt @@ -374,7 +374,6 @@ Generating results... =============== Lighthouse Results =============== URL: http://127.0.0.1:8000/devtools/lighthouse/resources/lighthouse-basic.html Version: 6.5.0 -TestedAsMobileDevice: true ViewportDimensions: { "innerWidth": 980, "innerHeight": 1743, diff --git a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run.js b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run.js index c9957f351626..271256d3657e 100644 --- a/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run.js +++ b/third-party/chromium-webtests/webtests/http/tests/devtools/lighthouse/lighthouse-successful-run.js @@ -59,7 +59,6 @@ TestRunner.addResult('\n=============== Lighthouse Results ==============='); TestRunner.addResult(`URL: ${lhr.finalUrl}`); TestRunner.addResult(`Version: ${lhr.lighthouseVersion}`); - TestRunner.addResult(`TestedAsMobileDevice: ${artifacts.TestedAsMobileDevice}`); TestRunner.addResult(`ViewportDimensions: ${JSON.stringify(artifacts.ViewportDimensions, null, 2)}`); TestRunner.addResult('\n'); diff --git a/types/artifacts.d.ts b/types/artifacts.d.ts index 0e6d9355080f..df3951d2532d 100644 --- a/types/artifacts.d.ts +++ b/types/artifacts.d.ts @@ -28,8 +28,6 @@ declare global { fetchTime: string; /** A set of warnings about unexpected things encountered while loading and testing the page. */ LighthouseRunWarnings: Array; - /** Whether the page was loaded on either a real or emulated mobile device. */ - TestedAsMobileDevice: boolean; /** Device which Chrome is running on. */ HostFormFactor: 'desktop'|'mobile'; /** The user agent string of the version of Chrome used. */ diff --git a/types/config.d.ts b/types/config.d.ts index c36d3d09e939..17841ff9da7f 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -84,6 +84,7 @@ declare global { export interface Settings extends Required { throttling: Required; + screenEmulation: ScreenEmulationSettings; } export interface Pass extends Required { diff --git a/types/externs.d.ts b/types/externs.d.ts index eae7cf446524..587d3772fc53 100644 --- a/types/externs.d.ts +++ b/types/externs.d.ts @@ -125,6 +125,19 @@ declare global { export type OutputMode = 'json' | 'html' | 'csv'; + export type ScreenEmulationSettings = { + /** Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override. */ + width: number; + /** Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override. */ + height: number; + /** Overriding device scale factor value. 0 disables the override. */ + deviceScaleFactor: number; + /** Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text autosizing and more. */ + mobile: boolean; + /** Whether screen emulation is disabled. If true, the other emulation settings are ignored. */ + disabled: boolean; + }; + /** * Options that are found in both the flags used by the Lighthouse module * interface and the Config's `settings` object. @@ -148,10 +161,14 @@ declare global { gatherMode?: boolean | string; /** Flag indicating that the browser storage should not be reset for the audit. */ disableStorageReset?: boolean; - /** How emulation (useragent, device screen metrics, touch) should be applied. `none` indicates Lighthouse should leave the host browser as-is. */ - emulatedFormFactor?: 'mobile'|'desktop'|'none'; - /** Dangerous setting only to be used by Lighthouse team. Disables the device metrics and touch emulation that emulatedFormFactor defines. Details in emulation.js */ - internalDisableDeviceScreenEmulation?: boolean + + /** How Lighthouse should interpret this run in regards to scoring performance metrics and skipping mobile-only tests in desktop. Must be set even if throttling/emulation is being applied outside of Lighthouse. */ + formFactor?: 'mobile'|'desktop'; + /** Screen emulation properties (width, height, dpr, mobile viewport) to apply or an object of `{disabled: true}` if Lighthouse should avoid applying screen emulation. If either emulation is applied outside of Lighthouse, or it's being run on a mobile device, it typically should be set to disabled. For desktop, we recommend applying consistent desktop screen emulation. */ + screenEmulation?: Partial; + /** User Agent string to apply, `false` to not change the host's UA string, or `true` to use Lighthouse's default UA string. */ + emulatedUserAgent?: string | boolean; + /** The method used to throttle the network. */ throttlingMethod?: 'devtools'|'simulate'|'provided'; /** The throttling config settings. */ @@ -210,7 +227,7 @@ declare global { /** Flag to print a list of all required trace categories. */ listTraceCategories: boolean; /** A preset audit of selected audit categories to run. */ - preset?: 'experimental'|'perf'; + preset?: 'experimental'|'perf'|'desktop'; /** A flag to enable logLevel 'verbose'. */ verbose: boolean; /** A flag to enable logLevel 'silent'. */