浏览代码

Twitter's unit test

Cqoicebordel 10 年前
父节点
当前提交
d6e511fc2f
共有 3 个文件被更改,包括 511 次插入5 次删除
  1. 8
    5
      searx/engines/twitter.py
  2. 502
    0
      searx/tests/engines/test_twitter.py
  3. 1
    0
      searx/tests/test_engines.py

+ 8
- 5
searx/engines/twitter.py 查看文件

13
 from urlparse import urljoin
13
 from urlparse import urljoin
14
 from urllib import urlencode
14
 from urllib import urlencode
15
 from lxml import html
15
 from lxml import html
16
-from cgi import escape
17
 from datetime import datetime
16
 from datetime import datetime
17
+from searx.engines.xpath import extract_text
18
 
18
 
19
 # engine dependent config
19
 # engine dependent config
20
 categories = ['social media']
20
 categories = ['social media']
22
 
22
 
23
 # search-url
23
 # search-url
24
 base_url = 'https://twitter.com/'
24
 base_url = 'https://twitter.com/'
25
-search_url = base_url+'search?'
25
+search_url = base_url + 'search?'
26
 
26
 
27
 # specific xpath variables
27
 # specific xpath variables
28
 results_xpath = '//li[@data-item-type="tweet"]'
28
 results_xpath = '//li[@data-item-type="tweet"]'
29
 link_xpath = './/small[@class="time"]//a'
29
 link_xpath = './/small[@class="time"]//a'
30
-title_xpath = './/span[@class="username js-action-profile-name"]//text()'
30
+title_xpath = './/span[@class="username js-action-profile-name"]'
31
 content_xpath = './/p[@class="js-tweet-text tweet-text"]'
31
 content_xpath = './/p[@class="js-tweet-text tweet-text"]'
32
 timestamp_xpath = './/span[contains(@class,"_timestamp")]'
32
 timestamp_xpath = './/span[contains(@class,"_timestamp")]'
33
 
33
 
39
     # set language if specified
39
     # set language if specified
40
     if params['language'] != 'all':
40
     if params['language'] != 'all':
41
         params['cookies']['lang'] = params['language'].split('_')[0]
41
         params['cookies']['lang'] = params['language'].split('_')[0]
42
+    else:
43
+        params['cookies']['lang'] = 'en'
42
 
44
 
43
     return params
45
     return params
44
 
46
 
53
     for tweet in dom.xpath(results_xpath):
55
     for tweet in dom.xpath(results_xpath):
54
         link = tweet.xpath(link_xpath)[0]
56
         link = tweet.xpath(link_xpath)[0]
55
         url = urljoin(base_url, link.attrib.get('href'))
57
         url = urljoin(base_url, link.attrib.get('href'))
56
-        title = ''.join(tweet.xpath(title_xpath))
57
-        content = escape(html.tostring(tweet.xpath(content_xpath)[0], method='text', encoding='UTF-8').decode("utf-8"))
58
+        title = extract_text(tweet.xpath(title_xpath))
59
+        content = extract_text(tweet.xpath(content_xpath)[0])
60
+
58
         pubdate = tweet.xpath(timestamp_xpath)
61
         pubdate = tweet.xpath(timestamp_xpath)
59
         if len(pubdate) > 0:
62
         if len(pubdate) > 0:
60
             timestamp = float(pubdate[0].attrib.get('data-time'))
63
             timestamp = float(pubdate[0].attrib.get('data-time'))

+ 502
- 0
searx/tests/engines/test_twitter.py 查看文件

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

+ 1
- 0
searx/tests/test_engines.py 查看文件

23
 from searx.tests.engines.test_searchcode_doc import *  # noqa
23
 from searx.tests.engines.test_searchcode_doc import *  # noqa
24
 from searx.tests.engines.test_soundcloud import *  # noqa
24
 from searx.tests.engines.test_soundcloud import *  # noqa
25
 from searx.tests.engines.test_stackoverflow import *  # noqa
25
 from searx.tests.engines.test_stackoverflow import *  # noqa
26
+from searx.tests.engines.test_twitter import *  # noqa
26
 from searx.tests.engines.test_vimeo import *  # noqa
27
 from searx.tests.engines.test_vimeo import *  # noqa
27
 from searx.tests.engines.test_www500px import *  # noqa
28
 from searx.tests.engines.test_www500px import *  # noqa
28
 from searx.tests.engines.test_youtube import *  # noqa
29
 from searx.tests.engines.test_youtube import *  # noqa