Skip to content

Commit

Permalink
core(seo): properly handle anchors in SVG (#6483)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhulce committed Nov 6, 2018
1 parent 2f43eb9 commit abf7c4d
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 8 deletions.
6 changes: 6 additions & 0 deletions lighthouse-cli/test/fixtures/seo/seo-failure-cases.html
Expand Up @@ -27,6 +27,12 @@ <h2>Anchor text</h2>
<a href='https://example.com'>click this</a>
<a href='/test.html'> click this </a>
<a href='/test.html'>CLICK THIS</a>
<svg>
<a href='https://example.com/svg-link'>
<circle cx="50" cy="50" r="50"/>
<text x="0" y="0" >click this</text>
</a>
</svg>

<!-- FAIL(plugins): java applet on page -->
<applet code=HelloWorld.class width="200" height="200" id='applet'>
Expand Down
4 changes: 2 additions & 2 deletions lighthouse-cli/test/smokehouse/seo/expectations.js
Expand Up @@ -99,10 +99,10 @@ module.exports = [
},
'link-text': {
score: 0,
displayValue: '3 links found',
displayValue: '4 links found',
details: {
items: {
length: 3,
length: 4,
},
},
},
Expand Down
10 changes: 9 additions & 1 deletion lighthouse-core/audits/seo/link-text.js
Expand Up @@ -41,14 +41,22 @@ class LinkText extends Audit {
static audit(artifacts) {
const failingLinks = artifacts.CrawlableLinks
.filter(link => {
const href = link.href.toLowerCase();
if (
link.href.toLowerCase().startsWith('javascript:') ||
href.startsWith('javascript:') ||
href.startsWith('mailto:') ||
URL.equalWithExcludedFragments(link.href, artifacts.URL.finalUrl)
) {
return false;
}

return BLOCKLIST.has(link.text.trim().toLowerCase());
})
.map(link => {
return {
href: link.href,
text: link.text.trim(),
};
});

const headings = [
Expand Down
19 changes: 15 additions & 4 deletions lighthouse-core/gather/gatherers/seo/crawlable-links.js
Expand Up @@ -13,19 +13,30 @@ class CrawlableLinks extends Gatherer {
* @param {LH.Gatherer.PassContext} passContext
* @return {Promise<LH.Artifacts['CrawlableLinks']>}
*/
afterPass(passContext) {
async afterPass(passContext) {
const expression = `(function() {
${pageFunctions.getElementsInDocumentString}; // define function on page
const resolveURLOrNull = url => {
try { return new URL(url, window.location.href).href; }
catch (_) { return null; }
};
const selector = 'a[href]:not([rel~="nofollow"])';
const elements = getElementsInDocument(selector);
return elements
.map(node => ({
href: node.href,
text: node.innerText
href: node.href instanceof SVGAnimatedString ?
resolveURLOrNull(node.href.baseVal) :
node.href,
text: node.href instanceof SVGAnimatedString ?
node.textContent :
node.innerText,
}));
})()`;

return passContext.driver.evaluateAsync(expression);
/** @type {LH.Artifacts['CrawlableLinks']} */
const links = await passContext.driver.evaluateAsync(expression, {useIsolation: true});
return links.filter(link => typeof link.href === 'string' && link.href);
}
}

Expand Down
3 changes: 2 additions & 1 deletion lighthouse-core/lib/page-functions.js
Expand Up @@ -84,13 +84,14 @@ function checkTimeSinceLastLongTask() {
*/
/* istanbul ignore next */
function getElementsInDocument(selector) {
const realMatchesFn = window.__ElementMatches || window.Element.prototype.matches;
/** @type {Array<Element>} */
const results = [];

/** @param {NodeListOf<Element>} nodes */
const _findAllElements = nodes => {
for (let i = 0, el; el = nodes[i]; ++i) {
if (!selector || window.__ElementMatches.call(el, selector)) {
if (!selector || realMatchesFn.call(el, selector)) {
results.push(el);
}
// If the element has a shadow root, dig deeper.
Expand Down
Expand Up @@ -64,6 +64,21 @@ describe('SEO: link text audit', () => {
assert.equal(auditResult.rawValue, true);
});

it('ignores mailto: links', () => {
const artifacts = {
URL: {
finalUrl: 'https://example.com/page.html',
},
CrawlableLinks: [
{href: 'mailto:info@example.com', text: 'click here'},
{href: 'mailto:mailmaster@localhost', text: 'click here'},
],
};

const auditResult = LinkTextAudit.audit(artifacts);
assert.equal(auditResult.rawValue, true);
});

it('passes when all links have descriptive texts', () => {
const artifacts = {
URL: {
Expand Down

0 comments on commit abf7c4d

Please sign in to comment.