diff --git a/cli/cli-flags.js b/cli/cli-flags.js index edcb0d641d5d..81cb63384379 100644 --- a/cli/cli-flags.js +++ b/cli/cli-flags.js @@ -53,8 +53,8 @@ function getYargsParser(manualArgv) { 'lighthouse --extra-headers=./path/to/file.json', 'Path to JSON file of HTTP Header key/value pairs to send in requests') .example( - 'lighthouse --only-categories=performance,pwa', - 'Only run the specified categories. Available categories: accessibility, best-practices, performance, pwa, seo') + 'lighthouse --only-categories=performance,seo', + 'Only run the specified categories. Available categories: accessibility, best-practices, performance, seo') // We only have the single string positional argument, the url. .option('_', { @@ -188,7 +188,7 @@ function getYargsParser(manualArgv) { array: true, type: 'string', coerce: splitCommaSeparatedValues, - describe: 'Only run the specified categories. Available categories: accessibility, best-practices, performance, pwa, seo', + describe: 'Only run the specified categories. Available categories: accessibility, best-practices, performance, seo', }, 'skip-audits': { array: true, diff --git a/cli/test/fixtures/esm-config.js b/cli/test/fixtures/esm-config.js index 32769b77b52a..d558472c2d02 100644 --- a/cli/test/fixtures/esm-config.js +++ b/cli/test/fixtures/esm-config.js @@ -5,7 +5,7 @@ */ /** - * Config file for running PWA smokehouse audits for axe. + * Config file for running accessibility smokehouse audits for axe. */ /** @type {LH.Config} */ diff --git a/cli/test/smokehouse/core-tests.js b/cli/test/smokehouse/core-tests.js index a25c0a3c18bd..ad59c85c23c7 100644 --- a/cli/test/smokehouse/core-tests.js +++ b/cli/test/smokehouse/core-tests.js @@ -32,9 +32,6 @@ import metricsDelayedFcp from './test-definitions/metrics-delayed-fcp.js'; import metricsDelayedLcp from './test-definitions/metrics-delayed-lcp.js'; import metricsTrickyTti from './test-definitions/metrics-tricky-tti.js'; import metricsTrickyTtiLateFcp from './test-definitions/metrics-tricky-tti-late-fcp.js'; -import offlineOnlineOnly from './test-definitions/offline-online-only.js'; -import offlineReady from './test-definitions/offline-ready.js'; -import offlineSwBroken from './test-definitions/offline-sw-broken.js'; import oopifRequests from './test-definitions/oopif-requests.js'; import oopifScripts from './test-definitions/oopif-scripts.js'; import perfBudgets from './test-definitions/perf-budgets.js'; @@ -46,11 +43,6 @@ import perfFonts from './test-definitions/perf-fonts.js'; import perfFrameMetrics from './test-definitions/perf-frame-metrics.js'; import perfPreload from './test-definitions/perf-preload.js'; import perfTraceElements from './test-definitions/perf-trace-elements.js'; -import pwaAirhorner from './test-definitions/pwa-airhorner.js'; -import pwaCaltrain from './test-definitions/pwa-caltrain.js'; -import pwaChromestatus from './test-definitions/pwa-chromestatus.js'; -import pwaRocks from './test-definitions/pwa-rocks.js'; -import pwaSvgomg from './test-definitions/pwa-svgomg.js'; import redirectsClientPaintServer from './test-definitions/redirects-client-paint-server.js'; import redirectsHistoryPushState from './test-definitions/redirects-history-push-state.js'; import redirectsHttp from './test-definitions/redirects-http.js'; @@ -98,9 +90,6 @@ const smokeTests = [ metricsDelayedLcp, metricsTrickyTti, metricsTrickyTtiLateFcp, - offlineOnlineOnly, - offlineReady, - offlineSwBroken, oopifRequests, oopifScripts, perfBudgets, @@ -112,11 +101,6 @@ const smokeTests = [ perfFrameMetrics, perfPreload, perfTraceElements, - pwaAirhorner, - pwaCaltrain, - pwaChromestatus, - pwaRocks, - pwaSvgomg, redirectsClientPaintServer, redirectsHistoryPushState, redirectsHttp, diff --git a/cli/test/smokehouse/frontends/smokehouse-bin.js b/cli/test/smokehouse/frontends/smokehouse-bin.js index c416f7e9f513..2ae51e1bf6aa 100644 --- a/cli/test/smokehouse/frontends/smokehouse-bin.js +++ b/cli/test/smokehouse/frontends/smokehouse-bin.js @@ -59,7 +59,7 @@ function getDefinitionsToRun(allTestDefns, requestedIds, excludedTests) { } else { smokes = allTestDefns.filter(test => { // Include all tests that *include* requested id. - // e.g. a requested 'pwa' will match 'pwa-airhorner', 'pwa-caltrain', etc + // e.g. a requested 'perf' will match 'perf-preload', 'perf-trace-elements', etc return requestedIds.some(requestedId => test.id.includes(requestedId)); }); console.log(`Running ONLY smoketests for: ${smokes.map(t => t.id).join(' ')}\n`); @@ -125,7 +125,7 @@ async function begin() { const rawArgv = y .help('help') .usage('node $0 [] ') - .example('node $0 -j=1 pwa seo', 'run pwa and seo tests serially') + .example('node $0 -j=1 perf seo', 'run perf and seo tests serially') .option('_', { array: true, type: 'string', diff --git a/cli/test/smokehouse/test-definitions/a11y.js b/cli/test/smokehouse/test-definitions/a11y.js index eaf31836cef9..2d36103267dc 100644 --- a/cli/test/smokehouse/test-definitions/a11y.js +++ b/cli/test/smokehouse/test-definitions/a11y.js @@ -5,7 +5,7 @@ */ /** - * Config for running PWA smokehouse audits for axe. + * Config for running accessibility smokehouse audits for axe. * @type {LH.Config} */ const config = { diff --git a/cli/test/smokehouse/test-definitions/offline-online-only.js b/cli/test/smokehouse/test-definitions/offline-online-only.js deleted file mode 100644 index 3de59f788b69..000000000000 --- a/cli/test/smokehouse/test-definitions/offline-online-only.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: [ - 'best-practices', - ], - onlyAudits: [ - 'is-on-https', - 'redirects-http', - 'viewport', - 'user-timings', - 'critical-request-chains', - 'render-blocking-resources', - 'installable-manifest', - 'splash-screen', - 'themed-omnibox', - 'aria-valid-attr', - 'aria-allowed-attr', - 'color-contrast', - 'image-alt', - 'label', - 'tabindex', - 'content-width', - ], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results from testing a page that does not work offline. - */ -const expectations = { - lhr: { - requestedUrl: 'http://localhost:10200/online-only.html', - finalDisplayedUrl: 'http://localhost:10200/online-only.html', - audits: { - 'is-on-https': { - score: 1, - }, - 'redirects-http': { - // localhost, so redirect check is n/a. - score: null, - scoreDisplayMode: 'notApplicable', - }, - 'geolocation-on-start': { - score: 1, - }, - 'render-blocking-resources': { - score: 1, - }, - 'paste-preventing-inputs': { - score: 1, - }, - 'viewport': { - score: 1, - }, - 'user-timings': { - scoreDisplayMode: 'notApplicable', - }, - 'critical-request-chains': { - scoreDisplayMode: 'notApplicable', - }, - 'installable-manifest': { - score: 0, - details: {items: [{reason: 'Page has no manifest URL'}]}, - }, - 'splash-screen': { - score: 0, - }, - 'themed-omnibox': { - score: 0, - }, - 'aria-valid-attr': { - scoreDisplayMode: 'notApplicable', - }, - 'aria-allowed-attr': { - scoreDisplayMode: 'notApplicable', - }, - 'color-contrast': { - score: 1, - }, - 'image-alt': { - scoreDisplayMode: 'notApplicable', - }, - 'label': { - scoreDisplayMode: 'notApplicable', - }, - 'tabindex': { - scoreDisplayMode: 'notApplicable', - }, - 'content-width': { - score: 1, - }, - }, - }, -}; - -export default { - id: 'offline-online-only', - expectations, - config, - runSerially: true, -}; diff --git a/cli/test/smokehouse/test-definitions/offline-ready.js b/cli/test/smokehouse/test-definitions/offline-ready.js deleted file mode 100644 index d4dd0c90dc9d..000000000000 --- a/cli/test/smokehouse/test-definitions/offline-ready.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: [ - 'best-practices', - ], - onlyAudits: [ - 'is-on-https', - 'viewport', - 'user-timings', - 'critical-request-chains', - 'render-blocking-resources', - 'installable-manifest', - 'splash-screen', - 'themed-omnibox', - 'aria-valid-attr', - 'aria-allowed-attr', - 'color-contrast', - 'image-alt', - 'label', - 'tabindex', - 'content-width', - ], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results from testing the a local test page that works - * offline with a service worker. - */ -const expectations = { - artifacts: { - WebAppManifest: { - value: { - icons: { - value: [ - {value: {src: {value: 'http://localhost:10503/launcher-icon-0-75x.png'}}}, - {value: {src: {value: 'http://localhost:10503/launcher-icon-1x.png'}}}, - {value: {src: {value: 'http://localhost:10503/launcher-icon-1-5x.png'}}}, - {value: {src: {value: 'http://localhost:10503/launcher-icon-2x.png'}}}, - {value: {src: {value: 'http://localhost:10503/launcher-icon-3x.png'}}}, - ], - }, - }, - }, - InstallabilityErrors: { - errors: [ - // Icon errors were consolidated in M118 - // https://bugs.chromium.org/p/chromium/issues/detail?id=1476999 - { - _minChromiumVersion: '118', - errorId: 'no-acceptable-icon', - }, - { - _maxChromiumVersion: '117', - errorId: 'no-icon-available', - }, - ], - }, - }, - lhr: { - requestedUrl: 'http://localhost:10503/offline-ready.html', - finalDisplayedUrl: 'http://localhost:10503/offline-ready.html', - audits: { - 'is-on-https': { - score: 1, - }, - 'viewport': { - score: 1, - }, - 'user-timings': { - scoreDisplayMode: 'notApplicable', - }, - 'critical-request-chains': { - scoreDisplayMode: 'notApplicable', - }, - 'installable-manifest': { - score: 0, - details: {items: [ - // Icon errors were consolidated in M118 - // https://bugs.chromium.org/p/chromium/issues/detail?id=1476999 - { - _minChromiumVersion: '118', - reason: 'No supplied icon is at least 144\xa0px square in PNG, SVG or WebP format, with the purpose attribute unset or set to "any"', - }, - { - _maxChromiumVersion: '117', - reason: 'Downloaded icon was empty or corrupted', - }, - ]}, - }, - 'splash-screen': { - score: 0, - }, - 'themed-omnibox': { - score: 0, - }, - 'aria-valid-attr': { - scoreDisplayMode: 'notApplicable', - }, - 'aria-allowed-attr': { - scoreDisplayMode: 'notApplicable', - }, - 'color-contrast': { - score: 1, - }, - 'image-alt': { - score: 0, - }, - 'label': { - scoreDisplayMode: 'notApplicable', - }, - 'tabindex': { - scoreDisplayMode: 'notApplicable', - }, - 'content-width': { - score: 1, - }, - }, - }, -}; - -export default { - id: 'offline-ready', - expectations, - config, - runSerially: true, -}; diff --git a/cli/test/smokehouse/test-definitions/offline-sw-broken.js b/cli/test/smokehouse/test-definitions/offline-sw-broken.js deleted file mode 100644 index afa03814e17a..000000000000 --- a/cli/test/smokehouse/test-definitions/offline-sw-broken.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: [ - 'best-practices', - ], - onlyAudits: [ - 'is-on-https', - 'viewport', - 'user-timings', - 'critical-request-chains', - 'render-blocking-resources', - 'installable-manifest', - 'splash-screen', - 'themed-omnibox', - 'aria-valid-attr', - 'aria-allowed-attr', - 'color-contrast', - 'image-alt', - 'label', - 'tabindex', - 'content-width', - ], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results from testing the a local test page with a broken service worker. - */ -const expectations = { - lhr: { - requestedUrl: 'http://localhost:10503/offline-ready.html?broken', - // This page's SW has a `fetch` handler that doesn't provide a 200 response. - finalDisplayedUrl: 'http://localhost:10503/offline-ready.html?broken', - audits: { - 'installable-manifest': { - score: 0, - details: {items: {length: 1}}, - // TODO: 'warn-not-offline-capable' was disabled in m91. Turn back on once - // issues are addressed and check is re-enabled: https://crbug.com/1187668#c22 - // warnings: {length: 1}, - }, - }, - }, - artifacts: { - InstallabilityErrors: { - errors: [ - // Icon errors were consolidated in M118 - // https://bugs.chromium.org/p/chromium/issues/detail?id=1476999 - { - _minChromiumVersion: '118', - errorId: 'no-acceptable-icon', - }, - { - _maxChromiumVersion: '117', - errorId: 'no-icon-available', - }, - ], - }, - }, -}; - -export default { - id: 'offline-sw-broken', - expectations, - config, - runSerially: true, -}; diff --git a/cli/test/smokehouse/test-definitions/pwa-airhorner.js b/cli/test/smokehouse/test-definitions/pwa-airhorner.js deleted file mode 100644 index 7d92629b3b4a..000000000000 --- a/cli/test/smokehouse/test-definitions/pwa-airhorner.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import pwaDetailsExpectations from './pwa-expectations-details.js'; - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: ['pwa'], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results for airhorner.com. - */ -const expectations = { - lhr: { - requestedUrl: 'https://airhorner.com', - finalDisplayedUrl: 'https://airhorner.com/', - audits: { - 'viewport': { - score: 1, - }, - 'installable-manifest': { - score: 1, - details: {items: [], debugData: {manifestUrl: 'https://airhorner.com/manifest.json'}}, - }, - 'splash-screen': { - score: 1, - details: {items: [pwaDetailsExpectations]}, - }, - 'themed-omnibox': { - score: 1, - details: {items: [{...pwaDetailsExpectations, themeColor: '#2196F3'}]}, - }, - 'content-width': { - score: 1, - }, - - // "manual" audits. Just verify in the results. - 'pwa-cross-browser': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-page-transitions': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-each-page-has-url': { - score: null, - scoreDisplayMode: 'manual', - }, - }, - }, -}; - -export default { - id: 'pwa-airhorner', - expectations, - config, -}; diff --git a/cli/test/smokehouse/test-definitions/pwa-caltrain.js b/cli/test/smokehouse/test-definitions/pwa-caltrain.js deleted file mode 100644 index c439ef0cbcf3..000000000000 --- a/cli/test/smokehouse/test-definitions/pwa-caltrain.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import pwaDetailsExpectations from './pwa-expectations-details.js'; - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: ['pwa'], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results for caltrainschedule.io. - */ -const expectations = { - lhr: { - requestedUrl: 'https://caltrainschedule.io/', - finalDisplayedUrl: 'https://caltrainschedule.io/', - audits: { - 'viewport': { - score: 1, - }, - 'installable-manifest': { - score: 1, - details: {items: [], debugData: {manifestUrl: 'https://caltrainschedule.io/manifest.json'}}, - }, - 'splash-screen': { - score: 1, - details: {items: [pwaDetailsExpectations]}, - }, - 'themed-omnibox': { - score: 0, - }, - 'content-width': { - score: 1, - }, - - // "manual" audits. Just verify in the results. - 'pwa-cross-browser': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-page-transitions': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-each-page-has-url': { - score: null, - scoreDisplayMode: 'manual', - }, - }, - }, -}; - -export default { - id: 'pwa-caltrain', - expectations, - config, -}; diff --git a/cli/test/smokehouse/test-definitions/pwa-chromestatus.js b/cli/test/smokehouse/test-definitions/pwa-chromestatus.js deleted file mode 100644 index f092733be793..000000000000 --- a/cli/test/smokehouse/test-definitions/pwa-chromestatus.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: ['pwa'], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results for chromestatus.com. - */ -const expectations = { - lhr: { - requestedUrl: 'https://chromestatus.com/features', - finalDisplayedUrl: 'https://chromestatus.com/features', - audits: { - 'viewport': { - score: 1, - }, - 'installable-manifest': { - score: 0, - details: {items: [{reason: 'Page has no manifest URL'}]}, - }, - 'splash-screen': { - score: 0, - }, - 'themed-omnibox': { - score: 0, - }, - 'content-width': { - score: 1, - }, - - // "manual" audits. Just verify in the results. - 'pwa-cross-browser': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-page-transitions': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-each-page-has-url': { - score: null, - scoreDisplayMode: 'manual', - }, - }, - }, -}; - -export default { - id: 'pwa-chromestatus', - expectations, - config, -}; diff --git a/cli/test/smokehouse/test-definitions/pwa-expectations-details.js b/cli/test/smokehouse/test-definitions/pwa-expectations-details.js deleted file mode 100644 index 2d20e42e91a4..000000000000 --- a/cli/test/smokehouse/test-definitions/pwa-expectations-details.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -export default { - isParseFailure: false, - hasStartUrl: true, - hasIconsAtLeast144px: true, - hasIconsAtLeast512px: true, - hasPWADisplayValue: true, - hasBackgroundColor: true, - hasThemeColor: true, - hasShortName: true, - hasName: true, -}; diff --git a/cli/test/smokehouse/test-definitions/pwa-rocks.js b/cli/test/smokehouse/test-definitions/pwa-rocks.js deleted file mode 100644 index 2258dd8bc9fb..000000000000 --- a/cli/test/smokehouse/test-definitions/pwa-rocks.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import pwaDetailsExpectations from './pwa-expectations-details.js'; - -const pwaRocksExpectations = {...pwaDetailsExpectations, hasIconsAtLeast512px: false}; - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: ['pwa'], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results for (the archived) pwa.rocks. - */ -const expectations = { - lhr: { - // Archived version of https://github.com/pwarocks/pwa.rocks - // Fork is here: https://github.com/connorjclark/pwa.rocks - requestedUrl: 'https://connorjclark.github.io/pwa.rocks/', - finalDisplayedUrl: 'https://connorjclark.github.io/pwa.rocks/', - audits: { - 'viewport': { - score: 1, - }, - 'installable-manifest': { - score: 1, - details: {items: [], debugData: {manifestUrl: 'https://connorjclark.github.io/pwa.rocks/pwa.webmanifest'}}, - }, - 'splash-screen': { - score: 0, - details: {items: [pwaRocksExpectations]}, - }, - 'themed-omnibox': { - score: 0, - details: {items: [pwaRocksExpectations]}, - }, - 'content-width': { - score: 1, - }, - - // "manual" audits. Just verify in the results. - 'pwa-cross-browser': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-page-transitions': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-each-page-has-url': { - score: null, - scoreDisplayMode: 'manual', - }, - }, - }, -}; - -export default { - id: 'pwa-rocks', - expectations, - config, -}; diff --git a/cli/test/smokehouse/test-definitions/pwa-svgomg.js b/cli/test/smokehouse/test-definitions/pwa-svgomg.js deleted file mode 100644 index 2507152368bb..000000000000 --- a/cli/test/smokehouse/test-definitions/pwa-svgomg.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import pwaDetailsExpectations from './pwa-expectations-details.js'; - -const jakeExpectations = {...pwaDetailsExpectations, hasShortName: false}; - -/** @type {LH.Config} */ -const config = { - extends: 'lighthouse:default', - settings: { - onlyCategories: ['pwa'], - }, -}; - -/** - * @type {Smokehouse.ExpectedRunnerResult} - * Expected Lighthouse results for svgomg. - */ -const expectations = { - lhr: { - runWarnings: { - // In DevTools the IndexedDB storage from the initial load will persist into the Lighthouse run, emitting a warning. - // For non-DevTools runners, this will revert to the default expectation of 0 run warnings. - _runner: 'devtools', - length: 1, - }, - requestedUrl: 'https://jakearchibald.github.io/svgomg/', - finalDisplayedUrl: 'https://jakearchibald.github.io/svgomg/', - audits: { - 'viewport': { - score: 1, - }, - 'installable-manifest': { - score: 1, - details: {items: [], debugData: {manifestUrl: 'https://jakearchibald.github.io/svgomg/manifest.json'}}, - }, - 'splash-screen': { - score: 1, - details: {items: [jakeExpectations]}, - }, - 'themed-omnibox': { - score: 1, - details: {items: [jakeExpectations]}, - }, - 'content-width': { - score: 1, - }, - - // "manual" audits. Just verify in the results. - 'pwa-cross-browser': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-page-transitions': { - score: null, - scoreDisplayMode: 'manual', - }, - 'pwa-each-page-has-url': { - score: null, - scoreDisplayMode: 'manual', - }, - }, - }, -}; - -export default { - id: 'pwa-svgomg', - expectations, - config, -}; diff --git a/clients/extension/scripts/settings-controller.js b/clients/extension/scripts/settings-controller.js index 98d574c5e7d3..f71e2e81a477 100644 --- a/clients/extension/scripts/settings-controller.js +++ b/clients/extension/scripts/settings-controller.js @@ -25,9 +25,6 @@ const DEFAULT_CATEGORIES = [{ }, { id: 'seo', title: 'SEO', -}, { - id: 'pwa', - title: 'PWA', }]; /** @typedef {{backend: string, selectedCategories: string[], device: string, locale: string}} Settings */ diff --git a/clients/test/extension/popup-test-pptr.js b/clients/test/extension/popup-test-pptr.js index be99df387515..e9d4da0f97f6 100644 --- a/clients/test/extension/popup-test-pptr.js +++ b/clients/test/extension/popup-test-pptr.js @@ -17,7 +17,6 @@ const lighthouseExtensionPath = path.resolve(LH_ROOT, 'dist/extension-chrome'); const mockStorage = { [STORAGE_KEYS.Categories]: { 'performance': true, - 'pwa': true, 'seo': true, 'accessibility': false, 'best-practices': false, diff --git a/core/audits/content-width.js b/core/audits/content-width.js deleted file mode 100644 index f0f554a522a9..000000000000 --- a/core/audits/content-width.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import {Audit} from './audit.js'; -import * as i18n from '../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that provides detail on the content size of a web site compared to the viewport, which is the size of the screen the site is displayed on. This descriptive title is shown to users when the site's content is sized appropriately. */ - title: 'Content is sized correctly for the viewport', - /** Title of a Lighthouse audit that provides detail on the content size of a web site compared to the viewport, which is the size of the screen the site is displayed on. This descriptive title is shown to users when the site's content is not sized appropriately. */ - failureTitle: 'Content is not sized correctly for the viewport', - /** Description of a Lighthouse audit that tells the user why they should care that a site's content size should match its viewport size, which is the size of the screen the site is displayed on. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'If the width of your app\'s content doesn\'t match the width ' + - 'of the viewport, your app might not be optimized for mobile screens. ' + - '[Learn how to size content for the viewport](https://developer.chrome.com/docs/lighthouse/pwa/content-width/).', - /** - * @description Explanatory message stating that the viewport size and window size differ. - * @example {100} innerWidth - * @example {101} outerWidth - * */ - explanation: 'The viewport size of {innerWidth}px does not match the window ' + - 'size of {outerWidth}px.', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -class ContentWidth extends Audit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return { - id: 'content-width', - title: str_(UIStrings.title), - failureTitle: str_(UIStrings.failureTitle), - description: str_(UIStrings.description), - requiredArtifacts: ['ViewportDimensions'], - }; - } - - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {LH.Audit.Product} - */ - static audit(artifacts, context) { - const viewportWidth = artifacts.ViewportDimensions.innerWidth; - const windowWidth = artifacts.ViewportDimensions.outerWidth; - const widthsMatch = viewportWidth === windowWidth; - - if (context.settings.formFactor === 'desktop') { - return { - score: 1, - notApplicable: true, - }; - } - - let explanation; - if (!widthsMatch) { - explanation = str_(UIStrings.explanation, - {innerWidth: artifacts.ViewportDimensions.innerWidth, - outerWidth: artifacts.ViewportDimensions.outerWidth}); - } - - return { - score: Number(widthsMatch), - explanation, - }; - } -} - -export default ContentWidth; -export {UIStrings}; diff --git a/core/audits/installable-manifest.js b/core/audits/installable-manifest.js deleted file mode 100644 index c3cb52a32d87..000000000000 --- a/core/audits/installable-manifest.js +++ /dev/null @@ -1,248 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import {Audit} from './audit.js'; -import * as i18n from '../lib/i18n/i18n.js'; -import {ManifestValues} from '../computed/manifest-values.js'; - -/* eslint-disable max-len */ -const UIStrings = { - /** Title of a Lighthouse audit that provides detail on if a website is installable as an application. This descriptive title is shown to users when a webapp is installable. */ - 'title': 'Web app manifest and service worker meet the installability requirements', - /** Title of a Lighthouse audit that provides detail on if a website is installable as an application. This descriptive title is shown to users when a webapp is not installable. */ - 'failureTitle': 'Web app manifest or service worker do not meet the installability requirements', - /** Description of a Lighthouse audit that tells the user why installability is important for webapps. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - 'description': `Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper service worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more about manifest installability requirements](https://developer.chrome.com/docs/lighthouse/pwa/installable-manifest/).`, - /** Label for a column in a data table; entries in the column will be a string explaining why a failure occurred. */ - 'columnValue': 'Failure reason', - /** - * @description [ICU Syntax] Label for an audit identifying the number of installability errors found in the page. - */ - 'displayValue': `{itemCount, plural, - =1 {1 reason} - other {# reasons} - }`, - /** - * @description Error message describing a DevTools error id that was found and has not been identified by this audit. - * @example {platform-not-supported-on-android} errorId - */ - 'noErrorId': `Installability error id '{errorId}' is not recognized`, - /** Error message explaining that the page is not loaded in the frame. */ - 'not-in-main-frame': `Page is not loaded in the main frame`, - /** Error message explaining that the page is served from a secure origin. */ - 'not-from-secure-origin': 'Page is not served from a secure origin', - /** Error message explaining that the page has no manifest URL. */ - 'no-manifest': 'Page has no manifest URL', - /** Error message explaining that the provided manifest URL is invalid. */ - 'start-url-not-valid': `Manifest start URL is not valid`, - /** Error message explaining that the provided manifest does not contain a name or short_name field. */ - 'manifest-missing-name-or-short-name': 'Manifest does not contain a `name` or `short_name` field', - /** Error message explaining that the manifest display property must be one of 'standalone', 'fullscreen', or 'minimal-ui'. */ - 'manifest-display-not-supported': 'Manifest `display` property must be one of `standalone`, `fullscreen`, or `minimal-ui`', - /** Error message explaining that the manifest could not be fetched, might be empty, or could not be parsed. */ - 'manifest-empty': `Manifest could not be fetched, is empty, or could not be parsed`, - /** Error message explaining that the manifest could not be fetched, might be empty, or could not be parsed. */ - 'manifest-parsing-or-network-error': 'Manifest could not be fetched, is empty, or could not be parsed', - /** - * @description Error message explaining that the manifest does not contain a suitable icon. - * @example {192} value0 - */ - 'manifest-missing-suitable-icon': `Manifest does not contain a suitable icon - PNG, SVG or WebP format of at least {value0}\xa0px is required, the sizes attribute must be set, and the purpose attribute, if set, must include "any".`, - - /** - * @description Error message explaining that the manifest does not supply an icon of the correct format. - * @example {192} value0 - */ - 'no-acceptable-icon': `No supplied icon is at least {value0}\xa0px square in PNG, SVG or WebP format, with the purpose attribute unset or set to "any"`, - - /** Error message explaining that the icon could not be downloaded. */ - 'cannot-download-icon': `Could not download a required icon from the manifest`, - /** Error message explaining that the downloaded icon was empty or corrupt. */ - 'no-icon-available': `Downloaded icon was empty or corrupted`, - /** Error message explaining that the specified application platform is not supported on Android. */ - 'platform-not-supported-on-android': `The specified application platform is not supported on Android`, - /** Error message explaining that a Play store ID was not provided. */ - 'no-id-specified': `No Play store ID provided`, - /** Error message explaining that the Play Store app URL and Play Store ID do not match. */ - 'ids-do-not-match': `The Play Store app URL and Play Store ID do not match`, - /** Error message explaining that the app is already installed. */ - 'already-installed': `The app is already installed`, - /** Error message explaining that a URL in the manifest contains a username, password, or port. */ - 'url-not-supported-for-webapk': `A URL in the manifest contains a username, password, or port`, - /** Error message explaining that the page is loaded in an incognito window. */ - 'in-incognito': `Page is loaded in an incognito window`, - // TODO: perhaps edit this message to make it more actionable for LH users - /** Error message explaining that the page does not work offline. */ - 'not-offline-capable': `Page does not work offline`, - /** Error message explaining that service worker could not be checked without a start_url. */ - 'no-url-for-service-worker': `Could not check service worker without a 'start_url' field in the manifest`, - /** Error message explaining that the manifest specifies prefer_related_applications: true. */ - 'prefer-related-applications': `Manifest specifies prefer_related_applications: true`, - /** Error message explaining that prefer_related_applications is only supported on Chrome Beta and Stable channels on Android. */ - 'prefer-related-applications-only-beta-stable': `prefer_related_applications is only supported on Chrome Beta and Stable channels on Android.`, - /** Error message explaining that the manifest contains 'display_override' field, and the - first supported display mode must be one of 'standalone', 'fullscreen', or 'minimal-ui'. */ - 'manifest-display-override-not-supported': `Manifest contains 'display_override' field, and the first supported display mode must be one of 'standalone', 'fullscreen', or 'minimal-ui'`, - /** Error message explaining that the web manifest's URL changed while the manifest was being downloaded by the browser. */ - 'manifest-location-changed': `Manifest URL changed while the manifest was being fetched.`, - /** Warning message explaining that the page does not work offline. */ - // TODO(COMPAT): This error was removed in M118, we can remove this message when it hits stable. - 'warn-not-offline-capable': `Page does not work offline. The page will not be regarded as installable after Chrome 93, stable release August 2021.`, - /** Error message explaining that Lighthouse failed while checking if the page is installable, and directing the user to try again in a new Chrome. */ - 'protocol-timeout': `Lighthouse could not determine if the page is installable. Please try with a newer version of Chrome.`, - /** Message logged when the web app has been uninstalled o desktop, signalling that the install banner state is being reset. */ - 'pipeline-restarted': 'PWA has been uninstalled and installability checks resetting.', -}; -/* eslint-enable max-len */ - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview - * Audits if the page's web app manifest and service worker qualify for triggering a beforeinstallprompt event. - * https://github.com/GoogleChrome/lighthouse/issues/23#issuecomment-270453303 - * - * Requirements based on Chrome Devtools' installability requirements. - * Origin of logging: - * https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/installable/installable_logging.cc - * DevTools InstallabilityError implementation: - * https://source.chromium.org/search?q=getInstallabilityErrorMessages&ss=chromium%2Fchromium%2Fsrc:third_party%2Fdevtools-frontend%2Fsrc%2Ffront_end%2Fresources%2F - */ - -class InstallableManifest extends Audit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return { - id: 'installable-manifest', - title: str_(UIStrings.title), - failureTitle: str_(UIStrings.failureTitle), - description: str_(UIStrings.description), - supportedModes: ['navigation'], - requiredArtifacts: ['WebAppManifest', 'InstallabilityErrors'], - }; - } - - /** - * @param {LH.Artifacts} artifacts - * @return {{i18nErrors: Array; warnings: Array}} - */ - static getInstallabilityErrors(artifacts) { - const installabilityErrors = artifacts.InstallabilityErrors.errors; - const i18nErrors = []; - const warnings = []; - const errorArgumentsRegex = /{([^}]+)}/g; - - for (const err of installabilityErrors) { - // Filter out errorId 'in-incognito' since Lighthouse recommends incognito. - if (err.errorId === 'in-incognito') continue; - - if (err.errorId === 'warn-not-offline-capable') { - warnings.push(str_(UIStrings[err.errorId])); - continue; - } - - // Filter out errorId 'pipeline-restarted' since it only applies when the PWA is uninstalled. - if (err.errorId === 'pipeline-restarted') { - continue; - } - - // @ts-expect-error errorIds from protocol should match up against the strings dict - const matchingString = UIStrings[err.errorId]; - - // Handle an errorId we don't recognize. - if (matchingString === undefined) { - i18nErrors.push(str_(UIStrings.noErrorId, {errorId: err.errorId})); - continue; - } - - // Get the i18m argument names of the installability error message, if any. - const UIStringArguments = matchingString.match(errorArgumentsRegex) || []; - - /** - * If there is an argument value, get it. - * We only expect a `minimum-icon-size-in-pixels` errorArg[0] for two errorIds, currently. - */ - const value0 = err.errorArguments?.length && err.errorArguments[0].value; - - if (matchingString && err.errorArguments.length !== UIStringArguments.length) { - // Matching string, but have the incorrect number of arguments for the message. - const stringArgs = JSON.stringify(err.errorArguments); - const msg = err.errorArguments.length > UIStringArguments.length ? - `${err.errorId} has unexpected arguments ${stringArgs}` : - `${err.errorId} does not have the expected number of arguments.`; - i18nErrors.push(msg); - } else if (matchingString && value0) { - i18nErrors.push(str_(matchingString, {value0})); - } else if (matchingString) { - i18nErrors.push(str_(matchingString)); - } - } - - return {i18nErrors, warnings}; - } - - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {Promise} - * - */ - static async audit(artifacts, context) { - const {i18nErrors, warnings} = InstallableManifest.getInstallabilityErrors(artifacts); - - const manifestUrl = artifacts.WebAppManifest ? artifacts.WebAppManifest.url : null; - - /** @type {LH.Audit.Details.Table['headings']} */ - const headings = [ - {key: 'reason', valueType: 'text', label: str_(UIStrings.columnValue)}, - ]; - - // Errors for report table. - /** @type {LH.Audit.Details.Table['items']} */ - const errorReasons = i18nErrors.map(reason => { - return {reason}; - }); - - // If InstallabilityErrors is empty, double check ManifestValues to make sure nothing was missed. - // InstallabilityErrors can be empty erroneously in our DevTools web tests. - if (!errorReasons.length) { - const manifestValues = await ManifestValues.request(artifacts, context); - if (manifestValues.isParseFailure) { - errorReasons.push({ - reason: manifestValues.parseFailureReason, - }); - } - } - - // Include the detailed pass/fail checklist as a diagnostic. - /** @type {LH.Audit.Details.DebugData} */ - const debugData = { - type: 'debugdata', - manifestUrl, - }; - - if (errorReasons.length > 0) { - return { - score: 0, - warnings, - numericValue: errorReasons.length, - numericUnit: 'element', - displayValue: str_(UIStrings.displayValue, {itemCount: errorReasons.length}), - details: {...Audit.makeTableDetails(headings, errorReasons), debugData}, - }; - } - return { - score: 1, - warnings, - details: {...Audit.makeTableDetails(headings, errorReasons), debugData}, - }; - } -} - -export default InstallableManifest; -export {UIStrings}; diff --git a/core/audits/manual/pwa-cross-browser.js b/core/audits/manual/pwa-cross-browser.js deleted file mode 100644 index 66e2f503d54c..000000000000 --- a/core/audits/manual/pwa-cross-browser.js +++ /dev/null @@ -1,40 +0,0 @@ - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import ManualAudit from './manual-audit.js'; -import * as i18n from '../../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that prompts the user to manually check that their site works across different web browsers. */ - title: 'Site works cross-browser', - /** Description of a Lighthouse audit that tells the user why they should make sites work across different browsers. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'To reach the most number of users, sites should work across ' + - 'every major browser. [Learn about cross-browser compatibility](https://developer.chrome.com/docs/lighthouse/pwa/pwa-cross-browser/).', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview Manual PWA audit for cross browser support. - */ - -class PWACrossBrowser extends ManualAudit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return Object.assign({ - id: 'pwa-cross-browser', - title: str_(UIStrings.title), - description: str_(UIStrings.description), - }, super.partialMeta); - } -} - -export default PWACrossBrowser; -export {UIStrings}; - diff --git a/core/audits/manual/pwa-each-page-has-url.js b/core/audits/manual/pwa-each-page-has-url.js deleted file mode 100644 index 5ddfa2a1ee1b..000000000000 --- a/core/audits/manual/pwa-each-page-has-url.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import ManualAudit from './manual-audit.js'; -import * as i18n from '../../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that prompts the user to manually check that each page on their website uses a unique URL. */ - title: 'Each page has a URL', - /** Description of a Lighthouse audit that tells the user why they should use unique URLs for each web page. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'Ensure individual pages are deep linkable via URL and that URLs are ' + - 'unique for the purpose of shareability on social media. [Learn more about providing deep links](https://developer.chrome.com/docs/lighthouse/pwa/pwa-each-page-has-url/).', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview Manual PWA audit to ensure every page has a deep link. - */ - -class PWAEachPageHasURL extends ManualAudit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return Object.assign({ - id: 'pwa-each-page-has-url', - title: str_(UIStrings.title), - description: str_(UIStrings.description), - }, super.partialMeta); - } -} - -export default PWAEachPageHasURL; -export {UIStrings}; diff --git a/core/audits/manual/pwa-page-transitions.js b/core/audits/manual/pwa-page-transitions.js deleted file mode 100644 index 1b7173698a0a..000000000000 --- a/core/audits/manual/pwa-page-transitions.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import ManualAudit from './manual-audit.js'; -import * as i18n from '../../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that prompts the user to manually check that page transitions (navigating to other pages on a website) shouldn't feel like they are waiting for the network to load. */ - title: 'Page transitions don\'t feel like they block on the network', - /** Description of a Lighthouse audit that tells the user why they should make transitions in their web app feel fast. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'Transitions should feel snappy as you tap around, even on a slow network. ' + - 'This experience is key to a user\'s perception of performance. [Learn more about page transitions](https://developer.chrome.com/docs/lighthouse/pwa/pwa-page-transitions/).', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview Manual PWA audit for janky-free page transitions. - */ - -class PWAPageTransitions extends ManualAudit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return Object.assign({ - id: 'pwa-page-transitions', - title: str_(UIStrings.title), - description: str_(UIStrings.description), - }, super.partialMeta); - } -} - -export default PWAPageTransitions; -export {UIStrings}; diff --git a/core/audits/maskable-icon.js b/core/audits/maskable-icon.js deleted file mode 100644 index b7e6de0010f5..000000000000 --- a/core/audits/maskable-icon.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import {Audit} from './audit.js'; -import {ManifestValues} from '../computed/manifest-values.js'; -import * as i18n from '../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that provides detail on if the manifest contains a maskable icon. This descriptive title is shown to users when the manifest contains at least one maskable icon. */ - title: 'Manifest has a maskable icon', - /** Title of a Lighthouse audit that provides detial on if the manifest contains a maskable icon. this descriptive title is shown to users when the manifest contains no icons that are maskable. */ - failureTitle: 'Manifest doesn\'t have a maskable icon', - /** Description of a Lighthouse audit that tells the user why they their manifest should have at least one maskable icon. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'A maskable icon ensures that the image fills the entire ' + - 'shape without being letterboxed when installing ' + - 'the app on a device. [Learn about maskable manifest icons](https://developer.chrome.com/docs/lighthouse/pwa/maskable-icon-audit/).', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview - * Audits if a manifest contains at least one icon that is maskable - * - * Requirements: - * * manifest is not empty - * * manifest has valid icons - * * at least one of the icons has a purpose of 'maskable' - */ - -class MaskableIcon extends Audit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return { - id: 'maskable-icon', - title: str_(UIStrings.title), - failureTitle: str_(UIStrings.failureTitle), - description: str_(UIStrings.description), - supportedModes: ['navigation'], - requiredArtifacts: ['WebAppManifest', 'InstallabilityErrors'], - }; - } - - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {Promise} - */ - static async audit(artifacts, context) { - const manifestValues = await ManifestValues.request(artifacts, context); - if (manifestValues.isParseFailure) { - return { - score: 0, - explanation: manifestValues.parseFailureReason, - }; - } - const maskableIconCheck = manifestValues.allChecks.find(i => i.id === 'hasMaskableIcon'); - return { - score: maskableIconCheck?.passing ? 1 : 0, - }; - } -} - -export default MaskableIcon; -export {UIStrings}; diff --git a/core/audits/multi-check-audit.js b/core/audits/multi-check-audit.js deleted file mode 100644 index f8fb271b7f0a..000000000000 --- a/core/audits/multi-check-audit.js +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Base class for boolean audits that can have multiple reasons for failure - */ - -import {Audit} from './audit.js'; - -class MultiCheckAudit extends Audit { - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {Promise} - */ - static async audit(artifacts, context) { - const multiProduct = await this.audit_(artifacts, context); - return this.createAuditProduct(multiProduct); - } - - /** - * @param {{failures: Array, manifestValues?: LH.Artifacts.ManifestValues}} result - * @return {LH.Audit.Product} - */ - static createAuditProduct(result) { - /** @type {LH.Audit.MultiCheckAuditDetails} */ - const detailsItem = { - ...result, - ...result.manifestValues, - manifestValues: undefined, - allChecks: undefined, - }; - - if (result.manifestValues?.allChecks) { - result.manifestValues.allChecks.forEach(check => { - detailsItem[check.id] = check.passing; - }); - } - - // Include the detailed pass/fail checklist as a diagnostic. - /** @type {LH.Audit.Details.DebugData} */ - const details = { - type: 'debugdata', - // TODO: Consider not nesting detailsItem under `items`. - items: [detailsItem], - }; - - // If we fail, share the failures - if (result.failures.length > 0) { - return { - score: 0, - // TODO(#11495): make this i18n-able. - explanation: `Failures: ${result.failures.join(',\n')}.`, - details, - }; - } - - // Otherwise, we pass - return { - score: 1, - details, - }; - } - - /* eslint-disable no-unused-vars */ - - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {Promise<{failures: Array, manifestValues?: LH.Artifacts.ManifestValues}>} - */ - static audit_(artifacts, context) { - throw new Error('audit_ unimplemented'); - } - - /* eslint-enable no-unused-vars */ -} - -export default MultiCheckAudit; diff --git a/core/audits/splash-screen.js b/core/audits/splash-screen.js deleted file mode 100644 index a1b4eed68854..000000000000 --- a/core/audits/splash-screen.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import MultiCheckAudit from './multi-check-audit.js'; -import {ManifestValues} from '../computed/manifest-values.js'; -import * as i18n from '../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that provides detail on splash screens. This descriptive title is shown to users when the site has a custom splash screen. */ - title: 'Configured for a custom splash screen', - /** Title of a Lighthouse audit that provides detail on splash screens. This descriptive title is shown to users when the site does not have a custom splash screen. */ - failureTitle: 'Is not configured for a custom splash screen', - /** Description of a Lighthouse audit that tells the user why they should configure a custom splash screen. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'A themed splash screen ensures a high-quality experience when ' + - 'users launch your app from their homescreens. ' + - '[Learn more about splash screens](https://developer.chrome.com/docs/lighthouse/pwa/splash-screen/).', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview - * Audits if a page is configured for a custom splash screen when launched - * https://github.com/GoogleChrome/lighthouse/issues/24 - * - * Requirements: - * * manifest is not empty - * * manifest has a valid name - * * manifest has a valid background_color - * * manifest has a valid theme_color - * * manifest contains icon that's a png and size >= 512px - */ - -class SplashScreen extends MultiCheckAudit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return { - id: 'splash-screen', - title: str_(UIStrings.title), - failureTitle: str_(UIStrings.failureTitle), - description: str_(UIStrings.description), - supportedModes: ['navigation'], - requiredArtifacts: ['WebAppManifest', 'InstallabilityErrors'], - }; - } - - /** - * @param {LH.Artifacts.ManifestValues} manifestValues - * @param {Array} failures - */ - static assessManifest(manifestValues, failures) { - if (manifestValues.isParseFailure && manifestValues.parseFailureReason) { - failures.push(manifestValues.parseFailureReason); - return; - } - - const splashScreenCheckIds = [ - 'hasName', - 'hasBackgroundColor', - 'hasThemeColor', - 'hasIconsAtLeast512px', - ]; - - manifestValues.allChecks - .filter(item => splashScreenCheckIds.includes(item.id)) - .forEach(item => { - if (!item.passing) { - failures.push(item.failureText); - } - }); - } - - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {Promise<{failures: Array, manifestValues: LH.Artifacts.ManifestValues}>} - */ - static async audit_(artifacts, context) { - /** @type {Array} */ - const failures = []; - - const manifestValues = await ManifestValues.request(artifacts, context); - SplashScreen.assessManifest(manifestValues, failures); - - return { - failures, - manifestValues, - }; - } -} - -export default SplashScreen; -export {UIStrings}; diff --git a/core/audits/themed-omnibox.js b/core/audits/themed-omnibox.js deleted file mode 100644 index b2c44d7c0a75..000000000000 --- a/core/audits/themed-omnibox.js +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import MultiCheckAudit from './multi-check-audit.js'; -import {ManifestValues} from '../computed/manifest-values.js'; -import * as i18n from '../lib/i18n/i18n.js'; - -const UIStrings = { - /** Title of a Lighthouse audit that provides detail on the theme color the web page has set for the browser's address bar. This descriptive title is shown to users when an address-bar theme color has been set. */ - title: 'Sets a theme color for the address bar.', - /** Title of a Lighthouse audit that provides detail on the theme color the web page has set for the browser's address bar. This descriptive title is shown to users when an address-bar theme color has not been set. */ - failureTitle: 'Does not set a theme color for the address bar.', - /** Description of a Lighthouse audit that tells the user why they should set a theme color for the browser's address bar. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - description: 'The browser address bar can be themed to match your site. ' + - '[Learn more about theming the address bar](https://developer.chrome.com/docs/lighthouse/pwa/themed-omnibox/).', -}; - -const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); - -/** - * @fileoverview - * Audits if a page is configured for a themed address bar - * - * Requirements: - * * manifest is not empty - * * manifest has a theme_color - * * HTML has a theme-color meta - * - * Color validity is explicitly not checked. - */ - -class ThemedOmnibox extends MultiCheckAudit { - /** - * @return {LH.Audit.Meta} - */ - static get meta() { - return { - id: 'themed-omnibox', - title: str_(UIStrings.title), - failureTitle: str_(UIStrings.failureTitle), - description: str_(UIStrings.description), - supportedModes: ['navigation'], - requiredArtifacts: ['WebAppManifest', 'InstallabilityErrors', 'MetaElements'], - }; - } - - /** - * @param {LH.Artifacts.MetaElement|undefined} themeColorMeta - * @param {Array} failures - */ - static assessMetaThemecolor(themeColorMeta, failures) { - if (!themeColorMeta) { - // TODO(#7238): i18n - failures.push('No `` tag found'); - } else if (!themeColorMeta.content) { - failures.push('The theme-color meta tag did not contain a content value'); - } - } - - /** - * @param {LH.Artifacts.ManifestValues} manifestValues - * @param {Array} failures - */ - static assessManifest(manifestValues, failures) { - if (manifestValues.isParseFailure && manifestValues.parseFailureReason) { - failures.push(manifestValues.parseFailureReason); - return; - } - - const themeColorCheck = manifestValues.allChecks.find(i => i.id === 'hasThemeColor'); - if (themeColorCheck && !themeColorCheck.passing) { - failures.push(themeColorCheck.failureText); - } - } - - /** - * @param {LH.Artifacts} artifacts - * @param {LH.Audit.Context} context - * @return {Promise<{failures: Array, manifestValues: LH.Artifacts.ManifestValues, themeColor: ?string}>} - */ - static async audit_(artifacts, context) { - /** @type {Array} */ - const failures = []; - - const themeColorMeta = artifacts.MetaElements.find(meta => meta.name === 'theme-color'); - const manifestValues = await ManifestValues.request(artifacts, context); - ThemedOmnibox.assessManifest(manifestValues, failures); - ThemedOmnibox.assessMetaThemecolor(themeColorMeta, failures); - - return { - failures, - manifestValues, - themeColor: themeColorMeta?.content || null, - }; - } -} - -export default ThemedOmnibox; -export {UIStrings}; diff --git a/core/computed/manifest-values.js b/core/computed/manifest-values.js deleted file mode 100644 index af9042cb674e..000000000000 --- a/core/computed/manifest-values.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import {makeComputedArtifact} from './computed-artifact.js'; -import * as icons from '../lib/icons.js'; - -const PWA_DISPLAY_VALUES = ['minimal-ui', 'fullscreen', 'standalone']; - -// Historically, Chrome recommended 12 chars as the maximum short_name length to prevent truncation. -// For more discussion, see https://github.com/GoogleChrome/lighthouse/issues/69 and https://developer.chrome.com/apps/manifest/name#short_name -const SUGGESTED_SHORTNAME_LENGTH = 12; - -class ManifestValues { - /** @typedef {(val: NonNullable, errors: LH.Artifacts.InstallabilityErrors['errors']) => boolean} Validator */ - - /** - * @return {Array<{id: LH.Artifacts.ManifestValueCheckID, failureText: string, validate: Validator}>} - */ - static get manifestChecks() { - return [ - { - id: 'hasStartUrl', - failureText: 'Manifest does not contain a `start_url`', - validate: manifestValue => !!manifestValue.start_url.value, - }, - { - id: 'hasIconsAtLeast144px', - failureText: 'Manifest does not have a PNG icon of at least 144px', - validate: manifestValue => icons.doExist(manifestValue) && - icons.pngSizedAtLeast(144, manifestValue).length > 0, - }, - { - id: 'hasIconsAtLeast512px', - failureText: 'Manifest does not have a PNG icon of at least 512px', - validate: manifestValue => icons.doExist(manifestValue) && - icons.pngSizedAtLeast(512, manifestValue).length > 0, - }, - { - id: 'fetchesIcon', - failureText: 'Manifest icon failed to be fetched', - validate: (manifestValue, errors) => { - const failedToFetchIconErrorIds = [ - 'cannot-download-icon', - 'no-icon-available', - ]; - return icons.doExist(manifestValue) && - !errors.some(error => failedToFetchIconErrorIds.includes(error.errorId)); - }, - }, - { - id: 'hasPWADisplayValue', - failureText: 'Manifest\'s `display` value is not one of: ' + PWA_DISPLAY_VALUES.join(' | '), - validate: manifestValue => PWA_DISPLAY_VALUES.includes(manifestValue.display.value), - }, - { - id: 'hasBackgroundColor', - failureText: 'Manifest does not have `background_color`', - validate: manifestValue => !!manifestValue.background_color.value, - }, - { - id: 'hasThemeColor', - failureText: 'Manifest does not have `theme_color`', - validate: manifestValue => !!manifestValue.theme_color.value, - }, - { - id: 'hasShortName', - failureText: 'Manifest does not have `short_name`', - validate: manifestValue => !!manifestValue.short_name.value, - }, - { - id: 'shortNameLength', - failureText: `Manifest's \`short_name\` is too long (>${SUGGESTED_SHORTNAME_LENGTH} ` + - `characters) to be displayed on a homescreen without truncation`, - // Pass if there's no short_name. Don't want to report a non-existent string is too long - validate: manifestValue => !!manifestValue.short_name.value && - manifestValue.short_name.value.length <= SUGGESTED_SHORTNAME_LENGTH, - }, - { - id: 'hasName', - failureText: 'Manifest does not have `name`', - validate: manifestValue => !!manifestValue.name.value, - }, - { - id: 'hasMaskableIcon', - failureText: 'Manifest does not have at least one icon that is maskable', - validate: ManifestValue => icons.doExist(ManifestValue) && - icons.containsMaskableIcon(ManifestValue), - }, - ]; - } - - /** - * Returns results of all manifest checks - * @param {Pick} Manifest - * @return {Promise} - */ - static async compute_({WebAppManifest, InstallabilityErrors}) { - // if the manifest isn't there or is invalid json, we report that and bail - if (WebAppManifest === null) { - return { - isParseFailure: true, - parseFailureReason: 'No manifest was fetched', - allChecks: [], - }; - } - const manifestValue = WebAppManifest.value; - if (manifestValue === undefined) { - return { - isParseFailure: true, - parseFailureReason: 'Manifest failed to parse as valid JSON', - allChecks: [], - }; - } - - // manifest is valid, so do the rest of the checks - const remainingChecks = ManifestValues.manifestChecks.map(item => { - return { - id: item.id, - failureText: item.failureText, - passing: item.validate(manifestValue, InstallabilityErrors.errors), - }; - }); - - return { - isParseFailure: false, - allChecks: remainingChecks, - }; - } -} - -const ManifestValuesComputed = - makeComputedArtifact(ManifestValues, ['InstallabilityErrors', 'WebAppManifest']); -export {ManifestValuesComputed as ManifestValues}; diff --git a/core/config/default-config.js b/core/config/default-config.js index 085b090ba696..a3400c424440 100644 --- a/core/config/default-config.js +++ b/core/config/default-config.js @@ -90,15 +90,6 @@ const UIStrings = { seoCrawlingGroupTitle: 'Crawling and Indexing', /** Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers. */ seoCrawlingGroupDescription: 'To appear in search results, crawlers need access to your app.', - /** Title of the Progressive Web Application (PWA) category of audits. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. Also used as a label of a score gauge. */ - pwaCategoryTitle: 'PWA', - /** Description of the Progressive Web Application (PWA) category. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ - pwaCategoryDescription: 'These checks validate the aspects of a Progressive Web App. ' + - '[Learn what makes a good Progressive Web App](https://web.dev/articles/pwa-checklist).', - /** Description of the Progressive Web Application (PWA) manual checks category, containing a list of additional validators must be run by hand in order to check all PWA best practices. This is displayed at the top of a list of manually run audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc.. No character length limits. */ - pwaCategoryManualDescription: 'These checks are required by the baseline ' + - '[PWA Checklist](https://web.dev/articles/pwa-checklist) but are ' + - 'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.', /** Title of the Best Practices category of audits. This is displayed at the top of a list of audits focused on topics related to following web development best practices and accepted guidelines. Also used as a label of a score gauge; try to limit to 20 characters. */ bestPracticesCategoryTitle: 'Best Practices', /** Title of the Trust & Safety group of audits. This is displayed at the top of a list of audits focused on maintaining user trust and protecting security in web development. */ @@ -109,10 +100,6 @@ const UIStrings = { bestPracticesBrowserCompatGroupTitle: 'Browser Compatibility', /** Title of the General group of the Best Practices category. Within this section are the audits that don't belong to a specific group but are of general interest. */ bestPracticesGeneralGroupTitle: 'General', - /** Title of the Installable section of the web app category. Within this section are audits that check if Chrome supports installing the web site as an app on their device. */ - pwaInstallableGroupTitle: 'Installable', - /** Title of the "PWA Optimized" section of the web app category. Within this section are audits that check if the developer has taken advantage of features to make their web page more enjoyable and engaging for the user. */ - pwaOptimizedGroupTitle: 'PWA Optimized', }; const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); @@ -137,7 +124,6 @@ const defaultConfig = { {id: 'Inputs', gatherer: 'inputs'}, {id: 'IFrameElements', gatherer: 'iframe-elements'}, {id: 'ImageElements', gatherer: 'image-elements'}, - {id: 'InstallabilityErrors', gatherer: 'installability-errors'}, {id: 'InspectorIssues', gatherer: 'inspector-issues'}, {id: 'JsUsage', gatherer: 'js-usage'}, {id: 'LinkElements', gatherer: 'link-elements'}, @@ -154,7 +140,6 @@ const defaultConfig = { {id: 'TagsBlockingFirstPaint', gatherer: 'dobetterweb/tags-blocking-first-paint'}, {id: 'TraceElements', gatherer: 'trace-elements'}, {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'}, - {id: 'WebAppManifest', gatherer: 'web-app-manifest'}, // Artifact copies are renamed for compatibility with legacy artifacts. {id: 'devtoolsLogs', gatherer: 'devtools-log-compat'}, @@ -186,11 +171,6 @@ const defaultConfig = { 'user-timings', 'critical-request-chains', 'redirects', - 'installable-manifest', - 'splash-screen', - 'themed-omnibox', - 'maskable-icon', - 'content-width', 'image-aspect-ratio', 'image-size-responsive', 'deprecations', @@ -220,9 +200,6 @@ const defaultConfig = { 'prioritize-lcp-image', 'csp-xss', 'script-treemap-data', - 'manual/pwa-cross-browser', - 'manual/pwa-page-transitions', - 'manual/pwa-each-page-has-url', 'accessibility/accesskeys', 'accessibility/aria-allowed-attr', 'accessibility/aria-allowed-role', @@ -345,12 +322,6 @@ const defaultConfig = { title: str_(UIStrings.diagnosticsGroupTitle), description: str_(UIStrings.diagnosticsGroupDescription), }, - 'pwa-installable': { - title: str_(UIStrings.pwaInstallableGroupTitle), - }, - 'pwa-optimized': { - title: str_(UIStrings.pwaOptimizedGroupTitle), - }, 'a11y-best-practices': { title: str_(UIStrings.a11yBestPracticesGroupTitle), description: str_(UIStrings.a11yBestPracticesGroupDescription), @@ -622,26 +593,6 @@ const defaultConfig = { {id: 'structured-data', weight: 0}, ], }, - 'pwa': { - title: str_(UIStrings.pwaCategoryTitle), - description: str_(UIStrings.pwaCategoryDescription), - manualDescription: str_(UIStrings.pwaCategoryManualDescription), - supportedModes: ['navigation'], - auditRefs: [ - // Installable - {id: 'installable-manifest', weight: 2, group: 'pwa-installable'}, - // PWA Optimized - {id: 'splash-screen', weight: 1, group: 'pwa-optimized'}, - {id: 'themed-omnibox', weight: 1, group: 'pwa-optimized'}, - {id: 'content-width', weight: 1, group: 'pwa-optimized'}, - {id: 'viewport', weight: 2, group: 'pwa-optimized'}, - {id: 'maskable-icon', weight: 1, group: 'pwa-optimized'}, - // Manual audits - {id: 'pwa-cross-browser', weight: 0}, - {id: 'pwa-page-transitions', weight: 0}, - {id: 'pwa-each-page-has-url', weight: 0}, - ], - }, }, }; diff --git a/core/gather/gatherers/installability-errors.js b/core/gather/gatherers/installability-errors.js deleted file mode 100644 index b92cf3d0e659..000000000000 --- a/core/gather/gatherers/installability-errors.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import log from 'lighthouse-logger'; - -import BaseGatherer from '../base-gatherer.js'; - -class InstallabilityErrors extends BaseGatherer { - /** @type {LH.Gatherer.GathererMeta} */ - meta = { - supportedModes: ['snapshot', 'navigation'], - }; - - /** - * Creates an Artifacts.InstallabilityErrors, tranforming data from the protocol - * for old versions of Chrome. - * @param {LH.Gatherer.ProtocolSession} session - * @return {Promise} - */ - static async getInstallabilityErrors(session) { - const status = { - msg: 'Get webapp installability errors', - id: 'lh:gather:getInstallabilityErrors', - }; - log.time(status); - const response = await session.sendCommand('Page.getInstallabilityErrors'); - - const errors = response.installabilityErrors; - - log.timeEnd(status); - return {errors}; - } - - /** - * @param {LH.Gatherer.Context} context - * @return {Promise} - */ - async getArtifact(context) { - const driver = context.driver; - - try { - return await InstallabilityErrors.getInstallabilityErrors(driver.defaultSession); - } catch { - return { - errors: [ - {errorId: 'protocol-timeout', errorArguments: []}, - ], - }; - } - } -} - -export default InstallabilityErrors; diff --git a/core/gather/gatherers/web-app-manifest.js b/core/gather/gatherers/web-app-manifest.js deleted file mode 100644 index 8db2bdae6f32..000000000000 --- a/core/gather/gatherers/web-app-manifest.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import log from 'lighthouse-logger'; - -import {parseManifest} from '../../lib/manifest-parser.js'; -import BaseGatherer from '../base-gatherer.js'; - -class WebAppManifest extends BaseGatherer { - /** @type {LH.Gatherer.GathererMeta} */ - meta = { - supportedModes: ['snapshot', 'navigation'], - }; - - /** - * @param {LH.Gatherer.ProtocolSession} session - * @return {Promise<{url: string, data: string}|null>} - */ - static async fetchAppManifest(session) { - // In all environments but LR, Page.getAppManifest finishes very quickly. - // In LR, there is a bug that causes this command to hang until outgoing - // requests finish. This has been seen in long polling (where it will never - // return) and when other requests take a long time to finish. We allow 10 seconds - // for outgoing requests to finish. Anything more, and we continue the run without - // a manifest. - // Googlers, see: http://b/124008171 - session.setNextProtocolTimeout(10000); - let response; - try { - response = await session.sendCommand('Page.getAppManifest'); - } catch (err) { - if (err.code === 'PROTOCOL_TIMEOUT') { - // LR will timeout fetching the app manifest in some cases, move on without one. - // https://github.com/GoogleChrome/lighthouse/issues/7147#issuecomment-461210921 - log.error('WebAppManifest', 'Failed fetching manifest', err); - return null; - } - - throw err; - } - - let data = response.data; - - // We're not reading `response.errors` however it may contain critical and noncritical - // errors from Blink's manifest parser: - // https://chromedevtools.github.io/debugger-protocol-viewer/tot/Page/#type-AppManifestError - if (!data) { - // If the data is empty, the page had no manifest. - return null; - } - - const BOM_LENGTH = 3; - const BOM_FIRSTCHAR = 65279; - const isBomEncoded = data.charCodeAt(0) === BOM_FIRSTCHAR; - - if (isBomEncoded) { - data = Buffer.from(data) - .slice(BOM_LENGTH) - .toString(); - } - - return {...response, data}; - } - - /** - * Uses the debugger protocol to fetch the manifest from within the context of - * the target page, reusing any credentials, emulation, etc, already established - * there. - * - * Returns the parsed manifest or null if the page had no manifest. If the manifest - * was unparseable as JSON, manifest.value will be undefined and manifest.warning - * will have the reason. See manifest-parser.js for more information. - * - * @param {LH.Gatherer.ProtocolSession} session - * @param {string} pageUrl - * @return {Promise} - */ - static async getWebAppManifest(session, pageUrl) { - const status = {msg: 'Get webapp manifest', id: 'lh:gather:getWebAppManifest'}; - log.time(status); - const response = await WebAppManifest.fetchAppManifest(session); - if (!response) return null; - const manifest = parseManifest(response.data, response.url, pageUrl); - log.timeEnd(status); - return manifest; - } - - /** - * @param {LH.Gatherer.Context} context - * @return {Promise} - */ - async getArtifact(context) { - const driver = context.driver; - const {finalDisplayedUrl} = context.baseArtifacts.URL; - try { - return await WebAppManifest.getWebAppManifest(driver.defaultSession, finalDisplayedUrl); - } catch { - return null; - } - } -} - -export default WebAppManifest; diff --git a/core/test/audits/content-width-test.js b/core/test/audits/content-width-test.js deleted file mode 100644 index 0ad9f815b08a..000000000000 --- a/core/test/audits/content-width-test.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert/strict'; - -import Audit from '../../audits/content-width.js'; -import * as constants from '../../config/constants.js'; - -/** @param {LH.SharedFlagsSettings['formFactor']} formFactor */ -const getFakeContext = (formFactor = 'mobile') => ({ - computedCache: new Map(), - settings: { - formFactor: formFactor, - screenEmulation: constants.screenEmulationMetrics[formFactor], - }, -}); - -describe('Mobile-friendly: content-width audit', () => { - it('fails when scroll width differs from viewport width', () => { - const product = Audit.audit({ - ViewportDimensions: { - innerWidth: 100, - outerWidth: 300, - }, - }, getFakeContext()); - - assert.equal(product.score, 0); - assert.ok(product.explanation); - }); - - it('passes when widths match', () => { - return assert.equal(Audit.audit({ - HostUserAgent: '', - ViewportDimensions: { - innerWidth: 300, - outerWidth: 300, - }, - }, getFakeContext()).score, 1); - }); - - it('not applicable when run on desktop', () => { - const product = Audit.audit({ - ViewportDimensions: { - innerWidth: 300, - outerWidth: 450, - }, - }, getFakeContext('desktop')); - - assert.equal(product.notApplicable, true); - }); -}); diff --git a/core/test/audits/installable-manifest-test.js b/core/test/audits/installable-manifest-test.js deleted file mode 100644 index ed3b2749e078..000000000000 --- a/core/test/audits/installable-manifest-test.js +++ /dev/null @@ -1,225 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert/strict'; - -import InstallableManifestAudit from '../../audits/installable-manifest.js'; -import {parseManifest} from '../../lib/manifest-parser.js'; -import {readJson} from '../test-utils.js'; - -const manifest = readJson('../fixtures/manifest.json', import.meta); -const manifestDirtyJpg = readJson('../fixtures/manifest-dirty-jpg.json', import.meta); - -const manifestSrc = JSON.stringify(manifest); -const manifestDirtyJpgSrc = JSON.stringify(manifestDirtyJpg); -const EXAMPLE_MANIFEST_URL = 'https://example.com/manifest.json'; -const EXAMPLE_DOC_URL = 'https://example.com/index.html'; - -function generateMockArtifacts(src = manifestSrc) { - const exampleManifest = parseManifest(src, EXAMPLE_MANIFEST_URL, EXAMPLE_DOC_URL); - - const clonedArtifacts = JSON.parse(JSON.stringify({ - WebAppManifest: exampleManifest, - InstallabilityErrors: {errors: []}, - URL: {finalDisplayedUrl: 'https://example.com'}, - })); - return clonedArtifacts; -} -function generateMockAuditContext() { - return { - computedCache: new Map(), - }; -} -describe('PWA: webapp install banner audit', () => { - describe('basics', () => { - it('fails if page had no manifest', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'no-manifest', errorArguments: []}); - artifacts.WebAppManifest = null; - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - assert.ok(items[0].reason.formattedDefault.includes('no manifest')); - }); - }); - - it('passes when manifest url matches', () => { - const artifacts = generateMockArtifacts(); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(artifacts.WebAppManifest.url, EXAMPLE_MANIFEST_URL); - const debugData = result.details.debugData; - assert.strictEqual(debugData.manifestUrl, EXAMPLE_MANIFEST_URL); - }); - }); - - it('fails with a non-parsable manifest', () => { - const artifacts = generateMockArtifacts('{,:}'); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-empty', errorArguments: []}); - const context = generateMockAuditContext(); - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/could not be parsed/); - }); - }); - - it('fails when an empty manifest is present', () => { - const artifacts = generateMockArtifacts('{}'); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-empty', errorArguments: []}); - const context = generateMockAuditContext(); - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/is empty/); - }); - }); - - it('passes with complete manifest and SW', () => { - const context = generateMockAuditContext(); - return InstallableManifestAudit.audit(generateMockArtifacts(), context).then(result => { - assert.strictEqual(result.score, 1, result.explanation); - assert.strictEqual(result.explanation, undefined, result.explanation); - }); - }); - }); - - describe('one-off-failures', () => { - it('fails when a manifest contains no start_url', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'no-url-for-service-worker', - errorArguments: []}); - artifacts.WebAppManifest.value.start_url.value = undefined; - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/without a 'start_url'/); - }); - }); - - it('fails when a manifest contains no name or short_name', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-missing-name-or-short-name', - errorArguments: []}); - artifacts.WebAppManifest.value.name.value = undefined; - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/does not contain a `name`/); - }); - }); - - it('fails if page had no icons in the manifest', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-missing-suitable-icon', - errorArguments: [{name: 'minimum-icon-size-in-pixels', value: '144'}]}); - artifacts.WebAppManifest.value.icons.value = []; - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/does not contain a suitable icon/); - }); - }); - - it('fails if page had no fetchable icons in the manifest', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'cannot-download-icon', - errorArguments: []}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/Could not download a required icon from/); - }); - }); - }); - - describe('installability error handling', () => { - it('fails when InstallabilityError doesnt have enough errorArguments', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-missing-suitable-icon', - errorArguments: []}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toMatch(/number of arguments/); - }); - }); - - it('fails when InstallabilityError contains too many errorArguments', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-missing-suitable-icon', - errorArguments: [{name: 'argument-1', value: '144'}, {name: 'argument-2', value: '144'}]}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toMatch(/unexpected arguments/); - }); - }); - - it('fails when we receive an unknown InstallabilityError id', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'new-error-id', errorArguments: []}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - assert.ok(items[0].reason.formattedDefault.includes('is not recognized')); - }); - }); - - it('ignores in-incognito InstallabilityError', () => { - const artifacts = generateMockArtifacts(); - artifacts.InstallabilityErrors.errors.push({errorId: 'in-incognito', errorArguments: []}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 1); - }); - }); - }); - - describe('warnings', () => { - it('presents a warning if a warn-not-offline-capable if present', () => { - const artifacts = generateMockArtifacts(manifestDirtyJpgSrc); - artifacts.InstallabilityErrors.errors.push({errorId: 'warn-not-offline-capable'}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - expect(result.warnings).toHaveLength(1); - expect(result.warnings[0]).toBeDisplayString(/not work offline.*August 2021/); - }); - }); - }); - - it('fails if icons were present, but no valid PNG present', () => { - const artifacts = generateMockArtifacts(manifestDirtyJpgSrc); - artifacts.InstallabilityErrors.errors.push({errorId: 'manifest-missing-suitable-icon', - errorArguments: [{name: 'minimum-icon-size-in-pixels', value: '144'}]}); - const context = generateMockAuditContext(); - - return InstallableManifestAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - const items = result.details.items; - expect(items[0].reason).toBeDisplayString(/PNG/); - }); - }); -}); diff --git a/core/test/audits/maskable-icon-test.js b/core/test/audits/maskable-icon-test.js deleted file mode 100644 index ff5f3a9a834f..000000000000 --- a/core/test/audits/maskable-icon-test.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import MaskableIconAudit from '../../audits/maskable-icon.js'; -import {parseManifest} from '../../lib/manifest-parser.js'; -import {readJson} from '../test-utils.js'; - -const manifest = readJson('../fixtures/manifest.json', import.meta); -const manifestWithoutMaskable = readJson('../fixtures/manifest-no-maskable-icon.json', import.meta); - -const manifestSrc = JSON.stringify(manifest); -const manifestWithoutMaskableSrc = JSON.stringify(manifestWithoutMaskable); -const EXAMPLE_MANIFEST_URL = 'https://example.com/manifest.json'; -const EXAMPLE_DOC_URL = 'https://example.com/index.html'; - -/** - * @param {string} - */ -function generateMockArtifacts(src = manifestSrc) { - const exampleManifest = parseManifest(src, EXAMPLE_MANIFEST_URL, EXAMPLE_DOC_URL); - - return { - WebAppManifest: exampleManifest, - InstallabilityErrors: {errors: []}, - }; -} - -function generateMockAuditContext() { - return { - computedCache: new Map(), - }; -} - -describe('Maskable Icon Audit', () => { - const context = generateMockAuditContext(); - - it('fails when the manifest fails to be parsed', async () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest = null; - - const auditResult = await MaskableIconAudit.audit(artifacts, context); - expect(auditResult.score).toEqual(0); - }); - - it('fails when the manifest contains no maskable icons', async () => { - const artifacts = generateMockArtifacts(manifestWithoutMaskableSrc); - - const auditResult = await MaskableIconAudit.audit(artifacts, context); - expect(auditResult.score).toEqual(0); - }); - - it('passes when the manifest contains at least one maskable icon', async () => { - const artifacts = generateMockArtifacts(); - - const auditResult = await MaskableIconAudit.audit(artifacts, context); - expect(auditResult.score).toEqual(1); - }); -}); diff --git a/core/test/audits/splash-screen-test.js b/core/test/audits/splash-screen-test.js deleted file mode 100644 index 269795a5d722..000000000000 --- a/core/test/audits/splash-screen-test.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert/strict'; - -import SplashScreenAudit from '../../audits/splash-screen.js'; -import {parseManifest} from '../../lib/manifest-parser.js'; -import {readJson} from '../test-utils.js'; - -const manifest = readJson('../fixtures/manifest.json', import.meta); -const manifestDirtyJpg = readJson('../fixtures/manifest-dirty-jpg.json', import.meta); - -const manifestSrc = JSON.stringify(manifest); -const manifestDirtyJpgSrc = JSON.stringify(manifestDirtyJpg); -const EXAMPLE_MANIFEST_URL = 'https://example.com/manifest.json'; -const EXAMPLE_DOC_URL = 'https://example.com/index.html'; - -/** - * @param {string} src - */ -function generateMockArtifacts(src = manifestSrc) { - const exampleManifest = parseManifest(src, EXAMPLE_MANIFEST_URL, EXAMPLE_DOC_URL); - - return { - WebAppManifest: exampleManifest, - InstallabilityErrors: {errors: []}, - }; -} -function generateMockAuditContext() { - return { - computedCache: new Map(), - }; -} -describe('PWA: splash screen audit', () => { - describe('basics', () => { - it('fails if page had no manifest', () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest = null; - const context = generateMockAuditContext(); - - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('No manifest was fetched'), result.explanation); - }); - }); - - it('fails with a non-parsable manifest', () => { - const artifacts = generateMockArtifacts('{,:}'); - const context = generateMockAuditContext(); - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('failed to parse as valid JSON')); - }); - }); - - it('fails when an empty manifest is present', () => { - const artifacts = generateMockArtifacts('{}'); - const context = generateMockAuditContext(); - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation); - assert.strictEqual(result.details.items[0].failures.length, 4); - }); - }); - - it('passes with complete manifest and SW', () => { - const context = generateMockAuditContext(); - return SplashScreenAudit.audit(generateMockArtifacts(), context).then(result => { - assert.strictEqual(result.score, 1, result.explanation); - assert.strictEqual(result.explanation, undefined, result.explanation); - }); - }); - }); - - describe('one-off-failures', () => { - it('fails when a manifest contains no name', () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest.value.name.value = undefined; - const context = generateMockAuditContext(); - - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('name'), result.explanation); - }); - }); - - it('fails when a manifest contains no background color', () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest.value.background_color.value = undefined; - const context = generateMockAuditContext(); - - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('background_color'), result.explanation); - }); - }); - - it('fails when a manifest contains no theme color', () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest.value.theme_color.value = undefined; - const context = generateMockAuditContext(); - - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('theme_color'), result.explanation); - }); - }); - - it('fails if page had no icons in the manifest', () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest.value.icons.value = []; - const context = generateMockAuditContext(); - - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('PNG icon'), result.explanation); - }); - }); - - it('fails if icons were present, but no valid PNG present', () => { - const artifacts = generateMockArtifacts(manifestDirtyJpgSrc); - const context = generateMockAuditContext(); - - return SplashScreenAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('PNG icon'), result.explanation); - const failures = result.details.items[0].failures; - assert.strictEqual(failures.length, 1, failures); - }); - }); - }); -}); diff --git a/core/test/audits/themed-omnibox-test.js b/core/test/audits/themed-omnibox-test.js deleted file mode 100644 index ccd7be4bdbe3..000000000000 --- a/core/test/audits/themed-omnibox-test.js +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert/strict'; - -import ThemedOmniboxAudit from '../../audits/themed-omnibox.js'; -import {parseManifest} from '../../lib/manifest-parser.js'; -import {readJson} from '../test-utils.js'; - -const manifest = readJson('../fixtures/manifest.json', import.meta); - -const manifestSrc = JSON.stringify(manifest); -const EXAMPLE_MANIFEST_URL = 'https://example.com/manifest.json'; -const EXAMPLE_DOC_URL = 'https://example.com/index.html'; - -/** - * @param {string} src - */ -function generateMockArtifacts(src = manifestSrc) { - const exampleManifest = parseManifest(src, EXAMPLE_MANIFEST_URL, EXAMPLE_DOC_URL); - - return { - WebAppManifest: exampleManifest, - InstallabilityErrors: {errors: []}, - MetaElements: [{name: 'theme-color', content: '#bada55'}], - }; -} - -function generateMockAuditContext() { - return { - computedCache: new Map(), - }; -} -describe('PWA: themed omnibox audit', () => { - it('fails if page had no manifest', () => { - const artifacts = generateMockArtifacts(); - artifacts.WebAppManifest = null; - const context = generateMockAuditContext(); - - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.strictEqual(result.score, 0); - assert.ok(result.explanation.includes('No manifest was fetched'), result.explanation); - }); - }); - - // Need to disable camelcase check for dealing with theme_color. - /* eslint-disable camelcase */ - it('fails when a minimal manifest contains no theme_color', () => { - const artifacts = generateMockArtifacts(JSON.stringify({ - start_url: '/', - })); - const context = generateMockAuditContext(); - - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 0); - assert.ok(result.explanation); - }); - }); - - it('succeeds when a minimal manifest contains a theme_color', () => { - const artifacts = generateMockArtifacts(JSON.stringify({ - theme_color: '#bada55', - })); - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 1); - assert.equal(result.explanation, undefined); - }); - }); - - /* eslint-enable camelcase */ - it('succeeds when a complete manifest contains a theme_color', () => { - const artifacts = generateMockArtifacts(); - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 1); - assert.equal(result.explanation, undefined); - }); - }); - - it('fails and warns when no theme-color meta tag found', () => { - const artifacts = generateMockArtifacts(); - artifacts.MetaElements = []; - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 0); - assert.ok(result.explanation); - }); - }); - - it('succeeds when theme-color present in the html', () => { - const artifacts = generateMockArtifacts(); - artifacts.MetaElements = [{name: 'theme-color', content: '#fafa33'}]; - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 1); - assert.equal(result.explanation, undefined); - }); - }); - - it('succeeds when theme-color has a CSS nickname content value', () => { - const artifacts = generateMockArtifacts(); - artifacts.MetaElements = [{name: 'theme-color', content: 'red'}]; - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 1); - assert.equal(result.explanation, undefined); - }); - }); - - it('succeeds when theme-color has a CSS4 nickname content value', async () => { - const artifacts = generateMockArtifacts(); - artifacts.MetaElements = [{name: 'theme-color', content: 'rebeccapurple'}]; // <3 - const context = generateMockAuditContext(); - - const result = await ThemedOmniboxAudit.audit(artifacts, context); - assert.equal(result.score, 1); - assert.equal(result.explanation, undefined); - }); - - it('fails if HTML theme color is good, but manifest themecolor is bad', () => { - const artifacts = generateMockArtifacts(JSON.stringify({ - start_url: '/', - })); - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 0); - assert.ok(result.explanation.includes('does not have `theme_color`'), result.explanation); - }); - }); - - it('fails if HTML theme color is bad, and manifest themecolor is good', () => { - const artifacts = generateMockArtifacts(); - artifacts.MetaElements = [{name: 'theme-color'}]; - const context = generateMockAuditContext(); - return ThemedOmniboxAudit.audit(artifacts, context).then(result => { - assert.equal(result.score, 0); - assert.ok(result.explanation.includes('theme-color meta tag'), result.explanation); - }); - }); -}); diff --git a/core/test/computed/manifest-values-test.js b/core/test/computed/manifest-values-test.js deleted file mode 100644 index e9f7ffd03634..000000000000 --- a/core/test/computed/manifest-values-test.js +++ /dev/null @@ -1,282 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert/strict'; - -import {ManifestValues} from '../../computed/manifest-values.js'; -import {parseManifest} from '../../lib/manifest-parser.js'; -import {readJson} from '../test-utils.js'; - -const manifest = readJson('../fixtures/manifest.json', import.meta); - -const manifestSrc = JSON.stringify(manifest); - -function getMockContext() { - return { - computedCache: new Map(), - }; -} - -/** - * Simple manifest parsing helper when the manifest URLs aren't material to the - * test. Uses example.com URLs for testing. - * @param {string} manifestSrc - * @return {!ManifestNode<(!Manifest|undefined)>} - */ -function noUrlManifestParser(manifestSrc) { - const EXAMPLE_MANIFEST_URL = 'https://example.com/manifest.json'; - const EXAMPLE_DOC_URL = 'https://example.com/index.html'; - return parseManifest(manifestSrc, EXAMPLE_MANIFEST_URL, EXAMPLE_DOC_URL); -} - -describe('ManifestValues computed artifact', () => { - it('reports a parse failure if page had no manifest', async () => { - const WebAppManifest = null; - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - assert.equal(results.isParseFailure, true); - assert.ok(results.parseFailureReason, 'No manifest was fetched'); - assert.equal(results.allChecks.length, 0); - }); - - it('reports a parse failure if page had an unparseable manifest', async () => { - const WebAppManifest = noUrlManifestParser('{:,}'); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - assert.equal(results.isParseFailure, true); - assert.ok(results.parseFailureReason.includes('failed to parse as valid JSON')); - assert.equal(results.allChecks.length, 0); - }); - - it('passes the parsing checks on an empty manifest', async () => { - const WebAppManifest = noUrlManifestParser('{}'); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - assert.equal(results.isParseFailure, false); - assert.equal(results.parseFailureReason, undefined); - }); - - it('passes the all checks with fixture manifest', async () => { - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - assert.equal(results.isParseFailure, false); - assert.equal(results.parseFailureReason, undefined); - - assert.equal(results.allChecks.length, ManifestValues.manifestChecks.length); - assert.equal(results.allChecks.every(i => i.passing), true, 'not all checks passed'); - }); - - describe('color checks', () => { - it('fails when a minimal manifest contains no background_color', async () => { - const WebAppManifest = noUrlManifestParser(JSON.stringify({ - start_url: '/', - })); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const colorResults = results.allChecks.filter(i => i.id.includes('Color')); - assert.equal(colorResults.every(i => i.passing === false), true); - }); - - it('succeeds when a minimal manifest contains a valid background_color', async () => { - const WebAppManifest = noUrlManifestParser(JSON.stringify({ - background_color: '#FAFAFA', - theme_color: '#FAFAFA', - })); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const colorResults = results.allChecks.filter(i => i.id.includes('Color')); - assert.equal(colorResults.every(i => i.passing === true), true); - }); - }); - - describe('hasPWADisplayValue', () => { - const check = ManifestValues.manifestChecks.find(i => i.id === 'hasPWADisplayValue'); - - it('passes accepted values', () => { - let manifestValue; - manifestValue = noUrlManifestParser(JSON.stringify({display: 'minimal-ui'})).value; - assert.equal(check.validate(manifestValue), true, 'doesnt pass minimal-ui'); - manifestValue = noUrlManifestParser(JSON.stringify({display: 'standalone'})).value; - assert.equal(check.validate(manifestValue), true, 'doesnt pass standalone'); - manifestValue = noUrlManifestParser(JSON.stringify({display: 'fullscreen'})).value; - assert.equal(check.validate(manifestValue), true, 'doesnt pass fullscreen'); - }); - it('fails invalid values', () => { - let manifestValue; - manifestValue = noUrlManifestParser(JSON.stringify({display: 'display'})).value; - assert.equal(check.validate(manifestValue), false, 'doesnt fail display'); - manifestValue = noUrlManifestParser(JSON.stringify({display: ''})).value; - assert.equal(check.validate(manifestValue), false, 'doesnt fail empty string'); - }); - }); - - describe('icons checks', () => { - describe('icons exist check', () => { - it('fails when a manifest contains no icons array', async () => { - const manifestSrc = JSON.stringify({ - name: 'NoIconsHere', - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Icons')); - assert.equal(iconResults.every(i => i.passing === false), true); - }); - - it('fails when a manifest contains no icons', async () => { - const manifestSrc = JSON.stringify({ - icons: [], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Icons')); - assert.equal(iconResults.every(i => i.passing === false), true); - }); - - it('fails when a manifest icon fails to fetch icon', async () => { - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon.png', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: [{errorId: 'no-icon-available'}]}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - expect(results.allChecks.map(r => r.id)).toContain('fetchesIcon'); - }); - }); - - describe('icons at least X size check', () => { - it('fails when a manifest contains an icon with no size', async () => { - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon.png', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Icons')); - - assert.equal(iconResults.every(i => i.passing === false), true); - }); - - it('succeeds when there\'s one icon with multiple sizes, and one is valid', async () => { - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon.png', - sizes: '72x72 96x96 128x128 256x256 1024x1024', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Icons')); - - assert.equal(iconResults.every(i => i.passing === true), true); - }); - - it('succeeds when there\'s two icons, one with and one without valid size', async () => { - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon.png', - }, { - src: 'icon2.png', - sizes: '1256x1256', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Icons')); - - assert.equal(iconResults.every(i => i.passing === true), true); - }); - - it('fails when an icon has a valid size, though it\'s non-square.', async () => { - // See also: https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/banners/app_banner_data_fetcher_unittest.cc&sq=package:chromium&type=cs&q=%22Non-square%20is%20okay%22%20file:%5Esrc/chrome/browser/banners/ - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon-non-square.png', - sizes: '200x220', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Icons')); - - assert.equal(iconResults.every(i => i.passing === false), true); - }); - }); - - describe('manifest has at least one maskable icon', () => { - it('fails when no maskable icon exists', async () => { - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon.png', - purpose: 'any', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Maskable')); - - assert.equal(iconResults.every(i => i.passing === false), true); - }); - - it('passes when an icon has the maskable purpose property', async () => { - const manifestSrc = JSON.stringify({ - icons: [{ - src: 'icon.png', - }, { - src: 'icon2.png', - purpose: 'maskable', - }], - }); - const WebAppManifest = noUrlManifestParser(manifestSrc); - const InstallabilityErrors = {errors: []}; - const artifacts = {WebAppManifest, InstallabilityErrors}; - - const results = await ManifestValues.request(artifacts, getMockContext()); - const iconResults = results.allChecks.filter(i => i.id.includes('Maskable')); - - assert.equal(iconResults.every(i => i.passing === true), true); - }); - }); - }); -}); 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 e3d58c210eaa..1475891a5564 100644 --- a/core/test/fixtures/user-flows/reports/sample-flow-result.json +++ b/core/test/fixtures/user-flows/reports/sample-flow-result.json @@ -388,93 +388,6 @@ }, "guidanceLevel": 2 }, - "installable-manifest": { - "id": "installable-manifest", - "title": "Web app manifest or service worker do not meet the installability requirements", - "description": "Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper service worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more about manifest installability requirements](https://developer.chrome.com/docs/lighthouse/pwa/installable-manifest/).", - "score": 0, - "scoreDisplayMode": "binary", - "numericValue": 1, - "numericUnit": "element", - "displayValue": "1 reason", - "warnings": [], - "details": { - "type": "table", - "headings": [ - { - "key": "reason", - "valueType": "text", - "label": "Failure reason" - } - ], - "items": [ - { - "reason": "Page has no manifest URL" - } - ], - "debugData": { - "type": "debugdata", - "manifestUrl": null - } - } - }, - "splash-screen": { - "id": "splash-screen", - "title": "Is not configured for a custom splash screen", - "description": "A themed splash screen ensures a high-quality experience when users launch your app from their homescreens. [Learn more about splash screens](https://developer.chrome.com/docs/lighthouse/pwa/splash-screen/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "Failures: No manifest was fetched.", - "details": { - "type": "debugdata", - "items": [ - { - "failures": [ - "No manifest was fetched" - ], - "isParseFailure": true, - "parseFailureReason": "No manifest was fetched" - } - ] - } - }, - "themed-omnibox": { - "id": "themed-omnibox", - "title": "Does not set a theme color for the address bar.", - "description": "The browser address bar can be themed to match your site. [Learn more about theming the address bar](https://developer.chrome.com/docs/lighthouse/pwa/themed-omnibox/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "Failures: No manifest was fetched,\nNo `` tag found.", - "details": { - "type": "debugdata", - "items": [ - { - "failures": [ - "No manifest was fetched", - "No `` tag found" - ], - "themeColor": null, - "isParseFailure": true, - "parseFailureReason": "No manifest was fetched" - } - ] - } - }, - "maskable-icon": { - "id": "maskable-icon", - "title": "Manifest doesn't have a maskable icon", - "description": "A maskable icon ensures that the image fills the entire shape without being letterboxed when installing the app on a device. [Learn about maskable manifest icons](https://developer.chrome.com/docs/lighthouse/pwa/maskable-icon-audit/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "No manifest was fetched" - }, - "content-width": { - "id": "content-width", - "title": "Content is sized correctly for the viewport", - "description": "If the width of your app's content doesn't match the width of the viewport, your app might not be optimized for mobile screens. [Learn how to size content for the viewport](https://developer.chrome.com/docs/lighthouse/pwa/content-width/).", - "score": 1, - "scoreDisplayMode": "binary" - }, "image-aspect-ratio": { "id": "image-aspect-ratio", "title": "Displays images with correct aspect ratio", @@ -2113,27 +2026,6 @@ ] } }, - "pwa-cross-browser": { - "id": "pwa-cross-browser", - "title": "Site works cross-browser", - "description": "To reach the most number of users, sites should work across every major browser. [Learn about cross-browser compatibility](https://developer.chrome.com/docs/lighthouse/pwa/pwa-cross-browser/).", - "score": null, - "scoreDisplayMode": "manual" - }, - "pwa-page-transitions": { - "id": "pwa-page-transitions", - "title": "Page transitions don't feel like they block on the network", - "description": "Transitions should feel snappy as you tap around, even on a slow network. This experience is key to a user's perception of performance. [Learn more about page transitions](https://developer.chrome.com/docs/lighthouse/pwa/pwa-page-transitions/).", - "score": null, - "scoreDisplayMode": "manual" - }, - "pwa-each-page-has-url": { - "id": "pwa-each-page-has-url", - "title": "Each page has a URL", - "description": "Ensure individual pages are deep linkable via URL and that URLs are unique for the purpose of shareability on social media. [Learn more about providing deep links](https://developer.chrome.com/docs/lighthouse/pwa/pwa-each-page-has-url/).", - "score": null, - "scoreDisplayMode": "manual" - }, "accesskeys": { "id": "accesskeys", "title": "`[accesskey]` values are unique", @@ -4588,60 +4480,6 @@ ], "id": "seo", "score": 1 - }, - "pwa": { - "title": "PWA", - "description": "These checks validate the aspects of a Progressive Web App. [Learn what makes a good Progressive Web App](https://web.dev/articles/pwa-checklist).", - "manualDescription": "These checks are required by the baseline [PWA Checklist](https://web.dev/articles/pwa-checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually.", - "supportedModes": [ - "navigation" - ], - "auditRefs": [ - { - "id": "installable-manifest", - "weight": 2, - "group": "pwa-installable" - }, - { - "id": "splash-screen", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "themed-omnibox", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "content-width", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "viewport", - "weight": 2, - "group": "pwa-optimized" - }, - { - "id": "maskable-icon", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "pwa-cross-browser", - "weight": 0 - }, - { - "id": "pwa-page-transitions", - "weight": 0 - }, - { - "id": "pwa-each-page-has-url", - "weight": 0 - } - ], - "id": "pwa", - "score": 0.38 } }, "categoryGroups": { @@ -4656,12 +4494,6 @@ "title": "Diagnostics", "description": "More information about the performance of your application. These numbers don't [directly affect](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) the Performance score." }, - "pwa-installable": { - "title": "Installable" - }, - "pwa-optimized": { - "title": "PWA Optimized" - }, "a11y-best-practices": { "title": "Best practices", "description": "These items highlight common accessibility best practices." @@ -5878,283 +5710,283 @@ }, { "startTime": 108, - "name": "lh:audit:installable-manifest", + "name": "lh:audit:image-aspect-ratio", "duration": 1, "entryType": "measure" }, { "startTime": 109, - "name": "lh:audit:splash-screen", + "name": "lh:audit:image-size-responsive", "duration": 1, "entryType": "measure" }, { "startTime": 110, - "name": "lh:computed:ManifestValues", + "name": "lh:audit:deprecations", "duration": 1, "entryType": "measure" }, { "startTime": 111, - "name": "lh:audit:themed-omnibox", + "name": "lh:audit:third-party-cookies", "duration": 1, "entryType": "measure" }, { "startTime": 112, - "name": "lh:audit:maskable-icon", + "name": "lh:audit:mainthread-work-breakdown", "duration": 1, "entryType": "measure" }, { "startTime": 113, - "name": "lh:audit:content-width", + "name": "lh:computed:MainThreadTasks", "duration": 1, "entryType": "measure" }, { "startTime": 114, - "name": "lh:audit:image-aspect-ratio", + "name": "lh:audit:bootup-time", "duration": 1, "entryType": "measure" }, { "startTime": 115, - "name": "lh:audit:image-size-responsive", + "name": "lh:computed:TBTImpactTasks", "duration": 1, "entryType": "measure" }, { "startTime": 116, - "name": "lh:audit:deprecations", + "name": "lh:audit:uses-rel-preconnect", "duration": 1, "entryType": "measure" }, { "startTime": 117, - "name": "lh:audit:third-party-cookies", + "name": "lh:audit:font-display", "duration": 1, "entryType": "measure" }, { "startTime": 118, - "name": "lh:audit:mainthread-work-breakdown", + "name": "lh:audit:diagnostics", "duration": 1, "entryType": "measure" }, { "startTime": 119, - "name": "lh:computed:MainThreadTasks", + "name": "lh:audit:network-requests", "duration": 1, "entryType": "measure" }, { "startTime": 120, - "name": "lh:audit:bootup-time", + "name": "lh:computed:EntityClassification", "duration": 1, "entryType": "measure" }, { "startTime": 121, - "name": "lh:computed:TBTImpactTasks", + "name": "lh:audit:network-rtt", "duration": 1, "entryType": "measure" }, { "startTime": 122, - "name": "lh:audit:uses-rel-preconnect", + "name": "lh:audit:network-server-latency", "duration": 1, "entryType": "measure" }, { "startTime": 123, - "name": "lh:audit:font-display", + "name": "lh:audit:main-thread-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 124, - "name": "lh:audit:diagnostics", + "name": "lh:audit:metrics", "duration": 1, "entryType": "measure" }, { "startTime": 125, - "name": "lh:audit:network-requests", + "name": "lh:computed:TimingSummary", "duration": 1, "entryType": "measure" }, { "startTime": 126, - "name": "lh:computed:EntityClassification", + "name": "lh:computed:FirstContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 127, - "name": "lh:audit:network-rtt", + "name": "lh:computed:LargestContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 128, - "name": "lh:audit:network-server-latency", + "name": "lh:computed:LCPBreakdown", "duration": 1, "entryType": "measure" }, { "startTime": 129, - "name": "lh:audit:main-thread-tasks", + "name": "lh:computed:TimeToFirstByte", "duration": 1, "entryType": "measure" }, { "startTime": 130, - "name": "lh:audit:metrics", + "name": "lh:computed:LCPImageRecord", "duration": 1, "entryType": "measure" }, { "startTime": 131, - "name": "lh:computed:TimingSummary", + "name": "lh:audit:performance-budget", "duration": 1, "entryType": "measure" }, { "startTime": 132, - "name": "lh:computed:FirstContentfulPaintAllFrames", + "name": "lh:computed:ResourceSummary", "duration": 1, "entryType": "measure" }, { "startTime": 133, - "name": "lh:computed:LargestContentfulPaintAllFrames", + "name": "lh:audit:timing-budget", "duration": 1, "entryType": "measure" }, { "startTime": 134, - "name": "lh:computed:LCPBreakdown", + "name": "lh:audit:resource-summary", "duration": 1, "entryType": "measure" }, { "startTime": 135, - "name": "lh:computed:TimeToFirstByte", + "name": "lh:audit:third-party-summary", "duration": 1, "entryType": "measure" }, { "startTime": 136, - "name": "lh:computed:LCPImageRecord", + "name": "lh:audit:third-party-facades", "duration": 1, "entryType": "measure" }, { "startTime": 137, - "name": "lh:audit:performance-budget", + "name": "lh:audit:largest-contentful-paint-element", "duration": 1, "entryType": "measure" }, { "startTime": 138, - "name": "lh:computed:ResourceSummary", + "name": "lh:audit:lcp-lazy-loaded", "duration": 1, "entryType": "measure" }, { "startTime": 139, - "name": "lh:audit:timing-budget", + "name": "lh:audit:layout-shifts", "duration": 1, "entryType": "measure" }, { "startTime": 140, - "name": "lh:audit:resource-summary", + "name": "lh:computed:TraceEngineResult", "duration": 1, "entryType": "measure" }, { "startTime": 141, - "name": "lh:audit:third-party-summary", + "name": "lh:audit:long-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 142, - "name": "lh:audit:third-party-facades", + "name": "lh:audit:non-composited-animations", "duration": 1, "entryType": "measure" }, { "startTime": 143, - "name": "lh:audit:largest-contentful-paint-element", + "name": "lh:audit:unsized-images", "duration": 1, "entryType": "measure" }, { "startTime": 144, - "name": "lh:audit:lcp-lazy-loaded", + "name": "lh:audit:valid-source-maps", "duration": 1, "entryType": "measure" }, { "startTime": 145, - "name": "lh:audit:layout-shifts", + "name": "lh:audit:prioritize-lcp-image", "duration": 1, "entryType": "measure" }, { "startTime": 146, - "name": "lh:computed:TraceEngineResult", + "name": "lh:audit:csp-xss", "duration": 1, "entryType": "measure" }, { "startTime": 147, - "name": "lh:audit:long-tasks", + "name": "lh:audit:script-treemap-data", "duration": 1, "entryType": "measure" }, { "startTime": 148, - "name": "lh:audit:non-composited-animations", + "name": "lh:computed:ModuleDuplication", "duration": 1, "entryType": "measure" }, { "startTime": 149, - "name": "lh:audit:unsized-images", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 150, - "name": "lh:audit:valid-source-maps", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 151, - "name": "lh:audit:prioritize-lcp-image", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 152, - "name": "lh:audit:csp-xss", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 153, - "name": "lh:audit:script-treemap-data", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 154, - "name": "lh:computed:ModuleDuplication", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, @@ -6208,720 +6040,666 @@ }, { "startTime": 163, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:accesskeys", "duration": 1, "entryType": "measure" }, { "startTime": 164, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:aria-allowed-attr", "duration": 1, "entryType": "measure" }, { "startTime": 165, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:aria-allowed-role", "duration": 1, "entryType": "measure" }, { "startTime": 166, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:aria-command-name", "duration": 1, "entryType": "measure" }, { "startTime": 167, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:aria-dialog-name", "duration": 1, "entryType": "measure" }, { "startTime": 168, - "name": "lh:computed:UnusedJavascriptSummary", + "name": "lh:audit:aria-hidden-body", "duration": 1, "entryType": "measure" }, { "startTime": 169, - "name": "lh:audit:pwa-cross-browser", + "name": "lh:audit:aria-hidden-focus", "duration": 1, "entryType": "measure" }, { "startTime": 170, - "name": "lh:audit:pwa-page-transitions", + "name": "lh:audit:aria-input-field-name", "duration": 1, "entryType": "measure" }, { "startTime": 171, - "name": "lh:audit:pwa-each-page-has-url", + "name": "lh:audit:aria-meter-name", "duration": 1, "entryType": "measure" }, { "startTime": 172, - "name": "lh:audit:accesskeys", + "name": "lh:audit:aria-progressbar-name", "duration": 1, "entryType": "measure" }, { "startTime": 173, - "name": "lh:audit:aria-allowed-attr", + "name": "lh:audit:aria-required-attr", "duration": 1, "entryType": "measure" }, { "startTime": 174, - "name": "lh:audit:aria-allowed-role", + "name": "lh:audit:aria-required-children", "duration": 1, "entryType": "measure" }, { "startTime": 175, - "name": "lh:audit:aria-command-name", + "name": "lh:audit:aria-required-parent", "duration": 1, "entryType": "measure" }, { "startTime": 176, - "name": "lh:audit:aria-dialog-name", + "name": "lh:audit:aria-roles", "duration": 1, "entryType": "measure" }, { "startTime": 177, - "name": "lh:audit:aria-hidden-body", + "name": "lh:audit:aria-text", "duration": 1, "entryType": "measure" }, { "startTime": 178, - "name": "lh:audit:aria-hidden-focus", + "name": "lh:audit:aria-toggle-field-name", "duration": 1, "entryType": "measure" }, { "startTime": 179, - "name": "lh:audit:aria-input-field-name", + "name": "lh:audit:aria-tooltip-name", "duration": 1, "entryType": "measure" }, { "startTime": 180, - "name": "lh:audit:aria-meter-name", + "name": "lh:audit:aria-treeitem-name", "duration": 1, "entryType": "measure" }, { "startTime": 181, - "name": "lh:audit:aria-progressbar-name", + "name": "lh:audit:aria-valid-attr-value", "duration": 1, "entryType": "measure" }, { "startTime": 182, - "name": "lh:audit:aria-required-attr", + "name": "lh:audit:aria-valid-attr", "duration": 1, "entryType": "measure" }, { "startTime": 183, - "name": "lh:audit:aria-required-children", + "name": "lh:audit:button-name", "duration": 1, "entryType": "measure" }, { "startTime": 184, - "name": "lh:audit:aria-required-parent", + "name": "lh:audit:bypass", "duration": 1, "entryType": "measure" }, { "startTime": 185, - "name": "lh:audit:aria-roles", + "name": "lh:audit:color-contrast", "duration": 1, "entryType": "measure" }, { "startTime": 186, - "name": "lh:audit:aria-text", + "name": "lh:audit:definition-list", "duration": 1, "entryType": "measure" }, { "startTime": 187, - "name": "lh:audit:aria-toggle-field-name", + "name": "lh:audit:dlitem", "duration": 1, "entryType": "measure" }, { "startTime": 188, - "name": "lh:audit:aria-tooltip-name", + "name": "lh:audit:document-title", "duration": 1, "entryType": "measure" }, { "startTime": 189, - "name": "lh:audit:aria-treeitem-name", + "name": "lh:audit:duplicate-id-aria", "duration": 1, "entryType": "measure" }, { "startTime": 190, - "name": "lh:audit:aria-valid-attr-value", + "name": "lh:audit:empty-heading", "duration": 1, "entryType": "measure" }, { "startTime": 191, - "name": "lh:audit:aria-valid-attr", + "name": "lh:audit:form-field-multiple-labels", "duration": 1, "entryType": "measure" }, { "startTime": 192, - "name": "lh:audit:button-name", + "name": "lh:audit:frame-title", "duration": 1, "entryType": "measure" }, { "startTime": 193, - "name": "lh:audit:bypass", + "name": "lh:audit:heading-order", "duration": 1, "entryType": "measure" }, { "startTime": 194, - "name": "lh:audit:color-contrast", + "name": "lh:audit:html-has-lang", "duration": 1, "entryType": "measure" }, { "startTime": 195, - "name": "lh:audit:definition-list", + "name": "lh:audit:html-lang-valid", "duration": 1, "entryType": "measure" }, { "startTime": 196, - "name": "lh:audit:dlitem", + "name": "lh:audit:html-xml-lang-mismatch", "duration": 1, "entryType": "measure" }, { "startTime": 197, - "name": "lh:audit:document-title", + "name": "lh:audit:identical-links-same-purpose", "duration": 1, "entryType": "measure" }, { "startTime": 198, - "name": "lh:audit:duplicate-id-aria", + "name": "lh:audit:image-alt", "duration": 1, "entryType": "measure" }, { "startTime": 199, - "name": "lh:audit:empty-heading", + "name": "lh:audit:image-redundant-alt", "duration": 1, "entryType": "measure" }, { "startTime": 200, - "name": "lh:audit:form-field-multiple-labels", + "name": "lh:audit:input-button-name", "duration": 1, "entryType": "measure" }, { "startTime": 201, - "name": "lh:audit:frame-title", + "name": "lh:audit:input-image-alt", "duration": 1, "entryType": "measure" }, { "startTime": 202, - "name": "lh:audit:heading-order", + "name": "lh:audit:label-content-name-mismatch", "duration": 1, "entryType": "measure" }, { "startTime": 203, - "name": "lh:audit:html-has-lang", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 204, - "name": "lh:audit:html-lang-valid", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 205, - "name": "lh:audit:html-xml-lang-mismatch", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 206, - "name": "lh:audit:identical-links-same-purpose", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 207, - "name": "lh:audit:image-alt", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 208, - "name": "lh:audit:image-redundant-alt", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 209, - "name": "lh:audit:input-button-name", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 210, - "name": "lh:audit:input-image-alt", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 211, - "name": "lh:audit:label-content-name-mismatch", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 212, "name": "lh:audit:label", "duration": 1, "entryType": "measure" }, { - "startTime": 213, + "startTime": 204, "name": "lh:audit:landmark-one-main", "duration": 1, "entryType": "measure" }, { - "startTime": 214, + "startTime": 205, "name": "lh:audit:link-name", "duration": 1, "entryType": "measure" }, { - "startTime": 215, + "startTime": 206, "name": "lh:audit:link-in-text-block", "duration": 1, "entryType": "measure" }, { - "startTime": 216, + "startTime": 207, "name": "lh:audit:list", "duration": 1, "entryType": "measure" }, { - "startTime": 217, + "startTime": 208, "name": "lh:audit:listitem", "duration": 1, "entryType": "measure" }, { - "startTime": 218, + "startTime": 209, "name": "lh:audit:meta-refresh", "duration": 1, "entryType": "measure" }, { - "startTime": 219, + "startTime": 210, "name": "lh:audit:meta-viewport", "duration": 1, "entryType": "measure" }, { - "startTime": 220, + "startTime": 211, "name": "lh:audit:object-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 221, + "startTime": 212, "name": "lh:audit:select-name", "duration": 1, "entryType": "measure" }, { - "startTime": 222, + "startTime": 213, "name": "lh:audit:skip-link", "duration": 1, "entryType": "measure" }, { - "startTime": 223, + "startTime": 214, "name": "lh:audit:tabindex", "duration": 1, "entryType": "measure" }, { - "startTime": 224, + "startTime": 215, "name": "lh:audit:table-duplicate-name", "duration": 1, "entryType": "measure" }, { - "startTime": 225, + "startTime": 216, "name": "lh:audit:table-fake-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 226, + "startTime": 217, "name": "lh:audit:target-size", "duration": 1, "entryType": "measure" }, { - "startTime": 227, + "startTime": 218, "name": "lh:audit:td-has-header", "duration": 1, "entryType": "measure" }, { - "startTime": 228, + "startTime": 219, "name": "lh:audit:td-headers-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 229, + "startTime": 220, "name": "lh:audit:th-has-data-cells", "duration": 1, "entryType": "measure" }, { - "startTime": 230, + "startTime": 221, "name": "lh:audit:valid-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 231, + "startTime": 222, "name": "lh:audit:video-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 232, + "startTime": 223, "name": "lh:audit:custom-controls-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 233, + "startTime": 224, "name": "lh:audit:custom-controls-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 234, + "startTime": 225, "name": "lh:audit:focus-traps", "duration": 1, "entryType": "measure" }, { - "startTime": 235, + "startTime": 226, "name": "lh:audit:focusable-controls", "duration": 1, "entryType": "measure" }, { - "startTime": 236, + "startTime": 227, "name": "lh:audit:interactive-element-affordance", "duration": 1, "entryType": "measure" }, { - "startTime": 237, + "startTime": 228, "name": "lh:audit:logical-tab-order", "duration": 1, "entryType": "measure" }, { - "startTime": 238, + "startTime": 229, "name": "lh:audit:managed-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 239, + "startTime": 230, "name": "lh:audit:offscreen-content-hidden", "duration": 1, "entryType": "measure" }, { - "startTime": 240, + "startTime": 231, "name": "lh:audit:use-landmarks", "duration": 1, "entryType": "measure" }, { - "startTime": 241, + "startTime": 232, "name": "lh:audit:visual-order-follows-dom", "duration": 1, "entryType": "measure" }, { - "startTime": 242, + "startTime": 233, "name": "lh:audit:uses-long-cache-ttl", "duration": 1, "entryType": "measure" }, { - "startTime": 243, + "startTime": 234, "name": "lh:audit:total-byte-weight", "duration": 1, "entryType": "measure" }, { - "startTime": 244, + "startTime": 235, "name": "lh:audit:offscreen-images", "duration": 1, "entryType": "measure" }, { - "startTime": 245, + "startTime": 236, "name": "lh:audit:render-blocking-resources", "duration": 1, "entryType": "measure" }, { - "startTime": 246, + "startTime": 237, "name": "lh:computed:UnusedCSS", "duration": 1, "entryType": "measure" }, { - "startTime": 247, + "startTime": 238, "name": "lh:computed:FirstContentfulPaint", "duration": 1, "entryType": "measure" }, { - "startTime": 248, + "startTime": 239, "name": "lh:audit:unminified-css", "duration": 1, "entryType": "measure" }, { - "startTime": 249, + "startTime": 240, "name": "lh:audit:unminified-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 250, + "startTime": 241, "name": "lh:audit:unused-css-rules", "duration": 1, "entryType": "measure" }, { - "startTime": 251, + "startTime": 242, "name": "lh:audit:unused-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 252, + "startTime": 243, "name": "lh:audit:modern-image-formats", "duration": 1, "entryType": "measure" }, { - "startTime": 253, + "startTime": 244, "name": "lh:audit:uses-optimized-images", "duration": 1, "entryType": "measure" }, { - "startTime": 254, + "startTime": 245, "name": "lh:audit:uses-text-compression", "duration": 1, "entryType": "measure" }, { - "startTime": 255, + "startTime": 246, "name": "lh:audit:uses-responsive-images", "duration": 1, "entryType": "measure" }, { - "startTime": 256, + "startTime": 247, "name": "lh:computed:ImageRecords", "duration": 1, "entryType": "measure" }, { - "startTime": 257, + "startTime": 248, "name": "lh:audit:efficient-animated-content", "duration": 1, "entryType": "measure" }, { - "startTime": 258, + "startTime": 249, "name": "lh:audit:duplicated-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 259, + "startTime": 250, "name": "lh:audit:legacy-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 260, + "startTime": 251, "name": "lh:audit:doctype", "duration": 1, "entryType": "measure" }, { - "startTime": 261, + "startTime": 252, "name": "lh:audit:charset", "duration": 1, "entryType": "measure" }, { - "startTime": 262, + "startTime": 253, "name": "lh:audit:dom-size", "duration": 1, "entryType": "measure" }, { - "startTime": 263, + "startTime": 254, "name": "lh:audit:geolocation-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 264, + "startTime": 255, "name": "lh:audit:inspector-issues", "duration": 1, "entryType": "measure" }, { - "startTime": 265, + "startTime": 256, "name": "lh:audit:no-document-write", "duration": 1, "entryType": "measure" }, { - "startTime": 266, + "startTime": 257, "name": "lh:audit:js-libraries", "duration": 1, "entryType": "measure" }, { - "startTime": 267, + "startTime": 258, "name": "lh:audit:notification-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 268, + "startTime": 259, "name": "lh:audit:paste-preventing-inputs", "duration": 1, "entryType": "measure" }, { - "startTime": 269, + "startTime": 260, "name": "lh:audit:uses-passive-event-listeners", "duration": 1, "entryType": "measure" }, { - "startTime": 270, + "startTime": 261, "name": "lh:audit:meta-description", "duration": 1, "entryType": "measure" }, { - "startTime": 271, + "startTime": 262, "name": "lh:audit:http-status-code", "duration": 1, "entryType": "measure" }, { - "startTime": 272, + "startTime": 263, "name": "lh:audit:font-size", "duration": 1, "entryType": "measure" }, { - "startTime": 273, + "startTime": 264, "name": "lh:audit:link-text", "duration": 1, "entryType": "measure" }, { - "startTime": 274, + "startTime": 265, "name": "lh:audit:crawlable-anchors", "duration": 1, "entryType": "measure" }, { - "startTime": 275, + "startTime": 266, "name": "lh:audit:is-crawlable", "duration": 1, "entryType": "measure" }, { - "startTime": 276, + "startTime": 267, "name": "lh:audit:robots-txt", "duration": 1, "entryType": "measure" }, { - "startTime": 277, + "startTime": 268, "name": "lh:audit:hreflang", "duration": 1, "entryType": "measure" }, { - "startTime": 278, + "startTime": 269, "name": "lh:audit:canonical", "duration": 1, "entryType": "measure" }, { - "startTime": 279, + "startTime": 270, "name": "lh:audit:structured-data", "duration": 1, "entryType": "measure" }, { - "startTime": 280, + "startTime": 271, "name": "lh:audit:bf-cache", "duration": 1, "entryType": "measure" }, { - "startTime": 281, + "startTime": 272, "name": "lh:runner:generate", "duration": 1, "entryType": "measure" } ], - "total": 282 + "total": 273 }, "i18n": { "rendererFormattedStrings": { @@ -6952,7 +6730,6 @@ "opportunityResourceColumnLabel": "Opportunity", "opportunitySavingsColumnLabel": "Estimated Savings", "passedAuditsGroupTitle": "Passed audits", - "pwaRemovalMessage": "As per [Chrome’s updated Installability Criteria](https://developer.chrome.com/blog/update-install-criteria), Lighthouse will be deprecating the PWA category in a future release. Please refer to the [updated PWA documentation](https://developer.chrome.com/docs/devtools/progressive-web-apps/) for future PWA testing.", "runtimeAnalysisWindow": "Initial page load", "runtimeAnalysisWindowSnapshot": "Point-in-time snapshot", "runtimeAnalysisWindowTimespan": "User interactions timespan", @@ -7205,50 +6982,6 @@ "core/audits/redirects.js | description": [ "audits.redirects.description" ], - "core/audits/installable-manifest.js | failureTitle": [ - "audits[installable-manifest].title" - ], - "core/audits/installable-manifest.js | description": [ - "audits[installable-manifest].description" - ], - "core/audits/installable-manifest.js | displayValue": [ - { - "values": { - "itemCount": 1 - }, - "path": "audits[installable-manifest].displayValue" - } - ], - "core/audits/installable-manifest.js | columnValue": [ - "audits[installable-manifest].details.headings[0].label" - ], - "core/audits/installable-manifest.js | no-manifest": [ - "audits[installable-manifest].details.items[0].reason" - ], - "core/audits/splash-screen.js | failureTitle": [ - "audits[splash-screen].title" - ], - "core/audits/splash-screen.js | description": [ - "audits[splash-screen].description" - ], - "core/audits/themed-omnibox.js | failureTitle": [ - "audits[themed-omnibox].title" - ], - "core/audits/themed-omnibox.js | description": [ - "audits[themed-omnibox].description" - ], - "core/audits/maskable-icon.js | failureTitle": [ - "audits[maskable-icon].title" - ], - "core/audits/maskable-icon.js | description": [ - "audits[maskable-icon].description" - ], - "core/audits/content-width.js | title": [ - "audits[content-width].title" - ], - "core/audits/content-width.js | description": [ - "audits[content-width].description" - ], "core/audits/image-aspect-ratio.js | title": [ "audits[image-aspect-ratio].title" ], @@ -7512,24 +7245,6 @@ "core/audits/csp-xss.js | noCsp": [ "audits[csp-xss].details.items[0].description" ], - "core/audits/manual/pwa-cross-browser.js | title": [ - "audits[pwa-cross-browser].title" - ], - "core/audits/manual/pwa-cross-browser.js | description": [ - "audits[pwa-cross-browser].description" - ], - "core/audits/manual/pwa-page-transitions.js | title": [ - "audits[pwa-page-transitions].title" - ], - "core/audits/manual/pwa-page-transitions.js | description": [ - "audits[pwa-page-transitions].description" - ], - "core/audits/manual/pwa-each-page-has-url.js | title": [ - "audits[pwa-each-page-has-url].title" - ], - "core/audits/manual/pwa-each-page-has-url.js | description": [ - "audits[pwa-each-page-has-url].description" - ], "core/audits/accessibility/accesskeys.js | title": [ "audits.accesskeys.title" ], @@ -8198,15 +7913,6 @@ "core/config/default-config.js | seoCategoryManualDescription": [ "categories.seo.manualDescription" ], - "core/config/default-config.js | pwaCategoryTitle": [ - "categories.pwa.title" - ], - "core/config/default-config.js | pwaCategoryDescription": [ - "categories.pwa.description" - ], - "core/config/default-config.js | pwaCategoryManualDescription": [ - "categories.pwa.manualDescription" - ], "core/config/default-config.js | metricGroupTitle": [ "categoryGroups.metrics.title" ], @@ -8222,12 +7928,6 @@ "core/config/default-config.js | diagnosticsGroupDescription": [ "categoryGroups.diagnostics.description" ], - "core/config/default-config.js | pwaInstallableGroupTitle": [ - "categoryGroups[pwa-installable].title" - ], - "core/config/default-config.js | pwaOptimizedGroupTitle": [ - "categoryGroups[pwa-optimized].title" - ], "core/config/default-config.js | a11yBestPracticesGroupTitle": [ "categoryGroups[a11y-best-practices].title" ], @@ -10997,12 +10697,6 @@ "title": "Diagnostics", "description": "More information about the performance of your application. These numbers don't [directly affect](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) the Performance score." }, - "pwa-installable": { - "title": "Installable" - }, - "pwa-optimized": { - "title": "PWA Optimized" - }, "a11y-best-practices": { "title": "Best practices", "description": "These items highlight common accessibility best practices." @@ -11888,7 +11582,6 @@ "opportunityResourceColumnLabel": "Opportunity", "opportunitySavingsColumnLabel": "Estimated Savings", "passedAuditsGroupTitle": "Passed audits", - "pwaRemovalMessage": "As per [Chrome’s updated Installability Criteria](https://developer.chrome.com/blog/update-install-criteria), Lighthouse will be deprecating the PWA category in a future release. Please refer to the [updated PWA documentation](https://developer.chrome.com/docs/devtools/progressive-web-apps/) for future PWA testing.", "runtimeAnalysisWindow": "Initial page load", "runtimeAnalysisWindowSnapshot": "Point-in-time snapshot", "runtimeAnalysisWindowTimespan": "User interactions timespan", @@ -12384,12 +12077,6 @@ "core/config/default-config.js | diagnosticsGroupDescription": [ "categoryGroups.diagnostics.description" ], - "core/config/default-config.js | pwaInstallableGroupTitle": [ - "categoryGroups[pwa-installable].title" - ], - "core/config/default-config.js | pwaOptimizedGroupTitle": [ - "categoryGroups[pwa-optimized].title" - ], "core/config/default-config.js | a11yBestPracticesGroupTitle": [ "categoryGroups[a11y-best-practices].title" ], @@ -15316,12 +15003,6 @@ "title": "Diagnostics", "description": "More information about the performance of your application. These numbers don't [directly affect](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) the Performance score." }, - "pwa-installable": { - "title": "Installable" - }, - "pwa-optimized": { - "title": "PWA Optimized" - }, "a11y-best-practices": { "title": "Best practices", "description": "These items highlight common accessibility best practices." @@ -16794,7 +16475,6 @@ "opportunityResourceColumnLabel": "Opportunity", "opportunitySavingsColumnLabel": "Estimated Savings", "passedAuditsGroupTitle": "Passed audits", - "pwaRemovalMessage": "As per [Chrome’s updated Installability Criteria](https://developer.chrome.com/blog/update-install-criteria), Lighthouse will be deprecating the PWA category in a future release. Please refer to the [updated PWA documentation](https://developer.chrome.com/docs/devtools/progressive-web-apps/) for future PWA testing.", "runtimeAnalysisWindow": "Initial page load", "runtimeAnalysisWindowSnapshot": "Point-in-time snapshot", "runtimeAnalysisWindowTimespan": "User interactions timespan", @@ -17396,12 +17076,6 @@ "core/config/default-config.js | diagnosticsGroupDescription": [ "categoryGroups.diagnostics.description" ], - "core/config/default-config.js | pwaInstallableGroupTitle": [ - "categoryGroups[pwa-installable].title" - ], - "core/config/default-config.js | pwaOptimizedGroupTitle": [ - "categoryGroups[pwa-optimized].title" - ], "core/config/default-config.js | a11yBestPracticesGroupTitle": [ "categoryGroups[a11y-best-practices].title" ], @@ -17934,115 +17608,28 @@ }, "guidanceLevel": 2 }, - "installable-manifest": { - "id": "installable-manifest", - "title": "Web app manifest or service worker do not meet the installability requirements", - "description": "Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper service worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more about manifest installability requirements](https://developer.chrome.com/docs/lighthouse/pwa/installable-manifest/).", - "score": 0, + "image-aspect-ratio": { + "id": "image-aspect-ratio", + "title": "Displays images with correct aspect ratio", + "description": "Image display dimensions should match natural aspect ratio. [Learn more about image aspect ratio](https://developer.chrome.com/docs/lighthouse/best-practices/image-aspect-ratio/).", + "score": 1, "scoreDisplayMode": "binary", - "numericValue": 1, - "numericUnit": "element", - "displayValue": "1 reason", - "warnings": [], "details": { "type": "table", - "headings": [ - { - "key": "reason", - "valueType": "text", - "label": "Failure reason" - } - ], - "items": [ - { - "reason": "Page has no manifest URL" - } - ], - "debugData": { - "type": "debugdata", - "manifestUrl": null - } + "headings": [], + "items": [] } }, - "splash-screen": { - "id": "splash-screen", - "title": "Is not configured for a custom splash screen", - "description": "A themed splash screen ensures a high-quality experience when users launch your app from their homescreens. [Learn more about splash screens](https://developer.chrome.com/docs/lighthouse/pwa/splash-screen/).", - "score": 0, + "image-size-responsive": { + "id": "image-size-responsive", + "title": "Serves images with appropriate resolution", + "description": "Image natural dimensions should be proportional to the display size and the pixel ratio to maximize image clarity. [Learn how to provide responsive images](https://web.dev/articles/serve-responsive-images).", + "score": 1, "scoreDisplayMode": "binary", - "explanation": "Failures: No manifest was fetched.", "details": { - "type": "debugdata", - "items": [ - { - "failures": [ - "No manifest was fetched" - ], - "isParseFailure": true, - "parseFailureReason": "No manifest was fetched" - } - ] - } - }, - "themed-omnibox": { - "id": "themed-omnibox", - "title": "Does not set a theme color for the address bar.", - "description": "The browser address bar can be themed to match your site. [Learn more about theming the address bar](https://developer.chrome.com/docs/lighthouse/pwa/themed-omnibox/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "Failures: No manifest was fetched,\nNo `` tag found.", - "details": { - "type": "debugdata", - "items": [ - { - "failures": [ - "No manifest was fetched", - "No `` tag found" - ], - "themeColor": null, - "isParseFailure": true, - "parseFailureReason": "No manifest was fetched" - } - ] - } - }, - "maskable-icon": { - "id": "maskable-icon", - "title": "Manifest doesn't have a maskable icon", - "description": "A maskable icon ensures that the image fills the entire shape without being letterboxed when installing the app on a device. [Learn about maskable manifest icons](https://developer.chrome.com/docs/lighthouse/pwa/maskable-icon-audit/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "No manifest was fetched" - }, - "content-width": { - "id": "content-width", - "title": "Content is sized correctly for the viewport", - "description": "If the width of your app's content doesn't match the width of the viewport, your app might not be optimized for mobile screens. [Learn how to size content for the viewport](https://developer.chrome.com/docs/lighthouse/pwa/content-width/).", - "score": 1, - "scoreDisplayMode": "binary" - }, - "image-aspect-ratio": { - "id": "image-aspect-ratio", - "title": "Displays images with correct aspect ratio", - "description": "Image display dimensions should match natural aspect ratio. [Learn more about image aspect ratio](https://developer.chrome.com/docs/lighthouse/best-practices/image-aspect-ratio/).", - "score": 1, - "scoreDisplayMode": "binary", - "details": { - "type": "table", - "headings": [], - "items": [] - } - }, - "image-size-responsive": { - "id": "image-size-responsive", - "title": "Serves images with appropriate resolution", - "description": "Image natural dimensions should be proportional to the display size and the pixel ratio to maximize image clarity. [Learn how to provide responsive images](https://web.dev/articles/serve-responsive-images).", - "score": 1, - "scoreDisplayMode": "binary", - "details": { - "type": "table", - "headings": [], - "items": [] + "type": "table", + "headings": [], + "items": [] } }, "deprecations": { @@ -19580,27 +19167,6 @@ ] } }, - "pwa-cross-browser": { - "id": "pwa-cross-browser", - "title": "Site works cross-browser", - "description": "To reach the most number of users, sites should work across every major browser. [Learn about cross-browser compatibility](https://developer.chrome.com/docs/lighthouse/pwa/pwa-cross-browser/).", - "score": null, - "scoreDisplayMode": "manual" - }, - "pwa-page-transitions": { - "id": "pwa-page-transitions", - "title": "Page transitions don't feel like they block on the network", - "description": "Transitions should feel snappy as you tap around, even on a slow network. This experience is key to a user's perception of performance. [Learn more about page transitions](https://developer.chrome.com/docs/lighthouse/pwa/pwa-page-transitions/).", - "score": null, - "scoreDisplayMode": "manual" - }, - "pwa-each-page-has-url": { - "id": "pwa-each-page-has-url", - "title": "Each page has a URL", - "description": "Ensure individual pages are deep linkable via URL and that URLs are unique for the purpose of shareability on social media. [Learn more about providing deep links](https://developer.chrome.com/docs/lighthouse/pwa/pwa-each-page-has-url/).", - "score": null, - "scoreDisplayMode": "manual" - }, "accesskeys": { "id": "accesskeys", "title": "`[accesskey]` values are unique", @@ -22167,60 +21733,6 @@ ], "id": "seo", "score": 0.91 - }, - "pwa": { - "title": "PWA", - "description": "These checks validate the aspects of a Progressive Web App. [Learn what makes a good Progressive Web App](https://web.dev/articles/pwa-checklist).", - "manualDescription": "These checks are required by the baseline [PWA Checklist](https://web.dev/articles/pwa-checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually.", - "supportedModes": [ - "navigation" - ], - "auditRefs": [ - { - "id": "installable-manifest", - "weight": 2, - "group": "pwa-installable" - }, - { - "id": "splash-screen", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "themed-omnibox", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "content-width", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "viewport", - "weight": 2, - "group": "pwa-optimized" - }, - { - "id": "maskable-icon", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "pwa-cross-browser", - "weight": 0 - }, - { - "id": "pwa-page-transitions", - "weight": 0 - }, - { - "id": "pwa-each-page-has-url", - "weight": 0 - } - ], - "id": "pwa", - "score": 0.38 } }, "categoryGroups": { @@ -22235,12 +21747,6 @@ "title": "Diagnostics", "description": "More information about the performance of your application. These numbers don't [directly affect](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) the Performance score." }, - "pwa-installable": { - "title": "Installable" - }, - "pwa-optimized": { - "title": "PWA Optimized" - }, "a11y-best-practices": { "title": "Best practices", "description": "These items highlight common accessibility best practices." @@ -23437,283 +22943,283 @@ }, { "startTime": 106, - "name": "lh:audit:installable-manifest", + "name": "lh:audit:image-aspect-ratio", "duration": 1, "entryType": "measure" }, { "startTime": 107, - "name": "lh:audit:splash-screen", + "name": "lh:audit:image-size-responsive", "duration": 1, "entryType": "measure" }, { "startTime": 108, - "name": "lh:computed:ManifestValues", + "name": "lh:audit:deprecations", "duration": 1, "entryType": "measure" }, { "startTime": 109, - "name": "lh:audit:themed-omnibox", + "name": "lh:audit:third-party-cookies", "duration": 1, "entryType": "measure" }, { "startTime": 110, - "name": "lh:audit:maskable-icon", + "name": "lh:audit:mainthread-work-breakdown", "duration": 1, "entryType": "measure" }, { "startTime": 111, - "name": "lh:audit:content-width", + "name": "lh:computed:MainThreadTasks", "duration": 1, "entryType": "measure" }, { "startTime": 112, - "name": "lh:audit:image-aspect-ratio", + "name": "lh:audit:bootup-time", "duration": 1, "entryType": "measure" }, { "startTime": 113, - "name": "lh:audit:image-size-responsive", + "name": "lh:computed:TBTImpactTasks", "duration": 1, "entryType": "measure" }, { "startTime": 114, - "name": "lh:audit:deprecations", + "name": "lh:audit:uses-rel-preconnect", "duration": 1, "entryType": "measure" }, { "startTime": 115, - "name": "lh:audit:third-party-cookies", + "name": "lh:audit:font-display", "duration": 1, "entryType": "measure" }, { "startTime": 116, - "name": "lh:audit:mainthread-work-breakdown", + "name": "lh:audit:diagnostics", "duration": 1, "entryType": "measure" }, { "startTime": 117, - "name": "lh:computed:MainThreadTasks", + "name": "lh:audit:network-requests", "duration": 1, "entryType": "measure" }, { "startTime": 118, - "name": "lh:audit:bootup-time", + "name": "lh:computed:EntityClassification", "duration": 1, "entryType": "measure" }, { "startTime": 119, - "name": "lh:computed:TBTImpactTasks", + "name": "lh:audit:network-rtt", "duration": 1, "entryType": "measure" }, { "startTime": 120, - "name": "lh:audit:uses-rel-preconnect", + "name": "lh:audit:network-server-latency", "duration": 1, "entryType": "measure" }, { "startTime": 121, - "name": "lh:audit:font-display", + "name": "lh:audit:main-thread-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 122, - "name": "lh:audit:diagnostics", + "name": "lh:audit:metrics", "duration": 1, "entryType": "measure" }, { "startTime": 123, - "name": "lh:audit:network-requests", + "name": "lh:computed:TimingSummary", "duration": 1, "entryType": "measure" }, { "startTime": 124, - "name": "lh:computed:EntityClassification", + "name": "lh:computed:FirstContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 125, - "name": "lh:audit:network-rtt", + "name": "lh:computed:LargestContentfulPaintAllFrames", "duration": 1, "entryType": "measure" }, { "startTime": 126, - "name": "lh:audit:network-server-latency", + "name": "lh:computed:LCPBreakdown", "duration": 1, "entryType": "measure" }, { "startTime": 127, - "name": "lh:audit:main-thread-tasks", + "name": "lh:computed:TimeToFirstByte", "duration": 1, "entryType": "measure" }, { "startTime": 128, - "name": "lh:audit:metrics", + "name": "lh:computed:LCPImageRecord", "duration": 1, "entryType": "measure" }, { "startTime": 129, - "name": "lh:computed:TimingSummary", + "name": "lh:audit:performance-budget", "duration": 1, "entryType": "measure" }, { "startTime": 130, - "name": "lh:computed:FirstContentfulPaintAllFrames", + "name": "lh:computed:ResourceSummary", "duration": 1, "entryType": "measure" }, { "startTime": 131, - "name": "lh:computed:LargestContentfulPaintAllFrames", + "name": "lh:audit:timing-budget", "duration": 1, "entryType": "measure" }, { "startTime": 132, - "name": "lh:computed:LCPBreakdown", + "name": "lh:audit:resource-summary", "duration": 1, "entryType": "measure" }, { "startTime": 133, - "name": "lh:computed:TimeToFirstByte", + "name": "lh:audit:third-party-summary", "duration": 1, "entryType": "measure" }, { "startTime": 134, - "name": "lh:computed:LCPImageRecord", + "name": "lh:audit:third-party-facades", "duration": 1, "entryType": "measure" }, { "startTime": 135, - "name": "lh:audit:performance-budget", + "name": "lh:audit:largest-contentful-paint-element", "duration": 1, "entryType": "measure" }, { "startTime": 136, - "name": "lh:computed:ResourceSummary", + "name": "lh:audit:lcp-lazy-loaded", "duration": 1, "entryType": "measure" }, { "startTime": 137, - "name": "lh:audit:timing-budget", + "name": "lh:audit:layout-shifts", "duration": 1, "entryType": "measure" }, { "startTime": 138, - "name": "lh:audit:resource-summary", + "name": "lh:computed:TraceEngineResult", "duration": 1, "entryType": "measure" }, { "startTime": 139, - "name": "lh:audit:third-party-summary", + "name": "lh:audit:long-tasks", "duration": 1, "entryType": "measure" }, { "startTime": 140, - "name": "lh:audit:third-party-facades", + "name": "lh:audit:non-composited-animations", "duration": 1, "entryType": "measure" }, { "startTime": 141, - "name": "lh:audit:largest-contentful-paint-element", + "name": "lh:audit:unsized-images", "duration": 1, "entryType": "measure" }, { "startTime": 142, - "name": "lh:audit:lcp-lazy-loaded", + "name": "lh:audit:valid-source-maps", "duration": 1, "entryType": "measure" }, { "startTime": 143, - "name": "lh:audit:layout-shifts", + "name": "lh:audit:prioritize-lcp-image", "duration": 1, "entryType": "measure" }, { "startTime": 144, - "name": "lh:computed:TraceEngineResult", + "name": "lh:audit:csp-xss", "duration": 1, "entryType": "measure" }, { "startTime": 145, - "name": "lh:audit:long-tasks", + "name": "lh:audit:script-treemap-data", "duration": 1, "entryType": "measure" }, { "startTime": 146, - "name": "lh:audit:non-composited-animations", + "name": "lh:computed:ModuleDuplication", "duration": 1, "entryType": "measure" }, { "startTime": 147, - "name": "lh:audit:unsized-images", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 148, - "name": "lh:audit:valid-source-maps", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 149, - "name": "lh:audit:prioritize-lcp-image", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 150, - "name": "lh:audit:csp-xss", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 151, - "name": "lh:audit:script-treemap-data", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, { "startTime": 152, - "name": "lh:computed:ModuleDuplication", + "name": "lh:computed:UnusedJavascriptSummary", "duration": 1, "entryType": "measure" }, @@ -23761,720 +23267,666 @@ }, { "startTime": 160, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 161, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 162, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 163, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 164, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 165, - "name": "lh:computed:UnusedJavascriptSummary", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 166, - "name": "lh:audit:pwa-cross-browser", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 167, - "name": "lh:audit:pwa-page-transitions", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 168, - "name": "lh:audit:pwa-each-page-has-url", - "duration": 1, - "entryType": "measure" - }, - { - "startTime": 169, "name": "lh:audit:accesskeys", "duration": 1, "entryType": "measure" }, { - "startTime": 170, + "startTime": 161, "name": "lh:audit:aria-allowed-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 171, + "startTime": 162, "name": "lh:audit:aria-allowed-role", "duration": 1, "entryType": "measure" }, { - "startTime": 172, + "startTime": 163, "name": "lh:audit:aria-command-name", "duration": 1, "entryType": "measure" }, { - "startTime": 173, + "startTime": 164, "name": "lh:audit:aria-dialog-name", "duration": 1, "entryType": "measure" }, { - "startTime": 174, + "startTime": 165, "name": "lh:audit:aria-hidden-body", "duration": 1, "entryType": "measure" }, { - "startTime": 175, + "startTime": 166, "name": "lh:audit:aria-hidden-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 176, + "startTime": 167, "name": "lh:audit:aria-input-field-name", "duration": 1, "entryType": "measure" }, { - "startTime": 177, + "startTime": 168, "name": "lh:audit:aria-meter-name", "duration": 1, "entryType": "measure" }, { - "startTime": 178, + "startTime": 169, "name": "lh:audit:aria-progressbar-name", "duration": 1, "entryType": "measure" }, { - "startTime": 179, + "startTime": 170, "name": "lh:audit:aria-required-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 180, + "startTime": 171, "name": "lh:audit:aria-required-children", "duration": 1, "entryType": "measure" }, { - "startTime": 181, + "startTime": 172, "name": "lh:audit:aria-required-parent", "duration": 1, "entryType": "measure" }, { - "startTime": 182, + "startTime": 173, "name": "lh:audit:aria-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 183, + "startTime": 174, "name": "lh:audit:aria-text", "duration": 1, "entryType": "measure" }, { - "startTime": 184, + "startTime": 175, "name": "lh:audit:aria-toggle-field-name", "duration": 1, "entryType": "measure" }, { - "startTime": 185, + "startTime": 176, "name": "lh:audit:aria-tooltip-name", "duration": 1, "entryType": "measure" }, { - "startTime": 186, + "startTime": 177, "name": "lh:audit:aria-treeitem-name", "duration": 1, "entryType": "measure" }, { - "startTime": 187, + "startTime": 178, "name": "lh:audit:aria-valid-attr-value", "duration": 1, "entryType": "measure" }, { - "startTime": 188, + "startTime": 179, "name": "lh:audit:aria-valid-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 189, + "startTime": 180, "name": "lh:audit:button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 190, + "startTime": 181, "name": "lh:audit:bypass", "duration": 1, "entryType": "measure" }, { - "startTime": 191, + "startTime": 182, "name": "lh:audit:color-contrast", "duration": 1, "entryType": "measure" }, { - "startTime": 192, + "startTime": 183, "name": "lh:audit:definition-list", "duration": 1, "entryType": "measure" }, { - "startTime": 193, + "startTime": 184, "name": "lh:audit:dlitem", "duration": 1, "entryType": "measure" }, { - "startTime": 194, + "startTime": 185, "name": "lh:audit:document-title", "duration": 1, "entryType": "measure" }, { - "startTime": 195, + "startTime": 186, "name": "lh:audit:duplicate-id-aria", "duration": 1, "entryType": "measure" }, { - "startTime": 196, + "startTime": 187, "name": "lh:audit:empty-heading", "duration": 1, "entryType": "measure" }, { - "startTime": 197, + "startTime": 188, "name": "lh:audit:form-field-multiple-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 198, + "startTime": 189, "name": "lh:audit:frame-title", "duration": 1, "entryType": "measure" }, { - "startTime": 199, + "startTime": 190, "name": "lh:audit:heading-order", "duration": 1, "entryType": "measure" }, { - "startTime": 200, + "startTime": 191, "name": "lh:audit:html-has-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 201, + "startTime": 192, "name": "lh:audit:html-lang-valid", "duration": 1, "entryType": "measure" }, { - "startTime": 202, + "startTime": 193, "name": "lh:audit:html-xml-lang-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 203, + "startTime": 194, "name": "lh:audit:identical-links-same-purpose", "duration": 1, "entryType": "measure" }, { - "startTime": 204, + "startTime": 195, "name": "lh:audit:image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 205, + "startTime": 196, "name": "lh:audit:image-redundant-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 206, + "startTime": 197, "name": "lh:audit:input-button-name", "duration": 1, "entryType": "measure" }, { - "startTime": 207, + "startTime": 198, "name": "lh:audit:input-image-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 208, + "startTime": 199, "name": "lh:audit:label-content-name-mismatch", "duration": 1, "entryType": "measure" }, { - "startTime": 209, + "startTime": 200, "name": "lh:audit:label", "duration": 1, "entryType": "measure" }, { - "startTime": 210, + "startTime": 201, "name": "lh:audit:landmark-one-main", "duration": 1, "entryType": "measure" }, { - "startTime": 211, + "startTime": 202, "name": "lh:audit:link-name", "duration": 1, "entryType": "measure" }, { - "startTime": 212, + "startTime": 203, "name": "lh:audit:link-in-text-block", "duration": 1, "entryType": "measure" }, { - "startTime": 213, + "startTime": 204, "name": "lh:audit:list", "duration": 1, "entryType": "measure" }, { - "startTime": 214, + "startTime": 205, "name": "lh:audit:listitem", "duration": 1, "entryType": "measure" }, { - "startTime": 215, + "startTime": 206, "name": "lh:audit:meta-refresh", "duration": 1, "entryType": "measure" }, { - "startTime": 216, + "startTime": 207, "name": "lh:audit:meta-viewport", "duration": 1, "entryType": "measure" }, { - "startTime": 217, + "startTime": 208, "name": "lh:audit:object-alt", "duration": 1, "entryType": "measure" }, { - "startTime": 218, + "startTime": 209, "name": "lh:audit:select-name", "duration": 1, "entryType": "measure" }, { - "startTime": 219, + "startTime": 210, "name": "lh:audit:skip-link", "duration": 1, "entryType": "measure" }, { - "startTime": 220, + "startTime": 211, "name": "lh:audit:tabindex", "duration": 1, "entryType": "measure" }, { - "startTime": 221, + "startTime": 212, "name": "lh:audit:table-duplicate-name", "duration": 1, "entryType": "measure" }, { - "startTime": 222, + "startTime": 213, "name": "lh:audit:table-fake-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 223, + "startTime": 214, "name": "lh:audit:target-size", "duration": 1, "entryType": "measure" }, { - "startTime": 224, + "startTime": 215, "name": "lh:audit:td-has-header", "duration": 1, "entryType": "measure" }, { - "startTime": 225, + "startTime": 216, "name": "lh:audit:td-headers-attr", "duration": 1, "entryType": "measure" }, { - "startTime": 226, + "startTime": 217, "name": "lh:audit:th-has-data-cells", "duration": 1, "entryType": "measure" }, { - "startTime": 227, + "startTime": 218, "name": "lh:audit:valid-lang", "duration": 1, "entryType": "measure" }, { - "startTime": 228, + "startTime": 219, "name": "lh:audit:video-caption", "duration": 1, "entryType": "measure" }, { - "startTime": 229, + "startTime": 220, "name": "lh:audit:custom-controls-labels", "duration": 1, "entryType": "measure" }, { - "startTime": 230, + "startTime": 221, "name": "lh:audit:custom-controls-roles", "duration": 1, "entryType": "measure" }, { - "startTime": 231, + "startTime": 222, "name": "lh:audit:focus-traps", "duration": 1, "entryType": "measure" }, { - "startTime": 232, + "startTime": 223, "name": "lh:audit:focusable-controls", "duration": 1, "entryType": "measure" }, { - "startTime": 233, + "startTime": 224, "name": "lh:audit:interactive-element-affordance", "duration": 1, "entryType": "measure" }, { - "startTime": 234, + "startTime": 225, "name": "lh:audit:logical-tab-order", "duration": 1, "entryType": "measure" }, { - "startTime": 235, + "startTime": 226, "name": "lh:audit:managed-focus", "duration": 1, "entryType": "measure" }, { - "startTime": 236, + "startTime": 227, "name": "lh:audit:offscreen-content-hidden", "duration": 1, "entryType": "measure" }, { - "startTime": 237, + "startTime": 228, "name": "lh:audit:use-landmarks", "duration": 1, "entryType": "measure" }, { - "startTime": 238, + "startTime": 229, "name": "lh:audit:visual-order-follows-dom", "duration": 1, "entryType": "measure" }, { - "startTime": 239, + "startTime": 230, "name": "lh:audit:uses-long-cache-ttl", "duration": 1, "entryType": "measure" }, { - "startTime": 240, + "startTime": 231, "name": "lh:audit:total-byte-weight", "duration": 1, "entryType": "measure" }, { - "startTime": 241, + "startTime": 232, "name": "lh:audit:offscreen-images", "duration": 1, "entryType": "measure" }, { - "startTime": 242, + "startTime": 233, "name": "lh:audit:render-blocking-resources", "duration": 1, "entryType": "measure" }, { - "startTime": 243, + "startTime": 234, "name": "lh:computed:UnusedCSS", "duration": 1, "entryType": "measure" }, { - "startTime": 244, + "startTime": 235, "name": "lh:computed:FirstContentfulPaint", "duration": 1, "entryType": "measure" }, { - "startTime": 245, + "startTime": 236, "name": "lh:audit:unminified-css", "duration": 1, "entryType": "measure" }, { - "startTime": 246, + "startTime": 237, "name": "lh:audit:unminified-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 247, + "startTime": 238, "name": "lh:audit:unused-css-rules", "duration": 1, "entryType": "measure" }, { - "startTime": 248, + "startTime": 239, "name": "lh:audit:unused-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 249, + "startTime": 240, "name": "lh:audit:modern-image-formats", "duration": 1, "entryType": "measure" }, { - "startTime": 250, + "startTime": 241, "name": "lh:audit:uses-optimized-images", "duration": 1, "entryType": "measure" }, { - "startTime": 251, + "startTime": 242, "name": "lh:audit:uses-text-compression", "duration": 1, "entryType": "measure" }, { - "startTime": 252, + "startTime": 243, "name": "lh:audit:uses-responsive-images", "duration": 1, "entryType": "measure" }, { - "startTime": 253, + "startTime": 244, "name": "lh:computed:ImageRecords", "duration": 1, "entryType": "measure" }, { - "startTime": 254, + "startTime": 245, "name": "lh:audit:efficient-animated-content", "duration": 1, "entryType": "measure" }, { - "startTime": 255, + "startTime": 246, "name": "lh:audit:duplicated-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 256, + "startTime": 247, "name": "lh:audit:legacy-javascript", "duration": 1, "entryType": "measure" }, { - "startTime": 257, + "startTime": 248, "name": "lh:audit:doctype", "duration": 1, "entryType": "measure" }, { - "startTime": 258, + "startTime": 249, "name": "lh:audit:charset", "duration": 1, "entryType": "measure" }, { - "startTime": 259, + "startTime": 250, "name": "lh:audit:dom-size", "duration": 1, "entryType": "measure" }, { - "startTime": 260, + "startTime": 251, "name": "lh:audit:geolocation-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 261, + "startTime": 252, "name": "lh:audit:inspector-issues", "duration": 1, "entryType": "measure" }, { - "startTime": 262, + "startTime": 253, "name": "lh:audit:no-document-write", "duration": 1, "entryType": "measure" }, { - "startTime": 263, + "startTime": 254, "name": "lh:audit:js-libraries", "duration": 1, "entryType": "measure" }, { - "startTime": 264, + "startTime": 255, "name": "lh:audit:notification-on-start", "duration": 1, "entryType": "measure" }, { - "startTime": 265, + "startTime": 256, "name": "lh:audit:paste-preventing-inputs", "duration": 1, "entryType": "measure" }, { - "startTime": 266, + "startTime": 257, "name": "lh:audit:uses-passive-event-listeners", "duration": 1, "entryType": "measure" }, { - "startTime": 267, + "startTime": 258, "name": "lh:audit:meta-description", "duration": 1, "entryType": "measure" }, { - "startTime": 268, + "startTime": 259, "name": "lh:audit:http-status-code", "duration": 1, "entryType": "measure" }, { - "startTime": 269, + "startTime": 260, "name": "lh:audit:font-size", "duration": 1, "entryType": "measure" }, { - "startTime": 270, + "startTime": 261, "name": "lh:audit:link-text", "duration": 1, "entryType": "measure" }, { - "startTime": 271, + "startTime": 262, "name": "lh:audit:crawlable-anchors", "duration": 1, "entryType": "measure" }, { - "startTime": 272, + "startTime": 263, "name": "lh:audit:is-crawlable", "duration": 1, "entryType": "measure" }, { - "startTime": 273, + "startTime": 264, "name": "lh:audit:robots-txt", "duration": 1, "entryType": "measure" }, { - "startTime": 274, + "startTime": 265, "name": "lh:audit:hreflang", "duration": 1, "entryType": "measure" }, { - "startTime": 275, + "startTime": 266, "name": "lh:audit:canonical", "duration": 1, "entryType": "measure" }, { - "startTime": 276, + "startTime": 267, "name": "lh:audit:structured-data", "duration": 1, "entryType": "measure" }, { - "startTime": 277, + "startTime": 268, "name": "lh:audit:bf-cache", "duration": 1, "entryType": "measure" }, { - "startTime": 278, + "startTime": 269, "name": "lh:runner:generate", "duration": 1, "entryType": "measure" } ], - "total": 279 + "total": 270 }, "i18n": { "rendererFormattedStrings": { @@ -24505,7 +23957,6 @@ "opportunityResourceColumnLabel": "Opportunity", "opportunitySavingsColumnLabel": "Estimated Savings", "passedAuditsGroupTitle": "Passed audits", - "pwaRemovalMessage": "As per [Chrome’s updated Installability Criteria](https://developer.chrome.com/blog/update-install-criteria), Lighthouse will be deprecating the PWA category in a future release. Please refer to the [updated PWA documentation](https://developer.chrome.com/docs/devtools/progressive-web-apps/) for future PWA testing.", "runtimeAnalysisWindow": "Initial page load", "runtimeAnalysisWindowSnapshot": "Point-in-time snapshot", "runtimeAnalysisWindowTimespan": "User interactions timespan", @@ -24760,50 +24211,6 @@ "core/audits/redirects.js | description": [ "audits.redirects.description" ], - "core/audits/installable-manifest.js | failureTitle": [ - "audits[installable-manifest].title" - ], - "core/audits/installable-manifest.js | description": [ - "audits[installable-manifest].description" - ], - "core/audits/installable-manifest.js | displayValue": [ - { - "values": { - "itemCount": 1 - }, - "path": "audits[installable-manifest].displayValue" - } - ], - "core/audits/installable-manifest.js | columnValue": [ - "audits[installable-manifest].details.headings[0].label" - ], - "core/audits/installable-manifest.js | no-manifest": [ - "audits[installable-manifest].details.items[0].reason" - ], - "core/audits/splash-screen.js | failureTitle": [ - "audits[splash-screen].title" - ], - "core/audits/splash-screen.js | description": [ - "audits[splash-screen].description" - ], - "core/audits/themed-omnibox.js | failureTitle": [ - "audits[themed-omnibox].title" - ], - "core/audits/themed-omnibox.js | description": [ - "audits[themed-omnibox].description" - ], - "core/audits/maskable-icon.js | failureTitle": [ - "audits[maskable-icon].title" - ], - "core/audits/maskable-icon.js | description": [ - "audits[maskable-icon].description" - ], - "core/audits/content-width.js | title": [ - "audits[content-width].title" - ], - "core/audits/content-width.js | description": [ - "audits[content-width].description" - ], "core/audits/image-aspect-ratio.js | title": [ "audits[image-aspect-ratio].title" ], @@ -25055,24 +24462,6 @@ "core/audits/csp-xss.js | noCsp": [ "audits[csp-xss].details.items[0].description" ], - "core/audits/manual/pwa-cross-browser.js | title": [ - "audits[pwa-cross-browser].title" - ], - "core/audits/manual/pwa-cross-browser.js | description": [ - "audits[pwa-cross-browser].description" - ], - "core/audits/manual/pwa-page-transitions.js | title": [ - "audits[pwa-page-transitions].title" - ], - "core/audits/manual/pwa-page-transitions.js | description": [ - "audits[pwa-page-transitions].description" - ], - "core/audits/manual/pwa-each-page-has-url.js | title": [ - "audits[pwa-each-page-has-url].title" - ], - "core/audits/manual/pwa-each-page-has-url.js | description": [ - "audits[pwa-each-page-has-url].description" - ], "core/audits/accessibility/accesskeys.js | title": [ "audits.accesskeys.title" ], @@ -25764,15 +25153,6 @@ "core/config/default-config.js | seoCategoryManualDescription": [ "categories.seo.manualDescription" ], - "core/config/default-config.js | pwaCategoryTitle": [ - "categories.pwa.title" - ], - "core/config/default-config.js | pwaCategoryDescription": [ - "categories.pwa.description" - ], - "core/config/default-config.js | pwaCategoryManualDescription": [ - "categories.pwa.manualDescription" - ], "core/config/default-config.js | metricGroupTitle": [ "categoryGroups.metrics.title" ], @@ -25788,12 +25168,6 @@ "core/config/default-config.js | diagnosticsGroupDescription": [ "categoryGroups.diagnostics.description" ], - "core/config/default-config.js | pwaInstallableGroupTitle": [ - "categoryGroups[pwa-installable].title" - ], - "core/config/default-config.js | pwaOptimizedGroupTitle": [ - "categoryGroups[pwa-optimized].title" - ], "core/config/default-config.js | a11yBestPracticesGroupTitle": [ "categoryGroups[a11y-best-practices].title" ], diff --git a/core/test/gather/gatherers/installability-errors-test.js b/core/test/gather/gatherers/installability-errors-test.js deleted file mode 100644 index 4b9ef5044401..000000000000 --- a/core/test/gather/gatherers/installability-errors-test.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import InstallabilityErrors from '../../../gather/gatherers/installability-errors.js'; -import {createMockSession} from '../mock-driver.js'; - -describe('.getInstallabilityErrors', () => { - let session = createMockSession(); - - beforeEach(() => { - session = createMockSession(); - }); - - it('should return the response from the protocol', async () => { - session.sendCommand - .mockResponse('Page.getInstallabilityErrors', { - installabilityErrors: [{errorId: 'no-icon-available', errorArguments: []}], - }); - - const result = await InstallabilityErrors.getInstallabilityErrors(session.asSession()); - expect(result).toEqual({ - errors: [{errorId: 'no-icon-available', errorArguments: []}], - }); - }); -}); diff --git a/core/test/gather/gatherers/web-app-manifest-test.js b/core/test/gather/gatherers/web-app-manifest-test.js deleted file mode 100644 index ee61e185f8a9..000000000000 --- a/core/test/gather/gatherers/web-app-manifest-test.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import fs from 'fs'; - -import {LH_ROOT} from '../../../../shared/root.js'; -import WebAppManifest from '../../../gather/gatherers/web-app-manifest.js'; -import {createMockSession} from '../mock-driver.js'; - -describe('WebAppManifest Gatherer', () => { - let session = createMockSession(); - - beforeEach(() => { - session = createMockSession(); - }); - - describe('.getAppManifest', () => { - it('should return null when no manifest', async () => { - session.sendCommand.mockResponse('Page.getAppManifest', {data: undefined, url: '/manifest'}); - const result = await WebAppManifest.fetchAppManifest(session.asSession()); - expect(result).toEqual(null); - }); - - it('should return the manifest', async () => { - const manifest = {name: 'The App'}; - session.sendCommand.mockResponse('Page.getAppManifest', { - data: JSON.stringify(manifest), - url: '/manifest', - }); - const result = await WebAppManifest.fetchAppManifest(session.asSession()); - expect(result).toEqual({data: JSON.stringify(manifest), url: '/manifest'}); - }); - - it('should handle BOM-encoded manifest', async () => { - const manifestWithoutBOM = fs - .readFileSync(LH_ROOT + '/core/test/fixtures/manifest.json') - .toString(); - const manifestWithBOM = fs - .readFileSync(LH_ROOT + '/core/test/fixtures/manifest-bom.json') - .toString(); - - session.sendCommand.mockResponse('Page.getAppManifest', { - data: manifestWithBOM, - url: '/manifest', - }); - const result = await WebAppManifest.fetchAppManifest(session.asSession()); - expect(result).toEqual({data: manifestWithoutBOM, url: '/manifest'}); - }); - }); - - describe('.getWebAppManifest', () => { - const MANIFEST_URL = 'https://example.com/manifest.json'; - const PAGE_URL = 'https://example.com/index.html'; - - it('should return null when there is no manifest', async () => { - session.sendCommand - .mockResponse('Page.getAppManifest', {}) - .mockResponse('Page.getInstallabilityErrors', {installabilityErrors: []}); - const result = await WebAppManifest.getWebAppManifest(session.asSession(), PAGE_URL); - expect(result).toEqual(null); - }); - - it('should parse the manifest when found', async () => { - const manifest = {name: 'App'}; - session.sendCommand - .mockResponse('Page.getAppManifest', {data: JSON.stringify(manifest), url: MANIFEST_URL}) - .mockResponse('Page.getInstallabilityErrors', {installabilityErrors: []}); - - const result = await WebAppManifest.getWebAppManifest(session.asSession(), PAGE_URL); - expect(result).toHaveProperty('raw', JSON.stringify(manifest)); - expect(result?.value).toMatchObject({ - name: {value: 'App', raw: 'App'}, - start_url: {value: PAGE_URL, raw: undefined}, - }); - expect(result?.url).toMatch(MANIFEST_URL); - }); - }); -}); diff --git a/core/test/lib/lighthouse-compatibility-test.js b/core/test/lib/lighthouse-compatibility-test.js index e0fbb2e8b7de..b69f8654d4f6 100644 --- a/core/test/lib/lighthouse-compatibility-test.js +++ b/core/test/lib/lighthouse-compatibility-test.js @@ -63,7 +63,7 @@ describe('backward compatibility', () => { delete audit.details.type; } } - assert.ok(undefinedCount > 4); // Make sure something's being tested. + assert.ok(undefinedCount > 3); // Make sure something's being tested. assert.notDeepStrictEqual(clonedSampleResult.audits, sampleResult.audits); // Original audit results should be restored. @@ -82,7 +82,7 @@ describe('backward compatibility', () => { audit.details.type = 'diagnostic'; } } - assert.ok(diagnosticCount > 4); // Make sure something's being tested. + assert.ok(diagnosticCount > 3); // Make sure something's being tested. assert.notDeepStrictEqual(clonedSampleResult.audits, sampleResult.audits); // Original audit results should be restored. diff --git a/core/test/results/sample_v2.json b/core/test/results/sample_v2.json index a5f960d95a95..822328eef7f4 100644 --- a/core/test/results/sample_v2.json +++ b/core/test/results/sample_v2.json @@ -866,93 +866,6 @@ }, "guidanceLevel": 2 }, - "installable-manifest": { - "id": "installable-manifest", - "title": "Web app manifest or service worker do not meet the installability requirements", - "description": "Service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. With proper service worker and manifest implementations, browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more about manifest installability requirements](https://developer.chrome.com/docs/lighthouse/pwa/installable-manifest/).", - "score": 0, - "scoreDisplayMode": "binary", - "numericValue": 1, - "numericUnit": "element", - "displayValue": "1 reason", - "warnings": [], - "details": { - "type": "table", - "headings": [ - { - "key": "reason", - "valueType": "text", - "label": "Failure reason" - } - ], - "items": [ - { - "reason": "Page has no manifest URL" - } - ], - "debugData": { - "type": "debugdata", - "manifestUrl": null - } - } - }, - "splash-screen": { - "id": "splash-screen", - "title": "Is not configured for a custom splash screen", - "description": "A themed splash screen ensures a high-quality experience when users launch your app from their homescreens. [Learn more about splash screens](https://developer.chrome.com/docs/lighthouse/pwa/splash-screen/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "Failures: No manifest was fetched.", - "details": { - "type": "debugdata", - "items": [ - { - "failures": [ - "No manifest was fetched" - ], - "isParseFailure": true, - "parseFailureReason": "No manifest was fetched" - } - ] - } - }, - "themed-omnibox": { - "id": "themed-omnibox", - "title": "Does not set a theme color for the address bar.", - "description": "The browser address bar can be themed to match your site. [Learn more about theming the address bar](https://developer.chrome.com/docs/lighthouse/pwa/themed-omnibox/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "Failures: No manifest was fetched,\nNo `` tag found.", - "details": { - "type": "debugdata", - "items": [ - { - "failures": [ - "No manifest was fetched", - "No `` tag found" - ], - "themeColor": null, - "isParseFailure": true, - "parseFailureReason": "No manifest was fetched" - } - ] - } - }, - "maskable-icon": { - "id": "maskable-icon", - "title": "Manifest doesn't have a maskable icon", - "description": "A maskable icon ensures that the image fills the entire shape without being letterboxed when installing the app on a device. [Learn about maskable manifest icons](https://developer.chrome.com/docs/lighthouse/pwa/maskable-icon-audit/).", - "score": 0, - "scoreDisplayMode": "binary", - "explanation": "No manifest was fetched" - }, - "content-width": { - "id": "content-width", - "title": "Content is sized correctly for the viewport", - "description": "If the width of your app's content doesn't match the width of the viewport, your app might not be optimized for mobile screens. [Learn how to size content for the viewport](https://developer.chrome.com/docs/lighthouse/pwa/content-width/).", - "score": 1, - "scoreDisplayMode": "binary" - }, "image-aspect-ratio": { "id": "image-aspect-ratio", "title": "Displays images with incorrect aspect ratio", @@ -3037,27 +2950,6 @@ ] } }, - "pwa-cross-browser": { - "id": "pwa-cross-browser", - "title": "Site works cross-browser", - "description": "To reach the most number of users, sites should work across every major browser. [Learn about cross-browser compatibility](https://developer.chrome.com/docs/lighthouse/pwa/pwa-cross-browser/).", - "score": null, - "scoreDisplayMode": "manual" - }, - "pwa-page-transitions": { - "id": "pwa-page-transitions", - "title": "Page transitions don't feel like they block on the network", - "description": "Transitions should feel snappy as you tap around, even on a slow network. This experience is key to a user's perception of performance. [Learn more about page transitions](https://developer.chrome.com/docs/lighthouse/pwa/pwa-page-transitions/).", - "score": null, - "scoreDisplayMode": "manual" - }, - "pwa-each-page-has-url": { - "id": "pwa-each-page-has-url", - "title": "Each page has a URL", - "description": "Ensure individual pages are deep linkable via URL and that URLs are unique for the purpose of shareability on social media. [Learn more about providing deep links](https://developer.chrome.com/docs/lighthouse/pwa/pwa-each-page-has-url/).", - "score": null, - "scoreDisplayMode": "manual" - }, "accesskeys": { "id": "accesskeys", "title": "`[accesskey]` values are unique", @@ -6675,60 +6567,6 @@ ], "id": "seo", "score": 0.73 - }, - "pwa": { - "title": "PWA", - "description": "These checks validate the aspects of a Progressive Web App. [Learn what makes a good Progressive Web App](https://web.dev/articles/pwa-checklist).", - "manualDescription": "These checks are required by the baseline [PWA Checklist](https://web.dev/articles/pwa-checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually.", - "supportedModes": [ - "navigation" - ], - "auditRefs": [ - { - "id": "installable-manifest", - "weight": 2, - "group": "pwa-installable" - }, - { - "id": "splash-screen", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "themed-omnibox", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "content-width", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "viewport", - "weight": 2, - "group": "pwa-optimized" - }, - { - "id": "maskable-icon", - "weight": 1, - "group": "pwa-optimized" - }, - { - "id": "pwa-cross-browser", - "weight": 0 - }, - { - "id": "pwa-page-transitions", - "weight": 0 - }, - { - "id": "pwa-each-page-has-url", - "weight": 0 - } - ], - "id": "pwa", - "score": 0.38 } }, "categoryGroups": { @@ -6743,12 +6581,6 @@ "title": "Diagnostics", "description": "More information about the performance of your application. These numbers don't [directly affect](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) the Performance score." }, - "pwa-installable": { - "title": "Installable" - }, - "pwa-optimized": { - "title": "PWA Optimized" - }, "a11y-best-practices": { "title": "Best practices", "description": "These items highlight common accessibility best practices." @@ -8113,42 +7945,6 @@ "duration": 100, "entryType": "measure" }, - { - "startTime": 0, - "name": "lh:audit:installable-manifest", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:audit:splash-screen", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:computed:ManifestValues", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:audit:themed-omnibox", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:audit:maskable-icon", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:audit:content-width", - "duration": 100, - "entryType": "measure" - }, { "startTime": 0, "name": "lh:audit:image-aspect-ratio", @@ -8497,24 +8293,6 @@ "duration": 100, "entryType": "measure" }, - { - "startTime": 0, - "name": "lh:audit:pwa-cross-browser", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:audit:pwa-page-transitions", - "duration": 100, - "entryType": "measure" - }, - { - "startTime": 0, - "name": "lh:audit:pwa-each-page-has-url", - "duration": 100, - "entryType": "measure" - }, { "startTime": 0, "name": "lh:audit:accesskeys", @@ -9225,7 +9003,6 @@ "opportunityResourceColumnLabel": "Opportunity", "opportunitySavingsColumnLabel": "Estimated Savings", "passedAuditsGroupTitle": "Passed audits", - "pwaRemovalMessage": "As per [Chrome’s updated Installability Criteria](https://developer.chrome.com/blog/update-install-criteria), Lighthouse will be deprecating the PWA category in a future release. Please refer to the [updated PWA documentation](https://developer.chrome.com/docs/devtools/progressive-web-apps/) for future PWA testing.", "runtimeAnalysisWindow": "Initial page load", "runtimeAnalysisWindowSnapshot": "Point-in-time snapshot", "runtimeAnalysisWindowTimespan": "User interactions timespan", @@ -9530,50 +9307,6 @@ "core/audits/redirects.js | description": [ "audits.redirects.description" ], - "core/audits/installable-manifest.js | failureTitle": [ - "audits[installable-manifest].title" - ], - "core/audits/installable-manifest.js | description": [ - "audits[installable-manifest].description" - ], - "core/audits/installable-manifest.js | displayValue": [ - { - "values": { - "itemCount": 1 - }, - "path": "audits[installable-manifest].displayValue" - } - ], - "core/audits/installable-manifest.js | columnValue": [ - "audits[installable-manifest].details.headings[0].label" - ], - "core/audits/installable-manifest.js | no-manifest": [ - "audits[installable-manifest].details.items[0].reason" - ], - "core/audits/splash-screen.js | failureTitle": [ - "audits[splash-screen].title" - ], - "core/audits/splash-screen.js | description": [ - "audits[splash-screen].description" - ], - "core/audits/themed-omnibox.js | failureTitle": [ - "audits[themed-omnibox].title" - ], - "core/audits/themed-omnibox.js | description": [ - "audits[themed-omnibox].description" - ], - "core/audits/maskable-icon.js | failureTitle": [ - "audits[maskable-icon].title" - ], - "core/audits/maskable-icon.js | description": [ - "audits[maskable-icon].description" - ], - "core/audits/content-width.js | title": [ - "audits[content-width].title" - ], - "core/audits/content-width.js | description": [ - "audits[content-width].description" - ], "core/audits/image-aspect-ratio.js | failureTitle": [ "audits[image-aspect-ratio].title" ], @@ -9945,24 +9678,6 @@ "core/audits/csp-xss.js | noCsp": [ "audits[csp-xss].details.items[0].description" ], - "core/audits/manual/pwa-cross-browser.js | title": [ - "audits[pwa-cross-browser].title" - ], - "core/audits/manual/pwa-cross-browser.js | description": [ - "audits[pwa-cross-browser].description" - ], - "core/audits/manual/pwa-page-transitions.js | title": [ - "audits[pwa-page-transitions].title" - ], - "core/audits/manual/pwa-page-transitions.js | description": [ - "audits[pwa-page-transitions].description" - ], - "core/audits/manual/pwa-each-page-has-url.js | title": [ - "audits[pwa-each-page-has-url].title" - ], - "core/audits/manual/pwa-each-page-has-url.js | description": [ - "audits[pwa-each-page-has-url].description" - ], "core/audits/accessibility/accesskeys.js | title": [ "audits.accesskeys.title" ], @@ -10713,15 +10428,6 @@ "core/config/default-config.js | seoCategoryManualDescription": [ "categories.seo.manualDescription" ], - "core/config/default-config.js | pwaCategoryTitle": [ - "categories.pwa.title" - ], - "core/config/default-config.js | pwaCategoryDescription": [ - "categories.pwa.description" - ], - "core/config/default-config.js | pwaCategoryManualDescription": [ - "categories.pwa.manualDescription" - ], "core/config/default-config.js | metricGroupTitle": [ "categoryGroups.metrics.title" ], @@ -10737,12 +10443,6 @@ "core/config/default-config.js | diagnosticsGroupDescription": [ "categoryGroups.diagnostics.description" ], - "core/config/default-config.js | pwaInstallableGroupTitle": [ - "categoryGroups[pwa-installable].title" - ], - "core/config/default-config.js | pwaOptimizedGroupTitle": [ - "categoryGroups[pwa-optimized].title" - ], "core/config/default-config.js | a11yBestPracticesGroupTitle": [ "categoryGroups[a11y-best-practices].title" ], diff --git a/core/test/runner-test.js b/core/test/runner-test.js index e2ac5b8c2285..372388414b0c 100644 --- a/core/test/runner-test.js +++ b/core/test/runner-test.js @@ -130,9 +130,9 @@ describe('Runner', () => { const url = 'https://example.com'; const generateConfig = settings => initializeConfig('navigation', { artifacts: [ - {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'}, + {id: 'MetaElements', gatherer: 'meta-elements'}, ], - audits: ['content-width'], + audits: ['viewport'], settings, }).then(r => r.resolvedConfig); const artifactsPath = '.tmp/test_artifacts'; @@ -149,7 +149,7 @@ describe('Runner', () => { expect(saveArtifactsSpy).toHaveBeenCalled(); const saveArtifactArg = saveArtifactsSpy.mock.calls[0][0]; - assert.ok(saveArtifactArg.ViewportDimensions); + assert.ok(saveArtifactArg.MetaElements); expect(mockGatherImpl).toHaveBeenCalled(); expect(runAuditSpy).not.toHaveBeenCalled(); @@ -195,7 +195,7 @@ describe('Runner', () => { auditMode: moduleDir + '/fixtures/artifacts/perflog/', }, audits: [ - 'content-width', + 'viewport', ], }); @@ -315,11 +315,11 @@ describe('Runner', () => { const url = 'https://example.com'; const {resolvedConfig} = await initializeConfig('navigation', { artifacts: [{ - id: 'ViewportDimensions', - gatherer: 'viewport-dimensions', + id: 'MetaElements', + gatherer: 'meta-elements', }], audits: [ - 'content-width', + 'viewport', ], }); @@ -394,11 +394,11 @@ describe('Runner', () => { it('finds correct timings for multiple gather/audit pairs run separately', async () => { const {resolvedConfig} = await initializeConfig('navigation', { artifacts: [{ - id: 'ViewportDimensions', - gatherer: 'viewport-dimensions', + id: 'MetaElements', + gatherer: 'meta-elements', }], audits: [ - 'content-width', + 'viewport', ], }); const options1 = {resolvedConfig, driverMock, computedCache: new Map()}; @@ -472,19 +472,19 @@ describe('Runner', () => { auditMode: moduleDir + '/fixtures/artifacts/empty-artifacts/', }, audits: [ - // requires the ViewportDimensions artifact - 'content-width', + // requires the MetaElements artifact + 'viewport', ], artifacts: [ - {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'}, + {id: 'MetaElements', gatherer: 'meta-elements'}, ], }); return runGatherAndAudit({}, {resolvedConfig}).then(results => { - const auditResult = results.lhr.audits['content-width']; + const auditResult = results.lhr.audits['viewport']; assert.strictEqual(auditResult.score, null); assert.strictEqual(auditResult.scoreDisplayMode, 'error'); - assert.ok(auditResult.errorMessage.includes('ViewportDimensions')); + assert.ok(auditResult.errorMessage.includes('MetaElements')); }); }); @@ -497,7 +497,7 @@ describe('Runner', () => { const errorMessage = 'blurst of times'; const artifacts = { ...baseArtifacts, - ViewportDimensions: new Error(errorMessage), + MetaElements: new Error(errorMessage), }; const artifactsPath = '.tmp/test_artifacts'; const resolvedPath = path.resolve(process.cwd(), artifactsPath); @@ -509,16 +509,16 @@ describe('Runner', () => { auditMode: resolvedPath, }, audits: [ - // requires ViewportDimensions artifact - 'content-width', + // requires MetaElements artifact + 'viewport', ], artifacts: [ - {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'}, + {id: 'MetaElements', gatherer: 'meta-elements'}, ], }); const results = await runGatherAndAudit({}, {resolvedConfig}); - const auditResult = results.lhr.audits['content-width']; + const auditResult = results.lhr.audits['viewport']; assert.strictEqual(auditResult.score, null); assert.strictEqual(auditResult.scoreDisplayMode, 'error'); assert.ok(auditResult.errorMessage.includes(errorMessage)); @@ -730,10 +730,10 @@ describe('Runner', () => { const url = 'https://example.com/'; const {resolvedConfig} = await initializeConfig('navigation', { audits: [ - 'content-width', + 'viewport', ], artifacts: [ - {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'}, + {id: 'MetaElements', gatherer: 'meta-elements'}, ], }); @@ -742,7 +742,7 @@ describe('Runner', () => { assert.ok(results.lhr.lighthouseVersion); assert.ok(results.lhr.fetchTime); assert.equal(results.lhr.requestedUrl, url); - assert.equal(results.lhr.audits['content-width'].id, 'content-width'); + assert.equal(results.lhr.audits['viewport'].id, 'viewport'); expect(mockGatherImpl).toHaveBeenCalled(); }); }); @@ -751,18 +751,18 @@ describe('Runner', () => { const url = 'https://example.com/'; const {resolvedConfig} = await initializeConfig('navigation', { artifacts: [{ - id: 'ViewportDimensions', - gatherer: 'viewport-dimensions', + id: 'MetaElements', + gatherer: 'meta-elements', }], audits: [ - 'content-width', + 'viewport', ], categories: { category: { title: 'Category', description: '', auditRefs: [ - {id: 'content-width', weight: 1}, + {id: 'viewport', weight: 1}, ], }, }, @@ -774,10 +774,8 @@ describe('Runner', () => { assert.ok(results.lhr.lighthouseVersion); assert.ok(results.lhr.fetchTime); assert.equal(results.lhr.requestedUrl, url); - assert.equal(results.lhr.audits['content-width'].id, 'content-width'); - assert.equal(results.lhr.audits['content-width'].score, 1); - assert.equal(results.lhr.categories.category.score, 1); - assert.equal(results.lhr.categories.category.auditRefs[0].id, 'content-width'); + assert.equal(results.lhr.audits['viewport'].id, 'viewport'); + assert.equal(results.lhr.categories.category.auditRefs[0].id, 'viewport'); }); }); @@ -797,7 +795,7 @@ describe('Runner', () => { auditMode: moduleDir + '/fixtures/artifacts/perflog/', }, audits: [ - 'content-width', + 'viewport', ], }); @@ -812,18 +810,17 @@ describe('Runner', () => { const {resolvedConfig} = await initializeConfig('navigation', { artifacts: [ {id: 'MetaElements', gatherer: 'meta-elements'}, - {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'}, ], audits: [ - 'content-width', + 'viewport', ], }); const options = {resolvedConfig, driverMock, computedCache: new Map()}; return runGatherAndAudit(createGatherFn(url), options).then(results => { // User-specified artifact. - assert.ok(results.artifacts.ViewportDimensions); + assert.ok(results.artifacts.MetaElements); }); }); diff --git a/core/test/scenarios/__snapshots__/api-test-pptr.js.snap b/core/test/scenarios/__snapshots__/api-test-pptr.js.snap index 92a41db24960..9bbfb0ed85d2 100644 --- a/core/test/scenarios/__snapshots__/api-test-pptr.js.snap +++ b/core/test/scenarios/__snapshots__/api-test-pptr.js.snap @@ -29,7 +29,6 @@ Array [ "canonical", "charset", "color-contrast", - "content-width", "crawlable-anchors", "critical-request-chains", "csp-xss", @@ -72,7 +71,6 @@ Array [ "input-button-name", "input-image-alt", "inspector-issues", - "installable-manifest", "interactive", "interactive-element-affordance", "is-crawlable", @@ -96,7 +94,6 @@ Array [ "main-thread-tasks", "mainthread-work-breakdown", "managed-focus", - "maskable-icon", "max-potential-fid", "meta-description", "meta-refresh", @@ -115,9 +112,6 @@ Array [ "paste-preventing-inputs", "performance-budget", "prioritize-lcp-image", - "pwa-cross-browser", - "pwa-each-page-has-url", - "pwa-page-transitions", "redirects", "redirects-http", "render-blocking-resources", @@ -129,7 +123,6 @@ Array [ "server-response-time", "skip-link", "speed-index", - "splash-screen", "structured-data", "tabindex", "table-duplicate-name", @@ -138,7 +131,6 @@ Array [ "td-has-header", "td-headers-attr", "th-has-data-cells", - "themed-omnibox", "third-party-cookies", "third-party-facades", "third-party-summary", @@ -196,7 +188,6 @@ Array [ "canonical", "charset", "color-contrast", - "content-width", "crawlable-anchors", "critical-request-chains", "csp-xss", @@ -239,7 +230,6 @@ Array [ "input-button-name", "input-image-alt", "inspector-issues", - "installable-manifest", "interactive", "interactive-element-affordance", "is-crawlable", @@ -263,7 +253,6 @@ Array [ "main-thread-tasks", "mainthread-work-breakdown", "managed-focus", - "maskable-icon", "max-potential-fid", "meta-description", "meta-refresh", @@ -282,9 +271,6 @@ Array [ "paste-preventing-inputs", "performance-budget", "prioritize-lcp-image", - "pwa-cross-browser", - "pwa-each-page-has-url", - "pwa-page-transitions", "redirects", "redirects-http", "render-blocking-resources", @@ -296,7 +282,6 @@ Array [ "server-response-time", "skip-link", "speed-index", - "splash-screen", "structured-data", "tabindex", "table-duplicate-name", @@ -305,7 +290,6 @@ Array [ "td-has-header", "td-headers-attr", "th-has-data-cells", - "themed-omnibox", "third-party-cookies", "third-party-facades", "third-party-summary", diff --git a/docs/architecture.md b/docs/architecture.md index 761b6114b263..ce83a712586b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -16,7 +16,7 @@ _Some incomplete notes_ * **Report** - The report UI, created client-side from the LHR. See [HTML Report Generation Overview](../report/README.md) for details. ### Audit/Report terminology -* **Category** - Roll-up collection of audits and audit groups into a user-facing section of the report (eg. `Best Practices`). Applies weighting and overall scoring to the section. Examples: PWA, Accessibility, Best Practices. +* **Category** - Roll-up collection of audits and audit groups into a user-facing section of the report (eg. `Best Practices`). Applies weighting and overall scoring to the section. Examples: Accessibility, Best Practices. * **Audit title** - Short user-visible title for the successful audit. eg. “All image elements have `[alt]` attributes.” * **Audit failureTitle** - Short user-visible title for a failing audit. eg. “Some image elements do not have `[alt]` attributes.” * **Audit description** - Explanation of why the user should care about the audit. Not necessarily how to fix it, unless there is no external link that explains it. ([See description guidelines](../CONTRIBUTING.md#audit-description-guidelines)). eg. “Informative elements should aim for short, descriptive alternate text. Decorative elements can be ignored with an empty alt attribute. [Learn more].” diff --git a/docs/scoring.md b/docs/scoring.md index 57aec98815fb..a224ccd30488 100644 --- a/docs/scoring.md +++ b/docs/scoring.md @@ -4,14 +4,6 @@ ➡️ Please read [Lighthouse Performance Scoring at developer.chrome.com](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/). -## How is the PWA (Progressive Web App) score calculated? - -The PWA category doesn't get a 0-100 score, but instead is evaluated in 3 separate groups (Fast and reliable, Installable, and PWA Optimized). In order to satisfy each grouping (and get the associated badge), every audit within the group must be passing. - -![Lighthouse PWA badge - states](https://user-images.githubusercontent.com/39191/80662283-c292d280-8a45-11ea-84e8-7f8248657acf.png) - -Note on https redirects: some metrics in this category have issues with https redirects because of TLS-handshake errors. More specifically you will run into this when using the ```simplehttp2server``` npm package. Subsequent metrics will fail after the https redirects (see [#1217](https://github.com/GoogleChrome/lighthouse/issues/1217), [#5910](https://github.com/GoogleChrome/lighthouse/issues/5910)). - ## How is the Best Practices score calculated? All audits in the Best Practices category are equally weighted. Therefore, implementing each audit correctly will increase your overall score by ~6 points. diff --git a/docs/understanding-results.md b/docs/understanding-results.md index 400c5cc198e9..15a3d3c69087 100644 --- a/docs/understanding-results.md +++ b/docs/understanding-results.md @@ -174,14 +174,14 @@ An array containing the different categories, their scores, and the results of t ### Example ```json { - "pwa": { - "id": "pwa", - "title": "Progressive Web App", - "description": "PWAs are awesome. [Learn more](...)", + "seo": { + "id": "seo", + "title": "SEO", + "description": "These checks ensure that your page is following basic search engine optimization advice...", "score": 0.54, "auditRefs": [ { - "id": "is-on-https", + "id": "crawlable-anchors", "weight": 1 } ] diff --git a/flow-report/src/help-dialog.tsx b/flow-report/src/help-dialog.tsx index 079b11a95ef5..de62615fe649 100644 --- a/flow-report/src/help-dialog.tsx +++ b/flow-report/src/help-dialog.tsx @@ -84,7 +84,6 @@ export const HelpDialog: FunctionComponent<{onClose: () => void}> = ({ strings.categoryAccessibility, strings.categoryBestPractices, strings.categorySeo, - strings.categoryProgressiveWebApp, ]} /> --quiet --chrome-flags="--headless" Launch Headless Chrome, turn off logging lighthouse --extra-headers "{\"Cookie\":\"monster=blue\", \"x-men\":\"wolverine\"}" Stringify'd JSON HTTP Header key/value pairs to send in requests lighthouse --extra-headers=./path/to/file.json Path to JSON file of HTTP Header key/value pairs to send in requests - lighthouse --only-categories=performance,pwa Only run the specified categories. Available categories: accessibility, best-practices, performance, pwa, seo + lighthouse --only-categories=performance,seo Only run the specified categories. Available categories: accessibility, best-practices, performance, seo For more information on Lighthouse, see https://developers.google.com/web/tools/lighthouse/. ``` diff --git a/report/assets/styles.css b/report/assets/styles.css index ccf385d1e1de..b24a90c25b3f 100644 --- a/report/assets/styles.css +++ b/report/assets/styles.css @@ -103,8 +103,6 @@ --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7); --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7); --plugin-icon-size: 65%; - --pwa-icon-margin: 0 var(--default-padding); - --pwa-icon-size: var(--topbar-logo-size); --report-background-color: #fff; --report-border-color-secondary: #ebebeb; --report-font-family-monospace: 'Roboto Mono', 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace; @@ -157,15 +155,6 @@ --fail-icon-url: url('data:image/svg+xml;utf8,warn'); --error-icon-url: url('data:image/svg+xml;utf8,error'); - --pwa-installable-gray-url: url('data:image/svg+xml;utf8,'); - --pwa-optimized-gray-url: url('data:image/svg+xml;utf8,'); - - --pwa-installable-gray-url-dark: url('data:image/svg+xml;utf8,'); - --pwa-optimized-gray-url-dark: url('data:image/svg+xml;utf8,'); - - --pwa-installable-color-url: url('data:image/svg+xml;utf8,'); - --pwa-optimized-color-url: url('data:image/svg+xml;utf8,'); - --swap-locale-icon-url: url('data:image/svg+xml;utf8,'); } @@ -207,8 +196,6 @@ /* SVGs */ --plugin-icon-url: var(--plugin-icon-url-dark); - --pwa-installable-gray-url: var(--pwa-installable-gray-url-dark); - --pwa-optimized-gray-url: var(--pwa-optimized-gray-url-dark); } } @@ -229,7 +216,6 @@ --header-padding: 16px 0 16px 0; --image-preview-size: 24px; --plugin-icon-size: 75%; - --pwa-icon-margin: 0 7px 0 -3px; --report-font-size: 14px; --report-line-height: 20px; --score-icon-margin-left: 2px; @@ -261,7 +247,6 @@ --header-padding: 16px 0 16px 0; --screenshot-overlay-background: transparent; --plugin-icon-size: 75%; - --pwa-icon-margin: 0 7px 0 -3px; --report-font-family-monospace: 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace; --report-font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif; --report-font-size: 12px; @@ -931,16 +916,6 @@ margin-bottom: calc(var(--audit-group-margin-bottom) / 2); } -.lh-audit-group__header::before { - /* By default, groups don't get an icon */ - content: none; - width: var(--pwa-icon-size); - height: var(--pwa-icon-size); - margin: var(--pwa-icon-margin); - display: inline-block; - vertical-align: middle; -} - /* Style the "over budget" columns red. */ .lh-audit-group--budgets #performance-budget tbody tr td:nth-child(4), .lh-audit-group--budgets #performance-budget tbody tr td:nth-child(5), @@ -958,21 +933,6 @@ margin: 0 0 var(--default-padding); } -.lh-audit-group--pwa-installable .lh-audit-group__header::before { - content: ''; - background-image: var(--pwa-installable-gray-url); -} -.lh-audit-group--pwa-optimized .lh-audit-group__header::before { - content: ''; - background-image: var(--pwa-optimized-gray-url); -} -.lh-audit-group--pwa-installable.lh-badged .lh-audit-group__header::before { - background-image: var(--pwa-installable-color-url); -} -.lh-audit-group--pwa-optimized.lh-badged .lh-audit-group__header::before { - background-image: var(--pwa-optimized-color-url); -} - .lh-audit-group--metrics .lh-audit-group__summary { margin-top: 0; margin-bottom: 0; @@ -1323,10 +1283,8 @@ .lh-scores-header .lh-gauge__wrapper, .lh-scores-header .lh-fraction__wrapper, -.lh-scores-header .lh-gauge--pwa__wrapper, .lh-sticky-header .lh-gauge__wrapper, -.lh-sticky-header .lh-fraction__wrapper, -.lh-sticky-header .lh-gauge--pwa__wrapper { +.lh-sticky-header .lh-fraction__wrapper { width: var(--gauge-wrapper-width); } diff --git a/report/assets/templates.html b/report/assets/templates.html index 16b0352c16f1..cc45d2c86982 100644 --- a/report/assets/templates.html +++ b/report/assets/templates.html @@ -514,101 +514,6 @@ - - - -