diff --git a/build/build-bundle.js b/build/build-bundle.js index a78ee577f56e..5e46fc1a4560 100644 --- a/build/build-bundle.js +++ b/build/build-bundle.js @@ -190,7 +190,6 @@ async function buildBundle(entryPath, distPath, opts = {minify: true}) { // resolved eventually. plugins.partialLoaders.inlineFs({ verbose: Boolean(process.env.DEBUG), - ignorePaths: [require.resolve('puppeteer-core/lib/esm/puppeteer/common/Page.js')], }), plugins.partialLoaders.rmGetModuleDirectory, plugins.partialLoaders.replaceText({ diff --git a/build/gh-pages-app.js b/build/gh-pages-app.js index 7a7b088db39a..6ca6b157fd35 100644 --- a/build/gh-pages-app.js +++ b/build/gh-pages-app.js @@ -185,7 +185,7 @@ class GhPagesApp { if (this.preloadScripts.length) { const preloads = this.preloadScripts.map(fileName => - `` + `` ).join('\n'); const endHeadIndex = htmlSrc.indexOf(''); if (endHeadIndex === -1) { diff --git a/cli/test/smokehouse/test-definitions/dobetterweb.js b/cli/test/smokehouse/test-definitions/dobetterweb.js index 1f50e9e7151e..0c72f7273167 100644 --- a/cli/test/smokehouse/test-definitions/dobetterweb.js +++ b/cli/test/smokehouse/test-definitions/dobetterweb.js @@ -313,7 +313,7 @@ const expectations = { score: 0, }, 'no-document-write': { - score: 0, + score: 0.5, details: { items: { length: 3, @@ -350,7 +350,7 @@ const expectations = { }, }, 'uses-passive-event-listeners': { - score: 0, + score: 0.5, details: { items: { // Note: Originally this was 7 but M56 defaults document-level @@ -436,7 +436,7 @@ const expectations = { }, }, 'dom-size': { - score: 1, + score: null, numericValue: 153, details: { items: [ @@ -494,18 +494,6 @@ const expectations = { }], }, }, - { - // Support for this was added in M109 - // https://crbug.com/1350944 - _maxChromiumVersion: '108', - reason: 'Pages that have requested notifications permissions are not currently eligible for back/forward cache.', - failureType: 'Pending browser support', - subItems: { - items: [{ - frameUrl: 'http://localhost:10200/dobetterweb/dbw_tester.html', - }], - }, - }, { // This issue only appears in the DevTools runner for some reason. // TODO: Investigate why this doesn't happen on the CLI runner. @@ -575,7 +563,7 @@ const expectations = { }}}, }, 'largest-contentful-paint-element': { - score: null, + score: 0, displayValue: /\d+\xa0ms/, details: { items: [ diff --git a/cli/test/smokehouse/test-definitions/perf-diagnostics-third-party.js b/cli/test/smokehouse/test-definitions/perf-diagnostics-third-party.js index 2fe1fa3388e7..d546fe8dc3e9 100644 --- a/cli/test/smokehouse/test-definitions/perf-diagnostics-third-party.js +++ b/cli/test/smokehouse/test-definitions/perf-diagnostics-third-party.js @@ -21,7 +21,7 @@ const expectations = { finalDisplayedUrl: 'http://localhost:10200/perf/third-party.html', audits: { 'third-party-facades': { - score: 0, + score: 0.5, displayValue: '1 facade alternative available', details: { items: [ diff --git a/cli/test/smokehouse/test-definitions/perf-diagnostics-unsized-images.js b/cli/test/smokehouse/test-definitions/perf-diagnostics-unsized-images.js index a696a97a3d2b..26fa5a43b69f 100644 --- a/cli/test/smokehouse/test-definitions/perf-diagnostics-unsized-images.js +++ b/cli/test/smokehouse/test-definitions/perf-diagnostics-unsized-images.js @@ -21,7 +21,7 @@ const expectations = { finalDisplayedUrl: 'http://localhost:10200/perf/unsized-images.html', audits: { 'unsized-images': { - score: 0, + score: 0.5, details: { items: [ { diff --git a/cli/test/smokehouse/test-definitions/perf-fonts.js b/cli/test/smokehouse/test-definitions/perf-fonts.js index 460374b99b31..1c2b592c19c1 100644 --- a/cli/test/smokehouse/test-definitions/perf-fonts.js +++ b/cli/test/smokehouse/test-definitions/perf-fonts.js @@ -67,7 +67,7 @@ const expectations = { finalDisplayedUrl: 'http://localhost:10200/perf/fonts.html', audits: { 'font-display': { - score: 0, + score: 0.5, details: { items: [ { diff --git a/cli/test/smokehouse/test-definitions/perf-trace-elements.js b/cli/test/smokehouse/test-definitions/perf-trace-elements.js index 38bdf8fc2ca2..3c4642b30a1d 100644 --- a/cli/test/smokehouse/test-definitions/perf-trace-elements.js +++ b/cli/test/smokehouse/test-definitions/perf-trace-elements.js @@ -143,7 +143,7 @@ const expectations = { finalDisplayedUrl: 'http://localhost:10200/perf/trace-elements.html', audits: { 'largest-contentful-paint-element': { - score: null, + score: 0, displayValue: /\d+\xa0ms/, details: { items: { diff --git a/cli/test/smokehouse/test-definitions/redirects-single-server.js b/cli/test/smokehouse/test-definitions/redirects-single-server.js index ad4e729baa50..c24a024d6f1f 100644 --- a/cli/test/smokehouse/test-definitions/redirects-single-server.js +++ b/cli/test/smokehouse/test-definitions/redirects-single-server.js @@ -45,7 +45,7 @@ const expectations = { numericValue: '>=2000', }, 'redirects': { - score: 0.29, + score: 0, numericValue: '>=2000', details: { items: [ diff --git a/clients/lightrider/lightrider-entry.js b/clients/lightrider/lightrider-entry.js index 98aa867df07e..d3d106421103 100644 --- a/clients/lightrider/lightrider-entry.js +++ b/clients/lightrider/lightrider-entry.js @@ -9,8 +9,8 @@ import {Buffer} from 'buffer'; import log from 'lighthouse-logger'; -import {CDPBrowser} from 'puppeteer-core/lib/esm/puppeteer/common/Browser.js'; -import {Connection as PptrConnection} from 'puppeteer-core/lib/esm/puppeteer/common/Connection.js'; +import {CdpBrowser} from 'puppeteer-core/lib/esm/puppeteer/cdp/Browser.js'; +import {Connection as PptrConnection} from 'puppeteer-core/lib/esm/puppeteer/cdp/Connection.js'; import lighthouse, * as api from '../../core/index.js'; import {LighthouseError} from '../../core/lib/lh-error.js'; @@ -46,7 +46,7 @@ async function getPageFromConnection(connection) { const pptrConnection = new PptrConnection(mainTargetInfo.url, transport); - const browser = await CDPBrowser._create( + const browser = await CdpBrowser._create( 'chrome', pptrConnection, [] /* contextIds */, diff --git a/core/audits/audit.js b/core/audits/audit.js index b8acb6b96ee5..7b6766e9fb7d 100644 --- a/core/audits/audit.js +++ b/core/audits/audit.js @@ -49,6 +49,7 @@ class Audit { static get SCORING_MODES() { return { NUMERIC: 'numeric', + METRIC_SAVINGS: 'metricSavings', BINARY: 'binary', MANUAL: 'manual', INFORMATIVE: 'informative', @@ -321,7 +322,8 @@ class Audit { */ static _normalizeAuditScore(score, scoreDisplayMode, auditId) { if (scoreDisplayMode !== Audit.SCORING_MODES.BINARY && - scoreDisplayMode !== Audit.SCORING_MODES.NUMERIC) { + scoreDisplayMode !== Audit.SCORING_MODES.NUMERIC && + scoreDisplayMode !== Audit.SCORING_MODES.METRIC_SAVINGS) { return null; } @@ -363,6 +365,7 @@ class Audit { // Default to binary scoring. let scoreDisplayMode = audit.meta.scoreDisplayMode || Audit.SCORING_MODES.BINARY; + let score = product.score; // But override if product contents require it. if (product.errorMessage !== undefined) { @@ -371,9 +374,21 @@ class Audit { } else if (product.notApplicable) { // Audit was determined to not apply to the page. scoreDisplayMode = Audit.SCORING_MODES.NOT_APPLICABLE; + } else if (product.scoreDisplayMode) { + scoreDisplayMode = product.scoreDisplayMode; } - const score = Audit._normalizeAuditScore(product.score, scoreDisplayMode, audit.meta.id); + if (scoreDisplayMode === Audit.SCORING_MODES.METRIC_SAVINGS) { + if (score && score >= Util.PASS_THRESHOLD) { + score = 1; + } else if (Object.values(product.metricSavings || {}).some(v => v)) { + score = 0; + } else { + score = 0.5; + } + } + + score = Audit._normalizeAuditScore(score, scoreDisplayMode, audit.meta.id); let auditTitle = audit.meta.title; if (audit.meta.failureTitle) { diff --git a/core/audits/bf-cache.js b/core/audits/bf-cache.js index 6548098ed55f..55eefe73c1fe 100644 --- a/core/audits/bf-cache.js +++ b/core/audits/bf-cache.js @@ -61,6 +61,7 @@ class BFCache extends Audit { supportedModes: ['navigation', 'timespan'], guidanceLevel: 2, requiredArtifacts: ['BFCacheFailures'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } diff --git a/core/audits/bootup-time.js b/core/audits/bootup-time.js index de752266c567..a858517ea700 100644 --- a/core/audits/bootup-time.js +++ b/core/audits/bootup-time.js @@ -14,6 +14,7 @@ import {MainThreadTasks} from '../computed/main-thread-tasks.js'; import {getExecutionTimingsByURL} from '../lib/tracehouse/task-summary.js'; import {TBTImpactTasks} from '../computed/tbt-impact-tasks.js'; import {Sentry} from '../lib/sentry.js'; +import {Util} from '../../shared/util.js'; const UIStrings = { /** Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This descriptive title is shown to users when the amount is acceptable and no user action is required. */ @@ -47,7 +48,7 @@ class BootupTime extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 1, requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'], }; @@ -169,6 +170,8 @@ class BootupTime extends Audit { return { score, + scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined, + notApplicable: !results.length, numericValue: totalBootupTime, numericUnit: 'millisecond', displayValue: totalBootupTime > 0 ? diff --git a/core/audits/byte-efficiency/byte-efficiency-audit.js b/core/audits/byte-efficiency/byte-efficiency-audit.js index 3f1bc8b679cb..f7d2219fdf4b 100644 --- a/core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/core/audits/byte-efficiency/byte-efficiency-audit.js @@ -309,7 +309,7 @@ class ByteEfficiencyAudit extends Audit { displayValue, numericValue: wastedMs, numericUnit: 'millisecond', - score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs), + score: results.length ? 0 : 1, details, metricSavings, }; diff --git a/core/audits/byte-efficiency/duplicated-javascript.js b/core/audits/byte-efficiency/duplicated-javascript.js index 0b96293bd8e7..09d0fd916f1a 100644 --- a/core/audits/byte-efficiency/duplicated-javascript.js +++ b/core/audits/byte-efficiency/duplicated-javascript.js @@ -46,7 +46,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit { id: 'duplicated-javascript', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 2, requiredArtifacts: ['devtoolsLogs', 'traces', 'SourceMaps', 'Scripts', 'GatherContext', 'URL'], diff --git a/core/audits/byte-efficiency/efficient-animated-content.js b/core/audits/byte-efficiency/efficient-animated-content.js index 86e56a1b393d..2138d24a5513 100644 --- a/core/audits/byte-efficiency/efficient-animated-content.js +++ b/core/audits/byte-efficiency/efficient-animated-content.js @@ -36,7 +36,7 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit { id: 'efficient-animated-content', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['devtoolsLogs', 'traces', 'GatherContext', 'URL'], }; diff --git a/core/audits/byte-efficiency/legacy-javascript.js b/core/audits/byte-efficiency/legacy-javascript.js index 108e652e207d..434f16c94a2f 100644 --- a/core/audits/byte-efficiency/legacy-javascript.js +++ b/core/audits/byte-efficiency/legacy-javascript.js @@ -115,7 +115,7 @@ class LegacyJavascript extends ByteEfficiencyAudit { static get meta() { return { id: 'legacy-javascript', - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, description: str_(UIStrings.description), title: str_(UIStrings.title), guidanceLevel: 2, diff --git a/core/audits/byte-efficiency/modern-image-formats.js b/core/audits/byte-efficiency/modern-image-formats.js index 9effa094c6d6..ab6ef44c0cba 100644 --- a/core/audits/byte-efficiency/modern-image-formats.js +++ b/core/audits/byte-efficiency/modern-image-formats.js @@ -34,7 +34,7 @@ class ModernImageFormats extends ByteEfficiencyAudit { id: 'modern-image-formats', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['OptimizedImages', 'devtoolsLogs', 'traces', 'URL', 'GatherContext', 'ImageElements'], diff --git a/core/audits/byte-efficiency/offscreen-images.js b/core/audits/byte-efficiency/offscreen-images.js index 12f3d56ea847..8acb5edd7890 100644 --- a/core/audits/byte-efficiency/offscreen-images.js +++ b/core/audits/byte-efficiency/offscreen-images.js @@ -48,7 +48,7 @@ class OffscreenImages extends ByteEfficiencyAudit { id: 'offscreen-images', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, supportedModes: ['navigation'], guidanceLevel: 2, requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext', 'devtoolsLogs', diff --git a/core/audits/byte-efficiency/render-blocking-resources.js b/core/audits/byte-efficiency/render-blocking-resources.js index e0af6960ef8e..2b350165944c 100644 --- a/core/audits/byte-efficiency/render-blocking-resources.js +++ b/core/audits/byte-efficiency/render-blocking-resources.js @@ -12,7 +12,6 @@ import {Audit} from '../audit.js'; import * as i18n from '../../lib/i18n/i18n.js'; import {BaseNode} from '../../lib/dependency-graph/base-node.js'; -import {ByteEfficiencyAudit} from './byte-efficiency-audit.js'; import {UnusedCSS} from '../../computed/unused-css.js'; import {NetworkRequest} from '../../lib/network-request.js'; import {ProcessedNavigation} from '../../computed/processed-navigation.js'; @@ -113,7 +112,7 @@ class RenderBlockingResources extends Audit { id: 'render-blocking-resources', title: str_(UIStrings.title), supportedModes: ['navigation'], - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, description: str_(UIStrings.description), guidanceLevel: 2, // TODO: look into adding an `optionalArtifacts` property that captures the non-required nature @@ -296,7 +295,7 @@ class RenderBlockingResources extends Audit { return { displayValue, - score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs), + score: results.length ? 0 : 1, numericValue: wastedMs, numericUnit: 'millisecond', details, diff --git a/core/audits/byte-efficiency/total-byte-weight.js b/core/audits/byte-efficiency/total-byte-weight.js index 6d04cb90bdf7..ab6d6289f39b 100644 --- a/core/audits/byte-efficiency/total-byte-weight.js +++ b/core/audits/byte-efficiency/total-byte-weight.js @@ -8,6 +8,7 @@ import {Audit} from '../audit.js'; import * as i18n from '../../lib/i18n/i18n.js'; import {NetworkRequest} from '../../lib/network-request.js'; import {NetworkRecords} from '../../computed/network-records.js'; +import {Util} from '../../../shared/util.js'; const UIStrings = { /** Title of a diagnostic audit that provides detail on large network resources required during page load. 'Payloads' is roughly equivalent to 'resources'. This descriptive title is shown to users when the amount is acceptable and no user action is required. */ @@ -34,7 +35,7 @@ class TotalByteWeight extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 1, requiredArtifacts: ['devtoolsLogs'], }; @@ -98,6 +99,7 @@ class TotalByteWeight extends Audit { return { score, + scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined, numericValue: totalBytes, numericUnit: 'byte', displayValue: str_(UIStrings.displayValue, {totalBytes}), diff --git a/core/audits/byte-efficiency/unminified-css.js b/core/audits/byte-efficiency/unminified-css.js index 530df95af1d8..4d9935555348 100644 --- a/core/audits/byte-efficiency/unminified-css.js +++ b/core/audits/byte-efficiency/unminified-css.js @@ -34,7 +34,7 @@ class UnminifiedCSS extends ByteEfficiencyAudit { id: 'unminified-css', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['CSSUsage', 'devtoolsLogs', 'traces', 'URL', 'GatherContext'], }; diff --git a/core/audits/byte-efficiency/unminified-javascript.js b/core/audits/byte-efficiency/unminified-javascript.js index 7c7e408e2d0f..f25045961371 100644 --- a/core/audits/byte-efficiency/unminified-javascript.js +++ b/core/audits/byte-efficiency/unminified-javascript.js @@ -42,7 +42,7 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit { id: 'unminified-javascript', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['Scripts', 'devtoolsLogs', 'traces', 'GatherContext', 'URL'], }; diff --git a/core/audits/byte-efficiency/unused-css-rules.js b/core/audits/byte-efficiency/unused-css-rules.js index df1488a35e1c..6c3eb9f2a7a4 100644 --- a/core/audits/byte-efficiency/unused-css-rules.js +++ b/core/audits/byte-efficiency/unused-css-rules.js @@ -32,7 +32,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit { id: 'unused-css-rules', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 2, requiredArtifacts: ['CSSUsage', 'URL', 'devtoolsLogs', 'traces', 'GatherContext'], }; diff --git a/core/audits/byte-efficiency/unused-javascript.js b/core/audits/byte-efficiency/unused-javascript.js index c14c648f7f04..cab06ae9bccf 100644 --- a/core/audits/byte-efficiency/unused-javascript.js +++ b/core/audits/byte-efficiency/unused-javascript.js @@ -66,7 +66,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit { id: 'unused-javascript', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 2, requiredArtifacts: ['JsUsage', 'Scripts', 'SourceMaps', 'GatherContext', 'devtoolsLogs', 'traces', 'URL'], diff --git a/core/audits/byte-efficiency/uses-long-cache-ttl.js b/core/audits/byte-efficiency/uses-long-cache-ttl.js index 06ea079e76b1..f850f3220cd5 100644 --- a/core/audits/byte-efficiency/uses-long-cache-ttl.js +++ b/core/audits/byte-efficiency/uses-long-cache-ttl.js @@ -44,7 +44,7 @@ class CacheHeaders extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['devtoolsLogs'], }; @@ -266,11 +266,6 @@ class CacheHeaders extends Audit { a.url.localeCompare(b.url); }); - const score = Audit.computeLogNormalScore( - {p10: context.options.p10, median: context.options.median}, - totalWastedBytes - ); - /** @type {LH.Audit.Details.Table['headings']} */ const headings = [ {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)}, @@ -285,7 +280,7 @@ class CacheHeaders extends Audit { {wastedBytes: totalWastedBytes, sortedBy: ['totalBytes'], skipSumming: ['cacheLifetimeMs']}); return { - score, + score: results.length ? 0 : 1, numericValue: totalWastedBytes, numericUnit: 'byte', displayValue: str_(UIStrings.displayValue, {itemCount: results.length}), diff --git a/core/audits/byte-efficiency/uses-optimized-images.js b/core/audits/byte-efficiency/uses-optimized-images.js index 55e63c19bda9..1c5c347f3f72 100644 --- a/core/audits/byte-efficiency/uses-optimized-images.js +++ b/core/audits/byte-efficiency/uses-optimized-images.js @@ -34,7 +34,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { id: 'uses-optimized-images', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 2, requiredArtifacts: ['OptimizedImages', 'ImageElements', 'GatherContext', 'devtoolsLogs', 'traces', 'URL'], diff --git a/core/audits/byte-efficiency/uses-responsive-images-snapshot.js b/core/audits/byte-efficiency/uses-responsive-images-snapshot.js index aac8475ba54d..0fbdc3c4f9b1 100644 --- a/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +++ b/core/audits/byte-efficiency/uses-responsive-images-snapshot.js @@ -42,6 +42,7 @@ class UsesResponsiveImagesSnapshot extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.description), + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, supportedModes: ['snapshot'], guidanceLevel: 2, requiredArtifacts: ['ImageElements', 'ViewportDimensions'], diff --git a/core/audits/byte-efficiency/uses-responsive-images.js b/core/audits/byte-efficiency/uses-responsive-images.js index 4daee0be1973..7d1aebd85750 100644 --- a/core/audits/byte-efficiency/uses-responsive-images.js +++ b/core/audits/byte-efficiency/uses-responsive-images.js @@ -45,7 +45,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { id: 'uses-responsive-images', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 2, requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext', 'devtoolsLogs', 'traces', 'URL'], diff --git a/core/audits/byte-efficiency/uses-text-compression.js b/core/audits/byte-efficiency/uses-text-compression.js index 982811c38e78..6821453d6e54 100644 --- a/core/audits/byte-efficiency/uses-text-compression.js +++ b/core/audits/byte-efficiency/uses-text-compression.js @@ -36,7 +36,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { id: 'uses-text-compression', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC, + scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['ResponseCompression', 'GatherContext', 'devtoolsLogs', 'traces', 'URL'], }; diff --git a/core/audits/csp-xss.js b/core/audits/csp-xss.js index 7e1a3d329c56..3bbdf4414fee 100644 --- a/core/audits/csp-xss.js +++ b/core/audits/csp-xss.js @@ -21,7 +21,7 @@ const UIStrings = { /** Summary text for the results of a Lighthouse audit that evaluates the security of a page's CSP. This is displayed if no CSP is being enforced. "CSP" stands for "Content Security Policy". "CSP" does not need to be translated. */ noCsp: 'No CSP found in enforcement mode', /** Message shown when one or more CSPs are defined in a tag. Shown in a table with a list of other CSP bypasses and warnings. "CSP" stands for "Content Security Policy". "CSP" and "HTTP" do not need to be translated. */ - metaTagMessage: 'The page contains a CSP defined in a tag. ' + + metaTagMessage: 'The page contains a CSP defined in a `` tag. ' + 'Consider moving the CSP to an HTTP header or ' + 'defining another strict CSP in an HTTP header.', /** Label for a column in a data table; entries will be a directive of a CSP. "CSP" stands for "Content Security Policy". */ diff --git a/core/audits/dobetterweb/dom-size.js b/core/audits/dobetterweb/dom-size.js index 5be4ed83e899..e6d119ac45e9 100644 --- a/core/audits/dobetterweb/dom-size.js +++ b/core/audits/dobetterweb/dom-size.js @@ -14,6 +14,7 @@ import {Audit} from '../audit.js'; import * as i18n from '../../lib/i18n/i18n.js'; import {TBTImpactTasks} from '../../computed/tbt-impact-tasks.js'; +import {Util} from '../../../shared/util.js'; const UIStrings = { /** Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This descriptive title is shown to users when the amount is acceptable and no user action is required. */ @@ -53,7 +54,7 @@ class DOMSize extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 1, requiredArtifacts: ['DOMStats', 'URL', 'GatherContext'], __internalOptionalArtifacts: ['traces', 'devtoolsLogs'], @@ -167,6 +168,7 @@ class DOMSize extends Audit { return { score, + scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined, numericValue: stats.totalBodyElements, numericUnit: 'element', displayValue: str_(UIStrings.displayValue, {itemCount: stats.totalBodyElements}), diff --git a/core/audits/dobetterweb/no-document-write.js b/core/audits/dobetterweb/no-document-write.js index 0d1f21a19fd2..d6e5c60f495e 100644 --- a/core/audits/dobetterweb/no-document-write.js +++ b/core/audits/dobetterweb/no-document-write.js @@ -55,6 +55,7 @@ class NoDocWriteAudit extends ViolationAudit { description: str_(UIStrings.description), guidanceLevel: 2, requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'], + scoreDisplayMode: ViolationAudit.SCORING_MODES.METRIC_SAVINGS, }; } diff --git a/core/audits/dobetterweb/uses-http2.js b/core/audits/dobetterweb/uses-http2.js index 135a60ac296e..1102c0aa1fa8 100644 --- a/core/audits/dobetterweb/uses-http2.js +++ b/core/audits/dobetterweb/uses-http2.js @@ -15,7 +15,6 @@ import {Audit} from '../audit.js'; import {EntityClassification} from '../../computed/entity-classification.js'; import UrlUtils from '../../lib/url-utils.js'; -import {ByteEfficiencyAudit} from '../byte-efficiency/byte-efficiency-audit.js'; import {LanternInteractive} from '../../computed/metrics/lantern-interactive.js'; import {NetworkRequest} from '../../lib/network-request.js'; import {NetworkRecords} from '../../computed/network-records.js'; @@ -61,7 +60,7 @@ class UsesHTTP2Audit extends Audit { id: 'uses-http2', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, supportedModes: ['timespan', 'navigation'], requiredArtifacts: ['URL', 'devtoolsLogs', 'traces', 'GatherContext'], @@ -300,7 +299,7 @@ class UsesHTTP2Audit extends Audit { displayValue, numericValue: wastedMsTti, numericUnit: 'millisecond', - score: ByteEfficiencyAudit.scoreForWastedMs(wastedMsTti), + score: resources.length ? 0 : 1, details, metricSavings: {LCP: wasteLcp.savings, FCP: wasteFcp.savings}, }; diff --git a/core/audits/dobetterweb/uses-passive-event-listeners.js b/core/audits/dobetterweb/uses-passive-event-listeners.js index 04ca00240e67..279000d2ecdf 100644 --- a/core/audits/dobetterweb/uses-passive-event-listeners.js +++ b/core/audits/dobetterweb/uses-passive-event-listeners.js @@ -38,6 +38,7 @@ class PassiveEventsAudit extends ViolationAudit { description: str_(UIStrings.description), guidanceLevel: 3, requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'], + scoreDisplayMode: ViolationAudit.SCORING_MODES.METRIC_SAVINGS, }; } diff --git a/core/audits/font-display.js b/core/audits/font-display.js index 6ea69d9fad10..232e23ebfbcd 100644 --- a/core/audits/font-display.js +++ b/core/audits/font-display.js @@ -51,6 +51,7 @@ class FontDisplay extends Audit { supportedModes: ['navigation'], guidanceLevel: 3, requiredArtifacts: ['devtoolsLogs', 'CSSUsage', 'URL'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } diff --git a/core/audits/installable-manifest.js b/core/audits/installable-manifest.js index d6914ff74892..415e98ed43d1 100644 --- a/core/audits/installable-manifest.js +++ b/core/audits/installable-manifest.js @@ -39,9 +39,9 @@ const UIStrings = { /** Error message explaining that the provided manifest URL is invalid. */ 'start-url-not-valid': `Manifest start URL is not valid`, /** Error message explaining that the provided manifest does not contain a name or short_name field. */ - 'manifest-missing-name-or-short-name': `Manifest does not contain a 'name' or 'short_name' field`, + 'manifest-missing-name-or-short-name': 'Manifest does not contain a `name` or `short_name` field', /** Error message explaining that the manifest display property must be one of 'standalone', 'fullscreen', or 'minimal-ui'. */ - 'manifest-display-not-supported': `Manifest 'display' property must be one of 'standalone', 'fullscreen', or 'minimal-ui'`, + 'manifest-display-not-supported': 'Manifest `display` property must be one of `standalone`, `fullscreen`, or `minimal-ui`', /** Error message explaining that the manifest could not be fetched, might be empty, or could not be parsed. */ 'manifest-empty': `Manifest could not be fetched, is empty, or could not be parsed`, /** diff --git a/core/audits/largest-contentful-paint-element.js b/core/audits/largest-contentful-paint-element.js index e911762979ad..3ba21b75ce9d 100644 --- a/core/audits/largest-contentful-paint-element.js +++ b/core/audits/largest-contentful-paint-element.js @@ -46,7 +46,7 @@ class LargestContentfulPaintElement extends Audit { id: 'largest-contentful-paint-element', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 1, supportedModes: ['navigation'], requiredArtifacts: @@ -162,7 +162,8 @@ class LargestContentfulPaintElement extends Audit { const lcpSavings = Math.max(0, metricLcp - idealLcp); return { - score: 1, + score: lcpSavings ? 0 : 1, + scoreDisplayMode: lcpSavings ? undefined : Audit.SCORING_MODES.INFORMATIVE, displayValue, details, metricSavings: { diff --git a/core/audits/layout-shift-elements.js b/core/audits/layout-shift-elements.js index 03c9c2c1c2be..22121acdd4dd 100644 --- a/core/audits/layout-shift-elements.js +++ b/core/audits/layout-shift-elements.js @@ -5,7 +5,8 @@ import {Audit} from './audit.js'; import * as i18n from '../lib/i18n/i18n.js'; -import {CumulativeLayoutShift} from '../computed/metrics/cumulative-layout-shift.js'; +import {CumulativeLayoutShift as CumulativeLayoutShiftComputed} from '../computed/metrics/cumulative-layout-shift.js'; +import CumulativeLayoutShift from './metrics/cumulative-layout-shift.js'; const UIStrings = { /** Descriptive title of a diagnostic audit that provides up to the top five elements contributing to Cumulative Layout Shift. */ @@ -27,7 +28,7 @@ class LayoutShiftElements extends Audit { id: 'layout-shift-elements', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 2, requiredArtifacts: ['traces', 'TraceElements'], }; @@ -64,10 +65,13 @@ class LayoutShiftElements extends Audit { } const {cumulativeLayoutShift: clsSavings} = - await CumulativeLayoutShift.request(artifacts.traces[Audit.DEFAULT_PASS], context); + await CumulativeLayoutShiftComputed.request(artifacts.traces[Audit.DEFAULT_PASS], context); + + const passed = clsSavings <= CumulativeLayoutShift.defaultOptions.p10; return { - score: 1, + score: passed ? 1 : 0, + scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined, metricSavings: { CLS: clsSavings, }, diff --git a/core/audits/lcp-lazy-loaded.js b/core/audits/lcp-lazy-loaded.js index fe5032599800..2e4c467cd102 100644 --- a/core/audits/lcp-lazy-loaded.js +++ b/core/audits/lcp-lazy-loaded.js @@ -33,6 +33,7 @@ class LargestContentfulPaintLazyLoaded extends Audit { failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), supportedModes: ['navigation'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 3, requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements', 'traces', 'devtoolsLogs', 'GatherContext', 'URL'], diff --git a/core/audits/long-tasks.js b/core/audits/long-tasks.js index ae933c6b9e07..63727312f8a4 100644 --- a/core/audits/long-tasks.js +++ b/core/audits/long-tasks.js @@ -11,6 +11,7 @@ import {MainThreadTasks} from '../computed/main-thread-tasks.js'; import {PageDependencyGraph} from '../computed/page-dependency-graph.js'; import {LoadSimulator} from '../computed/load-simulator.js'; import {getJavaScriptURLs, getAttributableURLForTask} from '../lib/tracehouse/task-summary.js'; +import {TotalBlockingTime} from '../computed/metrics/total-blocking-time.js'; /** We don't always have timing data for short tasks, if we're missing timing data. Treat it as though it were 0ms. */ const DEFAULT_TIMING = {startTime: 0, endTime: 0, duration: 0}; @@ -66,8 +67,8 @@ class LongTasks extends Audit { scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE, title: str_(UIStrings.title), description: str_(UIStrings.description), + requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'], guidanceLevel: 1, - requiredArtifacts: ['traces', 'devtoolsLogs', 'URL'], }; } @@ -180,6 +181,9 @@ class LongTasks extends Audit { const devtoolsLog = artifacts.devtoolsLogs[LongTasks.DEFAULT_PASS]; const networkRecords = await NetworkRecords.request(devtoolsLog, context); + const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context); + const tbtResult = await TotalBlockingTime.request(metricComputationData, context); + /** @type {Map|undefined} */ let taskTimingsByEvent; @@ -245,6 +249,9 @@ class LongTasks extends Audit { notApplicable: results.length === 0, details: tableDetails, displayValue, + metricSavings: { + TBT: tbtResult.timing, + }, }; } } diff --git a/core/audits/mainthread-work-breakdown.js b/core/audits/mainthread-work-breakdown.js index d1621132bcbd..63a3bbc4baef 100644 --- a/core/audits/mainthread-work-breakdown.js +++ b/core/audits/mainthread-work-breakdown.js @@ -16,6 +16,7 @@ import * as i18n from '../lib/i18n/i18n.js'; import {MainThreadTasks} from '../computed/main-thread-tasks.js'; import {TotalBlockingTime} from '../computed/metrics/total-blocking-time.js'; import {Sentry} from '../lib/sentry.js'; +import {Util} from '../../shared/util.js'; const UIStrings = { /** Title of a diagnostic audit that provides detail on the main thread work the browser did to load the page. This descriptive title is shown to users when the amount is acceptable and no user action is required. */ @@ -44,7 +45,7 @@ class MainThreadWorkBreakdown extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, guidanceLevel: 1, requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'], }; @@ -141,6 +142,7 @@ class MainThreadWorkBreakdown extends Audit { return { score, + scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined, numericValue: totalExecutionTime, numericUnit: 'millisecond', displayValue: str_(i18n.UIStrings.seconds, {timeInMs: totalExecutionTime}), diff --git a/core/audits/prioritize-lcp-image.js b/core/audits/prioritize-lcp-image.js index 0b2988227627..78e716b12877 100644 --- a/core/audits/prioritize-lcp-image.js +++ b/core/audits/prioritize-lcp-image.js @@ -10,7 +10,6 @@ import {NetworkRequest} from '../lib/network-request.js'; import {MainResource} from '../computed/main-resource.js'; import {LanternLargestContentfulPaint} from '../computed/metrics/lantern-largest-contentful-paint.js'; import {LoadSimulator} from '../computed/load-simulator.js'; -import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js'; import {LCPImageRecord} from '../computed/lcp-image-record.js'; const UIStrings = { @@ -40,7 +39,7 @@ class PrioritizeLcpImage extends Audit { supportedModes: ['navigation'], guidanceLevel: 3, requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements'], - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } @@ -282,7 +281,7 @@ class PrioritizeLcpImage extends Audit { } return { - score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs), + score: results.length ? 0 : 1, numericValue: wastedMs, numericUnit: 'millisecond', displayValue: wastedMs ? str_(i18n.UIStrings.displayValueMsSavings, {wastedMs}) : '', diff --git a/core/audits/redirects.js b/core/audits/redirects.js index 3407a2f8111d..d3bb075696d0 100644 --- a/core/audits/redirects.js +++ b/core/audits/redirects.js @@ -5,7 +5,6 @@ */ import {Audit} from './audit.js'; -import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js'; import * as i18n from '../lib/i18n/i18n.js'; import {ProcessedTrace} from '../computed/processed-trace.js'; import {NetworkRecords} from '../computed/network-records.js'; @@ -29,7 +28,7 @@ class Redirects extends Audit { id: 'redirects', title: str_(UIStrings.title), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, supportedModes: ['navigation'], guidanceLevel: 2, requiredArtifacts: ['URL', 'GatherContext', 'devtoolsLogs', 'traces'], @@ -145,7 +144,7 @@ class Redirects extends Audit { {overallSavingsMs: totalWastedMs}); return { - score: ByteEfficiencyAudit.scoreForWastedMs(totalWastedMs), + score: tableRows.length ? 0 : 1, numericValue: totalWastedMs, numericUnit: 'millisecond', displayValue: totalWastedMs ? diff --git a/core/audits/server-response-time.js b/core/audits/server-response-time.js index 2d3229238ba5..08292b9539ba 100644 --- a/core/audits/server-response-time.js +++ b/core/audits/server-response-time.js @@ -39,6 +39,7 @@ class ServerResponseTime extends Audit { supportedModes: ['navigation'], guidanceLevel: 1, requiredArtifacts: ['devtoolsLogs', 'URL', 'GatherContext'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } @@ -91,6 +92,7 @@ class ServerResponseTime extends Audit { numericValue: responseTime, numericUnit: 'millisecond', score: Number(passed), + scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined, displayValue, details, metricSavings: { diff --git a/core/audits/third-party-facades.js b/core/audits/third-party-facades.js index 79089df20787..241a9fc28aec 100644 --- a/core/audits/third-party-facades.js +++ b/core/audits/third-party-facades.js @@ -88,6 +88,7 @@ class ThirdPartyFacades extends Audit { supportedModes: ['navigation'], guidanceLevel: 3, requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } diff --git a/core/audits/third-party-summary.js b/core/audits/third-party-summary.js index 3a0a0c8ae260..9fb32436eb6b 100644 --- a/core/audits/third-party-summary.js +++ b/core/audits/third-party-summary.js @@ -75,6 +75,7 @@ class ThirdPartySummary extends Audit { failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), guidanceLevel: 1, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'], }; } @@ -257,8 +258,11 @@ class ThirdPartySummary extends Audit { const details = Audit.makeTableDetails(headings, results, {...overallSummary, isEntityGrouped: true}); + const passed = overallSummary.wastedMs <= PASS_THRESHOLD_IN_MS; + return { - score: Number(overallSummary.wastedMs <= PASS_THRESHOLD_IN_MS), + score: Number(passed), + scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined, displayValue: str_(UIStrings.displayValue, { timeInMs: overallSummary.wastedMs, }), diff --git a/core/audits/unsized-images.js b/core/audits/unsized-images.js index 5cfa9d064ae9..62a8d7aedc6b 100644 --- a/core/audits/unsized-images.js +++ b/core/audits/unsized-images.js @@ -36,6 +36,7 @@ class UnsizedImages extends Audit { description: str_(UIStrings.description), guidanceLevel: 3, requiredArtifacts: ['ImageElements'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } @@ -153,7 +154,6 @@ class UnsizedImages extends Audit { return { score: unsizedImages.length > 0 ? 0 : 1, - notApplicable: images.length === 0, details: Audit.makeTableDetails(headings, unsizedImages), metricSavings: { CLS: 0, diff --git a/core/audits/uses-rel-preconnect.js b/core/audits/uses-rel-preconnect.js index 6f4b405dc77f..c6908fd4c0e9 100644 --- a/core/audits/uses-rel-preconnect.js +++ b/core/audits/uses-rel-preconnect.js @@ -5,7 +5,6 @@ */ import {Audit} from './audit.js'; -import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js'; import UrlUtils from '../lib/url-utils.js'; import * as i18n from '../lib/i18n/i18n.js'; import {NetworkRecords} from '../computed/network-records.js'; @@ -64,7 +63,7 @@ class UsesRelPreconnectAudit extends Audit { supportedModes: ['navigation'], guidanceLevel: 3, requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'LinkElements'], - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } @@ -268,7 +267,7 @@ class UsesRelPreconnectAudit extends Audit { {overallSavingsMs: maxWastedLcp, sortedBy: ['wastedMs']}); return { - score: ByteEfficiencyAudit.scoreForWastedMs(maxWastedLcp), + score: results.length ? 0 : 1, numericValue: maxWastedLcp, numericUnit: 'millisecond', displayValue: maxWastedLcp ? diff --git a/core/audits/uses-rel-preload.js b/core/audits/uses-rel-preload.js index e66d42b28027..8582616be956 100644 --- a/core/audits/uses-rel-preload.js +++ b/core/audits/uses-rel-preload.js @@ -7,7 +7,6 @@ import UrlUtils from '../lib/url-utils.js'; import {NetworkRequest} from '../lib/network-request.js'; import {Audit} from './audit.js'; -import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js'; import {CriticalRequestChains} from '../computed/critical-request-chains.js'; import * as i18n from '../lib/i18n/i18n.js'; import {MainResource} from '../computed/main-resource.js'; @@ -45,7 +44,7 @@ class UsesRelPreloadAudit extends Audit { supportedModes: ['navigation'], guidanceLevel: 3, requiredArtifacts: ['devtoolsLogs', 'traces', 'URL'], - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } @@ -244,7 +243,7 @@ class UsesRelPreloadAudit extends Audit { {overallSavingsMs: wastedMs, sortedBy: ['wastedMs']}); return { - score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs), + score: results.length ? 0 : 1, numericValue: wastedMs, numericUnit: 'millisecond', displayValue: wastedMs ? diff --git a/core/audits/viewport.js b/core/audits/viewport.js index 0c0a27e035f5..108ecd7f4dde 100644 --- a/core/audits/viewport.js +++ b/core/audits/viewport.js @@ -36,6 +36,7 @@ class Viewport extends Audit { description: str_(UIStrings.description), guidanceLevel: 3, requiredArtifacts: ['MetaElements'], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, }; } diff --git a/core/audits/work-during-interaction.js b/core/audits/work-during-interaction.js index cfa1fb5b48b6..92f8cf60bf7c 100644 --- a/core/audits/work-during-interaction.js +++ b/core/audits/work-during-interaction.js @@ -58,7 +58,7 @@ class WorkDuringInteraction extends Audit { title: str_(UIStrings.title), failureTitle: str_(UIStrings.failureTitle), description: str_(UIStrings.description), - scoreDisplayMode: Audit.SCORING_MODES.NUMERIC, + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, supportedModes: ['timespan'], guidanceLevel: 1, requiredArtifacts: ['traces', 'devtoolsLogs', 'TraceElements'], @@ -273,8 +273,12 @@ class WorkDuringInteraction extends Audit { const duration = interactionEvent.args.data.duration; const displayValue = str_(UIStrings.displayValue, {timeInMs: duration, interactionType}); + + const passed = duration < InteractionToNextPaint.defaultOptions.p10; + return { - score: duration < InteractionToNextPaint.defaultOptions.p10 ? 1 : 0, + score: passed ? 1 : 0, + scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined, displayValue, details: { type: 'list', diff --git a/core/gather/session.js b/core/gather/session.js index 81d1f3aea40f..410af08b08c2 100644 --- a/core/gather/session.js +++ b/core/gather/session.js @@ -29,6 +29,7 @@ class ProtocolSession extends CrdpEventEmitter { this._nextProtocolTimeout = undefined; this._handleProtocolEvent = this._handleProtocolEvent.bind(this); + // @ts-expect-error Puppeteer expects the handler params to be type `unknown` this._cdpSession.on('*', this._handleProtocolEvent); } @@ -112,6 +113,7 @@ class ProtocolSession extends CrdpEventEmitter { * @return {Promise} */ async dispose() { + // @ts-expect-error Puppeteer expects the handler params to be type `unknown` this._cdpSession.off('*', this._handleProtocolEvent); await this._cdpSession.detach(); } diff --git a/core/lib/csp-evaluator.js b/core/lib/csp-evaluator.js index b3b7b4928d09..4f4bec138be5 100644 --- a/core/lib/csp-evaluator.js +++ b/core/lib/csp-evaluator.js @@ -19,31 +19,31 @@ import {isIcuMessage} from '../../shared/localization/format.js'; const UIStrings = { /** Message shown when a CSP does not have a base-uri directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "base-uri", "'none'", and "'self'" do not need to be translated. */ - missingBaseUri: 'Missing base-uri allows injected tags to set the base URL for all ' + + missingBaseUri: 'Missing `base-uri` allows injected `` tags to set the base URL for all ' + 'relative URLs (e.g. scripts) to an attacker controlled domain. ' + - 'Consider setting base-uri to \'none\' or \'self\'.', + 'Consider setting `base-uri` to `\'none\'` or `\'self\'`.', /** Message shown when a CSP does not have a script-src directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "script-src" does not need to be translated. */ - missingScriptSrc: 'script-src directive is missing. ' + + missingScriptSrc: '`script-src` directive is missing. ' + 'This can allow the execution of unsafe scripts.', /** Message shown when a CSP does not have a script-src directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "object-src" and "'none'" do not need to be translated. */ - missingObjectSrc: 'Missing object-src allows the injection of plugins ' + - 'that execute unsafe scripts. Consider setting object-src to \'none\' if you can.', + missingObjectSrc: 'Missing `object-src` allows the injection of plugins ' + + 'that execute unsafe scripts. Consider setting `object-src` to `\'none\'` if you can.', /** Message shown when a CSP uses a domain allowlist to filter out malicious scripts. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "CSP", "'strict-dynamic'", "nonces", and "hashes" do not need to be translated. "allowlists" can be interpreted as "whitelist". */ strictDynamic: 'Host allowlists can frequently be bypassed. Consider using ' + - 'CSP nonces or hashes instead, along with \'strict-dynamic\' if necessary.', + 'CSP nonces or hashes instead, along with `\'strict-dynamic\'` if necessary.', /** Message shown when a CSP allows inline scripts to be run in the page. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "CSP", "'unsafe-inline'", "nonces", and "hashes" do not need to be translated. */ - unsafeInline: '\'unsafe-inline\' allows the execution of unsafe in-page scripts ' + + unsafeInline: '`\'unsafe-inline\'` allows the execution of unsafe in-page scripts ' + 'and event handlers. Consider using CSP nonces or hashes to allow scripts individually.', /** Message shown when a CSP is not backwards compatible with browsers that do not support CSP nonces/hashes. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "'unsafe-inline'", "nonces", and "hashes" do not need to be translated. */ - unsafeInlineFallback: 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' + + unsafeInlineFallback: 'Consider adding `\'unsafe-inline\'` (ignored by browsers supporting ' + 'nonces/hashes) to be backward compatible with older browsers.', /** Message shown when a CSP is not backwards compatible with browsers that do not support the 'strict-dynamic' keyword. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "http:", "https:", and "'strict-dynamic'" do not need to be translated. */ allowlistFallback: 'Consider adding https: and http: URL schemes (ignored by browsers ' + - 'supporting \'strict-dynamic\') to be backward compatible with older browsers.', + 'supporting `\'strict-dynamic\'`) to be backward compatible with older browsers.', /** Message shown when a CSP only provides a reporting destination through the report-to directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "report-to", "report-uri", and "Chromium" do not need to be translated. */ reportToOnly: 'The reporting destination is only configured via the report-to directive. ' + 'This directive is only supported in Chromium-based browsers so it is ' + - 'recommended to also use a report-uri directive.', + 'recommended to also use a `report-uri` directive.', /** Message shown when a CSP does not provide a reporting destination. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "CSP" does not need to be translated. */ reportingDestinationMissing: 'No CSP configures a reporting destination. ' + 'This makes it difficult to maintain the CSP over time and monitor for any breakages.', @@ -65,13 +65,13 @@ const UIStrings = { */ unknownKeyword: '{keyword} seems to be an invalid keyword.', /** Message shown when a CSP uses the deprecated reflected-xss directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "reflected-xss", "CSP2" and "X-XSS-Protection" do not need to be translated. */ - deprecatedReflectedXSS: 'reflected-xss is deprecated since CSP2. ' + + deprecatedReflectedXSS: '`reflected-xss` is deprecated since CSP2. ' + 'Please, use the X-XSS-Protection header instead.', /** Message shown when a CSP uses the deprecated referrer directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "referrer", "CSP2" and "Referrer-Policy" do not need to be translated. */ - deprecatedReferrer: 'referrer is deprecated since CSP2. ' + + deprecatedReferrer: '`referrer` is deprecated since CSP2. ' + 'Please, use the Referrer-Policy header instead.', /** Message shown when a CSP uses the deprecated disown-opener directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "disown-opener", "CSP3" and "Cross-Origin-Opener-Policy" do not need to be translated. */ - deprecatedDisownOpener: 'disown-opener is deprecated since CSP3. ' + + deprecatedDisownOpener: '`disown-opener` is deprecated since CSP3. ' + 'Please, use the Cross-Origin-Opener-Policy header instead.', /** * @description Message shown when a CSP wildcard allows unsafe scripts to be run in the page. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". diff --git a/core/scripts/pptr-run-devtools.js b/core/scripts/pptr-run-devtools.js index 3b198d40084a..00ab1e6f1e74 100644 --- a/core/scripts/pptr-run-devtools.js +++ b/core/scripts/pptr-run-devtools.js @@ -28,7 +28,7 @@ import {fileURLToPath} from 'url'; import * as puppeteer from 'puppeteer-core'; import yargs from 'yargs'; import * as yargsHelpers from 'yargs/helpers'; -import {getChromePath} from 'chrome-launcher'; +import {launch} from 'chrome-launcher'; import esMain from 'es-main'; import {parseChromeFlags} from '../../cli/run.js'; @@ -145,33 +145,26 @@ function addSniffer(receiver, methodName, override) { } async function waitForLighthouseReady() { + // @ts-expect-error import + const {ViewManager, DockController} = await import('./ui/legacy/legacy.js'); + // @ts-expect-error import + const {LighthousePanel} = await import('./panels/lighthouse/lighthouse.js'); + // @ts-expect-error import + const {TargetManager} = await import('./core/sdk/sdk.js'); + // @ts-expect-error import + const {AdvancedApp} = await import('./panels/emulation/emulation.js'); + // Undocking later in the function can cause hiccups when Lighthouse enables device emulation. - // @ts-expect-error global - UI.dockController.setDockSide('undocked'); + DockController.DockController.instance().setDockSide('undocked'); + + await ViewManager.ViewManager.instance().showView('lighthouse'); - // @ts-expect-error global - const viewManager = UI.viewManager || (UI.ViewManager.ViewManager || UI.ViewManager).instance(); - const views = viewManager.views || viewManager._views; - const panelName = views.has('lighthouse') ? 'lighthouse' : 'audits'; - await viewManager.showView(panelName); + const panel = LighthousePanel.LighthousePanel.instance(); - // @ts-expect-error global - const panel = UI.panels.lighthouse || UI.panels.audits; const button = panel.contentElement.querySelector('button'); if (button.disabled) throw new Error('Start button disabled'); - // Give the main target model a moment to be available. - // Otherwise, 'SDK.TargetManager.TargetManager.instance().mainTarget()' is null. - // @ts-expect-error global - if (self.runtime && self.runtime.loadLegacyModule) { - // This exposes TargetManager via self.SDK. - try { - // @ts-expect-error global - await self.runtime.loadLegacyModule('core/sdk/sdk-legacy.js'); - } catch {} - } - // @ts-expect-error global - const targetManager = SDK.targetManager || (SDK.TargetManager.TargetManager || SDK.TargetManager).instance(); + const targetManager = TargetManager.TargetManager.instance(); if (targetManager.primaryPageTarget() === null) { if (targetManager?.observeTargets) { await new Promise(resolve => targetManager.observeTargets({ @@ -186,16 +179,16 @@ async function waitForLighthouseReady() { } // Ensure the emulation model is ready before Lighthouse starts by enabling device emulation. - // @ts-expect-error global - const {deviceModeView} = Emulation.AdvancedApp.instance(); + const {deviceModeView} = AdvancedApp.AdvancedApp.instance(); if (!deviceModeView.isDeviceModeOn()) { deviceModeView.toggleDeviceMode(); } } async function runLighthouse() { - // @ts-expect-error global - const panel = UI.panels.lighthouse || UI.panels.audits; + // @ts-expect-error import + const {LighthousePanel} = await import('./panels/lighthouse/lighthouse.js'); + const panel = LighthousePanel.LighthousePanel.instance(); /** @type {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>} */ const resultPromise = new Promise((resolve, reject) => { @@ -233,9 +226,11 @@ async function runLighthouse() { return resultPromise; } -function enableDevToolsThrottling() { - // @ts-expect-error global - const panel = UI.panels.lighthouse || UI.panels.audits; +async function enableDevToolsThrottling() { + // @ts-expect-error import + const {LighthousePanel} = await import('./panels/lighthouse/lighthouse.js'); + const panel = LighthousePanel.LighthousePanel.instance(); + const toolbarRoot = panel.contentElement.querySelector('.lighthouse-settings-pane .toolbar').shadowRoot; toolbarRoot.querySelector('option[value="devtools"]').selected = true; toolbarRoot.querySelector('select').dispatchEvent(new Event('change')); @@ -300,12 +295,17 @@ function dismissDialog(dialog) { * @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, logs: string[]}>} */ async function testUrlFromDevtools(url, options = {}) { - const {config, chromeFlags} = options; + const {config, chromeFlags = []} = options; + + const newChromeFlags = [ + ...chromeFlags, + '--auto-open-devtools-for-tabs', + ]; + + const chrome = await launch({chromeFlags: newChromeFlags}); - const browser = await puppeteer.launch({ - executablePath: getChromePath(), - args: chromeFlags, - devtools: true, + const browser = await puppeteer.connect({ + browserURL: `http://127.0.0.1:${chrome.port}`, defaultViewport: null, }); diff --git a/core/test/audits/audit-test.js b/core/test/audits/audit-test.js index 73b622907733..7ebdac4d2273 100644 --- a/core/test/audits/audit-test.js +++ b/core/test/audits/audit-test.js @@ -42,6 +42,18 @@ class NumericAudit extends Audit { } } +class MetricSavings extends Audit { + static get meta() { + return { + id: 'metric-savings', + title: 'Passing', + description: 'Description', + requiredArtifacts: [], + scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS, + }; + } +} + describe('Audit', () => { it('throws if an audit does not override the meta', () => { assert.throws(_ => A.meta); @@ -75,6 +87,16 @@ describe('Audit', () => { assert.strictEqual(auditResult.score, 1); }); + it('override scoreDisplayMode if set on audit product', () => { + assert.strictEqual(NumericAudit.meta.scoreDisplayMode, Audit.SCORING_MODES.NUMERIC); + const auditResult = Audit.generateAuditResult(NumericAudit, { + score: 1, + scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE, + }); + assert.strictEqual(auditResult.scoreDisplayMode, Audit.SCORING_MODES.INFORMATIVE); + assert.strictEqual(auditResult.score, null); + }); + it('switches to an ERROR and is not scored if an errorMessage is passed in', () => { const errorMessage = 'ERRRRR'; const auditResult = Audit.generateAuditResult(NumericAudit, {score: 1, errorMessage}); @@ -102,6 +124,35 @@ describe('Audit', () => { }); }); + describe('METRIC_SAVINGS scoring mode', () => { + it('passes if audit product is passing', () => { + const auditResult = Audit.generateAuditResult( + MetricSavings, + {score: 1, metricSavings: {TBT: 100}} + ); + assert.strictEqual(auditResult.scoreDisplayMode, Audit.SCORING_MODES.METRIC_SAVINGS); + assert.strictEqual(auditResult.score, 1); + }); + + it('fails if audit product is not passing and there was metric savings', () => { + const auditResult = Audit.generateAuditResult( + MetricSavings, + {score: 0, metricSavings: {TBT: 100}} + ); + assert.strictEqual(auditResult.scoreDisplayMode, Audit.SCORING_MODES.METRIC_SAVINGS); + assert.strictEqual(auditResult.score, 0); + }); + + it('average if audit product is not passing and there was no metric savings', () => { + const auditResult = Audit.generateAuditResult( + MetricSavings, + {score: 0, metricSavings: {TBT: 0}} + ); + assert.strictEqual(auditResult.scoreDisplayMode, Audit.SCORING_MODES.METRIC_SAVINGS); + assert.strictEqual(auditResult.score, 0.5); + }); + }); + it('throws if an audit returns a score > 1', () => { assert.throws(_ => Audit.generateAuditResult(PassOrFailAudit, {score: 100}), /is > 1/); assert.throws(_ => Audit.generateAuditResult(PassOrFailAudit, {score: 2}), /is > 1/); diff --git a/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js index 00e0a7ee83c4..5d4f5a256404 100644 --- a/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js +++ b/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js @@ -175,7 +175,7 @@ describe('Byte efficiency base audit', () => { assert.equal(result.metricSavings.LCP, 2380); }); - it('should score the wastedMs', async () => { + it('should fail if there are any results regardless of wastedMs', async () => { const perfectResult = await ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, items: [{url: 'http://example.com/', wastedBytes: 1 * 1000}], @@ -196,22 +196,10 @@ describe('Byte efficiency base audit', () => { items: [{url: 'http://example.com/', wastedBytes: 400 * 1000}], }, simulator, metricComputationInput, {computedCache: new Map()}); - assert.equal(perfectResult.score, 1, 'scores perfect wastedMs'); - assert.ok(goodResult.score > 0.75 && goodResult.score < 1, 'scores good wastedMs'); - assert.ok(averageResult.score > 0.5 && averageResult.score < 0.75, 'scores average wastedMs'); - assert.ok(failingResult.score < 0.5, 'scores failing wastedMs'); - }); - - it('should score negative wastedMs as perfect', async () => { - metricComputationInput.gatherContext.gatherMode = 'timespan'; - const negativeResult = await ByteEfficiencyAudit.createAuditProduct({ - headings: baseHeadings, - items: [{url: 'http://example.com/', wastedBytes: -1 * 10000}], - }, simulator, metricComputationInput, {computedCache: new Map()}); - - assert.equal(negativeResult.score, 1); - assert.ok(negativeResult.numericValue < 0); - assert.equal(negativeResult.numericValue, negativeResult.details.overallSavingsMs); + assert.equal(perfectResult.score, 0, 'scores perfect wastedMs'); + assert.equal(goodResult.score, 0, 'scores good wastedMs'); + assert.equal(averageResult.score, 0, 'scores average wastedMs'); + assert.equal(failingResult.score, 0, 'scores failing wastedMs'); }); it('should populate KiB', async () => { diff --git a/core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js b/core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js index 0bd589b05445..1b8f1799e286 100644 --- a/core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js +++ b/core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js @@ -227,7 +227,7 @@ describe('Cache headers audit', () => { const context = {options, computedCache: new Map()}; return CacheHeadersAudit.audit(getArtifacts(networkRecords), context).then(result => { - assert.equal(result.score, 1); + assert.equal(result.score, 0); const items = result.details.items; assert.equal(items.length, 1); }); diff --git a/core/test/audits/csp-xss-test.js b/core/test/audits/csp-xss-test.js index 564ed6807b95..c1fc0b74eb2e 100644 --- a/core/test/audits/csp-xss-test.js +++ b/core/test/audits/csp-xss-test.js @@ -26,8 +26,8 @@ const STATIC_RESULTS = { severity: SEVERITY.high, description: { formattedDefault: - 'Missing object-src allows the injection of plugins that execute unsafe scripts. ' + - 'Consider setting object-src to \'none\' if you can.', + 'Missing `object-src` allows the injection of plugins that execute unsafe scripts. ' + + 'Consider setting `object-src` to `\'none\'` if you can.', }, directive: 'object-src', }, @@ -35,9 +35,9 @@ const STATIC_RESULTS = { severity: SEVERITY.high, description: { formattedDefault: - 'Missing base-uri allows injected tags to set the base URL for all ' + + 'Missing `base-uri` allows injected `` tags to set the base URL for all ' + 'relative URLs (e.g. scripts) to an attacker controlled domain. ' + - 'Consider setting base-uri to \'none\' or \'self\'.', + 'Consider setting `base-uri` to `\'none\'` or `\'self\'`.', }, directive: 'base-uri', }, @@ -45,7 +45,7 @@ const STATIC_RESULTS = { severity: SEVERITY.medium, description: { formattedDefault: - 'The page contains a CSP defined in a tag. ' + + 'The page contains a CSP defined in a `` tag. ' + 'Consider moving the CSP to an HTTP header or ' + 'defining another strict CSP in an HTTP header.', }, @@ -55,7 +55,7 @@ const STATIC_RESULTS = { severity: SEVERITY.medium, description: { formattedDefault: - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' + + 'Consider adding `\'unsafe-inline\'` (ignored by browsers supporting ' + 'nonces/hashes) to be backward compatible with older browsers.', }, directive: 'script-src', diff --git a/core/test/audits/installable-manifest-test.js b/core/test/audits/installable-manifest-test.js index 2bd2def26861..ed3b2749e078 100644 --- a/core/test/audits/installable-manifest-test.js +++ b/core/test/audits/installable-manifest-test.js @@ -115,7 +115,7 @@ describe('PWA: webapp install banner audit', () => { return InstallableManifestAudit.audit(artifacts, context).then(result => { assert.strictEqual(result.score, 0); const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/does not contain a 'name'/); + expect(items[0].reason).toBeDisplayString(/does not contain a `name`/); }); }); diff --git a/core/test/audits/largest-contentful-paint-element-test.js b/core/test/audits/largest-contentful-paint-element-test.js index bec68ce47009..0fb5ac9ee24c 100644 --- a/core/test/audits/largest-contentful-paint-element-test.js +++ b/core/test/audits/largest-contentful-paint-element-test.js @@ -100,7 +100,7 @@ describe('Performance: largest-contentful-paint-element audit', () => { const context = {settings: artifacts.settings, computedCache: new Map()}; const auditResult = await LargestContentfulPaintElementAudit.audit(artifacts, context); - expect(auditResult.score).toEqual(1); + expect(auditResult.score).toEqual(0); expect(auditResult.notApplicable).toBeUndefined(); expect(auditResult.displayValue).toBeDisplayString('5,800\xa0ms'); expect(auditResult.metricSavings).toEqual({LCP: 3304}); // 5804 - 2500 (p10 mobile) diff --git a/core/test/audits/layout-shift-elements-test.js b/core/test/audits/layout-shift-elements-test.js index fee977af8ecd..a3525cade98f 100644 --- a/core/test/audits/layout-shift-elements-test.js +++ b/core/test/audits/layout-shift-elements-test.js @@ -48,7 +48,7 @@ describe('Performance: layout-shift-elements audit', () => { }; const auditResult = await LayoutShiftElementsAudit.audit(artifacts, {computedCache: new Map()}); - expect(auditResult.score).toEqual(1); + expect(auditResult.score).toEqual(0); expect(auditResult.displayValue).toBeDisplayString('1 element found'); expect(auditResult.metricSavings).toEqual({CLS: 0.4}); expect(auditResult.details.items).toHaveLength(1); diff --git a/core/test/audits/long-tasks-test.js b/core/test/audits/long-tasks-test.js index 5d002bcd03e1..fe87d5a496af 100644 --- a/core/test/audits/long-tasks-test.js +++ b/core/test/audits/long-tasks-test.js @@ -6,6 +6,7 @@ import {readJson} from '../test-utils.js'; import LongTasks from '../../audits/long-tasks.js'; +import {defaultSettings} from '../../config/constants.js'; import {createTestTrace} from '../create-test-trace.js'; import {networkRecordsToDevtoolsLog} from '../network-records-to-devtools-log.js'; @@ -52,27 +53,46 @@ function generateTraceWithLongTasks({count, duration = 200, withChildTasks = fal return createTestTrace({ topLevelTasks: traceTasks, timeOrigin: BASE_TS, + traceEnd: BASE_TS + 20_000, }); } describe('Long tasks audit', () => { - const devtoolsLog = networkRecordsToDevtoolsLog([{url: TASK_URL}]); + const devtoolsLog = networkRecordsToDevtoolsLog([{ + url: TASK_URL, + priority: 'High', + }]); const URL = { requestedUrl: TASK_URL, mainDocumentUrl: TASK_URL, finalDisplayedUrl: TASK_URL, }; - it('should pass if there are no long tasks', async () => { + let context; + + beforeEach(() => { + const settings = JSON.parse(JSON.stringify(defaultSettings)); + settings.throttlingMethod = 'devtools'; + + context = { + computedCache: new Map(), + settings, + }; + }); + + it('should pass and be non-applicable if there are no long tasks', async () => { const artifacts = { URL, traces: {defaultPass: generateTraceWithLongTasks({count: 0})}, devtoolsLogs: {defaultPass: devtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result.details.items).toHaveLength(0); expect(result.score).toBe(1); expect(result.displayValue).toBeUndefined(); + expect(result.notApplicable).toBeTruthy(); + expect(result.metricSavings).toEqual({TBT: 0}); }); it('should return a list of long tasks with duration >= 50 ms', async () => { @@ -80,8 +100,9 @@ describe('Long tasks audit', () => { URL, traces: {defaultPass: generateTraceWithLongTasks({count: 4})}, devtoolsLogs: {defaultPass: devtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result.details.items).toMatchObject([ {url: 'Unattributable', duration: 200, startTime: 1000}, {url: 'Unattributable', duration: 200, startTime: 2000}, @@ -91,11 +112,13 @@ describe('Long tasks audit', () => { expect(result.score).toBe(0); expect(result.displayValue).toBeDisplayString('4 long tasks found'); expect(result.notApplicable).toBeFalsy(); + expect(result.metricSavings).toEqual({TBT: 600}); // 4 * (200ms - 50ms) }); it('should filter out tasks with duration less than 50 ms', async () => { const trace = createTestTrace({ timeOrigin: BASE_TS, + traceEnd: BASE_TS + 20_000, topLevelTasks: [ {ts: BASE_TS, duration: 1}, {ts: BASE_TS + 1000, duration: 30}, @@ -108,24 +131,29 @@ describe('Long tasks audit', () => { URL, traces: {defaultPass: trace}, devtoolsLogs: {defaultPass: devtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result.details.items).toMatchObject([ {url: 'Unattributable', duration: 100, startTime: 2000}, {url: 'Unattributable', duration: 50, startTime: 4000}, ]); expect(result.score).toBe(0); expect(result.displayValue).toBeDisplayString('2 long tasks found'); + expect(result.metricSavings).toEqual({TBT: 50}); // (100ms - 50ms) + (50ms - 50ms) }); it('should not filter out tasks with duration >= 50 ms only after throttling', async () => { const artifacts = { URL, traces: {defaultPass: generateTraceWithLongTasks({count: 4, duration: 25})}, - devtoolsLogs: {defaultPass: networkRecordsToDevtoolsLog([ - {url: TASK_URL, timing: {connectEnd: 50, connectStart: 0.01, sslStart: 25, sslEnd: 40}}, - ])}, + devtoolsLogs: {defaultPass: networkRecordsToDevtoolsLog([{ + url: TASK_URL, + priority: 'High', + timing: {connectEnd: 50, connectStart: 0.01, sslStart: 25, sslEnd: 40}, + }])}, + GatherContext: {gatherMode: 'navigation'}, }; const context = { computedCache: new Map(), @@ -152,6 +180,7 @@ describe('Long tasks audit', () => { expect(result.score).toBe(0); expect(result.details.items).toHaveLength(4); expect(result.displayValue).toBeDisplayString('4 long tasks found'); + expect(result.metricSavings).toEqual({TBT: 200}); // 4 * (100ms - 50ms) }); it('should populate url when tasks have an attributable url', async () => { @@ -160,13 +189,15 @@ describe('Long tasks audit', () => { URL, traces: {defaultPass: trace}, devtoolsLogs: {defaultPass: devtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result.details.items).toMatchObject([ {url: TASK_URL, duration: 300, startTime: 1000}, ]); expect(result.score).toBe(0); expect(result.displayValue).toBeDisplayString('1 long task found'); + expect(result.metricSavings).toEqual({TBT: 250}); // 300ms - 50ms }); it('should include more than 20 tasks in debugData', async () => { @@ -181,18 +212,21 @@ describe('Long tasks audit', () => { const trace = createTestTrace({ timeOrigin: BASE_TS, + traceEnd: BASE_TS + 100_000, topLevelTasks, }); const artifacts = { URL, traces: {defaultPass: trace}, devtoolsLogs: {defaultPass: devtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result.details.items).toHaveLength(20); expect(result.score).toBe(0); expect(result.displayValue).toBeDisplayString('20 long tasks found'); + expect(result.metricSavings).toEqual({TBT: 249.99}); // (55ms - 50ms) * 50 const debugData = result.details.debugData; expect(debugData).toMatchObject({ @@ -218,8 +252,9 @@ describe('Long tasks audit', () => { URL, traces: {defaultPass: trace}, devtoolsLogs: {defaultPass: devtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result).toMatchObject({ score: 0, @@ -250,8 +285,9 @@ describe('Long tasks audit', () => { URL, traces: {defaultPass: redirectTrace}, devtoolsLogs: {defaultPass: redirectDevtoolsLog}, + GatherContext: {gatherMode: 'navigation'}, }; - const result = await LongTasks.audit(artifacts, {computedCache: new Map()}); + const result = await LongTasks.audit(artifacts, context); expect(result).toMatchObject({ score: 0, @@ -268,6 +304,7 @@ describe('Long tasks audit', () => { }], }, }); + expect(result.metricSavings.TBT).toBeApproximately(353.53); const debugData = result.details.debugData; expect(debugData).toStrictEqual({ diff --git a/core/test/audits/redirects-test.js b/core/test/audits/redirects-test.js index ce8d699ce974..0401648a22e8 100644 --- a/core/test/audits/redirects-test.js +++ b/core/test/audits/redirects-test.js @@ -162,7 +162,7 @@ describe('Performance: Redirects audit', () => { const output = await RedirectsAudit.audit(artifacts, context); expect(output.details.items).toHaveLength(3); - expect(Math.round(output.score * 100) / 100).toMatchInlineSnapshot(`0.29`); + expect(Math.round(output.score * 100) / 100).toMatchInlineSnapshot(`0`); expect(output.numericValue).toMatchInlineSnapshot(`2000`); expect(output.metricSavings).toEqual({LCP: 2000, FCP: 2000}); }); @@ -201,7 +201,7 @@ describe('Performance: Redirects audit', () => { const context = {settings: {}, computedCache: new Map()}; return RedirectsAudit.audit(artifacts, context).then(output => { expect(output.details.items).toHaveLength(4); - expect(Math.round(output.score * 100) / 100).toMatchInlineSnapshot(`0.2`); + expect(Math.round(output.score * 100) / 100).toMatchInlineSnapshot(`0`); expect(output.numericValue).toMatchInlineSnapshot(`3000`); expect(output.metricSavings).toEqual({LCP: 3000, FCP: 3000}); }); @@ -212,7 +212,7 @@ describe('Performance: Redirects audit', () => { const context = {settings: {}, computedCache: new Map()}; return RedirectsAudit.audit(artifacts, context).then(output => { expect(output.details.items).toHaveLength(3); - expect(Math.round(output.score * 100) / 100).toMatchInlineSnapshot(`0.29`); + expect(Math.round(output.score * 100) / 100).toMatchInlineSnapshot(`0`); expect(output.numericValue).toMatchInlineSnapshot(`2000`); expect(output.metricSavings).toEqual({LCP: 2000, FCP: 2000}); }); @@ -223,7 +223,7 @@ describe('Performance: Redirects audit', () => { const context = {settings: {}, computedCache: new Map()}; return RedirectsAudit.audit(artifacts, context).then(output => { expect(output.details.items).toHaveLength(2); - expect(output.score).toEqual(0.48); + expect(output.score).toEqual(0); expect(output.numericValue).toMatchInlineSnapshot(`1000`); expect(output.metricSavings).toEqual({LCP: 1000, FCP: 1000}); }); @@ -257,7 +257,7 @@ describe('Performance: Redirects audit', () => { const output = await RedirectsAudit.audit(artifacts, context); expect(output).toMatchObject({ - score: expect.toBeApproximately(0.2), + score: 0, numericValue: 3000, details: { items: [ diff --git a/core/test/audits/unsized-images-test.js b/core/test/audits/unsized-images-test.js index 043122325ec4..cdb4a83df1d2 100644 --- a/core/test/audits/unsized-images-test.js +++ b/core/test/audits/unsized-images-test.js @@ -578,11 +578,11 @@ describe('Sized images audit', () => { }); }); - it('is not applicable when there are no images', async () => { + it('is applicable and passing when there are no images', async () => { const result = await UnsizedImagesAudit.audit({ ImageElements: [], }); - expect(result.notApplicable).toEqual(true); + expect(result.notApplicable).toBeUndefined(); expect(result.score).toEqual(1); expect(result.metricSavings).toEqual({CLS: 0}); }); diff --git a/core/test/audits/work-during-interaction-test.js b/core/test/audits/work-during-interaction-test.js index 04662e08a066..348fd37d6d3d 100644 --- a/core/test/audits/work-during-interaction-test.js +++ b/core/test/audits/work-during-interaction-test.js @@ -259,6 +259,7 @@ Object { "INP": 368, }, "score": 0, + "scoreDisplayMode": undefined, } `); }); 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 43ab24ddef6d..12ecd3682f25 100644 --- a/core/test/fixtures/user-flows/reports/sample-flow-result.json +++ b/core/test/fixtures/user-flows/reports/sample-flow-result.json @@ -37,7 +37,7 @@ "title": "Has a `` tag with `width` or `initial-scale`", "description": "A `` not only optimizes your app for mobile screen sizes, but also prevents [a 300 millisecond delay to user input](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/). [Learn more about using the viewport meta tag](https://developer.chrome.com/docs/lighthouse/pwa/viewport/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "metricSavings": { "INP": 0 @@ -224,8 +224,8 @@ "id": "server-response-time", "title": "Initial server response time was short", "description": "Keep the server response time for the main document short because all other requests depend on it. [Learn more about the Time to First Byte metric](https://developer.chrome.com/docs/lighthouse/performance/time-to-first-byte/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 19.687999999999988, "numericUnit": "millisecond", "displayValue": "Root document took 20 ms", @@ -360,7 +360,7 @@ "title": "Avoid multiple page redirects", "description": "Redirects introduce additional delays before the page can be loaded. [Learn how to avoid page redirects](https://developer.chrome.com/docs/lighthouse/performance/redirects/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -510,8 +510,8 @@ "id": "mainthread-work-breakdown", "title": "Minimizes main-thread work", "description": "Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to minimize main-thread work](https://developer.chrome.com/docs/lighthouse/performance/mainthread-work-breakdown/)", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 959.4240000000008, "numericUnit": "millisecond", "displayValue": "1.0 s", @@ -575,8 +575,8 @@ "id": "bootup-time", "title": "JavaScript execution time", "description": "Consider reducing the time spent parsing, compiling, and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to reduce Javascript execution time](https://developer.chrome.com/docs/lighthouse/performance/bootup-time/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 288.41200000000015, "numericUnit": "millisecond", "displayValue": "0.3 s", @@ -658,7 +658,7 @@ "title": "Preconnect to required origins", "description": "Consider adding `preconnect` or `dns-prefetch` resource hints to establish early connections to important third-party origins. [Learn how to preconnect to required origins](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -683,7 +683,7 @@ "title": "All text remains visible during webfont loads", "description": "Leverage the `font-display` CSS feature to ensure text is user-visible while webfonts are loading. [Learn more about `font-display`](https://developer.chrome.com/docs/lighthouse/performance/font-display/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "details": { "type": "table", @@ -1419,8 +1419,8 @@ "id": "third-party-summary", "title": "Minimize third-party usage", "description": "Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn how to minimize third-party impact](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "displayValue": "Third-party code blocked the main thread for 0 ms", "metricSavings": { "TBT": 0 @@ -1597,7 +1597,7 @@ "title": "Largest Contentful Paint image was not lazily loaded", "description": "Above-the-fold images that are lazily loaded render later in the page lifecycle, which can delay the largest contentful paint. [Learn more about optimal lazy loading](https://web.dev/lcp-lazy-loading/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "metricSavings": { "LCP": 0 }, @@ -1765,6 +1765,9 @@ "score": null, "scoreDisplayMode": "informative", "displayValue": "3 long tasks found", + "metricSavings": { + "TBT": 143 + }, "details": { "type": "table", "headings": [ @@ -1870,8 +1873,8 @@ "id": "unsized-images", "title": "Image elements do not have explicit `width` and `height`", "description": "Set an explicit width and height on image elements to reduce layout shifts and improve CLS. [Learn how to set image dimensions](https://web.dev/optimize-cls/#images-without-dimensions)", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "metricSavings": { "CLS": 0 }, @@ -1930,7 +1933,7 @@ "title": "Preload Largest Contentful Paint image", "description": "If the LCP element is dynamically added to the page, you should preload the image in order to improve LCP. [Learn more about preloading LCP elements](https://web.dev/optimize-lcp/#optimize-when-the-resource-is-discovered).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -2776,7 +2779,7 @@ "title": "Uses efficient cache policy on static assets", "description": "A long cache lifetime can speed up repeat visits to your page. [Learn more about efficient cache policies](https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "byte", "displayValue": "0 resources found", @@ -2791,8 +2794,8 @@ "id": "total-byte-weight", "title": "Avoids enormous network payloads", "description": "Large network payloads cost users real money and are highly correlated with long load times. [Learn how to reduce payload sizes](https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 186728, "numericUnit": "byte", "displayValue": "Total size was 182 KiB", @@ -2863,7 +2866,7 @@ "title": "Defer offscreen images", "description": "Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. [Learn how to defer offscreen images](https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -2896,7 +2899,7 @@ "title": "Eliminate render-blocking resources", "description": "Resources are blocking the first paint of your page. Consider delivering critical JS/CSS inline and deferring all non-critical JS/styles. [Learn how to eliminate render-blocking resources](https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "metricSavings": { @@ -2916,7 +2919,7 @@ "title": "Minify CSS", "description": "Minifying CSS files can reduce network payload sizes. [Learn how to minify CSS](https://developer.chrome.com/docs/lighthouse/performance/unminified-css/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -2948,7 +2951,7 @@ "title": "Minify JavaScript", "description": "Minifying JavaScript files can reduce payload sizes and script parse time. [Learn how to minify JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unminified-javascript/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -2981,7 +2984,7 @@ "title": "Reduce unused CSS", "description": "Reduce unused rules from stylesheets and defer CSS not used for above-the-fold content to decrease bytes consumed by network activity. [Learn how to reduce unused CSS](https://developer.chrome.com/docs/lighthouse/performance/unused-css-rules/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3013,7 +3016,7 @@ "title": "Reduce unused JavaScript", "description": "Reduce unused JavaScript and defer loading scripts until they are required to decrease bytes consumed by network activity. [Learn how to reduce unused JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unused-javascript/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3045,7 +3048,7 @@ "title": "Serve images in next-gen formats", "description": "Image formats like WebP and AVIF often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more about modern image formats](https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3078,7 +3081,7 @@ "title": "Efficiently encode images", "description": "Optimized images load faster and consume less cellular data. [Learn how to efficiently encode images](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3111,7 +3114,7 @@ "title": "Enable text compression", "description": "Text-based resources should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more about text compression](https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3143,7 +3146,7 @@ "title": "Properly size images", "description": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn how to size images](https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3175,7 +3178,7 @@ "title": "Use video formats for animated content", "description": "Large GIFs are inefficient for delivering animated content. Consider using MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save network bytes. [Learn more about efficient video formats](https://developer.chrome.com/docs/lighthouse/performance/efficient-animated-content/)", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3207,7 +3210,7 @@ "title": "Remove duplicate modules in JavaScript bundles", "description": "Remove large, duplicate JavaScript modules from bundles to reduce unnecessary bytes consumed by network activity. ", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -3238,8 +3241,8 @@ "id": "legacy-javascript", "title": "Avoid serving legacy JavaScript to modern browsers", "description": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn how to use modern JavaScript](https://web.dev/publish-modern-javascript/)", - "score": 1, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "Potential savings of 0 KiB", @@ -3328,8 +3331,8 @@ "id": "dom-size", "title": "Avoids an excessive DOM size", "description": "A large DOM will increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 70, "numericUnit": "element", "displayValue": "70 elements", @@ -3445,7 +3448,7 @@ "title": "Avoids `document.write()`", "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn how to avoid document.write()](https://developer.chrome.com/docs/lighthouse/best-practices/no-document-write/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [], @@ -3533,7 +3536,7 @@ "title": "Uses passive listeners to improve scrolling performance", "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more about adopting passive event listeners](https://developer.chrome.com/docs/lighthouse/best-practices/uses-passive-event-listeners/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [], @@ -3711,7 +3714,7 @@ "title": "Page didn't prevent back/forward cache restoration", "description": "Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://developer.chrome.com/docs/lighthouse/performance/bf-cache/)", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "guidanceLevel": 2 } }, @@ -8810,8 +8813,8 @@ "id": "mainthread-work-breakdown", "title": "Minimizes main-thread work", "description": "Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to minimize main-thread work](https://developer.chrome.com/docs/lighthouse/performance/mainthread-work-breakdown/)", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 867.7709999999973, "numericUnit": "millisecond", "displayValue": "0.9 s", @@ -8880,8 +8883,8 @@ "id": "bootup-time", "title": "JavaScript execution time", "description": "Consider reducing the time spent parsing, compiling, and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to reduce Javascript execution time](https://developer.chrome.com/docs/lighthouse/performance/bootup-time/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 352.6489999999996, "numericUnit": "millisecond", "displayValue": "0.4 s", @@ -9537,8 +9540,8 @@ "id": "third-party-summary", "title": "Minimize third-party usage", "description": "Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn how to minimize third-party impact](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "displayValue": "Third-party code blocked the main thread for 0 ms", "metricSavings": { "TBT": 0 @@ -9607,8 +9610,8 @@ "id": "layout-shift-elements", "title": "Avoid large layout shifts", "description": "These DOM elements contribute most to the CLS of the page. [Learn how to improve CLS](https://web.dev/optimize-cls/)", - "score": null, - "scoreDisplayMode": "informative", + "score": 0, + "scoreDisplayMode": "metricSavings", "displayValue": "1 element found", "metricSavings": { "CLS": 0.13125 @@ -9659,6 +9662,9 @@ "score": null, "scoreDisplayMode": "informative", "displayValue": "1 long task found", + "metricSavings": { + "TBT": 122.83299999999986 + }, "details": { "type": "table", "headings": [ @@ -9740,8 +9746,8 @@ "id": "unsized-images", "title": "Image elements do not have explicit `width` and `height`", "description": "Set an explicit width and height on image elements to reduce layout shifts and improve CLS. [Learn how to set image dimensions](https://web.dev/optimize-cls/#images-without-dimensions)", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "metricSavings": { "CLS": 0 }, @@ -9824,10 +9830,10 @@ }, "uses-long-cache-ttl": { "id": "uses-long-cache-ttl", - "title": "Uses efficient cache policy on static assets", + "title": "Serve static assets with an efficient cache policy", "description": "A long cache lifetime can speed up repeat visits to your page. [Learn more about efficient cache policies](https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/).", - "score": 0.93, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 21491.399999999998, "numericUnit": "byte", "displayValue": "9 resources found", @@ -9979,8 +9985,8 @@ "id": "total-byte-weight", "title": "Avoids enormous network payloads", "description": "Large network payloads cost users real money and are highly correlated with long load times. [Learn how to reduce payload sizes](https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 66067, "numericUnit": "byte", "displayValue": "Total size was 65 KiB", @@ -10051,7 +10057,7 @@ "title": "Minify CSS", "description": "Minifying CSS files can reduce network payload sizes. [Learn how to minify CSS](https://developer.chrome.com/docs/lighthouse/performance/unminified-css/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10083,7 +10089,7 @@ "title": "Minify JavaScript", "description": "Minifying JavaScript files can reduce payload sizes and script parse time. [Learn how to minify JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unminified-javascript/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10116,7 +10122,7 @@ "title": "Reduce unused CSS", "description": "Reduce unused rules from stylesheets and defer CSS not used for above-the-fold content to decrease bytes consumed by network activity. [Learn how to reduce unused CSS](https://developer.chrome.com/docs/lighthouse/performance/unused-css-rules/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10148,7 +10154,7 @@ "title": "Reduce unused JavaScript", "description": "Reduce unused JavaScript and defer loading scripts until they are required to decrease bytes consumed by network activity. [Learn how to reduce unused JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unused-javascript/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10180,7 +10186,7 @@ "title": "Serve images in next-gen formats", "description": "Image formats like WebP and AVIF often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more about modern image formats](https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10213,7 +10219,7 @@ "title": "Efficiently encode images", "description": "Optimized images load faster and consume less cellular data. [Learn how to efficiently encode images](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10246,7 +10252,7 @@ "title": "Enable text compression", "description": "Text-based resources should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more about text compression](https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10278,7 +10284,7 @@ "title": "Properly size images", "description": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn how to size images](https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10310,7 +10316,7 @@ "title": "Use video formats for animated content", "description": "Large GIFs are inefficient for delivering animated content. Consider using MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save network bytes. [Learn more about efficient video formats](https://developer.chrome.com/docs/lighthouse/performance/efficient-animated-content/)", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10342,7 +10348,7 @@ "title": "Remove duplicate modules in JavaScript bundles", "description": "Remove large, duplicate JavaScript modules from bundles to reduce unnecessary bytes consumed by network activity. ", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10374,7 +10380,7 @@ "title": "Avoid serving legacy JavaScript to modern browsers", "description": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn how to use modern JavaScript](https://web.dev/publish-modern-javascript/)", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -10418,7 +10424,7 @@ "title": "Avoids `document.write()`", "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn how to avoid document.write()](https://developer.chrome.com/docs/lighthouse/best-practices/no-document-write/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [], @@ -10431,7 +10437,7 @@ "title": "Uses passive listeners to improve scrolling performance", "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more about adopting passive event listeners](https://developer.chrome.com/docs/lighthouse/best-practices/uses-passive-event-listeners/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [], @@ -10443,8 +10449,8 @@ "id": "work-during-interaction", "title": "Minimizes work during key interaction", "description": "This is the thread-blocking work occurring during the Interaction to Next Paint measurement. [Learn more about the Interaction to Next Paint metric](https://web.dev/inp/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "displayValue": "60 ms spent on event 'keypress'", "metricSavings": { "INP": 64 @@ -10638,7 +10644,7 @@ "title": "Page didn't prevent back/forward cache restoration", "description": "Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://developer.chrome.com/docs/lighthouse/performance/bf-cache/)", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "guidanceLevel": 2 } }, @@ -12129,7 +12135,7 @@ "core/audits/valid-source-maps.js | description": [ "audits[valid-source-maps].description" ], - "core/audits/byte-efficiency/uses-long-cache-ttl.js | title": [ + "core/audits/byte-efficiency/uses-long-cache-ttl.js | failureTitle": [ "audits[uses-long-cache-ttl].title" ], "core/audits/byte-efficiency/uses-long-cache-ttl.js | description": [ @@ -12415,7 +12421,7 @@ "title": "Has a `` tag with `width` or `initial-scale`", "description": "A `` not only optimizes your app for mobile screen sizes, but also prevents [a 300 millisecond delay to user input](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/). [Learn more about using the viewport meta tag](https://developer.chrome.com/docs/lighthouse/pwa/viewport/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "metricSavings": { "INP": 0 @@ -12573,8 +12579,8 @@ "id": "unsized-images", "title": "Image elements do not have explicit `width` and `height`", "description": "Set an explicit width and height on image elements to reduce layout shifts and improve CLS. [Learn how to set image dimensions](https://web.dev/optimize-cls/#images-without-dimensions)", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "metricSavings": { "CLS": 0 }, @@ -14373,8 +14379,8 @@ "id": "uses-responsive-images-snapshot", "title": "Images were larger than their displayed size", "description": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn how to size images](https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/).", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [ @@ -14436,8 +14442,8 @@ "id": "dom-size", "title": "Avoids an excessive DOM size", "description": "A large DOM will increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 361, "numericUnit": "element", "displayValue": "361 elements", @@ -17649,7 +17655,7 @@ "title": "Has a `` tag with `width` or `initial-scale`", "description": "A `` not only optimizes your app for mobile screen sizes, but also prevents [a 300 millisecond delay to user input](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/). [Learn more about using the viewport meta tag](https://developer.chrome.com/docs/lighthouse/pwa/viewport/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "metricSavings": { "INP": 0 @@ -17836,8 +17842,8 @@ "id": "server-response-time", "title": "Initial server response time was short", "description": "Keep the server response time for the main document short because all other requests depend on it. [Learn more about the Time to First Byte metric](https://developer.chrome.com/docs/lighthouse/performance/time-to-first-byte/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 10.263, "numericUnit": "millisecond", "displayValue": "Root document took 10 ms", @@ -17972,7 +17978,7 @@ "title": "Avoid multiple page redirects", "description": "Redirects introduce additional delays before the page can be loaded. [Learn how to avoid page redirects](https://developer.chrome.com/docs/lighthouse/performance/redirects/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -18122,8 +18128,8 @@ "id": "mainthread-work-breakdown", "title": "Minimizes main-thread work", "description": "Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to minimize main-thread work](https://developer.chrome.com/docs/lighthouse/performance/mainthread-work-breakdown/)", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 454.39200000000045, "numericUnit": "millisecond", "displayValue": "0.5 s", @@ -18187,8 +18193,8 @@ "id": "bootup-time", "title": "JavaScript execution time", "description": "Consider reducing the time spent parsing, compiling, and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to reduce Javascript execution time](https://developer.chrome.com/docs/lighthouse/performance/bootup-time/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 169.10399999999998, "numericUnit": "millisecond", "displayValue": "0.2 s", @@ -18276,7 +18282,7 @@ "title": "Preconnect to required origins", "description": "Consider adding `preconnect` or `dns-prefetch` resource hints to establish early connections to important third-party origins. [Learn how to preconnect to required origins](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -18301,7 +18307,7 @@ "title": "All text remains visible during webfont loads", "description": "Leverage the `font-display` CSS feature to ensure text is user-visible while webfonts are loading. [Learn more about `font-display`](https://developer.chrome.com/docs/lighthouse/performance/font-display/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "details": { "type": "table", @@ -18995,8 +19001,8 @@ "id": "third-party-summary", "title": "Minimize third-party usage", "description": "Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn how to minimize third-party impact](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "displayValue": "Third-party code blocked the main thread for 0 ms", "metricSavings": { "TBT": 0 @@ -19154,7 +19160,7 @@ "title": "Largest Contentful Paint image was not lazily loaded", "description": "Above-the-fold images that are lazily loaded render later in the page lifecycle, which can delay the largest contentful paint. [Learn more about optimal lazy loading](https://web.dev/lcp-lazy-loading/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "metricSavings": { "LCP": 0 }, @@ -19213,6 +19219,9 @@ "score": null, "scoreDisplayMode": "informative", "displayValue": "2 long tasks found", + "metricSavings": { + "TBT": 13 + }, "details": { "type": "table", "headings": [ @@ -19305,8 +19314,8 @@ "id": "unsized-images", "title": "Image elements do not have explicit `width` and `height`", "description": "Set an explicit width and height on image elements to reduce layout shifts and improve CLS. [Learn how to set image dimensions](https://web.dev/optimize-cls/#images-without-dimensions)", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "metricSavings": { "CLS": 0 }, @@ -19384,7 +19393,7 @@ "title": "Preload Largest Contentful Paint image", "description": "If the LCP element is dynamically added to the page, you should preload the image in order to improve LCP. [Learn more about preloading LCP elements](https://web.dev/optimize-lcp/#optimize-when-the-resource-is-discovered).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20267,7 +20276,7 @@ "title": "Uses efficient cache policy on static assets", "description": "A long cache lifetime can speed up repeat visits to your page. [Learn more about efficient cache policies](https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "byte", "displayValue": "0 resources found", @@ -20282,8 +20291,8 @@ "id": "total-byte-weight", "title": "Avoids enormous network payloads", "description": "Large network payloads cost users real money and are highly correlated with long load times. [Learn how to reduce payload sizes](https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 127688, "numericUnit": "byte", "displayValue": "Total size was 125 KiB", @@ -20334,7 +20343,7 @@ "title": "Defer offscreen images", "description": "Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. [Learn how to defer offscreen images](https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20367,7 +20376,7 @@ "title": "Eliminate render-blocking resources", "description": "Resources are blocking the first paint of your page. Consider delivering critical JS/CSS inline and deferring all non-critical JS/styles. [Learn how to eliminate render-blocking resources](https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "metricSavings": { @@ -20387,7 +20396,7 @@ "title": "Minify CSS", "description": "Minifying CSS files can reduce network payload sizes. [Learn how to minify CSS](https://developer.chrome.com/docs/lighthouse/performance/unminified-css/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20419,7 +20428,7 @@ "title": "Minify JavaScript", "description": "Minifying JavaScript files can reduce payload sizes and script parse time. [Learn how to minify JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unminified-javascript/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20452,7 +20461,7 @@ "title": "Reduce unused CSS", "description": "Reduce unused rules from stylesheets and defer CSS not used for above-the-fold content to decrease bytes consumed by network activity. [Learn how to reduce unused CSS](https://developer.chrome.com/docs/lighthouse/performance/unused-css-rules/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20484,7 +20493,7 @@ "title": "Reduce unused JavaScript", "description": "Reduce unused JavaScript and defer loading scripts until they are required to decrease bytes consumed by network activity. [Learn how to reduce unused JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unused-javascript/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20515,8 +20524,8 @@ "id": "modern-image-formats", "title": "Serve images in next-gen formats", "description": "Image formats like WebP and AVIF often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more about modern image formats](https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images/).", - "score": 0.78, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 300, "numericUnit": "millisecond", "displayValue": "Potential savings of 67 KiB", @@ -20595,7 +20604,7 @@ "title": "Efficiently encode images", "description": "Optimized images load faster and consume less cellular data. [Learn how to efficiently encode images](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20628,7 +20637,7 @@ "title": "Enable text compression", "description": "Text-based resources should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more about text compression](https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20659,8 +20668,8 @@ "id": "uses-responsive-images", "title": "Properly size images", "description": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn how to size images](https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/).", - "score": 0.78, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 300, "numericUnit": "millisecond", "displayValue": "Potential savings of 51 KiB", @@ -20736,7 +20745,7 @@ "title": "Use video formats for animated content", "description": "Large GIFs are inefficient for delivering animated content. Consider using MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save network bytes. [Learn more about efficient video formats](https://developer.chrome.com/docs/lighthouse/performance/efficient-animated-content/)", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20768,7 +20777,7 @@ "title": "Remove duplicate modules in JavaScript bundles", "description": "Remove large, duplicate JavaScript modules from bundles to reduce unnecessary bytes consumed by network activity. ", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20799,8 +20808,8 @@ "id": "legacy-javascript", "title": "Avoid serving legacy JavaScript to modern browsers", "description": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn how to use modern JavaScript](https://web.dev/publish-modern-javascript/)", - "score": 1, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -20889,8 +20898,8 @@ "id": "dom-size", "title": "Avoids an excessive DOM size", "description": "A large DOM will increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 81, "numericUnit": "element", "displayValue": "81 elements", @@ -21006,7 +21015,7 @@ "title": "Avoids `document.write()`", "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn how to avoid document.write()](https://developer.chrome.com/docs/lighthouse/best-practices/no-document-write/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [], @@ -21094,7 +21103,7 @@ "title": "Uses passive listeners to improve scrolling performance", "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more about adopting passive event listeners](https://developer.chrome.com/docs/lighthouse/best-practices/uses-passive-event-listeners/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [], @@ -21329,7 +21338,7 @@ "title": "Page didn't prevent back/forward cache restoration", "description": "Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://developer.chrome.com/docs/lighthouse/performance/bf-cache/)", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "guidanceLevel": 2 } }, diff --git a/core/test/gather/driver/target-manager-test.js b/core/test/gather/driver/target-manager-test.js index e750ac346e57..ff492d909902 100644 --- a/core/test/gather/driver/target-manager-test.js +++ b/core/test/gather/driver/target-manager-test.js @@ -6,7 +6,7 @@ import {EventEmitter} from 'events'; -import {CDPSessionImpl} from 'puppeteer-core/lib/cjs/puppeteer/common/Connection.js'; +import {CdpCDPSession} from 'puppeteer-core/lib/cjs/puppeteer/cdp/CDPSession.js'; import {TargetManager} from '../../../gather/driver/target-manager.js'; import {createMockCdpSession} from '../mock-driver.js'; @@ -251,9 +251,9 @@ describe('TargetManager', () => { } const mockCdpConnection = new MockCdpConnection(); - /** @type {LH.Puppeteer.CDPSession} */ + /** @type {import('puppeteer-core').CDPSession} */ // @ts-expect-error - close enough to the real thing. - const cdpSession = new CDPSessionImpl(mockCdpConnection, '', sessionId); + const cdpSession = new CdpCDPSession(mockCdpConnection, '', sessionId); return cdpSession; } diff --git a/core/test/gather/session-test.js b/core/test/gather/session-test.js index 2806399c6f34..9d88989786be 100644 --- a/core/test/gather/session-test.js +++ b/core/test/gather/session-test.js @@ -6,7 +6,7 @@ import {EventEmitter} from 'events'; -import {CDPSessionImpl} from 'puppeteer-core/lib/cjs/puppeteer/common/Connection.js'; +import {CdpCDPSession} from 'puppeteer-core/lib/cjs/puppeteer/cdp/CDPSession.js'; import {ProtocolSession} from '../../gather/session.js'; import { @@ -17,6 +17,27 @@ import { timers, } from '../test-utils.js'; +/** + * @param {number} id + * @return {LH.Crdp.Page.FrameNavigatedEvent} + */ +function mockFrameNavigated(id) { + return { + frame: { + id: String(id), + loaderId: String(id), + url: `https://example.com/page${id}`, + domainAndRegistry: 'example.com', + securityOrigin: 'https://example.com', + mimeType: 'text/html', + secureContextType: 'Secure', + crossOriginIsolatedContextType: 'NotIsolated', + gatedAPIFeatures: [], + }, + type: 'Navigation', + }; +} + describe('ProtocolSession', () => { before(() => timers.useFakeTimers()); after(() => timers.dispose()); @@ -33,20 +54,23 @@ describe('ProtocolSession', () => { rawSend = fnAny().mockResolvedValue(Promise.resolve()); // @ts-expect-error - Individual mock functions are applied as necessary. - puppeteerSession = new CDPSessionImpl({_rawSend: rawSend}, '', 'root'); + puppeteerSession = new CdpCDPSession({_rawSend: rawSend}, '', 'root'); session = new ProtocolSession(puppeteerSession); }); describe('responds to events from the underlying CDPSession', () => { + const mockNavigated1 = mockFrameNavigated(1); + const mockNavigated2 = mockFrameNavigated(2); + it('once', async () => { const callback = fnAny(); session.once('Page.frameNavigated', callback); - puppeteerSession.emit('Page.frameNavigated', {id: 1}); + puppeteerSession.emit('Page.frameNavigated', mockNavigated1); expect(callback).toHaveBeenCalledTimes(1); - expect(callback).toHaveBeenCalledWith({id: 1}); + expect(callback).toHaveBeenCalledWith(mockNavigated1); - puppeteerSession.emit('Page.frameNavigated', {id: 2}); + puppeteerSession.emit('Page.frameNavigated', mockNavigated2); expect(callback).toHaveBeenCalledTimes(1); }); @@ -54,25 +78,25 @@ describe('ProtocolSession', () => { const callback = fnAny(); session.on('Page.frameNavigated', callback); - puppeteerSession.emit('Page.frameNavigated', {id: 1}); + puppeteerSession.emit('Page.frameNavigated', mockNavigated1); expect(callback).toHaveBeenCalledTimes(1); - expect(callback).toHaveBeenCalledWith({id: 1}); + expect(callback).toHaveBeenCalledWith(mockNavigated1); - puppeteerSession.emit('Page.frameNavigated', {id: 2}); + puppeteerSession.emit('Page.frameNavigated', mockNavigated2); expect(callback).toHaveBeenCalledTimes(2); - expect(callback).toHaveBeenCalledWith({id: 2}); + expect(callback).toHaveBeenCalledWith(mockNavigated2); }); it('off', async () => { const callback = fnAny(); session.on('Page.frameNavigated', callback); - puppeteerSession.emit('Page.frameNavigated', {id: 1}); + puppeteerSession.emit('Page.frameNavigated', mockNavigated1); expect(callback).toHaveBeenCalledTimes(1); - expect(callback).toHaveBeenCalledWith({id: 1}); + expect(callback).toHaveBeenCalledWith(mockNavigated1); session.off('Page.frameNavigated', callback); - puppeteerSession.emit('Page.frameNavigated', {id: 2}); + puppeteerSession.emit('Page.frameNavigated', mockNavigated2); expect(callback).toHaveBeenCalledTimes(1); }); }); diff --git a/core/test/lib/csp-evaluator-test.js b/core/test/lib/csp-evaluator-test.js index 1d4662f27da7..3bd24a8574e9 100644 --- a/core/test/lib/csp-evaluator-test.js +++ b/core/test/lib/csp-evaluator-test.js @@ -43,7 +43,7 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'script-src directive is missing. This can allow the execution of unsafe scripts.' + '`script-src` directive is missing. This can allow the execution of unsafe scripts.' ); }); @@ -55,8 +55,8 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'Missing object-src allows the injection of plugins that execute unsafe scripts. ' + - 'Consider setting object-src to \'none\' if you can.' + 'Missing `object-src` allows the injection of plugins that execute unsafe scripts. ' + + 'Consider setting `object-src` to `\'none\'` if you can.' ); }); @@ -68,9 +68,9 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'Missing base-uri allows injected tags to set the base URL for all ' + + 'Missing `base-uri` allows injected `` tags to set the base URL for all ' + 'relative URLs (e.g. scripts) to an attacker controlled domain. ' + - 'Consider setting base-uri to \'none\' or \'self\'.' + 'Consider setting `base-uri` to `\'none\'` or `\'self\'`.' ); }); @@ -82,7 +82,7 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - '\'unsafe-inline\' allows the execution of unsafe in-page scripts ' + + '`\'unsafe-inline\'` allows the execution of unsafe in-page scripts ' + 'and event handlers. Consider using CSP nonces or hashes to allow scripts individually.' ); }); @@ -124,7 +124,7 @@ describe('getTranslatedDescription', () => { expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( 'Host allowlists can frequently be bypassed. Consider using ' + - 'CSP nonces or hashes instead, along with \'strict-dynamic\' if necessary.' + 'CSP nonces or hashes instead, along with `\'strict-dynamic\'` if necessary.' ); }); @@ -137,7 +137,7 @@ describe('getTranslatedDescription', () => { expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( 'Consider adding https: and http: URL schemes (ignored by browsers ' + - 'supporting \'strict-dynamic\') to be backward compatible with older browsers.' + 'supporting `\'strict-dynamic\'`) to be backward compatible with older browsers.' ); }); @@ -149,7 +149,7 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' + + 'Consider adding `\'unsafe-inline\'` (ignored by browsers supporting ' + 'nonces/hashes) to be backward compatible with older browsers.' ); }); @@ -235,7 +235,7 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'reflected-xss is deprecated since CSP2. ' + + '`reflected-xss` is deprecated since CSP2. ' + 'Please, use the X-XSS-Protection header instead.' ); }); @@ -250,7 +250,7 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'referrer is deprecated since CSP2. ' + + '`referrer` is deprecated since CSP2. ' + 'Please, use the Referrer-Policy header instead.' ); }); @@ -265,7 +265,7 @@ describe('getTranslatedDescription', () => { expect(translated).toHaveLength(1); expect(isIcuMessage(translated[0])).toBeTruthy(); expect(translated[0]).toBeDisplayString( - 'disown-opener is deprecated since CSP3. ' + + '`disown-opener` is deprecated since CSP3. ' + 'Please, use the Cross-Origin-Opener-Policy header instead.' ); }); diff --git a/core/test/results/sample_v2.json b/core/test/results/sample_v2.json index 01dac7b23c55..48d6485e3a2f 100644 --- a/core/test/results/sample_v2.json +++ b/core/test/results/sample_v2.json @@ -51,7 +51,7 @@ "title": "Has a `` tag with `width` or `initial-scale`", "description": "A `` not only optimizes your app for mobile screen sizes, but also prevents [a 300 millisecond delay to user input](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/). [Learn more about using the viewport meta tag](https://developer.chrome.com/docs/lighthouse/pwa/viewport/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "metricSavings": { "INP": 0 @@ -327,8 +327,8 @@ "id": "server-response-time", "title": "Initial server response time was short", "description": "Keep the server response time for the main document short because all other requests depend on it. [Learn more about the Time to First Byte metric](https://developer.chrome.com/docs/lighthouse/performance/time-to-first-byte/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 568.468, "numericUnit": "millisecond", "displayValue": "Root document took 570 ms", @@ -782,7 +782,7 @@ "title": "Avoid multiple page redirects", "description": "Redirects introduce additional delays before the page can be loaded. [Learn how to avoid page redirects](https://developer.chrome.com/docs/lighthouse/performance/redirects/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -1031,8 +1031,8 @@ "id": "mainthread-work-breakdown", "title": "Minimize main-thread work", "description": "Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to minimize main-thread work](https://developer.chrome.com/docs/lighthouse/performance/mainthread-work-breakdown/)", - "score": 0.86, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 2240.732999999998, "numericUnit": "millisecond", "displayValue": "2.2 s", @@ -1096,8 +1096,8 @@ "id": "bootup-time", "title": "Reduce JavaScript execution time", "description": "Consider reducing the time spent parsing, compiling, and executing JS. You may find delivering smaller JS payloads helps with this. [Learn how to reduce Javascript execution time](https://developer.chrome.com/docs/lighthouse/performance/bootup-time/).", - "score": 0.89, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 1324.3129999999999, "numericUnit": "millisecond", "displayValue": "1.3 s", @@ -1179,7 +1179,7 @@ "title": "Preconnect to required origins", "description": "Consider adding `preconnect` or `dns-prefetch` resource hints to establish early connections to important third-party origins. [Learn how to preconnect to required origins](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -1204,7 +1204,7 @@ "title": "All text remains visible during webfont loads", "description": "Leverage the `font-display` CSS feature to ensure text is user-visible while webfonts are loading. [Learn more about `font-display`](https://developer.chrome.com/docs/lighthouse/performance/font-display/).", "score": 1, - "scoreDisplayMode": "binary", + "scoreDisplayMode": "metricSavings", "warnings": [], "details": { "type": "table", @@ -2193,8 +2193,8 @@ "id": "third-party-summary", "title": "Minimize third-party usage", "description": "Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn how to minimize third-party impact](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).", - "score": 1, - "scoreDisplayMode": "binary", + "score": null, + "scoreDisplayMode": "informative", "displayValue": "Third-party code blocked the main thread for 0 ms", "metricSavings": { "TBT": 23.424291876693196 @@ -2274,8 +2274,8 @@ "id": "largest-contentful-paint-element", "title": "Largest Contentful Paint element", "description": "This is the largest contentful element painted within the viewport. [Learn more about the Largest Contentful Paint element](https://developer.chrome.com/docs/lighthouse/performance/lighthouse-largest-contentful-paint/)", - "score": null, - "scoreDisplayMode": "informative", + "score": 0, + "scoreDisplayMode": "metricSavings", "displayValue": "13,320 ms", "metricSavings": { "LCP": 10819.961 @@ -2374,8 +2374,8 @@ "id": "layout-shift-elements", "title": "Avoid large layout shifts", "description": "These DOM elements contribute most to the CLS of the page. [Learn how to improve CLS](https://web.dev/optimize-cls/)", - "score": null, - "scoreDisplayMode": "informative", + "score": 0, + "scoreDisplayMode": "metricSavings", "displayValue": "5 elements found", "metricSavings": { "CLS": 0.13570762803819444 @@ -2502,6 +2502,9 @@ "score": null, "scoreDisplayMode": "informative", "displayValue": "2 long tasks found", + "metricSavings": { + "TBT": 1220.691999999999 + }, "details": { "type": "table", "headings": [ @@ -2669,8 +2672,8 @@ "id": "unsized-images", "title": "Image elements do not have explicit `width` and `height`", "description": "Set an explicit width and height on image elements to reduce layout shifts and improve CLS. [Learn how to set image dimensions](https://web.dev/optimize-cls/#images-without-dimensions)", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "metricSavings": { "CLS": 0 }, @@ -2747,8 +2750,8 @@ "id": "prioritize-lcp-image", "title": "Preload Largest Contentful Paint image", "description": "If the LCP element is dynamically added to the page, you should preload the image in order to improve LCP. [Learn more about preloading LCP elements](https://web.dev/optimize-lcp/#optimize-when-the-resource-is-discovered).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4064,8 +4067,8 @@ "id": "uses-long-cache-ttl", "title": "Serve static assets with an efficient cache policy", "description": "A long cache lifetime can speed up repeat visits to your page. [Learn more about efficient cache policies](https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/).", - "score": 0.02, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 1370996, "numericUnit": "byte", "displayValue": "19 resources found", @@ -4242,8 +4245,8 @@ "id": "total-byte-weight", "title": "Avoids enormous network payloads", "description": "Large network payloads cost users real money and are highly correlated with long load times. [Learn how to reduce payload sizes](https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 1437431, "numericUnit": "byte", "displayValue": "Total size was 1,404 KiB", @@ -4314,7 +4317,7 @@ "title": "Defer offscreen images", "description": "Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. [Learn how to defer offscreen images](https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4346,8 +4349,8 @@ "id": "render-blocking-resources", "title": "Eliminate render-blocking resources", "description": "Resources are blocking the first paint of your page. Consider delivering critical JS/CSS inline and deferring all non-critical JS/styles. [Learn how to eliminate render-blocking resources](https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "Potential savings of 0 ms", @@ -4415,7 +4418,7 @@ "title": "Minify CSS", "description": "Minifying CSS files can reduce network payload sizes. [Learn how to minify CSS](https://developer.chrome.com/docs/lighthouse/performance/unminified-css/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4446,8 +4449,8 @@ "id": "unminified-javascript", "title": "Minify JavaScript", "description": "Minifying JavaScript files can reduce payload sizes and script parse time. [Learn how to minify JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unminified-javascript/).", - "score": 0.43, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 1200, "numericUnit": "millisecond", "displayValue": "Potential savings of 82 KiB", @@ -4503,7 +4506,7 @@ "title": "Reduce unused CSS", "description": "Reduce unused rules from stylesheets and defer CSS not used for above-the-fold content to decrease bytes consumed by network activity. [Learn how to reduce unused CSS](https://developer.chrome.com/docs/lighthouse/performance/unused-css-rules/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4534,8 +4537,8 @@ "id": "unused-javascript", "title": "Reduce unused JavaScript", "description": "Reduce unused JavaScript and defer loading scripts until they are required to decrease bytes consumed by network activity. [Learn how to reduce unused JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unused-javascript/).", - "score": 0.56, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 750, "numericUnit": "millisecond", "displayValue": "Potential savings of 64 KiB", @@ -4605,8 +4608,8 @@ "id": "modern-image-formats", "title": "Serve images in next-gen formats", "description": "Image formats like WebP and AVIF often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more about modern image formats](https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images/).", - "score": 0.69, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 450, "numericUnit": "millisecond", "displayValue": "Potential savings of 76 KiB", @@ -4725,7 +4728,7 @@ "title": "Efficiently encode images", "description": "Optimized images load faster and consume less cellular data. [Learn how to efficiently encode images](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4757,8 +4760,8 @@ "id": "uses-text-compression", "title": "Enable text compression", "description": "Text-based resources should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more about text compression](https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/).", - "score": 0.25, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 2400, "numericUnit": "millisecond", "displayValue": "Potential savings of 143 KiB", @@ -4817,7 +4820,7 @@ "title": "Properly size images", "description": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn how to size images](https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4848,8 +4851,8 @@ "id": "efficient-animated-content", "title": "Use video formats for animated content", "description": "Large GIFs are inefficient for delivering animated content. Consider using MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save network bytes. [Learn more about efficient video formats](https://developer.chrome.com/docs/lighthouse/performance/efficient-animated-content/)", - "score": 0.18, - "scoreDisplayMode": "numeric", + "score": 0, + "scoreDisplayMode": "metricSavings", "numericValue": 3450, "numericUnit": "millisecond", "displayValue": "Potential savings of 666 KiB", @@ -4903,7 +4906,7 @@ "title": "Remove duplicate modules in JavaScript bundles", "description": "Remove large, duplicate JavaScript modules from bundles to reduce unnecessary bytes consumed by network activity. ", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "displayValue": "", @@ -4934,8 +4937,8 @@ "id": "legacy-javascript", "title": "Avoid serving legacy JavaScript to modern browsers", "description": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn how to use modern JavaScript](https://web.dev/publish-modern-javascript/)", - "score": 0.69, - "scoreDisplayMode": "numeric", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "numericValue": 450, "numericUnit": "millisecond", "displayValue": "Potential savings of 26 KiB", @@ -5034,8 +5037,8 @@ "id": "dom-size", "title": "Avoids an excessive DOM size", "description": "A large DOM will increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).", - "score": 1, - "scoreDisplayMode": "numeric", + "score": null, + "scoreDisplayMode": "informative", "numericValue": 153, "numericUnit": "element", "displayValue": "153 elements", @@ -5175,8 +5178,8 @@ "id": "no-document-write", "title": "Avoid `document.write()`", "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn how to avoid document.write()](https://developer.chrome.com/docs/lighthouse/best-practices/no-document-write/).", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [ @@ -5355,7 +5358,7 @@ "title": "Use HTTP/2", "description": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers and multiplexing. [Learn more about HTTP/2](https://developer.chrome.com/docs/lighthouse/best-practices/uses-http2/).", "score": 1, - "scoreDisplayMode": "numeric", + "scoreDisplayMode": "metricSavings", "numericValue": 0, "numericUnit": "millisecond", "metricSavings": { @@ -5374,8 +5377,8 @@ "id": "uses-passive-event-listeners", "title": "Does not use passive listeners to improve scrolling performance", "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more about adopting passive event listeners](https://developer.chrome.com/docs/lighthouse/best-practices/uses-passive-event-listeners/).", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "details": { "type": "table", "headings": [ @@ -5656,8 +5659,8 @@ "id": "bf-cache", "title": "Page prevented back/forward cache restoration", "description": "Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://developer.chrome.com/docs/lighthouse/performance/bf-cache/)", - "score": 0, - "scoreDisplayMode": "binary", + "score": 0.5, + "scoreDisplayMode": "metricSavings", "displayValue": "2 failure reasons", "details": { "type": "table", diff --git a/core/test/scenarios/__snapshots__/api-test-pptr.js.snap b/core/test/scenarios/__snapshots__/api-test-pptr.js.snap index 74bb14f2c64e..60778737c6cc 100644 --- a/core/test/scenarios/__snapshots__/api-test-pptr.js.snap +++ b/core/test/scenarios/__snapshots__/api-test-pptr.js.snap @@ -488,7 +488,6 @@ Array [ "non-composited-animations", "preload-fonts", "third-party-summary", - "unsized-images", "user-timings", ] `; @@ -545,6 +544,7 @@ Array [ exports[`Individual modes API startTimespan should compute results from timespan after page load 2`] = ` Array [ + "bootup-time", "duplicated-javascript", "efficient-animated-content", "legacy-javascript", @@ -556,7 +556,6 @@ Array [ "third-party-summary", "unminified-css", "unminified-javascript", - "unsized-images", "unused-css-rules", "unused-javascript", "user-timings", diff --git a/core/test/scenarios/api-test-pptr.js b/core/test/scenarios/api-test-pptr.js index 5b92af735dbb..b939100cd09c 100644 --- a/core/test/scenarios/api-test-pptr.js +++ b/core/test/scenarios/api-test-pptr.js @@ -164,7 +164,7 @@ describe('Individual modes API', function() { const run = await api.startTimespan(state.page); for (const iframe of page.frames()) { if (iframe.url().includes('/oopif-simple-page.html')) { - iframe.click('button'); + await iframe.click('button'); } } await page.waitForNetworkIdle().catch(() => {}); @@ -180,10 +180,6 @@ describe('Individual modes API', function() { .map((r) => ({url: r.url, sessionTargetType: r.sessionTargetType})) // @ts-expect-error .sort((a, b) => a.url.localeCompare(b.url)); - expect(networkRequests).toHaveLength(10); - expect(networkRequests.filter(r => r.sessionTargetType === 'page')).toHaveLength(2); - expect(networkRequests.filter(r => r.sessionTargetType === 'iframe')).toHaveLength(2); - expect(networkRequests.filter(r => r.sessionTargetType === 'worker')).toHaveLength(6); expect(networkRequests).toMatchInlineSnapshot(` Array [ Object { diff --git a/package.json b/package.json index c347aa8bcfb4..383faf731273 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ "pako": "^2.0.3", "preact": "^10.7.2", "pretty-json-stringify": "^0.0.2", - "puppeteer": "21.1.1", + "puppeteer": "^21.3.6", "resolve": "^1.22.1", "rollup": "^2.52.7", "rollup-plugin-polyfill-node": "^0.12.0", @@ -199,7 +199,7 @@ "open": "^8.4.0", "parse-cache-control": "1.0.1", "ps-list": "^8.0.0", - "puppeteer-core": "21.1.1", + "puppeteer-core": "^21.3.6", "robots-parser": "^3.0.1", "semver": "^5.3.0", "speedline-core": "^1.4.3", diff --git a/proto/lighthouse-result.proto b/proto/lighthouse-result.proto index 6f5dc7c2af2e..183a88b02ce3 100644 --- a/proto/lighthouse-result.proto +++ b/proto/lighthouse-result.proto @@ -310,6 +310,34 @@ message LhrCategory { repeated string supported_modes = 7; } +// The ways an audit score should be interpreted: +enum ScoreDisplayMode { + // Unknown mode. This should not be used. + SCORE_DISPLAY_MODE_UNSPECIFIED = 0; + // Pass/fail audit (0 and 1 are the only possible scores). + binary = 1; + // Scores of 0-1, inclusive. + numeric = 2; + // Audit result score is determined by the metric savings and product score. + // 1 - audit passed + // 0.5 - audit failed and had no metric savings + // 0 - audit failed and had metric savings + metricSavings = 8; + // The audit is an FYI only, and can't be interpreted as pass/fail. Score is + // NaN and should be ignored. + informative = 3; + // The audit turned out to not apply to the page. Score is NaN and should be + // ignored. + not_applicable = 4; + notApplicable = 7; + // The audit exists only to tell you to review something yourself. Score is + // NaN and should be ignored + manual = 5; + // There was an error while running the audit (check `error_message` for + // details). Score is NaN and should be ignored. + error = 6; +} + // Message containing the result of an individual Lighthouse audit. message AuditResult { // The internal audit id @@ -328,29 +356,6 @@ message AuditResult { // This value is nullable, so is a `Value` type google.protobuf.Value score = 4; - // The ways an audit score should be interpreted: - enum ScoreDisplayMode { - // Unknown mode. This should not be used. - SCORE_DISPLAY_MODE_UNSPECIFIED = 0; - // Pass/fail audit (0 and 1 are the only possible scores). - binary = 1; - // Scores of 0-1, inclusive. - numeric = 2; - // The audit is an FYI only, and can't be interpreted as pass/fail. Score is - // NaN and should be ignored. - informative = 3; - // The audit turned out to not apply to the page. Score is NaN and should be - // ignored. - not_applicable = 4; - notApplicable = 7; - // The audit exists only to tell you to review something yourself. Score is - // NaN and should be ignored - manual = 5; - // There was an error while running the audit (check `error_message` for - // details). Score is NaN and should be ignored. - error = 6; - } - // The mode for how the score should be interpreted. ScoreDisplayMode score_display_mode = 5; diff --git a/report/renderer/performance-category-renderer.js b/report/renderer/performance-category-renderer.js index 24740c5a59c2..5d971cab7eb4 100644 --- a/report/renderer/performance-category-renderer.js +++ b/report/renderer/performance-category-renderer.js @@ -234,10 +234,21 @@ export class PerformanceCategoryRenderer extends CategoryRenderer { }); auditImpacts.sort((a, b) => { - // Sort audits by impact, prioritizing those with a higher overallImpact first, - // then falling back to linearImpact, guidance level and score. + // Performance diagnostics should only have score display modes of "informative" and "metricSavings" + // If the score display mode is "metricSavings", the `score` will be a coarse approximation of the overall impact. + // Therefore, it makes sense to sort audits by score first to ensure visual clarity with the score icons. + const scoreA = a.auditRef.result.scoreDisplayMode === 'informative' + ? 100 + : Number(a.auditRef.result.score); + const scoreB = b.auditRef.result.scoreDisplayMode === 'informative' + ? 100 + : Number(b.auditRef.result.score); + if (scoreA !== scoreB) return scoreA - scoreB; + + // Overall impact is the estimated improvement to the performance score if (a.overallImpact !== b.overallImpact) return b.overallImpact - a.overallImpact; + // Fall back to the linear impact if the normal impact is rounded down to 0 if ( a.overallImpact === 0 && b.overallImpact === 0 && a.overallLinearImpact !== b.overallLinearImpact @@ -245,15 +256,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer { return b.overallLinearImpact - a.overallLinearImpact; } - if (a.guidanceLevel !== b.guidanceLevel) return b.guidanceLevel - a.guidanceLevel; - - const scoreA = a.auditRef.result.scoreDisplayMode === 'informative' - ? 100 - : Number(a.auditRef.result.score); - const scoreB = b.auditRef.result.scoreDisplayMode === 'informative' - ? 100 - : Number(b.auditRef.result.score); - return scoreA - scoreB; + // Audits that have no estimated savings should be prioritized by the guidance level + return b.guidanceLevel - a.guidanceLevel; }); if (auditImpacts.length) { diff --git a/report/test/renderer/pwa-category-renderer-test.js b/report/test/renderer/pwa-category-renderer-test.js index e02c4d43601a..98d92f55da74 100644 --- a/report/test/renderer/pwa-category-renderer-test.js +++ b/report/test/renderer/pwa-category-renderer-test.js @@ -100,7 +100,7 @@ describe('PwaCategoryRenderer', () => { // Expect results to all be scorable or n/a for (const auditRef of auditRefs) { - const matcher = expect.stringMatching(/(binary)|(notApplicable)/); + const matcher = expect.stringMatching(/(binary|notApplicable|metricSavings)/); expect(auditRef.result.scoreDisplayMode).toEqual(matcher); } diff --git a/shared/localization/locales/en-US.json b/shared/localization/locales/en-US.json index 0e87fb3aecce..2cf8aee84f97 100644 --- a/shared/localization/locales/en-US.json +++ b/shared/localization/locales/en-US.json @@ -774,7 +774,7 @@ "message": "Syntax" }, "core/audits/csp-xss.js | metaTagMessage": { - "message": "The page contains a CSP defined in a tag. Consider moving the CSP to an HTTP header or defining another strict CSP in an HTTP header." + "message": "The page contains a CSP defined in a `` tag. Consider moving the CSP to an HTTP header or defining another strict CSP in an HTTP header." }, "core/audits/csp-xss.js | noCsp": { "message": "No CSP found in enforcement mode" @@ -1026,7 +1026,7 @@ "message": "Page is loaded in an incognito window" }, "core/audits/installable-manifest.js | manifest-display-not-supported": { - "message": "Manifest 'display' property must be one of 'standalone', 'fullscreen', or 'minimal-ui'" + "message": "Manifest `display` property must be one of `standalone`, `fullscreen`, or `minimal-ui`" }, "core/audits/installable-manifest.js | manifest-display-override-not-supported": { "message": "Manifest contains 'display_override' field, and the first supported display mode must be one of 'standalone', 'fullscreen', or 'minimal-ui'" @@ -1038,7 +1038,7 @@ "message": "Manifest URL changed while the manifest was being fetched." }, "core/audits/installable-manifest.js | manifest-missing-name-or-short-name": { - "message": "Manifest does not contain a 'name' or 'short_name' field" + "message": "Manifest does not contain a `name` or `short_name` field" }, "core/audits/installable-manifest.js | manifest-missing-suitable-icon": { "message": "Manifest does not contain a suitable icon - PNG, SVG or WebP format of at least {value0} px is required, the sizes attribute must be set, and the purpose attribute, if set, must include \"any\"." @@ -2211,25 +2211,25 @@ "message": "Pages that use WebXR are not currently eligible for back/forward cache." }, "core/lib/csp-evaluator.js | allowlistFallback": { - "message": "Consider adding https: and http: URL schemes (ignored by browsers supporting 'strict-dynamic') to be backward compatible with older browsers." + "message": "Consider adding https: and http: URL schemes (ignored by browsers supporting `'strict-dynamic'`) to be backward compatible with older browsers." }, "core/lib/csp-evaluator.js | deprecatedDisownOpener": { - "message": "disown-opener is deprecated since CSP3. Please, use the Cross-Origin-Opener-Policy header instead." + "message": "`disown-opener` is deprecated since CSP3. Please, use the Cross-Origin-Opener-Policy header instead." }, "core/lib/csp-evaluator.js | deprecatedReferrer": { - "message": "referrer is deprecated since CSP2. Please, use the Referrer-Policy header instead." + "message": "`referrer` is deprecated since CSP2. Please, use the Referrer-Policy header instead." }, "core/lib/csp-evaluator.js | deprecatedReflectedXSS": { - "message": "reflected-xss is deprecated since CSP2. Please, use the X-XSS-Protection header instead." + "message": "`reflected-xss` is deprecated since CSP2. Please, use the X-XSS-Protection header instead." }, "core/lib/csp-evaluator.js | missingBaseUri": { - "message": "Missing base-uri allows injected tags to set the base URL for all relative URLs (e.g. scripts) to an attacker controlled domain. Consider setting base-uri to 'none' or 'self'." + "message": "Missing `base-uri` allows injected `` tags to set the base URL for all relative URLs (e.g. scripts) to an attacker controlled domain. Consider setting `base-uri` to `'none'` or `'self'`." }, "core/lib/csp-evaluator.js | missingObjectSrc": { - "message": "Missing object-src allows the injection of plugins that execute unsafe scripts. Consider setting object-src to 'none' if you can." + "message": "Missing `object-src` allows the injection of plugins that execute unsafe scripts. Consider setting `object-src` to `'none'` if you can." }, "core/lib/csp-evaluator.js | missingScriptSrc": { - "message": "script-src directive is missing. This can allow the execution of unsafe scripts." + "message": "`script-src` directive is missing. This can allow the execution of unsafe scripts." }, "core/lib/csp-evaluator.js | missingSemicolon": { "message": "Did you forget the semicolon? {keyword} seems to be a directive, not a keyword." @@ -2250,10 +2250,10 @@ "message": "No CSP configures a reporting destination. This makes it difficult to maintain the CSP over time and monitor for any breakages." }, "core/lib/csp-evaluator.js | reportToOnly": { - "message": "The reporting destination is only configured via the report-to directive. This directive is only supported in Chromium-based browsers so it is recommended to also use a report-uri directive." + "message": "The reporting destination is only configured via the report-to directive. This directive is only supported in Chromium-based browsers so it is recommended to also use a `report-uri` directive." }, "core/lib/csp-evaluator.js | strictDynamic": { - "message": "Host allowlists can frequently be bypassed. Consider using CSP nonces or hashes instead, along with 'strict-dynamic' if necessary." + "message": "Host allowlists can frequently be bypassed. Consider using CSP nonces or hashes instead, along with `'strict-dynamic'` if necessary." }, "core/lib/csp-evaluator.js | unknownDirective": { "message": "Unknown CSP directive." @@ -2262,10 +2262,10 @@ "message": "{keyword} seems to be an invalid keyword." }, "core/lib/csp-evaluator.js | unsafeInline": { - "message": "'unsafe-inline' allows the execution of unsafe in-page scripts and event handlers. Consider using CSP nonces or hashes to allow scripts individually." + "message": "`'unsafe-inline'` allows the execution of unsafe in-page scripts and event handlers. Consider using CSP nonces or hashes to allow scripts individually." }, "core/lib/csp-evaluator.js | unsafeInlineFallback": { - "message": "Consider adding 'unsafe-inline' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers." + "message": "Consider adding `'unsafe-inline'` (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers." }, "core/lib/deprecation-description.js | feature": { "message": "Check the feature status page for more details." diff --git a/shared/localization/locales/en-XL.json b/shared/localization/locales/en-XL.json index f6bedacd9b2e..466442cd41ad 100644 --- a/shared/localization/locales/en-XL.json +++ b/shared/localization/locales/en-XL.json @@ -774,7 +774,7 @@ "message": "Ŝýn̂t́âx́" }, "core/audits/csp-xss.js | metaTagMessage": { - "message": "T̂h́ê ṕâǵê ćôńt̂áîńŝ á ĈŚP̂ d́êf́îńêd́ îń â t́âǵ. Ĉón̂śîd́êŕ m̂óv̂ín̂ǵ t̂h́ê ĆŜṔ t̂ó âń ĤT́T̂Ṕ ĥéâd́êŕ ôŕ d̂éf̂ín̂ín̂ǵ âńôt́ĥér̂ śt̂ŕîćt̂ ĆŜṔ îń âń ĤT́T̂Ṕ ĥéâd́êŕ." + "message": "T̂h́ê ṕâǵê ćôńt̂áîńŝ á ĈŚP̂ d́êf́îńêd́ îń â `` t́âǵ. Ĉón̂śîd́êŕ m̂óv̂ín̂ǵ t̂h́ê ĆŜṔ t̂ó âń ĤT́T̂Ṕ ĥéâd́êŕ ôŕ d̂éf̂ín̂ín̂ǵ âńôt́ĥér̂ śt̂ŕîćt̂ ĆŜṔ îń âń ĤT́T̂Ṕ ĥéâd́êŕ." }, "core/audits/csp-xss.js | noCsp": { "message": "N̂ó ĈŚP̂ f́ôún̂d́ îń êńf̂ór̂ćêḿêńt̂ ḿôd́ê" @@ -1026,7 +1026,7 @@ "message": "P̂áĝé îś l̂óâd́êd́ îń âń îńĉóĝńît́ô ẃîńd̂óŵ" }, "core/audits/installable-manifest.js | manifest-display-not-supported": { - "message": "M̂án̂íf̂éŝt́ 'd̂íŝṕl̂áŷ' ṕr̂óp̂ér̂t́ŷ ḿûśt̂ b́ê ón̂é ôf́ 'ŝt́âńd̂ál̂ón̂é', 'f̂úl̂ĺŝćr̂éêń', ôŕ 'm̂ín̂ím̂ál̂-úî'" + "message": "M̂án̂íf̂éŝt́ `display` p̂ŕôṕêŕt̂ý m̂úŝt́ b̂é ôńê óf̂ `standalone`, `fullscreen`, ór̂ `minimal-ui`" }, "core/audits/installable-manifest.js | manifest-display-override-not-supported": { "message": "M̂án̂íf̂éŝt́ ĉón̂t́âín̂ś 'd̂íŝṕl̂áŷ_óv̂ér̂ŕîd́ê' f́îél̂d́, âńd̂ t́ĥé f̂ír̂śt̂ śûṕp̂ór̂t́êd́ d̂íŝṕl̂áŷ ḿôd́ê ḿûśt̂ b́ê ón̂é ôf́ 'ŝt́âńd̂ál̂ón̂é', 'f̂úl̂ĺŝćr̂éêń', ôŕ 'm̂ín̂ím̂ál̂-úî'" @@ -1038,7 +1038,7 @@ "message": "M̂án̂íf̂éŝt́ ÛŔL̂ ćĥán̂ǵêd́ ŵh́îĺê t́ĥé m̂án̂íf̂éŝt́ ŵáŝ b́êín̂ǵ f̂ét̂ćĥéd̂." }, "core/audits/installable-manifest.js | manifest-missing-name-or-short-name": { - "message": "M̂án̂íf̂éŝt́ d̂óêś n̂ót̂ ćôńt̂áîń â 'ńâḿê' ór̂ 'śĥór̂t́_n̂ám̂é' f̂íêĺd̂" + "message": "M̂án̂íf̂éŝt́ d̂óêś n̂ót̂ ćôńt̂áîń â `name` ór̂ `short_name` f́îél̂d́" }, "core/audits/installable-manifest.js | manifest-missing-suitable-icon": { "message": "M̂án̂íf̂éŝt́ d̂óêś n̂ót̂ ćôńt̂áîń â śûít̂áb̂ĺê íĉón̂ - ṔN̂Ǵ, ŜV́Ĝ ór̂ Ẃêb́P̂ f́ôŕm̂át̂ óf̂ át̂ ĺêáŝt́ {value0} p̂x́ îś r̂éq̂úîŕêd́, t̂h́ê śîźêś ât́t̂ŕîb́ût́ê ḿûśt̂ b́ê śêt́, âńd̂ t́ĥé p̂úr̂ṕôśê át̂t́r̂íb̂út̂é, îf́ ŝét̂, ḿûśt̂ ín̂ćl̂úd̂é \"âńŷ\"." @@ -2211,25 +2211,25 @@ "message": "P̂áĝéŝ t́ĥát̂ úŝé Ŵéb̂X́R̂ ár̂é n̂ót̂ ćûŕr̂én̂t́l̂ý êĺîǵîb́l̂é f̂ór̂ b́âćk̂/f́ôŕŵár̂d́ ĉáĉh́ê." }, "core/lib/csp-evaluator.js | allowlistFallback": { - "message": "Ĉón̂śîd́êŕ âd́d̂ín̂ǵ ĥt́t̂ṕŝ: án̂d́ ĥt́t̂ṕ: ÛŔL̂ śĉh́êḿêś (îǵn̂ór̂éd̂ b́ŷ b́r̂óŵśêŕŝ śûṕp̂ór̂t́îńĝ 'śt̂ŕîćt̂-d́ŷńâḿîć') t̂ó b̂é b̂áĉḱŵár̂d́ ĉóm̂ṕât́îb́l̂é ŵít̂h́ ôĺd̂ér̂ b́r̂óŵśêŕŝ." + "message": "Ĉón̂śîd́êŕ âd́d̂ín̂ǵ ĥt́t̂ṕŝ: án̂d́ ĥt́t̂ṕ: ÛŔL̂ śĉh́êḿêś (îǵn̂ór̂éd̂ b́ŷ b́r̂óŵśêŕŝ śûṕp̂ór̂t́îńĝ `'strict-dynamic'`) t́ô b́ê b́âćk̂ẃâŕd̂ ćôḿp̂át̂íb̂ĺê ẃît́ĥ ól̂d́êŕ b̂ŕôẃŝér̂ś." }, "core/lib/csp-evaluator.js | deprecatedDisownOpener": { - "message": "d̂íŝóŵń-ôṕêńêŕ îś d̂ép̂ŕêćât́êd́ ŝín̂ćê ĆŜṔ3. P̂ĺêáŝé, ûśê t́ĥé Ĉŕôśŝ-Ór̂íĝín̂-Óp̂én̂ér̂-Ṕôĺîćŷ h́êád̂ér̂ ín̂śt̂éâd́." + "message": "`disown-opener` îś d̂ép̂ŕêćât́êd́ ŝín̂ćê ĆŜṔ3. P̂ĺêáŝé, ûśê t́ĥé Ĉŕôśŝ-Ór̂íĝín̂-Óp̂én̂ér̂-Ṕôĺîćŷ h́êád̂ér̂ ín̂śt̂éâd́." }, "core/lib/csp-evaluator.js | deprecatedReferrer": { - "message": "r̂éf̂ér̂ŕêŕ îś d̂ép̂ŕêćât́êd́ ŝín̂ćê ĆŜṔ2. P̂ĺêáŝé, ûśê t́ĥé R̂éf̂ér̂ŕêŕ-P̂ól̂íĉý ĥéâd́êŕ îńŝt́êád̂." + "message": "`referrer` îś d̂ép̂ŕêćât́êd́ ŝín̂ćê ĆŜṔ2. P̂ĺêáŝé, ûśê t́ĥé R̂éf̂ér̂ŕêŕ-P̂ól̂íĉý ĥéâd́êŕ îńŝt́êád̂." }, "core/lib/csp-evaluator.js | deprecatedReflectedXSS": { - "message": "r̂éf̂ĺêćt̂éd̂-x́ŝś îś d̂ép̂ŕêćât́êd́ ŝín̂ćê ĆŜṔ2. P̂ĺêáŝé, ûśê t́ĥé X̂-X́ŜŚ-P̂ŕôt́êćt̂íôń ĥéâd́êŕ îńŝt́êád̂." + "message": "`reflected-xss` îś d̂ép̂ŕêćât́êd́ ŝín̂ćê ĆŜṔ2. P̂ĺêáŝé, ûśê t́ĥé X̂-X́ŜŚ-P̂ŕôt́êćt̂íôń ĥéâd́êŕ îńŝt́êád̂." }, "core/lib/csp-evaluator.js | missingBaseUri": { - "message": "M̂íŝśîńĝ b́âśê-úr̂í âĺl̂óŵś îńĵéĉt́êd́ t̂áĝś t̂ó ŝét̂ t́ĥé b̂áŝé ÛŔL̂ f́ôŕ âĺl̂ ŕêĺât́îv́ê ÚR̂Ĺŝ (é.ĝ. śĉŕîṕt̂ś) t̂ó âń ât́t̂áĉḱêŕ ĉón̂t́r̂ól̂ĺêd́ d̂óm̂áîń. Ĉón̂śîd́êŕ ŝét̂t́îńĝ b́âśê-úr̂í t̂ó 'n̂ón̂é' ôŕ 'ŝél̂f́'." + "message": "M̂íŝśîńĝ `base-uri` ál̂ĺôẃŝ ín̂j́êćt̂éd̂ `` t́âǵŝ t́ô śêt́ t̂h́ê b́âśê ÚR̂Ĺ f̂ór̂ ál̂ĺ r̂él̂át̂ív̂é ÛŔL̂ś (ê.ǵ. ŝćr̂íp̂t́ŝ) t́ô án̂ át̂t́âćk̂ér̂ ćôńt̂ŕôĺl̂éd̂ d́ôḿâín̂. Ćôńŝíd̂ér̂ śêt́t̂ín̂ǵ `base-uri` t̂ó `'none'` ôŕ `'self'`." }, "core/lib/csp-evaluator.js | missingObjectSrc": { - "message": "M̂íŝśîńĝ ób̂j́êćt̂-śr̂ć âĺl̂óŵś t̂h́ê ín̂j́êćt̂íôń ôf́ p̂ĺûǵîńŝ t́ĥát̂ éx̂éĉút̂é ûńŝáf̂é ŝćr̂íp̂t́ŝ. Ćôńŝíd̂ér̂ śêt́t̂ín̂ǵ ôb́ĵéĉt́-ŝŕĉ t́ô 'ńôńê' íf̂ ýôú ĉán̂." + "message": "M̂íŝśîńĝ `object-src` ál̂ĺôẃŝ t́ĥé îńĵéĉt́îón̂ óf̂ ṕl̂úĝín̂ś t̂h́ât́ êx́êćût́ê ún̂śâf́ê śĉŕîṕt̂ś. Ĉón̂śîd́êŕ ŝét̂t́îńĝ `object-src` t́ô `'none'` íf̂ ýôú ĉán̂." }, "core/lib/csp-evaluator.js | missingScriptSrc": { - "message": "ŝćr̂íp̂t́-ŝŕĉ d́îŕêćt̂ív̂é îś m̂íŝśîńĝ. T́ĥíŝ ćâń âĺl̂óŵ t́ĥé êx́êćût́îón̂ óf̂ ún̂śâf́ê śĉŕîṕt̂ś." + "message": "`script-src` d̂ír̂éĉt́îv́ê íŝ ḿîśŝín̂ǵ. T̂h́îś ĉán̂ ál̂ĺôẃ t̂h́ê éx̂éĉút̂íôń ôf́ ûńŝáf̂é ŝćr̂íp̂t́ŝ." }, "core/lib/csp-evaluator.js | missingSemicolon": { "message": "D̂íd̂ ýôú f̂ór̂ǵêt́ t̂h́ê śêḿîćôĺôń? {keyword} ŝéêḿŝ t́ô b́ê á d̂ír̂éĉt́îv́ê, ńôt́ â ḱêýŵór̂d́." @@ -2250,10 +2250,10 @@ "message": "N̂ó ĈŚP̂ ćôńf̂íĝúr̂éŝ á r̂ép̂ór̂t́îńĝ d́êśt̂ín̂át̂íôń. T̂h́îś m̂ák̂éŝ ít̂ d́îf́f̂íĉúl̂t́ t̂ó m̂áîńt̂áîń t̂h́ê ĆŜṔ ôv́êŕ t̂ím̂é âńd̂ ḿôńît́ôŕ f̂ór̂ án̂ý b̂ŕêák̂áĝéŝ." }, "core/lib/csp-evaluator.js | reportToOnly": { - "message": "T̂h́ê ŕêṕôŕt̂ín̂ǵ d̂éŝt́îńât́îón̂ íŝ ón̂ĺŷ ćôńf̂íĝúr̂éd̂ v́îá t̂h́ê ŕêṕôŕt̂-t́ô d́îŕêćt̂ív̂é. T̂h́îś d̂ír̂éĉt́îv́ê íŝ ón̂ĺŷ śûṕp̂ór̂t́êd́ îń Ĉh́r̂óm̂íûḿ-b̂áŝéd̂ b́r̂óŵśêŕŝ śô ít̂ íŝ ŕêćôḿm̂én̂d́êd́ t̂ó âĺŝó ûśê á r̂ép̂ór̂t́-ûŕî d́îŕêćt̂ív̂é." + "message": "T̂h́ê ŕêṕôŕt̂ín̂ǵ d̂éŝt́îńât́îón̂ íŝ ón̂ĺŷ ćôńf̂íĝúr̂éd̂ v́îá t̂h́ê ŕêṕôŕt̂-t́ô d́îŕêćt̂ív̂é. T̂h́îś d̂ír̂éĉt́îv́ê íŝ ón̂ĺŷ śûṕp̂ór̂t́êd́ îń Ĉh́r̂óm̂íûḿ-b̂áŝéd̂ b́r̂óŵśêŕŝ śô ít̂ íŝ ŕêćôḿm̂én̂d́êd́ t̂ó âĺŝó ûśê á `report-uri` d̂ír̂éĉt́îv́ê." }, "core/lib/csp-evaluator.js | strictDynamic": { - "message": "Ĥóŝt́ âĺl̂óŵĺîśt̂ś ĉán̂ f́r̂éq̂úêńt̂ĺŷ b́ê b́ŷṕâśŝéd̂. Ćôńŝíd̂ér̂ úŝín̂ǵ ĈŚP̂ ńôńĉéŝ ór̂ h́âśĥéŝ ín̂śt̂éâd́, âĺôńĝ ẃît́ĥ 'śt̂ŕîćt̂-d́ŷńâḿîć' îf́ n̂éĉéŝśâŕŷ." + "message": "Ĥóŝt́ âĺl̂óŵĺîśt̂ś ĉán̂ f́r̂éq̂úêńt̂ĺŷ b́ê b́ŷṕâśŝéd̂. Ćôńŝíd̂ér̂ úŝín̂ǵ ĈŚP̂ ńôńĉéŝ ór̂ h́âśĥéŝ ín̂śt̂éâd́, âĺôńĝ ẃît́ĥ `'strict-dynamic'` íf̂ ńêćêśŝár̂ý." }, "core/lib/csp-evaluator.js | unknownDirective": { "message": "Ûńk̂ńôẃn̂ ĆŜṔ d̂ír̂éĉt́îv́ê." @@ -2262,10 +2262,10 @@ "message": "{keyword} ŝéêḿŝ t́ô b́ê án̂ ín̂v́âĺîd́ k̂éŷẃôŕd̂." }, "core/lib/csp-evaluator.js | unsafeInline": { - "message": "'ûńŝáf̂é-îńl̂ín̂é' âĺl̂óŵś t̂h́ê éx̂éĉút̂íôń ôf́ ûńŝáf̂é îń-p̂áĝé ŝćr̂íp̂t́ŝ án̂d́ êv́êńt̂ h́âńd̂ĺêŕŝ. Ćôńŝíd̂ér̂ úŝín̂ǵ ĈŚP̂ ńôńĉéŝ ór̂ h́âśĥéŝ t́ô ál̂ĺôẃ ŝćr̂íp̂t́ŝ ín̂d́îv́îd́ûál̂ĺŷ." + "message": "`'unsafe-inline'` âĺl̂óŵś t̂h́ê éx̂éĉút̂íôń ôf́ ûńŝáf̂é îń-p̂áĝé ŝćr̂íp̂t́ŝ án̂d́ êv́êńt̂ h́âńd̂ĺêŕŝ. Ćôńŝíd̂ér̂ úŝín̂ǵ ĈŚP̂ ńôńĉéŝ ór̂ h́âśĥéŝ t́ô ál̂ĺôẃ ŝćr̂íp̂t́ŝ ín̂d́îv́îd́ûál̂ĺŷ." }, "core/lib/csp-evaluator.js | unsafeInlineFallback": { - "message": "Ĉón̂śîd́êŕ âd́d̂ín̂ǵ 'ûńŝáf̂é-îńl̂ín̂é' (îǵn̂ór̂éd̂ b́ŷ b́r̂óŵśêŕŝ śûṕp̂ór̂t́îńĝ ńôńĉéŝ/h́âśĥéŝ) t́ô b́ê b́âćk̂ẃâŕd̂ ćôḿp̂át̂íb̂ĺê ẃît́ĥ ól̂d́êŕ b̂ŕôẃŝér̂ś." + "message": "Ĉón̂śîd́êŕ âd́d̂ín̂ǵ `'unsafe-inline'` (îǵn̂ór̂éd̂ b́ŷ b́r̂óŵśêŕŝ śûṕp̂ór̂t́îńĝ ńôńĉéŝ/h́âśĥéŝ) t́ô b́ê b́âćk̂ẃâŕd̂ ćôḿp̂át̂íb̂ĺê ẃît́ĥ ól̂d́êŕ b̂ŕôẃŝér̂ś." }, "core/lib/deprecation-description.js | feature": { "message": "Ĉh́êćk̂ t́ĥé f̂éât́ûŕê śt̂át̂úŝ ṕâǵê f́ôŕ m̂ór̂é d̂ét̂áîĺŝ." diff --git a/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts b/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts index e1561888b511..89389467ff51 100644 --- a/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts +++ b/third-party/devtools-tests/e2e/lighthouse/navigation_test.ts @@ -131,6 +131,7 @@ describe('Navigation', async function() { 'maskable-icon', 'document-title', 'html-has-lang', + 'render-blocking-resources', 'meta-description', ]); diff --git a/types/audit.d.ts b/types/audit.d.ts index ba7f761f4acc..9df7851432be 100644 --- a/types/audit.d.ts +++ b/types/audit.d.ts @@ -90,6 +90,8 @@ declare module Audit { metricSavings?: MetricSavings; /** Score details including p10 and median for calculating an audit's log-normal score. */ scoringOptions?: ScoreOptions; + /** A string identifying how the score should be interpreted for display. Overrides audit meta `scoreDisplayMode` if defined. */ + scoreDisplayMode?: AuditResult.ScoreDisplayMode; } /** The Audit.Product type for audits that do not return a `numericValue`. */ diff --git a/types/internal/rxjs.d.ts b/types/internal/rxjs.d.ts index af8ea04083d2..850eddaf38aa 100644 --- a/types/internal/rxjs.d.ts +++ b/types/internal/rxjs.d.ts @@ -22,5 +22,8 @@ declare module 'rxjs' { export const tap: any; export const throwIfEmpty: any; export const firstValueFrom: any; + export const delay: any; + export const startWith: any; + export const switchMap: any; } diff --git a/types/lhr/audit-result.d.ts b/types/lhr/audit-result.d.ts index b206e0b713a0..ce84f6cb2c2e 100644 --- a/types/lhr/audit-result.d.ts +++ b/types/lhr/audit-result.d.ts @@ -12,6 +12,13 @@ interface ScoreDisplayModes { NUMERIC: 'numeric'; /** Pass/fail audit (0 and 1 are only possible scores). */ BINARY: 'binary'; + /** + * Audit result score is determined by the metric savings and product score: + * 1 - audit passed based on product score + * 0.5 - audit failed and had no metric savings + * 0 - audit failed and had metric savings + */ + METRIC_SAVINGS: 'metricSavings'; /** The audit exists only to tell you to review something yourself. Score is null and should be ignored. */ MANUAL: 'manual'; /** The audit is an FYI only, and can't be interpreted as pass/fail. Score is null and should be ignored. */ diff --git a/yarn.lock b/yarn.lock index 98f75080534a..9de024759bdd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1089,15 +1089,15 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@puppeteer/browsers@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.7.0.tgz#714a25ad6963f5478e36004ea7eda254870a4659" - integrity sha512-sl7zI0IkbQGak/+IE3VEEZab5SSOlI5F6558WvzWGC1n3+C722rfewC1ZIkcF9dsoGSsxhsONoseVlNQG4wWvQ== +"@puppeteer/browsers@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.7.1.tgz#04f1e3aec4b87f50a7acc8f64be2149bda014f0a" + integrity sha512-nIb8SOBgDEMFY2iS2MdnUZOg2ikcYchRrBoF+wtdjieRFKR2uGRipHY/oFLo+2N6anDualyClPzGywTHRGrLfw== dependencies: debug "4.3.4" extract-zip "2.0.1" progress "2.0.3" - proxy-agent "6.3.0" + proxy-agent "6.3.1" tar-fs "3.0.4" unbzip2-stream "1.4.3" yargs "17.7.1" @@ -1689,7 +1689,7 @@ agent-base@6: dependencies: debug "4" -agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: +agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== @@ -2284,12 +2284,13 @@ chrome-launcher@^1.0.0: is-wsl "^2.2.0" lighthouse-logger "^2.0.1" -chromium-bidi@0.4.22: - version "0.4.22" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.22.tgz#625dab72946e177f538da2d2b8a681652ef916da" - integrity sha512-wR7Y9Ioez+cNXT4ZP7VNM1HRTljpNnMSLw4/RnwhhZUP4yCU7kIQND00YiktuHekch68jklGPK1q9Jkb29+fQg== +chromium-bidi@0.4.28: + version "0.4.28" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.28.tgz#05befef4f3f19003198237245780d1c60e6f4dbc" + integrity sha512-2HZ74QlAApJrEwcGlU/sUu0s4VS+FI3CJ09Toc9aE9VemMyhHZXeaROQgJKNRaYMUTUx6qIv1cLBs3F+vfgjSw== dependencies: mitt "3.0.1" + urlpattern-polyfill "9.0.0" ci-info@^3.2.0: version "3.3.1" @@ -2618,14 +2619,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" - integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== +cosmiconfig@8.3.6: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== dependencies: - import-fresh "^3.2.1" + import-fresh "^3.3.0" js-yaml "^4.1.0" - parse-json "^5.0.0" + parse-json "^5.2.0" path-type "^4.0.0" cosmiconfig@^5.1.0: @@ -2871,7 +2872,7 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -devtools-protocol@0.0.1159816, devtools-protocol@0.0.1200039: +devtools-protocol@0.0.1179426, devtools-protocol@0.0.1200039: version "0.0.1200039" resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1200039.tgz#5a546df3edff52e6226abfc26f559e05647c956b" integrity sha512-ZQCB/hbFmu23fLrR3OX3FEJ56dBHXBO+OrnXBtutZzJmIRWycDb7Yz4aO7BiFF9moUgBS+Xo7+EAWZhZraOUfg== @@ -4154,10 +4155,10 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz#75cb70d04811685667183b31ab158d006750418a" - integrity sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw== +https-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== dependencies: agent-base "^7.0.2" debug "4" @@ -4220,7 +4221,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -5659,19 +5660,19 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz#db42120c64292685dafaf2bd921e223c56bfb13b" - integrity sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA== +pac-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" + integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== dependencies: "@tootallnate/quickjs-emscripten" "^0.23.0" agent-base "^7.0.2" debug "^4.3.4" get-uri "^6.0.1" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" pac-resolver "^7.0.0" - socks-proxy-agent "^8.0.1" + socks-proxy-agent "^8.0.2" pac-resolver@^7.0.0: version "7.0.0" @@ -5712,7 +5713,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -5926,19 +5927,19 @@ protobufjs@^6.10.0: "@types/node" ">=13.7.0" long "^4.0.0" -proxy-agent@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.0.tgz#72f7bb20eb06049db79f7f86c49342c34f9ba08d" - integrity sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og== +proxy-agent@6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.1.tgz#40e7b230552cf44fd23ffaf7c59024b692612687" + integrity sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ== dependencies: agent-base "^7.0.2" debug "^4.3.4" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" lru-cache "^7.14.1" - pac-proxy-agent "^7.0.0" + pac-proxy-agent "^7.0.1" proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.1" + socks-proxy-agent "^8.0.2" proxy-from-env@^1.1.0: version "1.1.0" @@ -5968,26 +5969,26 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer-core@21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.1.1.tgz#59be20b6f69acc2139ba2d9e02a33793b59254ff" - integrity sha512-Tlcajcf44zwfa9Sbwv3T8BtaNMJ69wtpHIxwl2NOBTyTK3D1wppQovXTjfw0TDOm3a16eCfQ+5BMi3vRQ4kuAQ== +puppeteer-core@21.3.6, puppeteer-core@^21.3.6: + version "21.3.6" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.3.6.tgz#5507fafb790692ff887e368de71a1c5a0d08af1e" + integrity sha512-ZH6tjTdRXwW2fx5W3jBbG+yUVQdDfZW1kjfwvWwMzsnKEli5ZwV70Zp97GOebHQHrK8zM3vX5VqI9sd48c9PnQ== dependencies: - "@puppeteer/browsers" "1.7.0" - chromium-bidi "0.4.22" + "@puppeteer/browsers" "1.7.1" + chromium-bidi "0.4.28" cross-fetch "4.0.0" debug "4.3.4" - devtools-protocol "0.0.1159816" - ws "8.13.0" + devtools-protocol "0.0.1179426" + ws "8.14.2" -puppeteer@21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-21.1.1.tgz#48f895dc89f1a8874a0cefcfb05a22f979e6f9a8" - integrity sha512-2TLntjGA4qLrI9/8N0UK/5OoZJ2Ue7QgphN2SD+RsaHiha12AEiVyMGsB+i6LY1IoPAtEgYIjblQ7lw3kWDNRw== +puppeteer@^21.3.6: + version "21.3.6" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-21.3.6.tgz#961a44cd532ab5344ed53d7714aa56b4602ace10" + integrity sha512-ulK9+KLvdaVsG0EKbKyw/DCXCz88rsnrvIJg9tY8AmkGR01AxI4ZJTH9BJl1OE7cLfh2vxjBvY+xfvJod6rfgw== dependencies: - "@puppeteer/browsers" "1.7.0" - cosmiconfig "8.2.0" - puppeteer-core "21.1.1" + "@puppeteer/browsers" "1.7.1" + cosmiconfig "8.3.6" + puppeteer-core "21.3.6" q@^1.5.1: version "1.5.1" @@ -6471,12 +6472,12 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" - integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== +socks-proxy-agent@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad" + integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g== dependencies: - agent-base "^7.0.1" + agent-base "^7.0.2" debug "^4.3.4" socks "^2.7.1" @@ -7139,6 +7140,11 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +urlpattern-polyfill@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz#bc7e386bb12fd7898b58d1509df21d3c29ab3460" + integrity sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g== + use@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" @@ -7350,7 +7356,12 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.13.0, ws@>=7.4.6: +ws@8.14.2: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + +ws@>=7.4.6: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==