From ff752065fdf00e842d91a68008179575e35494f5 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Mon, 28 Jun 2021 15:32:41 -0400 Subject: [PATCH 1/9] new_audit(fr): uses-responsive-images-snapshot --- demo.js | 21 ++++ .../uses-responsive-images-snapshot.js | 95 +++++++++++++++++++ .../byte-efficiency/uses-responsive-images.js | 55 +++++++---- .../fraggle-rock/config/default-config.js | 27 +++++- lighthouse-core/lib/i18n/locales/en-US.json | 6 ++ lighthouse-core/lib/i18n/locales/en-XL.json | 6 ++ 6 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 demo.js create mode 100644 lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js diff --git a/demo.js b/demo.js new file mode 100644 index 000000000000..6e6ce3378cfe --- /dev/null +++ b/demo.js @@ -0,0 +1,21 @@ +'use strict'; +const puppeteer = require('puppeteer'); +const lighthouse = require('./lighthouse-core/fraggle-rock/api.js'); + +async function run() { + const browser = await puppeteer.launch({ + headless: false, + }); + const page = await browser.newPage(); + + await page.goto('http://localhost:8080/responsive-image.html'); + await new Promise(r => setTimeout(r, 1000)); + + const result = await lighthouse.snapshot({page}); + + console.log(JSON.stringify(result.lhr, null, 2)); + + await page.close(); + await browser.close(); +} +run(); diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js new file mode 100644 index 000000000000..add66f028729 --- /dev/null +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js @@ -0,0 +1,95 @@ +/** + * @license Copyright 2021 The Lighthouse Authors. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +/** + * @fileoverview Checks to see if the images used on the page are larger than + * their display sizes. The audit will list all images that are larger than + * their display size with DPR (a 1000px wide image displayed as a + * 500px high-res image on a Retina display is 100% used); + */ +'use strict'; + +const Audit = require('../audit.js'); +const UsesResponsiveImages = require('./uses-responsive-images.js'); +const URL = require('../../lib/url-shim.js'); +const i18n = require('../../lib/i18n/i18n.js'); + +const UIStrings = { + /** Label for a column in a data table; entries will be the dimensions of an image as it appears on the page. */ + columnDisplayedDimensions: 'Displayed dimensions', + /** Label for a column in a data table; entries will be the dimensions of an image from it's source file. */ + columnActualDimensions: 'Actual dimensions', +}; + +Object.assign(UIStrings, UsesResponsiveImages.UIStrings); + +const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); + +class UsesResponsiveImagesSnapshot extends Audit { + /** + * @return {LH.Audit.Meta} + */ + static get meta() { + return { + id: 'uses-responsive-images-snapshot', + // @ts-ignore Title imported from UsesResponsiveImages. + title: str_(UIStrings.title), + // @ts-ignore Description imported from UsesResponsiveImages. + description: str_(UIStrings.description), + supportedModes: ['snapshot'], + requiredArtifacts: ['ImageElements', 'ViewportDimensions'], + }; + } + + /** + * @param {LH.Artifacts} artifacts + * @return {Promise} + */ + static async audit(artifacts) { + /** @type {LH.Audit.Details.TableItem[]} */ + const items = []; + for (const image of artifacts.ImageElements) { + if (!image.naturalDimensions) continue; + const actual = image.naturalDimensions; + const displayed = UsesResponsiveImages.getDisplayedDimensions( + {...image, naturalWidth: actual.width, naturalHeight: actual.height}, + artifacts.ViewportDimensions + ); + + const actualPixels = actual.width * actual.height * + Math.pow(artifacts.ViewportDimensions.devicePixelRatio, 2); + const usedPixels = displayed.width * displayed.height * + Math.pow(artifacts.ViewportDimensions.devicePixelRatio, 2); + + if (actualPixels <= usedPixels) continue; + + items.push({ + url: URL.elideDataURI(image.src), + displayedDimensions: `${displayed.width}x${displayed.height}`, + actualDimensions: `${actual.width}x${actual.height}`, + }); + } + + /** @type {LH.Audit.Details.Table['headings']} */ + const headings = [ + /* eslint-disable max-len */ + {key: 'url', itemType: 'thumbnail', text: ''}, + {key: 'url', itemType: 'url', text: str_(i18n.UIStrings.columnURL)}, + {key: 'displayedDimensions', itemType: 'text', text: str_(UIStrings.columnDisplayedDimensions)}, + {key: 'actualDimensions', itemType: 'text', text: str_(UIStrings.columnActualDimensions)}, + /* eslint-enable max-len */ + ]; + + const details = Audit.makeTableDetails(headings, items); + + return { + score: items.length ? 0 : 1, + details, + }; + } +} + +module.exports = UsesResponsiveImagesSnapshot; +module.exports.UIStrings = UIStrings; diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index 14f5993962f4..918a323ad754 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -47,6 +47,39 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { }; } + /** + * @param {LH.Artifacts.ImageElement & {naturalWidth: number, naturalHeight: number}} image + * @param {LH.Artifacts.ViewportDimensions} ViewportDimensions + * @return {{width: number, height: number}}; + */ + static getDisplayedDimensions(image, ViewportDimensions) { + if (image.displayedWidth && image.displayedHeight) { + return { + width: image.displayedWidth, + height: image.displayedHeight, + }; + } + + // If the image has 0 dimensions, it's probably hidden/offscreen, so we'll be as forgiving as possible + // and assume it's the size of two viewports. See https://github.com/GoogleChrome/lighthouse/issues/7236 + const viewportWidth = ViewportDimensions.innerWidth; + const viewportHeight = ViewportDimensions.innerHeight * 2; + const imageAspectRatio = image.naturalWidth / image.naturalHeight; + const viewportAspectRatio = viewportWidth / viewportHeight; + let usedViewportWidth = viewportWidth; + let usedViewportHeight = viewportHeight; + if (imageAspectRatio > viewportAspectRatio) { + usedViewportHeight = viewportWidth / imageAspectRatio; + } else { + usedViewportWidth = viewportHeight * imageAspectRatio; + } + + return { + width: usedViewportWidth, + height: usedViewportHeight, + }; + } + /** * @param {LH.Artifacts.ImageElement & {naturalWidth: number, naturalHeight: number}} image * @param {LH.Artifacts.ViewportDimensions} ViewportDimensions @@ -60,26 +93,9 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { return null; } - let usedPixels = image.displayedWidth * image.displayedHeight * + const displayed = this.getDisplayedDimensions(image, ViewportDimensions); + const usedPixels = displayed.width * displayed.height * Math.pow(ViewportDimensions.devicePixelRatio, 2); - // If the image has 0 dimensions, it's probably hidden/offscreen, so we'll be as forgiving as possible - // and assume it's the size of two viewports. See https://github.com/GoogleChrome/lighthouse/issues/7236 - if (!usedPixels) { - const viewportWidth = ViewportDimensions.innerWidth; - const viewportHeight = ViewportDimensions.innerHeight * 2; - const imageAspectRatio = image.naturalWidth / image.naturalHeight; - const viewportAspectRatio = viewportWidth / viewportHeight; - let usedViewportWidth = viewportWidth; - let usedViewportHeight = viewportHeight; - if (imageAspectRatio > viewportAspectRatio) { - usedViewportHeight = viewportWidth / imageAspectRatio; - } else { - usedViewportWidth = viewportHeight * imageAspectRatio; - } - - usedPixels = usedViewportWidth * usedViewportHeight * - Math.pow(ViewportDimensions.devicePixelRatio, 2); - } const url = URL.elideDataURI(image.src); const actualPixels = image.naturalWidth * image.naturalHeight; @@ -163,3 +179,4 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { module.exports = UsesResponsiveImages; module.exports.UIStrings = UIStrings; +module.exports.str_ = str_; diff --git a/lighthouse-core/fraggle-rock/config/default-config.js b/lighthouse-core/fraggle-rock/config/default-config.js index 5ea13da23253..9a58ad459896 100644 --- a/lighthouse-core/fraggle-rock/config/default-config.js +++ b/lighthouse-core/fraggle-rock/config/default-config.js @@ -7,6 +7,29 @@ const legacyDefaultConfig = require('../../config/default-config.js'); +/** @type {LH.Config.AuditJson[]} */ +const frAudits = [ + 'byte-efficiency/uses-responsive-images-snapshot', +]; + +/** @type {Record} */ +const frCategories = { + 'performance': [ + {id: 'uses-responsive-images-snapshot', weight: 0, group: 'diagnostics'}, + ], +}; + +/** @return {LH.Config.Json['categories']} */ +function mergeCategories() { + if (!legacyDefaultConfig.categories) return {}; + const categories = legacyDefaultConfig.categories; + for (const key of Object.keys(frCategories)) { + if (!categories[key]) continue; + categories[key].auditRefs.push(...frCategories[key]); + } + return categories; +} + // Ensure all artifact IDs match the typedefs. /** @type {Record} */ const artifacts = { @@ -168,8 +191,8 @@ const defaultConfig = { }, ], settings: legacyDefaultConfig.settings, - audits: legacyDefaultConfig.audits, - categories: legacyDefaultConfig.categories, + audits: [...(legacyDefaultConfig.audits || []), ...frAudits], + categories: mergeCategories(), groups: legacyDefaultConfig.groups, }; diff --git a/lighthouse-core/lib/i18n/locales/en-US.json b/lighthouse-core/lib/i18n/locales/en-US.json index 6add2bc73144..909efe754071 100644 --- a/lighthouse-core/lib/i18n/locales/en-US.json +++ b/lighthouse-core/lib/i18n/locales/en-US.json @@ -548,6 +548,12 @@ "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title": { "message": "Efficiently encode images" }, + "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | columnActualDimensions": { + "message": "Actual dimensions" + }, + "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | columnDisplayedDimensions": { + "message": "Displayed dimensions" + }, "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": { "message": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn more](https://web.dev/uses-responsive-images/)." }, diff --git a/lighthouse-core/lib/i18n/locales/en-XL.json b/lighthouse-core/lib/i18n/locales/en-XL.json index fce4eeae83a4..5d4cc114aa6a 100644 --- a/lighthouse-core/lib/i18n/locales/en-XL.json +++ b/lighthouse-core/lib/i18n/locales/en-XL.json @@ -548,6 +548,12 @@ "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title": { "message": "Êf́f̂íĉíêńt̂ĺŷ én̂ćôd́ê ím̂áĝéŝ" }, + "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | columnActualDimensions": { + "message": "Âćt̂úâĺ d̂ím̂én̂śîón̂ś" + }, + "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | columnDisplayedDimensions": { + "message": "D̂íŝṕl̂áŷéd̂ d́îḿêńŝíôńŝ" + }, "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": { "message": "Ŝér̂v́ê ím̂áĝéŝ t́ĥát̂ ár̂é âṕp̂ŕôṕr̂íât́êĺŷ-śîźêd́ t̂ó ŝáv̂é ĉél̂ĺûĺâŕ d̂át̂á âńd̂ ím̂ṕr̂óv̂é l̂óâd́ t̂ím̂é. [L̂éâŕn̂ ḿôŕê](https://web.dev/uses-responsive-images/)." }, From 18303285ec12edc02d28b1d9b031dc5342c9a8f3 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Mon, 28 Jun 2021 16:32:18 -0400 Subject: [PATCH 2/9] add test --- .../uses-responsive-images-snapshot.js | 6 +- .../byte-efficiency/uses-responsive-images.js | 11 +- .../uses-responsive-images-snapshot-test.js | 140 ++++++++++++++++++ 3 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js index add66f028729..4f4e804ca7a4 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js @@ -58,10 +58,8 @@ class UsesResponsiveImagesSnapshot extends Audit { artifacts.ViewportDimensions ); - const actualPixels = actual.width * actual.height * - Math.pow(artifacts.ViewportDimensions.devicePixelRatio, 2); - const usedPixels = displayed.width * displayed.height * - Math.pow(artifacts.ViewportDimensions.devicePixelRatio, 2); + const actualPixels = actual.width * actual.height; + const usedPixels = displayed.width * displayed.height; if (actualPixels <= usedPixels) continue; diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index 918a323ad754..1287df4f4cd5 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -55,8 +55,8 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { static getDisplayedDimensions(image, ViewportDimensions) { if (image.displayedWidth && image.displayedHeight) { return { - width: image.displayedWidth, - height: image.displayedHeight, + width: image.displayedWidth * ViewportDimensions.devicePixelRatio, + height: image.displayedHeight * ViewportDimensions.devicePixelRatio, }; } @@ -75,8 +75,8 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { } return { - width: usedViewportWidth, - height: usedViewportHeight, + width: usedViewportWidth * ViewportDimensions.devicePixelRatio, + height: usedViewportHeight * ViewportDimensions.devicePixelRatio, }; } @@ -94,8 +94,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { } const displayed = this.getDisplayedDimensions(image, ViewportDimensions); - const usedPixels = displayed.width * displayed.height * - Math.pow(ViewportDimensions.devicePixelRatio, 2); + const usedPixels = displayed.width * displayed.height; const url = URL.elideDataURI(image.src); const actualPixels = image.naturalWidth * image.naturalHeight; diff --git a/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js b/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js new file mode 100644 index 000000000000..2dd00283ad54 --- /dev/null +++ b/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js @@ -0,0 +1,140 @@ +/** + * @license Copyright 2021 The Lighthouse Authors. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +'use strict'; + +const UsesResponsiveImagesSnapshot = + require('../../../audits/byte-efficiency/uses-responsive-images-snapshot.js'); + +/* eslint-env jest */ + +/** + * @param {Partial=} partial + * @return {LH.Artifacts.ImageElement} + */ +function mockElement(partial = {}) { + return { + src: 'https://www.paulirish.com/avatar150.jpg', + srcset: '', + displayedWidth: 200, + displayedHeight: 200, + clientRect: { + top: 50, + bottom: 250, + left: 50, + right: 250, + }, + attributeWidth: '', + attributeHeight: '', + naturalDimensions: undefined, + cssEffectiveRules: undefined, + computedStyles: {position: 'absolute', objectFit: '', imageRendering: ''}, + isCss: false, + isPicture: false, + isInShadowDOM: false, + node: { + lhId: '__nodeid__', + devtoolsNodePath: '1,HTML,1,BODY,1,DIV,1,IMG', + selector: 'body > div > img', + nodeLabel: 'img', + snippet: '', + boundingRect: { + top: 50, + bottom: 250, + left: 50, + right: 250, + width: 200, + height: 200, + }, + }, + ...partial, + }; +} + +it('reports images that are bigger than displayed dimensions', async () => { + const artifacts = { + ImageElements: [ + mockElement({naturalDimensions: {width: 500, height: 500}}), + ], + ViewportDimensions: { + width: 500, + height: 500, + devicePixelRatio: 1, + }, + }; + + const result = await UsesResponsiveImagesSnapshot.audit(artifacts); + + expect(result.score).toEqual(0); + expect(result.details.items).toEqual([ + { + actualDimensions: '500x500', + displayedDimensions: '200x200', + url: 'https://www.paulirish.com/avatar150.jpg', + }, + ]); +}); + +it('ignores images smaller or equal to displayed dimensions', async () => { + const artifacts = { + ImageElements: [ + mockElement({naturalDimensions: {width: 200, height: 200}}), + mockElement({naturalDimensions: {width: 40, height: 40}}), + ], + ViewportDimensions: { + width: 500, + height: 500, + devicePixelRatio: 1, + }, + }; + + const result = await UsesResponsiveImagesSnapshot.audit(artifacts); + + expect(result.score).toEqual(1); + expect(result.details.items).toEqual([]); +}); + +it('ignores images with no natural dimensions', async () => { + const artifacts = { + ImageElements: [ + mockElement(), + ], + ViewportDimensions: { + width: 500, + height: 500, + devicePixelRatio: 1, + }, + }; + + const result = await UsesResponsiveImagesSnapshot.audit(artifacts); + + expect(result.score).toEqual(1); + expect(result.details.items).toEqual([]); +}); + +it('uses pixel ratio to compute used pixels', async () => { + const artifacts = { + ImageElements: [ + mockElement({naturalDimensions: {width: 400, height: 400}}), + mockElement({naturalDimensions: {width: 500, height: 500}}), + ], + ViewportDimensions: { + width: 500, + height: 500, + devicePixelRatio: 2, + }, + }; + + const result = await UsesResponsiveImagesSnapshot.audit(artifacts); + + expect(result.score).toEqual(0); + expect(result.details.items).toEqual([ + { + actualDimensions: '500x500', + displayedDimensions: '400x400', + url: 'https://www.paulirish.com/avatar150.jpg', + }, + ]); +}); From 36da58fdec054465bfede0499382857afa4be652 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Mon, 28 Jun 2021 16:32:58 -0400 Subject: [PATCH 3/9] api test --- lighthouse-core/test/fraggle-rock/api-test-pptr.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighthouse-core/test/fraggle-rock/api-test-pptr.js b/lighthouse-core/test/fraggle-rock/api-test-pptr.js index 16635b0db4fb..13f775f98caa 100644 --- a/lighthouse-core/test/fraggle-rock/api-test-pptr.js +++ b/lighthouse-core/test/fraggle-rock/api-test-pptr.js @@ -95,7 +95,7 @@ describe('Fraggle Rock API', () => { const {auditResults, erroredAudits, failedAudits} = getAuditsBreakdown(lhr); // TODO(FR-COMPAT): This assertion can be removed when full compatibility is reached. - expect(auditResults.length).toMatchInlineSnapshot(`81`); + expect(auditResults.length).toMatchInlineSnapshot(`82`); expect(erroredAudits).toHaveLength(0); expect(failedAudits.map(audit => audit.id)).toContain('label'); From eb8bdcf9d3b675311ccf92c2503b2529ba3ec1c6 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Mon, 28 Jun 2021 16:40:03 -0400 Subject: [PATCH 4/9] ope --- lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index 1287df4f4cd5..df97afe8767a 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -178,4 +178,3 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { module.exports = UsesResponsiveImages; module.exports.UIStrings = UIStrings; -module.exports.str_ = str_; From 4105ea956d893d0fc82393dba1dfe2ee77708d09 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Mon, 28 Jun 2021 16:45:27 -0400 Subject: [PATCH 5/9] rm demo --- demo.js | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 demo.js diff --git a/demo.js b/demo.js deleted file mode 100644 index 6e6ce3378cfe..000000000000 --- a/demo.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; -const puppeteer = require('puppeteer'); -const lighthouse = require('./lighthouse-core/fraggle-rock/api.js'); - -async function run() { - const browser = await puppeteer.launch({ - headless: false, - }); - const page = await browser.newPage(); - - await page.goto('http://localhost:8080/responsive-image.html'); - await new Promise(r => setTimeout(r, 1000)); - - const result = await lighthouse.snapshot({page}); - - console.log(JSON.stringify(result.lhr, null, 2)); - - await page.close(); - await browser.close(); -} -run(); From 4c7f8884864f934e26ab6b609e45372cd4d079b7 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 29 Jun 2021 14:55:27 -0400 Subject: [PATCH 6/9] import str_ --- .../byte-efficiency/uses-responsive-images-snapshot.js | 8 ++------ .../audits/byte-efficiency/uses-responsive-images.js | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js index 4f4e804ca7a4..34149e53124b 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js @@ -23,8 +23,6 @@ const UIStrings = { columnActualDimensions: 'Actual dimensions', }; -Object.assign(UIStrings, UsesResponsiveImages.UIStrings); - const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); class UsesResponsiveImagesSnapshot extends Audit { @@ -34,10 +32,8 @@ class UsesResponsiveImagesSnapshot extends Audit { static get meta() { return { id: 'uses-responsive-images-snapshot', - // @ts-ignore Title imported from UsesResponsiveImages. - title: str_(UIStrings.title), - // @ts-ignore Description imported from UsesResponsiveImages. - description: str_(UIStrings.description), + title: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.title), + description: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.description), supportedModes: ['snapshot'], requiredArtifacts: ['ImageElements', 'ViewportDimensions'], }; diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index df97afe8767a..1287df4f4cd5 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -178,3 +178,4 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { module.exports = UsesResponsiveImages; module.exports.UIStrings = UIStrings; +module.exports.str_ = str_; From 3bf789efa1fc8d4b43ef3eccbb3be4bcb29da323 Mon Sep 17 00:00:00 2001 From: Adam Raine <6752989+adamraine@users.noreply.github.com> Date: Thu, 1 Jul 2021 13:56:18 -0400 Subject: [PATCH 7/9] Update lighthouse-core/fraggle-rock/config/default-config.js Co-authored-by: Patrick Hulce --- lighthouse-core/fraggle-rock/config/default-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighthouse-core/fraggle-rock/config/default-config.js b/lighthouse-core/fraggle-rock/config/default-config.js index 9a58ad459896..dbbeca99df0e 100644 --- a/lighthouse-core/fraggle-rock/config/default-config.js +++ b/lighthouse-core/fraggle-rock/config/default-config.js @@ -13,7 +13,7 @@ const frAudits = [ ]; /** @type {Record} */ -const frCategories = { +const frCategoryAuditRefExtensions = { 'performance': [ {id: 'uses-responsive-images-snapshot', weight: 0, group: 'diagnostics'}, ], From 542cfb3121055273a35e49b2d458a3c0f3135fcd Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Thu, 1 Jul 2021 14:23:56 -0400 Subject: [PATCH 8/9] fix --- .../uses-responsive-images-snapshot-test.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js b/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js index 2dd00283ad54..7d09140d155a 100644 --- a/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/uses-responsive-images-snapshot-test.js @@ -138,3 +138,27 @@ it('uses pixel ratio to compute used pixels', async () => { }, ]); }); + +it('passes if pixel difference is within the threshold', async () => { + const artifacts = { + ImageElements: [ + mockElement({naturalDimensions: {width: 201, height: 200}}), + ], + ViewportDimensions: { + width: 500, + height: 500, + devicePixelRatio: 1, + }, + }; + + const result = await UsesResponsiveImagesSnapshot.audit(artifacts); + + expect(result.score).toEqual(1); + expect(result.details.items).toEqual([ + { + actualDimensions: '201x200', + displayedDimensions: '200x200', + url: 'https://www.paulirish.com/avatar150.jpg', + }, + ]); +}); From 6ee1eb381a112adfaf68d64884471c5dec32693d Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Thu, 1 Jul 2021 14:24:13 -0400 Subject: [PATCH 9/9] threshold --- .../byte-efficiency/uses-responsive-images-snapshot.js | 7 ++++++- lighthouse-core/fraggle-rock/config/default-config.js | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js index 34149e53124b..ed255ff87fe4 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js @@ -25,6 +25,9 @@ const UIStrings = { const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); +// Based on byte threshold of 4096, with 3 bytes per pixel. +const IGNORE_THRESHOLD_IN_PIXELS = 1365; + class UsesResponsiveImagesSnapshot extends Audit { /** * @return {LH.Audit.Meta} @@ -44,6 +47,7 @@ class UsesResponsiveImagesSnapshot extends Audit { * @return {Promise} */ static async audit(artifacts) { + let score = 1; /** @type {LH.Audit.Details.TableItem[]} */ const items = []; for (const image of artifacts.ImageElements) { @@ -58,6 +62,7 @@ class UsesResponsiveImagesSnapshot extends Audit { const usedPixels = displayed.width * displayed.height; if (actualPixels <= usedPixels) continue; + if (actualPixels - usedPixels > IGNORE_THRESHOLD_IN_PIXELS) score = 0; items.push({ url: URL.elideDataURI(image.src), @@ -79,7 +84,7 @@ class UsesResponsiveImagesSnapshot extends Audit { const details = Audit.makeTableDetails(headings, items); return { - score: items.length ? 0 : 1, + score, details, }; } diff --git a/lighthouse-core/fraggle-rock/config/default-config.js b/lighthouse-core/fraggle-rock/config/default-config.js index dbbeca99df0e..3ba94899047b 100644 --- a/lighthouse-core/fraggle-rock/config/default-config.js +++ b/lighthouse-core/fraggle-rock/config/default-config.js @@ -23,9 +23,9 @@ const frCategoryAuditRefExtensions = { function mergeCategories() { if (!legacyDefaultConfig.categories) return {}; const categories = legacyDefaultConfig.categories; - for (const key of Object.keys(frCategories)) { + for (const key of Object.keys(frCategoryAuditRefExtensions)) { if (!categories[key]) continue; - categories[key].auditRefs.push(...frCategories[key]); + categories[key].auditRefs.push(...frCategoryAuditRefExtensions[key]); } return categories; }