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