Skip to content

Commit

Permalink
feat(Twitter): Add support for Twitter moments (#72)
Browse files Browse the repository at this point in the history
* Add support for Twitter Moments

* Update README

* Update tests

* support moment event urls and fix tests

* use includesSomeOfArray

Co-authored-by: Michaël De Boey <info@michaeldeboey.be>
  • Loading branch information
osiux and MichaelDeBoey authored Mar 2, 2020
1 parent f9c510e commit ed6f881
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 10 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ have to include it yourself. The recommended way of including it is by using

```md
https://twitter.com/MichaelDeBoey93/status/1152991421789548546

https://twitter.com/i/moments/994601867987619840
```

<details>
Expand Down Expand Up @@ -466,6 +468,13 @@ https://twitter.com/MichaelDeBoey93/status/1152991421789548546
July 21, 2019
</a>
</blockquote>

<a
class="twitter-moment"
href="https://twitter.com/i/moments/994601867987619840"
>
🔥 Design Tips
</a>
```

</details>
Expand Down Expand Up @@ -614,6 +623,7 @@ Thanks goes to these people ([emoji key][emojis]):

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
61 changes: 56 additions & 5 deletions src/__tests__/transformers/Twitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ import { cache, getMarkdownASTForFile, parseASTToMarkdown } from '../helpers';

jest.mock('node-fetch', () => jest.fn());

const mockFetch = html =>
fetchMock.mockResolvedValue({ json: () => Promise.resolve({ html }) });
const mockFetch = (status, moment) =>
fetchMock
.mockResolvedValueOnce({ json: () => Promise.resolve({ html: status }) })
.mockResolvedValueOnce({ json: () => Promise.resolve({ html: status }) })
.mockResolvedValueOnce({ json: () => Promise.resolve({ html: moment }) })
.mockResolvedValueOnce({ json: () => Promise.resolve({ html: moment }) })
.mockResolvedValueOnce({ json: () => Promise.resolve({ html: moment }) })
.mockResolvedValueOnce({ json: () => Promise.resolve({ html: moment }) });

beforeEach(() => {
fetchMock.mockClear();
fetchMock.mockReset();
});

cases(
Expand All @@ -37,6 +43,10 @@ cases(
url: 'https://twitter.com/MichaelDeBoey93',
valid: false,
},
'moment edit url': {
url: 'https://twitter.com/i/moments/edit/994601867987619840',
valid: false,
},
'status url': {
url: 'https://twitter.com/kentcdodds/status/1078755736455278592',
valid: true,
Expand All @@ -45,10 +55,26 @@ cases(
url: 'https://www.twitter.com/kentcdodds/status/1078755736455278592',
valid: true,
},
'moment url': {
url: 'https://twitter.com/i/moments/994601867987619840',
valid: true,
},
"moment url having 'www' subdomain": {
url: 'https://www.twitter.com/i/moments/994601867987619840',
valid: true,
},
"moment url having '/events/' path": {
url: 'https://twitter.com/i/events/994601867987619840',
valid: true,
},
"moment url having 'www' subdomain & '/events/' path": {
url: 'https://www.twitter.com/i/events/994601867987619840',
valid: true,
},
}
);

test('Gets the correct Twitter iframe', async () => {
test('Gets the correct Twitter status iframe', async () => {
mockFetch(
`<blockquote class="twitter-tweet-mocked-fetch-transformer"><p lang="en" dir="ltr">example</p>&mdash; Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1078755736455278592">December 28, 2018</a></blockquote>`
);
Expand All @@ -62,9 +88,24 @@ test('Gets the correct Twitter iframe', async () => {
);
});

test('Gets the correct Twitter moment link', async () => {
mockFetch(
`<a class="twitter-moment-mocked-fetch-transformer" href="https://twitter.com/i/moments/994601867987619840">🔥 Design Tips</a>`
);

const html = await getHTML(
'https://twitter.com/i/moments/994601867987619840'
);

expect(html).toMatchInlineSnapshot(
`"<a class=\\"twitter-moment-mocked-fetch-transformer\\" href=\\"https://twitter.com/i/moments/994601867987619840\\">🔥 Design Tips</a>"`
);
});

