From 7417346e5b367f787d370de9700592cccbb5d5ea Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 22 Aug 2023 09:18:10 -0700 Subject: [PATCH 01/10] start --- core/config/config.js | 75 ++--- core/config/constants.js | 20 +- core/config/validation.js | 62 +--- core/gather/base-artifacts.js | 3 +- core/gather/driver/navigation.js | 2 +- core/gather/driver/prepare.js | 51 +--- core/gather/navigation-runner.js | 145 ++------- core/lib/navigation-error.js | 8 +- core/test/config/config-test.js | 136 ++------- core/test/config/validation-test.js | 25 +- core/test/gather/base-artifacts-test.js | 9 - core/test/gather/driver/prepare-test.js | 147 ++++----- core/test/gather/navigation-runner-test.js | 333 ++++++--------------- core/test/lib/navigation-error-test.js | 15 - types/config.d.ts | 38 --- 15 files changed, 257 insertions(+), 812 deletions(-) diff --git a/core/config/config.js b/core/config/config.js index 259152fd6d45..b206fee1b4a7 100644 --- a/core/config/config.js +++ b/core/config/config.js @@ -10,7 +10,7 @@ import log from 'lighthouse-logger'; import {Runner} from '../runner.js'; import defaultConfig from './default-config.js'; -import {defaultNavigationConfig, nonSimulatedPassConfigOverrides} from './constants.js'; // eslint-disable-line max-len +import {nonSimulatedSettingsOverrides} from './constants.js'; // eslint-disable-line max-len import { throwInvalidDependencyOrder, isValidArtifactDependency, @@ -171,6 +171,8 @@ async function resolveArtifactsToDefns(artifacts, configDir) { artifactDefns.push(artifact); } + assertArtifactTopologicalOrder(artifactDefns); + log.timeEnd(status); return artifactDefns; } @@ -192,61 +194,29 @@ function overrideSettingsForGatherMode(settings, gatherMode) { /** * Overrides the quiet windows when throttlingMethod requires observation. * - * @param {LH.Config.NavigationDefn} navigation * @param {LH.Config.Settings} settings */ -function overrideNavigationThrottlingWindows(navigation, settings) { - if (navigation.disableThrottling) return; +function overrideThrottlingWindows(settings) { if (settings.throttlingMethod === 'simulate') return; - navigation.cpuQuietThresholdMs = Math.max( - navigation.cpuQuietThresholdMs || 0, - nonSimulatedPassConfigOverrides.cpuQuietThresholdMs + settings.cpuQuietThresholdMs = Math.max( + settings.cpuQuietThresholdMs || 0, + nonSimulatedSettingsOverrides.cpuQuietThresholdMs ); - navigation.networkQuietThresholdMs = Math.max( - navigation.networkQuietThresholdMs || 0, - nonSimulatedPassConfigOverrides.networkQuietThresholdMs + settings.networkQuietThresholdMs = Math.max( + settings.networkQuietThresholdMs || 0, + nonSimulatedSettingsOverrides.networkQuietThresholdMs ); - navigation.pauseAfterFcpMs = Math.max( - navigation.pauseAfterFcpMs || 0, - nonSimulatedPassConfigOverrides.pauseAfterFcpMs + settings.pauseAfterFcpMs = Math.max( + settings.pauseAfterFcpMs || 0, + nonSimulatedSettingsOverrides.pauseAfterFcpMs ); - navigation.pauseAfterLoadMs = Math.max( - navigation.pauseAfterLoadMs || 0, - nonSimulatedPassConfigOverrides.pauseAfterLoadMs + settings.pauseAfterLoadMs = Math.max( + settings.pauseAfterLoadMs || 0, + nonSimulatedSettingsOverrides.pauseAfterLoadMs ); } -/** - * @param {LH.Config.AnyArtifactDefn[]|null|undefined} artifactDefns - * @param {LH.Config.Settings} settings - * @return {LH.Config.NavigationDefn[] | null} - */ -function resolveFakeNavigations(artifactDefns, settings) { - if (!artifactDefns) return null; - - const status = {msg: 'Resolve navigation definitions', id: 'lh:config:resolveNavigationsToDefns'}; - log.time(status, 'verbose'); - - const resolvedNavigation = { - ...defaultNavigationConfig, - artifacts: artifactDefns, - pauseAfterFcpMs: settings.pauseAfterFcpMs, - pauseAfterLoadMs: settings.pauseAfterLoadMs, - networkQuietThresholdMs: settings.networkQuietThresholdMs, - cpuQuietThresholdMs: settings.cpuQuietThresholdMs, - blankPage: settings.blankPage, - }; - - overrideNavigationThrottlingWindows(resolvedNavigation, settings); - - const navigations = [resolvedNavigation]; - assertArtifactTopologicalOrder(navigations); - - log.timeEnd(status); - return navigations; -} - /** * @param {LH.Gatherer.GatherMode} gatherMode * @param {LH.Config=} config @@ -264,15 +234,13 @@ async function initializeConfig(gatherMode, config, flags = {}) { const settings = resolveSettings(configWorkingCopy.settings || {}, flags); overrideSettingsForGatherMode(settings, gatherMode); + overrideThrottlingWindows(settings); const artifacts = await resolveArtifactsToDefns(configWorkingCopy.artifacts, configDir); - const navigations = resolveFakeNavigations(artifacts, settings); - /** @type {LH.Config.ResolvedConfig} */ let resolvedConfig = { artifacts, - navigations, audits: await resolveAuditsToDefns(configWorkingCopy.audits, configDir), categories: configWorkingCopy.categories || null, groups: configWorkingCopy.groups || null, @@ -296,15 +264,6 @@ function getConfigDisplayString(resolvedConfig) { /** @type {LH.Config.ResolvedConfig} */ const resolvedConfigCopy = JSON.parse(JSON.stringify(resolvedConfig)); - if (resolvedConfigCopy.navigations) { - for (const navigation of resolvedConfigCopy.navigations) { - for (let i = 0; i < navigation.artifacts.length; ++i) { - // @ts-expect-error Breaking the Config.AnyArtifactDefn type. - navigation.artifacts[i] = navigation.artifacts[i].id; - } - } - } - if (resolvedConfigCopy.artifacts) { for (const artifactDefn of resolvedConfigCopy.artifacts) { // @ts-expect-error Breaking the Config.AnyArtifactDefn type. diff --git a/core/config/constants.js b/core/config/constants.js index b66350f3a62e..876fb2f836be 100644 --- a/core/config/constants.js +++ b/core/config/constants.js @@ -127,22 +127,7 @@ const defaultSettings = { skipAudits: null, }; -/** @type {Required} */ -const defaultNavigationConfig = { - id: 'defaultPass', - loadFailureMode: 'fatal', - disableThrottling: false, - disableStorageReset: false, - pauseAfterFcpMs: 0, - pauseAfterLoadMs: 0, - networkQuietThresholdMs: 0, - cpuQuietThresholdMs: 0, - blockedUrlPatterns: [], - blankPage: 'about:blank', - artifacts: [], -}; - -const nonSimulatedPassConfigOverrides = { +const nonSimulatedSettingsOverrides = { pauseAfterFcpMs: 5250, pauseAfterLoadMs: 5250, networkQuietThresholdMs: 5250, @@ -154,6 +139,5 @@ export { screenEmulationMetrics, userAgents, defaultSettings, - defaultNavigationConfig, - nonSimulatedPassConfigOverrides, + nonSimulatedSettingsOverrides, }; diff --git a/core/config/validation.js b/core/config/validation.js index 0128565cee1d..74cd196249a8 100644 --- a/core/config/validation.js +++ b/core/config/validation.js @@ -68,43 +68,6 @@ function assertValidArtifact(artifactDefn) { } } -/** - * Throws an error if the provided object does not implement the required navigations interface. - * @param {LH.Config.ResolvedConfig['navigations']} navigationsDefn - * @return {{warnings: string[]}} - */ -function assertValidNavigations(navigationsDefn) { - if (!navigationsDefn || !navigationsDefn.length) return {warnings: []}; - - /** @type {string[]} */ - const warnings = []; - - // Assert that the first navigation has loadFailureMode fatal. - const firstNavigation = navigationsDefn[0]; - if (firstNavigation.loadFailureMode !== 'fatal') { - const currentMode = firstNavigation.loadFailureMode; - const warning = [ - `"${firstNavigation.id}" is the first navigation but had a failure mode of ${currentMode}.`, - `The first navigation will always be treated as loadFailureMode=fatal.`, - ].join(' '); - - warnings.push(warning); - firstNavigation.loadFailureMode = 'fatal'; - } - - // Assert that navigations have unique IDs. - const navigationIds = navigationsDefn.map(navigation => navigation.id); - const duplicateId = navigationIds.find( - (id, i) => navigationIds.slice(i + 1).some(other => id === other) - ); - - if (duplicateId) { - throw new Error(`Navigation must have unique identifiers, but "${duplicateId}" was repeated.`); - } - - return {warnings}; -} - /** * Throws an error if the provided object does not implement the required properties of an audit * definition. @@ -225,20 +188,18 @@ function assertValidSettings(settings) { /** * Asserts that artifacts are in a valid dependency order that can be computed. * - * @param {Array} navigations + * @param {Array} artifactDefns */ -function assertArtifactTopologicalOrder(navigations) { +function assertArtifactTopologicalOrder(artifactDefns) { const availableArtifacts = new Set(); - for (const navigation of navigations) { - for (const artifact of navigation.artifacts) { - availableArtifacts.add(artifact.id); - if (!artifact.dependencies) continue; + for (const artifact of artifactDefns) { + availableArtifacts.add(artifact.id); + if (!artifact.dependencies) continue; - for (const [dependencyKey, {id: dependencyId}] of Object.entries(artifact.dependencies)) { - if (availableArtifacts.has(dependencyId)) continue; - throwInvalidDependencyOrder(artifact.id, dependencyKey); - } + for (const [dependencyKey, {id: dependencyId}] of Object.entries(artifact.dependencies)) { + if (availableArtifacts.has(dependencyId)) continue; + throwInvalidDependencyOrder(artifact.id, dependencyKey); } } } @@ -248,8 +209,6 @@ function assertArtifactTopologicalOrder(navigations) { * @return {{warnings: string[]}} */ function assertValidConfig(resolvedConfig) { - const {warnings} = assertValidNavigations(resolvedConfig.navigations); - /** @type {Set} */ const artifactIds = new Set(); for (const artifactDefn of resolvedConfig.artifacts || []) { @@ -266,7 +225,9 @@ function assertValidConfig(resolvedConfig) { assertValidCategories(resolvedConfig.categories, resolvedConfig.audits, resolvedConfig.groups); assertValidSettings(resolvedConfig.settings); - return {warnings}; + + // Currently no warnings are available, but leaving this here if we ever decide to introduce any. + return {warnings: []}; } /** @@ -303,7 +264,6 @@ export { isValidArtifactDependency, assertValidPluginName, assertValidArtifact, - assertValidNavigations, assertValidAudit, assertValidCategories, assertValidSettings, diff --git a/core/gather/base-artifacts.js b/core/gather/base-artifacts.js index c1b9e80ec90e..3ec1fb87bd88 100644 --- a/core/gather/base-artifacts.js +++ b/core/gather/base-artifacts.js @@ -60,12 +60,11 @@ function deduplicateWarnings(warnings) { /** * @param {LH.BaseArtifacts} baseArtifacts - * @param {Partial} gathererArtifacts + * @param {Partial} gathererArtifacts * @return {LH.Artifacts} */ function finalizeArtifacts(baseArtifacts, gathererArtifacts) { const warnings = baseArtifacts.LighthouseRunWarnings - .concat(gathererArtifacts.LighthouseRunWarnings || []) .concat(getEnvironmentWarnings({settings: baseArtifacts.settings, baseArtifacts})); // Cast to remove the partial from gathererArtifacts. diff --git a/core/gather/driver/navigation.js b/core/gather/driver/navigation.js index 209e1d734e39..6388872a7e9e 100644 --- a/core/gather/driver/navigation.js +++ b/core/gather/driver/navigation.js @@ -39,7 +39,7 @@ const DEFAULT_NETWORK_QUIET_THRESHOLD = 5000; // Controls how long to wait between longtasks before determining the CPU is idle, off by default const DEFAULT_CPU_QUIET_THRESHOLD = 0; -/** @typedef {{waitUntil: Array<'fcp'|'load'|'navigated'>} & LH.Config.SharedPassNavigationJson & Partial>} NavigationOptions */ +/** @typedef {{waitUntil: Array<'fcp'|'load'|'navigated'>} & Partial} NavigationOptions */ /** @param {NavigationOptions} options */ function resolveWaitForFullyLoadedOptions(options) { diff --git a/core/gather/driver/prepare.js b/core/gather/driver/prepare.js index 2349bda315f6..1200f74ea2c9 100644 --- a/core/gather/driver/prepare.js +++ b/core/gather/driver/prepare.js @@ -114,21 +114,18 @@ async function resetStorageForUrl(session, url) { * * @param {LH.Gatherer.ProtocolSession} session * @param {LH.Config.Settings} settings - * @param {{disableThrottling: boolean, blockedUrlPatterns?: string[]}} options */ -async function prepareThrottlingAndNetwork(session, settings, options) { +async function prepareThrottlingAndNetwork(session, settings) { const status = {msg: 'Preparing network conditions', id: `lh:gather:prepareThrottlingAndNetwork`}; log.time(status); - if (options.disableThrottling) await emulation.clearThrottling(session); + if (settings.throttlingMethod === 'provided') await emulation.clearThrottling(session); else await emulation.throttle(session, settings); // Set request blocking before any network activity. // No "clearing" is done at the end of the recording since Network.setBlockedURLs([]) will unset all if // neccessary at the beginning of the next section. - const blockedUrls = (options.blockedUrlPatterns || []).concat( - settings.blockedUrlPatterns || [] - ); + const blockedUrls = settings.blockedUrlPatterns || []; await session.sendCommand('Network.setBlockedURLs', {urls: blockedUrls}); const headers = settings.extraHeaders; @@ -163,10 +160,7 @@ async function prepareTargetForTimespanMode(driver, settings) { log.time(status); await prepareDeviceEmulation(driver, settings); - await prepareThrottlingAndNetwork(driver.defaultSession, settings, { - disableThrottling: false, - blockedUrlPatterns: undefined, - }); + await prepareThrottlingAndNetwork(driver.defaultSession, settings); await warmUpIntlSegmenter(driver); log.timeEnd(status); @@ -192,11 +186,16 @@ async function warmUpIntlSegmenter(driver) { * * @param {LH.Gatherer.Driver} driver * @param {LH.Config.Settings} settings + * @param {LH.NavigationRequestor} requestor + * @return {Promise<{warnings: Array}>} */ -async function prepareTargetForNavigationMode(driver, settings) { +async function prepareTargetForNavigationMode(driver, settings, requestor) { const status = {msg: 'Preparing target for navigation mode', id: 'lh:prepare:navigationMode'}; log.time(status); + /** @type {Array} */ + const warnings = []; + await prepareDeviceEmulation(driver, settings); // Automatically handle any JavaScript dialogs to prevent a hung renderer. @@ -212,41 +211,20 @@ async function prepareTargetForNavigationMode(driver, settings) { await warmUpIntlSegmenter(driver); - log.timeEnd(status); -} - -/** - * Prepares a target for a particular navigation by resetting storage and setting network. - * - * This method assumes `prepareTargetForNavigationMode` has already been invoked. - * - * @param {LH.Gatherer.ProtocolSession} session - * @param {LH.Config.Settings} settings - * @param {Pick & {requestor: LH.NavigationRequestor}} navigation - * @return {Promise<{warnings: Array}>} - */ -async function prepareTargetForIndividualNavigation(session, settings, navigation) { - const status = {msg: 'Preparing target for navigation', id: 'lh:prepare:navigation'}; - log.time(status); - - /** @type {Array} */ - const warnings = []; - - const {requestor} = navigation; const shouldResetStorage = !settings.disableStorageReset && - !navigation.disableStorageReset && // Without prior knowledge of the destination, we cannot know which URL to clear storage for. typeof requestor === 'string'; if (shouldResetStorage) { - const requestedUrl = requestor; - const {warnings: storageWarnings} = await resetStorageForUrl(session, requestedUrl); + const {warnings: storageWarnings} = + await resetStorageForUrl(driver.defaultSession, requestor); warnings.push(...storageWarnings); } - await prepareThrottlingAndNetwork(session, settings, navigation); + await prepareThrottlingAndNetwork(driver.defaultSession, settings); log.timeEnd(status); + return {warnings}; } @@ -254,6 +232,5 @@ export { prepareThrottlingAndNetwork, prepareTargetForTimespanMode, prepareTargetForNavigationMode, - prepareTargetForIndividualNavigation, enableAsyncStacks, }; diff --git a/core/gather/navigation-runner.js b/core/gather/navigation-runner.js index c4cf1136a25c..332c4e1d36db 100644 --- a/core/gather/navigation-runner.js +++ b/core/gather/navigation-runner.js @@ -14,7 +14,6 @@ import * as prepare from './driver/prepare.js'; import {gotoURL} from './driver/navigation.js'; import * as storage from './driver/storage.js'; import * as emulation from '../lib/emulation.js'; -import {defaultNavigationConfig} from '../config/constants.js'; import {initializeConfig} from '../config/config.js'; import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js'; import * as format from '../../shared/localization/format.js'; @@ -30,7 +29,6 @@ import {NetworkRecords} from '../computed/network-records.js'; * @property {Driver} driver * @property {LH.Puppeteer.Page} page * @property {LH.Config.ResolvedConfig} resolvedConfig - * @property {LH.Config.NavigationDefn} navigation * @property {LH.NavigationRequestor} requestor * @property {LH.BaseArtifacts} baseArtifacts * @property {Map} computedCache @@ -50,36 +48,17 @@ async function _setup({driver, resolvedConfig, requestor}) { // We can't trigger the navigation through user interaction if we reset the page before starting. if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) { - await gotoURL(driver, defaultNavigationConfig.blankPage, {waitUntil: ['navigated']}); + await gotoURL(driver, resolvedConfig.settings.blankPage, {waitUntil: ['navigated']}); } const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'navigation'}); - await prepare.prepareTargetForNavigationMode(driver, resolvedConfig.settings); + const {warnings} = + await prepare.prepareTargetForNavigationMode(driver, resolvedConfig.settings, requestor); - return {baseArtifacts}; -} - -/** - * @param {NavigationContext} navigationContext - * @return {Promise<{warnings: Array}>} - */ -async function _setupNavigation({requestor, driver, navigation, resolvedConfig}) { - // We can't trigger the navigation through user interaction if we reset the page before starting. - if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) { - await gotoURL(driver, navigation.blankPage, {...navigation, waitUntil: ['navigated']}); - } - - const {warnings} = await prepare.prepareTargetForIndividualNavigation( - driver.defaultSession, - resolvedConfig.settings, - { - ...navigation, - requestor, - } - ); + baseArtifacts.LighthouseRunWarnings.push(...warnings); - return {warnings}; + return {baseArtifacts}; } /** @@ -91,20 +70,20 @@ async function _cleanupNavigation({driver}) { /** * @param {NavigationContext} navigationContext - * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined, warnings: Array}>} + * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined}>} */ async function _navigate(navigationContext) { const {driver, resolvedConfig, requestor} = navigationContext; try { const {requestedUrl, mainDocumentUrl, warnings} = await gotoURL(driver, requestor, { - ...navigationContext.navigation, - debugNavigation: resolvedConfig.settings.debugNavigation, - maxWaitForFcp: resolvedConfig.settings.maxWaitForFcp, - maxWaitForLoad: resolvedConfig.settings.maxWaitForLoad, - waitUntil: navigationContext.navigation.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'], + ...resolvedConfig.settings, + waitUntil: resolvedConfig.settings.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'], }); - return {requestedUrl, mainDocumentUrl, navigationError: undefined, warnings}; + + navigationContext.baseArtifacts.LighthouseRunWarnings.push(...warnings); + + return {requestedUrl, mainDocumentUrl, navigationError: undefined}; } catch (err) { if (!(err instanceof LighthouseError)) throw err; if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG') throw err; @@ -115,7 +94,6 @@ async function _navigate(navigationContext) { requestedUrl: requestor, mainDocumentUrl: requestor, navigationError: err, - warnings: [], }; } } @@ -158,36 +136,32 @@ async function _collectDebugData(navigationContext, phaseState) { /** * @param {NavigationContext} navigationContext * @param {PhaseState} phaseState - * @param {Awaited>} setupResult * @param {Awaited>} navigateResult - * @return {Promise<{artifacts: Partial, warnings: Array, pageLoadError: LH.LighthouseError | undefined}>} + * @return {Promise>} */ async function _computeNavigationResult( navigationContext, phaseState, - setupResult, navigateResult ) { - const {navigationError, mainDocumentUrl} = navigateResult; - const warnings = [...setupResult.warnings, ...navigateResult.warnings]; + const {navigationError, requestedUrl, mainDocumentUrl} = navigateResult; const debugData = await _collectDebugData(navigationContext, phaseState); const pageLoadError = debugData.records ? getPageLoadError(navigationError, { url: mainDocumentUrl, - loadFailureMode: navigationContext.navigation.loadFailureMode, networkRecords: debugData.records, - warnings, + warnings: navigationContext.baseArtifacts.LighthouseRunWarnings, }) : navigationError; if (pageLoadError) { const locale = navigationContext.resolvedConfig.settings.locale; const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage, locale); - log.error('NavigationRunner', localizedMessage, navigateResult.requestedUrl); + log.error('NavigationRunner', localizedMessage, requestedUrl); /** @type {Partial} */ const artifacts = {}; - const pageLoadErrorId = `pageLoadError-${navigationContext.navigation.id}`; + const pageLoadErrorId = 'pageLoadError-defaultPass'; if (debugData.devtoolsLog) { artifacts.DevtoolsLogError = debugData.devtoolsLog; artifacts.devtoolsLogs = {[pageLoadErrorId]: debugData.devtoolsLog}; @@ -197,20 +171,14 @@ async function _computeNavigationResult( artifacts.traces = {[pageLoadErrorId]: debugData.trace}; } - return { - pageLoadError, - artifacts, - warnings: [...warnings, pageLoadError.friendlyMessage], - }; + navigationContext.baseArtifacts.LighthouseRunWarnings.push(pageLoadError.friendlyMessage); + navigationContext.baseArtifacts.PageLoadError = pageLoadError; + + return artifacts; } else { await collectPhaseArtifacts({phase: 'getArtifact', ...phaseState}); - const artifacts = await awaitArtifacts(phaseState.artifactState); - return { - artifacts, - warnings, - pageLoadError: undefined, - }; + return await awaitArtifacts(phaseState.artifactState); } } @@ -219,6 +187,10 @@ async function _computeNavigationResult( * @return {ReturnType} */ async function _navigation(navigationContext) { + if (!navigationContext.resolvedConfig.artifacts) { + throw new Error('No artifacts were defined on the config'); + } + const artifactState = getEmptyArtifactState(); const phaseState = { url: await navigationContext.driver.url(), @@ -226,14 +198,12 @@ async function _navigation(navigationContext) { driver: navigationContext.driver, page: navigationContext.page, computedCache: navigationContext.computedCache, - artifactDefinitions: navigationContext.navigation.artifacts, + artifactDefinitions: navigationContext.resolvedConfig.artifacts, artifactState, baseArtifacts: navigationContext.baseArtifacts, settings: navigationContext.resolvedConfig.settings, }; - const setupResult = await _setupNavigation(navigationContext); - const disableAsyncStacks = await prepare.enableAsyncStacks(navigationContext.driver.defaultSession); @@ -262,58 +232,7 @@ async function _navigation(navigationContext) { await _cleanupNavigation(navigationContext); - return _computeNavigationResult(navigationContext, phaseState, setupResult, navigateResult); -} - -/** - * @param {{driver: Driver, page: LH.Puppeteer.Page, resolvedConfig: LH.Config.ResolvedConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.BaseArtifacts, computedCache: NavigationContext['computedCache']}} args - * @return {Promise<{artifacts: Partial}>} - */ -async function _navigations(args) { - const { - driver, - page, - resolvedConfig, - requestor, - baseArtifacts, - computedCache, - } = args; - - if (!resolvedConfig.artifacts || !resolvedConfig.navigations) { - throw new Error('No artifacts were defined on the config'); - } - - /** @type {Partial} */ - const artifacts = {}; - /** @type {Array} */ - const LighthouseRunWarnings = []; - - for (const navigation of resolvedConfig.navigations) { - const navigationContext = { - driver, - page, - navigation, - requestor, - resolvedConfig, - baseArtifacts, - computedCache, - }; - - let shouldHaltNavigations = false; - const navigationResult = await _navigation(navigationContext); - if (navigation.loadFailureMode === 'fatal') { - if (navigationResult.pageLoadError) { - artifacts.PageLoadError = navigationResult.pageLoadError; - shouldHaltNavigations = true; - } - } - - LighthouseRunWarnings.push(...navigationResult.warnings); - Object.assign(artifacts, navigationResult.artifacts); - if (shouldHaltNavigations) break; - } - - return {artifacts: {...artifacts, LighthouseRunWarnings}}; + return _computeNavigationResult(navigationContext, phaseState, navigateResult); } /** @@ -369,11 +288,15 @@ async function navigationGather(page, requestor, options = {}) { driver, lhBrowser, lhPage, + page, resolvedConfig, requestor: normalizedRequestor, + computedCache: new Map(), }; const {baseArtifacts} = await _setup(context); - const {artifacts} = await _navigations({...context, page, baseArtifacts, computedCache}); + + const artifacts = await _navigation({...context, baseArtifacts}); + await _cleanup(context); return finalizeArtifacts(baseArtifacts, artifacts); @@ -386,9 +309,7 @@ async function navigationGather(page, requestor, options = {}) { export { navigationGather, _setup, - _setupNavigation, _navigate, _navigation, - _navigations, _cleanup, }; diff --git a/core/lib/navigation-error.js b/core/lib/navigation-error.js index ea155442df37..d49a6261c3fd 100644 --- a/core/lib/navigation-error.js +++ b/core/lib/navigation-error.js @@ -113,11 +113,11 @@ function getNonHtmlError(finalRecord) { * Returns an error if the page load should be considered failed, e.g. from a * main document request failure, a security issue, etc. * @param {LH.LighthouseError|undefined} navigationError - * @param {{url: string, loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'], networkRecords: Array, warnings: Array}} context + * @param {{url: string, networkRecords: Array, warnings: Array}} context * @return {LH.LighthouseError|undefined} */ function getPageLoadError(navigationError, context) { - const {url, loadFailureMode, networkRecords} = context; + const {url, networkRecords} = context; /** @type {LH.Artifacts.NetworkRequest|undefined} */ let mainRecord = NetworkAnalyzer.findResourceForUrl(networkRecords, url); @@ -148,10 +148,6 @@ function getPageLoadError(navigationError, context) { const interstitialError = getInterstitialError(mainRecord, networkRecords); const nonHtmlError = getNonHtmlError(finalRecord); - // Check to see if we need to ignore the page load failure. - // e.g. When the driver is offline, the load will fail without page offline support. - if (loadFailureMode === 'ignore') return; - // We want to special-case the interstitial beyond FAILED_DOCUMENT_REQUEST. See https://github.com/GoogleChrome/lighthouse/pull/8865#issuecomment-497507618 if (interstitialError) return interstitialError; diff --git a/core/test/config/config-test.js b/core/test/config/config-test.js index 002eb6a25a12..2622f9a04bbe 100644 --- a/core/test/config/config-test.js +++ b/core/test/config/config-test.js @@ -7,14 +7,12 @@ import jestMock from 'jest-mock'; import {Audit as BaseAudit} from '../../audits/audit.js'; -import * as constants from '../../config/constants.js'; import BaseGatherer from '../../gather/base-gatherer.js'; import {initializeConfig, getConfigDisplayString} from '../../config/config.js'; import {LH_ROOT} from '../../../root.js'; import * as format from '../../../shared/localization/format.js'; import defaultConfig from '../../config/default-config.js'; - -const {nonSimulatedPassConfigOverrides} = constants; +import {nonSimulatedSettingsOverrides} from '../../config/constants.js'; describe('Config', () => { /** @type {LH.Gatherer.GatherMode} */ @@ -76,6 +74,27 @@ describe('Config', () => { }); }); + it('should ensure minimum quiet thresholds when throttlingMethod is devtools', async () => { + gatherMode = 'navigation'; + const config = { + settings: { + cpuQuietThresholdMs: 10_000, + }, + artifacts: [{id: 'Accessibility', gatherer: 'accessibility'}], + }; + + const {resolvedConfig} = await initializeConfig(gatherMode, config, { + throttlingMethod: 'devtools', + }); + + expect(resolvedConfig.settings).toMatchObject({ + cpuQuietThresholdMs: 10_000, + pauseAfterFcpMs: nonSimulatedSettingsOverrides.pauseAfterFcpMs, + pauseAfterLoadMs: nonSimulatedSettingsOverrides.pauseAfterLoadMs, + networkQuietThresholdMs: nonSimulatedSettingsOverrides.networkQuietThresholdMs, + }); + }); + it('should resolve artifact definitions', async () => { const config = {artifacts: [{id: 'Accessibility', gatherer: 'accessibility'}]}; const {resolvedConfig} = await initializeConfig(gatherMode, config); @@ -203,25 +222,6 @@ describe('Config', () => { }); }); - it('should resolve artifact dependencies in navigations', async () => { - const {resolvedConfig} = await initializeConfig('snapshot', config); - expect(resolvedConfig).toMatchObject({ - navigations: [ - { - artifacts: [ - {id: 'Dependency'}, - { - id: 'Dependent', - dependencies: { - ImageElements: {id: 'Dependency'}, - }, - }, - ], - }, - ], - }); - }); - it('should throw when dependencies are out of order in artifacts', async () => { if (!config.artifacts) throw new Error('Failed to run beforeEach'); config.artifacts = [config.artifacts[1], config.artifacts[0]]; @@ -244,70 +244,6 @@ describe('Config', () => { }); }); - describe('.resolveFakeNavigations', () => { - it('should resolve a single fake navigation definitions', async () => { - const config = { - artifacts: [{id: 'Accessibility', gatherer: 'accessibility'}], - }; - const {resolvedConfig} = await initializeConfig('navigation', config); - - expect(resolvedConfig).toMatchObject({ - artifacts: [{id: 'Accessibility', gatherer: {path: 'accessibility'}}], - navigations: [{ - id: 'defaultPass', - artifacts: [{id: 'Accessibility', gatherer: {path: 'accessibility'}}], - }], - }); - }); - - it('should set default properties on navigations', async () => { - gatherMode = 'navigation'; - const config = { - artifacts: [{id: 'Accessibility', gatherer: 'accessibility'}], - }; - const {resolvedConfig} = await initializeConfig(gatherMode, config); - - expect(resolvedConfig).toMatchObject({ - navigations: [ - { - id: 'defaultPass', - blankPage: 'about:blank', - artifacts: [{id: 'Accessibility', gatherer: {path: 'accessibility'}}], - loadFailureMode: 'fatal', - disableThrottling: false, - networkQuietThresholdMs: 1000, - cpuQuietThresholdMs: 1000, - }, - ], - }); - }); - - it('should ensure minimum quiet thresholds when throttlingMethod is devtools', async () => { - gatherMode = 'navigation'; - const config = { - settings: { - cpuQuietThresholdMs: 10_000, - }, - artifacts: [{id: 'Accessibility', gatherer: 'accessibility'}], - }; - - const {resolvedConfig} = await initializeConfig(gatherMode, config, { - throttlingMethod: 'devtools', - }); - - expect(resolvedConfig).toMatchObject({ - navigations: [ - { - cpuQuietThresholdMs: 10_000, - pauseAfterFcpMs: nonSimulatedPassConfigOverrides.pauseAfterFcpMs, - pauseAfterLoadMs: nonSimulatedPassConfigOverrides.pauseAfterLoadMs, - networkQuietThresholdMs: nonSimulatedPassConfigOverrides.networkQuietThresholdMs, - }, - ], - }); - }); - }); - describe('.resolveExtensions', () => { /** @type {LH.Config} */ let extensionConfig; @@ -366,9 +302,6 @@ describe('Config', () => { artifacts: [ {id: 'Accessibility'}, ], - navigations: [ - {id: 'defaultPass', artifacts: [{id: 'Accessibility'}]}, - ], }); }); @@ -414,18 +347,6 @@ describe('Config', () => { ]); }); - it('should merge in navigations', async () => { - const {resolvedConfig} = await initializeConfig('navigation', extensionConfig); - if (!resolvedConfig.navigations) throw new Error(`No navigations created`); - - expect(resolvedConfig.navigations).toHaveLength(1); - const hasNavigation = resolvedConfig.navigations[0].artifacts. - some(a => a.id === 'ExtraArtifact'); - if (!hasNavigation) { - expect(resolvedConfig.navigations[0].artifacts).toContain('ExtraArtifact'); - } - }); - it('should merge in audits', async () => { const {resolvedConfig} = await initializeConfig('navigation', extensionConfig); if (!resolvedConfig.audits) throw new Error(`No audits created`); @@ -453,19 +374,6 @@ describe('Config', () => { }); }); - it('should use failure mode fatal for the fake navigation', async () => { - /** @type {LH.Config} */ - const extensionConfig = { - extends: 'lighthouse:default', - }; - - const {resolvedConfig, warnings} = await initializeConfig('navigation', extensionConfig); - const navigations = resolvedConfig.navigations; - if (!navigations) throw new Error(`Failed to initialize navigations`); - expect(warnings).toHaveLength(0); - expect(navigations[0].loadFailureMode).toEqual('fatal'); - }); - it('should validate the resolvedConfig with fatal errors', async () => { /** @type {LH.Config} */ const extensionConfig = { diff --git a/core/test/config/validation-test.js b/core/test/config/validation-test.js index 1c454456c610..7525f61f71f2 100644 --- a/core/test/config/validation-test.js +++ b/core/test/config/validation-test.js @@ -4,7 +4,7 @@ * 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. */ -import {defaultSettings, defaultNavigationConfig} from '../../config/constants.js'; +import {defaultSettings} from '../../config/constants.js'; import defaultConfig from '../../config/default-config.js'; import {Audit as BaseAudit} from '../../audits/audit.js'; import BaseGatherer from '../../gather/base-gatherer.js'; @@ -114,29 +114,6 @@ describe('Config Validation', () => { }); }); - describe('.assertValidNavigations', () => { - it('should add warning if navigations uses non-fatal loadFailureMode', () => { - /** @type {Array} */ - const navigations = [{...defaultNavigationConfig, loadFailureMode: 'warn', artifacts: []}]; - const {warnings} = validation.assertValidNavigations(navigations); - expect(warnings).toHaveLength(1); - expect(warnings[0]).toContain('but had a failure mode'); - expect(navigations[0].loadFailureMode).toEqual('fatal'); - }); - - - it('should throw if navigations do not have unique ids', () => { - /** @type {Array} */ - const navigations = [ - {...defaultNavigationConfig, id: 'first', artifacts: []}, - {...defaultNavigationConfig, id: 'second', artifacts: []}, - {...defaultNavigationConfig, id: 'first', artifacts: []}, - ]; - const invocation = () => validation.assertValidNavigations(navigations); - expect(invocation).toThrow(/must have unique.*but "first" was repeated/); - }); - }); - describe('.assertValidAudit', () => { it('should throw if audit is not a function', () => { // @ts-expect-error - We are intentionally creating a malformed input. diff --git a/core/test/gather/base-artifacts-test.js b/core/test/gather/base-artifacts-test.js index 757d6a2a2732..d0dbc250bc05 100644 --- a/core/test/gather/base-artifacts-test.js +++ b/core/test/gather/base-artifacts-test.js @@ -65,7 +65,6 @@ describe('finalizeArtifacts', () => { it('should merge the two objects', () => { baseArtifacts.LighthouseRunWarnings = [{i18nId: '1', formattedDefault: 'Yes'}]; - gathererArtifacts.LighthouseRunWarnings = [{i18nId: '2', formattedDefault: 'No'}]; gathererArtifacts.HostUserAgent = 'Desktop Chrome'; const winningError = new LighthouseError(LighthouseError.errors.NO_LCP); @@ -85,7 +84,6 @@ describe('finalizeArtifacts', () => { RobotsTxt: {status: 404, content: null}, LighthouseRunWarnings: [ {i18nId: '1', formattedDefault: 'Yes'}, - {i18nId: '2', formattedDefault: 'No'}, ], }); }); @@ -109,18 +107,11 @@ describe('finalizeArtifacts', () => { {i18nId: '1', formattedDefault: 'Yes', values: {test: 1}}, {i18nId: '1', formattedDefault: 'Yes', values: {test: 2}}, ]; - gathererArtifacts.LighthouseRunWarnings = [ - {i18nId: '1', formattedDefault: 'Yes', values: {test: 1}}, - {i18nId: '1', formattedDefault: 'Yes', values: {test: 3}}, - {i18nId: '2', formattedDefault: 'No'}, - ]; const artifacts = finalizeArtifacts(baseArtifacts, gathererArtifacts); expect(artifacts.LighthouseRunWarnings).toEqual([ {i18nId: '1', formattedDefault: 'Yes', values: {test: 1}}, {i18nId: '1', formattedDefault: 'Yes', values: {test: 2}}, - {i18nId: '1', formattedDefault: 'Yes', values: {test: 3}}, - {i18nId: '2', formattedDefault: 'No'}, ]); }); diff --git a/core/test/gather/driver/prepare-test.js b/core/test/gather/driver/prepare-test.js index fd97233c1f4e..1ea085cac8b2 100644 --- a/core/test/gather/driver/prepare-test.js +++ b/core/test/gather/driver/prepare-test.js @@ -52,8 +52,7 @@ describe('.prepareThrottlingAndNetwork()', () => { uploadThroughputKbps: 8, cpuSlowdownMultiplier: 2, }, - }, - constants.defaultNavigationConfig + } ); expect(sessionMock.sendCommand.findInvocation('Network.emulateNetworkConditions')).toEqual({ @@ -72,7 +71,7 @@ describe('.prepareThrottlingAndNetwork()', () => { sessionMock.asSession(), { ...constants.defaultSettings, - throttlingMethod: 'devtools', + throttlingMethod: 'provided', throttling: { ...constants.defaultSettings.throttling, requestLatencyMs: 100, @@ -80,10 +79,6 @@ describe('.prepareThrottlingAndNetwork()', () => { uploadThroughputKbps: 8, cpuSlowdownMultiplier: 2, }, - }, - { - ...constants.defaultNavigationConfig, - disableThrottling: true, } ); @@ -104,10 +99,6 @@ describe('.prepareThrottlingAndNetwork()', () => { { ...constants.defaultSettings, blockedUrlPatterns: null, - }, - { - ...constants.defaultNavigationConfig, - blockedUrlPatterns: [], } ); @@ -122,23 +113,18 @@ describe('.prepareThrottlingAndNetwork()', () => { { ...constants.defaultSettings, blockedUrlPatterns: ['https://a.example.com'], - }, - { - ...constants.defaultNavigationConfig, - blockedUrlPatterns: ['https://b.example.com'], } ); expect(sessionMock.sendCommand.findInvocation('Network.setBlockedURLs')).toEqual({ - urls: ['https://b.example.com', 'https://a.example.com'], + urls: ['https://a.example.com'], }); }); it('sets extraHeaders', async () => { await prepare.prepareThrottlingAndNetwork( sessionMock.asSession(), - {...constants.defaultSettings, extraHeaders: {'Cookie': 'monster', 'x-men': 'wolverine'}}, - {...constants.defaultNavigationConfig} + {...constants.defaultSettings, extraHeaders: {'Cookie': 'monster', 'x-men': 'wolverine'}} ); expect(sessionMock.sendCommand.findInvocation('Network.setExtraHTTPHeaders')).toEqual({ @@ -150,71 +136,9 @@ describe('.prepareThrottlingAndNetwork()', () => { }); }); -describe('.prepareTargetForIndividualNavigation()', () => { - it('clears storage when not disabled', async () => { - await prepare.prepareTargetForIndividualNavigation( - sessionMock.asSession(), - {...constants.defaultSettings, disableStorageReset: false}, - {...constants.defaultNavigationConfig, disableStorageReset: false, requestor: url} - ); - - expect(storageMock.clearDataForOrigin).toHaveBeenCalled(); - expect(storageMock.clearBrowserCaches).toHaveBeenCalled(); - }); - - it('does not clear storage when globally disabled', async () => { - await prepare.prepareTargetForIndividualNavigation( - sessionMock.asSession(), - {...constants.defaultSettings, disableStorageReset: true}, - {...constants.defaultNavigationConfig, disableStorageReset: false, requestor: url} - ); - - expect(storageMock.clearDataForOrigin).not.toHaveBeenCalled(); - expect(storageMock.clearBrowserCaches).not.toHaveBeenCalled(); - }); - - it('does not clear storage when disabled per navigation', async () => { - await prepare.prepareTargetForIndividualNavigation( - sessionMock.asSession(), - {...constants.defaultSettings, disableStorageReset: false}, - {...constants.defaultNavigationConfig, disableStorageReset: true, requestor: url} - ); - - expect(storageMock.clearDataForOrigin).not.toHaveBeenCalled(); - expect(storageMock.clearBrowserCaches).not.toHaveBeenCalled(); - }); - - it('does not clear storage when given a callback requestor', async () => { - await prepare.prepareTargetForIndividualNavigation( - sessionMock.asSession(), - {...constants.defaultSettings, disableStorageReset: false}, - {...constants.defaultNavigationConfig, disableStorageReset: false, requestor: () => {}} - ); - - expect(storageMock.clearDataForOrigin).not.toHaveBeenCalled(); - expect(storageMock.clearBrowserCaches).not.toHaveBeenCalled(); - }); - - it('collects storage warnings', async () => { - storageMock.getImportantStorageWarning.mockResolvedValue('This is a storage warning'); - storageMock.clearDataForOrigin.mockResolvedValue(['This is a clear data warning']); - storageMock.clearBrowserCaches.mockResolvedValue(['This is a clear cache warning']); - const {warnings} = await prepare.prepareTargetForIndividualNavigation( - sessionMock.asSession(), - {...constants.defaultSettings, disableStorageReset: false}, - {...constants.defaultNavigationConfig, disableStorageReset: false, requestor: url} - ); - - expect(warnings).toEqual([ - 'This is a storage warning', - 'This is a clear data warning', - 'This is a clear cache warning', - ]); - }); -}); - describe('.prepareTargetForNavigationMode()', () => { let driverMock = createMockDriver(); + let requestor = fnAny(); beforeEach(() => { driverMock = createMockDriver(); @@ -223,12 +147,17 @@ describe('.prepareTargetForNavigationMode()', () => { sessionMock.sendCommand .mockResponse('Network.enable') .mockResponse('Network.setUserAgentOverride') + .mockResponse('Network.emulateNetworkConditions') + .mockResponse('Network.setBlockedURLs') + .mockResponse('Emulation.setCPUThrottlingRate') .mockResponse('Emulation.setDeviceMetricsOverride') .mockResponse('Emulation.setTouchEmulationEnabled') .mockResponse('Debugger.enable') .mockResponse('Debugger.setSkipAllPauses') .mockResponse('Debugger.setAsyncCallStackDepth') .mockResponse('Page.enable'); + + requestor = fnAny(); }); it('emulates the target device', async () => { @@ -241,7 +170,7 @@ describe('.prepareTargetForNavigationMode()', () => { width: 200, height: 300, }, - }); + }, requestor); expect(sessionMock.sendCommand.findInvocation('Emulation.setDeviceMetricsOverride')).toEqual({ mobile: true, @@ -254,7 +183,7 @@ describe('.prepareTargetForNavigationMode()', () => { it('cache natives on new document', async () => { await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { ...constants.defaultSettings, - }); + }, requestor); expect(driverMock._executionContext.cacheNativesOnNewDocument).toHaveBeenCalled(); }); @@ -263,7 +192,7 @@ describe('.prepareTargetForNavigationMode()', () => { await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { ...constants.defaultSettings, throttlingMethod: 'simulate', - }); + }, requestor); const invocations = driverMock._executionContext.evaluateOnNewDocument.mock.calls; if (!invocations.length) expect(invocations).toHaveLength(1); @@ -277,7 +206,7 @@ describe('.prepareTargetForNavigationMode()', () => { await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { ...constants.defaultSettings, throttlingMethod: 'devtools', - }); + }, requestor); const invocations = driverMock._executionContext.evaluateOnNewDocument.mock.calls; const matchingInvocations = invocations.filter(argList => @@ -295,7 +224,7 @@ describe('.prepareTargetForNavigationMode()', () => { await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { ...constants.defaultSettings, - }); + }, requestor); await flushAllTimersAndMicrotasks(); @@ -304,6 +233,52 @@ describe('.prepareTargetForNavigationMode()', () => { promptText: 'Lighthouse prompt response', }); }); + + it('clears storage when not disabled', async () => { + await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { + ...constants.defaultSettings, + disableStorageReset: false, + }, url); + + expect(storageMock.clearDataForOrigin).toHaveBeenCalled(); + expect(storageMock.clearBrowserCaches).toHaveBeenCalled(); + }); + + it('does not clear storage when globally disabled', async () => { + await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { + ...constants.defaultSettings, + disableStorageReset: true, + }, url); + + expect(storageMock.clearDataForOrigin).not.toHaveBeenCalled(); + expect(storageMock.clearBrowserCaches).not.toHaveBeenCalled(); + }); + + it('does not clear storage when given a callback requestor', async () => { + await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { + ...constants.defaultSettings, + disableStorageReset: false, + }, requestor); + + expect(storageMock.clearDataForOrigin).not.toHaveBeenCalled(); + expect(storageMock.clearBrowserCaches).not.toHaveBeenCalled(); + }); + + it('collects storage warnings', async () => { + storageMock.getImportantStorageWarning.mockResolvedValue('This is a storage warning'); + storageMock.clearDataForOrigin.mockResolvedValue(['This is a clear data warning']); + storageMock.clearBrowserCaches.mockResolvedValue(['This is a clear cache warning']); + const {warnings} = await prepare.prepareTargetForNavigationMode(driverMock.asDriver(), { + ...constants.defaultSettings, + disableStorageReset: false, + }, url); + + expect(warnings).toEqual([ + 'This is a storage warning', + 'This is a clear data warning', + 'This is a clear cache warning', + ]); + }); }); describe('.prepareTargetForTimespanMode()', () => { diff --git a/core/test/gather/navigation-runner-test.js b/core/test/gather/navigation-runner-test.js index c61b8a7bd672..824eef7ad9d6 100644 --- a/core/test/gather/navigation-runner-test.js +++ b/core/test/gather/navigation-runner-test.js @@ -15,6 +15,7 @@ import { import {fnAny} from '../test-utils.js'; import {networkRecordsToDevtoolsLog} from '../network-records-to-devtools-log.js'; import {Runner as runnerActual} from '../../runner.js'; +import {defaultSettings} from '../../config/constants.js'; const mocks = await mockDriverSubmodules(); const mockRunner = await mockRunnerModule(); @@ -31,7 +32,6 @@ const {LighthouseError} = await import('../../lib/lh-error.js'); const DevtoolsLogGatherer = (await import('../../gather/gatherers/devtools-log.js')).default; const TraceGatherer = (await import('../../gather/gatherers/trace.js')).default; const {initializeConfig} = await import('../../config/config.js'); -const {defaultNavigationConfig} = await import('../../config/constants.js'); /** @typedef {{meta: LH.Gatherer.GathererMeta<'Accessibility'>, getArtifact: Mock, startInstrumentation: Mock, stopInstrumentation: Mock, startSensitiveInstrumentation: Mock, stopSensitiveInstrumentation: Mock}} MockGatherer */ @@ -47,8 +47,6 @@ describe('NavigationRunner', () => { let page; /** @type {LH.Config.ResolvedConfig} */ let resolvedConfig; - /** @type {LH.Config.NavigationDefn} */ - let navigation; /** @type {Map} */ let computedCache; /** @type {LH.BaseArtifacts} */ @@ -68,8 +66,8 @@ describe('NavigationRunner', () => { }; } - /** @return {{navigation: LH.Config.NavigationDefn, gatherers: {timespan: MockGatherer, snapshot: MockGatherer, navigation: MockGatherer}}} */ - function createNavigation() { + /** @return {{resolvedConfig: LH.Config.ResolvedConfig, gatherers: {timespan: MockGatherer, snapshot: MockGatherer, navigation: MockGatherer}}} */ + function createMockConfig() { const timespanGatherer = createGathererDefn(); timespanGatherer.instance.meta.supportedModes = ['timespan', 'navigation']; timespanGatherer.instance.getArtifact = fnAny().mockResolvedValue({type: 'timespan'}); @@ -80,8 +78,8 @@ describe('NavigationRunner', () => { navigationGatherer.instance.meta.supportedModes = ['navigation']; navigationGatherer.instance.getArtifact = fnAny().mockResolvedValue({type: 'navigation'}); - const navigation = { - ...defaultNavigationConfig, + const resolvedConfig = { + settings: JSON.parse(JSON.stringify(defaultSettings)), artifacts: [ {id: 'Timespan', gatherer: timespanGatherer}, {id: 'Snapshot', gatherer: snapshotGatherer}, @@ -90,7 +88,8 @@ describe('NavigationRunner', () => { }; return { - navigation, + // @ts-expect-error + resolvedConfig, gatherers: { timespan: /** @type {any} */ (timespanGatherer.instance), snapshot: /** @type {any} */ (snapshotGatherer.instance), @@ -103,7 +102,6 @@ describe('NavigationRunner', () => { requestedUrl = 'http://example.com'; requestor = requestedUrl; resolvedConfig = (await initializeConfig('navigation')).resolvedConfig; - navigation = createNavigation().navigation; computedCache = new Map(); baseArtifacts = createMockBaseArtifacts(); baseArtifacts.URL = {finalDisplayedUrl: ''}; @@ -185,159 +183,13 @@ describe('NavigationRunner', () => { }); }); - describe('_navigations', () => { - const run = () => - runner._navigations({driver, page, resolvedConfig, requestor, computedCache, baseArtifacts}); - - it('should throw if no navigations available', async () => { - resolvedConfig = {...resolvedConfig, navigations: null}; - await expect(run()).rejects.toBeTruthy(); - }); - - it('should navigate as many times as there are navigations', async () => { - // initializeConfig always produces a single config navigation. - // Artificially construct multiple navigations to test on the navigation runner. - const originalNavigation = resolvedConfig.navigations?.[0]; - if (!originalNavigation) throw new Error('Should always have navigations'); - const artifactDefns = originalNavigation.artifacts.filter(a => - ['FontSize', 'ConsoleMessages', 'ViewportDimensions', 'AnchorElements'].includes(a.id) - ); - const newNavigations = []; - for (let i = 0; i < artifactDefns.length; ++i) { - const artifactDefn = artifactDefns[i]; - newNavigations.push({ - ...originalNavigation, - id: i ? String(i) : 'default', - artifacts: [artifactDefn], - }); - } - - resolvedConfig.navigations = newNavigations; - - await run(); - const navigations = mocks.navigationMock.gotoURL.mock.calls; - const pageNavigations = navigations.filter(call => call[1] === requestedUrl); - expect(pageNavigations).toHaveLength(4); - }); - - it('should backfill requested URL using a callback requestor', async () => { - requestedUrl = 'https://backfill.example.com'; - requestor = () => {}; - resolvedConfig = (await initializeConfig( - 'navigation', - { - ...resolvedConfig, - artifacts: [ - {id: 'FontSize', gatherer: 'seo/font-size'}, - {id: 'MetaElements', gatherer: 'meta-elements'}, - ], - } - )).resolvedConfig; - mocks.navigationMock.gotoURL.mockReturnValue({ - requestedUrl, - mainDocumentUrl: requestedUrl, - warnings: [], - }); - - const {artifacts} = await run(); - expect(artifacts.URL).toBeUndefined(); - expect(baseArtifacts.URL).toEqual({ - requestedUrl, - mainDocumentUrl: requestedUrl, - finalDisplayedUrl: requestedUrl, - }); - }); - - it('should merge artifacts between navigations', async () => { - // initializeConfig always produces a single config navigation. - // Artificially construct multiple navigations to test on the navigation runner. - if (!resolvedConfig.navigations) throw new Error('Should always have navigations'); - const firstNavigation = resolvedConfig.navigations[0]; - const secondNavigation = {...firstNavigation, id: 'second'}; - const fontSizeDef = firstNavigation.artifacts.find(a => a.id === 'FontSize'); - const consoleMsgDef = firstNavigation.artifacts.find(a => a.id === 'ConsoleMessages'); - if (!fontSizeDef || !consoleMsgDef) throw new Error('Artifact definitions not found'); - secondNavigation.artifacts = [fontSizeDef]; - firstNavigation.artifacts = [consoleMsgDef]; - resolvedConfig.navigations.push(secondNavigation); - - // Both gatherers will error in these test conditions, but artifact errors - // will be merged into single `artifacts` object. - const {artifacts} = await run(); - const artifactIds = Object.keys(artifacts); - expect(artifactIds).toContain('FontSize'); - expect(artifactIds).toContain('ConsoleMessages'); - }); - - it('should retain PageLoadError and associated warnings', async () => { - resolvedConfig = (await initializeConfig( - 'navigation', - { - ...resolvedConfig, - artifacts: [ - {id: 'FontSize', gatherer: 'seo/font-size'}, - {id: 'MetaElements', gatherer: 'meta-elements'}, - ], - } - )).resolvedConfig; - - // Ensure the first real page load fails. - mocks.navigationMock.gotoURL.mockImplementation((driver, url) => { - if (url === 'about:blank') return {finalDisplayedUrl: 'about:blank', warnings: []}; - throw new LighthouseError(LighthouseError.errors.PAGE_HUNG); - }); - - const {artifacts} = await run(); - - // Validate that we stopped repeating navigations. - const urls = mocks.navigationMock.gotoURL.mock.calls.map(call => call[1]); - expect(urls).toEqual(['about:blank', 'http://example.com']); - - // Validate that the toplevel warning is added, finalURL is set, and error is kept. - const artifactIds = Object.keys(artifacts).sort(); - expect(artifactIds).toEqual(['LighthouseRunWarnings', 'PageLoadError']); - - expect(artifacts.LighthouseRunWarnings).toHaveLength(1); - - expect(baseArtifacts.URL).toEqual({ - requestedUrl, - mainDocumentUrl: requestedUrl, - finalDisplayedUrl: requestedUrl, - }); - }); - }); - describe('_navigation', () => { - /** @param {LH.Config.NavigationDefn} navigation */ - const run = navigation => runner._navigation({ - driver, - page, - resolvedConfig, - navigation, - requestor, - computedCache, - baseArtifacts, - }); - it('completes an end-to-end navigation', async () => { - const {artifacts} = await run(navigation); - const artifactIds = Object.keys(artifacts); - expect(artifactIds).toContain('Timespan'); - expect(artifactIds).toContain('Snapshot'); - - // Once for about:blank, once for the requested URL. - expect(mocks.navigationMock.gotoURL).toHaveBeenCalledTimes(2); - }); - - it('skips about:blank if config option is set to true', async () => { - resolvedConfig.settings.skipAboutBlank = true; - - const {artifacts} = await runner._navigation({ + const artifacts = await runner._navigation({ driver, page, - resolvedConfig, - navigation, - requestor: requestedUrl, + resolvedConfig: createMockConfig().resolvedConfig, + requestor, computedCache, baseArtifacts, }); @@ -345,30 +197,19 @@ describe('NavigationRunner', () => { expect(artifactIds).toContain('Timespan'); expect(artifactIds).toContain('Snapshot'); - // Only once for the requested URL. + // Once for the requested URL. about:blank is done outside this function. expect(mocks.navigationMock.gotoURL).toHaveBeenCalledTimes(1); }); - it('skips about:blank if using a callback requestor', async () => { - const {artifacts} = await runner._navigation({ + it('collects timespan, snapshot, and navigation artifacts', async () => { + const artifacts = await runner._navigation({ driver, page, - resolvedConfig, - navigation, - requestor: () => {}, + resolvedConfig: createMockConfig().resolvedConfig, + requestor, computedCache, baseArtifacts, }); - const artifactIds = Object.keys(artifacts); - expect(artifactIds).toContain('Timespan'); - expect(artifactIds).toContain('Snapshot'); - - // Only once for the requested URL. - expect(mocks.navigationMock.gotoURL).toHaveBeenCalledTimes(1); - }); - - it('collects timespan, snapshot, and navigation artifacts', async () => { - const {artifacts} = await run(navigation); expect(artifacts).toEqual({ Navigation: {type: 'navigation'}, Timespan: {type: 'timespan'}, @@ -377,11 +218,19 @@ describe('NavigationRunner', () => { }); it('supports dependencies between phases', async () => { - const {navigation, gatherers} = createNavigation(); - navigation.artifacts[1].dependencies = {Accessibility: {id: 'Timespan'}}; - navigation.artifacts[2].dependencies = {Accessibility: {id: 'Timespan'}}; + const {resolvedConfig, gatherers} = createMockConfig(); + if (!resolvedConfig.artifacts) throw new Error('No artifacts'); + resolvedConfig.artifacts[1].dependencies = {Accessibility: {id: 'Timespan'}}; + resolvedConfig.artifacts[2].dependencies = {Accessibility: {id: 'Timespan'}}; - const {artifacts} = await run(navigation); + const artifacts = await runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); expect(artifacts).toEqual({ Navigation: {type: 'navigation'}, Timespan: {type: 'timespan'}, @@ -398,15 +247,24 @@ describe('NavigationRunner', () => { }); it('passes through an error in dependencies', async () => { - const {navigation} = createNavigation(); + const {resolvedConfig} = createMockConfig(); + if (!resolvedConfig.artifacts) throw new Error('No artifacts'); + const err = new Error('Error in dependency chain'); - navigation.artifacts[0].gatherer.instance.startInstrumentation = jestMock + resolvedConfig.artifacts[0].gatherer.instance.startInstrumentation = jestMock .fn() .mockRejectedValue(err); - navigation.artifacts[1].dependencies = {Accessibility: {id: 'Timespan'}}; - navigation.artifacts[2].dependencies = {Accessibility: {id: 'Timespan'}}; + resolvedConfig.artifacts[1].dependencies = {Accessibility: {id: 'Timespan'}}; + resolvedConfig.artifacts[2].dependencies = {Accessibility: {id: 'Timespan'}}; - const {artifacts} = await run(navigation); + const artifacts = await runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); expect(artifacts).toEqual({ Navigation: expect.any(Error), @@ -416,11 +274,20 @@ describe('NavigationRunner', () => { }); it('passes through an error in startSensitiveInstrumentation', async () => { - const {navigation, gatherers} = createNavigation(); + const {resolvedConfig, gatherers} = createMockConfig(); + if (!resolvedConfig.artifacts) throw new Error('No artifacts'); + const err = new Error('Error in startSensitiveInstrumentation'); gatherers.navigation.startSensitiveInstrumentation.mockRejectedValue(err); - const {artifacts} = await run(navigation); + const artifacts = await runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); expect(artifacts).toEqual({ Navigation: err, @@ -430,11 +297,20 @@ describe('NavigationRunner', () => { }); it('passes through an error in startInstrumentation', async () => { - const {navigation, gatherers} = createNavigation(); + const {resolvedConfig, gatherers} = createMockConfig(); + if (!resolvedConfig.artifacts) throw new Error('No artifacts'); + const err = new Error('Error in startInstrumentation'); gatherers.timespan.startInstrumentation.mockRejectedValue(err); - const {artifacts} = await run(navigation); + const artifacts = await runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); expect(artifacts).toEqual({ Navigation: {type: 'navigation'}, @@ -443,8 +319,8 @@ describe('NavigationRunner', () => { }); }); - it('returns navigate errors', async () => { - const {navigation} = createNavigation(); + it('sets navigate errors on base artifacts', async () => { + const {resolvedConfig} = createMockConfig(); const noFcp = new LighthouseError(LighthouseError.errors.NO_FCP); mocks.navigationMock.gotoURL.mockImplementation( @@ -455,13 +331,20 @@ describe('NavigationRunner', () => { } ); - const {artifacts, pageLoadError} = await run(navigation); - expect(pageLoadError).toBe(noFcp); + const artifacts = await runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); + expect(baseArtifacts.PageLoadError).toBe(noFcp); expect(artifacts).toEqual({}); }); it('finds page load errors in network records when available', async () => { - const {navigation, gatherers} = createNavigation(); + const {resolvedConfig, gatherers} = createMockConfig(); mocks.navigationMock.gotoURL.mockResolvedValue({mainDocumentUrl: requestedUrl, warnings: []}); const devtoolsLog = networkRecordsToDevtoolsLog([{url: requestedUrl, failed: true}]); gatherers.timespan.meta.symbol = DevtoolsLogGatherer.symbol; @@ -469,8 +352,16 @@ describe('NavigationRunner', () => { gatherers.navigation.meta.symbol = TraceGatherer.symbol; gatherers.navigation.getArtifact = fnAny().mockResolvedValue({traceEvents: []}); - const {artifacts, pageLoadError} = await run(navigation); - expect(pageLoadError).toBeInstanceOf(LighthouseError); + const artifacts = await runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); + + expect(baseArtifacts.PageLoadError).toBeInstanceOf(LighthouseError); expect(artifacts).toEqual({ DevtoolsLogError: expect.any(Array), TraceError: {traceEvents: []}, @@ -480,61 +371,21 @@ describe('NavigationRunner', () => { }); it('cleans up throttling before getArtifact', async () => { - const {navigation, gatherers} = createNavigation(); + const {resolvedConfig, gatherers} = createMockConfig(); gatherers.navigation.getArtifact = fnAny().mockImplementation(() => { expect(mocks.emulationMock.clearThrottling).toHaveBeenCalled(); }); - await run(navigation); - expect(mocks.emulationMock.clearThrottling).toHaveBeenCalledTimes(1); - }); - }); - - describe('_setupNavigation', () => { - it('should setup the page on the blankPage', async () => { - navigation.blankPage = 'data:text/html;...'; - await runner._setupNavigation({ - driver, - page, - navigation, - requestor: requestedUrl, - resolvedConfig, - computedCache, - baseArtifacts, - }); - expect(mocks.navigationMock.gotoURL).toHaveBeenCalledWith( - expect.anything(), - 'data:text/html;...', - expect.anything() - ); - }); - - it('should prepare target for navigation', async () => { - await runner._setupNavigation({ + await runner._navigation({ driver, page, - navigation, - requestor: requestedUrl, resolvedConfig, + requestor, computedCache, baseArtifacts, }); - expect(mocks.prepareMock.prepareTargetForIndividualNavigation).toHaveBeenCalled(); - }); - it('should return the warnings from preparation', async () => { - const warnings = ['Warning A', 'Warning B']; - mocks.prepareMock.prepareTargetForIndividualNavigation.mockResolvedValue({warnings}); - const result = await runner._setupNavigation({ - driver, - page, - navigation, - requestor: requestedUrl, - resolvedConfig, - computedCache, - baseArtifacts, - }); - expect(result).toEqual({warnings}); + expect(mocks.emulationMock.clearThrottling).toHaveBeenCalledTimes(1); }); }); @@ -543,7 +394,6 @@ describe('NavigationRunner', () => { runner._navigate({ driver, page, - navigation, requestor, resolvedConfig, computedCache, @@ -564,7 +414,8 @@ describe('NavigationRunner', () => { const warnings = ['Warning A', 'Warning B']; mocks.navigationMock.gotoURL.mockResolvedValue({requestedUrl, mainDocumentUrl, warnings}); const result = await run(); - expect(result).toEqual({requestedUrl, mainDocumentUrl, warnings, navigationError: undefined}); + expect(result).toEqual({requestedUrl, mainDocumentUrl, navigationError: undefined}); + expect(baseArtifacts.LighthouseRunWarnings).toEqual(warnings); }); it('should catch navigation errors', async () => { @@ -575,8 +426,8 @@ describe('NavigationRunner', () => { requestedUrl, mainDocumentUrl: requestedUrl, navigationError, - warnings: [], }); + expect(baseArtifacts.LighthouseRunWarnings).toEqual([]); }); it('should throw regular errors', async () => { diff --git a/core/test/lib/navigation-error-test.js b/core/test/lib/navigation-error-test.js index 25302d2a5002..5d5c9be62f70 100644 --- a/core/test/lib/navigation-error-test.js +++ b/core/test/lib/navigation-error-test.js @@ -295,21 +295,6 @@ describe('#getPageLoadError', () => { expect(error).toBeUndefined(); }); - it('passes when the page is expected to fail', () => { - const mainRecord = makeNetworkRequest(); - const context = { - url: 'http://the-page.com', - networkRecords: [mainRecord], - loadFailureMode: LoadFailureMode.ignore, - warnings: [], - }; - mainRecord.url = context.url; - mainRecord.failed = true; - - const error = getPageLoadError(undefined, context); - expect(error).toBeUndefined(); - }); - it('passes when the page redirects to MIME type text/html', () => { const mainRecord = makeNetworkRequest(); const context = { diff --git a/types/config.d.ts b/types/config.d.ts index 02b104a12f84..743d07bf6f03 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -34,45 +34,11 @@ declare module Config { interface ResolvedConfig { settings: Settings; artifacts: AnyArtifactDefn[] | null; - navigations: NavigationDefn[] | null; audits: AuditDefn[] | null; categories: Record | null; groups: Record | null; } - interface SharedPassNavigationJson { - /** - * Controls the behavior when the navigation fails to complete (due to server error, no FCP, etc). - * Fatal means Lighthouse will exit immediately and return a runtimeError / non-zero exit code. - * Warn means a toplevel warning will appear in the report, but the run will complete with success. - * Ignore means a failure is expected and no action should be taken. - */ - loadFailureMode?: 'fatal'|'warn'|'ignore'; - /** The number of milliseconds to wait after FCP until the page should be considered loaded. */ - pauseAfterFcpMs?: number; - /** The number of milliseconds to wait after the load event until the page should be considered loaded. */ - pauseAfterLoadMs?: number; - /** The number of milliseconds to wait between high priority network requests or 3 simulataneous requests before the page should be considered loaded. */ - networkQuietThresholdMs?: number; - /** The number of milliseconds to wait between long tasks until the page should be considered loaded. */ - cpuQuietThresholdMs?: number; - /** Substring patterns of network resources to block during this navigation, '*' wildcards supported though unnecessary as prefix or suffix (due to substring matching). */ - blockedUrlPatterns?: string[]; - /** The URL to use for the "blank" neutral page in between navigations. Defaults to `about:blank`. */ - blankPage?: string; - } - - interface NavigationJson extends SharedPassNavigationJson { - /** The identifier for the navigation. Config extension will deduplicate navigations with the same id. */ - id: string; - /** Whether throttling settings should be skipped for the pass. */ - disableThrottling?: boolean; - /** Whether storage clearing (service workers, cache storage) should be skipped for the pass. A run-wide setting of `true` takes precedence over this value. */ - disableStorageReset?: boolean; - /** The array of artifacts to collect during the navigation. */ - artifacts?: Array; - } - interface ArtifactJson { id: string; gatherer: GathererJson; @@ -121,10 +87,6 @@ declare module Config { type Settings = ConfigSettings; - interface NavigationDefn extends Omit, 'artifacts'> { - artifacts: AnyArtifactDefn[]; - } - interface ArtifactDefn { id: string; gatherer: GathererDefn; From 8ca5c60c3b6adc7bbc5e2bf5e5d962b7c8165a0a Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 22 Aug 2023 15:16:13 -0700 Subject: [PATCH 02/10] fix throttle --- core/gather/driver/prepare.js | 3 +-- core/test/gather/driver/prepare-test.js | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/gather/driver/prepare.js b/core/gather/driver/prepare.js index 1200f74ea2c9..862cfd63266b 100644 --- a/core/gather/driver/prepare.js +++ b/core/gather/driver/prepare.js @@ -119,8 +119,7 @@ async function prepareThrottlingAndNetwork(session, settings) { const status = {msg: 'Preparing network conditions', id: `lh:gather:prepareThrottlingAndNetwork`}; log.time(status); - if (settings.throttlingMethod === 'provided') await emulation.clearThrottling(session); - else await emulation.throttle(session, settings); + await emulation.throttle(session, settings); // Set request blocking before any network activity. // No "clearing" is done at the end of the recording since Network.setBlockedURLs([]) will unset all if diff --git a/core/test/gather/driver/prepare-test.js b/core/test/gather/driver/prepare-test.js index 1ea085cac8b2..be83f257979b 100644 --- a/core/test/gather/driver/prepare-test.js +++ b/core/test/gather/driver/prepare-test.js @@ -88,9 +88,10 @@ describe('.prepareThrottlingAndNetwork()', () => { uploadThroughput: 0, offline: false, }); - expect(sessionMock.sendCommand.findInvocation('Emulation.setCPUThrottlingRate')).toEqual({ - rate: 1, - }); + + // CPU throttling is intentionally not cleared. + expect(sessionMock.sendCommand.findAllInvocations('Emulation.setCPUThrottlingRate')) + .toHaveLength(0); }); it('unsets url patterns when empty', async () => { From f7594df4e325c7ec2652b7338db4718957019240 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 22 Aug 2023 15:44:36 -0700 Subject: [PATCH 03/10] sample --- .../user-flows/artifacts/step0/artifacts.json | 174 ++- .../user-flows/artifacts/step1/artifacts.json | 104 +- .../user-flows/artifacts/step2/artifacts.json | 66 +- .../user-flows/artifacts/step3/artifacts.json | 168 ++- .../reports/sample-flow-result.json | 1190 ++++++++--------- core/test/results/artifacts/artifacts.json | 8 +- core/test/results/sample_v2.json | 14 +- 7 files changed, 931 insertions(+), 793 deletions(-) diff --git a/core/test/fixtures/user-flows/artifacts/step0/artifacts.json b/core/test/fixtures/user-flows/artifacts/step0/artifacts.json index 06dcafaf3966..7deaf88dedda 100644 --- a/core/test/fixtures/user-flows/artifacts/step0/artifacts.json +++ b/core/test/fixtures/user-flows/artifacts/step0/artifacts.json @@ -6,406 +6,464 @@ "entryType": "measure", "startTime": 0, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveArtifactsToDefns", "entryType": "measure", "startTime": 1, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveNavigationsToDefns", "entryType": "measure", "startTime": 2, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:runner:gather", "entryType": "measure", "startTime": 3, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:driver:connect", "entryType": "measure", "startTime": 4, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getBenchmarkIndex", "entryType": "measure", "startTime": 5, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getVersion", "entryType": "measure", "startTime": 6, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:prepare:navigationMode", "entryType": "measure", "startTime": 7, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:prepare:navigation", "entryType": "measure", "startTime": 8, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:storage:clearDataForOrigin", "entryType": "measure", "startTime": 9, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:storage:clearBrowserCaches", "entryType": "measure", "startTime": 10, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:prepareThrottlingAndNetwork", "entryType": "measure", "startTime": 11, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:driver:navigate", "entryType": "measure", "startTime": 12, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DevtoolsLog", "entryType": "measure", "startTime": 13, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Trace", "entryType": "measure", "startTime": 14, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:NetworkRecords", "entryType": "measure", "startTime": 15, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DevtoolsLog", "entryType": "measure", "startTime": 16, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Trace", "entryType": "measure", "startTime": 17, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Accessibility", "entryType": "measure", "startTime": 18, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:AnchorElements", "entryType": "measure", "startTime": 19, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ConsoleMessages", "entryType": "measure", "startTime": 20, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:CSSUsage", "entryType": "measure", "startTime": 21, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Doctype", "entryType": "measure", "startTime": 22, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DOMStats", "entryType": "measure", "startTime": 23, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:EmbeddedContent", "entryType": "measure", "startTime": 24, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FontSize", "entryType": "measure", "startTime": 25, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Inputs", "entryType": "measure", "startTime": 26, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:GlobalListeners", "entryType": "measure", "startTime": 27, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ImageElements", "entryType": "measure", "startTime": 28, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:InstallabilityErrors", "entryType": "measure", "startTime": 29, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getInstallabilityErrors", "entryType": "measure", "startTime": 30, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:InspectorIssues", "entryType": "measure", "startTime": 31, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:JsUsage", "entryType": "measure", "startTime": 32, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:LinkElements", "entryType": "measure", "startTime": 33, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:MainResource", "entryType": "measure", "startTime": 34, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:MainDocumentContent", "entryType": "measure", "startTime": 35, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:MetaElements", "entryType": "measure", "startTime": 36, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:NetworkUserAgent", "entryType": "measure", "startTime": 37, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:OptimizedImages", "entryType": "measure", "startTime": 38, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ResponseCompression", "entryType": "measure", "startTime": 39, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:RobotsTxt", "entryType": "measure", "startTime": 40, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ServiceWorker", "entryType": "measure", "startTime": 41, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Scripts", "entryType": "measure", "startTime": 42, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:SourceMaps", "entryType": "measure", "startTime": 43, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Stacks", "entryType": "measure", "startTime": 44, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:collectStacks", "entryType": "measure", "startTime": 45, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TagsBlockingFirstPaint", "entryType": "measure", "startTime": 46, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TapTargets", "entryType": "measure", "startTime": 47, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TraceElements", "entryType": "measure", "startTime": 48, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:ProcessedTrace", "entryType": "measure", "startTime": 49, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:ProcessedNavigation", "entryType": "measure", "startTime": 50, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:Responsiveness", "entryType": "measure", "startTime": 51, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ViewportDimensions", "entryType": "measure", "startTime": 52, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:WebAppManifest", "entryType": "measure", "startTime": 53, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:devtoolsLogs", "entryType": "measure", "startTime": 54, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:traces", "entryType": "measure", "startTime": 55, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FullPageScreenshot", "entryType": "measure", "startTime": 56, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:BFCacheFailures", "entryType": "measure", "startTime": 57, "duration": 1, - "detail": null + "detail": null, + "gather": true } ], "LighthouseRunWarnings": [], diff --git a/core/test/fixtures/user-flows/artifacts/step1/artifacts.json b/core/test/fixtures/user-flows/artifacts/step1/artifacts.json index fb8dbd865666..1b8c6ecce0f3 100644 --- a/core/test/fixtures/user-flows/artifacts/step1/artifacts.json +++ b/core/test/fixtures/user-flows/artifacts/step1/artifacts.json @@ -6,224 +6,256 @@ "entryType": "measure", "startTime": 0, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveArtifactsToDefns", "entryType": "measure", "startTime": 1, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveNavigationsToDefns", "entryType": "measure", "startTime": 2, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:driver:connect", "entryType": "measure", "startTime": 3, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getBenchmarkIndex", "entryType": "measure", "startTime": 4, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getVersion", "entryType": "measure", "startTime": 5, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:prepare:timespanMode", "entryType": "measure", "startTime": 6, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:prepareThrottlingAndNetwork", "entryType": "measure", "startTime": 7, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:runner:gather", "entryType": "measure", "startTime": 8, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DevtoolsLog", "entryType": "measure", "startTime": 9, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Trace", "entryType": "measure", "startTime": 10, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ConsoleMessages", "entryType": "measure", "startTime": 11, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:CSSUsage", "entryType": "measure", "startTime": 12, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:GlobalListeners", "entryType": "measure", "startTime": 13, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ImageElements", "entryType": "measure", "startTime": 14, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:InspectorIssues", "entryType": "measure", "startTime": 15, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:NetworkRecords", "entryType": "measure", "startTime": 16, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:JsUsage", "entryType": "measure", "startTime": 17, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:NetworkUserAgent", "entryType": "measure", "startTime": 18, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:OptimizedImages", "entryType": "measure", "startTime": 19, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ResponseCompression", "entryType": "measure", "startTime": 20, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Scripts", "entryType": "measure", "startTime": 21, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:SourceMaps", "entryType": "measure", "startTime": 22, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TraceElements", "entryType": "measure", "startTime": 23, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:ProcessedTrace", "entryType": "measure", "startTime": 24, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:ProcessedNavigation", "entryType": "measure", "startTime": 25, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:Responsiveness", "entryType": "measure", "startTime": 26, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ViewportDimensions", "entryType": "measure", "startTime": 27, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:devtoolsLogs", "entryType": "measure", "startTime": 28, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:traces", "entryType": "measure", "startTime": 29, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FullPageScreenshot", "entryType": "measure", "startTime": 30, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:BFCacheFailures", "entryType": "measure", "startTime": 31, "duration": 1, - "detail": null + "detail": null, + "gather": true } ], "LighthouseRunWarnings": [], @@ -231,10 +263,10 @@ "output": "json", "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, - "pauseAfterFcpMs": 1000, - "pauseAfterLoadMs": 1000, - "networkQuietThresholdMs": 1000, - "cpuQuietThresholdMs": 1000, + "pauseAfterFcpMs": 5250, + "pauseAfterLoadMs": 5250, + "networkQuietThresholdMs": 5250, + "cpuQuietThresholdMs": 5250, "formFactor": "mobile", "throttling": { "rttMs": 150, diff --git a/core/test/fixtures/user-flows/artifacts/step2/artifacts.json b/core/test/fixtures/user-flows/artifacts/step2/artifacts.json index 7904f69aefcc..97965a27e92b 100644 --- a/core/test/fixtures/user-flows/artifacts/step2/artifacts.json +++ b/core/test/fixtures/user-flows/artifacts/step2/artifacts.json @@ -6,154 +6,176 @@ "entryType": "measure", "startTime": 0, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveArtifactsToDefns", "entryType": "measure", "startTime": 1, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveNavigationsToDefns", "entryType": "measure", "startTime": 2, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:driver:connect", "entryType": "measure", "startTime": 3, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:runner:gather", "entryType": "measure", "startTime": 4, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getBenchmarkIndex", "entryType": "measure", "startTime": 5, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getVersion", "entryType": "measure", "startTime": 6, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Accessibility", "entryType": "measure", "startTime": 7, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:AnchorElements", "entryType": "measure", "startTime": 8, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Doctype", "entryType": "measure", "startTime": 9, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DOMStats", "entryType": "measure", "startTime": 10, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:EmbeddedContent", "entryType": "measure", "startTime": 11, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FontSize", "entryType": "measure", "startTime": 12, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Inputs", "entryType": "measure", "startTime": 13, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ImageElements", "entryType": "measure", "startTime": 14, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:MetaElements", "entryType": "measure", "startTime": 15, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:RobotsTxt", "entryType": "measure", "startTime": 16, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Stacks", "entryType": "measure", "startTime": 17, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:collectStacks", "entryType": "measure", "startTime": 18, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TapTargets", "entryType": "measure", "startTime": 19, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ViewportDimensions", "entryType": "measure", "startTime": 20, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FullPageScreenshot", "entryType": "measure", "startTime": 21, "duration": 1, - "detail": null + "detail": null, + "gather": true } ], "LighthouseRunWarnings": [], diff --git a/core/test/fixtures/user-flows/artifacts/step3/artifacts.json b/core/test/fixtures/user-flows/artifacts/step3/artifacts.json index 014f3b6c9866..94c86fe8a65f 100644 --- a/core/test/fixtures/user-flows/artifacts/step3/artifacts.json +++ b/core/test/fixtures/user-flows/artifacts/step3/artifacts.json @@ -6,392 +6,448 @@ "entryType": "measure", "startTime": 0, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveArtifactsToDefns", "entryType": "measure", "startTime": 1, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:config:resolveNavigationsToDefns", "entryType": "measure", "startTime": 2, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:runner:gather", "entryType": "measure", "startTime": 3, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:driver:connect", "entryType": "measure", "startTime": 4, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getBenchmarkIndex", "entryType": "measure", "startTime": 5, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getVersion", "entryType": "measure", "startTime": 6, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:prepare:navigationMode", "entryType": "measure", "startTime": 7, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:prepare:navigation", "entryType": "measure", "startTime": 8, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:prepareThrottlingAndNetwork", "entryType": "measure", "startTime": 9, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:driver:navigate", "entryType": "measure", "startTime": 10, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DevtoolsLog", "entryType": "measure", "startTime": 11, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Trace", "entryType": "measure", "startTime": 12, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:NetworkRecords", "entryType": "measure", "startTime": 13, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DevtoolsLog", "entryType": "measure", "startTime": 14, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Trace", "entryType": "measure", "startTime": 15, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Accessibility", "entryType": "measure", "startTime": 16, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:AnchorElements", "entryType": "measure", "startTime": 17, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ConsoleMessages", "entryType": "measure", "startTime": 18, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:CSSUsage", "entryType": "measure", "startTime": 19, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Doctype", "entryType": "measure", "startTime": 20, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:DOMStats", "entryType": "measure", "startTime": 21, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:EmbeddedContent", "entryType": "measure", "startTime": 22, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FontSize", "entryType": "measure", "startTime": 23, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Inputs", "entryType": "measure", "startTime": 24, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:GlobalListeners", "entryType": "measure", "startTime": 25, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ImageElements", "entryType": "measure", "startTime": 26, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:InstallabilityErrors", "entryType": "measure", "startTime": 27, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getInstallabilityErrors", "entryType": "measure", "startTime": 28, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:InspectorIssues", "entryType": "measure", "startTime": 29, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:JsUsage", "entryType": "measure", "startTime": 30, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:LinkElements", "entryType": "measure", "startTime": 31, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:MainResource", "entryType": "measure", "startTime": 32, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:MainDocumentContent", "entryType": "measure", "startTime": 33, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:MetaElements", "entryType": "measure", "startTime": 34, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:NetworkUserAgent", "entryType": "measure", "startTime": 35, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:OptimizedImages", "entryType": "measure", "startTime": 36, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ResponseCompression", "entryType": "measure", "startTime": 37, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:RobotsTxt", "entryType": "measure", "startTime": 38, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ServiceWorker", "entryType": "measure", "startTime": 39, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Scripts", "entryType": "measure", "startTime": 40, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:SourceMaps", "entryType": "measure", "startTime": 41, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:Stacks", "entryType": "measure", "startTime": 42, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:collectStacks", "entryType": "measure", "startTime": 43, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TagsBlockingFirstPaint", "entryType": "measure", "startTime": 44, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TapTargets", "entryType": "measure", "startTime": 45, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:TraceElements", "entryType": "measure", "startTime": 46, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:ProcessedTrace", "entryType": "measure", "startTime": 47, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:ProcessedNavigation", "entryType": "measure", "startTime": 48, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:computed:Responsiveness", "entryType": "measure", "startTime": 49, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:ViewportDimensions", "entryType": "measure", "startTime": 50, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:WebAppManifest", "entryType": "measure", "startTime": 51, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:devtoolsLogs", "entryType": "measure", "startTime": 52, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:traces", "entryType": "measure", "startTime": 53, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:FullPageScreenshot", "entryType": "measure", "startTime": 54, "duration": 1, - "detail": null + "detail": null, + "gather": true }, { "name": "lh:gather:getArtifact:BFCacheFailures", "entryType": "measure", "startTime": 55, "duration": 1, - "detail": null + "detail": null, + "gather": true } ], "LighthouseRunWarnings": [], diff --git a/core/test/fixtures/user-flows/reports/sample-flow-result.json b/core/test/fixtures/user-flows/reports/sample-flow-result.json index ea772b00c567..d5363b6bed9c 100644 --- a/core/test/fixtures/user-flows/reports/sample-flow-result.json +++ b/core/test/fixtures/user-flows/reports/sample-flow-result.json @@ -5461,571 +5461,571 @@ }, { "startTime": 60, - "name": "lh:config:resolveNavigationsToDefns", + "name": "lh:runner:audit", "duration": 1, "entryType": "measure" }, { "startTime": 61, - "name": "lh:runner:audit", + "name": "lh:runner:auditing", "duration": 1, "entryType": "measure" }, { "startTime": 62, - "name": "lh:runner:auditing", + "name": "lh:audit:is-on-https", "duration": 1, "entryType": "measure" }, { "startTime": 63, - "name": "lh:audit:is-on-https", + "name": "lh:computed:NetworkRecords", "duration": 1, "entryType": "measure" }, { "startTime": 64, - "name": "lh:computed:NetworkRecords", + "name": "lh:audit:viewport", "duration": 1, "entryType": "measure" }, { "startTime": 65, - "name": "lh:audit:viewport", + "name": "lh:computed:ViewportMeta", "duration": 1, "entryType": "measure" }, { "startTime": 66, - "name": "lh:computed:ViewportMeta", + "name": "lh:audit:first-contentful-paint", "duration": 1, "entryType": "measure" }, { "startTime": 67, - "name": "lh:audit:first-contentful-paint", + "name": "lh:computed:FirstContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 68, - "name": "lh:computed:FirstContentfulPaint", + "name": "lh:computed:ProcessedTrace", "duration": 1, "entryType": "measure" }, { "startTime": 69, - "name": "lh:computed:ProcessedTrace", + "name": "lh:computed:ProcessedNavigation", "duration": 1, "entryType": "measure" }, { "startTime": 70, - "name": "lh:computed:ProcessedNavigation", + "name": "lh:computed:LanternFirstContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 71, - "name": "lh:computed:LanternFirstContentfulPaint", + "name": "lh:computed:PageDependencyGraph", "duration": 1, "entryType": "measure" }, { "startTime": 72, - "name": "lh:computed:PageDependencyGraph", + "name": "lh:computed:LoadSimulator", "duration": 1, "entryType": "measure" }, { "startTime": 73, - "name": "lh:computed:LoadSimulator", + "name": "lh:computed:NetworkAnalysis", "duration": 1, "entryType": "measure" }, { "startTime": 74, - "name": "lh:computed:NetworkAnalysis", + "name": "lh:audit:largest-contentful-paint", "duration": 1, "entryType": "measure" }, { "startTime": 75, - "name": "lh:audit:largest-contentful-paint", + "name": "lh:computed:LargestContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 76, - "name": "lh:computed:LargestContentfulPaint", + "name": "lh:computed:LanternLargestContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 77, - "name": "lh:computed:LanternLargestContentfulPaint", + "name": "lh:audit:first-meaningful-paint", "duration": 1, "entryType": "measure" }, { "startTime": 78, - "name": "lh:audit:first-meaningful-paint", + "name": "lh:computed:FirstMeaningfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 79, - "name": "lh:computed:FirstMeaningfulPaint", + "name": "lh:computed:LanternFirstMeaningfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 80, - "name": "lh:computed:LanternFirstMeaningfulPaint", + "name": "lh:audit:speed-index", "duration": 1, "entryType": "measure" }, { "startTime": 81, - "name": "lh:audit:speed-index", + "name": "lh:computed:SpeedIndex", "duration": 1, "entryType": "measure" }, { "startTime": 82, - "name": "lh:computed:SpeedIndex", + "name": "lh:computed:LanternSpeedIndex", "duration": 1, "entryType": "measure" }, { "startTime": 83, - "name": "lh:computed:LanternSpeedIndex", + "name": "lh:computed:Speedline", "duration": 1, "entryType": "measure" }, { "startTime": 84, - "name": "lh:computed:Speedline", + "name": "lh:audit:screenshot-thumbnails", "duration": 1, "entryType": "measure" }, { "startTime": 85, - "name": "lh:audit:screenshot-thumbnails", + "name": "lh:audit:final-screenshot", "duration": 1, "entryType": "measure" }, { "startTime": 86, - "name": "lh:audit:final-screenshot", + "name": "lh:computed:Screenshots", "duration": 1, "entryType": "measure" }, { "startTime": 87, - "name": "lh:computed:Screenshots", + "name": "lh:audit:total-blocking-time", "duration": 1, "entryType": "measure" }, { "startTime": 88, - "name": "lh:audit:total-blocking-time", + "name": "lh:computed:TotalBlockingTime", "duration": 1, "entryType": "measure" }, { "startTime": 89, - "name": "lh:computed:TotalBlockingTime", + "name": "lh:computed:LanternTotalBlockingTime", "duration": 1, "entryType": "measure" }, { "startTime": 90, - "name": "lh:computed:LanternTotalBlockingTime", + "name": "lh:computed:LanternInteractive", "duration": 1, "entryType": "measure" }, { "startTime": 91, - "name": "lh:computed:LanternInteractive", + "name": "lh:audit:max-potential-fid", "duration": 1, "entryType": "measure" }, { "startTime": 92, - "name": "lh:audit:max-potential-fid", + "name": "lh:computed:MaxPotentialFID", "duration": 1, "entryType": "measure" }, { "startTime": 93, - "name": "lh:computed:MaxPotentialFID", + "name": "lh:computed:LanternMaxPotentialFID", "duration": 1, "entryType": "measure" }, { "startTime": 94, - "name": "lh:computed:LanternMaxPotentialFID", + "name": "lh:audit:cumulative-layout-shift", "duration": 1, "entryType": "measure" }, { "startTime": 95, - "name": "lh:audit:cumulative-layout-shift", + "name": "lh:computed:CumulativeLayoutShift", "duration": 1, "entryType": "measure" }, { "startTime": 96, - "name": "lh:computed:CumulativeLayoutShift", + "name": "lh:audit:errors-in-console", "duration": 1, "entryType": "measure" }, { "startTime": 97, - "name": "lh:audit:errors-in-console", + "name": "lh:computed:JSBundles", "duration": 1, "entryType": "measure" }, { "startTime": 98, - "name": "lh:computed:JSBundles", + "name": "lh:audit:server-response-time", "duration": 1, "entryType": "measure" }, { "startTime": 99, - "name": "lh:audit:server-response-time", + "name": "lh:computed:MainResource", "duration": 1, "entryType": "measure" }, { "startTime": 100, - "name": "lh:computed:MainResource", + "name": "lh:audit:interactive", "duration": 1, "entryType": "measure" }, { "startTime": 101, - "name": "lh:audit:interactive", + "name": "lh:computed:Interactive", "duration": 1, "entryType": "measure" }, { "startTime": 102, - "name": "lh:computed:Interactive", + "name": "lh:audit:user-timings", "duration": 1, "entryType": "measure" }, { "startTime": 103, - "name": "lh:audit:user-timings", + "name": "lh:computed:UserTimings", "duration": 1, "entryType": "measure" }, { "startTime": 104, - "name": "lh:computed:UserTimings", + "name": "lh:audit:critical-request-chains", "duration": 1, "entryType": "measure" }, { "startTime": 105, - "name": "lh:audit:critical-request-chains", + "name": "lh:computed:CriticalRequestChains", "duration": 1, "entryType": "measure" }, { "startTime": 106, - "name": "lh:computed:CriticalRequestChains", + "name": "lh:audit:redirects", "duration": 1, "entryType": "measure" }, { "startTime": 107, - "name": "lh:audit:redirects", + "name": "lh:audit:installable-manifest", "duration": 1, "entryType": "measure" }, { "startTime": 108, - "name": "lh:audit:installable-manifest", + "name": "lh:audit:splash-screen", "duration": 1, "entryType": "measure" }, { "startTime": 109, - "name": "lh:audit:splash-screen", + "name": "lh:computed:ManifestValues", "duration": 1, "entryType": "measure" }, { "startTime": 110, - "name": "lh:computed:ManifestValues", + "name": "lh:audit:themed-omnibox", "duration": 1, "entryType": "measure" }, { "startTime": 111, - "name": "lh:audit:themed-omnibox", + "name": "lh:audit:maskable-icon", "duration": 1, "entryType": "measure" }, { "startTime": 112, - "name": "lh:audit:maskable-icon", + "name": "lh:audit:content-width", "duration": 1, "entryType": "measure" }, { "startTime": 113, - "name": "lh:audit:content-width", + "name": "lh:audit:image-aspect-ratio", "duration": 1, "entryType": "measure" }, { "startTime": 114, - "name": "lh:audit:image-aspect-ratio", + "name": "lh:audit:image-size-responsive", "duration": 1, "entryType": "measure" }, { "startTime": 115, - "name": "lh:audit:image-size-responsive", + "name": "lh:audit:preload-fonts", "duration": 1, "entryType": "measure" }, { "startTime": 116, - "name": "lh:audit:preload-fonts", + "name": "lh:audit:deprecations", "duration": 1, "entryType": "measure" }, { "startTime": 117, - "name": "lh:audit:deprecations", + "name": "lh:audit:mainthread-work-breakdown", "duration": 1, "entryType": "measure" }, { "startTime": 118, - "name": "lh:audit:mainthread-work-breakdown", + "name": "lh:computed:MainThreadTasks", "duration": 1, "entryType": "measure" }, { "startTime": 119, - "name": "lh:computed:MainThreadTasks", + "name": "lh:audit:bootup-time", "duration": 1, "entryType": "measure" }, { "startTime": 120, - "name": "lh:audit:bootup-time", + "name": "lh:audit:uses-rel-preload", "duration": 1, "entryType": "measure" }, { "startTime": 121, - "name": "lh:audit:uses-rel-preload", + "name": "lh:audit:uses-rel-preconnect", "duration": 1, "entryType": "measure" }, { "startTime": 122, - "name": "lh:audit:uses-rel-preconnect", + "name": "lh:audit:font-display", "duration": 1, "entryType": "measure" }, { "startTime": 123, - "name": "lh:audit:font-display", + "name": "lh:audit:diagnostics", "duration": 1, "entryType": "measure" }, { "startTime": 124, - "name": "lh:audit:diagnostics", + "name": "lh:audit:network-requests", "duration": 1, "entryType": "measure" }, { "startTime": 125, - "name": "lh:audit:network-requests", + "name": "lh:computed:EntityClassification", "duration": 1, "entryType": "measure" }, { "startTime": 126, - "name": "lh:computed:EntityClassification", + "name": "lh:audit:network-rtt", "duration": 1, "entryType": "measure" }, { "startTime": 127, - "name": "lh:audit:network-rtt", + "name": "lh:audit:network-server-latency", "duration": 1, "entryType": "measure" }, { "startTime": 128, - "name": "lh:audit:network-server-latency", + "name": "lh:audit:main-thread-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 129, - "name": "lh:audit:main-thread-tasks", + "name": "lh:audit:metrics", "duration": 1, "entryType": "measure" }, { "startTime": 130, - "name": "lh:audit:metrics", + "name": "lh:computed:TimingSummary", "duration": 1, "entryType": "measure" }, { "startTime": 131, - "name": "lh:computed:TimingSummary", + "name": "lh:computed:FirstContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 132, - "name": "lh:computed:FirstContentfulPaintAllFrames", + "name": "lh:computed:LargestContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 133, - "name": "lh:computed:LargestContentfulPaintAllFrames", + "name": "lh:computed:LCPBreakdown", "duration": 1, "entryType": "measure" }, { "startTime": 134, - "name": "lh:computed:LCPBreakdown", + "name": "lh:computed:TimeToFirstByte", "duration": 1, "entryType": "measure" }, { "startTime": 135, - "name": "lh:computed:TimeToFirstByte", + "name": "lh:computed:LCPImageRecord", "duration": 1, "entryType": "measure" }, { "startTime": 136, - "name": "lh:computed:LCPImageRecord", + "name": "lh:audit:performance-budget", "duration": 1, "entryType": "measure" }, { "startTime": 137, - "name": "lh:audit:performance-budget", + "name": "lh:computed:ResourceSummary", "duration": 1, "entryType": "measure" }, { "startTime": 138, - "name": "lh:computed:ResourceSummary", + "name": "lh:audit:timing-budget", "duration": 1, "entryType": "measure" }, { "startTime": 139, - "name": "lh:audit:timing-budget", + "name": "lh:audit:third-party-summary", "duration": 1, "entryType": "measure" }, { "startTime": 140, - "name": "lh:audit:third-party-summary", + "name": "lh:computed:TBTImpactTasks", "duration": 1, "entryType": "measure" }, { "startTime": 141, - "name": "lh:computed:TBTImpactTasks", + "name": "lh:audit:third-party-facades", "duration": 1, "entryType": "measure" }, { "startTime": 142, - "name": "lh:audit:third-party-facades", + "name": "lh:audit:largest-contentful-paint-element", "duration": 1, "entryType": "measure" }, { "startTime": 143, - "name": "lh:audit:largest-contentful-paint-element", + "name": "lh:audit:lcp-lazy-loaded", "duration": 1, "entryType": "measure" }, { "startTime": 144, - "name": "lh:audit:lcp-lazy-loaded", + "name": "lh:audit:layout-shift-elements", "duration": 1, "entryType": "measure" }, { "startTime": 145, - "name": "lh:audit:layout-shift-elements", + "name": "lh:audit:long-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 146, - "name": "lh:audit:long-tasks", + "name": "lh:audit:no-unload-listeners", "duration": 1, "entryType": "measure" }, { "startTime": 147, - "name": "lh:audit:no-unload-listeners", + "name": "lh:audit:non-composited-animations", "duration": 1, "entryType": "measure" }, { "startTime": 148, - "name": "lh:audit:non-composited-animations", + "name": "lh:audit:unsized-images", "duration": 1, "entryType": "measure" }, { "startTime": 149, - "name": "lh:audit:unsized-images", + "name": "lh:audit:valid-source-maps", "duration": 1, "entryType": "measure" }, { "startTime": 150, - "name": "lh:audit:valid-source-maps", + "name": "lh:audit:prioritize-lcp-image", "duration": 1, "entryType": "measure" }, { "startTime": 151, - "name": "lh:audit:prioritize-lcp-image", + "name": "lh:audit:csp-xss", "duration": 1, "entryType": "measure" }, { "startTime": 152, - "name": "lh:audit:csp-xss", + "name": "lh:audit:script-treemap-data", "duration": 1, "entryType": "measure" }, { "startTime": 153, - "name": "lh:audit:script-treemap-data", + "name": "lh:computed:ModuleDuplication", "duration": 1, "entryType": "measure" }, { "startTime": 154, - "name": "lh:computed:ModuleDuplication", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, @@ -6109,708 +6109,702 @@ }, { "startTime": 168, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:pwa-cross-browser", "duration": 1, "entryType": "measure" }, { "startTime": 169, - "name": "lh:audit:pwa-cross-browser", + "name": "lh:audit:pwa-page-transitions", "duration": 1, "entryType": "measure" }, { "startTime": 170, - "name": "lh:audit:pwa-page-transitions", + "name": "lh:audit:pwa-each-page-has-url", "duration": 1, "entryType": "measure" }, { "startTime": 171, - "name": "lh:audit:pwa-each-page-has-url", + "name": "lh:audit:accesskeys", "duration": 1, "entryType": "measure" }, { "startTime": 172, - "name": "lh:audit:accesskeys", + "name": "lh:audit:aria-allowed-attr", "duration": 1, "entryType": "measure" }, { "startTime": 173, - "name": "lh:audit:aria-allowed-attr", + "name": "lh:audit:aria-allowed-role", "duration": 1, "entryType": "measure" }, { "startTime": 174, - "name": "lh:audit:aria-allowed-role", + "name": "lh:audit:aria-command-name", "duration": 1, "entryType": "measure" }, { "startTime": 175, - "name": "lh:audit:aria-command-name", + "name": "lh:audit:aria-dialog-name", "duration": 1, "entryType": "measure" }, { "startTime": 176, - "name": "lh:audit:aria-dialog-name", + "name": "lh:audit:aria-hidden-body", "duration": 1, "entryType": "measure" }, { "startTime": 177, - "name": "lh:audit:aria-hidden-body", + "name": "lh:audit:aria-hidden-focus", "duration": 1, "entryType": "measure" }, { "startTime": 178, - "name": "lh:audit:aria-hidden-focus", + "name": "lh:audit:aria-input-field-name", "duration": 1, "entryType": "measure" }, { "startTime": 179, - "name": "lh:audit:aria-input-field-name", + "name": "lh:audit:aria-meter-name", "duration": 1, "entryType": "measure" }, { "startTime": 180, - "name": "lh:audit:aria-meter-name", + "name": "lh:audit:aria-progressbar-name", "duration": 1, "entryType": "measure" }, { "startTime": 181, - "name": "lh:audit:aria-progressbar-name", + "name": "lh:audit:aria-required-attr", "duration": 1, "entryType": "measure" }, { "startTime": 182, - "name": "lh:audit:aria-required-attr", + "name": "lh:audit:aria-required-children", "duration": 1, "entryType": "measure" }, { "startTime": 183, - "name": "lh:audit:aria-required-children", + "name": "lh:audit:aria-required-parent", "duration": 1, "entryType": "measure" }, { "startTime": 184, - "name": "lh:audit:aria-required-parent", + "name": "lh:audit:aria-roles", "duration": 1, "entryType": "measure" }, { "startTime": 185, - "name": "lh:audit:aria-roles", + "name": "lh:audit:aria-text", "duration": 1, "entryType": "measure" }, { "startTime": 186, - "name": "lh:audit:aria-text", + "name": "lh:audit:aria-toggle-field-name", "duration": 1, "entryType": "measure" }, { "startTime": 187, - "name": "lh:audit:aria-toggle-field-name", + "name": "lh:audit:aria-tooltip-name", "duration": 1, "entryType": "measure" }, { "startTime": 188, - "name": "lh:audit:aria-tooltip-name", + "name": "lh:audit:aria-treeitem-name", "duration": 1, "entryType": "measure" }, { "startTime": 189, - "name": "lh:audit:aria-treeitem-name", + "name": "lh:audit:aria-valid-attr-value", "duration": 1, "entryType": "measure" }, { "startTime": 190, - "name": "lh:audit:aria-valid-attr-value", + "name": "lh:audit:aria-valid-attr", "duration": 1, "entryType": "measure" }, { "startTime": 191, - "name": "lh:audit:aria-valid-attr", + "name": "lh:audit:button-name", "duration": 1, "entryType": "measure" }, { "startTime": 192, - "name": "lh:audit:button-name", + "name": "lh:audit:bypass", "duration": 1, "entryType": "measure" }, { "startTime": 193, - "name": "lh:audit:bypass", + "name": "lh:audit:color-contrast", "duration": 1, "entryType": "measure" }, { "startTime": 194, - "name": "lh:audit:color-contrast", + "name": "lh:audit:definition-list", "duration": 1, "entryType": "measure" }, { "startTime": 195, - "name": "lh:audit:definition-list", + "name": "lh:audit:dlitem", "duration": 1, "entryType": "measure" }, { "startTime": 196, - "name": "lh:audit:dlitem", + "name": "lh:audit:document-title", "duration": 1, "entryType": "measure" }, { "startTime": 197, - "name": "lh:audit:document-title", + "name": "lh:audit:duplicate-id-active", "duration": 1, "entryType": "measure" }, { "startTime": 198, - "name": "lh:audit:duplicate-id-active", + "name": "lh:audit:duplicate-id-aria", "duration": 1, "entryType": "measure" }, { "startTime": 199, - "name": "lh:audit:duplicate-id-aria", + "name": "lh:audit:empty-heading", "duration": 1, "entryType": "measure" }, { "startTime": 200, - "name": "lh:audit:empty-heading", + "name": "lh:audit:form-field-multiple-labels", "duration": 1, "entryType": "measure" }, { "startTime": 201, - "name": "lh:audit:form-field-multiple-labels", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 202, "name": "lh:audit:frame-title", "duration": 1, "entryType": "measure" }, { - "startTime": 203, + "startTime": 202, "name": "lh:audit:heading-order", "duration": 1, "entryType": "measure" }, { - "startTime": 204, + "startTime": 203, "name": "lh:audit:html-has-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 205, + "startTime": 204, "name": "lh:audit:html-lang-valid", "duration": 1, "entryType": "measure" }, { - "startTime": 206, + "startTime": 205, "name": "lh:audit:html-xml-lang-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 207, + "startTime": 206, "name": "lh:audit:identical-links-same-purpose", "duration": 1, "entryType": "measure" }, { - "startTime": 208, + "startTime": 207, "name": "lh:audit:image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 209, + "startTime": 208, "name": "lh:audit:image-redundant-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 210, + "startTime": 209, "name": "lh:audit:input-button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 211, + "startTime": 210, "name": "lh:audit:input-image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 212, + "startTime": 211, "name": "lh:audit:label-content-name-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 213, + "startTime": 212, "name": "lh:audit:label", "duration": 1, "entryType": "measure" }, { - "startTime": 214, + "startTime": 213, "name": "lh:audit:landmark-one-main", "duration": 1, "entryType": "measure" }, { - "startTime": 215, + "startTime": 214, "name": "lh:audit:link-name", "duration": 1, "entryType": "measure" }, { - "startTime": 216, + "startTime": 215, "name": "lh:audit:link-in-text-block", "duration": 1, "entryType": "measure" }, { - "startTime": 217, + "startTime": 216, "name": "lh:audit:list", "duration": 1, "entryType": "measure" }, { - "startTime": 218, + "startTime": 217, "name": "lh:audit:listitem", "duration": 1, "entryType": "measure" }, { - "startTime": 219, + "startTime": 218, "name": "lh:audit:meta-refresh", "duration": 1, "entryType": "measure" }, { - "startTime": 220, + "startTime": 219, "name": "lh:audit:meta-viewport", "duration": 1, "entryType": "measure" }, { - "startTime": 221, + "startTime": 220, "name": "lh:audit:object-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 222, + "startTime": 221, "name": "lh:audit:select-name", "duration": 1, "entryType": "measure" }, { - "startTime": 223, + "startTime": 222, "name": "lh:audit:skip-link", "duration": 1, "entryType": "measure" }, { - "startTime": 224, + "startTime": 223, "name": "lh:audit:tabindex", "duration": 1, "entryType": "measure" }, { - "startTime": 225, + "startTime": 224, "name": "lh:audit:table-duplicate-name", "duration": 1, "entryType": "measure" }, { - "startTime": 226, + "startTime": 225, "name": "lh:audit:table-fake-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 227, + "startTime": 226, "name": "lh:audit:target-size", "duration": 1, "entryType": "measure" }, { - "startTime": 228, + "startTime": 227, "name": "lh:audit:td-has-header", "duration": 1, "entryType": "measure" }, { - "startTime": 229, + "startTime": 228, "name": "lh:audit:td-headers-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 230, + "startTime": 229, "name": "lh:audit:th-has-data-cells", "duration": 1, "entryType": "measure" }, { - "startTime": 231, + "startTime": 230, "name": "lh:audit:valid-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 232, + "startTime": 231, "name": "lh:audit:video-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 233, + "startTime": 232, "name": "lh:audit:custom-controls-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 234, + "startTime": 233, "name": "lh:audit:custom-controls-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 235, + "startTime": 234, "name": "lh:audit:focus-traps", "duration": 1, "entryType": "measure" }, { - "startTime": 236, + "startTime": 235, "name": "lh:audit:focusable-controls", "duration": 1, "entryType": "measure" }, { - "startTime": 237, + "startTime": 236, "name": "lh:audit:interactive-element-affordance", "duration": 1, "entryType": "measure" }, { - "startTime": 238, + "startTime": 237, "name": "lh:audit:logical-tab-order", "duration": 1, "entryType": "measure" }, { - "startTime": 239, + "startTime": 238, "name": "lh:audit:managed-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 240, + "startTime": 239, "name": "lh:audit:offscreen-content-hidden", "duration": 1, "entryType": "measure" }, { - "startTime": 241, + "startTime": 240, "name": "lh:audit:use-landmarks", "duration": 1, "entryType": "measure" }, { - "startTime": 242, + "startTime": 241, "name": "lh:audit:visual-order-follows-dom", "duration": 1, "entryType": "measure" }, { - "startTime": 243, + "startTime": 242, "name": "lh:audit:uses-long-cache-ttl", "duration": 1, "entryType": "measure" }, { - "startTime": 244, + "startTime": 243, "name": "lh:audit:total-byte-weight", "duration": 1, "entryType": "measure" }, { - "startTime": 245, + "startTime": 244, "name": "lh:audit:offscreen-images", "duration": 1, "entryType": "measure" }, { - "startTime": 246, + "startTime": 245, "name": "lh:audit:render-blocking-resources", "duration": 1, "entryType": "measure" }, { - "startTime": 247, + "startTime": 246, "name": "lh:computed:UnusedCSS", "duration": 1, "entryType": "measure" }, { - "startTime": 248, + "startTime": 247, "name": "lh:computed:FirstContentfulPaint", "duration": 1, "entryType": "measure" }, { - "startTime": 249, + "startTime": 248, "name": "lh:audit:unminified-css", "duration": 1, "entryType": "measure" }, { - "startTime": 250, + "startTime": 249, "name": "lh:audit:unminified-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 251, + "startTime": 250, "name": "lh:audit:unused-css-rules", "duration": 1, "entryType": "measure" }, { - "startTime": 252, + "startTime": 251, "name": "lh:audit:unused-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 253, + "startTime": 252, "name": "lh:audit:modern-image-formats", "duration": 1, "entryType": "measure" }, { - "startTime": 254, + "startTime": 253, "name": "lh:audit:uses-optimized-images", "duration": 1, "entryType": "measure" }, { - "startTime": 255, + "startTime": 254, "name": "lh:audit:uses-text-compression", "duration": 1, "entryType": "measure" }, { - "startTime": 256, + "startTime": 255, "name": "lh:audit:uses-responsive-images", "duration": 1, "entryType": "measure" }, { - "startTime": 257, + "startTime": 256, "name": "lh:computed:ImageRecords", "duration": 1, "entryType": "measure" }, { - "startTime": 258, + "startTime": 257, "name": "lh:audit:efficient-animated-content", "duration": 1, "entryType": "measure" }, { - "startTime": 259, + "startTime": 258, "name": "lh:audit:duplicated-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 260, + "startTime": 259, "name": "lh:audit:legacy-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 261, + "startTime": 260, "name": "lh:audit:doctype", "duration": 1, "entryType": "measure" }, { - "startTime": 262, + "startTime": 261, "name": "lh:audit:charset", "duration": 1, "entryType": "measure" }, { - "startTime": 263, + "startTime": 262, "name": "lh:audit:dom-size", "duration": 1, "entryType": "measure" }, { - "startTime": 264, + "startTime": 263, "name": "lh:audit:geolocation-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 265, + "startTime": 264, "name": "lh:audit:inspector-issues", "duration": 1, "entryType": "measure" }, { - "startTime": 266, + "startTime": 265, "name": "lh:audit:no-document-write", "duration": 1, "entryType": "measure" }, { - "startTime": 267, + "startTime": 266, "name": "lh:audit:js-libraries", "duration": 1, "entryType": "measure" }, { - "startTime": 268, + "startTime": 267, "name": "lh:audit:notification-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 269, + "startTime": 268, "name": "lh:audit:paste-preventing-inputs", "duration": 1, "entryType": "measure" }, { - "startTime": 270, + "startTime": 269, "name": "lh:audit:uses-passive-event-listeners", "duration": 1, "entryType": "measure" }, { - "startTime": 271, + "startTime": 270, "name": "lh:audit:meta-description", "duration": 1, "entryType": "measure" }, { - "startTime": 272, + "startTime": 271, "name": "lh:audit:http-status-code", "duration": 1, "entryType": "measure" }, { - "startTime": 273, + "startTime": 272, "name": "lh:audit:font-size", "duration": 1, "entryType": "measure" }, { - "startTime": 274, + "startTime": 273, "name": "lh:audit:link-text", "duration": 1, "entryType": "measure" }, { - "startTime": 275, + "startTime": 274, "name": "lh:audit:crawlable-anchors", "duration": 1, "entryType": "measure" }, { - "startTime": 276, + "startTime": 275, "name": "lh:audit:is-crawlable", "duration": 1, "entryType": "measure" }, { - "startTime": 277, + "startTime": 276, "name": "lh:audit:robots-txt", "duration": 1, "entryType": "measure" }, { - "startTime": 278, + "startTime": 277, "name": "lh:audit:tap-targets", "duration": 1, "entryType": "measure" }, { - "startTime": 279, + "startTime": 278, "name": "lh:audit:hreflang", "duration": 1, "entryType": "measure" }, { - "startTime": 280, + "startTime": 279, "name": "lh:audit:plugins", "duration": 1, "entryType": "measure" }, { - "startTime": 281, + "startTime": 280, "name": "lh:audit:canonical", "duration": 1, "entryType": "measure" }, { - "startTime": 282, + "startTime": 281, "name": "lh:audit:structured-data", "duration": 1, "entryType": "measure" }, { - "startTime": 283, + "startTime": 282, "name": "lh:audit:bf-cache", "duration": 1, "entryType": "measure" }, { - "startTime": 284, + "startTime": 283, "name": "lh:runner:generate", "duration": 1, "entryType": "measure" } ], - "total": 285 + "total": 284 }, "i18n": { "rendererFormattedStrings": { @@ -10390,10 +10384,10 @@ "output": "json", "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, - "pauseAfterFcpMs": 1000, - "pauseAfterLoadMs": 1000, - "networkQuietThresholdMs": 1000, - "cpuQuietThresholdMs": 1000, + "pauseAfterFcpMs": 5250, + "pauseAfterLoadMs": 5250, + "networkQuietThresholdMs": 5250, + "cpuQuietThresholdMs": 5250, "formFactor": "mobile", "throttling": { "rttMs": 150, @@ -11139,259 +11133,259 @@ }, { "startTime": 34, - "name": "lh:config:resolveNavigationsToDefns", + "name": "lh:runner:audit", "duration": 1, "entryType": "measure" }, { "startTime": 35, - "name": "lh:runner:audit", + "name": "lh:runner:auditing", "duration": 1, "entryType": "measure" }, { "startTime": 36, - "name": "lh:runner:auditing", + "name": "lh:audit:is-on-https", "duration": 1, "entryType": "measure" }, { "startTime": 37, - "name": "lh:audit:is-on-https", + "name": "lh:computed:NetworkRecords", "duration": 1, "entryType": "measure" }, { "startTime": 38, - "name": "lh:computed:NetworkRecords", + "name": "lh:audit:screenshot-thumbnails", "duration": 1, "entryType": "measure" }, { "startTime": 39, - "name": "lh:audit:screenshot-thumbnails", + "name": "lh:computed:Speedline", "duration": 1, "entryType": "measure" }, { "startTime": 40, - "name": "lh:computed:Speedline", + "name": "lh:computed:ProcessedTrace", "duration": 1, "entryType": "measure" }, { "startTime": 41, - "name": "lh:computed:ProcessedTrace", + "name": "lh:audit:final-screenshot", "duration": 1, "entryType": "measure" }, { "startTime": 42, - "name": "lh:audit:final-screenshot", + "name": "lh:computed:Screenshots", "duration": 1, "entryType": "measure" }, { "startTime": 43, - "name": "lh:computed:Screenshots", + "name": "lh:audit:total-blocking-time", "duration": 1, "entryType": "measure" }, { "startTime": 44, - "name": "lh:audit:total-blocking-time", + "name": "lh:computed:TotalBlockingTime", "duration": 1, "entryType": "measure" }, { "startTime": 45, - "name": "lh:computed:TotalBlockingTime", + "name": "lh:audit:cumulative-layout-shift", "duration": 1, "entryType": "measure" }, { "startTime": 46, - "name": "lh:audit:cumulative-layout-shift", + "name": "lh:computed:CumulativeLayoutShift", "duration": 1, "entryType": "measure" }, { "startTime": 47, - "name": "lh:computed:CumulativeLayoutShift", + "name": "lh:audit:interaction-to-next-paint", "duration": 1, "entryType": "measure" }, { "startTime": 48, - "name": "lh:audit:interaction-to-next-paint", + "name": "lh:computed:Responsiveness", "duration": 1, "entryType": "measure" }, { "startTime": 49, - "name": "lh:computed:Responsiveness", + "name": "lh:audit:errors-in-console", "duration": 1, "entryType": "measure" }, { "startTime": 50, - "name": "lh:audit:errors-in-console", + "name": "lh:computed:JSBundles", "duration": 1, "entryType": "measure" }, { "startTime": 51, - "name": "lh:computed:JSBundles", + "name": "lh:audit:user-timings", "duration": 1, "entryType": "measure" }, { "startTime": 52, - "name": "lh:audit:user-timings", + "name": "lh:computed:UserTimings", "duration": 1, "entryType": "measure" }, { "startTime": 53, - "name": "lh:computed:UserTimings", + "name": "lh:audit:image-aspect-ratio", "duration": 1, "entryType": "measure" }, { "startTime": 54, - "name": "lh:audit:image-aspect-ratio", + "name": "lh:audit:image-size-responsive", "duration": 1, "entryType": "measure" }, { "startTime": 55, - "name": "lh:audit:image-size-responsive", + "name": "lh:audit:preload-fonts", "duration": 1, "entryType": "measure" }, { "startTime": 56, - "name": "lh:audit:preload-fonts", + "name": "lh:audit:deprecations", "duration": 1, "entryType": "measure" }, { "startTime": 57, - "name": "lh:audit:deprecations", + "name": "lh:audit:mainthread-work-breakdown", "duration": 1, "entryType": "measure" }, { "startTime": 58, - "name": "lh:audit:mainthread-work-breakdown", + "name": "lh:computed:MainThreadTasks", "duration": 1, "entryType": "measure" }, { "startTime": 59, - "name": "lh:computed:MainThreadTasks", + "name": "lh:audit:bootup-time", "duration": 1, "entryType": "measure" }, { "startTime": 60, - "name": "lh:audit:bootup-time", + "name": "lh:audit:network-requests", "duration": 1, "entryType": "measure" }, { "startTime": 61, - "name": "lh:audit:network-requests", + "name": "lh:computed:EntityClassification", "duration": 1, "entryType": "measure" }, { "startTime": 62, - "name": "lh:computed:EntityClassification", + "name": "lh:audit:network-rtt", "duration": 1, "entryType": "measure" }, { "startTime": 63, - "name": "lh:audit:network-rtt", + "name": "lh:computed:NetworkAnalysis", "duration": 1, "entryType": "measure" }, { "startTime": 64, - "name": "lh:computed:NetworkAnalysis", + "name": "lh:audit:network-server-latency", "duration": 1, "entryType": "measure" }, { "startTime": 65, - "name": "lh:audit:network-server-latency", + "name": "lh:audit:main-thread-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 66, - "name": "lh:audit:main-thread-tasks", + "name": "lh:audit:third-party-summary", "duration": 1, "entryType": "measure" }, { "startTime": 67, - "name": "lh:audit:third-party-summary", + "name": "lh:computed:TBTImpactTasks", "duration": 1, "entryType": "measure" }, { "startTime": 68, - "name": "lh:computed:TBTImpactTasks", + "name": "lh:audit:layout-shift-elements", "duration": 1, "entryType": "measure" }, { "startTime": 69, - "name": "lh:audit:layout-shift-elements", + "name": "lh:audit:long-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 70, - "name": "lh:audit:long-tasks", + "name": "lh:audit:no-unload-listeners", "duration": 1, "entryType": "measure" }, { "startTime": 71, - "name": "lh:audit:no-unload-listeners", + "name": "lh:audit:non-composited-animations", "duration": 1, "entryType": "measure" }, { "startTime": 72, - "name": "lh:audit:non-composited-animations", + "name": "lh:audit:unsized-images", "duration": 1, "entryType": "measure" }, { "startTime": 73, - "name": "lh:audit:unsized-images", + "name": "lh:audit:valid-source-maps", "duration": 1, "entryType": "measure" }, { "startTime": 74, - "name": "lh:audit:valid-source-maps", + "name": "lh:audit:script-treemap-data", "duration": 1, "entryType": "measure" }, { "startTime": 75, - "name": "lh:audit:script-treemap-data", + "name": "lh:computed:ModuleDuplication", "duration": 1, "entryType": "measure" }, { "startTime": 76, - "name": "lh:computed:ModuleDuplication", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, @@ -11409,144 +11403,138 @@ }, { "startTime": 79, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 80, "name": "lh:audit:uses-long-cache-ttl", "duration": 1, "entryType": "measure" }, { - "startTime": 81, + "startTime": 80, "name": "lh:audit:total-byte-weight", "duration": 1, "entryType": "measure" }, { - "startTime": 82, + "startTime": 81, "name": "lh:audit:unminified-css", "duration": 1, "entryType": "measure" }, { - "startTime": 83, + "startTime": 82, "name": "lh:computed:LoadSimulator", "duration": 1, "entryType": "measure" }, { - "startTime": 84, + "startTime": 83, "name": "lh:audit:unminified-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 85, + "startTime": 84, "name": "lh:audit:unused-css-rules", "duration": 1, "entryType": "measure" }, { - "startTime": 86, + "startTime": 85, "name": "lh:computed:UnusedCSS", "duration": 1, "entryType": "measure" }, { - "startTime": 87, + "startTime": 86, "name": "lh:audit:unused-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 88, + "startTime": 87, "name": "lh:audit:modern-image-formats", "duration": 1, "entryType": "measure" }, { - "startTime": 89, + "startTime": 88, "name": "lh:audit:uses-optimized-images", "duration": 1, "entryType": "measure" }, { - "startTime": 90, + "startTime": 89, "name": "lh:audit:uses-text-compression", "duration": 1, "entryType": "measure" }, { - "startTime": 91, + "startTime": 90, "name": "lh:audit:uses-responsive-images", "duration": 1, "entryType": "measure" }, { - "startTime": 92, + "startTime": 91, "name": "lh:computed:ImageRecords", "duration": 1, "entryType": "measure" }, { - "startTime": 93, + "startTime": 92, "name": "lh:audit:efficient-animated-content", "duration": 1, "entryType": "measure" }, { - "startTime": 94, + "startTime": 93, "name": "lh:audit:duplicated-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 95, + "startTime": 94, "name": "lh:audit:legacy-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 96, + "startTime": 95, "name": "lh:audit:inspector-issues", "duration": 1, "entryType": "measure" }, { - "startTime": 97, + "startTime": 96, "name": "lh:audit:no-document-write", "duration": 1, "entryType": "measure" }, { - "startTime": 98, + "startTime": 97, "name": "lh:audit:uses-passive-event-listeners", "duration": 1, "entryType": "measure" }, { - "startTime": 99, + "startTime": 98, "name": "lh:audit:work-during-interaction", "duration": 1, "entryType": "measure" }, { - "startTime": 100, + "startTime": 99, "name": "lh:audit:bf-cache", "duration": 1, "entryType": "measure" }, { - "startTime": 101, + "startTime": 100, "name": "lh:runner:generate", "duration": 1, "entryType": "measure" } ], - "total": 102 + "total": 101 }, "i18n": { "rendererFormattedStrings": { @@ -15971,564 +15959,558 @@ }, { "startTime": 24, - "name": "lh:config:resolveNavigationsToDefns", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 25, "name": "lh:runner:audit", "duration": 1, "entryType": "measure" }, { - "startTime": 26, + "startTime": 25, "name": "lh:runner:auditing", "duration": 1, "entryType": "measure" }, { - "startTime": 27, + "startTime": 26, "name": "lh:audit:viewport", "duration": 1, "entryType": "measure" }, { - "startTime": 28, + "startTime": 27, "name": "lh:computed:ViewportMeta", "duration": 1, "entryType": "measure" }, { - "startTime": 29, + "startTime": 28, "name": "lh:audit:image-aspect-ratio", "duration": 1, "entryType": "measure" }, { - "startTime": 30, + "startTime": 29, "name": "lh:audit:image-size-responsive", "duration": 1, "entryType": "measure" }, { - "startTime": 31, + "startTime": 30, "name": "lh:audit:unsized-images", "duration": 1, "entryType": "measure" }, { - "startTime": 32, + "startTime": 31, "name": "lh:audit:accesskeys", "duration": 1, "entryType": "measure" }, { - "startTime": 33, + "startTime": 32, "name": "lh:audit:aria-allowed-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 34, + "startTime": 33, "name": "lh:audit:aria-allowed-role", "duration": 1, "entryType": "measure" }, { - "startTime": 35, + "startTime": 34, "name": "lh:audit:aria-command-name", "duration": 1, "entryType": "measure" }, { - "startTime": 36, + "startTime": 35, "name": "lh:audit:aria-dialog-name", "duration": 1, "entryType": "measure" }, { - "startTime": 37, + "startTime": 36, "name": "lh:audit:aria-hidden-body", "duration": 1, "entryType": "measure" }, { - "startTime": 38, + "startTime": 37, "name": "lh:audit:aria-hidden-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 39, + "startTime": 38, "name": "lh:audit:aria-input-field-name", "duration": 1, "entryType": "measure" }, { - "startTime": 40, + "startTime": 39, "name": "lh:audit:aria-meter-name", "duration": 1, "entryType": "measure" }, { - "startTime": 41, + "startTime": 40, "name": "lh:audit:aria-progressbar-name", "duration": 1, "entryType": "measure" }, { - "startTime": 42, + "startTime": 41, "name": "lh:audit:aria-required-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 43, + "startTime": 42, "name": "lh:audit:aria-required-children", "duration": 1, "entryType": "measure" }, { - "startTime": 44, + "startTime": 43, "name": "lh:audit:aria-required-parent", "duration": 1, "entryType": "measure" }, { - "startTime": 45, + "startTime": 44, "name": "lh:audit:aria-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 46, + "startTime": 45, "name": "lh:audit:aria-text", "duration": 1, "entryType": "measure" }, { - "startTime": 47, + "startTime": 46, "name": "lh:audit:aria-toggle-field-name", "duration": 1, "entryType": "measure" }, { - "startTime": 48, + "startTime": 47, "name": "lh:audit:aria-tooltip-name", "duration": 1, "entryType": "measure" }, { - "startTime": 49, + "startTime": 48, "name": "lh:audit:aria-treeitem-name", "duration": 1, "entryType": "measure" }, { - "startTime": 50, + "startTime": 49, "name": "lh:audit:aria-valid-attr-value", "duration": 1, "entryType": "measure" }, { - "startTime": 51, + "startTime": 50, "name": "lh:audit:aria-valid-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 52, + "startTime": 51, "name": "lh:audit:button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 53, + "startTime": 52, "name": "lh:audit:bypass", "duration": 1, "entryType": "measure" }, { - "startTime": 54, + "startTime": 53, "name": "lh:audit:color-contrast", "duration": 1, "entryType": "measure" }, { - "startTime": 55, + "startTime": 54, "name": "lh:audit:definition-list", "duration": 1, "entryType": "measure" }, { - "startTime": 56, + "startTime": 55, "name": "lh:audit:dlitem", "duration": 1, "entryType": "measure" }, { - "startTime": 57, + "startTime": 56, "name": "lh:audit:document-title", "duration": 1, "entryType": "measure" }, { - "startTime": 58, + "startTime": 57, "name": "lh:audit:duplicate-id-active", "duration": 1, "entryType": "measure" }, { - "startTime": 59, + "startTime": 58, "name": "lh:audit:duplicate-id-aria", "duration": 1, "entryType": "measure" }, { - "startTime": 60, + "startTime": 59, "name": "lh:audit:empty-heading", "duration": 1, "entryType": "measure" }, { - "startTime": 61, + "startTime": 60, "name": "lh:audit:form-field-multiple-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 62, + "startTime": 61, "name": "lh:audit:frame-title", "duration": 1, "entryType": "measure" }, { - "startTime": 63, + "startTime": 62, "name": "lh:audit:heading-order", "duration": 1, "entryType": "measure" }, { - "startTime": 64, + "startTime": 63, "name": "lh:audit:html-has-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 65, + "startTime": 64, "name": "lh:audit:html-lang-valid", "duration": 1, "entryType": "measure" }, { - "startTime": 66, + "startTime": 65, "name": "lh:audit:html-xml-lang-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 67, + "startTime": 66, "name": "lh:audit:identical-links-same-purpose", "duration": 1, "entryType": "measure" }, { - "startTime": 68, + "startTime": 67, "name": "lh:audit:image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 69, + "startTime": 68, "name": "lh:audit:image-redundant-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 70, + "startTime": 69, "name": "lh:audit:input-button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 71, + "startTime": 70, "name": "lh:audit:input-image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 72, + "startTime": 71, "name": "lh:audit:label-content-name-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 73, + "startTime": 72, "name": "lh:audit:label", "duration": 1, "entryType": "measure" }, { - "startTime": 74, + "startTime": 73, "name": "lh:audit:landmark-one-main", "duration": 1, "entryType": "measure" }, { - "startTime": 75, + "startTime": 74, "name": "lh:audit:link-name", "duration": 1, "entryType": "measure" }, { - "startTime": 76, + "startTime": 75, "name": "lh:audit:link-in-text-block", "duration": 1, "entryType": "measure" }, { - "startTime": 77, + "startTime": 76, "name": "lh:audit:list", "duration": 1, "entryType": "measure" }, { - "startTime": 78, + "startTime": 77, "name": "lh:audit:listitem", "duration": 1, "entryType": "measure" }, { - "startTime": 79, + "startTime": 78, "name": "lh:audit:meta-refresh", "duration": 1, "entryType": "measure" }, { - "startTime": 80, + "startTime": 79, "name": "lh:audit:meta-viewport", "duration": 1, "entryType": "measure" }, { - "startTime": 81, + "startTime": 80, "name": "lh:audit:object-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 82, + "startTime": 81, "name": "lh:audit:select-name", "duration": 1, "entryType": "measure" }, { - "startTime": 83, + "startTime": 82, "name": "lh:audit:skip-link", "duration": 1, "entryType": "measure" }, { - "startTime": 84, + "startTime": 83, "name": "lh:audit:tabindex", "duration": 1, "entryType": "measure" }, { - "startTime": 85, + "startTime": 84, "name": "lh:audit:table-duplicate-name", "duration": 1, "entryType": "measure" }, { - "startTime": 86, + "startTime": 85, "name": "lh:audit:table-fake-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 87, + "startTime": 86, "name": "lh:audit:target-size", "duration": 1, "entryType": "measure" }, { - "startTime": 88, + "startTime": 87, "name": "lh:audit:td-has-header", "duration": 1, "entryType": "measure" }, { - "startTime": 89, + "startTime": 88, "name": "lh:audit:td-headers-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 90, + "startTime": 89, "name": "lh:audit:th-has-data-cells", "duration": 1, "entryType": "measure" }, { - "startTime": 91, + "startTime": 90, "name": "lh:audit:valid-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 92, + "startTime": 91, "name": "lh:audit:video-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 93, + "startTime": 92, "name": "lh:audit:custom-controls-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 94, + "startTime": 93, "name": "lh:audit:custom-controls-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 95, + "startTime": 94, "name": "lh:audit:focus-traps", "duration": 1, "entryType": "measure" }, { - "startTime": 96, + "startTime": 95, "name": "lh:audit:focusable-controls", "duration": 1, "entryType": "measure" }, { - "startTime": 97, + "startTime": 96, "name": "lh:audit:interactive-element-affordance", "duration": 1, "entryType": "measure" }, { - "startTime": 98, + "startTime": 97, "name": "lh:audit:logical-tab-order", "duration": 1, "entryType": "measure" }, { - "startTime": 99, + "startTime": 98, "name": "lh:audit:managed-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 100, + "startTime": 99, "name": "lh:audit:offscreen-content-hidden", "duration": 1, "entryType": "measure" }, { - "startTime": 101, + "startTime": 100, "name": "lh:audit:use-landmarks", "duration": 1, "entryType": "measure" }, { - "startTime": 102, + "startTime": 101, "name": "lh:audit:visual-order-follows-dom", "duration": 1, "entryType": "measure" }, { - "startTime": 103, + "startTime": 102, "name": "lh:audit:uses-responsive-images-snapshot", "duration": 1, "entryType": "measure" }, { - "startTime": 104, + "startTime": 103, "name": "lh:audit:doctype", "duration": 1, "entryType": "measure" }, { - "startTime": 105, + "startTime": 104, "name": "lh:audit:dom-size", "duration": 1, "entryType": "measure" }, { - "startTime": 106, + "startTime": 105, "name": "lh:audit:js-libraries", "duration": 1, "entryType": "measure" }, { - "startTime": 107, + "startTime": 106, "name": "lh:audit:paste-preventing-inputs", "duration": 1, "entryType": "measure" }, { - "startTime": 108, + "startTime": 107, "name": "lh:audit:meta-description", "duration": 1, "entryType": "measure" }, { - "startTime": 109, + "startTime": 108, "name": "lh:audit:font-size", "duration": 1, "entryType": "measure" }, { - "startTime": 110, + "startTime": 109, "name": "lh:audit:link-text", "duration": 1, "entryType": "measure" }, { - "startTime": 111, + "startTime": 110, "name": "lh:audit:crawlable-anchors", "duration": 1, "entryType": "measure" }, { - "startTime": 112, + "startTime": 111, "name": "lh:audit:robots-txt", "duration": 1, "entryType": "measure" }, { - "startTime": 113, + "startTime": 112, "name": "lh:audit:tap-targets", "duration": 1, "entryType": "measure" }, { - "startTime": 114, + "startTime": 113, "name": "lh:audit:plugins", "duration": 1, "entryType": "measure" }, { - "startTime": 115, + "startTime": 114, "name": "lh:audit:structured-data", "duration": 1, "entryType": "measure" }, { - "startTime": 116, + "startTime": 115, "name": "lh:runner:generate", "duration": 1, "entryType": "measure" } ], - "total": 117 + "total": 116 }, "i18n": { "rendererFormattedStrings": { @@ -22792,571 +22774,571 @@ }, { "startTime": 58, - "name": "lh:config:resolveNavigationsToDefns", + "name": "lh:runner:audit", "duration": 1, "entryType": "measure" }, { "startTime": 59, - "name": "lh:runner:audit", + "name": "lh:runner:auditing", "duration": 1, "entryType": "measure" }, { "startTime": 60, - "name": "lh:runner:auditing", + "name": "lh:audit:is-on-https", "duration": 1, "entryType": "measure" }, { "startTime": 61, - "name": "lh:audit:is-on-https", + "name": "lh:computed:NetworkRecords", "duration": 1, "entryType": "measure" }, { "startTime": 62, - "name": "lh:computed:NetworkRecords", + "name": "lh:audit:viewport", "duration": 1, "entryType": "measure" }, { "startTime": 63, - "name": "lh:audit:viewport", + "name": "lh:computed:ViewportMeta", "duration": 1, "entryType": "measure" }, { "startTime": 64, - "name": "lh:computed:ViewportMeta", + "name": "lh:audit:first-contentful-paint", "duration": 1, "entryType": "measure" }, { "startTime": 65, - "name": "lh:audit:first-contentful-paint", + "name": "lh:computed:FirstContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 66, - "name": "lh:computed:FirstContentfulPaint", + "name": "lh:computed:ProcessedTrace", "duration": 1, "entryType": "measure" }, { "startTime": 67, - "name": "lh:computed:ProcessedTrace", + "name": "lh:computed:ProcessedNavigation", "duration": 1, "entryType": "measure" }, { "startTime": 68, - "name": "lh:computed:ProcessedNavigation", + "name": "lh:computed:LanternFirstContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 69, - "name": "lh:computed:LanternFirstContentfulPaint", + "name": "lh:computed:PageDependencyGraph", "duration": 1, "entryType": "measure" }, { "startTime": 70, - "name": "lh:computed:PageDependencyGraph", + "name": "lh:computed:LoadSimulator", "duration": 1, "entryType": "measure" }, { "startTime": 71, - "name": "lh:computed:LoadSimulator", + "name": "lh:computed:NetworkAnalysis", "duration": 1, "entryType": "measure" }, { "startTime": 72, - "name": "lh:computed:NetworkAnalysis", + "name": "lh:audit:largest-contentful-paint", "duration": 1, "entryType": "measure" }, { "startTime": 73, - "name": "lh:audit:largest-contentful-paint", + "name": "lh:computed:LargestContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 74, - "name": "lh:computed:LargestContentfulPaint", + "name": "lh:computed:LanternLargestContentfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 75, - "name": "lh:computed:LanternLargestContentfulPaint", + "name": "lh:audit:first-meaningful-paint", "duration": 1, "entryType": "measure" }, { "startTime": 76, - "name": "lh:audit:first-meaningful-paint", + "name": "lh:computed:FirstMeaningfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 77, - "name": "lh:computed:FirstMeaningfulPaint", + "name": "lh:computed:LanternFirstMeaningfulPaint", "duration": 1, "entryType": "measure" }, { "startTime": 78, - "name": "lh:computed:LanternFirstMeaningfulPaint", + "name": "lh:audit:speed-index", "duration": 1, "entryType": "measure" }, { "startTime": 79, - "name": "lh:audit:speed-index", + "name": "lh:computed:SpeedIndex", "duration": 1, "entryType": "measure" }, { "startTime": 80, - "name": "lh:computed:SpeedIndex", + "name": "lh:computed:LanternSpeedIndex", "duration": 1, "entryType": "measure" }, { "startTime": 81, - "name": "lh:computed:LanternSpeedIndex", + "name": "lh:computed:Speedline", "duration": 1, "entryType": "measure" }, { "startTime": 82, - "name": "lh:computed:Speedline", + "name": "lh:audit:screenshot-thumbnails", "duration": 1, "entryType": "measure" }, { "startTime": 83, - "name": "lh:audit:screenshot-thumbnails", + "name": "lh:audit:final-screenshot", "duration": 1, "entryType": "measure" }, { "startTime": 84, - "name": "lh:audit:final-screenshot", + "name": "lh:computed:Screenshots", "duration": 1, "entryType": "measure" }, { "startTime": 85, - "name": "lh:computed:Screenshots", + "name": "lh:audit:total-blocking-time", "duration": 1, "entryType": "measure" }, { "startTime": 86, - "name": "lh:audit:total-blocking-time", + "name": "lh:computed:TotalBlockingTime", "duration": 1, "entryType": "measure" }, { "startTime": 87, - "name": "lh:computed:TotalBlockingTime", + "name": "lh:computed:LanternTotalBlockingTime", "duration": 1, "entryType": "measure" }, { "startTime": 88, - "name": "lh:computed:LanternTotalBlockingTime", + "name": "lh:computed:LanternInteractive", "duration": 1, "entryType": "measure" }, { "startTime": 89, - "name": "lh:computed:LanternInteractive", + "name": "lh:audit:max-potential-fid", "duration": 1, "entryType": "measure" }, { "startTime": 90, - "name": "lh:audit:max-potential-fid", + "name": "lh:computed:MaxPotentialFID", "duration": 1, "entryType": "measure" }, { "startTime": 91, - "name": "lh:computed:MaxPotentialFID", + "name": "lh:computed:LanternMaxPotentialFID", "duration": 1, "entryType": "measure" }, { "startTime": 92, - "name": "lh:computed:LanternMaxPotentialFID", + "name": "lh:audit:cumulative-layout-shift", "duration": 1, "entryType": "measure" }, { "startTime": 93, - "name": "lh:audit:cumulative-layout-shift", + "name": "lh:computed:CumulativeLayoutShift", "duration": 1, "entryType": "measure" }, { "startTime": 94, - "name": "lh:computed:CumulativeLayoutShift", + "name": "lh:audit:errors-in-console", "duration": 1, "entryType": "measure" }, { "startTime": 95, - "name": "lh:audit:errors-in-console", + "name": "lh:computed:JSBundles", "duration": 1, "entryType": "measure" }, { "startTime": 96, - "name": "lh:computed:JSBundles", + "name": "lh:audit:server-response-time", "duration": 1, "entryType": "measure" }, { "startTime": 97, - "name": "lh:audit:server-response-time", + "name": "lh:computed:MainResource", "duration": 1, "entryType": "measure" }, { "startTime": 98, - "name": "lh:computed:MainResource", + "name": "lh:audit:interactive", "duration": 1, "entryType": "measure" }, { "startTime": 99, - "name": "lh:audit:interactive", + "name": "lh:computed:Interactive", "duration": 1, "entryType": "measure" }, { "startTime": 100, - "name": "lh:computed:Interactive", + "name": "lh:audit:user-timings", "duration": 1, "entryType": "measure" }, { "startTime": 101, - "name": "lh:audit:user-timings", + "name": "lh:computed:UserTimings", "duration": 1, "entryType": "measure" }, { "startTime": 102, - "name": "lh:computed:UserTimings", + "name": "lh:audit:critical-request-chains", "duration": 1, "entryType": "measure" }, { "startTime": 103, - "name": "lh:audit:critical-request-chains", + "name": "lh:computed:CriticalRequestChains", "duration": 1, "entryType": "measure" }, { "startTime": 104, - "name": "lh:computed:CriticalRequestChains", + "name": "lh:audit:redirects", "duration": 1, "entryType": "measure" }, { "startTime": 105, - "name": "lh:audit:redirects", + "name": "lh:audit:installable-manifest", "duration": 1, "entryType": "measure" }, { "startTime": 106, - "name": "lh:audit:installable-manifest", + "name": "lh:audit:splash-screen", "duration": 1, "entryType": "measure" }, { "startTime": 107, - "name": "lh:audit:splash-screen", + "name": "lh:computed:ManifestValues", "duration": 1, "entryType": "measure" }, { "startTime": 108, - "name": "lh:computed:ManifestValues", + "name": "lh:audit:themed-omnibox", "duration": 1, "entryType": "measure" }, { "startTime": 109, - "name": "lh:audit:themed-omnibox", + "name": "lh:audit:maskable-icon", "duration": 1, "entryType": "measure" }, { "startTime": 110, - "name": "lh:audit:maskable-icon", + "name": "lh:audit:content-width", "duration": 1, "entryType": "measure" }, { "startTime": 111, - "name": "lh:audit:content-width", + "name": "lh:audit:image-aspect-ratio", "duration": 1, "entryType": "measure" }, { "startTime": 112, - "name": "lh:audit:image-aspect-ratio", + "name": "lh:audit:image-size-responsive", "duration": 1, "entryType": "measure" }, { "startTime": 113, - "name": "lh:audit:image-size-responsive", + "name": "lh:audit:preload-fonts", "duration": 1, "entryType": "measure" }, { "startTime": 114, - "name": "lh:audit:preload-fonts", + "name": "lh:audit:deprecations", "duration": 1, "entryType": "measure" }, { "startTime": 115, - "name": "lh:audit:deprecations", + "name": "lh:audit:mainthread-work-breakdown", "duration": 1, "entryType": "measure" }, { "startTime": 116, - "name": "lh:audit:mainthread-work-breakdown", + "name": "lh:computed:MainThreadTasks", "duration": 1, "entryType": "measure" }, { "startTime": 117, - "name": "lh:computed:MainThreadTasks", + "name": "lh:audit:bootup-time", "duration": 1, "entryType": "measure" }, { "startTime": 118, - "name": "lh:audit:bootup-time", + "name": "lh:audit:uses-rel-preload", "duration": 1, "entryType": "measure" }, { "startTime": 119, - "name": "lh:audit:uses-rel-preload", + "name": "lh:audit:uses-rel-preconnect", "duration": 1, "entryType": "measure" }, { "startTime": 120, - "name": "lh:audit:uses-rel-preconnect", + "name": "lh:audit:font-display", "duration": 1, "entryType": "measure" }, { "startTime": 121, - "name": "lh:audit:font-display", + "name": "lh:audit:diagnostics", "duration": 1, "entryType": "measure" }, { "startTime": 122, - "name": "lh:audit:diagnostics", + "name": "lh:audit:network-requests", "duration": 1, "entryType": "measure" }, { "startTime": 123, - "name": "lh:audit:network-requests", + "name": "lh:computed:EntityClassification", "duration": 1, "entryType": "measure" }, { "startTime": 124, - "name": "lh:computed:EntityClassification", + "name": "lh:audit:network-rtt", "duration": 1, "entryType": "measure" }, { "startTime": 125, - "name": "lh:audit:network-rtt", + "name": "lh:audit:network-server-latency", "duration": 1, "entryType": "measure" }, { "startTime": 126, - "name": "lh:audit:network-server-latency", + "name": "lh:audit:main-thread-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 127, - "name": "lh:audit:main-thread-tasks", + "name": "lh:audit:metrics", "duration": 1, "entryType": "measure" }, { "startTime": 128, - "name": "lh:audit:metrics", + "name": "lh:computed:TimingSummary", "duration": 1, "entryType": "measure" }, { "startTime": 129, - "name": "lh:computed:TimingSummary", + "name": "lh:computed:FirstContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 130, - "name": "lh:computed:FirstContentfulPaintAllFrames", + "name": "lh:computed:LargestContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 131, - "name": "lh:computed:LargestContentfulPaintAllFrames", + "name": "lh:computed:LCPBreakdown", "duration": 1, "entryType": "measure" }, { "startTime": 132, - "name": "lh:computed:LCPBreakdown", + "name": "lh:computed:TimeToFirstByte", "duration": 1, "entryType": "measure" }, { "startTime": 133, - "name": "lh:computed:TimeToFirstByte", + "name": "lh:computed:LCPImageRecord", "duration": 1, "entryType": "measure" }, { "startTime": 134, - "name": "lh:computed:LCPImageRecord", + "name": "lh:audit:performance-budget", "duration": 1, "entryType": "measure" }, { "startTime": 135, - "name": "lh:audit:performance-budget", + "name": "lh:computed:ResourceSummary", "duration": 1, "entryType": "measure" }, { "startTime": 136, - "name": "lh:computed:ResourceSummary", + "name": "lh:audit:timing-budget", "duration": 1, "entryType": "measure" }, { "startTime": 137, - "name": "lh:audit:timing-budget", + "name": "lh:audit:third-party-summary", "duration": 1, "entryType": "measure" }, { "startTime": 138, - "name": "lh:audit:third-party-summary", + "name": "lh:computed:TBTImpactTasks", "duration": 1, "entryType": "measure" }, { "startTime": 139, - "name": "lh:computed:TBTImpactTasks", + "name": "lh:audit:third-party-facades", "duration": 1, "entryType": "measure" }, { "startTime": 140, - "name": "lh:audit:third-party-facades", + "name": "lh:audit:largest-contentful-paint-element", "duration": 1, "entryType": "measure" }, { "startTime": 141, - "name": "lh:audit:largest-contentful-paint-element", + "name": "lh:audit:lcp-lazy-loaded", "duration": 1, "entryType": "measure" }, { "startTime": 142, - "name": "lh:audit:lcp-lazy-loaded", + "name": "lh:audit:layout-shift-elements", "duration": 1, "entryType": "measure" }, { "startTime": 143, - "name": "lh:audit:layout-shift-elements", + "name": "lh:audit:long-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 144, - "name": "lh:audit:long-tasks", + "name": "lh:audit:no-unload-listeners", "duration": 1, "entryType": "measure" }, { "startTime": 145, - "name": "lh:audit:no-unload-listeners", + "name": "lh:audit:non-composited-animations", "duration": 1, "entryType": "measure" }, { "startTime": 146, - "name": "lh:audit:non-composited-animations", + "name": "lh:audit:unsized-images", "duration": 1, "entryType": "measure" }, { "startTime": 147, - "name": "lh:audit:unsized-images", + "name": "lh:audit:valid-source-maps", "duration": 1, "entryType": "measure" }, { "startTime": 148, - "name": "lh:audit:valid-source-maps", + "name": "lh:audit:prioritize-lcp-image", "duration": 1, "entryType": "measure" }, { "startTime": 149, - "name": "lh:audit:prioritize-lcp-image", + "name": "lh:audit:csp-xss", "duration": 1, "entryType": "measure" }, { "startTime": 150, - "name": "lh:audit:csp-xss", + "name": "lh:audit:script-treemap-data", "duration": 1, "entryType": "measure" }, { "startTime": 151, - "name": "lh:audit:script-treemap-data", + "name": "lh:computed:ModuleDuplication", "duration": 1, "entryType": "measure" }, { "startTime": 152, - "name": "lh:computed:ModuleDuplication", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, @@ -23434,708 +23416,702 @@ }, { "startTime": 165, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 166, "name": "lh:audit:pwa-cross-browser", "duration": 1, "entryType": "measure" }, { - "startTime": 167, + "startTime": 166, "name": "lh:audit:pwa-page-transitions", "duration": 1, "entryType": "measure" }, { - "startTime": 168, + "startTime": 167, "name": "lh:audit:pwa-each-page-has-url", "duration": 1, "entryType": "measure" }, { - "startTime": 169, + "startTime": 168, "name": "lh:audit:accesskeys", "duration": 1, "entryType": "measure" }, { - "startTime": 170, + "startTime": 169, "name": "lh:audit:aria-allowed-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 171, + "startTime": 170, "name": "lh:audit:aria-allowed-role", "duration": 1, "entryType": "measure" }, { - "startTime": 172, + "startTime": 171, "name": "lh:audit:aria-command-name", "duration": 1, "entryType": "measure" }, { - "startTime": 173, + "startTime": 172, "name": "lh:audit:aria-dialog-name", "duration": 1, "entryType": "measure" }, { - "startTime": 174, + "startTime": 173, "name": "lh:audit:aria-hidden-body", "duration": 1, "entryType": "measure" }, { - "startTime": 175, + "startTime": 174, "name": "lh:audit:aria-hidden-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 176, + "startTime": 175, "name": "lh:audit:aria-input-field-name", "duration": 1, "entryType": "measure" }, { - "startTime": 177, + "startTime": 176, "name": "lh:audit:aria-meter-name", "duration": 1, "entryType": "measure" }, { - "startTime": 178, + "startTime": 177, "name": "lh:audit:aria-progressbar-name", "duration": 1, "entryType": "measure" }, { - "startTime": 179, + "startTime": 178, "name": "lh:audit:aria-required-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 180, + "startTime": 179, "name": "lh:audit:aria-required-children", "duration": 1, "entryType": "measure" }, { - "startTime": 181, + "startTime": 180, "name": "lh:audit:aria-required-parent", "duration": 1, "entryType": "measure" }, { - "startTime": 182, + "startTime": 181, "name": "lh:audit:aria-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 183, + "startTime": 182, "name": "lh:audit:aria-text", "duration": 1, "entryType": "measure" }, { - "startTime": 184, + "startTime": 183, "name": "lh:audit:aria-toggle-field-name", "duration": 1, "entryType": "measure" }, { - "startTime": 185, + "startTime": 184, "name": "lh:audit:aria-tooltip-name", "duration": 1, "entryType": "measure" }, { - "startTime": 186, + "startTime": 185, "name": "lh:audit:aria-treeitem-name", "duration": 1, "entryType": "measure" }, { - "startTime": 187, + "startTime": 186, "name": "lh:audit:aria-valid-attr-value", "duration": 1, "entryType": "measure" }, { - "startTime": 188, + "startTime": 187, "name": "lh:audit:aria-valid-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 189, + "startTime": 188, "name": "lh:audit:button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 190, + "startTime": 189, "name": "lh:audit:bypass", "duration": 1, "entryType": "measure" }, { - "startTime": 191, + "startTime": 190, "name": "lh:audit:color-contrast", "duration": 1, "entryType": "measure" }, { - "startTime": 192, + "startTime": 191, "name": "lh:audit:definition-list", "duration": 1, "entryType": "measure" }, { - "startTime": 193, + "startTime": 192, "name": "lh:audit:dlitem", "duration": 1, "entryType": "measure" }, { - "startTime": 194, + "startTime": 193, "name": "lh:audit:document-title", "duration": 1, "entryType": "measure" }, { - "startTime": 195, + "startTime": 194, "name": "lh:audit:duplicate-id-active", "duration": 1, "entryType": "measure" }, { - "startTime": 196, + "startTime": 195, "name": "lh:audit:duplicate-id-aria", "duration": 1, "entryType": "measure" }, { - "startTime": 197, + "startTime": 196, "name": "lh:audit:empty-heading", "duration": 1, "entryType": "measure" }, { - "startTime": 198, + "startTime": 197, "name": "lh:audit:form-field-multiple-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 199, + "startTime": 198, "name": "lh:audit:frame-title", "duration": 1, "entryType": "measure" }, { - "startTime": 200, + "startTime": 199, "name": "lh:audit:heading-order", "duration": 1, "entryType": "measure" }, { - "startTime": 201, + "startTime": 200, "name": "lh:audit:html-has-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 202, + "startTime": 201, "name": "lh:audit:html-lang-valid", "duration": 1, "entryType": "measure" }, { - "startTime": 203, + "startTime": 202, "name": "lh:audit:html-xml-lang-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 204, + "startTime": 203, "name": "lh:audit:identical-links-same-purpose", "duration": 1, "entryType": "measure" }, { - "startTime": 205, + "startTime": 204, "name": "lh:audit:image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 206, + "startTime": 205, "name": "lh:audit:image-redundant-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 207, + "startTime": 206, "name": "lh:audit:input-button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 208, + "startTime": 207, "name": "lh:audit:input-image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 209, + "startTime": 208, "name": "lh:audit:label-content-name-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 210, + "startTime": 209, "name": "lh:audit:label", "duration": 1, "entryType": "measure" }, { - "startTime": 211, + "startTime": 210, "name": "lh:audit:landmark-one-main", "duration": 1, "entryType": "measure" }, { - "startTime": 212, + "startTime": 211, "name": "lh:audit:link-name", "duration": 1, "entryType": "measure" }, { - "startTime": 213, + "startTime": 212, "name": "lh:audit:link-in-text-block", "duration": 1, "entryType": "measure" }, { - "startTime": 214, + "startTime": 213, "name": "lh:audit:list", "duration": 1, "entryType": "measure" }, { - "startTime": 215, + "startTime": 214, "name": "lh:audit:listitem", "duration": 1, "entryType": "measure" }, { - "startTime": 216, + "startTime": 215, "name": "lh:audit:meta-refresh", "duration": 1, "entryType": "measure" }, { - "startTime": 217, + "startTime": 216, "name": "lh:audit:meta-viewport", "duration": 1, "entryType": "measure" }, { - "startTime": 218, + "startTime": 217, "name": "lh:audit:object-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 219, + "startTime": 218, "name": "lh:audit:select-name", "duration": 1, "entryType": "measure" }, { - "startTime": 220, + "startTime": 219, "name": "lh:audit:skip-link", "duration": 1, "entryType": "measure" }, { - "startTime": 221, + "startTime": 220, "name": "lh:audit:tabindex", "duration": 1, "entryType": "measure" }, { - "startTime": 222, + "startTime": 221, "name": "lh:audit:table-duplicate-name", "duration": 1, "entryType": "measure" }, { - "startTime": 223, + "startTime": 222, "name": "lh:audit:table-fake-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 224, + "startTime": 223, "name": "lh:audit:target-size", "duration": 1, "entryType": "measure" }, { - "startTime": 225, + "startTime": 224, "name": "lh:audit:td-has-header", "duration": 1, "entryType": "measure" }, { - "startTime": 226, + "startTime": 225, "name": "lh:audit:td-headers-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 227, + "startTime": 226, "name": "lh:audit:th-has-data-cells", "duration": 1, "entryType": "measure" }, { - "startTime": 228, + "startTime": 227, "name": "lh:audit:valid-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 229, + "startTime": 228, "name": "lh:audit:video-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 230, + "startTime": 229, "name": "lh:audit:custom-controls-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 231, + "startTime": 230, "name": "lh:audit:custom-controls-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 232, + "startTime": 231, "name": "lh:audit:focus-traps", "duration": 1, "entryType": "measure" }, { - "startTime": 233, + "startTime": 232, "name": "lh:audit:focusable-controls", "duration": 1, "entryType": "measure" }, { - "startTime": 234, + "startTime": 233, "name": "lh:audit:interactive-element-affordance", "duration": 1, "entryType": "measure" }, { - "startTime": 235, + "startTime": 234, "name": "lh:audit:logical-tab-order", "duration": 1, "entryType": "measure" }, { - "startTime": 236, + "startTime": 235, "name": "lh:audit:managed-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 237, + "startTime": 236, "name": "lh:audit:offscreen-content-hidden", "duration": 1, "entryType": "measure" }, { - "startTime": 238, + "startTime": 237, "name": "lh:audit:use-landmarks", "duration": 1, "entryType": "measure" }, { - "startTime": 239, + "startTime": 238, "name": "lh:audit:visual-order-follows-dom", "duration": 1, "entryType": "measure" }, { - "startTime": 240, + "startTime": 239, "name": "lh:audit:uses-long-cache-ttl", "duration": 1, "entryType": "measure" }, { - "startTime": 241, + "startTime": 240, "name": "lh:audit:total-byte-weight", "duration": 1, "entryType": "measure" }, { - "startTime": 242, + "startTime": 241, "name": "lh:audit:offscreen-images", "duration": 1, "entryType": "measure" }, { - "startTime": 243, + "startTime": 242, "name": "lh:audit:render-blocking-resources", "duration": 1, "entryType": "measure" }, { - "startTime": 244, + "startTime": 243, "name": "lh:computed:UnusedCSS", "duration": 1, "entryType": "measure" }, { - "startTime": 245, + "startTime": 244, "name": "lh:computed:FirstContentfulPaint", "duration": 1, "entryType": "measure" }, { - "startTime": 246, + "startTime": 245, "name": "lh:audit:unminified-css", "duration": 1, "entryType": "measure" }, { - "startTime": 247, + "startTime": 246, "name": "lh:audit:unminified-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 248, + "startTime": 247, "name": "lh:audit:unused-css-rules", "duration": 1, "entryType": "measure" }, { - "startTime": 249, + "startTime": 248, "name": "lh:audit:unused-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 250, + "startTime": 249, "name": "lh:audit:modern-image-formats", "duration": 1, "entryType": "measure" }, { - "startTime": 251, + "startTime": 250, "name": "lh:audit:uses-optimized-images", "duration": 1, "entryType": "measure" }, { - "startTime": 252, + "startTime": 251, "name": "lh:audit:uses-text-compression", "duration": 1, "entryType": "measure" }, { - "startTime": 253, + "startTime": 252, "name": "lh:audit:uses-responsive-images", "duration": 1, "entryType": "measure" }, { - "startTime": 254, + "startTime": 253, "name": "lh:computed:ImageRecords", "duration": 1, "entryType": "measure" }, { - "startTime": 255, + "startTime": 254, "name": "lh:audit:efficient-animated-content", "duration": 1, "entryType": "measure" }, { - "startTime": 256, + "startTime": 255, "name": "lh:audit:duplicated-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 257, + "startTime": 256, "name": "lh:audit:legacy-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 258, + "startTime": 257, "name": "lh:audit:doctype", "duration": 1, "entryType": "measure" }, { - "startTime": 259, + "startTime": 258, "name": "lh:audit:charset", "duration": 1, "entryType": "measure" }, { - "startTime": 260, + "startTime": 259, "name": "lh:audit:dom-size", "duration": 1, "entryType": "measure" }, { - "startTime": 261, + "startTime": 260, "name": "lh:audit:geolocation-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 262, + "startTime": 261, "name": "lh:audit:inspector-issues", "duration": 1, "entryType": "measure" }, { - "startTime": 263, + "startTime": 262, "name": "lh:audit:no-document-write", "duration": 1, "entryType": "measure" }, { - "startTime": 264, + "startTime": 263, "name": "lh:audit:js-libraries", "duration": 1, "entryType": "measure" }, { - "startTime": 265, + "startTime": 264, "name": "lh:audit:notification-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 266, + "startTime": 265, "name": "lh:audit:paste-preventing-inputs", "duration": 1, "entryType": "measure" }, { - "startTime": 267, + "startTime": 266, "name": "lh:audit:uses-passive-event-listeners", "duration": 1, "entryType": "measure" }, { - "startTime": 268, + "startTime": 267, "name": "lh:audit:meta-description", "duration": 1, "entryType": "measure" }, { - "startTime": 269, + "startTime": 268, "name": "lh:audit:http-status-code", "duration": 1, "entryType": "measure" }, { - "startTime": 270, + "startTime": 269, "name": "lh:audit:font-size", "duration": 1, "entryType": "measure" }, { - "startTime": 271, + "startTime": 270, "name": "lh:audit:link-text", "duration": 1, "entryType": "measure" }, { - "startTime": 272, + "startTime": 271, "name": "lh:audit:crawlable-anchors", "duration": 1, "entryType": "measure" }, { - "startTime": 273, + "startTime": 272, "name": "lh:audit:is-crawlable", "duration": 1, "entryType": "measure" }, { - "startTime": 274, + "startTime": 273, "name": "lh:audit:robots-txt", "duration": 1, "entryType": "measure" }, { - "startTime": 275, + "startTime": 274, "name": "lh:audit:tap-targets", "duration": 1, "entryType": "measure" }, { - "startTime": 276, + "startTime": 275, "name": "lh:audit:hreflang", "duration": 1, "entryType": "measure" }, { - "startTime": 277, + "startTime": 276, "name": "lh:audit:plugins", "duration": 1, "entryType": "measure" }, { - "startTime": 278, + "startTime": 277, "name": "lh:audit:canonical", "duration": 1, "entryType": "measure" }, { - "startTime": 279, + "startTime": 278, "name": "lh:audit:structured-data", "duration": 1, "entryType": "measure" }, { - "startTime": 280, + "startTime": 279, "name": "lh:audit:bf-cache", "duration": 1, "entryType": "measure" }, { - "startTime": 281, + "startTime": 280, "name": "lh:runner:generate", "duration": 1, "entryType": "measure" } ], - "total": 282 + "total": 281 }, "i18n": { "rendererFormattedStrings": { diff --git a/core/test/results/artifacts/artifacts.json b/core/test/results/artifacts/artifacts.json index 5d481c289a38..41f222473c60 100644 --- a/core/test/results/artifacts/artifacts.json +++ b/core/test/results/artifacts/artifacts.json @@ -40,10 +40,10 @@ ], "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, - "pauseAfterFcpMs": 1000, - "pauseAfterLoadMs": 1000, - "networkQuietThresholdMs": 1000, - "cpuQuietThresholdMs": 1000, + "pauseAfterFcpMs": 5250, + "pauseAfterLoadMs": 5250, + "networkQuietThresholdMs": 5250, + "cpuQuietThresholdMs": 5250, "formFactor": "mobile", "throttling": { "rttMs": 150, diff --git a/core/test/results/sample_v2.json b/core/test/results/sample_v2.json index 3f8ab8135a6a..9ee2ca51e3ce 100644 --- a/core/test/results/sample_v2.json +++ b/core/test/results/sample_v2.json @@ -5557,10 +5557,10 @@ ], "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, - "pauseAfterFcpMs": 1000, - "pauseAfterLoadMs": 1000, - "networkQuietThresholdMs": 1000, - "cpuQuietThresholdMs": 1000, + "pauseAfterFcpMs": 5250, + "pauseAfterLoadMs": 5250, + "networkQuietThresholdMs": 5250, + "cpuQuietThresholdMs": 5250, "formFactor": "mobile", "throttling": { "rttMs": 150, @@ -7729,12 +7729,6 @@ "duration": 100, "entryType": "measure" }, - { - "startTime": 0, - "name": "lh:config:resolveNavigationsToDefns", - "duration": 100, - "entryType": "measure" - }, { "startTime": 0, "name": "lh:runner:audit", From acf2310102d324205938065d09978e3d9924b1ee Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 22 Aug 2023 15:50:46 -0700 Subject: [PATCH 04/10] filters --- core/config/filters.js | 28 ------------- core/test/config/filters-test.js | 67 ++------------------------------ 2 files changed, 4 insertions(+), 91 deletions(-) diff --git a/core/config/filters.js b/core/config/filters.js index aee7a2a88f5a..9a49abaae2dd 100644 --- a/core/config/filters.js +++ b/core/config/filters.js @@ -107,30 +107,6 @@ function filterArtifactsByGatherMode(artifacts, mode) { }); } -/** - * Filters an array of navigations down to the set supported by the available artifacts. - * - * @param {LH.Config.ResolvedConfig['navigations']} navigations - * @param {Array} availableArtifacts - * @return {LH.Config.ResolvedConfig['navigations']} - */ -function filterNavigationsByAvailableArtifacts(navigations, availableArtifacts) { - if (!navigations) return navigations; - - const availableArtifactIds = new Set( - availableArtifacts.map(artifact => artifact.id).concat(baseArtifactKeys) - ); - - return navigations - .map(navigation => { - return { - ...navigation, - artifacts: navigation.artifacts.filter((artifact) => availableArtifactIds.has(artifact.id)), - }; - }) - .filter(navigation => navigation.artifacts.length); -} - /** * Filters an array of audits down to the set that can be computed using only the specified artifacts. * @@ -318,13 +294,10 @@ function filterConfigByExplicitFilters(resolvedConfig, filters) { if (artifacts && resolvedConfig.settings.disableFullPageScreenshot) { artifacts = artifacts.filter(({id}) => id !== 'FullPageScreenshot'); } - const navigations = - filterNavigationsByAvailableArtifacts(resolvedConfig.navigations, artifacts || []); return { ...resolvedConfig, artifacts, - navigations, audits, categories, }; @@ -335,7 +308,6 @@ export { filterConfigByExplicitFilters, filterArtifactsByGatherMode, filterArtifactsByAvailableAudits, - filterNavigationsByAvailableArtifacts, filterAuditsByAvailableArtifacts, filterAuditsByGatherMode, filterCategoriesByAvailableAudits, diff --git a/core/test/config/filters-test.js b/core/test/config/filters-test.js index 288cda2d9083..2d4e3c21f986 100644 --- a/core/test/config/filters-test.js +++ b/core/test/config/filters-test.js @@ -8,7 +8,7 @@ import log from 'lighthouse-logger'; import {Audit as BaseAudit} from '../../audits/audit.js'; import BaseGatherer from '../../gather/base-gatherer.js'; -import {defaultSettings, defaultNavigationConfig} from '../../config/constants.js'; +import {defaultSettings} from '../../config/constants.js'; import * as filters from '../../config/filters.js'; describe('Config Filtering', () => { @@ -77,37 +77,18 @@ describe('Config Filtering', () => { {id: 'Snapshot2', gatherer: {instance: snapshotGatherer}}, ]; - /** @type {Array} */ - const navigations = [ - { - ...defaultNavigationConfig, - id: 'firstPass', - artifacts: [ - {id: 'Snapshot', gatherer: {instance: snapshotGatherer}}, - {id: 'Timespan', gatherer: {instance: timespanGatherer}}, - ], - }, - { - ...defaultNavigationConfig, - id: 'secondPass', - artifacts: [ - {id: 'Snapshot2', gatherer: {instance: snapshotGatherer}}, - ], - }, - ]; - /** @type {Array} */ const audits = [SnapshotAudit, TimespanAudit, NavigationAudit, ManualAudit].map(audit => ({ implementation: audit, options: {}, })); - return {artifacts, navigationArtifacts, navigations, audits}; + return {artifacts, navigationArtifacts, audits}; } - let {artifacts, navigationArtifacts, navigations, audits} = createTestObjects(); + let {artifacts, navigationArtifacts, audits} = createTestObjects(); beforeEach(() => { - ({artifacts, navigationArtifacts, navigations, audits} = createTestObjects()); + ({artifacts, navigationArtifacts, audits} = createTestObjects()); }); describe('filterArtifactsByGatherMode', () => { @@ -199,32 +180,6 @@ describe('Config Filtering', () => { }); }); - describe('filterNavigationsByAvailableArtifacts', () => { - it('should handle null', () => { - expect(filters.filterNavigationsByAvailableArtifacts(null, [])).toBe(null); - }); - - it('should filter out entire navigations', () => { - const partialArtifacts = [{id: 'Timespan', gatherer: {instance: snapshotGatherer}}]; - const filtered = filters.filterNavigationsByAvailableArtifacts(navigations, partialArtifacts); - expect(filtered).toMatchObject([ - {id: 'firstPass', artifacts: [{id: 'Timespan'}]}, - ]); - }); - - it('should filter within navigation', () => { - const partialArtifacts = [ - {id: 'Snapshot', gatherer: {instance: snapshotGatherer}}, - {id: 'Snapshot2', gatherer: {instance: snapshotGatherer}}, - ]; - const filtered = filters.filterNavigationsByAvailableArtifacts(navigations, partialArtifacts); - expect(filtered).toMatchObject([ - {id: 'firstPass', artifacts: [{id: 'Snapshot'}]}, - {id: 'secondPass', artifacts: [{id: 'Snapshot2'}]}, - ]); - }); - }); - describe('filterAuditsByAvailableArtifacts', () => { it('should handle null', () => { expect(filters.filterAuditsByAvailableArtifacts(null, [])).toBe(null); @@ -400,7 +355,6 @@ describe('Config Filtering', () => { it('should filter the entire config', () => { const config = { artifacts, - navigations, audits, categories, groups: null, @@ -408,7 +362,6 @@ describe('Config Filtering', () => { }; expect(filters.filterConfigByGatherMode(config, 'snapshot')).toMatchObject({ - navigations, artifacts: [{id: 'Snapshot'}], audits: [{implementation: SnapshotAudit}, {implementation: ManualAudit}], categories: { @@ -427,7 +380,6 @@ describe('Config Filtering', () => { beforeEach(() => { resolvedConfig = { artifacts: navigationArtifacts, - navigations, audits, categories, groups: null, @@ -564,7 +516,6 @@ describe('Config Filtering', () => { skipAudits: null, }); expect(filtered).toMatchObject({ - navigations: [{id: 'firstPass'}], artifacts: [{id: 'Snapshot'}, {id: 'Timespan'}], audits: [ {implementation: SnapshotAudit}, @@ -591,7 +542,6 @@ describe('Config Filtering', () => { skipAudits: null, }); expect(filtered).toMatchObject({ - navigations: [{id: 'firstPass'}], artifacts: [{id: 'Snapshot'}, {id: 'Timespan'}], audits: [ {implementation: SnapshotAudit}, @@ -611,8 +561,6 @@ describe('Config Filtering', () => { resolvedConfig = { ...resolvedConfig, }; - resolvedConfig.navigations?.[0].artifacts.push( - {id: 'FullPageScreenshot', gatherer: {instance: fpsGatherer}}); resolvedConfig.artifacts?.push( {id: 'FullPageScreenshot', gatherer: {instance: fpsGatherer}}); @@ -622,7 +570,6 @@ describe('Config Filtering', () => { skipAudits: null, }); expect(filtered).toMatchObject({ - navigations: [{id: 'firstPass'}], artifacts: [{id: 'Snapshot'}, {id: 'Timespan'}, {id: 'FullPageScreenshot'}], }); }); @@ -634,8 +581,6 @@ describe('Config Filtering', () => { resolvedConfig = { ...resolvedConfig, }; - resolvedConfig.navigations?.[0].artifacts.push( - {id: 'FullPageScreenshot', gatherer: {instance: fpsGatherer}}); resolvedConfig.artifacts?.push( {id: 'FullPageScreenshot', gatherer: {instance: fpsGatherer}}); @@ -645,7 +590,6 @@ describe('Config Filtering', () => { skipAudits: null, }); expect(filtered).toMatchObject({ - navigations: [{id: 'firstPass'}], artifacts: [{id: 'Snapshot'}, {id: 'Timespan'}, {id: 'FullPageScreenshot'}], }); }); @@ -658,8 +602,6 @@ describe('Config Filtering', () => { ...resolvedConfig, }; resolvedConfig.settings.disableFullPageScreenshot = true; - resolvedConfig.navigations?.[0].artifacts.push( - {id: 'FullPageScreenshot', gatherer: {instance: fpsGatherer}}); resolvedConfig.artifacts?.push( {id: 'FullPageScreenshot', gatherer: {instance: fpsGatherer}}); @@ -669,7 +611,6 @@ describe('Config Filtering', () => { skipAudits: null, }); expect(filtered).toMatchObject({ - navigations: [{id: 'firstPass'}], artifacts: [{id: 'Snapshot'}, {id: 'Timespan'}], }); }); From e0b5c86fb50101467f9c4c5a14fc8a16ba6da7c6 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 22 Aug 2023 16:05:01 -0700 Subject: [PATCH 05/10] e2e --- .../devtools-tests/e2e/lighthouse/navigation_test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts b/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts index 5f59f2ae2131..4e9bbf63f095 100644 --- a/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts +++ b/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts @@ -90,10 +90,10 @@ describe('Navigation', async function() { const {lhr, artifacts, reportEl} = await waitForResult(); // 1 initial about:blank jump - // 1 about:blank jump + 1 navigation for the default pass + // 1 navigation for the actual page load // 2 navigations to go to chrome://terms and back testing bfcache - // 1 navigation after auditing to reset state - assert.strictEqual(numNavigations, 6); + // 1 refresh after auditing to reset state + assert.strictEqual(numNavigations, 5); assert.strictEqual(lhr.lighthouseVersion, '11.0.0'); assert.match(lhr.finalUrl, /^https:\/\/localhost:[0-9]+\/test\/e2e\/resources\/lighthouse\/hello.html/); From 2535e0814ea133e8a34d3d8f9b7f4fca7a7a5f53 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 22 Aug 2023 16:36:32 -0700 Subject: [PATCH 06/10] test coverage --- core/config/config.js | 3 -- core/config/validation.js | 23 ++++++------ core/test/config/validation-test.js | 43 ++++++++++++++-------- core/test/gather/navigation-runner-test.js | 16 ++++++++ 4 files changed, 55 insertions(+), 30 deletions(-) diff --git a/core/config/config.js b/core/config/config.js index b206fee1b4a7..d16aea63f6b3 100644 --- a/core/config/config.js +++ b/core/config/config.js @@ -15,7 +15,6 @@ import { throwInvalidDependencyOrder, isValidArtifactDependency, throwInvalidArtifactDependency, - assertArtifactTopologicalOrder, assertValidConfig, } from './validation.js'; import {filterConfigByGatherMode, filterConfigByExplicitFilters} from './filters.js'; @@ -171,8 +170,6 @@ async function resolveArtifactsToDefns(artifacts, configDir) { artifactDefns.push(artifact); } - assertArtifactTopologicalOrder(artifactDefns); - log.timeEnd(status); return artifactDefns; } diff --git a/core/config/validation.js b/core/config/validation.js index 74cd196249a8..4940548ecd56 100644 --- a/core/config/validation.js +++ b/core/config/validation.js @@ -186,14 +186,19 @@ function assertValidSettings(settings) { } /** - * Asserts that artifacts are in a valid dependency order that can be computed. + * Asserts that artifacts are unique, valid and are in a dependency order that can be computed. * * @param {Array} artifactDefns */ -function assertArtifactTopologicalOrder(artifactDefns) { +function assertValidArtifacts(artifactDefns) { + /** @type {Set} */ const availableArtifacts = new Set(); for (const artifact of artifactDefns) { + if (availableArtifacts.has(artifact.id)) { + throw new Error(`Config defined multiple artifacts with id '${artifact.id}'`); + } + availableArtifacts.add(artifact.id); if (!artifact.dependencies) continue; @@ -201,6 +206,8 @@ function assertArtifactTopologicalOrder(artifactDefns) { if (availableArtifacts.has(dependencyId)) continue; throwInvalidDependencyOrder(artifact.id, dependencyKey); } + + assertValidArtifact(artifact); } } @@ -209,15 +216,7 @@ function assertArtifactTopologicalOrder(artifactDefns) { * @return {{warnings: string[]}} */ function assertValidConfig(resolvedConfig) { - /** @type {Set} */ - const artifactIds = new Set(); - for (const artifactDefn of resolvedConfig.artifacts || []) { - if (artifactIds.has(artifactDefn.id)) { - throw new Error(`Config defined multiple artifacts with id '${artifactDefn.id}'`); - } - artifactIds.add(artifactDefn.id); - assertValidArtifact(artifactDefn); - } + assertValidArtifacts(resolvedConfig.artifacts || []); for (const auditDefn of resolvedConfig.audits || []) { assertValidAudit(auditDefn); @@ -267,7 +266,7 @@ export { assertValidAudit, assertValidCategories, assertValidSettings, - assertArtifactTopologicalOrder, + assertValidArtifacts, assertValidConfig, throwInvalidDependencyOrder, throwInvalidArtifactDependency, diff --git a/core/test/config/validation-test.js b/core/test/config/validation-test.js index 7525f61f71f2..dbc5601cf7be 100644 --- a/core/test/config/validation-test.js +++ b/core/test/config/validation-test.js @@ -9,7 +9,7 @@ import defaultConfig from '../../config/default-config.js'; import {Audit as BaseAudit} from '../../audits/audit.js'; import BaseGatherer from '../../gather/base-gatherer.js'; import * as validation from '../../config/validation.js'; -import {initializeConfig} from '../../config/config.js'; +import LinkElements from '../../gather/gatherers/link-elements.js'; /** @typedef {LH.Gatherer.GathererMeta['supportedModes']} SupportedModes */ @@ -30,20 +30,6 @@ beforeEach(() => { }); describe('Config Validation', () => { - describe('assertValidConfig', () => { - it('should throw if multiple artifacts have the same id', async () => { - const {resolvedConfig} = await initializeConfig('navigation'); - if (!resolvedConfig.artifacts) throw new Error('No config artifacts'); - - const imageElArtifact = resolvedConfig.artifacts.find(a => a.id === 'ImageElements'); - if (!imageElArtifact) throw new Error('Could not find ImageElements artifact'); - - resolvedConfig.artifacts.push(imageElArtifact); - - expect(() => validation.assertValidConfig(resolvedConfig)).toThrow(/Config defined multiple/); - }); - }); - describe('isValidArtifactDependency', () => { /** @type {Array<{dependent: SupportedModes, dependency: SupportedModes, isValid: boolean}>} */ const combinations = [ @@ -84,6 +70,33 @@ describe('Config Validation', () => { }); }); + describe('.assertValidArtifacts', () => { + it('should throw if multiple artifacts have the same id', async () => { + const artifacts = [ + {id: 'Artifact1', gatherer: {instance: new BaseGatherer()}}, + {id: 'Artifact1', gatherer: {instance: new BaseGatherer()}}, + ]; + const invocation = () => validation.assertValidArtifacts(artifacts); + expect(invocation).toThrow(/Config defined multiple/); + }); + + it('should throw if dependencies are out of order', async () => { + const dependentGatherer = new LinkElements(); + + /** @type {LH.Config.AnyArtifactDefn[]} */ + const artifacts = [ + { + id: 'LinkElements', + gatherer: {instance: dependentGatherer}, + dependencies: {DevtoolsLog: {id: 'DevtoolsLog'}}, + }, + {id: 'DevtoolsLog', gatherer: {instance: new BaseGatherer()}}, + ]; + const invocation = () => validation.assertValidArtifacts(artifacts); + expect(invocation).toThrow(/Failed to find dependency/); + }); + }); + describe('.assertValidArtifact', () => { it('should throw if gatherer does not have a meta object', () => { const gatherer = new BaseGatherer(); diff --git a/core/test/gather/navigation-runner-test.js b/core/test/gather/navigation-runner-test.js index 824eef7ad9d6..97a8dc83de09 100644 --- a/core/test/gather/navigation-runner-test.js +++ b/core/test/gather/navigation-runner-test.js @@ -387,6 +387,22 @@ describe('NavigationRunner', () => { expect(mocks.emulationMock.clearThrottling).toHaveBeenCalledTimes(1); }); + + it('throws if artifacts are missing', async () => { + const {resolvedConfig} = createMockConfig(); + resolvedConfig.artifacts = null; + + const artifactsPromise = runner._navigation({ + driver, + page, + resolvedConfig, + requestor, + computedCache, + baseArtifacts, + }); + + await expect(artifactsPromise).rejects.toThrow('No artifacts were defined on the config'); + }); }); describe('_navigate', () => { From 655425a428d4e1e5ef99acdacc5e82ab1e928c93 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Fri, 25 Aug 2023 13:12:18 -0700 Subject: [PATCH 07/10] fix --- core/config/validation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/config/validation.js b/core/config/validation.js index 4940548ecd56..e0d22a221c6b 100644 --- a/core/config/validation.js +++ b/core/config/validation.js @@ -195,6 +195,8 @@ function assertValidArtifacts(artifactDefns) { const availableArtifacts = new Set(); for (const artifact of artifactDefns) { + assertValidArtifact(artifact); + if (availableArtifacts.has(artifact.id)) { throw new Error(`Config defined multiple artifacts with id '${artifact.id}'`); } @@ -206,8 +208,6 @@ function assertValidArtifacts(artifactDefns) { if (availableArtifacts.has(dependencyId)) continue; throwInvalidDependencyOrder(artifact.id, dependencyKey); } - - assertValidArtifact(artifact); } } From eec6a5023e13556e1ce9cbebaa14d42e7cc236d6 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Fri, 25 Aug 2023 13:30:21 -0700 Subject: [PATCH 08/10] test --- core/test/config/validation-test.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/test/config/validation-test.js b/core/test/config/validation-test.js index dbc5601cf7be..5d77c1391dd9 100644 --- a/core/test/config/validation-test.js +++ b/core/test/config/validation-test.js @@ -72,9 +72,13 @@ describe('Config Validation', () => { describe('.assertValidArtifacts', () => { it('should throw if multiple artifacts have the same id', async () => { + const instance = new BaseGatherer(); + instance.meta.supportedModes = ['navigation']; + instance.getArtifact = () => {}; + const artifacts = [ - {id: 'Artifact1', gatherer: {instance: new BaseGatherer()}}, - {id: 'Artifact1', gatherer: {instance: new BaseGatherer()}}, + {id: 'Artifact1', gatherer: {instance}}, + {id: 'Artifact1', gatherer: {instance}}, ]; const invocation = () => validation.assertValidArtifacts(artifacts); expect(invocation).toThrow(/Config defined multiple/); From f5b0c40da978a51454d4d5eaaf306062ff0db56e Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Wed, 20 Sep 2023 15:59:15 -0700 Subject: [PATCH 09/10] cleanup --- core/config/config.js | 6 +++--- core/config/validation.js | 4 ---- core/gather/base-artifacts.js | 7 ++++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/core/config/config.js b/core/config/config.js index 8611600a8095..d821fa3e0421 100644 --- a/core/config/config.js +++ b/core/config/config.js @@ -218,7 +218,7 @@ function overrideThrottlingWindows(settings) { * @param {LH.Gatherer.GatherMode} gatherMode * @param {LH.Config=} config * @param {LH.Flags=} flags - * @return {Promise<{resolvedConfig: LH.Config.ResolvedConfig, warnings: string[]}>} + * @return {Promise<{resolvedConfig: LH.Config.ResolvedConfig}>} */ async function initializeConfig(gatherMode, config, flags = {}) { const status = {msg: 'Initialize config', id: 'lh:config'}; @@ -244,13 +244,13 @@ async function initializeConfig(gatherMode, config, flags = {}) { settings, }; - const {warnings} = assertValidConfig(resolvedConfig); + assertValidConfig(resolvedConfig); resolvedConfig = filterConfigByGatherMode(resolvedConfig, gatherMode); resolvedConfig = filterConfigByExplicitFilters(resolvedConfig, settings); log.timeEnd(status); - return {resolvedConfig, warnings}; + return {resolvedConfig}; } /** diff --git a/core/config/validation.js b/core/config/validation.js index 98075d2712f6..1abf6637b539 100644 --- a/core/config/validation.js +++ b/core/config/validation.js @@ -213,7 +213,6 @@ function assertValidArtifacts(artifactDefns) { /** * @param {LH.Config.ResolvedConfig} resolvedConfig - * @return {{warnings: string[]}} */ function assertValidConfig(resolvedConfig) { assertValidArtifacts(resolvedConfig.artifacts || []); @@ -224,9 +223,6 @@ function assertValidConfig(resolvedConfig) { assertValidCategories(resolvedConfig.categories, resolvedConfig.audits, resolvedConfig.groups); assertValidSettings(resolvedConfig.settings); - - // Currently no warnings are available, but leaving this here if we ever decide to introduce any. - return {warnings: []}; } /** diff --git a/core/gather/base-artifacts.js b/core/gather/base-artifacts.js index eb0407132530..f9cb5469d244 100644 --- a/core/gather/base-artifacts.js +++ b/core/gather/base-artifacts.js @@ -64,15 +64,16 @@ function deduplicateWarnings(warnings) { * @return {LH.Artifacts} */ function finalizeArtifacts(baseArtifacts, gathererArtifacts) { - const warnings = baseArtifacts.LighthouseRunWarnings - .concat(getEnvironmentWarnings({settings: baseArtifacts.settings, baseArtifacts})); + baseArtifacts.LighthouseRunWarnings.push( + ...getEnvironmentWarnings({settings: baseArtifacts.settings, baseArtifacts}) + ); // Cast to remove the partial from gathererArtifacts. const artifacts = /** @type {LH.Artifacts} */ ({...baseArtifacts, ...gathererArtifacts}); // Set the post-run meta artifacts. artifacts.Timing = log.getTimeEntries(); - artifacts.LighthouseRunWarnings = deduplicateWarnings(warnings); + artifacts.LighthouseRunWarnings = deduplicateWarnings(baseArtifacts.LighthouseRunWarnings); if (artifacts.PageLoadError && !artifacts.URL.finalDisplayedUrl) { artifacts.URL.finalDisplayedUrl = artifacts.URL.requestedUrl || ''; From 7339569b9627b19f8d8ed27c0e799f3e933453ef Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Thu, 28 Sep 2023 12:59:45 -0700 Subject: [PATCH 10/10] fix other bug --- core/gather/navigation-runner.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/gather/navigation-runner.js b/core/gather/navigation-runner.js index ecebf160a55c..bd68b24d8edc 100644 --- a/core/gather/navigation-runner.js +++ b/core/gather/navigation-runner.js @@ -48,7 +48,13 @@ async function _setup({driver, resolvedConfig, requestor}) { // We can't trigger the navigation through user interaction if we reset the page before starting. if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) { + // Disable network monitor on the blank page to prevent it from picking up network requests and + // frame navigated events before the run starts. + await driver._networkMonitor?.disable(); + await gotoURL(driver, resolvedConfig.settings.blankPage, {waitUntil: ['navigated']}); + + await driver._networkMonitor?.enable(); } const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'navigation'});