123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. # -*- coding: utf-8 -*-
  2. from collections import defaultdict
  3. import mock
  4. from searx.engines import twitter
  5. from searx.testing import SearxTestCase
  6. class TestTwitterEngine(SearxTestCase):
  7. def test_request(self):
  8. query = 'test_query'
  9. dicto = defaultdict(dict)
  10. dicto['pageno'] = 0
  11. dicto['language'] = 'fr_FR'
  12. params = twitter.request(query, dicto)
  13. self.assertIn('url', params)
  14. self.assertIn(query, params['url'])
  15. self.assertIn('twitter.com', params['url'])
  16. self.assertIn('cookies', params)
  17. self.assertIn('lang', params['cookies'])
  18. self.assertIn('fr', params['cookies']['lang'])
  19. dicto['language'] = 'all'
  20. params = twitter.request(query, dicto)
  21. self.assertIn('cookies', params)
  22. self.assertIn('lang', params['cookies'])
  23. self.assertIn('en', params['cookies']['lang'])
  24. def test_response(self):
  25. self.assertRaises(AttributeError, twitter.response, None)
  26. self.assertRaises(AttributeError, twitter.response, [])
  27. self.assertRaises(AttributeError, twitter.response, '')
  28. self.assertRaises(AttributeError, twitter.response, '[]')
  29. response = mock.Mock(text='<html></html>')
  30. self.assertEqual(twitter.response(response), [])
  31. html = """
  32. <li class="js-stream-item stream-item stream-item expanding-stream-item" data-item-id="563005573290287105"
  33. id="stream-item-tweet-563005573290287105" data-item-type="tweet">
  34. <div class="tweet original-tweet js-stream-tweet js-actionable-tweet js-profile-popup-actionable
  35. js-original-tweet has-cards has-native-media" data-tweet-id="563005573290287105" data-disclosure-type=""
  36. data-item-id="563005573290287105" data-screen-name="Jalopnik" data-name="Jalopnik"
  37. data-user-id="3060631" data-has-native-media="true" data-has-cards="true" data-card-type="photo"
  38. data-expanded-footer="&lt;div class=&quot;js-tweet-details-fixer
  39. tweet-details-fixer&quot;&gt;&#10;&#10;&#10;
  40. &lt;div class=&quot;cards-media-container js-media-container&quot;&gt;&lt;div
  41. data-card-url=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot; data-card-type=&quot;
  42. photo&quot; class=&quot;cards-base cards-multimedia&quot; data-element-context=&quot;platform_photo_card
  43. &quot;&gt;&#10;&#10;&#10; &lt;a class=&quot;media media-thumbnail twitter-timeline-link is-preview
  44. &quot; data-url=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
  45. data-resolved-url-large=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
  46. href=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot;&gt;&#10;
  47. &lt;div class=&quot;&quot;&gt;&#10; &lt;img src=&quot;
  48. https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg&quot;
  49. alt=&quot;Embedded image permalink&quot; width=&quot;636&quot; height=&quot;309&quot;&gt;&#10;
  50. &lt;/div&gt;&#10;&#10; &lt;/a&gt;&#10;&#10; &lt;div class=&quot;cards-content&quot;&gt;&#10;
  51. &lt;div class=&quot;byline&quot;&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;/div&gt;&#10;
  52. &#10;&lt;/div&gt;&#10;&#10;&#10;&#10;&#10;&lt;/div&gt;&#10;&#10;&#10;&#10; &lt;div
  53. class=&quot;js-machine-translated-tweet-container&quot;&gt;&lt;/div&gt;&#10; &lt;div
  54. class=&quot;js-tweet-stats-container tweet-stats-container &quot;&gt;&#10; &lt;/div&gt;&#10;&#10;
  55. &lt;div class=&quot;client-and-actions&quot;&gt;&#10; &lt;span class=&quot;metadata&quot;&gt;&#10;
  56. &lt;span&gt;5:06 PM - 4 Feb 2015&lt;/span&gt;&#10;&#10; &amp;middot; &lt;a
  57. class=&quot;permalink-link js-permalink js-nav&quot; href=&quot;/Jalopnik/status/563005573290287105
  58. &quot;tabindex=&quot;-1&quot;&gt;Details&lt;/a&gt;&#10; &#10;&#10; &#10; &#10;
  59. &#10;&#10; &lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&#10;&lt;/div&gt;&#10;" data-you-follow="false"
  60. data-you-block="false">
  61. <div class="context">
  62. </div>
  63. <div class="content">
  64. <div class="stream-item-header">
  65. <a class="account-group js-account-group js-action-profile js-user-profile-link js-nav"
  66. href="/Jalopnik" data-user-id="3060631">
  67. <img class="avatar js-action-profile-avatar"
  68. src="https://pbs.twimg.com/profile_images/2976430168/5cd4a59_bigger.jpeg" alt="">
  69. <strong class="fullname js-action-profile-name show-popup-with-id" data-aria-label-part>
  70. Jalopnik
  71. </strong>
  72. <span>&rlm;</span>
  73. <span class="username js-action-profile-name" data-aria-label-part>
  74. <s>@</s><b>TitleName</b>
  75. </span>
  76. </a>
  77. <small class="time">
  78. <a href="/this.is.the.url"
  79. class="tweet-timestamp js-permalink js-nav js-tooltip" title="5:06 PM - 4 Feb 2015" >
  80. <span class="u-hiddenVisually" data-aria-label-part="last">17 minutes ago</span>
  81. </a>
  82. </small>
  83. </div>
  84. <p class="js-tweet-text tweet-text" lang="en" data-aria-label-part="0">
  85. This is the content étude à€
  86. <a href="http://t.co/nRWsqQAwBL" rel="nofollow" dir="ltr"
  87. data-expanded-url="http://jalo.ps/ReMENu4" class="twitter-timeline-link"
  88. target="_blank" title="http://jalo.ps/ReMENu4" >
  89. <span class="tco-ellipsis">
  90. </span>
  91. <span class="invisible">http://</span><span class="js-display-url">link.in.tweet</span>
  92. <span class="invisible"></span>
  93. <span class="tco-ellipsis">
  94. <span class="invisible">&nbsp;</span>
  95. </span>
  96. </a>
  97. <a href="http://t.co/rbFsfeE0l3" class="twitter-timeline-link u-hidden"
  98. data-pre-embedded="true" dir="ltr">
  99. pic.twitter.com/rbFsfeE0l3
  100. </a>
  101. </p>
  102. <div class="expanded-content js-tweet-details-dropdown">
  103. </div>
  104. <div class="stream-item-footer">
  105. <a class="details with-icn js-details" href="/Jalopnik/status/563005573290287105">
  106. <span class="Icon Icon--photo">
  107. </span>
  108. <b>
  109. <span class="expand-stream-item js-view-details">
  110. View photo
  111. </span>
  112. <span class="collapse-stream-item js-hide-details">
  113. Hide photo
  114. </span>
  115. </b>
  116. </a>
  117. <span class="ProfileTweet-action--reply u-hiddenVisually">
  118. <span class="ProfileTweet-actionCount" aria-hidden="true" data-tweet-stat-count="0">
  119. <span class="ProfileTweet-actionCountForAria" >0 replies</span>
  120. </span>
  121. </span>
  122. <span class="ProfileTweet-action--retweet u-hiddenVisually">
  123. <span class="ProfileTweet-actionCount" data-tweet-stat-count="8">
  124. <span class="ProfileTweet-actionCountForAria" data-aria-label-part>8 retweets</span>
  125. </span>
  126. </span>
  127. <span class="ProfileTweet-action--favorite u-hiddenVisually">
  128. <span class="ProfileTweet-actionCount" data-tweet-stat-count="14">
  129. <span class="ProfileTweet-actionCountForAria" data-aria-label-part>14 favorites</span>
  130. </span>
  131. </span>
  132. <div role="group" aria-label="Tweet actions" class="ProfileTweet-actionList u-cf js-actions">
  133. <div class="ProfileTweet-action ProfileTweet-action--reply">
  134. <button class="ProfileTweet-actionButton u-textUserColorHover js-actionButton
  135. js-actionReply" data-modal="ProfileTweet-reply" type="button" title="Reply">
  136. <span class="Icon Icon--reply">
  137. </span>
  138. <span class="u-hiddenVisually">Reply</span>
  139. <span class="ProfileTweet-actionCount u-textUserColorHover
  140. ProfileTweet-actionCount--isZero">
  141. <span class="ProfileTweet-actionCountForPresentation" aria-hidden="true">
  142. </span>
  143. </span>
  144. </button>
  145. </div>
  146. <div class="ProfileTweet-action ProfileTweet-action--retweet js-toggleState js-toggleRt">
  147. <button class="ProfileTweet-actionButton js-actionButton js-actionRetweet js-tooltip"
  148. title="Retweet" data-modal="ProfileTweet-retweet" type="button">
  149. <span class="Icon Icon--retweet">
  150. </span>
  151. <span class="u-hiddenVisually">Retweet</span>
  152. <span class="ProfileTweet-actionCount">
  153. <span class="ProfileTweet-actionCountForPresentation">8</span>
  154. </span>
  155. </button>
  156. <button class="ProfileTweet-actionButtonUndo js-actionButton js-actionRetweet"
  157. data-modal="ProfileTweet-retweet" title="Undo retweet" type="button">
  158. <span class="Icon Icon--retweet">
  159. </span>
  160. <span class="u-hiddenVisually">Retweeted</span>
  161. <span class="ProfileTweet-actionCount">
  162. <span class="ProfileTweet-actionCountForPresentation">8</span>
  163. </span>
  164. </button>
  165. </div>
  166. <div class="ProfileTweet-action ProfileTweet-action--favorite js-toggleState">
  167. <button class="ProfileTweet-actionButton js-actionButton js-actionFavorite js-tooltip"
  168. title="Favorite" type="button">
  169. <span class="Icon Icon--favorite">
  170. </span>
  171. <span class="u-hiddenVisually">Favorite</span>
  172. <span class="ProfileTweet-actionCount">
  173. <span class="ProfileTweet-actionCountForPresentation">14</span>
  174. </span>
  175. </button>
  176. <button class="ProfileTweet-actionButtonUndo u-linkClean js-actionButton
  177. js-actionFavorite" title="Undo favorite" type="button">
  178. <span class="Icon Icon--favorite">
  179. </span>
  180. <span class="u-hiddenVisually">Favorited</span>
  181. <span class="ProfileTweet-actionCount">
  182. <span class="ProfileTweet-actionCountForPresentation">
  183. 14
  184. </span>
  185. </span>
  186. </button>
  187. </div>
  188. <div class="ProfileTweet-action ProfileTweet-action--more js-more-ProfileTweet-actions">
  189. <div class="dropdown">
  190. <button class="ProfileTweet-actionButton u-textUserColorHover dropdown-toggle
  191. js-tooltip js-dropdown-toggle" type="button" title="More">
  192. <span class="Icon Icon--dots">
  193. </span>
  194. <span class="u-hiddenVisually">More</span>
  195. </button>
  196. <div class="dropdown-menu">
  197. <div class="dropdown-caret">
  198. <div class="caret-outer">
  199. </div>
  200. <div class="caret-inner">
  201. </div>
  202. </div>
  203. <ul>
  204. <li class="share-via-dm js-actionShareViaDM" data-nav="share_tweet_dm">
  205. <button type="button" class="dropdown-link">
  206. Share via Direct Message
  207. </button>
  208. </li>
  209. <li class="embed-link js-actionEmbedTweet" data-nav="embed_tweet">
  210. <button type="button" class="dropdown-link">
  211. Embed Tweet
  212. </button>
  213. </li>
  214. <li class="mute-user-item pretty-link">
  215. <button type="button" class="dropdown-link">
  216. Mute
  217. </button>
  218. </li>
  219. <li class="unmute-user-item pretty-link">
  220. <button type="button" class="dropdown-link">
  221. Unmute
  222. </button>
  223. </li>
  224. <li class="block-or-report-link js-actionBlockOrReport"
  225. data-nav="block_or_report">
  226. <button type="button" class="dropdown-link">
  227. Block or report
  228. </button>
  229. </li>
  230. </ul>
  231. </div>
  232. </div>
  233. </div>
  234. </div>
  235. </div>
  236. </div>
  237. </div>
  238. </li>
  239. """
  240. response = mock.Mock(text=html)
  241. results = twitter.response(response)
  242. self.assertEqual(type(results), list)
  243. self.assertEqual(len(results), 1)
  244. self.assertEqual(results[0]['title'], '@TitleName')
  245. self.assertEqual(results[0]['url'], 'https://twitter.com/this.is.the.url')
  246. self.assertIn(u'This is the content', results[0]['content'])
  247. # self.assertIn(u'This is the content étude à€', results[0]['content'])
  248. html = """
  249. <li class="js-stream-item stream-item stream-item expanding-stream-item" data-item-id="563005573290287105"
  250. id="stream-item-tweet-563005573290287105" data-item-type="tweet">
  251. <div class="tweet original-tweet js-stream-tweet js-actionable-tweet js-profile-popup-actionable
  252. js-original-tweet has-cards has-native-media" data-tweet-id="563005573290287105" data-disclosure-type=""
  253. data-item-id="563005573290287105" data-screen-name="Jalopnik" data-name="Jalopnik"
  254. data-user-id="3060631" data-has-native-media="true" data-has-cards="true" data-card-type="photo"
  255. data-expanded-footer="&lt;div class=&quot;js-tweet-details-fixer
  256. tweet-details-fixer&quot;&gt;&#10;&#10;&#10;
  257. &lt;div class=&quot;cards-media-container js-media-container&quot;&gt;&lt;div
  258. data-card-url=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot; data-card-type=&quot;
  259. photo&quot; class=&quot;cards-base cards-multimedia&quot; data-element-context=&quot;platform_photo_card
  260. &quot;&gt;&#10;&#10;&#10; &lt;a class=&quot;media media-thumbnail twitter-timeline-link is-preview
  261. &quot; data-url=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
  262. data-resolved-url-large=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
  263. href=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot;&gt;&#10;
  264. &lt;div class=&quot;&quot;&gt;&#10; &lt;img src=&quot;
  265. https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg&quot;
  266. alt=&quot;Embedded image permalink&quot; width=&quot;636&quot; height=&quot;309&quot;&gt;&#10;
  267. &lt;/div&gt;&#10;&#10; &lt;/a&gt;&#10;&#10; &lt;div class=&quot;cards-content&quot;&gt;&#10;
  268. &lt;div class=&quot;byline&quot;&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;/div&gt;&#10;
  269. &#10;&lt;/div&gt;&#10;&#10;&#10;&#10;&#10;&lt;/div&gt;&#10;&#10;&#10;&#10; &lt;div
  270. class=&quot;js-machine-translated-tweet-container&quot;&gt;&lt;/div&gt;&#10; &lt;div
  271. class=&quot;js-tweet-stats-container tweet-stats-container &quot;&gt;&#10; &lt;/div&gt;&#10;&#10;
  272. &lt;div class=&quot;client-and-actions&quot;&gt;&#10; &lt;span class=&quot;metadata&quot;&gt;&#10;
  273. &lt;span&gt;5:06 PM - 4 Feb 2015&lt;/span&gt;&#10;&#10; &amp;middot; &lt;a
  274. class=&quot;permalink-link js-permalink js-nav&quot; href=&quot;/Jalopnik/status/563005573290287105
  275. &quot;tabindex=&quot;-1&quot;&gt;Details&lt;/a&gt;&#10; &#10;&#10; &#10; &#10;
  276. &#10;&#10; &lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&#10;&lt;/div&gt;&#10;" data-you-follow="false"
  277. data-you-block="false">
  278. <div class="context">
  279. </div>
  280. <div class="content">
  281. <div class="stream-item-header">
  282. <a class="account-group js-account-group js-action-profile js-user-profile-link js-nav"
  283. href="/Jalopnik" data-user-id="3060631">
  284. <img class="avatar js-action-profile-avatar"
  285. src="https://pbs.twimg.com/profile_images/2976430168/5cd4a59_bigger.jpeg" alt="">
  286. <strong class="fullname js-action-profile-name show-popup-with-id" data-aria-label-part>
  287. Jalopnik
  288. </strong>
  289. <span>&rlm;</span>
  290. <span class="username js-action-profile-name" data-aria-label-part>
  291. <s>@</s><b>TitleName</b>
  292. </span>
  293. </a>
  294. <small class="time">
  295. <a href="/this.is.the.url"
  296. class="tweet-timestamp js-permalink js-nav js-tooltip" title="5:06 PM - 4 Feb 2015" >
  297. <span class="_timestamp js-short-timestamp js-relative-timestamp" data-time="1423065963"
  298. data-time-ms="1423065963000" data-long-form="true" aria-hidden="true">
  299. 17m
  300. </span>
  301. <span class="u-hiddenVisually" data-aria-label-part="last">17 minutes ago</span>
  302. </a>
  303. </small>
  304. </div>
  305. <p class="js-tweet-text tweet-text" lang="en" data-aria-label-part="0">
  306. This is the content étude à€
  307. <a href="http://t.co/nRWsqQAwBL" rel="nofollow" dir="ltr"
  308. data-expanded-url="http://jalo.ps/ReMENu4" class="twitter-timeline-link"
  309. target="_blank" title="http://jalo.ps/ReMENu4" >
  310. <span class="tco-ellipsis">
  311. </span>
  312. <span class="invisible">http://</span><span class="js-display-url">link.in.tweet</span>
  313. <span class="invisible"></span>
  314. <span class="tco-ellipsis">
  315. <span class="invisible">&nbsp;</span>
  316. </span>
  317. </a>
  318. <a href="http://t.co/rbFsfeE0l3" class="twitter-timeline-link u-hidden"
  319. data-pre-embedded="true" dir="ltr">
  320. pic.twitter.com/rbFsfeE0l3
  321. </a>
  322. </p>
  323. <div class="expanded-content js-tweet-details-dropdown">
  324. </div>
  325. <div class="stream-item-footer">
  326. <a class="details with-icn js-details" href="/Jalopnik/status/563005573290287105">
  327. <span class="Icon Icon--photo">
  328. </span>
  329. <b>
  330. <span class="expand-stream-item js-view-details">
  331. View photo
  332. </span>
  333. <span class="collapse-stream-item js-hide-details">
  334. Hide photo
  335. </span>
  336. </b>
  337. </a>
  338. <span class="ProfileTweet-action--reply u-hiddenVisually">
  339. <span class="ProfileTweet-actionCount" aria-hidden="true" data-tweet-stat-count="0">
  340. <span class="ProfileTweet-actionCountForAria" >0 replies</span>
  341. </span>
  342. </span>
  343. <span class="ProfileTweet-action--retweet u-hiddenVisually">
  344. <span class="ProfileTweet-actionCount" data-tweet-stat-count="8">
  345. <span class="ProfileTweet-actionCountForAria" data-aria-label-part>8 retweets</span>
  346. </span>
  347. </span>
  348. <span class="ProfileTweet-action--favorite u-hiddenVisually">
  349. <span class="ProfileTweet-actionCount" data-tweet-stat-count="14">
  350. <span class="ProfileTweet-actionCountForAria" data-aria-label-part>14 favorites</span>
  351. </span>
  352. </span>
  353. <div role="group" aria-label="Tweet actions" class="ProfileTweet-actionList u-cf js-actions">
  354. <div class="ProfileTweet-action ProfileTweet-action--reply">
  355. <button class="ProfileTweet-actionButton u-textUserColorHover js-actionButton
  356. js-actionReply" data-modal="ProfileTweet-reply" type="button" title="Reply">
  357. <span class="Icon Icon--reply">
  358. </span>
  359. <span class="u-hiddenVisually">Reply</span>
  360. <span class="ProfileTweet-actionCount u-textUserColorHover
  361. ProfileTweet-actionCount--isZero">
  362. <span class="ProfileTweet-actionCountForPresentation" aria-hidden="true">
  363. </span>
  364. </span>
  365. </button>
  366. </div>
  367. <div class="ProfileTweet-action ProfileTweet-action--retweet js-toggleState js-toggleRt">
  368. <button class="ProfileTweet-actionButton js-actionButton js-actionRetweet js-tooltip"
  369. title="Retweet" data-modal="ProfileTweet-retweet" type="button">
  370. <span class="Icon Icon--retweet">
  371. </span>
  372. <span class="u-hiddenVisually">Retweet</span>
  373. <span class="ProfileTweet-actionCount">
  374. <span class="ProfileTweet-actionCountForPresentation">8</span>
  375. </span>
  376. </button>
  377. <button class="ProfileTweet-actionButtonUndo js-actionButton js-actionRetweet"
  378. data-modal="ProfileTweet-retweet" title="Undo retweet" type="button">
  379. <span class="Icon Icon--retweet">
  380. </span>
  381. <span class="u-hiddenVisually">Retweeted</span>
  382. <span class="ProfileTweet-actionCount">
  383. <span class="ProfileTweet-actionCountForPresentation">8</span>
  384. </span>
  385. </button>
  386. </div>
  387. <div class="ProfileTweet-action ProfileTweet-action--favorite js-toggleState">
  388. <button class="ProfileTweet-actionButton js-actionButton js-actionFavorite js-tooltip"
  389. title="Favorite" type="button">
  390. <span class="Icon Icon--favorite">
  391. </span>
  392. <span class="u-hiddenVisually">Favorite</span>
  393. <span class="ProfileTweet-actionCount">
  394. <span class="ProfileTweet-actionCountForPresentation">14</span>
  395. </span>
  396. </button>
  397. <button class="ProfileTweet-actionButtonUndo u-linkClean js-actionButton
  398. js-actionFavorite" title="Undo favorite" type="button">
  399. <span class="Icon Icon--favorite">
  400. </span>
  401. <span class="u-hiddenVisually">Favorited</span>
  402. <span class="ProfileTweet-actionCount">
  403. <span class="ProfileTweet-actionCountForPresentation">
  404. 14
  405. </span>
  406. </span>
  407. </button>
  408. </div>
  409. <div class="ProfileTweet-action ProfileTweet-action--more js-more-ProfileTweet-actions">
  410. <div class="dropdown">
  411. <button class="ProfileTweet-actionButton u-textUserColorHover dropdown-toggle
  412. js-tooltip js-dropdown-toggle" type="button" title="More">
  413. <span class="Icon Icon--dots">
  414. </span>
  415. <span class="u-hiddenVisually">More</span>
  416. </button>
  417. <div class="dropdown-menu">
  418. <div class="dropdown-caret">
  419. <div class="caret-outer">
  420. </div>
  421. <div class="caret-inner">
  422. </div>
  423. </div>
  424. <ul>
  425. <li class="share-via-dm js-actionShareViaDM" data-nav="share_tweet_dm">
  426. <button type="button" class="dropdown-link">
  427. Share via Direct Message
  428. </button>
  429. </li>
  430. <li class="embed-link js-actionEmbedTweet" data-nav="embed_tweet">
  431. <button type="button" class="dropdown-link">
  432. Embed Tweet
  433. </button>
  434. </li>
  435. <li class="mute-user-item pretty-link">
  436. <button type="button" class="dropdown-link">
  437. Mute
  438. </button>
  439. </li>
  440. <li class="unmute-user-item pretty-link">
  441. <button type="button" class="dropdown-link">
  442. Unmute
  443. </button>
  444. </li>
  445. <li class="block-or-report-link js-actionBlockOrReport"
  446. data-nav="block_or_report">
  447. <button type="button" class="dropdown-link">
  448. Block or report
  449. </button>
  450. </li>
  451. </ul>
  452. </div>
  453. </div>
  454. </div>
  455. </div>
  456. </div>
  457. </div>
  458. </div>
  459. </li>
  460. """
  461. response = mock.Mock(text=html)
  462. results = twitter.response(response)
  463. self.assertEqual(type(results), list)
  464. self.assertEqual(len(results), 1)
  465. self.assertEqual(results[0]['title'], '@TitleName')
  466. self.assertEqual(results[0]['url'], 'https://twitter.com/this.is.the.url')
  467. self.assertIn(u'This is the content', results[0]['content'])
  468. html = """
  469. <li class="b_algo" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
  470. <div Class="sa_mc">
  471. <div class="sb_tlst">
  472. <h2>
  473. <a href="http://this.should.be.the.link/" h="ID=SERP,5124.1">
  474. <strong>This</strong> should be the title</a>
  475. </h2>
  476. </div>
  477. <div class="sb_meta">
  478. <cite>
  479. <strong>this</strong>.meta.com</cite>
  480. <span class="c_tlbxTrg">
  481. <span class="c_tlbxH" H="BASE:CACHEDPAGEDEFAULT" K="SERP,5125.1">
  482. </span>
  483. </span>
  484. </div>
  485. <p>
  486. <strong>This</strong> should be the content.</p>
  487. </div>
  488. </li>
  489. """
  490. response = mock.Mock(text=html)
  491. results = twitter.response(response)
  492. self.assertEqual(type(results), list)
  493. self.assertEqual(len(results), 0)