From 97c38ddf37e01f05b47d554a275e6e57b537fa18 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Fri, 14 Apr 2017 19:54:42 -0700 Subject: [PATCH 01/10] table basics --- .../byte-efficiency/byte-efficiency-audit.js | 3 +- .../uses-request-compression.js | 18 ++++++++ .../report/v2/renderer/details-renderer.js | 44 +++++++++++++++++++ lighthouse-core/report/v2/renderer/dom.js | 22 ++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 33e4764c8c28..1d67b0bda586 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -105,7 +105,8 @@ class UnusedBytes extends Audit { extendedInfo: { formatter: Formatter.SUPPORTED_FORMATS.TABLE, value: {results, tableHeadings: result.tableHeadings} - } + }, + details: result.details }; } diff --git a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js index 8ec7df151161..351375826ced 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js +++ b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js @@ -89,6 +89,14 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { }); }); + const tableItems = results.map(result => { + return [ + {type: 'url', text: result.url}, + {type: 'text', text: result.totalBytes}, + {type: 'text', text: result.potentialSavings}, + ]; + }); + let debugString; return { passes: totalWastedBytes < TOTAL_WASTED_BYTES_THRESHOLD, @@ -98,6 +106,16 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { url: 'Uncompressed resource URL', totalKb: 'Original', potentialSavings: 'GZIP Savings', + }, + + details: { + type: 'table', + header: [ + {type: 'text', text: 'Uncompressed resource URL'}, + {type: 'text', text: 'Original'}, + {type: 'text', text: 'GZIP Savings'}, + ], + items: tableItems } }; } diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index 98c9375d17d5..c4ca864b037e 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -34,8 +34,14 @@ class DetailsRenderer { switch (details.type) { case 'text': return this._renderText(details); + case 'url': + return this._renderURL(details); + case 'block': + return this._renderBlock(details); case 'cards': return this._renderCards(/** @type {!DetailsRenderer.CardsDetailsJSON} */ (details)); + case 'table': + return this._renderTable(/** @type {!DetailsRenderer.TableDetailsJSON} */ (details)); case 'list': return this._renderList(/** @type {!DetailsRenderer.ListDetailsJSON} */ (details)); default: @@ -53,6 +59,17 @@ class DetailsRenderer { return element; } + + /** + * @param {!DetailsRenderer.DetailsJSON} text + * @return {!Element} + */ + _renderURL(text) { + const element = this._dom._renderText(text); + element.classList.add('lh-text__url'); + return element; + } + /** * @param {!DetailsRenderer.ListDetailsJSON} list * @return {!Element} @@ -73,6 +90,33 @@ class DetailsRenderer { return element; } + + /** + * @param {!DetailsRenderer.TableDetailsJSON} details + * @return {!Element} + */ + _renderTable(details) { + const element = this._dom.createElement('details', 'lh-details', {open: true}); + if (details.header) { + element.appendChild(this._dom.createElement('summary')).textContent = 'View details'; + } + + const tableElem = element.createChild('table', 'lh-table lh-table__multicolumn'); + const theadTrElem = tableElem.createChild('thead').createChild('tr'); + for (const heading of details.header) { + theadTrElem.createChild('th').appendChild(this.render(heading)); + } + + const tbodyElem = tableElem.createChild('tbody'); + for (const row of details.items) { + const rowElem = tbodyElem.createChild('tr'); + for (const columnItem of row) { + rowElem.createChild('td').appendChild(this.render(columnItem)); + } + } + return element; + } + /** * @param {!DetailsRenderer.CardsDetailsJSON} details * @return {!Element} diff --git a/lighthouse-core/report/v2/renderer/dom.js b/lighthouse-core/report/v2/renderer/dom.js index 412087a94d5e..cc4f9a838cf7 100644 --- a/lighthouse-core/report/v2/renderer/dom.js +++ b/lighthouse-core/report/v2/renderer/dom.js @@ -24,6 +24,28 @@ class DOM { constructor(document) { /** @private {!Document} */ this._document = document; + + this._registerDOMExtensions(); + } + + _registerDOMExtensions() { + if (typeof self === 'undefined' || !self.Element) return; + if (self.Element.prototype.createChild) return; + + const instance = this; + /** + * From devtools' DOMExtension.js, except without 3rd customElementType param + * @param {string} elementName + * @param {string=} className + * @return {!Element} + */ + self.Element.prototype.createChild = function(elementName, className) { + const element = instance.createElement(elementName, className); + this.appendChild(element); + return element; + }; + + self.DocumentFragment.prototype.createChild = self.Element.prototype.createChild; } /** From d7dac3aa07e9b4391149c2d2f05f8676c1ed68fd Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Fri, 14 Apr 2017 20:54:42 -0700 Subject: [PATCH 02/10] optimized images workin. --- .../byte-efficiency/byte-efficiency-audit.js | 20 ++++++++++- .../byte-efficiency/uses-optimized-images.js | 7 ++++ .../uses-request-compression.js | 21 +++--------- .../report/v2/renderer/details-renderer.js | 34 ++++++++++++++++--- lighthouse-core/report/v2/report-styles.css | 15 ++++++++ 5 files changed, 75 insertions(+), 22 deletions(-) diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 1d67b0bda586..0113a7ed630f 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -96,6 +96,16 @@ class UnusedBytes extends Audit { displayValue = `Potential savings of ${wastedKbDisplay} (~${wastedMsDisplay})`; } + const headingKeys = Object.keys(result.headings); + const tableRows = results.map(item => { + return headingKeys.map(key => { + return { + type: result.headings[key].itemType, + text: item[key] + }; + }); + }); + return { debugString, displayValue, @@ -106,7 +116,15 @@ class UnusedBytes extends Audit { formatter: Formatter.SUPPORTED_FORMATS.TABLE, value: {results, tableHeadings: result.tableHeadings} }, - details: result.details + details: { + type: 'table', + header: 'whats up', + itemHeaders: Object.keys(result.headings).map(key => ({ + type: 'text', + text: result.headings[key].text + })), + items: tableRows + } }; } diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js index c17ce71cb8f5..82773e0698b2 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js @@ -131,6 +131,13 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { totalKb: 'Original', webpSavings: 'WebP Savings', jpegSavings: 'JPEG Savings', + }, + headings: { + preview: {itemType: 'thumbnail', text: ''}, + url: {itemType: 'url', text: 'URL'}, + totalKb: {itemType: 'text', text: 'Original'}, + webpSavings: {itemType: 'text', text: 'Savings as WebP'}, + jpegSavings: {itemType: 'text', text: 'Savings as JPEG'}, } }; } diff --git a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js index 351375826ced..86ca943a2f7f 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js +++ b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js @@ -89,14 +89,6 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { }); }); - const tableItems = results.map(result => { - return [ - {type: 'url', text: result.url}, - {type: 'text', text: result.totalBytes}, - {type: 'text', text: result.potentialSavings}, - ]; - }); - let debugString; return { passes: totalWastedBytes < TOTAL_WASTED_BYTES_THRESHOLD, @@ -107,15 +99,10 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { totalKb: 'Original', potentialSavings: 'GZIP Savings', }, - - details: { - type: 'table', - header: [ - {type: 'text', text: 'Uncompressed resource URL'}, - {type: 'text', text: 'Original'}, - {type: 'text', text: 'GZIP Savings'}, - ], - items: tableItems + headings: { + url: {itemType: 'url', text: 'Uncompressed resource URL'}, + totalKb: {itemType: 'text', text: 'Original'}, + potentialSavings: {itemType: 'text', text: 'GZIP Savings'}, } }; } diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index c4ca864b037e..f062594ba5aa 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -36,6 +36,8 @@ class DetailsRenderer { return this._renderText(details); case 'url': return this._renderURL(details); + case 'thumbnail': + return this._renderThumbnail(details); case 'block': return this._renderBlock(details); case 'cards': @@ -59,17 +61,41 @@ class DetailsRenderer { return element; } - /** * @param {!DetailsRenderer.DetailsJSON} text * @return {!Element} */ _renderURL(text) { - const element = this._dom._renderText(text); + const element = this._renderText(text); element.classList.add('lh-text__url'); return element; } + /** + * @param {!DetailsRenderer.DetailsJSON} obj + * @return {!Element} + */ + _renderThumbnail(obj) { + const element = this._dom.createElement('img', 'lh-thumbnail'); + element.src = obj.text.url; + // ignore obj.text.mimeType + element.alt = 'Image preview'; + return element; + } + + /** + * @param {!DetailsRenderer.DetailsJSON} block + * @return {!Element} + */ + _renderBlock(block) { + const element = this._dom.createElement('div', 'lh-block'); + const items = block.items || []; + for (const item of items) { + element.appendChild(this.render(item)); + } + return element; + } + /** * @param {!DetailsRenderer.ListDetailsJSON} list * @return {!Element} @@ -98,12 +124,12 @@ class DetailsRenderer { _renderTable(details) { const element = this._dom.createElement('details', 'lh-details', {open: true}); if (details.header) { - element.appendChild(this._dom.createElement('summary')).textContent = 'View details'; + element.appendChild(this._dom.createElement('summary')).textContent = details.header || 'SUP'; } const tableElem = element.createChild('table', 'lh-table lh-table__multicolumn'); const theadTrElem = tableElem.createChild('thead').createChild('tr'); - for (const heading of details.header) { + for (const heading of details.itemHeaders) { theadTrElem.createChild('th').appendChild(this.render(heading)); } diff --git a/lighthouse-core/report/v2/report-styles.css b/lighthouse-core/report/v2/report-styles.css index d0d7277db2da..369ecc63fe2b 100644 --- a/lighthouse-core/report/v2/report-styles.css +++ b/lighthouse-core/report/v2/report-styles.css @@ -487,4 +487,19 @@ summary.lh-passed-audits-summary::-webkit-details-marker { } } + +.lh-table { + --image-preview: 24px; +} + +.lh-thumbnail img { + height: var(--image-preview); + width: var(--image-preview); + object-fit: contain; +} + +.lh-thumbnail { + width: calc(var(--image-preview) * 2); +} + /*# sourceURL=report.styles.css */ From a9ad5ca977a6418b710e8f254229ccb022a79721 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Fri, 14 Apr 2017 22:02:46 -0700 Subject: [PATCH 03/10] move table creation helpers into Audit --- lighthouse-core/audits/audit.js | 68 +++++++++++++++++++ .../byte-efficiency/byte-efficiency-audit.js | 23 ++----- .../byte-efficiency/offscreen-images.js | 15 ++-- .../byte-efficiency/total-byte-weight.js | 20 ++++-- .../byte-efficiency/unused-css-rules.js | 15 ++-- .../byte-efficiency/uses-optimized-images.js | 23 +++---- .../uses-request-compression.js | 17 ++--- .../byte-efficiency/uses-responsive-images.js | 17 +++-- .../dobetterweb/link-blocking-first-paint.js | 18 +++-- lighthouse-core/report/v2/report-styles.css | 6 +- 10 files changed, 142 insertions(+), 80 deletions(-) diff --git a/lighthouse-core/audits/audit.js b/lighthouse-core/audits/audit.js index fb6708086bf7..85ec71b888e0 100644 --- a/lighthouse-core/audits/audit.js +++ b/lighthouse-core/audits/audit.js @@ -56,6 +56,61 @@ class Audit { }); } + /** + * @param {!Audit.Headings} headings + * @return {*} + */ + static makeV1TableHeadings(headings) { + const tableHeadings = {}; + headings.forEach(heading => tableHeadings[heading.key] = heading.text); + return tableHeadings; + } + + /** + * @param {!Audit.Headings} headings + * @param {!Object} results + * @return {!Array} + */ + static makeV2TableRows(headings, results) { + const tableRows = results.map(item => { + return headings.map(heading => { + return { + type: heading.itemType, + text: item[heading.key] + }; + }); + }); + return tableRows; + } + + /** + * @param {!Audit.Headings} headings + * @return {!Array} + */ + static makeV2TableHeaders(headings) { + return headings.map(heading => ({ + type: 'text', + text: heading.text + })); + } + + /** + * @param {!Audit.Headings} headings + * @param {!Object} results + * @return {!DetailsRenderer.DetailsJSON} + */ + static makeV2TableDetails(headings, results) { + const v2TableHeaders = Audit.makeV2TableHeaders(headings); + const v2TableRows = Audit.makeV2TableRows(headings, results); + return { + type: 'table', + header: 'View Details', + itemHeaders: v2TableHeaders, + items: v2TableRows + }; + } + + /** * @param {!Audit} audit * @param {!AuditResult} result @@ -97,3 +152,16 @@ class Audit { } module.exports = Audit; + + +/** @typedef {{ + * key: string, + * itemType: string, + * text: string, + * }} + */ +Audit.Heading; // eslint-disable-line no-unused-expressions + + +/** @typedef {!Array} */ +Audit.Headings; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 0113a7ed630f..3d2825b96b94 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -96,15 +96,8 @@ class UnusedBytes extends Audit { displayValue = `Potential savings of ${wastedKbDisplay} (~${wastedMsDisplay})`; } - const headingKeys = Object.keys(result.headings); - const tableRows = results.map(item => { - return headingKeys.map(key => { - return { - type: result.headings[key].itemType, - text: item[key] - }; - }); - }); + const v1TableHeadings = Audit.makeV1TableHeadings(result.headings); + const v2TableDetails = Audit.makeV2TableDetails(result.headings, results); return { debugString, @@ -114,17 +107,9 @@ class UnusedBytes extends Audit { !!result.passes, extendedInfo: { formatter: Formatter.SUPPORTED_FORMATS.TABLE, - value: {results, tableHeadings: result.tableHeadings} + value: {results, tableHeadings: v1TableHeadings} }, - details: { - type: 'table', - header: 'whats up', - itemHeaders: Object.keys(result.headings).map(key => ({ - type: 'text', - text: result.headings[key].text - })), - items: tableRows - } + details: v2TableDetails }; } diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js index 9595384e8608..a78e820bcae5 100644 --- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js +++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js @@ -132,15 +132,18 @@ class OffscreenImages extends ByteEfficiencyAudit { const loadedEarly = item.requestStartTime < ttiTimestamp; return isWasteful && loadedEarly; }); + + const headings = [ + {key: 'preview', itemType: 'thumbnail', text: ''}, + {key: 'url', itemType: 'url', text: 'URL'}, + {key: 'totalKb', itemType: 'text', text: 'Original'}, + {key: 'potentialSavings', itemType: 'text', text: 'Potential Savings'}, + ]; + return { debugString, results, - tableHeadings: { - preview: '', - url: 'URL', - totalKb: 'Original', - potentialSavings: 'Potential Savings', - } + headings, }; }); } diff --git a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js index 51cdb3cdade4..fe57831fe88a 100644 --- a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js +++ b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js @@ -70,7 +70,7 @@ class TotalByteWeight extends ByteEfficiencyAudit { totalMs: this.bytesToMsString(record.transferSize, networkThroughput), }; - totalBytes += result.totalBytes; + totalBytes += totalBytes; prev.push(result); return prev; }, []).sort((itemA, itemB) => itemB.totalBytes - itemA.totalBytes).slice(0, 10); @@ -84,6 +84,15 @@ class TotalByteWeight extends ByteEfficiencyAudit { SCORING_MEDIAN, SCORING_POINT_OF_DIMINISHING_RETURNS); const score = 100 * distribution.computeComplementaryPercentile(totalBytes); + const headings = [ + {key: 'url', itemType: 'url', text: 'URL'}, + {key: 'totalKb', itemType: 'text', text: 'Total Size'}, + {key: 'totalMs', itemType: 'text', text: 'Transfer Time'}, + ]; + + const v1TableHeadings = Audit.makeV1TableHeadings(headings); + const v2TableDetails = Audit.makeV2TableDetails(headings, results); + return { rawValue: totalBytes, optimalValue: this.meta.optimalValue, @@ -93,13 +102,10 @@ class TotalByteWeight extends ByteEfficiencyAudit { formatter: Formatter.SUPPORTED_FORMATS.TABLE, value: { results, - tableHeadings: { - url: 'URL', - totalKb: 'Total Size', - totalMs: 'Transfer Time', - } + tableHeadings: v1TableHeadings } - } + }, + details: v2TableDetails }; }); } diff --git a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js index 6fa3aa5ed46f..d6cbbfe37f2a 100644 --- a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +++ b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js @@ -179,14 +179,17 @@ class UnusedCSSRules extends ByteEfficiencyAudit { return UnusedCSSRules.mapSheetToResult(indexedSheets[sheetId], pageUrl); }).filter(sheet => sheet && sheet.wastedBytes > 1024); + + const headings = [ + {key: 'url', itemType: 'url', text: 'URL'}, + {key: 'numUnused', itemType: 'url', text: 'Unused Rules'}, + {key: 'totalKb', itemType: 'text', text: 'Original'}, + {key: 'potentialSavings', itemType: 'text', text: 'Potential Savings'}, + ]; + return { results, - tableHeadings: { - url: 'URL', - numUnused: 'Unused Rules', - totalKb: 'Original', - potentialSavings: 'Potential Savings', - } + headings }; } } diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js index 82773e0698b2..05e05c8970fc 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js @@ -121,24 +121,19 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { debugString = `Lighthouse was unable to decode some of your images: ${urls.join(', ')}`; } + const headings = [ + {key: 'preview', itemType: 'thumbnail', text: ''}, + {key: 'url', itemType: 'url', text: 'URL'}, + {key: 'totalKb', itemType: 'text', text: 'Original'}, + {key: 'webpSavings', itemType: 'text', text: 'Savings as WebP'}, + {key: 'jpegSavings', itemType: 'text', text: 'Savings as JPEG'}, + ]; + return { passes: hasAllEfficientImages && totalWastedBytes < TOTAL_WASTED_BYTES_THRESHOLD, debugString, results, - tableHeadings: { - preview: '', - url: 'URL', - totalKb: 'Original', - webpSavings: 'WebP Savings', - jpegSavings: 'JPEG Savings', - }, - headings: { - preview: {itemType: 'thumbnail', text: ''}, - url: {itemType: 'url', text: 'URL'}, - totalKb: {itemType: 'text', text: 'Original'}, - webpSavings: {itemType: 'text', text: 'Savings as WebP'}, - jpegSavings: {itemType: 'text', text: 'Savings as JPEG'}, - } + headings }; } } diff --git a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js index 86ca943a2f7f..adefab7c81eb 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js +++ b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js @@ -90,20 +90,17 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { }); let debugString; + const headings = [ + {key: 'url', itemType: 'url', text: 'Uncompressed resource URL'}, + {key: 'totalKb', itemType: 'text', text: 'Original'}, + {key: 'potentialSavings', itemType: 'text', text: 'GZIP Savings'}, + ]; + return { passes: totalWastedBytes < TOTAL_WASTED_BYTES_THRESHOLD, debugString, results, - tableHeadings: { - url: 'Uncompressed resource URL', - totalKb: 'Original', - potentialSavings: 'GZIP Savings', - }, - headings: { - url: {itemType: 'url', text: 'Uncompressed resource URL'}, - totalKb: {itemType: 'text', text: 'Original'}, - potentialSavings: {itemType: 'text', text: 'GZIP Savings'}, - } + headings, }; } } diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index e82b4f54ad35..af0a0b667f8d 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -110,17 +110,20 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { }, new Map()); const results = Array.from(resultsMap.values()) - .filter(item => item.wastedBytes > IGNORE_THRESHOLD_IN_BYTES); + .filter(item => item.wastedBytes > IGNORE_THRESHOLD_IN_BYTES); + + const headings = [ + {key: 'preview', itemType: 'thumbnail', text: ''}, + {key: 'url', itemType: 'url', text: 'URL'}, + {key: 'totalKb', itemType: 'text', text: 'Original'}, + {key: 'potentialSavings', itemType: 'text', text: 'Potential Savings'}, + ]; + return { debugString, passes: !results.find(item => item.isWasteful), results, - tableHeadings: { - preview: '', - url: 'URL', - totalKb: 'Original', - potentialSavings: 'Potential Savings', - } + headings, }; } } diff --git a/lighthouse-core/audits/dobetterweb/link-blocking-first-paint.js b/lighthouse-core/audits/dobetterweb/link-blocking-first-paint.js index d1b0ff65e4d6..6557212057cd 100644 --- a/lighthouse-core/audits/dobetterweb/link-blocking-first-paint.js +++ b/lighthouse-core/audits/dobetterweb/link-blocking-first-paint.js @@ -85,6 +85,15 @@ class LinkBlockingFirstPaintAudit extends Audit { displayValue = `${results.length} resource delayed first paint by ${delayTime}ms`; } + const headings = [ + {key: 'url', itemType: 'url', text: 'URL'}, + {key: 'totalKb', itemType: 'text', text: 'Size (KB)'}, + {key: 'totalMs', itemType: 'text', text: 'Delayed Paint By (ms)'}, + ]; + + const v1TableHeadings = Audit.makeV1TableHeadings(headings); + const v2TableDetails = Audit.makeV2TableDetails(headings, results); + return { displayValue, rawValue: results.length === 0, @@ -92,13 +101,10 @@ class LinkBlockingFirstPaintAudit extends Audit { formatter: Formatter.SUPPORTED_FORMATS.TABLE, value: { results, - tableHeadings: { - url: 'URL', - totalKb: 'Size (KB)', - totalMs: 'Delayed Paint By (ms)' - } + tableHeadings: v1TableHeadings } - } + }, + details: v2TableDetails }; } diff --git a/lighthouse-core/report/v2/report-styles.css b/lighthouse-core/report/v2/report-styles.css index 369ecc63fe2b..f6569af9619d 100644 --- a/lighthouse-core/report/v2/report-styles.css +++ b/lighthouse-core/report/v2/report-styles.css @@ -492,14 +492,10 @@ summary.lh-passed-audits-summary::-webkit-details-marker { --image-preview: 24px; } -.lh-thumbnail img { +img.lh-thumbnail { height: var(--image-preview); width: var(--image-preview); object-fit: contain; } -.lh-thumbnail { - width: calc(var(--image-preview) * 2); -} - /*# sourceURL=report.styles.css */ From 6a3ca902be69048e36d130c1a85a8262ad586371 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Fri, 14 Apr 2017 22:13:41 -0700 Subject: [PATCH 04/10] fix existing tests. --- lighthouse-core/audits/audit.js | 3 --- .../audits/byte-efficiency/total-byte-weight.js | 2 +- .../byte-efficiency/uses-responsive-images.js | 2 +- .../report/v2/renderer/details-renderer.js | 2 +- lighthouse-core/report/v2/renderer/dom.js | 12 +++++++----- .../byte-efficiency-audit-test.js | 16 ++++++++-------- .../uses-optimized-images-test.js | 6 +++--- 7 files changed, 21 insertions(+), 22 deletions(-) diff --git a/lighthouse-core/audits/audit.js b/lighthouse-core/audits/audit.js index 85ec71b888e0..4392ab91f288 100644 --- a/lighthouse-core/audits/audit.js +++ b/lighthouse-core/audits/audit.js @@ -110,7 +110,6 @@ class Audit { }; } - /** * @param {!Audit} audit * @param {!AuditResult} result @@ -153,7 +152,6 @@ class Audit { module.exports = Audit; - /** @typedef {{ * key: string, * itemType: string, @@ -162,6 +160,5 @@ module.exports = Audit; */ Audit.Heading; // eslint-disable-line no-unused-expressions - /** @typedef {!Array} */ Audit.Headings; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js index fe57831fe88a..27c4745e7f2e 100644 --- a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js +++ b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js @@ -70,7 +70,7 @@ class TotalByteWeight extends ByteEfficiencyAudit { totalMs: this.bytesToMsString(record.transferSize, networkThroughput), }; - totalBytes += totalBytes; + totalBytes += result.totalBytes; prev.push(result); return prev; }, []).sort((itemA, itemB) => itemB.totalBytes - itemA.totalBytes).slice(0, 10); diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index af0a0b667f8d..ff0ca0bb2642 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -110,7 +110,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { }, new Map()); const results = Array.from(resultsMap.values()) - .filter(item => item.wastedBytes > IGNORE_THRESHOLD_IN_BYTES); + .filter(item => item.wastedBytes > IGNORE_THRESHOLD_IN_BYTES); const headings = [ {key: 'preview', itemType: 'thumbnail', text: ''}, diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index f062594ba5aa..47b3b9c6c7e9 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -124,7 +124,7 @@ class DetailsRenderer { _renderTable(details) { const element = this._dom.createElement('details', 'lh-details', {open: true}); if (details.header) { - element.appendChild(this._dom.createElement('summary')).textContent = details.header || 'SUP'; + element.appendChild(this._dom.createElement('summary')).textContent = details.header; } const tableElem = element.createChild('table', 'lh-table lh-table__multicolumn'); diff --git a/lighthouse-core/report/v2/renderer/dom.js b/lighthouse-core/report/v2/renderer/dom.js index cc4f9a838cf7..a888623fa43b 100644 --- a/lighthouse-core/report/v2/renderer/dom.js +++ b/lighthouse-core/report/v2/renderer/dom.js @@ -25,16 +25,19 @@ class DOM { /** @private {!Document} */ this._document = document; - this._registerDOMExtensions(); + this._installDOMExtensions(); } - _registerDOMExtensions() { + /** + * Add some API sugar + */ + _installDOMExtensions() { if (typeof self === 'undefined' || !self.Element) return; if (self.Element.prototype.createChild) return; - const instance = this; + /** - * From devtools' DOMExtension.js, except without 3rd customElementType param + * From devtools' DOMExtension.js (minus the 3rd customElementType param) * @param {string} elementName * @param {string=} className * @return {!Element} @@ -44,7 +47,6 @@ class DOM { this.appendChild(element); return element; }; - self.DocumentFragment.prototype.createChild = self.Element.prototype.createChild; } diff --git a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js index 53a34ba12d1a..360eed1a882f 100644 --- a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js @@ -23,7 +23,7 @@ const assert = require('assert'); describe('Byte efficiency base audit', () => { it('should format as extendedInfo', () => { const result = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [], }); @@ -33,18 +33,18 @@ describe('Byte efficiency base audit', () => { it('should set the rawValue', () => { const goodResultInferred = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [{wastedBytes: 2345, totalBytes: 3000, wastedPercent: 65}], }); const badResultInferred = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [{wastedBytes: 45000, totalBytes: 45000, wastedPercent: 100}], }); const badResultExplicit = ByteEfficiencyAudit.createAuditResult({ passes: false, - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [{wastedBytes: 2345, totalBytes: 3000, wastedPercent: 65}], }); @@ -55,7 +55,7 @@ describe('Byte efficiency base audit', () => { it('should populate Kb', () => { const result = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [{wastedBytes: 2048, totalBytes: 4096, wastedPercent: 50}], }); @@ -65,7 +65,7 @@ describe('Byte efficiency base audit', () => { it('should populate Ms', () => { const result = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [{wastedBytes: 350, totalBytes: 700, wastedPercent: 50}], }, 1000); @@ -75,7 +75,7 @@ describe('Byte efficiency base audit', () => { it('should sort on wastedBytes', () => { const result = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [ {wastedBytes: 350, totalBytes: 700, wastedPercent: 50}, {wastedBytes: 450, totalBytes: 1000, wastedPercent: 50}, @@ -90,7 +90,7 @@ describe('Byte efficiency base audit', () => { it('should create a display value', () => { const result = ByteEfficiencyAudit.createAuditResult({ - tableHeadings: {value: 'Label'}, + headings: [{key: 'value', text: 'Label'}], results: [ {wastedBytes: 512, totalBytes: 700, wastedPercent: 50}, {wastedBytes: 512, totalBytes: 1000, wastedPercent: 50}, diff --git a/lighthouse-core/test/audits/byte-efficiency/uses-optimized-images-test.js b/lighthouse-core/test/audits/byte-efficiency/uses-optimized-images-test.js index c3959d29ed24..3d0907c292ec 100644 --- a/lighthouse-core/test/audits/byte-efficiency/uses-optimized-images-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/uses-optimized-images-test.js @@ -67,10 +67,10 @@ describe('Page uses optimized images', () => { ], }); - const headings = auditResult.tableHeadings; + const headings = auditResult.headings.map(heading => heading.text); assert.equal(auditResult.passes, false); - assert.deepEqual(Object.keys(headings).map(key => headings[key]), - ['', 'URL', 'Original', 'WebP Savings', 'JPEG Savings'], + assert.deepEqual(headings, + ['', 'URL', 'Original', 'Savings as WebP', 'Savings as JPEG'], 'table headings are correct and in order'); }); From 531a4607f9f8875c1c6dfb4867cb8270b7cc818d Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Thu, 20 Apr 2017 21:46:36 -0700 Subject: [PATCH 05/10] some jsdoc feedback. --- lighthouse-core/audits/audit.js | 14 +++++++------- .../byte-efficiency/byte-efficiency-audit.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lighthouse-core/audits/audit.js b/lighthouse-core/audits/audit.js index 4392ab91f288..e7ac7d3495dc 100644 --- a/lighthouse-core/audits/audit.js +++ b/lighthouse-core/audits/audit.js @@ -57,8 +57,8 @@ class Audit { } /** - * @param {!Audit.Headings} headings - * @return {*} + * @param {!Audit.Headings} headings + * @return {!Array} */ static makeV1TableHeadings(headings) { const tableHeadings = {}; @@ -67,8 +67,8 @@ class Audit { } /** - * @param {!Audit.Headings} headings - * @param {!Object} results + * @param {!Audit.Headings} headings + * @param {!Array>} results * @return {!Array} */ static makeV2TableRows(headings, results) { @@ -84,7 +84,7 @@ class Audit { } /** - * @param {!Audit.Headings} headings + * @param {!Audit.Headings} headings * @return {!Array} */ static makeV2TableHeaders(headings) { @@ -95,8 +95,8 @@ class Audit { } /** - * @param {!Audit.Headings} headings - * @param {!Object} results + * @param {!Audit.Headings} headings + * @param {!Array>} results * @return {!DetailsRenderer.DetailsJSON} */ static makeV2TableDetails(headings, results) { diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 3d2825b96b94..4db5027e5f72 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -70,7 +70,7 @@ class UnusedBytes extends Audit { /** * @param {!{debugString: string=, passes: boolean=, tableHeadings: !Object, - * results: !Array}} result + * results: !Array>}} result * @param {number} networkThroughput * @return {!AuditResult} */ From 6505c697bdadfdc0d06d52ed8ef31cc9aa6f515c Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Mon, 1 May 2017 15:28:08 -0700 Subject: [PATCH 06/10] feedback --- lighthouse-core/audits/audit.js | 7 +++- .../byte-efficiency/byte-efficiency-audit.js | 2 +- .../byte-efficiency/offscreen-images.js | 3 +- .../byte-efficiency/total-byte-weight.js | 4 +- .../byte-efficiency/unused-css-rules.js | 3 +- .../byte-efficiency/uses-optimized-images.js | 3 +- .../uses-request-compression.js | 2 +- .../byte-efficiency/uses-responsive-images.js | 3 +- .../report/v2/renderer/details-renderer.js | 37 ++++++++++++++---- lighthouse-core/report/v2/renderer/dom.js | 39 +++++++------------ lighthouse-core/report/v2/report-styles.css | 3 +- 11 files changed, 59 insertions(+), 47 deletions(-) diff --git a/lighthouse-core/audits/audit.js b/lighthouse-core/audits/audit.js index e7ac7d3495dc..b978d319aebc 100644 --- a/lighthouse-core/audits/audit.js +++ b/lighthouse-core/audits/audit.js @@ -67,6 +67,8 @@ class Audit { } /** + * Table cells will use the type specified in headings[x].itemType. However a custom type + * can be provided: results[x].propName = {type: 'code', text: '...'} * @param {!Audit.Headings} headings * @param {!Array>} results * @return {!Array} @@ -74,9 +76,12 @@ class Audit { static makeV2TableRows(headings, results) { const tableRows = results.map(item => { return headings.map(heading => { + const value = item[heading.key]; + if (typeof value === 'object' && value.type) return value; + return { type: heading.itemType, - text: item[heading.key] + text: value[heading.key] }; }); }); diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 4db5027e5f72..331b05506124 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -69,7 +69,7 @@ class UnusedBytes extends Audit { } /** - * @param {!{debugString: string=, passes: boolean=, tableHeadings: !Object, + * @param {!{debugString: string=, passes: boolean=, headings: !Audit.Headings, * results: !Array>}} result * @param {number} networkThroughput * @return {!AuditResult} diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js index a78e820bcae5..6a069ed4309b 100644 --- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js +++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js @@ -95,8 +95,7 @@ class OffscreenImages extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, tableHeadings: Object, - * passes: boolean=, debugString: string=}} + * @return {{results: !Array, headings: !Audit.Headings, debugString: string=}} */ static audit_(artifacts) { const images = artifacts.ImageUsage; diff --git a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js index 27c4745e7f2e..b8413bc3c5a9 100644 --- a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js +++ b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js @@ -90,8 +90,8 @@ class TotalByteWeight extends ByteEfficiencyAudit { {key: 'totalMs', itemType: 'text', text: 'Transfer Time'}, ]; - const v1TableHeadings = Audit.makeV1TableHeadings(headings); - const v2TableDetails = Audit.makeV2TableDetails(headings, results); + const v1TableHeadings = ByteEfficiencyAudit.makeV1TableHeadings(headings); + const v2TableDetails = ByteEfficiencyAudit.makeV2TableDetails(headings, results); return { rawValue: totalBytes, diff --git a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js index d6cbbfe37f2a..61900da386d1 100644 --- a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +++ b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js @@ -164,8 +164,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, tableHeadings: Object, - * passes: boolean=, debugString: string=}} + * @return {{results: !Array, headings: !Audit.Headings}} */ static audit_(artifacts) { const styles = artifacts.Styles; diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js index 05e05c8970fc..323f4300df49 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js @@ -63,8 +63,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, tableHeadings: Object, - * passes: boolean=, debugString: string=}} + * @return {{results: !Array, headings: !Audit.Headings, passes: boolean=, debugString: string=}} */ static audit_(artifacts) { const images = artifacts.OptimizedImages; diff --git a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js index adefab7c81eb..2b43cd0fd0b0 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js +++ b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js @@ -47,7 +47,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts * @param {number} networkThroughput - * @return {!AuditResult} + * @return {{results: !Array, passes: boolean=, headings: !Audit.Headings, debugString: string=}} */ static audit_(artifacts) { const uncompressedResponses = artifacts.ResponseCompression; diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index ff0ca0bb2642..4325818bcaf4 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -80,8 +80,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, tableHeadings: Object, - * passes: boolean=, debugString: string=}} + * @return {{results: !Array, headings: !Audit.Headings, passes: boolean=, debugString: string=}} */ static audit_(artifacts) { const images = artifacts.ImageUsage; diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index 47b3b9c6c7e9..02ffb2b11906 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -72,7 +72,7 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.DetailsJSON} obj + * @param {!DetailsRenderer.ThumbnailDetails} obj * @return {!Element} */ _renderThumbnail(obj) { @@ -80,6 +80,7 @@ class DetailsRenderer { element.src = obj.text.url; // ignore obj.text.mimeType element.alt = 'Image preview'; + element.title = obj.text.url; return element; } @@ -122,22 +123,24 @@ class DetailsRenderer { * @return {!Element} */ _renderTable(details) { - const element = this._dom.createElement('details', 'lh-details', {open: true}); + const element = this._dom.createElement('details', 'lh-details'); if (details.header) { element.appendChild(this._dom.createElement('summary')).textContent = details.header; } - const tableElem = element.createChild('table', 'lh-table lh-table__multicolumn'); - const theadTrElem = tableElem.createChild('thead').createChild('tr'); + const tableElem = this._dom.createChildOf(element, 'table', 'lh-table'); + const theadElem = this._dom.createChildOf(tableElem, 'thead'); + const theadTrElem = this._dom.createChildOf(theadElem, 'tr'); + for (const heading of details.itemHeaders) { - theadTrElem.createChild('th').appendChild(this.render(heading)); + this._dom.createChildOf(theadTrElem, 'th').appendChild(this.render(heading)); } - const tbodyElem = tableElem.createChild('tbody'); + const tbodyElem = this._dom.createChildOf(tableElem, 'tbody'); for (const row of details.items) { - const rowElem = tbodyElem.createChild('tr'); + const rowElem = this._dom.createChildOf(tbodyElem, 'tr'); for (const columnItem of row) { - rowElem.createChild('td').appendChild(this.render(columnItem)); + this._dom.createChildOf(rowElem, 'td').appendChild(this.render(columnItem)); } } return element; @@ -204,3 +207,21 @@ DetailsRenderer.ListDetailsJSON; // eslint-disable-line no-unused-expressions * }} */ DetailsRenderer.CardsDetailsJSON; // eslint-disable-line no-unused-expressions + +/** @typedef {{ + * type: string, + * header: ({text: string}|undefined), + * items: !Array, + * itemHeaders: !Array + * }} + */ +DetailsRenderer.TableDetailsJSON; // eslint-disable-line no-unused-expressions + + +/** @typedef {{ + * type: string, + * url: ({text: string}|undefined), + * mimeType: ({text: string}|undefined) + * }} + */ +DetailsRenderer.ThumbnailDetails; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/report/v2/renderer/dom.js b/lighthouse-core/report/v2/renderer/dom.js index a888623fa43b..3b96b4b28460 100644 --- a/lighthouse-core/report/v2/renderer/dom.js +++ b/lighthouse-core/report/v2/renderer/dom.js @@ -24,30 +24,6 @@ class DOM { constructor(document) { /** @private {!Document} */ this._document = document; - - this._installDOMExtensions(); - } - - /** - * Add some API sugar - */ - _installDOMExtensions() { - if (typeof self === 'undefined' || !self.Element) return; - if (self.Element.prototype.createChild) return; - const instance = this; - - /** - * From devtools' DOMExtension.js (minus the 3rd customElementType param) - * @param {string} elementName - * @param {string=} className - * @return {!Element} - */ - self.Element.prototype.createChild = function(elementName, className) { - const element = instance.createElement(elementName, className); - this.appendChild(element); - return element; - }; - self.DocumentFragment.prototype.createChild = self.Element.prototype.createChild; } /** @@ -72,6 +48,21 @@ class DOM { return element; } + /** + * @param {!Element} parentElem + * @param {string} elementName + * @param {string=} className + * @param {!Object=} attrs Attribute key/val pairs. + * Note: if an attribute key has an undefined value, this method does not + * set the attribute on the node. + * @return {!Element} + */ + createChildOf(parentElem, elementName, className, attrs) { + const element = this.createElement(elementName, className, attrs); + parentElem.appendChild(element); + return element; + } + /** * @param {string} selector * @param {!Node} context diff --git a/lighthouse-core/report/v2/report-styles.css b/lighthouse-core/report/v2/report-styles.css index f6569af9619d..c3f54076cc7b 100644 --- a/lighthouse-core/report/v2/report-styles.css +++ b/lighthouse-core/report/v2/report-styles.css @@ -487,12 +487,11 @@ summary.lh-passed-audits-summary::-webkit-details-marker { } } - .lh-table { --image-preview: 24px; } -img.lh-thumbnail { +.lh-thumbnail { height: var(--image-preview); width: var(--image-preview); object-fit: contain; From 9a3e79fb434b04278a84e81142bd7d5833c026d7 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Mon, 1 May 2017 16:09:24 -0700 Subject: [PATCH 07/10] fix thumbnail rendering. --- lighthouse-core/audits/audit.js | 2 +- .../audits/byte-efficiency/offscreen-images.js | 1 + .../byte-efficiency/uses-optimized-images.js | 2 +- .../byte-efficiency/uses-responsive-images.js | 1 + .../report/v2/renderer/details-renderer.js | 17 +++++++++++------ lighthouse-core/report/v2/renderer/dom.js | 7 +++++++ 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lighthouse-core/audits/audit.js b/lighthouse-core/audits/audit.js index b978d319aebc..fce625e1e215 100644 --- a/lighthouse-core/audits/audit.js +++ b/lighthouse-core/audits/audit.js @@ -81,7 +81,7 @@ class Audit { return { type: heading.itemType, - text: value[heading.key] + text: value }; }); }); diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js index 6a069ed4309b..b3b6907e5b8e 100644 --- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js +++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js @@ -83,6 +83,7 @@ class OffscreenImages extends ByteEfficiencyAudit { return { url, preview: { + type: 'thumbnail', url: image.networkRecord.url, mimeType: image.networkRecord.mimeType }, diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js index 323f4300df49..bac204a92bc6 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js @@ -105,7 +105,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { results.push({ url, isCrossOrigin: !image.isSameOrigin, - preview: {url: image.url, mimeType: image.mimeType}, + preview: {url: image.url, mimeType: image.mimeType, type: 'thumbnail'}, totalBytes: image.originalSize, wastedBytes: webpSavings.bytes, webpSavings: this.toSavingsString(webpSavings.bytes, webpSavings.percent), diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index 4325818bcaf4..9bea788c1e37 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -68,6 +68,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { return { url, preview: { + type: 'thumbnail', url: image.networkRecord.url, mimeType: image.networkRecord.mimeType }, diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index 02ffb2b11906..507a377531b4 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -73,14 +73,17 @@ class DetailsRenderer { /** * @param {!DetailsRenderer.ThumbnailDetails} obj - * @return {!Element} + * @return {!DocumentFragment} */ - _renderThumbnail(obj) { + _renderThumbnail(value) { + if (/^image/.test(value.mimeType) === false) { + return this.dom.createDocumentFragment(); + } + const element = this._dom.createElement('img', 'lh-thumbnail'); - element.src = obj.text.url; - // ignore obj.text.mimeType + element.src = value.url; element.alt = 'Image preview'; - element.title = obj.text.url; + element.title = value.url; return element; } @@ -120,9 +123,11 @@ class DetailsRenderer { /** * @param {!DetailsRenderer.TableDetailsJSON} details - * @return {!Element} + * @return {!DocumentFragment} */ _renderTable(details) { + if (!details.items.length) return this._dom.createDocumentFragment(); + const element = this._dom.createElement('details', 'lh-details'); if (details.header) { element.appendChild(this._dom.createElement('summary')).textContent = details.header; diff --git a/lighthouse-core/report/v2/renderer/dom.js b/lighthouse-core/report/v2/renderer/dom.js index 3b96b4b28460..3647bd14943c 100644 --- a/lighthouse-core/report/v2/renderer/dom.js +++ b/lighthouse-core/report/v2/renderer/dom.js @@ -63,6 +63,13 @@ class DOM { return element; } + /** + * @return {!DocumentFragment} + */ + createDocumentFragment() { + return this._document.createDocumentFragment(); + } + /** * @param {string} selector * @param {!Node} context From 103fe620baf648eedf0d2d2fe095dd2d7650fe89 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Mon, 1 May 2017 16:42:53 -0700 Subject: [PATCH 08/10] fix closure --- .../report/v2/renderer/details-renderer.js | 27 +++++-------------- lighthouse-core/report/v2/renderer/dom.js | 7 ----- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index 507a377531b4..1c8a870e9457 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -38,8 +38,6 @@ class DetailsRenderer { return this._renderURL(details); case 'thumbnail': return this._renderThumbnail(details); - case 'block': - return this._renderBlock(details); case 'cards': return this._renderCards(/** @type {!DetailsRenderer.CardsDetailsJSON} */ (details)); case 'table': @@ -72,12 +70,12 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.ThumbnailDetails} obj - * @return {!DocumentFragment} + * @param {!DetailsRenderer.ThumbnailDetails} value + * @return {!Element} */ _renderThumbnail(value) { if (/^image/.test(value.mimeType) === false) { - return this.dom.createDocumentFragment(); + return this._dom.createElement('span'); } const element = this._dom.createElement('img', 'lh-thumbnail'); @@ -87,19 +85,6 @@ class DetailsRenderer { return element; } - /** - * @param {!DetailsRenderer.DetailsJSON} block - * @return {!Element} - */ - _renderBlock(block) { - const element = this._dom.createElement('div', 'lh-block'); - const items = block.items || []; - for (const item of items) { - element.appendChild(this.render(item)); - } - return element; - } - /** * @param {!DetailsRenderer.ListDetailsJSON} list * @return {!Element} @@ -123,10 +108,10 @@ class DetailsRenderer { /** * @param {!DetailsRenderer.TableDetailsJSON} details - * @return {!DocumentFragment} + * @return {!Element} */ _renderTable(details) { - if (!details.items.length) return this._dom.createDocumentFragment(); + if (!details.items.length) return this._dom.createElement('span'); const element = this._dom.createElement('details', 'lh-details'); if (details.header) { @@ -216,7 +201,7 @@ DetailsRenderer.CardsDetailsJSON; // eslint-disable-line no-unused-expressions /** @typedef {{ * type: string, * header: ({text: string}|undefined), - * items: !Array, + * items: !Array>, * itemHeaders: !Array * }} */ diff --git a/lighthouse-core/report/v2/renderer/dom.js b/lighthouse-core/report/v2/renderer/dom.js index 3647bd14943c..3b96b4b28460 100644 --- a/lighthouse-core/report/v2/renderer/dom.js +++ b/lighthouse-core/report/v2/renderer/dom.js @@ -63,13 +63,6 @@ class DOM { return element; } - /** - * @return {!DocumentFragment} - */ - createDocumentFragment() { - return this._document.createDocumentFragment(); - } - /** * @param {string} selector * @param {!Node} context From b2b18fe10c55b4c4abc4bf8d420f2e2e6f604b73 Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Tue, 2 May 2017 10:09:56 -0700 Subject: [PATCH 09/10] --image-preview-size --- lighthouse-core/report/v2/report-styles.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lighthouse-core/report/v2/report-styles.css b/lighthouse-core/report/v2/report-styles.css index c3f54076cc7b..a75854b03034 100644 --- a/lighthouse-core/report/v2/report-styles.css +++ b/lighthouse-core/report/v2/report-styles.css @@ -488,12 +488,12 @@ summary.lh-passed-audits-summary::-webkit-details-marker { } .lh-table { - --image-preview: 24px; + --image-preview-size: 24px; } .lh-thumbnail { - height: var(--image-preview); - width: var(--image-preview); + height: var(--image-preview-size); + width: var(--image-preview-size); object-fit: contain; } From f12c6a8c1bc143ff3030a219e1824402b1b2d08a Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Thu, 4 May 2017 13:58:31 -0700 Subject: [PATCH 10/10] feedback --- lighthouse-core/audits/audit.js | 23 ++++++++++++------- .../byte-efficiency/byte-efficiency-audit.js | 3 +-- .../byte-efficiency/offscreen-images.js | 2 +- .../byte-efficiency/uses-optimized-images.js | 2 +- .../uses-request-compression.js | 2 +- .../byte-efficiency/uses-responsive-images.js | 2 +- .../report/v2/renderer/details-renderer.js | 4 +++- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lighthouse-core/audits/audit.js b/lighthouse-core/audits/audit.js index fce625e1e215..29ba3cf200c1 100644 --- a/lighthouse-core/audits/audit.js +++ b/lighthouse-core/audits/audit.js @@ -70,7 +70,7 @@ class Audit { * Table cells will use the type specified in headings[x].itemType. However a custom type * can be provided: results[x].propName = {type: 'code', text: '...'} * @param {!Audit.Headings} headings - * @param {!Array>} results + * @param {!Array>} results * @return {!Array} */ static makeV2TableRows(headings, results) { @@ -157,13 +157,20 @@ class Audit { module.exports = Audit; +/** @typedef { + * !Array<{ + * key: string, + * itemType: string, + * text: string, + * }>} + */ +Audit.Headings; // eslint-disable-line no-unused-expressions + /** @typedef {{ - * key: string, - * itemType: string, - * text: string, + * results: !Array>, + * headings: !Audit.Headings, + * passes: boolean, + * debugString: (string|undefined) * }} */ -Audit.Heading; // eslint-disable-line no-unused-expressions - -/** @typedef {!Array} */ -Audit.Headings; // eslint-disable-line no-unused-expressions +Audit.HeadingsResult; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 331b05506124..d7263dfbf722 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -69,8 +69,7 @@ class UnusedBytes extends Audit { } /** - * @param {!{debugString: string=, passes: boolean=, headings: !Audit.Headings, - * results: !Array>}} result + * @param {!Audit.HeadingsResult} result * @param {number} networkThroughput * @return {!AuditResult} */ diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js index b3b6907e5b8e..6f2941d7e9aa 100644 --- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js +++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js @@ -96,7 +96,7 @@ class OffscreenImages extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, headings: !Audit.Headings, debugString: string=}} + * @return {!Audit.HeadingsResult} */ static audit_(artifacts) { const images = artifacts.ImageUsage; diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js index bac204a92bc6..ee45ce6db18c 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js @@ -63,7 +63,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, headings: !Audit.Headings, passes: boolean=, debugString: string=}} + * @return {!Audit.HeadingsResult} */ static audit_(artifacts) { const images = artifacts.OptimizedImages; diff --git a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js index 2b43cd0fd0b0..e9fa764602ef 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-request-compression.js +++ b/lighthouse-core/audits/byte-efficiency/uses-request-compression.js @@ -47,7 +47,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts * @param {number} networkThroughput - * @return {{results: !Array, passes: boolean=, headings: !Audit.Headings, debugString: string=}} + * @return {!Audit.HeadingsResult} */ static audit_(artifacts) { const uncompressedResponses = artifacts.ResponseCompression; diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index 9bea788c1e37..b53e56b3b530 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -81,7 +81,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { /** * @param {!Artifacts} artifacts - * @return {{results: !Array, headings: !Audit.Headings, passes: boolean=, debugString: string=}} + * @return {!Audit.HeadingsResult} */ static audit_(artifacts) { const images = artifacts.ImageUsage; diff --git a/lighthouse-core/report/v2/renderer/details-renderer.js b/lighthouse-core/report/v2/renderer/details-renderer.js index 1c8a870e9457..d11c48f7838f 100644 --- a/lighthouse-core/report/v2/renderer/details-renderer.js +++ b/lighthouse-core/report/v2/renderer/details-renderer.js @@ -70,6 +70,8 @@ class DetailsRenderer { } /** + * Create small thumbnail with scaled down image asset. + * If the supplied details doesn't have an image/* mimeType, then an empty span is returned. * @param {!DetailsRenderer.ThumbnailDetails} value * @return {!Element} */ @@ -80,7 +82,7 @@ class DetailsRenderer { const element = this._dom.createElement('img', 'lh-thumbnail'); element.src = value.url; - element.alt = 'Image preview'; + element.alt = ''; element.title = value.url; return element; }