Skip to content

Commit

Permalink
Add table list formatter for HTML audits (#1505)
Browse files Browse the repository at this point in the history
  • Loading branch information
ebidel authored and brendankenny committed Jan 25, 2017
1 parent 59581c7 commit e1fc9e0
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 27 deletions.
17 changes: 14 additions & 3 deletions lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ module.exports = [
score: false,
extendedInfo: {
value: {
length: 11
results: {
length: 11
}
}
}
},
Expand Down Expand Up @@ -127,12 +129,21 @@ module.exports = [
value: {
// Note: This would normally be 7 but M56 defaults document-level
// listeners to passive. See https://www.chromestatus.com/features/5093566007214080
length: 4
results: {
length: 4
}
}
}
},
'uses-optimized-images': {
score: false
score: false,
extendedInfo: {
value: {
results: {
length: 1
}
}
}
},
'uses-responsive-images': {
score: false,
Expand Down
9 changes: 6 additions & 3 deletions lighthouse-core/audits/dobetterweb/uses-http2.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class UsesHTTP2Audit extends Audit {
return sameHost && notH2;
}).map(record => {
return {
label: record.protocol,
protocol: record.protocol,
url: record.url // .url is a getter and not copied over for the assign.
};
});
Expand All @@ -74,8 +74,11 @@ class UsesHTTP2Audit extends Audit {
rawValue: resources.length === 0,
displayValue: displayValue,
extendedInfo: {
formatter: Formatter.SUPPORTED_FORMATS.URLLIST,
value: resources
formatter: Formatter.SUPPORTED_FORMATS.TABLE,
value: {
results: resources,
tableHeadings: {url: 'URL', protocol: 'Protocol'}
}
}
});
}
Expand Down
23 changes: 18 additions & 5 deletions lighthouse-core/audits/dobetterweb/uses-optimized-images.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,26 @@ class UsesOptimizedImages extends Audit {
const url = URL.getDisplayName(image.url);
const webpSavings = UsesOptimizedImages.computeSavings(image, 'webp');

let label = `${originalKb} KB total, webp savings: ${webpSavings.percent}%`;
if (webpSavings.bytes > WEBP_ALREADY_OPTIMIZED_THRESHOLD_IN_BYTES) {
hasAllEfficientImages = false;
}

let jpegSavingsLabel;
if (/(jpeg|bmp)/.test(image.mimeType)) {
const jpegSavings = UsesOptimizedImages.computeSavings(image, 'jpeg');
if (jpegSavings.bytes > 0) {
hasAllEfficientImages = false;
label += `, jpeg savings: ${jpegSavings.percent}%`;
jpegSavingsLabel = `${jpegSavings.percent}%`;
}
}

totalWastedBytes += webpSavings.bytes;
results.push({url, label});
results.push({
url,
total: `${originalKb} KB`,
webpSavings: `${webpSavings.percent}%`,
jpegSavings: jpegSavingsLabel
});
return results;
}, []);

