From 8ad2b62d0e39955208d75efb04543bda354214c6 Mon Sep 17 00:00:00 2001 From: Matt Zeunert Date: Fri, 3 May 2019 22:50:00 +0100 Subject: [PATCH 01/55] tests: make update:sample-artifacts work for a single artifact type (#8802) --- CONTRIBUTING.md | 10 ++++++++ .../scripts/update-report-fixtures.js | 25 ++++++++++++++++--- .../test/results/artifacts/artifacts.json | 2 +- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9683b30894c5..bcc51735a94d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,6 +73,16 @@ Don't: If no reference doc exists yet, then you can use the `description` as a stopgap for explaining both why the audit is important and how to fix it. +## Updating sample artifacts and LHR JSON + +``` +yarn run update:sample-artifacts # update all artifacts +yarn run update:sample-artifacts ScriptElements # update just one artifact +yarn run update:sample-json # update sample LHR based on sample artifacts +``` + +When updating all artifacts, usually you'll need to revert changes to the `*.devtoolslog.json` and `*.trace.json` files and manually review changes to `artifacts.json` to make sure they are related to your work. + ## Tracking Errors We track our errors in the wild with Sentry. In general, do not worry about wrapping your audits or gatherers in try/catch blocks and reporting every error that could possibly occur; `lighthouse-core/runner.js` and `lighthouse-core/gather/gather-runner.js` already catch and report any errors that occur while running a gatherer or audit, including errors fatal to the entire run. However, there are some situations when you might want to explicitly handle an error and report it to Sentry or wrap it to avoid reporting. Generally, you can interact with Sentry simply by requiring the `lighthouse-core/lib/sentry.js` file and call its methods. The module exports a delegate that will correctly handle the error reporting based on the user's opt-in preference and will simply no-op if they haven't so you don't need to check. diff --git a/lighthouse-core/scripts/update-report-fixtures.js b/lighthouse-core/scripts/update-report-fixtures.js index 216a07bb8d9b..300941ab6aaf 100644 --- a/lighthouse-core/scripts/update-report-fixtures.js +++ b/lighthouse-core/scripts/update-report-fixtures.js @@ -7,6 +7,9 @@ const cli = require('../../lighthouse-cli/run.js'); const cliFlags = require('../../lighthouse-cli/cli-flags.js'); +const assetSaver = require('../lib/asset-saver.js'); + +const artifactPath = 'lighthouse-core/test/results/artifacts'; const {server} = require('../../lighthouse-cli/test/fixtures/static-server.js'); @@ -32,9 +35,10 @@ const budgetedConfig = { }; /** - * Update the report artifacts + * Update the report artifacts. If artifactName is set only that artifact will be updated. + * @param {keyof LH.Artifacts=} artifactName */ -async function update() { +async function update(artifactName) { // get an available port server.listen(0, 'localhost'); const port = await new Promise(res => server.on('listening', () => { @@ -43,15 +47,28 @@ async function update() { res(address.port); })); + const oldArtifacts = await assetSaver.loadArtifacts(artifactPath); + const url = `http://localhost:${port}/dobetterweb/dbw_tester.html`; const rawFlags = [ - '--gather-mode=lighthouse-core/test/results/artifacts', + `--gather-mode=${artifactPath}`, '--throttling-method=devtools', url, ].join(' '); const flags = cliFlags.getFlags(rawFlags); await cli.runLighthouse(url, flags, budgetedConfig); await new Promise(res => server.close(res)); + + if (artifactName) { + // Revert everything except the one artifact + const newArtifacts = await assetSaver.loadArtifacts(artifactPath); + if (!(artifactName in newArtifacts) && !(artifactName in oldArtifacts)) { + console.warn(`❌ Unknown artifact name: '${artifactName}'. Reverting artifacts...`); // eslint-disable-line no-console + } + const finalArtifacts = oldArtifacts; + finalArtifacts[artifactName] = newArtifacts[artifactName]; + await assetSaver.saveArtifacts(finalArtifacts, artifactPath); + } } -update(); +update(/** @type {keyof LH.Artifacts | undefined} */ (process.argv[2])); diff --git a/lighthouse-core/test/results/artifacts/artifacts.json b/lighthouse-core/test/results/artifacts/artifacts.json index a65e10c93f44..8509b2cdd195 100644 --- a/lighthouse-core/test/results/artifacts/artifacts.json +++ b/lighthouse-core/test/results/artifacts/artifacts.json @@ -2023,4 +2023,4 @@ "systemId": "", "publicId": "" } -} +} \ No newline at end of file From 5aec0639e6bdee6eca39fbec8ec5a7786f7601e5 Mon Sep 17 00:00:00 2001 From: Katie Hempenius Date: Fri, 3 May 2019 17:18:43 -0700 Subject: [PATCH 02/55] core(lightwallet): add performance-budget audit (#8539) --- .../test/cli/__snapshots__/index-test.js.snap | 15 ++ lighthouse-core/audits/performance-budget.js | 148 ++++++++++++++++ lighthouse-core/config/default-config.js | 10 ++ lighthouse-core/lib/i18n/en-US.json | 20 +++ .../test/audits/performance-budget-test.js | 164 ++++++++++++++++++ .../html/renderer/report-renderer-test.js | 4 +- lighthouse-core/test/results/sample_v2.json | 35 +++- proto/sample_v2_round_trip.json | 29 +++- 8 files changed, 419 insertions(+), 6 deletions(-) create mode 100644 lighthouse-core/audits/performance-budget.js create mode 100644 lighthouse-core/test/audits/performance-budget-test.js diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap index e6033bb2d710..d1e45ec5d810 100644 --- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap +++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap @@ -120,6 +120,9 @@ Object { Object { "path": "offline-start-url", }, + Object { + "path": "performance-budget", + }, Object { "path": "resource-summary", }, @@ -814,6 +817,10 @@ Object { "id": "font-display", "weight": 0, }, + Object { + "id": "performance-budget", + "weight": 0, + }, Object { "group": "diagnostics", "id": "resource-summary", @@ -1043,6 +1050,10 @@ Object { "description": "These are opportunities to to improve the experience of reading tabular or list data using assistive technology, like a screen reader.", "title": "Tables and lists", }, + "budgets": Object { + "description": "Performance budgets set standards for the performance of your site.", + "title": "Budgets", + }, "diagnostics": Object { "description": "More information about the performance of your application.", "title": "Diagnostics", @@ -1286,6 +1297,10 @@ Object { "description": "These are opportunities to to improve the experience of reading tabular or list data using assistive technology, like a screen reader.", "title": "Tables and lists", }, + "budgets": Object { + "description": "Performance budgets set standards for the performance of your site.", + "title": "Budgets", + }, "diagnostics": Object { "description": "More information about the performance of your application.", "title": "Diagnostics", diff --git a/lighthouse-core/audits/performance-budget.js b/lighthouse-core/audits/performance-budget.js new file mode 100644 index 000000000000..f47f4f1dec62 --- /dev/null +++ b/lighthouse-core/audits/performance-budget.js @@ -0,0 +1,148 @@ +/** + * @license Copyright 2019 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 Audit = require('./audit.js'); +const ResourceSummary = require('../computed/resource-summary.js'); +const i18n = require('../lib/i18n/i18n.js'); + +const UIStrings = { + /** Title of a Lighthouse audit that compares the size and quantity of page resources against targets set by the user. These targets are thought of as "performance budgets" because these metrics impact page performance (i.e. how quickly a page loads). */ + title: 'Performance budget', + /** Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. */ + description: 'Keep the quantity and size of network requests under the targets ' + + 'set by the provided performance budget.', + /** [ICU Syntax] Entry in a data table identifying the number of network requests of a particular type. Count will be a whole number. String should be as short as possible to be able to fit well into the table. */ + requestCountOverBudget: `{count, plural, + =1 {1 request} + other {# requests} + }`, +}; + +const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); + +/** @typedef {{count: number, size: number}} ResourceEntry */ +/** @typedef {{resourceType: LH.Budget.ResourceType, label: string, requestCount: number, size: number, sizeOverBudget: number | undefined, countOverBudget: string | undefined}} BudgetItem */ + +class ResourceBudget extends Audit { + /** + * @return {LH.Audit.Meta} + */ + static get meta() { + return { + id: 'performance-budget', + title: str_(UIStrings.title), + description: str_(UIStrings.description), + scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE, + requiredArtifacts: ['devtoolsLogs', 'URL'], + }; + } + + /** + * @param {LH.Budget.ResourceType} resourceType + * @return {string} + */ + static getRowLabel(resourceType) { + /** @type {Record} */ + const strMappings = { + 'total': i18n.UIStrings.totalResourceType, + 'document': i18n.UIStrings.documentResourceType, + 'script': i18n.UIStrings.scriptResourceType, + 'stylesheet': i18n.UIStrings.stylesheetResourceType, + 'image': i18n.UIStrings.imageResourceType, + 'media': i18n.UIStrings.mediaResourceType, + 'font': i18n.UIStrings.fontResourceType, + 'other': i18n.UIStrings.otherResourceType, + 'third-party': i18n.UIStrings.thirdPartyResourceType, + }; + return strMappings[resourceType]; + } + + /** + * @param {LH.Budget} budget + * @param {Record} summary + * @return {Array} + */ + static tableItems(budget, summary) { + const resourceTypes = /** @type {Array} */ (Object.keys(summary)); + return resourceTypes.map((resourceType) => { + const label = str_(this.getRowLabel(resourceType)); + const requestCount = summary[resourceType].count; + const size = summary[resourceType].size; + + let sizeOverBudget; + let countOverBudget; + + if (budget.resourceSizes) { + const sizeBudget = budget.resourceSizes.find(b => b.resourceType === resourceType); + if (sizeBudget && (size > (sizeBudget.budget * 1024))) { + sizeOverBudget = size - (sizeBudget.budget * 1024); + } + } + if (budget.resourceCounts) { + const countBudget = budget.resourceCounts.find(b => b.resourceType === resourceType); + if (countBudget && (requestCount > countBudget.budget)) { + const requestDifference = requestCount - countBudget.budget; + countOverBudget = str_(UIStrings.requestCountOverBudget, {count: requestDifference}); + } + } + return { + resourceType, + label, + requestCount, + size, + countOverBudget, + sizeOverBudget, + }; + }).filter((row) => { + // Only resources with budgets should be included in the table + if (budget.resourceSizes) { + if (budget.resourceSizes.some(b => b.resourceType === row.resourceType)) return true; + } + if (budget.resourceCounts) { + if (budget.resourceCounts.some(b => b.resourceType === row.resourceType)) return true; + } + return false; + }).sort((a, b) => { + return (b.sizeOverBudget || 0) - (a.sizeOverBudget || 0); + }); + } + + /** + * @param {LH.Artifacts} artifacts + * @param {LH.Audit.Context} context + * @return {Promise} + */ + static async audit(artifacts, context) { + const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; + const summary = await ResourceSummary.request({devtoolsLog, URL: artifacts.URL}, context); + const budget = context.settings.budgets ? context.settings.budgets[0] : undefined; + + if (!budget) { + return { + score: 0, + notApplicable: true, + }; + } + + /** @type { LH.Audit.Details.Table['headings'] } */ + const headers = [ + {key: 'label', itemType: 'text', text: 'Resource Type'}, + {key: 'requestCount', itemType: 'numeric', text: 'Requests'}, + {key: 'size', itemType: 'bytes', text: 'Transfer Size'}, + {key: 'countOverBudget', itemType: 'text', text: ''}, + {key: 'sizeOverBudget', itemType: 'bytes', text: 'Over Budget'}, + ]; + + return { + details: Audit.makeTableDetails(headers, this.tableItems(budget, summary)), + score: 1, + }; + } +} + +module.exports = ResourceBudget; +module.exports.UIStrings = UIStrings; diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js index f896cd423fb9..9184e54928f1 100644 --- a/lighthouse-core/config/default-config.js +++ b/lighthouse-core/config/default-config.js @@ -13,6 +13,10 @@ const i18n = require('../lib/i18n/i18n.js'); const UIStrings = { /** Title of the Performance category of audits. Equivalent to 'Web performance', this term is inclusive of all web page speed and loading optimization topics. Also used as a label of a score gauge; try to limit to 20 characters. */ performanceCategoryTitle: 'Performance', + /** Title of the Budgets section of the Performance Category. 'Budgets' refers to a budget (like a financial budget), but applied to the amount of resources on a page, rather than money. */ + budgetsGroupTitle: 'Budgets', + /** Description of the Budgets section of the Performance category. Within this section the budget results are displayed. */ + budgetsGroupDescription: 'Performance budgets set standards for the performance of your site.', /** Title of the speed metrics section of the Performance category. Within this section are various speed metrics which quantify the pageload performance into values presented in seconds and milliseconds. */ metricGroupTitle: 'Metrics', /** Title of the opportunity section of the Performance category. Within this section are audits with imperative titles that suggest actions the user can take to improve the loading performance of their web page. 'Suggestion'/'Optimization'/'Recommendation' are reasonable synonyms for 'opportunity' in this case. */ @@ -192,6 +196,7 @@ const defaultConfig = { 'main-thread-tasks', 'metrics', 'offline-start-url', + 'performance-budget', 'resource-summary', 'manual/pwa-cross-browser', 'manual/pwa-page-transitions', @@ -287,6 +292,10 @@ const defaultConfig = { title: str_(UIStrings.loadOpportunitiesGroupTitle), description: str_(UIStrings.loadOpportunitiesGroupDescription), }, + 'budgets': { + title: str_(UIStrings.budgetsGroupTitle), + description: str_(UIStrings.budgetsGroupDescription), + }, 'diagnostics': { title: str_(UIStrings.diagnosticsGroupTitle), description: str_(UIStrings.diagnosticsGroupDescription), @@ -379,6 +388,7 @@ const defaultConfig = { {id: 'bootup-time', weight: 0, group: 'diagnostics'}, {id: 'mainthread-work-breakdown', weight: 0, group: 'diagnostics'}, {id: 'font-display', weight: 0, group: 'diagnostics'}, + {id: 'performance-budget', weight: 0}, {id: 'resource-summary', weight: 0, group: 'diagnostics'}, // Audits past this point don't belong to a group and will not be shown automatically {id: 'network-requests', weight: 0}, diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json index 0f82ba362b6a..e40e4f0483bd 100644 --- a/lighthouse-core/lib/i18n/en-US.json +++ b/lighthouse-core/lib/i18n/en-US.json @@ -743,6 +743,18 @@ "message": "Server Backend Latencies", "description": "Descriptive title of a Lighthouse audit that tells the user the server latencies observed from each origin the page connected to. This is displayed in a list of audit titles that Lighthouse generates." }, + "lighthouse-core/audits/performance-budget.js | description": { + "message": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", + "description": "Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits." + }, + "lighthouse-core/audits/performance-budget.js | requestCountOverBudget": { + "message": "{count, plural,\n =1 {1 request}\n other {# requests}\n }", + "description": "[ICU Syntax] Entry in a data table identifying the number of network requests of a particular type. Count will be a whole number. String should be as short as possible to be able to fit well into the table." + }, + "lighthouse-core/audits/performance-budget.js | title": { + "message": "Performance budget", + "description": "Title of a Lighthouse audit that compares the size and quantity of page resources against targets set by the user. These targets are thought of as \"performance budgets\" because these metrics impact page performance (i.e. how quickly a page loads)." + }, "lighthouse-core/audits/redirects.js | description": { "message": "Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/redirects).", "description": "Description of a Lighthouse audit that tells users why they should reduce the number of server-side redirects on their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation." @@ -1115,6 +1127,14 @@ "message": "Tables and lists", "description": "Title of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to improve the experience of reading tabular or list data using assistive technology." }, + "lighthouse-core/config/default-config.js | budgetsGroupDescription": { + "message": "Performance budgets set standards for the performance of your site.", + "description": "Description of the Budgets section of the Performance category. Within this section the budget results are displayed." + }, + "lighthouse-core/config/default-config.js | budgetsGroupTitle": { + "message": "Budgets", + "description": "Title of the Budgets section of the Performance Category. 'Budgets' refers to a budget (like a financial budget), but applied to the amount of resources on a page, rather than money." + }, "lighthouse-core/config/default-config.js | diagnosticsGroupDescription": { "message": "More information about the performance of your application.", "description": "Description of the diagnostics section of the Performance category. Within this section are audits with non-imperative titles that provide more detail on the page's page load performance characteristics. Whereas the 'Opportunities' suggest an action along with expected time savings, diagnostics do not. Within this section, the user may read the details and deduce additional actions they could take." diff --git a/lighthouse-core/test/audits/performance-budget-test.js b/lighthouse-core/test/audits/performance-budget-test.js new file mode 100644 index 000000000000..6cb3f09afbef --- /dev/null +++ b/lighthouse-core/test/audits/performance-budget-test.js @@ -0,0 +1,164 @@ +/** + * @license Copyright 2019 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 ResourceBudgetAudit = require('../../audits/performance-budget.js'); +const networkRecordsToDevtoolsLog = require('../network-records-to-devtools-log.js'); + +/* eslint-env jest */ + +describe('Performance: Resource budgets audit', () => { + let artifacts; + let context; + beforeEach(() => { + artifacts = { + devtoolsLogs: { + defaultPass: networkRecordsToDevtoolsLog([ + {url: 'http://example.com/file.html', resourceType: 'Document', transferSize: 30}, + {url: 'http://example.com/app.js', resourceType: 'Script', transferSize: 10}, + {url: 'http://third-party.com/script.js', resourceType: 'Script', transferSize: 50}, + {url: 'http://third-party.com/file.jpg', resourceType: 'Image', transferSize: 70}, + ]), + }, + URL: {requestedURL: 'http://example.com', finalURL: 'http://example.com'}, + }; + context = {computedCache: new Map(), settings: {}}; + }); + + describe('with a budget.json', () => { + beforeEach(() => { + context.settings.budgets = [{ + resourceSizes: [ + { + resourceType: 'script', + budget: 0, + }, + { + resourceType: 'image', + budget: 1000, + }, + ], + resourceCounts: [ + { + resourceType: 'script', + budget: 0, + }, + { + resourceType: 'image', + budget: 1000, + }, + ], + }]; + }); + + it('includes table columns for requet & file size overages', async () => { + const result = await ResourceBudgetAudit.audit(artifacts, context); + expect(result.details.headings).toHaveLength(5); + }); + + it('table item information is correct', async () => { + const result = await ResourceBudgetAudit.audit(artifacts, context); + const item = result.details.items[0]; + expect(item.label).toBeDisplayString('Script'); + expect(item.requestCount).toBe(2); + expect(item.size).toBe(60); + expect(item.sizeOverBudget).toBe(60); + expect(item.countOverBudget).toBeDisplayString('2 requests'); + }); + + describe('request & transfer size overage', () => { + it('are displayed', async () => { + const result = await ResourceBudgetAudit.audit(artifacts, context); + const scriptRow = result.details.items.find(r => r.resourceType === 'script'); + expect(scriptRow.sizeOverBudget).toBe(60); + expect(scriptRow.countOverBudget).toBeDisplayString('2 requests'); + }); + + it('are empty for passing budgets', async () => { + const result = await ResourceBudgetAudit.audit(artifacts, context); + const imageRow = result.details.items.find(r => r.resourceType === 'image'); + expect(imageRow.sizeOverBudget).toBeUndefined(); + expect(imageRow.countOverBudget).toBeUndefined(); + }); + + it('convert budgets from kilobytes to bytes during calculations', async () => { + context.settings.budgets = [{ + resourceSizes: [ + { + resourceType: 'document', + budget: 20, + }, + ], + }]; + const result = await ResourceBudgetAudit.audit(artifacts, context); + expect(result.details.items[0].siveOverBudget).toBeUndefined(); + }); + }); + + it('only includes rows for resource types with budgets', async () => { + const result = await ResourceBudgetAudit.audit(artifacts, context); + expect(result.details.items).toHaveLength(2); + }); + + it('sorts rows by descending file size overage', async () => { + context.settings.budgets = [{ + resourceSizes: [ + { + resourceType: 'document', + budget: 0, + }, + { + resourceType: 'script', + budget: 0, + }, + { + resourceType: 'image', + budget: 0, + }, + ], + }]; + const result = await ResourceBudgetAudit.audit(artifacts, context); + const items = result.details.items; + items.slice(0, -1).forEach((item, index) => { + expect(item.size).toBeGreaterThanOrEqual(items[index + 1].size); + }); + }); + + it('uses the first budget in budgets', async () => { + context.settings.budgets = [{ + resourceSizes: [ + { + resourceType: 'image', + budget: 0, + }, + ], + }, + { + resourceSizes: [ + { + resourceType: 'script', + budget: 0, + }, + ], + }, + ]; + const result = await ResourceBudgetAudit.audit(artifacts, context); + expect(result.details.items[0].resourceType).toBe('image'); + }); + }); + + describe('without a budget.json', () => { + beforeEach(() => { + context.settings.budgets = null; + }); + + it('audit does not apply', async () => { + const result = await ResourceBudgetAudit.audit(artifacts, context); + expect(result.details).toBeUndefined(); + expect(result.notApplicable).toBe(true); + }); + }); +}); diff --git a/lighthouse-core/test/report/html/renderer/report-renderer-test.js b/lighthouse-core/test/report/html/renderer/report-renderer-test.js index d6d8e6a987f5..efa3966da230 100644 --- a/lighthouse-core/test/report/html/renderer/report-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/report-renderer-test.js @@ -265,8 +265,10 @@ describe('ReportRenderer', () => { const container = renderer._dom._document.body; const reportElement = renderer.renderReport(sampleResults, container); + // TODO(khempenius): Remove "+1" once budgets renderer code is added. + // Until budgets renderer code is added, JSON vs. DOM comparison will differ by 1. const notApplicableElementCount = reportElement .querySelectorAll('.lh-audit--notapplicable').length; - assert.strictEqual(notApplicableCount, notApplicableElementCount); + assert.strictEqual(notApplicableCount, notApplicableElementCount + 1); }); }); diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index ada4c2fae507..c045c7cddc02 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -1273,6 +1273,13 @@ "explanation": "No usable web app manifest found on page.", "warnings": [] }, + "performance-budget": { + "id": "performance-budget", + "title": "Performance budget", + "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", + "score": null, + "scoreDisplayMode": "notApplicable" + }, "resource-summary": { "id": "resource-summary", "title": "Keep request counts low and transfer sizes small", @@ -3200,6 +3207,10 @@ "weight": 0, "group": "diagnostics" }, + { + "id": "performance-budget", + "weight": 0 + }, { "id": "resource-summary", "weight": 0, @@ -3704,6 +3715,10 @@ "title": "Opportunities", "description": "These optimizations can speed up your page load." }, + "budgets": { + "title": "Budgets", + "description": "Performance budgets set standards for the performance of your site." + }, "diagnostics": { "title": "Diagnostics", "description": "More information about the performance of your application." @@ -4174,7 +4189,7 @@ }, { "startTime": 0, - "name": "lh:audit:resource-summary", + "name": "lh:audit:performance-budget", "duration": 100, "entryType": "measure" }, @@ -4184,6 +4199,12 @@ "duration": 100, "entryType": "measure" }, + { + "startTime": 0, + "name": "lh:audit:resource-summary", + "duration": 100, + "entryType": "measure" + }, { "startTime": 0, "name": "lh:audit:pwa-cross-browser", @@ -4960,6 +4981,12 @@ "lighthouse-core/audits/network-server-latency.js | description": [ "audits[network-server-latency].description" ], + "lighthouse-core/audits/performance-budget.js | title": [ + "audits[performance-budget].title" + ], + "lighthouse-core/audits/performance-budget.js | description": [ + "audits[performance-budget].description" + ], "lighthouse-core/audits/resource-summary.js | title": [ "audits[resource-summary].title" ], @@ -5497,6 +5524,12 @@ "lighthouse-core/config/default-config.js | loadOpportunitiesGroupDescription": [ "categoryGroups[load-opportunities].description" ], + "lighthouse-core/config/default-config.js | budgetsGroupTitle": [ + "categoryGroups.budgets.title" + ], + "lighthouse-core/config/default-config.js | budgetsGroupDescription": [ + "categoryGroups.budgets.description" + ], "lighthouse-core/config/default-config.js | diagnosticsGroupTitle": [ "categoryGroups.diagnostics.title" ], diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json index 1af6af0d7551..d9f5ba95c53b 100644 --- a/proto/sample_v2_round_trip.json +++ b/proto/sample_v2_round_trip.json @@ -1100,7 +1100,7 @@ "name": "WordPress" } ], - "summary": null, + "summary": {}, "type": "table" }, "id": "js-libraries", @@ -1227,7 +1227,7 @@ "details": { "headings": [], "items": [], - "summary": null, + "summary": {}, "type": "table" }, "id": "link-text", @@ -1883,7 +1883,7 @@ "vulnCount": 2.0 } ], - "summary": null, + "summary": {}, "type": "table" }, "displayValue": "2 vulnerabilities detected", @@ -2031,6 +2031,13 @@ "scoreDisplayMode": "binary", "title": "Prevents users to paste into password fields" }, + "performance-budget": { + "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", + "id": "performance-budget", + "score": null, + "scoreDisplayMode": "notApplicable", + "title": "Performance budget" + }, "plugins": { "description": "Search engines can't index plugin content, and many devices restrict plugins or don't support them. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/plugins).", "details": { @@ -3436,6 +3443,10 @@ "id": "font-display", "weight": 0.0 }, + { + "id": "performance-budget", + "weight": 0.0 + }, { "group": "diagnostics", "id": "resource-summary", @@ -3671,6 +3682,10 @@ "description": "These are opportunities to to improve the experience of reading tabular or list data using assistive technology, like a screen reader.", "title": "Tables and lists" }, + "budgets": { + "description": "Performance budgets set standards for the performance of your site.", + "title": "Budgets" + }, "diagnostics": { "description": "More information about the performance of your application.", "title": "Diagnostics" @@ -4178,7 +4193,7 @@ { "duration": 100.0, "entryType": "measure", - "name": "lh:audit:resource-summary", + "name": "lh:audit:performance-budget", "startTime": 0.0 }, { @@ -4187,6 +4202,12 @@ "name": "lh:computed:ResourceSummary", "startTime": 0.0 }, + { + "duration": 100.0, + "entryType": "measure", + "name": "lh:audit:resource-summary", + "startTime": 0.0 + }, { "duration": 100.0, "entryType": "measure", From ec08e7b9375d3cbe38938b95a0dadfd8287d532e Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Sat, 4 May 2019 01:01:29 -0500 Subject: [PATCH 03/55] tests(smokehouse): change metric assertions from score to numericValue (#8805) --- lighthouse-cli/test/smokehouse/perf/expectations.js | 10 +++++----- .../test/smokehouse/perf/lantern-expectations.js | 6 ++++-- .../test/smokehouse/tricky-metrics/expectations.js | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lighthouse-cli/test/smokehouse/perf/expectations.js b/lighthouse-cli/test/smokehouse/perf/expectations.js index 3fcebd574d0c..8fbf1586fad0 100644 --- a/lighthouse-cli/test/smokehouse/perf/expectations.js +++ b/lighthouse-cli/test/smokehouse/perf/expectations.js @@ -15,19 +15,19 @@ module.exports = [ finalUrl: 'http://localhost:10200/preload.html', audits: { 'speed-index': { - score: '>=0.80', + score: '>=0.80', // primarily just making sure it didn't fail/go crazy, specific value isn't that important }, 'first-meaningful-paint': { - score: '>=0.90', + score: '>=0.90', // primarily just making sure it didn't fail/go crazy, specific value isn't that important }, 'first-cpu-idle': { - score: '>=0.90', + score: '>=0.90', // primarily just making sure it didn't fail/go crazy, specific value isn't that important }, 'interactive': { - score: '>=0.90', + score: '>=0.90', // primarily just making sure it didn't fail/go crazy, specific value isn't that important }, 'time-to-first-byte': { - // Can be flaky, so test float numericValue instead of boolean score + // Can be flaky, so test float numericValue instead of binary score numericValue: '<1000', }, 'network-requests': { diff --git a/lighthouse-cli/test/smokehouse/perf/lantern-expectations.js b/lighthouse-cli/test/smokehouse/perf/lantern-expectations.js index 49a768160d68..64b1ebc9fca2 100644 --- a/lighthouse-cli/test/smokehouse/perf/lantern-expectations.js +++ b/lighthouse-cli/test/smokehouse/perf/lantern-expectations.js @@ -33,7 +33,8 @@ module.exports = [ audits: { 'interactive': { // Make sure all of the CPU time is reflected in the perf metrics as well. - score: '<.2', + // The scripts stalls for 3 seconds and lantern has a 4x multiplier so 12s minimum. + numericValue: '>12000', }, 'bootup-time': { details: { @@ -56,7 +57,8 @@ module.exports = [ audits: { 'interactive': { // Make sure all of the CPU time is reflected in the perf metrics as well. - score: '<.2', + // The scripts stalls for 3 seconds and lantern has a 4x multiplier so 12s minimum. + numericValue: '>12000', }, 'bootup-time': { details: { diff --git a/lighthouse-cli/test/smokehouse/tricky-metrics/expectations.js b/lighthouse-cli/test/smokehouse/tricky-metrics/expectations.js index c94244363e05..1aafb8e72c71 100644 --- a/lighthouse-cli/test/smokehouse/tricky-metrics/expectations.js +++ b/lighthouse-cli/test/smokehouse/tricky-metrics/expectations.js @@ -15,11 +15,11 @@ module.exports = [ finalUrl: 'http://localhost:10200/tricky-tti.html', audits: { 'first-cpu-idle': { - score: '<75', + // stalls for 5 seconds, 5 seconds out, so should be around 10s numericValue: '>9000', }, 'interactive': { - score: '<75', + // stalls for 5 seconds, 5 seconds out, so should be around 10s numericValue: '>9000', }, }, From 86c8a0f88665f92fe0d5210aabcf7ad6b2dbcbc0 Mon Sep 17 00:00:00 2001 From: cjamcl Date: Sun, 5 May 2019 12:39:37 -0700 Subject: [PATCH 04/55] misc(proto): assert libprotoc version (#8863) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 392f9dfaab23..2014e4714849 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ultradumbBenchmark": "./lighthouse-core/scripts/benchmark.js", "mixed-content": "./lighthouse-cli/index.js --chrome-flags='--headless' --preset=mixed-content", "minify-latest-run": "./lighthouse-core/scripts/lantern/minify-trace.js ./latest-run/defaultPass.trace.json ./latest-run/defaultPass.trace.min.json && ./lighthouse-core/scripts/lantern/minify-devtoolslog.js ./latest-run/defaultPass.devtoolslog.json ./latest-run/defaultPass.devtoolslog.min.json", - "compile-proto": "protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf to compile the proto file.\" && false)", + "compile-proto": "[ \"$(protoc --version)\" == 'libprotoc 3.6.1' ] && protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf to compile the proto file.\" && false)", "build-proto-roundtrip": "cd proto/scripts && PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2 python json_roundtrip_via_proto.py" }, "devDependencies": { From e6a0dce9ad50ba27e9cd1542fce5ea433672802d Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Sun, 5 May 2019 15:52:58 -0700 Subject: [PATCH 05/55] docs: update throttling (#8854) --- docs/throttling.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/throttling.md b/docs/throttling.md index 761c660ae803..c54b162cd190 100644 --- a/docs/throttling.md +++ b/docs/throttling.md @@ -7,22 +7,22 @@ This is the standard recommendation for mobile throttling: - Throughput: 1.6Mbps down / 750 Kbps up. - Packet loss: none. -These exact figures are used as [Lighthouse's throttling default](https://github.com/GoogleChrome/lighthouse/blob/8f500e00243e07ef0a80b39334bedcc8ddc8d3d0/lighthouse-core/config/constants.js#L19-L26) and represent roughly the bottom 25% of 4G connections and top 25% of 3G connections. They are identical to the [WebPageTest "Mobile 3G - Fast" preset](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample) and, due to a lower latency, slightly faster for some pages than the [WebPageTest "4G" preset](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample). +These exact figures are used as [Lighthouse's throttling default](https://github.com/GoogleChrome/lighthouse/blob/8f500e00243e07ef0a80b39334bedcc8ddc8d3d0/lighthouse-core/config/constants.js#L19-L26) and represent roughly the bottom 25% of 4G connections and top 25% of 3G connections (In Lighthouse it is sometimes called "Slow 4G" used to be labeled as "Fast 3G"). This preset is identical to the [WebPageTest's "Mobile 3G - Fast"](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample) and, due to a lower latency, slightly faster for some pages than the [WebPageTest "4G" preset](https://github.com/WPO-Foundation/webpagetest/blob/master/www/settings/connectivity.ini.sample). -## Throttling basics +## Types of throttling -1. Simulated throttling, which Lighthouse uses by default, is computed throttling _after a trace has been recorded_ which makes it very fast and deterministic. However, due to the imperfect nature of predicting alternate execution paths, there is inherent inaccuracy that is summarized in this doc: [Lighthouse Metric Variability and Accuracy](https://docs.google.com/document/d/1BqtL-nG53rxWOI5RO0pItSRPowZVnYJ_gBEQCJ5EeUE/edit). The TLDR: while it's roughly as accurate or better than DevTools throttling for most sites, it suffers from edge cases and a deep investigation to performance should use _Packet-level_ throttling tools. -1. The DevTools network throttling, which Lighthouse uses for `throttlingMethod='devtools'`, is implemented within Chrome at the _request-level_. As a result, it has some downsides that are now summarized in this doc: [Network Throttling & Chrome - status](https://docs.google.com/document/d/1TwWLaLAfnBfbk5_ZzpGXegPapCIfyzT4MWuZgspKUAQ/edit). The TLDR: while it's a [decent approximation](https://docs.google.com/document/d/10lfVdS1iDWCRKQXPfbxEn4Or99D64mvNlugP1AQuFlE/edit), it's not a sufficient model of a slow connection. The [multipliers used in Lighthouse](https://github.com/GoogleChrome/lighthouse/blob/3be483287a530fb560c843b7299ef9cfe91ce1cc/lighthouse-core/lib/emulation.js#L33-L39) attempt to correct for the differences. -1. _Proxy-level_ throttling tools do not affect UDP data, so they're not recommended. -1. _Packet-level_ throttling tools are able to make the most accurate network simulation. +Within web performance testing, there are four typical styles of throttling: -## How do I get high-quality packet-level throttling? +1. **_Simulated throttling_**, which Lighthouse uses by **default**, uses a simulation of a page load, based on the data observed in the initial unthrottled load. This approach which makes it both very fast and deterministic. However, due to the imperfect nature of predicting alternate execution paths, there is inherent inaccuracy that is summarized in this doc: [Lighthouse Metric Variability and Accuracy](https://docs.google.com/document/d/1BqtL-nG53rxWOI5RO0pItSRPowZVnYJ_gBEQCJ5EeUE/edit). The TLDR: while it's roughly as accurate or better than DevTools throttling for most sites, it suffers from edge cases and a deep investigation to performance should use _Packet-level_ throttling tools. +1. **_Request-level_** throttling, also referred to as _DevTools throttling_ or in the Audits Panel as _Applied throttling_, is how throttling is implemented with Chrome DevTools. In real mobile connectivity, latency affects things at the packet level rather than the request level. As a result, this throttling isn't highly accurate. It also has a few more downsides that are summarized in [Network Throttling & Chrome - status](https://docs.google.com/document/d/1TwWLaLAfnBfbk5_ZzpGXegPapCIfyzT4MWuZgspKUAQ/edit). The TLDR: while it's a [decent approximation](https://docs.google.com/document/d/10lfVdS1iDWCRKQXPfbxEn4Or99D64mvNlugP1AQuFlE/edit), it's not a sufficient model of a slow connection. The [multipliers used in Lighthouse](https://github.com/GoogleChrome/lighthouse/blob/3be483287a530fb560c843b7299ef9cfe91ce1cc/lighthouse-core/lib/emulation.js#L33-L39) attempt to correct for the differences. +1. **_Proxy-level_** throttling tools do not affect UDP data, so they're decent, but not ideal. +1. **_Packet-level_** throttling tools are able to make the most accurate network simulation. While this approach can model real network conditions most effectively, it also can introduce [more variance](https://docs.google.com/document/d/1BqtL-nG53rxWOI5RO0pItSRPowZVnYJ_gBEQCJ5EeUE/edit) than request-level or simulated throttling. [WebPageTest uses](https://github.com/WPO-Foundation/wptagent/blob/master/docs/remote_trafficshaping.md) packet-level throttling. -If you want to use more accurate throttling, read on. +Lighthouse, by default, uses simulated throttling as it provides both quick evaluation and minimized variance. However, some may want to experiment with more accurate throttling... -This Performance Calendar article, [Testing with Realistic Networking Conditions](https://calendar.perfplanet.com/2016/testing-with-realistic-networking-conditions/), has a good explanation of packet-level traffic shaping (which applies across TCP/UDP/ICMP) and recommendations. +## How do I get packet-level throttling? -### Using `comcast` for network throttling +This Performance Calendar article, [Testing with Realistic Networking Conditions](https://calendar.perfplanet.com/2016/testing-with-realistic-networking-conditions/), has a good explanation of packet-level traffic shaping (which applies across TCP/UDP/ICMP) and recommendations. The `comcast` Go package appears to be the most usable Mac/Linux commandline app for managing your network connection. Important to note: it changes your **entire** machine's network interface. Also, **`comcast` requires `sudo`** (as all packet-level shapers do). From 6846807c4d887848a21a00389d26fe00f6527ef7 Mon Sep 17 00:00:00 2001 From: Shane Exterkamp Date: Sun, 5 May 2019 16:13:38 -0700 Subject: [PATCH 06/55] report(redesign): make dark mode details less intense (#8845) --- lighthouse-core/report/html/report-styles.css | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css index 996e0347876d..2522e0e23a85 100644 --- a/lighthouse-core/report/html/report-styles.css +++ b/lighthouse-core/report/html/report-styles.css @@ -85,6 +85,7 @@ --color-black-600: #757575; --color-black-800: #424242; --color-black-900: #212121; + --off-black: #111111; --color-black: #000000; --color-blue: #2962FF; --color-green-700: #018642; @@ -135,6 +136,7 @@ --topbar-height: 36px; --topbar-icon-size: 24px; --topbar-padding: 0 8px; + --metrics-toggle-color: var(--color-black-200); --color-average-secondary: var(--color-orange-700); --color-average: var(--color-orange); @@ -180,7 +182,8 @@ --topbar-bg: var(--color-black); --plugin-badge-bg: var(--color-black-800); --header-bg-color: var(--color-black-900); - --env-item-bg: rgba(0, 0, 0, 0.5); + --env-item-bg: var(--color-black); + --report-secondary-border-color: var(--color-black-200); --body-background-color: var(--color-black-900); --body-text-color: var(--color-black-100); @@ -643,6 +646,11 @@ .lh-metrics-toggle__lines { fill: var(--color-metric-toggle-lines); } + +.lh-metrics-toggle label { + background-color: var(--metrics-toggle-color); +} + .lh-metrics-toggle label .lh-metrics-toggle__icon--less { padding-left: 8px; } From 24fe98250b81410e7567a9cb379b8fbb19ea3f2d Mon Sep 17 00:00:00 2001 From: Brendan Kenny Date: Sun, 5 May 2019 16:52:22 -0700 Subject: [PATCH 07/55] misc(proto): require protobuf 3.7.1, add stricter audit details test (#8867) --- lighthouse-core/test/report/proto-test.js | 21 ++++----------------- package.json | 2 +- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/lighthouse-core/test/report/proto-test.js b/lighthouse-core/test/report/proto-test.js index 8282ffbf3dd3..2c13b20ef1e2 100644 --- a/lighthouse-core/test/report/proto-test.js +++ b/lighthouse-core/test/report/proto-test.js @@ -21,23 +21,10 @@ describe('round trip JSON comparison subsets', () => { sampleJson = JSON.parse(preprocessor.processForProto(sample)); }); - it('has the same audit results sans details', () => { - Object.keys(sampleJson.audits).forEach(audit => { - delete sampleJson.audits[audit].details; - }); - - expect(roundTripJson.audits).toMatchObject(sampleJson.audits); - }); - - it('has the same audit results & details if applicable', () => { - Object.keys(sampleJson.audits).forEach(auditId => { - expect(roundTripJson.audits[auditId]).toMatchObject(sampleJson.audits[auditId]); - - if ('details' in sampleJson.audits[auditId]) { - expect(roundTripJson.audits[auditId].details) - .toMatchObject(sampleJson.audits[auditId].details); - } - }); + it('has the same audit results and details (if applicable)', () => { + for (const auditId of Object.keys(sampleJson.audits)) { + expect(roundTripJson.audits[auditId]).toEqual(sampleJson.audits[auditId]); + } }); it('has the same i18n rendererFormattedStrings', () => { diff --git a/package.json b/package.json index 2014e4714849..5b92e60b3808 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ultradumbBenchmark": "./lighthouse-core/scripts/benchmark.js", "mixed-content": "./lighthouse-cli/index.js --chrome-flags='--headless' --preset=mixed-content", "minify-latest-run": "./lighthouse-core/scripts/lantern/minify-trace.js ./latest-run/defaultPass.trace.json ./latest-run/defaultPass.trace.min.json && ./lighthouse-core/scripts/lantern/minify-devtoolslog.js ./latest-run/defaultPass.devtoolslog.json ./latest-run/defaultPass.devtoolslog.min.json", - "compile-proto": "[ \"$(protoc --version)\" == 'libprotoc 3.6.1' ] && protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf to compile the proto file.\" && false)", + "compile-proto": "protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf ≥ 3.7.1 to compile the proto file.\" && false)", "build-proto-roundtrip": "cd proto/scripts && PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2 python json_roundtrip_via_proto.py" }, "devDependencies": { From a902661addd4aeb75e89bbcf0874502084066e91 Mon Sep 17 00:00:00 2001 From: Matt Zeunert Date: Mon, 6 May 2019 05:27:08 +0100 Subject: [PATCH 08/55] tests: add tap targets to dobetterweb sample page (#8803) --- CONTRIBUTING.md | 2 +- .../test/fixtures/dobetterweb/dbw_tester.html | 10 +++ .../dobetterweb/dbw-expectations.js | 6 +- .../scripts/update-report-fixtures.js | 3 +- .../test/results/artifacts/artifacts.json | 35 +++++++++- lighthouse-core/test/results/sample_v2.json | 62 +++++++++++++++--- proto/sample_v2_round_trip.json | 65 +++++++++++++++---- 7 files changed, 153 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bcc51735a94d..7b4d425a05b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ yarn run update:sample-artifacts ScriptElements # update just one artifact yarn run update:sample-json # update sample LHR based on sample artifacts ``` -When updating all artifacts, usually you'll need to revert changes to the `*.devtoolslog.json` and `*.trace.json` files and manually review changes to `artifacts.json` to make sure they are related to your work. +When updating artifacts, usually you'll need to revert changes to the `*.devtoolslog.json` and `*.trace.json` files and manually review changes to `artifacts.json` to make sure they are related to your work. ## Tracking Errors diff --git a/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html b/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html index a5fb28e31379..5e0042bd4956 100644 --- a/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html +++ b/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html @@ -182,6 +182,16 @@

Do better web tester page

+ + + + + + + + + + + + + + + + +

Ripping off some webfont smoke tests

+

Do we need such text

+

+ + + diff --git a/lighthouse-cli/test/smokehouse/perf/expectations.js b/lighthouse-cli/test/smokehouse/perf/expectations.js index 8fbf1586fad0..b0d64f39f607 100644 --- a/lighthouse-cli/test/smokehouse/perf/expectations.js +++ b/lighthouse-cli/test/smokehouse/perf/expectations.js @@ -6,7 +6,7 @@ 'use strict'; /** - * Expected Lighthouse audit values for --preset=perf tests + * Expected Lighthouse audit values for perf tests. */ module.exports = [ { @@ -65,6 +65,84 @@ module.exports = [ }, }, }, + { + lhr: { + requestedUrl: 'http://localhost:10200/perf/perf-budgets/load-things.html', + finalUrl: 'http://localhost:10200/perf/perf-budgets/load-things.html', + audits: { + 'resource-summary': { + score: null, + displayValue: '11 requests • 164 KB', + details: { + items: [ + {resourceType: 'total', requestCount: 11, size: '168000±1000'}, + {resourceType: 'font', requestCount: 2, size: '80000±1000'}, + {resourceType: 'script', requestCount: 3, size: '55000±1000'}, + {resourceType: 'image', requestCount: 2, size: '28000±1000'}, + {resourceType: 'document', requestCount: 1, size: '2100±100'}, + {resourceType: 'other', requestCount: 2, size: '1250±50'}, + {resourceType: 'stylesheet', requestCount: 1, size: '450±100'}, + {resourceType: 'media', requestCount: 0, size: 0}, + {resourceType: 'third-party', requestCount: 0, size: 0}, + ], + }, + }, + 'performance-budget': { + score: null, + details: { + // Undefined items are asserting that the property isn't included in the table item. + items: [ + { + resourceType: 'total', + countOverBudget: '3 requests', + sizeOverBudget: '65000±1000', + }, + { + resourceType: 'script', + countOverBudget: '2 requests', + sizeOverBudget: '25000±1000', + }, + { + resourceType: 'font', + countOverBudget: undefined, + sizeOverBudget: '4000±500', + }, + { + resourceType: 'document', + countOverBudget: '1 request', + sizeOverBudget: '1100±50', + }, + { + resourceType: 'stylesheet', + countOverBudget: undefined, + sizeOverBudget: '450±100', + }, + { + resourceType: 'image', + countOverBudget: '1 request', + sizeOverBudget: undefined, + }, + { + resourceType: 'media', + countOverBudget: undefined, + sizeOverBudget: undefined, + }, + { + resourceType: 'other', + countOverBudget: '1 request', + sizeOverBudget: undefined, + }, + { + resourceType: 'third-party', + countOverBudget: undefined, + sizeOverBudget: undefined, + }, + ], + }, + }, + }, + }, + }, { lhr: { requestedUrl: 'http://localhost:10200/perf/fonts.html', diff --git a/lighthouse-cli/test/smokehouse/perf/perf-config.js b/lighthouse-cli/test/smokehouse/perf/perf-config.js new file mode 100644 index 000000000000..6b22341a9df2 --- /dev/null +++ b/lighthouse-cli/test/smokehouse/perf/perf-config.js @@ -0,0 +1,50 @@ +/** + * @license Copyright 2019 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'; + +/** @type {LH.Config.Json} */ +const perfConfig = { + extends: 'lighthouse:default', + settings: { + throttlingMethod: 'devtools', + onlyCategories: ['performance'], + + // A mixture of under, over, and meeting budget to exercise all paths. + budgets: [{ + resourceCounts: [ + {resourceType: 'total', budget: 8}, + {resourceType: 'stylesheet', budget: 1}, // meets budget + {resourceType: 'image', budget: 1}, + {resourceType: 'media', budget: 0}, + {resourceType: 'font', budget: 2}, // meets budget + {resourceType: 'script', budget: 1}, + {resourceType: 'document', budget: 0}, + {resourceType: 'other', budget: 1}, + {resourceType: 'third-party', budget: 0}, + ], + resourceSizes: [ + {resourceType: 'total', budget: 100}, + {resourceType: 'stylesheet', budget: 0}, + {resourceType: 'image', budget: 30}, // meets budget + {resourceType: 'media', budget: 0}, + {resourceType: 'font', budget: 75}, + {resourceType: 'script', budget: 30}, + {resourceType: 'document', budget: 1}, + {resourceType: 'other', budget: 2}, // meets budget + {resourceType: 'third-party', budget: 0}, + ], + timings: [ + {metric: 'first-contentful-paint', budget: 2000, tolerance: 100}, + {metric: 'first-cpu-idle', budget: 2000, tolerance: 100}, + {metric: 'interactive', budget: 2000, tolerance: 100}, + {metric: 'first-meaningful-paint', budget: 2000, tolerance: 100}, + {metric: 'estimated-input-latency', budget: 2000, tolerance: 100}, + ], + }], + }, +}; + +module.exports = perfConfig; diff --git a/lighthouse-cli/test/smokehouse/smoke-test-dfns.js b/lighthouse-cli/test/smokehouse/smoke-test-dfns.js index 261eedd149f9..c12b14fcdffe 100644 --- a/lighthouse-cli/test/smokehouse/smoke-test-dfns.js +++ b/lighthouse-cli/test/smokehouse/smoke-test-dfns.js @@ -67,7 +67,7 @@ const SMOKE_TEST_DFNS = [{ }, { id: 'perf', expectations: 'perf/expectations.js', - config: 'lighthouse-core/config/perf-config.js', + config: 'perf/perf-config.js', batch: 'perf-metric', }, { id: 'lantern', diff --git a/lighthouse-cli/test/smokehouse/smokehouse-report.js b/lighthouse-cli/test/smokehouse/smokehouse-report.js index 33b50b502dd0..656561f32152 100644 --- a/lighthouse-cli/test/smokehouse/smokehouse-report.js +++ b/lighthouse-cli/test/smokehouse/smokehouse-report.js @@ -10,7 +10,7 @@ const log = require('lighthouse-logger'); const VERBOSE = Boolean(process.env.LH_SMOKE_VERBOSE); const NUMBER_REGEXP = /(?:\d|\.)+/.source; -const OPS_REGEXP = /<=?|>=?|\+\/-/.source; +const OPS_REGEXP = /<=?|>=?|\+\/-|±/.source; // An optional number, optional whitespace, an operator, optional whitespace, a number. const NUMERICAL_EXPECTATION_REGEXP = new RegExp(`^(${NUMBER_REGEXP})?\\s*(${OPS_REGEXP})\\s*(${NUMBER_REGEXP})$`); @@ -20,6 +20,7 @@ const NUMERICAL_EXPECTATION_REGEXP = * - Greater than/less than operators, e.g. "<100", ">90" * - Regular expressions * - Strict equality + * - plus or minus a margin of error, e.g. '10+/-5', '100±10' * * @param {*} actual * @param {*} expected @@ -39,6 +40,7 @@ function matchesExpectation(actual, expected) { case '<=': return actual <= postfixNumber; case '+/-': + case '±': return Math.abs(actual - prefixNumber) <= postfixNumber; default: throw new Error(`unexpected operator ${operator}`); @@ -80,16 +82,13 @@ function findDifference(path, actual, expected) { } // We only care that all expected's own properties are on actual (and not the other way around). + // Note an expected `undefined` can match an actual that is either `undefined` or not defined. for (const key of Object.keys(expected)) { // Bracket numbers, but property names requiring quotes will still be unquoted. const keyAccessor = /^\d+$/.test(key) ? `[${key}]` : `.${key}`; const keyPath = path + keyAccessor; const expectedValue = expected[key]; - if (!(key in actual)) { - return {path: keyPath, actual: undefined, expected: expectedValue}; - } - const actualValue = actual[key]; const subDifference = findDifference(keyPath, actualValue, expectedValue); From 2a354e4382118e0aca1575d7e755d9cbf9509f18 Mon Sep 17 00:00:00 2001 From: Brendan Kenny Date: Sun, 5 May 2019 23:10:01 -0700 Subject: [PATCH 11/55] tests(lightwallet): add budget to golden LHR (#8870) --- .../scripts/assert-golden-lhr-unchanged.sh | 2 +- .../scripts/update-report-fixtures.js | 21 +- .../html/renderer/report-renderer-test.js | 4 +- lighthouse-core/test/results/sample-config.js | 51 ++++ lighthouse-core/test/results/sample_v2.json | 287 ++++++++++++++++-- package.json | 2 +- proto/sample_v2_round_trip.json | 97 +++++- 7 files changed, 416 insertions(+), 48 deletions(-) create mode 100644 lighthouse-core/test/results/sample-config.js diff --git a/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh b/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh index 7f29084464a3..c407e460c980 100644 --- a/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh +++ b/lighthouse-core/scripts/assert-golden-lhr-unchanged.sh @@ -28,7 +28,7 @@ trap teardown EXIT colorText "Generating a fresh LHR..." "$purple" set -x -node "$lhroot_path/lighthouse-cli" -A="$lhroot_path/lighthouse-core/test/results/artifacts" --throttling-method=devtools --quiet --output=json --output-path="$freshLHRPath" +node "$lhroot_path/lighthouse-cli" -A="$lhroot_path/lighthouse-core/test/results/artifacts" --config-path="$lhroot_path/lighthouse-core/test/results/sample-config.js" --quiet --output=json --output-path="$freshLHRPath" set +x # remove timing from both diff --git a/lighthouse-core/scripts/update-report-fixtures.js b/lighthouse-core/scripts/update-report-fixtures.js index e59f726d2023..10d64051c397 100644 --- a/lighthouse-core/scripts/update-report-fixtures.js +++ b/lighthouse-core/scripts/update-report-fixtures.js @@ -11,28 +11,10 @@ const assetSaver = require('../lib/asset-saver.js'); const artifactPath = 'lighthouse-core/test/results/artifacts'; const {server} = require('../../lighthouse-cli/test/fixtures/static-server.js'); +const budgetedConfig = require('../test/results/sample-config.js'); /** @typedef {import('net').AddressInfo} AddressInfo */ -/** @type {LH.Config.Json} */ -const budgetedConfig = { - extends: 'lighthouse:default', - settings: { - budgets: [{ - resourceSizes: [ - {resourceType: 'script', budget: 125}, - {resourceType: 'total', budget: 500}, - ], - timings: [ - {metric: 'interactive', budget: 5000, tolerance: 1000}, - ], - resourceCounts: [ - {resourceType: 'third-party', budget: 0}, - ], - }], - }, -}; - /** * Update the report artifacts. If artifactName is set only that artifact will be updated. * @param {keyof LH.Artifacts=} artifactName @@ -51,7 +33,6 @@ async function update(artifactName) { const url = `http://localhost:${port}/dobetterweb/dbw_tester.html`; const rawFlags = [ `--gather-mode=${artifactPath}`, - '--throttling-method=devtools', url, ].join(' '); const flags = cliFlags.getFlags(rawFlags); diff --git a/lighthouse-core/test/report/html/renderer/report-renderer-test.js b/lighthouse-core/test/report/html/renderer/report-renderer-test.js index efa3966da230..d6d8e6a987f5 100644 --- a/lighthouse-core/test/report/html/renderer/report-renderer-test.js +++ b/lighthouse-core/test/report/html/renderer/report-renderer-test.js @@ -265,10 +265,8 @@ describe('ReportRenderer', () => { const container = renderer._dom._document.body; const reportElement = renderer.renderReport(sampleResults, container); - // TODO(khempenius): Remove "+1" once budgets renderer code is added. - // Until budgets renderer code is added, JSON vs. DOM comparison will differ by 1. const notApplicableElementCount = reportElement .querySelectorAll('.lh-audit--notapplicable').length; - assert.strictEqual(notApplicableCount, notApplicableElementCount + 1); + assert.strictEqual(notApplicableCount, notApplicableElementCount); }); }); diff --git a/lighthouse-core/test/results/sample-config.js b/lighthouse-core/test/results/sample-config.js new file mode 100644 index 000000000000..9f8144dbb57c --- /dev/null +++ b/lighthouse-core/test/results/sample-config.js @@ -0,0 +1,51 @@ +/** + * @license Copyright 2019 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'; + +/** + * @fileoverview Config used for generating the sample_v2 golden LHR. + */ + +/** @type {LH.Config.Json} */ +const budgetedConfig = { + extends: 'lighthouse:default', + settings: { + throttlingMethod: 'devtools', + budgets: [{ + resourceCounts: [ + {resourceType: 'total', budget: 10}, + {resourceType: 'stylesheet', budget: 2}, + {resourceType: 'image', budget: 2}, + {resourceType: 'media', budget: 0}, + {resourceType: 'font', budget: 1}, + {resourceType: 'script', budget: 2}, + {resourceType: 'document', budget: 1}, + {resourceType: 'other', budget: 2}, + {resourceType: 'third-party', budget: 1}, + ], + resourceSizes: [ + {resourceType: 'total', budget: 100}, + {resourceType: 'stylesheet', budget: 5}, + {resourceType: 'image', budget: 30}, + {resourceType: 'media', budget: 0}, + {resourceType: 'font', budget: 20}, + {resourceType: 'script', budget: 30}, + {resourceType: 'document', budget: 15}, + {resourceType: 'other', budget: 5}, + {resourceType: 'third-party', budget: 25}, + ], + timings: [ + {metric: 'first-contentful-paint', budget: 3000, tolerance: 100}, + {metric: 'first-cpu-idle', budget: 2900, tolerance: 100}, + {metric: 'interactive', budget: 2900, tolerance: 100}, + {metric: 'first-meaningful-paint', budget: 2000, tolerance: 100}, + {metric: 'estimated-input-latency', budget: 100, tolerance: 100}, + ], + }], + }, +}; + +module.exports = budgetedConfig; diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index 6ab2a57a4ebc..cfe88c52dff9 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -1278,7 +1278,103 @@ "title": "Performance budget", "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", "score": null, - "scoreDisplayMode": "notApplicable" + "scoreDisplayMode": "informative", + "details": { + "type": "table", + "headings": [ + { + "key": "label", + "itemType": "text", + "text": "Resource Type" + }, + { + "key": "requestCount", + "itemType": "numeric", + "text": "Requests" + }, + { + "key": "size", + "itemType": "bytes", + "text": "Transfer Size" + }, + { + "key": "countOverBudget", + "itemType": "text", + "text": "" + }, + { + "key": "sizeOverBudget", + "itemType": "bytes", + "text": "Over Budget" + } + ], + "items": [ + { + "resourceType": "script", + "label": "Script", + "requestCount": 4, + "size": 103675, + "countOverBudget": "2 requests", + "sizeOverBudget": 72955 + }, + { + "resourceType": "total", + "label": "Total", + "requestCount": 18, + "size": 160738, + "countOverBudget": "8 requests", + "sizeOverBudget": 58338 + }, + { + "resourceType": "other", + "label": "Other", + "requestCount": 2, + "size": 12861, + "sizeOverBudget": 7741 + }, + { + "resourceType": "third-party", + "label": "Third-party", + "requestCount": 2, + "size": 30174, + "countOverBudget": "1 request", + "sizeOverBudget": 4574 + }, + { + "resourceType": "stylesheet", + "label": "Stylesheet", + "requestCount": 7, + "size": 5352, + "countOverBudget": "5 requests", + "sizeOverBudget": 232 + }, + { + "resourceType": "image", + "label": "Image", + "requestCount": 2, + "size": 24741 + }, + { + "resourceType": "media", + "label": "Media", + "requestCount": 0, + "size": 0 + }, + { + "resourceType": "font", + "label": "Font", + "requestCount": 0, + "size": 0 + }, + { + "resourceType": "document", + "label": "Document", + "requestCount": 3, + "size": 14109, + "countOverBudget": "2 requests" + } + ] + } }, "resource-summary": { "id": "resource-summary", @@ -3086,7 +3182,113 @@ "disableStorageReset": false, "emulatedFormFactor": "mobile", "channel": "cli", - "budgets": null, + "budgets": [ + { + "resourceSizes": [ + { + "resourceType": "total", + "budget": 100 + }, + { + "resourceType": "stylesheet", + "budget": 5 + }, + { + "resourceType": "image", + "budget": 30 + }, + { + "resourceType": "media", + "budget": 0 + }, + { + "resourceType": "font", + "budget": 20 + }, + { + "resourceType": "script", + "budget": 30 + }, + { + "resourceType": "document", + "budget": 15 + }, + { + "resourceType": "other", + "budget": 5 + }, + { + "resourceType": "third-party", + "budget": 25 + } + ], + "resourceCounts": [ + { + "resourceType": "total", + "budget": 10 + }, + { + "resourceType": "stylesheet", + "budget": 2 + }, + { + "resourceType": "image", + "budget": 2 + }, + { + "resourceType": "media", + "budget": 0 + }, + { + "resourceType": "font", + "budget": 1 + }, + { + "resourceType": "script", + "budget": 2 + }, + { + "resourceType": "document", + "budget": 1 + }, + { + "resourceType": "other", + "budget": 2 + }, + { + "resourceType": "third-party", + "budget": 1 + } + ], + "timings": [ + { + "metric": "first-contentful-paint", + "budget": 3000, + "tolerance": 100 + }, + { + "metric": "first-cpu-idle", + "budget": 2900, + "tolerance": 100 + }, + { + "metric": "interactive", + "budget": 2900, + "tolerance": 100 + }, + { + "metric": "first-meaningful-paint", + "budget": 2000, + "tolerance": 100 + }, + { + "metric": "estimated-input-latency", + "budget": 100, + "tolerance": 100 + } + ] + } + ], "locale": "en-US", "blockedUrlPatterns": null, "additionalTraceCategories": null, @@ -5024,47 +5226,88 @@ "lighthouse-core/audits/performance-budget.js | description": [ "audits[performance-budget].description" ], - "lighthouse-core/audits/resource-summary.js | title": [ - "audits[resource-summary].title" - ], - "lighthouse-core/audits/resource-summary.js | description": [ - "audits[resource-summary].description" + "lighthouse-core/lib/i18n/i18n.js | scriptResourceType": [ + "audits[performance-budget].details.items[0].label", + "audits[resource-summary].details.items[1].label" ], - "lighthouse-core/audits/resource-summary.js | displayValue": [ + "lighthouse-core/audits/performance-budget.js | requestCountOverBudget": [ { "values": { - "requestCount": 18, - "byteCount": 160738 + "count": 2 }, - "path": "audits[resource-summary].displayValue" + "path": "audits[performance-budget].details.items[0].countOverBudget" + }, + { + "values": { + "count": 8 + }, + "path": "audits[performance-budget].details.items[1].countOverBudget" + }, + { + "values": { + "count": 1 + }, + "path": "audits[performance-budget].details.items[3].countOverBudget" + }, + { + "values": { + "count": 5 + }, + "path": "audits[performance-budget].details.items[4].countOverBudget" + }, + { + "values": { + "count": 2 + }, + "path": "audits[performance-budget].details.items[8].countOverBudget" } ], "lighthouse-core/lib/i18n/i18n.js | totalResourceType": [ + "audits[performance-budget].details.items[1].label", "audits[resource-summary].details.items[0].label" ], - "lighthouse-core/lib/i18n/i18n.js | scriptResourceType": [ - "audits[resource-summary].details.items[1].label" - ], - "lighthouse-core/lib/i18n/i18n.js | imageResourceType": [ - "audits[resource-summary].details.items[2].label" - ], - "lighthouse-core/lib/i18n/i18n.js | documentResourceType": [ - "audits[resource-summary].details.items[3].label" - ], "lighthouse-core/lib/i18n/i18n.js | otherResourceType": [ + "audits[performance-budget].details.items[2].label", "audits[resource-summary].details.items[4].label" ], + "lighthouse-core/lib/i18n/i18n.js | thirdPartyResourceType": [ + "audits[performance-budget].details.items[3].label", + "audits[resource-summary].details.items[8].label" + ], "lighthouse-core/lib/i18n/i18n.js | stylesheetResourceType": [ + "audits[performance-budget].details.items[4].label", "audits[resource-summary].details.items[5].label" ], + "lighthouse-core/lib/i18n/i18n.js | imageResourceType": [ + "audits[performance-budget].details.items[5].label", + "audits[resource-summary].details.items[2].label" + ], "lighthouse-core/lib/i18n/i18n.js | mediaResourceType": [ + "audits[performance-budget].details.items[6].label", "audits[resource-summary].details.items[6].label" ], "lighthouse-core/lib/i18n/i18n.js | fontResourceType": [ + "audits[performance-budget].details.items[7].label", "audits[resource-summary].details.items[7].label" ], - "lighthouse-core/lib/i18n/i18n.js | thirdPartyResourceType": [ - "audits[resource-summary].details.items[8].label" + "lighthouse-core/lib/i18n/i18n.js | documentResourceType": [ + "audits[performance-budget].details.items[8].label", + "audits[resource-summary].details.items[3].label" + ], + "lighthouse-core/audits/resource-summary.js | title": [ + "audits[resource-summary].title" + ], + "lighthouse-core/audits/resource-summary.js | description": [ + "audits[resource-summary].description" + ], + "lighthouse-core/audits/resource-summary.js | displayValue": [ + { + "values": { + "requestCount": 18, + "byteCount": 160738 + }, + "path": "audits[resource-summary].displayValue" + } ], "lighthouse-core/audits/accessibility/accesskeys.js | title": [ "audits.accesskeys.title" diff --git a/package.json b/package.json index 5b92e60b3808..52098cfe8df7 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "i18n:checks": "./lighthouse-core/scripts/i18n/assert-strings-collected.sh", "i18n:collect-strings": "node lighthouse-core/scripts/i18n/collect-strings.js", "update:sample-artifacts": "node lighthouse-core/scripts/update-report-fixtures.js", - "update:sample-json": "yarn i18n:collect-strings && node ./lighthouse-cli -A=./lighthouse-core/test/results/artifacts --throttling-method=devtools --output=json --output-path=./lighthouse-core/test/results/sample_v2.json && node lighthouse-core/scripts/cleanup-LHR-for-diff.js ./lighthouse-core/test/results/sample_v2.json --only-remove-timing && yarn compile-proto && yarn build-proto-roundtrip", + "update:sample-json": "yarn i18n:collect-strings && node ./lighthouse-cli -A=./lighthouse-core/test/results/artifacts --config-path=./lighthouse-core/test/results/sample-config.js --output=json --output-path=./lighthouse-core/test/results/sample_v2.json && node lighthouse-core/scripts/cleanup-LHR-for-diff.js ./lighthouse-core/test/results/sample_v2.json --only-remove-timing && yarn compile-proto && yarn build-proto-roundtrip", "diff:sample-json": "yarn i18n:checks && bash lighthouse-core/scripts/assert-golden-lhr-unchanged.sh", "ultradumbBenchmark": "./lighthouse-core/scripts/benchmark.js", "mixed-content": "./lighthouse-cli/index.js --chrome-flags='--headless' --preset=mixed-content", diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json index 3885c9a5519f..0bd017558014 100644 --- a/proto/sample_v2_round_trip.json +++ b/proto/sample_v2_round_trip.json @@ -2033,9 +2033,104 @@ }, "performance-budget": { "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", + "details": { + "headings": [ + { + "itemType": "text", + "key": "label", + "text": "Resource Type" + }, + { + "itemType": "numeric", + "key": "requestCount", + "text": "Requests" + }, + { + "itemType": "bytes", + "key": "size", + "text": "Transfer Size" + }, + { + "itemType": "text", + "key": "countOverBudget" + }, + { + "itemType": "bytes", + "key": "sizeOverBudget", + "text": "Over Budget" + } + ], + "items": [ + { + "countOverBudget": "2 requests", + "label": "Script", + "requestCount": 4.0, + "resourceType": "script", + "size": 103675.0, + "sizeOverBudget": 72955.0 + }, + { + "countOverBudget": "8 requests", + "label": "Total", + "requestCount": 18.0, + "resourceType": "total", + "size": 160738.0, + "sizeOverBudget": 58338.0 + }, + { + "label": "Other", + "requestCount": 2.0, + "resourceType": "other", + "size": 12861.0, + "sizeOverBudget": 7741.0 + }, + { + "countOverBudget": "1 request", + "label": "Third-party", + "requestCount": 2.0, + "resourceType": "third-party", + "size": 30174.0, + "sizeOverBudget": 4574.0 + }, + { + "countOverBudget": "5 requests", + "label": "Stylesheet", + "requestCount": 7.0, + "resourceType": "stylesheet", + "size": 5352.0, + "sizeOverBudget": 232.0 + }, + { + "label": "Image", + "requestCount": 2.0, + "resourceType": "image", + "size": 24741.0 + }, + { + "label": "Media", + "requestCount": 0.0, + "resourceType": "media", + "size": 0.0 + }, + { + "label": "Font", + "requestCount": 0.0, + "resourceType": "font", + "size": 0.0 + }, + { + "countOverBudget": "2 requests", + "label": "Document", + "requestCount": 3.0, + "resourceType": "document", + "size": 14109.0 + } + ], + "type": "table" + }, "id": "performance-budget", "score": null, - "scoreDisplayMode": "notApplicable", + "scoreDisplayMode": "informative", "title": "Performance budget" }, "plugins": { From b92ceeafab91aac7f08a8854fbb620c27c4649e2 Mon Sep 17 00:00:00 2001 From: Shane Exterkamp Date: Mon, 6 May 2019 00:42:34 -0700 Subject: [PATCH 12/55] report(pwa): properly hide n/a gauge when it's n/a (#8872) --- lighthouse-core/report/html/templates.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html index 76d5d002420b..f63d437baceb 100644 --- a/lighthouse-core/report/html/templates.html +++ b/lighthouse-core/report/html/templates.html @@ -698,7 +698,7 @@ display: inline; } /* Just optimized. Same n/a line as no passing groups. */ - .lh-gauge--pwa__wrapper.lh-badged--pwa-optimized .lh-gauge--pwa__na-line { + .lh-gauge--pwa__wrapper.lh-badged--pwa-optimized:not(.lh-badged--pwa-installable):not(.lh-badged--pwa-fast-reliable) .lh-gauge--pwa__na-line { display: inline; } From aac5a08feaa9ad100ebe2b82d484056a1ea29f9c Mon Sep 17 00:00:00 2001 From: cjamcl Date: Mon, 6 May 2019 09:24:38 -0700 Subject: [PATCH 13/55] report(redesign): toggle dark theme in menu (#8843) --- .../report/html/renderer/report-ui-features.js | 4 ++++ lighthouse-core/report/html/report-styles.css | 17 ++--------------- lighthouse-core/report/html/templates.html | 11 ++++++++++- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 4144966f5b37..cb9ef74d9c09 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -370,6 +370,10 @@ class ReportUIFeatures { this.saveAsGist(); break; } + case 'toggle-dark': { + this._toggleDarkTheme(); + break; + } } this.closeExportDropdown(); diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css index 310b00fac7e5..f09c9f423607 100644 --- a/lighthouse-core/report/html/report-styles.css +++ b/lighthouse-core/report/html/report-styles.css @@ -386,21 +386,8 @@ .report-icon--download { background-image: url('data:image/svg+xml;utf8,'); } - -.dark .report-icon--share { - background-image: url('data:image/svg+xml;utf8,'); -} -.dark .report-icon--print { - background-image: url('data:image/svg+xml;utf8,'); -} -.dark .report-icon--copy { - background-image: url('data:image/svg+xml;utf8,'); -} -.dark .report-icon--open { - background-image: url('data:image/svg+xml;utf8,'); -} -.dark .report-icon--download { - background-image: url('data:image/svg+xml;utf8,'); +.report-icon--dark { + background-image:url('data:image/svg+xml;utf8,'); } /* Node */ diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html index f63d437baceb..5893421c3e72 100644 --- a/lighthouse-core/report/html/templates.html +++ b/lighthouse-core/report/html/templates.html @@ -267,7 +267,7 @@ } .lh-export__button.active + .lh-export__dropdown { opacity: 1; - clip: rect(0, 164px, 210px, 0); + clip: rect(0, 187px, 242px, 0); } .lh-export__dropdown { position: absolute; @@ -305,6 +305,14 @@ background-color: transparent; text-indent: 18px; } + .dark .report-icon { + color: var(--color-black-900); + filter: invert(1); + } + .dark .lh-export__dropdown a:hover, + .dark .lh-export__dropdown a:focus { + background-color: #BDBDBD; + } /* copy icon needs slight adjustments to look great */ .lh-export__dropdown .report-icon--copy { background-size: 16px; @@ -351,6 +359,7 @@ Save as JSON Open in Viewer Save as Gist + Toggle Dark Theme From 34b945153b000f38d100aa61260634409e383ddd Mon Sep 17 00:00:00 2001 From: cjamcl Date: Mon, 6 May 2019 11:46:49 -0700 Subject: [PATCH 14/55] report(redesign): design review feedback (#8785) --- .../report/html/renderer/category-renderer.js | 4 +- .../renderer/performance-category-renderer.js | 3 +- .../html/renderer/report-ui-features.js | 12 ---- lighthouse-core/report/html/report-styles.css | 58 +++++++++++-------- lighthouse-core/report/html/templates.html | 5 +- 5 files changed, 40 insertions(+), 42 deletions(-) diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js index e61b4b3b5a08..b03bdf238c89 100644 --- a/lighthouse-core/report/html/renderer/category-renderer.js +++ b/lighthouse-core/report/html/renderer/category-renderer.js @@ -128,10 +128,10 @@ class CategoryRenderer { // Add list of warnings or singular warning const warningsEl = this.dom.createChildOf(titleEl, 'div', 'lh-warnings'); + this.dom.createChildOf(warningsEl, 'span').textContent = Util.UIStrings.warningHeader; if (warnings.length === 1) { - warningsEl.textContent = `${Util.UIStrings.warningHeader} ${warnings.join('')}`; + warningsEl.appendChild(this.dom.document().createTextNode(warnings.join(''))); } else { - warningsEl.textContent = Util.UIStrings.warningHeader; const warningsUl = this.dom.createChildOf(warningsEl, 'ul'); for (const warning of warnings) { const item = this.dom.createChildOf(warningsUl, 'li'); diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js index 0575015c025e..943f80d69a92 100644 --- a/lighthouse-core/report/html/renderer/performance-category-renderer.js +++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js @@ -147,8 +147,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { // 'Values are estimated and may vary' is used as the category description for PSI if (environment !== 'PSI') { - const estValuesEl = this.dom.createChildOf(metricsColumn2El, 'div', - 'lh-metrics__disclaimer lh-metrics__disclaimer'); + const estValuesEl = this.dom.createChildOf(metricsColumn1El, 'div', 'lh-metrics__disclaimer'); estValuesEl.textContent = Util.UIStrings.varianceDisclaimer; } diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index cb9ef74d9c09..22f78f9b5b7a 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -82,7 +82,6 @@ class ReportUIFeatures { this.json = report; this._setupMediaQueryListeners(); - this._setupSmoothScroll(); this._setupExportButton(); this._setupThirdPartyFilter(); this._setupStickyHeaderElements(); @@ -114,17 +113,6 @@ class ReportUIFeatures { this.onMediaQueryChange(mediaQuery); } - _setupSmoothScroll() { - for (const el of this._dom.findAll('a.lh-gauge__wrapper', this._document)) { - const anchorElement = /** @type {HTMLAnchorElement} */ (el); - anchorElement.addEventListener('click', e => { - e.preventDefault(); - window.history.pushState({}, '', anchorElement.hash); - this._dom.find(anchorElement.hash, this._document).scrollIntoView({behavior: 'smooth'}); - }); - } - } - /** * Handle media query change events. * @param {MediaQueryList|MediaQueryListEvent} mql diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css index f09c9f423607..39f3534151ab 100644 --- a/lighthouse-core/report/html/report-styles.css +++ b/lighthouse-core/report/html/report-styles.css @@ -76,6 +76,7 @@ --lh-audit-group-vpadding: 8px; --lh-section-vpadding: 12px; --chevron-size: 12px; + --inner-audit-left-padding: calc(var(--score-shape-size) + var(--score-shape-margin-left) + var(--score-shape-margin-right)); /* Palette. */ --color-black-100: #F5F5F5; @@ -105,8 +106,8 @@ --env-name-min-width: 220px; --env-tem-padding: 10px 0px; --expandable-padding: 0 0 2px calc(var(--score-shape-margin-left) + var(--score-shape-size) + var(--score-shape-margin-right)); - --gauge-circle-size-big: 120px; - --gauge-circle-size: 96px; + --gauge-circle-size-big: 112px; + --gauge-circle-size: 80px; --header-padding: 20px 0 20px 0; --highlighter-bg: var(--color-black-400); --icon-square-size: calc(var(--score-shape-size) * 0.88); @@ -116,14 +117,13 @@ --plugin-icon-size: 65%; --pwa-icon-margin: 0 6px 0 -2px; --pwa-icon-size: var(--topbar-icon-size); - --score-container-padding: 12px; - --score-container-width: 160px; - --score-number-font-size-big: 42px; - --score-number-font-size: 34px; + --score-container-padding: 8px; + --score-container-width: 148px; + --score-number-font-size-big: 38px; + --score-number-font-size: 28px; --score-shape-margin-left: 4px; --score-shape-margin-right: 12px; - --score-shape-margin-top: 7px; - --score-shape-margin: var(--score-shape-margin-top) var(--score-shape-margin-right) 0 var(--score-shape-margin-left); + --score-shape-margin: 0 var(--score-shape-margin-right) 0 var(--score-shape-margin-left); --score-shape-size: 12px; --score-title-font-size-big: 28px; --score-title-font-size: 20px; @@ -133,7 +133,7 @@ --scorescale-width: 18px; --section-padding: 40px; --topbar-bg: var(--color-black-100); - --topbar-height: 36px; + --topbar-height: 32px; --topbar-icon-size: 24px; --topbar-padding: 0 8px; --metrics-toggle-color: var(--color-black-200); @@ -215,19 +215,17 @@ --header-padding: 16px 0 16px 0; --plugin-icon-size: 75%; --pwa-icon-margin: 0 7px 0 -3px; - --score-container-padding: 8px; --score-container-width: 112px; --score-number-font-size-big: 34px; --score-number-font-size: 26px; --score-shape-margin-left: 2px; - --score-shape-margin-top: 7px; --score-shape-size: 10px; --score-title-font-size-big: 22px; --score-title-font-size: 14px; --score-title-line-height-big: 26px; --score-title-line-height: 20px; --section-padding: 24px; - --topbar-height: 32px; + --topbar-height: 28px; --topbar-icon-size: 20px; } @@ -328,7 +326,6 @@ } .lh-audit__description, .lh-audit__stackpack { - --inner-audit-left-padding: calc(var(--text-indent) + var(--lh-audit-index-width) + 2 * var(--audit-item-gap)); --inner-audit-right-padding: calc(var(--text-indent) + 2px); padding-left: var(--inner-audit-left-padding); padding-right: var(--inner-audit-right-padding); @@ -340,6 +337,7 @@ font-size: var(--body-font-size); margin-top: var(--default-padding); margin-bottom: var(--default-padding); + margin-left: var(--inner-audit-left-padding); /* whatever the .lh-details side margins are */ width: 100%; } @@ -461,6 +459,7 @@ } .lh-category-header__description { font-size: var(--body-font-size); + text-align: center; margin: 0px auto; max-width: 400px; } @@ -491,6 +490,7 @@ /* Expandable Details (Audit Groups, Audits) */ .lh-audit__header { display: flex; + align-items: center; font-weight: 500; padding: var(--lh-audit-vpadding) 0; } @@ -563,9 +563,10 @@ .lh-metric__innerwrap { display: flex; + align-items: center; flex-wrap: wrap; justify-content: space-between; - padding: 8px var(--text-indent); + padding: 10px var(--text-indent); } .lh-metric__details { @@ -579,7 +580,6 @@ .lh-metrics__disclaimer { color: var(--medium-75-gray); - text-align: right; margin: var(--lh-section-vpadding) 0; padding: 0 var(--text-indent); } @@ -711,9 +711,7 @@ } .lh-load-opportunity__header .lh-load-opportunity__col { - background-color: var(--medium-50-gray); color: var(--medium-75-gray); - text-align: center; display: unset; line-height: calc(2.3 * var(--body-font-size)); } @@ -724,10 +722,12 @@ .lh-load-opportunity__col--one { flex: 5; + align-items: center; margin-right: 2px; } .lh-load-opportunity__col--two { flex: 4; + text-align: right; } .lh-audit--load-opportunity .lh-audit__display-text { @@ -888,6 +888,10 @@ /* When the header takes 100% width, the chevron becomes small. */ max-width: calc(100% - var(--chevron-size)); } +/* max-width makes the metric toggle not flush. metrics doesn't have a chevron so unset. */ +.lh-audit-group--metrics .lh-audit-group__header { + max-width: unset; +} .lh-audit-group__header span.lh-audit-group__title { font-weight: bold; @@ -970,11 +974,13 @@ .lh-warnings { --item-margin: calc(var(--body-line-height) / 6); - border: 1px solid var(--color-average); - border-radius: 4px; + color: var(--color-average); margin: var(--lh-audit-vpadding) 0; padding: calc(var(--lh-audit-vpadding) / 2) var(--lh-audit-vpadding); } +.lh-warnings span { + font-weight: bold; +} .lh-warnings--toplevel { --item-margin: calc(var(--header-line-height) / 4); @@ -1160,18 +1166,18 @@ .lh-table { --image-preview-size: 24px; border-collapse: collapse; + /* Can't assign padding to table, so shorten the width instead. */ + width: calc(100% - var(--inner-audit-left-padding)); } -.lh-table thead { - background-color: var(--lh-table-higlight-bg); -} .lh-table thead th { font-weight: normal; + color: var(--color-black-600); /* See text-wrapping comment on .lh-container. */ word-break: normal; } -.lh-table tbody tr:nth-child(even) { +.lh-table tbody tr:nth-child(odd) { background-color: var(--lh-table-higlight-bg); } @@ -1179,6 +1185,12 @@ .lh-table td { padding: 8px 6px; } +.lh-table th:first-child { + padding-left: 0; +} +.lh-table th:last-child { + padding-right: 0; +} /* Looks unnecessary, but mostly for keeping the s left-aligned */ .lh-table-column--text, diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html index 5893421c3e72..cc96aee34ddc 100644 --- a/lighthouse-core/report/html/templates.html +++ b/lighthouse-core/report/html/templates.html @@ -180,8 +180,6 @@ justify-content: center; background-color: var(--color-sticky-header-bg); border-bottom: 1px solid var(--color-black-200); - padding-top: var(--score-container-padding); - padding-bottom: 4px; z-index: 1000; pointer-events: none; opacity: 0; @@ -234,6 +232,7 @@ .lh-topbar__logo { width: var(--topbar-icon-size); height: var(--topbar-icon-size); + user-select: none; } .lh-topbar__logo .shape { fill: var(--body-text-color); @@ -267,7 +266,7 @@ } .lh-export__button.active + .lh-export__dropdown { opacity: 1; - clip: rect(0, 187px, 242px, 0); + clip: rect(-1px, 187px, 242px, -3px); } .lh-export__dropdown { position: absolute; From 4e09c92db835537fcd49ec10380ef3b46420371d Mon Sep 17 00:00:00 2001 From: Paul Irish Date: Mon, 6 May 2019 11:56:52 -0700 Subject: [PATCH 15/55] docs: update budget link (#8871) --- lighthouse-core/audits/performance-budget.js | 4 +- lighthouse-core/audits/resource-summary.js | 4 +- lighthouse-core/lib/i18n/en-US.json | 8 +-- lighthouse-core/test/results/sample_v2.json | 4 +- proto/sample_v2_round_trip.json | 72 ++++++++++---------- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/lighthouse-core/audits/performance-budget.js b/lighthouse-core/audits/performance-budget.js index f47f4f1dec62..71f3630e908b 100644 --- a/lighthouse-core/audits/performance-budget.js +++ b/lighthouse-core/audits/performance-budget.js @@ -12,9 +12,9 @@ const i18n = require('../lib/i18n/i18n.js'); const UIStrings = { /** Title of a Lighthouse audit that compares the size and quantity of page resources against targets set by the user. These targets are thought of as "performance budgets" because these metrics impact page performance (i.e. how quickly a page loads). */ title: 'Performance budget', - /** Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. */ + /** Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation. */ description: 'Keep the quantity and size of network requests under the targets ' + - 'set by the provided performance budget.', + 'set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).', /** [ICU Syntax] Entry in a data table identifying the number of network requests of a particular type. Count will be a whole number. String should be as short as possible to be able to fit well into the table. */ requestCountOverBudget: `{count, plural, =1 {1 request} diff --git a/lighthouse-core/audits/resource-summary.js b/lighthouse-core/audits/resource-summary.js index b39add3ae424..d7835a9cbecd 100644 --- a/lighthouse-core/audits/resource-summary.js +++ b/lighthouse-core/audits/resource-summary.js @@ -12,9 +12,9 @@ const i18n = require('../lib/i18n/i18n.js'); const UIStrings = { /** Imperative title of a Lighthouse audit that tells the user to minimize the size and quantity of resources used to load the page. */ title: 'Keep request counts low and transfer sizes small', - /** Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. */ + /** Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation. */ description: 'To set budgets for the quantity and size of page resources,' + - ' add a budget.json file.', + ' add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).', /** [ICU Syntax] Label for an audit identifying the number of requests and kilobytes used to load the page. */ displayValue: `{requestCount, plural, =1 {1 request} other {# requests}}` + ` • { byteCount, number, bytes } KB`, diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json index e40e4f0483bd..02e444d58783 100644 --- a/lighthouse-core/lib/i18n/en-US.json +++ b/lighthouse-core/lib/i18n/en-US.json @@ -744,8 +744,8 @@ "description": "Descriptive title of a Lighthouse audit that tells the user the server latencies observed from each origin the page connected to. This is displayed in a list of audit titles that Lighthouse generates." }, "lighthouse-core/audits/performance-budget.js | description": { - "message": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", - "description": "Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits." + "message": "Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).", + "description": "Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation." }, "lighthouse-core/audits/performance-budget.js | requestCountOverBudget": { "message": "{count, plural,\n =1 {1 request}\n other {# requests}\n }", @@ -764,8 +764,8 @@ "description": "Imperative title of a Lighthouse audit that tells the user to eliminate the redirects taken through multiple URLs to load the page. This is shown in a list of audits that Lighthouse generates." }, "lighthouse-core/audits/resource-summary.js | description": { - "message": "To set budgets for the quantity and size of page resources, add a budget.json file.", - "description": "Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits." + "message": "To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).", + "description": "Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation." }, "lighthouse-core/audits/resource-summary.js | displayValue": { "message": "{requestCount, plural, =1 {1 request} other {# requests}} • { byteCount, number, bytes } KB", diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index cfe88c52dff9..95f002dd0a56 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -1276,7 +1276,7 @@ "performance-budget": { "id": "performance-budget", "title": "Performance budget", - "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", + "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).", "score": null, "scoreDisplayMode": "informative", "details": { @@ -1379,7 +1379,7 @@ "resource-summary": { "id": "resource-summary", "title": "Keep request counts low and transfer sizes small", - "description": "To set budgets for the quantity and size of page resources, add a budget.json file.", + "description": "To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).", "score": null, "scoreDisplayMode": "informative", "displayValue": "18 requests • 157 KB", diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json index 0bd017558014..89196f36325c 100644 --- a/proto/sample_v2_round_trip.json +++ b/proto/sample_v2_round_trip.json @@ -2032,7 +2032,7 @@ "title": "Prevents users to paste into password fields" }, "performance-budget": { - "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget.", + "description": "Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).", "details": { "headings": [ { @@ -2245,7 +2245,7 @@ "title": "Eliminate render-blocking resources" }, "resource-summary": { - "description": "To set budgets for the quantity and size of page resources, add a budget.json file.", + "description": "To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).", "details": { "headings": [ { @@ -2453,51 +2453,51 @@ "details": { "headings": [ { - "itemType": "node", - "key": "tapTarget", + "itemType": "node", + "key": "tapTarget", "text": "Tap Target" - }, + }, { - "itemType": "text", - "key": "size", + "itemType": "text", + "key": "size", "text": "Size" - }, + }, { - "itemType": "node", - "key": "overlappingTarget", + "itemType": "node", + "key": "overlappingTarget", "text": "Overlapping Target" } - ], + ], "items": [ { - "height": 18.0, - "overlapScoreRatio": 0.8333333333333334, + "height": 18.0, + "overlapScoreRatio": 0.8333333333333334, "overlappingTarget": { - "path": "3,HTML,1,BODY,18,BUTTON", - "selector": "body > button.small-button", - "snippet": "", + "path": "3,HTML,1,BODY,18,BUTTON", + "selector": "body > button.small-button", + "snippet": "", "type": "node" - }, - "overlappingTargetScore": 720.0, - "size": "200x18", + }, + "overlappingTargetScore": 720.0, + "size": "200x18", "tapTarget": { - "path": "3,HTML,1,BODY,17,BUTTON", - "selector": "body > button.small-button", - "snippet": "", + "path": "3,HTML,1,BODY,17,BUTTON", + "selector": "body > button.small-button", + "snippet": "", "type": "node" - }, - "tapTargetScore": 864.0, + }, + "tapTargetScore": 864.0, "width": 200.0 } - ], + ], "type": "table" - }, - "displayValue": "0% appropriately sized tap targets", - "id": "tap-targets", - "score": 0.0, - "scoreDisplayMode": "binary", + }, + "displayValue": "0% appropriately sized tap targets", + "id": "tap-targets", + "score": 0.0, + "scoreDisplayMode": "binary", "title": "Tap targets are not sized appropriately" - }, + }, "td-headers-attr": { "description": "Screen readers have features to make navigating tables easier. Ensuring `` cells using the `[headers]` attribute only refer to other cells in the same table may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse).", "id": "td-headers-attr", @@ -3773,11 +3773,11 @@ "id": "structured-data", "weight": 0.0 } - ], - "description": "These checks ensure that your page is optimized for search engine results ranking. There are additional factors Lighthouse does not check that may affect your search ranking. [Learn more](https://support.google.com/webmasters/answer/35769).", - "id": "seo", - "manualDescription": "Run these additional validators on your site to check additional SEO best practices.", - "score": 0.73, + ], + "description": "These checks ensure that your page is optimized for search engine results ranking. There are additional factors Lighthouse does not check that may affect your search ranking. [Learn more](https://support.google.com/webmasters/answer/35769).", + "id": "seo", + "manualDescription": "Run these additional validators on your site to check additional SEO best practices.", + "score": 0.73, "title": "SEO" } }, From 3dba4ee3c198de9db257d5c955be925fc94693b6 Mon Sep 17 00:00:00 2001 From: cjamcl Date: Mon, 6 May 2019 12:05:46 -0700 Subject: [PATCH 16/55] report(redesign): don't init sticky header features for single category reports (#8883) --- .../report/html/renderer/report-ui-features.js | 10 +++++++--- .../report/html/renderer/report-ui-features-test.js | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index 22f78f9b5b7a..e0733f3b5fea 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -84,15 +84,19 @@ class ReportUIFeatures { this._setupMediaQueryListeners(); this._setupExportButton(); this._setupThirdPartyFilter(); - this._setupStickyHeaderElements(); this._setUpCollapseDetailsAfterPrinting(); this._resetUIState(); this._document.addEventListener('keyup', this.onKeyUp); this._document.addEventListener('copy', this.onCopy); - this._document.addEventListener('scroll', this._updateStickyHeaderOnScroll); - window.addEventListener('resize', this._updateStickyHeaderOnScroll); const topbarLogo = this._dom.find('.lh-topbar__logo', this._document); topbarLogo.addEventListener('click', this._toggleDarkTheme); + + // There is only a sticky header when at least 2 categories are present. + if (Object.keys(this.json.categories).length >= 2) { + this._setupStickyHeaderElements(); + this._document.addEventListener('scroll', this._updateStickyHeaderOnScroll); + window.addEventListener('resize', this._updateStickyHeaderOnScroll); + } } /** diff --git a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js index 09c8ed5eac12..4e83cd0cca93 100644 --- a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js +++ b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js @@ -108,6 +108,15 @@ describe('ReportUIFeatures', () => { assert.equal(dom.findAll('.lh-category', container).length, 5); }); + it('should init a report with a single category', () => { + const lhr = JSON.parse(JSON.stringify(sampleResults)); + lhr.categories = { + performance: lhr.categories.performance, + }; + const container = render(lhr); + assert.equal(dom.findAll('.lh-category', container).length, 1); + }); + describe('third-party filtering', () => { let container; From 33941167bea15939c8a3a375cbf7d0a2c1df06bb Mon Sep 17 00:00:00 2001 From: Shane Exterkamp Date: Mon, 6 May 2019 14:22:12 -0700 Subject: [PATCH 17/55] i18n: import tamil translated strings (#8886) --- lighthouse-core/lib/i18n/locales/ta.json | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lighthouse-core/lib/i18n/locales/ta.json b/lighthouse-core/lib/i18n/locales/ta.json index a081a20f86a3..ba2e010b9272 100644 --- a/lighthouse-core/lib/i18n/locales/ta.json +++ b/lighthouse-core/lib/i18n/locales/ta.json @@ -447,10 +447,10 @@ "message": "மதிப்பு" }, "lighthouse-core/audits/dobetterweb/dom-size.js | description": { - "message": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. The sweet spot is a tree depth < 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size)." + "message": "~1,500 DOM உறுப்புகளுக்குக் குறைவான உறுப்புகளைக் கொண்ட பக்கங்களை உலாவிப் பொறியாளர்கள் பரிந்துரைக்கின்றனர். 32 உறுப்புகளுக்குக் குறைவான கிளை அடுக்குகளும் 60க்குக் குறைவான துணைக்கிளை/கிளை உறுப்புகளும் இருப்பது உகந்தது. ஒரு பெரிய DOM, நினைவகப் பயன்பாட்டை அதிகரிக்கலாம், [ஸ்டைல் கணக்கீடுகளை] நீட்டிக்கலாம், மேலும் (https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations) அதிகச் செலவு பிடிக்கும் [தளவமைப்பு மறுசீராக்கங்களை] ஏற்படுத்தலாம் (https://developers.google.com/speed/articles/reflow). [மேலும் அறிக](https://developers.google.com/web/tools/lighthouse/audits/dom-size)." }, "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": { - "message": "{itemCount, plural,\n =1 {1 element}\n other {# elements}\n }" + "message": "{itemCount,plural, =1{1 உறுப்பு}other{# உறுப்புகள்}}" }, "lighthouse-core/audits/dobetterweb/dom-size.js | failureTitle": { "message": "அபரிமிதமான DOM அளவைத் தவிர்க்கவும்" @@ -459,7 +459,7 @@ "message": "அதிகபட்ச DOM கிளை அடுக்கு" }, "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMElements": { - "message": "Total DOM Elements" + "message": "மொத்த DOM உறுப்புக்கள்" }, "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMWidth": { "message": "அதிகபட்சத் துணை உறுப்புகள்" @@ -876,22 +876,22 @@ "message": "SEO" }, "lighthouse-core/config/default-config.js | seoContentGroupDescription": { - "message": "Format your HTML in a way that enables crawlers to better understand your app’s content." + "message": "ஆப்ஸின் உள்ளடக்கத்தை crawlerகள் நன்கு புரிந்துகொள்ள வசதியாக HTMLலைப் பொருத்தமாக வடிவமைக்கவும்." }, "lighthouse-core/config/default-config.js | seoContentGroupTitle": { - "message": "Content Best Practices" + "message": "உள்ளடக்கம் தொடர்பான சிறந்த நடைமுறைகள்" }, "lighthouse-core/config/default-config.js | seoCrawlingGroupDescription": { - "message": "To appear in search results, crawlers need access to your app." + "message": "தேடல் முடிவுகளில் தோன்ற உங்கள் ஆப்ஸிற்கான அணுகல் crawlerகளுக்குத் தேவை." }, "lighthouse-core/config/default-config.js | seoCrawlingGroupTitle": { - "message": "Crawling and Indexing" + "message": "Crawling மற்றும் அட்டவணைப்படுத்துதல்" }, "lighthouse-core/config/default-config.js | seoMobileGroupDescription": { - "message": "Make sure your pages are mobile friendly so users don’t have to pinch or zoom in order to read the content pages. [Learn more](https://developers.google.com/search/mobile-sites/)." + "message": "பக்கங்களின் உள்ளடக்கத்தைப் பயனர்கள் பின்ச் செய்தோ பெரிதாக்கியோ படிக்க வேண்டிய அவசியம் ஏற்படாதவாறு உங்கள் பக்கங்கள் மொபைலுக்கு ஏற்றவையாக இருக்க வேண்டும். [மேலும் அறிக](https://developers.google.com/search/mobile-sites/)." }, "lighthouse-core/config/default-config.js | seoMobileGroupTitle": { - "message": "Mobile Friendly" + "message": "மொபைலுக்கேற்றது" }, "lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": { "message": "தற்காலிக நினைவக TTL" @@ -927,7 +927,7 @@ "message": "உங்கள் பக்கம் ஏற்றப்படுகையில் டிரேஸைப் பதிவுசெய்யும்போது ஏதோ தவறாகிவிட்டது. Lighthouseஸை மீண்டும் இயக்கவும். ({errorCode})" }, "lighthouse-core/lib/lh-error.js | criTimeout": { - "message": "Timeout waiting for initial Debugger Protocol connection." + "message": "முதல் பிழைதிருத்தும் நெறிமுறை இணைப்பிற்கான நேரமுடிவு காத்திருப்பு." }, "lighthouse-core/lib/lh-error.js | didntCollectScreenshots": { "message": "பக்கம் ஏற்றப்பட்டபோது ஸ்க்ரீன்ஷாட்கள் எதையும் Chrome சேகரிக்கவில்லை. பக்கத்தில் உள்ளடக்கம் காண்பிக்கப்படுவதை உறுதிசெய்து, Lighthouseஸை மீண்டும் இயக்கவும். ({errorCode})" @@ -1020,45 +1020,45 @@ "message": "எச்சரிக்கைகள்: " }, "stack-packs/packs/wordpress.js | efficient_animated_content": { - "message": "Consider uploading your GIF to a service which will make it available to embed as an HTML5 video." + "message": "உங்கள் GIFஃபை HTML5 வீடியோவாக உட்பொதிந்து கிடைக்கச் செய்யும் சேவையில் பதிவேற்ற முயலுங்கள்." }, "stack-packs/packs/wordpress.js | offscreen_images": { - "message": "Install a [lazy-load WordPress plugin](https://wordpress.org/plugins/search/lazy+load/) that provides the ability to defer any offscreen images, or switch to a theme that provides that functionality. Also consider using [the AMP plugin](https://wordpress.org/plugins/amp/)." + "message": "[lazy-load WordPress செருகுநிரலை] நிறுவி (https://wordpress.org/plugins/search/lazy+load/) திரைக்கு வெளியிலுள்ள படங்களைத் தேவையான போது ஏற்றும் திறனைப் பெறலாம் அல்லது அந்தத் திறனுள்ள தீமிற்கு மாறலாம். [AMP செருகுநிரலையும்](https://wordpress.org/plugins/amp/) பயன்படுத்திப் பார்க்கவும்." }, "stack-packs/packs/wordpress.js | render_blocking_resources": { - "message": "There are a number of WordPress plugins that can help you [inline critical assets](https://wordpress.org/plugins/search/critical+css/) or [defer less important resources](https://wordpress.org/plugins/search/defer+css+javascript/). Beware that optimizations provided by these plugins may break features of your theme or plugins, so you will likely need to make code changes." + "message": "[முக்கியமான சொத்துகளை முன்னிலைப்படுத்தவும்](https://wordpress.org/plugins/search/critical+css/) அல்லது [முக்கியத்துவம் குறைவான ஆதாரங்களைத் தவிர்க்கவும்](https://wordpress.org/plugins/search/defer+css+javascript/) உங்களுக்கு உதவக்கூடிய பல WordPress செருகுநிரல்கள் உள்ளன. இந்தச் செருகுநிரல்கள் வழங்கும் மேம்படுத்துதல்கள் உங்களின் தீம் அல்லது செருகுநிரலில் உள்ள அம்சத்தைப் பாதிக்கும். அதனால் நீங்கள் குறியீட்டில் சில மாற்றங்களைச் செய்ய வேண்டியிருக்கும்." }, "stack-packs/packs/wordpress.js | time_to_first_byte": { - "message": "Themes, plugins, and server specifications all contribute to server response time. Consider finding a more optimized theme, carefully selecting an optimization plugin, and/or upgrading your server." + "message": "தீம்கள், செருகுநிரல்கள் மற்றும் சேவையக விவரக்குறிப்புகள் அனைத்தும் சேவையகத்தின் வேகத்தை நிர்ணயிக்கும். இன்னும் பல மேம்படுத்தப்பட்ட தீம்மைக் கண்டறிந்து, மேம்படுத்தும் செருகுநிரலைக் கவனமாகத் தேர்ந்தெடுத்து மற்றும்/அல்லது சேவையகத்தை மேம்படுத்தவும்." }, "stack-packs/packs/wordpress.js | total_byte_weight": { - "message": "Consider showing excerpts in your post lists (e.g. via the more tag), reducing the number of posts shown on a given page, breaking your long posts into multiple pages, or using a plugin to lazy-load comments." + "message": "இடுகை பட்டியல்களில் முக்கியமான பகுதியை மட்டும் காட்டலாம் (உதாரணமாக மேலும் என்ற குறிச்சொல்லுடன்), பக்கத்தில் இடுகைகளின் எண்ணிக்கையைக் குறைக்கலாம், ஒரு பெரிய இடுகையைப் பல சின்ன பக்கங்களாகப் பிரிக்கலாம் அல்லது தேவையுள்ள போது மட்டும் கருத்துகளைச் செருகுநிரல்கள் மூலம் ஏற்றலாம்." }, "stack-packs/packs/wordpress.js | unminified_css": { - "message": "A number of [WordPress plugins](https://wordpress.org/plugins/search/minify+css/) can speed up your site by concatenating, minifying, and compressing your styles. You may also want to use a build process to do this minification up-front if possible." + "message": "பல [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/search/minify+javascript/) உங்கள் இணையதளத்தில் உள்ள ஸ்டைல்களைச் சிறிதாக்கி சுருக்கி மற்றும் ஒன்றிணைத்து அதை வேகப்படுத்தலாம். ஸ்கிரிப்ட்டைச் சிறிதாக்க பதிப்பு முறைமையைப் பயன்படுத்த நேரிடலாம்." }, "stack-packs/packs/wordpress.js | unminified_javascript": { - "message": "A number of [WordPress plugins](https://wordpress.org/plugins/search/minify+javascript/) can speed up your site by concatenating, minifying, and compressing your scripts. You may also want to use a build process to do this minification up front if possible." + "message": "பல [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/search/minify+javascript/) உங்கள் இணையதளத்தில் உள்ள ஸ்கிரிப்ட்டைச் சிறிதாக்கி சுருக்கி மற்றும் ஒன்றிணைத்து அதை வேகப்படுத்தலாம். ஸ்கிரிப்ட்டைச் சிறிதாக்க பதிப்பு முறைமையைப் பயன்படுத்த நேரிடலாம்." }, "stack-packs/packs/wordpress.js | unused_css_rules": { - "message": "Consider reducing, or switching, the number of [WordPress plugins](https://wordpress.org/plugins/) loading unused CSS in your page. To identify plugins that are adding extraneous CSS, try running [code coverage](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) in Chrome DevTools. You can identify the theme/plugin responsible from the URL of the stylesheet. Look out for plugins that have many stylesheets in the list which have a lot of red in code coverage. A plugin should only enqueue a stylesheet if it is actually used on the page." + "message": "உங்கள் பக்கத்தில் [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/) ஏற்றும் பயன்படுத்தப்படாத CSSகளின் எண்ணிக்கையைக் குறைக்கவும் அல்லது மாற்றிப் பார்க்கவும். Chrome DevToolsஸில் [குறியீட்டுக் கவரேஜ்] (https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) என்பதை இயக்கி தேவையற்ற CSSஸைச் சேர்க்கும் செருகுநிரல்களைக் கண்டறியலாம். stylesheetடின் URLலில் இதற்குக் காரணமான தீம்/செருகுநிரலைக் கண்டறியலாம். அதிகமாகச் சிகப்பில் உள்ள குறியீட்டு கவரேஜைக் கொண்ட பட்டியலில் பல stylesheetகளைக் கொண்ட செருகுநிரல்களைக் கண்டறியவும். இணையப் பக்கத்தில் பயன்படுத்தும் பட்சத்தில் ஒரு செருகுநிரல் ஒரு stylesheetடை மட்டுமே வரிசையில் சேர்க்க வேண்டும்." }, "stack-packs/packs/wordpress.js | unused_javascript": { - "message": "Consider reducing, or switching, the number of [WordPress plugins](https://wordpress.org/plugins/) loading unused JavaScript in your page. To identify plugins that are adding extraneous JS, try running [code coverage](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) in Chrome DevTools. You can identify the theme/plugin responsible from the URL of the script. Look out for plugins that have many scripts in the list which have a lot of red in code coverage. A plugin should only enqueue a script if it is actually used on the page." + "message": "உங்கள் பக்கத்தில் [WordPress செருகுநிரல்கள்](https://wordpress.org/plugins/) ஏற்றும் பயன்படுத்தப்படாத JavaScriptகளின் எண்ணிக்கையைக் குறைக்கவும் அல்லது மாற்றிப் பார்க்கவும். Chrome DevToolsஸில் [குறியீட்டுக் கவரேஜ்](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) என்பதை இயக்கி தேவையற்ற JSகளைச் சேர்க்கும் செருகுநிரல்களைக் கண்டறியவும். ஸ்கிரிப்ட்டின் URLலில் இதற்குக் காரணமான தீம்/செருகுநிரலைக் கண்டறியலாம். அதிகமாகச் சிகப்பில் உள்ள குறியீட்டு கவரேஜைக் கொண்ட பட்டியலில் பல ஸ்கிரிப்ட்களைக் கொண்ட செருகுநிரல்களைக் கண்டறியவும். இணையப் பக்கத்தில் பயன்படுத்தும் பட்சத்தில் ஒரு செருகுநிரல் ஒரு ஸ்கிரிப்ட்டை மட்டுமே வரிசையில் சேர்க்க வேண்டும்." }, "stack-packs/packs/wordpress.js | uses_long_cache_ttl": { - "message": "Read about [Browser Caching in WordPress](https://codex.wordpress.org/WordPress_Optimization#Browser_Caching)." + "message": "[WordPressஸில் உலாவியின் தற்காலிக சேமிப்பு] பற்றி அறிய: (https://codex.wordpress.org/WordPress_Optimization#Browser_Caching)." }, "stack-packs/packs/wordpress.js | uses_optimized_images": { - "message": "Consider using an [image optimization WordPress plugin](https://wordpress.org/plugins/search/optimize+images/) that compresses your images while retaining quality." + "message": "[படங்களை மேம்படுத்தும் WordPress செருகுநிரல்](https://wordpress.org/plugins/search/optimize+images/) என்பதைப் பயன்படுத்தி உங்கள் படங்களின் தரத்தை இழக்காமல் சுருங்கச் செய்யலாம்." }, "stack-packs/packs/wordpress.js | uses_responsive_images": { - "message": "Upload images directly through the [media library](https://codex.wordpress.org/Media_Library_Screen) to ensure that the required image sizes are available, and then insert them from the media library or use the image widget to ensure the optimal image sizes are used (including those for the responsive breakpoints). Avoid using `Full Size` images unless the dimensions are adequate for their usage. [Learn More](https://codex.wordpress.org/Inserting_Images_into_Posts_and_Pages#Image_Size)." + "message": "[மீடியா லைப்ரரி](https://codex.wordpress.org/Media_Library_Screen) மூலம் படத்தை நேரடியாகப் பதிவேற்றி சரியான அளவில் கிடைக்கிறதா என்று உறுதிப்படுத்தலாம், பிறகு மீடியா லைப்ரரியிலிருந்தோ பட விட்ஜெட்டைப் பயன்படுத்தியோ மேம்படுத்தப்பட்ட அளவில் படங்கள் பயன்படுத்தப்படுகிறதா என்பதை உறுதி செய்யவும் (பதிலளிக்கும் அளவு வரம்புகள் உட்பட). பயன்படுத்துவதற்கு ஏதுவான அளவுகள் உள்ளபோது மட்டுமே 'முழு அளவு' படங்களைப் பயன்படுத்தவும். [மேலும் அறிக](https://codex.wordpress.org/Inserting_Images_into_Posts_and_Pages#Image_Size)." }, "stack-packs/packs/wordpress.js | uses_text_compression": { - "message": "You can enable text compression in your web server configuration." + "message": "இணையச் சேவையக உள்ளமைவில் உரை சுருக்குதலை இயக்கலாம்." }, "stack-packs/packs/wordpress.js | uses_webp_images": { - "message": "Consider using a [plugin](https://wordpress.org/plugins/search/convert+webp/) or service that will automatically convert your uploaded images to the optimal formats." + "message": "நீங்கள் பதிவேற்றிய படங்களை மேம்படுத்தப்பட்ட வடிவத்திற்குத் தானாக மாற்ற ஒரு [செருகுநிரலையோ](https://wordpress.org/plugins/search/convert+webp/) சேவையையோ பயன்படுத்திப் பாருங்கள்." } } \ No newline at end of file From acfa77796a64b649ea448f15889f5a099a484191 Mon Sep 17 00:00:00 2001 From: Shane Exterkamp Date: Mon, 6 May 2019 17:31:38 -0700 Subject: [PATCH 18/55] new_audit: add apple-touch-icon PWA audit (#8857) --- .../test/cli/__snapshots__/index-test.js.snap | 8 ++ .../test/smokehouse/pwa-expectations.js | 6 ++ .../test/smokehouse/pwa2-expectations.js | 9 ++ .../test/smokehouse/pwa3-expectations.js | 3 + lighthouse-core/audits/apple-touch-icon.js | 71 ++++++++++++++ lighthouse-core/config/default-config.js | 2 + lighthouse-core/lib/i18n/en-US.json | 16 +++ .../test/audits/apple-touch-icon-test.js | 98 +++++++++++++++++++ .../html/renderer/category-renderer-test.js | 4 +- lighthouse-core/test/results/sample_v2.json | 27 ++++- proto/sample_v2_round_trip.json | 21 +++- 11 files changed, 261 insertions(+), 4 deletions(-) create mode 100644 lighthouse-core/audits/apple-touch-icon.js create mode 100644 lighthouse-core/test/audits/apple-touch-icon-test.js diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap index d1e45ec5d810..13268b7bcf6d 100644 --- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap +++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap @@ -69,6 +69,9 @@ Object { Object { "path": "installable-manifest", }, + Object { + "path": "apple-touch-icon", + }, Object { "path": "splash-screen", }, @@ -923,6 +926,11 @@ Object { "id": "without-javascript", "weight": 1, }, + Object { + "group": "pwa-optimized", + "id": "apple-touch-icon", + "weight": 1, + }, Object { "id": "pwa-cross-browser", "weight": 0, diff --git a/lighthouse-cli/test/smokehouse/pwa-expectations.js b/lighthouse-cli/test/smokehouse/pwa-expectations.js index b4a91c98c0b1..85616240be77 100644 --- a/lighthouse-cli/test/smokehouse/pwa-expectations.js +++ b/lighthouse-cli/test/smokehouse/pwa-expectations.js @@ -66,6 +66,9 @@ module.exports = [ 'content-width': { score: 1, }, + 'apple-touch-icon': { + score: 1, + }, // "manual" audits. Just verify in the results. 'pwa-cross-browser': { @@ -128,6 +131,9 @@ module.exports = [ 'content-width': { score: 1, }, + 'apple-touch-icon': { + score: 1, + }, // "manual" audits. Just verify in the results. 'pwa-cross-browser': { diff --git a/lighthouse-cli/test/smokehouse/pwa2-expectations.js b/lighthouse-cli/test/smokehouse/pwa2-expectations.js index 3a56038d3e74..b8b93d09b62d 100644 --- a/lighthouse-cli/test/smokehouse/pwa2-expectations.js +++ b/lighthouse-cli/test/smokehouse/pwa2-expectations.js @@ -61,6 +61,12 @@ module.exports = [ 'content-width': { score: 1, }, + 'apple-touch-icon': { + score: 1, + warnings: [ + /apple-touch-icon-precomposed/, + ], + }, // "manual" audits. Just verify in the results. 'pwa-cross-browser': { @@ -123,6 +129,9 @@ module.exports = [ 'content-width': { score: 1, }, + 'apple-touch-icon': { + score: 0, + }, // "manual" audits. Just verify in the results. 'pwa-cross-browser': { diff --git a/lighthouse-cli/test/smokehouse/pwa3-expectations.js b/lighthouse-cli/test/smokehouse/pwa3-expectations.js index 351a34272daf..d05f37387f03 100644 --- a/lighthouse-cli/test/smokehouse/pwa3-expectations.js +++ b/lighthouse-cli/test/smokehouse/pwa3-expectations.js @@ -58,6 +58,9 @@ module.exports = [ 'content-width': { score: 1, }, + 'apple-touch-icon': { + score: 1, + }, // "manual" audits. Just verify in the results. 'pwa-cross-browser': { diff --git a/lighthouse-core/audits/apple-touch-icon.js b/lighthouse-core/audits/apple-touch-icon.js new file mode 100644 index 000000000000..46313a29e1e6 --- /dev/null +++ b/lighthouse-core/audits/apple-touch-icon.js @@ -0,0 +1,71 @@ +/** + * @license Copyright 2019 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 Audit = require('./audit.js'); +const i18n = require('../lib/i18n/i18n.js'); + +/** + * @fileoverview Audits if a page has an `apple-touch-icon` link element with a valid href. + */ + +const UIStrings = { + /** Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page contains a valid iOS touch icon. "apple-touch-icon" is an HTML attribute value and should not be translated. */ + title: 'Provides a valid `apple-touch-icon`', + /** Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page does not contain a valid iOS touch icon. "apple-touch-icon" is an HTML attribute value and should not be translated. */ + failureTitle: 'Does not provide a valid `apple-touch-icon`', + /** Description of a Lighthouse audit that tells the user what having a valid apple-touch-icon does. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. "apple-touch-icon" is an HTML attribute value and should not be translated. */ + description: 'For ideal appearance on iOS when users add to the home screen, define an ' + + 'apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. ' + + '[Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).', + /** Warning that HTML attribute `apple-touch-icon-precomposed` should not be used in favor of `apple-touch-icon`. "apple-touch-icon-precomposed" and "apple-touch-icon" are HTML attribute values and should not be translated. */ + precomposedWarning: '`apple-touch-icon-precomposed` is out of date; ' + + '`apple-touch-icon` is preferred.', +}; + +const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); + +class AppleTouchIcon extends Audit { + /** + * @return {LH.Audit.Meta} + */ + static get meta() { + return { + id: 'apple-touch-icon', + title: str_(UIStrings.title), + failureTitle: str_(UIStrings.failureTitle), + description: str_(UIStrings.description), + requiredArtifacts: ['LinkElements'], + }; + } + + /** + * @param {LH.Artifacts} artifacts + * @return {LH.Audit.Product} + */ + static audit(artifacts) { + const appleTouchIcons = artifacts.LinkElements + .filter(el => el.rel === 'apple-touch-icon' || el.rel === 'apple-touch-icon-precomposed') + .filter(el => !!el.href); + + // Audit passes if an `apple-touch-icon` exists. + const passed = appleTouchIcons.length !== 0; + + const warnings = []; + if (appleTouchIcons.filter(el => el.rel === 'apple-touch-icon-precomposed').length !== 0 + && appleTouchIcons.filter(el => el.rel === 'apple-touch-icon').length === 0) { + warnings.push(str_(UIStrings.precomposedWarning)); + } + + return { + score: passed ? 1 : 0, + warnings, + }; + } +} + +module.exports = AppleTouchIcon; +module.exports.UIStrings = UIStrings; diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js index 9184e54928f1..42672983cf19 100644 --- a/lighthouse-core/config/default-config.js +++ b/lighthouse-core/config/default-config.js @@ -179,6 +179,7 @@ const defaultConfig = { 'critical-request-chains', 'redirects', 'installable-manifest', + 'apple-touch-icon', 'splash-screen', 'themed-omnibox', 'content-width', @@ -523,6 +524,7 @@ const defaultConfig = { {id: 'content-width', weight: 1, group: 'pwa-optimized'}, {id: 'viewport', weight: 2, group: 'pwa-optimized'}, {id: 'without-javascript', weight: 1, group: 'pwa-optimized'}, + {id: 'apple-touch-icon', weight: 1, group: 'pwa-optimized'}, // Manual audits {id: 'pwa-cross-browser', weight: 0}, {id: 'pwa-page-transitions', weight: 0}, diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json index 02e444d58783..adfc34c8fa21 100644 --- a/lighthouse-core/lib/i18n/en-US.json +++ b/lighthouse-core/lib/i18n/en-US.json @@ -423,6 +423,22 @@ "message": "`