diff --git a/lighthouse-cli/bin.js b/lighthouse-cli/bin.js index c1637015bd69..696d5239a1fd 100644 --- a/lighthouse-cli/bin.js +++ b/lighthouse-cli/bin.js @@ -31,6 +31,7 @@ import {runLighthouse} from './run.js'; import lighthouse from '../lighthouse-core/index.js'; import {askPermission} from './sentry-prompt.js'; import {LH_ROOT} from '../root.js'; +import {getConfigDisplayString} from '../lighthouse-core/fraggle-rock/config/config.js'; const pkg = JSON.parse(fs.readFileSync(LH_ROOT + '/package.json', 'utf-8')); @@ -120,8 +121,13 @@ async function begin() { } if (cliFlags.printConfig) { - const config = await lighthouse.generateConfig(configJson, cliFlags); - process.stdout.write(config.getPrintString()); + if (cliFlags.legacyNavigation) { + const config = await lighthouse.generateLegacyConfig(configJson, cliFlags); + process.stdout.write(config.getPrintString()); + } else { + const config = await lighthouse.generateConfig(configJson, cliFlags); + process.stdout.write(getConfigDisplayString(config)); + } return; } diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap index 08c96c5f3686..5ccfbeeb47e6 100644 --- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap +++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap @@ -1,1138 +1,49 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`CLI Tests print-config should print the default config and exit immediately after 1`] = ` +exports[`CLI Tests print-config fraggle rock should print the overridden config and exit immediately after 1`] = ` Object { - "audits": Array [ - Object { - "path": "is-on-https", - }, - Object { - "path": "service-worker", - }, - Object { - "path": "viewport", - }, - Object { - "path": "metrics/first-contentful-paint", - }, - Object { - "path": "metrics/largest-contentful-paint", - }, - Object { - "path": "metrics/first-meaningful-paint", - }, - Object { - "path": "metrics/speed-index", - }, - Object { - "path": "screenshot-thumbnails", - }, - Object { - "path": "final-screenshot", - }, - Object { - "path": "metrics/total-blocking-time", - }, - Object { - "path": "metrics/max-potential-fid", - }, - Object { - "path": "metrics/cumulative-layout-shift", - }, - Object { - "path": "errors-in-console", - }, - Object { - "path": "server-response-time", - }, - Object { - "path": "metrics/interactive", - }, - Object { - "path": "user-timings", - }, - Object { - "path": "critical-request-chains", - }, - Object { - "path": "redirects", - }, - Object { - "path": "installable-manifest", - }, - Object { - "path": "apple-touch-icon", - }, - Object { - "path": "splash-screen", - }, - Object { - "path": "themed-omnibox", - }, - Object { - "path": "maskable-icon", - }, - Object { - "path": "content-width", - }, - Object { - "path": "image-aspect-ratio", - }, - Object { - "path": "image-size-responsive", - }, - Object { - "path": "preload-fonts", - }, - Object { - "path": "deprecations", - }, - Object { - "path": "mainthread-work-breakdown", - }, - Object { - "path": "bootup-time", - }, - Object { - "path": "uses-rel-preload", - }, - Object { - "path": "uses-rel-preconnect", - }, - Object { - "path": "font-display", - }, - Object { - "path": "diagnostics", - }, - Object { - "path": "network-requests", - }, - Object { - "path": "network-rtt", - }, - Object { - "path": "network-server-latency", - }, - Object { - "path": "main-thread-tasks", - }, - Object { - "path": "metrics", - }, - Object { - "path": "performance-budget", - }, - Object { - "path": "timing-budget", - }, - Object { - "path": "resource-summary", - }, - Object { - "path": "third-party-summary", - }, - Object { - "path": "third-party-facades", - }, - Object { - "path": "largest-contentful-paint-element", - }, - Object { - "path": "lcp-lazy-loaded", - }, - Object { - "path": "layout-shift-elements", - }, - Object { - "path": "long-tasks", - }, - Object { - "path": "no-unload-listeners", - }, - Object { - "path": "non-composited-animations", - }, - Object { - "path": "unsized-images", - }, - Object { - "path": "valid-source-maps", - }, - Object { - "path": "preload-lcp-image", - }, - Object { - "path": "csp-xss", - }, - Object { - "path": "full-page-screenshot", - }, - Object { - "path": "script-treemap-data", - }, - Object { - "path": "manual/pwa-cross-browser", - }, - Object { - "path": "manual/pwa-page-transitions", - }, - Object { - "path": "manual/pwa-each-page-has-url", - }, - Object { - "path": "accessibility/accesskeys", - }, - Object { - "path": "accessibility/aria-allowed-attr", - }, - Object { - "path": "accessibility/aria-command-name", - }, - Object { - "path": "accessibility/aria-hidden-body", - }, - Object { - "path": "accessibility/aria-hidden-focus", - }, - Object { - "path": "accessibility/aria-input-field-name", - }, - Object { - "path": "accessibility/aria-meter-name", - }, - Object { - "path": "accessibility/aria-progressbar-name", - }, - Object { - "path": "accessibility/aria-required-attr", - }, - Object { - "path": "accessibility/aria-required-children", - }, - Object { - "path": "accessibility/aria-required-parent", - }, - Object { - "path": "accessibility/aria-roles", - }, - Object { - "path": "accessibility/aria-toggle-field-name", - }, - Object { - "path": "accessibility/aria-tooltip-name", - }, - Object { - "path": "accessibility/aria-treeitem-name", - }, - Object { - "path": "accessibility/aria-valid-attr-value", - }, - Object { - "path": "accessibility/aria-valid-attr", - }, - Object { - "path": "accessibility/button-name", - }, - Object { - "path": "accessibility/bypass", - }, - Object { - "path": "accessibility/color-contrast", - }, - Object { - "path": "accessibility/definition-list", - }, - Object { - "path": "accessibility/dlitem", - }, - Object { - "path": "accessibility/document-title", - }, - Object { - "path": "accessibility/duplicate-id-active", - }, - Object { - "path": "accessibility/duplicate-id-aria", - }, - Object { - "path": "accessibility/form-field-multiple-labels", - }, - Object { - "path": "accessibility/frame-title", - }, - Object { - "path": "accessibility/heading-order", - }, - Object { - "path": "accessibility/html-has-lang", - }, - Object { - "path": "accessibility/html-lang-valid", - }, - Object { - "path": "accessibility/image-alt", - }, - Object { - "path": "accessibility/input-image-alt", - }, - Object { - "path": "accessibility/label", - }, - Object { - "path": "accessibility/link-name", - }, - Object { - "path": "accessibility/list", - }, - Object { - "path": "accessibility/listitem", - }, - Object { - "path": "accessibility/meta-refresh", - }, - Object { - "path": "accessibility/meta-viewport", - }, - Object { - "path": "accessibility/object-alt", - }, - Object { - "path": "accessibility/tabindex", - }, - Object { - "path": "accessibility/td-headers-attr", - }, - Object { - "path": "accessibility/th-has-data-cells", - }, - Object { - "path": "accessibility/valid-lang", - }, - Object { - "path": "accessibility/video-caption", - }, - Object { - "path": "accessibility/manual/custom-controls-labels", - }, - Object { - "path": "accessibility/manual/custom-controls-roles", - }, - Object { - "path": "accessibility/manual/focus-traps", - }, - Object { - "path": "accessibility/manual/focusable-controls", - }, - Object { - "path": "accessibility/manual/interactive-element-affordance", - }, - Object { - "path": "accessibility/manual/logical-tab-order", - }, - Object { - "path": "accessibility/manual/managed-focus", - }, - Object { - "path": "accessibility/manual/offscreen-content-hidden", - }, - Object { - "path": "accessibility/manual/use-landmarks", - }, - Object { - "path": "accessibility/manual/visual-order-follows-dom", - }, - Object { - "path": "byte-efficiency/uses-long-cache-ttl", - }, - Object { - "path": "byte-efficiency/total-byte-weight", - }, - Object { - "path": "byte-efficiency/offscreen-images", - }, - Object { - "path": "byte-efficiency/render-blocking-resources", - }, - Object { - "path": "byte-efficiency/unminified-css", - }, - Object { - "path": "byte-efficiency/unminified-javascript", - }, - Object { - "path": "byte-efficiency/unused-css-rules", - }, - Object { - "path": "byte-efficiency/unused-javascript", - }, - Object { - "path": "byte-efficiency/modern-image-formats", - }, - Object { - "path": "byte-efficiency/uses-optimized-images", - }, - Object { - "path": "byte-efficiency/uses-text-compression", - }, - Object { - "path": "byte-efficiency/uses-responsive-images", - }, - Object { - "path": "byte-efficiency/efficient-animated-content", - }, - Object { - "path": "byte-efficiency/duplicated-javascript", - }, - Object { - "path": "byte-efficiency/legacy-javascript", - }, - Object { - "path": "dobetterweb/doctype", - }, - Object { - "path": "dobetterweb/charset", - }, - Object { - "path": "dobetterweb/dom-size", - }, - Object { - "path": "dobetterweb/geolocation-on-start", - }, - Object { - "path": "dobetterweb/inspector-issues", - }, - Object { - "path": "dobetterweb/no-document-write", - }, - Object { - "path": "dobetterweb/no-vulnerable-libraries", - }, - Object { - "path": "dobetterweb/js-libraries", - }, - Object { - "path": "dobetterweb/notification-on-start", - }, - Object { - "path": "dobetterweb/password-inputs-can-be-pasted-into", - }, - Object { - "path": "dobetterweb/uses-http2", - }, - Object { - "path": "dobetterweb/uses-passive-event-listeners", - }, - Object { - "path": "seo/meta-description", - }, - Object { - "path": "seo/http-status-code", - }, - Object { - "path": "seo/font-size", - }, - Object { - "path": "seo/link-text", - }, - Object { - "path": "seo/crawlable-anchors", - }, - Object { - "path": "seo/is-crawlable", - }, - Object { - "path": "seo/robots-txt", - }, - Object { - "path": "seo/tap-targets", - }, - Object { - "path": "seo/hreflang", - }, - Object { - "path": "seo/plugins", - }, - Object { - "path": "seo/canonical", - }, - Object { - "path": "seo/manual/structured-data", - }, - ], - "categories": Object { - "accessibility": Object { - "auditRefs": Array [ - Object { - "group": "a11y-navigation", - "id": "accesskeys", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-allowed-attr", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-command-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-hidden-body", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-hidden-focus", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-input-field-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-meter-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-progressbar-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-required-attr", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-required-children", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-required-parent", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-roles", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-toggle-field-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-tooltip-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-treeitem-name", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "aria-valid-attr-value", - "weight": 10, - }, - Object { - "group": "a11y-aria", - "id": "aria-valid-attr", - "weight": 10, - }, - Object { - "group": "a11y-names-labels", - "id": "button-name", - "weight": 10, - }, - Object { - "group": "a11y-navigation", - "id": "bypass", - "weight": 3, - }, - Object { - "group": "a11y-color-contrast", - "id": "color-contrast", - "weight": 3, - }, - Object { - "group": "a11y-tables-lists", - "id": "definition-list", - "weight": 3, - }, - Object { - "group": "a11y-tables-lists", - "id": "dlitem", - "weight": 3, - }, - Object { - "group": "a11y-names-labels", - "id": "document-title", - "weight": 3, - }, - Object { - "group": "a11y-navigation", - "id": "duplicate-id-active", - "weight": 3, - }, - Object { - "group": "a11y-aria", - "id": "duplicate-id-aria", - "weight": 10, - }, - Object { - "group": "a11y-names-labels", - "id": "form-field-multiple-labels", - "weight": 2, - }, - Object { - "group": "a11y-names-labels", - "id": "frame-title", - "weight": 3, - }, - Object { - "group": "a11y-navigation", - "id": "heading-order", - "weight": 2, - }, - Object { - "group": "a11y-language", - "id": "html-has-lang", - "weight": 3, - }, - Object { - "group": "a11y-language", - "id": "html-lang-valid", - "weight": 3, - }, - Object { - "group": "a11y-names-labels", - "id": "image-alt", - "weight": 10, - }, - Object { - "group": "a11y-names-labels", - "id": "input-image-alt", - "weight": 10, - }, - Object { - "group": "a11y-names-labels", - "id": "label", - "weight": 10, - }, - Object { - "group": "a11y-names-labels", - "id": "link-name", - "weight": 3, - }, - Object { - "group": "a11y-tables-lists", - "id": "list", - "weight": 3, - }, - Object { - "group": "a11y-tables-lists", - "id": "listitem", - "weight": 3, - }, - Object { - "group": "a11y-best-practices", - "id": "meta-refresh", - "weight": 10, - }, - Object { - "group": "a11y-best-practices", - "id": "meta-viewport", - "weight": 10, - }, - Object { - "group": "a11y-names-labels", - "id": "object-alt", - "weight": 3, - }, - Object { - "group": "a11y-navigation", - "id": "tabindex", - "weight": 3, - }, - Object { - "group": "a11y-tables-lists", - "id": "td-headers-attr", - "weight": 3, - }, - Object { - "group": "a11y-tables-lists", - "id": "th-has-data-cells", - "weight": 3, - }, - Object { - "group": "a11y-language", - "id": "valid-lang", - "weight": 3, - }, - Object { - "group": "a11y-audio-video", - "id": "video-caption", - "weight": 10, - }, - Object { - "id": "logical-tab-order", - "weight": 0, - }, - Object { - "id": "focusable-controls", - "weight": 0, - }, - Object { - "id": "interactive-element-affordance", - "weight": 0, - }, - Object { - "id": "managed-focus", - "weight": 0, - }, - Object { - "id": "focus-traps", - "weight": 0, - }, - Object { - "id": "custom-controls-labels", - "weight": 0, - }, - Object { - "id": "custom-controls-roles", - "weight": 0, - }, - Object { - "id": "visual-order-follows-dom", - "weight": 0, - }, - Object { - "id": "offscreen-content-hidden", - "weight": 0, - }, - Object { - "id": "use-landmarks", - "weight": 0, - }, - ], - "description": "These checks highlight opportunities to [improve the accessibility of your web app](https://web.dev/lighthouse-accessibility/). Only a subset of accessibility issues can be automatically detected so manual testing is also encouraged.", - "manualDescription": "These items address areas which an automated testing tool cannot cover. Learn more in our guide on [conducting an accessibility review](https://web.dev/how-to-review/).", - "supportedModes": Array [ - "navigation", - "snapshot", - ], - "title": "Accessibility", - }, - "best-practices": Object { - "auditRefs": Array [ - Object { - "group": "best-practices-trust-safety", - "id": "is-on-https", - "weight": 1, - }, - Object { - "group": "best-practices-trust-safety", - "id": "geolocation-on-start", - "weight": 1, - }, - Object { - "group": "best-practices-trust-safety", - "id": "notification-on-start", - "weight": 1, - }, - Object { - "group": "best-practices-trust-safety", - "id": "no-vulnerable-libraries", - "weight": 1, - }, - Object { - "group": "best-practices-trust-safety", - "id": "csp-xss", - "weight": 0, - }, - Object { - "group": "best-practices-ux", - "id": "password-inputs-can-be-pasted-into", - "weight": 1, - }, - Object { - "group": "best-practices-ux", - "id": "image-aspect-ratio", - "weight": 1, - }, - Object { - "group": "best-practices-ux", - "id": "image-size-responsive", - "weight": 1, - }, - Object { - "group": "best-practices-ux", - "id": "preload-fonts", - "weight": 1, - }, - Object { - "group": "best-practices-browser-compat", - "id": "doctype", - "weight": 1, - }, - Object { - "group": "best-practices-browser-compat", - "id": "charset", - "weight": 1, - }, - Object { - "group": "best-practices-general", - "id": "js-libraries", - "weight": 0, - }, - Object { - "group": "best-practices-general", - "id": "deprecations", - "weight": 1, - }, - Object { - "group": "best-practices-general", - "id": "errors-in-console", - "weight": 1, - }, - Object { - "group": "best-practices-general", - "id": "valid-source-maps", - "weight": 0, - }, - Object { - "group": "best-practices-general", - "id": "inspector-issues", - "weight": 1, - }, - ], - "supportedModes": Array [ - "navigation", - "timespan", - "snapshot", - ], - "title": "Best Practices", - }, - "performance": Object { - "auditRefs": Array [ - Object { - "acronym": "FCP", - "group": "metrics", - "id": "first-contentful-paint", - "relevantAudits": Array [ - "server-response-time", - "render-blocking-resources", - "redirects", - "critical-request-chains", - "uses-text-compression", - "uses-rel-preconnect", - "uses-rel-preload", - "font-display", - "unminified-javascript", - "unminified-css", - "unused-css-rules", - ], - "weight": 10, - }, - Object { - "acronym": "TTI", - "group": "metrics", - "id": "interactive", - "weight": 10, - }, - Object { - "acronym": "SI", - "group": "metrics", - "id": "speed-index", - "weight": 10, - }, - Object { - "acronym": "TBT", - "group": "metrics", - "id": "total-blocking-time", - "relevantAudits": Array [ - "long-tasks", - "third-party-summary", - "third-party-facades", - "bootup-time", - "mainthread-work-breakdown", - "dom-size", - "duplicated-javascript", - "legacy-javascript", - "viewport", - ], - "weight": 30, - }, - Object { - "acronym": "LCP", - "group": "metrics", - "id": "largest-contentful-paint", - "relevantAudits": Array [ - "server-response-time", - "render-blocking-resources", - "redirects", - "critical-request-chains", - "uses-text-compression", - "uses-rel-preconnect", - "uses-rel-preload", - "font-display", - "unminified-javascript", - "unminified-css", - "unused-css-rules", - "largest-contentful-paint-element", - "preload-lcp-image", - "unused-javascript", - "efficient-animated-content", - "total-byte-weight", - ], - "weight": 25, - }, - Object { - "acronym": "CLS", - "group": "metrics", - "id": "cumulative-layout-shift", - "relevantAudits": Array [ - "layout-shift-elements", - "non-composited-animations", - "unsized-images", - ], - "weight": 15, - }, - Object { - "group": "hidden", - "id": "max-potential-fid", - "weight": 0, - }, - Object { - "acronym": "FMP", - "group": "hidden", - "id": "first-meaningful-paint", - "weight": 0, - }, - Object { - "id": "render-blocking-resources", - "weight": 0, - }, - Object { - "id": "uses-responsive-images", - "weight": 0, - }, - Object { - "id": "offscreen-images", - "weight": 0, - }, - Object { - "id": "unminified-css", - "weight": 0, - }, - Object { - "id": "unminified-javascript", - "weight": 0, - }, - Object { - "id": "unused-css-rules", - "weight": 0, - }, - Object { - "id": "unused-javascript", - "weight": 0, - }, - Object { - "id": "uses-optimized-images", - "weight": 0, - }, - Object { - "id": "modern-image-formats", - "weight": 0, - }, - Object { - "id": "uses-text-compression", - "weight": 0, - }, - Object { - "id": "uses-rel-preconnect", - "weight": 0, - }, - Object { - "id": "server-response-time", - "weight": 0, - }, - Object { - "id": "redirects", - "weight": 0, - }, - Object { - "id": "uses-rel-preload", - "weight": 0, - }, - Object { - "id": "uses-http2", - "weight": 0, - }, - Object { - "id": "efficient-animated-content", - "weight": 0, - }, - Object { - "id": "duplicated-javascript", - "weight": 0, - }, - Object { - "id": "legacy-javascript", - "weight": 0, - }, - Object { - "id": "preload-lcp-image", - "weight": 0, - }, - Object { - "id": "total-byte-weight", - "weight": 0, - }, - Object { - "id": "uses-long-cache-ttl", - "weight": 0, - }, - Object { - "id": "dom-size", - "weight": 0, - }, - Object { - "id": "critical-request-chains", - "weight": 0, - }, - Object { - "id": "user-timings", - "weight": 0, - }, - Object { - "id": "bootup-time", - "weight": 0, - }, - Object { - "id": "mainthread-work-breakdown", - "weight": 0, - }, - Object { - "id": "font-display", - "weight": 0, - }, - Object { - "id": "resource-summary", - "weight": 0, - }, - Object { - "id": "third-party-summary", - "weight": 0, - }, - Object { - "id": "third-party-facades", - "weight": 0, - }, - Object { - "id": "largest-contentful-paint-element", - "weight": 0, - }, - Object { - "id": "lcp-lazy-loaded", - "weight": 0, - }, - Object { - "id": "layout-shift-elements", - "weight": 0, - }, - Object { - "id": "uses-passive-event-listeners", - "weight": 0, - }, - Object { - "id": "no-document-write", - "weight": 0, - }, - Object { - "id": "long-tasks", - "weight": 0, - }, - Object { - "id": "non-composited-animations", - "weight": 0, - }, - Object { - "id": "unsized-images", - "weight": 0, - }, - Object { - "id": "viewport", - "weight": 0, - }, - Object { - "id": "no-unload-listeners", - "weight": 0, - }, - Object { - "group": "budgets", - "id": "performance-budget", - "weight": 0, - }, - Object { - "group": "budgets", - "id": "timing-budget", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "network-requests", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "network-rtt", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "network-server-latency", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "main-thread-tasks", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "diagnostics", - "weight": 0, - }, + "artifacts": Array [ + Object { + "gatherer": "devtools-log", + "id": "DevtoolsLog", + }, + Object { + "gatherer": "trace", + "id": "Trace", + }, + Object { + "gatherer": "stacks", + "id": "Stacks", + }, + Object { + "gatherer": "devtools-log-compat", + "id": "devtoolsLogs", + }, + Object { + "gatherer": "trace-compat", + "id": "traces", + }, + Object { + "gatherer": "full-page-screenshot", + "id": "FullPageScreenshot", + }, + ], + "audits": Array [ + Object { + "path": "metrics", + }, + Object { + "path": "full-page-screenshot", + }, + ], + "categories": Object { + "performance": Object { + "auditRefs": Array [ Object { "group": "hidden", "id": "metrics", "weight": 0, }, - Object { - "group": "hidden", - "id": "screenshot-thumbnails", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "final-screenshot", - "weight": 0, - }, - Object { - "group": "hidden", - "id": "script-treemap-data", - "weight": 0, - }, ], "supportedModes": Array [ "navigation", @@ -1141,153 +52,6 @@ Object { ], "title": "Performance", }, - "pwa": Object { - "auditRefs": Array [ - Object { - "group": "pwa-installable", - "id": "installable-manifest", - "weight": 2, - }, - Object { - "group": "pwa-optimized", - "id": "service-worker", - "weight": 1, - }, - Object { - "group": "pwa-optimized", - "id": "splash-screen", - "weight": 1, - }, - Object { - "group": "pwa-optimized", - "id": "themed-omnibox", - "weight": 1, - }, - Object { - "group": "pwa-optimized", - "id": "content-width", - "weight": 1, - }, - Object { - "group": "pwa-optimized", - "id": "viewport", - "weight": 2, - }, - Object { - "group": "pwa-optimized", - "id": "apple-touch-icon", - "weight": 1, - }, - Object { - "group": "pwa-optimized", - "id": "maskable-icon", - "weight": 1, - }, - Object { - "id": "pwa-cross-browser", - "weight": 0, - }, - Object { - "id": "pwa-page-transitions", - "weight": 0, - }, - Object { - "id": "pwa-each-page-has-url", - "weight": 0, - }, - ], - "description": "These checks validate the aspects of a Progressive Web App. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist).", - "manualDescription": "These checks are required by the baseline [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually.", - "supportedModes": Array [ - "navigation", - ], - "title": "PWA", - }, - "seo": Object { - "auditRefs": Array [ - Object { - "group": "seo-mobile", - "id": "viewport", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "document-title", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "meta-description", - "weight": 1, - }, - Object { - "group": "seo-crawl", - "id": "http-status-code", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "link-text", - "weight": 1, - }, - Object { - "group": "seo-crawl", - "id": "crawlable-anchors", - "weight": 1, - }, - Object { - "group": "seo-crawl", - "id": "is-crawlable", - "weight": 1, - }, - Object { - "group": "seo-crawl", - "id": "robots-txt", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "image-alt", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "hreflang", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "canonical", - "weight": 1, - }, - Object { - "group": "seo-mobile", - "id": "font-size", - "weight": 1, - }, - Object { - "group": "seo-content", - "id": "plugins", - "weight": 1, - }, - Object { - "group": "seo-mobile", - "id": "tap-targets", - "weight": 1, - }, - Object { - "id": "structured-data", - "weight": 0, - }, - ], - "description": "These checks ensure that your page is following basic search engine optimization advice. There are many additional factors Lighthouse does not score here that may affect your search ranking, including performance on [Core Web Vitals](https://web.dev/learn-web-vitals/). [Learn more](https://support.google.com/webmasters/answer/35769).", - "manualDescription": "Run these additional validators on your site to check additional SEO best practices.", - "supportedModes": Array [ - "navigation", - "snapshot", - ], - "title": "SEO", - }, }, "groups": Object { "a11y-aria": Object { @@ -1371,129 +135,31 @@ Object { "title": "Mobile Friendly", }, }, - "passes": Array [ - Object { + "navigations": Array [ + Object { + "artifacts": Array [ + "DevtoolsLog", + "Trace", + "Stacks", + "devtoolsLogs", + "traces", + "FullPageScreenshot", + ], "blankPage": "about:blank", "blockedUrlPatterns": Array [], "cpuQuietThresholdMs": 1000, - "gatherers": Array [ - Object { - "path": "css-usage", - }, - Object { - "path": "js-usage", - }, - Object { - "path": "viewport-dimensions", - }, - Object { - "path": "console-messages", - }, - Object { - "path": "anchor-elements", - }, - Object { - "path": "image-elements", - }, - Object { - "path": "link-elements", - }, - Object { - "path": "meta-elements", - }, - Object { - "path": "script-elements", - }, - Object { - "path": "scripts", - }, - Object { - "path": "iframe-elements", - }, - Object { - "path": "inputs", - }, - Object { - "path": "main-document-content", - }, - Object { - "path": "global-listeners", - }, - Object { - "path": "dobetterweb/doctype", - }, - Object { - "path": "dobetterweb/domstats", - }, - Object { - "path": "dobetterweb/optimized-images", - }, - Object { - "path": "dobetterweb/password-inputs-with-prevented-paste", - }, - Object { - "path": "dobetterweb/response-compression", - }, - Object { - "path": "dobetterweb/tags-blocking-first-paint", - }, - Object { - "path": "seo/font-size", - }, - Object { - "path": "seo/embedded-content", - }, - Object { - "path": "seo/robots-txt", - }, - Object { - "path": "seo/tap-targets", - }, - Object { - "path": "accessibility", - }, - Object { - "path": "trace-elements", - }, - Object { - "path": "inspector-issues", - }, - Object { - "path": "source-maps", - }, - Object { - "path": "full-page-screenshot", - }, - ], + "disableStorageReset": false, + "disableThrottling": false, + "id": "default", "loadFailureMode": "fatal", "networkQuietThresholdMs": 1000, - "passName": "defaultPass", "pauseAfterFcpMs": 1000, "pauseAfterLoadMs": 1000, - "recordTrace": true, - "useThrottling": true, - }, - Object { - "blankPage": "about:blank", - "blockedUrlPatterns": Array [], - "cpuQuietThresholdMs": 0, - "gatherers": Array [ - Object { - "path": "service-worker", - }, - ], - "loadFailureMode": "ignore", - "networkQuietThresholdMs": 0, - "passName": "offlinePass", - "pauseAfterFcpMs": 0, - "pauseAfterLoadMs": 0, - "recordTrace": false, - "useThrottling": false, }, ], "settings": Object { "additionalTraceCategories": null, - "auditMode": false, + "auditMode": true, "blockedUrlPatterns": null, "budgets": null, "channel": "cli", @@ -1506,10 +172,12 @@ Object { "locale": "en-US", "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, - "onlyAudits": null, + "onlyAudits": Array [ + "metrics", + ], "onlyCategories": null, "output": Array [ - "html", + "json", ], "precomputedLanternData": null, "screenEmulation": Object { @@ -1537,16 +205,16 @@ exports[`CLI Tests print-config should print the overridden config and exit imme Object { "audits": Array [ Object { - "path": "metrics", + "path": "dobetterweb/charset", }, ], "categories": Object { - "performance": Object { + "best-practices": Object { "auditRefs": Array [ Object { - "group": "hidden", - "id": "metrics", - "weight": 0, + "group": "best-practices-browser-compat", + "id": "charset", + "weight": 1, }, ], "supportedModes": Array [ @@ -1554,7 +222,7 @@ Object { "timespan", "snapshot", ], - "title": "Performance", + "title": "Best Practices", }, }, "groups": Object { @@ -1644,13 +312,20 @@ Object { "blankPage": "about:blank", "blockedUrlPatterns": Array [], "cpuQuietThresholdMs": 1000, - "gatherers": Array [], + "gatherers": Array [ + Object { + "path": "meta-elements", + }, + Object { + "path": "main-document-content", + }, + ], "loadFailureMode": "fatal", "networkQuietThresholdMs": 1000, "passName": "defaultPass", "pauseAfterFcpMs": 1000, "pauseAfterLoadMs": 1000, - "recordTrace": true, + "recordTrace": false, "useThrottling": true, }, ], @@ -1670,7 +345,7 @@ Object { "maxWaitForFcp": 30000, "maxWaitForLoad": 45000, "onlyAudits": Array [ - "metrics", + "charset", ], "onlyCategories": null, "output": Array [ diff --git a/lighthouse-cli/test/cli/index-test.js b/lighthouse-cli/test/cli/index-test.js index d9d06de61a3a..8b32f32f8ef7 100644 --- a/lighthouse-cli/test/cli/index-test.js +++ b/lighthouse-cli/test/cli/index-test.js @@ -82,21 +82,31 @@ describe('CLI Tests', function() { }); describe('print-config', () => { - it('should print the default config and exit immediately after', () => { - const ret = spawnSync('node', [indexPath, '--print-config'], {encoding: 'utf8'}); - - const config = JSON.parse(ret.stdout); - assert.strictEqual(config.settings.output[0], 'html'); - assert.strictEqual(config.settings.auditMode, false); - - expect(config).toMatchSnapshot(); + describe('fraggle rock', () => { + it('should print the overridden config and exit immediately after', () => { + const flags = [ + '--print-config', '-A', + '--output', 'json', + '--only-audits', 'metrics', + ]; + const ret = spawnSync('node', [indexPath, ...flags], {encoding: 'utf8'}); + + const config = JSON.parse(ret.stdout); + assert.strictEqual(config.settings.output[0], 'json'); + assert.strictEqual(config.settings.auditMode, true); + // FR config doesn't exclude full-page-screenshot with --only-audits + assert.strictEqual(config.audits.length, 2); + + expect(config).toMatchSnapshot(); + }); }); it('should print the overridden config and exit immediately after', () => { const flags = [ '--print-config', '-A', '--output', 'json', - '--only-audits', 'metrics', + '--only-audits', 'charset', + '--legacy-navigation', ]; const ret = spawnSync('node', [indexPath, ...flags], {encoding: 'utf8'}); diff --git a/lighthouse-core/config/config-helpers.js b/lighthouse-core/config/config-helpers.js index 2d9be28f2fb1..c6ac07ab44bd 100644 --- a/lighthouse-core/config/config-helpers.js +++ b/lighthouse-core/config/config-helpers.js @@ -578,6 +578,20 @@ function deepCloneConfigJson(json) { return cloned; } +/** + * @param {LH.Flags} flags + * @return {LH.Config.FRContext} + */ +function flagsToFRContext(flags) { + return { + configPath: flags?.configPath, + settingsOverrides: flags, + logLevel: flags?.logLevel, + hostname: flags?.hostname, + port: flags?.port, + }; +} + module.exports = { deepClone, deepCloneConfigJson, @@ -589,4 +603,5 @@ module.exports = { resolveGathererToDefn, resolveModulePath, resolveSettings, + flagsToFRContext, }; diff --git a/lighthouse-core/fraggle-rock/config/config.js b/lighthouse-core/fraggle-rock/config/config.js index 2fece7796b58..2241476f990e 100644 --- a/lighthouse-core/fraggle-rock/config/config.js +++ b/lighthouse-core/fraggle-rock/config/config.js @@ -8,6 +8,7 @@ const path = require('path'); const log = require('lighthouse-logger'); const Runner = require('../../runner.js'); +const format = require('../../../shared/localization/format.js'); const defaultConfig = require('./default-config.js'); const {defaultNavigationConfig, nonSimulatedPassConfigOverrides} = require('../../config/constants.js'); // eslint-disable-line max-len const { @@ -279,4 +280,47 @@ async function initializeConfig(configJSON, context) { return {config, warnings}; } -module.exports = {resolveWorkingCopy, initializeConfig}; +/** + * @param {LH.Config.FRConfig} config + * @return {string} + */ +function getConfigDisplayString(config) { + /** @type {LH.Config.FRConfig} */ + const jsonConfig = JSON.parse(JSON.stringify(config)); + + if (jsonConfig.navigations) { + for (const navigation of jsonConfig.navigations) { + for (let i = 0; i < navigation.artifacts.length; ++i) { + // @ts-expect-error Breaking the Config.AnyArtifactDefn type. + navigation.artifacts[i] = navigation.artifacts[i].id; + } + } + } + + if (jsonConfig.artifacts) { + for (const artifactDefn of jsonConfig.artifacts) { + // @ts-expect-error Breaking the Config.AnyArtifactDefn type. + artifactDefn.gatherer = artifactDefn.gatherer.path; + // Dependencies are not declared on Config JSON + artifactDefn.dependencies = undefined; + } + } + + if (jsonConfig.audits) { + for (const auditDefn of jsonConfig.audits) { + // @ts-expect-error Breaking the Config.AuditDefn type. + auditDefn.implementation = undefined; + if (Object.keys(auditDefn.options).length === 0) { + // @ts-expect-error Breaking the Config.AuditDefn type. + auditDefn.options = undefined; + } + } + } + + // Printed config is more useful with localized strings. + format.replaceIcuMessages(jsonConfig, jsonConfig.settings.locale); + + return JSON.stringify(jsonConfig, null, 2); +} + +module.exports = {resolveWorkingCopy, initializeConfig, getConfigDisplayString}; diff --git a/lighthouse-core/index.js b/lighthouse-core/index.js index 4ca5dac2ed93..372ddf094eea 100644 --- a/lighthouse-core/index.js +++ b/lighthouse-core/index.js @@ -11,6 +11,8 @@ const ChromeProtocol = require('./gather/connections/cri.js'); const Config = require('./config/config.js'); const URL = require('./lib/url-shim.js'); const fraggleRock = require('./fraggle-rock/api.js'); +const {initializeConfig} = require('./fraggle-rock/config/config.js'); +const {flagsToFRContext} = require('./config/config-helpers.js'); /** @typedef {import('./gather/connections/connection.js')} Connection */ @@ -38,19 +40,14 @@ const fraggleRock = require('./fraggle-rock/api.js'); * @return {Promise} */ async function lighthouse(url, flags = {}, configJSON, page) { - const configContext = { - configPath: flags.configPath, - settingsOverrides: flags, - logLevel: flags.logLevel, - hostname: flags.hostname, - port: flags.port, - }; + const configContext = flagsToFRContext(flags); return fraggleRock.navigation(url, {page, config: configJSON, configContext}); } /** * Run Lighthouse using the legacy navigation runner. * This is left in place for any clients that don't support FR navigations yet (e.g. Lightrider) + * @deprecated * @param {string=} url The URL to test. Optional if running in auditMode. * @param {LH.Flags=} flags Optional settings for the Lighthouse run. If present, * they will override any settings in the config. @@ -64,7 +61,7 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) { flags.logLevel = flags.logLevel || 'error'; log.setLevel(flags.logLevel); - const config = await generateConfig(configJSON, flags); + const config = await generateLegacyConfig(configJSON, flags); const computedCache = new Map(); const options = {config, computedCache}; const connection = userConnection || new ChromeProtocol(flags.port, flags.hostname); @@ -83,14 +80,32 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) { * not present, the default config is used. * @param {LH.Flags=} flags Optional settings for the Lighthouse run. If present, * they will override any settings in the config. + * @param {LH.Gatherer.GatherMode=} gatherMode Gather mode used to collect artifacts. If present + * the config may override certain settings based on the mode. + * @return {Promise} + */ +async function generateConfig(configJson, flags = {}, gatherMode = 'navigation') { + const configContext = flagsToFRContext(flags); + const {config} = await initializeConfig(configJson, {...configContext, gatherMode}); + return config; +} + +/** + * Generate a legacy Lighthouse Config. + * @deprecated + * @param {LH.Config.Json=} configJson Configuration for the Lighthouse run. If + * not present, the default config is used. + * @param {LH.Flags=} flags Optional settings for the Lighthouse run. If present, + * they will override any settings in the config. * @return {Promise} */ -function generateConfig(configJson, flags) { +function generateLegacyConfig(configJson, flags) { return Config.fromJson(configJson, flags); } lighthouse.legacyNavigation = legacyNavigation; lighthouse.generateConfig = generateConfig; +lighthouse.generateLegacyConfig = generateLegacyConfig; lighthouse.getAuditList = Runner.getAuditList; lighthouse.traceCategories = require('./gather/driver.js').traceCategories; lighthouse.Audit = require('./audits/audit.js'); diff --git a/lighthouse-core/test/fraggle-rock/config/config-test.js b/lighthouse-core/test/fraggle-rock/config/config-test.js index 02661c201a56..e06349e0d427 100644 --- a/lighthouse-core/test/fraggle-rock/config/config-test.js +++ b/lighthouse-core/test/fraggle-rock/config/config-test.js @@ -9,8 +9,10 @@ import {jest} from '@jest/globals'; import BaseAudit from '../../../audits/audit.js'; import constants from '../../../config/constants.js'; import BaseGatherer from '../../../fraggle-rock/gather/base-gatherer.js'; -import {initializeConfig} from '../../../fraggle-rock/config/config.js'; +import {initializeConfig, getConfigDisplayString} from '../../../fraggle-rock/config/config.js'; import {LH_ROOT} from '../../../../root.js'; +import format from '../../../../shared/localization/format.js'; +import defaultConfig from '../../../fraggle-rock/config/default-config.js'; const {nonSimulatedPassConfigOverrides} = constants; @@ -482,3 +484,68 @@ describe('Fraggle Rock Config', () => { .rejects.toThrow(/did not support any gather modes/); }); }); + +describe('getConfigDisplayString', () => { + it('doesn\'t include empty audit options in output', async () => { + const aOpt = 'auditOption'; + const configJson = { + extends: 'lighthouse:default', + passes: [{ + passName: 'defaultPass', + gatherers: [ + {path: 'script-elements'}, + ], + }], + audits: [ + // `options` merged into default `metrics` audit. + {path: 'metrics', options: {aOpt}}, + ], + }; + + const {config} = await initializeConfig(configJson, {gatherMode: 'navigation'}); + const printed = getConfigDisplayString(config); + const printedConfig = JSON.parse(printed); + + // Check that options weren't completely eliminated. + const metricsAudit = printedConfig.audits.find(/** @param {any} a */ a => a.path === 'metrics'); + expect(metricsAudit.options.aOpt).toEqual(aOpt); + + for (const audit of printedConfig.audits) { + if (audit.options) { + expect(audit.options).not.toEqual({}); + } + } + }); + + it('returns localized category titles', async () => { + const {config} = await initializeConfig(undefined, {gatherMode: 'navigation'}); + const printed = getConfigDisplayString(config); + const printedConfig = JSON.parse(printed); + let localizableCount = 0; + + for (const [printedCategoryId, printedCategory] of Object.entries(printedConfig.categories)) { + if (!defaultConfig.categories) throw new Error('Default config will have categories'); + if (!defaultConfig.settings?.locale) throw new Error('Default config will have a locale'); + const origTitle = defaultConfig.categories[printedCategoryId].title; + if (format.isIcuMessage(origTitle)) localizableCount++; + const i18nOrigTitle = format.getFormatted(origTitle, defaultConfig.settings.locale); + + expect(printedCategory.title).toStrictEqual(i18nOrigTitle); + } + + // Should have localized at least one string. + expect(localizableCount).toBeGreaterThan(0); + }); + + it('returns a valid ConfigJson that can make an identical Config', async () => { + // depends on defaultConfig having a `path` for all gatherers and audits. + const {config: firstConfig} = await initializeConfig(undefined, {gatherMode: 'navigation'}); + const firstPrint = getConfigDisplayString(firstConfig); + + const {config: secondConfig} = + await initializeConfig(JSON.parse(firstPrint), {gatherMode: 'navigation'}); + const secondPrint = getConfigDisplayString(secondConfig); + + expect(firstPrint).toEqual(secondPrint); + }); +});