From b66b0bf4609562d79b35ada59b356e2c978f77d1 Mon Sep 17 00:00:00 2001 From: Adam Raine <6752989+adamraine@users.noreply.github.com> Date: Wed, 4 Oct 2023 15:26:32 -0700 Subject: [PATCH] core: align performance audit score with metric savings (#15447) --- .../test-definitions/dobetterweb.js | 8 +- .../perf-diagnostics-third-party.js | 2 +- .../perf-diagnostics-unsized-images.js | 2 +- .../smokehouse/test-definitions/perf-fonts.js | 2 +- .../test-definitions/perf-trace-elements.js | 2 +- .../redirects-single-server.js | 2 +- core/audits/audit.js | 19 +- core/audits/bf-cache.js | 1 + core/audits/bootup-time.js | 5 +- .../byte-efficiency/byte-efficiency-audit.js | 2 +- .../byte-efficiency/duplicated-javascript.js | 2 +- .../efficient-animated-content.js | 2 +- .../byte-efficiency/legacy-javascript.js | 2 +- .../byte-efficiency/modern-image-formats.js | 2 +- .../byte-efficiency/offscreen-images.js | 2 +- .../render-blocking-resources.js | 5 +- .../byte-efficiency/total-byte-weight.js | 4 +- core/audits/byte-efficiency/unminified-css.js | 2 +- .../byte-efficiency/unminified-javascript.js | 2 +- .../byte-efficiency/unused-css-rules.js | 2 +- .../byte-efficiency/unused-javascript.js | 2 +- .../byte-efficiency/uses-long-cache-ttl.js | 9 +- .../byte-efficiency/uses-optimized-images.js | 2 +- .../uses-responsive-images-snapshot.js | 1 + .../byte-efficiency/uses-responsive-images.js | 2 +- .../byte-efficiency/uses-text-compression.js | 2 +- core/audits/dobetterweb/dom-size.js | 4 +- core/audits/dobetterweb/no-document-write.js | 1 + core/audits/dobetterweb/uses-http2.js | 5 +- .../uses-passive-event-listeners.js | 1 + core/audits/font-display.js | 1 + .../largest-contentful-paint-element.js | 5 +- core/audits/layout-shift-elements.js | 12 +- core/audits/lcp-lazy-loaded.js | 1 + core/audits/mainthread-work-breakdown.js | 4 +- core/audits/prioritize-lcp-image.js | 5 +- core/audits/redirects.js | 5 +- core/audits/server-response-time.js | 2 + core/audits/third-party-facades.js | 1 + core/audits/third-party-summary.js | 6 +- core/audits/unsized-images.js | 2 +- core/audits/uses-rel-preconnect.js | 5 +- core/audits/uses-rel-preload.js | 5 +- core/audits/viewport.js | 1 + core/audits/work-during-interaction.js | 8 +- core/test/audits/audit-test.js | 51 ++++ .../byte-efficiency-audit-test.js | 22 +- .../uses-long-cache-ttl-test.js | 2 +- .../largest-contentful-paint-element-test.js | 2 +- .../test/audits/layout-shift-elements-test.js | 2 +- core/test/audits/redirects-test.js | 10 +- core/test/audits/unsized-images-test.js | 4 +- .../audits/work-during-interaction-test.js | 1 + .../reports/sample-flow-result.json | 234 +++++++++--------- core/test/results/sample_v2.json | 106 ++++---- .../__snapshots__/api-test-pptr.js.snap | 3 +- proto/lighthouse-result.proto | 51 ++-- .../renderer/performance-category-renderer.js | 26 +- .../renderer/pwa-category-renderer-test.js | 2 +- .../e2e/lighthouse/navigation_test.ts | 1 + types/audit.d.ts | 2 + types/lhr/audit-result.d.ts | 7 + 62 files changed, 391 insertions(+), 297 deletions(-) diff --git a/cli/test/smokehouse/test-definitions/dobetterweb.js b/cli/test/smokehouse/test-definitions/dobetterweb.js index 384ce8f514c1..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: [ @@ -563,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/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/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/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/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/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/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/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 47d605d8e48d..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 }, @@ -1873,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 }, @@ -1933,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": "", @@ -2779,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", @@ -2794,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", @@ -2866,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": "", @@ -2899,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": { @@ -2919,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": "", @@ -2951,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": "", @@ -2984,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": "", @@ -3016,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": "", @@ -3048,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": "", @@ -3081,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": "", @@ -3114,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": "", @@ -3146,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": "", @@ -3178,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": "", @@ -3210,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": "", @@ -3241,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", @@ -3331,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", @@ -3448,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": [], @@ -3536,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": [], @@ -3714,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 } }, @@ -8813,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", @@ -8883,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", @@ -9540,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 @@ -9610,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 @@ -9746,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 }, @@ -9830,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", @@ -9985,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", @@ -10057,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": "", @@ -10089,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": "", @@ -10122,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": "", @@ -10154,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": "", @@ -10186,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": "", @@ -10219,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": "", @@ -10252,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": "", @@ -10284,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": "", @@ -10316,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": "", @@ -10348,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": "", @@ -10380,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": "", @@ -10424,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": [], @@ -10437,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": [], @@ -10449,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 @@ -10644,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 } }, @@ -12135,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": [ @@ -12421,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 @@ -12579,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 }, @@ -14379,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": [ @@ -14442,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", @@ -17655,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 @@ -17842,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", @@ -17978,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": "", @@ -18128,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", @@ -18193,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", @@ -18282,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": "", @@ -18307,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", @@ -19001,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 @@ -19160,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 }, @@ -19314,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 }, @@ -19393,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": "", @@ -20276,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", @@ -20291,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", @@ -20343,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": "", @@ -20376,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": { @@ -20396,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": "", @@ -20428,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": "", @@ -20461,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": "", @@ -20493,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": "", @@ -20524,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", @@ -20604,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": "", @@ -20637,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": "", @@ -20668,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", @@ -20745,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": "", @@ -20777,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": "", @@ -20808,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": "", @@ -20898,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", @@ -21015,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": [], @@ -21103,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": [], @@ -21338,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/results/sample_v2.json b/core/test/results/sample_v2.json index 80bf627e1fac..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 @@ -2672,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 }, @@ -2750,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": "", @@ -4067,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", @@ -4245,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", @@ -4317,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": "", @@ -4349,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", @@ -4418,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": "", @@ -4449,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", @@ -4506,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": "", @@ -4537,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", @@ -4608,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", @@ -4728,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": "", @@ -4760,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", @@ -4820,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": "", @@ -4851,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", @@ -4906,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": "", @@ -4937,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", @@ -5037,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", @@ -5178,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": [ @@ -5358,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": { @@ -5377,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": [ @@ -5659,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/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/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/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. */