diff --git a/routes/_components/status/StatusContent.html b/routes/_components/status/StatusContent.html index bfaba0e..34a3140 100644 --- a/routes/_components/status/StatusContent.html +++ b/routes/_components/status/StatusContent.html @@ -94,45 +94,42 @@ if (!this.refs.node) { return } + mark('hydrateContent') let node = this.refs.node let originalStatus = this.get('originalStatus') let uuid = this.get('uuid') let count = 0 - mark('hydrateContent') - if (originalStatus.tags && originalStatus.tags.length) { - let anchorTags = node.querySelectorAll('a[class~=hashtag][href^=http]') - for (let tag of originalStatus.tags) { - for (let i = 0, len = anchorTags.length; i < len; i++) { - let anchorTag = anchorTags[i] - if (anchorTag.getAttribute('href').endsWith(`/tags/${tag.name}`)) { - anchorTag.setAttribute('href', `/tags/${tag.name}`) - anchorTag.setAttribute('focus-key', `status-content-link-${uuid}-${++count}`) - anchorTag.removeAttribute('target') - anchorTag.removeAttribute('rel') + let anchors = node.getElementsByTagName('A') + let mentions = originalStatus.mentions + let tags = originalStatus.tags + for (let i = 0, len = anchors.length; i < len; i++) { + let anchor = anchors[i] + let href = anchor.getAttribute('href') + if (tags && anchor.classList.contains('hashtag')) { + for (let j = 0, jLen = tags.length; j < jLen; j++) { + let tag = tags[j] + if (href.endsWith(`/tags/${tag.name}`)) { + anchor.setAttribute('href', `/tags/${tag.name}`) + anchor.setAttribute('focus-key', `status-content-link-${uuid}-${++count}`) + anchor.removeAttribute('target') + anchor.removeAttribute('rel') } } - } - } - if (originalStatus.mentions && originalStatus.mentions.length) { - let anchorTags = node.querySelectorAll('a[class~=mention][href^=http]') - for (let mention of originalStatus.mentions) { - for (let i = 0, len = anchorTags.length; i < len; i++) { - let anchorTag = anchorTags[i] - if (anchorTag.getAttribute('href') === mention.url) { - anchorTag.setAttribute('href', `/accounts/${mention.id}`) - anchorTag.setAttribute('title', `@${mention.acct}`) - anchorTag.setAttribute('focus-key', `status-content-link-${uuid}-${++count}`) - anchorTag.removeAttribute('target') - anchorTag.removeAttribute('rel') + } else if (mentions && anchor.classList.contains('mention')) { + for (let j = 0, jLen = mentions.length; j < jLen; j++) { + let mention = mentions[j] + if (href === mention.url) { + anchor.setAttribute('href', `/accounts/${mention.id}`) + anchor.setAttribute('title', `@${mention.acct}`) + anchor.setAttribute('focus-key', `status-content-link-${uuid}-${++count}`) + anchor.removeAttribute('target') + anchor.removeAttribute('rel') } } + } else if (anchor.getAttribute('rel') === 'nofollow noopener') { + anchor.setAttribute('title', href) } } - let externalLinks = node.querySelectorAll('a[rel="nofollow noopener"]') - for (let i = 0, len = externalLinks.length; i < len; i++) { - let link = externalLinks[i] - link.setAttribute('title', link.getAttribute('href')) - } stop('hydrateContent') } } diff --git a/tests/spec/112-status-links.js b/tests/spec/112-status-links.js new file mode 100644 index 0000000..ea17412 --- /dev/null +++ b/tests/spec/112-status-links.js @@ -0,0 +1,56 @@ +import { + composeButton, + composeInput, + getNthStatus +} from '../utils' +import { foobarRole } from '../roles' + +fixture`112-status-links.js` + .page`http://localhost:4002` + +test('External links, hashtags, and mentions have correct attributes', async t => { + let text = 'Why hello there @admin and @baz and @quux ' + + 'and also #tag and #anotherTag and #yetAnotherTag ' + + 'and also http://example.com and https://joinmastodon.org and ' + + 'https://mastodon.social.' + + const nthAnchor = n => getNthStatus(0).find('.status-content a').nth(n) + + await t.useRole(foobarRole) + .typeText(composeInput, text, {paste: true}) + .click(composeButton) + .expect(getNthStatus(0).innerText).contains('Why hello there', {timeout: 20000}) + .expect(nthAnchor(0).getAttribute('href')).eql('/accounts/1') + .expect(nthAnchor(0).hasAttribute('rel')).notOk() + .expect(nthAnchor(0).getAttribute('title')).eql('@admin') + .expect(nthAnchor(0).hasAttribute('target')).notOk() + .expect(nthAnchor(1).getAttribute('href')).eql('/accounts/5') + .expect(nthAnchor(1).hasAttribute('rel')).notOk() + .expect(nthAnchor(1).getAttribute('title')).eql('@baz') + .expect(nthAnchor(1).hasAttribute('target')).notOk() + .expect(nthAnchor(2).getAttribute('href')).eql('/accounts/3') + .expect(nthAnchor(2).hasAttribute('rel')).notOk() + .expect(nthAnchor(2).getAttribute('title')).eql('@quux') + .expect(nthAnchor(2).hasAttribute('target')).notOk() + .expect(nthAnchor(3).getAttribute('href')).eql('/tags/tag') + .expect(nthAnchor(3).hasAttribute('rel')).notOk() + .expect(nthAnchor(3).hasAttribute('target')).notOk() + .expect(nthAnchor(4).getAttribute('href')).eql('/tags/anothertag') + .expect(nthAnchor(4).hasAttribute('rel')).notOk() + .expect(nthAnchor(4).hasAttribute('target')).notOk() + .expect(nthAnchor(5).getAttribute('href')).eql('/tags/yetanothertag') + .expect(nthAnchor(5).hasAttribute('rel')).notOk() + .expect(nthAnchor(5).hasAttribute('target')).notOk() + .expect(nthAnchor(6).getAttribute('href')).eql('http://example.com') + .expect(nthAnchor(6).getAttribute('rel')).eql('nofollow noopener') + .expect(nthAnchor(6).getAttribute('title')).eql('http://example.com') + .expect(nthAnchor(6).getAttribute('target')).eql('_blank') + .expect(nthAnchor(7).getAttribute('href')).eql('https://joinmastodon.org') + .expect(nthAnchor(7).getAttribute('rel')).eql('nofollow noopener') + .expect(nthAnchor(7).getAttribute('title')).eql('https://joinmastodon.org') + .expect(nthAnchor(7).getAttribute('target')).eql('_blank') + .expect(nthAnchor(8).getAttribute('href')).eql('https://mastodon.social') + .expect(nthAnchor(8).getAttribute('rel')).eql('nofollow noopener') + .expect(nthAnchor(8).getAttribute('title')).eql('https://mastodon.social') + .expect(nthAnchor(8).getAttribute('target')).eql('_blank') +})