Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: surface LCP element in a diagnostic audit #10517

Merged
merged 45 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c719e14
Adding TraceNode type
Beytoven Mar 20, 2020
de92e2f
Remove log statements
Beytoven Mar 24, 2020
03aeb94
LCP Surfacing: Take 1
Beytoven Mar 27, 2020
32a5028
LCP Surfacing: Take 2
Beytoven Mar 27, 2020
e05c36f
Quick lint fixes
Beytoven Mar 30, 2020
597c071
Rename type to metricTag
Beytoven Apr 2, 2020
f997276
Update gatherer to collect CLS nodes; not yet limiting to top 5
Beytoven Apr 6, 2020
e869cd2
Find top 5 CLS nodes
Beytoven Apr 9, 2020
be87033
Revert "Rename type to metricTag"
Beytoven Apr 9, 2020
9d305f6
Move LCP Node into it's own diagnostics audit
Beytoven Apr 10, 2020
11b59db
Add some unit tests. Clean up strings a bit
Beytoven Apr 13, 2020
bd716ba
Update sample_v2_round_trip.json
Beytoven Apr 14, 2020
02cac2d
Adding back some refactoring to make trace nodes expandable
Beytoven Apr 14, 2020
f21f316
Lint fixes
Beytoven Apr 14, 2020
e2d621a
Remove lhtemp attribute after fetching node data
Beytoven Apr 14, 2020
30eb87a
Update snapshot
Beytoven Apr 14, 2020
32d7ea0
Added smoke test
Beytoven Apr 17, 2020
8ba5923
Fix unit test
Beytoven Apr 17, 2020
4e07f98
Add comment
Beytoven Apr 17, 2020
a3698cd
Add instanbul ignore statement
Beytoven Apr 17, 2020
a3a0a1c
Merge branch 'master' into lcp-node-wo-rebase
paulirish Apr 21, 2020
036cda9
18 now
paulirish Apr 21, 2020
debfa2a
Update lighthouse-core/gather/gatherers/trace-nodes.js
Beytoven Apr 22, 2020
0749c9d
Addressing comments on TraceNodes gatherer
Beytoven Apr 23, 2020
25d965d
Pass constant into collectNodes
Beytoven Apr 23, 2020
8f7f5a4
Improved temp attribute removal
Beytoven Apr 23, 2020
f4fef46
Expand abbreviated name and add path to smoke test
Beytoven Apr 24, 2020
3289213
Cleaning up
Beytoven Apr 24, 2020
f94a576
Remove proto sample json
Beytoven Apr 24, 2020
196aeef
Update sample_json again
Beytoven Apr 27, 2020
54707a9
Pluralize i18n displayValue for LCP
Beytoven Apr 27, 2020
32578e4
Apply suggestions from Connor
Beytoven Apr 27, 2020
ce1ce71
Move element column header into i18n lib and remove added collisions
Beytoven Apr 27, 2020
2112020
node_id not optional
Beytoven Apr 27, 2020
e4f21e1
Fix sample artifacts to generate expected sample json
Beytoven Apr 28, 2020
c81a140
Renaming things from node to element
Beytoven Apr 28, 2020
c315fc9
Update sample_json once again
Beytoven Apr 28, 2020
5677171
More renaming
Beytoven Apr 28, 2020
10f3549
Update sample_v2 with trace element snippet
Beytoven Apr 29, 2020
ce436fd
Merge branch 'master' into lcp-node
Beytoven Apr 29, 2020
dadc138
Rename nodePath to devtoolsNodePath
Beytoven Apr 29, 2020
cb02c32
Merge branch 'master' into lcp-node
Beytoven Apr 30, 2020
0162497
Merge branch 'master' into lcp-node
Beytoven May 1, 2020
f239fac
Move fileoverview
Beytoven May 1, 2020
88e1c16
Update lighthouse-core/gather/gatherers/trace-elements.js
Beytoven May 1, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ Object {
Object {
"path": "third-party-summary",
},
Object {
"path": "largest-contentful-paint-element",
},
Object {
"path": "manual/pwa-cross-browser",
},
Expand Down Expand Up @@ -935,6 +938,11 @@ Object {
"id": "third-party-summary",
"weight": 0,
},
Object {
"group": "diagnostics",
"id": "largest-contentful-paint-element",
"weight": 0,
},
Object {
"group": "diagnostics",
"id": "uses-http2",
Expand Down Expand Up @@ -1311,6 +1319,9 @@ Object {
Object {
"path": "accessibility",
},
Object {
"path": "trace-elements",
},
],
"loadFailureMode": "fatal",
"networkQuietThresholdMs": 1000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,21 @@ module.exports = [
],
},
},
'largest-contentful-paint-element': {
score: null,
displayValue: '1 element found',
details: {
items: [
{
node: {
type: 'node',
nodeLabel: 'img',
path: '2,HTML,1,BODY,0,IMG',
},
Beytoven marked this conversation as resolved.
Show resolved Hide resolved
},
],
},
},
},
},
},
Expand Down
4 changes: 1 addition & 3 deletions lighthouse-core/audits/dobetterweb/dom-size.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ const UIStrings = {
'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://web.dev/dom-size).',
/** Table column header for the type of statistic. These statistics describe how big the DOM is (count of DOM elements, children, depth). */
columnStatistic: 'Statistic',
/** Table column header for the DOM element. Each DOM element is described with its HTML representation. */
columnElement: 'Element',
/** Table column header for the observed value of the DOM statistic. */
columnValue: 'Value',
/** [ICU Syntax] Label for an audit identifying the number of DOM elements found in the page. */
Expand Down Expand Up @@ -92,7 +90,7 @@ class DOMSize extends Audit {
/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
{key: 'statistic', itemType: 'text', text: str_(UIStrings.columnStatistic)},
{key: 'element', itemType: 'code', text: str_(UIStrings.columnElement)},
{key: 'element', itemType: 'code', text: str_(i18n_.UIStrings.columnElement)},
{key: 'value', itemType: 'numeric', text: str_(UIStrings.columnValue)},
];

Expand Down
78 changes: 78 additions & 0 deletions lighthouse-core/audits/largest-contentful-paint-element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

const Audit = require('./audit.js');
const i18n = require('../lib/i18n/i18n.js');

const UIStrings = {
/** Descriptive title of a diagnostic audit that provides the element that was determined to be the Largest Contentful Paint. */
title: 'Largest Contentful Paint element',
/** Description of a Lighthouse audit that tells the user that the element shown was determined to be the Largest Contentful Paint. */
description: 'This is the element that was identified as the Largest Contentful Paint. ' +
'[Learn More](https://web.dev/lighthouse-largest-contentful-paint)',
/** [ICU Syntax] Label for the audit identifying if an element was found. */
displayValue: `{itemCount, plural,
=1 {1 element found}
other {# elements found}
}`,
};

const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);