Expand All @@ -123,8 +128,16 @@ class UsesOptimizedImages extends Audit {
debugString,
rawValue: hasAllEfficientImages && totalWastedBytes < TOTAL_WASTED_BYTES_THRESHOLD,
extendedInfo: {
formatter: Formatter.SUPPORTED_FORMATS.URLLIST,
value: results
formatter: Formatter.SUPPORTED_FORMATS.TABLE,
value: {
results,
tableHeadings: {
url: 'URL',
total: 'Original (KB)',
webpSavings: 'WebP savings',
jpegSavings: 'JPEG savings'
}
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ class PassiveEventsAudit extends Audit {
return PassiveEventsAudit.generateAuditResult({
rawValue: groupedResults.length === 0,
extendedInfo: {
formatter: Formatter.SUPPORTED_FORMATS.URLLIST,
value: groupedResults
formatter: Formatter.SUPPORTED_FORMATS.TABLE,
value: {
results: groupedResults,
tableHeadings: {url: 'URL', lineCol: 'Line/Col', type: 'Type', code: 'Snippet'}
}
},
debugString
});
Expand Down
1 change: 1 addition & 0 deletions lighthouse-core/formatters/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Formatter {
urllist: require('./url-list'),
null: require('./null-formatter'),
speedline: require('./speedline-formatter'),
table: require('./table'),
userTimings: require('./user-timings')
};
}
Expand Down
72 changes: 72 additions & 0 deletions lighthouse-core/formatters/partials/table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<style>
.table_list {
margin-top: 8px;
border: 1px solid #EBEBEB;
max-width: 100%;
}
.table_list th,
.table_list td {
overflow: auto;
}
.table_list th {
background-color: #eee;
padding: 15px 10px;
}
.table_list td {
padding: 10px;
}
.table_list th:first-of-type,
.table_list td:first-of-type {
min-width: 40%;
white-space: nowrap;
}
.table_list tr:nth-child(even) {
background-color: #fafafa;
}
.table_list tr:hover {
background-color: #fafafa;
}
.table_list code {
white-space: pre;
font-family: monospace;
}

.table_list.multicolumn {
display: flex;
flex-direction: column;
}
.table_list.multicolumn tr {
display: flex;
}
.table_list.multicolumn th,
.table_list.multicolumn td {
flex: 1;
}
</style>

{{#createTable tableHeadings results }}

{{#if rows}}
<details class="subitem__details">
<summary class="subitem__detail">More information</summary>
<table class="table_list {{#if_not_eq headings.length 2}}multicolumn{{/if_not_eq}}"
cellspacing="0">
<thead>
<tr>
{{#each headings}}<th>{{this}}</th>{{/each}}
</tr>
</thead>
<tbody>
{{#each rows}}
<tr>
{{#each cols}}
<td>{{ sanitize this}}</td>
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
</details>
{{/if}}

{{/createTable}}
108 changes: 108 additions & 0 deletions lighthouse-core/formatters/table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @license
* Copyright 2017 Google Inc. 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 Formatter = require('./formatter');
const path = require('path');
const fs = require('fs');
const html = fs.readFileSync(path.join(__dirname, 'partials/table.html'), 'utf8');

class Table extends Formatter {
static getFormatter(type) {
switch (type) {
case 'pretty':
return result => {
let output = '';

if (!result || !result.results || !result.tableHeadings) {
return output;
}

const table = Table.createTable(result.tableHeadings, result.results);

table.rows.forEach(row => {
output += ' ';
row.cols.forEach(col => {
// Omit code snippet cols.
if (col.startsWith('`') && col.endsWith('`')) {
return;
}
output += `${col} `;
});
output += '\n';
});
return output;
};

case 'html':
// Returns a handlebars string to be used by the Report.
return html;

default:
throw new Error('Unknown formatter type');
}
}

/**
* Preps a formatted table (headings/col vals) for output.
* @param {!Object<string>} headings for the table. The order of this
* object's key/value pairs determines the order of the HTML table headings.
* There is special handling for certain keys:
* code: wraps the value in ticks as a markdown code snippet.
* lineCol: combines the values for the line and col keys into a single
* value "line/col".
* All other values are passed through as is.
* @param {!Array<!Object>} results Audit results.
* @return {{headings: !Array<string>, rows: !Array<{cols: !Array<*>}>}}
*/
static createTable(headings, results) {
headings = headings || {};
results = results || [];

const headingKeys = Object.keys(headings);

const rows = results.map(result => {
const cols = headingKeys.map(key => {
switch (key) {
case 'code':
return '`' + result[key].trim() + '`';
case 'lineCol':
return `${result.line}:${result.col}`;
default:
return result[key];
}
});

return {cols};
});

headings = headingKeys.map(key => headings[key]);

return {headings, rows};
}

static getHelpers() {
return {
createTable(headings, results, opts) {
return opts.fn(Table.createTable(headings, results));
}
};
}
}

module.exports = Table;
4 changes: 2 additions & 2 deletions lighthouse-core/report/report-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class ReportGenerator {
// !value
Handlebars.registerHelper('not', value => !value);

// value == value2?
// value !== value2
Handlebars.registerHelper('if_not_eq', function(lhs, rhs, options) {
if (lhs !== rhs) {
// eslint-disable-next-line no-invalid-this
Expand Down Expand Up @@ -139,7 +139,7 @@ class ReportGenerator {
// Ignore fatal errors from marked js.
}

// The input str has been santized and transformed. Mark it as safe so
// The input str has been sanitized and transformed. Mark it as safe so
// handlebars renders the text as HTML.
return new Handlebars.SafeString(str);
});
Expand Down
6 changes: 5 additions & 1 deletion lighthouse-core/test/audits/dobetterweb/uses-http2-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ describe('Resources are fetched over http/2', () => {
});
assert.equal(auditResult.rawValue, false);
assert.ok(auditResult.displayValue.match('4 requests were not'));
assert.equal(auditResult.extendedInfo.value.length, 4);
assert.equal(auditResult.extendedInfo.value.results.length, 4);

const headings = auditResult.extendedInfo.value.tableHeadings;
assert.deepEqual(Object.keys(headings).map(key => headings[key]),
['URL', 'Protocol'], 'table headings are correct and in order');
});

it('displayValue is correct when only one resource fails', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ describe('Page uses optimized images', () => {
});

assert.equal(auditResult.rawValue, false);

const headings = auditResult.extendedInfo.value.tableHeadings;
assert.deepEqual(Object.keys(headings).map(key => headings[key]),
['URL', 'Original (KB)', 'WebP savings', 'JPEG savings'],
'table headings are correct and in order');
});

it('fails when one png image is highly unoptimized', () => {
Expand Down Expand Up @@ -103,7 +108,7 @@ describe('Page uses optimized images', () => {
OptimizedImages: [image],
});

const actualUrl = auditResult.extendedInfo.value[0].url;
const actualUrl = auditResult.extendedInfo.value.results[0].url;
assert.ok(actualUrl.length < image.url.length, `${actualUrl} >= ${image.url}`);
});

Expand Down
Loading

0 comments on commit e1fc9e0

Please sign in to comment.