Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9a52573
Collect and show title for elements
mattzeunert May 15, 2019
7c5a98b
Update sample data
mattzeunert May 15, 2019
197d907
Test cases
mattzeunert May 15, 2019
0af87da
Revert artifact changes
mattzeunert May 15, 2019
42712bb
Try change artifacts again
mattzeunert May 15, 2019
51cc42a
Fix text color in dark mode
mattzeunert May 16, 2019
a97aba9
Test case for truncation
mattzeunert May 16, 2019
060d2e9
@returns -> @return
mattzeunert May 16, 2019
2e50172
Remove margin
mattzeunert May 16, 2019
0bec94e
Fix TS types
mattzeunert May 16, 2019
8bba61f
@return
mattzeunert May 16, 2019
fc4f272
Use var for snippet color
mattzeunert May 16, 2019
37e3e11
Fall back to tagName if no other label
mattzeunert May 16, 2019
eab63fc
Update sample json
mattzeunert May 16, 2019
6fdeac2
Revert summary
mattzeunert May 16, 2019
eb3406c
title -> nodeLabel
mattzeunert May 17, 2019
c9094c4
Update sample json
mattzeunert May 17, 2019
7880109
More detailed a11y smoke test expectations
mattzeunert May 17, 2019
6993160
Remove unnecesary styles
mattzeunert May 17, 2019
533ad17
Fix tap targets smoke test
mattzeunert May 17, 2019
1d11422
More feedback
mattzeunert May 18, 2019
97fdad4
Teal
mattzeunert May 18, 2019
5fbdf5d
Material colors and reduce snippet line height from 2em to 1.5em
mattzeunert May 20, 2019
f893550
Remove path from a11y expectations because all paths change if you ad…
mattzeunert May 20, 2019
8e64be0
Remove getNodeLabel dependency on truncate
mattzeunert May 20, 2019
ea1621d
Fix
mattzeunert May 20, 2019
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
414 changes: 330 additions & 84 deletions lighthouse-cli/test/smokehouse/a11y/expectations.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions lighthouse-cli/test/smokehouse/seo/expectations.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ module.exports = [
'\n too small target\n </a>',
'path': '2,HTML,1,BODY,3,DIV,21,DIV,0,A',
'selector': 'body > div > div > a',
'nodeLabel': 'too small target',
},
'overlappingTarget': {
'type': 'node',
Expand All @@ -213,6 +214,7 @@ module.exports = [
'\n big enough target\n </a>',
'path': '2,HTML,1,BODY,3,DIV,21,DIV,1,A',
'selector': 'body > div > div > a',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add to the a11y expectations as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now all we assert there is { length: 1 }, but I'll change it when I do the final artifact update for this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added details.items assertions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now all we assert there is { length: 1 }, but I'll change it when I do the final artifact update for this PR.

haha, uh oh, I guess I didn't know how much I was asking for :) I guess we can just cross our fingers that these are stable and all worth testing. I do really like the extra coverage this gets us

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When @mattzeunert does something, he does it right, and you get the works! 😎

'nodeLabel': 'big enough target',
},
'size': '100x30',
'width': 100,
Expand Down
1 change: 1 addition & 0 deletions lighthouse-core/audits/accessibility/axe-audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class AxeAudit extends Audit {
path: node.path,
snippet: node.html || node.snippet,
explanation: node.failureSummary,
nodeLabel: node.nodeLabel,
}),
}));
}
Expand Down
1 change: 1 addition & 0 deletions lighthouse-core/audits/seo/tap-targets.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ function targetToTableNode(target) {
snippet: target.snippet,
path: target.path,
selector: target.selector,
nodeLabel: target.nodeLabel,
};
}

Expand Down
6 changes: 5 additions & 1 deletion lighthouse-core/gather/gatherers/accessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
'use strict';

/* global window, document, getOuterHTMLSnippet, getNodePath */
/* global window, document, getOuterHTMLSnippet, getNodePath, getNodeLabel */