test('Plugin can transform Twitter links', async () => {
mockFetch(
`<blockquote class="twitter-tweet-mocked-fetch-plugin"><p lang="en" dir="ltr">example</p>&mdash; Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1078755736455278592">December 28, 2018</a></blockquote>`
`<blockquote class="twitter-tweet-mocked-fetch-plugin"><p lang="en" dir="ltr">example</p>&mdash; Kent C. Dodds (@kentcdodds) <a href="https://twitter.com/kentcdodds/status/1078755736455278592">December 28, 2018</a></blockquote>`,
`<a class="twitter-moment-mocked-fetch-plugin" href="https://twitter.com/i/moments/994601867987619840">🔥 Design Tips</a>`
);
const markdownAST = getMarkdownASTForFile('Twitter');

Expand All @@ -78,10 +119,20 @@ test('Plugin can transform Twitter links', async () => {
<https://this-is-not-twitter.com/foobar/status/123>
<https://twitter.com/MichaelDeBoey93>
<https://twitter.com/i/moments/edit/994601867987619840>
<blockquote class=\\"twitter-tweet-mocked-fetch-plugin\\"><p lang=\\"en\\" dir=\\"ltr\\">example</p>&mdash; Kent C. Dodds (@kentcdodds) <a href=\\"https://twitter.com/kentcdodds/status/1078755736455278592\\">December 28, 2018</a></blockquote>
<blockquote class=\\"twitter-tweet-mocked-fetch-plugin\\"><p lang=\\"en\\" dir=\\"ltr\\">example</p>&mdash; Kent C. Dodds (@kentcdodds) <a href=\\"https://twitter.com/kentcdodds/status/1078755736455278592\\">December 28, 2018</a></blockquote>
<a class=\\"twitter-moment-mocked-fetch-plugin\\" href=\\"https://twitter.com/i/moments/994601867987619840\\">🔥 Design Tips</a>
<a class=\\"twitter-moment-mocked-fetch-plugin\\" href=\\"https://twitter.com/i/moments/994601867987619840\\">🔥 Design Tips</a>
<a class=\\"twitter-moment-mocked-fetch-plugin\\" href=\\"https://twitter.com/i/moments/994601867987619840\\">🔥 Design Tips</a>
<a class=\\"twitter-moment-mocked-fetch-plugin\\" href=\\"https://twitter.com/i/moments/994601867987619840\\">🔥 Design Tips</a>
"
`);
});
10 changes: 10 additions & 0 deletions src/__tests__/transformers/__fixtures__/Twitter.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ https://this-is-not-twitter.com/foobar/status/123

https://twitter.com/MichaelDeBoey93

https://twitter.com/i/moments/edit/994601867987619840

https://twitter.com/kentcdodds/status/1078755736455278592

https://www.twitter.com/kentcdodds/status/1078755736455278592

https://twitter.com/i/moments/994601867987619840

https://www.twitter.com/i/moments/994601867987619840

https://twitter.com/i/events/994601867987619840

https://www.twitter.com/i/events/994601867987619840
19 changes: 14 additions & 5 deletions src/transformers/Twitter.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { URL } from 'url';

import { fetchOEmbedData } from './utils';
import { fetchOEmbedData, includesSomeOfArray } from './utils';

export const shouldTransform = url => {
const { host, pathname } = new URL(url);

return (
['twitter.com', 'www.twitter.com'].includes(host) &&
pathname.includes('/status/')
(pathname.includes('/status/') ||
(includesSomeOfArray(pathname, ['/events/', '/moments/']) &&
!pathname.includes('/edit/')))
);
};

export const getHTML = url =>
fetchOEmbedData(
`https://publish.twitter.com/oembed?url=${url}&dnt=true&omit_script=true`
export const getHTML = url => {
/**
* For moments, Twitter oembed doesn't work with urls using 'events', they should
* use 'moments', even though they redirect from 'moments' to 'events' on the browser.
*/
const twitterUrl = url.replace('events', 'moments');

return fetchOEmbedData(
`https://publish.twitter.com/oembed?url=${twitterUrl}&dnt=true&omit_script=true`
).then(({ html }) =>
[html]
.map(s => s.replace(/\?ref_src=twsrc.*?fw/g, ''))
.map(s => s.replace(/<br>/g, '<br />'))
.join('')
.trim()
);
};

0 comments on commit ed6f881

Please sign in to comment.