class LargestContentfulPaintElement extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'largest-contentful-paint-element',
title: str_(UIStrings.title),
description: str_(UIStrings.description),
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
requiredArtifacts: ['TraceElements'],
};
}

/**
* @param {LH.Artifacts} artifacts
* @return {LH.Audit.Product}
*/
static audit(artifacts) {
const lcpElement =
artifacts.TraceElements.find(node => node.metricName === 'largest-contentful-paint');
const lcpElementDetails = [];
if (lcpElement) {
lcpElementDetails.push({
node: /** @type {LH.Audit.Details.NodeValue} */ ({
type: 'node',
path: lcpElement.devtoolsNodePath,
selector: lcpElement.selector,
nodeLabel: lcpElement.nodeLabel,
snippet: lcpElement.snippet,
}),
});
}

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
{key: 'node', itemType: 'node', text: str_(i18n.UIStrings.columnElement)},
];

const details = Audit.makeTableDetails(headings, lcpElementDetails);

const displayValue = str_(UIStrings.displayValue, {itemCount: lcpElementDetails.length});

return {
score: 1,
displayValue,
details,
};
}
}

module.exports = LargestContentfulPaintElement;
module.exports.UIStrings = UIStrings;
3 changes: 3 additions & 0 deletions lighthouse-core/config/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const defaultConfig = {
'seo/robots-txt',
'seo/tap-targets',
'accessibility',
'trace-elements',
],
},
{
Expand Down Expand Up @@ -230,6 +231,7 @@ const defaultConfig = {
'timing-budget',
'resource-summary',
'third-party-summary',
'largest-contentful-paint-element',
'manual/pwa-cross-browser',
'manual/pwa-page-transitions',
'manual/pwa-each-page-has-url',
Expand Down Expand Up @@ -448,6 +450,7 @@ const defaultConfig = {
{id: 'timing-budget', weight: 0, group: 'budgets'},
{id: 'resource-summary', weight: 0, group: 'diagnostics'},
{id: 'third-party-summary', weight: 0, group: 'diagnostics'},
{id: 'largest-contentful-paint-element', weight: 0, group: 'diagnostics'},
{id: 'uses-http2', weight: 0, group: 'diagnostics'},
{id: 'uses-passive-event-listeners', weight: 0, group: 'diagnostics'},
{id: 'no-document-write', weight: 0, group: 'diagnostics'},
Expand Down
105 changes: 105 additions & 0 deletions lighthouse-core/gather/gatherers/trace-elements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

/**
* @fileoverview
* This gatherer identifies the Largest Contentful Paint element from the trace. Since the trace only has the nodeId of the element,
* we temporarily add an attribute so that we can identify the element in the DOM.
*/

const Gatherer = require('./gatherer.js');
const pageFunctions = require('../../lib/page-functions.js');
const TraceProcessor = require('../../lib/tracehouse/trace-processor.js');

const LH_ATTRIBUTE_MARKER = 'lhtemp';

/**
* @param {string} attributeMarker
* @return {LH.Artifacts['TraceElements']}
*/
/* istanbul ignore next */
function collectTraceElements(attributeMarker) {
/** @type {Array<HTMLElement>} */
// @ts-ignore - put into scope via stringification
const markedElements = getElementsInDocument('[' + attributeMarker + ']'); // eslint-disable-line no-undef
/** @type {LH.Artifacts['TraceElements']} */
const TraceElements = [];
for (const element of markedElements) {
const metricName = element.getAttribute(attributeMarker) || '';
element.removeAttribute(attributeMarker);
// @ts-ignore - put into scope via stringification
TraceElements.push({
metricName,
// @ts-ignore - put into scope via stringification
devtoolsNodePath: getNodePath(element), // eslint-disable-line no-undef
// @ts-ignore - put into scope via stringification
selector: getNodeSelector(element), // eslint-disable-line no-undef
// @ts-ignore - put into scope via stringification
nodeLabel: getNodeLabel(element), // eslint-disable-line no-undef
// @ts-ignore - put into scope via stringification
snippet: getOuterHTMLSnippet(element), // eslint-disable-line no-undef
});
}
return TraceElements;
}

class TraceElements extends Gatherer {
/**
* @param {LH.TraceEvent | undefined} lcpEvent
* @return {number | undefined}
*/
static getNodeIDFromTraceEvent(lcpEvent) {
return lcpEvent && lcpEvent.args &&
lcpEvent.args.data && lcpEvent.args.data.nodeId;
}

/**
* @param {LH.Gatherer.PassContext} passContext
* @param {LH.Gatherer.LoadData} loadData
* @return {Promise<LH.Artifacts['TraceElements']>}
*/
async afterPass(passContext, loadData) {
const driver = passContext.driver;
if (!loadData.trace) {
throw new Error('Trace is missing!');
}
const traceOfTab = TraceProcessor.computeTraceOfTab(loadData.trace);
const lcpEvent = traceOfTab.largestContentfulPaintEvt;
/** @type {Array<number>} */
const backendNodeIds = [];

const lcpNodeId = TraceElements.getNodeIDFromTraceEvent(lcpEvent);
if (lcpNodeId) {
backendNodeIds.push(lcpNodeId);
}
// DOM.getDocument is necessary for pushNodesByBackendIdsToFrontend to properly retrieve nodeIds.
await driver.sendCommand('DOM.getDocument', {depth: -1, pierce: true});
const translatedIds = await driver.sendCommand('DOM.pushNodesByBackendIdsToFrontend',
{backendNodeIds: backendNodeIds});

// Mark the LCP element so we can find it in the page.
await driver.sendCommand('DOM.setAttributeValue', {
nodeId: translatedIds.nodeIds[0],
name: LH_ATTRIBUTE_MARKER,
value: 'largest-contentful-paint',
});

const expression = `(() => {
${pageFunctions.getElementsInDocumentString};
${pageFunctions.getNodePathString};
${pageFunctions.getNodeSelectorString};
${pageFunctions.getNodeLabelString};
${pageFunctions.getOuterHTMLSnippetString};

return (${collectTraceElements})('${LH_ATTRIBUTE_MARKER}');
})()`;

return driver.evaluateAsync(expression, {useIsolation: true});
}
}

module.exports = TraceElements;
2 changes: 2 additions & 0 deletions lighthouse-core/lib/i18n/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ const UIStrings = {
columnSource: 'Source',
/** Label for a column in a data table; entries will be how much a predetermined budget has been exeeded by. Depending on the context, this number could represent an excess in quantity or size of network requests, or, an excess in the duration of time that it takes for the page to load.*/
columnOverBudget: 'Over Budget',
/** Label for a column in a data table; entries will be a representation of a DOM element. */
columnElement: 'Element',
/** Label for a row in a data table; entries will be the total number and byte size of all resources loaded by a web page. */
totalResourceType: 'Total',
/** Label for a row in a data table; entries will be the total number and byte size of all 'Document' resources loaded by a web page. */
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/ar-XB.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "‏‮Page‬‏ ‏‮has‬‏ ‏‮the‬‏ ‏‮HTML‬‏ ‏‮doctype‬‏"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "‏‮Element‬‏"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "‏‮Statistic‬‏"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "الصفحة تحتوي على HTML DOCTYPE"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "العنصر"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "الإحصائية"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "Страницата съдържа doctype на HTML"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Елемент"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Статистически данни"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "La pàgina té el tipus de document HTML"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Element"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Estadística"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "Stránka má deklaraci typu dokumentu (DOCTYPE) HTML"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Prvek"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Statistika"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "Siden har dokumenttypen HTML"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Element"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Statistik"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "Seite verfügt über den HTML-DOCTYPE"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Element"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Statistik"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "Η σελίδα έχει τύπο εγγράφου (doctype) HTML"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Στοιχείο"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Στατιστικό στοιχείο"
},
Expand Down
3 changes: 0 additions & 3 deletions lighthouse-core/lib/i18n/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,6 @@
"lighthouse-core/audits/dobetterweb/doctype.js | title": {
"message": "Page has the HTML doctype"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
"message": "Element"
},
"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
"message": "Statistic"
},
Expand Down
Loading