const Gatherer = require('./gatherer');
const fs = require('fs');
Expand Down Expand Up @@ -57,6 +57,8 @@ function runA11yChecks() {
node.path = getNodePath(node.element);
// @ts-ignore - getOuterHTMLSnippet put into scope via stringification
node.snippet = getOuterHTMLSnippet(node.element);
// @ts-ignore - getNodeLabel put into scope via stringification
node.nodeLabel = getNodeLabel(node.element);
// avoid circular JSON concerns
node.element = node.any = node.all = node.none = undefined;
}));
Expand All @@ -77,6 +79,8 @@ class Accessibility extends Gatherer {
const expression = `(function () {
${pageFunctions.getOuterHTMLSnippetString};
${pageFunctions.getNodePathString};
${pageFunctions.getNodeLabelString};
${pageFunctions.truncateString};
${axeLibSource};
return (${runA11yChecks.toString()}());
})()`;
Expand Down
21 changes: 6 additions & 15 deletions lighthouse-core/gather/gatherers/seo/tap-targets.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
'use strict';

/* global getComputedStyle, getElementsInDocument, Node, getNodePath, getNodeSelector */
/* global getComputedStyle, getElementsInDocument, Node, getNodePath, getNodeSelector, getNodeLabel, truncate */

const Gatherer = require('../gatherer');
const pageFunctions = require('../../../lib/page-functions.js');
Expand Down Expand Up @@ -236,19 +236,6 @@ function elementIsPositionFixedStickyOrAbsolute(element) {
return false;
}

/**
* @param {string} str
* @param {number} maxLength
* @returns {string}
*/
/* istanbul ignore next */
function truncate(str, maxLength) {
if (str.length <= maxLength) {
return str;
}
return str.slice(0, maxLength - 1) + '…';
}

/**
* @returns {LH.Artifacts.TapTarget[]}
*/
Expand Down Expand Up @@ -289,11 +276,14 @@ function gatherTapTargets() {

targets.push({
clientRects: visibleClientRects,
// @ts-ignore - truncate put into scope via stringification
snippet: truncate(tapTargetElement.outerHTML, 300),
// @ts-ignore - getNodePath put into scope via stringification
path: getNodePath(tapTargetElement),
// @ts-ignore - getNodeSelector put into scope via stringification
selector: getNodeSelector(tapTargetElement),
// @ts-ignore - getNodeLabel put into scope via stringification
nodeLabel: getNodeLabel(tapTargetElement),
href: /** @type {HTMLAnchorElement} */(tapTargetElement)['href'] || '',
});
});
Expand All @@ -315,14 +305,15 @@ class TapTargets extends Gatherer {
${elementIsVisible.toString()};
${elementHasAncestorTapTarget.toString()};
${getVisibleClientRects.toString()};
${truncate.toString()};
${pageFunctions.truncateString};
${getClientRects.toString()};
${hasTextNodeSiblingsFormingTextBlock.toString()};
${elementIsInTextBlock.toString()};
${allClientRectsEmpty.toString()};
${rectContainsString};
${pageFunctions.getNodePathString};
${pageFunctions.getNodeSelectorString};
${pageFunctions.getNodeLabelString};
${gatherTapTargets.toString()};

return gatherTapTargets();
Expand Down
39 changes: 38 additions & 1 deletion lighthouse-core/lib/page-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function getNodePath(node) {

/**
* @param {Element} node
* @returns {string}
* @return {string}
*/
/* istanbul ignore next */
function getNodeSelector(node) {
Expand Down Expand Up @@ -218,6 +218,40 @@ function getNodeSelector(node) {
return parts.join(' > ');
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a comment for this function? Basically trying to provide a human recognizable label for the given element. Falls back to the tagName if no useful label is found.

* @param {HTMLElement} node
* @return {string|null}
*/
/* istanbul ignore next */
function getNodeLabel(node) {
const tagName = node.tagName.toLowerCase();
if (tagName !== 'html' && tagName !== 'body') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add back that comment about why we skip html and body

const title = node.innerText || node.getAttribute('alt') || node.getAttribute('aria-label');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a big deal, but I guess s/title/label

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also should use the standard node.textContent (unless innerText was intentional?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I specifically requested innerText because textContent gets us all that inline script instead of actual text

if (title) {
return truncate(title, 80);
} else {
const nodeToUseForTitle = node.querySelector('[alt], [aria-label]');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add comment about why it's useful to recursively search (what cases does this help with?)

if (nodeToUseForTitle) {
return getNodeLabel(/** @type {HTMLElement} */ (nodeToUseForTitle));
}
}
}
return tagName;
}

/**
* @param {string} str
* @param {number} maxLength
* @return {string}
*/
/* istanbul ignore next */
function truncate(str, maxLength) {
if (str.length <= maxLength) {
return str;
}
return str.slice(0, maxLength - 1) + '…';
}

module.exports = {
wrapRuntimeEvalErrorInBrowserString: wrapRuntimeEvalErrorInBrowser.toString(),
registerPerformanceObserverInPageString: registerPerformanceObserverInPage.toString(),
Expand All @@ -230,4 +264,7 @@ module.exports = {
getNodePathString: getNodePath.toString(),
getNodeSelectorString: getNodeSelector.toString(),
getNodeSelector: getNodeSelector,
getNodeLabel: getNodeLabel,
getNodeLabelString: getNodeLabel.toString(),
truncateString: truncate.toString(),
};
11 changes: 10 additions & 1 deletion lighthouse-core/report/html/renderer/details-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,15 +352,24 @@ class DetailsRenderer {
*/
renderNode(item) {
const element = this._dom.createElement('span', 'lh-node');
if (item.nodeLabel) {
const titleEl = this._dom.createElement('div');
Copy link
Contributor

@brendankenny brendankenny May 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

labelEl, etc

titleEl.textContent = item.nodeLabel;
element.appendChild(titleEl);
}
if (item.snippet) {
element.textContent = item.snippet;
const snippetEl = this._dom.createElement('div');
snippetEl.classList.add('lh-node__snippet');
snippetEl.textContent = item.snippet;
element.appendChild(snippetEl);
}
if (item.selector) {
element.title = item.selector;
}
if (item.path) element.setAttribute('data-path', item.path);
if (item.selector) element.setAttribute('data-selector', item.selector);
if (item.snippet) element.setAttribute('data-snippet', item.snippet);

return element;
}

Expand Down
6 changes: 3 additions & 3 deletions lighthouse-core/report/html/report-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,10 @@
}

/* Node */
.lh-node {
display: block;
.lh-node__snippet {
font-family: var(--monospace-font-family);
color: hsl(174, 100%, 27%);
color: var(--color-green-700);
font-size: 12px;
}

/* Score */
Expand Down
27 changes: 27 additions & 0 deletions lighthouse-core/test/lib/page-functions-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,31 @@ describe('Page Functions', () => {
assert.equal(pageFunctions.getNodeSelector(childEl), 'div#wrapper > div.child');
});
});

describe('getNodeLabel', () => {
it('Returns innerText if element has visible text', () => {
const el = dom.createElement('div');
el.innerText = 'Hello';
assert.equal(pageFunctions.getNodeLabel(el), 'Hello');
});
it('Falls back to children and alt/aria-label if a title can\'t be determined', () => {
const el = dom.createElement('div');
const childEl = dom.createElement('div', '', {'aria-label': 'Something'});
el.appendChild(childEl);
assert.equal(pageFunctions.getNodeLabel(el), 'Something');
});
it('Truncates long text', () => {
const el = dom.createElement('div');
el.setAttribute('alt', Array(100).fill('a').join(''));
assert.equal(pageFunctions.getNodeLabel(el).length, 80);
});
it('Uses tag name for html tags', () => {
const el = dom.createElement('html');
assert.equal(pageFunctions.getNodeLabel(el), 'html');
});
it('Uses tag name if there is no better label', () => {
const el = dom.createElement('div');
assert.equal(pageFunctions.getNodeLabel(el), 'div');
});
});
});
Loading