diff --git a/lighthouse-core/audits/seo/font-size.js b/lighthouse-core/audits/seo/font-size.js index a2efea7cce84..5720cd9f049a 100644 --- a/lighthouse-core/audits/seo/font-size.js +++ b/lighthouse-core/audits/seo/font-size.js @@ -36,13 +36,15 @@ function getUniqueFailingRules(fontSizeArtifact) { /** @type {Map} */ const failingRules = new Map(); - fontSizeArtifact.forEach(({cssRule, fontSize, textLength, node}) => { - const artifactId = getFontArtifactId(cssRule, node); + fontSizeArtifact.forEach((failingNodeData) => { + const {nodeId, cssRule, fontSize, textLength, parentNode} = failingNodeData; + const artifactId = getFontArtifactId(cssRule, nodeId); const failingRule = failingRules.get(artifactId); if (!failingRule) { failingRules.set(artifactId, { - node, + nodeId, + parentNode, cssRule, fontSize, textLength, @@ -76,11 +78,11 @@ function getAttributeMap(attributes = []) { /** * TODO: return unique selector, like axe-core does, instead of just id/class/name of a single node - * @param {FailingNodeData['node']} node + * @param {FailingNodeData['parentNode']} parentNode * @returns {string} */ -function getSelector(node) { - const attributeMap = getAttributeMap(node.attributes); +function getSelector(parentNode) { + const attributeMap = getAttributeMap(parentNode.attributes); if (attributeMap.has('id')) { return '#' + attributeMap.get('id'); @@ -91,40 +93,40 @@ function getSelector(node) { } } - return node.localName.toLowerCase(); + return parentNode.nodeName.toLowerCase(); } /** - * @param {FailingNodeData['node']} node + * @param {FailingNodeData['parentNode']} parentNode * @return {LH.Audit.Details.NodeValue} */ -function nodeToTableNode(node) { - const attributes = node.attributes || []; +function nodeToTableNode(parentNode) { + const attributes = parentNode.attributes || []; const attributesString = attributes.map((value, idx) => (idx % 2 === 0) ? ` ${value}` : `="${value}"` ).join(''); return { type: 'node', - selector: node.parentNode ? getSelector(node.parentNode) : '', - snippet: `<${node.localName}${attributesString}>`, + selector: parentNode.parentNode ? getSelector(parentNode.parentNode) : '', + snippet: `<${parentNode.nodeName.toLowerCase()}${attributesString}>`, }; } /** * @param {string} baseURL * @param {FailingNodeData['cssRule']} styleDeclaration - * @param {FailingNodeData['node']} node + * @param {FailingNodeData['parentNode']} parentNode * @returns {{source: LH.Audit.Details.UrlValue | LH.Audit.Details.SourceLocationValue | LH.Audit.Details.CodeValue, selector: string | LH.Audit.Details.NodeValue}} */ -function findStyleRuleSource(baseURL, styleDeclaration, node) { +function findStyleRuleSource(baseURL, styleDeclaration, parentNode) { if (!styleDeclaration || styleDeclaration.type === 'Attributes' || styleDeclaration.type === 'Inline' ) { return { source: {type: 'url', value: baseURL}, - selector: nodeToTableNode(node), + selector: nodeToTableNode(parentNode), }; } @@ -198,16 +200,16 @@ function findStyleRuleSource(baseURL, styleDeclaration, node) { /** * @param {FailingNodeData['cssRule']} styleDeclaration - * @param {FailingNodeData['node']} node + * @param {number} textNodeId * @return {string} */ -function getFontArtifactId(styleDeclaration, node) { +function getFontArtifactId(styleDeclaration, textNodeId) { if (styleDeclaration && styleDeclaration.type === 'Regular') { const startLine = styleDeclaration.range ? styleDeclaration.range.startLine : 0; const startColumn = styleDeclaration.range ? styleDeclaration.range.startColumn : 0; return `${styleDeclaration.styleSheetId}@${startLine}:${startColumn}`; } else { - return `node_${node.nodeId}`; + return `node_${textNodeId}`; } } @@ -274,9 +276,9 @@ class FontSize extends Audit { ]; const tableData = failingRules.sort((a, b) => b.textLength - a.textLength) - .map(({cssRule, textLength, fontSize, node}) => { + .map(({cssRule, textLength, fontSize, parentNode}) => { const percentageOfAffectedText = textLength / totalTextLength * 100; - const origin = findStyleRuleSource(pageUrl, cssRule, node); + const origin = findStyleRuleSource(pageUrl, cssRule, parentNode); return { source: origin.source, diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index 5681c43b8366..f0d590f66ecf 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -1204,19 +1204,6 @@ class Driver { return new LHElement(targetNode, this); } - /** - * Returns the flattened list of all DOM nodes within the document. - * @param {boolean=} pierce Whether to pierce through shadow trees and iframes. - * True by default. - * @return {Promise>} The found nodes, or [], resolved in a promise - */ - async getNodesInDocument(pierce = true) { - const flattenedDocument = await this.sendCommand('DOM.getFlattenedDocument', - {depth: -1, pierce}); - - return flattenedDocument.nodes ? flattenedDocument.nodes : []; - } - /** * Resolves a backend node ID (from a trace event, protocol, etc) to the object ID for use with * `Runtime.callFunctionOn`. `undefined` means the node could not be found. diff --git a/lighthouse-core/gather/gatherers/seo/font-size.js b/lighthouse-core/gather/gatherers/seo/font-size.js index 0ad5b636cadb..d900fbb7acc7 100644 --- a/lighthouse-core/gather/gatherers/seo/font-size.js +++ b/lighthouse-core/gather/gatherers/seo/font-size.js @@ -24,38 +24,8 @@ const MAX_NODES_SOURCE_RULE_FETCHED = 50; // number of nodes to fetch the source /** @typedef {import('../../driver.js')} Driver */ /** @typedef {LH.Artifacts.FontSize['analyzedFailingNodesData'][0]} NodeFontData */ -/** @typedef {LH.Artifacts.FontSize.DomNodeMaybeWithParent} DomNodeMaybeWithParent*/ /** @typedef {Map} BackendIdsToFontData */ -/** - * @param {LH.Artifacts.FontSize.DomNodeMaybeWithParent=} node - * @returns {node is LH.Artifacts.FontSize.DomNodeWithParent} - */ -function nodeInBody(node) { - if (!node) { - return false; - } - if (node.nodeName === 'BODY') { - return true; - } - return nodeInBody(node.parentNode); -} - -/** - * Get list of all nodes from the document body. - * - * @param {Driver} driver - * @returns {Promise} - */ -async function getAllNodesFromBody(driver) { - const nodes = /** @type {DomNodeMaybeWithParent[]} */ (await driver.getNodesInDocument()); - /** @type {Map} */ - const nodeMap = new Map(); - nodes.forEach(node => nodeMap.set(node.nodeId, node)); - nodes.forEach(node => (node.parentNode = nodeMap.get(node.parentId))); - return nodes.filter(nodeInBody); -} - /** * @param {LH.Crdp.CSS.CSSStyle} [style] * @return {boolean} @@ -187,12 +157,12 @@ function getTextLength(text) { /** * @param {Driver} driver - * @param {LH.Crdp.DOM.Node} node text node + * @param {number} nodeId text node * @returns {Promise} */ -async function fetchSourceRule(driver, node) { +async function fetchSourceRule(driver, nodeId) { const matchedRules = await driver.sendCommand('CSS.getMatchedStylesForNode', { - nodeId: node.nodeId, + nodeId, }); const sourceRule = getEffectiveFontRule(matchedRules); if (!sourceRule) return undefined; @@ -214,12 +184,22 @@ class FontSize extends Gatherer { * @param {Array} failingNodes */ static async fetchFailingNodeSourceRules(driver, failingNodes) { - const analysisPromises = failingNodes + const nodesToAnalyze = failingNodes .sort((a, b) => b.textLength - a.textLength) - .slice(0, MAX_NODES_SOURCE_RULE_FETCHED) - .map(async failingNode => { + .slice(0, MAX_NODES_SOURCE_RULE_FETCHED); + + // DOM.getDocument is necessary for pushNodesByBackendIdsToFrontend to properly retrieve nodeIds if the `DOM` domain was enabled before this gatherer, invoke it to be safe. + await driver.sendCommand('DOM.getDocument', {depth: -1, pierce: true}); + + const {nodeIds} = await driver.sendCommand('DOM.pushNodesByBackendIdsToFrontend', { + backendNodeIds: nodesToAnalyze.map(node => node.parentNode.backendNodeId), + }); + + const analysisPromises = nodesToAnalyze + .map(async (failingNode, i) => { + failingNode.nodeId = nodeIds[i]; try { - const cssRule = await fetchSourceRule(driver, failingNode.node); + const cssRule = await fetchSourceRule(driver, nodeIds[i]); failingNode.cssRule = cssRule; } catch (err) { // The node was deleted. We don't need to distinguish between lack-of-rule @@ -240,25 +220,35 @@ class FontSize extends Gatherer { } /** - * Maps backendNodeId of TextNodes to {fontSize, textLength}. - * + * Returns the TextNodes in a DOM Snapshot. + * Every entry is associated with a TextNode in the layout tree (not display: none). * @param {LH.Crdp.DOMSnapshot.CaptureSnapshotResponse} snapshot - * @return {BackendIdsToFontData} */ - calculateBackendIdsToFontData(snapshot) { + getTextNodesInLayoutFromSnapshot(snapshot) { const strings = snapshot.strings; + /** @param {number} index */ + const getString = (index) => strings[index]; + /** @param {number} index */ + const getFloat = (index) => parseFloat(strings[index]); - /** @type {BackendIdsToFontData} */ - const backendIdsToFontData = new Map(); - + const textNodesData = []; for (let j = 0; j < snapshot.documents.length; j++) { // `doc` is a flattened property list describing all the Nodes in a document, with all string // values deduped in the `strings` array. const doc = snapshot.documents[j]; - if (!doc.nodes.backendNodeId) { + if (!doc.nodes.backendNodeId || !doc.nodes.parentIndex || + !doc.nodes.attributes || !doc.nodes.nodeName) { throw new Error('Unexpected response from DOMSnapshot.captureSnapshot.'); } + const nodes = /** @type {Required} */ (doc.nodes); + + /** @param {number} parentIndex */ + const getParentData = (parentIndex) => ({ + backendNodeId: nodes.backendNodeId[parentIndex], + attributes: nodes.attributes[parentIndex].map(getString), + nodeName: getString(nodes.nodeName[parentIndex]), + }); for (const layoutIndex of doc.textBoxes.layoutIndex) { const text = strings[doc.layout.text[layoutIndex]]; @@ -267,45 +257,50 @@ class FontSize extends Gatherer { const nodeIndex = doc.layout.nodeIndex[layoutIndex]; const styles = doc.layout.styles[layoutIndex]; const [fontSizeStringId] = styles; + const fontSize = getFloat(fontSizeStringId); + + const parentIndex = nodes.parentIndex[nodeIndex]; + const grandParentIndex = nodes.parentIndex[parentIndex]; + const parentNode = getParentData(parentIndex); + const grandParentNode = + grandParentIndex !== undefined ? getParentData(grandParentIndex) : undefined; - const fontSize = parseFloat(strings[fontSizeStringId]); - backendIdsToFontData.set(doc.nodes.backendNodeId[nodeIndex], { + textNodesData.push({ + nodeIndex, + backendNodeId: nodes.backendNodeId[nodeIndex], fontSize, textLength: getTextLength(text), + parentNode: { + ...parentNode, + parentNode: grandParentNode, + }, }); } } - return backendIdsToFontData; + return textNodesData; } /** - * The only connection between a snapshot Node and an actual Protocol Node is backendId, - * so that is used to join the two data structures. DOMSnapshot.captureSnapshot doesn't - * give the entire Node object, so DOM.getFlattenedDocument is used. - * @param {BackendIdsToFontData} backendIdsToFontData - * @param {LH.Artifacts.FontSize.DomNodeWithParent[]} crdpNodes + * Get all the failing text nodes that don't meet the legible text threshold. + * @param {LH.Crdp.DOMSnapshot.CaptureSnapshotResponse} snapshot */ - findFailingNodes(backendIdsToFontData, crdpNodes) { + findFailingNodes(snapshot) { /** @type {NodeFontData[]} */ const failingNodes = []; let totalTextLength = 0; let failingTextLength = 0; - for (const crdpNode of crdpNodes) { - const partialFontData = backendIdsToFontData.get(crdpNode.backendNodeId); - if (!partialFontData) continue; - // `crdpNode` is a non-empty TextNode that is in the layout tree (not display: none). - - const {fontSize, textLength} = partialFontData; - totalTextLength += textLength; - if (fontSize < MINIMAL_LEGIBLE_FONT_SIZE_PX) { + for (const textNodeData of this.getTextNodesInLayoutFromSnapshot(snapshot)) { + totalTextLength += textNodeData.textLength; + if (textNodeData.fontSize < MINIMAL_LEGIBLE_FONT_SIZE_PX) { // Once a bad TextNode is identified, its parent Node is needed. - failingTextLength += textLength; + failingTextLength += textNodeData.textLength; failingNodes.push({ - node: crdpNode.parentNode, - textLength, - fontSize, + nodeId: 0, // Set later in fetchFailingNodeSourceRules. + parentNode: textNodeData.parentNode, + textLength: textNodeData.textLength, + fontSize: textNodeData.fontSize, }); } } @@ -331,20 +326,15 @@ class FontSize extends Gatherer { ]); // Get the computed font-size style of every node. - const snapshotPromise = passContext.driver.sendCommand('DOMSnapshot.captureSnapshot', { + const snapshot = await passContext.driver.sendCommand('DOMSnapshot.captureSnapshot', { computedStyles: ['font-size'], }); - const allNodesPromise = getAllNodesFromBody(passContext.driver); - const [snapshot, crdpNodes] = await Promise.all([snapshotPromise, allNodesPromise]); - const backendIdsToFontData = this.calculateBackendIdsToFontData(snapshot); - // `backendIdsToFontData` will include all non-empty TextNodes. - // `crdpNodes` will only contain the body node and its descendants. const { totalTextLength, failingTextLength, failingNodes, - } = this.findFailingNodes(backendIdsToFontData, crdpNodes); + } = this.findFailingNodes(snapshot); const { analyzedFailingNodesData, analyzedFailingTextLength, diff --git a/lighthouse-core/test/audits/seo/font-size-test.js b/lighthouse-core/test/audits/seo/font-size-test.js index 3dd35ad6291e..7696032e4f53 100644 --- a/lighthouse-core/test/audits/seo/font-size-test.js +++ b/lighthouse-core/test/audits/seo/font-size-test.js @@ -44,8 +44,8 @@ describe('SEO: Font size audit', () => { failingTextLength: 41, analyzedFailingTextLength: 41, analyzedFailingNodesData: [ - {textLength: 10, fontSize: 10, node: {nodeId: 1, localName: 'p', attributes: []}}, - {textLength: 31, fontSize: 11, node: {nodeId: 2, localName: 'p', attributes: []}}, + {nodeId: 1, textLength: 10, fontSize: 10, parentNode: {nodeName: 'p', attributes: []}}, + {nodeId: 2, textLength: 31, fontSize: 11, parentNode: {nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -65,7 +65,7 @@ describe('SEO: Font size audit', () => { failingTextLength: 0, analyzedFailingTextLength: 0, analyzedFailingNodesData: [ - {textLength: 0, fontSize: 11, node: {nodeId: 1, localName: 'p', attributes: []}}, + {nodeId: 1, textLength: 0, fontSize: 11, parentNode: {nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -84,8 +84,8 @@ describe('SEO: Font size audit', () => { failingTextLength: 33, analyzedFailingTextLength: 33, analyzedFailingNodesData: [ - {textLength: 11, fontSize: 10, node: {nodeId: 1, localName: 'p', attributes: []}}, - {textLength: 22, fontSize: 11, node: {nodeId: 2, localName: 'p', attributes: []}}, + {nodeId: 1, textLength: 11, fontSize: 10, parentNode: {nodeName: 'p', attributes: []}}, + {nodeId: 2, textLength: 22, fontSize: 11, parentNode: {nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -120,9 +120,9 @@ describe('SEO: Font size audit', () => { failingTextLength: 7, analyzedFailingTextLength: 7, analyzedFailingNodesData: [ - {textLength: 3, fontSize: 11, node: {nodeId: 1}, cssRule: style1}, - {textLength: 2, fontSize: 10, node: {nodeId: 2}, cssRule: style2}, - {textLength: 2, fontSize: 10, node: {nodeId: 3}, cssRule: style2}, + {nodeId: 1, textLength: 3, fontSize: 11, parentNode: {}, cssRule: style1}, + {nodeId: 2, textLength: 2, fontSize: 10, parentNode: {}, cssRule: style2}, + {nodeId: 3, textLength: 2, fontSize: 10, parentNode: {}, cssRule: style2}, ], }, TestedAsMobileDevice: true, @@ -144,7 +144,7 @@ describe('SEO: Font size audit', () => { failingTextLength: 50, analyzedFailingTextLength: 10, analyzedFailingNodesData: [ - {textLength: 10, fontSize: 10, node: {nodeId: 1, localName: 'p', attributes: []}}, + {textLength: 10, fontSize: 10, parentNode: {nodeId: 1, nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -169,7 +169,7 @@ describe('SEO: Font size audit', () => { failingTextLength: 50, analyzedFailingTextLength: 50, analyzedFailingNodesData: [ - {textLength: 50, fontSize: 10, node: {nodeId: 1, localName: 'p', attributes: []}}, + {textLength: 50, fontSize: 10, parentNode: {nodeId: 1, nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -188,8 +188,8 @@ describe('SEO: Font size audit', () => { failingTextLength: 33, analyzedFailingTextLength: 33, analyzedFailingNodesData: [ - {textLength: 11, fontSize: 10, node: {nodeId: 1, localName: 'p', attributes: []}}, - {textLength: 22, fontSize: 11, node: {nodeId: 2, localName: 'p', attributes: []}}, + {textLength: 11, fontSize: 10, parentNode: {nodeId: 1, nodeName: 'p', attributes: []}}, + {textLength: 22, fontSize: 11, parentNode: {nodeId: 2, nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -207,8 +207,8 @@ describe('SEO: Font size audit', () => { failingTextLength: 315, analyzedFailingTextLength: 315, analyzedFailingNodesData: [ - {textLength: 311, fontSize: 10, node: {nodeId: 1, localName: 'p', attributes: []}}, - {textLength: 4, fontSize: 11, node: {nodeId: 2, localName: 'p', attributes: []}}, + {textLength: 311, fontSize: 10, parentNode: {nodeId: 1, nodeName: 'p', attributes: []}}, + {textLength: 4, fontSize: 11, parentNode: {nodeId: 2, nodeName: 'p', attributes: []}}, ], }, TestedAsMobileDevice: true, @@ -236,7 +236,7 @@ describe('SEO: Font size audit', () => { MetaElements: makeMetaElements(validViewport), FontSize: { analyzedFailingNodesData: [ - {textLength: 1, fontSize: 1, node: {nodeId: 1, ...nodeProperties}, cssRule: style}, + {textLength: 1, fontSize: 1, parentNode: {...nodeProperties}, cssRule: style}, ], }, TestedAsMobileDevice: true, @@ -251,7 +251,7 @@ describe('SEO: Font size audit', () => { type: 'Inline', }, { parentNode: {attributes: ['id', 'my-parent']}, - localName: 'p', + nodeName: 'p', attributes: ['class', 'my-p'], }); @@ -273,7 +273,7 @@ describe('SEO: Font size audit', () => { type: 'Attributes', }, { parentNode: {attributes: ['id', 'my-parent']}, - localName: 'font', + nodeName: 'font', attributes: ['size', '10px'], }); diff --git a/lighthouse-core/test/gather/gatherers/seo/font-size-test.js b/lighthouse-core/test/gather/gatherers/seo/font-size-test.js index 56d19419e00e..092012b971b5 100644 --- a/lighthouse-core/test/gather/gatherers/seo/font-size-test.js +++ b/lighthouse-core/test/gather/gatherers/seo/font-size-test.js @@ -7,48 +7,53 @@ /* eslint-env jest */ +const assert = require('assert'); const FontSizeGather = require('../../../../gather/gatherers/seo/font-size.js'); let fontSizeGather; const TEXT_NODE_TYPE = 3; const smallText = ' body sm𝐀ll text '; const bigText = 'body 𝐁ig text'; -const bodyNode = {nodeId: 3, backendNodeId: 102, nodeName: 'BODY', parentId: 1, fontSize: '10px'}; +const bodyNode = { + nodeId: 2, backendNodeId: 102, + nodeName: 'BODY', parentId: 0, fontSize: '10px', attributes: ['blah', '1'], +}; const nodes = [ - {nodeId: 1, backendNodeId: 100, nodeName: 'HTML'}, - {nodeId: 2, backendNodeId: 101, nodeName: 'HEAD', parentId: 1}, + {nodeId: 0, backendNodeId: 100, nodeName: 'HTML'}, + {nodeId: 1, backendNodeId: 101, nodeName: 'HEAD', parentId: 0}, bodyNode, { - nodeId: 4, + nodeId: 3, backendNodeId: 103, nodeValue: 'head text', nodeType: TEXT_NODE_TYPE, - parentId: 2, + parentId: 1, }, { - nodeId: 5, + nodeId: 4, backendNodeId: 104, nodeValue: smallText, nodeType: TEXT_NODE_TYPE, - parentId: 3, + parentId: 2, }, - {nodeId: 6, backendNodeId: 105, nodeName: 'H1', parentId: 3}, + {nodeId: 5, backendNodeId: 105, nodeName: 'H1', parentId: 2}, { - nodeId: 7, + nodeId: 6, backendNodeId: 106, nodeValue: bigText, nodeType: TEXT_NODE_TYPE, - parentId: 6, + parentId: 5, }, - {nodeId: 8, backendNodeId: 107, nodeName: 'SCRIPT', parentId: 3}, + {nodeId: 7, backendNodeId: 107, nodeName: 'SCRIPT', parentId: 2}, { - nodeId: 9, + nodeId: 8, backendNodeId: 108, nodeValue: 'script text', nodeType: TEXT_NODE_TYPE, - parentId: 8, + parentId: 7, }, ]; +nodes.forEach((node, i) => assert(node.nodeId === i)); const stringsMap = {}; const strings = []; @@ -62,25 +67,45 @@ const getOrCreateStringIndex = value => { strings.push(value); return index; }; + +const nodeNamesNotInLayout = ['HEAD', 'HTML', 'SCRIPT']; +const nodeIndicesInLayout = nodes.map((node, i) => { + if (nodeNamesNotInLayout.includes(node.nodeName)) return null; + + if (node.nodeType === TEXT_NODE_TYPE) { + const parentNode = nodes[node.parentId]; + if (parentNode && nodeNamesNotInLayout.includes(parentNode.nodeName)) { + return null; + } + } + + return i; +}).filter(id => id !== null); +const nodesInLayout = nodeIndicesInLayout.map(index => nodes[index]); + const snapshot = { documents: [ { nodes: { backendNodeId: nodes.map(node => node.backendNodeId), + parentIndex: nodes.map(node => node.parentId), + attributes: nodes.map(node => + node.attributes ? node.attributes.map(getOrCreateStringIndex) : []), + nodeName: nodes.map(node => getOrCreateStringIndex(node.nodeName)), }, layout: { - nodeIndex: nodes.map((_, i) => i), - styles: nodes.map(node => [ + nodeIndex: nodeIndicesInLayout, + styles: nodesInLayout.map(node => ([ getOrCreateStringIndex(`${node.nodeValue === smallText ? 10 : 20}px`), - ]), - text: nodes.map(node => getOrCreateStringIndex(node.nodeValue)), + ])), + text: nodesInLayout.map(node => getOrCreateStringIndex(node.nodeValue)), }, textBoxes: { - layoutIndex: nodes.map((_, i) => i).filter(i => { - const node = nodes[i]; + layoutIndex: nodeIndicesInLayout.map((_, i) => i).filter(i => { + const node = nodes[nodeIndicesInLayout[i]]; if (node.nodeType !== TEXT_NODE_TYPE) return false; - const parentNode = nodes.find(n => n.nodeId === node.parentId); + const parentNode = nodes[node.parentId]; return parentNode && parentNode.nodeName !== 'SCRIPT'; }), }, @@ -99,7 +124,7 @@ describe('Font size gatherer', () => { const driver = { on() {}, off() {}, - async sendCommand(command) { + async sendCommand(command, args) { if (command === 'CSS.getMatchedStylesForNode') { return { inlineStyle: null, @@ -109,11 +134,14 @@ describe('Font size gatherer', () => { }; } else if (command === 'DOMSnapshot.captureSnapshot') { return snapshot; + } else if (command === 'DOM.pushNodesByBackendIdsToFrontend') { + return { + nodeIds: args.backendNodeIds.map(backendNodeId => { + return nodes.find(node => node.backendNodeId === backendNodeId).nodeId; + }), + }; } }, - async getNodesInDocument() { - return nodes; - }, }; const artifact = await fontSizeGather.afterPass({driver}); @@ -129,8 +157,19 @@ describe('Font size gatherer', () => { totalTextLength: expectedTotalTextLength, analyzedFailingNodesData: [ { + // nodeId of the failing body TextNode + nodeId: 2, fontSize: 10, - node: bodyNode, + parentNode: { + backendNodeId: 102, + attributes: bodyNode.attributes, + nodeName: bodyNode.nodeName, + parentNode: { + backendNodeId: 100, + attributes: [], + nodeName: 'HTML', + }, + }, textLength: expectedFailingTextLength, }, ], diff --git a/types/artifacts.d.ts b/types/artifacts.d.ts index 704db5079dba..e3e0cfa629aa 100644 --- a/types/artifacts.d.ts +++ b/types/artifacts.d.ts @@ -369,9 +369,20 @@ declare global { analyzedFailingTextLength: number; /** Elements that contain a text node that failed size criteria. */ analyzedFailingNodesData: Array<{ + /* nodeId of the failing TextNode. */ + nodeId: number; fontSize: number; textLength: number; - node: FontSize.DomNodeWithParent; + parentNode: { + backendNodeId: number; + attributes: string[]; + nodeName: string; + parentNode?: { + backendNodeId: number; + attributes: string[]; + nodeName: string; + }; + }; cssRule?: { type: 'Regular' | 'Inline' | 'Attributes'; range?: {startLine: number, startColumn: number}; @@ -382,17 +393,6 @@ declare global { }> } - export module FontSize { - export interface DomNodeWithParent extends Crdp.DOM.Node { - parentId: number; - parentNode: DomNodeWithParent; - } - - export interface DomNodeMaybeWithParent extends Crdp.DOM.Node { - parentNode?: DomNodeMaybeWithParent; - } - } - // TODO(bckenny): real type for parsed manifest. export type Manifest = ReturnType;